about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/tools/rust-analyzer/Cargo.lock102
-rw-r--r--src/tools/rust-analyzer/Cargo.toml24
-rw-r--r--src/tools/rust-analyzer/crates/base-db/src/lib.rs2
-rw-r--r--src/tools/rust-analyzer/crates/edition/src/lib.rs3
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/attr.rs5
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/body.rs15
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs44
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/body/scope.rs4
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/body/tests.rs23
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/data.rs9
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/expander.rs4
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/find_path.rs26
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/import_map.rs9
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs19
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/item_tree/tests.rs4
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/lib.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs30
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs5
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/proc_macros.rs6
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs6
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/nameres/tests.rs6
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/resolver.rs60
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/visibility.rs4
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/builtin/derive_macro.rs105
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs10
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/builtin/quote.rs9
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/db.rs37
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs11
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/files.rs11
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs9
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/hygiene.rs46
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/lib.rs6
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs21
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/name.rs49
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs14
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/display.rs49
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility/tests.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs26
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/lower.rs33
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs26
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs10
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests.rs23
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs12
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/variance.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir/Cargo.toml1
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/attrs.rs15
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/display.rs33
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/lib.rs118
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/semantics.rs66
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/semantics/child_by_source.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs32
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs74
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/symbols.rs253
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/assist_context.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs12
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_turbo_fish.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/apply_demorgan.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/bool_to_enum.rs1
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_closure_to_fn.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_from_to_tryfrom.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_while_to_loop.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_tuple_binding.rs10
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs36
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs18
-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_macro.rs18
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_guard.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_dbg.rs9
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_if_let_with_match.rs98
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_let_with_if_let.rs27
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_try_expr_with_match.rs8
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/unmerge_match_arm.rs10
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_block.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_return_type.rs177
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_return_type.rs119
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/tests.rs45
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/utils.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/utils/gen_trait_fn_body.rs32
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/utils/ref_field_expr.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions.rs6
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs298
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/env_vars.rs57
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs26
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/extern_abi.rs15
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs9
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/format_string.rs13
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs41
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/keyword.rs9
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/lifetime.rs9
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/mod_.rs9
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs11
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/context.rs5
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.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.rs46
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/lib.rs5
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/render.rs140
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs20
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/tests.rs55
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs51
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs202
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs14
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/tests/fn_param.rs16
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/tests/item.rs41
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/tests/item_list.rs53
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/tests/pattern.rs93
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/tests/predicate.rs25
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/tests/proc_macros.rs9
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/tests/raw_identifiers.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/tests/record.rs9
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/tests/special.rs24
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs99
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/tests/use_tree.rs9
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/tests/visibility.rs16
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/active_parameter.rs6
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/defs.rs39
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/documentation.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/famous_defs.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs43
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use/tests.rs34
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/items_locator.rs19
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/lib.rs32
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/rename.rs1
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/search.rs11
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs53
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt87
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/traits.rs8
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/generic_args_prohibited.rs19
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/inactive_code.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/macro_error.rs32
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs34
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs31
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/tests.rs55
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/tests/overly_long_real_world_cases.rs2726
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/annotations.rs8
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/doc_links.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs6
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/expand_macro.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/file_structure.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/fixture.rs20
-rwxr-xr-xsrc/tools/rust-analyzer/crates/ide/src/folding_ranges.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/goto_declaration.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/goto_definition.rs168
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs7
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/highlight_related.rs7
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/hover.rs51
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/hover/render.rs78
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/hover/tests.rs193
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs159
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs24
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs95
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/binding_mode.rs56
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/bounds.rs152
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs15
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_ret.rs3
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/discriminant.rs52
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/extern_block.rs26
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/generic_param.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_drop.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_static.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/join_lines.rs10
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/lib.rs26
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/moniker.rs18
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/move_item.rs6
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/navigation_target.rs16
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/parent_module.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/references.rs8
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/rename.rs20
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/runnables.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/signature_help.rs6
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/ssr.rs5
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/static_index.rs11
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs3
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs13
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_attributes.html2
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_block_mod_items.html6
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_const.html6
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html6
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html2
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html2
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_issue_18089.html4
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2015.html16
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2018.html16
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2021.html16
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2024.html16
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html20
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html110
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html12
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs8
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_tree.rs338
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/typing.rs12
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/typing/on_enter.rs5
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/view_syntax_tree.rs226
-rw-r--r--src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs5
-rw-r--r--src/tools/rust-analyzer/crates/load-cargo/src/lib.rs5
-rw-r--r--src/tools/rust-analyzer/crates/mbe/src/expander/transcriber.rs10
-rw-r--r--src/tools/rust-analyzer/crates/mbe/src/lib.rs3
-rw-r--r--src/tools/rust-analyzer/crates/mbe/src/tests.rs177
-rw-r--r--src/tools/rust-analyzer/crates/parser/src/event.rs2
-rw-r--r--src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs23
-rw-r--r--src/tools/rust-analyzer/crates/parser/src/parser.rs25
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/err/0056_let_else_right_curly_brace_struct.rast79
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/err/0056_let_else_right_curly_brace_struct.rs8
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/err/0057_let_else_right_curly_brace_arithmetic.rast42
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/err/0057_let_else_right_curly_brace_arithmetic.rs5
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/err/0057_let_else_right_curly_brace_format_args.rast90
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/err/0057_let_else_right_curly_brace_format_args.rs5
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/err/0058_let_else_right_curly_brace_range.rast40
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/err/0058_let_else_right_curly_brace_range.rs5
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/err/0059_let_else_right_curly_brace_closure.rast55
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/err/0059_let_else_right_curly_brace_closure.rs5
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/err/0060_let_else_right_curly_brace_unary.rast38
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/err/0060_let_else_right_curly_brace_unary.rs5
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/err/0061_let_else_right_curly_brace_do_yeet.rast90
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/err/0061_let_else_right_curly_brace_do_yeet.rs7
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/err/0062_let_else_right_curly_brace_become.rast40
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/err/0062_let_else_right_curly_brace_become.rs5
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/err/0063_let_else_right_curly_brace_reference.rast38
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/err/0063_let_else_right_curly_brace_reference.rs5
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/err/0064_let_else_right_curly_brace_assignment.rast45
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/err/0064_let_else_right_curly_brace_assignment.rs5
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/ref_expr.rast19
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/ref_expr.rs1
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/msg.rs20
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml1
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib.rs11
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs4
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/mod.rs130
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs13
-rw-r--r--src/tools/rust-analyzer/crates/profile/Cargo.toml5
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs2
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/tests.rs3
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/toolchain_info/rustc_cfg.rs2
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/workspace.rs45
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml5
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs1
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs1
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs4
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs45
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs27
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs12
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs11
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs25
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs2
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/test_runner.rs6
-rw-r--r--src/tools/rust-analyzer/crates/span/src/hygiene.rs29
-rw-r--r--src/tools/rust-analyzer/crates/span/src/map.rs2
-rw-r--r--src/tools/rust-analyzer/crates/stdx/Cargo.toml2
-rw-r--r--src/tools/rust-analyzer/crates/syntax-bridge/src/lib.rs17
-rw-r--r--src/tools/rust-analyzer/crates/syntax-bridge/src/to_parser_input.rs44
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/ast/edit.rs4
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/ast/expr_ext.rs23
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/ast/make.rs51
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs8
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs364
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs7
-rw-r--r--src/tools/rust-analyzer/crates/test-fixture/Cargo.toml2
-rw-r--r--src/tools/rust-analyzer/crates/test-fixture/src/lib.rs96
-rw-r--r--src/tools/rust-analyzer/crates/test-utils/src/fixture.rs2
-rw-r--r--src/tools/rust-analyzer/crates/test-utils/src/minicore.rs58
-rw-r--r--src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml2
-rw-r--r--src/tools/rust-analyzer/docs/dev/README.md16
-rw-r--r--src/tools/rust-analyzer/docs/dev/lsp-extensions.md19
-rw-r--r--src/tools/rust-analyzer/docs/user/generated_config.adoc32
-rw-r--r--src/tools/rust-analyzer/editors/code/package.json147
-rw-r--r--src/tools/rust-analyzer/editors/code/src/ast_inspector.ts216
-rw-r--r--src/tools/rust-analyzer/editors/code/src/commands.ts167
-rw-r--r--src/tools/rust-analyzer/editors/code/src/config.ts4
-rw-r--r--src/tools/rust-analyzer/editors/code/src/ctx.ts93
-rw-r--r--src/tools/rust-analyzer/editors/code/src/lsp_ext.ts4
-rw-r--r--src/tools/rust-analyzer/editors/code/src/main.ts5
-rw-r--r--src/tools/rust-analyzer/editors/code/src/syntax_tree_provider.ts301
-rw-r--r--src/tools/rust-analyzer/lib/lsp-server/src/msg.rs8
-rw-r--r--src/tools/rust-analyzer/lib/lsp-server/src/socket.rs23
-rw-r--r--src/tools/rust-analyzer/lib/lsp-server/src/stdio.rs23
-rw-r--r--src/tools/rust-analyzer/rust-version2
286 files changed, 9088 insertions, 2920 deletions
diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock
index 48b5f3aabfc..f92668a6a97 100644
--- a/src/tools/rust-analyzer/Cargo.lock
+++ b/src/tools/rust-analyzer/Cargo.lock
@@ -98,9 +98,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
 
 [[package]]
 name = "bitflags"
-version = "2.6.0"
+version = "2.7.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
+checksum = "1be3f42a67d6d345ecd59f675f3f012d6974981560836e938c22b424b85ce1be"
 
 [[package]]
 name = "borsh"
@@ -194,9 +194,9 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
 
 [[package]]
 name = "chalk-derive"
-version = "0.98.0"
+version = "0.99.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9426c8fd0fe61c3da880b801d3b510524df17843a8f9ec1f5b9cec24fb7412df"
+checksum = "572583d9b97f9d277e5c7607f8239a30e2e04d3ed3b47c87d1cb2152ae724073"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -206,19 +206,19 @@ dependencies = [
 
 [[package]]
 name = "chalk-ir"
-version = "0.98.0"
+version = "0.99.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d5f2eb1cd6054da221bd1ac0197fb2fe5e2caf3dcb93619398fc1433f8f09093"
+checksum = "e60e0ef9c81dce1336a9ed3c76f08775f5b623151d96d85ba45f7b10de76d1c7"
 dependencies = [
- "bitflags 2.6.0",
+ "bitflags 2.7.0",
  "chalk-derive",
 ]
 
 [[package]]
 name = "chalk-recursive"
-version = "0.98.0"
+version = "0.99.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "129dc03458f71cfb9c3cd621c9c68166a94e87b85b16ccd29af015d7ff9a1c61"
+checksum = "5a06350d614e22b03a69b8105e3541614450a7ea48bc58ecc6c6bd92731a3995"
 dependencies = [
  "chalk-derive",
  "chalk-ir",
@@ -229,9 +229,9 @@ dependencies = [
 
 [[package]]
 name = "chalk-solve"
-version = "0.98.0"
+version = "0.99.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d7e8a8c1e928f98cdf227b868416ef21dcd8cc3c61b347576d783713444d41c8"
+checksum = "0e428761e9b55bee516bfe2457caed8b6d1b86353f92ae825bbe438a36ce91e8"
 dependencies = [
  "chalk-derive",
  "chalk-ir",
@@ -523,6 +523,7 @@ dependencies = [
  "hir-def",
  "hir-expand",
  "hir-ty",
+ "indexmap",
  "intern",
  "itertools",
  "rustc-hash 2.0.0",
@@ -544,7 +545,7 @@ version = "0.0.0"
 dependencies = [
  "arrayvec",
  "base-db",
- "bitflags 2.6.0",
+ "bitflags 2.7.0",
  "cfg",
  "cov-mark",
  "dashmap",
@@ -610,7 +611,7 @@ version = "0.0.0"
 dependencies = [
  "arrayvec",
  "base-db",
- "bitflags 2.6.0",
+ "bitflags 2.7.0",
  "chalk-derive",
  "chalk-ir",
  "chalk-recursive",
@@ -734,7 +735,7 @@ version = "0.0.0"
 dependencies = [
  "arrayvec",
  "base-db",
- "bitflags 2.6.0",
+ "bitflags 2.7.0",
  "cov-mark",
  "crossbeam-channel",
  "either",
@@ -820,11 +821,11 @@ dependencies = [
 
 [[package]]
 name = "inotify"
-version = "0.9.6"
+version = "0.11.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f8069d3ec154eb856955c1c0fbffefbf5f3c40a104ec912d4797314c1801abff"
+checksum = "f37dccff2791ab604f9babef0ba14fbe0be30bd368dc541e2b08d07c8aa908f3"
 dependencies = [
- "bitflags 1.3.2",
+ "bitflags 2.7.0",
  "inotify-sys",
  "libc",
 ]
@@ -908,9 +909,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
 
 [[package]]
 name = "libc"
-version = "0.2.155"
+version = "0.2.169"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
+checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
 
 [[package]]
 name = "libloading"
@@ -938,7 +939,7 @@ version = "0.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d"
 dependencies = [
- "bitflags 2.6.0",
+ "bitflags 2.7.0",
  "libc",
  "redox_syscall",
 ]
@@ -1117,14 +1118,14 @@ dependencies = [
 
 [[package]]
 name = "mio"
-version = "0.8.11"
+version = "1.0.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c"
+checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd"
 dependencies = [
  "libc",
  "log",
  "wasi",
- "windows-sys 0.48.0",
+ "windows-sys 0.52.0",
 ]
 
 [[package]]
@@ -1142,7 +1143,7 @@ version = "0.28.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4"
 dependencies = [
- "bitflags 2.6.0",
+ "bitflags 2.7.0",
  "cfg-if",
  "cfg_aliases 0.1.1",
  "libc",
@@ -1156,12 +1157,11 @@ checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451"
 
 [[package]]
 name = "notify"
-version = "6.1.1"
+version = "8.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d"
+checksum = "2fee8403b3d66ac7b26aee6e40a897d85dc5ce26f44da36b8b73e987cc52e943"
 dependencies = [
- "bitflags 2.6.0",
- "crossbeam-channel",
+ "bitflags 2.7.0",
  "filetime",
  "fsevent-sys",
  "inotify",
@@ -1169,11 +1169,18 @@ dependencies = [
  "libc",
  "log",
  "mio",
+ "notify-types",
  "walkdir",
- "windows-sys 0.48.0",
+ "windows-sys 0.59.0",
 ]
 
 [[package]]
+name = "notify-types"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5e0826a989adedc2a244799e823aece04662b66609d96af8dff7ac6df9a8925d"
+
+[[package]]
 name = "nu-ansi-term"
 version = "0.50.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1371,6 +1378,7 @@ version = "0.0.0"
 dependencies = [
  "expect-test",
  "intern",
+ "libc",
  "libloading",
  "memmap2",
  "object 0.33.0",
@@ -1428,7 +1436,7 @@ dependencies = [
  "libc",
  "perf-event",
  "tikv-jemalloc-ctl",
- "windows-sys 0.52.0",
+ "windows-sys 0.59.0",
 ]
 
 [[package]]
@@ -1482,7 +1490,7 @@ version = "0.9.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "57206b407293d2bcd3af849ce869d52068623f19e1b5ff8e8778e3309439682b"
 dependencies = [
- "bitflags 2.6.0",
+ "bitflags 2.7.0",
  "memchr",
  "unicase",
 ]
@@ -1507,20 +1515,20 @@ dependencies = [
 
 [[package]]
 name = "ra-ap-rustc_abi"
-version = "0.87.0"
+version = "0.91.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "28b782af0a7a8df16ddf43cd70da9f17bc3b1ce712c9e4992b6edb16f5f53632"
+checksum = "d5246e9e1f450333a990877eabbc36fe0567e7cedd56d5365db319e14079cf2a"
 dependencies = [
- "bitflags 2.6.0",
+ "bitflags 2.7.0",
  "ra-ap-rustc_index",
  "tracing",
 ]
 
 [[package]]
 name = "ra-ap-rustc_index"
-version = "0.87.0"
+version = "0.91.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ce5742f134960482f543b35ecebec3cacc6d79a9a685713518b4d8d70c5f9aa8"
+checksum = "59fd8e4f5b34c434ec111efb0e0614954db048b9307d3b2e4cc3c915da9d2160"
 dependencies = [
  "ra-ap-rustc_index_macros",
  "smallvec",
@@ -1528,9 +1536,9 @@ dependencies = [
 
 [[package]]
 name = "ra-ap-rustc_index_macros"
-version = "0.87.0"
+version = "0.91.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d7ea011fcf68309a8835ad01d91c032cb18444617b00e2cab21d45b208164441"
+checksum = "2d34973fe081392bd1edb022e865e9952fcaa093f9cdae183edce64472e5e889"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -1539,9 +1547,9 @@ dependencies = [
 
 [[package]]
 name = "ra-ap-rustc_lexer"
-version = "0.87.0"
+version = "0.91.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eb76f0a4d4c20859e41f0a23bff0f37ab9ca9171c214a6c7dd72ea69434865dc"
+checksum = "52fa42c582e21b35e8f61a5afe3c63a9c722d995826762eb19b18beeccf5157f"
 dependencies = [
  "unicode-properties",
  "unicode-xid",
@@ -1549,9 +1557,9 @@ dependencies = [
 
 [[package]]
 name = "ra-ap-rustc_parse_format"
-version = "0.87.0"
+version = "0.91.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "06080bd35078305421a62da77f3c128482d8d44441b6da8ce9d146d1cd9cdb5b"
+checksum = "740383328d7033393e5385f4a6073b880d5811b0fc0fd2559e481f905940f2f8"
 dependencies = [
  "ra-ap-rustc_index",
  "ra-ap-rustc_lexer",
@@ -1559,9 +1567,9 @@ dependencies = [
 
 [[package]]
 name = "ra-ap-rustc_pattern_analysis"
-version = "0.87.0"
+version = "0.91.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "68a3154fe4c20c177d7b3c678a2d3a97aba0cca156ddef88959915041889daf0"
+checksum = "c39f544728f32cebffb1a8b92ba3c1f3dcb4144081438d192137ed197d479a9d"
 dependencies = [
  "ra-ap-rustc_index",
  "rustc-hash 2.0.0",
@@ -1626,7 +1634,7 @@ version = "0.5.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4"
 dependencies = [
- "bitflags 2.6.0",
+ "bitflags 2.7.0",
 ]
 
 [[package]]
@@ -1713,7 +1721,7 @@ dependencies = [
  "vfs",
  "vfs-notify",
  "walkdir",
- "windows-sys 0.52.0",
+ "windows-sys 0.59.0",
  "xflags",
  "xshell",
 ]
@@ -1936,7 +1944,7 @@ dependencies = [
  "jod-thread",
  "libc",
  "miow",
- "windows-sys 0.52.0",
+ "windows-sys 0.59.0",
 ]
 
 [[package]]
diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml
index 9440123de70..1029844cd3a 100644
--- a/src/tools/rust-analyzer/Cargo.toml
+++ b/src/tools/rust-analyzer/Cargo.toml
@@ -4,7 +4,7 @@ exclude = ["crates/proc-macro-srv/proc-macro-test/imp"]
 resolver = "2"
 
 [workspace.package]
-rust-version = "1.82"
+rust-version = "1.83"
 edition = "2021"
 license = "MIT OR Apache-2.0"
 authors = ["rust-analyzer team"]
@@ -79,6 +79,7 @@ span = { path = "./crates/span", version = "0.0.0" }
 stdx = { path = "./crates/stdx", version = "0.0.0" }
 syntax = { path = "./crates/syntax", version = "0.0.0" }
 syntax-bridge = { path = "./crates/syntax-bridge", version = "0.0.0" }
+test-fixture = { path = "./crates/test-fixture", version = "0.0.0" }
 test-utils = { path = "./crates/test-utils", version = "0.0.0" }
 toolchain = { path = "./crates/toolchain", version = "0.0.0" }
 tt = { path = "./crates/tt", version = "0.0.0" }
@@ -86,16 +87,15 @@ vfs-notify = { path = "./crates/vfs-notify", version = "0.0.0" }
 vfs = { path = "./crates/vfs", version = "0.0.0" }
 edition = { path = "./crates/edition", version = "0.0.0" }
 
-ra-ap-rustc_lexer = { version = "0.87", default-features = false }
-ra-ap-rustc_parse_format = { version = "0.87", default-features = false }
-ra-ap-rustc_index = { version = "0.87", default-features = false }
-ra-ap-rustc_abi = { version = "0.87", default-features = false }
-ra-ap-rustc_pattern_analysis = { version = "0.87", default-features = false }
+ra-ap-rustc_lexer = { version = "0.91", default-features = false }
+ra-ap-rustc_parse_format = { version = "0.91", default-features = false }
+ra-ap-rustc_index = { version = "0.91", default-features = false }
+ra-ap-rustc_abi = { version = "0.91", default-features = false }
+ra-ap-rustc_pattern_analysis = { version = "0.91", default-features = false }
 
 # local crates that aren't published to crates.io. These should not have versions.
-test-fixture = { path = "./crates/test-fixture" }
 
-# In-tree crates that are published separately and follow semver. See lib/README.md
+# in-tree crates that are published separately and follow semver. See lib/README.md
 line-index = { version = "0.1.2" }
 la-arena = { version = "0.3.1" }
 lsp-server = { version = "0.7.6" }
@@ -106,10 +106,10 @@ arrayvec = "0.7.4"
 bitflags = "2.4.1"
 cargo_metadata = "0.18.1"
 camino = "1.1.6"
-chalk-solve = { version = "0.98.0", default-features = false }
-chalk-ir = "0.98.0"
-chalk-recursive = { version = "0.98.0", default-features = false }
-chalk-derive = "0.98.0"
+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"
 crossbeam-channel = "0.5.8"
 dissimilar = "1.0.7"
 dot = "0.1.4"
diff --git a/src/tools/rust-analyzer/crates/base-db/src/lib.rs b/src/tools/rust-analyzer/crates/base-db/src/lib.rs
index 0a9e83bc3ba..c7e4168f6bc 100644
--- a/src/tools/rust-analyzer/crates/base-db/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/base-db/src/lib.rs
@@ -136,7 +136,7 @@ pub trait SourceRootDatabase: SourceDatabase {
     #[ra_salsa::input]
     fn source_root(&self, id: SourceRootId) -> Arc<SourceRoot>;
 
-    /// Crates whose root fool is in `id`.
+    /// Crates whose root file is in `id`.
     fn source_root_crates(&self, id: SourceRootId) -> Arc<[CrateId]>;
 }
 
diff --git a/src/tools/rust-analyzer/crates/edition/src/lib.rs b/src/tools/rust-analyzer/crates/edition/src/lib.rs
index c25d5b9557b..7e9c94af408 100644
--- a/src/tools/rust-analyzer/crates/edition/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/edition/src/lib.rs
@@ -5,7 +5,8 @@ use std::fmt;
 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
 #[repr(u8)]
 pub enum Edition {
-    Edition2015,
+    // The syntax context stuff needs the discriminants to start from 0 and be consecutive.
+    Edition2015 = 0,
     Edition2018,
     Edition2021,
     Edition2024,
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/attr.rs b/src/tools/rust-analyzer/crates/hir-def/src/attr.rs
index 37e2a99e60f..710bffcefe9 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/attr.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/attr.rs
@@ -122,6 +122,11 @@ impl Attrs {
         AttrQuery { attrs: self, key }
     }
 
+    pub fn rust_analyzer_tool(&self) -> impl Iterator<Item = &Attr> {
+        self.iter()
+            .filter(|&attr| attr.path.segments().first().is_some_and(|s| *s == sym::rust_analyzer))
+    }
+
     pub fn cfg(&self) -> Option<CfgExpr> {
         let mut cfgs = self.by_key(&sym::cfg).tt_values().map(CfgExpr::parse);
         let first = cfgs.next()?;
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body.rs b/src/tools/rust-analyzer/crates/hir-def/src/body.rs
index 433a956ff9a..de439249306 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/body.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/body.rs
@@ -15,7 +15,7 @@ use hir_expand::{name::Name, ExpandError, InFile};
 use la_arena::{Arena, ArenaMap, Idx, RawIdx};
 use rustc_hash::FxHashMap;
 use smallvec::SmallVec;
-use span::{Edition, MacroFileId};
+use span::{Edition, MacroFileId, SyntaxContextData};
 use syntax::{ast, AstPtr, SyntaxNodePtr};
 use triomphe::Arc;
 use tt::TextRange;
@@ -37,15 +37,22 @@ use crate::{
 
 /// A wrapper around [`span::SyntaxContextId`] that is intended only for comparisons.
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-pub struct HygieneId(pub(crate) span::SyntaxContextId);
+pub struct HygieneId(span::SyntaxContextId);
 
 impl HygieneId {
-    pub const ROOT: Self = Self(span::SyntaxContextId::ROOT);
+    // The edition doesn't matter here, we only use this for comparisons and to lookup the macro.
+    pub const ROOT: Self = Self(span::SyntaxContextId::root(Edition::Edition2015));
 
-    pub fn new(ctx: span::SyntaxContextId) -> Self {
+    pub fn new(mut ctx: span::SyntaxContextId) -> Self {
+        // See `Name` for why we're doing that.
+        ctx.remove_root_edition();
         Self(ctx)
     }
 
+    pub(crate) fn lookup(self, db: &dyn DefDatabase) -> SyntaxContextData {
+        db.lookup_intern_syntax_context(self.0)
+    }
+
     pub(crate) fn is_root(self) -> bool {
         self.0.is_root()
     }
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs
index 10b84d041bd..1327bb3ab59 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs
@@ -8,6 +8,7 @@ use std::mem;
 use base_db::CrateId;
 use either::Either;
 use hir_expand::{
+    mod_path::tool_path,
     name::{AsName, Name},
     span_map::{ExpansionSpanMap, SpanMap},
     InFile, MacroDefId,
@@ -27,6 +28,7 @@ use text_size::TextSize;
 use triomphe::Arc;
 
 use crate::{
+    attr::Attrs,
     body::{Body, BodyDiagnostic, BodySourceMap, ExprPtr, HygieneId, LabelPtr, PatPtr},
     builtin_type::BuiltinUint,
     data::adt::StructKind,
@@ -212,6 +214,43 @@ impl ExprCollector<'_> {
         body: Option<ast::Expr>,
         is_async_fn: bool,
     ) -> (Body, BodySourceMap) {
+        let skip_body = match self.owner {
+            DefWithBodyId::FunctionId(it) => self.db.attrs(it.into()),
+            DefWithBodyId::StaticId(it) => self.db.attrs(it.into()),
+            DefWithBodyId::ConstId(it) => self.db.attrs(it.into()),
+            DefWithBodyId::InTypeConstId(_) => Attrs::EMPTY,
+            DefWithBodyId::VariantId(it) => self.db.attrs(it.into()),
+        }
+        .rust_analyzer_tool()
+        .any(|attr| *attr.path() == tool_path![skip]);
+        // If #[rust_analyzer::skip] annotated, only construct enough information for the signature
+        // and skip the body.
+        if skip_body {
+            self.body.body_expr = self.missing_expr();
+            if let Some((param_list, mut attr_enabled)) = param_list {
+                if let Some(self_param) =
+                    param_list.self_param().filter(|_| attr_enabled.next().unwrap_or(false))
+                {
+                    let is_mutable =
+                        self_param.mut_token().is_some() && self_param.amp_token().is_none();
+                    let binding_id: la_arena::Idx<Binding> = self.alloc_binding(
+                        Name::new_symbol_root(sym::self_.clone()),
+                        BindingAnnotation::new(is_mutable, false),
+                    );
+                    self.body.self_param = Some(binding_id);
+                    self.source_map.self_param =
+                        Some(self.expander.in_file(AstPtr::new(&self_param)));
+                }
+                self.body.params = param_list
+                    .params()
+                    .zip(attr_enabled)
+                    .filter(|(_, enabled)| *enabled)
+                    .map(|_| self.missing_pat())
+                    .collect();
+            };
+            return (self.body, self.source_map);
+        }
+
         self.awaitable_context.replace(if is_async_fn {
             Awaitable::Yes
         } else {
@@ -542,10 +581,7 @@ impl ExprCollector<'_> {
                 let mutability = if raw_tok {
                     if e.mut_token().is_some() {
                         Mutability::Mut
-                    } else if e.const_token().is_some() {
-                        Mutability::Shared
                     } else {
-                        never!("parser only remaps to raw_token() if matching mutability token follows");
                         Mutability::Shared
                     }
                 } else {
@@ -2460,7 +2496,7 @@ impl ExprCollector<'_> {
             None => HygieneId::ROOT,
             Some(span_map) => {
                 let ctx = span_map.span_at(span_start).ctx;
-                HygieneId(self.db.lookup_intern_syntax_context(ctx).opaque_and_semitransparent)
+                HygieneId::new(self.db.lookup_intern_syntax_context(ctx).opaque_and_semitransparent)
             }
         }
     }
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/scope.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/scope.rs
index 63a7a9af201..08af470b965 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/body/scope.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/body/scope.rs
@@ -345,7 +345,7 @@ mod tests {
         }
     }
 
-    fn do_check(ra_fixture: &str, expected: &[&str]) {
+    fn do_check(#[rust_analyzer::rust_fixture] ra_fixture: &str, expected: &[&str]) {
         let (offset, code) = extract_offset(ra_fixture);
         let code = {
             let mut buf = String::new();
@@ -509,7 +509,7 @@ fn foo() {
         );
     }
 
-    fn do_check_local_name(ra_fixture: &str, expected_offset: u32) {
+    fn do_check_local_name(#[rust_analyzer::rust_fixture] ra_fixture: &str, expected_offset: u32) {
         let (db, position) = TestDB::with_position(ra_fixture);
         let file_id = position.file_id;
         let offset = position.offset;
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/tests.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/tests.rs
index 7e15a9f2d61..edc7c4c1f21 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/body/tests.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/body/tests.rs
@@ -7,7 +7,7 @@ use crate::{test_db::TestDB, ModuleDefId};
 
 use super::*;
 
-fn lower(ra_fixture: &str) -> (TestDB, Arc<Body>, DefWithBodyId) {
+fn lower(#[rust_analyzer::rust_fixture] ra_fixture: &str) -> (TestDB, Arc<Body>, DefWithBodyId) {
     let db = TestDB::with_files(ra_fixture);
 
     let krate = db.fetch_test_crate();
@@ -27,14 +27,14 @@ fn lower(ra_fixture: &str) -> (TestDB, Arc<Body>, DefWithBodyId) {
     (db, body, fn_def)
 }
 
-fn def_map_at(ra_fixture: &str) -> String {
+fn def_map_at(#[rust_analyzer::rust_fixture] ra_fixture: &str) -> String {
     let (db, position) = TestDB::with_position(ra_fixture);
 
     let module = db.module_at_position(position);
     module.def_map(&db).dump(&db)
 }
 
-fn check_block_scopes_at(ra_fixture: &str, expect: Expect) {
+fn check_block_scopes_at(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
     let (db, position) = TestDB::with_position(ra_fixture);
 
     let module = db.module_at_position(position);
@@ -42,7 +42,7 @@ fn check_block_scopes_at(ra_fixture: &str, expect: Expect) {
     expect.assert_eq(&actual);
 }
 
-fn check_at(ra_fixture: &str, expect: Expect) {
+fn check_at(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
     let actual = def_map_at(ra_fixture);
     expect.assert_eq(&actual);
 }
@@ -444,3 +444,18 @@ fn foo() {
 }"#
     );
 }
+
+#[test]
+fn skip_skips_body() {
+    let (db, body, owner) = lower(
+        r#"
+#[rust_analyzer::skip]
+async fn foo(a: (), b: i32) -> u32 {
+    0 + 1 + b()
+}
+"#,
+    );
+    let printed = body.pretty_print(&db, owner, Edition::CURRENT);
+    expect!["fn foo(�: (), �: i32) -> impl ::core::future::Future::<Output = u32> �"]
+        .assert_eq(&printed);
+}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/data.rs b/src/tools/rust-analyzer/crates/hir-def/src/data.rs
index d85bc9a4320..12f5f6ad79a 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/data.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/data.rs
@@ -244,7 +244,7 @@ bitflags::bitflags! {
 #[derive(Debug, Clone, PartialEq, Eq)]
 pub struct TraitData {
     pub name: Name,
-    pub items: Vec<(Name, AssocItemId)>,
+    pub items: Box<[(Name, AssocItemId)]>,
     pub flags: TraitFlags,
     pub visibility: RawVisibility,
     // box it as the vec is usually empty anyways
@@ -360,7 +360,7 @@ impl TraitAliasData {
 pub struct ImplData {
     pub target_trait: Option<TraitRef>,
     pub self_ty: TypeRefId,
-    pub items: Box<[AssocItemId]>,
+    pub items: Box<[(Name, AssocItemId)]>,
     pub is_negative: bool,
     pub is_unsafe: bool,
     // box it as the vec is usually empty anyways
@@ -393,7 +393,6 @@ impl ImplData {
         collector.collect(&item_tree, tree_id.tree_id(), &impl_def.items);
 
         let (items, macro_calls, diagnostics) = collector.finish();
-        let items = items.into_iter().map(|(_, item)| item).collect();
 
         (
             Arc::new(ImplData {
@@ -648,12 +647,12 @@ impl<'a> AssocItemCollector<'a> {
     fn finish(
         self,
     ) -> (
-        Vec<(Name, AssocItemId)>,
+        Box<[(Name, AssocItemId)]>,
         Option<Box<Vec<(AstId<ast::Item>, MacroCallId)>>>,
         Vec<DefDiagnostic>,
     ) {
         (
-            self.items,
+            self.items.into_boxed_slice(),
             if self.macro_calls.is_empty() { None } else { Some(Box::new(self.macro_calls)) },
             self.diagnostics,
         )
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expander.rs b/src/tools/rust-analyzer/crates/hir-def/src/expander.rs
index 5315c1c6fbd..108258d5a11 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/expander.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/expander.rs
@@ -10,7 +10,7 @@ use hir_expand::{
     ExpandResult, HirFileId, InFile, Lookup, MacroCallId,
 };
 use limit::Limit;
-use span::SyntaxContextId;
+use span::{Edition, SyntaxContextId};
 use syntax::{ast, Parse};
 use triomphe::Arc;
 
@@ -60,7 +60,7 @@ impl Expander {
 
     pub fn syntax_context(&self) -> SyntaxContextId {
         // FIXME:
-        SyntaxContextId::ROOT
+        SyntaxContextId::root(Edition::CURRENT)
     }
 
     pub fn enter_expand<T: ast::AstNode>(
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs b/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs
index a615abd1bbe..5d67902c8ac 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs
@@ -665,7 +665,7 @@ mod tests {
     /// module the cursor is in.
     #[track_caller]
     fn check_found_path_(
-        ra_fixture: &str,
+        #[rust_analyzer::rust_fixture] ra_fixture: &str,
         path: &str,
         prefer_prelude: bool,
         prefer_absolute: bool,
@@ -727,19 +727,35 @@ mod tests {
         expect.assert_eq(&res);
     }
 
-    fn check_found_path(ra_fixture: &str, path: &str, expect: Expect) {
+    fn check_found_path(
+        #[rust_analyzer::rust_fixture] ra_fixture: &str,
+        path: &str,
+        expect: Expect,
+    ) {
         check_found_path_(ra_fixture, path, false, false, false, expect);
     }
 
-    fn check_found_path_prelude(ra_fixture: &str, path: &str, expect: Expect) {
+    fn check_found_path_prelude(
+        #[rust_analyzer::rust_fixture] ra_fixture: &str,
+        path: &str,
+        expect: Expect,
+    ) {
         check_found_path_(ra_fixture, path, true, false, false, expect);
     }
 
-    fn check_found_path_absolute(ra_fixture: &str, path: &str, expect: Expect) {
+    fn check_found_path_absolute(
+        #[rust_analyzer::rust_fixture] ra_fixture: &str,
+        path: &str,
+        expect: Expect,
+    ) {
         check_found_path_(ra_fixture, path, false, true, false, expect);
     }
 
-    fn check_found_path_prefer_no_std(ra_fixture: &str, path: &str, expect: Expect) {
+    fn check_found_path_prefer_no_std(
+        #[rust_analyzer::rust_fixture] ra_fixture: &str,
+        path: &str,
+        expect: Expect,
+    ) {
         check_found_path_(ra_fixture, path, false, false, true, expect);
     }
 
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs b/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs
index 9574e5d9cd3..ac262950f13 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs
@@ -509,7 +509,12 @@ mod tests {
         }
     }
 
-    fn check_search(ra_fixture: &str, crate_name: &str, query: Query, expect: Expect) {
+    fn check_search(
+        #[rust_analyzer::rust_fixture] ra_fixture: &str,
+        crate_name: &str,
+        query: Query,
+        expect: Expect,
+    ) {
         let db = TestDB::with_files(ra_fixture);
         let crate_graph = db.crate_graph();
         let krate = crate_graph
@@ -587,7 +592,7 @@ mod tests {
         ))
     }
 
-    fn check(ra_fixture: &str, expect: Expect) {
+    fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
         let db = TestDB::with_files(ra_fixture);
         let crate_graph = db.crate_graph();
 
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs
index 2c3eb5c8e5e..0fec7674109 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs
@@ -162,6 +162,20 @@ impl ItemScope {
             .map(move |name| (name, self.get(name)))
     }
 
+    pub fn values(&self) -> impl Iterator<Item = (&Name, Item<ModuleDefId, ImportId>)> + '_ {
+        self.values.iter().map(|(n, &i)| (n, i))
+    }
+
+    pub fn types(
+        &self,
+    ) -> impl Iterator<Item = (&Name, Item<ModuleDefId, ImportOrExternCrate>)> + '_ {
+        self.types.iter().map(|(n, &i)| (n, i))
+    }
+
+    pub fn macros(&self) -> impl Iterator<Item = (&Name, Item<MacroId, ImportId>)> + '_ {
+        self.macros.iter().map(|(n, &i)| (n, i))
+    }
+
     pub fn imports(&self) -> impl Iterator<Item = ImportId> + '_ {
         self.use_imports_types
             .keys()
@@ -263,11 +277,6 @@ impl ItemScope {
         self.unnamed_consts.iter().copied()
     }
 
-    /// Iterate over all module scoped macros
-    pub(crate) fn macros(&self) -> impl Iterator<Item = (&Name, MacroId)> + '_ {
-        self.entries().filter_map(|(name, def)| def.take_macros().map(|macro_| (name, macro_)))
-    }
-
     /// Iterate over all legacy textual scoped macros visible at the end of the module
     pub fn legacy_macros(&self) -> impl Iterator<Item = (&Name, &[MacroId])> + '_ {
         self.legacy_macros.iter().map(|(name, def)| (name, &**def))
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/tests.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/tests.rs
index 0f53969d6c7..80b699649fb 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/tests.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/tests.rs
@@ -4,7 +4,7 @@ use test_fixture::WithFixture;
 
 use crate::{db::DefDatabase, test_db::TestDB};
 
-fn check(ra_fixture: &str, expect: Expect) {
+fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
     let (db, file_id) = TestDB::with_single_file(ra_fixture);
     let item_tree = db.file_item_tree(file_id.into());
     let pretty = item_tree.pretty_print(&db, Edition::CURRENT);
@@ -270,7 +270,7 @@ m!();
             // AstId: 2
             pub macro m2 { ... }
 
-            // AstId: 3, SyntaxContext: 0, ExpandTo: Items
+            // AstId: 3, SyntaxContext: 2, ExpandTo: Items
             m!(...);
         "#]],
     );
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs
index 0629d87e544..afdc49a2dc5 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs
@@ -107,7 +107,7 @@ impl LangItems {
         for (_, module_data) in crate_def_map.modules() {
             for impl_def in module_data.scope.impls() {
                 lang_items.collect_lang_item(db, impl_def, LangItemTarget::ImplDef);
-                for assoc in db.impl_data(impl_def).items.iter().copied() {
+                for &(_, assoc) in db.impl_data(impl_def).items.iter() {
                     match assoc {
                         AssocItemId::FunctionId(f) => {
                             lang_items.collect_lang_item(db, f, LangItemTarget::Function)
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs
index 8af27513ebc..84c105a0a34 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs
@@ -502,7 +502,7 @@ impl ModuleId {
     }
 
     /// Whether this module represents the crate root module
-    fn is_crate_root(&self) -> bool {
+    pub fn is_crate_root(&self) -> bool {
         self.local_id == DefMap::ROOT && self.block.is_none()
     }
 }
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs
index 511626b5ed9..8c5bd3b6d36 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs
@@ -35,9 +35,9 @@ macro_rules! f {
     };
 }
 
-struct#0:1@58..64#1# MyTraitMap2#0:2@31..42#0# {#0:1@72..73#1#
-    map#0:1@86..89#1#:#0:1@89..90#1# #0:1@89..90#1#::#0:1@91..93#1#std#0:1@93..96#1#::#0:1@96..98#1#collections#0:1@98..109#1#::#0:1@109..111#1#HashSet#0:1@111..118#1#<#0:1@118..119#1#(#0:1@119..120#1#)#0:1@120..121#1#>#0:1@121..122#1#,#0:1@122..123#1#
-}#0:1@132..133#1#
+struct#0:1@58..64#4# MyTraitMap2#0:2@31..42#2# {#0:1@72..73#4#
+    map#0:1@86..89#4#:#0:1@89..90#4# #0:1@89..90#4#::#0:1@91..93#4#std#0:1@93..96#4#::#0:1@96..98#4#collections#0:1@98..109#4#::#0:1@109..111#4#HashSet#0:1@111..118#4#<#0:1@118..119#4#(#0:1@119..120#4#)#0:1@120..121#4#>#0:1@121..122#4#,#0:1@122..123#4#
+}#0:1@132..133#4#
 "#]],
     );
 }
@@ -75,12 +75,12 @@ macro_rules! f {
     };
 }
 
-fn#0:2@30..32#0# main#0:2@33..37#0#(#0:2@37..38#0#)#0:2@38..39#0# {#0:2@40..41#0#
-    1#0:2@50..51#0#;#0:2@51..52#0#
-    1.0#0:2@61..64#0#;#0:2@64..65#0#
-    (#0:2@74..75#0#(#0:2@75..76#0#1#0:2@76..77#0#,#0:2@77..78#0# )#0:2@78..79#0#,#0:2@79..80#0# )#0:2@80..81#0#.#0:2@81..82#0#0#0:2@82..85#0#.#0:2@82..85#0#0#0:2@82..85#0#;#0:2@85..86#0#
-    let#0:2@95..98#0# x#0:2@99..100#0# =#0:2@101..102#0# 1#0:2@103..104#0#;#0:2@104..105#0#
-}#0:2@110..111#0#
+fn#0:2@30..32#2# main#0:2@33..37#2#(#0:2@37..38#2#)#0:2@38..39#2# {#0:2@40..41#2#
+    1#0:2@50..51#2#;#0:2@51..52#2#
+    1.0#0:2@61..64#2#;#0:2@64..65#2#
+    (#0:2@74..75#2#(#0:2@75..76#2#1#0:2@76..77#2#,#0:2@77..78#2# )#0:2@78..79#2#,#0:2@79..80#2# )#0:2@80..81#2#.#0:2@81..82#2#0#0:2@82..85#2#.#0:2@82..85#2#0#0:2@82..85#2#;#0:2@85..86#2#
+    let#0:2@95..98#2# x#0:2@99..100#2# =#0:2@101..102#2# 1#0:2@103..104#2#;#0:2@104..105#2#
+}#0:2@110..111#2#
 
 
 "#]],
@@ -171,7 +171,7 @@ fn main(foo: ()) {
     }
 
     fn main(foo: ()) {
-        /* error: unresolved macro unresolved */"helloworld!"#0:3@236..321#0#;
+        /* error: unresolved macro unresolved */"helloworld!"#0:3@236..321#2#;
     }
 }
 
@@ -197,7 +197,7 @@ macro_rules! mk_struct {
 #[macro_use]
 mod foo;
 
-struct#1:1@59..65#1# Foo#0:2@32..35#0#(#1:1@70..71#1#u32#0:2@41..44#0#)#1:1@74..75#1#;#1:1@75..76#1#
+struct#1:1@59..65#4# Foo#0:2@32..35#2#(#1:1@70..71#4#u32#0:2@41..44#2#)#1:1@74..75#4#;#1:1@75..76#4#
 "#]],
     );
 }
@@ -423,10 +423,10 @@ m! { foo, bar }
 macro_rules! m {
     ($($i:ident),*) => ( impl Bar { $(fn $i() {})* } );
 }
-impl#\1# Bar#\1# {#\1#
-    fn#\1# foo#\0#(#\1#)#\1# {#\1#}#\1#
-    fn#\1# bar#\0#(#\1#)#\1# {#\1#}#\1#
-}#\1#
+impl#\4# Bar#\4# {#\4#
+    fn#\4# foo#\2#(#\4#)#\4# {#\4#}#\4#
+    fn#\4# bar#\2#(#\4#)#\4# {#\4#}#\4#
+}#\4#
 "#]],
     );
 }
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs
index 5b9ffdf37be..408d03ff718 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs
@@ -47,7 +47,7 @@ use crate::{
 };
 
 #[track_caller]
-fn check_errors(ra_fixture: &str, expect: Expect) {
+fn check_errors(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
     let db = TestDB::with_files(ra_fixture);
     let krate = db.fetch_test_crate();
     let def_map = db.crate_def_map(krate);
@@ -77,7 +77,7 @@ fn check_errors(ra_fixture: &str, expect: Expect) {
 }
 
 #[track_caller]
-fn check(ra_fixture: &str, mut expect: Expect) {
+fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str, mut expect: Expect) {
     let extra_proc_macros = vec![(
         r#"
 #[proc_macro_attribute]
@@ -358,6 +358,7 @@ impl ProcMacroExpander for IdentityWhenValidProcMacroExpander {
         let (parse, _) = syntax_bridge::token_tree_to_syntax_node(
             subtree,
             syntax_bridge::TopEntryPoint::MacroItems,
+            &mut |_| span::Edition::CURRENT,
             span::Edition::CURRENT,
         );
         if parse.errors().is_empty() {
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/proc_macros.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/proc_macros.rs
index c0178adc9a6..70e3e1ed4e9 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/proc_macros.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/proc_macros.rs
@@ -181,9 +181,9 @@ fn foo(&self) {
     self.0. 1;
 }
 
-fn#0:1@45..47#0# foo#0:1@48..51#0#(#0:1@51..52#0#&#0:1@52..53#0#self#0:1@53..57#0# )#0:1@57..58#0# {#0:1@59..60#0#
-    self#0:1@65..69#0# .#0:1@69..70#0#0#0:1@70..71#0#.#0:1@71..72#0#1#0:1@73..74#0#;#0:1@74..75#0#
-}#0:1@76..77#0#"#]],
+fn#0:1@45..47#2# foo#0:1@48..51#2#(#0:1@51..52#2#&#0:1@52..53#2#self#0:1@53..57#2# )#0:1@57..58#2# {#0:1@59..60#2#
+    self#0:1@65..69#2# .#0:1@69..70#2#0#0:1@70..71#2#.#0:1@71..72#2#1#0:1@73..74#2#;#0:1@74..75#2#
+}#0:1@76..77#2#"#]],
     );
 }
 
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs
index 8beeda82bca..1e4b42dff5f 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs
@@ -74,7 +74,7 @@ pub(super) fn collect_defs(db: &dyn DefDatabase, def_map: DefMap, tree_id: TreeI
 
     let proc_macros = if krate.is_proc_macro {
         db.proc_macros()
-            .for_crate(def_map.krate, db.syntax_context(tree_id.file_id()))
+            .for_crate(def_map.krate, db.syntax_context(tree_id.file_id(), krate.edition))
             .unwrap_or_default()
     } else {
         Default::default()
@@ -717,8 +717,8 @@ impl DefCollector<'_> {
                 }
             }
             None => {
-                for (name, def) in root_scope.macros() {
-                    self.def_map.macro_use_prelude.insert(name.clone(), (def, extern_crate));
+                for (name, it) in root_scope.macros() {
+                    self.def_map.macro_use_prelude.insert(name.clone(), (it.def, extern_crate));
                 }
             }
         }
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests.rs
index 32c158415ba..318aee04f7b 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests.rs
@@ -11,19 +11,19 @@ use triomphe::Arc;
 
 use crate::{db::DefDatabase, nameres::DefMap, test_db::TestDB};
 
-fn compute_crate_def_map(ra_fixture: &str) -> Arc<DefMap> {
+fn compute_crate_def_map(#[rust_analyzer::rust_fixture] ra_fixture: &str) -> Arc<DefMap> {
     let db = TestDB::with_files(ra_fixture);
     let krate = db.fetch_test_crate();
     db.crate_def_map(krate)
 }
 
-fn render_crate_def_map(ra_fixture: &str) -> String {
+fn render_crate_def_map(#[rust_analyzer::rust_fixture] ra_fixture: &str) -> String {
     let db = TestDB::with_files(ra_fixture);
     let krate = db.fetch_test_crate();
     db.crate_def_map(krate).dump(&db)
 }
 
-fn check(ra_fixture: &str, expect: Expect) {
+fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
     let actual = render_crate_def_map(ra_fixture);
     expect.assert_eq(&actual);
 }
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 82da57a9bb2..0b9b6da8d51 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs
@@ -166,6 +166,17 @@ impl Resolver {
         db: &dyn DefDatabase,
         path: &Path,
     ) -> Option<(TypeNs, Option<usize>, Option<ImportOrExternCrate>)> {
+        self.resolve_path_in_type_ns_with_prefix_info(db, path).map(
+            |(resolution, remaining_segments, import, _)| (resolution, remaining_segments, import),
+        )
+    }
+
+    pub fn resolve_path_in_type_ns_with_prefix_info(
+        &self,
+        db: &dyn DefDatabase,
+        path: &Path,
+    ) -> Option<(TypeNs, Option<usize>, Option<ImportOrExternCrate>, ResolvePathResultPrefixInfo)>
+    {
         let path = match path {
             Path::BarePath(mod_path) => mod_path,
             Path::Normal(it) => it.mod_path(),
@@ -181,7 +192,12 @@ impl Resolver {
                     | LangItemTarget::ImplDef(_)
                     | LangItemTarget::Static(_) => return None,
                 };
-                return Some((type_ns, seg.as_ref().map(|_| 1), None));
+                return Some((
+                    type_ns,
+                    seg.as_ref().map(|_| 1),
+                    None,
+                    ResolvePathResultPrefixInfo::default(),
+                ));
             }
         };
         let first_name = path.segments().first()?;
@@ -197,17 +213,32 @@ impl Resolver {
                 Scope::ExprScope(_) | Scope::MacroDefScope(_) => continue,
                 Scope::GenericParams { params, def } => {
                     if let Some(id) = params.find_type_by_name(first_name, *def) {
-                        return Some((TypeNs::GenericParam(id), remaining_idx(), None));
+                        return Some((
+                            TypeNs::GenericParam(id),
+                            remaining_idx(),
+                            None,
+                            ResolvePathResultPrefixInfo::default(),
+                        ));
                     }
                 }
                 &Scope::ImplDefScope(impl_) => {
                     if *first_name == sym::Self_.clone() {
-                        return Some((TypeNs::SelfType(impl_), remaining_idx(), None));
+                        return Some((
+                            TypeNs::SelfType(impl_),
+                            remaining_idx(),
+                            None,
+                            ResolvePathResultPrefixInfo::default(),
+                        ));
                     }
                 }
                 &Scope::AdtScope(adt) => {
                     if *first_name == sym::Self_.clone() {
-                        return Some((TypeNs::AdtSelfType(adt), remaining_idx(), None));
+                        return Some((
+                            TypeNs::AdtSelfType(adt),
+                            remaining_idx(),
+                            None,
+                            ResolvePathResultPrefixInfo::default(),
+                        ));
                     }
                 }
                 Scope::BlockScope(m) => {
@@ -220,18 +251,6 @@ impl Resolver {
         self.module_scope.resolve_path_in_type_ns(db, path)
     }
 
-    pub fn resolve_path_in_type_ns_fully_with_imports(
-        &self,
-        db: &dyn DefDatabase,
-        path: &Path,
-    ) -> Option<(TypeNs, Option<ImportOrExternCrate>)> {
-        let (res, unresolved, imp) = self.resolve_path_in_type_ns(db, path)?;
-        if unresolved.is_some() {
-            return None;
-        }
-        Some((res, imp))
-    }
-
     pub fn resolve_path_in_type_ns_fully(
         &self,
         db: &dyn DefDatabase,
@@ -324,7 +343,7 @@ impl Resolver {
 
         if n_segments <= 1 {
             let mut hygiene_info = if !hygiene_id.is_root() {
-                let ctx = db.lookup_intern_syntax_context(hygiene_id.0);
+                let ctx = hygiene_id.lookup(db);
                 ctx.outer_expn.map(|expansion| {
                     let expansion = db.lookup_intern_macro_call(expansion);
                     (ctx.parent, expansion.def)
@@ -986,11 +1005,12 @@ impl ModuleItemMap {
         &self,
         db: &dyn DefDatabase,
         path: &ModPath,
-    ) -> Option<(TypeNs, Option<usize>, Option<ImportOrExternCrate>)> {
-        let (module_def, idx, _) =
+    ) -> Option<(TypeNs, Option<usize>, Option<ImportOrExternCrate>, ResolvePathResultPrefixInfo)>
+    {
+        let (module_def, idx, prefix_info) =
             self.def_map.resolve_path_locally(db, self.module_id, path, BuiltinShadowMode::Other);
         let (res, import) = to_type_ns(module_def)?;
-        Some((res, idx, import))
+        Some((res, idx, import, prefix_info))
     }
 }
 
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/visibility.rs b/src/tools/rust-analyzer/crates/hir-def/src/visibility.rs
index 4edb6835922..c4473e454a1 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/visibility.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/visibility.rs
@@ -240,12 +240,12 @@ impl Visibility {
 
                 if a_ancestors.any(|m| m == mod_b.local_id) {
                     // B is above A
-                    return Some(Visibility::Module(mod_a, expl_b));
+                    return Some(Visibility::Module(mod_a, expl_a));
                 }
 
                 if b_ancestors.any(|m| m == mod_a.local_id) {
                     // A is above B
-                    return Some(Visibility::Module(mod_b, expl_a));
+                    return Some(Visibility::Module(mod_b, expl_b));
                 }
 
                 None
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/derive_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/derive_macro.rs
index 4510a593af4..28b68121394 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/derive_macro.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/derive_macro.rs
@@ -4,7 +4,7 @@ use intern::sym;
 use itertools::{izip, Itertools};
 use parser::SyntaxKind;
 use rustc_hash::FxHashSet;
-use span::{MacroCallId, Span, SyntaxContextId};
+use span::{Edition, MacroCallId, Span, SyntaxContextId};
 use stdx::never;
 use syntax_bridge::DocCommentDesugarMode;
 use tracing::debug;
@@ -33,7 +33,7 @@ macro_rules! register_builtin {
         }
 
         impl BuiltinDeriveExpander {
-            pub fn expander(&self) -> fn(Span, &tt::TopSubtree) -> ExpandResult<tt::TopSubtree>  {
+            pub fn expander(&self) -> fn(&dyn ExpandDatabase, Span, &tt::TopSubtree) -> ExpandResult<tt::TopSubtree>  {
                 match *self {
                     $( BuiltinDeriveExpander::$trait => $expand, )*
                 }
@@ -58,8 +58,8 @@ impl BuiltinDeriveExpander {
         tt: &tt::TopSubtree,
         span: Span,
     ) -> ExpandResult<tt::TopSubtree> {
-        let span = span_with_def_site_ctxt(db, span, id);
-        self.expander()(span, tt)
+        let span = span_with_def_site_ctxt(db, span, id, Edition::CURRENT);
+        self.expander()(db, span, tt)
     }
 }
 
@@ -226,8 +226,12 @@ struct AdtParam {
 }
 
 // FIXME: This whole thing needs a refactor. Each derive requires its special values, and the result is a mess.
-fn parse_adt(tt: &tt::TopSubtree, call_site: Span) -> Result<BasicAdtInfo, ExpandError> {
-    let (adt, tm) = to_adt_syntax(tt, call_site)?;
+fn parse_adt(
+    db: &dyn ExpandDatabase,
+    tt: &tt::TopSubtree,
+    call_site: Span,
+) -> Result<BasicAdtInfo, ExpandError> {
+    let (adt, tm) = to_adt_syntax(db, tt, call_site)?;
     parse_adt_from_syntax(&adt, &tm, call_site)
 }
 
@@ -382,12 +386,14 @@ fn parse_adt_from_syntax(
 }
 
 fn to_adt_syntax(
+    db: &dyn ExpandDatabase,
     tt: &tt::TopSubtree,
     call_site: Span,
 ) -> Result<(ast::Adt, span::SpanMap<SyntaxContextId>), ExpandError> {
-    let (parsed, tm) = syntax_bridge::token_tree_to_syntax_node(
+    let (parsed, tm) = crate::db::token_tree_to_syntax_node(
+        db,
         tt,
-        syntax_bridge::TopEntryPoint::MacroItems,
+        crate::ExpandTo::Items,
         parser::Edition::CURRENT_FIXME,
     );
     let macro_items = ast::MacroItems::cast(parsed.syntax_node())
@@ -446,12 +452,13 @@ fn name_to_token(
 /// where B1, ..., BN are the bounds given by `bounds_paths`. Z is a phantom type, and
 /// therefore does not get bound by the derived trait.
 fn expand_simple_derive(
+    db: &dyn ExpandDatabase,
     invoc_span: Span,
     tt: &tt::TopSubtree,
     trait_path: tt::TopSubtree,
     make_trait_body: impl FnOnce(&BasicAdtInfo) -> tt::TopSubtree,
 ) -> ExpandResult<tt::TopSubtree> {
-    let info = match parse_adt(tt, invoc_span) {
+    let info = match parse_adt(db, tt, invoc_span) {
         Ok(info) => info,
         Err(e) => {
             return ExpandResult::new(
@@ -520,14 +527,22 @@ fn expand_simple_derive_with_parsed(
     }
 }
 
-fn copy_expand(span: Span, tt: &tt::TopSubtree) -> ExpandResult<tt::TopSubtree> {
+fn copy_expand(
+    db: &dyn ExpandDatabase,
+    span: Span,
+    tt: &tt::TopSubtree,
+) -> ExpandResult<tt::TopSubtree> {
     let krate = dollar_crate(span);
-    expand_simple_derive(span, tt, quote! {span => #krate::marker::Copy }, |_| quote! {span =>})
+    expand_simple_derive(db, span, tt, quote! {span => #krate::marker::Copy }, |_| quote! {span =>})
 }
 
-fn clone_expand(span: Span, tt: &tt::TopSubtree) -> ExpandResult<tt::TopSubtree> {
+fn clone_expand(
+    db: &dyn ExpandDatabase,
+    span: Span,
+    tt: &tt::TopSubtree,
+) -> ExpandResult<tt::TopSubtree> {
     let krate = dollar_crate(span);
-    expand_simple_derive(span, tt, quote! {span => #krate::clone::Clone }, |adt| {
+    expand_simple_derive(db, span, tt, quote! {span => #krate::clone::Clone }, |adt| {
         if matches!(adt.shape, AdtShape::Union) {
             let star = tt::Punct { char: '*', spacing: ::tt::Spacing::Alone, span };
             return quote! {span =>
@@ -576,9 +591,13 @@ fn and_and(span: Span) -> tt::TopSubtree {
     quote! {span => #and& }
 }
 
-fn default_expand(span: Span, tt: &tt::TopSubtree) -> ExpandResult<tt::TopSubtree> {
+fn default_expand(
+    db: &dyn ExpandDatabase,
+    span: Span,
+    tt: &tt::TopSubtree,
+) -> ExpandResult<tt::TopSubtree> {
     let krate = &dollar_crate(span);
-    expand_simple_derive(span, tt, quote! {span => #krate::default::Default }, |adt| {
+    expand_simple_derive(db, span, tt, quote! {span => #krate::default::Default }, |adt| {
         let body = match &adt.shape {
             AdtShape::Struct(fields) => {
                 let name = &adt.name;
@@ -615,9 +634,13 @@ fn default_expand(span: Span, tt: &tt::TopSubtree) -> ExpandResult<tt::TopSubtre
     })
 }
 
-fn debug_expand(span: Span, tt: &tt::TopSubtree) -> ExpandResult<tt::TopSubtree> {
+fn debug_expand(
+    db: &dyn ExpandDatabase,
+    span: Span,
+    tt: &tt::TopSubtree,
+) -> ExpandResult<tt::TopSubtree> {
     let krate = &dollar_crate(span);
-    expand_simple_derive(span, tt, quote! {span => #krate::fmt::Debug }, |adt| {
+    expand_simple_derive(db, span, tt, quote! {span => #krate::fmt::Debug }, |adt| {
         let for_variant = |name: String, v: &VariantShape| match v {
             VariantShape::Struct(fields) => {
                 let for_fields = fields.iter().map(|it| {
@@ -687,9 +710,13 @@ fn debug_expand(span: Span, tt: &tt::TopSubtree) -> ExpandResult<tt::TopSubtree>
     })
 }
 
-fn hash_expand(span: Span, tt: &tt::TopSubtree) -> ExpandResult<tt::TopSubtree> {
+fn hash_expand(
+    db: &dyn ExpandDatabase,
+    span: Span,
+    tt: &tt::TopSubtree,
+) -> ExpandResult<tt::TopSubtree> {
     let krate = &dollar_crate(span);
-    expand_simple_derive(span, tt, quote! {span => #krate::hash::Hash }, |adt| {
+    expand_simple_derive(db, span, tt, quote! {span => #krate::hash::Hash }, |adt| {
         if matches!(adt.shape, AdtShape::Union) {
             // FIXME: Return expand error here
             return quote! {span =>};
@@ -734,14 +761,22 @@ fn hash_expand(span: Span, tt: &tt::TopSubtree) -> ExpandResult<tt::TopSubtree>
     })
 }
 
-fn eq_expand(span: Span, tt: &tt::TopSubtree) -> ExpandResult<tt::TopSubtree> {
+fn eq_expand(
+    db: &dyn ExpandDatabase,
+    span: Span,
+    tt: &tt::TopSubtree,
+) -> ExpandResult<tt::TopSubtree> {
     let krate = dollar_crate(span);
-    expand_simple_derive(span, tt, quote! {span => #krate::cmp::Eq }, |_| quote! {span =>})
+    expand_simple_derive(db, span, tt, quote! {span => #krate::cmp::Eq }, |_| quote! {span =>})
 }
 
-fn partial_eq_expand(span: Span, tt: &tt::TopSubtree) -> ExpandResult<tt::TopSubtree> {
+fn partial_eq_expand(
+    db: &dyn ExpandDatabase,
+    span: Span,
+    tt: &tt::TopSubtree,
+) -> ExpandResult<tt::TopSubtree> {
     let krate = dollar_crate(span);
-    expand_simple_derive(span, tt, quote! {span => #krate::cmp::PartialEq }, |adt| {
+    expand_simple_derive(db, span, tt, quote! {span => #krate::cmp::PartialEq }, |adt| {
         if matches!(adt.shape, AdtShape::Union) {
             // FIXME: Return expand error here
             return quote! {span =>};
@@ -811,9 +846,13 @@ fn self_and_other_patterns(
     (self_patterns, other_patterns)
 }
 
-fn ord_expand(span: Span, tt: &tt::TopSubtree) -> ExpandResult<tt::TopSubtree> {
+fn ord_expand(
+    db: &dyn ExpandDatabase,
+    span: Span,
+    tt: &tt::TopSubtree,
+) -> ExpandResult<tt::TopSubtree> {
     let krate = &dollar_crate(span);
-    expand_simple_derive(span, tt, quote! {span => #krate::cmp::Ord }, |adt| {
+    expand_simple_derive(db, span, tt, quote! {span => #krate::cmp::Ord }, |adt| {
         fn compare(
             krate: &tt::Ident,
             left: tt::TopSubtree,
@@ -869,9 +908,13 @@ fn ord_expand(span: Span, tt: &tt::TopSubtree) -> ExpandResult<tt::TopSubtree> {
     })
 }
 
-fn partial_ord_expand(span: Span, tt: &tt::TopSubtree) -> ExpandResult<tt::TopSubtree> {
+fn partial_ord_expand(
+    db: &dyn ExpandDatabase,
+    span: Span,
+    tt: &tt::TopSubtree,
+) -> ExpandResult<tt::TopSubtree> {
     let krate = &dollar_crate(span);
-    expand_simple_derive(span, tt, quote! {span => #krate::cmp::PartialOrd }, |adt| {
+    expand_simple_derive(db, span, tt, quote! {span => #krate::cmp::PartialOrd }, |adt| {
         fn compare(
             krate: &tt::Ident,
             left: tt::TopSubtree,
@@ -932,8 +975,12 @@ fn partial_ord_expand(span: Span, tt: &tt::TopSubtree) -> ExpandResult<tt::TopSu
     })
 }
 
-fn coerce_pointee_expand(span: Span, tt: &tt::TopSubtree) -> ExpandResult<tt::TopSubtree> {
-    let (adt, _span_map) = match to_adt_syntax(tt, span) {
+fn coerce_pointee_expand(
+    db: &dyn ExpandDatabase,
+    span: Span,
+    tt: &tt::TopSubtree,
+) -> ExpandResult<tt::TopSubtree> {
+    let (adt, _span_map) = match to_adt_syntax(db, tt, span) {
         Ok(it) => it,
         Err(err) => {
             return ExpandResult::new(tt::TopSubtree::empty(tt::DelimSpan::from_single(span)), err);
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs
index 5b06de98757..310ddaaf9e9 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs
@@ -69,7 +69,7 @@ impl BuiltinFnLikeExpander {
         tt: &tt::TopSubtree,
         span: Span,
     ) -> ExpandResult<tt::TopSubtree> {
-        let span = span_with_def_site_ctxt(db, span, id);
+        let span = span_with_def_site_ctxt(db, span, id, Edition::CURRENT);
         self.expander()(db, id, tt, span)
     }
 
@@ -86,7 +86,7 @@ impl EagerExpander {
         tt: &tt::TopSubtree,
         span: Span,
     ) -> ExpandResult<tt::TopSubtree> {
-        let span = span_with_def_site_ctxt(db, span, id);
+        let span = span_with_def_site_ctxt(db, span, id, Edition::CURRENT);
         self.expander()(db, id, tt, span)
     }
 
@@ -221,7 +221,7 @@ fn assert_expand(
     tt: &tt::TopSubtree,
     span: Span,
 ) -> ExpandResult<tt::TopSubtree> {
-    let call_site_span = span_with_call_site_ctxt(db, span, id);
+    let call_site_span = span_with_call_site_ctxt(db, span, id, Edition::CURRENT);
 
     let mut iter = tt.iter();
 
@@ -342,7 +342,7 @@ fn panic_expand(
     span: Span,
 ) -> ExpandResult<tt::TopSubtree> {
     let dollar_crate = dollar_crate(span);
-    let call_site_span = span_with_call_site_ctxt(db, span, id);
+    let call_site_span = span_with_call_site_ctxt(db, span, id, Edition::CURRENT);
 
     let mac = if use_panic_2021(db, call_site_span) {
         sym::panic_2021.clone()
@@ -373,7 +373,7 @@ fn unreachable_expand(
     span: Span,
 ) -> ExpandResult<tt::TopSubtree> {
     let dollar_crate = dollar_crate(span);
-    let call_site_span = span_with_call_site_ctxt(db, span, id);
+    let call_site_span = span_with_call_site_ctxt(db, span, id, Edition::CURRENT);
 
     let mac = if use_panic_2021(db, call_site_span) {
         sym::unreachable_2021.clone()
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/quote.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/quote.rs
index 6c1abc26203..9b637fc7684 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/quote.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/quote.rs
@@ -102,6 +102,7 @@ macro_rules! quote_impl__ {
     ($span:ident $builder:ident # ) => {$crate::builtin::quote::__quote!(@PUNCT($span $builder) '#')};
     ($span:ident $builder:ident $ ) => {$crate::builtin::quote::__quote!(@PUNCT($span $builder) '$')};
     ($span:ident $builder:ident * ) => {$crate::builtin::quote::__quote!(@PUNCT($span $builder) '*')};
+    ($span:ident $builder:ident = ) => {$crate::builtin::quote::__quote!(@PUNCT($span $builder) '=')};
 
     ($span:ident $builder:ident $first:tt $($tail:tt)+ ) => {{
         $crate::builtin::quote::__quote!($span $builder $first);
@@ -225,7 +226,7 @@ mod tests {
     use ::tt::IdentIsRaw;
     use expect_test::expect;
     use intern::Symbol;
-    use span::{SpanAnchor, SyntaxContextId, ROOT_ERASED_FILE_AST_ID};
+    use span::{Edition, SpanAnchor, SyntaxContextId, ROOT_ERASED_FILE_AST_ID};
     use syntax::{TextRange, TextSize};
 
     use super::quote;
@@ -239,7 +240,7 @@ mod tests {
             ),
             ast_id: ROOT_ERASED_FILE_AST_ID,
         },
-        ctx: SyntaxContextId::ROOT,
+        ctx: SyntaxContextId::root(Edition::CURRENT),
     };
 
     #[test]
@@ -276,8 +277,8 @@ mod tests {
         assert_eq!(quoted.to_string(), "hello");
         let t = format!("{quoted:#?}");
         expect![[r#"
-            SUBTREE $$ 937550:0@0..0#0 937550:0@0..0#0
-              IDENT   hello 937550:0@0..0#0"#]]
+            SUBTREE $$ 937550:0@0..0#2 937550:0@0..0#2
+              IDENT   hello 937550:0@0..0#2"#]]
         .assert_eq(&t);
     }
 
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs
index f4e80ef9e26..b7804f888ae 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs
@@ -5,7 +5,7 @@ use either::Either;
 use limit::Limit;
 use mbe::MatchedArmIndex;
 use rustc_hash::FxHashSet;
-use span::{AstIdMap, EditionedFileId, Span, SyntaxContextData, SyntaxContextId};
+use span::{AstIdMap, Edition, EditionedFileId, Span, SyntaxContextData, SyntaxContextId};
 use syntax::{ast, AstNode, Parse, SyntaxElement, SyntaxError, SyntaxNode, SyntaxToken, T};
 use syntax_bridge::{syntax_node_to_token_tree, DocCommentDesugarMode};
 use triomphe::Arc;
@@ -136,12 +136,12 @@ pub trait ExpandDatabase: SourceDatabase {
         macro_call: MacroCallId,
     ) -> Option<Arc<ExpandResult<Arc<[SyntaxError]>>>>;
     #[ra_salsa::transparent]
-    fn syntax_context(&self, file: HirFileId) -> SyntaxContextId;
+    fn syntax_context(&self, file: HirFileId, edition: Edition) -> SyntaxContextId;
 }
 
-fn syntax_context(db: &dyn ExpandDatabase, file: HirFileId) -> SyntaxContextId {
+fn syntax_context(db: &dyn ExpandDatabase, file: HirFileId, edition: Edition) -> SyntaxContextId {
     match file.repr() {
-        HirFileIdRepr::FileId(_) => SyntaxContextId::ROOT,
+        HirFileIdRepr::FileId(_) => SyntaxContextId::root(edition),
         HirFileIdRepr::MacroFile(m) => {
             db.macro_arg_considering_derives(m.macro_call_id, &m.macro_call_id.lookup(db).kind)
                 .2
@@ -273,9 +273,9 @@ pub fn expand_speculative(
                 loc.krate,
                 &tt,
                 attr_arg.as_ref(),
-                span_with_def_site_ctxt(db, span, actual_macro_call),
-                span_with_call_site_ctxt(db, span, actual_macro_call),
-                span_with_mixed_site_ctxt(db, span, actual_macro_call),
+                span_with_def_site_ctxt(db, span, actual_macro_call, loc.def.edition),
+                span_with_call_site_ctxt(db, span, actual_macro_call, loc.def.edition),
+                span_with_mixed_site_ctxt(db, span, actual_macro_call, loc.def.edition),
             )
         }
         MacroDefKind::BuiltInAttr(_, it) if it.is_derive() => {
@@ -300,7 +300,7 @@ pub fn expand_speculative(
 
     fixup::reverse_fixups(&mut speculative_expansion.value, &undo_info);
     let (node, rev_tmap) =
-        token_tree_to_syntax_node(&speculative_expansion.value, expand_to, loc.def.edition);
+        token_tree_to_syntax_node(db, &speculative_expansion.value, expand_to, loc.def.edition);
 
     let syntax_node = node.syntax_node();
     let token = rev_tmap
@@ -346,6 +346,7 @@ fn parse_macro_expansion(
         macro_expand(db, macro_file.macro_call_id, loc);
 
     let (parse, mut rev_token_map) = token_tree_to_syntax_node(
+        db,
         match &tt {
             CowArc::Arc(it) => it,
             CowArc::Owned(it) => it,
@@ -699,9 +700,9 @@ fn expand_proc_macro(
             loc.krate,
             &macro_arg,
             attr_arg,
-            span_with_def_site_ctxt(db, span, id),
-            span_with_call_site_ctxt(db, span, id),
-            span_with_mixed_site_ctxt(db, span, id),
+            span_with_def_site_ctxt(db, span, id, loc.def.edition),
+            span_with_call_site_ctxt(db, span, id, loc.def.edition),
+            span_with_mixed_site_ctxt(db, span, id, loc.def.edition),
         )
     };
 
@@ -715,7 +716,8 @@ fn expand_proc_macro(
     ExpandResult { value: Arc::new(tt), err }
 }
 
-fn token_tree_to_syntax_node(
+pub(crate) fn token_tree_to_syntax_node(
+    db: &dyn ExpandDatabase,
     tt: &tt::TopSubtree,
     expand_to: ExpandTo,
     edition: parser::Edition,
@@ -727,7 +729,12 @@ fn token_tree_to_syntax_node(
         ExpandTo::Type => syntax_bridge::TopEntryPoint::Type,
         ExpandTo::Expr => syntax_bridge::TopEntryPoint::Expr,
     };
-    syntax_bridge::token_tree_to_syntax_node(tt, entry_point, edition)
+    syntax_bridge::token_tree_to_syntax_node(
+        tt,
+        entry_point,
+        &mut |ctx| ctx.lookup(db).edition,
+        edition,
+    )
 }
 
 fn check_tt_count(tt: &tt::TopSubtree) -> Result<(), ExpandResult<()>> {
@@ -751,5 +758,7 @@ fn check_tt_count(tt: &tt::TopSubtree) -> Result<(), ExpandResult<()>> {
 }
 
 fn setup_syntax_context_root(db: &dyn ExpandDatabase) {
-    db.intern_syntax_context(SyntaxContextData::root());
+    for edition in Edition::iter() {
+        db.intern_syntax_context(SyntaxContextData::root(edition));
+    }
 }
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs b/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs
index d1c39f32ca3..fef77acb7bb 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs
@@ -2,7 +2,7 @@
 
 use base_db::CrateId;
 use intern::sym;
-use span::{Edition, MacroCallId, Span, SyntaxContextId};
+use span::{Edition, HirFileIdRepr, MacroCallId, Span, SyntaxContextId};
 use stdx::TupleExt;
 use syntax::{ast, AstNode};
 use syntax_bridge::DocCommentDesugarMode;
@@ -20,6 +20,7 @@ use crate::{
 pub struct DeclarativeMacroExpander {
     pub mac: mbe::DeclarativeMacro,
     pub transparency: Transparency,
+    edition: Edition,
 }
 
 impl DeclarativeMacroExpander {
@@ -40,7 +41,7 @@ impl DeclarativeMacroExpander {
                 .mac
                 .expand(
                     &tt,
-                    |s| s.ctx = apply_mark(db, s.ctx, call_id, self.transparency),
+                    |s| s.ctx = apply_mark(db, s.ctx, call_id, self.transparency, self.edition),
                     span,
                     loc.def.edition,
                 )
@@ -159,6 +160,10 @@ impl DeclarativeMacroExpander {
                 transparency(&macro_def).unwrap_or(Transparency::Opaque),
             ),
         };
-        Arc::new(DeclarativeMacroExpander { mac, transparency })
+        let edition = ctx_edition(match id.file_id.repr() {
+            HirFileIdRepr::MacroFile(macro_file) => macro_file.macro_call_id.lookup(db).ctxt,
+            HirFileIdRepr::FileId(file) => SyntaxContextId::root(file.edition()),
+        });
+        Arc::new(DeclarativeMacroExpander { mac, transparency, edition })
     }
 }
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/files.rs b/src/tools/rust-analyzer/crates/hir-expand/src/files.rs
index 8c04d054029..13ddb0d4acc 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/files.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/files.rs
@@ -380,14 +380,14 @@ impl InFile<TextRange> {
     ) -> (FileRange, SyntaxContextId) {
         match self.file_id.repr() {
             HirFileIdRepr::FileId(file_id) => {
-                (FileRange { file_id, range: self.value }, SyntaxContextId::ROOT)
+                (FileRange { file_id, range: self.value }, SyntaxContextId::root(file_id.edition()))
             }
             HirFileIdRepr::MacroFile(mac_file) => {
                 match map_node_range_up(db, &db.expansion_span_map(mac_file), self.value) {
                     Some(it) => it,
                     None => {
                         let loc = db.lookup_intern_macro_call(mac_file.macro_call_id);
-                        (loc.kind.original_call_range(db), SyntaxContextId::ROOT)
+                        (loc.kind.original_call_range(db), SyntaxContextId::root(loc.def.edition))
                     }
                 }
             }
@@ -432,9 +432,10 @@ impl InFile<TextRange> {
         db: &dyn db::ExpandDatabase,
     ) -> Option<(FileRange, SyntaxContextId)> {
         match self.file_id.repr() {
-            HirFileIdRepr::FileId(file_id) => {
-                Some((FileRange { file_id, range: self.value }, SyntaxContextId::ROOT))
-            }
+            HirFileIdRepr::FileId(file_id) => Some((
+                FileRange { file_id, range: self.value },
+                SyntaxContextId::root(file_id.edition()),
+            )),
             HirFileIdRepr::MacroFile(mac_file) => {
                 map_node_range_up(db, &db.expansion_span_map(mac_file), self.value)
             }
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 3d2d52a0afe..eb430177390 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs
@@ -380,7 +380,7 @@ pub(crate) fn reverse_fixups(tt: &mut TopSubtree, undo_info: &SyntaxFixupUndoInf
         let span = |file_id| Span {
             range: TextRange::empty(TextSize::new(0)),
             anchor: SpanAnchor { file_id, ast_id: ROOT_ERASED_FILE_AST_ID },
-            ctx: SyntaxContextId::ROOT,
+            ctx: SyntaxContextId::root(span::Edition::Edition2015),
         };
         delimiter.open = span(delimiter.open.anchor.file_id);
         delimiter.close = span(delimiter.close.anchor.file_id);
@@ -441,8 +441,8 @@ fn transform_tt<'a, 'b>(
                 };
                 let len_diff = replacement.len() as i64 - old_len as i64;
                 tt.splice(i..i + old_len, replacement.flat_tokens().iter().cloned());
-                // `+1` for the loop.
-                i = i.checked_add_signed(len_diff as isize + 1).unwrap();
+                // Skip the newly inserted replacement, we don't want to visit it.
+                i += replacement.len();
 
                 for &subtree_idx in &subtrees_stack {
                     let tt::TokenTree::Subtree(subtree) = &mut tt[subtree_idx] else {
@@ -532,7 +532,7 @@ mod tests {
     }
 
     #[track_caller]
-    fn check(ra_fixture: &str, mut expect: Expect) {
+    fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str, mut expect: Expect) {
         let parsed = syntax::SourceFile::parse(ra_fixture, span::Edition::CURRENT);
         let span_map = SpanMap::RealSpanMap(Arc::new(RealSpanMap::absolute(EditionedFileId::new(
             FileId::from_raw(0),
@@ -562,6 +562,7 @@ mod tests {
         let (parse, _) = syntax_bridge::token_tree_to_syntax_node(
             &tt,
             syntax_bridge::TopEntryPoint::MacroItems,
+            &mut |_| parser::Edition::CURRENT,
             parser::Edition::CURRENT,
         );
         assert!(
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/hygiene.rs b/src/tools/rust-analyzer/crates/hir-expand/src/hygiene.rs
index f48de807c28..fe05af0ac9d 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/hygiene.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/hygiene.rs
@@ -24,26 +24,37 @@
 
 use std::iter;
 
-use span::{MacroCallId, Span, SyntaxContextData, SyntaxContextId};
+use span::{Edition, MacroCallId, Span, SyntaxContextData, SyntaxContextId};
 
 use crate::db::{ExpandDatabase, InternSyntaxContextQuery};
 
 pub use span::Transparency;
 
-pub fn span_with_def_site_ctxt(db: &dyn ExpandDatabase, span: Span, expn_id: MacroCallId) -> Span {
-    span_with_ctxt_from_mark(db, span, expn_id, Transparency::Opaque)
+pub fn span_with_def_site_ctxt(
+    db: &dyn ExpandDatabase,
+    span: Span,
+    expn_id: MacroCallId,
+    edition: Edition,
+) -> Span {
+    span_with_ctxt_from_mark(db, span, expn_id, Transparency::Opaque, edition)
 }
 
-pub fn span_with_call_site_ctxt(db: &dyn ExpandDatabase, span: Span, expn_id: MacroCallId) -> Span {
-    span_with_ctxt_from_mark(db, span, expn_id, Transparency::Transparent)
+pub fn span_with_call_site_ctxt(
+    db: &dyn ExpandDatabase,
+    span: Span,
+    expn_id: MacroCallId,
+    edition: Edition,
+) -> Span {
+    span_with_ctxt_from_mark(db, span, expn_id, Transparency::Transparent, edition)
 }
 
 pub fn span_with_mixed_site_ctxt(
     db: &dyn ExpandDatabase,
     span: Span,
     expn_id: MacroCallId,
+    edition: Edition,
 ) -> Span {
-    span_with_ctxt_from_mark(db, span, expn_id, Transparency::SemiTransparent)
+    span_with_ctxt_from_mark(db, span, expn_id, Transparency::SemiTransparent, edition)
 }
 
 fn span_with_ctxt_from_mark(
@@ -51,8 +62,12 @@ fn span_with_ctxt_from_mark(
     span: Span,
     expn_id: MacroCallId,
     transparency: Transparency,
+    edition: Edition,
 ) -> Span {
-    Span { ctx: apply_mark(db, SyntaxContextId::ROOT, expn_id, transparency), ..span }
+    Span {
+        ctx: apply_mark(db, SyntaxContextId::root(edition), expn_id, transparency, edition),
+        ..span
+    }
 }
 
 pub(super) fn apply_mark(
@@ -60,9 +75,10 @@ pub(super) fn apply_mark(
     ctxt: SyntaxContextId,
     call_id: MacroCallId,
     transparency: Transparency,
+    edition: Edition,
 ) -> SyntaxContextId {
     if transparency == Transparency::Opaque {
-        return apply_mark_internal(db, ctxt, call_id, transparency);
+        return apply_mark_internal(db, ctxt, call_id, transparency, edition);
     }
 
     let call_site_ctxt = db.lookup_intern_macro_call(call_id).ctxt;
@@ -73,7 +89,7 @@ pub(super) fn apply_mark(
     };
 
     if call_site_ctxt.is_root() {
-        return apply_mark_internal(db, ctxt, call_id, transparency);
+        return apply_mark_internal(db, ctxt, call_id, transparency, edition);
     }
 
     // Otherwise, `expn_id` is a macros 1.0 definition and the call site is in a
@@ -86,9 +102,9 @@ pub(super) fn apply_mark(
     //
     // See the example at `test/ui/hygiene/legacy_interaction.rs`.
     for (call_id, transparency) in ctxt.marks(db) {
-        call_site_ctxt = apply_mark_internal(db, call_site_ctxt, call_id, transparency);
+        call_site_ctxt = apply_mark_internal(db, call_site_ctxt, call_id, transparency, edition);
     }
-    apply_mark_internal(db, call_site_ctxt, call_id, transparency)
+    apply_mark_internal(db, call_site_ctxt, call_id, transparency, edition)
 }
 
 fn apply_mark_internal(
@@ -96,6 +112,7 @@ fn apply_mark_internal(
     ctxt: SyntaxContextId,
     call_id: MacroCallId,
     transparency: Transparency,
+    edition: Edition,
 ) -> SyntaxContextId {
     use base_db::ra_salsa;
 
@@ -108,13 +125,14 @@ fn apply_mark_internal(
     if transparency >= Transparency::Opaque {
         let parent = opaque;
         opaque = ra_salsa::plumbing::get_query_table::<InternSyntaxContextQuery>(db).get_or_insert(
-            (parent, call_id, transparency),
+            (parent, call_id, transparency, edition),
             |new_opaque| SyntaxContextData {
                 outer_expn: call_id,
                 outer_transparency: transparency,
                 parent,
                 opaque: new_opaque,
                 opaque_and_semitransparent: new_opaque,
+                edition,
             },
         );
     }
@@ -123,13 +141,14 @@ fn apply_mark_internal(
         let parent = opaque_and_semitransparent;
         opaque_and_semitransparent =
             ra_salsa::plumbing::get_query_table::<InternSyntaxContextQuery>(db).get_or_insert(
-                (parent, call_id, transparency),
+                (parent, call_id, transparency, edition),
                 |new_opaque_and_semitransparent| SyntaxContextData {
                     outer_expn: call_id,
                     outer_transparency: transparency,
                     parent,
                     opaque,
                     opaque_and_semitransparent: new_opaque_and_semitransparent,
+                    edition,
                 },
             );
     }
@@ -141,6 +160,7 @@ fn apply_mark_internal(
         parent,
         opaque,
         opaque_and_semitransparent,
+        edition,
     })
 }
 
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 a0c4c125db4..2c664029f61 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs
@@ -188,6 +188,8 @@ impl fmt::Display for RenderedExpandError {
 
 impl RenderedExpandError {
     const GENERAL_KIND: &str = "macro-error";
+    const DISABLED: &str = "proc-macro-disabled";
+    const ATTR_EXP_DISABLED: &str = "attribute-expansion-disabled";
 }
 
 impl ExpandErrorKind {
@@ -196,12 +198,12 @@ impl ExpandErrorKind {
             ExpandErrorKind::ProcMacroAttrExpansionDisabled => RenderedExpandError {
                 message: "procedural attribute macro expansion is disabled".to_owned(),
                 error: false,
-                kind: "proc-macros-disabled",
+                kind: RenderedExpandError::ATTR_EXP_DISABLED,
             },
             ExpandErrorKind::MacroDisabled => RenderedExpandError {
                 message: "proc-macro is explicitly disabled".to_owned(),
                 error: false,
-                kind: "proc-macro-disabled",
+                kind: RenderedExpandError::DISABLED,
             },
             &ExpandErrorKind::MissingProcMacroExpander(def_crate) => {
                 match db.proc_macros().get_error_for_crate(def_crate) {
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 7ecf5219873..89eae862bd9 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
@@ -273,10 +273,9 @@ fn convert_path(
                 res
             }
         }
-        ast::PathSegmentKind::SelfTypeKw => ModPath::from_segments(
-            PathKind::Plain,
-            Some(Name::new_symbol(sym::Self_.clone(), SyntaxContextId::ROOT)),
-        ),
+        ast::PathSegmentKind::SelfTypeKw => {
+            ModPath::from_segments(PathKind::Plain, Some(Name::new_symbol_root(sym::Self_.clone())))
+        }
         ast::PathSegmentKind::CrateKw => ModPath::from_segments(PathKind::Crate, iter::empty()),
         ast::PathSegmentKind::SelfKw => handle_super_kw(0)?,
         ast::PathSegmentKind::SuperKw => handle_super_kw(1)?,
@@ -399,6 +398,9 @@ macro_rules! __known_path {
     (core::fmt::Debug) => {};
     (std::fmt::format) => {};
     (core::ops::Try) => {};
+    (core::convert::From) => {};
+    (core::convert::TryFrom) => {};
+    (core::str::FromStr) => {};
     ($path:path) => {
         compile_error!("Please register your known path in the path module")
     };
@@ -415,3 +417,14 @@ macro_rules! __path {
 }
 
 pub use crate::__path as path;
+
+#[macro_export]
+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()),)*
+        ])
+    });
+}
+
+pub use crate::__tool_path as tool_path;
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/name.rs b/src/tools/rust-analyzer/crates/hir-expand/src/name.rs
index 267d5458333..cc53d2e34aa 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/name.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/name.rs
@@ -11,7 +11,7 @@ use syntax::utils::is_raw_identifier;
 /// and declarations. In theory, names should also carry hygiene info, but we are
 /// not there yet!
 ///
-/// Note that the rawness (`r#`) of names does not depend on whether they are written raw.
+/// Note that the rawness (`r#`) of names is not preserved. Names are always stored without a `r#` prefix.
 /// This is because we want to show (in completions etc.) names as raw depending on the needs
 /// of the current crate, for example if it is edition 2021 complete `gen` even if the defining
 /// crate is in edition 2024 and wrote `r#gen`, and the opposite holds as well.
@@ -77,20 +77,49 @@ impl Name {
     /// Hopefully, this should allow us to integrate hygiene cleaner in the
     /// future, and to switch to interned representation of names.
     fn new_text(text: &str) -> Name {
+        debug_assert!(!text.starts_with("r#"));
         Name { symbol: Symbol::intern(text), ctx: () }
     }
 
-    pub fn new(text: &str, ctx: SyntaxContextId) -> Name {
+    pub fn new(text: &str, mut ctx: SyntaxContextId) -> Name {
+        // For comparisons etc. we remove the edition, because sometimes we search for some `Name`
+        // and we don't know which edition it came from.
+        // Can't do that for all `SyntaxContextId`s because it breaks Salsa.
+        ctx.remove_root_edition();
         _ = ctx;
         Self::new_text(text)
     }
 
+    pub fn new_root(text: &str) -> Name {
+        // The edition doesn't matter for hygiene.
+        Self::new(text.trim_start_matches("r#"), SyntaxContextId::root(Edition::Edition2015))
+    }
+
     pub fn new_tuple_field(idx: usize) -> Name {
-        Name { symbol: Symbol::intern(&idx.to_string()), ctx: () }
+        let symbol = match idx {
+            0 => sym::INTEGER_0.clone(),
+            1 => sym::INTEGER_1.clone(),
+            2 => sym::INTEGER_2.clone(),
+            3 => sym::INTEGER_3.clone(),
+            4 => sym::INTEGER_4.clone(),
+            5 => sym::INTEGER_5.clone(),
+            6 => sym::INTEGER_6.clone(),
+            7 => sym::INTEGER_7.clone(),
+            8 => sym::INTEGER_8.clone(),
+            9 => sym::INTEGER_9.clone(),
+            10 => sym::INTEGER_10.clone(),
+            11 => sym::INTEGER_11.clone(),
+            12 => sym::INTEGER_12.clone(),
+            13 => sym::INTEGER_13.clone(),
+            14 => sym::INTEGER_14.clone(),
+            15 => sym::INTEGER_15.clone(),
+            _ => Symbol::intern(&idx.to_string()),
+        };
+        Name { symbol, ctx: () }
     }
 
     pub fn new_lifetime(lt: &ast::Lifetime) -> Name {
-        Name { symbol: Symbol::intern(lt.text().as_str()), ctx: () }
+        Self::new_text(lt.text().as_str().trim_start_matches("r#"))
     }
 
     /// Resolve a name from the text of token.
@@ -133,15 +162,18 @@ impl Name {
     }
 
     /// Returns the text this name represents if it isn't a tuple field.
+    ///
+    /// Do not use this for user-facing text, use `display` instead to handle editions properly.
     pub fn as_str(&self) -> &str {
         self.symbol.as_str()
     }
 
+    // FIXME: Remove this
     pub fn unescaped(&self) -> UnescapedName<'_> {
         UnescapedName(self)
     }
 
-    pub fn is_escaped(&self, edition: Edition) -> bool {
+    pub fn needs_escape(&self, edition: Edition) -> bool {
         is_raw_identifier(self.symbol.as_str(), edition)
     }
 
@@ -164,16 +196,19 @@ impl Name {
         &self.symbol
     }
 
-    pub const fn new_symbol(symbol: Symbol, ctx: SyntaxContextId) -> Self {
+    pub fn new_symbol(symbol: Symbol, ctx: SyntaxContextId) -> Self {
+        debug_assert!(!symbol.as_str().starts_with("r#"));
         _ = ctx;
         Self { symbol, ctx: () }
     }
 
     // FIXME: This needs to go once we have hygiene
-    pub const fn new_symbol_root(sym: Symbol) -> Self {
+    pub fn new_symbol_root(sym: Symbol) -> Self {
+        debug_assert!(!sym.as_str().starts_with("r#"));
         Self { symbol: sym, ctx: () }
     }
 
+    // FIXME: Remove this
     #[inline]
     pub fn eq_ident(&self, ident: &str) -> bool {
         self.as_str() == ident.trim_start_matches("r#")
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 9f01f1eb259..c8ff6cba3dd 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
@@ -856,7 +856,7 @@ fn impl_def_datum(
     let associated_ty_value_ids = impl_data
         .items
         .iter()
-        .filter_map(|item| match item {
+        .filter_map(|(_, item)| match item {
             AssocItemId::TypeAliasId(type_alias) => Some(*type_alias),
             _ => None,
         })
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 0a8bfaa70f8..2d7d4cacd2c 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
@@ -31,7 +31,10 @@ fn simplify(e: ConstEvalError) -> ConstEvalError {
 }
 
 #[track_caller]
-fn check_fail(ra_fixture: &str, error: impl FnOnce(ConstEvalError) -> bool) {
+fn check_fail(
+    #[rust_analyzer::rust_fixture] ra_fixture: &str,
+    error: impl FnOnce(ConstEvalError) -> bool,
+) {
     let (db, file_id) = TestDB::with_single_file(ra_fixture);
     match eval_goal(&db, file_id) {
         Ok(_) => panic!("Expected fail, but it succeeded"),
@@ -42,7 +45,7 @@ fn check_fail(ra_fixture: &str, error: impl FnOnce(ConstEvalError) -> bool) {
 }
 
 #[track_caller]
-fn check_number(ra_fixture: &str, answer: i128) {
+fn check_number(#[rust_analyzer::rust_fixture] ra_fixture: &str, answer: i128) {
     check_answer(ra_fixture, |b, _| {
         assert_eq!(
             b,
@@ -54,7 +57,7 @@ fn check_number(ra_fixture: &str, answer: i128) {
 }
 
 #[track_caller]
-fn check_str(ra_fixture: &str, answer: &str) {
+fn check_str(#[rust_analyzer::rust_fixture] ra_fixture: &str, answer: &str) {
     check_answer(ra_fixture, |b, mm| {
         let addr = usize::from_le_bytes(b[0..b.len() / 2].try_into().unwrap());
         let size = usize::from_le_bytes(b[b.len() / 2..].try_into().unwrap());
@@ -71,7 +74,10 @@ fn check_str(ra_fixture: &str, answer: &str) {
 }
 
 #[track_caller]
-fn check_answer(ra_fixture: &str, check: impl FnOnce(&[u8], &MemoryMap)) {
+fn check_answer(
+    #[rust_analyzer::rust_fixture] ra_fixture: &str,
+    check: impl FnOnce(&[u8], &MemoryMap),
+) {
     let (db, file_ids) = TestDB::with_many_files(ra_fixture);
     let file_id = *file_ids.last().unwrap();
     let r = match eval_goal(&db, file_id) {
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 a4e052a0362..3545bf76776 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs
@@ -471,10 +471,55 @@ impl HirDisplay for ProjectionTy {
         if f.should_truncate() {
             return write!(f, "{TYPE_HINT_TRUNCATION}");
         }
-
         let trait_ref = self.trait_ref(f.db);
+        let self_ty = trait_ref.self_type_parameter(Interner);
+
+        // 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 let TyKind::Placeholder(idx) = self_ty.kind(Interner) {
+                let db = f.db;
+                let id = from_placeholder_idx(db, *idx);
+                let generics = generics(db.upcast(), id.parent);
+
+                let substs = generics.placeholder_subst(db);
+                let bounds = db
+                    .generic_predicates(id.parent)
+                    .iter()
+                    .map(|pred| pred.clone().substitute(Interner, &substs))
+                    .filter(|wc| match wc.skip_binders() {
+                        WhereClause::Implemented(tr) => {
+                            match tr.self_type_parameter(Interner).kind(Interner) {
+                                TyKind::Alias(AliasTy::Projection(proj)) => proj == self,
+                                _ => false,
+                            }
+                        }
+                        WhereClause::TypeOutlives(t) => match t.ty.kind(Interner) {
+                            TyKind::Alias(AliasTy::Projection(proj)) => proj == self,
+                            _ => false,
+                        },
+                        // We shouldn't be here if these exist
+                        WhereClause::AliasEq(_) => false,
+                        WhereClause::LifetimeOutlives(_) => false,
+                    })
+                    .collect::<Vec<_>>();
+                if !bounds.is_empty() {
+                    return write_bounds_like_dyn_trait_with_prefix(
+                        f,
+                        "impl",
+                        Either::Left(
+                            &TyKind::Alias(AliasTy::Projection(self.clone())).intern(Interner),
+                        ),
+                        &bounds,
+                        SizedByDefault::NotSized,
+                    );
+                };
+            }
+        }
+
         write!(f, "<")?;
-        trait_ref.self_type_parameter(Interner).hir_fmt(f)?;
+        self_ty.hir_fmt(f)?;
         write!(f, " as ")?;
         trait_ref.hir_fmt(f)?;
         write!(
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility/tests.rs
index 8a56bd28b59..3060b610bb6 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility/tests.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility/tests.rs
@@ -26,7 +26,7 @@ enum DynCompatibilityViolationKind {
 }
 
 fn check_dyn_compatibility<'a>(
-    ra_fixture: &str,
+    #[rust_analyzer::rust_fixture] ra_fixture: &str,
     expected: impl IntoIterator<Item = (&'a str, Vec<DynCompatibilityViolationKind>)>,
 ) {
     let mut expected: FxHashMap<_, _> =
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 66159ddce2b..4d3896660b4 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
@@ -25,7 +25,10 @@ fn current_machine_data_layout() -> String {
     .unwrap()
 }
 
-fn eval_goal(ra_fixture: &str, minicore: &str) -> Result<Arc<Layout>, LayoutError> {
+fn eval_goal(
+    #[rust_analyzer::rust_fixture] ra_fixture: &str,
+    minicore: &str,
+) -> Result<Arc<Layout>, LayoutError> {
     let target_data_layout = current_machine_data_layout();
     let ra_fixture = format!(
         "//- target_data_layout: {target_data_layout}\n{minicore}//- /main.rs crate:test\n{ra_fixture}",
@@ -81,7 +84,10 @@ fn eval_goal(ra_fixture: &str, minicore: &str) -> Result<Arc<Layout>, LayoutErro
 }
 
 /// A version of `eval_goal` for types that can not be expressed in ADTs, like closures and `impl Trait`
-fn eval_expr(ra_fixture: &str, minicore: &str) -> Result<Arc<Layout>, LayoutError> {
+fn eval_expr(
+    #[rust_analyzer::rust_fixture] ra_fixture: &str,
+    minicore: &str,
+) -> Result<Arc<Layout>, LayoutError> {
     let target_data_layout = current_machine_data_layout();
     let ra_fixture = format!(
         "//- target_data_layout: {target_data_layout}\n{minicore}//- /main.rs crate:test\nfn main(){{let goal = {{{ra_fixture}}};}}",
@@ -114,21 +120,31 @@ fn eval_expr(ra_fixture: &str, minicore: &str) -> Result<Arc<Layout>, LayoutErro
 }
 
 #[track_caller]
-fn check_size_and_align(ra_fixture: &str, minicore: &str, size: u64, align: u64) {
+fn check_size_and_align(
+    #[rust_analyzer::rust_fixture] ra_fixture: &str,
+    minicore: &str,
+    size: u64,
+    align: u64,
+) {
     let l = eval_goal(ra_fixture, minicore).unwrap();
     assert_eq!(l.size.bytes(), size, "size mismatch");
     assert_eq!(l.align.abi.bytes(), align, "align mismatch");
 }
 
 #[track_caller]
-fn check_size_and_align_expr(ra_fixture: &str, minicore: &str, size: u64, align: u64) {
+fn check_size_and_align_expr(
+    #[rust_analyzer::rust_fixture] ra_fixture: &str,
+    minicore: &str,
+    size: u64,
+    align: u64,
+) {
     let l = eval_expr(ra_fixture, minicore).unwrap();
     assert_eq!(l.size.bytes(), size, "size mismatch");
     assert_eq!(l.align.abi.bytes(), align, "align mismatch");
 }
 
 #[track_caller]
-fn check_fail(ra_fixture: &str, e: LayoutError) {
+fn check_fail(#[rust_analyzer::rust_fixture] ra_fixture: &str, e: LayoutError) {
     let r = eval_goal(ra_fixture, "");
     assert_eq!(r, Err(e));
 }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs
index 24f67fd6602..432b8f4d94e 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs
@@ -761,8 +761,8 @@ impl<'a> TyLoweringContext<'a> {
         path: &Path,
         on_diagnostic: &mut dyn FnMut(&mut Self, PathLoweringDiagnostic),
     ) -> Option<(TypeNs, Option<usize>)> {
-        let (resolution, remaining_index, _) =
-            self.resolver.resolve_path_in_type_ns(self.db.upcast(), path)?;
+        let (resolution, remaining_index, _, prefix_info) =
+            self.resolver.resolve_path_in_type_ns_with_prefix_info(self.db.upcast(), path)?;
         let segments = path.segments();
 
         match path {
@@ -771,13 +771,12 @@ impl<'a> TyLoweringContext<'a> {
             _ => return Some((resolution, remaining_index)),
         };
 
-        let (module_segments, resolved_segment_idx, resolved_segment) = match remaining_index {
-            None => (
-                segments.strip_last(),
-                segments.len() - 1,
-                segments.last().expect("resolved path has at least one element"),
-            ),
-            Some(i) => (segments.take(i - 1), i - 1, segments.get(i - 1).unwrap()),
+        let (module_segments, resolved_segment_idx, enum_segment) = match remaining_index {
+            None if prefix_info.enum_variant => {
+                (segments.strip_last_two(), segments.len() - 1, Some(segments.len() - 2))
+            }
+            None => (segments.strip_last(), segments.len() - 1, None),
+            Some(i) => (segments.take(i - 1), i - 1, None),
         };
 
         for (i, mod_segment) in module_segments.iter().enumerate() {
@@ -792,9 +791,23 @@ impl<'a> TyLoweringContext<'a> {
             }
         }
 
+        if let Some(enum_segment) = enum_segment {
+            if segments.get(enum_segment).is_some_and(|it| it.args_and_bindings.is_some())
+                && segments.get(enum_segment + 1).is_some_and(|it| it.args_and_bindings.is_some())
+            {
+                on_diagnostic(
+                    self,
+                    PathLoweringDiagnostic::GenericArgsProhibited {
+                        segment: (enum_segment + 1) as u32,
+                        reason: GenericArgsProhibitedReason::EnumVariant,
+                    },
+                );
+            }
+        }
+
         self.handle_type_ns_resolution(
             &resolution,
-            resolved_segment,
+            segments.get(resolved_segment_idx).expect("should have resolved segment"),
             resolved_segment_idx,
             on_diagnostic,
         );
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs
index 62b071b2f32..182032f0481 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs
@@ -746,16 +746,9 @@ fn lookup_impl_assoc_item_for_trait_ref(
     let table = InferenceTable::new(db, env);
 
     let (impl_data, impl_subst) = find_matching_impl(impls, table, trait_ref)?;
-    let item = impl_data.items.iter().find_map(|&it| match it {
-        AssocItemId::FunctionId(f) => {
-            (db.function_data(f).name == *name).then_some(AssocItemId::FunctionId(f))
-        }
-        AssocItemId::ConstId(c) => db
-            .const_data(c)
-            .name
-            .as_ref()
-            .map(|n| n == name)
-            .and_then(|result| if result { Some(AssocItemId::ConstId(c)) } else { None }),
+    let item = impl_data.items.iter().find_map(|(n, it)| match *it {
+        AssocItemId::FunctionId(f) => (n == name).then_some(AssocItemId::FunctionId(f)),
+        AssocItemId::ConstId(c) => (n == name).then_some(AssocItemId::ConstId(c)),
         AssocItemId::TypeAliasId(_) => None,
     })?;
     Some((item, impl_subst))
@@ -850,7 +843,7 @@ fn is_inherent_impl_coherent(
         };
         rustc_has_incoherent_inherent_impls
             && !impl_data.items.is_empty()
-            && impl_data.items.iter().copied().all(|assoc| match assoc {
+            && impl_data.items.iter().all(|&(_, assoc)| match assoc {
                 AssocItemId::FunctionId(it) => db.function_data(it).rustc_allow_incoherent_impl,
                 AssocItemId::ConstId(it) => db.const_data(it).rustc_allow_incoherent_impl,
                 AssocItemId::TypeAliasId(it) => db.type_alias_data(it).rustc_allow_incoherent_impl,
@@ -1399,7 +1392,7 @@ fn iterate_inherent_methods(
         callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId, bool) -> ControlFlow<()>,
     ) -> ControlFlow<()> {
         for &impl_id in impls.for_self_ty(self_ty) {
-            for &item in table.db.impl_data(impl_id).items.iter() {
+            for &(ref item_name, item) in table.db.impl_data(impl_id).items.iter() {
                 let visible = match is_valid_impl_method_candidate(
                     table,
                     self_ty,
@@ -1408,6 +1401,7 @@ fn iterate_inherent_methods(
                     name,
                     impl_id,
                     item,
+                    item_name,
                 ) {
                     IsValidCandidate::Yes => true,
                     IsValidCandidate::NotVisible => false,
@@ -1467,6 +1461,7 @@ fn is_valid_impl_method_candidate(
     name: Option<&Name>,
     impl_id: ImplId,
     item: AssocItemId,
+    item_name: &Name,
 ) -> IsValidCandidate {
     match item {
         AssocItemId::FunctionId(f) => is_valid_impl_fn_candidate(
@@ -1477,11 +1472,12 @@ fn is_valid_impl_method_candidate(
             receiver_ty,
             self_ty,
             visible_from_module,
+            item_name,
         ),
         AssocItemId::ConstId(c) => {
             let db = table.db;
             check_that!(receiver_ty.is_none());
-            check_that!(name.is_none_or(|n| db.const_data(c).name.as_ref() == Some(n)));
+            check_that!(name.is_none_or(|n| n == item_name));
 
             if let Some(from_module) = visible_from_module {
                 if !db.const_visibility(c).is_visible_from(db.upcast(), from_module) {
@@ -1565,11 +1561,13 @@ fn is_valid_impl_fn_candidate(
     receiver_ty: Option<&Ty>,
     self_ty: &Ty,
     visible_from_module: Option<ModuleId>,
+    item_name: &Name,
 ) -> IsValidCandidate {
+    check_that!(name.is_none_or(|n| n == item_name));
+
     let db = table.db;
     let data = db.function_data(fn_id);
 
-    check_that!(name.is_none_or(|n| n == &data.name));
     if let Some(from_module) = visible_from_module {
         if !db.function_visibility(fn_id).is_visible_from(db.upcast(), from_module) {
             cov_mark::hit!(autoderef_candidate_not_visible);
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 ce43e90df7d..f1e86daea23 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
@@ -37,11 +37,15 @@ fn eval_main(db: &TestDB, file_id: EditionedFileId) -> Result<(String, String),
     Ok((output.stdout().into_owned(), output.stderr().into_owned()))
 }
 
-fn check_pass(ra_fixture: &str) {
+fn check_pass(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
     check_pass_and_stdio(ra_fixture, "", "");
 }
 
-fn check_pass_and_stdio(ra_fixture: &str, expected_stdout: &str, expected_stderr: &str) {
+fn check_pass_and_stdio(
+    #[rust_analyzer::rust_fixture] ra_fixture: &str,
+    expected_stdout: &str,
+    expected_stderr: &str,
+) {
     let (db, file_ids) = TestDB::with_many_files(ra_fixture);
     let file_id = *file_ids.last().unwrap();
     let x = eval_main(&db, file_id);
@@ -73,7 +77,7 @@ fn check_pass_and_stdio(ra_fixture: &str, expected_stdout: &str, expected_stderr
     }
 }
 
-fn check_panic(ra_fixture: &str, expected_panic: &str) {
+fn check_panic(#[rust_analyzer::rust_fixture] ra_fixture: &str, expected_panic: &str) {
     let (db, file_ids) = TestDB::with_many_files(ra_fixture);
     let file_id = *file_ids.last().unwrap();
     let e = eval_main(&db, file_id).unwrap_err();
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 b7607b5f639..00da9b25176 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs
@@ -69,27 +69,32 @@ fn setup_tracing() -> Option<tracing::subscriber::DefaultGuard> {
 }
 
 #[track_caller]
-fn check_types(ra_fixture: &str) {
+fn check_types(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
     check_impl(ra_fixture, false, true, false)
 }
 
 #[track_caller]
-fn check_types_source_code(ra_fixture: &str) {
+fn check_types_source_code(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
     check_impl(ra_fixture, false, true, true)
 }
 
 #[track_caller]
-fn check_no_mismatches(ra_fixture: &str) {
+fn check_no_mismatches(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
     check_impl(ra_fixture, true, false, false)
 }
 
 #[track_caller]
-fn check(ra_fixture: &str) {
+fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
     check_impl(ra_fixture, false, false, false)
 }
 
 #[track_caller]
-fn check_impl(ra_fixture: &str, allow_none: bool, only_types: bool, display_source: bool) {
+fn check_impl(
+    #[rust_analyzer::rust_fixture] ra_fixture: &str,
+    allow_none: bool,
+    only_types: bool,
+    display_source: bool,
+) {
     let _tracing = setup_tracing();
     let (db, files) = TestDB::with_many_files(ra_fixture);
 
@@ -282,7 +287,7 @@ fn pat_node(
     })
 }
 
-fn infer(ra_fixture: &str) -> String {
+fn infer(#[rust_analyzer::rust_fixture] ra_fixture: &str) -> String {
     infer_with_mismatches(ra_fixture, false)
 }
 
@@ -430,7 +435,7 @@ pub(crate) fn visit_module(
     visit_scope(db, crate_def_map, &crate_def_map[module_id].scope, cb);
     for impl_id in crate_def_map[module_id].scope.impls() {
         let impl_data = db.impl_data(impl_id);
-        for &item in impl_data.items.iter() {
+        for &(_, item) in impl_data.items.iter() {
             match item {
                 AssocItemId::FunctionId(it) => {
                     let body = db.body(it.into());
@@ -520,13 +525,13 @@ fn ellipsize(mut text: String, max_len: usize) -> String {
     text
 }
 
-fn check_infer(ra_fixture: &str, expect: Expect) {
+fn check_infer(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
     let mut actual = infer(ra_fixture);
     actual.push('\n');
     expect.assert_eq(&actual);
 }
 
-fn check_infer_with_mismatches(ra_fixture: &str, expect: Expect) {
+fn check_infer_with_mismatches(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
     let mut actual = infer_with_mismatches(ra_fixture, true);
     actual.push('\n');
     expect.assert_eq(&actual);
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 7de92d6b160..34d299edd1b 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
@@ -14,7 +14,7 @@ use crate::test_db::TestDB;
 
 use super::visit_module;
 
-fn check_closure_captures(ra_fixture: &str, expect: Expect) {
+fn check_closure_captures(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
     let (db, file_id) = TestDB::with_single_file(ra_fixture);
     let module = db.module_for_file(file_id);
     let def_map = module.def_map(&db);
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 5f0f341f393..15636604570 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
@@ -3802,3 +3802,15 @@ fn foo() {
         "#,
     );
 }
+
+#[test]
+fn tool_attr_skip() {
+    check_no_mismatches(
+        r#"
+#[rust_analyzer::skip]
+async fn foo(a: (), b: i32) -> u32 {
+    0 + 1 + b()
+}
+        "#,
+    );
+}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs b/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs
index 30711b16dfb..afd163fbd96 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs
@@ -968,7 +968,7 @@ struct FixedPoint<T, U, V>(&'static FixedPoint<(), T, U>, V);
     }
 
     #[track_caller]
-    fn check(ra_fixture: &str, expected: Expect) {
+    fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str, expected: Expect) {
         // use tracing_subscriber::{layer::SubscriberExt, Layer};
         // let my_layer = tracing_subscriber::fmt::layer();
         // let _g = tracing::subscriber::set_default(tracing_subscriber::registry().with(
diff --git a/src/tools/rust-analyzer/crates/hir/Cargo.toml b/src/tools/rust-analyzer/crates/hir/Cargo.toml
index 6aadc5c4f7e..c68ff706e48 100644
--- a/src/tools/rust-analyzer/crates/hir/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/hir/Cargo.toml
@@ -20,6 +20,7 @@ itertools.workspace = true
 smallvec.workspace = true
 tracing.workspace = true
 triomphe.workspace = true
+indexmap.workspace = true
 
 # local deps
 base-db.workspace = true
diff --git a/src/tools/rust-analyzer/crates/hir/src/attrs.rs b/src/tools/rust-analyzer/crates/hir/src/attrs.rs
index a23fdf1b393..4351a34e822 100644
--- a/src/tools/rust-analyzer/crates/hir/src/attrs.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/attrs.rs
@@ -12,7 +12,6 @@ use hir_def::{
 };
 use hir_expand::{mod_path::PathKind, name::Name};
 use hir_ty::{db::HirDatabase, method_resolution};
-use span::SyntaxContextId;
 
 use crate::{
     Adt, AsAssocItem, AssocItem, BuiltinType, Const, ConstParam, DocLinkDef, Enum, ExternCrateDecl,
@@ -90,6 +89,16 @@ impl HasAttrs for AssocItem {
     }
 }
 
+impl HasAttrs for crate::Crate {
+    fn attrs(self, db: &dyn HirDatabase) -> AttrsWithOwner {
+        let def = AttrDefId::ModuleId(self.root_module().id);
+        AttrsWithOwner::new(db.upcast(), def)
+    }
+    fn attr_id(self) -> AttrDefId {
+        AttrDefId::ModuleId(self.root_module().id)
+    }
+}
+
 /// Resolves the item `link` points to in the scope of `def`.
 pub fn resolve_doc_path_on(
     db: &dyn HirDatabase,
@@ -328,9 +337,7 @@ fn doc_modpath_from_str(link: &str) -> Option<ModPath> {
         };
         let parts = first_segment.into_iter().chain(parts).map(|segment| match segment.parse() {
             Ok(idx) => Name::new_tuple_field(idx),
-            Err(_) => {
-                Name::new(segment.split_once('<').map_or(segment, |it| it.0), SyntaxContextId::ROOT)
-            }
+            Err(_) => Name::new_root(segment.split_once('<').map_or(segment, |it| it.0)),
         });
         Some(ModPath::from_segments(kind, parts))
     };
diff --git a/src/tools/rust-analyzer/crates/hir/src/display.rs b/src/tools/rust-analyzer/crates/hir/src/display.rs
index e09ded32fbd..b29c91694d3 100644
--- a/src/tools/rust-analyzer/crates/hir/src/display.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/display.rs
@@ -23,10 +23,10 @@ use hir_ty::{
 use itertools::Itertools;
 
 use crate::{
-    Adt, AsAssocItem, AssocItem, AssocItemContainer, Const, ConstParam, Enum, ExternCrateDecl,
-    Field, Function, GenericParam, HasCrate, HasVisibility, Impl, LifetimeParam, Macro, Module,
-    SelfParam, Static, Struct, Trait, TraitAlias, TraitRef, TupleField, TyBuilder, Type, TypeAlias,
-    TypeOrConstParam, TypeParam, Union, Variant,
+    Adt, AsAssocItem, AssocItem, AssocItemContainer, Const, ConstParam, Crate, Enum,
+    ExternCrateDecl, Field, Function, GenericParam, HasCrate, HasVisibility, Impl, LifetimeParam,
+    Macro, Module, SelfParam, Static, Struct, Trait, TraitAlias, TraitRef, TupleField, TyBuilder,
+    Type, TypeAlias, TypeOrConstParam, TypeParam, Union, Variant,
 };
 
 impl HirDisplay for Function {
@@ -846,14 +846,27 @@ impl HirDisplay for TypeAlias {
 
 impl HirDisplay for Module {
     fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
-        // FIXME: Module doesn't have visibility saved in data.
+        match self.parent(f.db) {
+            Some(m) => write_visibility(m.id, self.visibility(f.db), f)?,
+            None => {
+                return match self.krate(f.db).display_name(f.db) {
+                    Some(name) => write!(f, "extern crate {name}"),
+                    None => f.write_str("extern crate {unknown}"),
+                }
+            }
+        }
         match self.name(f.db) {
             Some(name) => write!(f, "mod {}", name.display(f.db.upcast(), f.edition())),
-            None if self.is_crate_root() => match self.krate(f.db).display_name(f.db) {
-                Some(name) => write!(f, "extern crate {name}"),
-                None => f.write_str("extern crate {unknown}"),
-            },
-            None => f.write_str("mod {unnamed}"),
+            None => f.write_str("mod {unknown}"),
+        }
+    }
+}
+
+impl HirDisplay for Crate {
+    fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
+        match self.display_name(f.db) {
+            Some(name) => write!(f, "extern crate {name}"),
+            None => f.write_str("extern crate {unknown}"),
         }
     }
 }
diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs
index 00b4db54374..db3121d3cd3 100644
--- a/src/tools/rust-analyzer/crates/hir/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs
@@ -54,11 +54,11 @@ use hir_def::{
     per_ns::PerNs,
     resolver::{HasResolver, Resolver},
     type_ref::TypesSourceMap,
-    AssocItemId, AssocItemLoc, AttrDefId, CallableDefId, ConstId, ConstParamId, CrateRootModuleId,
-    DefWithBodyId, EnumId, EnumVariantId, ExternCrateId, FunctionId, GenericDefId, GenericParamId,
-    HasModule, ImplId, InTypeConstId, ItemContainerId, LifetimeParamId, LocalFieldId, Lookup,
-    MacroExpander, ModuleId, StaticId, StructId, SyntheticSyntax, TraitAliasId, TraitId, TupleId,
-    TypeAliasId, TypeOrConstParamId, TypeParamId, UnionId,
+    AdtId, AssocItemId, AssocItemLoc, AttrDefId, CallableDefId, ConstId, ConstParamId,
+    CrateRootModuleId, DefWithBodyId, EnumId, EnumVariantId, ExternCrateId, FunctionId,
+    GenericDefId, GenericParamId, HasModule, ImplId, InTypeConstId, ItemContainerId,
+    LifetimeParamId, LocalFieldId, Lookup, MacroExpander, MacroId, ModuleId, StaticId, StructId,
+    SyntheticSyntax, TraitAliasId, TupleId, TypeAliasId, TypeOrConstParamId, TypeParamId, UnionId,
 };
 use hir_expand::{
     attrs::collect_attrs, proc_macro::ProcMacroKind, AstId, MacroCallKind, RenderedExpandError,
@@ -83,7 +83,7 @@ use itertools::Itertools;
 use nameres::diagnostics::DefDiagnosticKind;
 use rustc_hash::FxHashSet;
 use smallvec::SmallVec;
-use span::{Edition, EditionedFileId, FileId, MacroCallId, SyntaxContextId};
+use span::{Edition, EditionedFileId, FileId, MacroCallId};
 use stdx::{format_to, impl_from, never};
 use syntax::{
     ast::{self, HasAttrs as _, HasGenericParams, HasName},
@@ -127,7 +127,7 @@ pub use {
         ImportPathConfig,
         // FIXME: This is here since some queries take it as input that are used
         // outside of hir.
-        {AdtId, MacroId, ModuleDefId},
+        {ModuleDefId, TraitId},
     },
     hir_expand::{
         attrs::{Attr, AttrId},
@@ -775,29 +775,16 @@ impl Module {
                     AssocItemId::ConstId(id) => !db.const_data(id).has_body,
                     AssocItemId::TypeAliasId(it) => db.type_alias_data(it).type_ref.is_none(),
                 });
-                impl_assoc_items_scratch.extend(db.impl_data(impl_def.id).items.iter().filter_map(
-                    |&item| {
-                        Some((
-                            item,
-                            match item {
-                                AssocItemId::FunctionId(it) => db.function_data(it).name.clone(),
-                                AssocItemId::ConstId(it) => {
-                                    db.const_data(it).name.as_ref()?.clone()
-                                }
-                                AssocItemId::TypeAliasId(it) => db.type_alias_data(it).name.clone(),
-                            },
-                        ))
-                    },
-                ));
+                impl_assoc_items_scratch.extend(db.impl_data(impl_def.id).items.iter().cloned());
 
                 let redundant = impl_assoc_items_scratch
                     .iter()
-                    .filter(|(id, name)| {
+                    .filter(|(name, id)| {
                         !items.iter().any(|(impl_name, impl_item)| {
                             discriminant(impl_item) == discriminant(id) && impl_name == name
                         })
                     })
-                    .map(|(item, name)| (name.clone(), AssocItem::from(*item)));
+                    .map(|(name, item)| (name.clone(), AssocItem::from(*item)));
                 for (name, assoc_item) in redundant {
                     acc.push(
                         TraitImplRedundantAssocItems {
@@ -812,7 +799,7 @@ impl Module {
 
                 let missing: Vec<_> = required_items
                     .filter(|(name, id)| {
-                        !impl_assoc_items_scratch.iter().any(|(impl_item, impl_name)| {
+                        !impl_assoc_items_scratch.iter().any(|(impl_name, impl_item)| {
                             discriminant(impl_item) == discriminant(id) && impl_name == name
                         })
                     })
@@ -844,7 +831,7 @@ impl Module {
                 source_map,
             );
 
-            for &item in db.impl_data(impl_def.id).items.iter() {
+            for &(_, item) in db.impl_data(impl_def.id).items.iter() {
                 AssocItem::from(item).diagnostics(db, acc, style_lints);
             }
         }
@@ -3000,6 +2987,10 @@ impl Macro {
         matches!(self.id, MacroId::MacroRulesId(id) if db.macro_rules_data(id).macro_export)
     }
 
+    pub fn is_proc_macro(self) -> bool {
+        matches!(self.id, MacroId::ProcMacroId(_))
+    }
+
     pub fn kind(&self, db: &dyn HirDatabase) -> MacroKind {
         match self.id {
             MacroId::Macro2Id(it) => match it.lookup(db.upcast()).expander {
@@ -3046,14 +3037,23 @@ impl Macro {
             MacroId::Macro2Id(it) => {
                 matches!(it.lookup(db.upcast()).expander, MacroExpander::BuiltInEager(eager) if eager.is_env_or_option_env())
             }
-            MacroId::MacroRulesId(_) | MacroId::ProcMacroId(_) => false,
+            MacroId::MacroRulesId(it) => {
+                matches!(it.lookup(db.upcast()).expander, MacroExpander::BuiltInEager(eager) if eager.is_env_or_option_env())
+            }
+            MacroId::ProcMacroId(_) => false,
         }
     }
 
     pub fn is_asm_or_global_asm(&self, db: &dyn HirDatabase) -> bool {
-        matches!(self.id, MacroId::Macro2Id(it) if {
-            matches!(it.lookup(db.upcast()).expander, MacroExpander::BuiltIn(m) if m.is_asm())
-        })
+        match self.id {
+            MacroId::Macro2Id(it) => {
+                matches!(it.lookup(db.upcast()).expander, MacroExpander::BuiltIn(m) if m.is_asm())
+            }
+            MacroId::MacroRulesId(it) => {
+                matches!(it.lookup(db.upcast()).expander, MacroExpander::BuiltIn(m) if m.is_asm())
+            }
+            MacroId::ProcMacroId(_) => false,
+        }
     }
 
     pub fn is_attr(&self, db: &dyn HirDatabase) -> bool {
@@ -3902,6 +3902,10 @@ impl ToolModule {
             db.crate_def_map(self.krate).registered_tools()[self.idx as usize].clone(),
         )
     }
+
+    pub fn krate(&self) -> Crate {
+        Crate { id: self.krate }
+    }
 }
 
 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
@@ -4290,7 +4294,7 @@ impl Impl {
     }
 
     pub fn items(self, db: &dyn HirDatabase) -> Vec<AssocItem> {
-        db.impl_data(self.id).items.iter().map(|&it| it.into()).collect()
+        db.impl_data(self.id).items.iter().map(|&(_, it)| it.into()).collect()
     }
 
     pub fn is_negative(self, db: &dyn HirDatabase) -> bool {
@@ -4731,6 +4735,14 @@ impl Type {
         Some((self.derived(ty.clone()), m))
     }
 
+    pub fn add_reference(&self, mutability: Mutability) -> Type {
+        let ty_mutability = match mutability {
+            Mutability::Shared => hir_ty::Mutability::Not,
+            Mutability::Mut => hir_ty::Mutability::Mut,
+        };
+        self.derived(TyKind::Ref(ty_mutability, error_lifetime(), self.ty.clone()).intern(Interner))
+    }
+
     pub fn is_slice(&self) -> bool {
         matches!(self.ty.kind(Interner), TyKind::Slice(..))
     }
@@ -4786,9 +4798,9 @@ impl Type {
     }
 
     /// Checks that particular type `ty` implements `std::future::IntoFuture` or
-    /// `std::future::Future`.
+    /// `std::future::Future` and returns the `Output` associated type.
     /// This function is used in `.await` syntax completion.
-    pub fn impls_into_future(&self, db: &dyn HirDatabase) -> bool {
+    pub fn into_future_output(&self, db: &dyn HirDatabase) -> Option<Type> {
         let trait_ = db
             .lang_item(self.env.krate, LangItem::IntoFutureIntoFuture)
             .and_then(|it| {
@@ -4800,16 +4812,18 @@ impl Type {
             .or_else(|| {
                 let future_trait = db.lang_item(self.env.krate, LangItem::Future)?;
                 future_trait.as_trait()
-            });
-
-        let trait_ = match trait_ {
-            Some(it) => it,
-            None => return false,
-        };
+            })?;
 
         let canonical_ty =
             Canonical { value: self.ty.clone(), binders: CanonicalVarKinds::empty(Interner) };
-        method_resolution::implements_trait(&canonical_ty, db, &self.env, trait_)
+        if !method_resolution::implements_trait_unique(&canonical_ty, db, &self.env, trait_) {
+            return None;
+        }
+
+        let output_assoc_type = db
+            .trait_data(trait_)
+            .associated_type_by_name(&Name::new_symbol_root(sym::Output.clone()))?;
+        self.normalize_trait_assoc_type(db, &[], output_assoc_type.into())
     }
 
     /// This does **not** resolve `IntoFuture`, only `Future`.
@@ -4824,10 +4838,31 @@ impl Type {
         let iterator_trait = db.lang_item(self.env.krate, LangItem::Iterator)?.as_trait()?;
         let iterator_item = db
             .trait_data(iterator_trait)
-            .associated_type_by_name(&Name::new_symbol(sym::Item.clone(), SyntaxContextId::ROOT))?;
+            .associated_type_by_name(&Name::new_symbol_root(sym::Item.clone()))?;
         self.normalize_trait_assoc_type(db, &[], iterator_item.into())
     }
 
+    /// Resolves the projection `<Self as IntoIterator>::IntoIter` and returns the resulting type
+    pub fn into_iterator_iter(self, db: &dyn HirDatabase) -> Option<Type> {
+        let trait_ = db.lang_item(self.env.krate, LangItem::IntoIterIntoIter).and_then(|it| {
+            let into_iter_fn = it.as_function()?;
+            let assoc_item = as_assoc_item(db, AssocItem::Function, into_iter_fn)?;
+            let into_iter_trait = assoc_item.container_or_implemented_trait(db)?;
+            Some(into_iter_trait.id)
+        })?;
+
+        let canonical_ty =
+            Canonical { value: self.ty.clone(), binders: CanonicalVarKinds::empty(Interner) };
+        if !method_resolution::implements_trait_unique(&canonical_ty, db, &self.env, trait_) {
+            return None;
+        }
+
+        let into_iter_assoc_type = db
+            .trait_data(trait_)
+            .associated_type_by_name(&Name::new_symbol_root(sym::IntoIter.clone()))?;
+        self.normalize_trait_assoc_type(db, &[], into_iter_assoc_type.into())
+    }
+
     /// Checks that particular type `ty` implements `std::ops::FnOnce`.
     ///
     /// This function can be used to check if a particular type is callable, since FnOnce is a
@@ -5117,7 +5152,7 @@ impl Type {
             let impls = db.inherent_impls_in_crate(krate);
 
             for impl_def in impls.for_self_ty(&self.ty) {
-                for &item in db.impl_data(*impl_def).items.iter() {
+                for &(_, item) in db.impl_data(*impl_def).items.iter() {
                     if callback(item) {
                         return;
                     }
@@ -5535,6 +5570,7 @@ impl Type {
                     walk_substs(db, type_, &opaque_ty.substitution, cb);
                 }
                 TyKind::Placeholder(_) => {
+                    cb(type_.derived(ty.clone()));
                     if let Some(bounds) = ty.impl_trait_bounds(db) {
                         walk_bounds(db, &type_.derived(ty.clone()), &bounds, cb);
                     }
diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs
index 34d169cd761..523bc6f10aa 100644
--- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs
@@ -39,8 +39,8 @@ use stdx::TupleExt;
 use syntax::{
     algo::skip_trivia_token,
     ast::{self, HasAttrs as _, HasGenericParams},
-    AstNode, AstToken, Direction, SmolStr, SyntaxKind, SyntaxNode, SyntaxNodePtr, SyntaxToken,
-    TextRange, TextSize,
+    AstNode, AstToken, Direction, SyntaxKind, SyntaxNode, SyntaxNodePtr, SyntaxToken, TextRange,
+    TextSize,
 };
 use triomphe::Arc;
 
@@ -136,8 +136,6 @@ pub struct Semantics<'db, DB> {
 pub struct SemanticsImpl<'db> {
     pub db: &'db dyn HirDatabase,
     s2d_cache: RefCell<SourceToDefCache>,
-    /// Rootnode to HirFileId cache
-    root_to_file_cache: RefCell<FxHashMap<SyntaxNode, HirFileId>>,
     /// MacroCall to its expansion's MacroFileId cache
     macro_call_cache: RefCell<FxHashMap<InFile<ast::MacroCall>, MacroFileId>>,
 }
@@ -304,12 +302,7 @@ impl<DB: HirDatabase> Semantics<'_, DB> {
 
 impl<'db> SemanticsImpl<'db> {
     fn new(db: &'db dyn HirDatabase) -> Self {
-        SemanticsImpl {
-            db,
-            s2d_cache: Default::default(),
-            root_to_file_cache: Default::default(),
-            macro_call_cache: Default::default(),
-        }
+        SemanticsImpl { db, s2d_cache: Default::default(), macro_call_cache: Default::default() }
     }
 
     pub fn parse(&self, file_id: EditionedFileId) -> ast::SourceFile {
@@ -483,7 +476,7 @@ impl<'db> SemanticsImpl<'db> {
             Some(
                 calls
                     .into_iter()
-                    .map(|call| macro_call_to_macro_id(self, ctx, call?).map(|id| Macro { id }))
+                    .map(|call| macro_call_to_macro_id(ctx, call?).map(|id| Macro { id }))
                     .collect(),
             )
         })
@@ -962,7 +955,7 @@ impl<'db> SemanticsImpl<'db> {
             let InMacroFile { file_id, value: mapped_tokens } = self.with_ctx(|ctx| {
                 Some(
                     ctx.cache
-                        .get_or_insert_expansion(self, macro_file)
+                        .get_or_insert_expansion(ctx.db, macro_file)
                         .map_range_down(span)?
                         .map(SmallVec::<[_; 2]>::from_iter),
                 )
@@ -986,7 +979,10 @@ impl<'db> SemanticsImpl<'db> {
                 process_expansion_for_token(&mut stack, include)?;
             }
             None => {
-                stack.push((file_id.into(), smallvec![(token, SyntaxContextId::ROOT)]));
+                stack.push((
+                    file_id.into(),
+                    smallvec![(token, SyntaxContextId::root(file_id.edition()))],
+                ));
             }
         }
 
@@ -1284,7 +1280,7 @@ impl<'db> SemanticsImpl<'db> {
                     let macro_file = file_id.macro_file()?;
 
                     self.with_ctx(|ctx| {
-                        let expansion_info = ctx.cache.get_or_insert_expansion(self, macro_file);
+                        let expansion_info = ctx.cache.get_or_insert_expansion(ctx.db, macro_file);
                         expansion_info.arg().map(|node| node?.parent()).transpose()
                     })
                 }
@@ -1315,8 +1311,8 @@ impl<'db> SemanticsImpl<'db> {
     }
 
     pub fn resolve_label(&self, label: &ast::Lifetime) -> Option<Label> {
-        let (parent, label_id) = self
-            .with_ctx(|ctx| ctx.label_ref_to_def(self.wrap_node_infile(label.clone()).as_ref()))?;
+        let src = self.wrap_node_infile(label.clone());
+        let (parent, label_id) = self.with_ctx(|ctx| ctx.label_ref_to_def(src.as_ref()))?;
         Some(Label { parent, label_id })
     }
 
@@ -1443,6 +1439,10 @@ impl<'db> SemanticsImpl<'db> {
         self.analyze(call.syntax())?.resolve_method_call_fallback(self.db, call)
     }
 
+    pub fn resolve_known_blanket_dual_impls(&self, call: &ast::MethodCallExpr) -> Option<Function> {
+        self.analyze(call.syntax())?.resolve_known_blanket_dual_impls(self.db, call)
+    }
+
     fn resolve_range_pat(&self, range_pat: &ast::RangePat) -> Option<StructId> {
         self.analyze(range_pat.syntax())?.resolve_range_pat(self.db, range_pat)
     }
@@ -1516,7 +1516,7 @@ impl<'db> SemanticsImpl<'db> {
         let macro_call = self.find_file(macro_call.syntax()).with_value(macro_call);
         self.with_ctx(|ctx| {
             ctx.macro_call_to_macro_call(macro_call)
-                .and_then(|call| macro_call_to_macro_id(self, ctx, call))
+                .and_then(|call| macro_call_to_macro_id(ctx, call))
                 .map(Into::into)
         })
         .or_else(|| {
@@ -1558,7 +1558,7 @@ impl<'db> SemanticsImpl<'db> {
         let item_in_file = self.wrap_node_infile(item.clone());
         let id = self.with_ctx(|ctx| {
             let macro_call_id = ctx.item_to_macro_call(item_in_file.as_ref())?;
-            macro_call_to_macro_id(self, ctx, macro_call_id)
+            macro_call_to_macro_id(ctx, macro_call_id)
         })?;
         Some(Macro { id })
     }
@@ -1591,14 +1591,11 @@ impl<'db> SemanticsImpl<'db> {
     pub fn resolve_mod_path_relative(
         &self,
         to: Module,
-        segments: impl IntoIterator<Item = SmolStr>,
+        segments: impl IntoIterator<Item = Name>,
     ) -> Option<impl Iterator<Item = ItemInNs>> {
         let items = to.id.resolver(self.db.upcast()).resolve_module_path_in_items(
             self.db.upcast(),
-            &ModPath::from_segments(
-                hir_def::path::PathKind::Plain,
-                segments.into_iter().map(|it| Name::new(&it, SyntaxContextId::ROOT)),
-            ),
+            &ModPath::from_segments(hir_def::path::PathKind::Plain, segments),
         );
         Some(items.iter_items().map(|(item, _)| item.into()))
     }
@@ -1722,10 +1719,11 @@ impl<'db> SemanticsImpl<'db> {
     }
 
     fn cache(&self, root_node: SyntaxNode, file_id: HirFileId) {
-        assert!(root_node.parent().is_none());
-        let mut cache = self.root_to_file_cache.borrow_mut();
-        let prev = cache.insert(root_node, file_id);
-        assert!(prev.is_none() || prev == Some(file_id));
+        SourceToDefCache::cache(
+            &mut self.s2d_cache.borrow_mut().root_to_file_cache,
+            root_node,
+            file_id,
+        );
     }
 
     pub fn assert_contains_node(&self, node: &SyntaxNode) {
@@ -1733,8 +1731,8 @@ impl<'db> SemanticsImpl<'db> {
     }
 
     fn lookup(&self, root_node: &SyntaxNode) -> Option<HirFileId> {
-        let cache = self.root_to_file_cache.borrow();
-        cache.get(root_node).copied()
+        let cache = self.s2d_cache.borrow();
+        cache.root_to_file_cache.get(root_node).copied()
     }
 
     fn wrap_node_infile<N: AstNode>(&self, node: N) -> InFile<N> {
@@ -1753,13 +1751,14 @@ impl<'db> SemanticsImpl<'db> {
         let file_id = self.lookup(&root_node).unwrap_or_else(|| {
             panic!(
                 "\n\nFailed to lookup {:?} in this Semantics.\n\
-                 Make sure to use only query nodes, derived from this instance of Semantics.\n\
+                 Make sure to only query nodes derived from this instance of Semantics.\n\
                  root node:   {:?}\n\
                  known nodes: {}\n\n",
                 node,
                 root_node,
-                self.root_to_file_cache
+                self.s2d_cache
                     .borrow()
+                    .root_to_file_cache
                     .keys()
                     .map(|it| format!("{it:?}"))
                     .collect::<Vec<_>>()
@@ -1906,7 +1905,6 @@ impl<'db> SemanticsImpl<'db> {
 }
 
 fn macro_call_to_macro_id(
-    sema: &SemanticsImpl<'_>,
     ctx: &mut SourceToDefCtx<'_, '_>,
     macro_call_id: MacroCallId,
 ) -> Option<MacroId> {
@@ -1922,7 +1920,7 @@ fn macro_call_to_macro_id(
                     it.to_ptr(db).to_node(&db.parse(file_id).syntax_node())
                 }
                 HirFileIdRepr::MacroFile(macro_file) => {
-                    let expansion_info = ctx.cache.get_or_insert_expansion(sema, macro_file);
+                    let expansion_info = ctx.cache.get_or_insert_expansion(ctx.db, macro_file);
                     it.to_ptr(db).to_node(&expansion_info.expanded().value)
                 }
             };
@@ -1934,7 +1932,7 @@ fn macro_call_to_macro_id(
                     it.to_ptr(db).to_node(&db.parse(file_id).syntax_node())
                 }
                 HirFileIdRepr::MacroFile(macro_file) => {
-                    let expansion_info = ctx.cache.get_or_insert_expansion(sema, macro_file);
+                    let expansion_info = ctx.cache.get_or_insert_expansion(ctx.db, macro_file);
                     it.to_ptr(db).to_node(&expansion_info.expanded().value)
                 }
             };
diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics/child_by_source.rs b/src/tools/rust-analyzer/crates/hir/src/semantics/child_by_source.rs
index ec65ea9a9a8..d5dfb985718 100644
--- a/src/tools/rust-analyzer/crates/hir/src/semantics/child_by_source.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/semantics/child_by_source.rs
@@ -56,7 +56,7 @@ impl ChildBySource for ImplId {
                 res[keys::ATTR_MACRO_CALL].insert(ast_id.to_ptr(db.upcast()), call_id);
             },
         );
-        data.items.iter().for_each(|&item| {
+        data.items.iter().for_each(|&(_, item)| {
             add_assoc_item(db, res, file_id, item);
         });
     }
diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs b/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs
index b5cc440fc22..3c9e7065c41 100644
--- a/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs
@@ -110,10 +110,7 @@ use syntax::{
     AstNode, AstPtr, SyntaxNode,
 };
 
-use crate::{
-    db::HirDatabase, semantics::child_by_source::ChildBySource, InFile, InlineAsmOperand,
-    SemanticsImpl,
-};
+use crate::{db::HirDatabase, semantics::child_by_source::ChildBySource, InFile, InlineAsmOperand};
 
 #[derive(Default)]
 pub(super) struct SourceToDefCache {
@@ -121,9 +118,21 @@ pub(super) struct SourceToDefCache {
     expansion_info_cache: FxHashMap<MacroFileId, ExpansionInfo>,
     pub(super) file_to_def_cache: FxHashMap<FileId, SmallVec<[ModuleId; 1]>>,
     pub(super) included_file_cache: FxHashMap<EditionedFileId, Option<MacroFileId>>,
+    /// Rootnode to HirFileId cache
+    pub(super) root_to_file_cache: FxHashMap<SyntaxNode, HirFileId>,
 }
 
 impl SourceToDefCache {
+    pub(super) fn cache(
+        root_to_file_cache: &mut FxHashMap<SyntaxNode, HirFileId>,
+        root_node: SyntaxNode,
+        file_id: HirFileId,
+    ) {
+        assert!(root_node.parent().is_none());
+        let prev = root_to_file_cache.insert(root_node, file_id);
+        assert!(prev.is_none() || prev == Some(file_id));
+    }
+
     pub(super) fn get_or_insert_include_for(
         &mut self,
         db: &dyn HirDatabase,
@@ -143,14 +152,14 @@ impl SourceToDefCache {
 
     pub(super) fn get_or_insert_expansion(
         &mut self,
-        sema: &SemanticsImpl<'_>,
+        db: &dyn HirDatabase,
         macro_file: MacroFileId,
     ) -> &ExpansionInfo {
         self.expansion_info_cache.entry(macro_file).or_insert_with(|| {
-            let exp_info = macro_file.expansion_info(sema.db.upcast());
+            let exp_info = macro_file.expansion_info(db.upcast());
 
             let InMacroFile { file_id, value } = exp_info.expanded();
-            sema.cache(value, file_id.into());
+            Self::cache(&mut self.root_to_file_cache, value, file_id.into());
 
             exp_info
         })
@@ -520,18 +529,11 @@ impl SourceToDefCtx<'_, '_> {
         node: InFile<&SyntaxNode>,
         mut cb: impl FnMut(&mut Self, InFile<SyntaxNode>) -> Option<T>,
     ) -> Option<T> {
-        use hir_expand::MacroFileIdExt;
         let parent = |this: &mut Self, node: InFile<&SyntaxNode>| match node.value.parent() {
             Some(parent) => Some(node.with_value(parent)),
             None => {
                 let macro_file = node.file_id.macro_file()?;
-
-                let expansion_info = this
-                    .cache
-                    .expansion_info_cache
-                    .entry(macro_file)
-                    .or_insert_with(|| macro_file.expansion_info(this.db.upcast()));
-
+                let expansion_info = this.cache.get_or_insert_expansion(this.db, macro_file);
                 expansion_info.arg().map(|node| node?.parent()).transpose()
             }
         };
diff --git a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs
index b699ccde412..6b78d7a3631 100644
--- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs
@@ -322,6 +322,68 @@ impl SourceAnalyzer {
         }
     }
 
+    // If the method is into(), try_into(), parse(), resolve it to from, try_from, from_str.
+    pub(crate) fn resolve_known_blanket_dual_impls(
+        &self,
+        db: &dyn HirDatabase,
+        call: &ast::MethodCallExpr,
+    ) -> Option<Function> {
+        // e.g. if the method call is let b = a.into(),
+        // - receiver_type is A (type of a)
+        // - return_type is B (type of b)
+        // We will find the definition of B::from(a: A).
+        let callable = self.resolve_method_call_as_callable(db, call)?;
+        let (_, receiver_type) = callable.receiver_param(db)?;
+        let return_type = callable.return_type();
+        let (search_method, substs) = match call.name_ref()?.text().as_str() {
+            "into" => {
+                let trait_ =
+                    self.resolver.resolve_known_trait(db.upcast(), &path![core::convert::From])?;
+                (
+                    self.trait_fn(db, trait_, "from")?,
+                    hir_ty::TyBuilder::subst_for_def(db, trait_, None)
+                        .push(return_type.ty)
+                        .push(receiver_type.ty)
+                        .build(),
+                )
+            }
+            "try_into" => {
+                let trait_ = self
+                    .resolver
+                    .resolve_known_trait(db.upcast(), &path![core::convert::TryFrom])?;
+                (
+                    self.trait_fn(db, trait_, "try_from")?,
+                    hir_ty::TyBuilder::subst_for_def(db, trait_, None)
+                        // If the method is try_into() or parse(), return_type is Result<T, Error>.
+                        // Get T from type arguments of Result<T, Error>.
+                        .push(return_type.type_arguments().next()?.ty)
+                        .push(receiver_type.ty)
+                        .build(),
+                )
+            }
+            "parse" => {
+                let trait_ =
+                    self.resolver.resolve_known_trait(db.upcast(), &path![core::str::FromStr])?;
+                (
+                    self.trait_fn(db, trait_, "from_str")?,
+                    hir_ty::TyBuilder::subst_for_def(db, trait_, None)
+                        .push(return_type.type_arguments().next()?.ty)
+                        .build(),
+                )
+            }
+            _ => return None,
+        };
+
+        let found_method = self.resolve_impl_method_or_trait_def(db, search_method, substs);
+        // If found_method == search_method, the method in trait itself is resolved.
+        // It means the blanket dual impl is not found.
+        if found_method == search_method {
+            None
+        } else {
+            Some(found_method.into())
+        }
+    }
+
     pub(crate) fn resolve_expr_as_callable(
         &self,
         db: &dyn HirDatabase,
@@ -1247,6 +1309,18 @@ impl SourceAnalyzer {
         Some((trait_id, fn_id))
     }
 
+    fn trait_fn(
+        &self,
+        db: &dyn HirDatabase,
+        trait_id: TraitId,
+        method_name: &str,
+    ) -> Option<FunctionId> {
+        db.trait_data(trait_id).items.iter().find_map(|(item_name, item)| match item {
+            AssocItemId::FunctionId(t) if item_name.as_str() == method_name => Some(*t),
+            _ => None,
+        })
+    }
+
     fn ty_of_expr(&self, db: &dyn HirDatabase, expr: &ast::Expr) -> Option<&Ty> {
         self.infer.as_ref()?.type_of_expr_or_pat(self.expr_id(db, expr)?)
     }
diff --git a/src/tools/rust-analyzer/crates/hir/src/symbols.rs b/src/tools/rust-analyzer/crates/hir/src/symbols.rs
index f8416f86bf9..a6b8ed70c36 100644
--- a/src/tools/rust-analyzer/crates/hir/src/symbols.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/symbols.rs
@@ -1,27 +1,34 @@
 //! File symbol extraction.
 
+use either::Either;
 use hir_def::{
     db::DefDatabase,
-    item_scope::ItemInNs,
+    item_scope::{ImportId, ImportOrExternCrate},
+    per_ns::Item,
     src::{HasChildSource, HasSource},
-    AdtId, AssocItemId, DefWithBodyId, HasModule, ImplId, Lookup, MacroId, ModuleDefId, ModuleId,
-    TraitId,
+    visibility::{Visibility, VisibilityExplicitness},
+    AdtId, AssocItemId, DefWithBodyId, ExternCrateId, HasModule, ImplId, Lookup, MacroId,
+    ModuleDefId, ModuleId, TraitId,
 };
-use hir_expand::HirFileId;
+use hir_expand::{name::Name, HirFileId};
 use hir_ty::{
     db::HirDatabase,
     display::{hir_display_with_types_map, 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};
 
+pub type FxIndexSet<T> = indexmap::IndexSet<T, std::hash::BuildHasherDefault<rustc_hash::FxHasher>>;
+
 /// The actual data that is stored in the index. It should be as compact as
 /// possible.
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub struct FileSymbol {
-    pub name: SmolStr,
+    pub name: Symbol,
     pub def: ModuleDef,
     pub loc: DeclarationLocation,
     pub container_name: Option<SmolStr>,
@@ -37,7 +44,7 @@ pub struct DeclarationLocation {
     /// This points to the whole syntax node of the declaration.
     pub ptr: SyntaxNodePtr,
     /// This points to the [`syntax::ast::Name`] identifier of the declaration.
-    pub name_ptr: AstPtr<syntax::ast::Name>,
+    pub name_ptr: AstPtr<Either<syntax::ast::Name, syntax::ast::NameRef>>,
 }
 
 impl DeclarationLocation {
@@ -55,7 +62,7 @@ struct SymbolCollectorWork {
 
 pub struct SymbolCollector<'a> {
     db: &'a dyn HirDatabase,
-    symbols: Vec<FileSymbol>,
+    symbols: FxIndexSet<FileSymbol>,
     work: Vec<SymbolCollectorWork>,
     current_container_name: Option<SmolStr>,
     edition: Edition,
@@ -86,11 +93,11 @@ impl<'a> SymbolCollector<'a> {
         }
     }
 
-    pub fn finish(self) -> Vec<FileSymbol> {
-        self.symbols
+    pub fn finish(self) -> Box<[FileSymbol]> {
+        self.symbols.into_iter().collect()
     }
 
-    pub fn collect_module(db: &dyn HirDatabase, module: Module) -> Vec<FileSymbol> {
+    pub fn collect_module(db: &dyn HirDatabase, module: Module) -> Box<[FileSymbol]> {
         let mut symbol_collector = SymbolCollector::new(db);
         symbol_collector.collect(module);
         symbol_collector.finish()
@@ -104,96 +111,174 @@ impl<'a> SymbolCollector<'a> {
     }
 
     fn collect_from_module(&mut self, module_id: ModuleId) {
-        let def_map = module_id.def_map(self.db.upcast());
-        let scope = &def_map[module_id.local_id].scope;
-
-        for module_def_id in scope.declarations() {
-            match module_def_id {
-                ModuleDefId::ModuleId(id) => self.push_module(id),
+        let push_decl = |this: &mut Self, def, name| {
+            match def {
+                ModuleDefId::ModuleId(id) => this.push_module(id, name),
                 ModuleDefId::FunctionId(id) => {
-                    self.push_decl(id, false);
-                    self.collect_from_body(id);
+                    this.push_decl(id, name, false);
+                    this.collect_from_body(id);
                 }
-                ModuleDefId::AdtId(AdtId::StructId(id)) => self.push_decl(id, false),
-                ModuleDefId::AdtId(AdtId::EnumId(id)) => self.push_decl(id, false),
-                ModuleDefId::AdtId(AdtId::UnionId(id)) => self.push_decl(id, false),
+                ModuleDefId::AdtId(AdtId::StructId(id)) => this.push_decl(id, name, false),
+                ModuleDefId::AdtId(AdtId::EnumId(id)) => this.push_decl(id, name, false),
+                ModuleDefId::AdtId(AdtId::UnionId(id)) => this.push_decl(id, name, false),
                 ModuleDefId::ConstId(id) => {
-                    self.push_decl(id, false);
-                    self.collect_from_body(id);
+                    this.push_decl(id, name, false);
+                    this.collect_from_body(id);
                 }
                 ModuleDefId::StaticId(id) => {
-                    self.push_decl(id, false);
-                    self.collect_from_body(id);
+                    this.push_decl(id, name, false);
+                    this.collect_from_body(id);
                 }
                 ModuleDefId::TraitId(id) => {
-                    self.push_decl(id, false);
-                    self.collect_from_trait(id);
+                    this.push_decl(id, name, false);
+                    this.collect_from_trait(id);
                 }
                 ModuleDefId::TraitAliasId(id) => {
-                    self.push_decl(id, false);
+                    this.push_decl(id, name, false);
                 }
                 ModuleDefId::TypeAliasId(id) => {
-                    self.push_decl(id, false);
+                    this.push_decl(id, name, false);
                 }
                 ModuleDefId::MacroId(id) => match id {
-                    MacroId::Macro2Id(id) => self.push_decl(id, false),
-                    MacroId::MacroRulesId(id) => self.push_decl(id, false),
-                    MacroId::ProcMacroId(id) => self.push_decl(id, false),
+                    MacroId::Macro2Id(id) => this.push_decl(id, name, false),
+                    MacroId::MacroRulesId(id) => this.push_decl(id, name, false),
+                    MacroId::ProcMacroId(id) => this.push_decl(id, name, false),
                 },
                 // Don't index these.
                 ModuleDefId::BuiltinType(_) => {}
                 ModuleDefId::EnumVariantId(_) => {}
             }
-        }
+        };
 
-        for impl_id in scope.impls() {
-            self.collect_from_impl(impl_id);
-        }
+        // Nested trees are very common, so a cache here will hit a lot.
+        let import_child_source_cache = &mut FxHashMap::default();
+
+        let mut push_import = |this: &mut Self, i: ImportId, name: &Name, def: ModuleDefId| {
+            let source = import_child_source_cache
+                .entry(i.import)
+                .or_insert_with(|| i.import.child_source(this.db.upcast()));
+            let Some(use_tree_src) = source.value.get(i.idx) else { return };
+            let Some(name_ptr) = use_tree_src
+                .rename()
+                .and_then(|rename| rename.name())
+                .map(Either::Left)
+                .or_else(|| use_tree_src.path()?.segment()?.name_ref().map(Either::Right))
+                .map(|it| AstPtr::new(&it))
+            else {
+                return;
+            };
+            let dec_loc = DeclarationLocation {
+                hir_file_id: source.file_id,
+                ptr: SyntaxNodePtr::new(use_tree_src.syntax()),
+                name_ptr,
+            };
+            this.symbols.insert(FileSymbol {
+                name: name.symbol().clone(),
+                def: def.into(),
+                container_name: this.current_container_name.clone(),
+                loc: dec_loc,
+                is_alias: false,
+                is_assoc: false,
+            });
+        };
 
-        // Record renamed imports.
-        // FIXME: In case it imports multiple items under different namespaces we just pick one arbitrarily
-        // for now.
-        for id in scope.imports() {
-            let source = id.import.child_source(self.db.upcast());
-            let Some(use_tree_src) = source.value.get(id.idx) else { continue };
-            let Some(rename) = use_tree_src.rename() else { continue };
-            let Some(name) = rename.name() else { continue };
-
-            let res = scope.fully_resolve_import(self.db.upcast(), id);
-            res.iter_items().for_each(|(item, _)| {
-                let def = match item {
-                    ItemInNs::Types(def) | ItemInNs::Values(def) => def,
-                    ItemInNs::Macros(def) => ModuleDefId::from(def),
-                }
-                .into();
+        let push_extern_crate =
+            |this: &mut Self, i: ExternCrateId, name: &Name, def: ModuleDefId| {
+                let loc = i.lookup(this.db.upcast());
+                let source = loc.source(this.db.upcast());
+                let Some(name_ptr) = source
+                    .value
+                    .rename()
+                    .and_then(|rename| rename.name())
+                    .map(Either::Left)
+                    .or_else(|| source.value.name_ref().map(Either::Right))
+                    .map(|it| AstPtr::new(&it))
+                else {
+                    return;
+                };
                 let dec_loc = DeclarationLocation {
                     hir_file_id: source.file_id,
-                    ptr: SyntaxNodePtr::new(use_tree_src.syntax()),
-                    name_ptr: AstPtr::new(&name),
+                    ptr: SyntaxNodePtr::new(source.value.syntax()),
+                    name_ptr,
                 };
-
-                self.symbols.push(FileSymbol {
-                    name: name.text().into(),
-                    def,
-                    container_name: self.current_container_name.clone(),
+                this.symbols.insert(FileSymbol {
+                    name: name.symbol().clone(),
+                    def: def.into(),
+                    container_name: this.current_container_name.clone(),
                     loc: dec_loc,
                     is_alias: false,
                     is_assoc: false,
                 });
-            });
+            };
+
+        let is_explicit_import = |vis| {
+            match vis {
+                Visibility::Module(_, VisibilityExplicitness::Explicit) => true,
+                Visibility::Module(_, VisibilityExplicitness::Implicit) => {
+                    // consider imports in the crate root explicit, as these are visibly
+                    // crate-wide anyways
+                    module_id.is_crate_root()
+                }
+                Visibility::Public => true,
+            }
+        };
+
+        let def_map = module_id.def_map(self.db.upcast());
+        let scope = &def_map[module_id.local_id].scope;
+
+        for impl_id in scope.impls() {
+            self.collect_from_impl(impl_id);
+        }
+
+        for (name, Item { def, vis, import }) in scope.types() {
+            if let Some(i) = import {
+                if is_explicit_import(vis) {
+                    match i {
+                        ImportOrExternCrate::Import(i) => push_import(self, i, name, def),
+                        ImportOrExternCrate::ExternCrate(i) => {
+                            push_extern_crate(self, i, name, def)
+                        }
+                    }
+                }
+                continue;
+            }
+            // self is a declaration
+            push_decl(self, def, name)
+        }
+
+        for (name, Item { def, vis, import }) in scope.macros() {
+            if let Some(i) = import {
+                if is_explicit_import(vis) {
+                    push_import(self, i, name, def.into());
+                }
+                continue;
+            }
+            // self is a declaration
+            push_decl(self, def.into(), name)
+        }
+
+        for (name, Item { def, vis, import }) in scope.values() {
+            if let Some(i) = import {
+                if is_explicit_import(vis) {
+                    push_import(self, i, name, def);
+                }
+                continue;
+            }
+            // self is a declaration
+            push_decl(self, def, name)
         }
 
         for const_id in scope.unnamed_consts() {
             self.collect_from_body(const_id);
         }
 
-        for (_, id) in scope.legacy_macros() {
+        for (name, id) in scope.legacy_macros() {
             for &id in id {
                 if id.module(self.db.upcast()) == module_id {
                     match id {
-                        MacroId::Macro2Id(id) => self.push_decl(id, false),
-                        MacroId::MacroRulesId(id) => self.push_decl(id, false),
-                        MacroId::ProcMacroId(id) => self.push_decl(id, false),
+                        MacroId::Macro2Id(id) => self.push_decl(id, name, false),
+                        MacroId::MacroRulesId(id) => self.push_decl(id, name, false),
+                        MacroId::ProcMacroId(id) => self.push_decl(id, name, false),
                     }
                 }
             }
@@ -223,8 +308,8 @@ impl<'a> SymbolCollector<'a> {
                 .to_smolstr(),
         );
         self.with_container_name(impl_name, |s| {
-            for &assoc_item_id in impl_data.items.iter() {
-                s.push_assoc_item(assoc_item_id)
+            for &(ref name, assoc_item_id) in &impl_data.items {
+                s.push_assoc_item(assoc_item_id, name)
             }
         })
     }
@@ -232,8 +317,8 @@ impl<'a> SymbolCollector<'a> {
     fn collect_from_trait(&mut self, trait_id: TraitId) {
         let trait_data = self.db.trait_data(trait_id);
         self.with_container_name(Some(trait_data.name.as_str().into()), |s| {
-            for &(_, assoc_item_id) in &trait_data.items {
-                s.push_assoc_item(assoc_item_id);
+            for &(ref name, assoc_item_id) in &trait_data.items {
+                s.push_assoc_item(assoc_item_id, name);
             }
         });
     }
@@ -266,15 +351,15 @@ impl<'a> SymbolCollector<'a> {
         }
     }
 
-    fn push_assoc_item(&mut self, assoc_item_id: AssocItemId) {
+    fn push_assoc_item(&mut self, assoc_item_id: AssocItemId, name: &Name) {
         match assoc_item_id {
-            AssocItemId::FunctionId(id) => self.push_decl(id, true),
-            AssocItemId::ConstId(id) => self.push_decl(id, true),
-            AssocItemId::TypeAliasId(id) => self.push_decl(id, true),
+            AssocItemId::FunctionId(id) => self.push_decl(id, name, true),
+            AssocItemId::ConstId(id) => self.push_decl(id, name, true),
+            AssocItemId::TypeAliasId(id) => self.push_decl(id, name, true),
         }
     }
 
-    fn push_decl<'db, L>(&mut self, id: L, is_assoc: bool)
+    fn push_decl<'db, L>(&mut self, id: L, name: &Name, is_assoc: bool)
     where
         L: Lookup<Database<'db> = dyn DefDatabase + 'db> + Into<ModuleDefId>,
         <L as Lookup>::Data: HasSource,
@@ -287,13 +372,13 @@ impl<'a> SymbolCollector<'a> {
         let dec_loc = DeclarationLocation {
             hir_file_id: source.file_id,
             ptr: SyntaxNodePtr::new(source.value.syntax()),
-            name_ptr: AstPtr::new(&name_node),
+            name_ptr: AstPtr::new(&name_node).wrap_left(),
         };
 
         if let Some(attrs) = def.attrs(self.db) {
             for alias in attrs.doc_aliases() {
-                self.symbols.push(FileSymbol {
-                    name: alias.as_str().into(),
+                self.symbols.insert(FileSymbol {
+                    name: alias.clone(),
                     def,
                     loc: dec_loc.clone(),
                     container_name: self.current_container_name.clone(),
@@ -303,8 +388,8 @@ impl<'a> SymbolCollector<'a> {
             }
         }
 
-        self.symbols.push(FileSymbol {
-            name: name_node.text().into(),
+        self.symbols.insert(FileSymbol {
+            name: name.symbol().clone(),
             def,
             container_name: self.current_container_name.clone(),
             loc: dec_loc,
@@ -313,7 +398,7 @@ impl<'a> SymbolCollector<'a> {
         });
     }
 
-    fn push_module(&mut self, module_id: ModuleId) {
+    fn push_module(&mut self, module_id: ModuleId, name: &Name) {
         let def_map = module_id.def_map(self.db.upcast());
         let module_data = &def_map[module_id.local_id];
         let Some(declaration) = module_data.origin.declaration() else { return };
@@ -322,15 +407,15 @@ impl<'a> SymbolCollector<'a> {
         let dec_loc = DeclarationLocation {
             hir_file_id: declaration.file_id,
             ptr: SyntaxNodePtr::new(module.syntax()),
-            name_ptr: AstPtr::new(&name_node),
+            name_ptr: AstPtr::new(&name_node).wrap_left(),
         };
 
         let def = ModuleDef::Module(module_id.into());
 
         if let Some(attrs) = def.attrs(self.db) {
             for alias in attrs.doc_aliases() {
-                self.symbols.push(FileSymbol {
-                    name: alias.as_str().into(),
+                self.symbols.insert(FileSymbol {
+                    name: alias.clone(),
                     def,
                     loc: dec_loc.clone(),
                     container_name: self.current_container_name.clone(),
@@ -340,8 +425,8 @@ impl<'a> SymbolCollector<'a> {
             }
         }
 
-        self.symbols.push(FileSymbol {
-            name: name_node.text().into(),
+        self.symbols.insert(FileSymbol {
+            name: name.symbol().clone(),
             def: ModuleDef::Module(module_id.into()),
             container_name: self.current_container_name.clone(),
             loc: dec_loc,
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/assist_context.rs b/src/tools/rust-analyzer/crates/ide-assists/src/assist_context.rs
index 074d943719f..64e77b2d698 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/assist_context.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/assist_context.rs
@@ -109,6 +109,10 @@ impl<'a> AssistContext<'a> {
         self.trimmed_range
     }
 
+    pub(crate) fn source_file(&self) -> &SourceFile {
+        &self.source_file
+    }
+
     pub(crate) fn token_at_offset(&self) -> TokenAtOffset<SyntaxToken> {
         self.source_file.syntax().token_at_offset(self.offset())
     }
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs
index 24b34f140bd..5899ec5a005 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs
@@ -212,8 +212,7 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>)
                     !hidden
                 })
                 .map(|(pat, _)| {
-                    make::match_arm(iter::once(pat), None, make::ext::expr_todo())
-                        .clone_for_update()
+                    make::match_arm(pat, None, make::ext::expr_todo()).clone_for_update()
                 });
 
             let catch_all_arm = new_match_arm_list
@@ -243,12 +242,9 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>)
 
             if needs_catch_all_arm && !has_catch_all_arm {
                 cov_mark::hit!(added_wildcard_pattern);
-                let arm = make::match_arm(
-                    iter::once(make::wildcard_pat().into()),
-                    None,
-                    make::ext::expr_todo(),
-                )
-                .clone_for_update();
+                let arm =
+                    make::match_arm(make::wildcard_pat().into(), None, make::ext::expr_todo())
+                        .clone_for_update();
                 todo_placeholders.push(arm.expr().unwrap());
                 added_arms.push(arm);
             }
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_turbo_fish.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_turbo_fish.rs
index 62700ab1809..04d63f5bc8f 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_turbo_fish.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_turbo_fish.rs
@@ -189,7 +189,7 @@ pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
 /// This will create a turbofish generic arg list corresponding to the number of arguments
 fn get_fish_head(make: &SyntaxFactory, number_of_arguments: usize) -> ast::GenericArgList {
     let args = (0..number_of_arguments).map(|_| make::type_arg(make::ty_placeholder()).into());
-    make.turbofish_generic_arg_list(args)
+    make.generic_arg_list(args, true)
 }
 
 #[cfg(test)]
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 f178a7e0cec..70fb5680052 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
@@ -252,7 +252,7 @@ fn tail_cb_impl(edit: &mut SourceChangeBuilder, e: &ast::Expr) {
 
 /// Add bang and parentheses to the expression.
 fn add_bang_paren(expr: ast::Expr) -> ast::Expr {
-    make::expr_prefix(T![!], make::expr_paren(expr))
+    make::expr_prefix(T![!], make::expr_paren(expr)).into()
 }
 
 #[cfg(test)]
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/bool_to_enum.rs
index f699899066b..cbd39796241 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/bool_to_enum.rs
@@ -195,6 +195,7 @@ fn bool_expr_to_enum_expr(expr: ast::Expr) -> ast::Expr {
             make::tail_only_block_expr(true_expr),
             Some(ast::ElseBranch::Block(make::tail_only_block_expr(false_expr))),
         )
+        .into()
     }
 }
 
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_closure_to_fn.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_closure_to_fn.rs
index 79f303b37a4..bb04a43cf96 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_closure_to_fn.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_closure_to_fn.rs
@@ -507,7 +507,7 @@ fn wrap_capture_in_deref_if_needed(
     if does_autoderef {
         return capture_name;
     }
-    make::expr_prefix(T![*], capture_name)
+    make::expr_prefix(T![*], capture_name).into()
 }
 
 fn capture_as_arg(ctx: &AssistContext<'_>, capture: &ClosureCapture) -> ast::Expr {
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_from_to_tryfrom.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_from_to_tryfrom.rs
index 67c72a93dad..dd2e9cbcb5f 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_from_to_tryfrom.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_from_to_tryfrom.rs
@@ -97,7 +97,7 @@ pub(crate) fn convert_from_to_tryfrom(acc: &mut Assists, ctx: &AssistContext<'_>
             );
 
             for r in return_exprs {
-                let t = r.expr().unwrap_or_else(make::expr_unit);
+                let t = r.expr().unwrap_or_else(make::ext::expr_unit);
                 ted::replace(t.syntax(), wrap_ok(t.clone()).syntax().clone_for_update());
             }
 
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_while_to_loop.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_while_to_loop.rs
index 434daa279ca..0b92beefbcd 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_while_to_loop.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_while_to_loop.rs
@@ -60,7 +60,7 @@ pub(crate) fn convert_while_to_loop(acc: &mut Assists, ctx: &AssistContext<'_>)
             .indent(while_indent_level);
             let block_expr = if is_pattern_cond(while_cond.clone()) {
                 let if_expr = make::expr_if(while_cond, while_body, Some(break_block.into()));
-                let stmts = iter::once(make::expr_stmt(if_expr).into());
+                let stmts = iter::once(make::expr_stmt(if_expr.into()).into());
                 make::block_expr(stmts, None)
             } else {
                 let if_cond = invert_boolean_expression(while_cond);
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_tuple_binding.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_tuple_binding.rs
index 7df6ca1565f..39142d60620 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_tuple_binding.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_tuple_binding.rs
@@ -1128,7 +1128,10 @@ fn main {
             destructure_tuple_binding_impl(acc, ctx, false)
         }
 
-        pub(crate) fn check_in_place_assist(ra_fixture_before: &str, ra_fixture_after: &str) {
+        pub(crate) fn check_in_place_assist(
+            #[rust_analyzer::rust_fixture] ra_fixture_before: &str,
+            #[rust_analyzer::rust_fixture] ra_fixture_after: &str,
+        ) {
             check_assist_by_label(
                 in_place_assist,
                 ra_fixture_before,
@@ -1138,7 +1141,10 @@ fn main {
             );
         }
 
-        pub(crate) fn check_sub_pattern_assist(ra_fixture_before: &str, ra_fixture_after: &str) {
+        pub(crate) fn check_sub_pattern_assist(
+            #[rust_analyzer::rust_fixture] ra_fixture_before: &str,
+            #[rust_analyzer::rust_fixture] ra_fixture_after: &str,
+        ) {
             check_assist_by_label(
                 assist,
                 ra_fixture_before,
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 0d1b6af7204..967da41c15f 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
@@ -1533,7 +1533,7 @@ impl FlowHandler {
                     .into(),
                     call_expr,
                 );
-                make::expr_if(condition.into(), block, None)
+                make::expr_if(condition.into(), block, None).into()
             }
             FlowHandler::IfOption { action } => {
                 let path = make::ext::ident_path("Some");
@@ -1544,7 +1544,7 @@ impl FlowHandler {
                 let action_expr = action.make_result_handler(Some(value));
                 let action_stmt = make::expr_stmt(action_expr);
                 let then = make::block_expr(iter::once(action_stmt.into()), None);
-                make::expr_if(cond.into(), then, None)
+                make::expr_if(cond.into(), then, None).into()
             }
             FlowHandler::MatchOption { none } => {
                 let some_name = "value";
@@ -1554,15 +1554,15 @@ impl FlowHandler {
                     let value_pat = make::ext::simple_ident_pat(make::name(some_name));
                     let pat = make::tuple_struct_pat(path, iter::once(value_pat.into()));
                     let value = make::expr_path(make::ext::ident_path(some_name));
-                    make::match_arm(iter::once(pat.into()), None, value)
+                    make::match_arm(pat.into(), None, value)
                 };
                 let none_arm = {
                     let path = make::ext::ident_path("None");
                     let pat = make::path_pat(path);
-                    make::match_arm(iter::once(pat), None, none.make_result_handler(None))
+                    make::match_arm(pat, None, none.make_result_handler(None))
                 };
                 let arms = make::match_arm_list(vec![some_arm, none_arm]);
-                make::expr_match(call_expr, arms)
+                make::expr_match(call_expr, arms).into()
             }
             FlowHandler::MatchResult { err } => {
                 let ok_name = "value";
@@ -1573,21 +1573,17 @@ impl FlowHandler {
                     let value_pat = make::ext::simple_ident_pat(make::name(ok_name));
                     let pat = make::tuple_struct_pat(path, iter::once(value_pat.into()));
                     let value = make::expr_path(make::ext::ident_path(ok_name));
-                    make::match_arm(iter::once(pat.into()), None, value)
+                    make::match_arm(pat.into(), None, value)
                 };
                 let err_arm = {
                     let path = make::ext::ident_path("Err");
                     let value_pat = make::ext::simple_ident_pat(make::name(err_name));
                     let pat = make::tuple_struct_pat(path, iter::once(value_pat.into()));
                     let value = make::expr_path(make::ext::ident_path(err_name));
-                    make::match_arm(
-                        iter::once(pat.into()),
-                        None,
-                        err.make_result_handler(Some(value)),
-                    )
+                    make::match_arm(pat.into(), None, err.make_result_handler(Some(value)))
                 };
                 let arms = make::match_arm_list(vec![ok_arm, err_arm]);
-                make::expr_match(call_expr, arms)
+                make::expr_match(call_expr, arms).into()
             }
         }
     }
@@ -1879,7 +1875,7 @@ fn make_body(ctx: &AssistContext<'_>, old_indent: IndentLevel, fun: &Function) -
                             .iter()
                             .map(|var| path_expr_from_local(ctx, var.local, fun.mods.edition));
                         let expr = make::expr_tuple(exprs);
-                        tail_expr = Some(expr);
+                        tail_expr = Some(expr.into());
                     }
                 },
             };
@@ -1910,7 +1906,7 @@ fn make_body(ctx: &AssistContext<'_>, old_indent: IndentLevel, fun: &Function) -
     match &handler {
         FlowHandler::None => block,
         FlowHandler::Try { kind } => {
-            let block = with_default_tail_expr(block, make::expr_unit());
+            let block = with_default_tail_expr(block, make::ext::expr_unit());
             map_tail_expr(block, |tail_expr| {
                 let constructor = match kind {
                     TryKind::Option => "Some",
@@ -1924,7 +1920,7 @@ fn make_body(ctx: &AssistContext<'_>, old_indent: IndentLevel, fun: &Function) -
         FlowHandler::If { .. } => {
             let controlflow_continue = make::expr_call(
                 make::expr_path(make::path_from_text("ControlFlow::Continue")),
-                make::arg_list(iter::once(make::expr_unit())),
+                make::arg_list([make::ext::expr_unit()]),
             );
             with_tail_expr(block, controlflow_continue)
         }
@@ -2127,17 +2123,17 @@ fn make_rewritten_flow(handler: &FlowHandler, arg_expr: Option<ast::Expr>) -> Op
         FlowHandler::None | FlowHandler::Try { .. } => return None,
         FlowHandler::If { .. } => make::expr_call(
             make::expr_path(make::path_from_text("ControlFlow::Break")),
-            make::arg_list(iter::once(make::expr_unit())),
+            make::arg_list([make::ext::expr_unit()]),
         ),
         FlowHandler::IfOption { .. } => {
-            let expr = arg_expr.unwrap_or_else(|| make::expr_tuple(Vec::new()));
-            let args = make::arg_list(iter::once(expr));
+            let expr = arg_expr.unwrap_or_else(make::ext::expr_unit);
+            let args = make::arg_list([expr]);
             make::expr_call(make::expr_path(make::ext::ident_path("Some")), args)
         }
         FlowHandler::MatchOption { .. } => make::expr_path(make::ext::ident_path("None")),
         FlowHandler::MatchResult { .. } => {
-            let expr = arg_expr.unwrap_or_else(|| make::expr_tuple(Vec::new()));
-            let args = make::arg_list(iter::once(expr));
+            let expr = arg_expr.unwrap_or_else(make::ext::expr_unit);
+            let args = make::arg_list([expr]);
             make::expr_call(make::expr_path(make::ext::ident_path("Err")), args)
         }
     };
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs
index 0cc807aff64..97321f4ec1e 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs
@@ -4,6 +4,7 @@ use ide_db::{
     syntax_helpers::{suggest_name, LexedStr},
 };
 use syntax::{
+    algo::ancestors_at_offset,
     ast::{
         self, edit::IndentLevel, edit_in_place::Indent, make, syntax_factory::SyntaxFactory,
         AstNode,
@@ -68,7 +69,10 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
     let node = if ctx.has_empty_selection() {
         if let Some(t) = ctx.token_at_offset().find(|it| it.kind() == T![;]) {
             t.parent().and_then(ast::ExprStmt::cast)?.syntax().clone()
-        } else if let Some(expr) = ctx.find_node_at_offset::<ast::Expr>() {
+        } else if let Some(expr) = ancestors_at_offset(ctx.source_file().syntax(), ctx.offset())
+            .next()
+            .and_then(ast::Expr::cast)
+        {
             expr.syntax().ancestors().find_map(valid_target_expr)?.syntax().clone()
         } else {
             return None;
@@ -469,11 +473,11 @@ mod tests {
             extract_variable,
             r#"
 fn main() -> i32 {
-    if true {
+    if$0 true {
         1
     } else {
         2
-    }$0
+    }
 }
 "#,
             r#"
@@ -581,11 +585,11 @@ fn main() {
             extract_variable,
             r#"
 fn main() -> i32 {
-    if true {
+    if$0 true {
         1
     } else {
         2
-    }$0
+    }
 }
 "#,
             r#"
@@ -676,11 +680,11 @@ fn main() {
             extract_variable,
             r#"
 fn main() -> i32 {
-    if true {
+    if$0 true {
         1
     } else {
         2
-    }$0
+    }
 }
 "#,
             r#"
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 c879a4a3d95..ac58af62525 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
@@ -933,7 +933,7 @@ mod tests_setter {
 
     use super::*;
 
-    fn check_not_applicable(ra_fixture: &str) {
+    fn check_not_applicable(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
         check_assist_not_applicable(generate_setter, ra_fixture)
     }
 
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_macro.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_macro.rs
index d558ec3bec7..cd6f900ba15 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_macro.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_macro.rs
@@ -38,21 +38,21 @@ use crate::{AssistContext, AssistId, AssistKind, Assists};
 pub(crate) fn inline_macro(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
     let unexpanded = ctx.find_node_at_offset::<ast::MacroCall>()?;
     let macro_call = ctx.sema.to_def(&unexpanded)?;
-    let expanded = ctx.sema.parse_or_expand(macro_call.as_file());
-    let span_map = ctx.sema.db.expansion_span_map(macro_call.as_macro_file());
-    let expanded = prettify_macro_expansion(
-        ctx.db(),
-        expanded,
-        &span_map,
-        ctx.sema.file_to_module_def(ctx.file_id())?.krate().into(),
-    );
+    let target_crate_id = ctx.sema.file_to_module_def(ctx.file_id())?.krate().into();
     let text_range = unexpanded.syntax().text_range();
 
     acc.add(
         AssistId("inline_macro", AssistKind::RefactorInline),
         "Inline macro".to_owned(),
         text_range,
-        |builder| builder.replace(text_range, expanded.to_string()),
+        |builder| {
+            let expanded = ctx.sema.parse_or_expand(macro_call.as_file());
+            let span_map = ctx.sema.db.expansion_span_map(macro_call.as_macro_file());
+            // Don't call `prettify_macro_expansion()` outside the actual assist action; it does some heavy rowan tree manipulation,
+            // which can be very costly for big macros when it is done *even without the assist being invoked*.
+            let expanded = prettify_macro_expansion(ctx.db(), expanded, &span_map, target_crate_id);
+            builder.replace(text_range, expanded.to_string())
+        },
     )
 }
 
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_guard.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_guard.rs
index f0c96fe3cb8..a487960d8d4 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_guard.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_guard.rs
@@ -61,7 +61,7 @@ pub(crate) fn move_guard_to_arm_body(acc: &mut Assists, ctx: &AssistContext<'_>)
             };
 
             edit.delete(guard.syntax().text_range());
-            edit.replace_ast(arm_expr, if_expr);
+            edit.replace_ast(arm_expr, if_expr.into());
         },
     )
 }
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_dbg.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_dbg.rs
index 94274f6d17c..1f57f7d3d37 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_dbg.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_dbg.rs
@@ -102,7 +102,7 @@ fn compute_dbg_replacement(macro_expr: ast::MacroExpr) -> Option<(TextRange, Opt
                         };
                         (range, None)
                     },
-                    _ => (macro_call.syntax().text_range(), Some(make::expr_unit())),
+                    _ => (macro_call.syntax().text_range(), Some(make::ext::expr_unit())),
                 }
             }
         }
@@ -152,7 +152,7 @@ fn compute_dbg_replacement(macro_expr: ast::MacroExpr) -> Option<(TextRange, Opt
         exprs => {
             let exprs = exprs.iter().cloned().map(replace_nested_dbgs);
             let expr = make::expr_tuple(exprs);
-            (macro_call.syntax().text_range(), Some(expr))
+            (macro_call.syntax().text_range(), Some(expr.into()))
         }
     })
 }
@@ -209,7 +209,10 @@ mod tests {
 
     use super::*;
 
-    fn check(ra_fixture_before: &str, ra_fixture_after: &str) {
+    fn check(
+        #[rust_analyzer::rust_fixture] ra_fixture_before: &str,
+        #[rust_analyzer::rust_fixture] ra_fixture_after: &str,
+    ) {
         check_assist(
             remove_dbg,
             &format!("fn main() {{\n{ra_fixture_before}\n}}"),
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_if_let_with_match.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_if_let_with_match.rs
index b31d45e6d45..e324d6eaaad 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_if_let_with_match.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_if_let_with_match.rs
@@ -1,4 +1,4 @@
-use std::iter::{self, successors};
+use std::iter::successors;
 
 use either::Either;
 use ide_db::{
@@ -8,11 +8,7 @@ use ide_db::{
     RootDatabase,
 };
 use syntax::{
-    ast::{
-        self,
-        edit::{AstNodeEdit, IndentLevel},
-        make, HasName,
-    },
+    ast::{self, edit::IndentLevel, edit_in_place::Indent, syntax_factory::SyntaxFactory, HasName},
     AstNode, TextRange, T,
 };
 
@@ -108,53 +104,58 @@ pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext<'
         AssistId("replace_if_let_with_match", AssistKind::RefactorRewrite),
         format!("Replace if{let_} with match"),
         available_range,
-        move |edit| {
+        move |builder| {
+            let make = SyntaxFactory::new();
             let match_expr = {
-                let else_arm = make_else_arm(ctx, else_block, &cond_bodies);
+                let else_arm = make_else_arm(ctx, &make, else_block, &cond_bodies);
                 let make_match_arm = |(pat, body): (_, ast::BlockExpr)| {
-                    let body = body.reset_indent().indent(IndentLevel(1));
+                    let body = make.block_expr(body.statements(), body.tail_expr());
+                    body.indent(IndentLevel::from(1));
+                    let body = unwrap_trivial_block(body);
                     match pat {
-                        Either::Left(pat) => {
-                            make::match_arm(iter::once(pat), None, unwrap_trivial_block(body))
+                        Either::Left(pat) => make.match_arm(pat, None, body),
+                        Either::Right(_) if !pat_seen => {
+                            make.match_arm(make.literal_pat("true").into(), None, body)
                         }
-                        Either::Right(_) if !pat_seen => make::match_arm(
-                            iter::once(make::literal_pat("true").into()),
-                            None,
-                            unwrap_trivial_block(body),
-                        ),
-                        Either::Right(expr) => make::match_arm(
-                            iter::once(make::wildcard_pat().into()),
-                            Some(expr),
-                            unwrap_trivial_block(body),
+                        Either::Right(expr) => make.match_arm(
+                            make.wildcard_pat().into(),
+                            Some(make.match_guard(expr)),
+                            body,
                         ),
                     }
                 };
-                let arms = cond_bodies.into_iter().map(make_match_arm).chain(iter::once(else_arm));
-                let match_expr = make::expr_match(scrutinee_to_be_expr, make::match_arm_list(arms));
-                match_expr.indent(IndentLevel::from_node(if_expr.syntax()))
+                let arms = cond_bodies.into_iter().map(make_match_arm).chain([else_arm]);
+                let match_expr = make.expr_match(scrutinee_to_be_expr, make.match_arm_list(arms));
+                match_expr.indent(IndentLevel::from_node(if_expr.syntax()));
+                match_expr.into()
             };
 
             let has_preceding_if_expr =
                 if_expr.syntax().parent().is_some_and(|it| ast::IfExpr::can_cast(it.kind()));
             let expr = if has_preceding_if_expr {
                 // make sure we replace the `else if let ...` with a block so we don't end up with `else expr`
-                make::block_expr(None, Some(match_expr)).into()
+                make.block_expr([], Some(match_expr)).into()
             } else {
                 match_expr
             };
-            edit.replace_ast::<ast::Expr>(if_expr.into(), expr);
+
+            let mut editor = builder.make_editor(if_expr.syntax());
+            editor.replace(if_expr.syntax(), expr.syntax());
+            editor.add_mappings(make.finish_with_mappings());
+            builder.add_file_edits(ctx.file_id(), editor);
         },
     )
 }
 
 fn make_else_arm(
     ctx: &AssistContext<'_>,
+    make: &SyntaxFactory,
     else_block: Option<ast::BlockExpr>,
     conditionals: &[(Either<ast::Pat, ast::Expr>, ast::BlockExpr)],
 ) -> ast::MatchArm {
     let (pattern, expr) = if let Some(else_block) = else_block {
         let pattern = match conditionals {
-            [(Either::Right(_), _)] => make::literal_pat("false").into(),
+            [(Either::Right(_), _)] => make.literal_pat("false").into(),
             [(Either::Left(pat), _)] => match ctx
                 .sema
                 .type_of_pat(pat)
@@ -164,24 +165,24 @@ fn make_else_arm(
                     if does_pat_match_variant(pat, &it.sad_pattern()) {
                         it.happy_pattern_wildcard()
                     } else if does_pat_variant_nested_or_literal(ctx, pat) {
-                        make::wildcard_pat().into()
+                        make.wildcard_pat().into()
                     } else {
                         it.sad_pattern()
                     }
                 }
-                None => make::wildcard_pat().into(),
+                None => make.wildcard_pat().into(),
             },
-            _ => make::wildcard_pat().into(),
+            _ => make.wildcard_pat().into(),
         };
         (pattern, unwrap_trivial_block(else_block))
     } else {
         let pattern = match conditionals {
-            [(Either::Right(_), _)] => make::literal_pat("false").into(),
-            _ => make::wildcard_pat().into(),
+            [(Either::Right(_), _)] => make.literal_pat("false").into(),
+            _ => make.wildcard_pat().into(),
         };
-        (pattern, make::expr_unit())
+        (pattern, make.expr_unit())
     };
-    make::match_arm(iter::once(pattern), None, expr)
+    make.match_arm(pattern, None, expr)
 }
 
 // Assist: replace_match_with_if_let
@@ -247,21 +248,21 @@ pub(crate) fn replace_match_with_if_let(acc: &mut Assists, ctx: &AssistContext<'
         }
         _ => " let",
     };
-    let target = match_expr.syntax().text_range();
     acc.add(
         AssistId("replace_match_with_if_let", AssistKind::RefactorRewrite),
         format!("Replace match with if{let_}"),
-        target,
-        move |edit| {
-            fn make_block_expr(expr: ast::Expr) -> ast::BlockExpr {
+        match_expr.syntax().text_range(),
+        move |builder| {
+            let make = SyntaxFactory::new();
+            let make_block_expr = |expr: ast::Expr| {
                 // Blocks with modifiers (unsafe, async, etc.) are parsed as BlockExpr, but are
                 // formatted without enclosing braces. If we encounter such block exprs,
                 // wrap them in another BlockExpr.
                 match expr {
                     ast::Expr::BlockExpr(block) if block.modifier().is_none() => block,
-                    expr => make::block_expr(iter::empty(), Some(expr)),
+                    expr => make.block_expr([], Some(expr)),
                 }
-            }
+            };
 
             let condition = match if_let_pat {
                 ast::Pat::LiteralPat(p)
@@ -272,20 +273,25 @@ pub(crate) fn replace_match_with_if_let(acc: &mut Assists, ctx: &AssistContext<'
                 ast::Pat::LiteralPat(p)
                     if p.literal().is_some_and(|it| it.token().kind() == T![false]) =>
                 {
-                    make::expr_prefix(T![!], scrutinee)
+                    make.expr_prefix(T![!], scrutinee).into()
                 }
-                _ => make::expr_let(if_let_pat, scrutinee).into(),
+                _ => make.expr_let(if_let_pat, scrutinee).into(),
             };
-            let then_block = make_block_expr(then_expr.reset_indent());
+            let then_expr = then_expr.clone_for_update();
+            then_expr.reindent_to(IndentLevel::single());
+            let then_block = make_block_expr(then_expr);
             let else_expr = if is_empty_expr(&else_expr) { None } else { Some(else_expr) };
-            let if_let_expr = make::expr_if(
+            let if_let_expr = make.expr_if(
                 condition,
                 then_block,
                 else_expr.map(make_block_expr).map(ast::ElseBranch::Block),
-            )
-            .indent(IndentLevel::from_node(match_expr.syntax()));
+            );
+            if_let_expr.indent(IndentLevel::from_node(match_expr.syntax()));
 
-            edit.replace_ast::<ast::Expr>(match_expr.into(), if_let_expr);
+            let mut editor = builder.make_editor(match_expr.syntax());
+            editor.replace(match_expr.syntax(), if_let_expr.syntax());
+            editor.add_mappings(make.finish_with_mappings());
+            builder.add_file_edits(ctx.file_id(), editor);
         },
     )
 }
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_let_with_if_let.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_let_with_if_let.rs
index c2be4593b97..c071d3022d2 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_let_with_if_let.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_let_with_if_let.rs
@@ -1,12 +1,6 @@
-use std::iter::once;
-
 use ide_db::ty_filter::TryEnum;
 use syntax::{
-    ast::{
-        self,
-        edit::{AstNodeEdit, IndentLevel},
-        make,
-    },
+    ast::{self, edit::IndentLevel, edit_in_place::Indent, syntax_factory::SyntaxFactory},
     AstNode, T,
 };
 
@@ -47,7 +41,9 @@ pub(crate) fn replace_let_with_if_let(acc: &mut Assists, ctx: &AssistContext<'_>
         AssistId("replace_let_with_if_let", AssistKind::RefactorRewrite),
         "Replace let with if let",
         target,
-        |edit| {
+        |builder| {
+            let mut editor = builder.make_editor(let_stmt.syntax());
+            let make = SyntaxFactory::new();
             let ty = ctx.sema.type_of_expr(&init);
             let happy_variant = ty
                 .and_then(|ty| TryEnum::from_ty(&ctx.sema, &ty.adjusted()))
@@ -55,17 +51,18 @@ pub(crate) fn replace_let_with_if_let(acc: &mut Assists, ctx: &AssistContext<'_>
             let pat = match happy_variant {
                 None => original_pat,
                 Some(var_name) => {
-                    make::tuple_struct_pat(make::ext::ident_path(var_name), once(original_pat))
-                        .into()
+                    make.tuple_struct_pat(make.ident_path(var_name), [original_pat]).into()
                 }
             };
 
-            let block =
-                make::ext::empty_block_expr().indent(IndentLevel::from_node(let_stmt.syntax()));
-            let if_ = make::expr_if(make::expr_let(pat, init).into(), block, None);
-            let stmt = make::expr_stmt(if_);
+            let block = make.block_expr([], None);
+            block.indent(IndentLevel::from_node(let_stmt.syntax()));
+            let if_expr = make.expr_if(make.expr_let(pat, init).into(), block, None);
+            let if_stmt = make.expr_stmt(if_expr.into());
 
-            edit.replace_ast(ast::Stmt::from(let_stmt), ast::Stmt::from(stmt));
+            editor.replace(let_stmt.syntax(), if_stmt.syntax());
+            editor.add_mappings(make.finish_with_mappings());
+            builder.add_file_edits(ctx.file_id(), editor);
         },
     )
 }
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_try_expr_with_match.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_try_expr_with_match.rs
index 2e26f59d030..88b50543dda 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_try_expr_with_match.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_try_expr_with_match.rs
@@ -71,19 +71,17 @@ pub(crate) fn replace_try_expr_with_match(
             };
 
             let happy_arm = make::match_arm(
-                iter::once(
-                    try_enum.happy_pattern(make::ident_pat(false, false, make::name("it")).into()),
-                ),
+                try_enum.happy_pattern(make::ident_pat(false, false, make::name("it")).into()),
                 None,
                 make::expr_path(make::ext::ident_path("it")),
             );
-            let sad_arm = make::match_arm(iter::once(sad_pat), None, sad_expr);
+            let sad_arm = make::match_arm(sad_pat, None, sad_expr);
 
             let match_arm_list = make::match_arm_list([happy_arm, sad_arm]);
 
             let expr_match = make::expr_match(expr, match_arm_list)
                 .indent(IndentLevel::from_node(qm_kw_parent.syntax()));
-            edit.replace_ast::<ast::Expr>(qm_kw_parent.into(), expr_match);
+            edit.replace_ast::<ast::Expr>(qm_kw_parent.into(), expr_match.into());
         },
     )
 }
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unmerge_match_arm.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unmerge_match_arm.rs
index c6cffb5434a..6b9f661d4de 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unmerge_match_arm.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unmerge_match_arm.rs
@@ -54,13 +54,9 @@ pub(crate) fn unmerge_match_arm(acc: &mut Assists, ctx: &AssistContext<'_>) -> O
             let pats_after = pipe_token
                 .siblings_with_tokens(Direction::Next)
                 .filter_map(|it| ast::Pat::cast(it.into_node()?));
-            // FIXME: We should add a leading pipe if the original arm has one.
-            let new_match_arm = make::match_arm(
-                pats_after,
-                match_arm.guard().and_then(|guard| guard.condition()),
-                match_arm_body,
-            )
-            .clone_for_update();
+            let new_pat = make::or_pat(pats_after, or_pat.leading_pipe().is_some());
+            let new_match_arm =
+                make::match_arm(new_pat, match_arm.guard(), match_arm_body).clone_for_update();
 
             let mut pipe_index = pipe_token.index();
             if pipe_token
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_block.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_block.rs
index f3e7f5f4167..fd37140e9c2 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_block.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_block.rs
@@ -61,7 +61,7 @@ pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option
                 }
             }
             None => {
-                let empty_tuple = make::expr_tuple([]);
+                let empty_tuple = make::ext::expr_unit();
                 make::let_stmt(pattern, ty, Some(empty_tuple)).to_string()
             }
         };
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_return_type.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_return_type.rs
index 64d5e2c9b82..f647b531b77 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_return_type.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_return_type.rs
@@ -1,11 +1,11 @@
+use either::Either;
 use ide_db::{
     famous_defs::FamousDefs,
     syntax_helpers::node_ext::{for_each_tail_expr, walk_expr},
 };
-use itertools::Itertools;
 use syntax::{
-    ast::{self, Expr, HasGenericArgs},
-    match_ast, AstNode, NodeOrToken, SyntaxKind, TextRange,
+    ast::{self, syntax_factory::SyntaxFactory, HasArgList, HasGenericArgs},
+    match_ast, AstNode, NodeOrToken, SyntaxKind,
 };
 
 use crate::{AssistContext, AssistId, AssistKind, Assists};
@@ -39,11 +39,11 @@ use crate::{AssistContext, AssistId, AssistKind, Assists};
 pub(crate) fn unwrap_return_type(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
     let ret_type = ctx.find_node_at_offset::<ast::RetType>()?;
     let parent = ret_type.syntax().parent()?;
-    let body = match_ast! {
+    let body_expr = match_ast! {
         match parent {
-            ast::Fn(func) => func.body()?,
+            ast::Fn(func) => func.body()?.into(),
             ast::ClosureExpr(closure) => match closure.body()? {
-                Expr::BlockExpr(block) => block,
+                ast::Expr::BlockExpr(block) => block.into(),
                 // closures require a block when a return type is specified
                 _ => return None,
             },
@@ -65,72 +65,110 @@ pub(crate) fn unwrap_return_type(acc: &mut Assists, ctx: &AssistContext<'_>) ->
     let happy_type = extract_wrapped_type(type_ref)?;
 
     acc.add(kind.assist_id(), kind.label(), type_ref.syntax().text_range(), |builder| {
-        let body = ast::Expr::BlockExpr(body);
+        let mut editor = builder.make_editor(&parent);
+        let make = SyntaxFactory::new();
 
         let mut exprs_to_unwrap = Vec::new();
         let tail_cb = &mut |e: &_| tail_cb_impl(&mut exprs_to_unwrap, e);
-        walk_expr(&body, &mut |expr| {
-            if let Expr::ReturnExpr(ret_expr) = expr {
+        walk_expr(&body_expr, &mut |expr| {
+            if let ast::Expr::ReturnExpr(ret_expr) = expr {
                 if let Some(ret_expr_arg) = &ret_expr.expr() {
                     for_each_tail_expr(ret_expr_arg, tail_cb);
                 }
             }
         });
-        for_each_tail_expr(&body, tail_cb);
+        for_each_tail_expr(&body_expr, tail_cb);
 
         let is_unit_type = is_unit_type(&happy_type);
         if is_unit_type {
-            let mut text_range = ret_type.syntax().text_range();
-
             if let Some(NodeOrToken::Token(token)) = ret_type.syntax().next_sibling_or_token() {
                 if token.kind() == SyntaxKind::WHITESPACE {
-                    text_range = TextRange::new(text_range.start(), token.text_range().end());
+                    editor.delete(token);
                 }
             }
 
-            builder.delete(text_range);
+            editor.delete(ret_type.syntax());
         } else {
-            builder.replace(type_ref.syntax().text_range(), happy_type.syntax().text());
+            editor.replace(type_ref.syntax(), happy_type.syntax());
         }
 
-        for ret_expr_arg in exprs_to_unwrap {
-            let ret_expr_str = ret_expr_arg.to_string();
-
-            let needs_replacing = match kind {
-                UnwrapperKind::Option => ret_expr_str.starts_with("Some("),
-                UnwrapperKind::Result => {
-                    ret_expr_str.starts_with("Ok(") || ret_expr_str.starts_with("Err(")
-                }
-            };
+        let mut final_placeholder = None;
+        for tail_expr in exprs_to_unwrap {
+            match &tail_expr {
+                ast::Expr::CallExpr(call_expr) => {
+                    let ast::Expr::PathExpr(path_expr) = call_expr.expr().unwrap() else {
+                        continue;
+                    };
+
+                    let path_str = path_expr.path().unwrap().to_string();
+                    let needs_replacing = match kind {
+                        UnwrapperKind::Option => path_str == "Some",
+                        UnwrapperKind::Result => path_str == "Ok" || path_str == "Err",
+                    };
+
+                    if !needs_replacing {
+                        continue;
+                    }
 
-            if needs_replacing {
-                let arg_list = ret_expr_arg.syntax().children().find_map(ast::ArgList::cast);
-                if let Some(arg_list) = arg_list {
+                    let arg_list = call_expr.arg_list().unwrap();
                     if is_unit_type {
-                        match ret_expr_arg.syntax().prev_sibling_or_token() {
-                            // Useful to delete the entire line without leaving trailing whitespaces
-                            Some(whitespace) => {
-                                let new_range = TextRange::new(
-                                    whitespace.text_range().start(),
-                                    ret_expr_arg.syntax().text_range().end(),
-                                );
-                                builder.delete(new_range);
+                        let tail_parent = tail_expr
+                            .syntax()
+                            .parent()
+                            .and_then(Either::<ast::ReturnExpr, ast::StmtList>::cast)
+                            .unwrap();
+                        match tail_parent {
+                            Either::Left(ret_expr) => {
+                                editor.replace(ret_expr.syntax(), make.expr_return(None).syntax())
                             }
-                            None => {
-                                builder.delete(ret_expr_arg.syntax().text_range());
+                            Either::Right(stmt_list) => {
+                                let new_block = if stmt_list.statements().next().is_none() {
+                                    make.expr_empty_block()
+                                } else {
+                                    make.block_expr(stmt_list.statements(), None)
+                                };
+                                editor.replace(
+                                    stmt_list.syntax(),
+                                    new_block.stmt_list().unwrap().syntax(),
+                                );
                             }
                         }
-                    } else {
-                        builder.replace(
-                            ret_expr_arg.syntax().text_range(),
-                            arg_list.args().join(", "),
+                    } else if let Some(first_arg) = arg_list.args().next() {
+                        editor.replace(tail_expr.syntax(), first_arg.syntax());
+                    }
+                }
+                ast::Expr::PathExpr(path_expr) => {
+                    let UnwrapperKind::Option = kind else {
+                        continue;
+                    };
+
+                    if path_expr.path().unwrap().to_string() != "None" {
+                        continue;
+                    }
+
+                    let new_tail_expr = make.expr_unit();
+                    editor.replace(path_expr.syntax(), new_tail_expr.syntax());
+                    if let Some(cap) = ctx.config.snippet_cap {
+                        editor.add_annotation(
+                            new_tail_expr.syntax(),
+                            builder.make_placeholder_snippet(cap),
                         );
+
+                        final_placeholder = Some(new_tail_expr);
                     }
                 }
-            } else if matches!(kind, UnwrapperKind::Option if ret_expr_str == "None") {
-                builder.replace(ret_expr_arg.syntax().text_range(), "()");
+                _ => (),
             }
         }
+
+        if let Some(cap) = ctx.config.snippet_cap {
+            if let Some(final_placeholder) = final_placeholder {
+                editor.add_annotation(final_placeholder.syntax(), builder.make_tabstop_after(cap));
+            }
+        }
+
+        editor.add_mappings(make.finish_with_mappings());
+        builder.add_file_edits(ctx.file_id(), editor);
     })
 }
 
@@ -168,12 +206,12 @@ impl UnwrapperKind {
 
 fn tail_cb_impl(acc: &mut Vec<ast::Expr>, e: &ast::Expr) {
     match e {
-        Expr::BreakExpr(break_expr) => {
+        ast::Expr::BreakExpr(break_expr) => {
             if let Some(break_expr_arg) = break_expr.expr() {
                 for_each_tail_expr(&break_expr_arg, &mut |e| tail_cb_impl(acc, e))
             }
         }
-        Expr::ReturnExpr(_) => {
+        ast::Expr::ReturnExpr(_) => {
             // all return expressions have already been handled by the walk loop
         }
         e => acc.push(e.clone()),
@@ -238,8 +276,7 @@ fn foo() -> Option<()$0> {
 }
 "#,
             r#"
-fn foo() {
-}
+fn foo() {}
 "#,
             "Unwrap Option return type",
         );
@@ -254,8 +291,7 @@ fn foo() -> Option<()$0>{
 }
 "#,
             r#"
-fn foo() {
-}
+fn foo() {}
 "#,
             "Unwrap Option return type",
         );
@@ -280,7 +316,42 @@ fn foo() -> i32 {
     if true {
         42
     } else {
-        ()
+        ${1:()}$0
+    }
+}
+"#,
+            "Unwrap Option return type",
+        );
+    }
+
+    #[test]
+    fn unwrap_option_return_type_multi_none() {
+        check_assist_by_label(
+            unwrap_return_type,
+            r#"
+//- minicore: option
+fn foo() -> Option<i3$02> {
+    if false {
+        return None;
+    }
+
+    if true {
+        Some(42)
+    } else {
+        None
+    }
+}
+"#,
+            r#"
+fn foo() -> i32 {
+    if false {
+        return ${1:()};
+    }
+
+    if true {
+        42
+    } else {
+        ${2:()}$0
     }
 }
 "#,
@@ -1262,8 +1333,7 @@ fn foo() -> Result<(), Box<dyn Error$0>> {
 }
 "#,
             r#"
-fn foo() {
-}
+fn foo() {}
 "#,
             "Unwrap Result return type",
         );
@@ -1278,8 +1348,7 @@ fn foo() -> Result<(), Box<dyn Error$0>>{
 }
 "#,
             r#"
-fn foo() {
-}
+fn foo() {}
 "#,
             "Unwrap Result return type",
         );
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_return_type.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_return_type.rs
index 658600cd2d0..0b145dcb06b 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_return_type.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_return_type.rs
@@ -6,10 +6,9 @@ use ide_db::{
     famous_defs::FamousDefs,
     syntax_helpers::node_ext::{for_each_tail_expr, walk_expr},
 };
-use itertools::Itertools;
 use syntax::{
-    ast::{self, make, Expr, HasGenericParams},
-    match_ast, ted, AstNode, ToSmolStr,
+    ast::{self, syntax_factory::SyntaxFactory, Expr, HasGenericArgs, HasGenericParams},
+    match_ast, AstNode,
 };
 
 use crate::{AssistContext, AssistId, AssistKind, Assists};
@@ -43,11 +42,11 @@ use crate::{AssistContext, AssistId, AssistKind, Assists};
 pub(crate) fn wrap_return_type(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
     let ret_type = ctx.find_node_at_offset::<ast::RetType>()?;
     let parent = ret_type.syntax().parent()?;
-    let body = match_ast! {
+    let body_expr = match_ast! {
         match parent {
-            ast::Fn(func) => func.body()?,
+            ast::Fn(func) => func.body()?.into(),
             ast::ClosureExpr(closure) => match closure.body()? {
-                Expr::BlockExpr(block) => block,
+                Expr::BlockExpr(block) => block.into(),
                 // closures require a block when a return type is specified
                 _ => return None,
             },
@@ -75,56 +74,65 @@ pub(crate) fn wrap_return_type(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
             kind.assist_id(),
             kind.label(),
             type_ref.syntax().text_range(),
-            |edit| {
-                let alias = wrapper_alias(ctx, &core_wrapper, type_ref, kind.symbol());
-                let new_return_ty =
-                    alias.unwrap_or_else(|| kind.wrap_type(type_ref)).clone_for_update();
-
-                let body = edit.make_mut(ast::Expr::BlockExpr(body.clone()));
+            |builder| {
+                let mut editor = builder.make_editor(&parent);
+                let make = SyntaxFactory::new();
+                let alias = wrapper_alias(ctx, &make, &core_wrapper, type_ref, kind.symbol());
+                let new_return_ty = alias.unwrap_or_else(|| match kind {
+                    WrapperKind::Option => make.ty_option(type_ref.clone()),
+                    WrapperKind::Result => make.ty_result(type_ref.clone(), make.ty_infer().into()),
+                });
 
                 let mut exprs_to_wrap = Vec::new();
                 let tail_cb = &mut |e: &_| tail_cb_impl(&mut exprs_to_wrap, e);
-                walk_expr(&body, &mut |expr| {
+                walk_expr(&body_expr, &mut |expr| {
                     if let Expr::ReturnExpr(ret_expr) = expr {
                         if let Some(ret_expr_arg) = &ret_expr.expr() {
                             for_each_tail_expr(ret_expr_arg, tail_cb);
                         }
                     }
                 });
-                for_each_tail_expr(&body, tail_cb);
+                for_each_tail_expr(&body_expr, tail_cb);
 
                 for ret_expr_arg in exprs_to_wrap {
-                    let happy_wrapped = make::expr_call(
-                        make::expr_path(make::ext::ident_path(kind.happy_ident())),
-                        make::arg_list(iter::once(ret_expr_arg.clone())),
-                    )
-                    .clone_for_update();
-                    ted::replace(ret_expr_arg.syntax(), happy_wrapped.syntax());
+                    let happy_wrapped = make.expr_call(
+                        make.expr_path(make.ident_path(kind.happy_ident())),
+                        make.arg_list(iter::once(ret_expr_arg.clone())),
+                    );
+                    editor.replace(ret_expr_arg.syntax(), happy_wrapped.syntax());
                 }
 
-                let old_return_ty = edit.make_mut(type_ref.clone());
-                ted::replace(old_return_ty.syntax(), new_return_ty.syntax());
+                editor.replace(type_ref.syntax(), new_return_ty.syntax());
 
                 if let WrapperKind::Result = kind {
                     // Add a placeholder snippet at the first generic argument that doesn't equal the return type.
                     // This is normally the error type, but that may not be the case when we inserted a type alias.
-                    let args =
-                        new_return_ty.syntax().descendants().find_map(ast::GenericArgList::cast);
-                    let error_type_arg = args.and_then(|list| {
-                        list.generic_args().find(|arg| match arg {
-                            ast::GenericArg::TypeArg(_) => {
-                                arg.syntax().text() != type_ref.syntax().text()
-                            }
-                            ast::GenericArg::LifetimeArg(_) => false,
-                            _ => true,
-                        })
+                    let args = new_return_ty
+                        .path()
+                        .unwrap()
+                        .segment()
+                        .unwrap()
+                        .generic_arg_list()
+                        .unwrap();
+                    let error_type_arg = args.generic_args().find(|arg| match arg {
+                        ast::GenericArg::TypeArg(_) => {
+                            arg.syntax().text() != type_ref.syntax().text()
+                        }
+                        ast::GenericArg::LifetimeArg(_) => false,
+                        _ => true,
                     });
                     if let Some(error_type_arg) = error_type_arg {
                         if let Some(cap) = ctx.config.snippet_cap {
-                            edit.add_placeholder_snippet(cap, error_type_arg);
+                            editor.add_annotation(
+                                error_type_arg.syntax(),
+                                builder.make_placeholder_snippet(cap),
+                            );
                         }
                     }
                 }
+
+                editor.add_mappings(make.finish_with_mappings());
+                builder.add_file_edits(ctx.file_id(), editor);
             },
         );
     }
@@ -176,22 +184,16 @@ impl WrapperKind {
             WrapperKind::Result => hir::sym::Result.clone(),
         }
     }
-
-    fn wrap_type(&self, type_ref: &ast::Type) -> ast::Type {
-        match self {
-            WrapperKind::Option => make::ext::ty_option(type_ref.clone()),
-            WrapperKind::Result => make::ext::ty_result(type_ref.clone(), make::ty_placeholder()),
-        }
-    }
 }
 
 // Try to find an wrapper type alias in the current scope (shadowing the default).
 fn wrapper_alias(
     ctx: &AssistContext<'_>,
+    make: &SyntaxFactory,
     core_wrapper: &hir::Enum,
     ret_type: &ast::Type,
     wrapper: hir::Symbol,
-) -> Option<ast::Type> {
+) -> Option<ast::PathType> {
     let wrapper_path = hir::ModPath::from_segments(
         hir::PathKind::Plain,
         iter::once(hir::Name::new_symbol_root(wrapper)),
@@ -207,25 +209,28 @@ fn wrapper_alias(
         })
         .find_map(|alias| {
             let mut inserted_ret_type = false;
-            let generic_params = alias
-                .source(ctx.db())?
-                .value
-                .generic_param_list()?
-                .generic_params()
-                .map(|param| match param {
-                    // Replace the very first type parameter with the functions return type.
-                    ast::GenericParam::TypeParam(_) if !inserted_ret_type => {
-                        inserted_ret_type = true;
-                        ret_type.to_smolstr()
+            let generic_args =
+                alias.source(ctx.db())?.value.generic_param_list()?.generic_params().map(|param| {
+                    match param {
+                        // Replace the very first type parameter with the function's return type.
+                        ast::GenericParam::TypeParam(_) if !inserted_ret_type => {
+                            inserted_ret_type = true;
+                            make.type_arg(ret_type.clone()).into()
+                        }
+                        ast::GenericParam::LifetimeParam(_) => {
+                            make.lifetime_arg(make.lifetime("'_")).into()
+                        }
+                        _ => make.type_arg(make.ty_infer().into()).into(),
                     }
-                    ast::GenericParam::LifetimeParam(_) => make::lifetime("'_").to_smolstr(),
-                    _ => make::ty_placeholder().to_smolstr(),
-                })
-                .join(", ");
+                });
 
             let name = alias.name(ctx.db());
-            let name = name.as_str();
-            Some(make::ty(&format!("{name}<{generic_params}>")))
+            let generic_arg_list = make.generic_arg_list(generic_args, false);
+            let path = make.path_unqualified(
+                make.path_segment_generics(make.name_ref(name.as_str()), generic_arg_list),
+            );
+
+            Some(make.ty_path(path))
         })
     })
 }
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 0b1ff87c5c2..48d2af6d3ff 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs
@@ -77,7 +77,11 @@ pub(crate) fn with_single_file(text: &str) -> (RootDatabase, EditionedFileId) {
 }
 
 #[track_caller]
-pub(crate) fn check_assist(assist: Handler, ra_fixture_before: &str, ra_fixture_after: &str) {
+pub(crate) fn check_assist(
+    assist: Handler,
+    #[rust_analyzer::rust_fixture] ra_fixture_before: &str,
+    #[rust_analyzer::rust_fixture] ra_fixture_after: &str,
+) {
     let ra_fixture_after = trim_indent(ra_fixture_after);
     check(assist, ra_fixture_before, ExpectedResult::After(&ra_fixture_after), None);
 }
@@ -85,8 +89,8 @@ pub(crate) fn check_assist(assist: Handler, ra_fixture_before: &str, ra_fixture_
 #[track_caller]
 pub(crate) fn check_assist_no_snippet_cap(
     assist: Handler,
-    ra_fixture_before: &str,
-    ra_fixture_after: &str,
+    #[rust_analyzer::rust_fixture] ra_fixture_before: &str,
+    #[rust_analyzer::rust_fixture] ra_fixture_after: &str,
 ) {
     let ra_fixture_after = trim_indent(ra_fixture_after);
     check_with_config(
@@ -101,8 +105,8 @@ pub(crate) fn check_assist_no_snippet_cap(
 #[track_caller]
 pub(crate) fn check_assist_import_one(
     assist: Handler,
-    ra_fixture_before: &str,
-    ra_fixture_after: &str,
+    #[rust_analyzer::rust_fixture] ra_fixture_before: &str,
+    #[rust_analyzer::rust_fixture] ra_fixture_after: &str,
 ) {
     let ra_fixture_after = trim_indent(ra_fixture_after);
     check_with_config(
@@ -118,8 +122,8 @@ pub(crate) fn check_assist_import_one(
 // so this is here to allow you choose.
 pub(crate) fn check_assist_by_label(
     assist: Handler,
-    ra_fixture_before: &str,
-    ra_fixture_after: &str,
+    #[rust_analyzer::rust_fixture] ra_fixture_before: &str,
+    #[rust_analyzer::rust_fixture] ra_fixture_after: &str,
     label: &str,
 ) {
     let ra_fixture_after = trim_indent(ra_fixture_after);
@@ -130,22 +134,36 @@ pub(crate) fn check_assist_by_label(
 // `extract_ranges` and mark the target as `<target> </target>` in the
 // fixture?
 #[track_caller]
-pub(crate) fn check_assist_target(assist: Handler, ra_fixture: &str, target: &str) {
+pub(crate) fn check_assist_target(
+    assist: Handler,
+    #[rust_analyzer::rust_fixture] ra_fixture: &str,
+    target: &str,
+) {
     check(assist, ra_fixture, ExpectedResult::Target(target), None);
 }
 
 #[track_caller]
-pub(crate) fn check_assist_not_applicable(assist: Handler, ra_fixture: &str) {
+pub(crate) fn check_assist_not_applicable(
+    assist: Handler,
+    #[rust_analyzer::rust_fixture] ra_fixture: &str,
+) {
     check(assist, ra_fixture, ExpectedResult::NotApplicable, None);
 }
 
 #[track_caller]
-pub(crate) fn check_assist_not_applicable_by_label(assist: Handler, ra_fixture: &str, label: &str) {
+pub(crate) fn check_assist_not_applicable_by_label(
+    assist: Handler,
+    #[rust_analyzer::rust_fixture] ra_fixture: &str,
+    label: &str,
+) {
     check(assist, ra_fixture, ExpectedResult::NotApplicable, Some(label));
 }
 
 #[track_caller]
-pub(crate) fn check_assist_not_applicable_for_import_one(assist: Handler, ra_fixture: &str) {
+pub(crate) fn check_assist_not_applicable_for_import_one(
+    assist: Handler,
+    #[rust_analyzer::rust_fixture] ra_fixture: &str,
+) {
     check_with_config(
         TEST_CONFIG_IMPORT_ONE,
         assist,
@@ -157,7 +175,10 @@ pub(crate) fn check_assist_not_applicable_for_import_one(assist: Handler, ra_fix
 
 /// Check assist in unresolved state. Useful to check assists for lazy computation.
 #[track_caller]
-pub(crate) fn check_assist_unresolved(assist: Handler, ra_fixture: &str) {
+pub(crate) fn check_assist_unresolved(
+    assist: Handler,
+    #[rust_analyzer::rust_fixture] ra_fixture: &str,
+) {
     check(assist, ra_fixture, ExpectedResult::Unresolved, None);
 }
 
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 e20c4ef09e8..78ff4417913 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs
@@ -246,7 +246,7 @@ pub(crate) fn vis_offset(node: &SyntaxNode) -> TextSize {
 }
 
 pub(crate) fn invert_boolean_expression(expr: ast::Expr) -> ast::Expr {
-    invert_special_case(&expr).unwrap_or_else(|| make::expr_prefix(T![!], expr))
+    invert_special_case(&expr).unwrap_or_else(|| make::expr_prefix(T![!], expr).into())
 }
 
 fn invert_special_case(expr: &ast::Expr) -> Option<ast::Expr> {
@@ -262,7 +262,7 @@ fn invert_special_case(expr: &ast::Expr) -> Option<ast::Expr> {
                 T![>] => T![<=],
                 T![>=] => T![<],
                 // Parenthesize other expressions before prefixing `!`
-                _ => return Some(make::expr_prefix(T![!], make::expr_paren(expr.clone()))),
+                _ => return Some(make::expr_prefix(T![!], make::expr_paren(expr.clone())).into()),
             };
             ted::replace(op_token, make::token(rev_token));
             Some(bin.into())
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/utils/gen_trait_fn_body.rs b/src/tools/rust-analyzer/crates/ide-assists/src/utils/gen_trait_fn_body.rs
index 75caf6d49f7..7a9bdfe1ecc 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/utils/gen_trait_fn_body.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils/gen_trait_fn_body.rs
@@ -66,7 +66,7 @@ fn gen_clone_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> {
                         let pat = make::record_pat(variant_name.clone(), pats.into_iter());
                         let fields = make::record_expr_field_list(fields);
                         let record_expr = make::record_expr(variant_name, fields).into();
-                        arms.push(make::match_arm(Some(pat.into()), None, record_expr));
+                        arms.push(make::match_arm(pat.into(), None, record_expr));
                     }
 
                     // => match self { Self::Name(arg1) => Self::Name(arg1.clone()) }
@@ -84,21 +84,21 @@ fn gen_clone_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> {
                         let pat = make::tuple_struct_pat(variant_name.clone(), pats.into_iter());
                         let struct_name = make::expr_path(variant_name);
                         let tuple_expr = make::expr_call(struct_name, make::arg_list(fields));
-                        arms.push(make::match_arm(Some(pat.into()), None, tuple_expr));
+                        arms.push(make::match_arm(pat.into(), None, tuple_expr));
                     }
 
                     // => match self { Self::Name => Self::Name }
                     None => {
                         let pattern = make::path_pat(variant_name.clone());
                         let variant_expr = make::expr_path(variant_name);
-                        arms.push(make::match_arm(Some(pattern), None, variant_expr));
+                        arms.push(make::match_arm(pattern, None, variant_expr));
                     }
                 }
             }
 
             let match_target = make::expr_path(make::ext::ident_path("self"));
             let list = make::match_arm_list(arms).indent(ast::edit::IndentLevel(1));
-            make::expr_match(match_target, list)
+            make::expr_match(match_target, list).into()
         }
         ast::Adt::Struct(strukt) => {
             match strukt.field_list() {
@@ -190,7 +190,7 @@ fn gen_debug_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> {
 
                         // => MyStruct { fields.. } => f.debug_struct("MyStruct")...finish(),
                         let pat = make::record_pat(variant_name.clone(), pats.into_iter());
-                        arms.push(make::match_arm(Some(pat.into()), None, expr));
+                        arms.push(make::match_arm(pat.into(), None, expr));
                     }
                     Some(ast::FieldList::TupleFieldList(list)) => {
                         // => f.debug_tuple(name)
@@ -223,7 +223,7 @@ fn gen_debug_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> {
 
                         // => MyStruct (fields..) => f.debug_tuple("MyStruct")...finish(),
                         let pat = make::tuple_struct_pat(variant_name.clone(), pats.into_iter());
-                        arms.push(make::match_arm(Some(pat.into()), None, expr));
+                        arms.push(make::match_arm(pat.into(), None, expr));
                     }
                     None => {
                         let fmt_string = make::expr_literal(&(format!("\"{name}\""))).into();
@@ -232,7 +232,7 @@ fn gen_debug_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> {
                         let macro_call = make::expr_macro_call(macro_name, args);
 
                         let variant_name = make::path_pat(variant_name);
-                        arms.push(make::match_arm(Some(variant_name), None, macro_call));
+                        arms.push(make::match_arm(variant_name, None, macro_call));
                     }
                 }
             }
@@ -241,7 +241,7 @@ fn gen_debug_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> {
             let list = make::match_arm_list(arms).indent(ast::edit::IndentLevel(1));
             let match_expr = make::expr_match(match_target, list);
 
-            let body = make::block_expr(None, Some(match_expr));
+            let body = make::block_expr(None, Some(match_expr.into()));
             let body = body.indent(ast::edit::IndentLevel(1));
             ted::replace(func.body()?.syntax(), body.clone_for_update().syntax());
             Some(())
@@ -485,7 +485,7 @@ fn gen_partial_eq(adt: &ast::Adt, func: &ast::Fn, trait_ref: Option<TraitRef>) -
                         let tuple = make::tuple_pat(vec![left.into(), right.into()]);
 
                         if let Some(expr) = expr {
-                            arms.push(make::match_arm(Some(tuple.into()), None, expr));
+                            arms.push(make::match_arm(tuple.into(), None, expr));
                         }
                     }
 
@@ -518,7 +518,7 @@ fn gen_partial_eq(adt: &ast::Adt, func: &ast::Fn, trait_ref: Option<TraitRef>) -
                         let tuple = make::tuple_pat(vec![left.into(), right.into()]);
 
                         if let Some(expr) = expr {
-                            arms.push(make::match_arm(Some(tuple.into()), None, expr));
+                            arms.push(make::match_arm(tuple.into(), None, expr));
                         }
                     }
                     None => continue,
@@ -538,12 +538,12 @@ fn gen_partial_eq(adt: &ast::Adt, func: &ast::Fn, trait_ref: Option<TraitRef>) -
                         } else {
                             eq_check
                         };
-                        arms.push(make::match_arm(Some(lhs), None, rhs));
+                        arms.push(make::match_arm(lhs, None, rhs));
                     }
 
-                    let match_target = make::expr_tuple(vec![lhs_name, rhs_name]);
+                    let match_target = make::expr_tuple([lhs_name, rhs_name]).into();
                     let list = make::match_arm_list(arms).indent(ast::edit::IndentLevel(1));
-                    make::expr_match(match_target, list)
+                    make::expr_match(match_target, list).into()
                 }
             };
 
@@ -599,15 +599,15 @@ fn gen_partial_ord(adt: &ast::Adt, func: &ast::Fn, trait_ref: Option<TraitRef>)
         let variant_name =
             make::path_pat(make::ext::path_from_idents(["core", "cmp", "Ordering", "Equal"])?);
         let lhs = make::tuple_struct_pat(make::ext::path_from_idents(["Some"])?, [variant_name]);
-        arms.push(make::match_arm(Some(lhs.into()), None, make::expr_empty_block()));
+        arms.push(make::match_arm(lhs.into(), None, make::expr_empty_block().into()));
 
         arms.push(make::match_arm(
-            [make::ident_pat(false, false, make::name("ord")).into()],
+            make::ident_pat(false, false, make::name("ord")).into(),
             None,
             make::expr_return(Some(make::expr_path(make::ext::ident_path("ord")))),
         ));
         let list = make::match_arm_list(arms).indent(ast::edit::IndentLevel(1));
-        Some(make::expr_stmt(make::expr_match(match_target, list)).into())
+        Some(make::expr_stmt(make::expr_match(match_target, list).into()).into())
     }
 
     fn gen_partial_cmp_call(lhs: ast::Expr, rhs: ast::Expr) -> ast::Expr {
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/utils/ref_field_expr.rs b/src/tools/rust-analyzer/crates/ide-assists/src/utils/ref_field_expr.rs
index e95b291dd71..d434872ea59 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/utils/ref_field_expr.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils/ref_field_expr.rs
@@ -121,7 +121,7 @@ impl RefData {
     /// Derefs `expr` and wraps it in parens if necessary
     pub(crate) fn wrap_expr(&self, mut expr: ast::Expr) -> ast::Expr {
         if self.needs_deref {
-            expr = make::expr_prefix(T![*], expr);
+            expr = make::expr_prefix(T![*], expr).into();
         }
 
         if self.needs_parentheses {
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs
index 414627fbaba..40669c65c57 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs
@@ -329,7 +329,7 @@ impl Completions {
         ctx: &CompletionContext<'_>,
         dot_access: &DotAccess,
         func: hir::Function,
-        receiver: Option<hir::Name>,
+        receiver: Option<SmolStr>,
         local_name: Option<hir::Name>,
     ) {
         if !ctx.check_stability(Some(&func.attrs(ctx.db))) {
@@ -475,7 +475,7 @@ impl Completions {
         &mut self,
         ctx: &CompletionContext<'_>,
         dot_access: &DotAccess,
-        receiver: Option<hir::Name>,
+        receiver: Option<SmolStr>,
         field: hir::Field,
         ty: &hir::Type,
     ) {
@@ -533,7 +533,7 @@ impl Completions {
     pub(crate) fn add_tuple_field(
         &mut self,
         ctx: &CompletionContext<'_>,
-        receiver: Option<hir::Name>,
+        receiver: Option<SmolStr>,
         field: usize,
         ty: &hir::Type,
     ) {
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs
index 26074672ba9..7679d9076de 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs
@@ -2,7 +2,7 @@
 
 use std::ops::ControlFlow;
 
-use hir::{sym, HasContainer, ItemContainer, MethodCandidateCallback, Name};
+use hir::{HasContainer, ItemContainer, MethodCandidateCallback, Name};
 use ide_db::FxHashSet;
 use syntax::SmolStr;
 
@@ -25,21 +25,49 @@ pub(crate) fn complete_dot(
         _ => return,
     };
 
+    let is_field_access = matches!(dot_access.kind, DotAccessKind::Field { .. });
+    let is_method_access_with_parens =
+        matches!(dot_access.kind, DotAccessKind::Method { has_parens: true });
+    let traits_in_scope = ctx.traits_in_scope();
+
     // Suggest .await syntax for types that implement Future trait
-    if receiver_ty.impls_into_future(ctx.db) {
+    if let Some(future_output) = receiver_ty.into_future_output(ctx.db) {
+        let await_str = SmolStr::new_static("await");
         let mut item = CompletionItem::new(
             CompletionItemKind::Keyword,
             ctx.source_range(),
-            SmolStr::new_static("await"),
+            await_str.clone(),
             ctx.edition,
         );
         item.detail("expr.await");
         item.add_to(acc, ctx.db);
-    }
 
-    let is_field_access = matches!(dot_access.kind, DotAccessKind::Field { .. });
-    let is_method_access_with_parens =
-        matches!(dot_access.kind, DotAccessKind::Method { has_parens: true });
+        // Completions that skip `.await`, e.g. `.await.foo()`.
+        let dot_access_kind = match &dot_access.kind {
+            DotAccessKind::Field { receiver_is_ambiguous_float_literal: _ } => {
+                DotAccessKind::Field { receiver_is_ambiguous_float_literal: false }
+            }
+            it @ DotAccessKind::Method { .. } => *it,
+        };
+        let dot_access = DotAccess {
+            receiver: dot_access.receiver.clone(),
+            receiver_ty: Some(hir::TypeInfo { original: future_output.clone(), adjusted: None }),
+            kind: dot_access_kind,
+            ctx: dot_access.ctx,
+        };
+        complete_fields(
+            acc,
+            ctx,
+            &future_output,
+            |acc, field, ty| acc.add_field(ctx, &dot_access, Some(await_str.clone()), field, &ty),
+            |acc, field, ty| acc.add_tuple_field(ctx, Some(await_str.clone()), field, &ty),
+            is_field_access,
+            is_method_access_with_parens,
+        );
+        complete_methods(ctx, &future_output, &traits_in_scope, |func| {
+            acc.add_method(ctx, &dot_access, func, Some(await_str.clone()), None)
+        });
+    }
 
     complete_fields(
         acc,
@@ -50,8 +78,44 @@ pub(crate) fn complete_dot(
         is_field_access,
         is_method_access_with_parens,
     );
+    complete_methods(ctx, receiver_ty, &traits_in_scope, |func| {
+        acc.add_method(ctx, dot_access, func, None, None)
+    });
 
-    complete_methods(ctx, receiver_ty, |func| acc.add_method(ctx, dot_access, func, None, None));
+    // FIXME:
+    // Checking for the existence of `iter()` is complicated in our setup, because we need to substitute
+    // its return type, so we instead check for `<&Self as IntoIterator>::IntoIter`.
+    // Does <&receiver_ty as IntoIterator>::IntoIter` exist? Assume `iter` is valid
+    let iter = receiver_ty
+        .strip_references()
+        .add_reference(hir::Mutability::Shared)
+        .into_iterator_iter(ctx.db)
+        .map(|ty| (ty, SmolStr::new_static("iter()")));
+    // Does <receiver_ty as IntoIterator>::IntoIter` exist?
+    let into_iter = || {
+        receiver_ty
+            .clone()
+            .into_iterator_iter(ctx.db)
+            .map(|ty| (ty, SmolStr::new_static("into_iter()")))
+    };
+    if let Some((iter, iter_sym)) = iter.or_else(into_iter) {
+        // Skip iterators, e.g. complete `.iter().filter_map()`.
+        let dot_access_kind = match &dot_access.kind {
+            DotAccessKind::Field { receiver_is_ambiguous_float_literal: _ } => {
+                DotAccessKind::Field { receiver_is_ambiguous_float_literal: false }
+            }
+            it @ DotAccessKind::Method { .. } => *it,
+        };
+        let dot_access = DotAccess {
+            receiver: dot_access.receiver.clone(),
+            receiver_ty: Some(hir::TypeInfo { original: iter.clone(), adjusted: None }),
+            kind: dot_access_kind,
+            ctx: dot_access.ctx,
+        };
+        complete_methods(ctx, &iter, &traits_in_scope, |func| {
+            acc.add_method(ctx, &dot_access, func, Some(iter_sym.clone()), None)
+        });
+    }
 }
 
 pub(crate) fn complete_undotted_self(
@@ -94,18 +158,16 @@ pub(crate) fn complete_undotted_self(
                         in_breakable: expr_ctx.in_breakable,
                     },
                 },
-                Some(Name::new_symbol_root(sym::self_.clone())),
+                Some(SmolStr::new_static("self")),
                 field,
                 &ty,
             )
         },
-        |acc, field, ty| {
-            acc.add_tuple_field(ctx, Some(Name::new_symbol_root(sym::self_.clone())), field, &ty)
-        },
+        |acc, field, ty| acc.add_tuple_field(ctx, Some(SmolStr::new_static("self")), field, &ty),
         true,
         false,
     );
-    complete_methods(ctx, &ty, |func| {
+    complete_methods(ctx, &ty, &ctx.traits_in_scope(), |func| {
         acc.add_method(
             ctx,
             &DotAccess {
@@ -118,7 +180,7 @@ pub(crate) fn complete_undotted_self(
                 },
             },
             func,
-            Some(Name::new_symbol_root(sym::self_.clone())),
+            Some(SmolStr::new_static("self")),
             None,
         )
     });
@@ -160,6 +222,7 @@ fn complete_fields(
 fn complete_methods(
     ctx: &CompletionContext<'_>,
     receiver: &hir::Type,
+    traits_in_scope: &FxHashSet<hir::TraitId>,
     f: impl FnMut(hir::Function),
 ) {
     struct Callback<'a, F> {
@@ -205,7 +268,7 @@ fn complete_methods(
     receiver.iterate_method_candidates_split_inherent(
         ctx.db,
         &ctx.scope,
-        &ctx.traits_in_scope(),
+        traits_in_scope,
         Some(ctx.module),
         None,
         Callback { ctx, f, seen_methods: FxHashSet::default() },
@@ -214,25 +277,13 @@ fn complete_methods(
 
 #[cfg(test)]
 mod tests {
-    use expect_test::{expect, Expect};
+    use expect_test::expect;
 
-    use crate::tests::{
-        check_edit, completion_list_no_kw, completion_list_no_kw_with_private_editable,
-    };
-
-    fn check(ra_fixture: &str, expect: Expect) {
-        let actual = completion_list_no_kw(ra_fixture);
-        expect.assert_eq(&actual);
-    }
-
-    fn check_with_private_editable(ra_fixture: &str, expect: Expect) {
-        let actual = completion_list_no_kw_with_private_editable(ra_fixture);
-        expect.assert_eq(&actual);
-    }
+    use crate::tests::{check_edit, check_no_kw, check_with_private_editable};
 
     #[test]
     fn test_struct_field_and_method_completion() {
-        check(
+        check_no_kw(
             r#"
 struct S { foo: u32 }
 impl S {
@@ -249,7 +300,7 @@ fn foo(s: S) { s.$0 }
 
     #[test]
     fn no_unstable_method_on_stable() {
-        check(
+        check_no_kw(
             r#"
 //- /main.rs crate:main deps:std
 fn foo(s: std::S) { s.$0 }
@@ -266,7 +317,7 @@ impl S {
 
     #[test]
     fn unstable_method_on_nightly() {
-        check(
+        check_no_kw(
             r#"
 //- toolchain:nightly
 //- /main.rs crate:main deps:std
@@ -286,7 +337,7 @@ impl S {
 
     #[test]
     fn test_struct_field_completion_self() {
-        check(
+        check_no_kw(
             r#"
 struct S { the_field: (u32,) }
 impl S {
@@ -302,7 +353,7 @@ impl S {
 
     #[test]
     fn test_struct_field_completion_autoderef() {
-        check(
+        check_no_kw(
             r#"
 struct A { the_field: (u32, i32) }
 impl A {
@@ -318,7 +369,7 @@ impl A {
 
     #[test]
     fn test_no_struct_field_completion_for_method_call() {
-        check(
+        check_no_kw(
             r#"
 struct A { the_field: u32 }
 fn foo(a: A) { a.$0() }
@@ -329,7 +380,7 @@ fn foo(a: A) { a.$0() }
 
     #[test]
     fn test_visibility_filtering() {
-        check(
+        check_no_kw(
             r#"
 //- /lib.rs crate:lib new_source_root:local
 pub mod m {
@@ -348,7 +399,7 @@ fn foo(a: lib::m::A) { a.$0 }
             "#]],
         );
 
-        check(
+        check_no_kw(
             r#"
 //- /lib.rs crate:lib new_source_root:library
 pub mod m {
@@ -367,7 +418,7 @@ fn foo(a: lib::m::A) { a.$0 }
             "#]],
         );
 
-        check(
+        check_no_kw(
             r#"
 //- /lib.rs crate:lib new_source_root:library
 pub mod m {
@@ -384,7 +435,7 @@ fn foo(a: lib::m::A) { a.$0 }
             "#]],
         );
 
-        check(
+        check_no_kw(
             r#"
 //- /lib.rs crate:lib new_source_root:local
 pub struct A {}
@@ -402,7 +453,7 @@ fn foo(a: lib::A) { a.$0 }
                 me pub_method() fn(&self)
             "#]],
         );
-        check(
+        check_no_kw(
             r#"
 //- /lib.rs crate:lib new_source_root:library
 pub struct A {}
@@ -524,7 +575,7 @@ fn foo(a: lib::A) { a.$0 }
 
     #[test]
     fn test_local_impls() {
-        check(
+        check_no_kw(
             r#"
 pub struct A {}
 mod m {
@@ -553,7 +604,7 @@ fn foo(a: A) {
 
     #[test]
     fn test_doc_hidden_filtering() {
-        check(
+        check_no_kw(
             r#"
 //- /lib.rs crate:lib deps:dep
 fn foo(a: dep::A) { a.$0 }
@@ -580,7 +631,7 @@ impl A {
 
     #[test]
     fn test_union_field_completion() {
-        check(
+        check_no_kw(
             r#"
 union U { field: u8, other: u16 }
 fn foo(u: U) { u.$0 }
@@ -594,7 +645,7 @@ fn foo(u: U) { u.$0 }
 
     #[test]
     fn test_method_completion_only_fitting_impls() {
-        check(
+        check_no_kw(
             r#"
 struct A<T> {}
 impl A<u32> {
@@ -613,7 +664,7 @@ fn foo(a: A<u32>) { a.$0 }
 
     #[test]
     fn test_trait_method_completion() {
-        check(
+        check_no_kw(
             r#"
 struct A {}
 trait Trait { fn the_method(&self); }
@@ -643,7 +694,7 @@ fn foo(a: A) { a.the_method();$0 }
 
     #[test]
     fn test_trait_method_completion_deduplicated() {
-        check(
+        check_no_kw(
             r"
 struct A {}
 trait Trait { fn the_method(&self); }
@@ -658,7 +709,7 @@ fn foo(a: &A) { a.$0 }
 
     #[test]
     fn completes_trait_method_from_other_module() {
-        check(
+        check_no_kw(
             r"
 struct A {}
 mod m {
@@ -676,7 +727,7 @@ fn foo(a: A) { a.$0 }
 
     #[test]
     fn test_no_non_self_method() {
-        check(
+        check_no_kw(
             r#"
 struct A {}
 impl A {
@@ -692,7 +743,7 @@ fn foo(a: A) {
 
     #[test]
     fn test_tuple_field_completion() {
-        check(
+        check_no_kw(
             r#"
 fn foo() {
    let b = (0, 3.14);
@@ -708,7 +759,7 @@ fn foo() {
 
     #[test]
     fn test_tuple_struct_field_completion() {
-        check(
+        check_no_kw(
             r#"
 struct S(i32, f64);
 fn foo() {
@@ -725,7 +776,7 @@ fn foo() {
 
     #[test]
     fn test_tuple_field_inference() {
-        check(
+        check_no_kw(
             r#"
 pub struct S;
 impl S { pub fn blah(&self) {} }
@@ -747,7 +798,7 @@ impl T {
 
     #[test]
     fn test_field_no_same_name() {
-        check(
+        check_no_kw(
             r#"
 //- minicore: deref
 struct A { field: u8 }
@@ -770,7 +821,7 @@ fn test(a: A) {
 
     #[test]
     fn test_tuple_field_no_same_index() {
-        check(
+        check_no_kw(
             r#"
 //- minicore: deref
 struct A(u8);
@@ -793,7 +844,7 @@ fn test(a: A) {
 
     #[test]
     fn test_tuple_struct_deref_to_tuple_no_same_index() {
-        check(
+        check_no_kw(
             r#"
 //- minicore: deref
 struct A(u8);
@@ -815,7 +866,7 @@ fn test(a: A) {
 
     #[test]
     fn test_completion_works_in_consts() {
-        check(
+        check_no_kw(
             r#"
 struct A { the_field: u32 }
 const X: u32 = {
@@ -830,7 +881,7 @@ const X: u32 = {
 
     #[test]
     fn works_in_simple_macro_1() {
-        check(
+        check_no_kw(
             r#"
 macro_rules! m { ($e:expr) => { $e } }
 struct A { the_field: u32 }
@@ -847,7 +898,7 @@ fn foo(a: A) {
     #[test]
     fn works_in_simple_macro_2() {
         // this doesn't work yet because the macro doesn't expand without the token -- maybe it can be fixed with better recovery
-        check(
+        check_no_kw(
             r#"
 macro_rules! m { ($e:expr) => { $e } }
 struct A { the_field: u32 }
@@ -863,7 +914,7 @@ fn foo(a: A) {
 
     #[test]
     fn works_in_simple_macro_recursive_1() {
-        check(
+        check_no_kw(
             r#"
 macro_rules! m { ($e:expr) => { $e } }
 struct A { the_field: u32 }
@@ -879,7 +930,7 @@ fn foo(a: A) {
 
     #[test]
     fn macro_expansion_resilient() {
-        check(
+        check_no_kw(
             r#"
 macro_rules! d {
     () => {};
@@ -905,7 +956,7 @@ fn foo(a: A) {
 
     #[test]
     fn test_method_completion_issue_3547() {
-        check(
+        check_no_kw(
             r#"
 struct HashSet<T> {}
 impl<T> HashSet<T> {
@@ -924,7 +975,7 @@ fn foo() {
 
     #[test]
     fn completes_method_call_when_receiver_is_a_macro_call() {
-        check(
+        check_no_kw(
             r#"
 struct S;
 impl S { fn foo(&self) {} }
@@ -939,7 +990,7 @@ fn main() { make_s!().f$0; }
 
     #[test]
     fn completes_after_macro_call_in_submodule() {
-        check(
+        check_no_kw(
             r#"
 macro_rules! empty {
     () => {};
@@ -967,7 +1018,7 @@ mod foo {
 
     #[test]
     fn issue_8931() {
-        check(
+        check_no_kw(
             r#"
 //- minicore: fn
 struct S;
@@ -994,7 +1045,7 @@ impl S {
 
     #[test]
     fn completes_bare_fields_and_methods_in_methods() {
-        check(
+        check_no_kw(
             r#"
 struct Foo { field: i32 }
 
@@ -1008,7 +1059,7 @@ impl Foo { fn foo(&self) { $0 } }"#,
                 bt u32              u32
             "#]],
         );
-        check(
+        check_no_kw(
             r#"
 struct Foo(i32);
 
@@ -1026,7 +1077,7 @@ impl Foo { fn foo(&mut self) { $0 } }"#,
 
     #[test]
     fn macro_completion_after_dot() {
-        check(
+        check_no_kw(
             r#"
 macro_rules! m {
     ($e:expr) => { $e };
@@ -1051,7 +1102,7 @@ fn f() {
 
     #[test]
     fn completes_method_call_when_receiver_type_has_errors_issue_10297() {
-        check(
+        check_no_kw(
             r#"
 //- minicore: iterator, sized
 struct Vec<T>;
@@ -1102,7 +1153,7 @@ fn main() {
 
     #[test]
     fn issue_12484() {
-        check(
+        check_no_kw(
             r#"
 //- minicore: sized
 trait SizeUser {
@@ -1124,7 +1175,7 @@ fn test(thing: impl Encrypt) {
 
     #[test]
     fn only_consider_same_type_once() {
-        check(
+        check_no_kw(
             r#"
 //- minicore: deref
 struct A(u8);
@@ -1150,7 +1201,7 @@ fn test(a: A) {
 
     #[test]
     fn no_inference_var_in_completion() {
-        check(
+        check_no_kw(
             r#"
 struct S<T>(T);
 fn test(s: S<Unknown>) {
@@ -1165,7 +1216,7 @@ fn test(s: S<Unknown>) {
 
     #[test]
     fn assoc_impl_1() {
-        check(
+        check_no_kw(
             r#"
 //- minicore: deref
 fn main() {
@@ -1206,7 +1257,7 @@ impl<F: core::ops::Deref<Target = impl Bar>> Foo<F> {
 
     #[test]
     fn assoc_impl_2() {
-        check(
+        check_no_kw(
             r#"
 //- minicore: deref
 fn main() {
@@ -1242,7 +1293,7 @@ impl<B: Bar, F: core::ops::Deref<Target = B>> Foo<F> {
 
     #[test]
     fn test_struct_function_field_completion() {
-        check(
+        check_no_kw(
             r#"
 struct S { va_field: u32, fn_field: fn() }
 fn foo() { S { va_field: 0, fn_field: || {} }.fi$0() }
@@ -1267,7 +1318,7 @@ fn foo() { (S { va_field: 0, fn_field: || {} }.fn_field)() }
 
     #[test]
     fn test_tuple_function_field_completion() {
-        check(
+        check_no_kw(
             r#"
 struct B(u32, fn())
 fn foo() {
@@ -1301,7 +1352,7 @@ fn foo() {
 
     #[test]
     fn test_fn_field_dot_access_method_has_parens_false() {
-        check(
+        check_no_kw(
             r#"
 struct Foo { baz: fn() }
 impl Foo {
@@ -1318,4 +1369,101 @@ fn baz() {
             "#]],
         );
     }
+
+    #[test]
+    fn skip_iter() {
+        check_no_kw(
+            r#"
+        //- minicore: iterator
+        fn foo() {
+            [].$0
+        }
+        "#,
+            expect![[r#"
+                me clone() (as Clone)                                       fn(&self) -> Self
+                me into_iter() (as IntoIterator) fn(self) -> <Self as IntoIterator>::IntoIter
+            "#]],
+        );
+        check_no_kw(
+            r#"
+//- minicore: iterator
+struct MyIntoIter;
+impl IntoIterator for MyIntoIter {
+    type Item = ();
+    type IntoIter = MyIterator;
+    fn into_iter(self) -> Self::IntoIter {
+        MyIterator
+    }
+}
+
+struct MyIterator;
+impl Iterator for MyIterator {
+    type Item = ();
+    fn next(&mut self) -> Self::Item {}
+}
+
+fn foo() {
+    MyIntoIter.$0
+}
+"#,
+            expect![[r#"
+                me into_iter() (as IntoIterator)                fn(self) -> <Self as IntoIterator>::IntoIter
+                me into_iter().by_ref() (as Iterator)                             fn(&mut self) -> &mut Self
+                me into_iter().into_iter() (as IntoIterator)    fn(self) -> <Self as IntoIterator>::IntoIter
+                me into_iter().next() (as Iterator)        fn(&mut self) -> Option<<Self as Iterator>::Item>
+                me into_iter().nth(…) (as Iterator) fn(&mut self, usize) -> Option<<Self as Iterator>::Item>
+            "#]],
+        );
+    }
+
+    #[test]
+    fn skip_await() {
+        check_no_kw(
+            r#"
+//- minicore: future
+struct Foo;
+impl Foo {
+    fn foo(self) {}
+}
+
+async fn foo() -> Foo { Foo }
+
+async fn bar() {
+    foo().$0
+}
+"#,
+            expect![[r#"
+    me await.foo()                                                                      fn(self)
+    me into_future() (use core::future::IntoFuture) fn(self) -> <Self as IntoFuture>::IntoFuture
+"#]],
+        );
+        check_edit(
+            "foo",
+            r#"
+//- minicore: future
+struct Foo;
+impl Foo {
+    fn foo(self) {}
+}
+
+async fn foo() -> Foo { Foo }
+
+async fn bar() {
+    foo().$0
+}
+"#,
+            r#"
+struct Foo;
+impl Foo {
+    fn foo(self) {}
+}
+
+async fn foo() -> Foo { Foo }
+
+async fn bar() {
+    foo().await.foo();$0
+}
+"#,
+        );
+    }
 }
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/env_vars.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/env_vars.rs
index 0b6790d42a6..40af5203e9c 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/env_vars.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/env_vars.rs
@@ -68,43 +68,40 @@ pub(crate) fn complete_cargo_env_vars(
 mod tests {
     use crate::tests::{check_edit, completion_list};
 
-    fn check(macro_name: &str) {
+    #[test]
+    fn completes_env_variable_in_env() {
         check_edit(
             "CARGO_BIN_NAME",
-            &format!(
-                r#"
-            #[rustc_builtin_macro]
-            macro {macro_name} {{
-                ($var:literal) => {{ 0 }}
-            }}
-
-            fn main() {{
-                let foo = {macro_name}!("CAR$0");
-            }}
-        "#
-            ),
-            &format!(
-                r#"
-            #[rustc_builtin_macro]
-            macro {macro_name} {{
-                ($var:literal) => {{ 0 }}
-            }}
-
-            fn main() {{
-                let foo = {macro_name}!("CARGO_BIN_NAME");
-            }}
-        "#
-            ),
+            r#"
+//- minicore: env
+fn main() {
+    let foo = env!("CAR$0");
+}
+        "#,
+            r#"
+fn main() {
+    let foo = env!("CARGO_BIN_NAME");
+}
+        "#,
         );
     }
-    #[test]
-    fn completes_env_variable_in_env() {
-        check("env")
-    }
 
     #[test]
     fn completes_env_variable_in_option_env() {
-        check("option_env");
+        check_edit(
+            "CARGO_BIN_NAME",
+            r#"
+//- minicore: env
+fn main() {
+    let foo = option_env!("CAR$0");
+}
+        "#,
+            r#"
+fn main() {
+    let foo = option_env!("CARGO_BIN_NAME");
+}
+        "#,
+        );
     }
 
     #[test]
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 c2e5eefe101..db18b531d7c 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
@@ -62,6 +62,7 @@ pub(crate) fn complete_expr_path(
         in_condition,
         incomplete_let,
         ref ref_expr_parent,
+        after_amp,
         ref is_func_update,
         ref innermost_ret_ty,
         ref impl_,
@@ -69,8 +70,23 @@ pub(crate) fn complete_expr_path(
         ..
     } = expr_ctx;
 
-    let wants_mut_token =
-        ref_expr_parent.as_ref().map(|it| it.mut_token().is_none()).unwrap_or(false);
+    let (has_raw_token, has_const_token, has_mut_token) = ref_expr_parent
+        .as_ref()
+        .map(|it| (it.raw_token().is_some(), it.const_token().is_some(), it.mut_token().is_some()))
+        .unwrap_or((false, false, false));
+
+    let wants_raw_token = ref_expr_parent.is_some() && !has_raw_token && after_amp;
+    let wants_const_token =
+        ref_expr_parent.is_some() && has_raw_token && !has_const_token && !has_mut_token;
+    let wants_mut_token = if ref_expr_parent.is_some() {
+        if has_raw_token {
+            !has_const_token && !has_mut_token
+        } else {
+            !has_mut_token
+        }
+    } else {
+        false
+    };
 
     let scope_def_applicable = |def| match def {
         ScopeDef::GenericParam(hir::GenericParam::LifetimeParam(_)) | ScopeDef::Label(_) => false,
@@ -354,6 +370,12 @@ pub(crate) fn complete_expr_path(
                         add_keyword("else if", "else if $1 {\n    $0\n}");
                     }
 
+                    if wants_raw_token {
+                        add_keyword("raw", "raw ");
+                    }
+                    if wants_const_token {
+                        add_keyword("const", "const ");
+                    }
                     if wants_mut_token {
                         add_keyword("mut", "mut ");
                     }
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/extern_abi.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/extern_abi.rs
index bcfda928c4d..7c2cc2a6c1d 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/extern_abi.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/extern_abi.rs
@@ -65,18 +65,13 @@ pub(crate) fn complete_extern_abi(
 
 #[cfg(test)]
 mod tests {
-    use expect_test::{expect, Expect};
+    use expect_test::expect;
 
-    use crate::tests::{check_edit, completion_list_no_kw};
-
-    fn check(ra_fixture: &str, expect: Expect) {
-        let actual = completion_list_no_kw(ra_fixture);
-        expect.assert_eq(&actual);
-    }
+    use crate::tests::{check_edit, check_no_kw};
 
     #[test]
     fn only_completes_in_string_literals() {
-        check(
+        check_no_kw(
             r#"
 $0 fn foo {}
 "#,
@@ -86,7 +81,7 @@ $0 fn foo {}
 
     #[test]
     fn requires_extern_prefix() {
-        check(
+        check_no_kw(
             r#"
 "$0" fn foo {}
 "#,
@@ -96,7 +91,7 @@ $0 fn foo {}
 
     #[test]
     fn works() {
-        check(
+        check_no_kw(
             r#"
 extern "$0" fn foo {}
 "#,
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs
index 3b2b2fd706e..73313eeaa6b 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs
@@ -5,7 +5,7 @@ use ide_db::imports::{
     insert_use::ImportScope,
 };
 use itertools::Itertools;
-use syntax::{ast, AstNode, SyntaxNode, ToSmolStr, T};
+use syntax::{ast, AstNode, SyntaxNode, ToSmolStr};
 
 use crate::{
     config::AutoImportExclusionType,
@@ -403,10 +403,11 @@ fn import_on_the_fly_method(
 
 fn import_name(ctx: &CompletionContext<'_>) -> String {
     let token_kind = ctx.token.kind();
-    if matches!(token_kind, T![.] | T![::]) {
-        String::new()
-    } else {
+
+    if token_kind.is_any_identifier() {
         ctx.token.to_string()
+    } else {
+        String::new()
     }
 }
 
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/format_string.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/format_string.rs
index a87c60c694a..dcd40c3412c 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/format_string.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/format_string.rs
@@ -61,18 +61,13 @@ pub(crate) fn format_string(
 
 #[cfg(test)]
 mod tests {
-    use expect_test::{expect, Expect};
+    use expect_test::expect;
 
-    use crate::tests::{check_edit, completion_list_no_kw};
-
-    fn check(ra_fixture: &str, expect: Expect) {
-        let actual = completion_list_no_kw(ra_fixture);
-        expect.assert_eq(&actual);
-    }
+    use crate::tests::{check_edit, check_no_kw};
 
     #[test]
     fn works_when_wrapped() {
-        check(
+        check_no_kw(
             r#"
 //- minicore: fmt
 macro_rules! print {
@@ -89,7 +84,7 @@ fn main() {
 
     #[test]
     fn no_completion_without_brace() {
-        check(
+        check_no_kw(
             r#"
 //- minicore: fmt
 fn main() {
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs
index 80d72b460f9..6d1945c4534 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs
@@ -514,18 +514,13 @@ fn function_declaration(
 
 #[cfg(test)]
 mod tests {
-    use expect_test::{expect, Expect};
+    use expect_test::expect;
 
-    use crate::tests::{check_edit, completion_list_no_kw};
-
-    fn check(ra_fixture: &str, expect: Expect) {
-        let actual = completion_list_no_kw(ra_fixture);
-        expect.assert_eq(&actual)
-    }
+    use crate::tests::{check_edit, check_no_kw};
 
     #[test]
     fn no_completion_inside_fn() {
-        check(
+        check_no_kw(
             r"
 trait Test { fn test(); fn test2(); }
 struct T;
@@ -544,7 +539,7 @@ impl Test for T {
             "#]],
         );
 
-        check(
+        check_no_kw(
             r"
 trait Test { fn test(); fn test2(); }
 struct T;
@@ -558,7 +553,7 @@ impl Test for T {
             expect![[""]],
         );
 
-        check(
+        check_no_kw(
             r"
 trait Test { fn test(); fn test2(); }
 struct T;
@@ -573,7 +568,7 @@ impl Test for T {
         );
 
         // https://github.com/rust-lang/rust-analyzer/pull/5976#issuecomment-692332191
-        check(
+        check_no_kw(
             r"
 trait Test { fn test(); fn test2(); }
 struct T;
@@ -587,7 +582,7 @@ impl Test for T {
             expect![[r#""#]],
         );
 
-        check(
+        check_no_kw(
             r"
 trait Test { fn test(_: i32); fn test2(); }
 struct T;
@@ -606,7 +601,7 @@ impl Test for T {
             "#]],
         );
 
-        check(
+        check_no_kw(
             r"
 trait Test { fn test(_: fn()); fn test2(); }
 struct T;
@@ -624,7 +619,7 @@ impl Test for T {
 
     #[test]
     fn no_completion_inside_const() {
-        check(
+        check_no_kw(
             r"
 trait Test { const TEST: fn(); const TEST2: u32; type Test; fn test(); }
 struct T;
@@ -636,7 +631,7 @@ impl Test for T {
             expect![[r#""#]],
         );
 
-        check(
+        check_no_kw(
             r"
 trait Test { const TEST: u32; const TEST2: u32; type Test; fn test(); }
 struct T;
@@ -653,7 +648,7 @@ impl Test for T {
             "#]],
         );
 
-        check(
+        check_no_kw(
             r"
 trait Test { const TEST: u32; const TEST2: u32; type Test; fn test(); }
 struct T;
@@ -670,7 +665,7 @@ impl Test for T {
             "#]],
         );
 
-        check(
+        check_no_kw(
             r"
 trait Test { const TEST: u32; const TEST2: u32; type Test; fn test(); }
 struct T;
@@ -689,7 +684,7 @@ impl Test for T {
             "#]],
         );
 
-        check(
+        check_no_kw(
             r"
 trait Test { const TEST: u32; const TEST2: u32; type Test; fn test(); }
 struct T;
@@ -703,7 +698,7 @@ impl Test for T {
             expect![[""]],
         );
 
-        check(
+        check_no_kw(
             r"
 trait Test { const TEST: u32; const TEST2: u32; type Test; fn test(); }
 struct T;
@@ -720,7 +715,7 @@ impl Test for T {
 
     #[test]
     fn no_completion_inside_type() {
-        check(
+        check_no_kw(
             r"
 trait Test { type Test; type Test2; fn test(); }
 struct T;
@@ -737,7 +732,7 @@ impl Test for T {
             "#]],
         );
 
-        check(
+        check_no_kw(
             r"
 trait Test { type Test; type Test2; fn test(); }
 struct T;
@@ -1263,7 +1258,7 @@ impl Foo<u32> for Bar {
 
     #[test]
     fn works_directly_in_impl() {
-        check(
+        check_no_kw(
             r#"
 trait Tr {
     fn required();
@@ -1277,7 +1272,7 @@ impl Tr for () {
             fn fn required()
         "#]],
         );
-        check(
+        check_no_kw(
             r#"
 trait Tr {
     fn provided() {}
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 4700ed6c1ae..6541ee502d8 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
@@ -32,14 +32,9 @@ pub(crate) fn complete_for_and_where(
 
 #[cfg(test)]
 mod tests {
-    use expect_test::{expect, Expect};
+    use expect_test::expect;
 
-    use crate::tests::{check_edit, completion_list};
-
-    fn check(ra_fixture: &str, expect: Expect) {
-        let actual = completion_list(ra_fixture);
-        expect.assert_eq(&actual)
-    }
+    use crate::tests::{check, check_edit};
 
     #[test]
     fn test_else_edit_after_if() {
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/lifetime.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/lifetime.rs
index 0692446381b..53a62fe49c5 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/lifetime.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/lifetime.rs
@@ -59,14 +59,9 @@ pub(crate) fn complete_label(
 
 #[cfg(test)]
 mod tests {
-    use expect_test::{expect, Expect};
+    use expect_test::expect;
 
-    use crate::tests::{check_edit, completion_list};
-
-    fn check(ra_fixture: &str, expect: Expect) {
-        let actual = completion_list(ra_fixture);
-        expect.assert_eq(&actual);
-    }
+    use crate::tests::{check, check_edit};
 
     #[test]
     fn check_lifetime_edit() {
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/mod_.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/mod_.rs
index f12f011a6bd..bafe3294209 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/mod_.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/mod_.rs
@@ -159,14 +159,9 @@ fn module_chain_to_containing_module_file(
 
 #[cfg(test)]
 mod tests {
-    use expect_test::{expect, Expect};
+    use expect_test::expect;
 
-    use crate::tests::completion_list;
-
-    fn check(ra_fixture: &str, expect: Expect) {
-        let actual = completion_list(ra_fixture);
-        expect.assert_eq(&actual);
-    }
+    use crate::tests::check;
 
     #[test]
     fn lib_module_completion() {
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs
index 7b57eea0524..67ea05e002b 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs
@@ -303,7 +303,7 @@ fn include_references(initial_element: &ast::Expr) -> (ast::Expr, ast::Expr) {
 
         resulting_element = ast::Expr::from(parent_deref_element);
 
-        new_element_opt = make::expr_prefix(syntax::T![*], new_element_opt);
+        new_element_opt = make::expr_prefix(syntax::T![*], new_element_opt).into();
     }
 
     if let Some(first_ref_expr) = resulting_element.syntax().parent().and_then(ast::RefExpr::cast) {
@@ -401,18 +401,13 @@ fn add_custom_postfix_completions(
 
 #[cfg(test)]
 mod tests {
-    use expect_test::{expect, Expect};
+    use expect_test::expect;
 
     use crate::{
-        tests::{check_edit, check_edit_with_config, completion_list, TEST_CONFIG},
+        tests::{check, check_edit, check_edit_with_config, TEST_CONFIG},
         CompletionConfig, Snippet,
     };
 
-    fn check(ra_fixture: &str, expect: Expect) {
-        let actual = completion_list(ra_fixture);
-        expect.assert_eq(&actual)
-    }
-
     #[test]
     fn postfix_completion_works_for_trivial_path_expression() {
         check(
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 3705e2c73d6..3a2a4a23a19 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs
@@ -146,6 +146,7 @@ pub(crate) struct PathExprCtx {
     pub(crate) in_condition: bool,
     pub(crate) incomplete_let: bool,
     pub(crate) ref_expr_parent: Option<ast::RefExpr>,
+    pub(crate) after_amp: bool,
     /// The surrounding RecordExpression we are completing a functional update
     pub(crate) is_func_update: Option<ast::RecordExpr>,
     pub(crate) self_param: Option<hir::SelfParam>,
@@ -390,7 +391,7 @@ pub(crate) struct DotAccess {
     pub(crate) ctx: DotAccessExprCtx,
 }
 
-#[derive(Debug)]
+#[derive(Debug, Clone, Copy)]
 pub(crate) enum DotAccessKind {
     Field {
         /// True if the receiver is an integer and there is no ident in the original file after it yet
@@ -402,7 +403,7 @@ pub(crate) enum DotAccessKind {
     },
 }
 
-#[derive(Debug, PartialEq, Eq)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
 pub(crate) struct DotAccessExprCtx {
     pub(crate) in_block_expr: bool,
     pub(crate) in_breakable: BreakableKind,
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs
index acce62a041c..3c4d489c0ff 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs
@@ -123,10 +123,11 @@ fn expand(
 ) -> Option<ExpansionResult> {
     let _p = tracing::info_span!("CompletionContext::expand").entered();
 
+    // Left biased since there may already be an identifier token there, and we appended to it.
     if !sema.might_be_inside_macro_call(&fake_ident_token)
         && original_file
             .token_at_offset(original_offset + relative_offset)
-            .right_biased()
+            .left_biased()
             .is_some_and(|original_token| !sema.might_be_inside_macro_call(&original_token))
     {
         // Recursion base case.
@@ -1150,6 +1151,9 @@ fn classify_name_ref(
         let after_if_expr = after_if_expr(it.clone());
         let ref_expr_parent =
             path.as_single_name_ref().and_then(|_| it.parent()).and_then(ast::RefExpr::cast);
+        let after_amp = non_trivia_sibling(it.clone().into(), Direction::Prev)
+            .map(|it| it.kind() == SyntaxKind::AMP)
+            .unwrap_or(false);
         let (innermost_ret_ty, self_param) = {
             let find_ret_ty = |it: SyntaxNode| {
                 if let Some(item) = ast::Item::cast(it.clone()) {
@@ -1219,6 +1223,7 @@ fn classify_name_ref(
                 after_if_expr,
                 in_condition,
                 ref_expr_parent,
+                after_amp,
                 is_func_update,
                 innermost_ret_ty,
                 self_param,
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 82a1c10c531..fc2bfc01e62 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
@@ -6,7 +6,7 @@ use crate::{
     tests::{position, TEST_CONFIG},
 };
 
-fn check_expected_type_and_name(ra_fixture: &str, expect: Expect) {
+fn check_expected_type_and_name(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
     let (db, pos) = position(ra_fixture);
     let config = TEST_CONFIG;
     let (completion_context, _analysis) = CompletionContext::new(&db, pos, &config).unwrap();
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 b91f915619d..dc2f9a76802 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs
@@ -79,7 +79,7 @@ pub struct CompletionItem {
     // FIXME: We shouldn't expose Mutability here (that is HIR types at all), its fine for now though
     // until we have more splitting completions in which case we should think about
     // generalizing this. See https://github.com/rust-lang/rust-analyzer/issues/12571
-    pub ref_match: Option<(Mutability, TextSize)>,
+    pub ref_match: Option<(CompletionItemRefMode, TextSize)>,
 
     /// The import data to add to completion's edits.
     /// (ImportPath, LastSegment)
@@ -128,8 +128,15 @@ impl fmt::Debug for CompletionItem {
             s.field("relevance", &self.relevance);
         }
 
-        if let Some((mutability, offset)) = &self.ref_match {
-            s.field("ref_match", &format!("&{}@{offset:?}", mutability.as_keyword_for_ref()));
+        if let Some((ref_mode, offset)) = self.ref_match {
+            let prefix = match ref_mode {
+                CompletionItemRefMode::Reference(mutability) => match mutability {
+                    Mutability::Shared => "&",
+                    Mutability::Mut => "&mut ",
+                },
+                CompletionItemRefMode::Dereference => "*",
+            };
+            s.field("ref_match", &format!("{}@{offset:?}", prefix));
         }
         if self.trigger_call_info {
             s.field("trigger_call_info", &true);
@@ -400,6 +407,12 @@ impl CompletionItemKind {
     }
 }
 
+#[derive(Copy, Clone, Debug)]
+pub enum CompletionItemRefMode {
+    Reference(Mutability),
+    Dereference,
+}
+
 impl CompletionItem {
     pub(crate) fn new(
         kind: impl Into<CompletionItemKind>,
@@ -441,15 +454,14 @@ impl CompletionItem {
         let mut relevance = self.relevance;
         relevance.type_match = Some(CompletionRelevanceTypeMatch::Exact);
 
-        self.ref_match.map(|(mutability, offset)| {
-            (
-                format!("&{}{}", mutability.as_keyword_for_ref(), self.label.primary),
-                ide_db::text_edit::Indel::insert(
-                    offset,
-                    format!("&{}", mutability.as_keyword_for_ref()),
-                ),
-                relevance,
-            )
+        self.ref_match.map(|(mode, offset)| {
+            let prefix = match mode {
+                CompletionItemRefMode::Reference(Mutability::Shared) => "&",
+                CompletionItemRefMode::Reference(Mutability::Mut) => "&mut ",
+                CompletionItemRefMode::Dereference => "*",
+            };
+            let label = format!("{prefix}{}", self.label.primary);
+            (label, ide_db::text_edit::Indel::insert(offset, String::from(prefix)), relevance)
         })
     }
 }
@@ -473,7 +485,7 @@ pub(crate) struct Builder {
     deprecated: bool,
     trigger_call_info: bool,
     relevance: CompletionRelevance,
-    ref_match: Option<(Mutability, TextSize)>,
+    ref_match: Option<(CompletionItemRefMode, TextSize)>,
     edition: Edition,
 }
 
@@ -657,8 +669,12 @@ impl Builder {
         self.imports_to_add.push(import_to_add);
         self
     }
-    pub(crate) fn ref_match(&mut self, mutability: Mutability, offset: TextSize) -> &mut Builder {
-        self.ref_match = Some((mutability, offset));
+    pub(crate) fn ref_match(
+        &mut self,
+        ref_mode: CompletionItemRefMode,
+        offset: TextSize,
+    ) -> &mut Builder {
+        self.ref_match = Some((ref_mode, offset));
         self
     }
 }
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs b/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs
index ca6c9ad9f08..56d7eeaf8ea 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs
@@ -33,8 +33,9 @@ use crate::{
 pub use crate::{
     config::{AutoImportExclusionType, CallableSnippets, CompletionConfig},
     item::{
-        CompletionItem, CompletionItemKind, CompletionRelevance, CompletionRelevancePostfixMatch,
-        CompletionRelevanceReturnType, CompletionRelevanceTypeMatch,
+        CompletionItem, CompletionItemKind, CompletionItemRefMode, CompletionRelevance,
+        CompletionRelevancePostfixMatch, CompletionRelevanceReturnType,
+        CompletionRelevanceTypeMatch,
     },
     snippet::{Snippet, SnippetScope},
 };
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 c239ca512da..61e8114d381 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs
@@ -18,7 +18,7 @@ use ide_db::{
     imports::import_assets::LocatedImport,
     RootDatabase, SnippetCap, SymbolKind,
 };
-use syntax::{ast, format_smolstr, AstNode, Edition, SmolStr, SyntaxKind, TextRange, ToSmolStr};
+use syntax::{ast, format_smolstr, AstNode, SmolStr, SyntaxKind, TextRange, ToSmolStr};
 
 use crate::{
     context::{DotAccess, DotAccessKind, PathCompletionCtx, PathKind, PatternContext},
@@ -28,7 +28,8 @@ use crate::{
         literal::render_variant_lit,
         macro_::{render_macro, render_macro_pat},
     },
-    CompletionContext, CompletionItem, CompletionItemKind, CompletionRelevance,
+    CompletionContext, CompletionItem, CompletionItemKind, CompletionItemRefMode,
+    CompletionRelevance,
 };
 /// Interface for data and methods required for items rendering.
 #[derive(Debug, Clone)]
@@ -122,7 +123,7 @@ impl<'a> RenderContext<'a> {
 pub(crate) fn render_field(
     ctx: RenderContext<'_>,
     dot_access: &DotAccess,
-    receiver: Option<hir::Name>,
+    receiver: Option<SmolStr>,
     field: hir::Field,
     ty: &hir::Type,
 ) -> CompletionItem {
@@ -136,7 +137,7 @@ pub(crate) fn render_field(
     let mut item = CompletionItem::new(
         SymbolKind::Field,
         ctx.source_range(),
-        field_with_receiver(db, receiver.as_ref(), &name, ctx.completion.edition),
+        field_with_receiver(receiver.as_deref(), &name),
         ctx.completion.edition,
     );
     item.set_relevance(CompletionRelevance {
@@ -158,8 +159,7 @@ pub(crate) fn render_field(
 
         builder.replace(
             ctx.source_range(),
-            field_with_receiver(db, receiver.as_ref(), &escaped_name, ctx.completion.edition)
-                .into(),
+            field_with_receiver(receiver.as_deref(), &escaped_name).into(),
         );
 
         let expected_fn_type =
@@ -183,17 +183,12 @@ pub(crate) fn render_field(
 
         item.text_edit(builder.finish());
     } else {
-        item.insert_text(field_with_receiver(
-            db,
-            receiver.as_ref(),
-            &escaped_name,
-            ctx.completion.edition,
-        ));
+        item.insert_text(field_with_receiver(receiver.as_deref(), &escaped_name));
     }
     if let Some(receiver) = &dot_access.receiver {
         if let Some(original) = ctx.completion.sema.original_ast_node(receiver.clone()) {
-            if let Some(ref_match) = compute_ref_match(ctx.completion, ty) {
-                item.ref_match(ref_match, original.syntax().text_range().start());
+            if let Some(ref_mode) = compute_ref_match(ctx.completion, ty) {
+                item.ref_match(ref_mode, original.syntax().text_range().start());
             }
         }
     }
@@ -201,33 +196,21 @@ pub(crate) fn render_field(
     item.build(db)
 }
 
-fn field_with_receiver(
-    db: &RootDatabase,
-    receiver: Option<&hir::Name>,
-    field_name: &str,
-    edition: Edition,
-) -> SmolStr {
-    receiver.map_or_else(
-        || field_name.into(),
-        |receiver| format_smolstr!("{}.{field_name}", receiver.display(db, edition)),
-    )
+fn field_with_receiver(receiver: Option<&str>, field_name: &str) -> SmolStr {
+    receiver
+        .map_or_else(|| field_name.into(), |receiver| format_smolstr!("{}.{field_name}", receiver))
 }
 
 pub(crate) fn render_tuple_field(
     ctx: RenderContext<'_>,
-    receiver: Option<hir::Name>,
+    receiver: Option<SmolStr>,
     field: usize,
     ty: &hir::Type,
 ) -> CompletionItem {
     let mut item = CompletionItem::new(
         SymbolKind::Field,
         ctx.source_range(),
-        field_with_receiver(
-            ctx.db(),
-            receiver.as_ref(),
-            &field.to_string(),
-            ctx.completion.edition,
-        ),
+        field_with_receiver(receiver.as_deref(), &field.to_string()),
         ctx.completion.edition,
     );
     item.detail(ty.display(ctx.db(), ctx.completion.edition).to_string())
@@ -440,7 +423,7 @@ fn render_resolution_path(
 
     let name = local_name.display_no_db(ctx.completion.edition).to_smolstr();
     let mut item = render_resolution_simple_(ctx, &local_name, import_to_add, resolution);
-    if local_name.is_escaped(completion.edition) {
+    if local_name.needs_escape(completion.edition) {
         item.insert_text(local_name.display_no_db(completion.edition).to_smolstr());
     }
     // Add `<>` for generic types
@@ -638,20 +621,34 @@ fn compute_exact_name_match(ctx: &CompletionContext<'_>, completion_name: &str)
 fn compute_ref_match(
     ctx: &CompletionContext<'_>,
     completion_ty: &hir::Type,
-) -> Option<hir::Mutability> {
+) -> Option<CompletionItemRefMode> {
     let expected_type = ctx.expected_type.as_ref()?;
-    if completion_ty != expected_type {
-        let expected_type_without_ref = expected_type.remove_ref()?;
-        if completion_ty.autoderef(ctx.db).any(|deref_ty| deref_ty == expected_type_without_ref) {
+    let expected_without_ref = expected_type.remove_ref();
+    let completion_without_ref = completion_ty.remove_ref();
+
+    if completion_ty == expected_type {
+        return None;
+    }
+
+    if let Some(expected_without_ref) = &expected_without_ref {
+        if completion_ty.autoderef(ctx.db).any(|ty| ty == *expected_without_ref) {
             cov_mark::hit!(suggest_ref);
             let mutability = if expected_type.is_mutable_reference() {
                 hir::Mutability::Mut
             } else {
                 hir::Mutability::Shared
             };
-            return Some(mutability);
-        };
+            return Some(CompletionItemRefMode::Reference(mutability));
+        }
+    }
+
+    if let Some(completion_without_ref) = completion_without_ref {
+        if completion_without_ref == *expected_type && completion_without_ref.is_copy(ctx.db) {
+            cov_mark::hit!(suggest_deref);
+            return Some(CompletionItemRefMode::Dereference);
+        }
     }
+
     None
 }
 
@@ -664,16 +661,16 @@ fn path_ref_match(
     if let Some(original_path) = &path_ctx.original_path {
         // At least one char was typed by the user already, in that case look for the original path
         if let Some(original_path) = completion.sema.original_ast_node(original_path.clone()) {
-            if let Some(ref_match) = compute_ref_match(completion, ty) {
-                item.ref_match(ref_match, original_path.syntax().text_range().start());
+            if let Some(ref_mode) = compute_ref_match(completion, ty) {
+                item.ref_match(ref_mode, original_path.syntax().text_range().start());
             }
         }
     } else {
         // completion requested on an empty identifier, there is no path here yet.
         // FIXME: This might create inconsistent completions where we show a ref match in macro inputs
         // as long as nothing was typed yet
-        if let Some(ref_match) = compute_ref_match(completion, ty) {
-            item.ref_match(ref_match, completion.position.offset);
+        if let Some(ref_mode) = compute_ref_match(completion, ty) {
+            item.ref_match(ref_mode, completion.position.offset);
         }
     }
 }
@@ -693,20 +690,28 @@ mod tests {
     };
 
     #[track_caller]
-    fn check(ra_fixture: &str, kind: impl Into<CompletionItemKind>, expect: Expect) {
+    fn check(
+        #[rust_analyzer::rust_fixture] ra_fixture: &str,
+        kind: impl Into<CompletionItemKind>,
+        expect: Expect,
+    ) {
         let actual = do_completion(ra_fixture, kind.into());
         expect.assert_debug_eq(&actual);
     }
 
     #[track_caller]
-    fn check_kinds(ra_fixture: &str, kinds: &[CompletionItemKind], expect: Expect) {
+    fn check_kinds(
+        #[rust_analyzer::rust_fixture] ra_fixture: &str,
+        kinds: &[CompletionItemKind],
+        expect: Expect,
+    ) {
         let actual: Vec<_> =
             kinds.iter().flat_map(|&kind| do_completion(ra_fixture, kind)).collect();
         expect.assert_debug_eq(&actual);
     }
 
     #[track_caller]
-    fn check_function_relevance(ra_fixture: &str, expect: Expect) {
+    fn check_function_relevance(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
         let actual: Vec<_> =
             do_completion(ra_fixture, CompletionItemKind::SymbolKind(SymbolKind::Method))
                 .into_iter()
@@ -717,7 +722,11 @@ mod tests {
     }
 
     #[track_caller]
-    fn check_relevance_for_kinds(ra_fixture: &str, kinds: &[CompletionItemKind], expect: Expect) {
+    fn check_relevance_for_kinds(
+        #[rust_analyzer::rust_fixture] ra_fixture: &str,
+        kinds: &[CompletionItemKind],
+        expect: Expect,
+    ) {
         let mut actual = get_all_items(TEST_CONFIG, ra_fixture, None);
         actual.retain(|it| kinds.contains(&it.kind));
         actual.sort_by_key(|it| cmp::Reverse(it.relevance.score()));
@@ -725,7 +734,7 @@ mod tests {
     }
 
     #[track_caller]
-    fn check_relevance(ra_fixture: &str, expect: Expect) {
+    fn check_relevance(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
         let mut actual = get_all_items(TEST_CONFIG, ra_fixture, None);
         actual.retain(|it| it.kind != CompletionItemKind::Snippet);
         actual.retain(|it| it.kind != CompletionItemKind::Keyword);
@@ -2053,7 +2062,42 @@ fn main() {
     }
 
     #[test]
-    fn suggest_deref() {
+    fn suggest_deref_copy() {
+        cov_mark::check!(suggest_deref);
+        check_relevance(
+            r#"
+//- minicore: copy
+struct Foo;
+
+impl Copy for Foo {}
+impl Clone for Foo {
+    fn clone(&self) -> Self { *self }
+}
+
+fn bar(x: Foo) {}
+
+fn main() {
+    let foo = &Foo;
+    bar($0);
+}
+"#,
+            expect![[r#"
+                st Foo Foo [type]
+                st Foo Foo [type]
+                ex Foo  [type]
+                lc foo &Foo [local]
+                lc *foo [type+local]
+                fn bar(…) fn(Foo) []
+                fn main() fn() []
+                md core  []
+                tt Clone  []
+                tt Copy  []
+            "#]],
+        );
+    }
+
+    #[test]
+    fn suggest_deref_trait() {
         check_relevance(
             r#"
 //- minicore: deref
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 a859d79e243..3b97d67169e 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
@@ -23,7 +23,7 @@ use crate::{
 #[derive(Debug)]
 enum FuncKind<'ctx> {
     Function(&'ctx PathCompletionCtx),
-    Method(&'ctx DotAccess, Option<hir::Name>),
+    Method(&'ctx DotAccess, Option<SmolStr>),
 }
 
 pub(crate) fn render_fn(
@@ -39,7 +39,7 @@ pub(crate) fn render_fn(
 pub(crate) fn render_method(
     ctx: RenderContext<'_>,
     dot_access: &DotAccess,
-    receiver: Option<hir::Name>,
+    receiver: Option<SmolStr>,
     local_name: Option<hir::Name>,
     func: hir::Function,
 ) -> Builder {
@@ -59,16 +59,8 @@ fn render(
 
     let (call, escaped_call) = match &func_kind {
         FuncKind::Method(_, Some(receiver)) => (
-            format_smolstr!(
-                "{}.{}",
-                receiver.unescaped().display(ctx.db()),
-                name.unescaped().display(ctx.db())
-            ),
-            format_smolstr!(
-                "{}.{}",
-                receiver.display(ctx.db(), completion.edition),
-                name.display(ctx.db(), completion.edition)
-            ),
+            format_smolstr!("{}.{}", receiver, name.unescaped().display(ctx.db())),
+            format_smolstr!("{}.{}", receiver, name.display(ctx.db(), completion.edition)),
         ),
         _ => (
             name.unescaped().display(db).to_smolstr(),
@@ -143,8 +135,8 @@ fn render(
         }
         FuncKind::Method(DotAccess { receiver: Some(receiver), .. }, _) => {
             if let Some(original_expr) = completion.sema.original_ast_node(receiver.clone()) {
-                if let Some(ref_match) = compute_ref_match(completion, &ret_type) {
-                    item.ref_match(ref_match, original_expr.syntax().text_range().start());
+                if let Some(ref_mode) = compute_ref_match(completion, &ret_type) {
+                    item.ref_match(ref_mode, original_expr.syntax().text_range().start());
                 }
             }
         }
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs
index 1815f340532..b7dbf0a6306 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs
@@ -89,22 +89,24 @@ pub(crate) const TEST_CONFIG: CompletionConfig<'_> = CompletionConfig {
     exclude_traits: &[],
 };
 
-pub(crate) fn completion_list(ra_fixture: &str) -> String {
+pub(crate) fn completion_list(#[rust_analyzer::rust_fixture] ra_fixture: &str) -> String {
     completion_list_with_config(TEST_CONFIG, ra_fixture, true, None)
 }
 
-pub(crate) fn completion_list_no_kw(ra_fixture: &str) -> String {
+pub(crate) fn completion_list_no_kw(#[rust_analyzer::rust_fixture] ra_fixture: &str) -> String {
     completion_list_with_config(TEST_CONFIG, ra_fixture, false, None)
 }
 
-pub(crate) fn completion_list_no_kw_with_private_editable(ra_fixture: &str) -> String {
+pub(crate) fn completion_list_no_kw_with_private_editable(
+    #[rust_analyzer::rust_fixture] ra_fixture: &str,
+) -> String {
     let mut config = TEST_CONFIG;
     config.enable_private_editable = true;
     completion_list_with_config(config, ra_fixture, false, None)
 }
 
 pub(crate) fn completion_list_with_trigger_character(
-    ra_fixture: &str,
+    #[rust_analyzer::rust_fixture] ra_fixture: &str,
     trigger_character: Option<char>,
 ) -> String {
     completion_list_with_config(TEST_CONFIG, ra_fixture, true, trigger_character)
@@ -112,7 +114,7 @@ pub(crate) fn completion_list_with_trigger_character(
 
 fn completion_list_with_config_raw(
     config: CompletionConfig<'_>,
-    ra_fixture: &str,
+    #[rust_analyzer::rust_fixture] ra_fixture: &str,
     include_keywords: bool,
     trigger_character: Option<char>,
 ) -> Vec<CompletionItem> {
@@ -135,7 +137,7 @@ fn completion_list_with_config_raw(
 
 fn completion_list_with_config(
     config: CompletionConfig<'_>,
-    ra_fixture: &str,
+    #[rust_analyzer::rust_fixture] ra_fixture: &str,
     include_keywords: bool,
     trigger_character: Option<char>,
 ) -> String {
@@ -148,7 +150,9 @@ fn completion_list_with_config(
 }
 
 /// Creates analysis from a multi-file fixture, returns positions marked with $0.
-pub(crate) fn position(ra_fixture: &str) -> (RootDatabase, FilePosition) {
+pub(crate) fn position(
+    #[rust_analyzer::rust_fixture] ra_fixture: &str,
+) -> (RootDatabase, FilePosition) {
     let change_fixture = ChangeFixture::parse(ra_fixture);
     let mut database = RootDatabase::default();
     database.enable_proc_attr_macros();
@@ -216,7 +220,11 @@ fn render_completion_list(completions: Vec<CompletionItem>) -> String {
 }
 
 #[track_caller]
-pub(crate) fn check_edit(what: &str, ra_fixture_before: &str, ra_fixture_after: &str) {
+pub(crate) fn check_edit(
+    what: &str,
+    #[rust_analyzer::rust_fixture] ra_fixture_before: &str,
+    #[rust_analyzer::rust_fixture] ra_fixture_after: &str,
+) {
     check_edit_with_config(TEST_CONFIG, what, ra_fixture_before, ra_fixture_after)
 }
 
@@ -253,11 +261,40 @@ pub(crate) fn check_edit_with_config(
     assert_eq_text!(&ra_fixture_after, &actual)
 }
 
-fn check_empty(ra_fixture: &str, expect: Expect) {
+pub(crate) fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
     let actual = completion_list(ra_fixture);
     expect.assert_eq(&actual);
 }
 
+pub(crate) fn check_with_base_items(
+    #[rust_analyzer::rust_fixture] ra_fixture: &str,
+    expect: Expect,
+) {
+    check(&format!("{BASE_ITEMS_FIXTURE}{ra_fixture}"), expect)
+}
+
+pub(crate) fn check_no_kw(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
+    let actual = completion_list_no_kw(ra_fixture);
+    expect.assert_eq(&actual)
+}
+
+pub(crate) fn check_with_private_editable(
+    #[rust_analyzer::rust_fixture] ra_fixture: &str,
+    expect: Expect,
+) {
+    let actual = completion_list_no_kw_with_private_editable(ra_fixture);
+    expect.assert_eq(&actual);
+}
+
+pub(crate) fn check_with_trigger_character(
+    #[rust_analyzer::rust_fixture] ra_fixture: &str,
+    trigger_character: Option<char>,
+    expect: Expect,
+) {
+    let actual = completion_list_with_trigger_character(ra_fixture, trigger_character);
+    expect.assert_eq(&actual)
+}
+
 pub(crate) fn get_all_items(
     config: CompletionConfig<'_>,
     code: &str,
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs
index ebf35820570..32d3b50f237 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs
@@ -1,12 +1,7 @@
 //! Completion tests for attributes.
-use expect_test::{expect, Expect};
+use expect_test::expect;
 
-use crate::tests::{check_edit, completion_list};
-
-fn check(ra_fixture: &str, expect: Expect) {
-    let actual = completion_list(ra_fixture);
-    expect.assert_eq(&actual);
-}
+use crate::tests::{check, check_edit};
 
 #[test]
 fn derive_helpers() {
@@ -788,14 +783,9 @@ mod cfg {
 mod derive {
     use super::*;
 
-    fn check_derive(ra_fixture: &str, expect: Expect) {
-        let actual = completion_list(ra_fixture);
-        expect.assert_eq(&actual);
-    }
-
     #[test]
     fn no_completion_for_incorrect_derive() {
-        check_derive(
+        check(
             r#"
 //- minicore: derive, copy, clone, ord, eq, default, fmt
 #[derive{$0)] struct Test;
@@ -806,7 +796,7 @@ mod derive {
 
     #[test]
     fn empty_derive() {
-        check_derive(
+        check(
             r#"
 //- minicore: derive, copy, clone, ord, eq, default, fmt
 #[derive($0)] struct Test;
@@ -828,7 +818,7 @@ mod derive {
 
     #[test]
     fn derive_with_input_before() {
-        check_derive(
+        check(
             r#"
 //- minicore: derive, copy, clone, ord, eq, default, fmt
 #[derive(serde::Serialize, PartialEq, $0)] struct Test;
@@ -849,7 +839,7 @@ mod derive {
 
     #[test]
     fn derive_with_input_after() {
-        check_derive(
+        check(
             r#"
 //- minicore: derive, copy, clone, ord, eq, default, fmt
 #[derive($0 serde::Serialize, PartialEq)] struct Test;
@@ -870,7 +860,7 @@ mod derive {
 
     #[test]
     fn derive_with_existing_derives() {
-        check_derive(
+        check(
             r#"
 //- minicore: derive, copy, clone, ord, eq, default, fmt
 #[derive(PartialEq, Eq, Or$0)] struct Test;
@@ -890,7 +880,7 @@ mod derive {
 
     #[test]
     fn derive_flyimport() {
-        check_derive(
+        check(
             r#"
 //- proc_macros: derive_identity
 //- minicore: derive
@@ -904,7 +894,7 @@ mod derive {
                 kw self::
             "#]],
         );
-        check_derive(
+        check(
             r#"
 //- proc_macros: derive_identity
 //- minicore: derive
@@ -940,7 +930,7 @@ use proc_macros::DeriveIdentity;
 
     #[test]
     fn qualified() {
-        check_derive(
+        check(
             r#"
 //- proc_macros: derive_identity
 //- minicore: derive, copy, clone
@@ -950,7 +940,7 @@ use proc_macros::DeriveIdentity;
                 de DeriveIdentity proc_macro DeriveIdentity
             "#]],
         );
-        check_derive(
+        check(
             r#"
 //- proc_macros: derive_identity
 //- minicore: derive, copy, clone
@@ -1056,19 +1046,14 @@ mod lint {
 mod repr {
     use super::*;
 
-    fn check_repr(ra_fixture: &str, expect: Expect) {
-        let actual = completion_list(ra_fixture);
-        expect.assert_eq(&actual);
-    }
-
     #[test]
     fn no_completion_for_incorrect_repr() {
-        check_repr(r#"#[repr{$0)] struct Test;"#, expect![[]])
+        check(r#"#[repr{$0)] struct Test;"#, expect![[]])
     }
 
     #[test]
     fn empty() {
-        check_repr(
+        check(
             r#"#[repr($0)] struct Test;"#,
             expect![[r#"
                 ba C
@@ -1093,12 +1078,12 @@ mod repr {
 
     #[test]
     fn transparent() {
-        check_repr(r#"#[repr(transparent, $0)] struct Test;"#, expect![[r#""#]]);
+        check(r#"#[repr(transparent, $0)] struct Test;"#, expect![[r#""#]]);
     }
 
     #[test]
     fn align() {
-        check_repr(
+        check(
             r#"#[repr(align(1), $0)] struct Test;"#,
             expect![[r#"
                 ba C
@@ -1121,7 +1106,7 @@ mod repr {
 
     #[test]
     fn packed() {
-        check_repr(
+        check(
             r#"#[repr(packed, $0)] struct Test;"#,
             expect![[r#"
                 ba C
@@ -1144,7 +1129,7 @@ mod repr {
 
     #[test]
     fn c() {
-        check_repr(
+        check(
             r#"#[repr(C, $0)] struct Test;"#,
             expect![[r#"
                 ba align($0)
@@ -1167,7 +1152,7 @@ mod repr {
 
     #[test]
     fn prim() {
-        check_repr(
+        check(
             r#"#[repr(usize, $0)] struct Test;"#,
             expect![[r#"
                 ba C
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 30466148686..e117dbf4bdf 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
@@ -4,18 +4,17 @@ use expect_test::{expect, Expect};
 use crate::{
     config::AutoImportExclusionType,
     tests::{
-        check_edit, check_empty, completion_list, completion_list_with_config, BASE_ITEMS_FIXTURE,
+        check, check_edit, check_with_base_items, completion_list_with_config, BASE_ITEMS_FIXTURE,
         TEST_CONFIG,
     },
     CompletionConfig,
 };
 
-fn check(ra_fixture: &str, expect: Expect) {
-    let actual = completion_list(&format!("{BASE_ITEMS_FIXTURE}{ra_fixture}"));
-    expect.assert_eq(&actual)
-}
-
-fn check_with_config(config: CompletionConfig<'_>, ra_fixture: &str, expect: Expect) {
+fn check_with_config(
+    config: CompletionConfig<'_>,
+    #[rust_analyzer::rust_fixture] ra_fixture: &str,
+    expect: Expect,
+) {
     let actual = completion_list_with_config(
         config,
         &format!("{BASE_ITEMS_FIXTURE}{ra_fixture}"),
@@ -28,7 +27,7 @@ fn check_with_config(config: CompletionConfig<'_>, ra_fixture: &str, expect: Exp
 #[test]
 fn complete_literal_struct_with_a_private_field() {
     // `FooDesc.bar` is private, the completion should not be triggered.
-    check(
+    check_with_base_items(
         r#"
 mod _69latrick {
     pub struct FooDesc { pub six: bool, pub neuf: Vec<String>, bar: bool }
@@ -67,6 +66,7 @@ fn baz() {
             kw loop
             kw match
             kw mut
+            kw raw
             kw return
             kw self::
             kw true
@@ -79,7 +79,7 @@ fn baz() {
 
 #[test]
 fn completes_various_bindings() {
-    check_empty(
+    check(
         r#"
 fn func(param0 @ (param1, param2): (i32, i32)) {
     let letlocal = 92;
@@ -125,7 +125,7 @@ fn func(param0 @ (param1, param2): (i32, i32)) {
 
 #[test]
 fn completes_all_the_things_in_fn_body() {
-    check(
+    check_with_base_items(
         r#"
 use non_existent::Unresolved;
 mod qualified { pub enum Enum { Variant } }
@@ -191,7 +191,7 @@ impl Unit {
             ?? Unresolved
         "#]],
     );
-    check(
+    check_with_base_items(
         r#"
 use non_existent::Unresolved;
 mod qualified { pub enum Enum { Variant } }
@@ -224,7 +224,7 @@ impl Unit {
 
 #[test]
 fn complete_in_block() {
-    check_empty(
+    check(
         r#"
     fn foo() {
         if true {
@@ -273,7 +273,7 @@ fn complete_in_block() {
 
 #[test]
 fn complete_after_if_expr() {
-    check_empty(
+    check(
         r#"
     fn foo() {
         if true {}
@@ -321,7 +321,7 @@ fn complete_after_if_expr() {
 
 #[test]
 fn complete_in_match_arm() {
-    check_empty(
+    check(
         r#"
     fn foo() {
         match () {
@@ -351,7 +351,7 @@ fn complete_in_match_arm() {
 
 #[test]
 fn completes_in_loop_ctx() {
-    check_empty(
+    check(
         r"fn my() { loop { $0 } }",
         expect![[r#"
             fn my()   fn()
@@ -390,7 +390,7 @@ fn completes_in_loop_ctx() {
             sn ppd
         "#]],
     );
-    check_empty(
+    check(
         r"fn my() { loop { foo.$0 } }",
         expect![[r#"
             sn box  Box::new(expr)
@@ -415,7 +415,7 @@ fn completes_in_loop_ctx() {
 
 #[test]
 fn completes_in_let_initializer() {
-    check_empty(
+    check(
         r#"fn main() { let _ = $0 }"#,
         expect![[r#"
             fn main() fn()
@@ -438,8 +438,116 @@ fn completes_in_let_initializer() {
 }
 
 #[test]
+fn completes_after_ref_expr() {
+    check(
+        r#"fn main() { let _ = &$0 }"#,
+        expect![[r#"
+            fn main() fn()
+            bt u32     u32
+            kw crate::
+            kw false
+            kw for
+            kw if
+            kw if let
+            kw loop
+            kw match
+            kw mut
+            kw raw
+            kw return
+            kw self::
+            kw true
+            kw unsafe
+            kw while
+            kw while let
+        "#]],
+    );
+    check(
+        r#"fn main() { let _ = &raw $0 }"#,
+        expect![[r#"
+            fn main() fn()
+            bt u32     u32
+            kw const
+            kw crate::
+            kw false
+            kw for
+            kw if
+            kw if let
+            kw loop
+            kw match
+            kw mut
+            kw return
+            kw self::
+            kw true
+            kw unsafe
+            kw while
+            kw while let
+        "#]],
+    );
+    check(
+        r#"fn main() { let _ = &raw const $0 }"#,
+        expect![[r#"
+            fn main() fn()
+            bt u32     u32
+            kw crate::
+            kw false
+            kw for
+            kw if
+            kw if let
+            kw loop
+            kw match
+            kw return
+            kw self::
+            kw true
+            kw unsafe
+            kw while
+            kw while let
+        "#]],
+    );
+    check(
+        r#"fn main() { let _ = &raw mut $0 }"#,
+        expect![[r#"
+            fn main() fn()
+            bt u32     u32
+            kw crate::
+            kw false
+            kw for
+            kw if
+            kw if let
+            kw loop
+            kw match
+            kw return
+            kw self::
+            kw true
+            kw unsafe
+            kw while
+            kw while let
+        "#]],
+    );
+    check(
+        r#"fn main() { let _ = &mut $0 }"#,
+        expect![[r#"
+            fn main() fn()
+            bt u32     u32
+            kw crate::
+            kw false
+            kw for
+            kw if
+            kw if let
+            kw loop
+            kw match
+            kw return
+            kw self::
+            kw true
+            kw unsafe
+            kw while
+            kw while let
+        "#]],
+    )
+}
+
+#[test]
 fn struct_initializer_field_expr() {
-    check_empty(
+    check(
         r#"
 struct Foo {
     pub f: i32,
@@ -475,7 +583,7 @@ fn foo() {
 fn shadowing_shows_single_completion() {
     cov_mark::check!(shadowing_shows_single_completion);
 
-    check_empty(
+    check(
         r#"
 fn foo() {
     let bar = 92;
@@ -508,7 +616,7 @@ fn foo() {
 
 #[test]
 fn in_macro_expr_frag() {
-    check_empty(
+    check(
         r#"
 macro_rules! m { ($e:expr) => { $e } }
 fn quux(x: i32) {
@@ -535,7 +643,7 @@ fn quux(x: i32) {
             kw while let
         "#]],
     );
-    check_empty(
+    check(
         r"
 macro_rules! m { ($e:expr) => { $e } }
 fn quux(x: i32) {
@@ -562,7 +670,7 @@ fn quux(x: i32) {
             kw while let
         "#]],
     );
-    check_empty(
+    check(
         r#"
 macro_rules! m { ($e:expr) => { $e } }
 fn quux(x: i32) {
@@ -595,7 +703,7 @@ fn quux(x: i32) {
 
 #[test]
 fn enum_qualified() {
-    check(
+    check_with_base_items(
         r#"
 impl Enum {
     type AssocType = ();
@@ -619,7 +727,7 @@ fn func() {
 
 #[test]
 fn ty_qualified_no_drop() {
-    check_empty(
+    check(
         r#"
 //- minicore: drop
 struct Foo;
@@ -636,7 +744,7 @@ fn func() {
 
 #[test]
 fn with_parens() {
-    check_empty(
+    check(
         r#"
 enum Enum {
     Variant()
@@ -657,7 +765,7 @@ fn func() {
 
 #[test]
 fn detail_impl_trait_in_return_position() {
-    check_empty(
+    check(
         r"
 //- minicore: sized
 trait Trait<T> {}
@@ -676,7 +784,7 @@ fn main() {
 
 #[test]
 fn detail_async_fn() {
-    check_empty(
+    check(
         r#"
 //- minicore: future, sized
 trait Trait<T> {}
@@ -697,7 +805,7 @@ fn main() {
 
 #[test]
 fn detail_impl_trait_in_argument_position() {
-    check_empty(
+    check(
         r"
 //- minicore: sized
 trait Trait<T> {}
@@ -717,7 +825,7 @@ fn main() {
 
 #[test]
 fn complete_record_expr_path() {
-    check(
+    check_with_base_items(
         r#"
 struct Zulu;
 impl Zulu {
@@ -738,7 +846,7 @@ fn main() {
 
 #[test]
 fn variant_with_struct() {
-    check_empty(
+    check(
         r#"
 pub struct YoloVariant {
     pub f: usize
@@ -813,7 +921,7 @@ fn return_value_no_block() {
 
 #[test]
 fn else_completion_after_if() {
-    check_empty(
+    check(
         r#"
 fn foo() { if foo {} $0 }
 "#,
@@ -854,7 +962,7 @@ fn foo() { if foo {} $0 }
             sn ppd
         "#]],
     );
-    check_empty(
+    check(
         r#"
 fn foo() { if foo {} el$0 }
 "#,
@@ -895,7 +1003,7 @@ fn foo() { if foo {} el$0 }
             sn ppd
         "#]],
     );
-    check_empty(
+    check(
         r#"
 fn foo() { bar(if foo {} $0) }
 "#,
@@ -919,7 +1027,7 @@ fn foo() { bar(if foo {} $0) }
             kw while let
         "#]],
     );
-    check_empty(
+    check(
         r#"
 fn foo() { bar(if foo {} el$0) }
 "#,
@@ -943,7 +1051,7 @@ fn foo() { bar(if foo {} el$0) }
             kw while let
         "#]],
     );
-    check_empty(
+    check(
         r#"
 fn foo() { if foo {} $0 let x = 92; }
 "#,
@@ -984,7 +1092,7 @@ fn foo() { if foo {} $0 let x = 92; }
             sn ppd
         "#]],
     );
-    check_empty(
+    check(
         r#"
 fn foo() { if foo {} el$0 let x = 92; }
 "#,
@@ -1025,7 +1133,7 @@ fn foo() { if foo {} el$0 let x = 92; }
             sn ppd
         "#]],
     );
-    check_empty(
+    check(
         r#"
 fn foo() { if foo {} el$0 { let x = 92; } }
 "#,
@@ -1070,7 +1178,7 @@ fn foo() { if foo {} el$0 { let x = 92; } }
 
 #[test]
 fn expr_no_unstable_item_on_stable() {
-    check_empty(
+    check(
         r#"
 //- /main.rs crate:main deps:std
 use std::*;
@@ -1121,7 +1229,7 @@ pub struct UnstableThisShouldNotBeListed;
 
 #[test]
 fn expr_unstable_item_on_nightly() {
-    check_empty(
+    check(
         r#"
 //- toolchain:nightly
 //- /main.rs crate:main deps:std
@@ -1174,7 +1282,7 @@ pub struct UnstableButWeAreOnNightlyAnyway;
 
 #[test]
 fn inside_format_args_completions_work() {
-    check_empty(
+    check(
         r#"
 //- minicore: fmt
 struct Foo;
@@ -1200,7 +1308,7 @@ fn main() {
             sn unsafe    unsafe {}
         "#]],
     );
-    check_empty(
+    check(
         r#"
 //- minicore: fmt
 struct Foo;
@@ -1230,7 +1338,7 @@ fn main() {
 
 #[test]
 fn inside_faulty_format_args_completions_work() {
-    check_empty(
+    check(
         r#"
 //- minicore: fmt
 struct Foo;
@@ -1256,7 +1364,7 @@ fn main() {
             sn unsafe    unsafe {}
         "#]],
     );
-    check_empty(
+    check(
         r#"
 //- minicore: fmt
 struct Foo;
@@ -1282,7 +1390,7 @@ fn main() {
             sn unsafe    unsafe {}
         "#]],
     );
-    check_empty(
+    check(
         r#"
 //- minicore: fmt
 struct Foo;
@@ -1308,7 +1416,7 @@ fn main() {
             sn unsafe    unsafe {}
         "#]],
     );
-    check_empty(
+    check(
         r#"
 //- minicore: fmt
 struct Foo;
@@ -1340,7 +1448,7 @@ fn main() {
 
 #[test]
 fn macro_that_ignores_completion_marker() {
-    check(
+    check_with_base_items(
         r#"
 macro_rules! helper {
     ($v:ident) => {};
@@ -1788,7 +1896,7 @@ fn foo<T: ExcludedTrait>() {
 
 #[test]
 fn hide_ragennew_synthetic_identifiers() {
-    check_empty(
+    check(
         r#"
 //- minicore: iterator
 fn bar() {
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs
index d413977f7c8..d491e438fef 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs
@@ -6,11 +6,15 @@ use crate::{
     CompletionConfig,
 };
 
-fn check(ra_fixture: &str, expect: Expect) {
+fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
     check_with_config(TEST_CONFIG, ra_fixture, expect);
 }
 
-fn check_with_config(config: CompletionConfig<'_>, ra_fixture: &str, expect: Expect) {
+fn check_with_config(
+    config: CompletionConfig<'_>,
+    #[rust_analyzer::rust_fixture] ra_fixture: &str,
+    expect: Expect,
+) {
     let (db, position) = crate::tests::position(ra_fixture);
     let (ctx, analysis) = crate::context::CompletionContext::new(&db, position, &config).unwrap();
 
@@ -1742,7 +1746,7 @@ fn intrinsics() {
     fn function() {
             transmute$0
     }
-    "#,
+"#,
         expect![[r#"
             fn transmute(…) (use core::mem::transmute) unsafe fn(Src) -> Dst
         "#]],
@@ -1763,7 +1767,9 @@ fn function() {
         mem::transmute$0
 }
 "#,
-        expect![""],
+        expect![[r#"
+            fn transmute(…) (use core::mem) unsafe fn(Src) -> Dst
+        "#]],
     );
 }
 
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/fn_param.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/fn_param.rs
index 4a89f874e15..451ce07c745 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/fn_param.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/fn_param.rs
@@ -1,16 +1,6 @@
-use expect_test::{expect, Expect};
+use expect_test::expect;
 
-use crate::tests::{completion_list, completion_list_with_trigger_character};
-
-fn check(ra_fixture: &str, expect: Expect) {
-    let actual = completion_list(ra_fixture);
-    expect.assert_eq(&actual);
-}
-
-fn check_with_trigger_character(ra_fixture: &str, trigger_character: char, expect: Expect) {
-    let actual = completion_list_with_trigger_character(ra_fixture, Some(trigger_character));
-    expect.assert_eq(&actual)
-}
+use crate::tests::{check, check_with_trigger_character};
 
 #[test]
 fn only_param() {
@@ -124,7 +114,7 @@ fn trigger_by_l_paren() {
         r#"
 fn foo($0)
 "#,
-        '(',
+        Some('('),
         expect![[]],
     )
 }
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 79561a0419f..bea6d60769c 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
@@ -2,20 +2,13 @@
 //!
 //! Except for use items which are tested in [super::use_tree] and mod declarations with are tested
 //! in [crate::completions::mod_].
-use expect_test::{expect, Expect};
+use expect_test::expect;
 
-use crate::tests::{completion_list, BASE_ITEMS_FIXTURE};
-
-use super::check_edit;
-
-fn check(ra_fixture: &str, expect: Expect) {
-    let actual = completion_list(&format!("{BASE_ITEMS_FIXTURE}{ra_fixture}"));
-    expect.assert_eq(&actual)
-}
+use crate::tests::{check_edit, check_with_base_items};
 
 #[test]
 fn target_type_or_trait_in_impl_block() {
-    check(
+    check_with_base_items(
         r#"
 impl Tra$0
 "#,
@@ -37,7 +30,7 @@ impl Tra$0
 
 #[test]
 fn target_type_in_trait_impl_block() {
-    check(
+    check_with_base_items(
         r#"
 impl Trait for Str$0
 "#,
@@ -59,7 +52,7 @@ impl Trait for Str$0
 
 #[test]
 fn after_trait_name_in_trait_def() {
-    check(
+    check_with_base_items(
         r"trait A $0",
         expect![[r#"
             kw where
@@ -69,21 +62,21 @@ fn after_trait_name_in_trait_def() {
 
 #[test]
 fn after_target_name_in_impl() {
-    check(
+    check_with_base_items(
         r"impl Trait $0",
         expect![[r#"
             kw for
             kw where
         "#]],
     );
-    check(
+    check_with_base_items(
         r"impl Trait f$0",
         expect![[r#"
             kw for
             kw where
         "#]],
     );
-    check(
+    check_with_base_items(
         r"impl Trait for Type $0",
         expect![[r#"
             kw where
@@ -93,44 +86,44 @@ fn after_target_name_in_impl() {
 
 #[test]
 fn completes_where() {
-    check(
+    check_with_base_items(
         r"struct Struct $0",
         expect![[r#"
         kw where
     "#]],
     );
-    check(
+    check_with_base_items(
         r"struct Struct $0 {}",
         expect![[r#"
         kw where
     "#]],
     );
     // FIXME: This shouldn't be completed here
-    check(
+    check_with_base_items(
         r"struct Struct $0 ()",
         expect![[r#"
         kw where
     "#]],
     );
-    check(
+    check_with_base_items(
         r"fn func() $0",
         expect![[r#"
         kw where
     "#]],
     );
-    check(
+    check_with_base_items(
         r"enum Enum $0",
         expect![[r#"
         kw where
     "#]],
     );
-    check(
+    check_with_base_items(
         r"enum Enum $0 {}",
         expect![[r#"
         kw where
     "#]],
     );
-    check(
+    check_with_base_items(
         r"trait Trait $0 {}",
         expect![[r#"
         kw where
@@ -140,7 +133,7 @@ fn completes_where() {
 
 #[test]
 fn before_record_field() {
-    check(
+    check_with_base_items(
         r#"
 struct Foo {
     $0
@@ -244,7 +237,7 @@ impl Copy for S where $0
 
 #[test]
 fn test_is_not_considered_macro() {
-    check(
+    check_with_base_items(
         r#"
 #[rustc_builtin]
 pub macro test($item:item) {
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/item_list.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/item_list.rs
index d3d52dc6dfc..841c42123a0 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/item_list.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/item_list.rs
@@ -1,16 +1,11 @@
 //! Completion tests for item list position.
-use expect_test::{expect, Expect};
+use expect_test::expect;
 
-use crate::tests::{check_edit, check_empty, completion_list, BASE_ITEMS_FIXTURE};
-
-fn check(ra_fixture: &str, expect: Expect) {
-    let actual = completion_list(&format!("{BASE_ITEMS_FIXTURE}{ra_fixture}"));
-    expect.assert_eq(&actual)
-}
+use crate::tests::{check, check_edit, check_with_base_items};
 
 #[test]
 fn in_mod_item_list() {
-    check(
+    check_with_base_items(
         r#"mod tests { $0 }"#,
         expect![[r#"
             ma makro!(…) macro_rules! makro
@@ -43,7 +38,7 @@ fn in_mod_item_list() {
 
 #[test]
 fn in_source_file_item_list() {
-    check(
+    check_with_base_items(
         r#"$0"#,
         expect![[r#"
             ma makro!(…) macro_rules! makro
@@ -76,7 +71,7 @@ fn in_source_file_item_list() {
 
 #[test]
 fn in_item_list_after_attr() {
-    check(
+    check_with_base_items(
         r#"#[attr] $0"#,
         expect![[r#"
             ma makro!(…) macro_rules! makro
@@ -109,7 +104,7 @@ fn in_item_list_after_attr() {
 
 #[test]
 fn in_qualified_path() {
-    check(
+    check_with_base_items(
         r#"crate::$0"#,
         expect![[r#"
             ma makro!(…) macro_rules! makro
@@ -120,7 +115,7 @@ fn in_qualified_path() {
 
 #[test]
 fn after_unsafe_token() {
-    check(
+    check_with_base_items(
         r#"unsafe $0"#,
         expect![[r#"
             kw async
@@ -134,7 +129,7 @@ fn after_unsafe_token() {
 
 #[test]
 fn after_async_token() {
-    check(
+    check_with_base_items(
         r#"async $0"#,
         expect![[r#"
             kw fn
@@ -145,7 +140,7 @@ fn after_async_token() {
 
 #[test]
 fn after_visibility() {
-    check(
+    check_with_base_items(
         r#"pub $0"#,
         expect![[r#"
             kw async
@@ -167,7 +162,7 @@ fn after_visibility() {
 
 #[test]
 fn after_visibility_unsafe() {
-    check(
+    check_with_base_items(
         r#"pub unsafe $0"#,
         expect![[r#"
             kw async
@@ -179,7 +174,7 @@ fn after_visibility_unsafe() {
 
 #[test]
 fn in_impl_assoc_item_list() {
-    check(
+    check_with_base_items(
         r#"impl Struct { $0 }"#,
         expect![[r#"
             ma makro!(…) macro_rules! makro
@@ -199,7 +194,7 @@ fn in_impl_assoc_item_list() {
 
 #[test]
 fn in_impl_assoc_item_list_after_attr() {
-    check(
+    check_with_base_items(
         r#"impl Struct { #[attr] $0 }"#,
         expect![[r#"
             ma makro!(…) macro_rules! makro
@@ -219,7 +214,7 @@ fn in_impl_assoc_item_list_after_attr() {
 
 #[test]
 fn in_trait_assoc_item_list() {
-    check(
+    check_with_base_items(
         r"trait Foo { $0 }",
         expect![[r#"
             ma makro!(…) macro_rules! makro
@@ -237,7 +232,7 @@ fn in_trait_assoc_item_list() {
 
 #[test]
 fn in_trait_assoc_fn_missing_body() {
-    check(
+    check_with_base_items(
         r#"trait Foo { fn function(); $0 }"#,
         expect![[r#"
             ma makro!(…) macro_rules! makro
@@ -255,7 +250,7 @@ fn in_trait_assoc_fn_missing_body() {
 
 #[test]
 fn in_trait_assoc_const_missing_body() {
-    check(
+    check_with_base_items(
         r#"trait Foo { const CONST: (); $0 }"#,
         expect![[r#"
             ma makro!(…) macro_rules! makro
@@ -273,7 +268,7 @@ fn in_trait_assoc_const_missing_body() {
 
 #[test]
 fn in_trait_assoc_type_aliases_missing_ty() {
-    check(
+    check_with_base_items(
         r#"trait Foo { type Type; $0 }"#,
         expect![[r#"
             ma makro!(…) macro_rules! makro
@@ -291,7 +286,7 @@ fn in_trait_assoc_type_aliases_missing_ty() {
 
 #[test]
 fn in_trait_impl_assoc_item_list() {
-    check(
+    check_with_base_items(
         r#"
 trait Test {
     type Type0;
@@ -326,7 +321,7 @@ impl Test for () {
 
 #[test]
 fn in_trait_impl_no_unstable_item_on_stable() {
-    check_empty(
+    check(
         r#"
 trait Test {
     #[unstable]
@@ -350,7 +345,7 @@ impl Test for () {
 
 #[test]
 fn in_trait_impl_unstable_item_on_nightly() {
-    check_empty(
+    check(
         r#"
 //- toolchain:nightly
 trait Test {
@@ -378,7 +373,7 @@ impl Test for () {
 
 #[test]
 fn after_unit_struct() {
-    check(
+    check_with_base_items(
         r#"struct S; f$0"#,
         expect![[r#"
             ma makro!(…) macro_rules! makro
@@ -500,7 +495,7 @@ type O = $0;
 #[test]
 fn inside_extern_blocks() {
     // Should suggest `fn`, `static`, `unsafe`
-    check(
+    check_with_base_items(
         r#"extern { $0 }"#,
         expect![[r#"
             ma makro!(…) macro_rules! makro
@@ -517,7 +512,7 @@ fn inside_extern_blocks() {
     );
 
     // Should suggest `fn`, `static`, `safe`, `unsafe`
-    check(
+    check_with_base_items(
         r#"unsafe extern { $0 }"#,
         expect![[r#"
             ma makro!(…) macro_rules! makro
@@ -534,7 +529,7 @@ fn inside_extern_blocks() {
         "#]],
     );
 
-    check(
+    check_with_base_items(
         r#"unsafe extern { pub safe $0 }"#,
         expect![[r#"
             kw fn
@@ -542,7 +537,7 @@ fn inside_extern_blocks() {
         "#]],
     );
 
-    check(
+    check_with_base_items(
         r#"unsafe extern { pub unsafe $0 }"#,
         expect![[r#"
             kw fn
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/pattern.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/pattern.rs
index 2f1f555e524..9ec27252fd7 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/pattern.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/pattern.rs
@@ -1,16 +1,11 @@
 //! Completion tests for pattern position.
-use expect_test::{expect, Expect};
+use expect_test::expect;
 
-use crate::tests::{check_edit, check_empty, completion_list, BASE_ITEMS_FIXTURE};
-
-fn check(ra_fixture: &str, expect: Expect) {
-    let actual = completion_list(&format!("{BASE_ITEMS_FIXTURE}\n{ra_fixture}"));
-    expect.assert_eq(&actual)
-}
+use crate::tests::{check, check_edit, check_with_base_items};
 
 #[test]
 fn wildcard() {
-    check(
+    check_with_base_items(
         r#"
 fn quux() {
     let _$0
@@ -22,7 +17,7 @@ fn quux() {
 
 #[test]
 fn ident_rebind_pat() {
-    check_empty(
+    check(
         r#"
 fn quux() {
     let en$0 @ x
@@ -37,7 +32,7 @@ fn quux() {
 
 #[test]
 fn ident_ref_pat() {
-    check_empty(
+    check(
         r#"
 fn quux() {
     let ref en$0
@@ -47,7 +42,7 @@ fn quux() {
             kw mut
         "#]],
     );
-    check_empty(
+    check(
         r#"
 fn quux() {
     let ref en$0 @ x
@@ -61,7 +56,7 @@ fn quux() {
 
 #[test]
 fn ident_ref_mut_pat() {
-    check_empty(
+    check(
         r#"
 fn quux() {
     let ref mut en$0
@@ -69,7 +64,7 @@ fn quux() {
 "#,
         expect![[r#""#]],
     );
-    check_empty(
+    check(
         r#"
 fn quux() {
     let ref mut en$0 @ x
@@ -81,7 +76,7 @@ fn quux() {
 
 #[test]
 fn ref_pat() {
-    check_empty(
+    check(
         r#"
 fn quux() {
     let &en$0
@@ -91,7 +86,7 @@ fn quux() {
             kw mut
         "#]],
     );
-    check_empty(
+    check(
         r#"
 fn quux() {
     let &mut en$0
@@ -99,7 +94,7 @@ fn quux() {
 "#,
         expect![[r#""#]],
     );
-    check_empty(
+    check(
         r#"
 fn foo() {
     for &$0 in () {}
@@ -113,7 +108,7 @@ fn foo() {
 
 #[test]
 fn refutable() {
-    check(
+    check_with_base_items(
         r#"
 fn foo() {
     if let a$0
@@ -139,7 +134,7 @@ fn foo() {
 
 #[test]
 fn irrefutable() {
-    check(
+    check_with_base_items(
         r#"
 enum SingleVariantEnum {
     Variant
@@ -168,7 +163,7 @@ fn foo() {
 
 #[test]
 fn in_param() {
-    check(
+    check_with_base_items(
         r#"
 fn foo(a$0) {
 }
@@ -185,7 +180,7 @@ fn foo(a$0) {
             kw ref
         "#]],
     );
-    check(
+    check_with_base_items(
         r#"
 fn foo(a$0: Tuple) {
 }
@@ -207,7 +202,7 @@ fn foo(a$0: Tuple) {
 
 #[test]
 fn only_fn_like_macros() {
-    check_empty(
+    check(
         r#"
 macro_rules! m { ($e:expr) => { $e } }
 
@@ -228,7 +223,7 @@ fn foo() {
 
 #[test]
 fn in_simple_macro_call() {
-    check_empty(
+    check(
         r#"
 macro_rules! m { ($e:expr) => { $e } }
 enum E { X }
@@ -249,7 +244,7 @@ fn foo() {
 
 #[test]
 fn omits_private_fields_pat() {
-    check_empty(
+    check(
         r#"
 mod foo {
     pub struct Record { pub field: i32, _field: i32 }
@@ -277,7 +272,7 @@ fn outer() {
 
 #[test]
 fn completes_self_pats() {
-    check_empty(
+    check(
         r#"
 struct Foo(i32);
 impl Foo {
@@ -301,7 +296,7 @@ impl Foo {
 
 #[test]
 fn enum_qualified() {
-    check(
+    check_with_base_items(
         r#"
 impl Enum {
     type AssocType = ();
@@ -323,7 +318,7 @@ fn func() {
 
 #[test]
 fn completes_in_record_field_pat() {
-    check_empty(
+    check(
         r#"
 struct Foo { bar: Bar }
 struct Bar(u32);
@@ -342,7 +337,7 @@ fn outer(Foo { bar: $0 }: Foo) {}
 
 #[test]
 fn skips_in_record_field_pat_name() {
-    check_empty(
+    check(
         r#"
 struct Foo { bar: Bar }
 struct Bar(u32);
@@ -357,7 +352,7 @@ fn outer(Foo { bar$0 }: Foo) {}
 
 #[test]
 fn completes_in_record_field_pat_with_generic_type_alias() {
-    check_empty(
+    check(
         r#"
 type Wrap<T> = T;
 
@@ -386,7 +381,7 @@ fn main() {
 
 #[test]
 fn completes_in_fn_param() {
-    check_empty(
+    check(
         r#"
 struct Foo { bar: Bar }
 struct Bar(u32);
@@ -405,7 +400,7 @@ fn foo($0) {}
 
 #[test]
 fn completes_in_closure_param() {
-    check_empty(
+    check(
         r#"
 struct Foo { bar: Bar }
 struct Bar(u32);
@@ -426,7 +421,7 @@ fn foo() {
 
 #[test]
 fn completes_no_delims_if_existing() {
-    check_empty(
+    check(
         r#"
 struct Bar(u32);
 fn foo() {
@@ -441,7 +436,7 @@ fn foo() {
             kw self::
         "#]],
     );
-    check_empty(
+    check(
         r#"
 struct Foo { bar: u32 }
 fn foo() {
@@ -456,7 +451,7 @@ fn foo() {
             kw self::
         "#]],
     );
-    check_empty(
+    check(
         r#"
 enum Enum {
     TupleVariant(u32)
@@ -471,7 +466,7 @@ fn foo() {
             bn TupleVariant TupleVariant
         "#]],
     );
-    check_empty(
+    check(
         r#"
 enum Enum {
     RecordVariant { field: u32 }
@@ -519,7 +514,7 @@ fn foo() {
 #[test]
 fn completes_enum_variant_pat_escape() {
     cov_mark::check!(enum_variant_pattern_path);
-    check_empty(
+    check(
         r#"
 enum Enum {
     A,
@@ -544,7 +539,7 @@ fn foo() {
         "#]],
     );
 
-    check_empty(
+    check(
         r#"
 enum Enum {
     A,
@@ -569,7 +564,7 @@ fn foo() {
 
 #[test]
 fn completes_associated_const() {
-    check_empty(
+    check(
         r#"
 #[derive(PartialEq, Eq)]
 struct Ty(u8);
@@ -590,7 +585,7 @@ fn f(t: Ty) {
         "#]],
     );
 
-    check_empty(
+    check(
         r#"
 enum MyEnum {}
 
@@ -612,7 +607,7 @@ fn f(e: MyEnum) {
         "#]],
     );
 
-    check_empty(
+    check(
         r#"
 union U {
     i: i32,
@@ -637,7 +632,7 @@ fn f(u: U) {
         "#]],
     );
 
-    check_empty(
+    check(
         r#"
 #![rustc_coherence_is_core]
 #[lang = "u32"]
@@ -659,7 +654,7 @@ fn f(v: u32) {
 
 #[test]
 fn in_method_param() {
-    check_empty(
+    check(
         r#"
 struct Ty(u8);
 
@@ -680,7 +675,7 @@ impl Ty {
             kw ref
         "#]],
     );
-    check_empty(
+    check(
         r#"
 struct Ty(u8);
 
@@ -701,7 +696,7 @@ impl Ty {
             kw ref
         "#]],
     );
-    check_empty(
+    check(
         r#"
 struct Ty(u8);
 
@@ -722,7 +717,7 @@ impl Ty {
             kw ref
         "#]],
     );
-    check_empty(
+    check(
         r#"
 struct Ty(u8);
 
@@ -743,7 +738,7 @@ impl Ty {
 
 #[test]
 fn through_alias() {
-    check_empty(
+    check(
         r#"
 enum Enum<T> {
     Unit,
@@ -770,7 +765,7 @@ fn f(x: EnumAlias<u8>) {
 
 #[test]
 fn pat_no_unstable_item_on_stable() {
-    check_empty(
+    check(
         r#"
 //- /main.rs crate:main deps:std
 use std::*;
@@ -795,7 +790,7 @@ pub enum Enum {
 
 #[test]
 fn pat_unstable_item_on_nightly() {
-    check_empty(
+    check(
         r#"
 //- toolchain:nightly
 //- /main.rs crate:main deps:std
@@ -908,7 +903,7 @@ fn foo() {
     );
 
     // Do not suggest reserved keywords
-    check_empty(
+    check(
         r#"
 struct Struct;
 
@@ -926,7 +921,7 @@ fn foo() {
 
 #[test]
 fn private_item_in_module_in_function_body() {
-    check_empty(
+    check(
         r#"
 fn main() {
     mod foo {
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/predicate.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/predicate.rs
index c1926359efc..65036f6a224 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/predicate.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/predicate.rs
@@ -1,17 +1,12 @@
 //! Completion tests for predicates and bounds.
-use expect_test::{expect, Expect};
+use expect_test::expect;
 
-use crate::tests::{check_empty, completion_list, BASE_ITEMS_FIXTURE};
-
-fn check(ra_fixture: &str, expect: Expect) {
-    let actual = completion_list(&format!("{BASE_ITEMS_FIXTURE}\n{ra_fixture}"));
-    expect.assert_eq(&actual)
-}
+use crate::tests::{check, check_with_base_items};
 
 #[test]
 fn predicate_start() {
     // FIXME: `for` kw
-    check(
+    check_with_base_items(
         r#"
 struct Foo<'lt, T, const C: usize> where $0 {}
 "#,
@@ -34,7 +29,7 @@ struct Foo<'lt, T, const C: usize> where $0 {}
 
 #[test]
 fn bound_for_type_pred() {
-    check(
+    check_with_base_items(
         r#"
 struct Foo<'lt, T, const C: usize> where T: $0 {}
 "#,
@@ -52,7 +47,7 @@ struct Foo<'lt, T, const C: usize> where T: $0 {}
 fn bound_for_lifetime_pred() {
     // FIXME: should only show lifetimes here, that is we shouldn't get any completions here when not typing
     // a `'`
-    check(
+    check_with_base_items(
         r#"
 struct Foo<'lt, T, const C: usize> where 'lt: $0 {}
 "#,
@@ -68,7 +63,7 @@ struct Foo<'lt, T, const C: usize> where 'lt: $0 {}
 
 #[test]
 fn bound_for_for_pred() {
-    check(
+    check_with_base_items(
         r#"
 struct Foo<'lt, T, const C: usize> where for<'a> T: $0 {}
 "#,
@@ -84,7 +79,7 @@ struct Foo<'lt, T, const C: usize> where for<'a> T: $0 {}
 
 #[test]
 fn param_list_for_for_pred() {
-    check(
+    check_with_base_items(
         r#"
 struct Foo<'lt, T, const C: usize> where for<'a> $0 {}
 "#,
@@ -107,7 +102,7 @@ struct Foo<'lt, T, const C: usize> where for<'a> $0 {}
 
 #[test]
 fn pred_on_fn_in_impl() {
-    check(
+    check_with_base_items(
         r#"
 impl Record {
     fn method(self) where $0 {}
@@ -132,7 +127,7 @@ impl Record {
 
 #[test]
 fn pred_no_unstable_item_on_stable() {
-    check_empty(
+    check(
         r#"
 //- /main.rs crate:main deps:std
 use std::*;
@@ -151,7 +146,7 @@ pub trait Trait {}
 
 #[test]
 fn pred_unstable_item_on_nightly() {
-    check_empty(
+    check(
         r#"
 //- toolchain:nightly
 //- /main.rs crate:main deps:std
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/proc_macros.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/proc_macros.rs
index afc286b6fb4..6b1dfe366ce 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/proc_macros.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/proc_macros.rs
@@ -1,12 +1,7 @@
 //! Completion tests for expressions.
-use expect_test::{expect, Expect};
+use expect_test::expect;
 
-use crate::tests::completion_list;
-
-fn check(ra_fixture: &str, expect: Expect) {
-    let actual = completion_list(ra_fixture);
-    expect.assert_eq(&actual)
-}
+use crate::tests::check;
 
 #[test]
 fn complete_dot_in_attr() {
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/raw_identifiers.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/raw_identifiers.rs
index d81b3d697aa..9ab66243b5c 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/raw_identifiers.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/raw_identifiers.rs
@@ -4,7 +4,7 @@ use itertools::Itertools;
 
 use crate::tests::{completion_list_with_config_raw, position, TEST_CONFIG};
 
-fn check(ra_fixture: &str, expect: Expect) {
+fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
     let completions = completion_list_with_config_raw(TEST_CONFIG, ra_fixture, true, None);
     let (db, position) = position(ra_fixture);
     let mut actual = db.file_text(position.file_id).to_string();
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/record.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/record.rs
index a9c9f604e07..a1013b86548 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/record.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/record.rs
@@ -1,14 +1,9 @@
-use expect_test::{expect, Expect};
+use expect_test::expect;
 
-use crate::tests::completion_list;
+use crate::tests::check;
 
 use super::check_edit;
 
-fn check(ra_fixture: &str, expect: Expect) {
-    let actual = completion_list(ra_fixture);
-    expect.assert_eq(&actual);
-}
-
 #[test]
 fn without_default_impl() {
     check(
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 6cfb2231a99..2b05184bdbe 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
@@ -5,32 +5,12 @@ use ide_db::SymbolKind;
 
 use crate::{
     tests::{
-        check_edit, completion_list, completion_list_no_kw, completion_list_with_trigger_character,
+        check, check_edit, check_no_kw, check_with_trigger_character, do_completion_with_config,
+        TEST_CONFIG,
     },
     CompletionItemKind,
 };
 
-use super::{do_completion_with_config, TEST_CONFIG};
-
-fn check_no_kw(ra_fixture: &str, expect: Expect) {
-    let actual = completion_list_no_kw(ra_fixture);
-    expect.assert_eq(&actual)
-}
-
-fn check(ra_fixture: &str, expect: Expect) {
-    let actual = completion_list(ra_fixture);
-    expect.assert_eq(&actual)
-}
-
-pub(crate) fn check_with_trigger_character(
-    ra_fixture: &str,
-    trigger_character: Option<char>,
-    expect: Expect,
-) {
-    let actual = completion_list_with_trigger_character(ra_fixture, trigger_character);
-    expect.assert_eq(&actual)
-}
-
 #[test]
 fn completes_if_prefix_is_keyword() {
     check_edit(
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs
index 9ea262bcc59..c7e2d058257 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs
@@ -1,16 +1,11 @@
 //! Completion tests for type position.
-use expect_test::{expect, Expect};
+use expect_test::expect;
 
-use crate::tests::{check_empty, completion_list, BASE_ITEMS_FIXTURE};
-
-fn check(ra_fixture: &str, expect: Expect) {
-    let actual = completion_list(&format!("{BASE_ITEMS_FIXTURE}\n{ra_fixture}"));
-    expect.assert_eq(&actual)
-}
+use crate::tests::{check, check_with_base_items};
 
 #[test]
 fn record_field_ty() {
-    check(
+    check_with_base_items(
         r#"
 struct Foo<'lt, T, const C: usize> {
     f: $0
@@ -37,7 +32,7 @@ struct Foo<'lt, T, const C: usize> {
 
 #[test]
 fn tuple_struct_field() {
-    check(
+    check_with_base_items(
         r#"
 struct Foo<'lt, T, const C: usize>(f$0);
 "#,
@@ -65,7 +60,7 @@ struct Foo<'lt, T, const C: usize>(f$0);
 
 #[test]
 fn fn_return_type() {
-    check(
+    check_with_base_items(
         r#"
 fn x<'lt, T, const C: usize>() -> $0
 "#,
@@ -88,7 +83,7 @@ fn x<'lt, T, const C: usize>() -> $0
 
 #[test]
 fn fn_return_type_no_local_items() {
-    check(
+    check_with_base_items(
         r#"
 fn foo() -> B$0 {
     struct Bar;
@@ -118,7 +113,7 @@ fn foo() -> B$0 {
 
 #[test]
 fn inferred_type_const() {
-    check(
+    check_with_base_items(
         r#"
 struct Foo<T>(T);
 const FOO: $0 = Foo(2);
@@ -143,7 +138,7 @@ const FOO: $0 = Foo(2);
 
 #[test]
 fn inferred_type_closure_param() {
-    check(
+    check_with_base_items(
         r#"
 fn f1(f: fn(i32) -> i32) {}
 fn f2() {
@@ -169,7 +164,7 @@ fn f2() {
 
 #[test]
 fn inferred_type_closure_return() {
-    check(
+    check_with_base_items(
         r#"
 fn f1(f: fn(u64) -> u64) {}
 fn f2() {
@@ -197,7 +192,7 @@ fn f2() {
 
 #[test]
 fn inferred_type_fn_return() {
-    check(
+    check_with_base_items(
         r#"
 fn f2(x: u64) -> $0 {
     x + 5
@@ -222,7 +217,7 @@ fn f2(x: u64) -> $0 {
 
 #[test]
 fn inferred_type_fn_param() {
-    check(
+    check_with_base_items(
         r#"
 fn f1(x: i32) {}
 fn f2(x: $0) {
@@ -248,7 +243,7 @@ fn f2(x: $0) {
 
 #[test]
 fn inferred_type_not_in_the_scope() {
-    check(
+    check_with_base_items(
         r#"
 mod a {
     pub struct Foo<T>(T);
@@ -282,7 +277,7 @@ fn foo<'lt, T, const C: usize>() {
 
 #[test]
 fn inferred_type_let() {
-    check(
+    check_with_base_items(
         r#"
 struct Foo<T>(T);
 fn foo<'lt, T, const C: usize>() {
@@ -311,7 +306,7 @@ fn foo<'lt, T, const C: usize>() {
 
 #[test]
 fn body_type_pos() {
-    check(
+    check_with_base_items(
         r#"
 fn foo<'lt, T, const C: usize>() {
     let local = ();
@@ -333,7 +328,7 @@ fn foo<'lt, T, const C: usize>() {
             kw self::
         "#]],
     );
-    check(
+    check_with_base_items(
         r#"
 fn foo<'lt, T, const C: usize>() {
     let local = ();
@@ -356,7 +351,7 @@ fn foo<'lt, T, const C: usize>() {
 #[test]
 fn completes_types_and_const_in_arg_list() {
     cov_mark::check!(complete_assoc_type_in_generics_list);
-    check(
+    check_with_base_items(
         r#"
 trait Trait1 {
     type Super;
@@ -372,7 +367,7 @@ fn foo<'lt, T: Trait2<$0>, const CONST_PARAM: usize>(_: T) {}
             ta Super =  (as Trait1) type Super
         "#]],
     );
-    check(
+    check_with_base_items(
         r#"
 trait Trait1 {
     type Super;
@@ -400,7 +395,7 @@ fn foo<'lt, T: Trait2<$0>, const CONST_PARAM: usize>(_: T) {}
             kw self::
         "#]],
     );
-    check(
+    check_with_base_items(
         r#"
 trait Trait2<T> {
     type Foo;
@@ -424,7 +419,7 @@ fn foo<'lt, T: Trait2<self::$0>, const CONST_PARAM: usize>(_: T) {}
 
 #[test]
 fn no_assoc_completion_outside_type_bounds() {
-    check(
+    check_with_base_items(
         r#"
 struct S;
 trait Tr<T> {
@@ -454,7 +449,7 @@ impl Tr<$0
 
 #[test]
 fn enum_qualified() {
-    check(
+    check_with_base_items(
         r#"
 impl Enum {
     type AssocType = ();
@@ -471,7 +466,7 @@ fn func(_: Enum::$0) {}
 
 #[test]
 fn completes_type_parameter_or_associated_type() {
-    check(
+    check_with_base_items(
         r#"
 trait MyTrait<T, U> {
     type Item1;
@@ -496,7 +491,7 @@ fn f(t: impl MyTrait<u$0
         "#]],
     );
 
-    check(
+    check_with_base_items(
         r#"
 trait MyTrait<T, U> {
     type Item1;
@@ -521,7 +516,7 @@ fn f(t: impl MyTrait<u8, u$0
         "#]],
     );
 
-    check(
+    check_with_base_items(
         r#"
 trait MyTrait<T, U> {
     type Item1;
@@ -539,7 +534,7 @@ fn f(t: impl MyTrait<u8, u8, I$0
 
 #[test]
 fn completes_type_parameter_or_associated_type_with_default_value() {
-    check(
+    check_with_base_items(
         r#"
 trait MyTrait<T, U = u8> {
     type Item1;
@@ -564,7 +559,7 @@ fn f(t: impl MyTrait<u$0
         "#]],
     );
 
-    check(
+    check_with_base_items(
         r#"
 trait MyTrait<T, U = u8> {
     type Item1;
@@ -591,7 +586,7 @@ fn f(t: impl MyTrait<u8, u$0
         "#]],
     );
 
-    check(
+    check_with_base_items(
         r#"
 trait MyTrait<T, U = u8> {
     type Item1;
@@ -609,7 +604,7 @@ fn f(t: impl MyTrait<u8, u8, I$0
 
 #[test]
 fn completes_types_after_associated_type() {
-    check(
+    check_with_base_items(
         r#"
 trait MyTrait {
     type Item1;
@@ -634,7 +629,7 @@ fn f(t: impl MyTrait<Item1 = $0
         "#]],
     );
 
-    check(
+    check_with_base_items(
         r#"
 trait MyTrait {
     type Item1;
@@ -659,7 +654,7 @@ fn f(t: impl MyTrait<Item1 = u8, Item2 = $0
         "#]],
     );
 
-    check(
+    check_with_base_items(
         r#"
 trait MyTrait {
     const C: usize;
@@ -678,7 +673,7 @@ fn f(t: impl MyTrait<C = $0
 
 #[test]
 fn type_pos_no_unstable_type_on_stable() {
-    check_empty(
+    check(
         r#"
 //- /main.rs crate:main deps:std
 use std::*;
@@ -702,7 +697,7 @@ pub struct S;
 
 #[test]
 fn type_pos_unstable_type_on_nightly() {
-    check_empty(
+    check(
         r#"
 //- toolchain:nightly
 //- /main.rs crate:main deps:std
@@ -729,7 +724,7 @@ pub struct S;
 #[test]
 fn completes_const_and_type_generics_separately() {
     // Function generic params
-    check(
+    check_with_base_items(
         r#"
     struct Foo;
     const X: usize = 0;
@@ -756,7 +751,7 @@ fn completes_const_and_type_generics_separately() {
     // FIXME: This should probably also suggest completions for types, at least those that have
     // associated constants usable in this position. For example, a user could be typing
     // `foo::<_, { usize::MAX }>()`, but we currently don't suggest `usize` in constant position.
-    check(
+    check_with_base_items(
         r#"
     struct Foo;
     const X: usize = 0;
@@ -775,7 +770,7 @@ fn completes_const_and_type_generics_separately() {
     );
 
     // Method generic params
-    check(
+    check_with_base_items(
         r#"
     const X: usize = 0;
     struct Foo;
@@ -799,7 +794,7 @@ fn completes_const_and_type_generics_separately() {
             kw self::
         "#]],
     );
-    check(
+    check_with_base_items(
         r#"
     const X: usize = 0;
     struct Foo;
@@ -818,7 +813,7 @@ fn completes_const_and_type_generics_separately() {
     );
 
     // Associated type generic params
-    check(
+    check_with_base_items(
         r#"
     const X: usize = 0;
     struct Foo;
@@ -843,7 +838,7 @@ fn completes_const_and_type_generics_separately() {
             kw self::
         "#]],
     );
-    check(
+    check_with_base_items(
         r#"
     const X: usize = 0;
     struct Foo;
@@ -862,7 +857,7 @@ fn completes_const_and_type_generics_separately() {
     );
 
     // Type generic params
-    check(
+    check_with_base_items(
         r#"
     const X: usize = 0;
     struct Foo<T, const N: usize>(T);
@@ -880,7 +875,7 @@ fn completes_const_and_type_generics_separately() {
     );
 
     // Type alias generic params
-    check(
+    check_with_base_items(
         r#"
     const X: usize = 0;
     struct Foo<T, const N: usize>(T);
@@ -899,7 +894,7 @@ fn completes_const_and_type_generics_separately() {
     );
 
     // Enum variant params
-    check(
+    check_with_base_items(
         r#"
     const X: usize = 0;
     enum Foo<T, const N: usize> { A(T), B }
@@ -917,7 +912,7 @@ fn completes_const_and_type_generics_separately() {
     );
 
     // Trait params
-    check(
+    check_with_base_items(
         r#"
     const X: usize = 0;
     trait Foo<T, const N: usize> {}
@@ -933,7 +928,7 @@ fn completes_const_and_type_generics_separately() {
     );
 
     // Trait alias params
-    check(
+    check_with_base_items(
         r#"
     #![feature(trait_alias)]
     const X: usize = 0;
@@ -951,7 +946,7 @@ fn completes_const_and_type_generics_separately() {
     );
 
     // Omitted lifetime params
-    check(
+    check_with_base_items(
         r#"
 struct S<'a, 'b, const C: usize, T>(core::marker::PhantomData<&'a &'b T>);
 fn foo<'a>() { S::<F$0, _>; }
@@ -964,7 +959,7 @@ fn foo<'a>() { S::<F$0, _>; }
         "#]],
     );
     // Explicit lifetime params
-    check(
+    check_with_base_items(
         r#"
 struct S<'a, 'b, const C: usize, T>(core::marker::PhantomData<&'a &'b T>);
 fn foo<'a>() { S::<'static, 'static, F$0, _>; }
@@ -976,7 +971,7 @@ fn foo<'a>() { S::<'static, 'static, F$0, _>; }
             kw self::
         "#]],
     );
-    check(
+    check_with_base_items(
         r#"
 struct S<'a, 'b, const C: usize, T>(core::marker::PhantomData<&'a &'b T>);
 fn foo<'a>() { S::<'static, F$0, _, _>; }
@@ -992,7 +987,7 @@ fn foo<'a>() { S::<'static, F$0, _, _>; }
 
 #[test]
 fn complete_traits_on_impl_trait_block() {
-    check(
+    check_with_base_items(
         r#"
 trait Foo {}
 
@@ -1012,7 +1007,7 @@ impl $0 for Bar { }
 
 #[test]
 fn complete_traits_with_path_on_impl_trait_block() {
-    check(
+    check_with_base_items(
         r#"
 mod outer {
     pub trait Foo {}
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/use_tree.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/use_tree.rs
index 2ea2e4e4c96..04b3a47a64d 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/use_tree.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/use_tree.rs
@@ -1,12 +1,7 @@
 //! Completion tests for use trees.
-use expect_test::{expect, Expect};
+use expect_test::expect;
 
-use crate::tests::completion_list;
-
-fn check(ra_fixture: &str, expect: Expect) {
-    let actual = completion_list(ra_fixture);
-    expect.assert_eq(&actual)
-}
+use crate::tests::check;
 
 #[test]
 fn use_tree_completion() {
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/visibility.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/visibility.rs
index c18d6e66dd6..4b5a0ac1c2b 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/visibility.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/visibility.rs
@@ -1,17 +1,7 @@
 //! Completion tests for visibility modifiers.
-use expect_test::{expect, Expect};
+use expect_test::expect;
 
-use crate::tests::{completion_list, completion_list_with_trigger_character};
-
-fn check(ra_fixture: &str, expect: Expect) {
-    let actual = completion_list(ra_fixture);
-    expect.assert_eq(&actual)
-}
-
-fn check_with_trigger_character(ra_fixture: &str, trigger_character: char, expect: Expect) {
-    let actual = completion_list_with_trigger_character(ra_fixture, Some(trigger_character));
-    expect.assert_eq(&actual)
-}
+use crate::tests::{check, check_with_trigger_character};
 
 #[test]
 fn empty_pub() {
@@ -20,7 +10,7 @@ fn empty_pub() {
         r#"
 pub($0)
 "#,
-        '(',
+        Some('('),
         expect![[r#"
             kw crate
             kw in
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/active_parameter.rs b/src/tools/rust-analyzer/crates/ide-db/src/active_parameter.rs
index 7482491fc63..11808fed3be 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/active_parameter.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/active_parameter.rs
@@ -4,7 +4,7 @@ use either::Either;
 use hir::{InFile, Semantics, Type};
 use parser::T;
 use syntax::{
-    ast::{self, HasArgList, HasName},
+    ast::{self, AstChildren, HasArgList, HasAttrs, HasName},
     match_ast, AstNode, NodeOrToken, SyntaxToken,
 };
 
@@ -37,6 +37,10 @@ impl ActiveParameter {
             _ => None,
         })
     }
+
+    pub fn attrs(&self) -> Option<AstChildren<ast::Attr>> {
+        self.src.as_ref().and_then(|param| Some(param.value.as_ref().right()?.attrs()))
+    }
 }
 
 /// Returns a [`hir::Callable`] this token is a part of and its argument index of said callable.
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 2d30bb41273..49d26dfe25c 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/defs.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/defs.rs
@@ -32,6 +32,7 @@ pub enum Definition {
     Field(Field),
     TupleField(TupleField),
     Module(Module),
+    Crate(Crate),
     Function(Function),
     Adt(Adt),
     Variant(Variant),
@@ -62,14 +63,19 @@ impl Definition {
     pub fn krate(&self, db: &RootDatabase) -> Option<Crate> {
         Some(match self {
             Definition::Module(m) => m.krate(),
+            &Definition::Crate(it) => it,
             _ => self.module(db)?.krate(),
         })
     }
 
+    /// Returns the module this definition resides in.
+    ///
+    /// As such, for modules themselves this will return the parent module.
     pub fn module(&self, db: &RootDatabase) -> Option<Module> {
         let module = match self {
             Definition::Macro(it) => it.module(db),
             Definition::Module(it) => it.parent(db)?,
+            Definition::Crate(_) => return None,
             Definition::Field(it) => it.parent_def(db).module(db),
             Definition::Function(it) => it.module(db),
             Definition::Adt(it) => it.module(db),
@@ -86,11 +92,11 @@ impl Definition {
             Definition::ExternCrateDecl(it) => it.module(db),
             Definition::DeriveHelper(it) => it.derive().module(db),
             Definition::InlineAsmOperand(it) => it.parent(db).module(db),
+            Definition::ToolModule(t) => t.krate().root_module(),
             Definition::BuiltinAttr(_)
             | Definition::BuiltinType(_)
             | Definition::BuiltinLifetime(_)
             | Definition::TupleField(_)
-            | Definition::ToolModule(_)
             | Definition::InlineAsmRegOrRegClass(_) => return None,
         };
         Some(module)
@@ -108,6 +114,7 @@ impl Definition {
         match self {
             Definition::Macro(it) => Some(it.module(db).into()),
             Definition::Module(it) => it.parent(db).map(Definition::Module),
+            Definition::Crate(_) => None,
             Definition::Field(it) => Some(it.parent_def(db).into()),
             Definition::Function(it) => container_to_definition(it.container(db)),
             Definition::Adt(it) => Some(it.module(db).into()),
@@ -137,6 +144,7 @@ impl Definition {
         let vis = match self {
             Definition::Field(sf) => sf.visibility(db),
             Definition::Module(it) => it.visibility(db),
+            Definition::Crate(_) => return None,
             Definition::Function(it) => it.visibility(db),
             Definition::Adt(it) => it.visibility(db),
             Definition::Const(it) => it.visibility(db),
@@ -146,8 +154,8 @@ impl Definition {
             Definition::TypeAlias(it) => it.visibility(db),
             Definition::Variant(it) => it.visibility(db),
             Definition::ExternCrateDecl(it) => it.visibility(db),
+            Definition::Macro(it) => it.visibility(db),
             Definition::BuiltinType(_) | Definition::TupleField(_) => Visibility::Public,
-            Definition::Macro(_) => return None,
             Definition::BuiltinAttr(_)
             | Definition::BuiltinLifetime(_)
             | Definition::ToolModule(_)
@@ -167,6 +175,9 @@ impl Definition {
             Definition::Macro(it) => it.name(db),
             Definition::Field(it) => it.name(db),
             Definition::Module(it) => it.name(db)?,
+            Definition::Crate(it) => {
+                Name::new_symbol_root(it.display_name(db)?.crate_name().symbol().clone())
+            }
             Definition::Function(it) => it.name(db),
             Definition::Adt(it) => it.name(db),
             Definition::Variant(it) => it.name(db),
@@ -202,6 +213,7 @@ impl Definition {
             Definition::Macro(it) => it.docs(db),
             Definition::Field(it) => it.docs(db),
             Definition::Module(it) => it.docs(db),
+            Definition::Crate(it) => it.docs(db),
             Definition::Function(it) => it.docs(db),
             Definition::Adt(it) => it.docs(db),
             Definition::Variant(it) => it.docs(db),
@@ -282,6 +294,7 @@ impl Definition {
             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(),
@@ -415,7 +428,7 @@ impl IdentClass {
             }
             IdentClass::NameRefClass(NameRefClass::ExternCrateShorthand { decl, krate }) => {
                 res.push((Definition::ExternCrateDecl(decl), None));
-                res.push((Definition::Module(krate.root_module()), None));
+                res.push((Definition::Crate(krate), None));
             }
             IdentClass::Operator(
                 OperatorClass::Await(func)
@@ -456,7 +469,7 @@ impl IdentClass {
             }
             IdentClass::NameRefClass(NameRefClass::ExternCrateShorthand { decl, krate }) => {
                 res.push(Definition::ExternCrateDecl(decl));
-                res.push(Definition::Module(krate.root_module()));
+                res.push(Definition::Crate(krate));
             }
             IdentClass::Operator(_) => (),
         }
@@ -800,7 +813,7 @@ impl NameRefClass {
                     let extern_crate = sema.to_def(&extern_crate_ast)?;
                     let krate = extern_crate.resolved_crate(sema.db)?;
                     Some(if extern_crate_ast.rename().is_some() {
-                        NameRefClass::Definition(Definition::Module(krate.root_module()), None)
+                        NameRefClass::Definition(Definition::Crate(krate), None)
                     } else {
                         NameRefClass::ExternCrateShorthand { krate, decl: extern_crate }
                     })
@@ -976,3 +989,19 @@ impl From<GenericDef> for Definition {
         }
     }
 }
+
+impl TryFrom<Definition> for GenericDef {
+    type Error = ();
+    fn try_from(def: Definition) -> Result<Self, Self::Error> {
+        match def {
+            Definition::Function(it) => Ok(it.into()),
+            Definition::Adt(it) => Ok(it.into()),
+            Definition::Trait(it) => Ok(it.into()),
+            Definition::TraitAlias(it) => Ok(it.into()),
+            Definition::TypeAlias(it) => Ok(it.into()),
+            Definition::SelfType(it) => Ok(it.into()),
+            Definition::Const(it) => Ok(it.into()),
+            _ => Err(()),
+        }
+    }
+}
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/documentation.rs b/src/tools/rust-analyzer/crates/ide-db/src/documentation.rs
index a0ef0f90a65..b83efcd02f7 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/documentation.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/documentation.rs
@@ -178,7 +178,7 @@ macro_rules! impl_has_docs {
 
 impl_has_docs![
     Variant, Field, Static, Const, Trait, TraitAlias, TypeAlias, Macro, Function, Adt, Module,
-    Impl,
+    Impl, Crate,
 ];
 
 macro_rules! impl_has_docs_enum {
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/famous_defs.rs b/src/tools/rust-analyzer/crates/ide-db/src/famous_defs.rs
index ba6e50abf65..9e3506d6f53 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/famous_defs.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/famous_defs.rs
@@ -106,6 +106,10 @@ impl FamousDefs<'_, '_> {
         self.find_trait("core:marker:Copy")
     }
 
+    pub fn core_marker_Sized(&self) -> Option<Trait> {
+        self.find_trait("core:marker:Sized")
+    }
+
     pub fn core_future_Future(&self) -> Option<Trait> {
         self.find_trait("core:future:Future")
     }
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs b/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs
index 8f0be1d9035..ad86d855b55 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs
@@ -1,15 +1,17 @@
 //! Look up accessible paths for items.
 
+use std::ops::ControlFlow;
+
 use hir::{
     db::HirDatabase, AsAssocItem, AssocItem, AssocItemContainer, Crate, HasCrate, ImportPathConfig,
-    ItemInNs, ModPath, Module, ModuleDef, PathResolution, PrefixKind, ScopeDef, Semantics,
+    ItemInNs, ModPath, Module, ModuleDef, Name, PathResolution, PrefixKind, ScopeDef, Semantics,
     SemanticsScope, Trait, TyFingerprint, Type,
 };
 use itertools::Itertools;
 use rustc_hash::{FxHashMap, FxHashSet};
 use syntax::{
     ast::{self, make, HasName},
-    AstNode, SmolStr, SyntaxNode,
+    AstNode, SyntaxNode,
 };
 
 use crate::{
@@ -51,7 +53,7 @@ pub struct TraitImportCandidate {
 #[derive(Debug)]
 pub struct PathImportCandidate {
     /// Optional qualifier before name.
-    pub qualifier: Vec<SmolStr>,
+    pub qualifier: Vec<Name>,
     /// The name the item (struct, trait, enum, etc.) should have.
     pub name: NameToImport,
 }
@@ -70,10 +72,18 @@ pub enum NameToImport {
 
 impl NameToImport {
     pub fn exact_case_sensitive(s: String) -> NameToImport {
+        let s = match s.strip_prefix("r#") {
+            Some(s) => s.to_owned(),
+            None => s,
+        };
         NameToImport::Exact(s, true)
     }
 
     pub fn fuzzy(s: String) -> NameToImport {
+        let s = match s.strip_prefix("r#") {
+            Some(s) => s.to_owned(),
+            None => s,
+        };
         // unless all chars are lowercase, we do a case sensitive search
         let case_sensitive = s.chars().any(|c| c.is_uppercase());
         NameToImport::Fuzzy(s, case_sensitive)
@@ -350,21 +360,27 @@ fn path_applicable_imports(
             .take(DEFAULT_QUERY_SEARCH_LIMIT.inner())
             .collect()
         }
+        // we have some unresolved qualifier that we search an import for
+        // The key here is that whatever we import must form a resolved path for the remainder of
+        // what follows
+        // FIXME: This doesn't handle visibility
         [first_qsegment, qualifier_rest @ ..] => items_locator::items_with_name(
             sema,
             current_crate,
-            NameToImport::Exact(first_qsegment.to_string(), true),
+            NameToImport::Exact(first_qsegment.as_str().to_owned(), true),
             AssocSearchMode::Exclude,
         )
         .filter_map(|item| {
-            import_for_item(
+            // we found imports for `first_qsegment`, now we need to filter these imports by whether
+            // they result in resolving the rest of the path successfully
+            validate_resolvable(
                 sema,
                 scope,
                 mod_path,
+                scope_filter,
                 &path_candidate.name,
                 item,
                 qualifier_rest,
-                scope_filter,
             )
         })
         .take(DEFAULT_QUERY_SEARCH_LIMIT.inner())
@@ -372,14 +388,16 @@ fn path_applicable_imports(
     }
 }
 
-fn import_for_item(
+/// Validates and builds an import for `resolved_qualifier` if the `unresolved_qualifier` appended
+/// to it resolves and there is a validate `candidate` after that.
+fn validate_resolvable(
     sema: &Semantics<'_, RootDatabase>,
     scope: &SemanticsScope<'_>,
     mod_path: impl Fn(ItemInNs) -> Option<ModPath>,
+    scope_filter: impl Fn(ItemInNs) -> bool,
     candidate: &NameToImport,
     resolved_qualifier: ItemInNs,
-    unresolved_qualifier: &[SmolStr],
-    scope_filter: impl Fn(ItemInNs) -> bool,
+    unresolved_qualifier: &[Name],
 ) -> Option<LocatedImport> {
     let _p = tracing::info_span!("ImportAssets::import_for_item").entered();
 
@@ -410,8 +428,11 @@ fn import_for_item(
                 module,
                 candidate.clone(),
                 AssocSearchMode::Exclude,
+                |it| match scope_filter(it) {
+                    true => ControlFlow::Break(it),
+                    false => ControlFlow::Continue(()),
+                },
             )
-            .find(|&it| scope_filter(it))
             .map(|item| LocatedImport::new(import_path_candidate, resolved_qualifier, item))
         }
         // FIXME
@@ -709,7 +730,7 @@ fn path_import_candidate(
                 if qualifier.first_qualifier().is_none_or(|it| sema.resolve_path(&it).is_none()) {
                     let qualifier = qualifier
                         .segments()
-                        .map(|seg| seg.name_ref().map(|name| SmolStr::new(name.text())))
+                        .map(|seg| seg.name_ref().map(|name| Name::new_root(&name.text())))
                         .collect::<Option<Vec<_>>>()?;
                     ImportCandidate::Path(PathImportCandidate { qualifier, name })
                 } else {
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use/tests.rs b/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use/tests.rs
index 81604b55272..decb0ea9d8a 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use/tests.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use/tests.rs
@@ -1244,8 +1244,8 @@ use ::ext::foo::Foo;
 
 fn check_with_config(
     path: &str,
-    ra_fixture_before: &str,
-    ra_fixture_after: &str,
+    #[rust_analyzer::rust_fixture] ra_fixture_before: &str,
+    #[rust_analyzer::rust_fixture] ra_fixture_after: &str,
     config: &InsertUseConfig,
 ) {
     let (db, file_id, pos) = if ra_fixture_before.contains(CURSOR_MARKER) {
@@ -1277,8 +1277,8 @@ fn check_with_config(
 
 fn check(
     path: &str,
-    ra_fixture_before: &str,
-    ra_fixture_after: &str,
+    #[rust_analyzer::rust_fixture] ra_fixture_before: &str,
+    #[rust_analyzer::rust_fixture] ra_fixture_after: &str,
     granularity: ImportGranularity,
 ) {
     check_with_config(
@@ -1295,19 +1295,35 @@ fn check(
     )
 }
 
-fn check_crate(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) {
+fn check_crate(
+    path: &str,
+    #[rust_analyzer::rust_fixture] ra_fixture_before: &str,
+    #[rust_analyzer::rust_fixture] ra_fixture_after: &str,
+) {
     check(path, ra_fixture_before, ra_fixture_after, ImportGranularity::Crate)
 }
 
-fn check_module(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) {
+fn check_module(
+    path: &str,
+    #[rust_analyzer::rust_fixture] ra_fixture_before: &str,
+    #[rust_analyzer::rust_fixture] ra_fixture_after: &str,
+) {
     check(path, ra_fixture_before, ra_fixture_after, ImportGranularity::Module)
 }
 
-fn check_none(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) {
+fn check_none(
+    path: &str,
+    #[rust_analyzer::rust_fixture] ra_fixture_before: &str,
+    #[rust_analyzer::rust_fixture] ra_fixture_after: &str,
+) {
     check(path, ra_fixture_before, ra_fixture_after, ImportGranularity::Item)
 }
 
-fn check_one(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) {
+fn check_one(
+    path: &str,
+    #[rust_analyzer::rust_fixture] ra_fixture_before: &str,
+    #[rust_analyzer::rust_fixture] ra_fixture_after: &str,
+) {
     check(path, ra_fixture_before, ra_fixture_after, ImportGranularity::One)
 }
 
@@ -1330,7 +1346,7 @@ fn check_merge_only_fail(ra_fixture0: &str, ra_fixture1: &str, mb: MergeBehavior
     assert_eq!(result.map(|u| u.to_string()), None);
 }
 
-fn check_guess(ra_fixture: &str, expected: ImportGranularityGuess) {
+fn check_guess(#[rust_analyzer::rust_fixture] ra_fixture: &str, expected: ImportGranularityGuess) {
     let syntax = ast::SourceFile::parse(ra_fixture, span::Edition::CURRENT).tree().syntax().clone();
     let file = ImportScope::from(syntax).unwrap();
     assert_eq!(super::guess_granularity_from_scope(&file), expected);
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/items_locator.rs b/src/tools/rust-analyzer/crates/ide-db/src/items_locator.rs
index 7f66ea0c103..a2062f36d3f 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/items_locator.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/items_locator.rs
@@ -2,6 +2,8 @@
 //! by its name and a few criteria.
 //! The main reason for this module to exist is the fact that project's items and dependencies' items
 //! are located in different caches, with different APIs.
+use std::ops::ControlFlow;
+
 use either::Either;
 use hir::{import_map, Crate, ItemInNs, Module, Semantics};
 use limit::Limit;
@@ -17,6 +19,7 @@ pub static DEFAULT_QUERY_SEARCH_LIMIT: Limit = Limit::new(100);
 
 pub use import_map::AssocSearchMode;
 
+// FIXME: Do callbacks instead to avoid allocations.
 /// Searches for importable items with the given name in the crate and its dependencies.
 pub fn items_with_name<'a>(
     sema: &'a Semantics<'_, RootDatabase>,
@@ -70,12 +73,13 @@ pub fn items_with_name<'a>(
 }
 
 /// Searches for importable items with the given name in the crate and its dependencies.
-pub fn items_with_name_in_module<'a>(
-    sema: &'a Semantics<'_, RootDatabase>,
+pub fn items_with_name_in_module<T>(
+    sema: &Semantics<'_, RootDatabase>,
     module: Module,
     name: NameToImport,
     assoc_item_search: AssocSearchMode,
-) -> impl Iterator<Item = ItemInNs> + 'a {
+    mut cb: impl FnMut(ItemInNs) -> ControlFlow<T>,
+) -> Option<T> {
     let _p = tracing::info_span!("items_with_name_in", name = name.text(), assoc_item_search = ?assoc_item_search, ?module)
         .entered();
 
@@ -107,14 +111,12 @@ pub fn items_with_name_in_module<'a>(
             local_query
         }
     };
-    let mut local_results = Vec::new();
     local_query.search(&[sema.db.module_symbols(module)], |local_candidate| {
-        local_results.push(match local_candidate.def {
+        cb(match local_candidate.def {
             hir::ModuleDef::Macro(macro_def) => ItemInNs::Macros(macro_def),
             def => ItemInNs::from(def),
         })
-    });
-    local_results.into_iter()
+    })
 }
 
 fn find_items<'a>(
@@ -142,7 +144,8 @@ fn find_items<'a>(
         local_results.push(match local_candidate.def {
             hir::ModuleDef::Macro(macro_def) => ItemInNs::Macros(macro_def),
             def => ItemInNs::from(def),
-        })
+        });
+        ControlFlow::<()>::Continue(())
     });
     local_results.into_iter().chain(external_importables)
 }
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs
index b3105e6524d..3a29232d331 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs
@@ -260,23 +260,23 @@ impl From<hir::MacroKind> for SymbolKind {
     }
 }
 
-impl From<hir::ModuleDefId> for SymbolKind {
-    fn from(it: hir::ModuleDefId) -> Self {
+impl From<hir::ModuleDef> for SymbolKind {
+    fn from(it: hir::ModuleDef) -> Self {
         match it {
-            hir::ModuleDefId::ConstId(..) => SymbolKind::Const,
-            hir::ModuleDefId::EnumVariantId(..) => SymbolKind::Variant,
-            hir::ModuleDefId::FunctionId(..) => SymbolKind::Function,
-            hir::ModuleDefId::MacroId(hir::MacroId::ProcMacroId(..)) => SymbolKind::ProcMacro,
-            hir::ModuleDefId::MacroId(..) => SymbolKind::Macro,
-            hir::ModuleDefId::ModuleId(..) => SymbolKind::Module,
-            hir::ModuleDefId::StaticId(..) => SymbolKind::Static,
-            hir::ModuleDefId::AdtId(hir::AdtId::StructId(..)) => SymbolKind::Struct,
-            hir::ModuleDefId::AdtId(hir::AdtId::EnumId(..)) => SymbolKind::Enum,
-            hir::ModuleDefId::AdtId(hir::AdtId::UnionId(..)) => SymbolKind::Union,
-            hir::ModuleDefId::TraitId(..) => SymbolKind::Trait,
-            hir::ModuleDefId::TraitAliasId(..) => SymbolKind::TraitAlias,
-            hir::ModuleDefId::TypeAliasId(..) => SymbolKind::TypeAlias,
-            hir::ModuleDefId::BuiltinType(..) => SymbolKind::TypeAlias,
+            hir::ModuleDef::Const(..) => SymbolKind::Const,
+            hir::ModuleDef::Variant(..) => SymbolKind::Variant,
+            hir::ModuleDef::Function(..) => SymbolKind::Function,
+            hir::ModuleDef::Macro(mac) if mac.is_proc_macro() => SymbolKind::ProcMacro,
+            hir::ModuleDef::Macro(..) => SymbolKind::Macro,
+            hir::ModuleDef::Module(..) => SymbolKind::Module,
+            hir::ModuleDef::Static(..) => SymbolKind::Static,
+            hir::ModuleDef::Adt(hir::Adt::Struct(..)) => SymbolKind::Struct,
+            hir::ModuleDef::Adt(hir::Adt::Enum(..)) => SymbolKind::Enum,
+            hir::ModuleDef::Adt(hir::Adt::Union(..)) => SymbolKind::Union,
+            hir::ModuleDef::Trait(..) => SymbolKind::Trait,
+            hir::ModuleDef::TraitAlias(..) => SymbolKind::TraitAlias,
+            hir::ModuleDef::TypeAlias(..) => SymbolKind::TypeAlias,
+            hir::ModuleDef::BuiltinType(..) => SymbolKind::TypeAlias,
         }
     }
 }
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 1d1679c3ff8..42efbd68e33 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/rename.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/rename.rs
@@ -134,6 +134,7 @@ impl Definition {
                     FieldSource::Pos(_) => None,
                 }
             }
+            Definition::Crate(_) => None,
             Definition::Module(module) => {
                 let src = module.declaration_source(sema.db)?;
                 let name = src.value.name()?;
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/search.rs b/src/tools/rust-analyzer/crates/ide-db/src/search.rs
index 68199dd8711..a75aba137be 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/search.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/search.rs
@@ -953,14 +953,19 @@ impl<'a> FindUsages<'a> {
 
             // Search for occurrences of the items name
             for offset in Self::match_indices(&text, finder, search_range) {
-                tree.token_at_offset(offset).for_each(|token| {
-                    let Some(str_token) = ast::String::cast(token.clone()) else { return };
+                let ret = tree.token_at_offset(offset).any(|token| {
+                    let Some(str_token) = ast::String::cast(token.clone()) else { return false };
                     if let Some((range, Some(nameres))) =
                         sema.check_for_format_args_template(token, offset)
                     {
-                        if self.found_format_args_ref(file_id, range, str_token, nameres, sink) {}
+                        return self
+                            .found_format_args_ref(file_id, range, str_token, nameres, sink);
                     }
+                    false
                 });
+                if ret {
+                    return;
+                }
 
                 for name in
                     Self::find_nodes(sema, name, &tree, offset).filter_map(ast::NameLike::cast)
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 94d354d28e5..c94644eeb89 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
@@ -25,6 +25,7 @@ use std::{
     fmt,
     hash::{Hash, Hasher},
     mem,
+    ops::ControlFlow,
 };
 
 use base_db::{
@@ -136,16 +137,13 @@ fn library_symbols(db: &dyn SymbolsDatabase, source_root_id: SourceRootId) -> Ar
         // the module or crate indices for those in salsa unless we need to.
         .for_each(|module| symbol_collector.collect(module));
 
-    let mut symbols = symbol_collector.finish();
-    symbols.shrink_to_fit();
-    Arc::new(SymbolIndex::new(symbols))
+    Arc::new(SymbolIndex::new(symbol_collector.finish()))
 }
 
 fn module_symbols(db: &dyn SymbolsDatabase, module: Module) -> Arc<SymbolIndex> {
     let _p = tracing::info_span!("module_symbols").entered();
 
-    let symbols = SymbolCollector::collect_module(db.upcast(), module);
-    Arc::new(SymbolIndex::new(symbols))
+    Arc::new(SymbolIndex::new(SymbolCollector::collect_module(db.upcast(), module)))
 }
 
 pub fn crate_symbols(db: &dyn SymbolsDatabase, krate: Crate) -> Box<[Arc<SymbolIndex>]> {
@@ -222,13 +220,16 @@ pub fn world_symbols(db: &RootDatabase, query: Query) -> Vec<FileSymbol> {
     };
 
     let mut res = vec![];
-    query.search(&indices, |f| res.push(f.clone()));
+    query.search::<()>(&indices, |f| {
+        res.push(f.clone());
+        ControlFlow::Continue(())
+    });
     res
 }
 
 #[derive(Default)]
 pub struct SymbolIndex {
-    symbols: Vec<FileSymbol>,
+    symbols: Box<[FileSymbol]>,
     map: fst::Map<Vec<u8>>,
 }
 
@@ -253,10 +254,10 @@ impl Hash for SymbolIndex {
 }
 
 impl SymbolIndex {
-    fn new(mut symbols: Vec<FileSymbol>) -> SymbolIndex {
+    fn new(mut symbols: Box<[FileSymbol]>) -> SymbolIndex {
         fn cmp(lhs: &FileSymbol, rhs: &FileSymbol) -> Ordering {
-            let lhs_chars = lhs.name.chars().map(|c| c.to_ascii_lowercase());
-            let rhs_chars = rhs.name.chars().map(|c| c.to_ascii_lowercase());
+            let lhs_chars = lhs.name.as_str().chars().map(|c| c.to_ascii_lowercase());
+            let rhs_chars = rhs.name.as_str().chars().map(|c| c.to_ascii_lowercase());
             lhs_chars.cmp(rhs_chars)
         }
 
@@ -316,11 +317,11 @@ impl SymbolIndex {
 }
 
 impl Query {
-    pub(crate) fn search<'sym>(
+    pub(crate) fn search<'sym, T>(
         self,
         indices: &'sym [Arc<SymbolIndex>],
-        cb: impl FnMut(&'sym FileSymbol),
-    ) {
+        cb: impl FnMut(&'sym FileSymbol) -> ControlFlow<T>,
+    ) -> Option<T> {
         let _p = tracing::info_span!("symbol_index::Query::search").entered();
         let mut op = fst::map::OpBuilder::new();
         match self.mode {
@@ -351,12 +352,12 @@ impl Query {
         }
     }
 
-    fn search_maps<'sym>(
+    fn search_maps<'sym, T>(
         &self,
         indices: &'sym [Arc<SymbolIndex>],
         mut stream: fst::map::Union<'_>,
-        mut cb: impl FnMut(&'sym FileSymbol),
-    ) {
+        mut cb: impl FnMut(&'sym FileSymbol) -> ControlFlow<T>,
+    ) -> Option<T> {
         let ignore_underscore_prefixed = !self.query.starts_with("__");
         while let Some((_, indexed_values)) = stream.next() {
             for &IndexedValue { index, value } in indexed_values {
@@ -377,15 +378,19 @@ impl Query {
                         continue;
                     }
                     // Hide symbols that start with `__` unless the query starts with `__`
-                    if ignore_underscore_prefixed && symbol.name.starts_with("__") {
+                    let symbol_name = symbol.name.as_str();
+                    if ignore_underscore_prefixed && symbol_name.starts_with("__") {
                         continue;
                     }
-                    if self.mode.check(&self.query, self.case_sensitive, &symbol.name) {
-                        cb(symbol);
+                    if self.mode.check(&self.query, self.case_sensitive, symbol_name) {
+                        if let Some(b) = cb(symbol).break_value() {
+                            return Some(b);
+                        }
                     }
                 }
             }
         }
+        None
     }
 
     fn matches_assoc_mode(&self, is_trait_assoc_item: bool) -> bool {
@@ -476,9 +481,9 @@ use Macro as ItemLikeMacro;
 use Macro as Trait; // overlay namespaces
 //- /b_mod.rs
 struct StructInModB;
-use super::Macro as SuperItemLikeMacro;
-use crate::b_mod::StructInModB as ThisStruct;
-use crate::Trait as IsThisJustATrait;
+pub(self) use super::Macro as SuperItemLikeMacro;
+pub(self) use crate::b_mod::StructInModB as ThisStruct;
+pub(self) use crate::Trait as IsThisJustATrait;
 "#,
         );
 
@@ -487,7 +492,7 @@ use crate::Trait as IsThisJustATrait;
             .into_iter()
             .map(|module_id| {
                 let mut symbols = SymbolCollector::collect_module(&db, module_id);
-                symbols.sort_by_key(|it| it.name.clone());
+                symbols.sort_by_key(|it| it.name.as_str().to_owned());
                 (module_id, symbols)
             })
             .collect();
@@ -514,7 +519,7 @@ struct Duplicate;
             .into_iter()
             .map(|module_id| {
                 let mut symbols = SymbolCollector::collect_module(&db, module_id);
-                symbols.sort_by_key(|it| it.name.clone());
+                symbols.sort_by_key(|it| it.name.as_str().to_owned());
                 (module_id, symbols)
             })
             .collect();
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs
index 365d726d2a9..557c95f704b 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs
@@ -421,7 +421,7 @@ mod tests {
     use super::*;
 
     #[track_caller]
-    fn check(ra_fixture: &str, expected: &str) {
+    fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str, expected: &str) {
         let (db, file_id, range_or_offset) = RootDatabase::with_range_or_offset(ra_fixture);
         let frange = FileRange { file_id, range: range_or_offset.into() };
 
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt
index 9d70942199c..535777dfcbe 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt
+++ b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt
@@ -631,7 +631,7 @@
                 def: Function(
                     Function {
                         id: FunctionId(
-                            3,
+                            2,
                         ),
                     },
                 ),
@@ -664,7 +664,7 @@
                 def: Function(
                     Function {
                         id: FunctionId(
-                            2,
+                            1,
                         ),
                     },
                 ),
@@ -794,7 +794,7 @@
                 def: Function(
                     Function {
                         id: FunctionId(
-                            1,
+                            3,
                         ),
                     },
                 ),
@@ -879,12 +879,10 @@
         [
             FileSymbol {
                 name: "IsThisJustATrait",
-                def: Macro(
-                    Macro {
-                        id: Macro2Id(
-                            Macro2Id(
-                                0,
-                            ),
+                def: Trait(
+                    Trait {
+                        id: TraitId(
+                            0,
                         ),
                     },
                 ),
@@ -897,12 +895,12 @@
                     ),
                     ptr: SyntaxNodePtr {
                         kind: USE_TREE,
-                        range: 111..143,
+                        range: 141..173,
                     },
                     name_ptr: AstPtr(
                         SyntaxNodePtr {
                             kind: NAME,
-                            range: 127..143,
+                            range: 157..173,
                         },
                     ),
                 },
@@ -911,40 +909,7 @@
                 is_assoc: false,
             },
             FileSymbol {
-                name: "StructInModB",
-                def: Adt(
-                    Struct(
-                        Struct {
-                            id: StructId(
-                                4,
-                            ),
-                        },
-                    ),
-                ),
-                loc: DeclarationLocation {
-                    hir_file_id: EditionedFileId(
-                        FileId(
-                            1,
-                        ),
-                        Edition2021,
-                    ),
-                    ptr: SyntaxNodePtr {
-                        kind: STRUCT,
-                        range: 0..20,
-                    },
-                    name_ptr: AstPtr(
-                        SyntaxNodePtr {
-                            kind: NAME,
-                            range: 7..19,
-                        },
-                    ),
-                },
-                container_name: None,
-                is_alias: false,
-                is_assoc: false,
-            },
-            FileSymbol {
-                name: "SuperItemLikeMacro",
+                name: "IsThisJustATrait",
                 def: Macro(
                     Macro {
                         id: Macro2Id(
@@ -963,12 +928,12 @@
                     ),
                     ptr: SyntaxNodePtr {
                         kind: USE_TREE,
-                        range: 25..59,
+                        range: 141..173,
                     },
                     name_ptr: AstPtr(
                         SyntaxNodePtr {
                             kind: NAME,
-                            range: 41..59,
+                            range: 157..173,
                         },
                     ),
                 },
@@ -977,7 +942,7 @@
                 is_assoc: false,
             },
             FileSymbol {
-                name: "ThisStruct",
+                name: "StructInModB",
                 def: Adt(
                     Struct(
                         Struct {
@@ -995,13 +960,13 @@
                         Edition2021,
                     ),
                     ptr: SyntaxNodePtr {
-                        kind: USE_TREE,
-                        range: 65..105,
+                        kind: STRUCT,
+                        range: 0..20,
                     },
                     name_ptr: AstPtr(
                         SyntaxNodePtr {
                             kind: NAME,
-                            range: 95..105,
+                            range: 7..19,
                         },
                     ),
                 },
@@ -1010,15 +975,15 @@
                 is_assoc: false,
             },
             FileSymbol {
-                name: "ThisStruct",
-                def: Adt(
-                    Struct(
-                        Struct {
-                            id: StructId(
-                                4,
+                name: "SuperItemLikeMacro",
+                def: Macro(
+                    Macro {
+                        id: Macro2Id(
+                            Macro2Id(
+                                0,
                             ),
-                        },
-                    ),
+                        ),
+                    },
                 ),
                 loc: DeclarationLocation {
                     hir_file_id: EditionedFileId(
@@ -1029,12 +994,12 @@
                     ),
                     ptr: SyntaxNodePtr {
                         kind: USE_TREE,
-                        range: 65..105,
+                        range: 35..69,
                     },
                     name_ptr: AstPtr(
                         SyntaxNodePtr {
                             kind: NAME,
-                            range: 95..105,
+                            range: 51..69,
                         },
                     ),
                 },
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/traits.rs b/src/tools/rust-analyzer/crates/ide-db/src/traits.rs
index 82aca50d039..0f67496d098 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/traits.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/traits.rs
@@ -123,7 +123,9 @@ mod tests {
     use crate::RootDatabase;
 
     /// Creates analysis from a multi-file fixture, returns positions marked with $0.
-    pub(crate) fn position(ra_fixture: &str) -> (RootDatabase, FilePosition) {
+    pub(crate) fn position(
+        #[rust_analyzer::rust_fixture] ra_fixture: &str,
+    ) -> (RootDatabase, FilePosition) {
         let change_fixture = ChangeFixture::parse(ra_fixture);
         let mut database = RootDatabase::default();
         database.apply_change(change_fixture.change);
@@ -133,7 +135,7 @@ mod tests {
         (database, FilePosition { file_id, offset })
     }
 
-    fn check_trait(ra_fixture: &str, expect: Expect) {
+    fn check_trait(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
         let (db, position) = position(ra_fixture);
         let sema = Semantics::new(&db);
         let file = sema.parse(position.file_id);
@@ -147,7 +149,7 @@ mod tests {
         expect.assert_eq(&actual);
     }
 
-    fn check_missing_assoc(ra_fixture: &str, expect: Expect) {
+    fn check_missing_assoc(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
         let (db, position) = position(ra_fixture);
         let sema = Semantics::new(&db);
         let file = sema.parse(position.file_id);
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/generic_args_prohibited.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/generic_args_prohibited.rs
index 2b59c1a22f6..7d62daf716c 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/generic_args_prohibited.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/generic_args_prohibited.rs
@@ -604,4 +604,23 @@ fn bar() {
         "#,
         );
     }
+
+    #[test]
+    fn enum_variant_type_ns() {
+        check_diagnostics(
+            r#"
+enum KvnDeserializerErr<I> {
+    UnexpectedKeyword { found: I, expected: I },
+}
+
+fn foo() {
+    let _x: KvnDeserializerErr<()> =
+        KvnDeserializerErr::<()>::UnexpectedKeyword { found: (), expected: () };
+    let _x: KvnDeserializerErr<()> =
+        KvnDeserializerErr::<()>::UnexpectedKeyword::<()> { found: (), expected: () };
+                                                // ^^^^^^ 💡 error: you can specify generic arguments on either the enum or the variant, but not both
+}
+        "#,
+        );
+    }
 }
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/inactive_code.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/inactive_code.rs
index 4c0c685e550..96a368eb0ea 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/inactive_code.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/inactive_code.rs
@@ -41,7 +41,7 @@ pub(crate) fn inactive_code(
 mod tests {
     use crate::{tests::check_diagnostics_with_config, DiagnosticsConfig};
 
-    pub(crate) fn check(ra_fixture: &str) {
+    pub(crate) fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
         let config = DiagnosticsConfig {
             disabled: std::iter::once("unlinked-file".to_owned()).collect(),
             ..DiagnosticsConfig::test_sample()
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/macro_error.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/macro_error.rs
index e177b72e4d4..99894fefef3 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/macro_error.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/macro_error.rs
@@ -4,13 +4,13 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, Severity};
 //
 // This diagnostic is shown for macro expansion errors.
 
-// Diagnostic: proc-macros-disabled
+// Diagnostic: attribute-expansion-disabled
 //
-// This diagnostic is shown for proc macros where proc macros have been disabled.
+// This diagnostic is shown for attribute proc macros when attribute expansions have been disabled.
 
 // Diagnostic: proc-macro-disabled
 //
-// This diagnostic is shown for proc macros that has been specifically disabled via `rust-analyzer.procMacro.ignored`.
+// This diagnostic is shown for proc macros that have been specifically disabled via `rust-analyzer.procMacro.ignored`.
 pub(crate) fn macro_error(ctx: &DiagnosticsContext<'_>, d: &hir::MacroError) -> Diagnostic {
     // Use more accurate position if available.
     let display_range = ctx.resolve_precise_location(&d.node, d.precise_location);
@@ -291,4 +291,30 @@ mod prim_never {}
 "#,
         );
     }
+
+    #[test]
+    fn no_stack_overflow_for_missing_binding() {
+        check_diagnostics(
+            r#"
+#[macro_export]
+macro_rules! boom {
+    (
+        $($code:literal),+,
+        $(param: $param:expr,)?
+    ) => {{
+        let _ = $crate::boom!(@param $($param)*);
+    }};
+    (@param) => { () };
+    (@param $param:expr) => { $param };
+}
+
+fn it_works() {
+    // NOTE: there is an error, but RA crashes before showing it
+    boom!("RAND", param: c7.clone());
+               // ^^^^^ error: expected literal
+}
+
+        "#,
+        );
+    }
 }
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs
index 08e6e7dced9..0bf600e5dfa 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs
@@ -26,7 +26,7 @@ mod tests {
     use test_utils::skip_slow_tests;
 
     #[track_caller]
-    fn check_diagnostics_no_bails(ra_fixture: &str) {
+    fn check_diagnostics_no_bails(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
         cov_mark::check_count!(validate_match_bailed_out, 0);
         crate::tests::check_diagnostics(ra_fixture)
     }
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs
index e5d871975b6..0f126a1a656 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs
@@ -399,4 +399,38 @@ fn f(s@m::Struct {
 "#,
         )
     }
+
+    #[test]
+    fn editions_between_macros() {
+        check_diagnostics(
+            r#"
+//- /edition2015.rs crate:edition2015 edition:2015
+#[macro_export]
+macro_rules! pass_expr_thorough {
+    ($e:expr) => { $e };
+}
+
+//- /edition2018.rs crate:edition2018 deps:edition2015 edition:2018
+async fn bar() {}
+async fn foo() {
+    edition2015::pass_expr_thorough!(bar().await);
+}
+        "#,
+        );
+        check_diagnostics(
+            r#"
+//- /edition2018.rs crate:edition2018 edition:2018
+pub async fn bar() {}
+#[macro_export]
+macro_rules! make_await {
+    () => { async { $crate::bar().await }; };
+}
+
+//- /edition2015.rs crate:edition2015 deps:edition2018 edition:2015
+fn foo() {
+    edition2018::make_await!();
+}
+        "#,
+        );
+    }
 }
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs
index f481365f2a5..4aff446de60 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs
@@ -61,7 +61,7 @@ mod tests {
     };
 
     #[track_caller]
-    pub(crate) fn check_diagnostics(ra_fixture: &str) {
+    pub(crate) fn check_diagnostics(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
         let mut config = DiagnosticsConfig::test_sample();
         config.disabled.insert("inactive-code".to_owned());
         config.disabled.insert("E0599".to_owned());
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 bfdda537405..56afb38cc81 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
@@ -1164,6 +1164,37 @@ struct Bar {
     }
 
     #[test]
+    fn trait_upcast_ok() {
+        check_diagnostics(
+            r#"
+//- minicore: coerce_unsized
+trait A: B {}
+trait B {}
+
+fn test(a: &dyn A) -> &dyn B {
+    a
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn trait_upcast_err() {
+        check_diagnostics(
+            r#"
+//- minicore: coerce_unsized
+trait A {} // `A` does not have `B` as a supertrait, so no upcast :c
+trait B {}
+
+fn test(a: &dyn A) -> &dyn B {
+    a
+  //^ error: expected &dyn B, found &dyn A
+}
+"#,
+        );
+    }
+
+    #[test]
     fn return_no_value() {
         check_diagnostics_with_disabled(
             r#"
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests.rs
index ec74640a54d..fc2a7db7174 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests.rs
@@ -1,5 +1,7 @@
 #![allow(clippy::print_stderr)]
 
+mod overly_long_real_world_cases;
+
 use ide_db::{
     assists::AssistResolveStrategy, base_db::SourceDatabase, LineIndexDatabase, RootDatabase,
 };
@@ -16,7 +18,10 @@ use crate::{DiagnosticsConfig, ExprFillDefaultMode, Severity};
 ///  * the first diagnostic fix trigger range touches the input cursor position
 ///  * that the contents of the file containing the cursor match `after` after the diagnostic fix is applied
 #[track_caller]
-pub(crate) fn check_fix(ra_fixture_before: &str, ra_fixture_after: &str) {
+pub(crate) fn check_fix(
+    #[rust_analyzer::rust_fixture] ra_fixture_before: &str,
+    #[rust_analyzer::rust_fixture] ra_fixture_after: &str,
+) {
     check_nth_fix(0, ra_fixture_before, ra_fixture_after);
 }
 /// Takes a multi-file input fixture with annotated cursor positions,
@@ -24,14 +29,21 @@ pub(crate) fn check_fix(ra_fixture_before: &str, ra_fixture_after: &str) {
 ///  * a diagnostic is produced
 ///  * every diagnostic fixes trigger range touches the input cursor position
 ///  * that the contents of the file containing the cursor match `after` after each diagnostic fix is applied
-pub(crate) fn check_fixes(ra_fixture_before: &str, ra_fixtures_after: Vec<&str>) {
+pub(crate) fn check_fixes(
+    #[rust_analyzer::rust_fixture] ra_fixture_before: &str,
+    ra_fixtures_after: Vec<&str>,
+) {
     for (i, ra_fixture_after) in ra_fixtures_after.iter().enumerate() {
         check_nth_fix(i, ra_fixture_before, ra_fixture_after)
     }
 }
 
 #[track_caller]
-fn check_nth_fix(nth: usize, ra_fixture_before: &str, ra_fixture_after: &str) {
+fn check_nth_fix(
+    nth: usize,
+    #[rust_analyzer::rust_fixture] ra_fixture_before: &str,
+    #[rust_analyzer::rust_fixture] ra_fixture_after: &str,
+) {
     let mut config = DiagnosticsConfig::test_sample();
     config.expr_fill_default = ExprFillDefaultMode::Default;
     check_nth_fix_with_config(config, nth, ra_fixture_before, ra_fixture_after)
@@ -39,8 +51,8 @@ fn check_nth_fix(nth: usize, ra_fixture_before: &str, ra_fixture_after: &str) {
 
 #[track_caller]
 pub(crate) fn check_fix_with_disabled(
-    ra_fixture_before: &str,
-    ra_fixture_after: &str,
+    #[rust_analyzer::rust_fixture] ra_fixture_before: &str,
+    #[rust_analyzer::rust_fixture] ra_fixture_after: &str,
     disabled: impl Iterator<Item = String>,
 ) {
     let mut config = DiagnosticsConfig::test_sample();
@@ -53,8 +65,8 @@ pub(crate) fn check_fix_with_disabled(
 fn check_nth_fix_with_config(
     config: DiagnosticsConfig,
     nth: usize,
-    ra_fixture_before: &str,
-    ra_fixture_after: &str,
+    #[rust_analyzer::rust_fixture] ra_fixture_before: &str,
+    #[rust_analyzer::rust_fixture] ra_fixture_after: &str,
 ) {
     let after = trim_indent(ra_fixture_after);
 
@@ -93,14 +105,20 @@ fn check_nth_fix_with_config(
     assert_eq_text!(&after, &actual);
 }
 
-pub(crate) fn check_fixes_unordered(ra_fixture_before: &str, ra_fixtures_after: Vec<&str>) {
+pub(crate) fn check_fixes_unordered(
+    #[rust_analyzer::rust_fixture] ra_fixture_before: &str,
+    ra_fixtures_after: Vec<&str>,
+) {
     for ra_fixture_after in ra_fixtures_after.iter() {
         check_has_fix(ra_fixture_before, ra_fixture_after)
     }
 }
 
 #[track_caller]
-pub(crate) fn check_has_fix(ra_fixture_before: &str, ra_fixture_after: &str) {
+pub(crate) fn check_has_fix(
+    #[rust_analyzer::rust_fixture] ra_fixture_before: &str,
+    #[rust_analyzer::rust_fixture] ra_fixture_after: &str,
+) {
     let after = trim_indent(ra_fixture_after);
 
     let (db, file_position) = RootDatabase::with_position(ra_fixture_before);
@@ -143,7 +161,10 @@ pub(crate) fn check_has_fix(ra_fixture_before: &str, ra_fixture_after: &str) {
 }
 
 #[track_caller]
-pub(crate) fn check_has_single_fix(ra_fixture_before: &str, ra_fixture_after: &str) {
+pub(crate) fn check_has_single_fix(
+    #[rust_analyzer::rust_fixture] ra_fixture_before: &str,
+    #[rust_analyzer::rust_fixture] ra_fixture_after: &str,
+) {
     let after = trim_indent(ra_fixture_after);
 
     let (db, file_position) = RootDatabase::with_position(ra_fixture_before);
@@ -189,7 +210,7 @@ pub(crate) fn check_has_single_fix(ra_fixture_before: &str, ra_fixture_after: &s
 }
 
 /// Checks that there's a diagnostic *without* fix at `$0`.
-pub(crate) fn check_no_fix(ra_fixture: &str) {
+pub(crate) fn check_no_fix(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
     let (db, file_position) = RootDatabase::with_position(ra_fixture);
     let diagnostic = super::full_diagnostics(
         &db,
@@ -203,21 +224,27 @@ pub(crate) fn check_no_fix(ra_fixture: &str) {
 }
 
 #[track_caller]
-pub(crate) fn check_diagnostics(ra_fixture: &str) {
+pub(crate) fn check_diagnostics(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
     let mut config = DiagnosticsConfig::test_sample();
     config.disabled.insert("inactive-code".to_owned());
     check_diagnostics_with_config(config, ra_fixture)
 }
 
 #[track_caller]
-pub(crate) fn check_diagnostics_with_disabled(ra_fixture: &str, disabled: &[&str]) {
+pub(crate) fn check_diagnostics_with_disabled(
+    #[rust_analyzer::rust_fixture] ra_fixture: &str,
+    disabled: &[&str],
+) {
     let mut config = DiagnosticsConfig::test_sample();
     config.disabled.extend(disabled.iter().map(|&s| s.to_owned()));
     check_diagnostics_with_config(config, ra_fixture)
 }
 
 #[track_caller]
-pub(crate) fn check_diagnostics_with_config(config: DiagnosticsConfig, ra_fixture: &str) {
+pub(crate) fn check_diagnostics_with_config(
+    config: DiagnosticsConfig,
+    #[rust_analyzer::rust_fixture] ra_fixture: &str,
+) {
     let (db, files) = RootDatabase::with_many_files(ra_fixture);
     let mut annotations = files
         .iter()
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests/overly_long_real_world_cases.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests/overly_long_real_world_cases.rs
new file mode 100644
index 00000000000..c6831d818aa
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests/overly_long_real_world_cases.rs
@@ -0,0 +1,2726 @@
+//! Overly long excerpts of failures from real world cases, that I was too lazy to minimize.
+
+use crate::tests::check_diagnostics_with_disabled;
+
+#[test]
+fn tracing_infinite_repeat() {
+    check_diagnostics_with_disabled(
+        r#"
+//- /core.rs crate:core
+#[rustc_builtin_macro]
+#[macro_export]
+macro_rules! concat {
+($($e:expr),* $(,)?) => {{ /* compiler built-in */ }};
+}
+#[rustc_builtin_macro]
+#[macro_export]
+macro_rules! file {
+() => {
+    /* compiler built-in */
+};
+}
+#[allow_internal_unsafe]
+#[allow_internal_unstable(fmt_internals)]
+#[rustc_builtin_macro]
+#[macro_export]
+macro_rules! format_args {
+($fmt:expr) => {{ /* compiler built-in */ }};
+($fmt:expr, $($args:tt)*) => {{ /* compiler built-in */ }};
+}
+#[rustc_builtin_macro]
+#[macro_export]
+macro_rules! line {
+() => {
+    /* compiler built-in */
+};
+}
+
+//- /tracing_core.rs crate:tracing_core deps:core
+#[macro_export]
+macro_rules! identify_callsite {
+($callsite:expr) => {
+    $crate::callsite::Identifier($callsite)
+};
+}
+
+#[macro_export]
+macro_rules! metadata {
+(
+    name: $name:expr,
+    target: $target:expr,
+    level: $level:expr,
+    fields: $fields:expr,
+    callsite: $callsite:expr,
+    kind: $kind:expr
+) => {
+    $crate::metadata! {
+        name: $name,
+        target: $target,
+        level: $level,
+        fields: $fields,
+        callsite: $callsite,
+        kind: $kind,
+    }
+};
+(
+    name: $name:expr,
+    target: $target:expr,
+    level: $level:expr,
+    fields: $fields:expr,
+    callsite: $callsite:expr,
+    kind: $kind:expr,
+) => {
+    $crate::metadata::Metadata::new(
+        $name,
+        $target,
+        $level,
+        $crate::__macro_support::Option::Some($crate::__macro_support::file!()),
+        $crate::__macro_support::Option::Some($crate::__macro_support::line!()),
+        $crate::__macro_support::Option::Some($crate::__macro_support::module_path!()),
+        $crate::field::FieldSet::new($fields, $crate::identify_callsite!($callsite)),
+        $kind,
+    )
+};
+}
+
+//- /tracing.rs crate:tracing deps:core,tracing_core
+#[doc(hidden)]
+pub mod __macro_support {
+// Re-export the `core` functions that are used in macros. This allows
+// a crate to be named `core` and avoid name clashes.
+// See here: https://github.com/tokio-rs/tracing/issues/2761
+pub use core::{concat, file, format_args, iter::Iterator, line, option::Option};
+}
+
+#[macro_export]
+macro_rules! span {
+(target: $target:expr, parent: $parent:expr, $lvl:expr, $name:expr) => {
+    $crate::span!(target: $target, parent: $parent, $lvl, $name,)
+};
+(target: $target:expr, parent: $parent:expr, $lvl:expr, $name:expr, $($fields:tt)*) => {
+    {
+        use $crate::__macro_support::Callsite as _;
+        static __CALLSITE: $crate::__macro_support::MacroCallsite = $crate::callsite2! {
+            name: $name,
+            kind: $crate::metadata::Kind::SPAN,
+            target: $target,
+            level: $lvl,
+            fields: $($fields)*
+        };
+        let mut interest = $crate::subscriber::Interest::never();
+        if $crate::level_enabled!($lvl)
+            && { interest = __CALLSITE.interest(); !interest.is_never() }
+            && $crate::__macro_support::__is_enabled(__CALLSITE.metadata(), interest)
+        {
+            let meta = __CALLSITE.metadata();
+            // span with explicit parent
+            $crate::Span::child_of(
+                $parent,
+                meta,
+                &$crate::valueset!(meta.fields(), $($fields)*),
+            )
+        } else {
+            let span = $crate::__macro_support::__disabled_span(__CALLSITE.metadata());
+            $crate::if_log_enabled! { $lvl, {
+                span.record_all(&$crate::valueset!(__CALLSITE.metadata().fields(), $($fields)*));
+            }};
+            span
+        }
+    }
+};
+(target: $target:expr, $lvl:expr, $name:expr, $($fields:tt)*) => {
+    {
+        use $crate::__macro_support::Callsite as _;
+        static __CALLSITE: $crate::callsite::DefaultCallsite = $crate::callsite2! {
+            name: $name,
+            kind: $crate::metadata::Kind::SPAN,
+            target: $target,
+            level: $lvl,
+            fields: $($fields)*
+        };
+        let mut interest = $crate::subscriber::Interest::never();
+        if $crate::level_enabled!($lvl)
+            && { interest = __CALLSITE.interest(); !interest.is_never() }
+            && $crate::__macro_support::__is_enabled(__CALLSITE.metadata(), interest)
+        {
+            let meta = __CALLSITE.metadata();
+            // span with contextual parent
+            $crate::Span::new(
+                meta,
+                &$crate::valueset!(meta.fields(), $($fields)*),
+            )
+        } else {
+            let span = $crate::__macro_support::__disabled_span(__CALLSITE.metadata());
+            $crate::if_log_enabled! { $lvl, {
+                span.record_all(&$crate::valueset!(__CALLSITE.metadata().fields(), $($fields)*));
+            }};
+            span
+        }
+    }
+};
+(target: $target:expr, parent: $parent:expr, $lvl:expr, $name:expr) => {
+    $crate::span!(target: $target, parent: $parent, $lvl, $name,)
+};
+(parent: $parent:expr, $lvl:expr, $name:expr, $($fields:tt)*) => {
+    $crate::span!(
+        target: module_path!(),
+        parent: $parent,
+        $lvl,
+        $name,
+        $($fields)*
+    )
+};
+(parent: $parent:expr, $lvl:expr, $name:expr) => {
+    $crate::span!(
+        target: module_path!(),
+        parent: $parent,
+        $lvl,
+        $name,
+    )
+};
+(target: $target:expr, $lvl:expr, $name:expr, $($fields:tt)*) => {
+    $crate::span!(
+        target: $target,
+        $lvl,
+        $name,
+        $($fields)*
+    )
+};
+(target: $target:expr, $lvl:expr, $name:expr) => {
+    $crate::span!(target: $target, $lvl, $name,)
+};
+($lvl:expr, $name:expr, $($fields:tt)*) => {
+    $crate::span!(
+        target: module_path!(),
+        $lvl,
+        $name,
+        $($fields)*
+    )
+};
+($lvl:expr, $name:expr) => {
+    $crate::span!(
+        target: module_path!(),
+        $lvl,
+        $name,
+    )
+};
+}
+
+#[macro_export]
+macro_rules! trace_span {
+(target: $target:expr, parent: $parent:expr, $name:expr, $($field:tt)*) => {
+    $crate::span!(
+        target: $target,
+        parent: $parent,
+        $crate::Level::TRACE,
+        $name,
+        $($field)*
+    )
+};
+(target: $target:expr, parent: $parent:expr, $name:expr) => {
+    $crate::trace_span!(target: $target, parent: $parent, $name,)
+};
+(parent: $parent:expr, $name:expr, $($field:tt)*) => {
+    $crate::span!(
+        target: module_path!(),
+        parent: $parent,
+        $crate::Level::TRACE,
+        $name,
+        $($field)*
+    )
+};
+(parent: $parent:expr, $name:expr) => {
+    $crate::trace_span!(parent: $parent, $name,)
+};
+(target: $target:expr, $name:expr, $($field:tt)*) => {
+    $crate::span!(
+        target: $target,
+        $crate::Level::TRACE,
+        $name,
+        $($field)*
+    )
+};
+(target: $target:expr, $name:expr) => {
+    $crate::trace_span!(target: $target, $name,)
+};
+($name:expr, $($field:tt)*) => {
+    $crate::span!(
+        target: module_path!(),
+        $crate::Level::TRACE,
+        $name,
+        $($field)*
+    )
+};
+($name:expr) => { $crate::trace_span!($name,) };
+}
+
+#[macro_export]
+macro_rules! debug_span {
+(target: $target:expr, parent: $parent:expr, $name:expr, $($field:tt)*) => {
+    $crate::span!(
+        target: $target,
+        parent: $parent,
+        $crate::Level::DEBUG,
+        $name,
+        $($field)*
+    )
+};
+(target: $target:expr, parent: $parent:expr, $name:expr) => {
+    $crate::debug_span!(target: $target, parent: $parent, $name,)
+};
+(parent: $parent:expr, $name:expr, $($field:tt)*) => {
+    $crate::span!(
+        target: module_path!(),
+        parent: $parent,
+        $crate::Level::DEBUG,
+        $name,
+        $($field)*
+    )
+};
+(parent: $parent:expr, $name:expr) => {
+    $crate::debug_span!(parent: $parent, $name,)
+};
+(target: $target:expr, $name:expr, $($field:tt)*) => {
+    $crate::span!(
+        target: $target,
+        $crate::Level::DEBUG,
+        $name,
+        $($field)*
+    )
+};
+(target: $target:expr, $name:expr) => {
+    $crate::debug_span!(target: $target, $name,)
+};
+($name:expr, $($field:tt)*) => {
+    $crate::span!(
+        target: module_path!(),
+        $crate::Level::DEBUG,
+        $name,
+        $($field)*
+    )
+};
+($name:expr) => {$crate::debug_span!($name,)};
+}
+
+#[macro_export]
+macro_rules! info_span {
+(target: $target:expr, parent: $parent:expr, $name:expr, $($field:tt)*) => {
+    $crate::span!(
+        target: $target,
+        parent: $parent,
+        $crate::Level::INFO,
+        $name,
+        $($field)*
+    )
+};
+(target: $target:expr, parent: $parent:expr, $name:expr) => {
+    $crate::info_span!(target: $target, parent: $parent, $name,)
+};
+(parent: $parent:expr, $name:expr, $($field:tt)*) => {
+    $crate::span!(
+        target: module_path!(),
+        parent: $parent,
+        $crate::Level::INFO,
+        $name,
+        $($field)*
+    )
+};
+(parent: $parent:expr, $name:expr) => {
+    $crate::info_span!(parent: $parent, $name,)
+};
+(target: $target:expr, $name:expr, $($field:tt)*) => {
+    $crate::span!(
+        target: $target,
+        $crate::Level::INFO,
+        $name,
+        $($field)*
+    )
+};
+(target: $target:expr, $name:expr) => {
+    $crate::info_span!(target: $target, $name,)
+};
+($name:expr, $($field:tt)*) => {
+    $crate::span!(
+        target: module_path!(),
+        $crate::Level::INFO,
+        $name,
+        $($field)*
+    )
+};
+($name:expr) => {$crate::info_span!($name,)};
+}
+
+#[macro_export]
+macro_rules! warn_span {
+(target: $target:expr, parent: $parent:expr, $name:expr, $($field:tt)*) => {
+    $crate::span!(
+        target: $target,
+        parent: $parent,
+        $crate::Level::WARN,
+        $name,
+        $($field)*
+    )
+};
+(target: $target:expr, parent: $parent:expr, $name:expr) => {
+    $crate::warn_span!(target: $target, parent: $parent, $name,)
+};
+(parent: $parent:expr, $name:expr, $($field:tt)*) => {
+    $crate::span!(
+        target: module_path!(),
+        parent: $parent,
+        $crate::Level::WARN,
+        $name,
+        $($field)*
+    )
+};
+(parent: $parent:expr, $name:expr) => {
+    $crate::warn_span!(parent: $parent, $name,)
+};
+(target: $target:expr, $name:expr, $($field:tt)*) => {
+    $crate::span!(
+        target: $target,
+        $crate::Level::WARN,
+        $name,
+        $($field)*
+    )
+};
+(target: $target:expr, $name:expr) => {
+    $crate::warn_span!(target: $target, $name,)
+};
+($name:expr, $($field:tt)*) => {
+    $crate::span!(
+        target: module_path!(),
+        $crate::Level::WARN,
+        $name,
+        $($field)*
+    )
+};
+($name:expr) => {$crate::warn_span!($name,)};
+}
+
+#[macro_export]
+macro_rules! error_span {
+(target: $target:expr, parent: $parent:expr, $name:expr, $($field:tt)*) => {
+    $crate::span!(
+        target: $target,
+        parent: $parent,
+        $crate::Level::ERROR,
+        $name,
+        $($field)*
+    )
+};
+(target: $target:expr, parent: $parent:expr, $name:expr) => {
+    $crate::error_span!(target: $target, parent: $parent, $name,)
+};
+(parent: $parent:expr, $name:expr, $($field:tt)*) => {
+    $crate::span!(
+        target: module_path!(),
+        parent: $parent,
+        $crate::Level::ERROR,
+        $name,
+        $($field)*
+    )
+};
+(parent: $parent:expr, $name:expr) => {
+    $crate::error_span!(parent: $parent, $name,)
+};
+(target: $target:expr, $name:expr, $($field:tt)*) => {
+    $crate::span!(
+        target: $target,
+        $crate::Level::ERROR,
+        $name,
+        $($field)*
+    )
+};
+(target: $target:expr, $name:expr) => {
+    $crate::error_span!(target: $target, $name,)
+};
+($name:expr, $($field:tt)*) => {
+    $crate::span!(
+        target: module_path!(),
+        $crate::Level::ERROR,
+        $name,
+        $($field)*
+    )
+};
+($name:expr) => {$crate::error_span!($name,)};
+}
+
+#[macro_export]
+macro_rules! event {
+// Name / target / parent.
+(name: $name:expr, target: $target:expr, parent: $parent:expr, $lvl:expr, { $($fields:tt)* } )=> ({
+    use $crate::__macro_support::Callsite as _;
+    static __CALLSITE: $crate::__macro_support::MacroCallsite = $crate::callsite2! {
+        name: $name,
+        kind: $crate::metadata::Kind::EVENT,
+        target: $target,
+        level: $lvl,
+        fields: $($fields)*
+    };
+
+    let enabled = $crate::level_enabled!($lvl) && {
+        let interest = __CALLSITE.interest();
+        !interest.is_never() && $crate::__macro_support::__is_enabled(__CALLSITE.metadata(), interest)
+    };
+    if enabled {
+        (|value_set: $crate::field::ValueSet| {
+            $crate::__tracing_log!(
+                $lvl,
+                __CALLSITE,
+                &value_set
+            );
+            let meta = __CALLSITE.metadata();
+            // event with explicit parent
+            $crate::Event::child_of(
+                $parent,
+                meta,
+                &value_set
+            );
+        })($crate::valueset!(__CALLSITE.metadata().fields(), $($fields)*));
+    } else {
+        $crate::__tracing_log!(
+            $lvl,
+            __CALLSITE,
+            &$crate::valueset!(__CALLSITE.metadata().fields(), $($fields)*)
+        );
+    }
+});
+(name: $name:expr, target: $target:expr, parent: $parent:expr, $lvl:expr, { $($fields:tt)* }, $($arg:tt)+ ) => (
+    $crate::event!(
+        name: $name,
+        target: $target,
+        parent: $parent,
+        $lvl,
+        { message = $crate::__macro_support::format_args!($($arg)+), $($fields)* }
+    )
+);
+(name: $name:expr, target: $target:expr, parent: $parent:expr, $lvl:expr, $($k:ident).+ = $($fields:tt)* ) => (
+    $crate::event!(name: $name, target: $target, parent: $parent, $lvl, { $($k).+ = $($fields)* })
+);
+(name: $name:expr, target: $target:expr, parent: $parent:expr, $lvl:expr, $($arg:tt)+) => (
+    $crate::event!(name: $name, target: $target, parent: $parent, $lvl, { $($arg)+ })
+);
+
+// Name / target.
+(name: $name:expr, target: $target:expr, $lvl:expr, { $($fields:tt)* } )=> ({
+    use $crate::__macro_support::Callsite as _;
+    static __CALLSITE: $crate::__macro_support::MacroCallsite = $crate::callsite2! {
+        name: $name,
+        kind: $crate::metadata::Kind::EVENT,
+        target: $target,
+        level: $lvl,
+        fields: $($fields)*
+    };
+    let enabled = $crate::level_enabled!($lvl) && {
+        let interest = __CALLSITE.interest();
+        !interest.is_never() && $crate::__macro_support::__is_enabled(__CALLSITE.metadata(), interest)
+    };
+    if enabled {
+        (|value_set: $crate::field::ValueSet| {
+            let meta = __CALLSITE.metadata();
+            // event with contextual parent
+            $crate::Event::dispatch(
+                meta,
+                &value_set
+            );
+            $crate::__tracing_log!(
+                $lvl,
+                __CALLSITE,
+                &value_set
+            );
+        })($crate::valueset!(__CALLSITE.metadata().fields(), $($fields)*));
+    } else {
+        $crate::__tracing_log!(
+            $lvl,
+            __CALLSITE,
+            &$crate::valueset!(__CALLSITE.metadata().fields(), $($fields)*)
+        );
+    }
+});
+(name: $name:expr, target: $target:expr, $lvl:expr, { $($fields:tt)* }, $($arg:tt)+ ) => (
+    $crate::event!(
+        name: $name,
+        target: $target,
+        $lvl,
+        { message = $crate::__macro_support::format_args!($($arg)+), $($fields)* }
+    )
+);
+(name: $name:expr, target: $target:expr, $lvl:expr, $($k:ident).+ = $($fields:tt)* ) => (
+    $crate::event!(name: $name, target: $target, $lvl, { $($k).+ = $($fields)* })
+);
+(name: $name:expr, target: $target:expr, $lvl:expr, $($arg:tt)+) => (
+    $crate::event!(name: $name, target: $target, $lvl, { $($arg)+ })
+);
+
+// Target / parent.
+(target: $target:expr, parent: $parent:expr, $lvl:expr, { $($fields:tt)* } )=> ({
+    use $crate::__macro_support::Callsite as _;
+    static __CALLSITE: $crate::callsite::DefaultCallsite = $crate::callsite2! {
+        name: $crate::__macro_support::concat!(
+            "event ",
+            $crate::__macro_support::file!(),
+            ":",
+            $crate::__macro_support::line!()
+        ),
+        kind: $crate::metadata::Kind::EVENT,
+        target: $target,
+        level: $lvl,
+        fields: $($fields)*
+    };
+
+    let enabled = $crate::level_enabled!($lvl) && {
+        let interest = __CALLSITE.interest();
+        !interest.is_never() && $crate::__macro_support::__is_enabled(__CALLSITE.metadata(), interest)
+    };
+    if enabled {
+        (|value_set: $crate::field::ValueSet| {
+            $crate::__tracing_log!(
+                $lvl,
+                __CALLSITE,
+                &value_set
+            );
+            let meta = __CALLSITE.metadata();
+            // event with explicit parent
+            $crate::Event::child_of(
+                $parent,
+                meta,
+                &value_set
+            );
+        })($crate::valueset!(__CALLSITE.metadata().fields(), $($fields)*));
+    } else {
+        $crate::__tracing_log!(
+            $lvl,
+            __CALLSITE,
+            &$crate::valueset!(__CALLSITE.metadata().fields(), $($fields)*)
+        );
+    }
+});
+(target: $target:expr, parent: $parent:expr, $lvl:expr, { $($fields:tt)* }, $($arg:tt)+ ) => (
+    $crate::event!(
+        target: $target,
+        parent: $parent,
+        $lvl,
+        { message = $crate::__macro_support::format_args!($($arg)+), $($fields)* }
+    )
+);
+(target: $target:expr, parent: $parent:expr, $lvl:expr, $($k:ident).+ = $($fields:tt)* ) => (
+    $crate::event!(target: $target, parent: $parent, $lvl, { $($k).+ = $($fields)* })
+);
+(target: $target:expr, parent: $parent:expr, $lvl:expr, $($arg:tt)+) => (
+    $crate::event!(target: $target, parent: $parent, $lvl, { $($arg)+ })
+);
+
+// Name / parent.
+(name: $name:expr, parent: $parent:expr, $lvl:expr, { $($fields:tt)* } )=> ({
+    use $crate::__macro_support::Callsite as _;
+    static __CALLSITE: $crate::__macro_support::MacroCallsite = $crate::callsite2! {
+        name: $name,
+        kind: $crate::metadata::Kind::EVENT,
+        target: module_path!(),
+        level: $lvl,
+        fields: $($fields)*
+    };
+
+    let enabled = $crate::level_enabled!($lvl) && {
+        let interest = __CALLSITE.interest();
+        !interest.is_never() && __CALLSITE.is_enabled(interest)
+    };
+    if enabled {
+        (|value_set: $crate::field::ValueSet| {
+            $crate::__tracing_log!(
+                $lvl,
+                __CALLSITE,
+                &value_set
+            );
+            let meta = __CALLSITE.metadata();
+            // event with explicit parent
+            $crate::Event::child_of(
+                $parent,
+                meta,
+                &value_set
+            );
+        })($crate::valueset!(__CALLSITE.metadata().fields(), $($fields)*));
+    } else {
+        $crate::__tracing_log!(
+            $lvl,
+            __CALLSITE,
+            &$crate::valueset!(__CALLSITE.metadata().fields(), $($fields)*)
+        );
+    }
+});
+(name: $name:expr, parent: $parent:expr, $lvl:expr, { $($fields:tt)* }, $($arg:tt)+ ) => (
+    $crate::event!(
+        name: $name,
+        parent: $parent,
+        $lvl,
+        { message = $crate::__macro_support::format_args!($($arg)+), $($fields)* }
+    )
+);
+(name: $name:expr, parent: $parent:expr, $lvl:expr, $($k:ident).+ = $($fields:tt)* ) => (
+    $crate::event!(name: $name, parent: $parent, $lvl, { $($k).+ = $($fields)* })
+);
+(name: $name:expr, parent: $parent:expr, $lvl:expr, $($arg:tt)+) => (
+    $crate::event!(name: $name, parent: $parent, $lvl, { $($arg)+ })
+);
+
+// Name.
+(name: $name:expr, $lvl:expr, { $($fields:tt)* } )=> ({
+    use $crate::__macro_support::Callsite as _;
+    static __CALLSITE: $crate::__macro_support::MacroCallsite = $crate::callsite2! {
+        name: $name,
+        kind: $crate::metadata::Kind::EVENT,
+        target: module_path!(),
+        level: $lvl,
+        fields: $($fields)*
+    };
+    let enabled = $crate::level_enabled!($lvl) && {
+        let interest = __CALLSITE.interest();
+        !interest.is_never() && $crate::__macro_support::__is_enabled(__CALLSITE.metadata(), interest)
+    };
+    if enabled {
+        (|value_set: $crate::field::ValueSet| {
+            let meta = __CALLSITE.metadata();
+            // event with contextual parent
+            $crate::Event::dispatch(
+                meta,
+                &value_set
+            );
+            $crate::__tracing_log!(
+                $lvl,
+                __CALLSITE,
+                &value_set
+            );
+        })($crate::valueset!(__CALLSITE.metadata().fields(), $($fields)*));
+    } else {
+        $crate::__tracing_log!(
+            $lvl,
+            __CALLSITE,
+            &$crate::valueset!(__CALLSITE.metadata().fields(), $($fields)*)
+        );
+    }
+});
+(name: $name:expr, $lvl:expr, { $($fields:tt)* }, $($arg:tt)+ ) => (
+    $crate::event!(
+        name: $name,
+        $lvl,
+        { message = $crate::__macro_support::format_args!($($arg)+), $($fields)* }
+    )
+);
+(name: $name:expr, $lvl:expr, $($k:ident).+ = $($fields:tt)* ) => (
+    $crate::event!(name: $name, $lvl, { $($k).+ = $($fields)* })
+);
+(name: $name:expr, $lvl:expr, $($arg:tt)+ ) => (
+    $crate::event!(name: $name, $lvl, { $($arg)+ })
+);
+
+// Target.
+(target: $target:expr, $lvl:expr, { $($fields:tt)* } )=> ({
+    use $crate::__macro_support::Callsite as _;
+    static __CALLSITE: $crate::callsite::DefaultCallsite = $crate::callsite2! {
+        name: $crate::__macro_support::concat!(
+            "event ",
+            $crate::__macro_support::file!(),
+            ":",
+            $crate::__macro_support::line!()
+        ),
+        kind: $crate::metadata::Kind::EVENT,
+        target: $target,
+        level: $lvl,
+        fields: $($fields)*
+    };
+    let enabled = $crate::level_enabled!($lvl) && {
+        let interest = __CALLSITE.interest();
+        !interest.is_never() && $crate::__macro_support::__is_enabled(__CALLSITE.metadata(), interest)
+    };
+    if enabled {
+        (|value_set: $crate::field::ValueSet| {
+            let meta = __CALLSITE.metadata();
+            // event with contextual parent
+            $crate::Event::dispatch(
+                meta,
+                &value_set
+            );
+            $crate::__tracing_log!(
+                $lvl,
+                __CALLSITE,
+                &value_set
+            );
+        })($crate::valueset!(__CALLSITE.metadata().fields(), $($fields)*));
+    } else {
+        $crate::__tracing_log!(
+            $lvl,
+            __CALLSITE,
+            &$crate::valueset!(__CALLSITE.metadata().fields(), $($fields)*)
+        );
+    }
+});
+(target: $target:expr, $lvl:expr, { $($fields:tt)* }, $($arg:tt)+ ) => (
+    $crate::event!(
+        target: $target,
+        $lvl,
+        { message = $crate::__macro_support::format_args!($($arg)+), $($fields)* }
+    )
+);
+(target: $target:expr, $lvl:expr, $($k:ident).+ = $($fields:tt)* ) => (
+    $crate::event!(target: $target, $lvl, { $($k).+ = $($fields)* })
+);
+(target: $target:expr, $lvl:expr, $($arg:tt)+ ) => (
+    $crate::event!(target: $target, $lvl, { $($arg)+ })
+);
+
+// Parent.
+(parent: $parent:expr, $lvl:expr, { $($fields:tt)* }, $($arg:tt)+ ) => (
+    $crate::event!(
+        target: module_path!(),
+        parent: $parent,
+        $lvl,
+        { message = $crate::__macro_support::format_args!($($arg)+), $($fields)* }
+    )
+);
+(parent: $parent:expr, $lvl:expr, $($k:ident).+ = $($field:tt)*) => (
+    $crate::event!(
+        target: module_path!(),
+        parent: $parent,
+        $lvl,
+        { $($k).+ = $($field)*}
+    )
+);
+(parent: $parent:expr, $lvl:expr, ?$($k:ident).+ = $($field:tt)*) => (
+    $crate::event!(
+        target: module_path!(),
+        parent: $parent,
+        $lvl,
+        { ?$($k).+ = $($field)*}
+    )
+);
+(parent: $parent:expr, $lvl:expr, %$($k:ident).+ = $($field:tt)*) => (
+    $crate::event!(
+        target: module_path!(),
+        parent: $parent,
+        $lvl,
+        { %$($k).+ = $($field)*}
+    )
+);
+(parent: $parent:expr, $lvl:expr, $($k:ident).+, $($field:tt)*) => (
+    $crate::event!(
+        target: module_path!(),
+        parent: $parent,
+        $lvl,
+        { $($k).+, $($field)*}
+    )
+);
+(parent: $parent:expr, $lvl:expr, %$($k:ident).+, $($field:tt)*) => (
+    $crate::event!(
+        target: module_path!(),
+        parent: $parent,
+        $lvl,
+        { %$($k).+, $($field)*}
+    )
+);
+(parent: $parent:expr, $lvl:expr, ?$($k:ident).+, $($field:tt)*) => (
+    $crate::event!(
+        target: module_path!(),
+        parent: $parent,
+        $lvl,
+        { ?$($k).+, $($field)*}
+    )
+);
+(parent: $parent:expr, $lvl:expr, $($arg:tt)+ ) => (
+    $crate::event!(target: module_path!(), parent: $parent, $lvl, { $($arg)+ })
+);
+
+// ...
+( $lvl:expr, { $($fields:tt)* }, $($arg:tt)+ ) => (
+    $crate::event!(
+        target: module_path!(),
+        $lvl,
+        { message = $crate::__macro_support::format_args!($($arg)+), $($fields)* }
+    )
+);
+( $lvl:expr, { $($fields:tt)* }, $($arg:tt)+ ) => (
+    $crate::event!(
+        target: module_path!(),
+        $lvl,
+        { message = format_args!($($arg)+), $($fields)* }
+    )
+);
+($lvl:expr, $($k:ident).+ = $($field:tt)*) => (
+    $crate::event!(
+        target: module_path!(),
+        $lvl,
+        { $($k).+ = $($field)*}
+    )
+);
+($lvl:expr, $($k:ident).+, $($field:tt)*) => (
+    $crate::event!(
+        target: module_path!(),
+        $lvl,
+        { $($k).+, $($field)*}
+    )
+);
+($lvl:expr, ?$($k:ident).+, $($field:tt)*) => (
+    $crate::event!(
+        target: module_path!(),
+        $lvl,
+        { ?$($k).+, $($field)*}
+    )
+);
+($lvl:expr, %$($k:ident).+, $($field:tt)*) => (
+    $crate::event!(
+        target: module_path!(),
+        $lvl,
+        { %$($k).+, $($field)*}
+    )
+);
+($lvl:expr, ?$($k:ident).+) => (
+    $crate::event!($lvl, ?$($k).+,)
+);
+($lvl:expr, %$($k:ident).+) => (
+    $crate::event!($lvl, %$($k).+,)
+);
+($lvl:expr, $($k:ident).+) => (
+    $crate::event!($lvl, $($k).+,)
+);
+( $lvl:expr, $($arg:tt)+ ) => (
+    $crate::event!(target: module_path!(), $lvl, { $($arg)+ })
+);
+}
+
+#[macro_export]
+macro_rules! event_enabled {
+($($rest:tt)*)=> (
+    $crate::enabled!(kind: $crate::metadata::Kind::EVENT, $($rest)*)
+)
+}
+
+#[macro_export]
+macro_rules! span_enabled {
+($($rest:tt)*)=> (
+    $crate::enabled!(kind: $crate::metadata::Kind::SPAN, $($rest)*)
+)
+}
+
+#[macro_export]
+macro_rules! enabled {
+(kind: $kind:expr, target: $target:expr, $lvl:expr, { $($fields:tt)* } )=> ({
+    if $crate::level_enabled!($lvl) {
+        use $crate::__macro_support::Callsite as _;
+        static __CALLSITE: $crate::callsite::DefaultCallsite = $crate::callsite2! {
+            name: $crate::__macro_support::concat!(
+                "enabled ",
+                $crate::__macro_support::file!(),
+                ":",
+                $crate::__macro_support::line!()
+            ),
+            kind: $kind.hint(),
+            target: $target,
+            level: $lvl,
+            fields: $($fields)*
+        };
+        let interest = __CALLSITE.interest();
+        if !interest.is_never() && $crate::__macro_support::__is_enabled(__CALLSITE.metadata(), interest) {
+            let meta = __CALLSITE.metadata();
+            $crate::dispatcher::get_default(|current| current.enabled(meta))
+        } else {
+            false
+        }
+    } else {
+        false
+    }
+});
+// Just target and level
+(kind: $kind:expr, target: $target:expr, $lvl:expr ) => (
+    $crate::enabled!(kind: $kind, target: $target, $lvl, { })
+);
+(target: $target:expr, $lvl:expr ) => (
+    $crate::enabled!(kind: $crate::metadata::Kind::HINT, target: $target, $lvl, { })
+);
+
+// These four cases handle fields with no values
+(kind: $kind:expr, target: $target:expr, $lvl:expr, $($field:tt)*) => (
+    $crate::enabled!(
+        kind: $kind,
+        target: $target,
+        $lvl,
+        { $($field)*}
+    )
+);
+(target: $target:expr, $lvl:expr, $($field:tt)*) => (
+    $crate::enabled!(
+        kind: $crate::metadata::Kind::HINT,
+        target: $target,
+        $lvl,
+        { $($field)*}
+    )
+);
+
+// Level and field case
+(kind: $kind:expr, $lvl:expr, $($field:tt)*) => (
+    $crate::enabled!(
+        kind: $kind,
+        target: module_path!(),
+        $lvl,
+        { $($field)*}
+    )
+);
+
+// Simplest `enabled!` case
+(kind: $kind:expr, $lvl:expr) => (
+    $crate::enabled!(kind: $kind, target: module_path!(), $lvl, { })
+);
+($lvl:expr) => (
+    $crate::enabled!(kind: $crate::metadata::Kind::HINT, target: module_path!(), $lvl, { })
+);
+
+// Fallthrough from above
+($lvl:expr, $($field:tt)*) => (
+    $crate::enabled!(
+        kind: $crate::metadata::Kind::HINT,
+        target: module_path!(),
+        $lvl,
+        { $($field)*}
+    )
+);
+}
+
+#[macro_export]
+macro_rules! trace {
+// Name / target / parent.
+(name: $name:expr, target: $target:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => (
+    $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::TRACE, { $($field)* }, $($arg)*)
+);
+(name: $name:expr, target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)* ) => (
+    $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::TRACE, { $($k).+ $($field)* })
+);
+(name: $name:expr, target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)* ) => (
+    $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::TRACE, { ?$($k).+ $($field)* })
+);
+(name: $name:expr, target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)* ) => (
+    $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::TRACE, { %$($k).+ $($field)* })
+);
+(name: $name:expr, target: $target:expr, parent: $parent:expr, $($arg:tt)+ ) => (
+    $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::TRACE, {}, $($arg)+)
+);
+
+// Name / target.
+(name: $name:expr, target: $target:expr, { $($field:tt)* }, $($arg:tt)* ) => (
+    $crate::event!(name: $name, target: $target, $crate::Level::TRACE, { $($field)* }, $($arg)*)
+);
+(name: $name:expr, target: $target:expr, $($k:ident).+ $($field:tt)* ) => (
+    $crate::event!(name: $name, target: $target, $crate::Level::TRACE, { $($k).+ $($field)* })
+);
+(name: $name:expr, target: $target:expr, ?$($k:ident).+ $($field:tt)* ) => (
+    $crate::event!(name: $name, target: $target, $crate::Level::TRACE, { ?$($k).+ $($field)* })
+);
+(name: $name:expr, target: $target:expr, %$($k:ident).+ $($field:tt)* ) => (
+    $crate::event!(name: $name, target: $target, $crate::Level::TRACE, { %$($k).+ $($field)* })
+);
+(name: $name:expr, target: $target:expr, $($arg:tt)+ ) => (
+    $crate::event!(name: $name, target: $target, $crate::Level::TRACE, {}, $($arg)+)
+);
+
+// Target / parent.
+(target: $target:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => (
+    $crate::event!(target: $target, parent: $parent, $crate::Level::TRACE, { $($field)* }, $($arg)*)
+);
+(target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)* ) => (
+    $crate::event!(target: $target, parent: $parent, $crate::Level::TRACE, { $($k).+ $($field)* })
+);
+(target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)* ) => (
+    $crate::event!(target: $target, parent: $parent, $crate::Level::TRACE, { ?$($k).+ $($field)* })
+);
+(target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)* ) => (
+    $crate::event!(target: $target, parent: $parent, $crate::Level::TRACE, { %$($k).+ $($field)* })
+);
+(target: $target:expr, parent: $parent:expr, $($arg:tt)+ ) => (
+    $crate::event!(target: $target, parent: $parent, $crate::Level::TRACE, {}, $($arg)+)
+);
+
+// Name / parent.
+(name: $name:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => (
+    $crate::event!(name: $name, parent: $parent, $crate::Level::TRACE, { $($field)* }, $($arg)*)
+);
+(name: $name:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)* ) => (
+    $crate::event!(name: $name, parent: $parent, $crate::Level::TRACE, { $($k).+ $($field)* })
+);
+(name: $name:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)* ) => (
+    $crate::event!(name: $name, parent: $parent, $crate::Level::TRACE, { ?$($k).+ $($field)* })
+);
+(name: $name:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)* ) => (
+    $crate::event!(name: $name, parent: $parent, $crate::Level::TRACE, { %$($k).+ $($field)* })
+);
+(name: $name:expr, parent: $parent:expr, $($arg:tt)+ ) => (
+    $crate::event!(name: $name, parent: $parent, $crate::Level::TRACE, {}, $($arg)+)
+);
+
+// Name.
+(name: $name:expr, { $($field:tt)* }, $($arg:tt)* ) => (
+    $crate::event!(name: $name, $crate::Level::TRACE, { $($field)* }, $($arg)*)
+);
+(name: $name:expr, $($k:ident).+ $($field:tt)* ) => (
+    $crate::event!(name: $name, $crate::Level::TRACE, { $($k).+ $($field)* })
+);
+(name: $name:expr, ?$($k:ident).+ $($field:tt)* ) => (
+    $crate::event!(name: $name, $crate::Level::TRACE, { ?$($k).+ $($field)* })
+);
+(name: $name:expr, %$($k:ident).+ $($field:tt)* ) => (
+    $crate::event!(name: $name, $crate::Level::TRACE, { %$($k).+ $($field)* })
+);
+(name: $name:expr, $($arg:tt)+ ) => (
+    $crate::event!(name: $name, $crate::Level::TRACE, {}, $($arg)+)
+);
+
+// Target.
+(target: $target:expr, { $($field:tt)* }, $($arg:tt)* ) => (
+    $crate::event!(target: $target, $crate::Level::TRACE, { $($field)* }, $($arg)*)
+);
+(target: $target:expr, $($k:ident).+ $($field:tt)* ) => (
+    $crate::event!(target: $target, $crate::Level::TRACE, { $($k).+ $($field)* })
+);
+(target: $target:expr, ?$($k:ident).+ $($field:tt)* ) => (
+    $crate::event!(target: $target, $crate::Level::TRACE, { ?$($k).+ $($field)* })
+);
+(target: $target:expr, %$($k:ident).+ $($field:tt)* ) => (
+    $crate::event!(target: $target, $crate::Level::TRACE, { %$($k).+ $($field)* })
+);
+(target: $target:expr, $($arg:tt)+ ) => (
+    $crate::event!(target: $target, $crate::Level::TRACE, {}, $($arg)+)
+);
+
+// Parent.
+(parent: $parent:expr, { $($field:tt)+ }, $($arg:tt)+ ) => (
+    $crate::event!(
+        target: module_path!(),
+        parent: $parent,
+        $crate::Level::TRACE,
+        { $($field)+ },
+        $($arg)+
+    )
+);
+(parent: $parent:expr, $($k:ident).+ = $($field:tt)*) => (
+    $crate::event!(
+        target: module_path!(),
+        parent: $parent,
+        $crate::Level::TRACE,
+        { $($k).+ = $($field)*}
+    )
+);
+(parent: $parent:expr, ?$($k:ident).+ = $($field:tt)*) => (
+    $crate::event!(
+        target: module_path!(),
+        parent: $parent,
+        $crate::Level::TRACE,
+        { ?$($k).+ = $($field)*}
+    )
+);
+(parent: $parent:expr, %$($k:ident).+ = $($field:tt)*) => (
+    $crate::event!(
+        target: module_path!(),
+        parent: $parent,
+        $crate::Level::TRACE,
+        { %$($k).+ = $($field)*}
+    )
+);
+(parent: $parent:expr, $($k:ident).+, $($field:tt)*) => (
+    $crate::event!(
+        target: module_path!(),
+        parent: $parent,
+        $crate::Level::TRACE,
+        { $($k).+, $($field)*}
+    )
+);
+(parent: $parent:expr, ?$($k:ident).+, $($field:tt)*) => (
+    $crate::event!(
+        target: module_path!(),
+        parent: $parent,
+        $crate::Level::TRACE,
+        { ?$($k).+, $($field)*}
+    )
+);
+(parent: $parent:expr, %$($k:ident).+, $($field:tt)*) => (
+    $crate::event!(
+        target: module_path!(),
+        parent: $parent,
+        $crate::Level::TRACE,
+        { %$($k).+, $($field)*}
+    )
+);
+(parent: $parent:expr, $($arg:tt)+) => (
+    $crate::event!(
+        target: module_path!(),
+        parent: $parent,
+        $crate::Level::TRACE,
+        {},
+        $($arg)+
+    )
+);
+
+// ...
+({ $($field:tt)+ }, $($arg:tt)+ ) => (
+    $crate::event!(
+        target: module_path!(),
+        $crate::Level::TRACE,
+        { $($field)+ },
+        $($arg)+
+    )
+);
+($($k:ident).+ = $($field:tt)*) => (
+    $crate::event!(
+        target: module_path!(),
+        $crate::Level::TRACE,
+        { $($k).+ = $($field)*}
+    )
+);
+(?$($k:ident).+ = $($field:tt)*) => (
+    $crate::event!(
+        target: module_path!(),
+        $crate::Level::TRACE,
+        { ?$($k).+ = $($field)*}
+    )
+);
+(%$($k:ident).+ = $($field:tt)*) => (
+    $crate::event!(
+        target: module_path!(),
+        $crate::Level::TRACE,
+        { %$($k).+ = $($field)*}
+    )
+);
+($($k:ident).+, $($field:tt)*) => (
+    $crate::event!(
+        target: module_path!(),
+        $crate::Level::TRACE,
+        { $($k).+, $($field)*}
+    )
+);
+(?$($k:ident).+, $($field:tt)*) => (
+    $crate::event!(
+        target: module_path!(),
+        $crate::Level::TRACE,
+        { ?$($k).+, $($field)*}
+    )
+);
+(%$($k:ident).+, $($field:tt)*) => (
+    $crate::event!(
+        target: module_path!(),
+        $crate::Level::TRACE,
+        { %$($k).+, $($field)*}
+    )
+);
+(?$($k:ident).+) => (
+    $crate::event!(
+        target: module_path!(),
+        $crate::Level::TRACE,
+        { ?$($k).+ }
+    )
+);
+(%$($k:ident).+) => (
+    $crate::event!(
+        target: module_path!(),
+        $crate::Level::TRACE,
+        { %$($k).+ }
+    )
+);
+($($k:ident).+) => (
+    $crate::event!(
+        target: module_path!(),
+        $crate::Level::TRACE,
+        { $($k).+ }
+    )
+);
+($($arg:tt)+) => (
+    $crate::event!(
+        target: module_path!(),
+        $crate::Level::TRACE,
+        $($arg)+
+    )
+);
+}
+
+#[macro_export]
+macro_rules! debug {
+// Name / target / parent.
+(name: $name:expr, target: $target:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => (
+    $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::DEBUG, { $($field)* }, $($arg)*)
+);
+(name: $name:expr, target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)* ) => (
+    $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::DEBUG, { $($k).+ $($field)* })
+);
+(name: $name:expr, target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)* ) => (
+    $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::DEBUG, { ?$($k).+ $($field)* })
+);
+(name: $name:expr, target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)* ) => (
+    $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::DEBUG, { %$($k).+ $($field)* })
+);
+(name: $name:expr, target: $target:expr, parent: $parent:expr, $($arg:tt)+ ) => (
+    $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::DEBUG, {}, $($arg)+)
+);
+
+// Name / target.
+(name: $name:expr, target: $target:expr, { $($field:tt)* }, $($arg:tt)* ) => (
+    $crate::event!(name: $name, target: $target, $crate::Level::DEBUG, { $($field)* }, $($arg)*)
+);
+(name: $name:expr, target: $target:expr, $($k:ident).+ $($field:tt)* ) => (
+    $crate::event!(name: $name, target: $target, $crate::Level::DEBUG, { $($k).+ $($field)* })
+);
+(name: $name:expr, target: $target:expr, ?$($k:ident).+ $($field:tt)* ) => (
+    $crate::event!(name: $name, target: $target, $crate::Level::DEBUG, { ?$($k).+ $($field)* })
+);
+(name: $name:expr, target: $target:expr, %$($k:ident).+ $($field:tt)* ) => (
+    $crate::event!(name: $name, target: $target, $crate::Level::DEBUG, { %$($k).+ $($field)* })
+);
+(name: $name:expr, target: $target:expr, $($arg:tt)+ ) => (
+    $crate::event!(name: $name, target: $target, $crate::Level::DEBUG, {}, $($arg)+)
+);
+
+// Target / parent.
+(target: $target:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => (
+    $crate::event!(target: $target, parent: $parent, $crate::Level::DEBUG, { $($field)* }, $($arg)*)
+);
+(target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)* ) => (
+    $crate::event!(target: $target, parent: $parent, $crate::Level::DEBUG, { $($k).+ $($field)* })
+);
+(target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)* ) => (
+    $crate::event!(target: $target, parent: $parent, $crate::Level::DEBUG, { ?$($k).+ $($field)* })
+);
+(target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)* ) => (
+    $crate::event!(target: $target, parent: $parent, $crate::Level::DEBUG, { %$($k).+ $($field)* })
+);
+(target: $target:expr, parent: $parent:expr, $($arg:tt)+ ) => (
+    $crate::event!(target: $target, parent: $parent, $crate::Level::DEBUG, {}, $($arg)+)
+);
+
+// Name / parent.
+(name: $name:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => (
+    $crate::event!(name: $name, parent: $parent, $crate::Level::DEBUG, { $($field)* }, $($arg)*)
+);
+(name: $name:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)* ) => (
+    $crate::event!(name: $name, parent: $parent, $crate::Level::DEBUG, { $($k).+ $($field)* })
+);
+(name: $name:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)* ) => (
+    $crate::event!(name: $name, parent: $parent, $crate::Level::DEBUG, { ?$($k).+ $($field)* })
+);
+(name: $name:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)* ) => (
+    $crate::event!(name: $name, parent: $parent, $crate::Level::DEBUG, { %$($k).+ $($field)* })
+);
+(name: $name:expr, parent: $parent:expr, $($arg:tt)+ ) => (
+    $crate::event!(name: $name, parent: $parent, $crate::Level::DEBUG, {}, $($arg)+)
+);
+
+// Name.
+(name: $name:expr, { $($field:tt)* }, $($arg:tt)* ) => (
+    $crate::event!(name: $name, $crate::Level::DEBUG, { $($field)* }, $($arg)*)
+);
+(name: $name:expr, $($k:ident).+ $($field:tt)* ) => (
+    $crate::event!(name: $name, $crate::Level::DEBUG, { $($k).+ $($field)* })
+);
+(name: $name:expr, ?$($k:ident).+ $($field:tt)* ) => (
+    $crate::event!(name: $name, $crate::Level::DEBUG, { ?$($k).+ $($field)* })
+);
+(name: $name:expr, %$($k:ident).+ $($field:tt)* ) => (
+    $crate::event!(name: $name, $crate::Level::DEBUG, { %$($k).+ $($field)* })
+);
+(name: $name:expr, $($arg:tt)+ ) => (
+    $crate::event!(name: $name, $crate::Level::DEBUG, {}, $($arg)+)
+);
+
+// Target.
+(target: $target:expr, { $($field:tt)* }, $($arg:tt)* ) => (
+    $crate::event!(target: $target, $crate::Level::DEBUG, { $($field)* }, $($arg)*)
+);
+(target: $target:expr, $($k:ident).+ $($field:tt)* ) => (
+    $crate::event!(target: $target, $crate::Level::DEBUG, { $($k).+ $($field)* })
+);
+(target: $target:expr, ?$($k:ident).+ $($field:tt)* ) => (
+    $crate::event!(target: $target, $crate::Level::DEBUG, { ?$($k).+ $($field)* })
+);
+(target: $target:expr, %$($k:ident).+ $($field:tt)* ) => (
+    $crate::event!(target: $target, $crate::Level::DEBUG, { %$($k).+ $($field)* })
+);
+(target: $target:expr, $($arg:tt)+ ) => (
+    $crate::event!(target: $target, $crate::Level::DEBUG, {}, $($arg)+)
+);
+
+// Parent.
+(parent: $parent:expr, { $($field:tt)+ }, $($arg:tt)+ ) => (
+    $crate::event!(
+        target: module_path!(),
+        parent: $parent,
+        $crate::Level::DEBUG,
+        { $($field)+ },
+        $($arg)+
+    )
+);
+(parent: $parent:expr, $($k:ident).+ = $($field:tt)*) => (
+    $crate::event!(
+        target: module_path!(),
+        parent: $parent,
+        $crate::Level::DEBUG,
+        { $($k).+ = $($field)*}
+    )
+);
+(parent: $parent:expr, ?$($k:ident).+ = $($field:tt)*) => (
+    $crate::event!(
+        target: module_path!(),
+        parent: $parent,
+        $crate::Level::DEBUG,
+        { ?$($k).+ = $($field)*}
+    )
+);
+(parent: $parent:expr, %$($k:ident).+ = $($field:tt)*) => (
+    $crate::event!(
+        target: module_path!(),
+        parent: $parent,
+        $crate::Level::DEBUG,
+        { %$($k).+ = $($field)*}
+    )
+);
+(parent: $parent:expr, $($k:ident).+, $($field:tt)*) => (
+    $crate::event!(
+        target: module_path!(),
+        parent: $parent,
+        $crate::Level::DEBUG,
+        { $($k).+, $($field)*}
+    )
+);
+(parent: $parent:expr, ?$($k:ident).+, $($field:tt)*) => (
+    $crate::event!(
+        target: module_path!(),
+        parent: $parent,
+        $crate::Level::DEBUG,
+        { ?$($k).+, $($field)*}
+    )
+);
+(parent: $parent:expr, %$($k:ident).+, $($field:tt)*) => (
+    $crate::event!(
+        target: module_path!(),
+        parent: $parent,
+        $crate::Level::DEBUG,
+        { %$($k).+, $($field)*}
+    )
+);
+(parent: $parent:expr, $($arg:tt)+) => (
+    $crate::event!(
+        target: module_path!(),
+        parent: $parent,
+        $crate::Level::DEBUG,
+        {},
+        $($arg)+
+    )
+);
+
+// ...
+({ $($field:tt)+ }, $($arg:tt)+ ) => (
+    $crate::event!(
+        target: module_path!(),
+        $crate::Level::DEBUG,
+        { $($field)+ },
+        $($arg)+
+    )
+);
+($($k:ident).+ = $($field:tt)*) => (
+    $crate::event!(
+        target: module_path!(),
+        $crate::Level::DEBUG,
+        { $($k).+ = $($field)*}
+    )
+);
+(?$($k:ident).+ = $($field:tt)*) => (
+    $crate::event!(
+        target: module_path!(),
+        $crate::Level::DEBUG,
+        { ?$($k).+ = $($field)*}
+    )
+);
+(%$($k:ident).+ = $($field:tt)*) => (
+    $crate::event!(
+        target: module_path!(),
+        $crate::Level::DEBUG,
+        { %$($k).+ = $($field)*}
+    )
+);
+($($k:ident).+, $($field:tt)*) => (
+    $crate::event!(
+        target: module_path!(),
+        $crate::Level::DEBUG,
+        { $($k).+, $($field)*}
+    )
+);
+(?$($k:ident).+, $($field:tt)*) => (
+    $crate::event!(
+        target: module_path!(),
+        $crate::Level::DEBUG,
+        { ?$($k).+, $($field)*}
+    )
+);
+(%$($k:ident).+, $($field:tt)*) => (
+    $crate::event!(
+        target: module_path!(),
+        $crate::Level::DEBUG,
+        { %$($k).+, $($field)*}
+    )
+);
+(?$($k:ident).+) => (
+    $crate::event!(
+        target: module_path!(),
+        $crate::Level::DEBUG,
+        { ?$($k).+ }
+    )
+);
+(%$($k:ident).+) => (
+    $crate::event!(
+        target: module_path!(),
+        $crate::Level::DEBUG,
+        { %$($k).+ }
+    )
+);
+($($k:ident).+) => (
+    $crate::event!(
+        target: module_path!(),
+        $crate::Level::DEBUG,
+        { $($k).+ }
+    )
+);
+($($arg:tt)+) => (
+    $crate::event!(
+        target: module_path!(),
+        $crate::Level::DEBUG,
+        $($arg)+
+    )
+);
+}
+
+#[macro_export]
+macro_rules! info {
+// Name / target / parent.
+(name: $name:expr, target: $target:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => (
+    $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::INFO, { $($field)* }, $($arg)*)
+);
+(name: $name:expr, target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)* ) => (
+    $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::INFO, { $($k).+ $($field)* })
+);
+(name: $name:expr, target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)* ) => (
+    $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::INFO, { ?$($k).+ $($field)* })
+);
+(name: $name:expr, target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)* ) => (
+    $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::INFO, { %$($k).+ $($field)* })
+);
+(name: $name:expr, target: $target:expr, parent: $parent:expr, $($arg:tt)+ ) => (
+    $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::INFO, {}, $($arg)+)
+);
+
+// Name / target.
+(name: $name:expr, target: $target:expr, { $($field:tt)* }, $($arg:tt)* ) => (
+    $crate::event!(name: $name, target: $target, $crate::Level::INFO, { $($field)* }, $($arg)*)
+);
+(name: $name:expr, target: $target:expr, $($k:ident).+ $($field:tt)* ) => (
+    $crate::event!(name: $name, target: $target, $crate::Level::INFO, { $($k).+ $($field)* })
+);
+(name: $name:expr, target: $target:expr, ?$($k:ident).+ $($field:tt)* ) => (
+    $crate::event!(name: $name, target: $target, $crate::Level::INFO, { ?$($k).+ $($field)* })
+);
+(name: $name:expr, target: $target:expr, %$($k:ident).+ $($field:tt)* ) => (
+    $crate::event!(name: $name, target: $target, $crate::Level::INFO, { %$($k).+ $($field)* })
+);
+(name: $name:expr, target: $target:expr, $($arg:tt)+ ) => (
+    $crate::event!(name: $name, target: $target, $crate::Level::INFO, {}, $($arg)+)
+);
+
+// Target / parent.
+(target: $target:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => (
+    $crate::event!(target: $target, parent: $parent, $crate::Level::INFO, { $($field)* }, $($arg)*)
+);
+(target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)* ) => (
+    $crate::event!(target: $target, parent: $parent, $crate::Level::INFO, { $($k).+ $($field)* })
+);
+(target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)* ) => (
+    $crate::event!(target: $target, parent: $parent, $crate::Level::INFO, { ?$($k).+ $($field)* })
+);
+(target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)* ) => (
+    $crate::event!(target: $target, parent: $parent, $crate::Level::INFO, { %$($k).+ $($field)* })
+);
+(target: $target:expr, parent: $parent:expr, $($arg:tt)+ ) => (
+    $crate::event!(target: $target, parent: $parent, $crate::Level::INFO, {}, $($arg)+)
+);
+
+// Name / parent.
+(name: $name:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => (
+    $crate::event!(name: $name, parent: $parent, $crate::Level::INFO, { $($field)* }, $($arg)*)
+);
+(name: $name:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)* ) => (
+    $crate::event!(name: $name, parent: $parent, $crate::Level::INFO, { $($k).+ $($field)* })
+);
+(name: $name:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)* ) => (
+    $crate::event!(name: $name, parent: $parent, $crate::Level::INFO, { ?$($k).+ $($field)* })
+);
+(name: $name:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)* ) => (
+    $crate::event!(name: $name, parent: $parent, $crate::Level::INFO, { %$($k).+ $($field)* })
+);
+(name: $name:expr, parent: $parent:expr, $($arg:tt)+ ) => (
+    $crate::event!(name: $name, parent: $parent, $crate::Level::INFO, {}, $($arg)+)
+);
+
+// Name.
+(name: $name:expr, { $($field:tt)* }, $($arg:tt)* ) => (
+    $crate::event!(name: $name, $crate::Level::INFO, { $($field)* }, $($arg)*)
+);
+(name: $name:expr, $($k:ident).+ $($field:tt)* ) => (
+    $crate::event!(name: $name, $crate::Level::INFO, { $($k).+ $($field)* })
+);
+(name: $name:expr, ?$($k:ident).+ $($field:tt)* ) => (
+    $crate::event!(name: $name, $crate::Level::INFO, { ?$($k).+ $($field)* })
+);
+(name: $name:expr, %$($k:ident).+ $($field:tt)* ) => (
+    $crate::event!(name: $name, $crate::Level::INFO, { %$($k).+ $($field)* })
+);
+(name: $name:expr, $($arg:tt)+ ) => (
+    $crate::event!(name: $name, $crate::Level::INFO, {}, $($arg)+)
+);
+
+// Target.
+(target: $target:expr, { $($field:tt)* }, $($arg:tt)* ) => (
+    $crate::event!(target: $target, $crate::Level::INFO, { $($field)* }, $($arg)*)
+);
+(target: $target:expr, $($k:ident).+ $($field:tt)* ) => (
+    $crate::event!(target: $target, $crate::Level::INFO, { $($k).+ $($field)* })
+);
+(target: $target:expr, ?$($k:ident).+ $($field:tt)* ) => (
+    $crate::event!(target: $target, $crate::Level::INFO, { ?$($k).+ $($field)* })
+);
+(target: $target:expr, %$($k:ident).+ $($field:tt)* ) => (
+    $crate::event!(target: $target, $crate::Level::INFO, { %$($k).+ $($field)* })
+);
+(target: $target:expr, $($arg:tt)+ ) => (
+    $crate::event!(target: $target, $crate::Level::INFO, {}, $($arg)+)
+);
+
+// Parent.
+(parent: $parent:expr, { $($field:tt)+ }, $($arg:tt)+ ) => (
+    $crate::event!(
+        target: module_path!(),
+        parent: $parent,
+        $crate::Level::INFO,
+        { $($field)+ },
+        $($arg)+
+    )
+);
+(parent: $parent:expr, $($k:ident).+ = $($field:tt)*) => (
+    $crate::event!(
+        target: module_path!(),
+        parent: $parent,
+        $crate::Level::INFO,
+        { $($k).+ = $($field)*}
+    )
+);
+(parent: $parent:expr, ?$($k:ident).+ = $($field:tt)*) => (
+    $crate::event!(
+        target: module_path!(),
+        parent: $parent,
+        $crate::Level::INFO,
+        { ?$($k).+ = $($field)*}
+    )
+);
+(parent: $parent:expr, %$($k:ident).+ = $($field:tt)*) => (
+    $crate::event!(
+        target: module_path!(),
+        parent: $parent,
+        $crate::Level::INFO,
+        { %$($k).+ = $($field)*}
+    )
+);
+(parent: $parent:expr, $($k:ident).+, $($field:tt)*) => (
+    $crate::event!(
+        target: module_path!(),
+        parent: $parent,
+        $crate::Level::INFO,
+        { $($k).+, $($field)*}
+    )
+);
+(parent: $parent:expr, ?$($k:ident).+, $($field:tt)*) => (
+    $crate::event!(
+        target: module_path!(),
+        parent: $parent,
+        $crate::Level::INFO,
+        { ?$($k).+, $($field)*}
+    )
+);
+(parent: $parent:expr, %$($k:ident).+, $($field:tt)*) => (
+    $crate::event!(
+        target: module_path!(),
+        parent: $parent,
+        $crate::Level::INFO,
+        { %$($k).+, $($field)*}
+    )
+);
+(parent: $parent:expr, $($arg:tt)+) => (
+    $crate::event!(
+        target: module_path!(),
+        parent: $parent,
+        $crate::Level::INFO,
+        {},
+        $($arg)+
+    )
+);
+
+// ...
+({ $($field:tt)+ }, $($arg:tt)+ ) => (
+    $crate::event!(
+        target: module_path!(),
+        $crate::Level::INFO,
+        { $($field)+ },
+        $($arg)+
+    )
+);
+($($k:ident).+ = $($field:tt)*) => (
+    $crate::event!(
+        target: module_path!(),
+        $crate::Level::INFO,
+        { $($k).+ = $($field)*}
+    )
+);
+(?$($k:ident).+ = $($field:tt)*) => (
+    $crate::event!(
+        target: module_path!(),
+        $crate::Level::INFO,
+        { ?$($k).+ = $($field)*}
+    )
+);
+(%$($k:ident).+ = $($field:tt)*) => (
+    $crate::event!(
+        target: module_path!(),
+        $crate::Level::INFO,
+        { %$($k).+ = $($field)*}
+    )
+);
+($($k:ident).+, $($field:tt)*) => (
+    $crate::event!(
+        target: module_path!(),
+        $crate::Level::INFO,
+        { $($k).+, $($field)*}
+    )
+);
+(?$($k:ident).+, $($field:tt)*) => (
+    $crate::event!(
+        target: module_path!(),
+        $crate::Level::INFO,
+        { ?$($k).+, $($field)*}
+    )
+);
+(%$($k:ident).+, $($field:tt)*) => (
+    $crate::event!(
+        target: module_path!(),
+        $crate::Level::INFO,
+        { %$($k).+, $($field)*}
+    )
+);
+(?$($k:ident).+) => (
+    $crate::event!(
+        target: module_path!(),
+        $crate::Level::INFO,
+        { ?$($k).+ }
+    )
+);
+(%$($k:ident).+) => (
+    $crate::event!(
+        target: module_path!(),
+        $crate::Level::INFO,
+        { %$($k).+ }
+    )
+);
+($($k:ident).+) => (
+    $crate::event!(
+        target: module_path!(),
+        $crate::Level::INFO,
+        { $($k).+ }
+    )
+);
+($($arg:tt)+) => (
+    $crate::event!(
+        target: module_path!(),
+        $crate::Level::INFO,
+        $($arg)+
+    )
+);
+}
+
+#[macro_export]
+macro_rules! warn {
+// Name / target / parent.
+(name: $name:expr, target: $target:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => (
+    $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::WARN, { $($field)* }, $($arg)*)
+);
+(name: $name:expr, target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)* ) => (
+    $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::WARN, { $($k).+ $($field)* })
+);
+(name: $name:expr, target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)* ) => (
+    $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::WARN, { ?$($k).+ $($field)* })
+);
+(name: $name:expr, target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)* ) => (
+    $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::WARN, { %$($k).+ $($field)* })
+);
+(name: $name:expr, target: $target:expr, parent: $parent:expr, $($arg:tt)+ ) => (
+    $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::WARN, {}, $($arg)+)
+);
+
+// Name / target.
+(name: $name:expr, target: $target:expr, { $($field:tt)* }, $($arg:tt)* ) => (
+    $crate::event!(name: $name, target: $target, $crate::Level::WARN, { $($field)* }, $($arg)*)
+);
+(name: $name:expr, target: $target:expr, $($k:ident).+ $($field:tt)* ) => (
+    $crate::event!(name: $name, target: $target, $crate::Level::WARN, { $($k).+ $($field)* })
+);
+(name: $name:expr, target: $target:expr, ?$($k:ident).+ $($field:tt)* ) => (
+    $crate::event!(name: $name, target: $target, $crate::Level::WARN, { ?$($k).+ $($field)* })
+);
+(name: $name:expr, target: $target:expr, %$($k:ident).+ $($field:tt)* ) => (
+    $crate::event!(name: $name, target: $target, $crate::Level::WARN, { %$($k).+ $($field)* })
+);
+(name: $name:expr, target: $target:expr, $($arg:tt)+ ) => (
+    $crate::event!(name: $name, target: $target, $crate::Level::WARN, {}, $($arg)+)
+);
+
+// Target / parent.
+(target: $target:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => (
+    $crate::event!(target: $target, parent: $parent, $crate::Level::WARN, { $($field)* }, $($arg)*)
+);
+(target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)* ) => (
+    $crate::event!(target: $target, parent: $parent, $crate::Level::WARN, { $($k).+ $($field)* })
+);
+(target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)* ) => (
+    $crate::event!(target: $target, parent: $parent, $crate::Level::WARN, { ?$($k).+ $($field)* })
+);
+(target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)* ) => (
+    $crate::event!(target: $target, parent: $parent, $crate::Level::WARN, { %$($k).+ $($field)* })
+);
+(target: $target:expr, parent: $parent:expr, $($arg:tt)+ ) => (
+    $crate::event!(target: $target, parent: $parent, $crate::Level::WARN, {}, $($arg)+)
+);
+
+// Name / parent.
+(name: $name:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => (
+    $crate::event!(name: $name, parent: $parent, $crate::Level::WARN, { $($field)* }, $($arg)*)
+);
+(name: $name:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)* ) => (
+    $crate::event!(name: $name, parent: $parent, $crate::Level::WARN, { $($k).+ $($field)* })
+);
+(name: $name:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)* ) => (
+    $crate::event!(name: $name, parent: $parent, $crate::Level::WARN, { ?$($k).+ $($field)* })
+);
+(name: $name:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)* ) => (
+    $crate::event!(name: $name, parent: $parent, $crate::Level::WARN, { %$($k).+ $($field)* })
+);
+(name: $name:expr, parent: $parent:expr, $($arg:tt)+ ) => (
+    $crate::event!(name: $name, parent: $parent, $crate::Level::WARN, {}, $($arg)+)
+);
+
+// Name.
+(name: $name:expr, { $($field:tt)* }, $($arg:tt)* ) => (
+    $crate::event!(name: $name, $crate::Level::WARN, { $($field)* }, $($arg)*)
+);
+(name: $name:expr, $($k:ident).+ $($field:tt)* ) => (
+    $crate::event!(name: $name, $crate::Level::WARN, { $($k).+ $($field)* })
+);
+(name: $name:expr, ?$($k:ident).+ $($field:tt)* ) => (
+    $crate::event!(name: $name, $crate::Level::WARN, { ?$($k).+ $($field)* })
+);
+(name: $name:expr, %$($k:ident).+ $($field:tt)* ) => (
+    $crate::event!(name: $name, $crate::Level::WARN, { %$($k).+ $($field)* })
+);
+(name: $name:expr, $($arg:tt)+ ) => (
+    $crate::event!(name: $name, $crate::Level::WARN, {}, $($arg)+)
+);
+
+// Target.
+(target: $target:expr, { $($field:tt)* }, $($arg:tt)* ) => (
+    $crate::event!(target: $target, $crate::Level::WARN, { $($field)* }, $($arg)*)
+);
+(target: $target:expr, $($k:ident).+ $($field:tt)* ) => (
+    $crate::event!(target: $target, $crate::Level::WARN, { $($k).+ $($field)* })
+);
+(target: $target:expr, ?$($k:ident).+ $($field:tt)* ) => (
+    $crate::event!(target: $target, $crate::Level::WARN, { ?$($k).+ $($field)* })
+);
+(target: $target:expr, %$($k:ident).+ $($field:tt)* ) => (
+    $crate::event!(target: $target, $crate::Level::WARN, { %$($k).+ $($field)* })
+);
+(target: $target:expr, $($arg:tt)+ ) => (
+    $crate::event!(target: $target, $crate::Level::WARN, {}, $($arg)+)
+);
+
+// Parent.
+(parent: $parent:expr, { $($field:tt)+ }, $($arg:tt)+ ) => (
+    $crate::event!(
+        target: module_path!(),
+        parent: $parent,
+        $crate::Level::WARN,
+        { $($field)+ },
+        $($arg)+
+    )
+);
+(parent: $parent:expr, $($k:ident).+ = $($field:tt)*) => (
+    $crate::event!(
+        target: module_path!(),
+        parent: $parent,
+        $crate::Level::WARN,
+        { $($k).+ = $($field)*}
+    )
+);
+(parent: $parent:expr, ?$($k:ident).+ = $($field:tt)*) => (
+    $crate::event!(
+        target: module_path!(),
+        parent: $parent,
+        $crate::Level::WARN,
+        { ?$($k).+ = $($field)*}
+    )
+);
+(parent: $parent:expr, %$($k:ident).+ = $($field:tt)*) => (
+    $crate::event!(
+        target: module_path!(),
+        parent: $parent,
+        $crate::Level::WARN,
+        { %$($k).+ = $($field)*}
+    )
+);
+(parent: $parent:expr, $($k:ident).+, $($field:tt)*) => (
+    $crate::event!(
+        target: module_path!(),
+        parent: $parent,
+        $crate::Level::WARN,
+        { $($k).+, $($field)*}
+    )
+);
+(parent: $parent:expr, ?$($k:ident).+, $($field:tt)*) => (
+    $crate::event!(
+        target: module_path!(),
+        parent: $parent,
+        $crate::Level::WARN,
+        { ?$($k).+, $($field)*}
+    )
+);
+(parent: $parent:expr, %$($k:ident).+, $($field:tt)*) => (
+    $crate::event!(
+        target: module_path!(),
+        parent: $parent,
+        $crate::Level::WARN,
+        { %$($k).+, $($field)*}
+    )
+);
+(parent: $parent:expr, $($arg:tt)+) => (
+    $crate::event!(
+        target: module_path!(),
+        parent: $parent,
+        $crate::Level::WARN,
+        {},
+        $($arg)+
+    )
+);
+
+// ...
+({ $($field:tt)+ }, $($arg:tt)+ ) => (
+    $crate::event!(
+        target: module_path!(),
+        $crate::Level::WARN,
+        { $($field)+ },
+        $($arg)+
+    )
+);
+($($k:ident).+ = $($field:tt)*) => (
+    $crate::event!(
+        target: module_path!(),
+        $crate::Level::WARN,
+        { $($k).+ = $($field)*}
+    )
+);
+(?$($k:ident).+ = $($field:tt)*) => (
+    $crate::event!(
+        target: module_path!(),
+        $crate::Level::WARN,
+        { ?$($k).+ = $($field)*}
+    )
+);
+(%$($k:ident).+ = $($field:tt)*) => (
+    $crate::event!(
+        target: module_path!(),
+        $crate::Level::WARN,
+        { %$($k).+ = $($field)*}
+    )
+);
+($($k:ident).+, $($field:tt)*) => (
+    $crate::event!(
+        target: module_path!(),
+        $crate::Level::WARN,
+        { $($k).+, $($field)*}
+    )
+);
+(?$($k:ident).+, $($field:tt)*) => (
+    $crate::event!(
+        target: module_path!(),
+        $crate::Level::WARN,
+        { ?$($k).+, $($field)*}
+    )
+);
+(%$($k:ident).+, $($field:tt)*) => (
+    $crate::event!(
+        target: module_path!(),
+        $crate::Level::WARN,
+        { %$($k).+, $($field)*}
+    )
+);
+(?$($k:ident).+) => (
+    $crate::event!(
+        target: module_path!(),
+        $crate::Level::WARN,
+        { ?$($k).+ }
+    )
+);
+(%$($k:ident).+) => (
+    $crate::event!(
+        target: module_path!(),
+        $crate::Level::WARN,
+        { %$($k).+ }
+    )
+);
+($($k:ident).+) => (
+    $crate::event!(
+        target: module_path!(),
+        $crate::Level::WARN,
+        { $($k).+ }
+    )
+);
+($($arg:tt)+) => (
+    $crate::event!(
+        target: module_path!(),
+        $crate::Level::WARN,
+        $($arg)+
+    )
+);
+}
+
+#[macro_export]
+macro_rules! error {
+// Name / target / parent.
+(name: $name:expr, target: $target:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => (
+    $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::ERROR, { $($field)* }, $($arg)*)
+);
+(name: $name:expr, target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)* ) => (
+    $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::ERROR, { $($k).+ $($field)* })
+);
+(name: $name:expr, target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)* ) => (
+    $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::ERROR, { ?$($k).+ $($field)* })
+);
+(name: $name:expr, target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)* ) => (
+    $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::ERROR, { %$($k).+ $($field)* })
+);
+(name: $name:expr, target: $target:expr, parent: $parent:expr, $($arg:tt)+ ) => (
+    $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::ERROR, {}, $($arg)+)
+);
+
+// Name / target.
+(name: $name:expr, target: $target:expr, { $($field:tt)* }, $($arg:tt)* ) => (
+    $crate::event!(name: $name, target: $target, $crate::Level::ERROR, { $($field)* }, $($arg)*)
+);
+(name: $name:expr, target: $target:expr, $($k:ident).+ $($field:tt)* ) => (
+    $crate::event!(name: $name, target: $target, $crate::Level::ERROR, { $($k).+ $($field)* })
+);
+(name: $name:expr, target: $target:expr, ?$($k:ident).+ $($field:tt)* ) => (
+    $crate::event!(name: $name, target: $target, $crate::Level::ERROR, { ?$($k).+ $($field)* })
+);
+(name: $name:expr, target: $target:expr, %$($k:ident).+ $($field:tt)* ) => (
+    $crate::event!(name: $name, target: $target, $crate::Level::ERROR, { %$($k).+ $($field)* })
+);
+(name: $name:expr, target: $target:expr, $($arg:tt)+ ) => (
+    $crate::event!(name: $name, target: $target, $crate::Level::ERROR, {}, $($arg)+)
+);
+
+// Target / parent.
+(target: $target:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => (
+    $crate::event!(target: $target, parent: $parent, $crate::Level::ERROR, { $($field)* }, $($arg)*)
+);
+(target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)* ) => (
+    $crate::event!(target: $target, parent: $parent, $crate::Level::ERROR, { $($k).+ $($field)* })
+);
+(target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)* ) => (
+    $crate::event!(target: $target, parent: $parent, $crate::Level::ERROR, { ?$($k).+ $($field)* })
+);
+(target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)* ) => (
+    $crate::event!(target: $target, parent: $parent, $crate::Level::ERROR, { %$($k).+ $($field)* })
+);
+(target: $target:expr, parent: $parent:expr, $($arg:tt)+ ) => (
+    $crate::event!(target: $target, parent: $parent, $crate::Level::ERROR, {}, $($arg)+)
+);
+
+// Name / parent.
+(name: $name:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => (
+    $crate::event!(name: $name, parent: $parent, $crate::Level::ERROR, { $($field)* }, $($arg)*)
+);
+(name: $name:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)* ) => (
+    $crate::event!(name: $name, parent: $parent, $crate::Level::ERROR, { $($k).+ $($field)* })
+);
+(name: $name:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)* ) => (
+    $crate::event!(name: $name, parent: $parent, $crate::Level::ERROR, { ?$($k).+ $($field)* })
+);
+(name: $name:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)* ) => (
+    $crate::event!(name: $name, parent: $parent, $crate::Level::ERROR, { %$($k).+ $($field)* })
+);
+(name: $name:expr, parent: $parent:expr, $($arg:tt)+ ) => (
+    $crate::event!(name: $name, parent: $parent, $crate::Level::ERROR, {}, $($arg)+)
+);
+
+// Name.
+(name: $name:expr, { $($field:tt)* }, $($arg:tt)* ) => (
+    $crate::event!(name: $name, $crate::Level::ERROR, { $($field)* }, $($arg)*)
+);
+(name: $name:expr, $($k:ident).+ $($field:tt)* ) => (
+    $crate::event!(name: $name, $crate::Level::ERROR, { $($k).+ $($field)* })
+);
+(name: $name:expr, ?$($k:ident).+ $($field:tt)* ) => (
+    $crate::event!(name: $name, $crate::Level::ERROR, { ?$($k).+ $($field)* })
+);
+(name: $name:expr, %$($k:ident).+ $($field:tt)* ) => (
+    $crate::event!(name: $name, $crate::Level::ERROR, { %$($k).+ $($field)* })
+);
+(name: $name:expr, $($arg:tt)+ ) => (
+    $crate::event!(name: $name, $crate::Level::ERROR, {}, $($arg)+)
+);
+
+// Target.
+(target: $target:expr, { $($field:tt)* }, $($arg:tt)* ) => (
+    $crate::event!(target: $target, $crate::Level::ERROR, { $($field)* }, $($arg)*)
+);
+(target: $target:expr, $($k:ident).+ $($field:tt)* ) => (
+    $crate::event!(target: $target, $crate::Level::ERROR, { $($k).+ $($field)* })
+);
+(target: $target:expr, ?$($k:ident).+ $($field:tt)* ) => (
+    $crate::event!(target: $target, $crate::Level::ERROR, { ?$($k).+ $($field)* })
+);
+(target: $target:expr, %$($k:ident).+ $($field:tt)* ) => (
+    $crate::event!(target: $target, $crate::Level::ERROR, { %$($k).+ $($field)* })
+);
+(target: $target:expr, $($arg:tt)+ ) => (
+    $crate::event!(target: $target, $crate::Level::ERROR, {}, $($arg)+)
+);
+
+// Parent.
+(parent: $parent:expr, { $($field:tt)+ }, $($arg:tt)+ ) => (
+    $crate::event!(
+        target: module_path!(),
+        parent: $parent,
+        $crate::Level::ERROR,
+        { $($field)+ },
+        $($arg)+
+    )
+);
+(parent: $parent:expr, $($k:ident).+ = $($field:tt)*) => (
+    $crate::event!(
+        target: module_path!(),
+        parent: $parent,
+        $crate::Level::ERROR,
+        { $($k).+ = $($field)*}
+    )
+);
+(parent: $parent:expr, ?$($k:ident).+ = $($field:tt)*) => (
+    $crate::event!(
+        target: module_path!(),
+        parent: $parent,
+        $crate::Level::ERROR,
+        { ?$($k).+ = $($field)*}
+    )
+);
+(parent: $parent:expr, %$($k:ident).+ = $($field:tt)*) => (
+    $crate::event!(
+        target: module_path!(),
+        parent: $parent,
+        $crate::Level::ERROR,
+        { %$($k).+ = $($field)*}
+    )
+);
+(parent: $parent:expr, $($k:ident).+, $($field:tt)*) => (
+    $crate::event!(
+        target: module_path!(),
+        parent: $parent,
+        $crate::Level::ERROR,
+        { $($k).+, $($field)*}
+    )
+);
+(parent: $parent:expr, ?$($k:ident).+, $($field:tt)*) => (
+    $crate::event!(
+        target: module_path!(),
+        parent: $parent,
+        $crate::Level::ERROR,
+        { ?$($k).+, $($field)*}
+    )
+);
+(parent: $parent:expr, %$($k:ident).+, $($field:tt)*) => (
+    $crate::event!(
+        target: module_path!(),
+        parent: $parent,
+        $crate::Level::ERROR,
+        { %$($k).+, $($field)*}
+    )
+);
+(parent: $parent:expr, $($arg:tt)+) => (
+    $crate::event!(
+        target: module_path!(),
+        parent: $parent,
+        $crate::Level::ERROR,
+        {},
+        $($arg)+
+    )
+);
+
+// ...
+({ $($field:tt)+ }, $($arg:tt)+ ) => (
+    $crate::event!(
+        target: module_path!(),
+        $crate::Level::ERROR,
+        { $($field)+ },
+        $($arg)+
+    )
+);
+($($k:ident).+ = $($field:tt)*) => (
+    $crate::event!(
+        target: module_path!(),
+        $crate::Level::ERROR,
+        { $($k).+ = $($field)*}
+    )
+);
+(?$($k:ident).+ = $($field:tt)*) => (
+    $crate::event!(
+        target: module_path!(),
+        $crate::Level::ERROR,
+        { ?$($k).+ = $($field)*}
+    )
+);
+(%$($k:ident).+ = $($field:tt)*) => (
+    $crate::event!(
+        target: module_path!(),
+        $crate::Level::ERROR,
+        { %$($k).+ = $($field)*}
+    )
+);
+($($k:ident).+, $($field:tt)*) => (
+    $crate::event!(
+        target: module_path!(),
+        $crate::Level::ERROR,
+        { $($k).+, $($field)*}
+    )
+);
+(?$($k:ident).+, $($field:tt)*) => (
+    $crate::event!(
+        target: module_path!(),
+        $crate::Level::ERROR,
+        { ?$($k).+, $($field)*}
+    )
+);
+(%$($k:ident).+, $($field:tt)*) => (
+    $crate::event!(
+        target: module_path!(),
+        $crate::Level::ERROR,
+        { %$($k).+, $($field)*}
+    )
+);
+(?$($k:ident).+) => (
+    $crate::event!(
+        target: module_path!(),
+        $crate::Level::ERROR,
+        { ?$($k).+ }
+    )
+);
+(%$($k:ident).+) => (
+    $crate::event!(
+        target: module_path!(),
+        $crate::Level::ERROR,
+        { %$($k).+ }
+    )
+);
+($($k:ident).+) => (
+    $crate::event!(
+        target: module_path!(),
+        $crate::Level::ERROR,
+        { $($k).+ }
+    )
+);
+($($arg:tt)+) => (
+    $crate::event!(
+        target: module_path!(),
+        $crate::Level::ERROR,
+        $($arg)+
+    )
+);
+}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! callsite {
+(name: $name:expr, kind: $kind:expr, fields: $($fields:tt)*) => {{
+    $crate::callsite! {
+        name: $name,
+        kind: $kind,
+        target: module_path!(),
+        level: $crate::Level::TRACE,
+        fields: $($fields)*
+    }
+}};
+(
+    name: $name:expr,
+    kind: $kind:expr,
+    level: $lvl:expr,
+    fields: $($fields:tt)*
+) => {{
+    $crate::callsite! {
+        name: $name,
+        kind: $kind,
+        target: module_path!(),
+        level: $lvl,
+        fields: $($fields)*
+    }
+}};
+(
+    name: $name:expr,
+    kind: $kind:expr,
+    target: $target:expr,
+    level: $lvl:expr,
+    fields: $($fields:tt)*
+) => {{
+    static META: $crate::Metadata<'static> = {
+        $crate::metadata! {
+            name: $name,
+            target: $target,
+            level: $lvl,
+            fields: $crate::fieldset!( $($fields)* ),
+            callsite: &__CALLSITE,
+            kind: $kind,
+        }
+    };
+    static __CALLSITE: $crate::callsite::DefaultCallsite = $crate::callsite::DefaultCallsite::new(&META);
+    __CALLSITE.register();
+    &__CALLSITE
+}};
+}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! callsite2 {
+(name: $name:expr, kind: $kind:expr, fields: $($fields:tt)*) => {{
+    $crate::callsite2! {
+        name: $name,
+        kind: $kind,
+        target: module_path!(),
+        level: $crate::Level::TRACE,
+        fields: $($fields)*
+    }
+}};
+(
+    name: $name:expr,
+    kind: $kind:expr,
+    level: $lvl:expr,
+    fields: $($fields:tt)*
+) => {{
+    $crate::callsite2! {
+        name: $name,
+        kind: $kind,
+        target: module_path!(),
+        level: $lvl,
+        fields: $($fields)*
+    }
+}};
+(
+    name: $name:expr,
+    kind: $kind:expr,
+    target: $target:expr,
+    level: $lvl:expr,
+    fields: $($fields:tt)*
+) => {{
+    static META: $crate::Metadata<'static> = {
+        $crate::metadata! {
+            name: $name,
+            target: $target,
+            level: $lvl,
+            fields: $crate::fieldset!( $($fields)* ),
+            callsite: &__CALLSITE,
+            kind: $kind,
+        }
+    };
+    $crate::callsite::DefaultCallsite::new(&META)
+}};
+}
+
+#[macro_export]
+#[doc(hidden)]
+macro_rules! level_enabled {
+($lvl:expr) => {
+    $lvl <= $crate::level_filters::STATIC_MAX_LEVEL
+        && $lvl <= $crate::level_filters::LevelFilter::current()
+};
+}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! valueset {
+
+// === base case ===
+(@ { $(,)* $($val:expr),* $(,)* }, $next:expr $(,)*) => {
+    &[ $($val),* ]
+};
+
+(@ { $(,)* $($out:expr),* }, $next:expr, $($k:ident).+ = ?$val:expr, $($rest:tt)*) => {
+    $crate::valueset!(
+        @ { $($out),*, (&$next, $crate::__macro_support::Option::Some(&debug(&$val) as &dyn Value)) },
+        $next,
+        $($rest)*
+    )
+};
+(@ { $(,)* $($out:expr),* }, $next:expr, $($k:ident).+ = %$val:expr, $($rest:tt)*) => {
+    $crate::valueset!(
+        @ { $($out),*, (&$next, $crate::__macro_support::Option::Some(&display(&$val) as &dyn Value)) },
+        $next,
+        $($rest)*
+    )
+};
+(@ { $(,)* $($out:expr),* }, $next:expr, $($k:ident).+ = $val:expr, $($rest:tt)*) => {
+    $crate::valueset!(
+        @ { $($out),*, (&$next, $crate::__macro_support::Option::Some(&$val as &dyn Value)) },
+        $next,
+        $($rest)*
+    )
+};
+(@ { $(,)* $($out:expr),* }, $next:expr, $($k:ident).+, $($rest:tt)*) => {
+    $crate::valueset!(
+        @ { $($out),*, (&$next, $crate::__macro_support::Option::Some(&$($k).+ as &dyn Value)) },
+        $next,
+        $($rest)*
+    )
+};
+(@ { $(,)* $($out:expr),* }, $next:expr, ?$($k:ident).+, $($rest:tt)*) => {
+    $crate::valueset!(
+        @ { $($out),*, (&$next, $crate::__macro_support::Option::Some(&debug(&$($k).+) as &dyn Value)) },
+        $next,
+        $($rest)*
+    )
+};
+(@ { $(,)* $($out:expr),* }, $next:expr, %$($k:ident).+, $($rest:tt)*) => {
+    $crate::valueset!(
+        @ { $($out),*, (&$next, $crate::__macro_support::Option::Some(&display(&$($k).+) as &dyn Value)) },
+        $next,
+        $($rest)*
+    )
+};
+(@ { $(,)* $($out:expr),* }, $next:expr, $($k:ident).+ = ?$val:expr) => {
+    $crate::valueset!(
+        @ { $($out),*, (&$next, $crate::__macro_support::Option::Some(&debug(&$val) as &dyn Value)) },
+        $next,
+    )
+};
+(@ { $(,)* $($out:expr),* }, $next:expr, $($k:ident).+ = %$val:expr) => {
+    $crate::valueset!(
+        @ { $($out),*, (&$next, $crate::__macro_support::Option::Some(&display(&$val) as &dyn Value)) },
+        $next,
+    )
+};
+(@ { $(,)* $($out:expr),* }, $next:expr, $($k:ident).+ = $val:expr) => {
+    $crate::valueset!(
+        @ { $($out),*, (&$next, $crate::__macro_support::Option::Some(&$val as &dyn Value)) },
+        $next,
+    )
+};
+(@ { $(,)* $($out:expr),* }, $next:expr, $($k:ident).+) => {
+    $crate::valueset!(
+        @ { $($out),*, (&$next, $crate::__macro_support::Option::Some(&$($k).+ as &dyn Value)) },
+        $next,
+    )
+};
+(@ { $(,)* $($out:expr),* }, $next:expr, ?$($k:ident).+) => {
+    $crate::valueset!(
+        @ { $($out),*, (&$next, $crate::__macro_support::Option::Some(&debug(&$($k).+) as &dyn Value)) },
+        $next,
+    )
+};
+(@ { $(,)* $($out:expr),* }, $next:expr, %$($k:ident).+) => {
+    $crate::valueset!(
+        @ { $($out),*, (&$next, $crate::__macro_support::Option::Some(&display(&$($k).+) as &dyn Value)) },
+        $next,
+    )
+};
+
+// Handle literal names
+(@ { $(,)* $($out:expr),* }, $next:expr, $k:literal = ?$val:expr, $($rest:tt)*) => {
+    $crate::valueset!(
+        @ { $($out),*, (&$next, $crate::__macro_support::Option::Some(&debug(&$val) as &dyn Value)) },
+        $next,
+        $($rest)*
+    )
+};
+(@ { $(,)* $($out:expr),* }, $next:expr, $k:literal = %$val:expr, $($rest:tt)*) => {
+    $crate::valueset!(
+        @ { $($out),*, (&$next, $crate::__macro_support::Option::Some(&display(&$val) as &dyn Value)) },
+        $next,
+        $($rest)*
+    )
+};
+(@ { $(,)* $($out:expr),* }, $next:expr, $k:literal = $val:expr, $($rest:tt)*) => {
+    $crate::valueset!(
+        @ { $($out),*, (&$next, $crate::__macro_support::Option::Some(&$val as &dyn Value)) },
+        $next,
+        $($rest)*
+    )
+};
+(@ { $(,)* $($out:expr),* }, $next:expr, $k:literal = ?$val:expr) => {
+    $crate::valueset!(
+        @ { $($out),*, (&$next, $crate::__macro_support::Option::Some(&debug(&$val) as &dyn Value)) },
+        $next,
+    )
+};
+(@ { $(,)* $($out:expr),* }, $next:expr, $k:literal = %$val:expr) => {
+    $crate::valueset!(
+        @ { $($out),*, (&$next, $crate::__macro_support::Option::Some(&display(&$val) as &dyn Value)) },
+        $next,
+    )
+};
+(@ { $(,)* $($out:expr),* }, $next:expr, $k:literal = $val:expr) => {
+    $crate::valueset!(
+        @ { $($out),*, (&$next, $crate::__macro_support::Option::Some(&$val as &dyn Value)) },
+        $next,
+    )
+};
+
+// Handle constant names
+(@ { $(,)* $($out:expr),* }, $next:expr, { $k:expr } = ?$val:expr, $($rest:tt)*) => {
+    $crate::valueset!(
+        @ { $($out),*, (&$next, Some(&debug(&$val) as &dyn Value)) },
+        $next,
+        $($rest)*
+    )
+};
+(@ { $(,)* $($out:expr),* }, $next:expr, { $k:expr } = %$val:expr, $($rest:tt)*) => {
+    $crate::valueset!(
+        @ { $($out),*, (&$next, Some(&display(&$val) as &dyn Value)) },
+        $next,
+        $($rest)*
+    )
+};
+(@ { $(,)* $($out:expr),* }, $next:expr, { $k:expr } = $val:expr, $($rest:tt)*) => {
+    $crate::valueset!(
+        @ { $($out),*, (&$next, Some(&$val as &dyn Value)) },
+        $next,
+        $($rest)*
+    )
+};
+(@ { $(,)* $($out:expr),* }, $next:expr, { $k:expr } = ?$val:expr) => {
+    $crate::valueset!(
+        @ { $($out),*, (&$next, Some(&debug(&$val) as &dyn Value)) },
+        $next,
+    )
+};
+(@ { $(,)* $($out:expr),* }, $next:expr, { $k:expr } = %$val:expr) => {
+    $crate::valueset!(
+        @ { $($out),*, (&$next, Some(&display(&$val) as &dyn Value)) },
+        $next,
+    )
+};
+(@ { $(,)* $($out:expr),* }, $next:expr, { $k:expr } = $val:expr) => {
+    $crate::valueset!(
+        @ { $($out),*, (&$next, Some(&$val as &dyn Value)) },
+        $next,
+    )
+};
+
+(@ { $(,)* $($out:expr),* }, $next:expr, $($rest:tt)+) => {
+    $crate::valueset!(@ { (&$next, $crate::__macro_support::Option::Some(&$crate::__macro_support::format_args!($($rest)+) as &dyn Value)), $($out),* }, $next, )
+};
+
+($fields:expr, $($kvs:tt)+) => {
+    {
+        #[allow(unused_imports)]
+        use $crate::field::{debug, display, Value};
+        let mut iter = $fields.iter();
+        $fields.value_set($crate::valueset!(
+            @ { },
+            $crate::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
+            $($kvs)+
+        ))
+    }
+};
+($fields:expr,) => {
+    {
+        $fields.value_set(&[])
+    }
+};
+}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! fieldset {
+(@ { $(,)* $($out:expr),* $(,)* } $(,)*) => {
+    &[ $($out),* ]
+};
+
+(@ { $(,)* $($out:expr),* } $($k:ident).+ = ?$val:expr, $($rest:tt)*) => {
+    $crate::fieldset!(@ { $($out),*, $crate::__tracing_stringify!($($k).+) } $($rest)*)
+};
+(@ { $(,)* $($out:expr),* } $($k:ident).+ = %$val:expr, $($rest:tt)*) => {
+    $crate::fieldset!(@ { $($out),*, $crate::__tracing_stringify!($($k).+) } $($rest)*)
+};
+(@ { $(,)* $($out:expr),* } $($k:ident).+ = $val:expr, $($rest:tt)*) => {
+    $crate::fieldset!(@ { $($out),*, $crate::__tracing_stringify!($($k).+) } $($rest)*)
+};
+(@ { $(,)* $($out:expr),* } ?$($k:ident).+, $($rest:tt)*) => {
+    $crate::fieldset!(@ { $($out),*, $crate::__tracing_stringify!($($k).+) } $($rest)*)
+};
+(@ { $(,)* $($out:expr),* } %$($k:ident).+, $($rest:tt)*) => {
+    $crate::fieldset!(@ { $($out),*, $crate::__tracing_stringify!($($k).+) } $($rest)*)
+};
+(@ { $(,)* $($out:expr),* } $($k:ident).+, $($rest:tt)*) => {
+    $crate::fieldset!(@ { $($out),*, $crate::__tracing_stringify!($($k).+) } $($rest)*)
+};
+
+// Handle literal names
+(@ { $(,)* $($out:expr),* } $k:literal = ?$val:expr, $($rest:tt)*) => {
+    $crate::fieldset!(@ { $($out),*, $k } $($rest)*)
+};
+(@ { $(,)* $($out:expr),* } $k:literal = %$val:expr, $($rest:tt)*) => {
+    $crate::fieldset!(@ { $($out),*, $k } $($rest)*)
+};
+(@ { $(,)* $($out:expr),* } $k:literal = $val:expr, $($rest:tt)*) => {
+    $crate::fieldset!(@ { $($out),*, $k } $($rest)*)
+};
+
+// Handle constant names
+(@ { $(,)* $($out:expr),* } { $k:expr } = ?$val:expr, $($rest:tt)*) => {
+    $crate::fieldset!(@ { $($out),*, $k } $($rest)*)
+};
+(@ { $(,)* $($out:expr),* } { $k:expr } = %$val:expr, $($rest:tt)*) => {
+    $crate::fieldset!(@ { $($out),*, $k } $($rest)*)
+};
+(@ { $(,)* $($out:expr),* } { $k:expr } = $val:expr, $($rest:tt)*) => {
+    $crate::fieldset!(@ { $($out),*, $k } $($rest)*)
+};
+
+(@ { $(,)* $($out:expr),* } $($rest:tt)+) => {
+    $crate::fieldset!(@ { "message", $($out),*, })
+};
+
+($($args:tt)*) => {
+    $crate::fieldset!(@ { } $($args)*,)
+};
+
+}
+
+#[cfg(feature = "log")]
+#[doc(hidden)]
+#[macro_export]
+macro_rules! level_to_log {
+($level:expr) => {
+    match $level {
+        $crate::Level::ERROR => $crate::log::Level::Error,
+        $crate::Level::WARN => $crate::log::Level::Warn,
+        $crate::Level::INFO => $crate::log::Level::Info,
+        $crate::Level::DEBUG => $crate::log::Level::Debug,
+        _ => $crate::log::Level::Trace,
+    }
+};
+}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __tracing_stringify {
+($($t:tt)*) => {
+    stringify!($($t)*)
+};
+}
+
+#[cfg(not(feature = "log"))]
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __tracing_log {
+($level:expr, $callsite:expr, $value_set:expr) => {};
+}
+
+#[cfg(feature = "log")]
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __tracing_log {
+($level:expr, $callsite:expr, $value_set:expr) => {
+    $crate::if_log_enabled! { $level, {
+        use $crate::log;
+        let level = $crate::level_to_log!($level);
+        if level <= log::max_level() {
+            let meta = $callsite.metadata();
+            let log_meta = log::Metadata::builder()
+                .level(level)
+                .target(meta.target())
+                .build();
+            let logger = log::logger();
+            if logger.enabled(&log_meta) {
+                $crate::__macro_support::__tracing_log(meta, logger, log_meta, $value_set)
+            }
+        }
+    }}
+};
+}
+
+#[cfg(not(feature = "log"))]
+#[doc(hidden)]
+#[macro_export]
+macro_rules! if_log_enabled {
+($lvl:expr, $e:expr;) => {
+    $crate::if_log_enabled! { $lvl, $e }
+};
+($lvl:expr, $if_log:block) => {
+    $crate::if_log_enabled! { $lvl, $if_log else {} }
+};
+($lvl:expr, $if_log:block else $else_block:block) => {
+    $else_block
+};
+}
+
+#[cfg(all(feature = "log", not(feature = "log-always")))]
+#[doc(hidden)]
+#[macro_export]
+macro_rules! if_log_enabled {
+($lvl:expr, $e:expr;) => {
+    $crate::if_log_enabled! { $lvl, $e }
+};
+($lvl:expr, $if_log:block) => {
+    $crate::if_log_enabled! { $lvl, $if_log else {} }
+};
+($lvl:expr, $if_log:block else $else_block:block) => {
+    if $crate::level_to_log!($lvl) <= $crate::log::STATIC_MAX_LEVEL {
+        if !$crate::dispatcher::has_been_set() {
+            $if_log
+        } else {
+            $else_block
+        }
+    } else {
+        $else_block
+    }
+};
+}
+
+#[cfg(all(feature = "log", feature = "log-always"))]
+#[doc(hidden)]
+#[macro_export]
+macro_rules! if_log_enabled {
+($lvl:expr, $e:expr;) => {
+    $crate::if_log_enabled! { $lvl, $e }
+};
+($lvl:expr, $if_log:block) => {
+    $crate::if_log_enabled! { $lvl, $if_log else {} }
+};
+($lvl:expr, $if_log:block else $else_block:block) => {
+    if $crate::level_to_log!($lvl) <= $crate::log::STATIC_MAX_LEVEL {
+        #[allow(unused_braces)]
+        $if_log
+    } else {
+        $else_block
+    }
+};
+}
+
+//- /lib.rs crate:ra_test_fixture deps:tracing
+fn foo() {
+tracing::error!();
+}
+    "#,
+        &["E0432", "inactive-code", "unresolved-macro-call", "syntax-error", "macro-error"],
+    );
+}
diff --git a/src/tools/rust-analyzer/crates/ide/src/annotations.rs b/src/tools/rust-analyzer/crates/ide/src/annotations.rs
index 6a4e5ba290e..18f866eb9fc 100644
--- a/src/tools/rust-analyzer/crates/ide/src/annotations.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/annotations.rs
@@ -220,7 +220,11 @@ mod tests {
         location: AnnotationLocation::AboveName,
     };
 
-    fn check_with_config(ra_fixture: &str, expect: Expect, config: &AnnotationConfig) {
+    fn check_with_config(
+        #[rust_analyzer::rust_fixture] ra_fixture: &str,
+        expect: Expect,
+        config: &AnnotationConfig,
+    ) {
         let (analysis, file_id) = fixture::file(ra_fixture);
 
         let annotations: Vec<Annotation> = analysis
@@ -233,7 +237,7 @@ mod tests {
         expect.assert_debug_eq(&annotations);
     }
 
-    fn check(ra_fixture: &str, expect: Expect) {
+    fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
         check_with_config(ra_fixture, expect, &DEFAULT_CONFIG);
     }
 
diff --git a/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs b/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs
index 8066894cd83..afd6f740c42 100644
--- a/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs
@@ -173,7 +173,7 @@ mod tests {
 
     fn check_hierarchy(
         exclude_tests: bool,
-        ra_fixture: &str,
+        #[rust_analyzer::rust_fixture] ra_fixture: &str,
         expected_nav: Expect,
         expected_incoming: Expect,
         expected_outgoing: Expect,
diff --git a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs
index 72fcac54177..bc9843f3f35 100644
--- a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs
@@ -199,6 +199,7 @@ pub(crate) fn resolve_doc_path_for_def(
 ) -> Option<Definition> {
     match def {
         Definition::Module(it) => it.resolve_doc_path(db, link, ns),
+        Definition::Crate(it) => it.resolve_doc_path(db, link, ns),
         Definition::Function(it) => it.resolve_doc_path(db, link, ns),
         Definition::Adt(it) => it.resolve_doc_path(db, link, ns),
         Definition::Variant(it) => it.resolve_doc_path(db, link, ns),
@@ -594,6 +595,7 @@ fn filename_and_frag_for_def(
             Adt::Enum(e) => format!("enum.{}.html", e.name(db).unescaped().display(db.upcast())),
             Adt::Union(u) => format!("union.{}.html", u.name(db).unescaped().display(db.upcast())),
         },
+        Definition::Crate(_) => String::from("index.html"),
         Definition::Module(m) => match m.name(db) {
             // `#[doc(keyword = "...")]` is internal used only by rust compiler
             Some(name) => {
diff --git a/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs b/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs
index fe91c81a615..d7291c4b9f3 100644
--- a/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs
@@ -16,7 +16,7 @@ use crate::{
 };
 
 fn check_external_docs(
-    ra_fixture: &str,
+    #[rust_analyzer::rust_fixture] ra_fixture: &str,
     target_dir: Option<&str>,
     expect_web_url: Option<Expect>,
     expect_local_url: Option<Expect>,
@@ -41,7 +41,7 @@ fn check_external_docs(
     }
 }
 
-fn check_rewrite(ra_fixture: &str, expect: Expect) {
+fn check_rewrite(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
     let (analysis, position) = fixture::position(ra_fixture);
     let sema = &Semantics::new(&*analysis.db);
     let (cursor_def, docs) = def_under_cursor(sema, &position);
@@ -49,7 +49,7 @@ fn check_rewrite(ra_fixture: &str, expect: Expect) {
     expect.assert_eq(&res)
 }
 
-fn check_doc_links(ra_fixture: &str) {
+fn check_doc_links(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
     let key_fn = |&(FileRange { file_id, range }, _): &_| (file_id, range.start());
 
     let (analysis, position, mut expected) = fixture::annotations(ra_fixture);
diff --git a/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs b/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs
index e028c5ff0cb..0ad894427b2 100644
--- a/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs
@@ -296,7 +296,7 @@ mod tests {
     use crate::fixture;
 
     #[track_caller]
-    fn check(ra_fixture: &str, expect: Expect) {
+    fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
         let (analysis, pos) = fixture::position(ra_fixture);
         let expansion = analysis.expand_macro(pos).unwrap().unwrap();
         let actual = format!("{}\n{}", expansion.name, expansion.expansion);
diff --git a/src/tools/rust-analyzer/crates/ide/src/file_structure.rs b/src/tools/rust-analyzer/crates/ide/src/file_structure.rs
index 5ef65c209ca..50977ee840c 100644
--- a/src/tools/rust-analyzer/crates/ide/src/file_structure.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/file_structure.rs
@@ -257,7 +257,7 @@ mod tests {
 
     use super::*;
 
-    fn check(ra_fixture: &str, expect: Expect) {
+    fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
         let file = SourceFile::parse(ra_fixture, span::Edition::CURRENT).ok().unwrap();
         let structure = file_structure(&file);
         expect.assert_debug_eq(&structure)
diff --git a/src/tools/rust-analyzer/crates/ide/src/fixture.rs b/src/tools/rust-analyzer/crates/ide/src/fixture.rs
index b16511072bd..a0612f48d37 100644
--- a/src/tools/rust-analyzer/crates/ide/src/fixture.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/fixture.rs
@@ -5,7 +5,7 @@ use test_utils::{extract_annotations, RangeOrOffset};
 use crate::{Analysis, AnalysisHost, FileId, FilePosition, FileRange};
 
 /// Creates analysis for a single file.
-pub(crate) fn file(ra_fixture: &str) -> (Analysis, FileId) {
+pub(crate) fn file(#[rust_analyzer::rust_fixture] ra_fixture: &str) -> (Analysis, FileId) {
     let mut host = AnalysisHost::default();
     let change_fixture = ChangeFixture::parse(ra_fixture);
     host.db.enable_proc_attr_macros();
@@ -14,7 +14,9 @@ pub(crate) fn file(ra_fixture: &str) -> (Analysis, FileId) {
 }
 
 /// Creates analysis from a multi-file fixture, returns positions marked with $0.
-pub(crate) fn position(ra_fixture: &str) -> (Analysis, FilePosition) {
+pub(crate) fn position(
+    #[rust_analyzer::rust_fixture] ra_fixture: &str,
+) -> (Analysis, FilePosition) {
     let mut host = AnalysisHost::default();
     let change_fixture = ChangeFixture::parse(ra_fixture);
     host.db.enable_proc_attr_macros();
@@ -25,7 +27,7 @@ pub(crate) fn position(ra_fixture: &str) -> (Analysis, FilePosition) {
 }
 
 /// Creates analysis for a single file, returns range marked with a pair of $0.
-pub(crate) fn range(ra_fixture: &str) -> (Analysis, FileRange) {
+pub(crate) fn range(#[rust_analyzer::rust_fixture] ra_fixture: &str) -> (Analysis, FileRange) {
     let mut host = AnalysisHost::default();
     let change_fixture = ChangeFixture::parse(ra_fixture);
     host.db.enable_proc_attr_macros();
@@ -36,7 +38,9 @@ pub(crate) fn range(ra_fixture: &str) -> (Analysis, FileRange) {
 }
 
 /// Creates analysis for a single file, returns range marked with a pair of $0 or a position marked with $0.
-pub(crate) fn range_or_position(ra_fixture: &str) -> (Analysis, FileId, RangeOrOffset) {
+pub(crate) fn range_or_position(
+    #[rust_analyzer::rust_fixture] ra_fixture: &str,
+) -> (Analysis, FileId, RangeOrOffset) {
     let mut host = AnalysisHost::default();
     let change_fixture = ChangeFixture::parse(ra_fixture);
     host.db.enable_proc_attr_macros();
@@ -46,7 +50,9 @@ pub(crate) fn range_or_position(ra_fixture: &str) -> (Analysis, FileId, RangeOrO
 }
 
 /// Creates analysis from a multi-file fixture, returns positions marked with $0.
-pub(crate) fn annotations(ra_fixture: &str) -> (Analysis, FilePosition, Vec<(FileRange, String)>) {
+pub(crate) fn annotations(
+    #[rust_analyzer::rust_fixture] ra_fixture: &str,
+) -> (Analysis, FilePosition, Vec<(FileRange, String)>) {
     let mut host = AnalysisHost::default();
     let change_fixture = ChangeFixture::parse(ra_fixture);
     host.db.enable_proc_attr_macros();
@@ -69,7 +75,9 @@ pub(crate) fn annotations(ra_fixture: &str) -> (Analysis, FilePosition, Vec<(Fil
 }
 
 /// Creates analysis from a multi-file fixture with annotations without $0
-pub(crate) fn annotations_without_marker(ra_fixture: &str) -> (Analysis, Vec<(FileRange, String)>) {
+pub(crate) fn annotations_without_marker(
+    #[rust_analyzer::rust_fixture] ra_fixture: &str,
+) -> (Analysis, Vec<(FileRange, String)>) {
     let mut host = AnalysisHost::default();
     let change_fixture = ChangeFixture::parse(ra_fixture);
     host.db.enable_proc_attr_macros();
diff --git a/src/tools/rust-analyzer/crates/ide/src/folding_ranges.rs b/src/tools/rust-analyzer/crates/ide/src/folding_ranges.rs
index c1b7693a650..e5a94ff9fe9 100755
--- a/src/tools/rust-analyzer/crates/ide/src/folding_ranges.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/folding_ranges.rs
@@ -286,7 +286,7 @@ mod tests {
 
     use super::*;
 
-    fn check(ra_fixture: &str) {
+    fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
         let (ranges, text) = extract_tags(ra_fixture, "fold");
 
         let parse = SourceFile::parse(&text, span::Edition::CURRENT);
diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_declaration.rs b/src/tools/rust-analyzer/crates/ide/src/goto_declaration.rs
index 7b6a5ef13e5..3742edc8db8 100644
--- a/src/tools/rust-analyzer/crates/ide/src/goto_declaration.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/goto_declaration.rs
@@ -83,7 +83,7 @@ mod tests {
 
     use crate::fixture;
 
-    fn check(ra_fixture: &str) {
+    fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
         let (analysis, position, expected) = fixture::annotations(ra_fixture);
         let navs = analysis
             .goto_declaration(position)
diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs
index 6c66907ec3e..f804cc36772 100644
--- a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs
@@ -81,6 +81,10 @@ pub(crate) fn goto_definition(
         return Some(RangeInfo::new(original_token.text_range(), navs));
     }
 
+    if let Some(navs) = find_definition_for_known_blanket_dual_impls(sema, &original_token) {
+        return Some(RangeInfo::new(original_token.text_range(), navs));
+    }
+
     let navs = sema
         .descend_into_macros_no_opaque(original_token.clone())
         .into_iter()
@@ -125,6 +129,18 @@ pub(crate) fn goto_definition(
     Some(RangeInfo::new(original_token.text_range(), navs))
 }
 
+// If the token is into(), try_into(), parse(), search the definition of From, TryFrom, FromStr.
+fn find_definition_for_known_blanket_dual_impls(
+    sema: &Semantics<'_, RootDatabase>,
+    original_token: &SyntaxToken,
+) -> Option<Vec<NavigationTarget>> {
+    let method_call = ast::MethodCallExpr::cast(original_token.parent()?.parent()?)?;
+    let target_method = sema.resolve_known_blanket_dual_impls(&method_call)?;
+
+    let def = Definition::from(target_method);
+    Some(def_to_nav(sema.db, def))
+}
+
 fn try_lookup_include_path(
     sema: &Semantics<'_, RootDatabase>,
     token: ast::String,
@@ -424,7 +440,7 @@ mod tests {
     use syntax::SmolStr;
 
     #[track_caller]
-    fn check(ra_fixture: &str) {
+    fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
         let (analysis, position, expected) = fixture::annotations(ra_fixture);
         let navs = analysis.goto_definition(position).unwrap().expect("no definition found").info;
 
@@ -443,14 +459,14 @@ mod tests {
         assert_eq!(expected, navs);
     }
 
-    fn check_unresolved(ra_fixture: &str) {
+    fn check_unresolved(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
         let (analysis, position) = fixture::position(ra_fixture);
         let navs = analysis.goto_definition(position).unwrap().expect("no definition found").info;
 
         assert!(navs.is_empty(), "didn't expect this to resolve anywhere: {navs:?}")
     }
 
-    fn check_name(expected_name: &str, ra_fixture: &str) {
+    fn check_name(expected_name: &str, #[rust_analyzer::rust_fixture] ra_fixture: &str) {
         let (analysis, position, _) = fixture::annotations(ra_fixture);
         let navs = analysis.goto_definition(position).unwrap().expect("no definition found").info;
         assert!(navs.len() < 2, "expected single navigation target but encountered {}", navs.len());
@@ -3022,4 +3038,150 @@ fn foo() {
 "#,
         );
     }
+    #[test]
+    fn into_call_to_from_definition() {
+        check(
+            r#"
+//- minicore: from
+struct A;
+
+struct B;
+
+impl From<A> for B {
+    fn from(value: A) -> Self {
+     //^^^^
+        B
+    }
+}
+
+fn f() {
+    let a = A;
+    let b: B = a.into$0();
+}
+        "#,
+        );
+    }
+
+    #[test]
+    fn into_call_to_from_definition_with_trait_bounds() {
+        check(
+            r#"
+//- minicore: from, iterator
+struct A;
+
+impl<T> From<T> for A
+where
+    T: IntoIterator<Item = i64>,
+{
+    fn from(value: T) -> Self {
+     //^^^^
+        A
+    }
+}
+
+fn f() {
+    let a: A = [1, 2, 3].into$0();
+}
+        "#,
+        );
+    }
+
+    #[test]
+    fn goto_into_definition_if_exists() {
+        check(
+            r#"
+//- minicore: from
+struct A;
+
+struct B;
+
+impl Into<B> for A {
+    fn into(self) -> B {
+     //^^^^
+        B
+    }
+}
+
+fn f() {
+    let a = A;
+    let b: B = a.into$0();
+}
+        "#,
+        );
+    }
+
+    #[test]
+    fn try_into_call_to_try_from_definition() {
+        check(
+            r#"
+//- minicore: from
+struct A;
+
+struct B;
+
+impl TryFrom<A> for B {
+    type Error = String;
+
+    fn try_from(value: A) -> Result<Self, Self::Error> {
+     //^^^^^^^^
+        Ok(B)
+    }
+}
+
+fn f() {
+    let a = A;
+    let b: Result<B, _> = a.try_into$0();
+}
+        "#,
+        );
+    }
+
+    #[test]
+    fn goto_try_into_definition_if_exists() {
+        check(
+            r#"
+//- minicore: from
+struct A;
+
+struct B;
+
+impl TryInto<B> for A {
+    type Error = String;
+
+    fn try_into(self) -> Result<B, Self::Error> {
+     //^^^^^^^^
+        Ok(B)
+    }
+}
+
+fn f() {
+    let a = A;
+    let b: Result<B, _> = a.try_into$0();
+}
+        "#,
+        );
+    }
+
+    #[test]
+    fn parse_call_to_from_str_definition() {
+        check(
+            r#"
+//- minicore: from, str
+struct A;
+
+impl FromStr for A {
+    type Error = String;
+
+    fn from_str(value: &str) -> Result<Self, Self::Error> {
+     //^^^^^^^^
+        Ok(A)
+    }
+}
+
+fn f() {
+    let a: Result<A, _> = "aaaaaa".parse$0();
+}
+        "#,
+        );
+    }
 }
diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs b/src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs
index 04da1f67e95..e926378367e 100644
--- a/src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs
@@ -129,7 +129,7 @@ mod tests {
 
     use crate::fixture;
 
-    fn check(ra_fixture: &str) {
+    fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
         let (analysis, position, expected) = fixture::annotations(ra_fixture);
 
         let navs = analysis.goto_implementation(position).unwrap().unwrap().info;
diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs b/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs
index c7ebd9a3531..2610d6c8863 100644
--- a/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs
@@ -24,9 +24,10 @@ pub(crate) fn goto_type_definition(
     let file: ast::SourceFile = sema.parse_guess_edition(file_id);
     let token: SyntaxToken =
         pick_best_token(file.syntax().token_at_offset(offset), |kind| match kind {
-            IDENT | INT_NUMBER | T![self] => 2,
+            IDENT | INT_NUMBER | T![self] => 3,
             kind if kind.is_trivia() => 0,
-            _ => 1,
+            T![;] => 1,
+            _ => 2,
         })?;
 
     let mut res = Vec::new();
@@ -118,7 +119,7 @@ mod tests {
 
     use crate::fixture;
 
-    fn check(ra_fixture: &str) {
+    fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
         let (analysis, position, expected) = fixture::annotations(ra_fixture);
         let navs = analysis.goto_type_definition(position).unwrap().unwrap().info;
         assert!(!navs.is_empty(), "navigation is empty");
diff --git a/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs b/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs
index 4002cbebad6..612bc36f628 100644
--- a/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs
@@ -684,12 +684,15 @@ mod tests {
     };
 
     #[track_caller]
-    fn check(ra_fixture: &str) {
+    fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
         check_with_config(ra_fixture, ENABLED_CONFIG);
     }
 
     #[track_caller]
-    fn check_with_config(ra_fixture: &str, config: HighlightRelatedConfig) {
+    fn check_with_config(
+        #[rust_analyzer::rust_fixture] ra_fixture: &str,
+        config: HighlightRelatedConfig,
+    ) {
         let (analysis, pos, annotations) = fixture::annotations(ra_fixture);
 
         let hls = analysis.highlight_related(config, pos).unwrap().unwrap_or_default();
diff --git a/src/tools/rust-analyzer/crates/ide/src/hover.rs b/src/tools/rust-analyzer/crates/ide/src/hover.rs
index 1431bd8ca29..18a3fed07ec 100644
--- a/src/tools/rust-analyzer/crates/ide/src/hover.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/hover.rs
@@ -6,7 +6,9 @@ mod tests;
 use std::{iter, ops::Not};
 
 use either::Either;
-use hir::{db::DefDatabase, GenericSubstitution, HasCrate, HasSource, LangItem, Semantics};
+use hir::{
+    db::DefDatabase, GenericDef, GenericSubstitution, HasCrate, HasSource, LangItem, Semantics,
+};
 use ide_db::{
     defs::{Definition, IdentClass, NameRefClass, OperatorClass},
     famous_defs::FamousDefs,
@@ -548,24 +550,29 @@ fn goto_type_action_for_def(
         });
     }
 
-    if let Definition::GenericParam(hir::GenericParam::TypeParam(it)) = def {
-        let krate = it.module(db).krate();
-        let sized_trait =
-            db.lang_item(krate.into(), LangItem::Sized).and_then(|lang_item| lang_item.as_trait());
-
-        it.trait_bounds(db)
-            .into_iter()
-            .filter(|&it| Some(it.into()) != sized_trait)
-            .for_each(|it| push_new_def(it.into()));
-    } else {
-        let ty = match def {
-            Definition::Local(it) => it.ty(db),
-            Definition::GenericParam(hir::GenericParam::ConstParam(it)) => it.ty(db),
-            Definition::Field(field) => field.ty(db),
-            Definition::Function(function) => function.ret_type(db),
-            _ => return HoverAction::goto_type_from_targets(db, targets, edition),
-        };
+    if let Ok(generic_def) = GenericDef::try_from(def) {
+        generic_def.type_or_const_params(db).into_iter().for_each(|it| {
+            walk_and_push_ty(db, &it.ty(db), &mut push_new_def);
+        });
+    }
 
+    let ty = match def {
+        Definition::Local(it) => Some(it.ty(db)),
+        Definition::Field(field) => Some(field.ty(db)),
+        Definition::TupleField(field) => Some(field.ty(db)),
+        Definition::Const(it) => Some(it.ty(db)),
+        Definition::Static(it) => Some(it.ty(db)),
+        Definition::Function(func) => {
+            for param in func.assoc_fn_params(db) {
+                walk_and_push_ty(db, param.ty(), &mut push_new_def);
+            }
+            Some(func.ret_type(db))
+        }
+        Definition::GenericParam(hir::GenericParam::ConstParam(it)) => Some(it.ty(db)),
+        Definition::GenericParam(hir::GenericParam::TypeParam(it)) => Some(it.ty(db)),
+        _ => None,
+    };
+    if let Some(ty) = ty {
         walk_and_push_ty(db, &ty, &mut push_new_def);
     }
 
@@ -592,6 +599,14 @@ fn walk_and_push_ty(
             traits.for_each(|it| push_new_def(it.into()));
         } else if let Some(trait_) = t.as_associated_type_parent_trait(db) {
             push_new_def(trait_.into());
+        } else if let Some(tp) = t.as_type_param(db) {
+            let sized_trait = db
+                .lang_item(t.krate(db).into(), LangItem::Sized)
+                .and_then(|lang_item| lang_item.as_trait());
+            tp.trait_bounds(db)
+                .into_iter()
+                .filter(|&it| Some(it.into()) != sized_trait)
+                .for_each(|it| push_new_def(it.into()));
         }
     });
 }
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 8fbd445d962..46242b75dd0 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, AssocItemContainer, CaptureKind,
+    db::ExpandDatabase, Adt, AsAssocItem, AsExternAssocItem, CaptureKind,
     DynCompatibilityViolation, HasCrate, HasSource, HirDisplay, Layout, LayoutError,
     MethodViolationCode, Name, Semantics, Symbol, Trait, Type, TypeInfo, VariantDef,
 };
@@ -376,7 +376,7 @@ pub(super) fn process_markup(
     Markup::from(markup)
 }
 
-fn definition_owner_name(db: &RootDatabase, def: &Definition, edition: Edition) -> Option<String> {
+fn definition_owner_name(db: &RootDatabase, def: Definition, edition: Edition) -> Option<String> {
     match def {
         Definition::Field(f) => {
             let parent = f.parent_def(db);
@@ -390,9 +390,52 @@ fn definition_owner_name(db: &RootDatabase, def: &Definition, edition: Edition)
                 _ => Some(parent_name),
             };
         }
-        Definition::Local(l) => l.parent(db).name(db),
         Definition::Variant(e) => Some(e.parent_enum(db).name(db)),
-
+        Definition::GenericParam(generic_param) => match generic_param.parent() {
+            hir::GenericDef::Adt(it) => Some(it.name(db)),
+            hir::GenericDef::Trait(it) => Some(it.name(db)),
+            hir::GenericDef::TraitAlias(it) => Some(it.name(db)),
+            hir::GenericDef::TypeAlias(it) => Some(it.name(db)),
+
+            hir::GenericDef::Impl(i) => i.self_ty(db).as_adt().map(|adt| adt.name(db)),
+            hir::GenericDef::Function(it) => {
+                let container = it.as_assoc_item(db).and_then(|assoc| match assoc.container(db) {
+                    hir::AssocItemContainer::Trait(t) => Some(t.name(db)),
+                    hir::AssocItemContainer::Impl(i) => {
+                        i.self_ty(db).as_adt().map(|adt| adt.name(db))
+                    }
+                });
+                match container {
+                    Some(name) => {
+                        return Some(format!(
+                            "{}::{}",
+                            name.display(db, edition),
+                            it.name(db).display(db, edition)
+                        ))
+                    }
+                    None => Some(it.name(db)),
+                }
+            }
+            hir::GenericDef::Const(it) => {
+                let container = it.as_assoc_item(db).and_then(|assoc| match assoc.container(db) {
+                    hir::AssocItemContainer::Trait(t) => Some(t.name(db)),
+                    hir::AssocItemContainer::Impl(i) => {
+                        i.self_ty(db).as_adt().map(|adt| adt.name(db))
+                    }
+                });
+                match container {
+                    Some(name) => {
+                        return Some(format!(
+                            "{}::{}",
+                            name.display(db, edition),
+                            it.name(db)?.display(db, edition)
+                        ))
+                    }
+                    None => it.name(db),
+                }
+            }
+        },
+        Definition::DeriveHelper(derive_helper) => Some(derive_helper.derive().name(db)),
         d => {
             if let Some(assoc_item) = d.as_assoc_item(db) {
                 match assoc_item.container(db) {
@@ -436,7 +479,7 @@ pub(super) fn definition(
     config: &HoverConfig,
     edition: Edition,
 ) -> Markup {
-    let mod_path = definition_mod_path(db, &def, edition);
+    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()
@@ -915,19 +958,22 @@ fn closure_ty(
     Some(res)
 }
 
-fn definition_mod_path(db: &RootDatabase, def: &Definition, edition: Edition) -> Option<String> {
-    if matches!(def, Definition::GenericParam(_) | Definition::Local(_) | Definition::Label(_)) {
+fn definition_path(db: &RootDatabase, &def: &Definition, edition: Edition) -> Option<String> {
+    if matches!(
+        def,
+        Definition::TupleField(_)
+            | Definition::Label(_)
+            | Definition::Local(_)
+            | Definition::BuiltinAttr(_)
+            | Definition::BuiltinLifetime(_)
+            | Definition::BuiltinType(_)
+            | Definition::InlineAsmRegOrRegClass(_)
+            | Definition::InlineAsmOperand(_)
+    ) {
         return None;
     }
-    let container: Option<Definition> =
-        def.as_assoc_item(db).and_then(|assoc| match assoc.container(db) {
-            AssocItemContainer::Trait(trait_) => Some(trait_.into()),
-            AssocItemContainer::Impl(impl_) => impl_.self_ty(db).as_adt().map(|adt| adt.into()),
-        });
-    container
-        .unwrap_or(*def)
-        .module(db)
-        .map(|module| path(db, module, definition_owner_name(db, def, edition), edition))
+    let rendered_parent = definition_owner_name(db, def, edition);
+    def.module(db).map(|module| path(db, module, rendered_parent, edition))
 }
 
 fn markup(
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 2e7637e4677..014b751f95b 100644
--- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs
@@ -23,7 +23,7 @@ const HOVER_BASE_CONFIG: HoverConfig = HoverConfig {
     max_subst_ty_len: super::SubstTyLen::Unlimited,
 };
 
-fn check_hover_no_result(ra_fixture: &str) {
+fn check_hover_no_result(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
     let (analysis, position) = fixture::position(ra_fixture);
     let hover = analysis
         .hover(
@@ -35,7 +35,7 @@ fn check_hover_no_result(ra_fixture: &str) {
 }
 
 #[track_caller]
-fn check(ra_fixture: &str, expect: Expect) {
+fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
     let (analysis, position) = fixture::position(ra_fixture);
     let hover = analysis
         .hover(
@@ -55,7 +55,7 @@ fn check(ra_fixture: &str, expect: Expect) {
 #[track_caller]
 fn check_hover_fields_limit(
     fields_count: impl Into<Option<usize>>,
-    ra_fixture: &str,
+    #[rust_analyzer::rust_fixture] ra_fixture: &str,
     expect: Expect,
 ) {
     let (analysis, position) = fixture::position(ra_fixture);
@@ -81,7 +81,7 @@ fn check_hover_fields_limit(
 #[track_caller]
 fn check_hover_enum_variants_limit(
     variants_count: impl Into<Option<usize>>,
-    ra_fixture: &str,
+    #[rust_analyzer::rust_fixture] ra_fixture: &str,
     expect: Expect,
 ) {
     let (analysis, position) = fixture::position(ra_fixture);
@@ -105,7 +105,11 @@ fn check_hover_enum_variants_limit(
 }
 
 #[track_caller]
-fn check_assoc_count(count: usize, ra_fixture: &str, expect: Expect) {
+fn check_assoc_count(
+    count: usize,
+    #[rust_analyzer::rust_fixture] ra_fixture: &str,
+    expect: Expect,
+) {
     let (analysis, position) = fixture::position(ra_fixture);
     let hover = analysis
         .hover(
@@ -126,7 +130,7 @@ fn check_assoc_count(count: usize, ra_fixture: &str, expect: Expect) {
     expect.assert_eq(&actual)
 }
 
-fn check_hover_no_links(ra_fixture: &str, expect: Expect) {
+fn check_hover_no_links(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
     let (analysis, position) = fixture::position(ra_fixture);
     let hover = analysis
         .hover(
@@ -143,7 +147,7 @@ fn check_hover_no_links(ra_fixture: &str, expect: Expect) {
     expect.assert_eq(&actual)
 }
 
-fn check_hover_no_memory_layout(ra_fixture: &str, expect: Expect) {
+fn check_hover_no_memory_layout(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
     let (analysis, position) = fixture::position(ra_fixture);
     let hover = analysis
         .hover(
@@ -160,7 +164,7 @@ fn check_hover_no_memory_layout(ra_fixture: &str, expect: Expect) {
     expect.assert_eq(&actual)
 }
 
-fn check_hover_no_markdown(ra_fixture: &str, expect: Expect) {
+fn check_hover_no_markdown(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
     let (analysis, position) = fixture::position(ra_fixture);
     let hover = analysis
         .hover(
@@ -181,7 +185,7 @@ fn check_hover_no_markdown(ra_fixture: &str, expect: Expect) {
     expect.assert_eq(&actual)
 }
 
-fn check_actions(ra_fixture: &str, expect: Expect) {
+fn check_actions(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
     let (analysis, file_id, position) = fixture::range_or_position(ra_fixture);
     let mut hover = analysis
         .hover(
@@ -206,13 +210,13 @@ fn check_actions(ra_fixture: &str, expect: Expect) {
     expect.assert_debug_eq(&hover.info.actions)
 }
 
-fn check_hover_range(ra_fixture: &str, expect: Expect) {
+fn check_hover_range(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
     let (analysis, range) = fixture::range(ra_fixture);
     let hover = analysis.hover(&HOVER_BASE_CONFIG, range).unwrap().unwrap();
     expect.assert_eq(hover.info.markup.as_str())
 }
 
-fn check_hover_range_actions(ra_fixture: &str, expect: Expect) {
+fn check_hover_range_actions(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
     let (analysis, range) = fixture::range(ra_fixture);
     let mut hover = analysis
         .hover(&HoverConfig { links_in_hover: true, ..HOVER_BASE_CONFIG }, range)
@@ -234,7 +238,7 @@ fn check_hover_range_actions(ra_fixture: &str, expect: Expect) {
     expect.assert_debug_eq(&hover.info.actions);
 }
 
-fn check_hover_range_no_results(ra_fixture: &str) {
+fn check_hover_range_no_results(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
     let (analysis, range) = fixture::range(ra_fixture);
     let hover = analysis.hover(&HOVER_BASE_CONFIG, range).unwrap();
     assert!(hover.is_none());
@@ -2365,6 +2369,97 @@ fn test() {
 }
 
 #[test]
+fn test_hover_show_type_def_for_func_param() {
+    check_actions(
+        r#"
+struct Bar;
+fn f(b: Bar) {
+
+}
+
+fn test() {
+    let b = Bar;
+    f$0(b);
+}
+"#,
+        expect![[r#"
+            [
+                Reference(
+                    FilePositionWrapper {
+                        file_id: FileId(
+                            0,
+                        ),
+                        offset: 15,
+                    },
+                ),
+                GoToType(
+                    [
+                        HoverGotoTypeData {
+                            mod_path: "ra_test_fixture::Bar",
+                            nav: NavigationTarget {
+                                file_id: FileId(
+                                    0,
+                                ),
+                                full_range: 0..11,
+                                focus_range: 7..10,
+                                name: "Bar",
+                                kind: Struct,
+                                description: "struct Bar",
+                            },
+                        },
+                    ],
+                ),
+            ]
+        "#]],
+    );
+}
+
+#[test]
+fn test_hover_show_type_def_for_trait_bound() {
+    check_actions(
+        r#"
+trait Bar {}
+fn f<T: Bar>(b: T) {
+
+}
+
+fn test() {
+    f$0();
+}
+"#,
+        expect![[r#"
+            [
+                Reference(
+                    FilePositionWrapper {
+                        file_id: FileId(
+                            0,
+                        ),
+                        offset: 16,
+                    },
+                ),
+                GoToType(
+                    [
+                        HoverGotoTypeData {
+                            mod_path: "ra_test_fixture::Bar",
+                            nav: NavigationTarget {
+                                file_id: FileId(
+                                    0,
+                                ),
+                                full_range: 0..12,
+                                focus_range: 6..9,
+                                name: "Bar",
+                                kind: Trait,
+                                description: "trait Bar",
+                            },
+                        },
+                    ],
+                ),
+            ]
+        "#]],
+    );
+}
+
+#[test]
 fn test_hover_non_ascii_space_doc() {
     check(
         "
@@ -4700,6 +4795,10 @@ fn hover_lifetime() {
             *'lifetime*
 
             ```rust
+            ra_test_fixture::foo
+            ```
+
+            ```rust
             'lifetime
             ```
         "#]],
@@ -4730,6 +4829,10 @@ impl<T: TraitA + TraitB> Foo<T$0> where T: Sized {}
             *T*
 
             ```rust
+            ra_test_fixture::Foo
+            ```
+
+            ```rust
             T: TraitA + TraitB
             ```
         "#]],
@@ -4744,6 +4847,10 @@ impl<T> Foo<T$0> {}
             *T*
 
             ```rust
+            ra_test_fixture::Foo
+            ```
+
+            ```rust
             T
             ```
         "#]],
@@ -4758,6 +4865,10 @@ impl<T: 'static> Foo<T$0> {}
             *T*
 
             ```rust
+            ra_test_fixture::Foo
+            ```
+
+            ```rust
             T: 'static
             ```
         "#]],
@@ -4778,6 +4889,10 @@ impl<T$0: Trait> Foo<T> {}
             *T*
 
             ```rust
+            ra_test_fixture::Foo
+            ```
+
+            ```rust
             T: Trait
             ```
         "#]],
@@ -4793,6 +4908,10 @@ impl<T$0: Trait + ?Sized> Foo<T> {}
             *T*
 
             ```rust
+            ra_test_fixture::Foo
+            ```
+
+            ```rust
             T: Trait + ?Sized
             ```
         "#]],
@@ -4813,6 +4932,10 @@ fn foo<T$0>() {}
                 *T*
 
                 ```rust
+                ra_test_fixture::foo
+                ```
+
+                ```rust
                 T
                 ```
 
@@ -4834,6 +4957,10 @@ fn foo<T$0: Sized>() {}
                 *T*
 
                 ```rust
+                ra_test_fixture::foo
+                ```
+
+                ```rust
                 T
                 ```
 
@@ -4855,6 +4982,10 @@ fn foo<T$0: ?Sized>() {}
                 *T*
 
                 ```rust
+                ra_test_fixture::foo
+                ```
+
+                ```rust
                 T: ?Sized
                 ```
 
@@ -4877,6 +5008,10 @@ fn foo<T$0: Trait>() {}
                 *T*
 
                 ```rust
+                ra_test_fixture::foo
+                ```
+
+                ```rust
                 T: Trait
                 ```
 
@@ -4899,6 +5034,10 @@ fn foo<T$0: Trait + Sized>() {}
                 *T*
 
                 ```rust
+                ra_test_fixture::foo
+                ```
+
+                ```rust
                 T: Trait
                 ```
 
@@ -4921,6 +5060,10 @@ fn foo<T$0: Trait + ?Sized>() {}
                 *T*
 
                 ```rust
+                ra_test_fixture::foo
+                ```
+
+                ```rust
                 T: Trait + ?Sized
                 ```
 
@@ -4942,6 +5085,10 @@ fn foo<T$0: ?Sized + Sized + Sized>() {}
                 *T*
 
                 ```rust
+                ra_test_fixture::foo
+                ```
+
+                ```rust
                 T
                 ```
 
@@ -4964,6 +5111,10 @@ fn foo<T$0: Sized + ?Sized + Sized + Trait>() {}
                 *T*
 
                 ```rust
+                ra_test_fixture::foo
+                ```
+
+                ```rust
                 T: Trait
                 ```
 
@@ -5011,6 +5162,10 @@ impl<const LEN: usize> Foo<LEN$0> {}
             *LEN*
 
             ```rust
+            ra_test_fixture::Foo
+            ```
+
+            ```rust
             const LEN: usize
             ```
         "#]],
@@ -6112,7 +6267,7 @@ use foo::bar::{self$0};
             ```
 
             ```rust
-            mod bar
+            pub mod bar
             ```
 
             ---
@@ -7857,7 +8012,7 @@ fn test() {
             *foo*
 
             ```rust
-            ra_test_fixture::S
+            ra_test_fixture::m::S
             ```
 
             ```rust
@@ -7886,7 +8041,7 @@ fn test() {
             *foo*
 
             ```rust
-            ra_test_fixture::S
+            ra_test_fixture::m::S
             ```
 
             ```rust
@@ -7916,7 +8071,7 @@ mod m {
             *foo*
 
             ```rust
-            ra_test_fixture::S
+            ra_test_fixture::m::inner::S
             ```
 
             ```rust
@@ -7946,7 +8101,7 @@ fn test() {
             *A*
 
             ```rust
-            ra_test_fixture::S
+            ra_test_fixture::m::S
             ```
 
             ```rust
@@ -7975,7 +8130,7 @@ fn test() {
             *A*
 
             ```rust
-            ra_test_fixture::S
+            ra_test_fixture::m::S
             ```
 
             ```rust
@@ -8005,7 +8160,7 @@ mod m {
             *A*
 
             ```rust
-            ra_test_fixture::S
+            ra_test_fixture::m::inner::S
             ```
 
             ```rust
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 faa65019eea..6d83a747d76 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs
@@ -1,6 +1,6 @@
 use std::{
     fmt::{self, Write},
-    mem::take,
+    mem::{self, take},
 };
 
 use either::Either;
@@ -24,6 +24,7 @@ use crate::{navigation_target::TryToNav, FileId};
 mod adjustment;
 mod bind_pat;
 mod binding_mode;
+mod bounds;
 mod chaining;
 mod closing_brace;
 mod closure_captures;
@@ -111,6 +112,9 @@ pub(crate) fn inlay_hints(
         }
         hints(event);
     }
+    if let Some(range_limit) = range_limit {
+        acc.retain(|hint| range_limit.contains_range(hint.range));
+    }
     acc
 }
 
@@ -264,6 +268,7 @@ fn hints(
                 ast::Type::PathType(path) => lifetime::fn_path_hints(hints, ctx, famous_defs, config, file_id, path),
                 _ => Some(()),
             },
+            ast::GenericParamList(it) => bounds::hints(hints, famous_defs, config, file_id, it),
             _ => Some(()),
         }
     };
@@ -273,6 +278,7 @@ fn hints(
 pub struct InlayHintsConfig {
     pub render_colons: bool,
     pub type_hints: bool,
+    pub sized_bound: bool,
     pub discriminant_hints: DiscriminantHints,
     pub parameter_hints: bool,
     pub generic_parameter_hints: GenericParameterHints,
@@ -294,6 +300,36 @@ pub struct InlayHintsConfig {
     pub closing_brace_hints_min_lines: Option<usize>,
     pub fields_to_resolve: InlayFieldsToResolve,
 }
+impl InlayHintsConfig {
+    fn lazy_text_edit(&self, finish: impl FnOnce() -> TextEdit) -> Lazy<TextEdit> {
+        if self.fields_to_resolve.resolve_text_edits {
+            Lazy::Lazy
+        } else {
+            let edit = finish();
+            never!(edit.is_empty(), "inlay hint produced an empty text edit");
+            Lazy::Computed(edit)
+        }
+    }
+
+    fn lazy_tooltip(&self, finish: impl FnOnce() -> InlayTooltip) -> Lazy<InlayTooltip> {
+        if self.fields_to_resolve.resolve_hint_tooltip
+            && self.fields_to_resolve.resolve_label_tooltip
+        {
+            Lazy::Lazy
+        } else {
+            let tooltip = finish();
+            never!(
+                match &tooltip {
+                    InlayTooltip::String(s) => s,
+                    InlayTooltip::Markdown(s) => s,
+                }
+                .is_empty(),
+                "inlay hint produced an empty tooltip"
+            );
+            Lazy::Computed(tooltip)
+        }
+    }
+}
 
 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
 pub struct InlayFieldsToResolve {
@@ -405,12 +441,32 @@ pub struct InlayHint {
     /// The actual label to show in the inlay hint.
     pub label: InlayHintLabel,
     /// Text edit to apply when "accepting" this inlay hint.
-    pub text_edit: Option<TextEdit>,
+    pub text_edit: Option<Lazy<TextEdit>>,
     /// Range to recompute inlay hints when trying to resolve for this hint. If this is none, the
     /// hint does not support resolving.
     pub resolve_parent: Option<TextRange>,
 }
 
+/// A type signaling that a value is either computed, or is available for computation.
+#[derive(Clone, Debug)]
+pub enum Lazy<T> {
+    Computed(T),
+    Lazy,
+}
+
+impl<T> Lazy<T> {
+    pub fn computed(self) -> Option<T> {
+        match self {
+            Lazy::Computed(it) => Some(it),
+            _ => None,
+        }
+    }
+
+    pub fn is_lazy(&self) -> bool {
+        matches!(self, Self::Lazy)
+    }
+}
+
 impl std::hash::Hash for InlayHint {
     fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
         self.range.hash(state);
@@ -419,7 +475,7 @@ impl std::hash::Hash for InlayHint {
         self.pad_right.hash(state);
         self.kind.hash(state);
         self.label.hash(state);
-        self.text_edit.is_some().hash(state);
+        mem::discriminant(&self.text_edit).hash(state);
     }
 }
 
@@ -436,10 +492,6 @@ impl InlayHint {
             resolve_parent: None,
         }
     }
-
-    pub fn needs_resolve(&self) -> Option<TextRange> {
-        self.resolve_parent.filter(|_| self.text_edit.is_some() || self.label.needs_resolve())
-    }
 }
 
 #[derive(Debug, Hash)]
@@ -456,7 +508,7 @@ pub struct InlayHintLabel {
 impl InlayHintLabel {
     pub fn simple(
         s: impl Into<String>,
-        tooltip: Option<InlayTooltip>,
+        tooltip: Option<Lazy<InlayTooltip>>,
         linked_location: Option<FileRange>,
     ) -> InlayHintLabel {
         InlayHintLabel {
@@ -500,10 +552,6 @@ impl InlayHintLabel {
         }
         self.parts.push(part);
     }
-
-    pub fn needs_resolve(&self) -> bool {
-        self.parts.iter().any(|part| part.linked_location.is_some() || part.tooltip.is_some())
-    }
 }
 
 impl From<String> for InlayHintLabel {
@@ -538,7 +586,6 @@ impl fmt::Debug for InlayHintLabel {
     }
 }
 
-#[derive(Hash)]
 pub struct InlayHintLabelPart {
     pub text: String,
     /// Source location represented by this label part. The client will use this to fetch the part's
@@ -549,13 +596,21 @@ pub struct InlayHintLabelPart {
     pub linked_location: Option<FileRange>,
     /// The tooltip to show when hovering over the inlay hint, this may invoke other actions like
     /// hover requests to show.
-    pub tooltip: Option<InlayTooltip>,
+    pub tooltip: Option<Lazy<InlayTooltip>>,
+}
+
+impl std::hash::Hash for InlayHintLabelPart {
+    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
+        self.text.hash(state);
+        self.linked_location.hash(state);
+        self.tooltip.is_some().hash(state);
+    }
 }
 
 impl fmt::Debug for InlayHintLabelPart {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         match self {
-            Self { text, linked_location: None, tooltip: None } => text.fmt(f),
+            Self { text, linked_location: None, tooltip: None | Some(Lazy::Lazy) } => text.fmt(f),
             Self { text, linked_location, tooltip } => f
                 .debug_struct("InlayHintLabelPart")
                 .field("text", text)
@@ -563,7 +618,8 @@ impl fmt::Debug for InlayHintLabelPart {
                 .field(
                     "tooltip",
                     &tooltip.as_ref().map_or("", |it| match it {
-                        InlayTooltip::String(it) | InlayTooltip::Markdown(it) => it,
+                        Lazy::Computed(InlayTooltip::String(it) | InlayTooltip::Markdown(it)) => it,
+                        Lazy::Lazy => "",
                     }),
                 )
                 .finish(),
@@ -722,19 +778,22 @@ fn hint_iterator(
 
 fn ty_to_text_edit(
     sema: &Semantics<'_, RootDatabase>,
+    config: &InlayHintsConfig,
     node_for_hint: &SyntaxNode,
     ty: &hir::Type,
     offset_to_insert: TextSize,
-    prefix: String,
-) -> Option<TextEdit> {
-    let scope = sema.scope(node_for_hint)?;
+    prefix: impl Into<String>,
+) -> Option<Lazy<TextEdit>> {
     // FIXME: Limit the length and bail out on excess somehow?
-    let rendered = ty.display_source_code(scope.db, scope.module().into(), false).ok()?;
-
-    let mut builder = TextEdit::builder();
-    builder.insert(offset_to_insert, prefix);
-    builder.insert(offset_to_insert, rendered);
-    Some(builder.finish())
+    let rendered = sema
+        .scope(node_for_hint)
+        .and_then(|scope| ty.display_source_code(scope.db, scope.module().into(), false).ok())?;
+    Some(config.lazy_text_edit(|| {
+        let mut builder = TextEdit::builder();
+        builder.insert(offset_to_insert, prefix.into());
+        builder.insert(offset_to_insert, rendered);
+        builder.finish()
+    }))
 }
 
 fn closure_has_block_body(closure: &ast::ClosureExpr) -> bool {
@@ -760,6 +819,7 @@ mod tests {
         render_colons: false,
         type_hints: false,
         parameter_hints: false,
+        sized_bound: false,
         generic_parameter_hints: GenericParameterHints {
             type_hints: false,
             lifetime_hints: false,
@@ -794,12 +854,15 @@ mod tests {
     };
 
     #[track_caller]
-    pub(super) fn check(ra_fixture: &str) {
+    pub(super) fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
         check_with_config(TEST_CONFIG, ra_fixture);
     }
 
     #[track_caller]
-    pub(super) fn check_with_config(config: InlayHintsConfig, ra_fixture: &str) {
+    pub(super) fn check_with_config(
+        config: InlayHintsConfig,
+        #[rust_analyzer::rust_fixture] ra_fixture: &str,
+    ) {
         let (analysis, file_id) = fixture::file(ra_fixture);
         let mut expected = extract_annotations(&analysis.file_text(file_id).unwrap());
         let inlay_hints = analysis.inlay_hints(&config, file_id, None).unwrap();
@@ -814,16 +877,33 @@ mod tests {
         assert_eq!(expected, actual, "\nExpected:\n{expected:#?}\n\nActual:\n{actual:#?}");
     }
 
+    #[track_caller]
+    pub(super) fn check_expect(
+        config: InlayHintsConfig,
+        #[rust_analyzer::rust_fixture] ra_fixture: &str,
+        expect: Expect,
+    ) {
+        let (analysis, file_id) = fixture::file(ra_fixture);
+        let inlay_hints = analysis.inlay_hints(&config, file_id, None).unwrap();
+        let filtered =
+            inlay_hints.into_iter().map(|hint| (hint.range, hint.label)).collect::<Vec<_>>();
+        expect.assert_debug_eq(&filtered)
+    }
+
     /// Computes inlay hints for the fixture, applies all the provided text edits and then runs
     /// expect test.
     #[track_caller]
-    pub(super) fn check_edit(config: InlayHintsConfig, ra_fixture: &str, expect: Expect) {
+    pub(super) fn check_edit(
+        config: InlayHintsConfig,
+        #[rust_analyzer::rust_fixture] ra_fixture: &str,
+        expect: Expect,
+    ) {
         let (analysis, file_id) = fixture::file(ra_fixture);
         let inlay_hints = analysis.inlay_hints(&config, file_id, None).unwrap();
 
         let edits = inlay_hints
             .into_iter()
-            .filter_map(|hint| hint.text_edit)
+            .filter_map(|hint| hint.text_edit?.computed())
             .reduce(|mut acc, next| {
                 acc.union(next).expect("merging text edits failed");
                 acc
@@ -836,11 +916,15 @@ mod tests {
     }
 
     #[track_caller]
-    pub(super) fn check_no_edit(config: InlayHintsConfig, ra_fixture: &str) {
+    pub(super) fn check_no_edit(
+        config: InlayHintsConfig,
+        #[rust_analyzer::rust_fixture] ra_fixture: &str,
+    ) {
         let (analysis, file_id) = fixture::file(ra_fixture);
         let inlay_hints = analysis.inlay_hints(&config, file_id, None).unwrap();
 
-        let edits: Vec<_> = inlay_hints.into_iter().filter_map(|hint| hint.text_edit).collect();
+        let edits: Vec<_> =
+            inlay_hints.into_iter().filter_map(|hint| hint.text_edit?.computed()).collect();
 
         assert!(edits.is_empty(), "unexpected edits: {edits:?}");
     }
@@ -870,4 +954,17 @@ fn foo() {
 "#,
         );
     }
+
+    #[test]
+    fn regression_18898() {
+        check(
+            r#"
+//- proc_macros: issue_18898
+#[proc_macros::issue_18898]
+fn foo() {
+    let
+}
+"#,
+        );
+    }
 }
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 4e48baa6f14..2acd4021cc1 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
@@ -162,11 +162,13 @@ pub(super) fn hints(
         let label = InlayHintLabelPart {
             text: if postfix { format!(".{}", text.trim_end()) } else { text.to_owned() },
             linked_location: None,
-            tooltip: Some(InlayTooltip::Markdown(format!(
-                "`{}` → `{}` ({coercion} coercion)",
-                source.display(sema.db, file_id.edition()),
-                target.display(sema.db, file_id.edition()),
-            ))),
+            tooltip: Some(config.lazy_tooltip(|| {
+                InlayTooltip::Markdown(format!(
+                    "`{}` → `{}` ({coercion} coercion)",
+                    source.display(sema.db, file_id.edition()),
+                    target.display(sema.db, file_id.edition()),
+                ))
+            })),
         };
         if postfix { &mut post } else { &mut pre }.label.append_part(label);
     }
@@ -183,7 +185,7 @@ pub(super) fn hints(
         return None;
     }
     if allow_edit {
-        let edit = {
+        let edit = Some(config.lazy_text_edit(|| {
             let mut b = TextEditBuilder::default();
             if let Some(pre) = &pre {
                 b.insert(
@@ -198,14 +200,14 @@ pub(super) fn hints(
                 );
             }
             b.finish()
-        };
+        }));
         match (&mut pre, &mut post) {
             (Some(pre), Some(post)) => {
-                pre.text_edit = Some(edit.clone());
-                post.text_edit = Some(edit);
+                pre.text_edit = edit.clone();
+                post.text_edit = edit;
             }
-            (Some(pre), None) => pre.text_edit = Some(edit),
-            (None, Some(post)) => post.text_edit = Some(edit),
+            (Some(pre), None) => pre.text_edit = edit,
+            (None, Some(post)) => post.text_edit = edit,
             (None, None) => (),
         }
     }
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 7a808fb4a92..ab5464156f0 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
@@ -78,13 +78,14 @@ pub(super) fn hints(
     let text_edit = if let Some(colon_token) = &type_ascriptable {
         ty_to_text_edit(
             sema,
+            config,
             desc_pat.syntax(),
             &ty,
             colon_token
                 .as_ref()
                 .map_or_else(|| pat.syntax().text_range(), |t| t.text_range())
                 .end(),
-            if colon_token.is_some() { String::new() } else { String::from(": ") },
+            if colon_token.is_some() { "" } else { ": " },
         )
     } else {
         None
@@ -185,7 +186,7 @@ mod tests {
     };
 
     #[track_caller]
-    fn check_types(ra_fixture: &str) {
+    fn check_types(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
         check_with_config(InlayHintsConfig { type_hints: true, ..DISABLED_CONFIG }, ra_fixture);
     }
 
@@ -391,36 +392,37 @@ fn main() {
     #[test]
     fn check_hint_range_limit() {
         let fixture = r#"
-        //- minicore: fn, sized
-        fn foo() -> impl Fn() { loop {} }
-        fn foo1() -> impl Fn(f64) { loop {} }
-        fn foo2() -> impl Fn(f64, f64) { loop {} }
-        fn foo3() -> impl Fn(f64, f64) -> u32 { loop {} }
-        fn foo4() -> &'static dyn Fn(f64, f64) -> u32 { loop {} }
-        fn foo5() -> &'static dyn Fn(&'static dyn Fn(f64, f64) -> u32, f64) -> u32 { loop {} }
-        fn foo6() -> impl Fn(f64, f64) -> u32 + Sized { loop {} }
-        fn foo7() -> *const (impl Fn(f64, f64) -> u32 + Sized) { loop {} }
-
-        fn main() {
-            let foo = foo();
-            let foo = foo1();
-            let foo = foo2();
-             // ^^^ impl Fn(f64, f64)
-            let foo = foo3();
-             // ^^^ impl Fn(f64, f64) -> u32
-            let foo = foo4();
-            let foo = foo5();
-            let foo = foo6();
-            let foo = foo7();
-        }
-        "#;
+//- minicore: fn, sized
+fn foo() -> impl Fn() { loop {} }
+fn foo1() -> impl Fn(f64) { loop {} }
+fn foo2() -> impl Fn(f64, f64) { loop {} }
+fn foo3() -> impl Fn(f64, f64) -> u32 { loop {} }
+fn foo4() -> &'static dyn Fn(f64, f64) -> u32 { loop {} }
+fn foo5() -> &'static dyn Fn(&'static dyn Fn(f64, f64) -> u32, f64) -> u32 { loop {} }
+fn foo6() -> impl Fn(f64, f64) -> u32 + Sized { loop {} }
+fn foo7() -> *const (impl Fn(f64, f64) -> u32 + Sized) { loop {} }
+
+fn main() {
+    let foo = foo();
+    let foo = foo1();
+    let foo = foo2();
+     // ^^^ impl Fn(f64, f64)
+    let foo = foo3();
+     // ^^^ impl Fn(f64, f64) -> u32
+    let foo = foo4();
+     // ^^^ &dyn Fn(f64, f64) -> u32
+    let foo = foo5();
+    let foo = foo6();
+    let foo = foo7();
+}
+"#;
         let (analysis, file_id) = fixture::file(fixture);
         let expected = extract_annotations(&analysis.file_text(file_id).unwrap());
         let inlay_hints = analysis
             .inlay_hints(
                 &InlayHintsConfig { type_hints: true, ..DISABLED_CONFIG },
                 file_id,
-                Some(TextRange::new(TextSize::from(500), TextSize::from(600))),
+                Some(TextRange::new(TextSize::from(491), TextSize::from(640))),
             )
             .unwrap();
         let actual =
@@ -1163,4 +1165,45 @@ fn main() {
 }"#,
         );
     }
+
+    #[test]
+    fn collapses_nested_impl_projections() {
+        check_types(
+            r#"
+//- minicore: sized
+trait T {
+    type Assoc;
+    fn f(self) -> Self::Assoc;
+}
+
+trait T2 {}
+trait T3<T> {}
+
+fn f(it: impl T<Assoc: T2>) {
+    let l = it.f();
+     // ^ impl T2
+}
+
+fn f2<G: T<Assoc: T2 + 'static>>(it: G) {
+    let l = it.f();
+      //^ impl T2 + 'static
+}
+
+fn f3<G: T>(it: G) where <G as T>::Assoc: T2 {
+    let l = it.f();
+      //^ impl T2
+}
+
+fn f4<G: T<Assoc: T2 + T3<()>>>(it: G) {
+    let l = it.f();
+      //^ impl T2 + T3<()>
+}
+
+fn f5<G: T<Assoc = ()>>(it: G) {
+    let l = it.f();
+      //^ ()
+}
+"#,
+        );
+    }
 }
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/binding_mode.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/binding_mode.rs
index 5afb98cb1c7..5bbb4fe4e66 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/binding_mode.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/binding_mode.rs
@@ -99,17 +99,24 @@ pub(super) fn hints(
     }
 
     if let hints @ [_, ..] = &mut acc[acc_base..] {
-        let mut edit = TextEditBuilder::default();
-        for h in &mut *hints {
-            edit.insert(
-                match h.position {
-                    InlayHintPosition::Before => h.range.start(),
-                    InlayHintPosition::After => h.range.end(),
-                },
-                h.label.parts.iter().map(|p| &*p.text).collect(),
-            );
-        }
-        let edit = edit.finish();
+        let edit = config.lazy_text_edit(|| {
+            let mut edit = TextEditBuilder::default();
+            for h in &mut *hints {
+                edit.insert(
+                    match h.position {
+                        InlayHintPosition::Before => h.range.start(),
+                        InlayHintPosition::After => h.range.end(),
+                    },
+                    h.label
+                        .parts
+                        .iter()
+                        .map(|p| &*p.text)
+                        .chain(h.pad_right.then_some(" "))
+                        .collect(),
+                );
+            }
+            edit.finish()
+        });
         hints.iter_mut().for_each(|h| h.text_edit = Some(edit.clone()));
     }
 
@@ -118,8 +125,10 @@ pub(super) fn hints(
 
 #[cfg(test)]
 mod tests {
+    use expect_test::expect;
+
     use crate::{
-        inlay_hints::tests::{check_with_config, DISABLED_CONFIG},
+        inlay_hints::tests::{check_edit, check_with_config, DISABLED_CONFIG},
         InlayHintsConfig,
     };
 
@@ -194,4 +203,27 @@ fn foo(s @ Struct { field, .. }: &Struct) {}
 "#,
         );
     }
+
+    #[test]
+    fn edits() {
+        check_edit(
+            InlayHintsConfig { binding_mode_hints: true, ..DISABLED_CONFIG },
+            r#"
+fn main() {
+    match &(0,) {
+        (x,) | (x,) => (),
+        ((x,) | (x,)) => (),
+    }
+}
+"#,
+            expect![[r#"
+                fn main() {
+                    match &(0,) {
+                        &(&((ref x,) | (ref x,))) => (),
+                        &((ref x,) | (ref x,)) => (),
+                    }
+                }
+            "#]],
+        );
+    }
 }
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bounds.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bounds.rs
new file mode 100644
index 00000000000..429ddd31cbd
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bounds.rs
@@ -0,0 +1,152 @@
+//! Implementation of trait bound hints.
+//!
+//! Currently this renders the implied `Sized` bound.
+use ide_db::{famous_defs::FamousDefs, FileRange};
+
+use span::EditionedFileId;
+use syntax::ast::{self, AstNode, HasTypeBounds};
+
+use crate::{
+    InlayHint, InlayHintLabel, InlayHintLabelPart, InlayHintPosition, InlayHintsConfig, InlayKind,
+    TryToNav,
+};
+
+pub(super) fn hints(
+    acc: &mut Vec<InlayHint>,
+    famous_defs @ FamousDefs(sema, _): &FamousDefs<'_, '_>,
+    config: &InlayHintsConfig,
+    _file_id: EditionedFileId,
+    params: ast::GenericParamList,
+) -> Option<()> {
+    if !config.sized_bound {
+        return None;
+    }
+
+    let linked_location =
+        famous_defs.core_marker_Sized().and_then(|it| it.try_to_nav(sema.db)).map(|it| {
+            let n = it.call_site();
+            FileRange { file_id: n.file_id, range: n.focus_or_full_range() }
+        });
+
+    for param in params.type_or_const_params() {
+        match param {
+            ast::TypeOrConstParam::Type(type_param) => {
+                let c = type_param.colon_token().map(|it| it.text_range());
+                let has_bounds =
+                    type_param.type_bound_list().is_some_and(|it| it.bounds().next().is_some());
+                acc.push(InlayHint {
+                    range: c.unwrap_or_else(|| type_param.syntax().text_range()),
+                    kind: InlayKind::Type,
+                    label: {
+                        let mut hint = InlayHintLabel::default();
+                        if c.is_none() {
+                            hint.parts.push(InlayHintLabelPart {
+                                text: ": ".to_owned(),
+                                linked_location: None,
+                                tooltip: None,
+                            });
+                        }
+                        hint.parts.push(InlayHintLabelPart {
+                            text: "Sized".to_owned(),
+                            linked_location,
+                            tooltip: None,
+                        });
+                        if has_bounds {
+                            hint.parts.push(InlayHintLabelPart {
+                                text: " +".to_owned(),
+                                linked_location: None,
+                                tooltip: None,
+                            });
+                        }
+                        hint
+                    },
+                    text_edit: None,
+                    position: InlayHintPosition::After,
+                    pad_left: c.is_some(),
+                    pad_right: has_bounds,
+                    resolve_parent: Some(params.syntax().text_range()),
+                });
+            }
+            ast::TypeOrConstParam::Const(_) => (),
+        }
+    }
+
+    Some(())
+}
+
+#[cfg(test)]
+mod tests {
+    use expect_test::expect;
+
+    use crate::inlay_hints::InlayHintsConfig;
+
+    use crate::inlay_hints::tests::{check_expect, check_with_config, DISABLED_CONFIG};
+
+    #[track_caller]
+    fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
+        check_with_config(InlayHintsConfig { sized_bound: true, ..DISABLED_CONFIG }, ra_fixture);
+    }
+
+    #[test]
+    fn smoke() {
+        check(
+            r#"
+fn foo<T>() {}
+    // ^ : Sized
+"#,
+        );
+    }
+
+    #[test]
+    fn with_colon() {
+        check(
+            r#"
+fn foo<T:>() {}
+     // ^ Sized
+"#,
+        );
+    }
+
+    #[test]
+    fn with_colon_and_bounds() {
+        check(
+            r#"
+fn foo<T: 'static>() {}
+     // ^ Sized +
+"#,
+        );
+    }
+
+    #[test]
+    fn location_works() {
+        check_expect(
+            InlayHintsConfig { sized_bound: true, ..DISABLED_CONFIG },
+            r#"
+//- minicore: sized
+fn foo<T>() {}
+"#,
+            expect![[r#"
+                [
+                    (
+                        7..8,
+                        [
+                            ": ",
+                            InlayHintLabelPart {
+                                text: "Sized",
+                                linked_location: Some(
+                                    FileRangeWrapper {
+                                        file_id: FileId(
+                                            1,
+                                        ),
+                                        range: 135..140,
+                                    },
+                                ),
+                                tooltip: "",
+                            },
+                        ],
+                    ),
+                ]
+            "#]],
+        );
+    }
+}
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 028ed1650f4..7fa7ab1a94d 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
@@ -81,28 +81,19 @@ mod tests {
 
     use crate::{
         fixture,
-        inlay_hints::tests::{check_with_config, DISABLED_CONFIG, TEST_CONFIG},
+        inlay_hints::tests::{check_expect, check_with_config, DISABLED_CONFIG, TEST_CONFIG},
         InlayHintsConfig,
     };
 
     #[track_caller]
-    fn check_chains(ra_fixture: &str) {
+    fn check_chains(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
         check_with_config(InlayHintsConfig { chaining_hints: true, ..DISABLED_CONFIG }, ra_fixture);
     }
 
     #[track_caller]
-    pub(super) fn check_expect(config: InlayHintsConfig, ra_fixture: &str, expect: Expect) {
-        let (analysis, file_id) = fixture::file(ra_fixture);
-        let inlay_hints = analysis.inlay_hints(&config, file_id, None).unwrap();
-        let filtered =
-            inlay_hints.into_iter().map(|hint| (hint.range, hint.label)).collect::<Vec<_>>();
-        expect.assert_debug_eq(&filtered)
-    }
-
-    #[track_caller]
     pub(super) fn check_expect_clear_loc(
         config: InlayHintsConfig,
-        ra_fixture: &str,
+        #[rust_analyzer::rust_fixture] ra_fixture: &str,
         expect: Expect,
     ) {
         let (analysis, file_id) = fixture::file(ra_fixture);
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 6827540fa82..7858b1d90a3 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
@@ -52,13 +52,14 @@ pub(super) fn hints(
     let text_edit = if has_block_body {
         ty_to_text_edit(
             sema,
+            config,
             closure.syntax(),
             &ty,
             arrow
                 .as_ref()
                 .map_or_else(|| param_list.syntax().text_range(), |t| t.text_range())
                 .end(),
-            if arrow.is_none() { String::from(" -> ") } else { String::new() },
+            if arrow.is_none() { " -> " } else { "" },
         )
     } else {
         None
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/discriminant.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/discriminant.rs
index 8f2949cb387..f1e1955d14c 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/discriminant.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/discriminant.rs
@@ -36,13 +36,14 @@ pub(super) fn enum_hints(
         return None;
     }
     for variant in enum_.variant_list()?.variants() {
-        variant_hints(acc, sema, &enum_, &variant);
+        variant_hints(acc, config, sema, &enum_, &variant);
     }
     Some(())
 }
 
 fn variant_hints(
     acc: &mut Vec<InlayHint>,
+    config: &InlayHintsConfig,
     sema: &Semantics<'_, RootDatabase>,
     enum_: &ast::Enum,
     variant: &ast::Variant,
@@ -75,9 +76,11 @@ fn variant_hints(
             }
             Err(_) => format!("{eq_} ?"),
         },
-        Some(InlayTooltip::String(match &d {
-            Ok(_) => "enum variant discriminant".into(),
-            Err(e) => format!("{e:?}"),
+        Some(config.lazy_tooltip(|| {
+            InlayTooltip::String(match &d {
+                Ok(_) => "enum variant discriminant".into(),
+                Err(e) => format!("{e:?}"),
+            })
         })),
         None,
     );
@@ -88,7 +91,9 @@ fn variant_hints(
         },
         kind: InlayKind::Discriminant,
         label,
-        text_edit: d.ok().map(|val| TextEdit::insert(range.start(), format!("{eq_} {val}"))),
+        text_edit: d.ok().map(|val| {
+            config.lazy_text_edit(|| TextEdit::insert(range.end(), format!("{eq_} {val}")))
+        }),
         position: InlayHintPosition::After,
         pad_left: false,
         pad_right: false,
@@ -99,13 +104,15 @@ fn variant_hints(
 }
 #[cfg(test)]
 mod tests {
+    use expect_test::expect;
+
     use crate::inlay_hints::{
-        tests::{check_with_config, DISABLED_CONFIG},
+        tests::{check_edit, check_with_config, DISABLED_CONFIG},
         DiscriminantHints, InlayHintsConfig,
     };
 
     #[track_caller]
-    fn check_discriminants(ra_fixture: &str) {
+    fn check_discriminants(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
         check_with_config(
             InlayHintsConfig { discriminant_hints: DiscriminantHints::Always, ..DISABLED_CONFIG },
             ra_fixture,
@@ -113,7 +120,7 @@ mod tests {
     }
 
     #[track_caller]
-    fn check_discriminants_fieldless(ra_fixture: &str) {
+    fn check_discriminants_fieldless(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
         check_with_config(
             InlayHintsConfig {
                 discriminant_hints: DiscriminantHints::Fieldless,
@@ -207,4 +214,33 @@ enum Enum {
 "#,
         );
     }
+
+    #[test]
+    fn edit() {
+        check_edit(
+            InlayHintsConfig { discriminant_hints: DiscriminantHints::Always, ..DISABLED_CONFIG },
+            r#"
+#[repr(u8)]
+enum Enum {
+    Variant(),
+    Variant1,
+    Variant2 {},
+    Variant3,
+    Variant5,
+    Variant6,
+}
+"#,
+            expect![[r#"
+                #[repr(u8)]
+                enum Enum {
+                    Variant() = 0,
+                    Variant1 = 1,
+                    Variant2 {} = 2,
+                    Variant3 = 3,
+                    Variant5 = 4,
+                    Variant6 = 5,
+                }
+            "#]],
+        );
+    }
 }
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/extern_block.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/extern_block.rs
index 4cc4925cda6..2bc91b68ed8 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/extern_block.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/extern_block.rs
@@ -8,7 +8,7 @@ use crate::{InlayHint, InlayHintsConfig};
 pub(super) fn extern_block_hints(
     acc: &mut Vec<InlayHint>,
     FamousDefs(_sema, _): &FamousDefs<'_, '_>,
-    _config: &InlayHintsConfig,
+    config: &InlayHintsConfig,
     _file_id: EditionedFileId,
     extern_block: ast::ExternBlock,
 ) -> Option<()> {
@@ -23,7 +23,9 @@ pub(super) fn extern_block_hints(
         pad_right: true,
         kind: crate::InlayKind::ExternUnsafety,
         label: crate::InlayHintLabel::from("unsafe"),
-        text_edit: Some(TextEdit::insert(abi.syntax().text_range().start(), "unsafe ".to_owned())),
+        text_edit: Some(config.lazy_text_edit(|| {
+            TextEdit::insert(abi.syntax().text_range().start(), "unsafe ".to_owned())
+        })),
         resolve_parent: Some(extern_block.syntax().text_range()),
     });
     Some(())
@@ -32,7 +34,7 @@ pub(super) fn extern_block_hints(
 pub(super) fn fn_hints(
     acc: &mut Vec<InlayHint>,
     FamousDefs(_sema, _): &FamousDefs<'_, '_>,
-    _config: &InlayHintsConfig,
+    config: &InlayHintsConfig,
     _file_id: EditionedFileId,
     fn_: &ast::Fn,
     extern_block: &ast::ExternBlock,
@@ -42,14 +44,14 @@ pub(super) fn fn_hints(
         return None;
     }
     let fn_ = fn_.fn_token()?;
-    acc.push(item_hint(extern_block, fn_));
+    acc.push(item_hint(config, extern_block, fn_));
     Some(())
 }
 
 pub(super) fn static_hints(
     acc: &mut Vec<InlayHint>,
     FamousDefs(_sema, _): &FamousDefs<'_, '_>,
-    _config: &InlayHintsConfig,
+    config: &InlayHintsConfig,
     _file_id: EditionedFileId,
     static_: &ast::Static,
     extern_block: &ast::ExternBlock,
@@ -59,11 +61,15 @@ pub(super) fn static_hints(
         return None;
     }
     let static_ = static_.static_token()?;
-    acc.push(item_hint(extern_block, static_));
+    acc.push(item_hint(config, extern_block, static_));
     Some(())
 }
 
-fn item_hint(extern_block: &ast::ExternBlock, token: SyntaxToken) -> InlayHint {
+fn item_hint(
+    config: &InlayHintsConfig,
+    extern_block: &ast::ExternBlock,
+    token: SyntaxToken,
+) -> InlayHint {
     InlayHint {
         range: token.text_range(),
         position: crate::InlayHintPosition::Before,
@@ -71,7 +77,7 @@ fn item_hint(extern_block: &ast::ExternBlock, token: SyntaxToken) -> InlayHint {
         pad_right: true,
         kind: crate::InlayKind::ExternUnsafety,
         label: crate::InlayHintLabel::from("unsafe"),
-        text_edit: {
+        text_edit: Some(config.lazy_text_edit(|| {
             let mut builder = TextEdit::builder();
             builder.insert(token.text_range().start(), "unsafe ".to_owned());
             if extern_block.unsafe_token().is_none() {
@@ -79,8 +85,8 @@ fn item_hint(extern_block: &ast::ExternBlock, token: SyntaxToken) -> InlayHint {
                     builder.insert(abi.syntax().text_range().start(), "unsafe ".to_owned());
                 }
             }
-            Some(builder.finish())
-        },
+            builder.finish()
+        })),
         resolve_parent: Some(extern_block.syntax().text_range()),
     }
 }
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/generic_param.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/generic_param.rs
index ed7ebc3b1e7..037b328d971 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/generic_param.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/generic_param.rs
@@ -142,7 +142,7 @@ mod tests {
     };
 
     #[track_caller]
-    fn generic_param_name_hints_always(ra_fixture: &str) {
+    fn generic_param_name_hints_always(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
         check_with_config(
             InlayHintsConfig {
                 generic_parameter_hints: GenericParameterHints {
@@ -157,7 +157,7 @@ mod tests {
     }
 
     #[track_caller]
-    fn generic_param_name_hints_const_only(ra_fixture: &str) {
+    fn generic_param_name_hints_const_only(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
         check_with_config(
             InlayHintsConfig {
                 generic_parameter_hints: GenericParameterHints {
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_drop.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_drop.rs
index dd4b3efeecf..1358d3722f8 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_drop.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_drop.rs
@@ -108,7 +108,7 @@ pub(super) fn hints(
             }
             let mut label = InlayHintLabel::simple(
                 name,
-                Some(crate::InlayTooltip::String("moz".into())),
+                Some(config.lazy_tooltip(|| crate::InlayTooltip::String("moz".into()))),
                 binding_source,
             );
             label.prepend_str("drop(");
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_static.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_static.rs
index 1560df37d0d..ae5b519b43d 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_static.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_static.rs
@@ -39,7 +39,9 @@ pub(super) fn hints(
                 range: t.text_range(),
                 kind: InlayKind::Lifetime,
                 label: "'static".into(),
-                text_edit: Some(TextEdit::insert(t.text_range().start(), "'static ".into())),
+                text_edit: Some(config.lazy_text_edit(|| {
+                    TextEdit::insert(t.text_range().start(), "'static ".into())
+                })),
                 position: InlayHintPosition::After,
                 pad_left: false,
                 pad_right: true,
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs
index a03ff6a52b4..a7b066700c5 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs
@@ -269,7 +269,7 @@ mod tests {
     };
 
     #[track_caller]
-    fn check_params(ra_fixture: &str) {
+    fn check_params(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
         check_with_config(
             InlayHintsConfig { parameter_hints: true, ..DISABLED_CONFIG },
             ra_fixture,
diff --git a/src/tools/rust-analyzer/crates/ide/src/join_lines.rs b/src/tools/rust-analyzer/crates/ide/src/join_lines.rs
index 5192f91a4a6..e4670177ecf 100644
--- a/src/tools/rust-analyzer/crates/ide/src/join_lines.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/join_lines.rs
@@ -307,7 +307,10 @@ mod tests {
 
     use super::*;
 
-    fn check_join_lines(ra_fixture_before: &str, ra_fixture_after: &str) {
+    fn check_join_lines(
+        #[rust_analyzer::rust_fixture] ra_fixture_before: &str,
+        #[rust_analyzer::rust_fixture] ra_fixture_after: &str,
+    ) {
         let config = JoinLinesConfig {
             join_else_if: true,
             remove_trailing_comma: true,
@@ -333,7 +336,10 @@ mod tests {
         assert_eq_text!(ra_fixture_after, &actual);
     }
 
-    fn check_join_lines_sel(ra_fixture_before: &str, ra_fixture_after: &str) {
+    fn check_join_lines_sel(
+        #[rust_analyzer::rust_fixture] ra_fixture_before: &str,
+        #[rust_analyzer::rust_fixture] ra_fixture_after: &str,
+    ) {
         let config = JoinLinesConfig {
             join_else_if: true,
             remove_trailing_comma: true,
diff --git a/src/tools/rust-analyzer/crates/ide/src/lib.rs b/src/tools/rust-analyzer/crates/ide/src/lib.rs
index 6e7c718953c..346e2862b0f 100644
--- a/src/tools/rust-analyzer/crates/ide/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/lib.rs
@@ -48,7 +48,6 @@ mod ssr;
 mod static_index;
 mod status;
 mod syntax_highlighting;
-mod syntax_tree;
 mod test_explorer;
 mod typing;
 mod view_crate_graph;
@@ -56,6 +55,7 @@ mod view_hir;
 mod view_item_tree;
 mod view_memory_layout;
 mod view_mir;
+mod view_syntax_tree;
 
 use std::{iter, panic::UnwindSafe};
 
@@ -120,7 +120,7 @@ pub use ide_assists::{
 };
 pub use ide_completion::{
     CallableSnippets, CompletionConfig, CompletionFieldsToResolve, CompletionItem,
-    CompletionItemKind, CompletionRelevance, Snippet, SnippetScope,
+    CompletionItemKind, CompletionItemRefMode, CompletionRelevance, Snippet, SnippetScope,
 };
 pub use ide_db::text_edit::{Indel, TextEdit};
 pub use ide_db::{
@@ -329,14 +329,8 @@ impl Analysis {
         })
     }
 
-    /// Returns a syntax tree represented as `String`, for debug purposes.
-    // FIXME: use a better name here.
-    pub fn syntax_tree(
-        &self,
-        file_id: FileId,
-        text_range: Option<TextRange>,
-    ) -> Cancellable<String> {
-        self.with_db(|db| syntax_tree::syntax_tree(db, file_id, text_range))
+    pub fn view_syntax_tree(&self, file_id: FileId) -> Cancellable<String> {
+        self.with_db(|db| view_syntax_tree::view_syntax_tree(db, file_id))
     }
 
     pub fn view_hir(&self, position: FilePosition) -> Cancellable<String> {
@@ -410,17 +404,11 @@ impl Analysis {
         &self,
         position: FilePosition,
         char_typed: char,
-        chars_to_exclude: Option<String>,
     ) -> Cancellable<Option<SourceChange>> {
         // Fast path to not even parse the file.
         if !typing::TRIGGER_CHARS.contains(char_typed) {
             return Ok(None);
         }
-        if let Some(chars_to_exclude) = chars_to_exclude {
-            if chars_to_exclude.contains(char_typed) {
-                return Ok(None);
-            }
-        }
 
         self.with_db(|db| typing::on_char_typed(db, position, char_typed))
     }
@@ -588,17 +576,17 @@ impl Analysis {
         self.with_db(|db| parent_module::parent_module(db, position))
     }
 
-    /// Returns crates this file belongs too.
+    /// Returns crates that this file belongs to.
     pub fn crates_for(&self, file_id: FileId) -> Cancellable<Vec<CrateId>> {
         self.with_db(|db| parent_module::crates_for(db, file_id))
     }
 
-    /// Returns crates this file belongs too.
+    /// Returns crates that this file belongs to.
     pub fn transitive_rev_deps(&self, crate_id: CrateId) -> Cancellable<Vec<CrateId>> {
         self.with_db(|db| db.crate_graph().transitive_rev_deps(crate_id).collect())
     }
 
-    /// Returns crates this file *might* belong too.
+    /// Returns crates that this file *might* belong to.
     pub fn relevant_crates_for(&self, file_id: FileId) -> Cancellable<Vec<CrateId>> {
         self.with_db(|db| db.relevant_crates(file_id).iter().copied().collect())
     }
diff --git a/src/tools/rust-analyzer/crates/ide/src/moniker.rs b/src/tools/rust-analyzer/crates/ide/src/moniker.rs
index 052466725fa..d97c12ebafb 100644
--- a/src/tools/rust-analyzer/crates/ide/src/moniker.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/moniker.rs
@@ -191,7 +191,7 @@ pub(crate) fn def_to_kind(db: &RootDatabase, def: Definition) -> SymbolInformati
             MacroKind::ProcMacro => Macro,
         },
         Definition::Field(..) | Definition::TupleField(..) => Field,
-        Definition::Module(..) => Module,
+        Definition::Module(..) | Definition::Crate(..) => Module,
         Definition::Function(it) => {
             if it.as_assoc_item(db).is_some() {
                 if it.has_self_param(db) {
@@ -405,7 +405,7 @@ mod tests {
 
     #[allow(dead_code)]
     #[track_caller]
-    fn no_moniker(ra_fixture: &str) {
+    fn no_moniker(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
         let (analysis, position) = fixture::position(ra_fixture);
         if let Some(x) = analysis.moniker(position).unwrap() {
             assert_eq!(x.info.len(), 0, "Moniker found but no moniker expected: {x:?}");
@@ -413,7 +413,12 @@ mod tests {
     }
 
     #[track_caller]
-    fn check_local_moniker(ra_fixture: &str, identifier: &str, package: &str, kind: MonikerKind) {
+    fn check_local_moniker(
+        #[rust_analyzer::rust_fixture] ra_fixture: &str,
+        identifier: &str,
+        package: &str,
+        kind: MonikerKind,
+    ) {
         let (analysis, position) = fixture::position(ra_fixture);
         let x = analysis.moniker(position).unwrap().expect("no moniker found").info;
         assert_eq!(x.len(), 1);
@@ -433,7 +438,12 @@ mod tests {
     }
 
     #[track_caller]
-    fn check_moniker(ra_fixture: &str, identifier: &str, package: &str, kind: MonikerKind) {
+    fn check_moniker(
+        #[rust_analyzer::rust_fixture] ra_fixture: &str,
+        identifier: &str,
+        package: &str,
+        kind: MonikerKind,
+    ) {
         let (analysis, position) = fixture::position(ra_fixture);
         let x = analysis.moniker(position).unwrap().expect("no moniker found").info;
         assert_eq!(x.len(), 1);
diff --git a/src/tools/rust-analyzer/crates/ide/src/move_item.rs b/src/tools/rust-analyzer/crates/ide/src/move_item.rs
index a232df2b82b..b0df9257ba1 100644
--- a/src/tools/rust-analyzer/crates/ide/src/move_item.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/move_item.rs
@@ -180,7 +180,11 @@ mod tests {
 
     use crate::Direction;
 
-    fn check(ra_fixture: &str, expect: Expect, direction: Direction) {
+    fn check(
+        #[rust_analyzer::rust_fixture] ra_fixture: &str,
+        expect: Expect,
+        direction: Direction,
+    ) {
         let (analysis, range) = fixture::range(ra_fixture);
         let edit = analysis.move_item(range, direction).unwrap().unwrap_or_default();
         let mut file = analysis.file_text(range.file_id).unwrap().to_string();
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 9259243db85..d9f80cb53dd 100644
--- a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs
@@ -44,13 +44,16 @@ pub struct NavigationTarget {
     ///
     /// This range must be contained within [`Self::full_range`].
     pub focus_range: Option<TextRange>,
+    // FIXME: Symbol
     pub name: SmolStr,
     pub kind: Option<SymbolKind>,
+    // FIXME: Symbol
     pub container_name: Option<SmolStr>,
     pub description: Option<String>,
     pub docs: Option<Documentation>,
     /// In addition to a `name` field, a `NavigationTarget` may also be aliased
     /// In such cases we want a `NavigationTarget` to be accessible by its alias
+    // FIXME: Symbol
     pub alias: Option<SmolStr>,
 }
 
@@ -191,11 +194,11 @@ impl TryToNav for FileSymbol {
                 NavigationTarget {
                     file_id,
                     name: self.is_alias.then(|| self.def.name(db)).flatten().map_or_else(
-                        || self.name.clone(),
+                        || self.name.as_str().into(),
                         |it| it.display_no_db(edition).to_smolstr(),
                     ),
-                    alias: self.is_alias.then(|| self.name.clone()),
-                    kind: Some(hir::ModuleDefId::from(self.def).into()),
+                    alias: self.is_alias.then(|| self.name.as_str().into()),
+                    kind: Some(self.def.into()),
                     full_range,
                     focus_range,
                     container_name: self.container_name.clone(),
@@ -225,6 +228,7 @@ impl TryToNav for Definition {
             Definition::Local(it) => Some(it.to_nav(db)),
             Definition::Label(it) => it.try_to_nav(db),
             Definition::Module(it) => Some(it.to_nav(db)),
+            Definition::Crate(it) => Some(it.to_nav(db)),
             Definition::Macro(it) => it.try_to_nav(db),
             Definition::Field(it) => it.try_to_nav(db),
             Definition::SelfType(it) => it.try_to_nav(db),
@@ -398,6 +402,12 @@ impl ToNav for hir::Module {
     }
 }
 
+impl ToNav for hir::Crate {
+    fn to_nav(&self, db: &RootDatabase) -> UpmappingResult<NavigationTarget> {
+        self.root_module().to_nav(db)
+    }
+}
+
 impl TryToNav for hir::Impl {
     fn try_to_nav(&self, db: &RootDatabase) -> Option<UpmappingResult<NavigationTarget>> {
         let InFile { file_id, value } = self.source(db)?;
diff --git a/src/tools/rust-analyzer/crates/ide/src/parent_module.rs b/src/tools/rust-analyzer/crates/ide/src/parent_module.rs
index b51a5cc4f4c..7a0c28d925a 100644
--- a/src/tools/rust-analyzer/crates/ide/src/parent_module.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/parent_module.rs
@@ -70,7 +70,7 @@ mod tests {
 
     use crate::fixture;
 
-    fn check(ra_fixture: &str) {
+    fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
         let (analysis, position, expected) = fixture::annotations(ra_fixture);
         let navs = analysis.parent_module(position).unwrap();
         let navs = navs
diff --git a/src/tools/rust-analyzer/crates/ide/src/references.rs b/src/tools/rust-analyzer/crates/ide/src/references.rs
index 46714df8d69..b1079312d3b 100644
--- a/src/tools/rust-analyzer/crates/ide/src/references.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/references.rs
@@ -1255,11 +1255,15 @@ impl Foo {
         );
     }
 
-    fn check(ra_fixture: &str, expect: Expect) {
+    fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
         check_with_scope(ra_fixture, None, expect)
     }
 
-    fn check_with_scope(ra_fixture: &str, search_scope: Option<SearchScope>, expect: Expect) {
+    fn check_with_scope(
+        #[rust_analyzer::rust_fixture] ra_fixture: &str,
+        search_scope: Option<SearchScope>,
+        expect: Expect,
+    ) {
         let (analysis, pos) = fixture::position(ra_fixture);
         let refs = analysis.find_all_refs(pos, search_scope).unwrap().unwrap();
 
diff --git a/src/tools/rust-analyzer/crates/ide/src/rename.rs b/src/tools/rust-analyzer/crates/ide/src/rename.rs
index 11bbd99110b..ba739df3092 100644
--- a/src/tools/rust-analyzer/crates/ide/src/rename.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/rename.rs
@@ -456,7 +456,11 @@ mod tests {
     use super::{RangeInfo, RenameError};
 
     #[track_caller]
-    fn check(new_name: &str, ra_fixture_before: &str, ra_fixture_after: &str) {
+    fn check(
+        new_name: &str,
+        #[rust_analyzer::rust_fixture] ra_fixture_before: &str,
+        #[rust_analyzer::rust_fixture] ra_fixture_after: &str,
+    ) {
         let ra_fixture_after = &trim_indent(ra_fixture_after);
         let (analysis, position) = fixture::position(ra_fixture_before);
         if !ra_fixture_after.starts_with("error: ") {
@@ -494,14 +498,22 @@ mod tests {
         };
     }
 
-    fn check_expect(new_name: &str, ra_fixture: &str, expect: Expect) {
+    fn check_expect(
+        new_name: &str,
+        #[rust_analyzer::rust_fixture] ra_fixture: &str,
+        expect: Expect,
+    ) {
         let (analysis, position) = fixture::position(ra_fixture);
         let source_change =
             analysis.rename(position, new_name).unwrap().expect("Expect returned a RenameError");
         expect.assert_eq(&filter_expect(source_change))
     }
 
-    fn check_expect_will_rename_file(new_name: &str, ra_fixture: &str, expect: Expect) {
+    fn check_expect_will_rename_file(
+        new_name: &str,
+        #[rust_analyzer::rust_fixture] ra_fixture: &str,
+        expect: Expect,
+    ) {
         let (analysis, position) = fixture::position(ra_fixture);
         let source_change = analysis
             .will_rename_file(position.file_id, new_name)
@@ -510,7 +522,7 @@ mod tests {
         expect.assert_eq(&filter_expect(source_change))
     }
 
-    fn check_prepare(ra_fixture: &str, expect: Expect) {
+    fn check_prepare(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
         let (analysis, position) = fixture::position(ra_fixture);
         let result = analysis
             .prepare_rename(position)
diff --git a/src/tools/rust-analyzer/crates/ide/src/runnables.rs b/src/tools/rust-analyzer/crates/ide/src/runnables.rs
index 3e39c750b13..32edacee51c 100644
--- a/src/tools/rust-analyzer/crates/ide/src/runnables.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/runnables.rs
@@ -748,7 +748,7 @@ mod tests {
 
     use crate::fixture;
 
-    fn check(ra_fixture: &str, expect: Expect) {
+    fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
         let (analysis, position) = fixture::position(ra_fixture);
         let result = analysis
             .runnables(position.file_id)
@@ -769,7 +769,7 @@ mod tests {
         expect.assert_debug_eq(&result);
     }
 
-    fn check_tests(ra_fixture: &str, expect: Expect) {
+    fn check_tests(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
         let (analysis, position) = fixture::position(ra_fixture);
         let tests = analysis.related_tests(position, None).unwrap();
         let navigation_targets = tests.into_iter().map(|runnable| runnable.nav).collect::<Vec<_>>();
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 84ccadc8c4e..f8c60418eb0 100644
--- a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs
@@ -692,7 +692,9 @@ mod tests {
     use crate::RootDatabase;
 
     /// Creates analysis from a multi-file fixture, returns positions marked with $0.
-    pub(crate) fn position(ra_fixture: &str) -> (RootDatabase, FilePosition) {
+    pub(crate) fn position(
+        #[rust_analyzer::rust_fixture] ra_fixture: &str,
+    ) -> (RootDatabase, FilePosition) {
         let change_fixture = ChangeFixture::parse(ra_fixture);
         let mut database = RootDatabase::default();
         database.apply_change(change_fixture.change);
@@ -703,7 +705,7 @@ mod tests {
     }
 
     #[track_caller]
-    fn check(ra_fixture: &str, expect: Expect) {
+    fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
         let fixture = format!(
             r#"
 //- minicore: sized, fn
diff --git a/src/tools/rust-analyzer/crates/ide/src/ssr.rs b/src/tools/rust-analyzer/crates/ide/src/ssr.rs
index 6def28e0b74..77a011cac19 100644
--- a/src/tools/rust-analyzer/crates/ide/src/ssr.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/ssr.rs
@@ -67,7 +67,10 @@ mod tests {
 
     use super::ssr_assists;
 
-    fn get_assists(ra_fixture: &str, resolve: AssistResolveStrategy) -> Vec<Assist> {
+    fn get_assists(
+        #[rust_analyzer::rust_fixture] ra_fixture: &str,
+        resolve: AssistResolveStrategy,
+    ) -> Vec<Assist> {
         let (mut db, file_id, range_or_offset) = RootDatabase::with_range_or_offset(ra_fixture);
         let mut local_roots = FxHashSet::default();
         local_roots.insert(test_fixture::WORKSPACE);
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 700e166b238..8050a38b3ca 100644
--- a/src/tools/rust-analyzer/crates/ide/src/static_index.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/static_index.rs
@@ -138,6 +138,7 @@ impl StaticIndex<'_> {
                     render_colons: true,
                     discriminant_hints: crate::DiscriminantHints::Fieldless,
                     type_hints: true,
+                    sized_bound: false,
                     parameter_hints: true,
                     generic_parameter_hints: crate::GenericParameterHints {
                         type_hints: false,
@@ -290,7 +291,10 @@ mod tests {
 
     use super::VendoredLibrariesConfig;
 
-    fn check_all_ranges(ra_fixture: &str, vendored_libs_config: VendoredLibrariesConfig<'_>) {
+    fn check_all_ranges(
+        #[rust_analyzer::rust_fixture] ra_fixture: &str,
+        vendored_libs_config: VendoredLibrariesConfig<'_>,
+    ) {
         let (analysis, ranges) = fixture::annotations_without_marker(ra_fixture);
         let s = StaticIndex::compute(&analysis, vendored_libs_config);
         let mut range_set: FxHashSet<_> = ranges.iter().map(|it| it.0).collect();
@@ -309,7 +313,10 @@ mod tests {
     }
 
     #[track_caller]
-    fn check_definitions(ra_fixture: &str, vendored_libs_config: VendoredLibrariesConfig<'_>) {
+    fn check_definitions(
+        #[rust_analyzer::rust_fixture] ra_fixture: &str,
+        vendored_libs_config: VendoredLibrariesConfig<'_>,
+    ) {
         let (analysis, ranges) = fixture::annotations_without_marker(ra_fixture);
         let s = StaticIndex::compute(&analysis, vendored_libs_config);
         let mut range_set: FxHashSet<_> = ranges.iter().map(|it| it.0).collect();
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 4f3d5d9d00c..22a2fe4e9eb 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
@@ -386,6 +386,9 @@ pub(super) fn highlight_def(
         Definition::Field(_) | Definition::TupleField(_) => {
             Highlight::new(HlTag::Symbol(SymbolKind::Field))
         }
+        Definition::Crate(_) => {
+            Highlight::new(HlTag::Symbol(SymbolKind::Module)) | HlMod::CrateRoot
+        }
         Definition::Module(module) => {
             let mut h = Highlight::new(HlTag::Symbol(SymbolKind::Module));
             if module.is_crate_root() {
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs
index 0a157c157c3..1be90ad6a1e 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs
@@ -28,7 +28,14 @@ pub(super) fn ra_fixture(
     expanded: &ast::String,
 ) -> Option<()> {
     let active_parameter = ActiveParameter::at_token(sema, expanded.syntax().clone())?;
-    if !active_parameter.ident().is_some_and(|name| name.text().starts_with("ra_fixture")) {
+    let has_rust_fixture_attr = active_parameter.attrs().is_some_and(|attrs| {
+        attrs.filter_map(|attr| attr.as_simple_path()).any(|path| {
+            path.segments()
+                .zip(["rust_analyzer", "rust_fixture"])
+                .all(|(seg, name)| seg.name_ref().map_or(false, |nr| nr.text() == name))
+        })
+    });
+    if !has_rust_fixture_attr {
         return None;
     }
     let value = literal.value().ok()?;
@@ -287,7 +294,9 @@ fn find_doc_string_in_attr(attr: &hir::Attr, it: &ast::Attr) -> Option<ast::Stri
 
 fn module_def_to_hl_tag(def: Definition) -> HlTag {
     let symbol = match def {
-        Definition::Module(_) | Definition::ExternCrateDecl(_) => SymbolKind::Module,
+        Definition::Module(_) | Definition::Crate(_) | Definition::ExternCrateDecl(_) => {
+            SymbolKind::Module
+        }
         Definition::Function(_) => SymbolKind::Function,
         Definition::Adt(hir::Adt::Struct(_)) => SymbolKind::Struct,
         Definition::Adt(hir::Adt::Enum(_)) => SymbolKind::Enum,
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 cad5a8b593f..485d44f97e1 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
@@ -46,7 +46,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .unresolved_reference    { color: #FC5555; text-decoration: wavy underline; }
 </style>
 <pre><code><span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute">allow</span><span class="parenthesis attribute">(</span><span class="none attribute">dead_code</span><span class="parenthesis attribute">)</span><span class="attribute_bracket attribute">]</span>
-<span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="tool_module attribute library">rustfmt</span><span class="operator attribute">::</span><span class="tool_module attribute library">skip</span><span class="attribute_bracket attribute">]</span>
+<span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="tool_module attribute">rustfmt</span><span class="operator attribute">::</span><span class="tool_module attribute">skip</span><span class="attribute_bracket attribute">]</span>
 <span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="module attribute crate_root library">proc_macros</span><span class="operator attribute">::</span><span class="attribute attribute library">identity</span><span class="attribute_bracket attribute">]</span>
 <span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="attribute attribute default_library library">derive</span><span class="parenthesis attribute">(</span><span class="derive attribute default_library library">Default</span><span class="parenthesis attribute">)</span><span class="attribute_bracket attribute">]</span>
 <span class="comment documentation">/// This is a doc comment</span>
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 edd9639768a..c6eab90e42b 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
@@ -45,7 +45,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; }
 .unresolved_reference    { color: #FC5555; text-decoration: wavy underline; }
 </style>
-<pre><code><span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">foo</span> <span class="brace">{</span>
+<pre><code><span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration public">foo</span> <span class="brace">{</span>
     <span class="parenthesis">(</span><span class="punctuation">$</span>foo<span class="colon">:</span>ident<span class="parenthesis">)</span> <span class="operator">=</span><span class="operator">&gt;</span> <span class="brace">{</span>
         <span class="keyword">mod</span> y <span class="brace">{</span>
             <span class="keyword">pub</span> <span class="keyword">struct</span> <span class="punctuation">$</span>foo<span class="semicolon">;</span>
@@ -53,9 +53,9 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
     <span class="brace">}</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="macro">foo</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="struct declaration macro public">Foo</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+    <span class="macro public">foo</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="struct declaration macro public">Foo</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
     <span class="keyword">mod</span> <span class="module declaration">module</span> <span class="brace">{</span>
-        <span class="macro">foo</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="struct declaration macro public">Bar</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+        <span class="macro public">foo</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="struct declaration macro public">Bar</span><span class="parenthesis macro">)</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>
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 05289cfe3fe..96cdb532dd5 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
@@ -45,7 +45,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; }
 .unresolved_reference    { color: #FC5555; text-decoration: wavy underline; }
 </style>
-<pre><code><span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">id</span> <span class="brace">{</span>
+<pre><code><span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration public">id</span> <span class="brace">{</span>
     <span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>tt<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">*</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>tt<span class="parenthesis">)</span><span class="punctuation">*</span>
     <span class="brace">}</span><span class="semicolon">;</span>
@@ -57,7 +57,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
     <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="brace">}</span>
-    <span class="macro">id</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span>
+    <span class="macro public">id</span><span class="macro_bang">!</span><span class="parenthesis macro">(</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>
@@ -78,7 +78,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
     <span class="keyword const">const</span> <span class="keyword">fn</span> <span class="function associated const declaration static trait">assoc_const_fn</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
 <span class="brace">}</span>
 
-<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">unsafe_deref</span> <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="brace">}</span><span class="semicolon">;</span>
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 aa9d23250c1..5ff96ae2a74 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
@@ -147,10 +147,10 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 <span class="brace">}</span>
 
 <span class="comment documentation">/// ```</span>
-<span class="comment documentation">///</span><span class="comment documentation"> </span><span class="keyword injected">macro_rules</span><span class="macro_bang injected">!</span><span class="none injected"> </span><span class="macro declaration injected">noop</span><span class="none injected"> </span><span class="brace injected">{</span><span class="none injected"> </span><span class="parenthesis injected">(</span><span class="punctuation injected">$</span><span class="none injected">expr</span><span class="colon injected">:</span><span class="none injected">expr</span><span class="parenthesis injected">)</span><span class="none injected"> </span><span class="operator injected">=</span><span class="operator injected">&gt;</span><span class="none injected"> </span><span class="brace injected">{</span><span class="none injected"> </span><span class="punctuation injected">$</span><span class="none injected">expr </span><span class="brace injected">}</span><span class="brace injected">}</span>
-<span class="comment documentation">///</span><span class="comment documentation"> </span><span class="macro injected">noop</span><span class="macro_bang injected">!</span><span class="parenthesis injected macro">(</span><span class="numeric_literal injected macro">1</span><span class="parenthesis injected macro">)</span><span class="semicolon injected">;</span>
+<span class="comment documentation">///</span><span class="comment documentation"> </span><span class="keyword injected">macro_rules</span><span class="macro_bang injected">!</span><span class="none injected"> </span><span class="macro declaration injected public">noop</span><span class="none injected"> </span><span class="brace injected">{</span><span class="none injected"> </span><span class="parenthesis injected">(</span><span class="punctuation injected">$</span><span class="none injected">expr</span><span class="colon injected">:</span><span class="none injected">expr</span><span class="parenthesis injected">)</span><span class="none injected"> </span><span class="operator injected">=</span><span class="operator injected">&gt;</span><span class="none injected"> </span><span class="brace injected">{</span><span class="none injected"> </span><span class="punctuation injected">$</span><span class="none injected">expr </span><span class="brace injected">}</span><span class="brace injected">}</span>
+<span class="comment documentation">///</span><span class="comment documentation"> </span><span class="macro injected public">noop</span><span class="macro_bang injected">!</span><span class="parenthesis injected macro">(</span><span class="numeric_literal injected macro">1</span><span class="parenthesis injected macro">)</span><span class="semicolon injected">;</span>
 <span class="comment documentation">/// ```</span>
-<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">noop</span> <span class="brace">{</span>
+<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration public">noop</span> <span class="brace">{</span>
     <span class="parenthesis">(</span><span class="punctuation">$</span>expr<span class="colon">:</span>expr<span class="parenthesis">)</span> <span class="operator">=</span><span class="operator">&gt;</span> <span class="brace">{</span>
         <span class="punctuation">$</span>expr
     <span class="brace">}</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 7820e4e5a5f..fe5f5ab6a9a 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html
@@ -45,7 +45,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; }
 .unresolved_reference    { color: #FC5555; text-decoration: wavy underline; }
 </style>
-<pre><code><span class="keyword">extern</span> <span class="keyword">crate</span> <span class="self_keyword crate_root public">self</span> <span class="keyword">as</span> <span class="module crate_root declaration">this</span><span class="semicolon">;</span>
+<pre><code><span class="keyword">extern</span> <span class="keyword">crate</span> <span class="self_keyword crate_root">self</span> <span class="keyword">as</span> <span class="module crate_root declaration">this</span><span class="semicolon">;</span>
 <span class="keyword">extern</span> <span class="keyword">crate</span> <span class="module crate_root default_library library">std</span><span class="semicolon">;</span>
 <span class="keyword">extern</span> <span class="keyword">crate</span> <span class="module crate_root default_library library">alloc</span> <span class="keyword">as</span> <span class="module crate_root declaration">abc</span><span class="semicolon">;</span>
 <span class="keyword">extern</span> <span class="keyword">crate</span> <span class="unresolved_reference">unresolved</span> <span class="keyword">as</span> <span class="module crate_root declaration">definitely_unresolved</span><span class="semicolon">;</span>
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 6c3fbcfcf41..5fbed35192b 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
@@ -45,7 +45,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; }
 .unresolved_reference    { color: #FC5555; text-decoration: wavy underline; }
 </style>
-<pre><code><span class="keyword">fn</span> <span class="function declaration">fixture</span><span class="parenthesis">(</span><span class="value_param declaration reference">ra_fixture</span><span class="colon">:</span> <span class="punctuation">&</span><span class="builtin_type">str</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
+<pre><code><span class="keyword">fn</span> <span class="function declaration">fixture</span><span class="parenthesis">(</span><span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="tool_module attribute">rust_analyzer</span><span class="operator attribute">::</span><span class="tool_module attribute">rust_fixture</span><span class="attribute_bracket attribute">]</span> <span class="value_param declaration reference">ra_fixture</span><span class="colon">:</span> <span class="punctuation">&</span><span class="builtin_type">str</span><span class="parenthesis">)</span> <span class="brace">{</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="function">fixture</span><span class="parenthesis">(</span><span class="string_literal">r#"</span><span class="none injected">
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 361dcd1bc37..06817af1b1f 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
@@ -46,8 +46,8 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .unresolved_reference    { color: #FC5555; text-decoration: wavy underline; }
 </style>
 <pre><code><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="macro">template</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="none macro">template</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+    <span class="macro public">template</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="none macro">template</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
 <span class="brace">}</span>
 
 <span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="module attribute crate_root library">proc_macros</span><span class="operator attribute">::</span><span class="attribute attribute library">issue_18089</span><span class="attribute_bracket attribute">]</span>
-<span class="keyword">fn</span> <span class="macro declaration">template</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span></code></pre>
\ No newline at end of file
+<span class="keyword">fn</span> <span class="macro declaration public">template</span><span class="parenthesis">(</span><span class="parenthesis">)</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/test_data/highlight_keywords_2015.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2015.html
index c2bf94fd9b6..2d3407dbcda 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
@@ -53,22 +53,22 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
     <span class="keyword">use</span> <span class="keyword crate_root public">super</span><span class="operator">::</span><span class="punctuation">*</span><span class="semicolon">;</span>
 <span class="brace">}</span>
 
-<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">void</span> <span class="brace">{</span>
+<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration public">void</span> <span class="brace">{</span>
     <span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>tt<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="operator">&gt;</span> <span class="brace">{</span><span class="brace">}</span>
 <span class="brace">}</span>
 
 <span class="keyword">struct</span> <span class="struct declaration">__</span> <span class="keyword">where</span> <span class="self_type_keyword">Self</span><span class="colon">:</span><span class="semicolon">;</span>
 <span class="keyword">fn</span> <span class="function declaration">__</span><span class="parenthesis">(</span><span class="punctuation">_</span><span class="colon">:</span> <span class="unresolved_reference">Self</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
-<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="keyword macro">Self</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="keyword macro">Self</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
 
 <span class="comment">// edition dependent</span>
-<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="none macro">try</span> <span class="none macro">async</span> <span class="none macro">await</span> <span class="none macro">gen</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="none macro">try</span> <span class="none macro">async</span> <span class="none macro">await</span> <span class="none macro">gen</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
 <span class="comment">// edition and context dependent</span>
-<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="none macro">dyn</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="none macro">dyn</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
 <span class="comment">// builtin custom syntax</span>
-<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="none macro">builtin</span> <span class="none macro">offset_of</span> <span class="none macro">format_args</span> <span class="none macro">asm</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="none macro">builtin</span> <span class="none macro">offset_of</span> <span class="none macro">format_args</span> <span class="none macro">asm</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
 <span class="comment">// contextual</span>
-<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="none macro">macro_rules</span><span class="comma macro">,</span> <span class="none macro">union</span><span class="comma macro">,</span> <span class="none macro">default</span><span class="comma macro">,</span> <span class="none macro">raw</span><span class="comma macro">,</span> <span class="none macro">auto</span><span class="comma macro">,</span> <span class="none macro">yeet</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="none macro">macro_rules</span><span class="comma macro">,</span> <span class="none macro">union</span><span class="comma macro">,</span> <span class="none macro">default</span><span class="comma macro">,</span> <span class="none macro">raw</span><span class="comma macro">,</span> <span class="none macro">auto</span><span class="comma macro">,</span> <span class="none macro">yeet</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
 <span class="comment">// reserved</span>
-<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="keyword macro">abstract</span> <span class="keyword macro">become</span> <span class="keyword macro">box</span> <span class="keyword macro">do</span> <span class="keyword macro">final</span> <span class="keyword macro">macro</span> <span class="keyword macro">override</span> <span class="keyword macro">priv</span> <span class="keyword macro">typeof</span> <span class="keyword macro">unsized</span> <span class="keyword macro">virtual</span> <span class="keyword control macro">yield</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
-<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span>'static 'self 'unsafe<span class="parenthesis macro">)</span></code></pre>
\ No newline at end of file
+<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="keyword macro">abstract</span> <span class="keyword macro">become</span> <span class="keyword macro">box</span> <span class="keyword macro">do</span> <span class="keyword macro">final</span> <span class="keyword macro">macro</span> <span class="keyword macro">override</span> <span class="keyword macro">priv</span> <span class="keyword macro">typeof</span> <span class="keyword macro">unsized</span> <span class="keyword macro">virtual</span> <span class="keyword control macro">yield</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span>'static 'self 'unsafe<span class="parenthesis macro">)</span></code></pre>
\ No newline at end of file
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 a30d16d5327..f8eb5d068a8 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
@@ -53,22 +53,22 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
     <span class="keyword">use</span> <span class="keyword crate_root public">super</span><span class="operator">::</span><span class="punctuation">*</span><span class="semicolon">;</span>
 <span class="brace">}</span>
 
-<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">void</span> <span class="brace">{</span>
+<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration public">void</span> <span class="brace">{</span>
     <span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>tt<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="operator">&gt;</span> <span class="brace">{</span><span class="brace">}</span>
 <span class="brace">}</span>
 
 <span class="keyword">struct</span> <span class="struct declaration">__</span> <span class="keyword">where</span> <span class="self_type_keyword">Self</span><span class="colon">:</span><span class="semicolon">;</span>
 <span class="keyword">fn</span> <span class="function declaration">__</span><span class="parenthesis">(</span><span class="punctuation">_</span><span class="colon">:</span> <span class="unresolved_reference">Self</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
-<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="keyword macro">Self</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="keyword macro">Self</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
 
 <span class="comment">// edition dependent</span>
-<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="keyword macro">try</span> <span class="keyword async macro">async</span> <span class="keyword async control macro">await</span> <span class="none macro">gen</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="keyword macro">try</span> <span class="keyword async macro">async</span> <span class="keyword async control macro">await</span> <span class="none macro">gen</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
 <span class="comment">// edition and context dependent</span>
-<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="keyword macro">dyn</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="keyword macro">dyn</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
 <span class="comment">// builtin custom syntax</span>
-<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="none macro">builtin</span> <span class="none macro">offset_of</span> <span class="none macro">format_args</span> <span class="none macro">asm</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="none macro">builtin</span> <span class="none macro">offset_of</span> <span class="none macro">format_args</span> <span class="none macro">asm</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
 <span class="comment">// contextual</span>
-<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="none macro">macro_rules</span><span class="comma macro">,</span> <span class="none macro">union</span><span class="comma macro">,</span> <span class="none macro">default</span><span class="comma macro">,</span> <span class="none macro">raw</span><span class="comma macro">,</span> <span class="none macro">auto</span><span class="comma macro">,</span> <span class="none macro">yeet</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="none macro">macro_rules</span><span class="comma macro">,</span> <span class="none macro">union</span><span class="comma macro">,</span> <span class="none macro">default</span><span class="comma macro">,</span> <span class="none macro">raw</span><span class="comma macro">,</span> <span class="none macro">auto</span><span class="comma macro">,</span> <span class="none macro">yeet</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
 <span class="comment">// reserved</span>
-<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="keyword macro">abstract</span> <span class="keyword macro">become</span> <span class="keyword macro">box</span> <span class="keyword macro">do</span> <span class="keyword macro">final</span> <span class="keyword macro">macro</span> <span class="keyword macro">override</span> <span class="keyword macro">priv</span> <span class="keyword macro">typeof</span> <span class="keyword macro">unsized</span> <span class="keyword macro">virtual</span> <span class="keyword control macro">yield</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
-<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span>'static 'self 'unsafe<span class="parenthesis macro">)</span></code></pre>
\ No newline at end of file
+<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="keyword macro">abstract</span> <span class="keyword macro">become</span> <span class="keyword macro">box</span> <span class="keyword macro">do</span> <span class="keyword macro">final</span> <span class="keyword macro">macro</span> <span class="keyword macro">override</span> <span class="keyword macro">priv</span> <span class="keyword macro">typeof</span> <span class="keyword macro">unsized</span> <span class="keyword macro">virtual</span> <span class="keyword control macro">yield</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span>'static 'self 'unsafe<span class="parenthesis macro">)</span></code></pre>
\ No newline at end of file
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 a30d16d5327..f8eb5d068a8 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
@@ -53,22 +53,22 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
     <span class="keyword">use</span> <span class="keyword crate_root public">super</span><span class="operator">::</span><span class="punctuation">*</span><span class="semicolon">;</span>
 <span class="brace">}</span>
 
-<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">void</span> <span class="brace">{</span>
+<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration public">void</span> <span class="brace">{</span>
     <span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>tt<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="operator">&gt;</span> <span class="brace">{</span><span class="brace">}</span>
 <span class="brace">}</span>
 
 <span class="keyword">struct</span> <span class="struct declaration">__</span> <span class="keyword">where</span> <span class="self_type_keyword">Self</span><span class="colon">:</span><span class="semicolon">;</span>
 <span class="keyword">fn</span> <span class="function declaration">__</span><span class="parenthesis">(</span><span class="punctuation">_</span><span class="colon">:</span> <span class="unresolved_reference">Self</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
-<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="keyword macro">Self</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="keyword macro">Self</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
 
 <span class="comment">// edition dependent</span>
-<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="keyword macro">try</span> <span class="keyword async macro">async</span> <span class="keyword async control macro">await</span> <span class="none macro">gen</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="keyword macro">try</span> <span class="keyword async macro">async</span> <span class="keyword async control macro">await</span> <span class="none macro">gen</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
 <span class="comment">// edition and context dependent</span>
-<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="keyword macro">dyn</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="keyword macro">dyn</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
 <span class="comment">// builtin custom syntax</span>
-<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="none macro">builtin</span> <span class="none macro">offset_of</span> <span class="none macro">format_args</span> <span class="none macro">asm</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="none macro">builtin</span> <span class="none macro">offset_of</span> <span class="none macro">format_args</span> <span class="none macro">asm</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
 <span class="comment">// contextual</span>
-<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="none macro">macro_rules</span><span class="comma macro">,</span> <span class="none macro">union</span><span class="comma macro">,</span> <span class="none macro">default</span><span class="comma macro">,</span> <span class="none macro">raw</span><span class="comma macro">,</span> <span class="none macro">auto</span><span class="comma macro">,</span> <span class="none macro">yeet</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="none macro">macro_rules</span><span class="comma macro">,</span> <span class="none macro">union</span><span class="comma macro">,</span> <span class="none macro">default</span><span class="comma macro">,</span> <span class="none macro">raw</span><span class="comma macro">,</span> <span class="none macro">auto</span><span class="comma macro">,</span> <span class="none macro">yeet</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
 <span class="comment">// reserved</span>
-<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="keyword macro">abstract</span> <span class="keyword macro">become</span> <span class="keyword macro">box</span> <span class="keyword macro">do</span> <span class="keyword macro">final</span> <span class="keyword macro">macro</span> <span class="keyword macro">override</span> <span class="keyword macro">priv</span> <span class="keyword macro">typeof</span> <span class="keyword macro">unsized</span> <span class="keyword macro">virtual</span> <span class="keyword control macro">yield</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
-<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span>'static 'self 'unsafe<span class="parenthesis macro">)</span></code></pre>
\ No newline at end of file
+<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="keyword macro">abstract</span> <span class="keyword macro">become</span> <span class="keyword macro">box</span> <span class="keyword macro">do</span> <span class="keyword macro">final</span> <span class="keyword macro">macro</span> <span class="keyword macro">override</span> <span class="keyword macro">priv</span> <span class="keyword macro">typeof</span> <span class="keyword macro">unsized</span> <span class="keyword macro">virtual</span> <span class="keyword control macro">yield</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span>'static 'self 'unsafe<span class="parenthesis macro">)</span></code></pre>
\ No newline at end of file
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 b82a3f9f819..fca84017069 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
@@ -53,22 +53,22 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
     <span class="keyword">use</span> <span class="keyword crate_root public">super</span><span class="operator">::</span><span class="punctuation">*</span><span class="semicolon">;</span>
 <span class="brace">}</span>
 
-<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">void</span> <span class="brace">{</span>
+<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration public">void</span> <span class="brace">{</span>
     <span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>tt<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="operator">&gt;</span> <span class="brace">{</span><span class="brace">}</span>
 <span class="brace">}</span>
 
 <span class="keyword">struct</span> <span class="struct declaration">__</span> <span class="keyword">where</span> <span class="self_type_keyword">Self</span><span class="colon">:</span><span class="semicolon">;</span>
 <span class="keyword">fn</span> <span class="function declaration">__</span><span class="parenthesis">(</span><span class="punctuation">_</span><span class="colon">:</span> <span class="unresolved_reference">Self</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
-<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="keyword macro">Self</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="keyword macro">Self</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
 
 <span class="comment">// edition dependent</span>
-<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="keyword macro">try</span> <span class="keyword async macro">async</span> <span class="keyword async control macro">await</span> <span class="keyword macro">gen</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="keyword macro">try</span> <span class="keyword async macro">async</span> <span class="keyword async control macro">await</span> <span class="keyword macro">gen</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
 <span class="comment">// edition and context dependent</span>
-<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="keyword macro">dyn</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="keyword macro">dyn</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
 <span class="comment">// builtin custom syntax</span>
-<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="none macro">builtin</span> <span class="none macro">offset_of</span> <span class="none macro">format_args</span> <span class="none macro">asm</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="none macro">builtin</span> <span class="none macro">offset_of</span> <span class="none macro">format_args</span> <span class="none macro">asm</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
 <span class="comment">// contextual</span>
-<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="none macro">macro_rules</span><span class="comma macro">,</span> <span class="none macro">union</span><span class="comma macro">,</span> <span class="none macro">default</span><span class="comma macro">,</span> <span class="none macro">raw</span><span class="comma macro">,</span> <span class="none macro">auto</span><span class="comma macro">,</span> <span class="none macro">yeet</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="none macro">macro_rules</span><span class="comma macro">,</span> <span class="none macro">union</span><span class="comma macro">,</span> <span class="none macro">default</span><span class="comma macro">,</span> <span class="none macro">raw</span><span class="comma macro">,</span> <span class="none macro">auto</span><span class="comma macro">,</span> <span class="none macro">yeet</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
 <span class="comment">// reserved</span>
-<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="keyword macro">abstract</span> <span class="keyword macro">become</span> <span class="keyword macro">box</span> <span class="keyword macro">do</span> <span class="keyword macro">final</span> <span class="keyword macro">macro</span> <span class="keyword macro">override</span> <span class="keyword macro">priv</span> <span class="keyword macro">typeof</span> <span class="keyword macro">unsized</span> <span class="keyword macro">virtual</span> <span class="keyword control macro">yield</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
-<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span>'static 'self 'unsafe<span class="parenthesis macro">)</span></code></pre>
\ No newline at end of file
+<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="keyword macro">abstract</span> <span class="keyword macro">become</span> <span class="keyword macro">box</span> <span class="keyword macro">do</span> <span class="keyword macro">final</span> <span class="keyword macro">macro</span> <span class="keyword macro">override</span> <span class="keyword macro">priv</span> <span class="keyword macro">typeof</span> <span class="keyword macro">unsized</span> <span class="keyword macro">virtual</span> <span class="keyword control macro">yield</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span>'static 'self 'unsafe<span class="parenthesis macro">)</span></code></pre>
\ No newline at end of file
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 06673d1a73c..f640a5e6ca7 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
@@ -53,34 +53,34 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
         <span class="comma macro proc_macro">,</span><span class="builtin_type macro proc_macro">i32</span> <span class="colon macro proc_macro">:</span><span class="field declaration macro proc_macro public">y</span> <span class="keyword macro proc_macro">pub</span>
     <span class="brace macro proc_macro">}</span> <span class="struct declaration macro proc_macro">Foo</span> <span class="keyword macro proc_macro">struct</span>
 <span class="brace macro proc_macro">}</span>
-<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">def_fn</span> <span class="brace">{</span>
+<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration public">def_fn</span> <span class="brace">{</span>
     <span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>tt<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">*</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>tt<span class="parenthesis">)</span><span class="punctuation">*</span><span class="brace">}</span>
 <span class="brace">}</span>
 
-<span class="macro">def_fn</span><span class="macro_bang">!</span> <span class="brace macro">{</span>
+<span class="macro public">def_fn</span><span class="macro_bang">!</span> <span class="brace macro">{</span>
     <span class="keyword macro">fn</span> <span class="function declaration macro">bar</span><span class="parenthesis macro">(</span><span class="parenthesis macro">)</span> <span class="operator macro">-</span><span class="operator macro">&gt;</span> <span class="builtin_type macro">u32</span> <span class="brace macro">{</span>
         <span class="numeric_literal macro">100</span>
     <span class="brace macro">}</span>
 <span class="brace macro">}</span>
 
-<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">dont_color_me_braces</span> <span class="brace">{</span>
+<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration public">dont_color_me_braces</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="numeric_literal">0</span><span class="brace">}</span>
 <span class="brace">}</span>
 
-<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">noop</span> <span class="brace">{</span>
+<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration public">noop</span> <span class="brace">{</span>
     <span class="parenthesis">(</span><span class="punctuation">$</span>expr<span class="colon">:</span>expr<span class="parenthesis">)</span> <span class="operator">=</span><span class="operator">&gt;</span> <span class="brace">{</span>
         <span class="punctuation">$</span>expr
     <span class="brace">}</span>
 <span class="brace">}</span>
 
 <span class="comment documentation">/// textually shadow previous definition</span>
-<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">noop</span> <span class="brace">{</span>
+<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration public">noop</span> <span class="brace">{</span>
     <span class="parenthesis">(</span><span class="punctuation">$</span>expr<span class="colon">:</span>expr<span class="parenthesis">)</span> <span class="operator">=</span><span class="operator">&gt;</span> <span class="brace">{</span>
         <span class="punctuation">$</span>expr
     <span class="brace">}</span>
 <span class="brace">}</span>
 
-<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">keyword_frag</span> <span class="brace">{</span>
+<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration public">keyword_frag</span> <span class="brace">{</span>
     <span class="parenthesis">(</span><span class="punctuation">$</span>type<span class="colon">:</span>ty<span class="parenthesis">)</span> <span class="operator">=</span><span class="operator">&gt;</span> <span class="parenthesis">(</span><span class="punctuation">$</span>type<span class="parenthesis">)</span>
 <span class="brace">}</span>
 
@@ -94,7 +94,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
     <span class="brace">}</span>
 <span class="brace">}</span>
 
-<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">id</span> <span class="brace">{</span>
+<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration public">id</span> <span class="brace">{</span>
     <span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>tt<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">*</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>tt<span class="parenthesis">)</span><span class="punctuation">*</span>
     <span class="brace">}</span><span class="semicolon">;</span>
@@ -106,10 +106,10 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 <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">struct</span> <span class="struct declaration">TestLocal</span><span class="semicolon">;</span>
     <span class="comment">// regression test, TestLocal here used to not resolve</span>
-    <span class="keyword">let</span> <span class="punctuation">_</span><span class="colon">:</span> <span class="struct">S</span><span class="angle">&lt;</span><span class="macro">id</span><span class="macro_bang">!</span><span class="bracket macro">[</span><span class="struct macro">TestLocal</span><span class="bracket macro">]</span><span class="angle">&gt;</span><span class="semicolon">;</span>
+    <span class="keyword">let</span> <span class="punctuation">_</span><span class="colon">:</span> <span class="struct">S</span><span class="angle">&lt;</span><span class="macro public">id</span><span class="macro_bang">!</span><span class="bracket macro">[</span><span class="struct macro">TestLocal</span><span class="bracket macro">]</span><span class="angle">&gt;</span><span class="semicolon">;</span>
 
     <span class="macro default_library library">format_args</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello, </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="parenthesis macro">(</span><span class="numeric_literal macro">92</span><span class="comma macro">,</span><span class="parenthesis macro">)</span><span class="operator macro">.</span><span class="field library macro">0</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
-    <span class="macro">dont_color_me_braces</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
-    <span class="macro">noop</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="macro macro">noop</span><span class="macro_bang macro">!</span><span class="parenthesis macro">(</span><span class="numeric_literal macro">1</span><span class="parenthesis macro">)</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+    <span class="macro public">dont_color_me_braces</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+    <span class="macro public">noop</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="macro macro public">noop</span><span class="macro_bang macro">!</span><span class="parenthesis macro">(</span><span class="numeric_literal macro">1</span><span class="parenthesis macro">)</span><span class="parenthesis macro">)</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_strings.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
index 1385ae0510a..0a7e273950d 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
@@ -45,14 +45,14 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; }
 .unresolved_reference    { color: #FC5555; text-decoration: wavy underline; }
 </style>
-<pre><code><span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">println</span> <span class="brace">{</span>
+<pre><code><span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration public">println</span> <span class="brace">{</span>
     <span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>arg<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="operator">&gt;</span> <span class="parenthesis">(</span><span class="brace">{</span>
         <span class="punctuation">$</span>crate<span class="colon">:</span><span class="colon">:</span>io<span class="colon">:</span><span class="colon">:</span>_print<span class="parenthesis">(</span>format_args_nl<span class="punctuation">!</span><span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>arg<span class="parenthesis">)</span><span class="punctuation">*</span><span class="parenthesis">)</span><span class="parenthesis">)</span><span class="semicolon">;</span>
     <span class="brace">}</span><span class="parenthesis">)</span>
 <span class="brace">}</span>
 
 <span class="keyword">mod</span> <span class="module declaration">panic</span> <span class="brace">{</span>
-    <span class="keyword">pub</span> <span class="keyword">macro</span> <span class="macro declaration">panic_2015</span> <span class="brace">{</span>
+    <span class="keyword">pub</span> <span class="keyword">macro</span> <span class="macro declaration public">panic_2015</span> <span class="brace">{</span>
         <span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">&gt;</span> <span class="parenthesis">(</span>
             panic<span class="parenthesis">(</span><span class="string_literal">"explicit panic"</span><span class="parenthesis">)</span>
         <span class="parenthesis">)</span><span class="comma">,</span>
@@ -73,12 +73,12 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
     <span class="brace">}</span>
 <span class="brace">}</span>
 
-<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">toho</span> <span class="brace">{</span>
+<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration public">toho</span> <span class="brace">{</span>
     <span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="operator">&gt;</span> <span class="parenthesis">(</span><span class="punctuation">$</span>crate<span class="colon">:</span><span class="colon">:</span>panic<span class="punctuation">!</span><span class="parenthesis">(</span><span class="string_literal">"not yet implemented"</span><span class="parenthesis">)</span><span class="parenthesis">)</span><span class="semicolon">;</span>
     <span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>arg<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">+</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="operator">&gt;</span> <span class="parenthesis">(</span><span class="punctuation">$</span>crate<span class="colon">:</span><span class="colon">:</span>panic<span class="punctuation">!</span><span class="parenthesis">(</span><span class="string_literal">"not yet implemented: {}"</span><span class="comma">,</span> format_args<span class="punctuation">!</span><span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>arg<span class="parenthesis">)</span><span class="punctuation">+</span><span class="parenthesis">)</span><span class="parenthesis">)</span><span class="parenthesis">)</span><span class="semicolon">;</span>
 <span class="brace">}</span>
 
-<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">reuse_twice</span> <span class="brace">{</span>
+<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration public">reuse_twice</span> <span class="brace">{</span>
     <span class="parenthesis">(</span><span class="punctuation">$</span>literal<span class="colon">:</span>literal<span class="parenthesis">)</span> <span class="operator">=</span><span class="operator">&gt;</span> <span class="brace">{</span><span class="brace">{</span>stringify<span class="punctuation">!</span><span class="parenthesis">(</span><span class="punctuation">$</span>literal<span class="parenthesis">)</span><span class="semicolon">;</span> format_args<span class="punctuation">!</span><span class="parenthesis">(</span><span class="punctuation">$</span>literal<span class="parenthesis">)</span><span class="brace">}</span><span class="brace">}</span><span class="semicolon">;</span>
 <span class="brace">}</span>
 
@@ -95,74 +95,74 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 
     <span class="keyword">let</span> <span class="variable declaration">a</span> <span class="operator">=</span> <span class="byte_literal">b'</span><span class="escape_sequence">\xFF</span><span class="byte_literal">'</span><span class="semicolon">;</span>
 
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="escape_sequence">{{</span><span class="string_literal macro">Hello</span><span class="escape_sequence">}}</span><span class="string_literal macro">"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="escape_sequence">{{</span><span class="string_literal macro">Hello</span><span class="escape_sequence">}}</span><span class="string_literal macro">"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
     <span class="comment">// from https://doc.rust-lang.org/std/fmt/index.html</span>
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>                 <span class="comment">// =&gt; "Hello"</span>
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello, </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"world"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>   <span class="comment">// =&gt; "Hello, world!"</span>
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"The number is </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>   <span class="comment">// =&gt; "The number is 1"</span>
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">?</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="parenthesis macro">(</span><span class="numeric_literal macro">3</span><span class="comma macro">,</span> <span class="numeric_literal macro">4</span><span class="parenthesis macro">)</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>          <span class="comment">// =&gt; "(3, 4)"</span>
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">value</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="variable declaration macro">value</span><span class="operator macro">=</span><span class="numeric_literal macro">4</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>      <span class="comment">// =&gt; "4"</span>
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="comma macro">,</span> <span class="numeric_literal macro">2</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>           <span class="comment">// =&gt; "1 2"</span>
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="numeric_literal">4</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">42</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>             <span class="comment">// =&gt; "0042" with leading zerosV</span>
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="comma macro">,</span> <span class="numeric_literal macro">2</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>   <span class="comment">// =&gt; "2 1 1 2"</span>
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">argument</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="variable declaration macro">argument</span> <span class="operator macro">=</span> <span class="string_literal macro">"test"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>   <span class="comment">// =&gt; "test"</span>
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="comma macro">,</span> <span class="variable declaration macro">name</span> <span class="operator macro">=</span> <span class="numeric_literal macro">2</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>          <span class="comment">// =&gt; "2 1"</span>
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">a</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="variable">c</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="variable">b</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="variable declaration macro">a</span><span class="operator macro">=</span><span class="string_literal macro">"a"</span><span class="comma macro">,</span> <span class="variable declaration macro">b</span><span class="operator macro">=</span><span class="char_literal macro">'b'</span><span class="comma macro">,</span> <span class="variable declaration macro">c</span><span class="operator macro">=</span><span class="numeric_literal macro">3</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>  <span class="comment">// =&gt; "a 3 b"</span>
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="escape_sequence">{{</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="escape_sequence">}}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">2</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>                       <span class="comment">// =&gt; "{2}"</span>
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">1</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="variable">width</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="variable declaration macro">width</span> <span class="operator macro">=</span> <span class="numeric_literal macro">5</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">&lt;</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">-</span><span class="format_specifier">&lt;</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">^</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">&gt;</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">+</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">#</span><span class="variable">x</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="numeric_literal macro">27</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="numeric_literal macro">-</span><span class="numeric_literal macro">5</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">#</span><span class="numeric_literal">0</span><span class="numeric_literal">10</span><span class="variable">x</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="numeric_literal macro">27</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0.01</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="numeric_literal">2</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="numeric_literal">0</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0.01</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="numeric_literal">2</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="numeric_literal">1</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="numeric_literal macro">0.01</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span>    <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="numeric_literal macro">0.01</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="numeric_literal">2</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span>   <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="numeric_literal macro">0.01</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="variable">number</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="variable">prec</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="variable declaration macro">prec</span> <span class="operator macro">=</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="variable declaration macro">number</span> <span class="operator macro">=</span> <span class="numeric_literal macro">0.01</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">` has 3 fractional digits"</span><span class="comma macro">,</span> <span class="string_literal macro">"Hello"</span><span class="comma macro">,</span> <span class="numeric_literal macro">3</span><span class="comma macro">,</span> <span class="variable declaration macro">name</span><span class="operator macro">=</span><span class="numeric_literal macro">1234.56</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">` has 3 characters"</span><span class="comma macro">,</span> <span class="string_literal macro">"Hello"</span><span class="comma macro">,</span> <span class="numeric_literal macro">3</span><span class="comma macro">,</span> <span class="variable declaration macro">name</span><span class="operator macro">=</span><span class="string_literal macro">"1234.56"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">&gt;</span><span class="numeric_literal">8</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">` has 3 right-aligned characters"</span><span class="comma macro">,</span> <span class="string_literal macro">"Hello"</span><span class="comma macro">,</span> <span class="numeric_literal macro">3</span><span class="comma macro">,</span> <span class="variable declaration macro">name</span><span class="operator macro">=</span><span class="string_literal macro">"1234.56"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>                 <span class="comment">// =&gt; "Hello"</span>
+    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello, </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"world"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>   <span class="comment">// =&gt; "Hello, world!"</span>
+    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"The number is </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>   <span class="comment">// =&gt; "The number is 1"</span>
+    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">?</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="parenthesis macro">(</span><span class="numeric_literal macro">3</span><span class="comma macro">,</span> <span class="numeric_literal macro">4</span><span class="parenthesis macro">)</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>          <span class="comment">// =&gt; "(3, 4)"</span>
+    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">value</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="variable declaration macro">value</span><span class="operator macro">=</span><span class="numeric_literal macro">4</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>      <span class="comment">// =&gt; "4"</span>
+    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="comma macro">,</span> <span class="numeric_literal macro">2</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>           <span class="comment">// =&gt; "1 2"</span>
+    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="numeric_literal">4</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">42</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>             <span class="comment">// =&gt; "0042" with leading zerosV</span>
+    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="comma macro">,</span> <span class="numeric_literal macro">2</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>   <span class="comment">// =&gt; "2 1 1 2"</span>
+    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">argument</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="variable declaration macro">argument</span> <span class="operator macro">=</span> <span class="string_literal macro">"test"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>   <span class="comment">// =&gt; "test"</span>
+    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="comma macro">,</span> <span class="variable declaration macro">name</span> <span class="operator macro">=</span> <span class="numeric_literal macro">2</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>          <span class="comment">// =&gt; "2 1"</span>
+    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">a</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="variable">c</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="variable">b</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="variable declaration macro">a</span><span class="operator macro">=</span><span class="string_literal macro">"a"</span><span class="comma macro">,</span> <span class="variable declaration macro">b</span><span class="operator macro">=</span><span class="char_literal macro">'b'</span><span class="comma macro">,</span> <span class="variable declaration macro">c</span><span class="operator macro">=</span><span class="numeric_literal macro">3</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>  <span class="comment">// =&gt; "a 3 b"</span>
+    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="escape_sequence">{{</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="escape_sequence">}}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">2</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>                       <span class="comment">// =&gt; "{2}"</span>
+    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">1</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="variable">width</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="variable declaration macro">width</span> <span class="operator macro">=</span> <span class="numeric_literal macro">5</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">&lt;</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">-</span><span class="format_specifier">&lt;</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">^</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">&gt;</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">+</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">#</span><span class="variable">x</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="numeric_literal macro">27</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="numeric_literal macro">-</span><span class="numeric_literal macro">5</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">#</span><span class="numeric_literal">0</span><span class="numeric_literal">10</span><span class="variable">x</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="numeric_literal macro">27</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0.01</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="numeric_literal">2</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="numeric_literal">0</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0.01</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="numeric_literal">2</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="numeric_literal">1</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="numeric_literal macro">0.01</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span>    <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="numeric_literal macro">0.01</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="numeric_literal">2</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span>   <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="numeric_literal macro">0.01</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="variable">number</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="variable">prec</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="variable declaration macro">prec</span> <span class="operator macro">=</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="variable declaration macro">number</span> <span class="operator macro">=</span> <span class="numeric_literal macro">0.01</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">` has 3 fractional digits"</span><span class="comma macro">,</span> <span class="string_literal macro">"Hello"</span><span class="comma macro">,</span> <span class="numeric_literal macro">3</span><span class="comma macro">,</span> <span class="variable declaration macro">name</span><span class="operator macro">=</span><span class="numeric_literal macro">1234.56</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">` has 3 characters"</span><span class="comma macro">,</span> <span class="string_literal macro">"Hello"</span><span class="comma macro">,</span> <span class="numeric_literal macro">3</span><span class="comma macro">,</span> <span class="variable declaration macro">name</span><span class="operator macro">=</span><span class="string_literal macro">"1234.56"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">&gt;</span><span class="numeric_literal">8</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">` has 3 right-aligned characters"</span><span class="comma macro">,</span> <span class="string_literal macro">"Hello"</span><span class="comma macro">,</span> <span class="numeric_literal macro">3</span><span class="comma macro">,</span> <span class="variable declaration macro">name</span><span class="operator macro">=</span><span class="string_literal macro">"1234.56"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
 
     <span class="keyword">let</span> <span class="punctuation">_</span> <span class="operator">=</span> <span class="string_literal">"{}"</span>
     <span class="keyword">let</span> <span class="punctuation">_</span> <span class="operator">=</span> <span class="string_literal">"{{}}"</span><span class="semicolon">;</span>
 
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="escape_sequence">{{</span><span class="escape_sequence">}}</span><span class="string_literal macro">"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="escape_sequence">{{</span><span class="string_literal macro"> Hello"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="escape_sequence">}}</span><span class="string_literal macro">"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="escape_sequence">{{</span><span class="string_literal macro">Hello</span><span class="escape_sequence">}}</span><span class="string_literal macro">"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="escape_sequence">{{</span><span class="string_literal macro"> Hello </span><span class="escape_sequence">}}</span><span class="string_literal macro">"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="escape_sequence">{{</span><span class="string_literal macro">Hello </span><span class="escape_sequence">}}</span><span class="string_literal macro">"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="escape_sequence">{{</span><span class="string_literal macro"> Hello</span><span class="escape_sequence">}}</span><span class="string_literal macro">"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="escape_sequence">{{</span><span class="escape_sequence">}}</span><span class="string_literal macro">"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="escape_sequence">{{</span><span class="string_literal macro"> Hello"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="escape_sequence">}}</span><span class="string_literal macro">"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="escape_sequence">{{</span><span class="string_literal macro">Hello</span><span class="escape_sequence">}}</span><span class="string_literal macro">"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="escape_sequence">{{</span><span class="string_literal macro"> Hello </span><span class="escape_sequence">}}</span><span class="string_literal macro">"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="escape_sequence">{{</span><span class="string_literal macro">Hello </span><span class="escape_sequence">}}</span><span class="string_literal macro">"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="escape_sequence">{{</span><span class="string_literal macro"> Hello</span><span class="escape_sequence">}}</span><span class="string_literal macro">"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
 
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">r"Hello, </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"world"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">r"Hello, </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"world"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
 
     <span class="comment">// escape sequences</span>
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello</span><span class="escape_sequence">\n</span><span class="string_literal macro">World"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="escape_sequence">\u{48}</span><span class="escape_sequence">\x65</span><span class="escape_sequence">\x6C</span><span class="escape_sequence">\x6C</span><span class="escape_sequence">\x6F</span><span class="string_literal macro"> World"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello</span><span class="escape_sequence">\n</span><span class="string_literal macro">World"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="escape_sequence">\u{48}</span><span class="escape_sequence">\x65</span><span class="escape_sequence">\x6C</span><span class="escape_sequence">\x6C</span><span class="escape_sequence">\x6F</span><span class="string_literal macro"> World"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
 
     <span class="keyword">let</span> <span class="punctuation">_</span> <span class="operator">=</span> <span class="string_literal">"</span><span class="escape_sequence">\x28</span><span class="escape_sequence">\x28</span><span class="escape_sequence">\x00</span><span class="escape_sequence">\x63</span><span class="invalid_escape_sequence">\xFF</span><span class="escape_sequence">\u{FF}</span><span class="escape_sequence">\n</span><span class="string_literal">"</span><span class="semicolon">;</span> <span class="comment">// invalid non-UTF8 escape sequences</span>
     <span class="keyword">let</span> <span class="punctuation">_</span> <span class="operator">=</span> <span class="string_literal">b"</span><span class="escape_sequence">\x28</span><span class="escape_sequence">\x28</span><span class="escape_sequence">\x00</span><span class="escape_sequence">\x63</span><span class="escape_sequence">\xFF</span><span class="invalid_escape_sequence">\u{FF}</span><span class="escape_sequence">\n</span><span class="string_literal">"</span><span class="semicolon">;</span> <span class="comment">// valid bytes, invalid unicodes</span>
     <span class="keyword">let</span> <span class="punctuation">_</span> <span class="operator">=</span> <span class="string_literal">c"</span><span class="escape_sequence">\u{FF}</span><span class="escape_sequence">\xFF</span><span class="string_literal">"</span><span class="semicolon">;</span> <span class="comment">// valid bytes, valid unicodes</span>
     <span class="keyword">let</span> <span class="variable declaration reference">backslash</span> <span class="operator">=</span> <span class="string_literal">r"\\"</span><span class="semicolon">;</span>
 
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="escape_sequence">\x41</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="variable declaration macro">A</span> <span class="operator macro">=</span> <span class="numeric_literal macro">92</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">ничоси</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="variable declaration macro">ничоси</span> <span class="operator macro">=</span> <span class="numeric_literal macro">92</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="escape_sequence">\x41</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="variable declaration macro">A</span> <span class="operator macro">=</span> <span class="numeric_literal macro">92</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">ничоси</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="variable declaration macro">ничоси</span> <span class="operator macro">=</span> <span class="numeric_literal macro">92</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
 
-    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="variable">x</span><span class="format_specifier">?</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> "</span><span class="comma macro">,</span> <span class="unresolved_reference macro">thingy</span><span class="comma macro">,</span> <span class="unresolved_reference macro">n2</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="variable">x</span><span class="format_specifier">?</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> "</span><span class="comma macro">,</span> <span class="unresolved_reference macro">thingy</span><span class="comma macro">,</span> <span class="unresolved_reference macro">n2</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
     <span class="macro default_library library">panic</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"{}"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
     <span class="macro default_library library">panic</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"more </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
     <span class="macro default_library library">assert</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="bool_literal macro">true</span><span class="comma macro">,</span> <span class="string_literal macro">"{}"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
     <span class="macro default_library library">assert</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="bool_literal macro">true</span><span class="comma macro">,</span> <span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> asdasd"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
-    <span class="macro">toho</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"{}fmt"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+    <span class="macro public">toho</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"{}fmt"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0</span><span class="parenthesis macro">)</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 macro">(</span>
@@ -175,6 +175,6 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
     <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>
     <span class="macro default_library library">format_args</span><span class="macro_bang">!</span><span class="parenthesis 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">"{}"</span><span class="parenthesis macro">)</span><span class="comma macro">,</span> <span class="string_literal macro">"{}"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
-    <span class="macro default_library library">format_args</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="variable">backslash</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="variable">CONSTANT</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="variable">m</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="variable macro reference">backslash</span><span class="comma macro">,</span> <span class="macro default_library library macro">format_args</span><span class="macro_bang macro">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0</span><span class="parenthesis macro">)</span><span class="comma macro">,</span> <span class="unresolved_reference macro">foo</span><span class="comma macro">,</span> <span class="string_literal macro">"bar"</span><span class="comma macro">,</span> <span class="macro macro">toho</span><span class="macro_bang macro">!</span><span class="parenthesis macro">(</span><span class="parenthesis macro">)</span><span class="comma macro">,</span> <span class="variable macro reference">backslash</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
-    <span class="macro">reuse_twice</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">backslash</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+    <span class="macro default_library library">format_args</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="variable">backslash</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="variable">CONSTANT</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="variable">m</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="variable macro reference">backslash</span><span class="comma macro">,</span> <span class="macro default_library library macro">format_args</span><span class="macro_bang macro">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0</span><span class="parenthesis macro">)</span><span class="comma macro">,</span> <span class="unresolved_reference macro">foo</span><span class="comma macro">,</span> <span class="string_literal macro">"bar"</span><span class="comma macro">,</span> <span class="macro macro public">toho</span><span class="macro_bang macro">!</span><span class="parenthesis macro">(</span><span class="parenthesis macro">)</span><span class="comma macro">,</span> <span class="variable macro reference">backslash</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+    <span class="macro public">reuse_twice</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">backslash</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="parenthesis macro">)</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_unsafe.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
index 4e69c82f3da..d9beac30898 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
@@ -45,12 +45,12 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; }
 .unresolved_reference    { color: #FC5555; text-decoration: wavy underline; }
 </style>
-<pre><code><span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">id</span> <span class="brace">{</span>
+<pre><code><span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration public">id</span> <span class="brace">{</span>
     <span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>tt<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">*</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>tt<span class="parenthesis">)</span><span class="punctuation">*</span>
     <span class="brace">}</span><span class="semicolon">;</span>
 <span class="brace">}</span>
-<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">unsafe_deref</span> <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="brace">}</span><span class="semicolon">;</span>
@@ -92,13 +92,13 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
     <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="macro">id</span><span class="macro_bang">!</span> <span class="brace macro">{</span>
-        <span class="keyword macro unsafe">unsafe</span> <span class="brace macro">{</span> <span class="macro macro 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="macro public">id</span><span class="macro_bang">!</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">!</span><span class="parenthesis macro">(</span><span class="parenthesis macro">)</span> <span class="brace macro">}</span>
     <span class="brace macro">}</span><span class="semicolon">;</span>
 
     <span class="keyword unsafe">unsafe</span> <span class="brace">{</span>
-        <span class="macro unsafe">unsafe_deref</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
-        <span class="macro unsafe">id</span><span class="macro_bang">!</span> <span class="brace macro">{</span> <span class="macro macro 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="semicolon">;</span>
+        <span class="macro public unsafe">unsafe_deref</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+        <span class="macro public unsafe">id</span><span class="macro_bang">!</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="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>
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 a20147add36..af52b33de64 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
@@ -990,7 +990,7 @@ impl t for foo {
 fn test_injection() {
     check_highlighting(
         r##"
-fn fixture(ra_fixture: &str) {}
+fn fixture(#[rust_analyzer::rust_fixture] ra_fixture: &str) {}
 
 fn main() {
     fixture(r#"
@@ -1188,7 +1188,11 @@ fn foo(x: &fn(&dyn Trait)) {}
 /// Highlights the code given by the `ra_fixture` argument, renders the
 /// result as HTML, and compares it with the HTML file given as `snapshot`.
 /// Note that the `snapshot` file is overwritten by the rendered HTML.
-fn check_highlighting(ra_fixture: &str, expect: ExpectFile, rainbow: bool) {
+fn check_highlighting(
+    #[rust_analyzer::rust_fixture] ra_fixture: &str,
+    expect: ExpectFile,
+    rainbow: bool,
+) {
     let (analysis, file_id) = fixture::file(ra_fixture.trim());
     let actual_html = &analysis.highlight_as_html(file_id, rainbow).unwrap();
     expect.assert_eq(actual_html)
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_tree.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_tree.rs
deleted file mode 100644
index e241cb82bd5..00000000000
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_tree.rs
+++ /dev/null
@@ -1,338 +0,0 @@
-use hir::Semantics;
-use ide_db::{FileId, RootDatabase};
-use syntax::{
-    AstNode, NodeOrToken, SourceFile, SyntaxKind::STRING, SyntaxToken, TextRange, TextSize,
-};
-
-// Feature: Show Syntax Tree
-//
-// Shows the parse tree of the current file. It exists mostly for debugging
-// rust-analyzer itself.
-//
-// |===
-// | Editor  | Action Name
-//
-// | VS Code | **rust-analyzer: Show Syntax Tree**
-// |===
-// image::https://user-images.githubusercontent.com/48062697/113065586-068bdb80-91b1-11eb-9507-fee67f9f45a0.gif[]
-pub(crate) fn syntax_tree(
-    db: &RootDatabase,
-    file_id: FileId,
-    text_range: Option<TextRange>,
-) -> String {
-    let sema = Semantics::new(db);
-    let parse = sema.parse_guess_edition(file_id);
-    if let Some(text_range) = text_range {
-        let node = match parse.syntax().covering_element(text_range) {
-            NodeOrToken::Node(node) => node,
-            NodeOrToken::Token(token) => {
-                if let Some(tree) = syntax_tree_for_string(&token, text_range) {
-                    return tree;
-                }
-                token.parent().unwrap()
-            }
-        };
-
-        format!("{node:#?}")
-    } else {
-        format!("{:#?}", parse.syntax())
-    }
-}
-
-/// Attempts parsing the selected contents of a string literal
-/// as rust syntax and returns its syntax tree
-fn syntax_tree_for_string(token: &SyntaxToken, text_range: TextRange) -> Option<String> {
-    // When the range is inside a string
-    // we'll attempt parsing it as rust syntax
-    // to provide the syntax tree of the contents of the string
-    match token.kind() {
-        STRING => syntax_tree_for_token(token, text_range),
-        _ => None,
-    }
-}
-
-fn syntax_tree_for_token(node: &SyntaxToken, text_range: TextRange) -> Option<String> {
-    // Range of the full node
-    let node_range = node.text_range();
-    let text = node.text().to_owned();
-
-    // We start at some point inside the node
-    // Either we have selected the whole string
-    // or our selection is inside it
-    let start = text_range.start() - node_range.start();
-
-    // how many characters we have selected
-    let len = text_range.len();
-
-    let node_len = node_range.len();
-
-    // We want to cap our length
-    let len = len.min(node_len);
-
-    // Ensure our slice is inside the actual string
-    let end =
-        if start + len < TextSize::of(&text) { start + len } else { TextSize::of(&text) - start };
-
-    let text = &text[TextRange::new(start, end)];
-
-    // Remove possible extra string quotes from the start
-    // and the end of the string
-    let text = text
-        .trim_start_matches('r')
-        .trim_start_matches('#')
-        .trim_start_matches('"')
-        .trim_end_matches('#')
-        .trim_end_matches('"')
-        .trim()
-        // Remove custom markers
-        .replace("$0", "");
-
-    let parsed = SourceFile::parse(&text, span::Edition::CURRENT_FIXME);
-
-    // If the "file" parsed without errors,
-    // return its syntax
-    if parsed.errors().is_empty() {
-        return Some(format!("{:#?}", parsed.tree().syntax()));
-    }
-
-    None
-}
-
-#[cfg(test)]
-mod tests {
-    use expect_test::expect;
-
-    use crate::fixture;
-
-    fn check(ra_fixture: &str, expect: expect_test::Expect) {
-        let (analysis, file_id) = fixture::file(ra_fixture);
-        let syn = analysis.syntax_tree(file_id, None).unwrap();
-        expect.assert_eq(&syn)
-    }
-    fn check_range(ra_fixture: &str, expect: expect_test::Expect) {
-        let (analysis, frange) = fixture::range(ra_fixture);
-        let syn = analysis.syntax_tree(frange.file_id, Some(frange.range)).unwrap();
-        expect.assert_eq(&syn)
-    }
-
-    #[test]
-    fn test_syntax_tree_without_range() {
-        // Basic syntax
-        check(
-            r#"fn foo() {}"#,
-            expect![[r#"
-                SOURCE_FILE@0..11
-                  FN@0..11
-                    FN_KW@0..2 "fn"
-                    WHITESPACE@2..3 " "
-                    NAME@3..6
-                      IDENT@3..6 "foo"
-                    PARAM_LIST@6..8
-                      L_PAREN@6..7 "("
-                      R_PAREN@7..8 ")"
-                    WHITESPACE@8..9 " "
-                    BLOCK_EXPR@9..11
-                      STMT_LIST@9..11
-                        L_CURLY@9..10 "{"
-                        R_CURLY@10..11 "}"
-            "#]],
-        );
-
-        check(
-            r#"
-fn test() {
-    assert!("
-    fn foo() {
-    }
-    ", "");
-}"#,
-            expect![[r#"
-                SOURCE_FILE@0..60
-                  FN@0..60
-                    FN_KW@0..2 "fn"
-                    WHITESPACE@2..3 " "
-                    NAME@3..7
-                      IDENT@3..7 "test"
-                    PARAM_LIST@7..9
-                      L_PAREN@7..8 "("
-                      R_PAREN@8..9 ")"
-                    WHITESPACE@9..10 " "
-                    BLOCK_EXPR@10..60
-                      STMT_LIST@10..60
-                        L_CURLY@10..11 "{"
-                        WHITESPACE@11..16 "\n    "
-                        EXPR_STMT@16..58
-                          MACRO_EXPR@16..57
-                            MACRO_CALL@16..57
-                              PATH@16..22
-                                PATH_SEGMENT@16..22
-                                  NAME_REF@16..22
-                                    IDENT@16..22 "assert"
-                              BANG@22..23 "!"
-                              TOKEN_TREE@23..57
-                                L_PAREN@23..24 "("
-                                STRING@24..52 "\"\n    fn foo() {\n     ..."
-                                COMMA@52..53 ","
-                                WHITESPACE@53..54 " "
-                                STRING@54..56 "\"\""
-                                R_PAREN@56..57 ")"
-                          SEMICOLON@57..58 ";"
-                        WHITESPACE@58..59 "\n"
-                        R_CURLY@59..60 "}"
-            "#]],
-        )
-    }
-
-    #[test]
-    fn test_syntax_tree_with_range() {
-        check_range(
-            r#"$0fn foo() {}$0"#,
-            expect![[r#"
-                FN@0..11
-                  FN_KW@0..2 "fn"
-                  WHITESPACE@2..3 " "
-                  NAME@3..6
-                    IDENT@3..6 "foo"
-                  PARAM_LIST@6..8
-                    L_PAREN@6..7 "("
-                    R_PAREN@7..8 ")"
-                  WHITESPACE@8..9 " "
-                  BLOCK_EXPR@9..11
-                    STMT_LIST@9..11
-                      L_CURLY@9..10 "{"
-                      R_CURLY@10..11 "}"
-            "#]],
-        );
-
-        check_range(
-            r#"
-fn test() {
-    $0assert!("
-    fn foo() {
-    }
-    ", "");$0
-}"#,
-            expect![[r#"
-                EXPR_STMT@16..58
-                  MACRO_EXPR@16..57
-                    MACRO_CALL@16..57
-                      PATH@16..22
-                        PATH_SEGMENT@16..22
-                          NAME_REF@16..22
-                            IDENT@16..22 "assert"
-                      BANG@22..23 "!"
-                      TOKEN_TREE@23..57
-                        L_PAREN@23..24 "("
-                        STRING@24..52 "\"\n    fn foo() {\n     ..."
-                        COMMA@52..53 ","
-                        WHITESPACE@53..54 " "
-                        STRING@54..56 "\"\""
-                        R_PAREN@56..57 ")"
-                  SEMICOLON@57..58 ";"
-            "#]],
-        );
-    }
-
-    #[test]
-    fn test_syntax_tree_inside_string() {
-        check_range(
-            r#"fn test() {
-    assert!("
-$0fn foo() {
-}$0
-fn bar() {
-}
-    ", "");
-}"#,
-            expect![[r#"
-                SOURCE_FILE@0..12
-                  FN@0..12
-                    FN_KW@0..2 "fn"
-                    WHITESPACE@2..3 " "
-                    NAME@3..6
-                      IDENT@3..6 "foo"
-                    PARAM_LIST@6..8
-                      L_PAREN@6..7 "("
-                      R_PAREN@7..8 ")"
-                    WHITESPACE@8..9 " "
-                    BLOCK_EXPR@9..12
-                      STMT_LIST@9..12
-                        L_CURLY@9..10 "{"
-                        WHITESPACE@10..11 "\n"
-                        R_CURLY@11..12 "}"
-            "#]],
-        );
-
-        // With a raw string
-        check_range(
-            r###"fn test() {
-    assert!(r#"
-$0fn foo() {
-}$0
-fn bar() {
-}
-    "#, "");
-}"###,
-            expect![[r#"
-                SOURCE_FILE@0..12
-                  FN@0..12
-                    FN_KW@0..2 "fn"
-                    WHITESPACE@2..3 " "
-                    NAME@3..6
-                      IDENT@3..6 "foo"
-                    PARAM_LIST@6..8
-                      L_PAREN@6..7 "("
-                      R_PAREN@7..8 ")"
-                    WHITESPACE@8..9 " "
-                    BLOCK_EXPR@9..12
-                      STMT_LIST@9..12
-                        L_CURLY@9..10 "{"
-                        WHITESPACE@10..11 "\n"
-                        R_CURLY@11..12 "}"
-            "#]],
-        );
-
-        // With a raw string
-        check_range(
-            r###"fn test() {
-    assert!(r$0#"
-fn foo() {
-}
-fn bar() {
-}"$0#, "");
-}"###,
-            expect![[r#"
-                SOURCE_FILE@0..25
-                  FN@0..12
-                    FN_KW@0..2 "fn"
-                    WHITESPACE@2..3 " "
-                    NAME@3..6
-                      IDENT@3..6 "foo"
-                    PARAM_LIST@6..8
-                      L_PAREN@6..7 "("
-                      R_PAREN@7..8 ")"
-                    WHITESPACE@8..9 " "
-                    BLOCK_EXPR@9..12
-                      STMT_LIST@9..12
-                        L_CURLY@9..10 "{"
-                        WHITESPACE@10..11 "\n"
-                        R_CURLY@11..12 "}"
-                  WHITESPACE@12..13 "\n"
-                  FN@13..25
-                    FN_KW@13..15 "fn"
-                    WHITESPACE@15..16 " "
-                    NAME@16..19
-                      IDENT@16..19 "bar"
-                    PARAM_LIST@19..21
-                      L_PAREN@19..20 "("
-                      R_PAREN@20..21 ")"
-                    WHITESPACE@21..22 " "
-                    BLOCK_EXPR@22..25
-                      STMT_LIST@22..25
-                        L_CURLY@22..23 "{"
-                        WHITESPACE@23..24 "\n"
-                        R_CURLY@24..25 "}"
-            "#]],
-        );
-    }
-}
diff --git a/src/tools/rust-analyzer/crates/ide/src/typing.rs b/src/tools/rust-analyzer/crates/ide/src/typing.rs
index 8998934e0e8..47d75f1c957 100644
--- a/src/tools/rust-analyzer/crates/ide/src/typing.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/typing.rs
@@ -436,14 +436,18 @@ mod tests {
         })
     }
 
-    fn type_char(char_typed: char, ra_fixture_before: &str, ra_fixture_after: &str) {
+    fn type_char(
+        char_typed: char,
+        #[rust_analyzer::rust_fixture] ra_fixture_before: &str,
+        #[rust_analyzer::rust_fixture] ra_fixture_after: &str,
+    ) {
         let actual = do_type_char(char_typed, ra_fixture_before)
             .unwrap_or_else(|| panic!("typing `{char_typed}` did nothing"));
 
         assert_eq_text!(ra_fixture_after, &actual);
     }
 
-    fn type_char_noop(char_typed: char, ra_fixture_before: &str) {
+    fn type_char_noop(char_typed: char, #[rust_analyzer::rust_fixture] ra_fixture_before: &str) {
         let file_change = do_type_char(char_typed, ra_fixture_before);
         assert_eq!(file_change, None)
     }
@@ -889,7 +893,7 @@ fn main() {
         type_char_noop(
             '{',
             r##"
-fn check_with(ra_fixture: &str, expect: Expect) {
+fn check_with(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
     let base = r#"
 enum E { T(), R$0, C }
 use self::E::X;
@@ -1191,7 +1195,7 @@ fn f(n: a<>b::<d>::c) {}
         type_char_noop(
             '(',
             r##"
-fn check_with(ra_fixture: &str, expect: Expect) {
+fn check_with(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
     let base = r#"
 enum E { T(), R$0, C }
 use self::E::X;
diff --git a/src/tools/rust-analyzer/crates/ide/src/typing/on_enter.rs b/src/tools/rust-analyzer/crates/ide/src/typing/on_enter.rs
index 773e352220e..e249c38c73d 100644
--- a/src/tools/rust-analyzer/crates/ide/src/typing/on_enter.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/typing/on_enter.rs
@@ -208,7 +208,10 @@ mod tests {
         Some(actual)
     }
 
-    fn do_check(ra_fixture_before: &str, ra_fixture_after: &str) {
+    fn do_check(
+        #[rust_analyzer::rust_fixture] ra_fixture_before: &str,
+        #[rust_analyzer::rust_fixture] ra_fixture_after: &str,
+    ) {
         let ra_fixture_after = &trim_indent(ra_fixture_after);
         let actual = apply_on_enter(ra_fixture_before).unwrap();
         assert_eq_text!(ra_fixture_after, &actual);
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 830c39e21ea..ff74e05e943 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
@@ -220,7 +220,9 @@ mod tests {
     use crate::fixture;
     use expect_test::expect;
 
-    fn make_memory_layout(ra_fixture: &str) -> Option<RecursiveMemoryLayout> {
+    fn make_memory_layout(
+        #[rust_analyzer::rust_fixture] ra_fixture: &str,
+    ) -> Option<RecursiveMemoryLayout> {
         let (analysis, position, _) = fixture::annotations(ra_fixture);
 
         view_memory_layout(&analysis.db, position)
diff --git a/src/tools/rust-analyzer/crates/ide/src/view_syntax_tree.rs b/src/tools/rust-analyzer/crates/ide/src/view_syntax_tree.rs
new file mode 100644
index 00000000000..218ee15a7dd
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/ide/src/view_syntax_tree.rs
@@ -0,0 +1,226 @@
+use hir::Semantics;
+use ide_db::{FileId, RootDatabase};
+use span::TextRange;
+use stdx::format_to;
+use syntax::{
+    ast::{self, IsString},
+    AstNode, AstToken, NodeOrToken, SourceFile, SyntaxNode, SyntaxToken, WalkEvent,
+};
+
+// Feature: Show Syntax Tree
+//
+// Shows a tree view with the syntax tree of the current file
+//
+// |===
+// | Editor  | Panel Name
+//
+// | VS Code | **Rust Syntax Tree**
+// |===
+pub(crate) fn view_syntax_tree(db: &RootDatabase, file_id: FileId) -> String {
+    let sema = Semantics::new(db);
+    let parse = sema.parse_guess_edition(file_id);
+    syntax_node_to_json(parse.syntax(), None)
+}
+
+fn syntax_node_to_json(node: &SyntaxNode, ctx: Option<InStringCtx>) -> String {
+    let mut result = String::new();
+    for event in node.preorder_with_tokens() {
+        match event {
+            WalkEvent::Enter(it) => {
+                let kind = it.kind();
+                let (text_range, inner_range_str) = match &ctx {
+                    Some(ctx) => {
+                        let inner_start: u32 = it.text_range().start().into();
+                        let inner_end: u32 = it.text_range().end().into();
+
+                        let mut true_start = inner_start + ctx.offset;
+                        let mut true_end = inner_end + ctx.offset;
+                        for pos in &ctx.marker_positions {
+                            if *pos >= inner_end {
+                                break;
+                            }
+
+                            // We conditionally add to true_start in case
+                            // the marker is between the start and end.
+                            true_start += 2 * (*pos < inner_start) as u32;
+                            true_end += 2;
+                        }
+
+                        let true_range = TextRange::new(true_start.into(), true_end.into());
+
+                        (
+                            true_range,
+                            format!(
+                                r#","istart":{:?},"iend":{:?}"#,
+                                it.text_range().start(),
+                                it.text_range().end()
+                            ),
+                        )
+                    }
+                    None => (it.text_range(), "".to_owned()),
+                };
+                let start = text_range.start();
+                let end = text_range.end();
+
+                match it {
+                    NodeOrToken::Node(_) => {
+                        format_to!(
+                            result,
+                            r#"{{"type":"Node","kind":"{kind:?}","start":{start:?},"end":{end:?}{inner_range_str},"children":["#
+                        );
+                    }
+                    NodeOrToken::Token(token) => {
+                        let comma = if token.next_sibling_or_token().is_some() { "," } else { "" };
+                        match parse_rust_string(token) {
+                            Some(parsed) => {
+                                format_to!(
+                                    result,
+                                    r#"{{"type":"Node","kind":"{kind:?}","start":{start:?},"end":{end:?}{inner_range_str},"children":[{parsed}]}}{comma}"#
+                                );
+                            }
+                            None => format_to!(
+                                result,
+                                r#"{{"type":"Token","kind":"{kind:?}","start":{start:?},"end":{end:?}{inner_range_str}}}{comma}"#
+                            ),
+                        }
+                    }
+                }
+            }
+            WalkEvent::Leave(it) => match it {
+                NodeOrToken::Node(node) => {
+                    let comma = if node.next_sibling_or_token().is_some() { "," } else { "" };
+                    format_to!(result, "]}}{comma}")
+                }
+                NodeOrToken::Token(_) => (),
+            },
+        }
+    }
+
+    result
+}
+
+fn parse_rust_string(token: SyntaxToken) -> Option<String> {
+    let string_node = ast::String::cast(token)?;
+    let text = string_node.value().ok()?;
+
+    let mut trim_result = String::new();
+    let mut marker_positions = Vec::new();
+    let mut skipped = 0;
+    let mut last_end = 0;
+    for (start, part) in text.match_indices("$0") {
+        marker_positions.push((start - skipped) as u32);
+        trim_result.push_str(&text[last_end..start]);
+        skipped += part.len();
+        last_end = start + part.len();
+    }
+    trim_result.push_str(&text[last_end..text.len()]);
+
+    let parsed = SourceFile::parse(&trim_result, span::Edition::CURRENT);
+
+    if !parsed.errors().is_empty() {
+        return None;
+    }
+
+    let node: &SyntaxNode = &parsed.syntax_node();
+
+    if node.children().count() == 0 {
+        // C'mon, you should have at least one node other than SOURCE_FILE
+        return None;
+    }
+
+    Some(syntax_node_to_json(
+        node,
+        Some(InStringCtx {
+            offset: string_node.text_range_between_quotes()?.start().into(),
+            marker_positions,
+        }),
+    ))
+}
+
+struct InStringCtx {
+    offset: u32,
+    marker_positions: Vec<u32>,
+}
+
+#[cfg(test)]
+mod tests {
+    use expect_test::expect;
+
+    use crate::fixture;
+
+    fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: expect_test::Expect) {
+        let (analysis, file_id) = fixture::file(ra_fixture);
+        let syn = analysis.view_syntax_tree(file_id).unwrap();
+        expect.assert_eq(&syn)
+    }
+
+    #[test]
+    fn view_syntax_tree() {
+        // Basic syntax
+        check(
+            r#"fn foo() {}"#,
+            expect![[
+                r#"{"type":"Node","kind":"SOURCE_FILE","start":0,"end":11,"children":[{"type":"Node","kind":"FN","start":0,"end":11,"children":[{"type":"Token","kind":"FN_KW","start":0,"end":2},{"type":"Token","kind":"WHITESPACE","start":2,"end":3},{"type":"Node","kind":"NAME","start":3,"end":6,"children":[{"type":"Token","kind":"IDENT","start":3,"end":6}]},{"type":"Node","kind":"PARAM_LIST","start":6,"end":8,"children":[{"type":"Token","kind":"L_PAREN","start":6,"end":7},{"type":"Token","kind":"R_PAREN","start":7,"end":8}]},{"type":"Token","kind":"WHITESPACE","start":8,"end":9},{"type":"Node","kind":"BLOCK_EXPR","start":9,"end":11,"children":[{"type":"Node","kind":"STMT_LIST","start":9,"end":11,"children":[{"type":"Token","kind":"L_CURLY","start":9,"end":10},{"type":"Token","kind":"R_CURLY","start":10,"end":11}]}]}]}]}"#
+            ]],
+        );
+
+        check(
+            r#"
+fn test() {
+    assert!("
+    fn foo() {
+    }
+    ", "");
+}"#,
+            expect![[
+                r#"{"type":"Node","kind":"SOURCE_FILE","start":0,"end":60,"children":[{"type":"Node","kind":"FN","start":0,"end":60,"children":[{"type":"Token","kind":"FN_KW","start":0,"end":2},{"type":"Token","kind":"WHITESPACE","start":2,"end":3},{"type":"Node","kind":"NAME","start":3,"end":7,"children":[{"type":"Token","kind":"IDENT","start":3,"end":7}]},{"type":"Node","kind":"PARAM_LIST","start":7,"end":9,"children":[{"type":"Token","kind":"L_PAREN","start":7,"end":8},{"type":"Token","kind":"R_PAREN","start":8,"end":9}]},{"type":"Token","kind":"WHITESPACE","start":9,"end":10},{"type":"Node","kind":"BLOCK_EXPR","start":10,"end":60,"children":[{"type":"Node","kind":"STMT_LIST","start":10,"end":60,"children":[{"type":"Token","kind":"L_CURLY","start":10,"end":11},{"type":"Token","kind":"WHITESPACE","start":11,"end":16},{"type":"Node","kind":"EXPR_STMT","start":16,"end":58,"children":[{"type":"Node","kind":"MACRO_EXPR","start":16,"end":57,"children":[{"type":"Node","kind":"MACRO_CALL","start":16,"end":57,"children":[{"type":"Node","kind":"PATH","start":16,"end":22,"children":[{"type":"Node","kind":"PATH_SEGMENT","start":16,"end":22,"children":[{"type":"Node","kind":"NAME_REF","start":16,"end":22,"children":[{"type":"Token","kind":"IDENT","start":16,"end":22}]}]}]},{"type":"Token","kind":"BANG","start":22,"end":23},{"type":"Node","kind":"TOKEN_TREE","start":23,"end":57,"children":[{"type":"Token","kind":"L_PAREN","start":23,"end":24},{"type":"Node","kind":"STRING","start":24,"end":52,"children":[{"type":"Node","kind":"SOURCE_FILE","start":25,"end":51,"istart":0,"iend":26,"children":[{"type":"Token","kind":"WHITESPACE","start":25,"end":30,"istart":0,"iend":5},{"type":"Node","kind":"FN","start":30,"end":46,"istart":5,"iend":21,"children":[{"type":"Token","kind":"FN_KW","start":30,"end":32,"istart":5,"iend":7},{"type":"Token","kind":"WHITESPACE","start":32,"end":33,"istart":7,"iend":8},{"type":"Node","kind":"NAME","start":33,"end":36,"istart":8,"iend":11,"children":[{"type":"Token","kind":"IDENT","start":33,"end":36,"istart":8,"iend":11}]},{"type":"Node","kind":"PARAM_LIST","start":36,"end":38,"istart":11,"iend":13,"children":[{"type":"Token","kind":"L_PAREN","start":36,"end":37,"istart":11,"iend":12},{"type":"Token","kind":"R_PAREN","start":37,"end":38,"istart":12,"iend":13}]},{"type":"Token","kind":"WHITESPACE","start":38,"end":39,"istart":13,"iend":14},{"type":"Node","kind":"BLOCK_EXPR","start":39,"end":46,"istart":14,"iend":21,"children":[{"type":"Node","kind":"STMT_LIST","start":39,"end":46,"istart":14,"iend":21,"children":[{"type":"Token","kind":"L_CURLY","start":39,"end":40,"istart":14,"iend":15},{"type":"Token","kind":"WHITESPACE","start":40,"end":45,"istart":15,"iend":20},{"type":"Token","kind":"R_CURLY","start":45,"end":46,"istart":20,"iend":21}]}]}]},{"type":"Token","kind":"WHITESPACE","start":46,"end":51,"istart":21,"iend":26}]}]},{"type":"Token","kind":"COMMA","start":52,"end":53},{"type":"Token","kind":"WHITESPACE","start":53,"end":54},{"type":"Token","kind":"STRING","start":54,"end":56},{"type":"Token","kind":"R_PAREN","start":56,"end":57}]}]}]},{"type":"Token","kind":"SEMICOLON","start":57,"end":58}]},{"type":"Token","kind":"WHITESPACE","start":58,"end":59},{"type":"Token","kind":"R_CURLY","start":59,"end":60}]}]}]}]}"#
+            ]],
+        )
+    }
+
+    #[test]
+    fn view_syntax_tree_inside_string() {
+        check(
+            r#"fn test() {
+    assert!("
+$0fn foo() {
+}$0
+fn bar() {
+}
+    ", "");
+}"#,
+            expect![[
+                r#"{"type":"Node","kind":"SOURCE_FILE","start":0,"end":65,"children":[{"type":"Node","kind":"FN","start":0,"end":65,"children":[{"type":"Token","kind":"FN_KW","start":0,"end":2},{"type":"Token","kind":"WHITESPACE","start":2,"end":3},{"type":"Node","kind":"NAME","start":3,"end":7,"children":[{"type":"Token","kind":"IDENT","start":3,"end":7}]},{"type":"Node","kind":"PARAM_LIST","start":7,"end":9,"children":[{"type":"Token","kind":"L_PAREN","start":7,"end":8},{"type":"Token","kind":"R_PAREN","start":8,"end":9}]},{"type":"Token","kind":"WHITESPACE","start":9,"end":10},{"type":"Node","kind":"BLOCK_EXPR","start":10,"end":65,"children":[{"type":"Node","kind":"STMT_LIST","start":10,"end":65,"children":[{"type":"Token","kind":"L_CURLY","start":10,"end":11},{"type":"Token","kind":"WHITESPACE","start":11,"end":16},{"type":"Node","kind":"EXPR_STMT","start":16,"end":63,"children":[{"type":"Node","kind":"MACRO_EXPR","start":16,"end":62,"children":[{"type":"Node","kind":"MACRO_CALL","start":16,"end":62,"children":[{"type":"Node","kind":"PATH","start":16,"end":22,"children":[{"type":"Node","kind":"PATH_SEGMENT","start":16,"end":22,"children":[{"type":"Node","kind":"NAME_REF","start":16,"end":22,"children":[{"type":"Token","kind":"IDENT","start":16,"end":22}]}]}]},{"type":"Token","kind":"BANG","start":22,"end":23},{"type":"Node","kind":"TOKEN_TREE","start":23,"end":62,"children":[{"type":"Token","kind":"L_PAREN","start":23,"end":24},{"type":"Node","kind":"STRING","start":24,"end":57,"children":[{"type":"Node","kind":"SOURCE_FILE","start":25,"end":56,"istart":0,"iend":31,"children":[{"type":"Token","kind":"WHITESPACE","start":25,"end":26,"istart":0,"iend":1},{"type":"Node","kind":"FN","start":26,"end":38,"istart":1,"iend":13,"children":[{"type":"Token","kind":"FN_KW","start":26,"end":28,"istart":1,"iend":3},{"type":"Token","kind":"WHITESPACE","start":28,"end":29,"istart":3,"iend":4},{"type":"Node","kind":"NAME","start":29,"end":32,"istart":4,"iend":7,"children":[{"type":"Token","kind":"IDENT","start":29,"end":32,"istart":4,"iend":7}]},{"type":"Node","kind":"PARAM_LIST","start":32,"end":34,"istart":7,"iend":9,"children":[{"type":"Token","kind":"L_PAREN","start":32,"end":33,"istart":7,"iend":8},{"type":"Token","kind":"R_PAREN","start":33,"end":34,"istart":8,"iend":9}]},{"type":"Token","kind":"WHITESPACE","start":34,"end":35,"istart":9,"iend":10},{"type":"Node","kind":"BLOCK_EXPR","start":35,"end":38,"istart":10,"iend":13,"children":[{"type":"Node","kind":"STMT_LIST","start":35,"end":38,"istart":10,"iend":13,"children":[{"type":"Token","kind":"L_CURLY","start":35,"end":36,"istart":10,"iend":11},{"type":"Token","kind":"WHITESPACE","start":36,"end":37,"istart":11,"iend":12},{"type":"Token","kind":"R_CURLY","start":37,"end":38,"istart":12,"iend":13}]}]}]},{"type":"Token","kind":"WHITESPACE","start":38,"end":39,"istart":13,"iend":14},{"type":"Node","kind":"FN","start":39,"end":51,"istart":14,"iend":26,"children":[{"type":"Token","kind":"FN_KW","start":39,"end":41,"istart":14,"iend":16},{"type":"Token","kind":"WHITESPACE","start":41,"end":42,"istart":16,"iend":17},{"type":"Node","kind":"NAME","start":42,"end":45,"istart":17,"iend":20,"children":[{"type":"Token","kind":"IDENT","start":42,"end":45,"istart":17,"iend":20}]},{"type":"Node","kind":"PARAM_LIST","start":45,"end":47,"istart":20,"iend":22,"children":[{"type":"Token","kind":"L_PAREN","start":45,"end":46,"istart":20,"iend":21},{"type":"Token","kind":"R_PAREN","start":46,"end":47,"istart":21,"iend":22}]},{"type":"Token","kind":"WHITESPACE","start":47,"end":48,"istart":22,"iend":23},{"type":"Node","kind":"BLOCK_EXPR","start":48,"end":51,"istart":23,"iend":26,"children":[{"type":"Node","kind":"STMT_LIST","start":48,"end":51,"istart":23,"iend":26,"children":[{"type":"Token","kind":"L_CURLY","start":48,"end":49,"istart":23,"iend":24},{"type":"Token","kind":"WHITESPACE","start":49,"end":50,"istart":24,"iend":25},{"type":"Token","kind":"R_CURLY","start":50,"end":51,"istart":25,"iend":26}]}]}]},{"type":"Token","kind":"WHITESPACE","start":51,"end":56,"istart":26,"iend":31}]}]},{"type":"Token","kind":"COMMA","start":57,"end":58},{"type":"Token","kind":"WHITESPACE","start":58,"end":59},{"type":"Token","kind":"STRING","start":59,"end":61},{"type":"Token","kind":"R_PAREN","start":61,"end":62}]}]}]},{"type":"Token","kind":"SEMICOLON","start":62,"end":63}]},{"type":"Token","kind":"WHITESPACE","start":63,"end":64},{"type":"Token","kind":"R_CURLY","start":64,"end":65}]}]}]}]}"#
+            ]],
+        );
+
+        // With a raw string
+        check(
+            r###"fn test() {
+    assert!(r#"
+$0fn foo() {
+}$0
+fn bar() {
+}
+    "#, "");
+}"###,
+            expect![[
+                r#"{"type":"Node","kind":"SOURCE_FILE","start":0,"end":68,"children":[{"type":"Node","kind":"FN","start":0,"end":68,"children":[{"type":"Token","kind":"FN_KW","start":0,"end":2},{"type":"Token","kind":"WHITESPACE","start":2,"end":3},{"type":"Node","kind":"NAME","start":3,"end":7,"children":[{"type":"Token","kind":"IDENT","start":3,"end":7}]},{"type":"Node","kind":"PARAM_LIST","start":7,"end":9,"children":[{"type":"Token","kind":"L_PAREN","start":7,"end":8},{"type":"Token","kind":"R_PAREN","start":8,"end":9}]},{"type":"Token","kind":"WHITESPACE","start":9,"end":10},{"type":"Node","kind":"BLOCK_EXPR","start":10,"end":68,"children":[{"type":"Node","kind":"STMT_LIST","start":10,"end":68,"children":[{"type":"Token","kind":"L_CURLY","start":10,"end":11},{"type":"Token","kind":"WHITESPACE","start":11,"end":16},{"type":"Node","kind":"EXPR_STMT","start":16,"end":66,"children":[{"type":"Node","kind":"MACRO_EXPR","start":16,"end":65,"children":[{"type":"Node","kind":"MACRO_CALL","start":16,"end":65,"children":[{"type":"Node","kind":"PATH","start":16,"end":22,"children":[{"type":"Node","kind":"PATH_SEGMENT","start":16,"end":22,"children":[{"type":"Node","kind":"NAME_REF","start":16,"end":22,"children":[{"type":"Token","kind":"IDENT","start":16,"end":22}]}]}]},{"type":"Token","kind":"BANG","start":22,"end":23},{"type":"Node","kind":"TOKEN_TREE","start":23,"end":65,"children":[{"type":"Token","kind":"L_PAREN","start":23,"end":24},{"type":"Node","kind":"STRING","start":24,"end":60,"children":[{"type":"Node","kind":"SOURCE_FILE","start":27,"end":58,"istart":0,"iend":31,"children":[{"type":"Token","kind":"WHITESPACE","start":27,"end":28,"istart":0,"iend":1},{"type":"Node","kind":"FN","start":28,"end":40,"istart":1,"iend":13,"children":[{"type":"Token","kind":"FN_KW","start":28,"end":30,"istart":1,"iend":3},{"type":"Token","kind":"WHITESPACE","start":30,"end":31,"istart":3,"iend":4},{"type":"Node","kind":"NAME","start":31,"end":34,"istart":4,"iend":7,"children":[{"type":"Token","kind":"IDENT","start":31,"end":34,"istart":4,"iend":7}]},{"type":"Node","kind":"PARAM_LIST","start":34,"end":36,"istart":7,"iend":9,"children":[{"type":"Token","kind":"L_PAREN","start":34,"end":35,"istart":7,"iend":8},{"type":"Token","kind":"R_PAREN","start":35,"end":36,"istart":8,"iend":9}]},{"type":"Token","kind":"WHITESPACE","start":36,"end":37,"istart":9,"iend":10},{"type":"Node","kind":"BLOCK_EXPR","start":37,"end":40,"istart":10,"iend":13,"children":[{"type":"Node","kind":"STMT_LIST","start":37,"end":40,"istart":10,"iend":13,"children":[{"type":"Token","kind":"L_CURLY","start":37,"end":38,"istart":10,"iend":11},{"type":"Token","kind":"WHITESPACE","start":38,"end":39,"istart":11,"iend":12},{"type":"Token","kind":"R_CURLY","start":39,"end":40,"istart":12,"iend":13}]}]}]},{"type":"Token","kind":"WHITESPACE","start":40,"end":41,"istart":13,"iend":14},{"type":"Node","kind":"FN","start":41,"end":53,"istart":14,"iend":26,"children":[{"type":"Token","kind":"FN_KW","start":41,"end":43,"istart":14,"iend":16},{"type":"Token","kind":"WHITESPACE","start":43,"end":44,"istart":16,"iend":17},{"type":"Node","kind":"NAME","start":44,"end":47,"istart":17,"iend":20,"children":[{"type":"Token","kind":"IDENT","start":44,"end":47,"istart":17,"iend":20}]},{"type":"Node","kind":"PARAM_LIST","start":47,"end":49,"istart":20,"iend":22,"children":[{"type":"Token","kind":"L_PAREN","start":47,"end":48,"istart":20,"iend":21},{"type":"Token","kind":"R_PAREN","start":48,"end":49,"istart":21,"iend":22}]},{"type":"Token","kind":"WHITESPACE","start":49,"end":50,"istart":22,"iend":23},{"type":"Node","kind":"BLOCK_EXPR","start":50,"end":53,"istart":23,"iend":26,"children":[{"type":"Node","kind":"STMT_LIST","start":50,"end":53,"istart":23,"iend":26,"children":[{"type":"Token","kind":"L_CURLY","start":50,"end":51,"istart":23,"iend":24},{"type":"Token","kind":"WHITESPACE","start":51,"end":52,"istart":24,"iend":25},{"type":"Token","kind":"R_CURLY","start":52,"end":53,"istart":25,"iend":26}]}]}]},{"type":"Token","kind":"WHITESPACE","start":53,"end":58,"istart":26,"iend":31}]}]},{"type":"Token","kind":"COMMA","start":60,"end":61},{"type":"Token","kind":"WHITESPACE","start":61,"end":62},{"type":"Token","kind":"STRING","start":62,"end":64},{"type":"Token","kind":"R_PAREN","start":64,"end":65}]}]}]},{"type":"Token","kind":"SEMICOLON","start":65,"end":66}]},{"type":"Token","kind":"WHITESPACE","start":66,"end":67},{"type":"Token","kind":"R_CURLY","start":67,"end":68}]}]}]}]}"#
+            ]],
+        );
+
+        // With a raw string
+        check(
+            r###"fn test() {
+    assert!(r$0#"
+fn foo() {
+}
+fn bar() {
+}"$0#, "");
+}"###,
+            expect![[
+                r#"{"type":"Node","kind":"SOURCE_FILE","start":0,"end":63,"children":[{"type":"Node","kind":"FN","start":0,"end":63,"children":[{"type":"Token","kind":"FN_KW","start":0,"end":2},{"type":"Token","kind":"WHITESPACE","start":2,"end":3},{"type":"Node","kind":"NAME","start":3,"end":7,"children":[{"type":"Token","kind":"IDENT","start":3,"end":7}]},{"type":"Node","kind":"PARAM_LIST","start":7,"end":9,"children":[{"type":"Token","kind":"L_PAREN","start":7,"end":8},{"type":"Token","kind":"R_PAREN","start":8,"end":9}]},{"type":"Token","kind":"WHITESPACE","start":9,"end":10},{"type":"Node","kind":"BLOCK_EXPR","start":10,"end":63,"children":[{"type":"Node","kind":"STMT_LIST","start":10,"end":63,"children":[{"type":"Token","kind":"L_CURLY","start":10,"end":11},{"type":"Token","kind":"WHITESPACE","start":11,"end":16},{"type":"Node","kind":"EXPR_STMT","start":16,"end":61,"children":[{"type":"Node","kind":"MACRO_EXPR","start":16,"end":60,"children":[{"type":"Node","kind":"MACRO_CALL","start":16,"end":60,"children":[{"type":"Node","kind":"PATH","start":16,"end":22,"children":[{"type":"Node","kind":"PATH_SEGMENT","start":16,"end":22,"children":[{"type":"Node","kind":"NAME_REF","start":16,"end":22,"children":[{"type":"Token","kind":"IDENT","start":16,"end":22}]}]}]},{"type":"Token","kind":"BANG","start":22,"end":23},{"type":"Node","kind":"TOKEN_TREE","start":23,"end":60,"children":[{"type":"Token","kind":"L_PAREN","start":23,"end":24},{"type":"Node","kind":"STRING","start":24,"end":55,"children":[{"type":"Node","kind":"SOURCE_FILE","start":27,"end":53,"istart":0,"iend":26,"children":[{"type":"Token","kind":"WHITESPACE","start":27,"end":28,"istart":0,"iend":1},{"type":"Node","kind":"FN","start":28,"end":40,"istart":1,"iend":13,"children":[{"type":"Token","kind":"FN_KW","start":28,"end":30,"istart":1,"iend":3},{"type":"Token","kind":"WHITESPACE","start":30,"end":31,"istart":3,"iend":4},{"type":"Node","kind":"NAME","start":31,"end":34,"istart":4,"iend":7,"children":[{"type":"Token","kind":"IDENT","start":31,"end":34,"istart":4,"iend":7}]},{"type":"Node","kind":"PARAM_LIST","start":34,"end":36,"istart":7,"iend":9,"children":[{"type":"Token","kind":"L_PAREN","start":34,"end":35,"istart":7,"iend":8},{"type":"Token","kind":"R_PAREN","start":35,"end":36,"istart":8,"iend":9}]},{"type":"Token","kind":"WHITESPACE","start":36,"end":37,"istart":9,"iend":10},{"type":"Node","kind":"BLOCK_EXPR","start":37,"end":40,"istart":10,"iend":13,"children":[{"type":"Node","kind":"STMT_LIST","start":37,"end":40,"istart":10,"iend":13,"children":[{"type":"Token","kind":"L_CURLY","start":37,"end":38,"istart":10,"iend":11},{"type":"Token","kind":"WHITESPACE","start":38,"end":39,"istart":11,"iend":12},{"type":"Token","kind":"R_CURLY","start":39,"end":40,"istart":12,"iend":13}]}]}]},{"type":"Token","kind":"WHITESPACE","start":40,"end":41,"istart":13,"iend":14},{"type":"Node","kind":"FN","start":41,"end":53,"istart":14,"iend":26,"children":[{"type":"Token","kind":"FN_KW","start":41,"end":43,"istart":14,"iend":16},{"type":"Token","kind":"WHITESPACE","start":43,"end":44,"istart":16,"iend":17},{"type":"Node","kind":"NAME","start":44,"end":47,"istart":17,"iend":20,"children":[{"type":"Token","kind":"IDENT","start":44,"end":47,"istart":17,"iend":20}]},{"type":"Node","kind":"PARAM_LIST","start":47,"end":49,"istart":20,"iend":22,"children":[{"type":"Token","kind":"L_PAREN","start":47,"end":48,"istart":20,"iend":21},{"type":"Token","kind":"R_PAREN","start":48,"end":49,"istart":21,"iend":22}]},{"type":"Token","kind":"WHITESPACE","start":49,"end":50,"istart":22,"iend":23},{"type":"Node","kind":"BLOCK_EXPR","start":50,"end":53,"istart":23,"iend":26,"children":[{"type":"Node","kind":"STMT_LIST","start":50,"end":53,"istart":23,"iend":26,"children":[{"type":"Token","kind":"L_CURLY","start":50,"end":51,"istart":23,"iend":24},{"type":"Token","kind":"WHITESPACE","start":51,"end":52,"istart":24,"iend":25},{"type":"Token","kind":"R_CURLY","start":52,"end":53,"istart":25,"iend":26}]}]}]}]}]},{"type":"Token","kind":"COMMA","start":55,"end":56},{"type":"Token","kind":"WHITESPACE","start":56,"end":57},{"type":"Token","kind":"STRING","start":57,"end":59},{"type":"Token","kind":"R_PAREN","start":59,"end":60}]}]}]},{"type":"Token","kind":"SEMICOLON","start":60,"end":61}]},{"type":"Token","kind":"WHITESPACE","start":61,"end":62},{"type":"Token","kind":"R_CURLY","start":62,"end":63}]}]}]}]}"#
+            ]],
+        );
+    }
+}
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 66b8900109c..b3b46421b50 100644
--- a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs
+++ b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs
@@ -174,6 +174,7 @@ define_symbols! {
     const_param_ty,
     Context,
     Continue,
+    convert,
     copy,
     Copy,
     core_panic,
@@ -239,6 +240,8 @@ define_symbols! {
     format_unsafe_arg,
     format,
     freeze,
+    From,
+    FromStr,
     from_output,
     from_residual,
     from_usize,
@@ -429,6 +432,7 @@ define_symbols! {
     shr,
     simd,
     sized,
+    skip,
     slice_len_fn,
     Some,
     start,
@@ -456,6 +460,7 @@ define_symbols! {
     transmute_trait,
     transparent,
     Try,
+    TryFrom,
     tuple_trait,
     u128,
     u16,
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 73899408652..00446b27cf2 100644
--- a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs
@@ -242,9 +242,6 @@ impl ProjectFolders {
                     }
                 }
 
-                if dirs.include.is_empty() {
-                    continue;
-                }
                 vfs::loader::Entry::Directories(dirs)
             };
 
@@ -267,7 +264,7 @@ impl ProjectFolders {
             };
 
             let file_set_roots = vec![VfsPath::from(ratoml_path.to_owned())];
-            let entry = vfs::loader::Entry::Files(vec![ratoml_path]);
+            let entry = vfs::loader::Entry::Files(vec![ratoml_path.to_owned()]);
 
             res.watch.push(res.load.len());
             res.load.push(entry);
diff --git a/src/tools/rust-analyzer/crates/mbe/src/expander/transcriber.rs b/src/tools/rust-analyzer/crates/mbe/src/expander/transcriber.rs
index 9255c5a6899..7710ea79389 100644
--- a/src/tools/rust-analyzer/crates/mbe/src/expander/transcriber.rs
+++ b/src/tools/rust-analyzer/crates/mbe/src/expander/transcriber.rs
@@ -38,7 +38,10 @@ impl<'t> Bindings<'t> {
             nesting_state.hit = true;
             b = match b {
                 Binding::Fragment(_) => break,
-                Binding::Missing(_) => break,
+                Binding::Missing(_) => {
+                    nesting_state.at_end = true;
+                    break;
+                }
                 Binding::Nested(bs) => bs.get(nesting_state.idx).ok_or_else(|| {
                     nesting_state.at_end = true;
                     binding_err!("could not find nested binding `{name}`")
@@ -445,6 +448,7 @@ fn expand_repeat(
     let mut counter = 0;
     let mut err = None;
 
+    let initial_restore_point = builder.restore_point();
     let mut restore_point = builder.restore_point();
     loop {
         let ExpandResult { value: (), err: e } =
@@ -462,6 +466,10 @@ fn expand_repeat(
 
         counter += 1;
         if counter == limit {
+            // FIXME: This is a bug here, we get here when we shouldn't, see https://github.com/rust-lang/rust-analyzer/issues/18910.
+            // If we don't restore we emit a lot of nodes which causes a stack overflow down the road. For now just ignore them,
+            // there is always an error here anyway.
+            builder.restore(initial_restore_point);
             err = Some(ExpandError::new(ctx.call_site, ExpandErrorKind::LimitExceeded));
             break;
         }
diff --git a/src/tools/rust-analyzer/crates/mbe/src/lib.rs b/src/tools/rust-analyzer/crates/mbe/src/lib.rs
index 6abf56d4b37..bebd29ef747 100644
--- a/src/tools/rust-analyzer/crates/mbe/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/mbe/src/lib.rs
@@ -369,7 +369,8 @@ pub fn expect_fragment<'t>(
 ) -> ExpandResult<tt::TokenTreesView<'t, Span>> {
     use ::parser;
     let buffer = tt_iter.remaining();
-    let parser_input = to_parser_input(edition, buffer);
+    // FIXME: Pass the correct edition per token. Due to the split between mbe and hir-expand it's complicated.
+    let parser_input = to_parser_input(buffer, &mut |_ctx| edition);
     let tree_traversal = entry_point.parse(&parser_input, edition);
     let mut cursor = buffer.cursor();
     let mut error = false;
diff --git a/src/tools/rust-analyzer/crates/mbe/src/tests.rs b/src/tools/rust-analyzer/crates/mbe/src/tests.rs
index e63ad113ffd..fb68d35a4c8 100644
--- a/src/tools/rust-analyzer/crates/mbe/src/tests.rs
+++ b/src/tools/rust-analyzer/crates/mbe/src/tests.rs
@@ -26,7 +26,7 @@ fn check_(
             file_id: EditionedFileId::new(FileId::from_raw(0), def_edition),
             ast_id: ErasedFileAstId::from_raw(0),
         },
-        SyntaxContextId::ROOT,
+        SyntaxContextId::root(Edition::CURRENT),
         decl,
     )
     .unwrap();
@@ -39,16 +39,20 @@ fn check_(
         file_id: EditionedFileId::new(FileId::from_raw(1), call_edition),
         ast_id: ErasedFileAstId::from_raw(0),
     };
-    let arg_tt =
-        syntax_bridge::parse_to_token_tree(call_edition, call_anchor, SyntaxContextId::ROOT, arg)
-            .unwrap();
+    let arg_tt = syntax_bridge::parse_to_token_tree(
+        call_edition,
+        call_anchor,
+        SyntaxContextId::root(Edition::CURRENT),
+        arg,
+    )
+    .unwrap();
     let res = mac.expand(
         &arg_tt,
         |_| (),
         Span {
             range: TextRange::up_to(TextSize::of(arg)),
             anchor: call_anchor,
-            ctx: SyntaxContextId::ROOT,
+            ctx: SyntaxContextId::root(Edition::CURRENT),
         },
         def_edition,
     );
@@ -59,7 +63,12 @@ fn check_(
     if render_debug {
         format_to!(expect_res, "{:#?}\n\n", res.value.0);
     }
-    let (node, _) = syntax_bridge::token_tree_to_syntax_node(&res.value.0, parse, def_edition);
+    let (node, _) = syntax_bridge::token_tree_to_syntax_node(
+        &res.value.0,
+        parse,
+        &mut |_| def_edition,
+        def_edition,
+    );
     format_to!(
         expect_res,
         "{}",
@@ -106,25 +115,25 @@ fn token_mapping_smoke_test() {
 struct MyTraitMap2
 "#,
         expect![[r#"
-            SUBTREE $$ 1:0@0..20#0 1:0@0..20#0
-              IDENT   struct 0:0@34..40#0
-              IDENT   MyTraitMap2 1:0@8..19#0
-              SUBTREE {} 0:0@48..49#0 0:0@100..101#0
-                IDENT   map 0:0@58..61#0
-                PUNCH   : [alone] 0:0@61..62#0
-                PUNCH   : [joint] 0:0@63..64#0
-                PUNCH   : [alone] 0:0@64..65#0
-                IDENT   std 0:0@65..68#0
-                PUNCH   : [joint] 0:0@68..69#0
-                PUNCH   : [alone] 0:0@69..70#0
-                IDENT   collections 0:0@70..81#0
-                PUNCH   : [joint] 0:0@81..82#0
-                PUNCH   : [alone] 0:0@82..83#0
-                IDENT   HashSet 0:0@83..90#0
-                PUNCH   < [alone] 0:0@90..91#0
-                SUBTREE () 0:0@91..92#0 0:0@92..93#0
-                PUNCH   > [joint] 0:0@93..94#0
-                PUNCH   , [alone] 0:0@94..95#0
+            SUBTREE $$ 1:0@0..20#2 1:0@0..20#2
+              IDENT   struct 0:0@34..40#2
+              IDENT   MyTraitMap2 1:0@8..19#2
+              SUBTREE {} 0:0@48..49#2 0:0@100..101#2
+                IDENT   map 0:0@58..61#2
+                PUNCH   : [alone] 0:0@61..62#2
+                PUNCH   : [joint] 0:0@63..64#2
+                PUNCH   : [alone] 0:0@64..65#2
+                IDENT   std 0:0@65..68#2
+                PUNCH   : [joint] 0:0@68..69#2
+                PUNCH   : [alone] 0:0@69..70#2
+                IDENT   collections 0:0@70..81#2
+                PUNCH   : [joint] 0:0@81..82#2
+                PUNCH   : [alone] 0:0@82..83#2
+                IDENT   HashSet 0:0@83..90#2
+                PUNCH   < [alone] 0:0@90..91#2
+                SUBTREE () 0:0@91..92#2 0:0@92..93#2
+                PUNCH   > [joint] 0:0@93..94#2
+                PUNCH   , [alone] 0:0@94..95#2
 
             struct MyTraitMap2 {
                 map: ::std::collections::HashSet<()>,
@@ -153,28 +162,28 @@ fn main() {
 }
 "#,
         expect![[r#"
-            SUBTREE $$ 1:0@0..63#0 1:0@0..63#0
-              IDENT   fn 1:0@1..3#0
-              IDENT   main 1:0@4..8#0
-              SUBTREE () 1:0@8..9#0 1:0@9..10#0
-              SUBTREE {} 1:0@11..12#0 1:0@61..62#0
-                LITERAL Integer 1 1:0@17..18#0
-                PUNCH   ; [alone] 1:0@18..19#0
-                LITERAL Float 1.0 1:0@24..27#0
-                PUNCH   ; [alone] 1:0@27..28#0
-                SUBTREE () 1:0@33..34#0 1:0@39..40#0
-                  SUBTREE () 1:0@34..35#0 1:0@37..38#0
-                    LITERAL Integer 1 1:0@35..36#0
-                    PUNCH   , [alone] 1:0@36..37#0
-                  PUNCH   , [alone] 1:0@38..39#0
-                PUNCH   . [alone] 1:0@40..41#0
-                LITERAL Float 0.0 1:0@41..44#0
-                PUNCH   ; [alone] 1:0@44..45#0
-                IDENT   let 1:0@50..53#0
-                IDENT   x 1:0@54..55#0
-                PUNCH   = [alone] 1:0@56..57#0
-                LITERAL Integer 1 1:0@58..59#0
-                PUNCH   ; [alone] 1:0@59..60#0
+            SUBTREE $$ 1:0@0..63#2 1:0@0..63#2
+              IDENT   fn 1:0@1..3#2
+              IDENT   main 1:0@4..8#2
+              SUBTREE () 1:0@8..9#2 1:0@9..10#2
+              SUBTREE {} 1:0@11..12#2 1:0@61..62#2
+                LITERAL Integer 1 1:0@17..18#2
+                PUNCH   ; [alone] 1:0@18..19#2
+                LITERAL Float 1.0 1:0@24..27#2
+                PUNCH   ; [alone] 1:0@27..28#2
+                SUBTREE () 1:0@33..34#2 1:0@39..40#2
+                  SUBTREE () 1:0@34..35#2 1:0@37..38#2
+                    LITERAL Integer 1 1:0@35..36#2
+                    PUNCH   , [alone] 1:0@36..37#2
+                  PUNCH   , [alone] 1:0@38..39#2
+                PUNCH   . [alone] 1:0@40..41#2
+                LITERAL Float 0.0 1:0@41..44#2
+                PUNCH   ; [alone] 1:0@44..45#2
+                IDENT   let 1:0@50..53#2
+                IDENT   x 1:0@54..55#2
+                PUNCH   = [alone] 1:0@56..57#2
+                LITERAL Integer 1 1:0@58..59#2
+                PUNCH   ; [alone] 1:0@59..60#2
 
             fn main(){
                 1;
@@ -200,14 +209,14 @@ fn expr_2021() {
     const { 1 },
 "#,
         expect![[r#"
-            SUBTREE $$ 1:0@0..25#0 1:0@0..25#0
-              IDENT   _ 1:0@5..6#0
-              PUNCH   ; [joint] 0:0@36..37#0
-              SUBTREE () 0:0@34..35#0 0:0@34..35#0
-                IDENT   const 1:0@12..17#0
-                SUBTREE {} 1:0@18..19#0 1:0@22..23#0
-                  LITERAL Integer 1 1:0@20..21#0
-              PUNCH   ; [alone] 0:0@39..40#0
+            SUBTREE $$ 1:0@0..25#2 1:0@0..25#2
+              IDENT   _ 1:0@5..6#2
+              PUNCH   ; [joint] 0:0@36..37#2
+              SUBTREE () 0:0@34..35#2 0:0@34..35#2
+                IDENT   const 1:0@12..17#2
+                SUBTREE {} 1:0@18..19#2 1:0@22..23#2
+                  LITERAL Integer 1 1:0@20..21#2
+              PUNCH   ; [alone] 0:0@39..40#2
 
             _;
             (const  {
@@ -228,13 +237,13 @@ fn expr_2021() {
         expect![[r#"
             ExpandError {
                 inner: (
-                    1:0@5..6#0,
+                    1:0@5..6#2,
                     NoMatchingRule,
                 ),
             }
 
-            SUBTREE $$ 1:0@0..8#0 1:0@0..8#0
-              PUNCH   ; [alone] 0:0@39..40#0
+            SUBTREE $$ 1:0@0..8#2 1:0@0..8#2
+              PUNCH   ; [alone] 0:0@39..40#2
 
             ;"#]],
     );
@@ -252,13 +261,13 @@ fn expr_2021() {
         expect![[r#"
             ExpandError {
                 inner: (
-                    1:0@5..10#0,
+                    1:0@5..10#2,
                     NoMatchingRule,
                 ),
             }
 
-            SUBTREE $$ 1:0@0..18#0 1:0@0..18#0
-              PUNCH   ; [alone] 0:0@39..40#0
+            SUBTREE $$ 1:0@0..18#2 1:0@0..18#2
+              PUNCH   ; [alone] 0:0@39..40#2
 
             ;"#]],
     );
@@ -278,26 +287,26 @@ fn expr_2021() {
     break 'foo bar,
 "#,
         expect![[r#"
-            SUBTREE $$ 1:0@0..76#0 1:0@0..76#0
-              LITERAL Integer 4 1:0@5..6#0
-              PUNCH   ; [joint] 0:0@41..42#0
-              LITERAL Str literal 1:0@12..21#0
-              PUNCH   ; [joint] 0:0@41..42#0
-              SUBTREE () 0:0@39..40#0 0:0@39..40#0
-                IDENT   funcall 1:0@27..34#0
-                SUBTREE () 1:0@34..35#0 1:0@35..36#0
-              PUNCH   ; [joint] 0:0@41..42#0
-              SUBTREE () 0:0@39..40#0 0:0@39..40#0
-                IDENT   future 1:0@42..48#0
-                PUNCH   . [alone] 1:0@48..49#0
-                IDENT   await 1:0@49..54#0
-              PUNCH   ; [joint] 0:0@41..42#0
-              SUBTREE () 0:0@39..40#0 0:0@39..40#0
-                IDENT   break 1:0@60..65#0
-                PUNCH   ' [joint] 1:0@66..67#0
-                IDENT   foo 1:0@67..70#0
-                IDENT   bar 1:0@71..74#0
-              PUNCH   ; [alone] 0:0@44..45#0
+            SUBTREE $$ 1:0@0..76#2 1:0@0..76#2
+              LITERAL Integer 4 1:0@5..6#2
+              PUNCH   ; [joint] 0:0@41..42#2
+              LITERAL Str literal 1:0@12..21#2
+              PUNCH   ; [joint] 0:0@41..42#2
+              SUBTREE () 0:0@39..40#2 0:0@39..40#2
+                IDENT   funcall 1:0@27..34#2
+                SUBTREE () 1:0@34..35#2 1:0@35..36#2
+              PUNCH   ; [joint] 0:0@41..42#2
+              SUBTREE () 0:0@39..40#2 0:0@39..40#2
+                IDENT   future 1:0@42..48#2
+                PUNCH   . [alone] 1:0@48..49#2
+                IDENT   await 1:0@49..54#2
+              PUNCH   ; [joint] 0:0@41..42#2
+              SUBTREE () 0:0@39..40#2 0:0@39..40#2
+                IDENT   break 1:0@60..65#2
+                PUNCH   ' [joint] 1:0@66..67#2
+                IDENT   foo 1:0@67..70#2
+                IDENT   bar 1:0@71..74#2
+              PUNCH   ; [alone] 0:0@44..45#2
 
             4;
             "literal";
@@ -319,13 +328,13 @@ fn expr_2021() {
         expect![[r#"
             ExpandError {
                 inner: (
-                    1:0@5..6#0,
+                    1:0@5..6#2,
                     NoMatchingRule,
                 ),
             }
 
-            SUBTREE $$ 1:0@0..8#0 1:0@0..8#0
-              PUNCH   ; [alone] 0:0@44..45#0
+            SUBTREE $$ 1:0@0..8#2 1:0@0..8#2
+              PUNCH   ; [alone] 0:0@44..45#2
 
             ;"#]],
     );
diff --git a/src/tools/rust-analyzer/crates/parser/src/event.rs b/src/tools/rust-analyzer/crates/parser/src/event.rs
index e38571dd3ec..b197b086f37 100644
--- a/src/tools/rust-analyzer/crates/parser/src/event.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/event.rs
@@ -12,7 +12,7 @@ use crate::{
 /// `Parser` produces a flat list of `Event`s.
 /// They are converted to a tree-structure in
 /// a separate pass, via `TreeBuilder`.
-#[derive(Debug)]
+#[derive(Debug, PartialEq)]
 pub(crate) enum Event {
     /// This event signifies the start of the node.
     /// It should be either abandoned (in which case the
diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs
index 3b3f11be130..389c01933c9 100644
--- a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs
@@ -134,10 +134,12 @@ pub(super) fn let_stmt(p: &mut Parser<'_>, with_semi: Semicolon) {
         // test_err let_else_right_curly_brace
         // fn func() { let Some(_) = {Some(1)} else { panic!("h") };}
         if let Some(expr) = expr_after_eq {
-            if BlockLike::is_blocklike(expr.kind()) {
-                p.error(
-                    "right curly brace `}` before `else` in a `let...else` statement not allowed",
-                )
+            if let Some(token) = expr.last_token(p) {
+                if token == T!['}'] {
+                    p.error(
+                        "right curly brace `}` before `else` in a `let...else` statement not allowed"
+                    )
+                }
             }
         }
 
@@ -339,13 +341,20 @@ fn lhs(p: &mut Parser<'_>, r: Restrictions) -> Option<(CompletedMarker, BlockLik
         //     // raw reference operator
         //     let _ = &raw mut foo;
         //     let _ = &raw const foo;
+        //     let _ = &raw foo;
         // }
         T![&] => {
             m = p.start();
             p.bump(T![&]);
-            if p.at_contextual_kw(T![raw]) && [T![mut], T![const]].contains(&p.nth(1)) {
-                p.bump_remap(T![raw]);
-                p.bump_any();
+            if p.at_contextual_kw(T![raw]) {
+                if [T![mut], T![const]].contains(&p.nth(1)) {
+                    p.bump_remap(T![raw]);
+                    p.bump_any();
+                } else if p.nth_at(1, SyntaxKind::IDENT) {
+                    // we treat raw as keyword in this case
+                    // &raw foo;
+                    p.bump_remap(T![raw]);
+                }
             } else {
                 p.eat(T![mut]);
             }
diff --git a/src/tools/rust-analyzer/crates/parser/src/parser.rs b/src/tools/rust-analyzer/crates/parser/src/parser.rs
index 75a75f601cf..2f6ba525747 100644
--- a/src/tools/rust-analyzer/crates/parser/src/parser.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/parser.rs
@@ -318,7 +318,8 @@ impl Marker {
             _ => unreachable!(),
         }
         p.push_event(Event::Finish);
-        CompletedMarker::new(self.pos, kind)
+        let end_pos = p.events.len() as u32;
+        CompletedMarker::new(self.pos, end_pos, kind)
     }
 
     /// Abandons the syntax tree node. All its children
@@ -336,13 +337,14 @@ impl Marker {
 }
 
 pub(crate) struct CompletedMarker {
-    pos: u32,
+    start_pos: u32,
+    end_pos: u32,
     kind: SyntaxKind,
 }
 
 impl CompletedMarker {
-    fn new(pos: u32, kind: SyntaxKind) -> Self {
-        CompletedMarker { pos, kind }
+    fn new(start_pos: u32, end_pos: u32, kind: SyntaxKind) -> Self {
+        CompletedMarker { start_pos, end_pos, kind }
     }
 
     /// This method allows to create a new node which starts
@@ -360,10 +362,10 @@ impl CompletedMarker {
     /// distance to `NEWSTART` into forward_parent(=2 in this case);
     pub(crate) fn precede(self, p: &mut Parser<'_>) -> Marker {
         let new_pos = p.start();
-        let idx = self.pos as usize;
+        let idx = self.start_pos as usize;
         match &mut p.events[idx] {
             Event::Start { forward_parent, .. } => {
-                *forward_parent = Some(new_pos.pos - self.pos);
+                *forward_parent = Some(new_pos.pos - self.start_pos);
             }
             _ => unreachable!(),
         }
@@ -376,7 +378,7 @@ impl CompletedMarker {
         let idx = m.pos as usize;
         match &mut p.events[idx] {
             Event::Start { forward_parent, .. } => {
-                *forward_parent = Some(self.pos - m.pos);
+                *forward_parent = Some(self.start_pos - m.pos);
             }
             _ => unreachable!(),
         }
@@ -386,4 +388,13 @@ impl CompletedMarker {
     pub(crate) fn kind(&self) -> SyntaxKind {
         self.kind
     }
+
+    pub(crate) fn last_token(&self, p: &Parser<'_>) -> Option<SyntaxKind> {
+        let end_pos = self.end_pos as usize;
+        debug_assert_eq!(p.events[end_pos - 1], Event::Finish);
+        p.events[..end_pos].iter().rev().find_map(|event| match event {
+            Event::Token { kind, .. } => Some(*kind),
+            _ => None,
+        })
+    }
 }
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0056_let_else_right_curly_brace_struct.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0056_let_else_right_curly_brace_struct.rast
new file mode 100644
index 00000000000..578dc2b0f96
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0056_let_else_right_curly_brace_struct.rast
@@ -0,0 +1,79 @@
+SOURCE_FILE
+  STRUCT
+    STRUCT_KW "struct"
+    WHITESPACE " "
+    NAME
+      IDENT "X"
+    WHITESPACE " "
+    RECORD_FIELD_LIST
+      L_CURLY "{"
+      RECORD_FIELD
+        NAME
+          IDENT "a"
+        COLON ":"
+        WHITESPACE " "
+        PATH_TYPE
+          PATH
+            PATH_SEGMENT
+              NAME_REF
+                IDENT "i32"
+      R_CURLY "}"
+  WHITESPACE "\n"
+  FN
+    FN_KW "fn"
+    WHITESPACE " "
+    NAME
+      IDENT "f"
+    PARAM_LIST
+      L_PAREN "("
+      R_PAREN ")"
+    WHITESPACE " "
+    BLOCK_EXPR
+      STMT_LIST
+        L_CURLY "{"
+        WHITESPACE "\n    "
+        LET_STMT
+          LET_KW "let"
+          WHITESPACE " "
+          IDENT_PAT
+            NAME
+              IDENT "foo"
+          WHITESPACE " "
+          EQ "="
+          WHITESPACE " "
+          RECORD_EXPR
+            PATH
+              PATH_SEGMENT
+                NAME_REF
+                  IDENT "X"
+            WHITESPACE " "
+            RECORD_EXPR_FIELD_LIST
+              L_CURLY "{"
+              WHITESPACE "\n        "
+              RECORD_EXPR_FIELD
+                NAME_REF
+                  IDENT "a"
+                COLON ":"
+                WHITESPACE " "
+                LITERAL
+                  INT_NUMBER "1"
+              WHITESPACE "\n    "
+              R_CURLY "}"
+          WHITESPACE " "
+          LET_ELSE
+            ELSE_KW "else"
+            WHITESPACE " "
+            BLOCK_EXPR
+              STMT_LIST
+                L_CURLY "{"
+                WHITESPACE "\n        "
+                EXPR_STMT
+                  RETURN_EXPR
+                    RETURN_KW "return"
+                  SEMICOLON ";"
+                WHITESPACE "\n    "
+                R_CURLY "}"
+          SEMICOLON ";"
+        WHITESPACE "\n"
+        R_CURLY "}"
+error 63: right curly brace `}` before `else` in a `let...else` statement not allowed
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0056_let_else_right_curly_brace_struct.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0056_let_else_right_curly_brace_struct.rs
new file mode 100644
index 00000000000..c0c0edc9830
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0056_let_else_right_curly_brace_struct.rs
@@ -0,0 +1,8 @@
+struct X {a: i32}
+fn f() {
+    let foo = X {
+        a: 1
+    } else {
+        return;
+    };
+}
\ No newline at end of file
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0057_let_else_right_curly_brace_arithmetic.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0057_let_else_right_curly_brace_arithmetic.rast
new file mode 100644
index 00000000000..8e994f22d41
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0057_let_else_right_curly_brace_arithmetic.rast
@@ -0,0 +1,42 @@
+SOURCE_FILE
+  ERROR
+    LET_KW "let"
+    WHITESPACE " "
+    IDENT_PAT
+      NAME
+        IDENT "foo"
+    WHITESPACE " "
+    EQ "="
+    WHITESPACE " "
+    BIN_EXPR
+      LITERAL
+        INT_NUMBER "1"
+      WHITESPACE " "
+      PLUS "+"
+      WHITESPACE " "
+      BLOCK_EXPR
+        STMT_LIST
+          L_CURLY "{"
+          WHITESPACE "\n    "
+          LITERAL
+            INT_NUMBER "1"
+          WHITESPACE "\n"
+          R_CURLY "}"
+    WHITESPACE " "
+    LET_ELSE
+      ELSE_KW "else"
+      WHITESPACE " "
+      BLOCK_EXPR
+        STMT_LIST
+          L_CURLY "{"
+          WHITESPACE "\n    "
+          EXPR_STMT
+            RETURN_EXPR
+              RETURN_KW "return"
+            SEMICOLON ";"
+          WHITESPACE "\n"
+          R_CURLY "}"
+    SEMICOLON ";"
+  WHITESPACE "\n"
+error 0: expected an item
+error 23: right curly brace `}` before `else` in a `let...else` statement not allowed
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0057_let_else_right_curly_brace_arithmetic.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0057_let_else_right_curly_brace_arithmetic.rs
new file mode 100644
index 00000000000..c29ddcce1ff
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0057_let_else_right_curly_brace_arithmetic.rs
@@ -0,0 +1,5 @@
+let foo = 1 + {
+    1
+} else {
+    return;
+};
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0057_let_else_right_curly_brace_format_args.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0057_let_else_right_curly_brace_format_args.rast
new file mode 100644
index 00000000000..055b583acec
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0057_let_else_right_curly_brace_format_args.rast
@@ -0,0 +1,90 @@
+SOURCE_FILE
+  FN
+    FN_KW "fn"
+    WHITESPACE " "
+    NAME
+      IDENT "r"
+    PARAM_LIST
+      L_PAREN "("
+      R_PAREN ")"
+    WHITESPACE " "
+    BLOCK_EXPR
+      STMT_LIST
+        L_CURLY "{"
+        WHITESPACE "\n    "
+        LET_STMT
+          LET_KW "let"
+          WHITESPACE " "
+          IDENT_PAT
+            NAME
+              IDENT "ok"
+          WHITESPACE " "
+          EQ "="
+          WHITESPACE " "
+          MACRO_EXPR
+            MACRO_CALL
+              PATH
+                PATH_SEGMENT
+                  NAME_REF
+                    IDENT "format_args"
+              BANG "!"
+              TOKEN_TREE
+                L_PAREN "("
+                STRING "\"\""
+                R_PAREN ")"
+          WHITESPACE " "
+          LET_ELSE
+            ELSE_KW "else"
+            WHITESPACE " "
+            BLOCK_EXPR
+              STMT_LIST
+                L_CURLY "{"
+                WHITESPACE " "
+                EXPR_STMT
+                  RETURN_EXPR
+                    RETURN_KW "return"
+                  SEMICOLON ";"
+                WHITESPACE " "
+                R_CURLY "}"
+          SEMICOLON ";"
+        WHITESPACE "\n\n    "
+        LET_STMT
+          LET_KW "let"
+          WHITESPACE " "
+          IDENT_PAT
+            NAME
+              IDENT "bad"
+          WHITESPACE " "
+          EQ "="
+          WHITESPACE " "
+          MACRO_EXPR
+            MACRO_CALL
+              PATH
+                PATH_SEGMENT
+                  NAME_REF
+                    IDENT "format_args"
+              BANG "!"
+              WHITESPACE " "
+              TOKEN_TREE
+                L_CURLY "{"
+                STRING "\"\""
+                R_CURLY "}"
+          WHITESPACE " "
+          LET_ELSE
+            ELSE_KW "else"
+            WHITESPACE " "
+            BLOCK_EXPR
+              STMT_LIST
+                L_CURLY "{"
+                WHITESPACE " "
+                EXPR_STMT
+                  RETURN_EXPR
+                    RETURN_KW "return"
+                  SEMICOLON ";"
+                WHITESPACE " "
+                R_CURLY "}"
+          SEMICOLON ";"
+        WHITESPACE "\n"
+        R_CURLY "}"
+  WHITESPACE "\n"
+error 89: right curly brace `}` before `else` in a `let...else` statement not allowed
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0057_let_else_right_curly_brace_format_args.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0057_let_else_right_curly_brace_format_args.rs
new file mode 100644
index 00000000000..5916fa07dc2
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0057_let_else_right_curly_brace_format_args.rs
@@ -0,0 +1,5 @@
+fn r() {
+    let ok = format_args!("") else { return; };
+
+    let bad = format_args! {""} else { return; };
+}
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0058_let_else_right_curly_brace_range.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0058_let_else_right_curly_brace_range.rast
new file mode 100644
index 00000000000..8c7fd8c295d
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0058_let_else_right_curly_brace_range.rast
@@ -0,0 +1,40 @@
+SOURCE_FILE
+  ERROR
+    LET_KW "let"
+    WHITESPACE " "
+    IDENT_PAT
+      NAME
+        IDENT "foo"
+    WHITESPACE " "
+    EQ "="
+    WHITESPACE " "
+    RANGE_EXPR
+      LITERAL
+        INT_NUMBER "1"
+      DOT2 ".."
+      BLOCK_EXPR
+        STMT_LIST
+          L_CURLY "{"
+          WHITESPACE "\n    "
+          LITERAL
+            INT_NUMBER "1"
+          WHITESPACE "\n"
+          R_CURLY "}"
+    WHITESPACE " "
+    LET_ELSE
+      ELSE_KW "else"
+      WHITESPACE " "
+      BLOCK_EXPR
+        STMT_LIST
+          L_CURLY "{"
+          WHITESPACE "\n    "
+          EXPR_STMT
+            RETURN_EXPR
+              RETURN_KW "return"
+            SEMICOLON ";"
+          WHITESPACE "\n"
+          R_CURLY "}"
+    SEMICOLON ";"
+  WHITESPACE "\n"
+error 0: expected an item
+error 22: right curly brace `}` before `else` in a `let...else` statement not allowed
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0058_let_else_right_curly_brace_range.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0058_let_else_right_curly_brace_range.rs
new file mode 100644
index 00000000000..5417131d28e
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0058_let_else_right_curly_brace_range.rs
@@ -0,0 +1,5 @@
+let foo = 1..{
+    1
+} else {
+    return;
+};
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0059_let_else_right_curly_brace_closure.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0059_let_else_right_curly_brace_closure.rast
new file mode 100644
index 00000000000..57925a0d192
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0059_let_else_right_curly_brace_closure.rast
@@ -0,0 +1,55 @@
+SOURCE_FILE
+  ERROR
+    LET_KW "let"
+    WHITESPACE " "
+    IDENT_PAT
+      NAME
+        IDENT "foo"
+    WHITESPACE " "
+    EQ "="
+    WHITESPACE " "
+    CLOSURE_EXPR
+      PARAM_LIST
+        PIPE "|"
+        PARAM
+          IDENT_PAT
+            NAME
+              IDENT "x"
+          COLON ":"
+          WHITESPACE " "
+          PATH_TYPE
+            PATH
+              PATH_SEGMENT
+                NAME_REF
+                  IDENT "i32"
+        PIPE "|"
+      WHITESPACE " "
+      BLOCK_EXPR
+        STMT_LIST
+          L_CURLY "{"
+          WHITESPACE "\n    "
+          PATH_EXPR
+            PATH
+              PATH_SEGMENT
+                NAME_REF
+                  IDENT "x"
+          WHITESPACE "\n"
+          R_CURLY "}"
+    WHITESPACE " "
+    LET_ELSE
+      ELSE_KW "else"
+      WHITESPACE " "
+      BLOCK_EXPR
+        STMT_LIST
+          L_CURLY "{"
+          WHITESPACE "\n    "
+          EXPR_STMT
+            RETURN_EXPR
+              RETURN_KW "return"
+            SEMICOLON ";"
+          WHITESPACE "\n"
+          R_CURLY "}"
+    SEMICOLON ";"
+  WHITESPACE "\n"
+error 0: expected an item
+error 28: right curly brace `}` before `else` in a `let...else` statement not allowed
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0059_let_else_right_curly_brace_closure.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0059_let_else_right_curly_brace_closure.rs
new file mode 100644
index 00000000000..89c7579b071
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0059_let_else_right_curly_brace_closure.rs
@@ -0,0 +1,5 @@
+let foo = |x: i32| {
+    x
+} else {
+    return;
+};
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0060_let_else_right_curly_brace_unary.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0060_let_else_right_curly_brace_unary.rast
new file mode 100644
index 00000000000..4fb70bd50e3
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0060_let_else_right_curly_brace_unary.rast
@@ -0,0 +1,38 @@
+SOURCE_FILE
+  ERROR
+    LET_KW "let"
+    WHITESPACE " "
+    IDENT_PAT
+      NAME
+        IDENT "foo"
+    WHITESPACE " "
+    EQ "="
+    WHITESPACE " "
+    PREFIX_EXPR
+      MINUS "-"
+      BLOCK_EXPR
+        STMT_LIST
+          L_CURLY "{"
+          WHITESPACE "\n    "
+          LITERAL
+            INT_NUMBER "1"
+          WHITESPACE "\n"
+          R_CURLY "}"
+    WHITESPACE " "
+    LET_ELSE
+      ELSE_KW "else"
+      WHITESPACE " "
+      BLOCK_EXPR
+        STMT_LIST
+          L_CURLY "{"
+          WHITESPACE "\n    "
+          EXPR_STMT
+            RETURN_EXPR
+              RETURN_KW "return"
+            SEMICOLON ";"
+          WHITESPACE "\n"
+          R_CURLY "}"
+    SEMICOLON ";"
+  WHITESPACE "\n"
+error 0: expected an item
+error 20: right curly brace `}` before `else` in a `let...else` statement not allowed
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0060_let_else_right_curly_brace_unary.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0060_let_else_right_curly_brace_unary.rs
new file mode 100644
index 00000000000..1ba7f7d761b
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0060_let_else_right_curly_brace_unary.rs
@@ -0,0 +1,5 @@
+let foo = -{
+    1
+} else {
+    return;
+};
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0061_let_else_right_curly_brace_do_yeet.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0061_let_else_right_curly_brace_do_yeet.rast
new file mode 100644
index 00000000000..e8eeeee695e
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0061_let_else_right_curly_brace_do_yeet.rast
@@ -0,0 +1,90 @@
+SOURCE_FILE
+  FN
+    FN_KW "fn"
+    WHITESPACE " "
+    NAME
+      IDENT "o"
+    PARAM_LIST
+      L_PAREN "("
+      R_PAREN ")"
+    WHITESPACE " "
+    RET_TYPE
+      THIN_ARROW "->"
+      WHITESPACE " "
+      PATH_TYPE
+        PATH
+          PATH_SEGMENT
+            NAME_REF
+              IDENT "Result"
+            GENERIC_ARG_LIST
+              L_ANGLE "<"
+              TYPE_ARG
+                TUPLE_TYPE
+                  L_PAREN "("
+                  R_PAREN ")"
+              COMMA ","
+              WHITESPACE " "
+              TYPE_ARG
+                TUPLE_TYPE
+                  L_PAREN "("
+                  R_PAREN ")"
+              R_ANGLE ">"
+    WHITESPACE " "
+    BLOCK_EXPR
+      STMT_LIST
+        L_CURLY "{"
+        WHITESPACE "\n    "
+        LET_STMT
+          LET_KW "let"
+          WHITESPACE " "
+          IDENT_PAT
+            NAME
+              IDENT "foo"
+          WHITESPACE " "
+          EQ "="
+          WHITESPACE " "
+          YEET_EXPR
+            DO_KW "do"
+            WHITESPACE " "
+            YEET_KW "yeet"
+            WHITESPACE " "
+            BLOCK_EXPR
+              STMT_LIST
+                L_CURLY "{"
+                WHITESPACE "\n        "
+                TUPLE_EXPR
+                  L_PAREN "("
+                  R_PAREN ")"
+                WHITESPACE "\n    "
+                R_CURLY "}"
+          WHITESPACE " "
+          LET_ELSE
+            ELSE_KW "else"
+            WHITESPACE " "
+            BLOCK_EXPR
+              STMT_LIST
+                L_CURLY "{"
+                WHITESPACE "\n        "
+                EXPR_STMT
+                  RETURN_EXPR
+                    RETURN_KW "return"
+                    WHITESPACE " "
+                    CALL_EXPR
+                      PATH_EXPR
+                        PATH
+                          PATH_SEGMENT
+                            NAME_REF
+                              IDENT "Ok"
+                      ARG_LIST
+                        L_PAREN "("
+                        TUPLE_EXPR
+                          L_PAREN "("
+                          R_PAREN ")"
+                        R_PAREN ")"
+                  SEMICOLON ";"
+                WHITESPACE "\n    "
+                R_CURLY "}"
+          SEMICOLON ";"
+        WHITESPACE "\n"
+        R_CURLY "}"
+error 67: right curly brace `}` before `else` in a `let...else` statement not allowed
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0061_let_else_right_curly_brace_do_yeet.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0061_let_else_right_curly_brace_do_yeet.rs
new file mode 100644
index 00000000000..188fb07d91b
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0061_let_else_right_curly_brace_do_yeet.rs
@@ -0,0 +1,7 @@
+fn o() -> Result<(), ()> {
+    let foo = do yeet {
+        ()
+    } else {
+        return Ok(());
+    };
+}
\ No newline at end of file
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0062_let_else_right_curly_brace_become.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0062_let_else_right_curly_brace_become.rast
new file mode 100644
index 00000000000..cc5e1278c3d
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0062_let_else_right_curly_brace_become.rast
@@ -0,0 +1,40 @@
+SOURCE_FILE
+  ERROR
+    LET_KW "let"
+    WHITESPACE " "
+    IDENT_PAT
+      NAME
+        IDENT "foo"
+    WHITESPACE " "
+    EQ "="
+    WHITESPACE " "
+    BECOME_EXPR
+      BECOME_KW "become"
+      WHITESPACE " "
+      BLOCK_EXPR
+        STMT_LIST
+          L_CURLY "{"
+          WHITESPACE "\n    "
+          TUPLE_EXPR
+            L_PAREN "("
+            R_PAREN ")"
+          WHITESPACE "\n"
+          R_CURLY "}"
+    WHITESPACE " "
+    LET_ELSE
+      ELSE_KW "else"
+      WHITESPACE " "
+      BLOCK_EXPR
+        STMT_LIST
+          L_CURLY "{"
+          WHITESPACE "\n    "
+          EXPR_STMT
+            RETURN_EXPR
+              RETURN_KW "return"
+            SEMICOLON ";"
+          WHITESPACE "\n"
+          R_CURLY "}"
+    SEMICOLON ";"
+  WHITESPACE "\n"
+error 0: expected an item
+error 27: right curly brace `}` before `else` in a `let...else` statement not allowed
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0062_let_else_right_curly_brace_become.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0062_let_else_right_curly_brace_become.rs
new file mode 100644
index 00000000000..622548b8f33
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0062_let_else_right_curly_brace_become.rs
@@ -0,0 +1,5 @@
+let foo = become {
+    ()
+} else {
+    return;
+};
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0063_let_else_right_curly_brace_reference.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0063_let_else_right_curly_brace_reference.rast
new file mode 100644
index 00000000000..ea2f4f28e2d
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0063_let_else_right_curly_brace_reference.rast
@@ -0,0 +1,38 @@
+SOURCE_FILE
+  ERROR
+    LET_KW "let"
+    WHITESPACE " "
+    IDENT_PAT
+      NAME
+        IDENT "foo"
+    WHITESPACE " "
+    EQ "="
+    WHITESPACE " "
+    REF_EXPR
+      AMP "&"
+      BLOCK_EXPR
+        STMT_LIST
+          L_CURLY "{"
+          WHITESPACE "\n    "
+          LITERAL
+            INT_NUMBER "1"
+          WHITESPACE "\n"
+          R_CURLY "}"
+    WHITESPACE " "
+    LET_ELSE
+      ELSE_KW "else"
+      WHITESPACE " "
+      BLOCK_EXPR
+        STMT_LIST
+          L_CURLY "{"
+          WHITESPACE "\n    "
+          EXPR_STMT
+            RETURN_EXPR
+              RETURN_KW "return"
+            SEMICOLON ";"
+          WHITESPACE "\n"
+          R_CURLY "}"
+    SEMICOLON ";"
+  WHITESPACE "\n"
+error 0: expected an item
+error 20: right curly brace `}` before `else` in a `let...else` statement not allowed
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0063_let_else_right_curly_brace_reference.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0063_let_else_right_curly_brace_reference.rs
new file mode 100644
index 00000000000..9a00dca3689
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0063_let_else_right_curly_brace_reference.rs
@@ -0,0 +1,5 @@
+let foo = &{
+    1
+} else {
+    return;
+};
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0064_let_else_right_curly_brace_assignment.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0064_let_else_right_curly_brace_assignment.rast
new file mode 100644
index 00000000000..47396140c5c
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0064_let_else_right_curly_brace_assignment.rast
@@ -0,0 +1,45 @@
+SOURCE_FILE
+  ERROR
+    LET_KW "let"
+    WHITESPACE " "
+    IDENT_PAT
+      NAME
+        IDENT "foo"
+    WHITESPACE " "
+    EQ "="
+    WHITESPACE " "
+    BIN_EXPR
+      PATH_EXPR
+        PATH
+          PATH_SEGMENT
+            NAME_REF
+              IDENT "bar"
+      WHITESPACE " "
+      EQ "="
+      WHITESPACE " "
+      BLOCK_EXPR
+        STMT_LIST
+          L_CURLY "{"
+          WHITESPACE "\n    "
+          LITERAL
+            INT_NUMBER "1"
+          WHITESPACE "\n"
+          R_CURLY "}"
+    WHITESPACE " "
+    LET_ELSE
+      ELSE_KW "else"
+      WHITESPACE " "
+      BLOCK_EXPR
+        STMT_LIST
+          L_CURLY "{"
+          WHITESPACE "\n    "
+          EXPR_STMT
+            RETURN_EXPR
+              RETURN_KW "return"
+            SEMICOLON ";"
+          WHITESPACE "\n"
+          R_CURLY "}"
+    SEMICOLON ";"
+  WHITESPACE "\n"
+error 0: expected an item
+error 25: right curly brace `}` before `else` in a `let...else` statement not allowed
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0064_let_else_right_curly_brace_assignment.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0064_let_else_right_curly_brace_assignment.rs
new file mode 100644
index 00000000000..08e677416f1
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0064_let_else_right_curly_brace_assignment.rs
@@ -0,0 +1,5 @@
+let foo = bar = {
+    1
+} else {
+    return;
+};
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/ref_expr.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/ref_expr.rast
index 108b0802c33..8dc916e5cc5 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/ref_expr.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/ref_expr.rast
@@ -134,6 +134,25 @@ SOURCE_FILE
                   NAME_REF
                     IDENT "foo"
           SEMICOLON ";"
+        WHITESPACE "\n    "
+        LET_STMT
+          LET_KW "let"
+          WHITESPACE " "
+          WILDCARD_PAT
+            UNDERSCORE "_"
+          WHITESPACE " "
+          EQ "="
+          WHITESPACE " "
+          REF_EXPR
+            AMP "&"
+            RAW_KW "raw"
+            WHITESPACE " "
+            PATH_EXPR
+              PATH
+                PATH_SEGMENT
+                  NAME_REF
+                    IDENT "foo"
+          SEMICOLON ";"
         WHITESPACE "\n"
         R_CURLY "}"
   WHITESPACE "\n"
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/ref_expr.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/ref_expr.rs
index c5262f4469b..31a2485b439 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/ref_expr.rs
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/ref_expr.rs
@@ -7,4 +7,5 @@ fn foo() {
     // raw reference operator
     let _ = &raw mut foo;
     let _ = &raw const foo;
+    let _ = &raw foo;
 }
diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/msg.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/msg.rs
index 6ea8db9a905..4b831e4aceb 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/msg.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/msg.rs
@@ -159,7 +159,7 @@ type ProtocolWrite<W: Write> = for<'o, 'msg> fn(out: &'o mut W, msg: &'msg str)
 #[cfg(test)]
 mod tests {
     use intern::{sym, Symbol};
-    use span::{ErasedFileAstId, Span, SpanAnchor, SyntaxContextId, TextRange, TextSize};
+    use span::{Edition, ErasedFileAstId, Span, SpanAnchor, SyntaxContextId, TextRange, TextSize};
     use tt::{
         Delimiter, DelimiterKind, Ident, Leaf, Literal, Punct, Spacing, TopSubtree,
         TopSubtreeBuilder,
@@ -180,12 +180,12 @@ mod tests {
             open: Span {
                 range: TextRange::empty(TextSize::new(0)),
                 anchor,
-                ctx: SyntaxContextId::ROOT,
+                ctx: SyntaxContextId::root(Edition::CURRENT),
             },
             close: Span {
                 range: TextRange::empty(TextSize::new(19)),
                 anchor,
-                ctx: SyntaxContextId::ROOT,
+                ctx: SyntaxContextId::root(Edition::CURRENT),
             },
             kind: DelimiterKind::Invisible,
         });
@@ -196,7 +196,7 @@ mod tests {
                 span: Span {
                     range: TextRange::at(TextSize::new(0), TextSize::of("struct")),
                     anchor,
-                    ctx: SyntaxContextId::ROOT,
+                    ctx: SyntaxContextId::root(Edition::CURRENT),
                 },
                 is_raw: tt::IdentIsRaw::No,
             }
@@ -208,7 +208,7 @@ mod tests {
                 span: Span {
                     range: TextRange::at(TextSize::new(5), TextSize::of("r#Foo")),
                     anchor,
-                    ctx: SyntaxContextId::ROOT,
+                    ctx: SyntaxContextId::root(Edition::CURRENT),
                 },
                 is_raw: tt::IdentIsRaw::Yes,
             }
@@ -219,7 +219,7 @@ mod tests {
             span: Span {
                 range: TextRange::at(TextSize::new(10), TextSize::of("\"Foo\"")),
                 anchor,
-                ctx: SyntaxContextId::ROOT,
+                ctx: SyntaxContextId::root(Edition::CURRENT),
             },
             kind: tt::LitKind::Str,
             suffix: None,
@@ -229,7 +229,7 @@ mod tests {
             span: Span {
                 range: TextRange::at(TextSize::new(13), TextSize::of('@')),
                 anchor,
-                ctx: SyntaxContextId::ROOT,
+                ctx: SyntaxContextId::root(Edition::CURRENT),
             },
             spacing: Spacing::Joint,
         }));
@@ -238,7 +238,7 @@ mod tests {
             Span {
                 range: TextRange::at(TextSize::new(14), TextSize::of('{')),
                 anchor,
-                ctx: SyntaxContextId::ROOT,
+                ctx: SyntaxContextId::root(Edition::CURRENT),
             },
         );
         builder.push(Leaf::Literal(Literal {
@@ -246,7 +246,7 @@ mod tests {
             span: Span {
                 range: TextRange::at(TextSize::new(15), TextSize::of("0u32")),
                 anchor,
-                ctx: SyntaxContextId::ROOT,
+                ctx: SyntaxContextId::root(Edition::CURRENT),
             },
             kind: tt::LitKind::Integer,
             suffix: Some(sym::u32.clone()),
@@ -254,7 +254,7 @@ mod tests {
         builder.close(Span {
             range: TextRange::at(TextSize::new(19), TextSize::of('}')),
             anchor,
-            ctx: SyntaxContextId::ROOT,
+            ctx: SyntaxContextId::root(Edition::CURRENT),
         });
 
         builder.build()
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml b/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml
index 00695c54737..191535ac55e 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml
@@ -14,6 +14,7 @@ doctest = false
 
 [dependencies]
 object.workspace = true
+libc.workspace = true
 libloading.workspace = true
 memmap2.workspace = true
 
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib.rs
index fe15d42b4e4..cbf7a277bfa 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib.rs
@@ -28,11 +28,16 @@ fn load_library(file: &Utf8Path) -> Result<Library, libloading::Error> {
 
 #[cfg(unix)]
 fn load_library(file: &Utf8Path) -> Result<Library, libloading::Error> {
+    // not defined by POSIX, different values on mips vs other targets
+    #[cfg(target_env = "gnu")]
+    use libc::RTLD_DEEPBIND;
     use libloading::os::unix::Library as UnixLibrary;
-    use std::os::raw::c_int;
+    // defined by POSIX
+    use libloading::os::unix::RTLD_NOW;
 
-    const RTLD_NOW: c_int = 0x00002;
-    const RTLD_DEEPBIND: c_int = 0x00008;
+    // MUSL and bionic don't have it..
+    #[cfg(not(target_env = "gnu"))]
+    const RTLD_DEEPBIND: std::os::raw::c_int = 0x0;
 
     unsafe { UnixLibrary::open(Some(file), RTLD_NOW | RTLD_DEEPBIND).map(|lib| lib.into()) }
 }
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs
index beaebf33300..c7614849e01 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs
@@ -440,7 +440,7 @@ mod tests {
                 file_id: EditionedFileId::current_edition(FileId::from_raw(0)),
                 ast_id: span::ErasedFileAstId::from_raw(0),
             },
-            ctx: SyntaxContextId::ROOT,
+            ctx: SyntaxContextId::root(span::Edition::CURRENT),
         };
         let s = TokenStream {
             token_trees: vec![
@@ -482,7 +482,7 @@ mod tests {
                 file_id: EditionedFileId::current_edition(FileId::from_raw(0)),
                 ast_id: span::ErasedFileAstId::from_raw(0),
             },
-            ctx: SyntaxContextId::ROOT,
+            ctx: SyntaxContextId::root(span::Edition::CURRENT),
         };
         let subtree_paren_a = vec![
             tt::TokenTree::Subtree(tt::Subtree {
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/mod.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/mod.rs
index dc6e71163b2..15de88ea656 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/mod.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/mod.rs
@@ -12,7 +12,7 @@ fn test_derive_empty() {
         "DeriveEmpty",
         r#"struct S;"#,
         expect!["SUBTREE $$ 1 1"],
-        expect!["SUBTREE $$ 42:2@0..100#0 42:2@0..100#0"],
+        expect!["SUBTREE $$ 42:2@0..100#2 42:2@0..100#2"],
     );
 }
 
@@ -29,12 +29,12 @@ fn test_derive_error() {
                 LITERAL Str #[derive(DeriveError)] struct S ; 1
               PUNCH   ; [alone] 1"#]],
         expect![[r#"
-            SUBTREE $$ 42:2@0..100#0 42:2@0..100#0
-              IDENT   compile_error 42:2@0..100#0
-              PUNCH   ! [alone] 42:2@0..100#0
-              SUBTREE () 42:2@0..100#0 42:2@0..100#0
-                LITERAL Str #[derive(DeriveError)] struct S ; 42:2@0..100#0
-              PUNCH   ; [alone] 42:2@0..100#0"#]],
+            SUBTREE $$ 42:2@0..100#2 42:2@0..100#2
+              IDENT   compile_error 42:2@0..100#2
+              PUNCH   ! [alone] 42:2@0..100#2
+              SUBTREE () 42:2@0..100#2 42:2@0..100#2
+                LITERAL Str #[derive(DeriveError)] struct S ; 42:2@0..100#2
+              PUNCH   ; [alone] 42:2@0..100#2"#]],
     );
 }
 
@@ -53,14 +53,14 @@ fn test_fn_like_macro_noop() {
               PUNCH   , [alone] 1
               SUBTREE [] 1 1"#]],
         expect![[r#"
-            SUBTREE $$ 42:2@0..100#0 42:2@0..100#0
-              IDENT   ident 42:2@0..5#0
-              PUNCH   , [alone] 42:2@5..6#0
-              LITERAL Integer 0 42:2@7..8#0
-              PUNCH   , [alone] 42:2@8..9#0
-              LITERAL Integer 1 42:2@10..11#0
-              PUNCH   , [alone] 42:2@11..12#0
-              SUBTREE [] 42:2@13..14#0 42:2@14..15#0"#]],
+            SUBTREE $$ 42:2@0..100#2 42:2@0..100#2
+              IDENT   ident 42:2@0..5#2
+              PUNCH   , [alone] 42:2@5..6#2
+              LITERAL Integer 0 42:2@7..8#2
+              PUNCH   , [alone] 42:2@8..9#2
+              LITERAL Integer 1 42:2@10..11#2
+              PUNCH   , [alone] 42:2@11..12#2
+              SUBTREE [] 42:2@13..14#2 42:2@14..15#2"#]],
     );
 }
 
@@ -75,10 +75,10 @@ fn test_fn_like_macro_clone_ident_subtree() {
               PUNCH   , [alone] 1
               SUBTREE [] 1 1"#]],
         expect![[r#"
-            SUBTREE $$ 42:2@0..100#0 42:2@0..100#0
-              IDENT   ident 42:2@0..5#0
-              PUNCH   , [alone] 42:2@5..6#0
-              SUBTREE [] 42:2@7..8#0 42:2@7..8#0"#]],
+            SUBTREE $$ 42:2@0..100#2 42:2@0..100#2
+              IDENT   ident 42:2@0..5#2
+              PUNCH   , [alone] 42:2@5..6#2
+              SUBTREE [] 42:2@7..8#2 42:2@7..8#2"#]],
     );
 }
 
@@ -91,8 +91,8 @@ fn test_fn_like_macro_clone_raw_ident() {
             SUBTREE $$ 1 1
               IDENT   r#async 1"#]],
         expect![[r#"
-            SUBTREE $$ 42:2@0..100#0 42:2@0..100#0
-              IDENT   r#async 42:2@0..7#0"#]],
+            SUBTREE $$ 42:2@0..100#2 42:2@0..100#2
+              IDENT   r#async 42:2@0..7#2"#]],
     );
 }
 
@@ -105,8 +105,8 @@ fn test_fn_like_fn_like_span_join() {
             SUBTREE $$ 1 1
               IDENT   r#joined 1"#]],
         expect![[r#"
-            SUBTREE $$ 42:2@0..100#0 42:2@0..100#0
-              IDENT   r#joined 42:2@0..11#0"#]],
+            SUBTREE $$ 42:2@0..100#2 42:2@0..100#2
+              IDENT   r#joined 42:2@0..11#2"#]],
     );
 }
 
@@ -121,10 +121,10 @@ fn test_fn_like_fn_like_span_ops() {
               IDENT   resolved_at_def_site 1
               IDENT   start_span 1"#]],
         expect![[r#"
-            SUBTREE $$ 42:2@0..100#0 42:2@0..100#0
-              IDENT   set_def_site 41:1@0..150#0
-              IDENT   resolved_at_def_site 42:2@13..33#0
-              IDENT   start_span 42:2@34..34#0"#]],
+            SUBTREE $$ 42:2@0..100#2 42:2@0..100#2
+              IDENT   set_def_site 41:1@0..150#2
+              IDENT   resolved_at_def_site 42:2@13..33#2
+              IDENT   start_span 42:2@34..34#2"#]],
     );
 }
 
@@ -143,14 +143,14 @@ fn test_fn_like_mk_literals() {
               LITERAL Integer 123i64 1
               LITERAL Integer 123 1"#]],
         expect![[r#"
-            SUBTREE $$ 42:2@0..100#0 42:2@0..100#0
-              LITERAL ByteStr byte_string 42:2@0..100#0
-              LITERAL Char c 42:2@0..100#0
-              LITERAL Str string 42:2@0..100#0
-              LITERAL Float 3.14f64 42:2@0..100#0
-              LITERAL Float 3.14 42:2@0..100#0
-              LITERAL Integer 123i64 42:2@0..100#0
-              LITERAL Integer 123 42:2@0..100#0"#]],
+            SUBTREE $$ 42:2@0..100#2 42:2@0..100#2
+              LITERAL ByteStr byte_string 42:2@0..100#2
+              LITERAL Char c 42:2@0..100#2
+              LITERAL Str string 42:2@0..100#2
+              LITERAL Float 3.14f64 42:2@0..100#2
+              LITERAL Float 3.14 42:2@0..100#2
+              LITERAL Integer 123i64 42:2@0..100#2
+              LITERAL Integer 123 42:2@0..100#2"#]],
     );
 }
 
@@ -164,9 +164,9 @@ fn test_fn_like_mk_idents() {
               IDENT   standard 1
               IDENT   r#raw 1"#]],
         expect![[r#"
-            SUBTREE $$ 42:2@0..100#0 42:2@0..100#0
-              IDENT   standard 42:2@0..100#0
-              IDENT   r#raw 42:2@0..100#0"#]],
+            SUBTREE $$ 42:2@0..100#2 42:2@0..100#2
+              IDENT   standard 42:2@0..100#2
+              IDENT   r#raw 42:2@0..100#2"#]],
     );
 }
 
@@ -198,27 +198,27 @@ fn test_fn_like_macro_clone_literals() {
               PUNCH   , [alone] 1
               LITERAL CStr null 1"#]],
         expect![[r#"
-            SUBTREE $$ 42:2@0..100#0 42:2@0..100#0
-              LITERAL Integer 1u16 42:2@0..4#0
-              PUNCH   , [alone] 42:2@4..5#0
-              LITERAL Integer 2_u32 42:2@6..11#0
-              PUNCH   , [alone] 42:2@11..12#0
-              PUNCH   - [alone] 42:2@13..14#0
-              LITERAL Integer 4i64 42:2@14..18#0
-              PUNCH   , [alone] 42:2@18..19#0
-              LITERAL Float 3.14f32 42:2@20..27#0
-              PUNCH   , [alone] 42:2@27..28#0
-              LITERAL Str hello bridge 42:2@29..43#0
-              PUNCH   , [alone] 42:2@43..44#0
-              LITERAL Str suffixedsuffix 42:2@45..61#0
-              PUNCH   , [alone] 42:2@61..62#0
-              LITERAL StrRaw(2) raw 42:2@63..73#0
-              PUNCH   , [alone] 42:2@73..74#0
-              LITERAL Char a 42:2@75..78#0
-              PUNCH   , [alone] 42:2@78..79#0
-              LITERAL Byte b 42:2@80..84#0
-              PUNCH   , [alone] 42:2@84..85#0
-              LITERAL CStr null 42:2@86..93#0"#]],
+            SUBTREE $$ 42:2@0..100#2 42:2@0..100#2
+              LITERAL Integer 1u16 42:2@0..4#2
+              PUNCH   , [alone] 42:2@4..5#2
+              LITERAL Integer 2_u32 42:2@6..11#2
+              PUNCH   , [alone] 42:2@11..12#2
+              PUNCH   - [alone] 42:2@13..14#2
+              LITERAL Integer 4i64 42:2@14..18#2
+              PUNCH   , [alone] 42:2@18..19#2
+              LITERAL Float 3.14f32 42:2@20..27#2
+              PUNCH   , [alone] 42:2@27..28#2
+              LITERAL Str hello bridge 42:2@29..43#2
+              PUNCH   , [alone] 42:2@43..44#2
+              LITERAL Str suffixedsuffix 42:2@45..61#2
+              PUNCH   , [alone] 42:2@61..62#2
+              LITERAL StrRaw(2) raw 42:2@63..73#2
+              PUNCH   , [alone] 42:2@73..74#2
+              LITERAL Char a 42:2@75..78#2
+              PUNCH   , [alone] 42:2@78..79#2
+              LITERAL Byte b 42:2@80..84#2
+              PUNCH   , [alone] 42:2@84..85#2
+              LITERAL CStr null 42:2@86..93#2"#]],
     );
 }
 
@@ -239,12 +239,12 @@ fn test_attr_macro() {
                 LITERAL Str #[attr_error(some arguments)] mod m {} 1
               PUNCH   ; [alone] 1"#]],
         expect![[r#"
-            SUBTREE $$ 42:2@0..100#0 42:2@0..100#0
-              IDENT   compile_error 42:2@0..100#0
-              PUNCH   ! [alone] 42:2@0..100#0
-              SUBTREE () 42:2@0..100#0 42:2@0..100#0
-                LITERAL Str #[attr_error(some arguments)] mod m {} 42:2@0..100#0
-              PUNCH   ; [alone] 42:2@0..100#0"#]],
+            SUBTREE $$ 42:2@0..100#2 42:2@0..100#2
+              IDENT   compile_error 42:2@0..100#2
+              PUNCH   ! [alone] 42:2@0..100#2
+              SUBTREE () 42:2@0..100#2 42:2@0..100#2
+                LITERAL Str #[attr_error(some arguments)] mod m {} 42:2@0..100#2
+              PUNCH   ; [alone] 42:2@0..100#2"#]],
     );
 }
 
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs
index 37d51050f32..4ce4544243a 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs
@@ -28,13 +28,18 @@ fn parse_string_spanned(
     ))
 }
 
-pub fn assert_expand(macro_name: &str, ra_fixture: &str, expect: Expect, expect_s: Expect) {
+pub fn assert_expand(
+    macro_name: &str,
+    #[rust_analyzer::rust_fixture] ra_fixture: &str,
+    expect: Expect,
+    expect_s: Expect,
+) {
     assert_expand_impl(macro_name, ra_fixture, None, expect, expect_s);
 }
 
 pub fn assert_expand_attr(
     macro_name: &str,
-    ra_fixture: &str,
+    #[rust_analyzer::rust_fixture] ra_fixture: &str,
     attr_args: &str,
     expect: Expect,
     expect_s: Expect,
@@ -76,7 +81,7 @@ fn assert_expand_impl(
             file_id: EditionedFileId::current_edition(FileId::from_raw(41)),
             ast_id: ErasedFileAstId::from_raw(1),
         },
-        ctx: SyntaxContextId::ROOT,
+        ctx: SyntaxContextId::root(span::Edition::CURRENT),
     };
     let call_site = Span {
         range: TextRange::new(0.into(), 100.into()),
@@ -84,7 +89,7 @@ fn assert_expand_impl(
             file_id: EditionedFileId::current_edition(FileId::from_raw(42)),
             ast_id: ErasedFileAstId::from_raw(2),
         },
-        ctx: SyntaxContextId::ROOT,
+        ctx: SyntaxContextId::root(span::Edition::CURRENT),
     };
     let mixed_site = call_site;
 
diff --git a/src/tools/rust-analyzer/crates/profile/Cargo.toml b/src/tools/rust-analyzer/crates/profile/Cargo.toml
index 2e3413f339b..3179c810f69 100644
--- a/src/tools/rust-analyzer/crates/profile/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/profile/Cargo.toml
@@ -21,7 +21,10 @@ jemalloc-ctl = { version = "0.5.0", package = "tikv-jemalloc-ctl", optional = tr
 perf-event = "=0.4.7"
 
 [target.'cfg(windows)'.dependencies]
-windows-sys = { version = "0.52", features = ["Win32_System_Threading", "Win32_System_ProcessStatus"] }
+windows-sys = { version = "0.59", features = [
+    "Win32_System_Threading",
+    "Win32_System_ProcessStatus",
+] }
 
 [features]
 cpu_profiler = []
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 4d906c2aeb3..e4a61134620 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
@@ -92,6 +92,8 @@ pub struct CargoConfig {
     pub sysroot_src: Option<AbsPathBuf>,
     /// rustc private crate source
     pub rustc_source: Option<RustLibSource>,
+    /// Extra includes to add to the VFS.
+    pub extra_includes: Vec<AbsPathBuf>,
     pub cfg_overrides: CfgOverrides,
     /// Invoke `cargo check` through the RUSTC_WRAPPER.
     pub wrap_rustc_in_build_scripts: bool,
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 681bce3a5a6..f1113831125 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/tests.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/tests.rs
@@ -49,6 +49,7 @@ fn load_workspace_from_metadata(file: &str) -> ProjectWorkspace {
         rustc_cfg: Vec::new(),
         toolchain: None,
         target_layout: Err("target_data_layout not loaded".into()),
+        extra_includes: Vec::new(),
     }
 }
 
@@ -63,6 +64,7 @@ fn load_rust_project(file: &str) -> (CrateGraph, ProcMacroPaths) {
         toolchain: None,
         target_layout: Err(Arc::from("test has no data layout")),
         cfg_overrides: Default::default(),
+        extra_includes: Vec::new(),
     };
     to_crate_graph(project_workspace, &mut Default::default())
 }
@@ -284,6 +286,7 @@ fn smoke_test_real_sysroot_cargo() {
         cfg_overrides: Default::default(),
         toolchain: None,
         target_layout: Err("target_data_layout not loaded".into()),
+        extra_includes: Vec::new(),
     };
     project_workspace.to_crate_graph(
         &mut {
diff --git a/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/rustc_cfg.rs b/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/rustc_cfg.rs
index afcc8120794..4bf9b59e7d0 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/rustc_cfg.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/rustc_cfg.rs
@@ -66,7 +66,7 @@ fn rustc_print_cfg(
         QueryConfig::Cargo(sysroot, cargo_toml) => {
             let mut cmd = sysroot.tool(Tool::Cargo, cargo_toml.parent());
             cmd.envs(extra_env);
-            cmd.args(["rustc"]).args(RUSTC_ARGS);
+            cmd.args(["rustc", "-Z", "unstable-options"]).args(RUSTC_ARGS);
             if let Some(target) = target {
                 cmd.args(["--target", target]);
             }
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 a345c6bcce4..f98d983ac06 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs
@@ -11,9 +11,8 @@ use base_db::{
 };
 use cfg::{CfgAtom, CfgDiff, CfgOptions};
 use intern::{sym, Symbol};
-use itertools::Itertools;
 use paths::{AbsPath, AbsPathBuf};
-use rustc_hash::FxHashMap;
+use rustc_hash::{FxHashMap, FxHashSet};
 use semver::Version;
 use span::{Edition, FileId};
 use tracing::instrument;
@@ -63,6 +62,8 @@ pub struct ProjectWorkspace {
     pub target_layout: TargetLayoutLoadResult,
     /// A set of cfg overrides for this workspace.
     pub cfg_overrides: CfgOverrides,
+    /// Additional includes to add for the VFS.
+    pub extra_includes: Vec<AbsPathBuf>,
 }
 
 #[derive(Clone)]
@@ -104,7 +105,15 @@ pub enum ProjectWorkspaceKind {
 impl fmt::Debug for ProjectWorkspace {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         // Make sure this isn't too verbose.
-        let Self { kind, sysroot, rustc_cfg, toolchain, target_layout, cfg_overrides } = self;
+        let Self {
+            kind,
+            sysroot,
+            rustc_cfg,
+            toolchain,
+            target_layout,
+            cfg_overrides,
+            extra_includes,
+        } = self;
         match kind {
             ProjectWorkspaceKind::Cargo { cargo, error: _, build_scripts, rustc, set_test } => f
                 .debug_struct("Cargo")
@@ -117,6 +126,7 @@ impl fmt::Debug for ProjectWorkspace {
                 )
                 .field("n_rustc_cfg", &rustc_cfg.len())
                 .field("n_cfg_overrides", &cfg_overrides.len())
+                .field("n_extra_includes", &extra_includes.len())
                 .field("toolchain", &toolchain)
                 .field("data_layout", &target_layout)
                 .field("set_test", set_test)
@@ -130,7 +140,8 @@ impl fmt::Debug for ProjectWorkspace {
                     .field("n_rustc_cfg", &rustc_cfg.len())
                     .field("toolchain", &toolchain)
                     .field("data_layout", &target_layout)
-                    .field("n_cfg_overrides", &cfg_overrides.len());
+                    .field("n_cfg_overrides", &cfg_overrides.len())
+                    .field("n_extra_includes", &extra_includes.len());
 
                 debug_struct.finish()
             }
@@ -144,6 +155,7 @@ impl fmt::Debug for ProjectWorkspace {
                 .field("toolchain", &toolchain)
                 .field("data_layout", &target_layout)
                 .field("n_cfg_overrides", &cfg_overrides.len())
+                .field("n_extra_includes", &extra_includes.len())
                 .field("set_test", set_test)
                 .finish(),
         }
@@ -320,6 +332,7 @@ impl ProjectWorkspace {
             cfg_overrides,
             toolchain,
             target_layout: data_layout.map(Arc::from).map_err(|it| Arc::from(it.to_string())),
+            extra_includes: config.extra_includes.clone(),
         })
     }
 
@@ -340,6 +353,7 @@ impl ProjectWorkspace {
             toolchain,
             target_layout: data_layout.map(Arc::from).map_err(|it| Arc::from(it.to_string())),
             cfg_overrides: config.cfg_overrides.clone(),
+            extra_includes: config.extra_includes.clone(),
         }
     }
 
@@ -399,6 +413,7 @@ impl ProjectWorkspace {
             toolchain,
             target_layout: data_layout.map(Arc::from).map_err(|it| Arc::from(it.to_string())),
             cfg_overrides: config.cfg_overrides.clone(),
+            extra_includes: config.extra_includes.clone(),
         })
     }
 
@@ -565,13 +580,20 @@ impl ProjectWorkspace {
 
                     PackageRoot {
                         is_local: krate.is_workspace_member,
-                        include: krate.include.iter().cloned().chain(build_file).collect(),
+                        include: krate
+                            .include
+                            .iter()
+                            .cloned()
+                            .chain(build_file)
+                            .chain(self.extra_includes.iter().cloned())
+                            .collect(),
                         exclude: krate.exclude.clone(),
                     }
                 })
+                .collect::<FxHashSet<_>>()
+                .into_iter()
                 .chain(mk_sysroot())
-                .unique()
-                .collect(),
+                .collect::<Vec<_>>(),
             ProjectWorkspaceKind::Cargo { cargo, rustc, build_scripts, error: _, set_test: _ } => {
                 cargo
                     .packages()
@@ -603,6 +625,8 @@ impl ProjectWorkspace {
 
                         let mut exclude = vec![pkg_root.join(".git")];
                         if is_local {
+                            include.extend(self.extra_includes.iter().cloned());
+
                             exclude.push(pkg_root.join("target"));
                         } else {
                             exclude.push(pkg_root.join("tests"));
@@ -619,11 +643,6 @@ impl ProjectWorkspace {
                             exclude: Vec::new(),
                         })
                     }))
-                    .chain(cargo.is_virtual_workspace().then(|| PackageRoot {
-                        is_local: true,
-                        include: vec![cargo.workspace_root().to_path_buf()],
-                        exclude: Vec::new(),
-                    }))
                     .collect()
             }
             ProjectWorkspaceKind::DetachedFile { file, cargo: cargo_script, .. } => {
@@ -661,6 +680,8 @@ impl ProjectWorkspace {
 
                         let mut exclude = vec![pkg_root.join(".git")];
                         if is_local {
+                            include.extend(self.extra_includes.iter().cloned());
+
                             exclude.push(pkg_root.join("target"));
                         } else {
                             exclude.push(pkg_root.join("tests"));
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml
index d06130ce8c5..c24cbb4a311 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml
@@ -76,7 +76,10 @@ vfs.workspace = true
 paths.workspace = true
 
 [target.'cfg(windows)'.dependencies]
-windows-sys = { version = "0.52", features = ["Win32_System_Diagnostics_Debug", "Win32_System_Threading"] }
+windows-sys = { version = "0.59", features = [
+  "Win32_System_Diagnostics_Debug",
+  "Win32_System_Threading",
+] }
 
 [target.'cfg(not(target_env = "msvc"))'.dependencies]
 jemallocator = { version = "0.5.0", package = "tikv-jemallocator", optional = true }
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs
index afe3455b780..bcaec520195 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
@@ -1051,6 +1051,7 @@ impl flags::AnalysisStats {
                 &InlayHintsConfig {
                     render_colons: false,
                     type_hints: true,
+                    sized_bound: false,
                     discriminant_hints: ide::DiscriminantHints::Always,
                     parameter_hints: true,
                     generic_parameter_hints: ide::GenericParameterHints {
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs
index 6b0ce4db7c9..199f61e70f0 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs
@@ -93,6 +93,7 @@ impl Tester {
             toolchain: None,
             target_layout: data_layout.map(Arc::from).map_err(|it| Arc::from(it.to_string())),
             cfg_overrides: Default::default(),
+            extra_includes: vec![],
         };
         let load_cargo_config = LoadCargoConfig {
             load_out_dirs_from_check: false,
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs
index 6ca7d9ac057..dc0f722aae6 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs
@@ -518,7 +518,7 @@ mod test {
     use test_fixture::ChangeFixture;
     use vfs::VfsPath;
 
-    fn position(ra_fixture: &str) -> (AnalysisHost, FilePosition) {
+    fn position(#[rust_analyzer::rust_fixture] ra_fixture: &str) -> (AnalysisHost, FilePosition) {
         let mut host = AnalysisHost::default();
         let change_fixture = ChangeFixture::parse(ra_fixture);
         host.raw_database_mut().apply_change(change_fixture.change);
@@ -530,7 +530,7 @@ mod test {
 
     /// If expected == "", then assert that there are no symbols (this is basically local symbol)
     #[track_caller]
-    fn check_symbol(ra_fixture: &str, expected: &str) {
+    fn check_symbol(#[rust_analyzer::rust_fixture] ra_fixture: &str, expected: &str) {
         let (host, position) = position(ra_fixture);
 
         let analysis = host.analysis();
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 30f0031905f..3dc4379258f 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
@@ -185,6 +185,8 @@ config_data! {
         inlayHints_genericParameterHints_type_enable: bool = false,
         /// Whether to show implicit drop hints.
         inlayHints_implicitDrops_enable: bool                      = false,
+        /// Whether to show inlay hints for the implied type parameter `Sized` bound.
+        inlayHints_implicitSizedBoundHints_enable: bool            = false,
         /// Whether to show inlay type hints for elided lifetimes in function signatures.
         inlayHints_lifetimeElisionHints_enable: LifetimeElisionDef = LifetimeElisionDef::Never,
         /// Whether to prefer using parameter names as the name for elided lifetime hints if possible.
@@ -324,8 +326,16 @@ config_data! {
         /// Show documentation.
         signatureInfo_documentation_enable: bool                       = true,
 
-        /// Specify the characters to exclude from triggering typing assists. The default trigger characters are `.`, `=`, `<`, `>`, `{`, and `(`.
-        typing_excludeChars: Option<String> = Some("|<".to_owned()),
+        /// Specify the characters allowed to invoke special on typing triggers.
+        /// - typing `=` after `let` tries to smartly add `;` if `=` is followed by an existing expression
+        /// - typing `=` between two expressions adds `;` when in statement position
+        /// - typing `=` to turn an assignment into an equality comparison removes `;` when in expression position
+        /// - typing `.` in a chain method call auto-indents
+        /// - typing `{` or `(` in front of an expression inserts a closing `}` or `)` after the expression
+        /// - typing `{` in a use item adds a closing `}` in the right place
+        /// - typing `>` to complete a return type `->` will insert a whitespace after it
+        /// - typing `<` in a path or type position inserts a closing `>` after the path or type.
+        typing_triggerChars: Option<String> = Some("=.".to_owned()),
 
 
         /// Enables automatic discovery of projects using [`DiscoverWorkspaceConfig::command`].
@@ -571,11 +581,8 @@ config_data! {
         /// avoid checking unnecessary things.
         cargo_buildScripts_useRustcWrapper: bool = true,
         /// List of cfg options to enable with the given values.
-        cargo_cfgs: FxHashMap<String, Option<String>> = {
-            let mut m = FxHashMap::default();
-            m.insert("debug_assertions".to_owned(), None);
-            m.insert("miri".to_owned(), None);
-            m
+        cargo_cfgs: Vec<String> = {
+            vec!["debug_assertions".into(), "miri".into()]
         },
         /// Extra arguments that are passed to every cargo invocation.
         cargo_extraArgs: Vec<String> = vec![],
@@ -728,6 +735,10 @@ config_data! {
         /// available on a nightly build.
         rustfmt_rangeFormatting_enable: bool = false,
 
+        /// Additional paths to include in the VFS. Generally for code that is
+        /// generated or otherwise managed by a build system outside of Cargo,
+        /// though Cargo might be the eventual consumer.
+        vfs_extraIncludes: Vec<String> = vec![],
 
         /// Workspace symbol search kind.
         workspace_symbol_search_kind: WorkspaceSymbolSearchKindDef = WorkspaceSymbolSearchKindDef::OnlyTypes,
@@ -1620,6 +1631,7 @@ impl Config {
         InlayHintsConfig {
             render_colons: self.inlayHints_renderColons().to_owned(),
             type_hints: self.inlayHints_typeHints_enable().to_owned(),
+            sized_bound: self.inlayHints_implicitSizedBoundHints_enable().to_owned(),
             parameter_hints: self.inlayHints_parameterHints_enable().to_owned(),
             generic_parameter_hints: GenericParameterHints {
                 type_hints: self.inlayHints_genericParameterHints_type_enable().to_owned(),
@@ -1926,6 +1938,13 @@ impl Config {
         });
         let sysroot_src =
             self.cargo_sysrootSrc(source_root).as_ref().map(|sysroot| self.root_path.join(sysroot));
+        let extra_includes = self
+            .vfs_extraIncludes(source_root)
+            .iter()
+            .map(String::as_str)
+            .map(AbsPathBuf::try_from)
+            .filter_map(Result::ok)
+            .collect();
 
         CargoConfig {
             all_targets: *self.cargo_allTargets(source_root),
@@ -1940,10 +1959,18 @@ impl Config {
             sysroot,
             sysroot_src,
             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),
@@ -2232,8 +2259,8 @@ impl Config {
         }
     }
 
-    pub fn typing_exclude_chars(&self) -> Option<String> {
-        self.typing_excludeChars().clone()
+    pub fn typing_trigger_chars(&self) -> &str {
+        self.typing_triggerChars().as_deref().unwrap_or_default()
     }
 
     // VSCode is our reference implementation, so we allow ourselves to work around issues by
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 7ac70efe2d6..190015d7faa 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
@@ -136,15 +136,13 @@ pub(crate) fn handle_memory_usage(state: &mut GlobalState, _: ()) -> anyhow::Res
     Ok(out)
 }
 
-pub(crate) fn handle_syntax_tree(
+pub(crate) fn handle_view_syntax_tree(
     snap: GlobalStateSnapshot,
-    params: lsp_ext::SyntaxTreeParams,
+    params: lsp_ext::ViewSyntaxTreeParams,
 ) -> anyhow::Result<String> {
-    let _p = tracing::info_span!("handle_syntax_tree").entered();
+    let _p = tracing::info_span!("handle_view_syntax_tree").entered();
     let id = from_proto::file_id(&snap, &params.text_document.uri)?;
-    let line_index = snap.file_line_index(id)?;
-    let text_range = params.range.and_then(|r| from_proto::text_range(&line_index, r).ok());
-    let res = snap.analysis.syntax_tree(id, text_range)?;
+    let res = snap.analysis.view_syntax_tree(id)?;
     Ok(res)
 }
 
@@ -436,29 +434,24 @@ pub(crate) fn handle_on_type_formatting(
     params: lsp_types::DocumentOnTypeFormattingParams,
 ) -> anyhow::Result<Option<Vec<lsp_ext::SnippetTextEdit>>> {
     let _p = tracing::info_span!("handle_on_type_formatting").entered();
+    let char_typed = params.ch.chars().next().unwrap_or('\0');
+    if !snap.config.typing_trigger_chars().contains(char_typed) {
+        return Ok(None);
+    }
+
     let mut position = from_proto::file_position(&snap, params.text_document_position)?;
     let line_index = snap.file_line_index(position.file_id)?;
 
     // in `ide`, the `on_type` invariant is that
     // `text.char_at(position) == typed_char`.
     position.offset -= TextSize::of('.');
-    let char_typed = params.ch.chars().next().unwrap_or('\0');
 
     let text = snap.analysis.file_text(position.file_id)?;
     if stdx::never!(!text[usize::from(position.offset)..].starts_with(char_typed)) {
         return Ok(None);
     }
 
-    // We have an assist that inserts ` ` after typing `->` in `fn foo() ->{`,
-    // but it requires precise cursor positioning to work, and one can't
-    // position the cursor with on_type formatting. So, let's just toggle this
-    // feature off here, hoping that we'll enable it one day, 😿.
-    if char_typed == '>' {
-        return Ok(None);
-    }
-    let chars_to_exclude = snap.config.typing_exclude_chars();
-
-    let edit = snap.analysis.on_char_typed(position, char_typed, chars_to_exclude)?;
+    let edit = snap.analysis.on_char_typed(position, char_typed)?;
     let edit = match edit {
         Some(it) => it,
         None => return Ok(None),
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 e7f5a7f5e78..61ec576dd4f 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs
@@ -47,7 +47,8 @@ use self::lsp::ext as lsp_ext;
 #[cfg(test)]
 mod integrated_benchmarks;
 
-use ide::{CompletionItem, CompletionRelevance};
+use hir::Mutability;
+use ide::{CompletionItem, CompletionItemRefMode, CompletionRelevance};
 use serde::de::DeserializeOwned;
 use tenthash::TentHasher;
 
@@ -132,8 +133,13 @@ fn completion_item_hash(item: &CompletionItem, is_ref_completion: bool) -> [u8;
         hasher.update(detail);
     }
     hash_completion_relevance(&mut hasher, &item.relevance);
-    if let Some((mutability, text_size)) = &item.ref_match {
-        hasher.update(mutability.as_keyword_for_ref());
+    if let Some((ref_mode, text_size)) = &item.ref_match {
+        let prefix = match ref_mode {
+            CompletionItemRefMode::Reference(Mutability::Shared) => "&",
+            CompletionItemRefMode::Reference(Mutability::Mut) => "&mut ",
+            CompletionItemRefMode::Dereference => "*",
+        };
+        hasher.update(prefix);
         hasher.update(u32::from(*text_size).to_le_bytes());
     }
     for (import_path, import_name) in &item.import_to_add {
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs
index f50cbba7acf..134de92feab 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs
@@ -108,19 +108,18 @@ impl Request for RebuildProcMacros {
     const METHOD: &'static str = "rust-analyzer/rebuildProcMacros";
 }
 
-pub enum SyntaxTree {}
+pub enum ViewSyntaxTree {}
 
-impl Request for SyntaxTree {
-    type Params = SyntaxTreeParams;
+impl Request for ViewSyntaxTree {
+    type Params = ViewSyntaxTreeParams;
     type Result = String;
-    const METHOD: &'static str = "rust-analyzer/syntaxTree";
+    const METHOD: &'static str = "rust-analyzer/viewSyntaxTree";
 }
 
 #[derive(Deserialize, Serialize, Debug)]
 #[serde(rename_all = "camelCase")]
-pub struct SyntaxTreeParams {
+pub struct ViewSyntaxTreeParams {
     pub text_document: TextDocumentIdentifier,
-    pub range: Option<Range>,
 }
 
 pub enum ViewHir {}
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 fe4d02dcb4f..a5516e7f9d4 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
@@ -547,7 +547,18 @@ pub(crate) fn inlay_hint(
     file_id: FileId,
     mut inlay_hint: InlayHint,
 ) -> Cancellable<lsp_types::InlayHint> {
-    let resolve_range_and_hash = inlay_hint.needs_resolve().map(|range| {
+    let hint_needs_resolve = |hint: &InlayHint| -> Option<TextRange> {
+        hint.resolve_parent.filter(|_| {
+            hint.text_edit.is_some()
+                || hint
+                    .label
+                    .parts
+                    .iter()
+                    .any(|part| part.linked_location.is_some() || part.tooltip.is_some())
+        })
+    };
+
+    let resolve_range_and_hash = hint_needs_resolve(&inlay_hint).map(|range| {
         (
             range,
             std::hash::BuildHasher::hash_one(
@@ -568,7 +579,11 @@ pub(crate) fn inlay_hint(
         something_to_resolve |= inlay_hint.text_edit.is_some();
         None
     } else {
-        inlay_hint.text_edit.take().map(|it| text_edit_vec(line_index, it))
+        inlay_hint
+            .text_edit
+            .take()
+            .and_then(|it| it.computed())
+            .map(|it| text_edit_vec(line_index, it))
     };
     let (label, tooltip) = inlay_hint_label(
         snap,
@@ -626,7 +641,7 @@ fn inlay_hint_label(
                 *something_to_resolve |= tooltip.is_some();
                 None
             } else {
-                match tooltip {
+                match tooltip.and_then(|it| it.computed()) {
                     Some(ide::InlayTooltip::String(s)) => {
                         Some(lsp_types::InlayHintTooltip::String(s))
                     }
@@ -650,7 +665,7 @@ fn inlay_hint_label(
                         *something_to_resolve |= part.tooltip.is_some();
                         None
                     } else {
-                        match part.tooltip {
+                        match part.tooltip.and_then(|it| it.computed()) {
                             Some(ide::InlayTooltip::String(s)) => {
                                 Some(lsp_types::InlayHintLabelPartTooltip::String(s))
                             }
@@ -1993,7 +2008,7 @@ fn bar(_: usize) {}
 
     #[track_caller]
     fn check_rendered_snippets_in_source(
-        ra_fixture: &str,
+        #[rust_analyzer::rust_fixture] ra_fixture: &str,
         edit: TextEdit,
         snippets: SnippetEdit,
         expect: Expect,
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs
index 97657b92658..d6dc8b521fd 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs
@@ -1145,7 +1145,7 @@ impl GlobalState {
             .on::<RETRY, lsp_ext::WorkspaceSymbol>(handlers::handle_workspace_symbol)
             .on::<NO_RETRY, lsp_ext::Ssr>(handlers::handle_ssr)
             .on::<NO_RETRY, lsp_ext::ViewRecursiveMemoryLayout>(handlers::handle_view_recursive_memory_layout)
-            .on::<NO_RETRY, lsp_ext::SyntaxTree>(handlers::handle_syntax_tree)
+            .on::<NO_RETRY, lsp_ext::ViewSyntaxTree>(handlers::handle_view_syntax_tree)
             .on::<NO_RETRY, lsp_ext::ViewHir>(handlers::handle_view_hir)
             .on::<NO_RETRY, lsp_ext::ViewMir>(handlers::handle_view_mir)
             .on::<NO_RETRY, lsp_ext::InterpretFunction>(handlers::handle_interpret_function)
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/test_runner.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/test_runner.rs
index 503b3ee43a1..3edfb812cf5 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/test_runner.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/test_runner.rs
@@ -18,7 +18,11 @@ pub(crate) enum TestState {
     Started,
     Ok,
     Ignored,
-    Failed { stdout: String },
+    Failed {
+        // the stdout field is not always present depending on cargo test flags
+        #[serde(skip_serializing_if = "String::is_empty", default)]
+        stdout: String,
+    },
 }
 
 #[derive(Debug, Deserialize)]
diff --git a/src/tools/rust-analyzer/crates/span/src/hygiene.rs b/src/tools/rust-analyzer/crates/span/src/hygiene.rs
index 87a948df550..6becc8e41ed 100644
--- a/src/tools/rust-analyzer/crates/span/src/hygiene.rs
+++ b/src/tools/rust-analyzer/crates/span/src/hygiene.rs
@@ -26,7 +26,7 @@ use crate::InternId;
 #[cfg(feature = "ra-salsa")]
 use ra_salsa::{InternId, InternValue};
 
-use crate::MacroCallId;
+use crate::{Edition, MacroCallId};
 
 /// Interned [`SyntaxContextData`].
 #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
@@ -59,11 +59,20 @@ impl fmt::Display for SyntaxContextId {
 }
 
 impl SyntaxContextId {
+    #[inline]
+    pub fn remove_root_edition(&mut self) {
+        if self.is_root() {
+            *self = Self::root(Edition::Edition2015);
+        }
+    }
+
     /// The root context, which is the parent of all other contexts. All [`FileId`]s have this context.
-    pub const ROOT: Self = SyntaxContextId(unsafe { InternId::new_unchecked(0) });
+    pub const fn root(edition: Edition) -> Self {
+        SyntaxContextId(unsafe { InternId::new_unchecked(edition as u32) })
+    }
 
     pub fn is_root(self) -> bool {
-        self == Self::ROOT
+        self.into_u32() <= Edition::LATEST as u32
     }
 
     /// Deconstruct a `SyntaxContextId` into a raw `u32`.
@@ -89,6 +98,7 @@ pub struct SyntaxContextData {
     // per crate. Though that is likely not a problem as `MacroCallId`s are already crate calling dependent.
     pub outer_expn: Option<MacroCallId>,
     pub outer_transparency: Transparency,
+    pub edition: Edition,
     pub parent: SyntaxContextId,
     /// This context, but with all transparent and semi-transparent expansions filtered away.
     pub opaque: SyntaxContextId,
@@ -98,10 +108,10 @@ pub struct SyntaxContextData {
 
 #[cfg(feature = "ra-salsa")]
 impl InternValue for SyntaxContextData {
-    type Key = (SyntaxContextId, Option<MacroCallId>, Transparency);
+    type Key = (SyntaxContextId, Option<MacroCallId>, Transparency, Edition);
 
     fn into_key(&self) -> Self::Key {
-        (self.parent, self.outer_expn, self.outer_transparency)
+        (self.parent, self.outer_expn, self.outer_transparency, self.edition)
     }
 }
 
@@ -118,13 +128,14 @@ impl std::fmt::Debug for SyntaxContextData {
 }
 
 impl SyntaxContextData {
-    pub fn root() -> Self {
+    pub fn root(edition: Edition) -> Self {
         SyntaxContextData {
             outer_expn: None,
             outer_transparency: Transparency::Opaque,
-            parent: SyntaxContextId::ROOT,
-            opaque: SyntaxContextId::ROOT,
-            opaque_and_semitransparent: SyntaxContextId::ROOT,
+            parent: SyntaxContextId::root(edition),
+            opaque: SyntaxContextId::root(edition),
+            opaque_and_semitransparent: SyntaxContextId::root(edition),
+            edition,
         }
     }
 }
diff --git a/src/tools/rust-analyzer/crates/span/src/map.rs b/src/tools/rust-analyzer/crates/span/src/map.rs
index 66bbce18594..dc35de67fd8 100644
--- a/src/tools/rust-analyzer/crates/span/src/map.rs
+++ b/src/tools/rust-analyzer/crates/span/src/map.rs
@@ -208,7 +208,7 @@ impl RealSpanMap {
         Span {
             range: range - offset,
             anchor: SpanAnchor { file_id: self.file_id, ast_id },
-            ctx: SyntaxContextId::ROOT,
+            ctx: SyntaxContextId::root(self.file_id.edition()),
         }
     }
 }
diff --git a/src/tools/rust-analyzer/crates/stdx/Cargo.toml b/src/tools/rust-analyzer/crates/stdx/Cargo.toml
index bf0d6df9ad8..1ebb48c577a 100644
--- a/src/tools/rust-analyzer/crates/stdx/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/stdx/Cargo.toml
@@ -23,7 +23,7 @@ itertools.workspace = true
 
 [target.'cfg(windows)'.dependencies]
 miow = "0.6.0"
-windows-sys = { version = "0.52", features = ["Win32_Foundation"] }
+windows-sys = { version = "0.59", features = ["Win32_Foundation"] }
 
 [features]
 # Uncomment to enable for the whole crate graph
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 ed8b1908d60..19801c49e43 100644
--- a/src/tools/rust-analyzer/crates/syntax-bridge/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/syntax-bridge/src/lib.rs
@@ -1,6 +1,6 @@
 //! Conversions between [`SyntaxNode`] and [`tt::TokenTree`].
 
-use std::fmt;
+use std::{fmt, hash::Hash};
 
 use intern::Symbol;
 use rustc_hash::{FxHashMap, FxHashSet};
@@ -58,7 +58,7 @@ pub mod dummy_test_span_utils {
             ),
             ast_id: span::ROOT_ERASED_FILE_AST_ID,
         },
-        ctx: SyntaxContextId::ROOT,
+        ctx: SyntaxContextId::root(Edition::CURRENT),
     };
 
     pub struct DummyTestSpanMap;
@@ -74,7 +74,7 @@ pub mod dummy_test_span_utils {
                     ),
                     ast_id: span::ROOT_ERASED_FILE_AST_ID,
                 },
-                ctx: SyntaxContextId::ROOT,
+                ctx: SyntaxContextId::root(Edition::CURRENT),
             }
         }
     }
@@ -141,15 +141,16 @@ where
 pub fn token_tree_to_syntax_node<Ctx>(
     tt: &tt::TopSubtree<SpanData<Ctx>>,
     entry_point: parser::TopEntryPoint,
-    edition: parser::Edition,
+    span_to_edition: &mut dyn FnMut(Ctx) -> Edition,
+    top_edition: Edition,
 ) -> (Parse<SyntaxNode>, SpanMap<Ctx>)
 where
-    SpanData<Ctx>: Copy + fmt::Debug,
-    Ctx: PartialEq,
+    Ctx: Copy + fmt::Debug + PartialEq + PartialEq + Eq + Hash,
 {
     let buffer = tt.view().strip_invisible();
-    let parser_input = to_parser_input(edition, buffer);
-    let parser_output = entry_point.parse(&parser_input, edition);
+    let parser_input = to_parser_input(buffer, span_to_edition);
+    // It matters what edition we parse with even when we escape all identifiers correctly.
+    let parser_output = entry_point.parse(&parser_input, top_edition);
     let mut tree_sink = TtTreeSink::new(buffer.cursor());
     for event in parser_output.iter() {
         match event {
diff --git a/src/tools/rust-analyzer/crates/syntax-bridge/src/to_parser_input.rs b/src/tools/rust-analyzer/crates/syntax-bridge/src/to_parser_input.rs
index 1bbb05f5507..0dcb2be316c 100644
--- a/src/tools/rust-analyzer/crates/syntax-bridge/src/to_parser_input.rs
+++ b/src/tools/rust-analyzer/crates/syntax-bridge/src/to_parser_input.rs
@@ -2,17 +2,20 @@
 //! format that works for our parser.
 
 use std::fmt;
+use std::hash::Hash;
 
-use span::Edition;
+use rustc_hash::FxHashMap;
+use span::{Edition, SpanData};
 use syntax::{SyntaxKind, SyntaxKind::*, T};
 
-pub fn to_parser_input<S: Copy + fmt::Debug>(
-    edition: Edition,
-    buffer: tt::TokenTreesView<'_, S>,
+pub fn to_parser_input<Ctx: Copy + fmt::Debug + PartialEq + Eq + Hash>(
+    buffer: tt::TokenTreesView<'_, SpanData<Ctx>>,
+    span_to_edition: &mut dyn FnMut(Ctx) -> Edition,
 ) -> parser::Input {
     let mut res = parser::Input::default();
 
     let mut current = buffer.cursor();
+    let mut syntax_context_to_edition_cache = FxHashMap::default();
 
     while !current.eof() {
         let tt = current.token_tree();
@@ -57,20 +60,25 @@ pub fn to_parser_input<S: Copy + fmt::Debug>(
                             res.was_joint();
                         }
                     }
-                    tt::Leaf::Ident(ident) => match ident.sym.as_str() {
-                        "_" => res.push(T![_]),
-                        i if i.starts_with('\'') => res.push(LIFETIME_IDENT),
-                        _ if ident.is_raw.yes() => res.push(IDENT),
-                        text => match SyntaxKind::from_keyword(text, edition) {
-                            Some(kind) => res.push(kind),
-                            None => {
-                                let contextual_keyword =
-                                    SyntaxKind::from_contextual_keyword(text, edition)
-                                        .unwrap_or(SyntaxKind::IDENT);
-                                res.push_ident(contextual_keyword);
-                            }
-                        },
-                    },
+                    tt::Leaf::Ident(ident) => {
+                        let edition = *syntax_context_to_edition_cache
+                            .entry(ident.span.ctx)
+                            .or_insert_with(|| span_to_edition(ident.span.ctx));
+                        match ident.sym.as_str() {
+                            "_" => res.push(T![_]),
+                            i if i.starts_with('\'') => res.push(LIFETIME_IDENT),
+                            _ if ident.is_raw.yes() => res.push(IDENT),
+                            text => match SyntaxKind::from_keyword(text, edition) {
+                                Some(kind) => res.push(kind),
+                                None => {
+                                    let contextual_keyword =
+                                        SyntaxKind::from_contextual_keyword(text, edition)
+                                            .unwrap_or(SyntaxKind::IDENT);
+                                    res.push_ident(contextual_keyword);
+                                }
+                            },
+                        }
+                    }
                     tt::Leaf::Punct(punct) => {
                         let kind = SyntaxKind::from_char(punct.char)
                             .unwrap_or_else(|| panic!("{punct:#?} is not a valid punct"));
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/edit.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/edit.rs
index de40d638be3..579f3ba8b4f 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/edit.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/edit.rs
@@ -153,8 +153,8 @@ impl<N: AstNode + Clone> AstNodeEdit for N {}
 #[test]
 fn test_increase_indent() {
     let arm_list = {
-        let arm = make::match_arm(iter::once(make::wildcard_pat().into()), None, make::expr_unit());
-        make::match_arm_list(vec![arm.clone(), arm])
+        let arm = make::match_arm(make::wildcard_pat().into(), None, make::ext::expr_unit());
+        make::match_arm_list([arm.clone(), arm])
     };
     assert_eq!(
         arm_list.syntax().to_string(),
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/expr_ext.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/expr_ext.rs
index 9466755576b..93faeb40c32 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/expr_ext.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/expr_ext.rs
@@ -10,7 +10,7 @@ use crate::{
         FormatArgsArg, FormatArgsExpr, MacroDef, Static, TokenTree,
     },
     AstToken,
-    SyntaxKind::*,
+    SyntaxKind::{self, *},
     SyntaxNode, SyntaxToken, T,
 };
 
@@ -50,6 +50,27 @@ impl From<ast::IfExpr> for ElseBranch {
     }
 }
 
+impl AstNode for ElseBranch {
+    fn can_cast(kind: SyntaxKind) -> bool {
+        ast::BlockExpr::can_cast(kind) || ast::IfExpr::can_cast(kind)
+    }
+
+    fn cast(syntax: SyntaxNode) -> Option<Self> {
+        if let Some(block_expr) = ast::BlockExpr::cast(syntax.clone()) {
+            Some(Self::Block(block_expr))
+        } else {
+            ast::IfExpr::cast(syntax).map(Self::IfExpr)
+        }
+    }
+
+    fn syntax(&self) -> &SyntaxNode {
+        match self {
+            ElseBranch::Block(block_expr) => block_expr.syntax(),
+            ElseBranch::IfExpr(if_expr) => if_expr.syntax(),
+        }
+    }
+}
+
 impl ast::IfExpr {
     pub fn condition(&self) -> Option<ast::Expr> {
         // If the condition is a BlockExpr, check if the then body is missing.
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs
index 282cbc4b3a4..dca231604fa 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs
@@ -63,6 +63,9 @@ pub mod ext {
         Some(expr)
     }
 
+    pub fn expr_unit() -> ast::Expr {
+        expr_tuple([]).into()
+    }
     pub fn expr_unreachable() -> ast::Expr {
         expr_from_text("unreachable!()")
     }
@@ -546,10 +549,6 @@ pub fn hacky_block_expr(
     ast_from_text(&format!("fn f() {buf}"))
 }
 
-pub fn expr_unit() -> ast::Expr {
-    expr_from_text("()")
-}
-
 pub fn expr_literal(text: &str) -> ast::Literal {
     assert_eq!(text.trim(), text);
     ast_from_text(&format!("fn f() {{ let _ = {text}; }}"))
@@ -559,8 +558,8 @@ pub fn expr_const_value(text: &str) -> ast::ConstArg {
     ast_from_text(&format!("trait Foo<const N: usize = {text}> {{}}"))
 }
 
-pub fn expr_empty_block() -> ast::Expr {
-    expr_from_text("{}")
+pub fn expr_empty_block() -> ast::BlockExpr {
+    ast_from_text("const C: () = {};")
 }
 pub fn expr_path(path: ast::Path) -> ast::Expr {
     expr_from_text(&path.to_string())
@@ -600,14 +599,14 @@ pub fn expr_try(expr: ast::Expr) -> ast::Expr {
 pub fn expr_await(expr: ast::Expr) -> ast::Expr {
     expr_from_text(&format!("{expr}.await"))
 }
-pub fn expr_match(expr: ast::Expr, match_arm_list: ast::MatchArmList) -> ast::Expr {
+pub fn expr_match(expr: ast::Expr, match_arm_list: ast::MatchArmList) -> ast::MatchExpr {
     expr_from_text(&format!("match {expr} {match_arm_list}"))
 }
 pub fn expr_if(
     condition: ast::Expr,
     then_branch: ast::BlockExpr,
     else_branch: Option<ast::ElseBranch>,
-) -> ast::Expr {
+) -> ast::IfExpr {
     let else_branch = match else_branch {
         Some(ast::ElseBranch::Block(block)) => format!("else {block}"),
         Some(ast::ElseBranch::IfExpr(if_expr)) => format!("else {if_expr}"),
@@ -623,7 +622,7 @@ pub fn expr_loop(block: ast::BlockExpr) -> ast::Expr {
     expr_from_text(&format!("loop {block}"))
 }
 
-pub fn expr_prefix(op: SyntaxKind, expr: ast::Expr) -> ast::Expr {
+pub fn expr_prefix(op: SyntaxKind, expr: ast::Expr) -> ast::PrefixExpr {
     let token = token(op);
     expr_from_text(&format!("{token}{expr}"))
 }
@@ -656,14 +655,14 @@ pub fn expr_field(receiver: ast::Expr, field: &str) -> ast::Expr {
 pub fn expr_paren(expr: ast::Expr) -> ast::Expr {
     expr_from_text(&format!("({expr})"))
 }
-pub fn expr_tuple(elements: impl IntoIterator<Item = ast::Expr>) -> ast::Expr {
+pub fn expr_tuple(elements: impl IntoIterator<Item = ast::Expr>) -> ast::TupleExpr {
     let expr = elements.into_iter().format(", ");
     expr_from_text(&format!("({expr})"))
 }
 pub fn expr_assignment(lhs: ast::Expr, rhs: ast::Expr) -> ast::Expr {
     expr_from_text(&format!("{lhs} = {rhs}"))
 }
-fn expr_from_text(text: &str) -> ast::Expr {
+fn expr_from_text<E: Into<ast::Expr> + AstNode>(text: &str) -> E {
     ast_from_text(&format!("const C: () = {text};"))
 }
 pub fn expr_let(pattern: ast::Pat, expr: ast::Expr) -> ast::LetExpr {
@@ -788,15 +787,21 @@ pub fn path_pat(path: ast::Path) -> ast::Pat {
     }
 }
 
-pub fn match_arm(
-    pats: impl IntoIterator<Item = ast::Pat>,
-    guard: Option<ast::Expr>,
-    expr: ast::Expr,
-) -> ast::MatchArm {
-    let pats_str = pats.into_iter().join(" | ");
+/// Returns a `Pat` if the path has just one segment, an `OrPat` otherwise.
+pub fn or_pat(pats: impl IntoIterator<Item = ast::Pat>, leading_pipe: bool) -> ast::Pat {
+    let leading_pipe = if leading_pipe { "| " } else { "" };
+    let pats = pats.into_iter().join(" | ");
+
+    return from_text(&format!("{leading_pipe}{pats}"));
+    fn from_text(text: &str) -> ast::Pat {
+        ast_from_text(&format!("fn f({text}: ())"))
+    }
+}
+
+pub fn match_arm(pat: ast::Pat, guard: Option<ast::MatchGuard>, expr: ast::Expr) -> ast::MatchArm {
     return match guard {
-        Some(guard) => from_text(&format!("{pats_str} if {guard} => {expr}")),
-        None => from_text(&format!("{pats_str} => {expr}")),
+        Some(guard) => from_text(&format!("{pat} {guard} => {expr}")),
+        None => from_text(&format!("{pat} => {expr}")),
     };
 
     fn from_text(text: &str) -> ast::MatchArm {
@@ -817,6 +822,14 @@ pub fn match_arm_with_guard(
     }
 }
 
+pub fn match_guard(condition: ast::Expr) -> ast::MatchGuard {
+    return from_text(&format!("if {condition}"));
+
+    fn from_text(text: &str) -> ast::MatchGuard {
+        ast_from_text(&format!("fn f() {{ match () {{() {text} => () }}"))
+    }
+}
+
 pub fn match_arm_list(arms: impl IntoIterator<Item = ast::MatchArm>) -> ast::MatchArmList {
     let arms_str = arms.into_iter().fold(String::new(), |mut acc, arm| {
         let needs_comma = arm.expr().is_none_or(|it| !it.is_block_like());
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs
index 81c7e15bcbc..56f94b965e3 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs
@@ -185,6 +185,14 @@ impl ast::Attr {
         Some((self.simple_name()?, tt))
     }
 
+    pub fn as_simple_path(&self) -> Option<ast::Path> {
+        let meta = self.meta()?;
+        if meta.eq_token().is_some() || meta.token_tree().is_some() {
+            return None;
+        }
+        self.path()
+    }
+
     pub fn simple_name(&self) -> Option<SmolStr> {
         let path = self.meta()?.path()?;
         match (path.segment(), path.qualifier()) {
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 bea6bfeafcf..572622db544 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
@@ -1,6 +1,9 @@
 //! Wrappers over [`make`] constructors
 use crate::{
-    ast::{self, make, HasGenericParams, HasName, HasTypeBounds, HasVisibility},
+    ast::{
+        self, make, HasArgList, HasGenericArgs, HasGenericParams, HasName, HasTypeBounds,
+        HasVisibility,
+    },
     syntax_editor::SyntaxMappingBuilder,
     AstNode, NodeOrToken, SyntaxKind, SyntaxNode, SyntaxToken,
 };
@@ -12,6 +15,14 @@ impl SyntaxFactory {
         make::name(name).clone_for_update()
     }
 
+    pub fn name_ref(&self, name: &str) -> ast::NameRef {
+        make::name_ref(name).clone_for_update()
+    }
+
+    pub fn lifetime(&self, text: &str) -> ast::Lifetime {
+        make::lifetime(text).clone_for_update()
+    }
+
     pub fn ty(&self, text: &str) -> ast::Type {
         make::ty(text).clone_for_update()
     }
@@ -24,6 +35,20 @@ impl SyntaxFactory {
         ast
     }
 
+    pub fn ty_path(&self, path: ast::Path) -> ast::PathType {
+        let ast::Type::PathType(ast) = make::ty_path(path.clone()).clone_for_update() else {
+            unreachable!()
+        };
+
+        if let Some(mut mapping) = self.mappings() {
+            let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+            builder.map_node(path.syntax().clone(), ast.path().unwrap().syntax().clone());
+            builder.finish(&mut mapping);
+        }
+
+        ast
+    }
+
     pub fn type_param(
         &self,
         name: ast::Name,
@@ -46,6 +71,71 @@ impl SyntaxFactory {
         ast
     }
 
+    pub fn path_segment(&self, name_ref: ast::NameRef) -> ast::PathSegment {
+        let ast = make::path_segment(name_ref.clone()).clone_for_update();
+
+        if let Some(mut mapping) = self.mappings() {
+            let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+            builder.map_node(name_ref.syntax().clone(), ast.name_ref().unwrap().syntax().clone());
+            builder.finish(&mut mapping);
+        }
+
+        ast
+    }
+
+    pub fn path_segment_generics(
+        &self,
+        name_ref: ast::NameRef,
+        generic_arg_list: ast::GenericArgList,
+    ) -> ast::PathSegment {
+        let ast::Type::PathType(path) = make::ty(&format!("{name_ref}{generic_arg_list}")) else {
+            unreachable!();
+        };
+
+        let ast = path.path().unwrap().segment().unwrap().clone_for_update();
+
+        if let Some(mut mapping) = self.mappings() {
+            let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+            builder.map_node(name_ref.syntax().clone(), ast.name_ref().unwrap().syntax().clone());
+            builder.map_node(
+                generic_arg_list.syntax().clone(),
+                ast.generic_arg_list().unwrap().syntax().clone(),
+            );
+            builder.finish(&mut mapping);
+        }
+
+        ast
+    }
+
+    pub fn path_unqualified(&self, segment: ast::PathSegment) -> ast::Path {
+        let ast = make::path_unqualified(segment.clone()).clone_for_update();
+
+        if let Some(mut mapping) = self.mappings() {
+            let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+            builder.map_node(segment.syntax().clone(), ast.segment().unwrap().syntax().clone());
+            builder.finish(&mut mapping);
+        }
+
+        ast
+    }
+
+    pub fn path_from_segments(
+        &self,
+        segments: impl IntoIterator<Item = ast::PathSegment>,
+        is_abs: bool,
+    ) -> ast::Path {
+        let (segments, input) = iterator_input(segments);
+        let ast = make::path_from_segments(segments, is_abs).clone_for_update();
+
+        if let Some(mut mapping) = self.mappings() {
+            let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+            builder.map_children(input.into_iter(), ast.segments().map(|it| it.syntax().clone()));
+            builder.finish(&mut mapping);
+        }
+
+        ast
+    }
+
     pub fn ident_pat(&self, ref_: bool, mut_: bool, name: ast::Name) -> ast::IdentPat {
         let ast = make::ident_pat(ref_, mut_, name.clone()).clone_for_update();
 
@@ -58,6 +148,32 @@ impl SyntaxFactory {
         ast
     }
 
+    pub fn wildcard_pat(&self) -> ast::WildcardPat {
+        make::wildcard_pat().clone_for_update()
+    }
+
+    pub fn literal_pat(&self, text: &str) -> ast::LiteralPat {
+        make::literal_pat(text).clone_for_update()
+    }
+
+    pub fn tuple_struct_pat(
+        &self,
+        path: ast::Path,
+        fields: impl IntoIterator<Item = ast::Pat>,
+    ) -> ast::TupleStructPat {
+        let (fields, input) = iterator_input(fields);
+        let ast = make::tuple_struct_pat(path.clone(), fields).clone_for_update();
+
+        if let Some(mut mapping) = self.mappings() {
+            let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+            builder.map_node(path.syntax().clone(), ast.path().unwrap().syntax().clone());
+            builder.map_children(input.into_iter(), ast.fields().map(|it| it.syntax().clone()));
+            builder.finish(&mut mapping);
+        }
+
+        ast
+    }
+
     pub fn block_expr(
         &self,
         statements: impl IntoIterator<Item = ast::Stmt>,
@@ -95,7 +211,20 @@ impl SyntaxFactory {
     }
 
     pub fn expr_empty_block(&self) -> ast::BlockExpr {
-        ast::BlockExpr { syntax: make::expr_empty_block().syntax().clone_for_update() }
+        make::expr_empty_block().clone_for_update()
+    }
+
+    pub fn expr_tuple(&self, fields: impl IntoIterator<Item = ast::Expr>) -> ast::TupleExpr {
+        let (fields, input) = iterator_input(fields);
+        let ast = make::expr_tuple(fields).clone_for_update();
+
+        if let Some(mut mapping) = self.mappings() {
+            let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+            builder.map_children(input.into_iter(), ast.fields().map(|it| it.syntax().clone()));
+            builder.finish(&mut mapping);
+        }
+
+        ast
     }
 
     pub fn expr_bin(&self, lhs: ast::Expr, op: ast::BinaryOp, rhs: ast::Expr) -> ast::BinExpr {
@@ -115,6 +244,10 @@ impl SyntaxFactory {
         ast
     }
 
+    pub fn expr_literal(&self, text: &str) -> ast::Literal {
+        make::expr_literal(text).clone_for_update()
+    }
+
     pub fn expr_path(&self, path: ast::Path) -> ast::Expr {
         let ast::Expr::PathExpr(ast) = make::expr_path(path.clone()).clone_for_update() else {
             unreachable!()
@@ -129,6 +262,49 @@ impl SyntaxFactory {
         ast.into()
     }
 
+    pub fn expr_prefix(&self, op: SyntaxKind, expr: ast::Expr) -> ast::PrefixExpr {
+        let ast = make::expr_prefix(op, expr.clone()).clone_for_update();
+
+        if let Some(mut mapping) = self.mappings() {
+            let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+            builder.map_node(expr.syntax().clone(), ast.expr().unwrap().syntax().clone());
+            builder.finish(&mut mapping);
+        }
+
+        ast
+    }
+
+    pub fn expr_call(&self, expr: ast::Expr, arg_list: ast::ArgList) -> ast::CallExpr {
+        // FIXME: `make::expr_call`` should return a `CallExpr`, not just an `Expr`
+        let ast::Expr::CallExpr(ast) =
+            make::expr_call(expr.clone(), arg_list.clone()).clone_for_update()
+        else {
+            unreachable!()
+        };
+
+        if let Some(mut mapping) = self.mappings() {
+            let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+            builder.map_node(expr.syntax().clone(), ast.expr().unwrap().syntax().clone());
+            builder.map_node(arg_list.syntax().clone(), ast.arg_list().unwrap().syntax().clone());
+            builder.finish(&mut mapping);
+        }
+
+        ast
+    }
+
+    pub fn arg_list(&self, args: impl IntoIterator<Item = ast::Expr>) -> ast::ArgList {
+        let (args, input) = iterator_input(args);
+        let ast = make::arg_list(args).clone_for_update();
+
+        if let Some(mut mapping) = self.mappings() {
+            let mut builder = SyntaxMappingBuilder::new(ast.syntax.clone());
+            builder.map_children(input.into_iter(), ast.args().map(|it| it.syntax().clone()));
+            builder.finish(&mut mapping);
+        }
+
+        ast
+    }
+
     pub fn expr_ref(&self, expr: ast::Expr, exclusive: bool) -> ast::Expr {
         let ast::Expr::RefExpr(ast) = make::expr_ref(expr.clone(), exclusive).clone_for_update()
         else {
@@ -160,6 +336,125 @@ impl SyntaxFactory {
         ast
     }
 
+    pub fn expr_if(
+        &self,
+        condition: ast::Expr,
+        then_branch: ast::BlockExpr,
+        else_branch: Option<ast::ElseBranch>,
+    ) -> ast::IfExpr {
+        let ast = make::expr_if(condition.clone(), then_branch.clone(), else_branch.clone())
+            .clone_for_update();
+
+        if let Some(mut mapping) = self.mappings() {
+            let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+            builder.map_node(condition.syntax().clone(), ast.condition().unwrap().syntax().clone());
+            builder.map_node(
+                then_branch.syntax().clone(),
+                ast.then_branch().unwrap().syntax().clone(),
+            );
+
+            if let Some(else_branch) = else_branch {
+                builder.map_node(
+                    else_branch.syntax().clone(),
+                    ast.else_branch().unwrap().syntax().clone(),
+                );
+            }
+            builder.finish(&mut mapping);
+        }
+
+        ast
+    }
+
+    pub fn expr_let(&self, pattern: ast::Pat, expr: ast::Expr) -> ast::LetExpr {
+        let ast = make::expr_let(pattern.clone(), expr.clone()).clone_for_update();
+
+        if let Some(mut mapping) = self.mappings() {
+            let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+            builder.map_node(pattern.syntax().clone(), ast.pat().unwrap().syntax().clone());
+            builder.map_node(expr.syntax().clone(), ast.expr().unwrap().syntax().clone());
+            builder.finish(&mut mapping);
+        }
+
+        ast
+    }
+
+    pub fn expr_stmt(&self, expr: ast::Expr) -> ast::ExprStmt {
+        let ast = make::expr_stmt(expr.clone()).clone_for_update();
+
+        if let Some(mut mapping) = self.mappings() {
+            let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+            builder.map_node(expr.syntax().clone(), ast.expr().unwrap().syntax().clone());
+            builder.finish(&mut mapping);
+        }
+
+        ast
+    }
+
+    pub fn expr_match(&self, expr: ast::Expr, match_arm_list: ast::MatchArmList) -> ast::MatchExpr {
+        let ast = make::expr_match(expr.clone(), match_arm_list.clone()).clone_for_update();
+
+        if let Some(mut mapping) = self.mappings() {
+            let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+            builder.map_node(expr.syntax().clone(), ast.expr().unwrap().syntax().clone());
+            builder.map_node(
+                match_arm_list.syntax().clone(),
+                ast.match_arm_list().unwrap().syntax().clone(),
+            );
+            builder.finish(&mut mapping);
+        }
+
+        ast
+    }
+
+    pub fn match_arm(
+        &self,
+        pat: ast::Pat,
+        guard: Option<ast::MatchGuard>,
+        expr: ast::Expr,
+    ) -> ast::MatchArm {
+        let ast = make::match_arm(pat.clone(), guard.clone(), expr.clone()).clone_for_update();
+
+        if let Some(mut mapping) = self.mappings() {
+            let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+            builder.map_node(pat.syntax().clone(), ast.pat().unwrap().syntax().clone());
+            if let Some(guard) = guard {
+                builder.map_node(guard.syntax().clone(), ast.guard().unwrap().syntax().clone());
+            }
+            builder.map_node(expr.syntax().clone(), ast.expr().unwrap().syntax().clone());
+            builder.finish(&mut mapping);
+        }
+
+        ast
+    }
+
+    pub fn match_guard(&self, condition: ast::Expr) -> ast::MatchGuard {
+        let ast = make::match_guard(condition.clone()).clone_for_update();
+
+        if let Some(mut mapping) = self.mappings() {
+            let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+            builder.map_node(condition.syntax().clone(), ast.condition().unwrap().syntax().clone());
+            builder.finish(&mut mapping);
+        }
+
+        ast
+    }
+
+    pub fn match_arm_list(
+        &self,
+        match_arms: impl IntoIterator<Item = ast::MatchArm>,
+    ) -> ast::MatchArmList {
+        let (match_arms, input) = iterator_input(match_arms);
+        let ast = make::match_arm_list(match_arms).clone_for_update();
+
+        if let Some(mut mapping) = self.mappings() {
+            let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+            builder.map_children(input.into_iter(), ast.arms().map(|it| it.syntax().clone()));
+            builder.finish(&mut mapping);
+        }
+
+        ast
+    }
+
     pub fn let_stmt(
         &self,
         pattern: ast::Pat,
@@ -185,6 +480,30 @@ impl SyntaxFactory {
         ast
     }
 
+    pub fn type_arg(&self, ty: ast::Type) -> ast::TypeArg {
+        let ast = make::type_arg(ty.clone()).clone_for_update();
+
+        if let Some(mut mapping) = self.mappings() {
+            let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+            builder.map_node(ty.syntax().clone(), ast.ty().unwrap().syntax().clone());
+            builder.finish(&mut mapping);
+        }
+
+        ast
+    }
+
+    pub fn lifetime_arg(&self, lifetime: ast::Lifetime) -> ast::LifetimeArg {
+        let ast = make::lifetime_arg(lifetime.clone()).clone_for_update();
+
+        if let Some(mut mapping) = self.mappings() {
+            let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+            builder.map_node(lifetime.syntax().clone(), ast.lifetime().unwrap().syntax().clone());
+            builder.finish(&mut mapping);
+        }
+
+        ast
+    }
+
     pub fn item_const(
         &self,
         visibility: Option<ast::Visibility>,
@@ -252,12 +571,17 @@ impl SyntaxFactory {
         ast
     }
 
-    pub fn turbofish_generic_arg_list(
+    pub fn generic_arg_list(
         &self,
         generic_args: impl IntoIterator<Item = ast::GenericArg>,
+        is_turbo: bool,
     ) -> ast::GenericArgList {
         let (generic_args, input) = iterator_input(generic_args);
-        let ast = make::turbofish_generic_arg_list(generic_args.clone()).clone_for_update();
+        let ast = if is_turbo {
+            make::turbofish_generic_arg_list(generic_args).clone_for_update()
+        } else {
+            make::generic_arg_list(generic_args).clone_for_update()
+        };
 
         if let Some(mut mapping) = self.mappings() {
             let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
@@ -503,11 +827,41 @@ impl SyntaxFactory {
         make::token(kind)
     }
 
-    pub fn whitespace(&self, text: &str) -> ast::SyntaxToken {
+    pub fn whitespace(&self, text: &str) -> SyntaxToken {
         make::tokens::whitespace(text)
     }
 }
 
+// `ext` constructors
+impl SyntaxFactory {
+    pub fn ident_path(&self, ident: &str) -> ast::Path {
+        self.path_unqualified(self.path_segment(self.name_ref(ident)))
+    }
+
+    pub fn expr_unit(&self) -> ast::Expr {
+        self.expr_tuple([]).into()
+    }
+
+    pub fn ty_option(&self, t: ast::Type) -> ast::PathType {
+        let generic_arg_list = self.generic_arg_list([self.type_arg(t).into()], false);
+        let path = self.path_unqualified(
+            self.path_segment_generics(self.name_ref("Option"), generic_arg_list),
+        );
+
+        self.ty_path(path)
+    }
+
+    pub fn ty_result(&self, t: ast::Type, e: ast::Type) -> ast::PathType {
+        let generic_arg_list =
+            self.generic_arg_list([self.type_arg(t).into(), self.type_arg(e).into()], false);
+        let path = self.path_unqualified(
+            self.path_segment_generics(self.name_ref("Result"), generic_arg_list),
+        );
+
+        self.ty_path(path)
+    }
+}
+
 // We need to collect `input` here instead of taking `impl IntoIterator + Clone`,
 // because if we took `impl IntoIterator + Clone`, that could be something like an
 // `Iterator::map` with a closure that also makes use of a `SyntaxFactory` constructor.
diff --git a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs
index 992a847663a..b82181ae13a 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs
@@ -335,7 +335,7 @@ mod tests {
     #[test]
     fn basic_usage() {
         let root = make::match_arm(
-            [make::wildcard_pat().into()],
+            make::wildcard_pat().into(),
             None,
             make::expr_tuple([
                 make::expr_bin_op(
@@ -344,7 +344,8 @@ mod tests {
                     make::expr_literal("2").into(),
                 ),
                 make::expr_literal("true").into(),
-            ]),
+            ])
+            .into(),
         );
 
         let to_wrap = root.syntax().descendants().find_map(ast::TupleExpr::cast).unwrap();
@@ -549,7 +550,7 @@ mod tests {
             None,
             None,
             make::param_list(None, []),
-            make::block_expr([], Some(make::expr_unit())),
+            make::block_expr([], Some(make::ext::expr_unit())),
             Some(make::ret_type(make::ty_unit())),
             false,
             false,
diff --git a/src/tools/rust-analyzer/crates/test-fixture/Cargo.toml b/src/tools/rust-analyzer/crates/test-fixture/Cargo.toml
index c860e7b1183..95f4cb9d67e 100644
--- a/src/tools/rust-analyzer/crates/test-fixture/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/test-fixture/Cargo.toml
@@ -2,6 +2,8 @@
 name = "test-fixture"
 version = "0.0.0"
 rust-version.workspace = true
+description = "Test fixtures for rust-analyzer."
+
 edition.workspace = true
 license.workspace = true
 authors.workspace = true
diff --git a/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs b/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs
index 0e72d796875..866379d940e 100644
--- a/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs
@@ -30,7 +30,9 @@ pub const WORKSPACE: base_db::SourceRootId = base_db::SourceRootId(0);
 
 pub trait WithFixture: Default + ExpandDatabase + SourceRootDatabase + 'static {
     #[track_caller]
-    fn with_single_file(ra_fixture: &str) -> (Self, EditionedFileId) {
+    fn with_single_file(
+        #[rust_analyzer::rust_fixture] ra_fixture: &str,
+    ) -> (Self, EditionedFileId) {
         let fixture = ChangeFixture::parse(ra_fixture);
         let mut db = Self::default();
         fixture.change.apply(&mut db);
@@ -39,7 +41,9 @@ pub trait WithFixture: Default + ExpandDatabase + SourceRootDatabase + 'static {
     }
 
     #[track_caller]
-    fn with_many_files(ra_fixture: &str) -> (Self, Vec<EditionedFileId>) {
+    fn with_many_files(
+        #[rust_analyzer::rust_fixture] ra_fixture: &str,
+    ) -> (Self, Vec<EditionedFileId>) {
         let fixture = ChangeFixture::parse(ra_fixture);
         let mut db = Self::default();
         fixture.change.apply(&mut db);
@@ -48,7 +52,7 @@ pub trait WithFixture: Default + ExpandDatabase + SourceRootDatabase + 'static {
     }
 
     #[track_caller]
-    fn with_files(ra_fixture: &str) -> Self {
+    fn with_files(#[rust_analyzer::rust_fixture] ra_fixture: &str) -> Self {
         let fixture = ChangeFixture::parse(ra_fixture);
         let mut db = Self::default();
         fixture.change.apply(&mut db);
@@ -58,7 +62,7 @@ pub trait WithFixture: Default + ExpandDatabase + SourceRootDatabase + 'static {
 
     #[track_caller]
     fn with_files_extra_proc_macros(
-        ra_fixture: &str,
+        #[rust_analyzer::rust_fixture] ra_fixture: &str,
         proc_macros: Vec<(String, ProcMacro)>,
     ) -> Self {
         let fixture = ChangeFixture::parse_with_proc_macros(ra_fixture, proc_macros);
@@ -69,21 +73,23 @@ pub trait WithFixture: Default + ExpandDatabase + SourceRootDatabase + 'static {
     }
 
     #[track_caller]
-    fn with_position(ra_fixture: &str) -> (Self, FilePosition) {
+    fn with_position(#[rust_analyzer::rust_fixture] ra_fixture: &str) -> (Self, FilePosition) {
         let (db, file_id, range_or_offset) = Self::with_range_or_offset(ra_fixture);
         let offset = range_or_offset.expect_offset();
         (db, FilePosition { file_id, offset })
     }
 
     #[track_caller]
-    fn with_range(ra_fixture: &str) -> (Self, FileRange) {
+    fn with_range(#[rust_analyzer::rust_fixture] ra_fixture: &str) -> (Self, FileRange) {
         let (db, file_id, range_or_offset) = Self::with_range_or_offset(ra_fixture);
         let range = range_or_offset.expect_range();
         (db, FileRange { file_id, range })
     }
 
     #[track_caller]
-    fn with_range_or_offset(ra_fixture: &str) -> (Self, EditionedFileId, RangeOrOffset) {
+    fn with_range_or_offset(
+        #[rust_analyzer::rust_fixture] ra_fixture: &str,
+    ) -> (Self, EditionedFileId, RangeOrOffset) {
         let fixture = ChangeFixture::parse(ra_fixture);
         let mut db = Self::default();
         fixture.change.apply(&mut db);
@@ -116,12 +122,12 @@ pub struct ChangeFixture {
 const SOURCE_ROOT_PREFIX: &str = "/";
 
 impl ChangeFixture {
-    pub fn parse(ra_fixture: &str) -> ChangeFixture {
+    pub fn parse(#[rust_analyzer::rust_fixture] ra_fixture: &str) -> ChangeFixture {
         Self::parse_with_proc_macros(ra_fixture, Vec::new())
     }
 
     pub fn parse_with_proc_macros(
-        ra_fixture: &str,
+        #[rust_analyzer::rust_fixture] ra_fixture: &str,
         mut proc_macro_defs: Vec<(String, ProcMacro)>,
     ) -> ChangeFixture {
         let FixtureWithProjectMeta {
@@ -376,8 +382,8 @@ impl ChangeFixture {
     }
 }
 
-fn default_test_proc_macros() -> [(String, ProcMacro); 8] {
-    [
+fn default_test_proc_macros() -> Box<[(String, ProcMacro)]> {
+    Box::new([
         (
             r#"
 #[proc_macro_attribute]
@@ -498,7 +504,22 @@ pub fn issue_17479(input: TokenStream) -> TokenStream {
                 disabled: false,
             },
         ),
-    ]
+        (
+            r#"
+#[proc_macro_attribute]
+pub fn issue_18898(_attr: TokenStream, input: TokenStream) -> TokenStream {
+    input
+}
+"#
+            .into(),
+            ProcMacro {
+                name: Symbol::intern("issue_18898"),
+                kind: ProcMacroKind::Bang,
+                expander: sync::Arc::new(Issue18898ProcMacroExpander),
+                disabled: false,
+            },
+        ),
+    ])
 }
 
 fn filter_test_proc_macros(
@@ -801,3 +822,54 @@ impl ProcMacroExpander for Issue17479ProcMacroExpander {
         })
     }
 }
+
+// Reads ident type within string quotes, for issue #17479.
+#[derive(Debug)]
+struct Issue18898ProcMacroExpander;
+impl ProcMacroExpander for Issue18898ProcMacroExpander {
+    fn expand(
+        &self,
+        subtree: &TopSubtree,
+        _: Option<&TopSubtree>,
+        _: &Env,
+        def_site: Span,
+        _: Span,
+        _: Span,
+        _: Option<String>,
+    ) -> Result<TopSubtree, ProcMacroExpansionError> {
+        let span = subtree
+            .token_trees()
+            .flat_tokens()
+            .last()
+            .ok_or_else(|| ProcMacroExpansionError::Panic("malformed input".to_owned()))?
+            .first_span();
+        let overly_long_subtree = quote! {span =>
+            {
+                let a = 5;
+                let a = 5;
+                let a = 5;
+                let a = 5;
+                let a = 5;
+                let a = 5;
+                let a = 5;
+                let a = 5;
+                let a = 5;
+                let a = 5;
+                let a = 5;
+                let a = 5;
+                let a = 5;
+                let a = 5;
+                let a = 5;
+                let a = 5;
+                let a = 5;
+                let a = 5;
+                let a = 5;
+            }
+        };
+        Ok(quote! { def_site =>
+            fn foo() {
+                #overly_long_subtree
+            }
+        })
+    }
+}
diff --git a/src/tools/rust-analyzer/crates/test-utils/src/fixture.rs b/src/tools/rust-analyzer/crates/test-utils/src/fixture.rs
index 54c9db7aacc..7fe26d53bf2 100644
--- a/src/tools/rust-analyzer/crates/test-utils/src/fixture.rs
+++ b/src/tools/rust-analyzer/crates/test-utils/src/fixture.rs
@@ -168,7 +168,7 @@ impl FixtureWithProjectMeta {
     /// That will set toolchain to nightly and include predefined proc macros and a subset of
     /// `libcore` into the fixture, see `minicore.rs` for what's available. Note that toolchain
     /// defaults to stable.
-    pub fn parse(ra_fixture: &str) -> Self {
+    pub fn parse(#[rust_analyzer::rust_fixture] ra_fixture: &str) -> Self {
         let fixture = trim_indent(ra_fixture);
         let mut fixture = fixture.as_str();
         let mut toolchain = None;
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 4a2346193b4..fd06736a252 100644
--- a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs
+++ b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs
@@ -32,7 +32,7 @@
 //!     error: fmt
 //!     fmt: option, result, transmute, coerce_unsized, copy, clone, derive
 //!     fn: tuple
-//!     from: sized
+//!     from: sized, result
 //!     future: pin
 //!     coroutine: pin
 //!     dispatch_from_dyn: unsize, pin
@@ -332,6 +332,25 @@ pub mod convert {
             t
         }
     }
+
+    pub trait TryFrom<T>: Sized {
+        type Error;
+        fn try_from(value: T) -> Result<Self, Self::Error>;
+    }
+    pub trait TryInto<T>: Sized {
+        type Error;
+        fn try_into(self) -> Result<T, Self::Error>;
+    }
+
+    impl<T, U> TryInto<U> for T
+    where
+        U: TryFrom<T>,
+    {
+        type Error = U::Error;
+        fn try_into(self) -> Result<U, U::Error> {
+            U::try_from(self)
+        }
+    }
     // endregion:from
 
     // region:as_ref
@@ -1510,7 +1529,7 @@ pub mod iter {
             impl<T, const N: usize> IntoIterator for [T; N] {
                 type Item = T;
                 type IntoIter = IntoIter<T, N>;
-                fn into_iter(self) -> I {
+                fn into_iter(self) -> Self::IntoIter {
                     IntoIter { data: self, range: IndexRange { start: 0, end: loop {} } }
                 }
             }
@@ -1520,6 +1539,29 @@ pub mod iter {
                     loop {}
                 }
             }
+            pub struct Iter<'a, T> {
+                slice: &'a [T],
+            }
+            impl<'a, T> IntoIterator for &'a [T; N] {
+                type Item = &'a T;
+                type IntoIter = Iter<'a, T>;
+                fn into_iter(self) -> Self::IntoIter {
+                    loop {}
+                }
+            }
+            impl<'a, T> IntoIterator for &'a [T] {
+                type Item = &'a T;
+                type IntoIter = Iter<'a, T>;
+                fn into_iter(self) -> Self::IntoIter {
+                    loop {}
+                }
+            }
+            impl<'a, T> Iterator for Iter<'a, T> {
+                type Item = &'a T;
+                fn next(&mut self) -> Option<T> {
+                    loop {}
+                }
+            }
         }
         pub use self::collect::IntoIterator;
     }
@@ -1532,6 +1574,15 @@ pub mod str {
     pub const unsafe fn from_utf8_unchecked(v: &[u8]) -> &str {
         ""
     }
+    pub trait FromStr: Sized {
+        type Err;
+        fn from_str(s: &str) -> Result<Self, Self::Err>;
+    }
+    impl str {
+        pub fn parse<F: FromStr>(&self) -> Result<F, F::Err> {
+            FromStr::from_str(self)
+        }
+    }
 }
 // endregion:str
 
@@ -1791,7 +1842,7 @@ pub mod prelude {
             cmp::{Eq, PartialEq},                    // :eq
             cmp::{Ord, PartialOrd},                  // :ord
             convert::AsRef,                          // :as_ref
-            convert::{From, Into},                   // :from
+            convert::{From, Into, TryFrom, TryInto}, // :from
             default::Default,                        // :default
             iter::{IntoIterator, Iterator},          // :iterator
             macros::builtin::{derive, derive_const}, // :derive
@@ -1806,6 +1857,7 @@ pub mod prelude {
             option::Option::{self, None, Some},      // :option
             panic,                                   // :panic
             result::Result::{self, Err, Ok},         // :result
+            str::FromStr,                            // :str
         };
     }
 
diff --git a/src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml b/src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml
index 09296dc6dd5..bc54d7168f0 100644
--- a/src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml
@@ -16,7 +16,7 @@ doctest = false
 tracing.workspace = true
 walkdir = "2.3.2"
 crossbeam-channel.workspace = true
-notify = "6.1.1"
+notify = "8.0.0"
 rayon = "1.10.0"
 
 stdx.workspace = true
diff --git a/src/tools/rust-analyzer/docs/dev/README.md b/src/tools/rust-analyzer/docs/dev/README.md
index cd0f49174cd..3ba492e0959 100644
--- a/src/tools/rust-analyzer/docs/dev/README.md
+++ b/src/tools/rust-analyzer/docs/dev/README.md
@@ -154,19 +154,21 @@ There are also several VS Code commands which might be of interest:
 
 * `rust-analyzer: Status` shows some memory-usage statistics.
 
-* `rust-analyzer: Syntax Tree` shows syntax tree of the current file/selection.
-
 * `rust-analyzer: View Hir` shows the HIR expressions within the function containing the cursor.
 
-  You can hover over syntax nodes in the opened text file to see the appropriate
-  rust code that it refers to and the rust editor will also highlight the proper
-  text range.
+* If `rust-analyzer.showSyntaxTree` is enabled in settings, `Rust Syntax Tree: Focus on Rust Syntax Tree View` shows the syntax tree of the current file.
+
+  You can click on nodes in the rust editor to go to the corresponding syntax node.
+
+  You can click on `Reveal Syntax Element` next to a syntax node to go to the corresponding rust code and highlight the proper text range.
 
   If you trigger Go to Definition in the inspected Rust source file,
-  the syntax tree read-only editor should scroll to and select the
+  the syntax tree view should scroll to and select the
   appropriate syntax node token.
 
-  ![demo](https://user-images.githubusercontent.com/36276403/78225773-6636a480-74d3-11ea-9d9f-1c9d42da03b0.png)
+  You can click on `Copy` next to a syntax node to copy a text representation of the node.
+
+  ![demo](https://github.com/user-attachments/assets/2d20ae87-0abf-495f-bee8-54aa2494a00d)
 
 ## Profiling
 
diff --git a/src/tools/rust-analyzer/docs/dev/lsp-extensions.md b/src/tools/rust-analyzer/docs/dev/lsp-extensions.md
index 21ac3a5a269..a632fc6f5fb 100644
--- a/src/tools/rust-analyzer/docs/dev/lsp-extensions.md
+++ b/src/tools/rust-analyzer/docs/dev/lsp-extensions.md
@@ -1,5 +1,5 @@
 <!---
-lsp/ext.rs hash: 6dd762ae19630ec0
+lsp/ext.rs hash: 2d8604825c458288
 
 If you need to change the above hash to make the test pass, please check if you
 need to adjust this doc as well and ping this issue:
@@ -710,6 +710,23 @@ interface SyntaxTreeParams {
 Returns textual representation of a parse tree for the file/selected region.
 Primarily for debugging, but very useful for all people working on rust-analyzer itself.
 
+## View Syntax Tree
+
+**Method:** `rust-analyzer/viewSyntaxTree`
+
+**Request:**
+
+```typescript
+interface ViewSyntaxTreeParams {
+    textDocument: TextDocumentIdentifier,
+}
+```
+
+**Response:** `string`
+
+Returns json representation of the file's syntax tree.
+Used to create a treeView for debugging and working on rust-analyzer itself.
+
 ## View Hir
 
 **Method:** `rust-analyzer/viewHir`
diff --git a/src/tools/rust-analyzer/docs/user/generated_config.adoc b/src/tools/rust-analyzer/docs/user/generated_config.adoc
index 5b86766aa8e..bd091db58d3 100644
--- a/src/tools/rust-analyzer/docs/user/generated_config.adoc
+++ b/src/tools/rust-analyzer/docs/user/generated_config.adoc
@@ -94,10 +94,10 @@ avoid checking unnecessary things.
 --
 Default:
 ----
-{
-  "miri": null,
-  "debug_assertions": null
-}
+[
+  "debug_assertions",
+  "miri"
+]
 ----
 List of cfg options to enable with the given values.
 
@@ -716,6 +716,11 @@ Whether to show generic type parameter name inlay hints.
 --
 Whether to show implicit drop hints.
 --
+[[rust-analyzer.inlayHints.implicitSizedBoundHints.enable]]rust-analyzer.inlayHints.implicitSizedBoundHints.enable (default: `false`)::
++
+--
+Whether to show inlay hints for the implied type parameter `Sized` bound.
+--
 [[rust-analyzer.inlayHints.lifetimeElisionHints.enable]]rust-analyzer.inlayHints.lifetimeElisionHints.enable (default: `"never"`)::
 +
 --
@@ -1046,10 +1051,25 @@ Show full signature of the callable. Only shows parameters if disabled.
 --
 Show documentation.
 --
-[[rust-analyzer.typing.excludeChars]]rust-analyzer.typing.excludeChars (default: `"|<"`)::
+[[rust-analyzer.typing.triggerChars]]rust-analyzer.typing.triggerChars (default: `"=."`)::
++
+--
+Specify the characters allowed to invoke special on typing triggers.
+- typing `=` after `let` tries to smartly add `;` if `=` is followed by an existing expression
+- typing `=` between two expressions adds `;` when in statement position
+- typing `=` to turn an assignment into an equality comparison removes `;` when in expression position
+- typing `.` in a chain method call auto-indents
+- typing `{` or `(` in front of an expression inserts a closing `}` or `)` after the expression
+- typing `{` in a use item adds a closing `}` in the right place
+- typing `>` to complete a return type `->` will insert a whitespace after it
+- typing `<` in a path or type position inserts a closing `>` after the path or type.
+--
+[[rust-analyzer.vfs.extraIncludes]]rust-analyzer.vfs.extraIncludes (default: `[]`)::
 +
 --
-Specify the characters to exclude from triggering typing assists. The default trigger characters are `.`, `=`, `<`, `>`, `{`, and `(`.
+Additional paths to include in the VFS. Generally for code that is
+generated or otherwise managed by a build system outside of Cargo,
+though Cargo might be the eventual consumer.
 --
 [[rust-analyzer.workspace.discoverConfig]]rust-analyzer.workspace.discoverConfig (default: `null`)::
 +
diff --git a/src/tools/rust-analyzer/editors/code/package.json b/src/tools/rust-analyzer/editors/code/package.json
index 80246bf3fea..8b066377f2b 100644
--- a/src/tools/rust-analyzer/editors/code/package.json
+++ b/src/tools/rust-analyzer/editors/code/package.json
@@ -109,11 +109,6 @@
         ],
         "commands": [
             {
-                "command": "rust-analyzer.syntaxTree",
-                "title": "Show Syntax Tree",
-                "category": "rust-analyzer (debug command)"
-            },
-            {
                 "command": "rust-analyzer.viewHir",
                 "title": "View Hir",
                 "category": "rust-analyzer (debug command)"
@@ -289,6 +284,30 @@
                 "category": "rust-analyzer"
             },
             {
+                "command": "rust-analyzer.syntaxTreeReveal",
+                "title": "Reveal Syntax Element",
+                "icon": "$(search)",
+                "category": "rust-analyzer (syntax tree)"
+            },
+            {
+                "command": "rust-analyzer.syntaxTreeCopy",
+                "title": "Copy",
+                "icon": "$(copy)",
+                "category": "rust-analyzer (syntax tree)"
+            },
+            {
+                "command": "rust-analyzer.syntaxTreeHideWhitespace",
+                "title": "Hide Whitespace",
+                "icon": "$(filter)",
+                "category": "rust-analyzer (syntax tree)"
+            },
+            {
+                "command": "rust-analyzer.syntaxTreeShowWhitespace",
+                "title": "Show Whitespace",
+                "icon": "$(filter-filled)",
+                "category": "rust-analyzer (syntax tree)"
+            },
+            {
                 "command": "rust-analyzer.viewMemoryLayout",
                 "title": "View Memory Layout",
                 "category": "rust-analyzer"
@@ -345,6 +364,11 @@
                         "default": true,
                         "type": "boolean"
                     },
+                    "rust-analyzer.showSyntaxTree": {
+                        "markdownDescription": "Whether to show the syntax tree view.",
+                        "default": false,
+                        "type": "boolean"
+                    },
                     "rust-analyzer.testExplorer": {
                         "markdownDescription": "Whether to show the test explorer.",
                         "default": false,
@@ -791,11 +815,14 @@
                 "properties": {
                     "rust-analyzer.cargo.cfgs": {
                         "markdownDescription": "List of cfg options to enable with the given values.",
-                        "default": {
-                            "miri": null,
-                            "debug_assertions": null
-                        },
-                        "type": "object"
+                        "default": [
+                            "debug_assertions",
+                            "miri"
+                        ],
+                        "type": "array",
+                        "items": {
+                            "type": "string"
+                        }
                     }
                 }
             },
@@ -2081,6 +2108,16 @@
             {
                 "title": "inlayHints",
                 "properties": {
+                    "rust-analyzer.inlayHints.implicitSizedBoundHints.enable": {
+                        "markdownDescription": "Whether to show inlay hints for the implied type parameter `Sized` bound.",
+                        "default": false,
+                        "type": "boolean"
+                    }
+                }
+            },
+            {
+                "title": "inlayHints",
+                "properties": {
                     "rust-analyzer.inlayHints.lifetimeElisionHints.enable": {
                         "markdownDescription": "Whether to show inlay type hints for elided lifetimes in function signatures.",
                         "default": "never",
@@ -2708,9 +2745,9 @@
             {
                 "title": "typing",
                 "properties": {
-                    "rust-analyzer.typing.excludeChars": {
-                        "markdownDescription": "Specify the characters to exclude from triggering typing assists. The default trigger characters are `.`, `=`, `<`, `>`, `{`, and `(`.",
-                        "default": "|<",
+                    "rust-analyzer.typing.triggerChars": {
+                        "markdownDescription": "Specify the characters allowed to invoke special on typing triggers.\n- typing `=` after `let` tries to smartly add `;` if `=` is followed by an existing expression\n- typing `=` between two expressions adds `;` when in statement position\n- typing `=` to turn an assignment into an equality comparison removes `;` when in expression position\n- typing `.` in a chain method call auto-indents\n- typing `{` or `(` in front of an expression inserts a closing `}` or `)` after the expression\n- typing `{` in a use item adds a closing `}` in the right place\n- typing `>` to complete a return type `->` will insert a whitespace after it\n- typing `<` in a path or type position inserts a closing `>` after the path or type.",
+                        "default": "=.",
                         "type": [
                             "null",
                             "string"
@@ -2719,6 +2756,19 @@
                 }
             },
             {
+                "title": "vfs",
+                "properties": {
+                    "rust-analyzer.vfs.extraIncludes": {
+                        "markdownDescription": "Additional paths to include in the VFS. Generally for code that is\ngenerated or otherwise managed by a build system outside of Cargo,\nthough Cargo might be the eventual consumer.",
+                        "default": [],
+                        "type": "array",
+                        "items": {
+                            "type": "string"
+                        }
+                    }
+                }
+            },
+            {
                 "title": "workspace",
                 "properties": {
                     "rust-analyzer.workspace.discoverConfig": {
@@ -2928,17 +2978,6 @@
                 "pattern": "$rustc"
             }
         ],
-        "colors": [
-            {
-                "id": "rust_analyzer.syntaxTreeBorder",
-                "description": "Color of the border displayed in the Rust source code for the selected syntax node (see \"Show Syntax Tree\" command)",
-                "defaults": {
-                    "dark": "#ffffff",
-                    "light": "#b700ff",
-                    "highContrast": "#b700ff"
-                }
-            }
-        ],
         "semanticTokenTypes": [
             {
                 "id": "angle",
@@ -3259,10 +3298,6 @@
         "menus": {
             "commandPalette": [
                 {
-                    "command": "rust-analyzer.syntaxTree",
-                    "when": "inRustProject"
-                },
-                {
                     "command": "rust-analyzer.viewHir",
                     "when": "inRustProject"
                 },
@@ -3344,6 +3379,22 @@
                 },
                 {
                     "command": "rust-analyzer.openWalkthrough"
+                },
+                {
+                    "command": "rust-analyzer.syntaxTreeReveal",
+                    "when": "false"
+                },
+                {
+                    "command": "rust-analyzer.syntaxTreeCopy",
+                    "when": "false"
+                },
+                {
+                    "command": "rust-analyzer.syntaxTreeHideWhitespace",
+                    "when": "false"
+                },
+                {
+                    "command": "rust-analyzer.syntaxTreeShowWhitespace",
+                    "when": "false"
                 }
             ],
             "editor/context": [
@@ -3357,6 +3408,30 @@
                     "when": "inRustProject && editorTextFocus && editorLangId == rust",
                     "group": "navigation@1001"
                 }
+            ],
+            "view/title": [
+                {
+                    "command": "rust-analyzer.syntaxTreeHideWhitespace",
+                    "group": "navigation",
+                    "when": "view == rustSyntaxTree && !rustSyntaxTree.hideWhitespace"
+                },
+                {
+                    "command": "rust-analyzer.syntaxTreeShowWhitespace",
+                    "group": "navigation",
+                    "when": "view == rustSyntaxTree && rustSyntaxTree.hideWhitespace"
+                }
+            ],
+            "view/item/context": [
+                {
+                    "command": "rust-analyzer.syntaxTreeCopy",
+                    "group": "inline",
+                    "when": "view == rustSyntaxTree"
+                },
+                {
+                    "command": "rust-analyzer.syntaxTreeReveal",
+                    "group": "inline",
+                    "when": "view == rustSyntaxTree"
+                }
             ]
         },
         "views": {
@@ -3366,6 +3441,22 @@
                     "name": "Rust Dependencies",
                     "when": "inRustProject && config.rust-analyzer.showDependenciesExplorer"
                 }
+            ],
+            "rustSyntaxTreeContainer": [
+                {
+                    "id": "rustSyntaxTree",
+                    "name": "Rust Syntax Tree",
+                    "when": "inRustProject && config.rust-analyzer.showSyntaxTree"
+                }
+            ]
+        },
+        "viewsContainers": {
+            "activitybar": [
+                {
+                    "id": "rustSyntaxTreeContainer",
+                    "title": "Rust Syntax Tree",
+                    "icon": "$(list-tree)"
+                }
             ]
         },
         "jsonValidation": [
diff --git a/src/tools/rust-analyzer/editors/code/src/ast_inspector.ts b/src/tools/rust-analyzer/editors/code/src/ast_inspector.ts
deleted file mode 100644
index 35b705c477e..00000000000
--- a/src/tools/rust-analyzer/editors/code/src/ast_inspector.ts
+++ /dev/null
@@ -1,216 +0,0 @@
-import * as vscode from "vscode";
-
-import type { Ctx, Disposable } from "./ctx";
-import { type RustEditor, isRustEditor, unwrapUndefinable } from "./util";
-
-// FIXME: consider implementing this via the Tree View API?
-// https://code.visualstudio.com/api/extension-guides/tree-view
-export class AstInspector implements vscode.HoverProvider, vscode.DefinitionProvider, Disposable {
-    private readonly astDecorationType = vscode.window.createTextEditorDecorationType({
-        borderColor: new vscode.ThemeColor("rust_analyzer.syntaxTreeBorder"),
-        borderStyle: "solid",
-        borderWidth: "2px",
-    });
-    private rustEditor: undefined | RustEditor;
-
-    // Lazy rust token range -> syntax tree file range.
-    private readonly rust2Ast = new Lazy(() => {
-        const astEditor = this.findAstTextEditor();
-        if (!this.rustEditor || !astEditor) return undefined;
-
-        const buf: [vscode.Range, vscode.Range][] = [];
-        for (let i = 0; i < astEditor.document.lineCount; ++i) {
-            const astLine = astEditor.document.lineAt(i);
-
-            // Heuristically look for nodes with quoted text (which are token nodes)
-            const isTokenNode = astLine.text.lastIndexOf('"') >= 0;
-            if (!isTokenNode) continue;
-
-            const rustRange = this.parseRustTextRange(this.rustEditor.document, astLine.text);
-            if (!rustRange) continue;
-
-            buf.push([rustRange, this.findAstNodeRange(astLine)]);
-        }
-        return buf;
-    });
-
-    constructor(ctx: Ctx) {
-        ctx.pushExtCleanup(
-            vscode.languages.registerHoverProvider({ scheme: "rust-analyzer" }, this),
-        );
-        ctx.pushExtCleanup(vscode.languages.registerDefinitionProvider({ language: "rust" }, this));
-        vscode.workspace.onDidCloseTextDocument(
-            this.onDidCloseTextDocument,
-            this,
-            ctx.subscriptions,
-        );
-        vscode.workspace.onDidChangeTextDocument(
-            this.onDidChangeTextDocument,
-            this,
-            ctx.subscriptions,
-        );
-        vscode.window.onDidChangeVisibleTextEditors(
-            this.onDidChangeVisibleTextEditors,
-            this,
-            ctx.subscriptions,
-        );
-    }
-    dispose() {
-        this.setRustEditor(undefined);
-    }
-
-    private onDidChangeTextDocument(event: vscode.TextDocumentChangeEvent) {
-        if (
-            this.rustEditor &&
-            event.document.uri.toString() === this.rustEditor.document.uri.toString()
-        ) {
-            this.rust2Ast.reset();
-        }
-    }
-
-    private onDidCloseTextDocument(doc: vscode.TextDocument) {
-        if (this.rustEditor && doc.uri.toString() === this.rustEditor.document.uri.toString()) {
-            this.setRustEditor(undefined);
-        }
-    }
-
-    private onDidChangeVisibleTextEditors(editors: readonly vscode.TextEditor[]) {
-        if (!this.findAstTextEditor()) {
-            this.setRustEditor(undefined);
-            return;
-        }
-        this.setRustEditor(editors.find(isRustEditor));
-    }
-
-    private findAstTextEditor(): undefined | vscode.TextEditor {
-        return vscode.window.visibleTextEditors.find(
-            (it) => it.document.uri.scheme === "rust-analyzer",
-        );
-    }
-
-    private setRustEditor(newRustEditor: undefined | RustEditor) {
-        if (this.rustEditor && this.rustEditor !== newRustEditor) {
-            this.rustEditor.setDecorations(this.astDecorationType, []);
-            this.rust2Ast.reset();
-        }
-        this.rustEditor = newRustEditor;
-    }
-
-    // additional positional params are omitted
-    provideDefinition(
-        doc: vscode.TextDocument,
-        pos: vscode.Position,
-    ): vscode.ProviderResult<vscode.DefinitionLink[]> {
-        if (!this.rustEditor || doc.uri.toString() !== this.rustEditor.document.uri.toString()) {
-            return;
-        }
-
-        const astEditor = this.findAstTextEditor();
-        if (!astEditor) return;
-
-        const rust2AstRanges = this.rust2Ast
-            .get()
-            ?.find(([rustRange, _]) => rustRange.contains(pos));
-        if (!rust2AstRanges) return;
-
-        const [rustFileRange, astFileRange] = rust2AstRanges;
-
-        astEditor.revealRange(astFileRange);
-        astEditor.selection = new vscode.Selection(astFileRange.start, astFileRange.end);
-
-        return [
-            {
-                targetRange: astFileRange,
-                targetUri: astEditor.document.uri,
-                originSelectionRange: rustFileRange,
-                targetSelectionRange: astFileRange,
-            },
-        ];
-    }
-
-    // additional positional params are omitted
-    provideHover(
-        doc: vscode.TextDocument,
-        hoverPosition: vscode.Position,
-    ): vscode.ProviderResult<vscode.Hover> {
-        if (!this.rustEditor) return;
-
-        const astFileLine = doc.lineAt(hoverPosition.line);
-
-        const rustFileRange = this.parseRustTextRange(this.rustEditor.document, astFileLine.text);
-        if (!rustFileRange) return;
-
-        this.rustEditor.setDecorations(this.astDecorationType, [rustFileRange]);
-        this.rustEditor.revealRange(rustFileRange);
-
-        const rustSourceCode = this.rustEditor.document.getText(rustFileRange);
-        const astFileRange = this.findAstNodeRange(astFileLine);
-
-        return new vscode.Hover(["```rust\n" + rustSourceCode + "\n```"], astFileRange);
-    }
-
-    private findAstNodeRange(astLine: vscode.TextLine): vscode.Range {
-        const lineOffset = astLine.range.start;
-        const begin = lineOffset.translate(undefined, astLine.firstNonWhitespaceCharacterIndex);
-        const end = lineOffset.translate(undefined, astLine.text.trimEnd().length);
-        return new vscode.Range(begin, end);
-    }
-
-    private parseRustTextRange(
-        doc: vscode.TextDocument,
-        astLine: string,
-    ): undefined | vscode.Range {
-        const parsedRange = /(\d+)\.\.(\d+)/.exec(astLine);
-        if (!parsedRange) return;
-
-        const [begin, end] = parsedRange.slice(1).map((off) => this.positionAt(doc, +off));
-        const actualBegin = unwrapUndefinable(begin);
-        const actualEnd = unwrapUndefinable(end);
-        return new vscode.Range(actualBegin, actualEnd);
-    }
-
-    // Memoize the last value, otherwise the CPU is at 100% single core
-    // with quadratic lookups when we build rust2Ast cache
-    cache?: { doc: vscode.TextDocument; offset: number; line: number };
-
-    positionAt(doc: vscode.TextDocument, targetOffset: number): vscode.Position {
-        if (doc.eol === vscode.EndOfLine.LF) {
-            return doc.positionAt(targetOffset);
-        }
-
-        // Dirty workaround for crlf line endings
-        // We are still in this prehistoric era of carriage returns here...
-
-        let line = 0;
-        let offset = 0;
-
-        const cache = this.cache;
-        if (cache?.doc === doc && cache.offset <= targetOffset) {
-            ({ line, offset } = cache);
-        }
-
-        while (true) {
-            const lineLenWithLf = doc.lineAt(line).text.length + 1;
-            if (offset + lineLenWithLf > targetOffset) {
-                this.cache = { doc, offset, line };
-                return doc.positionAt(targetOffset + line);
-            }
-            offset += lineLenWithLf;
-            line += 1;
-        }
-    }
-}
-
-class Lazy<T> {
-    val: undefined | T;
-
-    constructor(private readonly compute: () => undefined | T) {}
-
-    get() {
-        return this.val ?? (this.val = this.compute());
-    }
-
-    reset() {
-        this.val = undefined;
-    }
-}
diff --git a/src/tools/rust-analyzer/editors/code/src/commands.ts b/src/tools/rust-analyzer/editors/code/src/commands.ts
index 73e39c900e7..b3aa04af7ed 100644
--- a/src/tools/rust-analyzer/editors/code/src/commands.ts
+++ b/src/tools/rust-analyzer/editors/code/src/commands.ts
@@ -15,7 +15,6 @@ import {
     createTaskFromRunnable,
     createCargoArgs,
 } from "./run";
-import { AstInspector } from "./ast_inspector";
 import {
     isRustDocument,
     isCargoRunnableArgs,
@@ -31,8 +30,8 @@ import type { LanguageClient } from "vscode-languageclient/node";
 import { HOVER_REFERENCE_COMMAND } from "./client";
 import type { DependencyId } from "./dependencies_provider";
 import { log } from "./util";
+import type { SyntaxElement } from "./syntax_tree_provider";
 
-export * from "./ast_inspector";
 export * from "./run";
 
 export function analyzerStatus(ctx: CtxInit): Cmd {
@@ -288,13 +287,13 @@ export function openCargoToml(ctx: CtxInit): Cmd {
 
 export function revealDependency(ctx: CtxInit): Cmd {
     return async (editor: RustEditor) => {
-        if (!ctx.dependencies?.isInitialized()) {
+        if (!ctx.dependenciesProvider?.isInitialized()) {
             return;
         }
         const documentPath = editor.document.uri.fsPath;
-        const dep = ctx.dependencies?.getDependency(documentPath);
+        const dep = ctx.dependenciesProvider?.getDependency(documentPath);
         if (dep) {
-            await ctx.treeView?.reveal(dep, { select: true, expand: true });
+            await ctx.dependencyTreeView?.reveal(dep, { select: true, expand: true });
         } else {
             await revealParentChain(editor.document, ctx);
         }
@@ -340,10 +339,10 @@ async function revealParentChain(document: RustDocument, ctx: CtxInit) {
             // a open file referencing the old version
             return;
         }
-    } while (!ctx.dependencies?.contains(documentPath));
+    } while (!ctx.dependenciesProvider?.contains(documentPath));
     parentChain.reverse();
     for (const idx in parentChain) {
-        const treeView = ctx.treeView;
+        const treeView = ctx.dependencyTreeView;
         if (!treeView) {
             continue;
         }
@@ -357,6 +356,77 @@ export async function execRevealDependency(e: RustEditor): Promise<void> {
     await vscode.commands.executeCommand("rust-analyzer.revealDependency", e);
 }
 
+export function syntaxTreeReveal(): Cmd {
+    return async (element: SyntaxElement) => {
+        const activeEditor = vscode.window.activeTextEditor;
+
+        if (activeEditor !== undefined) {
+            const start = activeEditor.document.positionAt(element.start);
+            const end = activeEditor.document.positionAt(element.end);
+
+            const newSelection = new vscode.Selection(start, end);
+
+            activeEditor.selection = newSelection;
+            activeEditor.revealRange(newSelection);
+        }
+    };
+}
+
+function elementToString(
+    activeDocument: vscode.TextDocument,
+    element: SyntaxElement,
+    depth: number = 0,
+): string {
+    let result = "  ".repeat(depth);
+    const start = element.istart ?? element.start;
+    const end = element.iend ?? element.end;
+
+    result += `${element.kind}@${start}..${end}`;
+
+    if (element.type === "Token") {
+        const startPosition = activeDocument.positionAt(element.start);
+        const endPosition = activeDocument.positionAt(element.end);
+        const text = activeDocument.getText(new vscode.Range(startPosition, endPosition));
+        // JSON.stringify quotes and escapes the string for us.
+        result += ` ${JSON.stringify(text)}\n`;
+    } else {
+        result += "\n";
+        for (const child of element.children) {
+            result += elementToString(activeDocument, child, depth + 1);
+        }
+    }
+
+    return result;
+}
+
+export function syntaxTreeCopy(): Cmd {
+    return async (element: SyntaxElement) => {
+        const activeDocument = vscode.window.activeTextEditor?.document;
+        if (!activeDocument) {
+            return;
+        }
+
+        const result = elementToString(activeDocument, element);
+        await vscode.env.clipboard.writeText(result);
+    };
+}
+
+export function syntaxTreeHideWhitespace(ctx: CtxInit): Cmd {
+    return async () => {
+        if (ctx.syntaxTreeProvider !== undefined) {
+            await ctx.syntaxTreeProvider.toggleWhitespace();
+        }
+    };
+}
+
+export function syntaxTreeShowWhitespace(ctx: CtxInit): Cmd {
+    return async () => {
+        if (ctx.syntaxTreeProvider !== undefined) {
+            await ctx.syntaxTreeProvider.toggleWhitespace();
+        }
+    };
+}
+
 export function ssr(ctx: CtxInit): Cmd {
     return async () => {
         const editor = vscode.window.activeTextEditor;
@@ -426,89 +496,6 @@ export function serverVersion(ctx: CtxInit): Cmd {
     };
 }
 
-// Opens the virtual file that will show the syntax tree
-//
-// The contents of the file come from the `TextDocumentContentProvider`
-export function syntaxTree(ctx: CtxInit): Cmd {
-    const tdcp = new (class implements vscode.TextDocumentContentProvider {
-        readonly uri = vscode.Uri.parse("rust-analyzer-syntax-tree://syntaxtree/tree.rast");
-        readonly eventEmitter = new vscode.EventEmitter<vscode.Uri>();
-        constructor() {
-            vscode.workspace.onDidChangeTextDocument(
-                this.onDidChangeTextDocument,
-                this,
-                ctx.subscriptions,
-            );
-            vscode.window.onDidChangeActiveTextEditor(
-                this.onDidChangeActiveTextEditor,
-                this,
-                ctx.subscriptions,
-            );
-        }
-
-        private onDidChangeTextDocument(event: vscode.TextDocumentChangeEvent) {
-            if (isRustDocument(event.document)) {
-                // We need to order this after language server updates, but there's no API for that.
-                // Hence, good old sleep().
-                void sleep(10).then(() => this.eventEmitter.fire(this.uri));
-            }
-        }
-        private onDidChangeActiveTextEditor(editor: vscode.TextEditor | undefined) {
-            if (editor && isRustEditor(editor)) {
-                this.eventEmitter.fire(this.uri);
-            }
-        }
-
-        async provideTextDocumentContent(
-            uri: vscode.Uri,
-            ct: vscode.CancellationToken,
-        ): Promise<string> {
-            const rustEditor = ctx.activeRustEditor;
-            if (!rustEditor) return "";
-            const client = ctx.client;
-
-            // When the range based query is enabled we take the range of the selection
-            const range =
-                uri.query === "range=true" && !rustEditor.selection.isEmpty
-                    ? client.code2ProtocolConverter.asRange(rustEditor.selection)
-                    : null;
-
-            const params = { textDocument: { uri: rustEditor.document.uri.toString() }, range };
-            return client.sendRequest(ra.syntaxTree, params, ct);
-        }
-
-        get onDidChange(): vscode.Event<vscode.Uri> {
-            return this.eventEmitter.event;
-        }
-    })();
-
-    ctx.pushExtCleanup(new AstInspector(ctx));
-    ctx.pushExtCleanup(
-        vscode.workspace.registerTextDocumentContentProvider("rust-analyzer-syntax-tree", tdcp),
-    );
-    ctx.pushExtCleanup(
-        vscode.languages.setLanguageConfiguration("ra_syntax_tree", {
-            brackets: [["[", ")"]],
-        }),
-    );
-
-    return async () => {
-        const editor = vscode.window.activeTextEditor;
-        const rangeEnabled = !!editor && !editor.selection.isEmpty;
-
-        const uri = rangeEnabled ? vscode.Uri.parse(`${tdcp.uri.toString()}?range=true`) : tdcp.uri;
-
-        const document = await vscode.workspace.openTextDocument(uri);
-
-        tdcp.eventEmitter.fire(uri);
-
-        void (await vscode.window.showTextDocument(document, {
-            viewColumn: vscode.ViewColumn.Two,
-            preserveFocus: true,
-        }));
-    };
-}
-
 function viewHirOrMir(ctx: CtxInit, xir: "hir" | "mir"): Cmd {
     const viewXir = xir === "hir" ? "viewHir" : "viewMir";
     const requestType = xir === "hir" ? ra.viewHir : ra.viewMir;
diff --git a/src/tools/rust-analyzer/editors/code/src/config.ts b/src/tools/rust-analyzer/editors/code/src/config.ts
index 720c473c5b4..d1467a4e824 100644
--- a/src/tools/rust-analyzer/editors/code/src/config.ts
+++ b/src/tools/rust-analyzer/editors/code/src/config.ts
@@ -351,6 +351,10 @@ export class Config {
         return this.get<boolean>("showDependenciesExplorer");
     }
 
+    get showSyntaxTree() {
+        return this.get<boolean>("showSyntaxTree");
+    }
+
     get statusBarClickAction() {
         return this.get<string>("statusBar.clickAction");
     }
diff --git a/src/tools/rust-analyzer/editors/code/src/ctx.ts b/src/tools/rust-analyzer/editors/code/src/ctx.ts
index 37a54abf71f..5550bfa6558 100644
--- a/src/tools/rust-analyzer/editors/code/src/ctx.ts
+++ b/src/tools/rust-analyzer/editors/code/src/ctx.ts
@@ -19,6 +19,7 @@ import {
     RustDependenciesProvider,
     type DependencyId,
 } from "./dependencies_provider";
+import { SyntaxTreeProvider, type SyntaxElement } from "./syntax_tree_provider";
 import { execRevealDependency } from "./commands";
 import { PersistentState } from "./persistent_state";
 import { bootstrap } from "./bootstrap";
@@ -84,8 +85,12 @@ export class Ctx implements RustAnalyzerExtensionApi {
     private commandFactories: Record<string, CommandFactory>;
     private commandDisposables: Disposable[];
     private unlinkedFiles: vscode.Uri[];
-    private _dependencies: RustDependenciesProvider | undefined;
-    private _treeView: vscode.TreeView<Dependency | DependencyFile | DependencyId> | undefined;
+    private _dependenciesProvider: RustDependenciesProvider | undefined;
+    private _dependencyTreeView:
+        | vscode.TreeView<Dependency | DependencyFile | DependencyId>
+        | undefined;
+    private _syntaxTreeProvider: SyntaxTreeProvider | undefined;
+    private _syntaxTreeView: vscode.TreeView<SyntaxElement> | undefined;
     private lastStatus: ServerStatusParams | { health: "stopped" } = { health: "stopped" };
     private _serverVersion: string;
     private statusBarActiveEditorListener: Disposable;
@@ -102,12 +107,20 @@ export class Ctx implements RustAnalyzerExtensionApi {
         return this._client;
     }
 
-    get treeView() {
-        return this._treeView;
+    get dependencyTreeView() {
+        return this._dependencyTreeView;
     }
 
-    get dependencies() {
-        return this._dependencies;
+    get dependenciesProvider() {
+        return this._dependenciesProvider;
+    }
+
+    get syntaxTreeView() {
+        return this._syntaxTreeView;
+    }
+
+    get syntaxTreeProvider() {
+        return this._syntaxTreeProvider;
     }
 
     constructor(
@@ -278,6 +291,9 @@ export class Ctx implements RustAnalyzerExtensionApi {
         if (this.config.showDependenciesExplorer) {
             this.prepareTreeDependenciesView(client);
         }
+        if (this.config.showSyntaxTree) {
+            this.prepareSyntaxTreeView(client);
+        }
     }
 
     private prepareTreeDependenciesView(client: lc.LanguageClient) {
@@ -285,13 +301,13 @@ export class Ctx implements RustAnalyzerExtensionApi {
             ...this,
             client: client,
         };
-        this._dependencies = new RustDependenciesProvider(ctxInit);
-        this._treeView = vscode.window.createTreeView("rustDependencies", {
-            treeDataProvider: this._dependencies,
+        this._dependenciesProvider = new RustDependenciesProvider(ctxInit);
+        this._dependencyTreeView = vscode.window.createTreeView("rustDependencies", {
+            treeDataProvider: this._dependenciesProvider,
             showCollapseAll: true,
         });
 
-        this.pushExtCleanup(this._treeView);
+        this.pushExtCleanup(this._dependencyTreeView);
         vscode.window.onDidChangeActiveTextEditor(async (e) => {
             // we should skip documents that belong to the current workspace
             if (this.shouldRevealDependency(e)) {
@@ -303,7 +319,7 @@ export class Ctx implements RustAnalyzerExtensionApi {
             }
         });
 
-        this.treeView?.onDidChangeVisibility(async (e) => {
+        this.dependencyTreeView?.onDidChangeVisibility(async (e) => {
             if (e.visible) {
                 const activeEditor = vscode.window.activeTextEditor;
                 if (this.shouldRevealDependency(activeEditor)) {
@@ -322,10 +338,60 @@ export class Ctx implements RustAnalyzerExtensionApi {
             e !== undefined &&
             isRustEditor(e) &&
             !isDocumentInWorkspace(e.document) &&
-            (this.treeView?.visible || false)
+            (this.dependencyTreeView?.visible || false)
         );
     }
 
+    private prepareSyntaxTreeView(client: lc.LanguageClient) {
+        const ctxInit: CtxInit = {
+            ...this,
+            client: client,
+        };
+        this._syntaxTreeProvider = new SyntaxTreeProvider(ctxInit);
+        this._syntaxTreeView = vscode.window.createTreeView("rustSyntaxTree", {
+            treeDataProvider: this._syntaxTreeProvider,
+            showCollapseAll: true,
+        });
+
+        this.pushExtCleanup(this._syntaxTreeView);
+
+        vscode.window.onDidChangeActiveTextEditor(async () => {
+            if (this.syntaxTreeView?.visible) {
+                await this.syntaxTreeProvider?.refresh();
+            }
+        });
+
+        vscode.workspace.onDidChangeTextDocument(async () => {
+            if (this.syntaxTreeView?.visible) {
+                await this.syntaxTreeProvider?.refresh();
+            }
+        });
+
+        vscode.window.onDidChangeTextEditorSelection(async (e) => {
+            if (!this.syntaxTreeView?.visible || !isRustEditor(e.textEditor)) {
+                return;
+            }
+
+            const selection = e.selections[0];
+            if (selection === undefined) {
+                return;
+            }
+
+            const start = e.textEditor.document.offsetAt(selection.start);
+            const end = e.textEditor.document.offsetAt(selection.end);
+            const result = this.syntaxTreeProvider?.getElementByRange(start, end);
+            if (result !== undefined) {
+                await this.syntaxTreeView?.reveal(result);
+            }
+        });
+
+        this._syntaxTreeView.onDidChangeVisibility(async (e) => {
+            if (e.visible) {
+                await this.syntaxTreeProvider?.refresh();
+            }
+        });
+    }
+
     async restart() {
         // FIXME: We should re-use the client, that is ctx.deactivate() if none of the configs have changed
         await this.stopAndDispose();
@@ -423,7 +489,8 @@ export class Ctx implements RustAnalyzerExtensionApi {
                 } else {
                     statusBar.command = "rust-analyzer.openLogs";
                 }
-                this.dependencies?.refresh();
+                this.dependenciesProvider?.refresh();
+                void this.syntaxTreeProvider?.refresh();
                 break;
             case "warning":
                 statusBar.color = new vscode.ThemeColor("statusBarItem.warningForeground");
diff --git a/src/tools/rust-analyzer/editors/code/src/lsp_ext.ts b/src/tools/rust-analyzer/editors/code/src/lsp_ext.ts
index d52e314e219..af86d9efd14 100644
--- a/src/tools/rust-analyzer/editors/code/src/lsp_ext.ts
+++ b/src/tools/rust-analyzer/editors/code/src/lsp_ext.ts
@@ -48,6 +48,9 @@ export const runFlycheck = new lc.NotificationType<{
 export const syntaxTree = new lc.RequestType<SyntaxTreeParams, string, void>(
     "rust-analyzer/syntaxTree",
 );
+export const viewSyntaxTree = new lc.RequestType<ViewSyntaxTreeParams, string, void>(
+    "rust-analyzer/viewSyntaxTree",
+);
 export const viewCrateGraph = new lc.RequestType<ViewCrateGraphParams, string, void>(
     "rust-analyzer/viewCrateGraph",
 );
@@ -157,6 +160,7 @@ export type SyntaxTreeParams = {
     textDocument: lc.TextDocumentIdentifier;
     range: lc.Range | null;
 };
+export type ViewSyntaxTreeParams = { textDocument: lc.TextDocumentIdentifier };
 export type ViewCrateGraphParams = { full: boolean };
 export type ViewItemTreeParams = { textDocument: lc.TextDocumentIdentifier };
 
diff --git a/src/tools/rust-analyzer/editors/code/src/main.ts b/src/tools/rust-analyzer/editors/code/src/main.ts
index fdf43f66f94..c84b69b66cd 100644
--- a/src/tools/rust-analyzer/editors/code/src/main.ts
+++ b/src/tools/rust-analyzer/editors/code/src/main.ts
@@ -158,7 +158,6 @@ function createCommands(): Record<string, CommandFactory> {
         matchingBrace: { enabled: commands.matchingBrace },
         joinLines: { enabled: commands.joinLines },
         parentModule: { enabled: commands.parentModule },
-        syntaxTree: { enabled: commands.syntaxTree },
         viewHir: { enabled: commands.viewHir },
         viewMir: { enabled: commands.viewMir },
         interpretFunction: { enabled: commands.interpretFunction },
@@ -199,6 +198,10 @@ function createCommands(): Record<string, CommandFactory> {
         rename: { enabled: commands.rename },
         openLogs: { enabled: commands.openLogs },
         revealDependency: { enabled: commands.revealDependency },
+        syntaxTreeReveal: { enabled: commands.syntaxTreeReveal },
+        syntaxTreeCopy: { enabled: commands.syntaxTreeCopy },
+        syntaxTreeHideWhitespace: { enabled: commands.syntaxTreeHideWhitespace },
+        syntaxTreeShowWhitespace: { enabled: commands.syntaxTreeShowWhitespace },
     };
 }
 
diff --git a/src/tools/rust-analyzer/editors/code/src/syntax_tree_provider.ts b/src/tools/rust-analyzer/editors/code/src/syntax_tree_provider.ts
new file mode 100644
index 00000000000..c7e8007e838
--- /dev/null
+++ b/src/tools/rust-analyzer/editors/code/src/syntax_tree_provider.ts
@@ -0,0 +1,301 @@
+import * as vscode from "vscode";
+
+import { isRustEditor, setContextValue } from "./util";
+import type { CtxInit } from "./ctx";
+import * as ra from "./lsp_ext";
+
+export class SyntaxTreeProvider implements vscode.TreeDataProvider<SyntaxElement> {
+    private _onDidChangeTreeData: vscode.EventEmitter<SyntaxElement | undefined | void> =
+        new vscode.EventEmitter<SyntaxElement | undefined | void>();
+    readonly onDidChangeTreeData: vscode.Event<SyntaxElement | undefined | void> =
+        this._onDidChangeTreeData.event;
+    ctx: CtxInit;
+    root: SyntaxNode | undefined;
+    hideWhitespace: boolean = false;
+
+    constructor(ctx: CtxInit) {
+        this.ctx = ctx;
+    }
+
+    getTreeItem(element: SyntaxElement): vscode.TreeItem {
+        return new SyntaxTreeItem(element);
+    }
+
+    getChildren(element?: SyntaxElement): vscode.ProviderResult<SyntaxElement[]> {
+        return this.getRawChildren(element);
+    }
+
+    getParent(element: SyntaxElement): vscode.ProviderResult<SyntaxElement> {
+        return element.parent;
+    }
+
+    resolveTreeItem(
+        item: SyntaxTreeItem,
+        element: SyntaxElement,
+        _token: vscode.CancellationToken,
+    ): vscode.ProviderResult<SyntaxTreeItem> {
+        const editor = vscode.window.activeTextEditor;
+
+        if (editor !== undefined) {
+            const start = editor.document.positionAt(element.start);
+            const end = editor.document.positionAt(element.end);
+            const range = new vscode.Range(start, end);
+
+            const text = editor.document.getText(range);
+            item.tooltip = new vscode.MarkdownString().appendCodeblock(text, "rust");
+        }
+
+        return item;
+    }
+
+    private getRawChildren(element?: SyntaxElement): SyntaxElement[] {
+        if (element?.type === "Node") {
+            if (this.hideWhitespace) {
+                return element.children.filter((e) => e.kind !== "WHITESPACE");
+            }
+
+            return element.children;
+        }
+
+        if (element?.type === "Token") {
+            return [];
+        }
+
+        if (element === undefined && this.root !== undefined) {
+            return [this.root];
+        }
+
+        return [];
+    }
+
+    async refresh(): Promise<void> {
+        const editor = vscode.window.activeTextEditor;
+
+        if (editor && isRustEditor(editor)) {
+            const params = { textDocument: { uri: editor.document.uri.toString() }, range: null };
+            const fileText = await this.ctx.client.sendRequest(ra.viewSyntaxTree, params);
+            this.root = JSON.parse(fileText, (_key, value: SyntaxElement) => {
+                if (value.type === "Node") {
+                    for (const child of value.children) {
+                        child.parent = value;
+                    }
+                }
+
+                return value;
+            });
+        } else {
+            this.root = undefined;
+        }
+
+        this._onDidChangeTreeData.fire();
+    }
+
+    getElementByRange(start: number, end: number): SyntaxElement | undefined {
+        if (this.root === undefined) {
+            return undefined;
+        }
+
+        let result: SyntaxElement = this.root;
+
+        if (this.root.start === start && this.root.end === end) {
+            return result;
+        }
+
+        let children = this.getRawChildren(this.root);
+
+        outer: while (true) {
+            for (const child of children) {
+                if (child.start <= start && child.end >= end) {
+                    result = child;
+                    if (start === end && start === child.end) {
+                        // When the cursor is on the very end of a token,
+                        // we assume the user wants the next token instead.
+                        continue;
+                    }
+
+                    if (child.type === "Token") {
+                        return result;
+                    } else {
+                        children = this.getRawChildren(child);
+                        continue outer;
+                    }
+                }
+            }
+
+            return result;
+        }
+    }
+
+    async toggleWhitespace() {
+        this.hideWhitespace = !this.hideWhitespace;
+        this._onDidChangeTreeData.fire();
+        await setContextValue("rustSyntaxTree.hideWhitespace", this.hideWhitespace);
+    }
+}
+
+export type SyntaxNode = {
+    type: "Node";
+    kind: string;
+    start: number;
+    end: number;
+    istart?: number;
+    iend?: number;
+    children: SyntaxElement[];
+    parent?: SyntaxElement;
+};
+
+type SyntaxToken = {
+    type: "Token";
+    kind: string;
+    start: number;
+    end: number;
+    istart?: number;
+    iend?: number;
+    parent?: SyntaxElement;
+};
+
+export type SyntaxElement = SyntaxNode | SyntaxToken;
+
+export class SyntaxTreeItem extends vscode.TreeItem {
+    constructor(private readonly element: SyntaxElement) {
+        super(element.kind);
+        const icon = getIcon(element.kind);
+        if (element.type === "Node") {
+            this.contextValue = "syntaxNode";
+            this.iconPath = icon ?? new vscode.ThemeIcon("list-tree");
+            this.collapsibleState = vscode.TreeItemCollapsibleState.Expanded;
+        } else {
+            this.contextValue = "syntaxToken";
+            this.iconPath = icon ?? new vscode.ThemeIcon("symbol-string");
+            this.collapsibleState = vscode.TreeItemCollapsibleState.None;
+        }
+
+        if (element.istart !== undefined && element.iend !== undefined) {
+            this.description = `${this.element.istart}..${this.element.iend}`;
+        } else {
+            this.description = `${this.element.start}..${this.element.end}`;
+        }
+    }
+}
+
+function getIcon(kind: string): vscode.ThemeIcon | undefined {
+    const icon = iconTable[kind];
+
+    if (icon !== undefined) {
+        return icon;
+    }
+
+    if (kind.endsWith("_KW")) {
+        return new vscode.ThemeIcon(
+            "symbol-keyword",
+            new vscode.ThemeColor("symbolIcon.keywordForeground"),
+        );
+    }
+
+    if (operators.includes(kind)) {
+        return new vscode.ThemeIcon(
+            "symbol-operator",
+            new vscode.ThemeColor("symbolIcon.operatorForeground"),
+        );
+    }
+
+    return undefined;
+}
+
+const iconTable: Record<string, vscode.ThemeIcon> = {
+    CALL_EXPR: new vscode.ThemeIcon("call-outgoing"),
+    COMMENT: new vscode.ThemeIcon("comment"),
+    ENUM: new vscode.ThemeIcon("symbol-enum", new vscode.ThemeColor("symbolIcon.enumForeground")),
+    FN: new vscode.ThemeIcon(
+        "symbol-function",
+        new vscode.ThemeColor("symbolIcon.functionForeground"),
+    ),
+    FLOAT_NUMBER: new vscode.ThemeIcon(
+        "symbol-number",
+        new vscode.ThemeColor("symbolIcon.numberForeground"),
+    ),
+    INDEX_EXPR: new vscode.ThemeIcon(
+        "symbol-array",
+        new vscode.ThemeColor("symbolIcon.arrayForeground"),
+    ),
+    INT_NUMBER: new vscode.ThemeIcon(
+        "symbol-number",
+        new vscode.ThemeColor("symbolIcon.numberForeground"),
+    ),
+    LITERAL: new vscode.ThemeIcon(
+        "symbol-misc",
+        new vscode.ThemeColor("symbolIcon.miscForeground"),
+    ),
+    MODULE: new vscode.ThemeIcon(
+        "symbol-module",
+        new vscode.ThemeColor("symbolIcon.moduleForeground"),
+    ),
+    METHOD_CALL_EXPR: new vscode.ThemeIcon("call-outgoing"),
+    PARAM: new vscode.ThemeIcon(
+        "symbol-parameter",
+        new vscode.ThemeColor("symbolIcon.parameterForeground"),
+    ),
+    RECORD_FIELD: new vscode.ThemeIcon(
+        "symbol-field",
+        new vscode.ThemeColor("symbolIcon.fieldForeground"),
+    ),
+    SOURCE_FILE: new vscode.ThemeIcon("file-code"),
+    STRING: new vscode.ThemeIcon("quote"),
+    STRUCT: new vscode.ThemeIcon(
+        "symbol-struct",
+        new vscode.ThemeColor("symbolIcon.structForeground"),
+    ),
+    TRAIT: new vscode.ThemeIcon(
+        "symbol-interface",
+        new vscode.ThemeColor("symbolIcon.interfaceForeground"),
+    ),
+    TYPE_PARAM: new vscode.ThemeIcon(
+        "symbol-type-parameter",
+        new vscode.ThemeColor("symbolIcon.typeParameterForeground"),
+    ),
+    VARIANT: new vscode.ThemeIcon(
+        "symbol-enum-member",
+        new vscode.ThemeColor("symbolIcon.enumMemberForeground"),
+    ),
+    WHITESPACE: new vscode.ThemeIcon("whitespace"),
+};
+
+const operators = [
+    "PLUS",
+    "PLUSEQ",
+    "MINUS",
+    "MINUSEQ",
+    "STAR",
+    "STAREQ",
+    "SLASH",
+    "SLASHEQ",
+    "PERCENT",
+    "PERCENTEQ",
+    "CARET",
+    "CARETEQ",
+    "AMP",
+    "AMPEQ",
+    "AMP2",
+    "PIPE",
+    "PIPEEQ",
+    "PIPE2",
+    "SHL",
+    "SHLEQ",
+    "SHR",
+    "SHREQ",
+    "EQ",
+    "EQ2",
+    "BANG",
+    "NEQ",
+    "L_ANGLE",
+    "LTEQ",
+    "R_ANGLE",
+    "GTEQ",
+    "COLON2",
+    "THIN_ARROW",
+    "FAT_ARROW",
+    "DOT",
+    "DOT2",
+    "DOT2EQ",
+    "AT",
+];
diff --git a/src/tools/rust-analyzer/lib/lsp-server/src/msg.rs b/src/tools/rust-analyzer/lib/lsp-server/src/msg.rs
index 11f98f50790..074bc43388a 100644
--- a/src/tools/rust-analyzer/lib/lsp-server/src/msg.rs
+++ b/src/tools/rust-analyzer/lib/lsp-server/src/msg.rs
@@ -181,15 +181,15 @@ impl Message {
 
         Ok(Some(msg))
     }
-    pub fn write(self, w: &mut impl Write) -> io::Result<()> {
+    pub fn write(&self, w: &mut impl Write) -> io::Result<()> {
         self._write(w)
     }
-    fn _write(self, w: &mut dyn Write) -> io::Result<()> {
+    fn _write(&self, w: &mut dyn Write) -> io::Result<()> {
         #[derive(Serialize)]
-        struct JsonRpc {
+        struct JsonRpc<'a> {
             jsonrpc: &'static str,
             #[serde(flatten)]
-            msg: Message,
+            msg: &'a Message,
         }
         let text = serde_json::to_string(&JsonRpc { jsonrpc: "2.0", msg: self })?;
         write_msg_text(w, &text)
diff --git a/src/tools/rust-analyzer/lib/lsp-server/src/socket.rs b/src/tools/rust-analyzer/lib/lsp-server/src/socket.rs
index 36d728456f7..48400abf229 100644
--- a/src/tools/rust-analyzer/lib/lsp-server/src/socket.rs
+++ b/src/tools/rust-analyzer/lib/lsp-server/src/socket.rs
@@ -15,8 +15,11 @@ pub(crate) fn socket_transport(
     stream: TcpStream,
 ) -> (Sender<Message>, Receiver<Message>, IoThreads) {
     let (reader_receiver, reader) = make_reader(stream.try_clone().unwrap());
-    let (writer_sender, writer) = make_write(stream);
-    let io_threads = make_io_threads(reader, writer);
+    let (writer_sender, writer, messages_to_drop) = make_write(stream);
+    let dropper = std::thread::spawn(move || {
+        messages_to_drop.into_iter().for_each(drop);
+    });
+    let io_threads = make_io_threads(reader, writer, dropper);
     (writer_sender, reader_receiver, io_threads)
 }
 
@@ -36,11 +39,21 @@ fn make_reader(stream: TcpStream) -> (Receiver<Message>, thread::JoinHandle<io::
     (reader_receiver, reader)
 }
 
-fn make_write(mut stream: TcpStream) -> (Sender<Message>, thread::JoinHandle<io::Result<()>>) {
+fn make_write(
+    mut stream: TcpStream,
+) -> (Sender<Message>, thread::JoinHandle<io::Result<()>>, Receiver<Message>) {
     let (writer_sender, writer_receiver) = bounded::<Message>(0);
+    let (drop_sender, drop_receiver) = bounded::<Message>(0);
     let writer = thread::spawn(move || {
-        writer_receiver.into_iter().try_for_each(|it| it.write(&mut stream)).unwrap();
+        writer_receiver
+            .into_iter()
+            .try_for_each(|it| {
+                let result = it.write(&mut stream);
+                let _ = drop_sender.send(it);
+                result
+            })
+            .unwrap();
         Ok(())
     });
-    (writer_sender, writer)
+    (writer_sender, writer, drop_receiver)
 }
diff --git a/src/tools/rust-analyzer/lib/lsp-server/src/stdio.rs b/src/tools/rust-analyzer/lib/lsp-server/src/stdio.rs
index 279a6bce080..8344c9f56b5 100644
--- a/src/tools/rust-analyzer/lib/lsp-server/src/stdio.rs
+++ b/src/tools/rust-analyzer/lib/lsp-server/src/stdio.rs
@@ -11,15 +11,24 @@ use crate::Message;
 
 /// Creates an LSP connection via stdio.
 pub(crate) fn stdio_transport() -> (Sender<Message>, Receiver<Message>, IoThreads) {
+    let (drop_sender, drop_receiver) = bounded::<Message>(0);
     let (writer_sender, writer_receiver) = bounded::<Message>(0);
     let writer = thread::Builder::new()
         .name("LspServerWriter".to_owned())
         .spawn(move || {
             let stdout = stdout();
             let mut stdout = stdout.lock();
-            writer_receiver.into_iter().try_for_each(|it| it.write(&mut stdout))
+            writer_receiver.into_iter().try_for_each(|it| {
+                let result = it.write(&mut stdout);
+                let _ = drop_sender.send(it);
+                result
+            })
         })
         .unwrap();
+    let dropper = thread::Builder::new()
+        .name("LspMessageDropper".to_owned())
+        .spawn(move || drop_receiver.into_iter().for_each(drop))
+        .unwrap();
     let (reader_sender, reader_receiver) = bounded::<Message>(0);
     let reader = thread::Builder::new()
         .name("LspServerReader".to_owned())
@@ -41,7 +50,7 @@ pub(crate) fn stdio_transport() -> (Sender<Message>, Receiver<Message>, IoThread
             Ok(())
         })
         .unwrap();
-    let threads = IoThreads { reader, writer };
+    let threads = IoThreads { reader, writer, dropper };
     (writer_sender, reader_receiver, threads)
 }
 
@@ -49,13 +58,15 @@ pub(crate) fn stdio_transport() -> (Sender<Message>, Receiver<Message>, IoThread
 pub(crate) fn make_io_threads(
     reader: thread::JoinHandle<io::Result<()>>,
     writer: thread::JoinHandle<io::Result<()>>,
+    dropper: thread::JoinHandle<()>,
 ) -> IoThreads {
-    IoThreads { reader, writer }
+    IoThreads { reader, writer, dropper }
 }
 
 pub struct IoThreads {
     reader: thread::JoinHandle<io::Result<()>>,
     writer: thread::JoinHandle<io::Result<()>>,
+    dropper: thread::JoinHandle<()>,
 }
 
 impl IoThreads {
@@ -64,6 +75,12 @@ impl IoThreads {
             Ok(r) => r?,
             Err(err) => std::panic::panic_any(err),
         }
+        match self.dropper.join() {
+            Ok(_) => (),
+            Err(err) => {
+                std::panic::panic_any(err);
+            }
+        }
         match self.writer.join() {
             Ok(r) => r,
             Err(err) => {
diff --git a/src/tools/rust-analyzer/rust-version b/src/tools/rust-analyzer/rust-version
index 517fdfe18ed..2d9a927c638 100644
--- a/src/tools/rust-analyzer/rust-version
+++ b/src/tools/rust-analyzer/rust-version
@@ -1 +1 @@
-fb546ee09b226bc4dd4b712d35a372d923c4fa54
+9a1d156f38c51441ee51e5a068f1d0caf4bb0f27