about summary refs log tree commit diff
diff options
context:
space:
mode:
authorhecatia-elegua <108802164+hecatia-elegua@users.noreply.github.com>2023-04-11 21:14:52 +0200
committerGitHub <noreply@github.com>2023-04-11 21:14:52 +0200
commit398af0259f7614ff8a0d603edd5d94ce9aecd5f2 (patch)
tree207fc25b5c6d73d5c7833fab11d24d2fa5a837d7
parent33ee157f3b05865984957953df21f4711f8e5cd6 (diff)
parent7501d3b721560637e27f904d9fce79182c41bef7 (diff)
downloadrust-398af0259f7614ff8a0d603edd5d94ce9aecd5f2.tar.gz
rust-398af0259f7614ff8a0d603edd5d94ce9aecd5f2.zip
Merge branch 'master' into alias-based-completion2
-rw-r--r--.github/workflows/ci.yaml9
-rw-r--r--Cargo.lock7
-rw-r--r--Cargo.toml1
-rw-r--r--crates/base-db/src/fixture.rs22
-rw-r--r--crates/hir-def/src/attr.rs6
-rw-r--r--crates/hir-def/src/attr/builtin.rs (renamed from crates/hir-def/src/builtin_attr.rs)0
-rw-r--r--crates/hir-def/src/body.rs56
-rw-r--r--crates/hir-def/src/body/lower.rs641
-rw-r--r--crates/hir-def/src/body/pretty.rs39
-rw-r--r--crates/hir-def/src/body/scope.rs2
-rw-r--r--crates/hir-def/src/child_by_source.rs3
-rw-r--r--crates/hir-def/src/data.rs6
-rw-r--r--crates/hir-def/src/data/adt.rs (renamed from crates/hir-def/src/adt.rs)2
-rw-r--r--crates/hir-def/src/db.rs2
-rw-r--r--crates/hir-def/src/dyn_map.rs2
-rw-r--r--crates/hir-def/src/dyn_map/keys.rs (renamed from crates/hir-def/src/keys.rs)0
-rw-r--r--crates/hir-def/src/generics.rs10
-rw-r--r--crates/hir-def/src/hir.rs (renamed from crates/hir-def/src/expr.rs)75
-rw-r--r--crates/hir-def/src/hir/type_ref.rs (renamed from crates/hir-def/src/type_ref.rs)2
-rw-r--r--crates/hir-def/src/item_tree.rs10
-rw-r--r--crates/hir-def/src/item_tree/lower.rs46
-rw-r--r--crates/hir-def/src/item_tree/pretty.rs16
-rw-r--r--crates/hir-def/src/item_tree/tests.rs12
-rw-r--r--crates/hir-def/src/layout.rs1
-rw-r--r--crates/hir-def/src/lib.rs13
-rw-r--r--crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs12
-rw-r--r--crates/hir-def/src/macro_expansion_tests/mod.rs (renamed from crates/hir-def/src/macro_expansion_tests.rs)0
-rw-r--r--crates/hir-def/src/nameres/attr_resolution.rs13
-rw-r--r--crates/hir-def/src/nameres/path_resolution.rs6
-rw-r--r--crates/hir-def/src/resolver.rs2
-rw-r--r--crates/hir-expand/src/builtin_derive_macro.rs131
-rw-r--r--crates/hir-expand/src/lib.rs4
-rw-r--r--crates/hir-expand/src/name.rs3
-rw-r--r--crates/hir-ty/src/chalk_db.rs57
-rw-r--r--crates/hir-ty/src/chalk_ext.rs13
-rw-r--r--crates/hir-ty/src/consteval.rs2
-rw-r--r--crates/hir-ty/src/consteval/tests.rs75
-rw-r--r--crates/hir-ty/src/db.rs13
-rw-r--r--crates/hir-ty/src/diagnostics/decl_check.rs4
-rw-r--r--crates/hir-ty/src/diagnostics/expr.rs2
-rw-r--r--crates/hir-ty/src/diagnostics/match_check.rs26
-rw-r--r--crates/hir-ty/src/diagnostics/unsafe_check.rs2
-rw-r--r--crates/hir-ty/src/display.rs79
-rw-r--r--crates/hir-ty/src/infer.rs136
-rw-r--r--crates/hir-ty/src/infer/closure.rs722
-rw-r--r--crates/hir-ty/src/infer/coerce.rs8
-rw-r--r--crates/hir-ty/src/infer/expr.rs153
-rw-r--r--crates/hir-ty/src/infer/mutability.rs14
-rw-r--r--crates/hir-ty/src/infer/pat.rs2
-rw-r--r--crates/hir-ty/src/inhabitedness.rs4
-rw-r--r--crates/hir-ty/src/lang_items.rs2
-rw-r--r--crates/hir-ty/src/layout.rs17
-rw-r--r--crates/hir-ty/src/layout/adt.rs2
-rw-r--r--crates/hir-ty/src/layout/tests.rs65
-rw-r--r--crates/hir-ty/src/layout/tests/closure.rs175
-rw-r--r--crates/hir-ty/src/lib.rs2
-rw-r--r--crates/hir-ty/src/lower.rs20
-rw-r--r--crates/hir-ty/src/method_resolution.rs17
-rw-r--r--crates/hir-ty/src/mir.rs198
-rw-r--r--crates/hir-ty/src/mir/borrowck.rs48
-rw-r--r--crates/hir-ty/src/mir/eval.rs172
-rw-r--r--crates/hir-ty/src/mir/lower.rs431
-rw-r--r--crates/hir-ty/src/mir/lower/as_place.rs2
-rw-r--r--crates/hir-ty/src/mir/lower/pattern_matching.rs17
-rw-r--r--crates/hir-ty/src/mir/pretty.rs51
-rw-r--r--crates/hir-ty/src/tests.rs6
-rw-r--r--crates/hir-ty/src/tests/coercion.rs41
-rw-r--r--crates/hir-ty/src/tests/macros.rs4
-rw-r--r--crates/hir-ty/src/tests/method_resolution.rs20
-rw-r--r--crates/hir-ty/src/tests/patterns.rs14
-rw-r--r--crates/hir-ty/src/tests/regression.rs68
-rw-r--r--crates/hir-ty/src/tests/simple.rs208
-rw-r--r--crates/hir-ty/src/tests/traits.rs163
-rw-r--r--crates/hir-ty/src/traits.rs14
-rw-r--r--crates/hir-ty/src/utils.rs23
-rw-r--r--crates/hir/src/diagnostics.rs27
-rw-r--r--crates/hir/src/display.rs13
-rw-r--r--crates/hir/src/from_id.rs2
-rw-r--r--crates/hir/src/lib.rs126
-rw-r--r--crates/hir/src/semantics.rs6
-rw-r--r--crates/hir/src/semantics/source_to_def.rs10
-rw-r--r--crates/hir/src/source_analyzer.rs4
-rw-r--r--crates/ide-assists/src/handlers/convert_nested_function_to_closure.rs209
-rw-r--r--crates/ide-assists/src/handlers/extract_function.rs41
-rw-r--r--crates/ide-assists/src/handlers/extract_variable.rs58
-rw-r--r--crates/ide-assists/src/handlers/generate_function.rs3
-rw-r--r--crates/ide-assists/src/handlers/reorder_impl_items.rs41
-rw-r--r--crates/ide-assists/src/handlers/sort_items.rs2
-rw-r--r--crates/ide-assists/src/lib.rs4
-rw-r--r--crates/ide-assists/src/tests/generated.rs27
-rw-r--r--crates/ide-assists/src/tests/sourcegen.rs2
-rw-r--r--crates/ide-completion/src/completions.rs61
-rw-r--r--crates/ide-completion/src/completions/dot.rs37
-rw-r--r--crates/ide-completion/src/completions/env_vars.rs4
-rw-r--r--crates/ide-completion/src/completions/flyimport.rs8
-rw-r--r--crates/ide-completion/src/completions/item_list/trait_impl.rs31
-rw-r--r--crates/ide-completion/src/completions/use_.rs3
-rw-r--r--crates/ide-completion/src/context.rs16
-rw-r--r--crates/ide-completion/src/tests.rs6
-rw-r--r--crates/ide-completion/src/tests/expression.rs109
-rw-r--r--crates/ide-completion/src/tests/flyimport.rs35
-rw-r--r--crates/ide-completion/src/tests/item_list.rs54
-rw-r--r--crates/ide-completion/src/tests/pattern.rs60
-rw-r--r--crates/ide-completion/src/tests/predicate.rs42
-rw-r--r--crates/ide-completion/src/tests/type_pos.rs52
-rw-r--r--crates/ide-completion/src/tests/use_tree.rs48
-rw-r--r--crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs19
-rw-r--r--crates/ide-diagnostics/src/handlers/mutability_errors.rs82
-rw-r--r--crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs2
-rw-r--r--crates/ide-diagnostics/src/handlers/type_mismatch.rs19
-rw-r--r--crates/ide-diagnostics/src/handlers/undeclared_label.rs63
-rw-r--r--crates/ide-diagnostics/src/handlers/unlinked_file.rs24
-rw-r--r--crates/ide-diagnostics/src/handlers/unreachable_label.rs91
-rw-r--r--crates/ide-diagnostics/src/lib.rs24
-rw-r--r--crates/ide/src/doc_links/tests.rs2
-rw-r--r--crates/ide/src/expand_macro.rs8
-rw-r--r--crates/ide/src/hover/tests.rs12
-rw-r--r--crates/ide/src/inlay_hints.rs16
-rw-r--r--crates/ide/src/inlay_hints/adjustment.rs12
-rw-r--r--crates/ide/src/inlay_hints/bind_pat.rs105
-rw-r--r--crates/ide/src/signature_help.rs18
-rw-r--r--crates/ide/src/static_index.rs1
-rw-r--r--crates/ide/src/syntax_highlighting.rs15
-rw-r--r--crates/ide/src/syntax_highlighting/escape.rs24
-rw-r--r--crates/ide/src/syntax_highlighting/highlight.rs6
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_strings.html10
-rw-r--r--crates/ide/src/syntax_highlighting/tests.rs10
-rw-r--r--crates/proc-macro-srv-cli/Cargo.toml1
-rw-r--r--crates/proc-macro-srv-cli/src/lib.rs (renamed from crates/proc-macro-srv/src/cli.rs)26
-rw-r--r--crates/proc-macro-srv-cli/src/main.rs5
-rw-r--r--crates/proc-macro-srv/src/abis/abi_1_63/mod.rs106
-rw-r--r--crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/buffer.rs156
-rw-r--r--crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/client.rs510
-rw-r--r--crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/closure.rs32
-rw-r--r--crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/handle.rs89
-rw-r--r--crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/mod.rs451
-rw-r--r--crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/rpc.rs304
-rw-r--r--crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/scoped_cell.rs81
-rw-r--r--crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/selfless_reify.rs83
-rw-r--r--crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/server.rs332
-rw-r--r--crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/diagnostic.rs166
-rw-r--r--crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/mod.rs1106
-rw-r--r--crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/quote.rs139
-rw-r--r--crates/proc-macro-srv/src/abis/abi_1_63/ra_server.rs840
-rw-r--r--crates/proc-macro-srv/src/abis/mod.rs146
-rw-r--r--crates/proc-macro-srv/src/dylib.rs28
-rw-r--r--crates/proc-macro-srv/src/lib.rs56
-rw-r--r--crates/proc-macro-srv/src/proc_macros.rs (renamed from crates/proc-macro-srv/src/abis/abi_sysroot/mod.rs)81
-rw-r--r--crates/proc-macro-srv/src/server.rs (renamed from crates/proc-macro-srv/src/abis/abi_sysroot/ra_server.rs)50
-rw-r--r--crates/proc-macro-srv/src/server/symbol.rs (renamed from crates/proc-macro-srv/src/abis/abi_sysroot/ra_server/symbol.rs)16
-rw-r--r--crates/proc-macro-srv/src/server/token_stream.rs (renamed from crates/proc-macro-srv/src/abis/abi_sysroot/ra_server/token_stream.rs)16
-rw-r--r--crates/proc-macro-srv/src/tests/utils.rs4
-rw-r--r--crates/project-model/src/build_scripts.rs1
-rw-r--r--crates/rust-analyzer/Cargo.toml5
-rw-r--r--crates/rust-analyzer/src/bin/main.rs2
-rw-r--r--crates/rust-analyzer/src/cli/analysis_stats.rs2
-rw-r--r--crates/rust-analyzer/src/config.rs27
-rw-r--r--crates/rust-analyzer/src/reload.rs19
-rw-r--r--crates/rust-analyzer/tests/slow-tests/main.rs73
-rw-r--r--crates/rust-analyzer/tests/slow-tests/support.rs19
-rw-r--r--crates/sourcegen/src/lib.rs26
-rw-r--r--crates/syntax/Cargo.toml2
-rw-r--r--crates/syntax/src/token_text.rs7
-rw-r--r--crates/test-utils/src/fixture.rs66
-rw-r--r--crates/test-utils/src/lib.rs2
-rw-r--r--crates/test-utils/src/minicore.rs17
-rw-r--r--crates/tt/Cargo.toml2
-rw-r--r--docs/user/generated_config.adoc5
-rw-r--r--editors/code/package.json17
-rw-r--r--editors/code/src/client.ts6
-rw-r--r--editors/code/src/commands.ts13
-rw-r--r--editors/code/src/ctx.ts14
172 files changed, 5165 insertions, 5934 deletions
diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index bb77324378a..6293b11d1e8 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -24,6 +24,9 @@ jobs:
     runs-on: ${{ matrix.os }}
     env:
       CC: deny_c
+      # we want to build r-a on stable to check that it keeps building on stable,
+      # but we also want to test our proc-macro-srv which depends on nightly features
+      RUSTC_BOOTSTRAP: 1
 
     strategy:
       fail-fast: false
@@ -50,15 +53,15 @@ jobs:
         run: sed -i '/\[profile.dev]/a opt-level=1' Cargo.toml
 
       - name: Compile (tests)
-        run: cargo test --no-run --locked
+        run: cargo test --no-run --locked  --features sysroot-abi
 
       # It's faster to `test` before `build` ¯\_(ツ)_/¯
       - name: Compile (rust-analyzer)
         if: matrix.os == 'ubuntu-latest'
-        run: cargo build --quiet
+        run: cargo build --quiet --features sysroot-abi
 
       - name: Test
-        run: cargo test -- --nocapture --quiet
+        run: cargo test --features sysroot-abi -- --nocapture --quiet
 
       - name: Run analysis-stats on rust-analyzer
         if: matrix.os == 'ubuntu-latest'
diff --git a/Cargo.lock b/Cargo.lock
index de050fd78d4..9d082215e85 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1250,6 +1250,7 @@ dependencies = [
 name = "proc-macro-srv-cli"
 version = "0.0.0"
 dependencies = [
+ "proc-macro-api",
  "proc-macro-srv",
 ]
 
@@ -1457,7 +1458,7 @@ dependencies = [
  "parking_lot 0.12.1",
  "parking_lot_core 0.9.6",
  "proc-macro-api",
- "proc-macro-srv",
+ "proc-macro-srv-cli",
  "profile",
  "project-model",
  "rayon",
@@ -1641,9 +1642,9 @@ checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
 
 [[package]]
 name = "smol_str"
-version = "0.1.25"
+version = "0.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9d268d24892b932cac466d214af6ec8a3ec99873f0f8664d9a384b49596db682"
+checksum = "74212e6bbe9a4352329b2f68ba3130c15a3f26fe88ff22dbdc6cdd58fa85e99c"
 dependencies = [
  "serde",
 ]
diff --git a/Cargo.toml b/Cargo.toml
index 511cb24a61e..985a1844c73 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -77,6 +77,7 @@ vfs = { path = "./crates/vfs", version = "0.0.0" }
 
 # non-local crates
 smallvec = { version = "1.10.0", features = ["const_new", "union", "const_generics"] }
+smol_str = "0.2.0"
 # the following crates are pinned to prevent us from pulling in syn 2 until all our dependencies have moved
 serde = { version = "=1.0.156", features = ["derive"] }
 serde_json = "1.0.94"
diff --git a/crates/base-db/src/fixture.rs b/crates/base-db/src/fixture.rs
index 3339580bfe2..f01f75b1387 100644
--- a/crates/base-db/src/fixture.rs
+++ b/crates/base-db/src/fixture.rs
@@ -4,7 +4,8 @@ use std::{mem, str::FromStr, sync::Arc};
 use cfg::CfgOptions;
 use rustc_hash::FxHashMap;
 use test_utils::{
-    extract_range_or_offset, Fixture, RangeOrOffset, CURSOR_MARKER, ESCAPED_CURSOR_MARKER,
+    extract_range_or_offset, Fixture, FixtureWithProjectMeta, RangeOrOffset, CURSOR_MARKER,
+    ESCAPED_CURSOR_MARKER,
 };
 use tt::token_id::{Leaf, Subtree, TokenTree};
 use vfs::{file_set::FileSet, VfsPath};
@@ -12,7 +13,7 @@ use vfs::{file_set::FileSet, VfsPath};
 use crate::{
     input::{CrateName, CrateOrigin, LangCrateOrigin},
     Change, CrateDisplayName, CrateGraph, CrateId, Dependency, Edition, Env, FileId, FilePosition,
-    FileRange, ProcMacro, ProcMacroExpander, ProcMacroExpansionError, ProcMacros,
+    FileRange, ProcMacro, ProcMacroExpander, ProcMacroExpansionError, ProcMacros, ReleaseChannel,
     SourceDatabaseExt, SourceRoot, SourceRootId,
 };
 
@@ -102,7 +103,14 @@ impl ChangeFixture {
         ra_fixture: &str,
         mut proc_macro_defs: Vec<(String, ProcMacro)>,
     ) -> ChangeFixture {
-        let (mini_core, proc_macro_names, fixture) = Fixture::parse(ra_fixture);
+        let FixtureWithProjectMeta { fixture, mini_core, proc_macro_names, toolchain } =
+            FixtureWithProjectMeta::parse(ra_fixture);
+        let toolchain = toolchain
+            .map(|it| {
+                ReleaseChannel::from_str(&it)
+                    .unwrap_or_else(|| panic!("unknown release channel found: {it}"))
+            })
+            .unwrap_or(ReleaseChannel::Stable);
         let mut change = Change::new();
 
         let mut files = Vec::new();
@@ -166,7 +174,7 @@ impl ChangeFixture {
                         .as_deref()
                         .map(Arc::from)
                         .ok_or_else(|| "target_data_layout unset".into()),
-                    None,
+                    Some(toolchain),
                 );
                 let prev = crates.insert(crate_name.clone(), crate_id);
                 assert!(prev.is_none());
@@ -205,7 +213,7 @@ impl ChangeFixture {
                 default_target_data_layout
                     .map(|x| x.into())
                     .ok_or_else(|| "target_data_layout unset".into()),
-                None,
+                Some(toolchain),
             );
         } else {
             for (from, to, prelude) in crate_deps {
@@ -247,7 +255,7 @@ impl ChangeFixture {
                 false,
                 CrateOrigin::Lang(LangCrateOrigin::Core),
                 target_layout.clone(),
-                None,
+                Some(toolchain),
             );
 
             for krate in all_crates {
@@ -286,7 +294,7 @@ impl ChangeFixture {
                 true,
                 CrateOrigin::Local { repo: None, name: None },
                 target_layout,
-                None,
+                Some(toolchain),
             );
             proc_macros.insert(proc_macros_crate, Ok(proc_macro));
 
diff --git a/crates/hir-def/src/attr.rs b/crates/hir-def/src/attr.rs
index 4dde093f968..cc59e780938 100644
--- a/crates/hir-def/src/attr.rs
+++ b/crates/hir-def/src/attr.rs
@@ -1,5 +1,7 @@
 //! A higher level attributes based on TokenTree, with also some shortcuts.
 
+pub mod builtin;
+
 #[cfg(test)]
 mod tests;
 
@@ -267,6 +269,10 @@ impl Attrs {
     pub fn is_proc_macro_derive(&self) -> bool {
         self.by_key("proc_macro_derive").exists()
     }
+
+    pub fn is_unstable(&self) -> bool {
+        self.by_key("unstable").exists()
+    }
 }
 
 use std::slice::Iter as SliceIter;
diff --git a/crates/hir-def/src/builtin_attr.rs b/crates/hir-def/src/attr/builtin.rs
index f7c1e683d0d..f7c1e683d0d 100644
--- a/crates/hir-def/src/builtin_attr.rs
+++ b/crates/hir-def/src/attr/builtin.rs
diff --git a/crates/hir-def/src/body.rs b/crates/hir-def/src/body.rs
index bd87eaa2217..9caa084f2a2 100644
--- a/crates/hir-def/src/body.rs
+++ b/crates/hir-def/src/body.rs
@@ -13,10 +13,12 @@ use cfg::{CfgExpr, CfgOptions};
 use drop_bomb::DropBomb;
 use either::Either;
 use hir_expand::{
-    attrs::RawAttrs, hygiene::Hygiene, ExpandError, ExpandResult, HirFileId, InFile, MacroCallId,
+    ast_id_map::AstIdMap, attrs::RawAttrs, hygiene::Hygiene, name::Name, AstId, ExpandError,
+    ExpandResult, HirFileId, InFile, MacroCallId,
 };
 use la_arena::{Arena, ArenaMap};
 use limit::Limit;
+use once_cell::unsync::OnceCell;
 use profile::Count;
 use rustc_hash::FxHashMap;
 use syntax::{ast, AstPtr, SyntaxNode, SyntaxNodePtr};
@@ -24,7 +26,7 @@ use syntax::{ast, AstPtr, SyntaxNode, SyntaxNodePtr};
 use crate::{
     attr::Attrs,
     db::DefDatabase,
-    expr::{
+    hir::{
         dummy_expr_id, Binding, BindingId, Expr, ExprId, Label, LabelId, Pat, PatId, RecordFieldPat,
     },
     item_scope::BuiltinShadowMode,
@@ -36,7 +38,43 @@ use crate::{
     UnresolvedMacro,
 };
 
-pub use lower::LowerCtx;
+pub struct LowerCtx<'a> {
+    pub db: &'a dyn DefDatabase,
+    hygiene: Hygiene,
+    ast_id_map: Option<(HirFileId, OnceCell<Arc<AstIdMap>>)>,
+}
+
+impl<'a> LowerCtx<'a> {
+    pub fn new(db: &'a dyn DefDatabase, hygiene: &Hygiene, file_id: HirFileId) -> Self {
+        LowerCtx { db, hygiene: hygiene.clone(), ast_id_map: Some((file_id, OnceCell::new())) }
+    }
+
+    pub fn with_file_id(db: &'a dyn DefDatabase, file_id: HirFileId) -> Self {
+        LowerCtx {
+            db,
+            hygiene: Hygiene::new(db.upcast(), file_id),
+            ast_id_map: Some((file_id, OnceCell::new())),
+        }
+    }
+
+    pub fn with_hygiene(db: &'a dyn DefDatabase, hygiene: &Hygiene) -> Self {
+        LowerCtx { db, hygiene: hygiene.clone(), ast_id_map: None }
+    }
+
+    pub(crate) fn hygiene(&self) -> &Hygiene {
+        &self.hygiene
+    }
+
+    pub(crate) fn lower_path(&self, ast: ast::Path) -> Option<Path> {
+        Path::from_src(ast, self)
+    }
+
+    pub(crate) fn ast_id<N: syntax::AstNode>(&self, item: &N) -> Option<AstId<N>> {
+        let &(file_id, ref ast_id_map) = self.ast_id_map.as_ref()?;
+        let ast_id_map = ast_id_map.get_or_init(|| self.db.ast_id_map(file_id));
+        Some(InFile::new(file_id, ast_id_map.ast_id(item)))
+    }
+}
 
 /// A subset of Expander that only deals with cfg attributes. We only need it to
 /// avoid cyclic queries in crate def map during enum processing.
@@ -76,6 +114,10 @@ impl CfgExpander {
         let attrs = self.parse_attrs(db, owner);
         attrs.is_cfg_enabled(&self.cfg_options)
     }
+
+    pub(crate) fn hygiene(&self) -> &Hygiene {
+        &self.hygiene
+    }
 }
 
 impl Expander {
@@ -180,6 +222,10 @@ impl Expander {
         mark.bomb.defuse();
     }
 
+    pub fn ctx<'a>(&self, db: &'a dyn DefDatabase) -> LowerCtx<'a> {
+        LowerCtx::new(db, &self.cfg_expander.hygiene, self.current_file_id)
+    }
+
     pub(crate) fn to_source<T>(&self, value: T) -> InFile<T> {
         InFile { file_id: self.current_file_id, value }
     }
@@ -232,7 +278,7 @@ impl Expander {
             // The overflow error should have been reported when it occurred (see the next branch),
             // so don't return overflow error here to avoid diagnostics duplication.
             cov_mark::hit!(overflow_but_not_me);
-            return ExpandResult::only_err(ExpandError::RecursionOverflowPosioned);
+            return ExpandResult::only_err(ExpandError::RecursionOverflowPoisoned);
         } else if self.recursion_limit(db).check(self.recursion_depth + 1).is_err() {
             self.recursion_depth = usize::MAX;
             cov_mark::hit!(your_stack_belongs_to_me);
@@ -343,6 +389,8 @@ pub enum BodyDiagnostic {
     MacroError { node: InFile<AstPtr<ast::MacroCall>>, message: String },
     UnresolvedProcMacro { node: InFile<AstPtr<ast::MacroCall>>, krate: CrateId },
     UnresolvedMacroCall { node: InFile<AstPtr<ast::MacroCall>>, path: ModPath },
+    UnreachableLabel { node: InFile<AstPtr<ast::Lifetime>>, name: Name },
+    UndeclaredLabel { node: InFile<AstPtr<ast::Lifetime>>, name: Name },
 }
 
 impl Body {
diff --git a/crates/hir-def/src/body/lower.rs b/crates/hir-def/src/body/lower.rs
index 53627375831..688c9e86bbb 100644
--- a/crates/hir-def/src/body/lower.rs
+++ b/crates/hir-def/src/body/lower.rs
@@ -7,77 +7,38 @@ use base_db::CrateId;
 use either::Either;
 use hir_expand::{
     ast_id_map::AstIdMap,
-    hygiene::Hygiene,
     name::{name, AsName, Name},
-    AstId, ExpandError, HirFileId, InFile,
+    AstId, ExpandError, InFile,
 };
 use intern::Interned;
 use la_arena::Arena;
-use once_cell::unsync::OnceCell;
 use profile::Count;
 use rustc_hash::FxHashMap;
 use smallvec::SmallVec;
 use syntax::{
     ast::{
-        self, ArrayExprKind, AstChildren, BlockExpr, HasArgList, HasLoopBody, HasName, LiteralKind,
+        self, ArrayExprKind, AstChildren, BlockExpr, HasArgList, HasLoopBody, HasName,
         SlicePatComponents,
     },
     AstNode, AstPtr, SyntaxNodePtr,
 };
 
 use crate::{
-    adt::StructKind,
-    body::{Body, BodySourceMap, Expander, ExprPtr, LabelPtr, LabelSource, PatPtr},
-    body::{BodyDiagnostic, ExprSource, PatSource},
-    builtin_type::{BuiltinFloat, BuiltinInt, BuiltinUint},
+    body::{Body, BodyDiagnostic, BodySourceMap, Expander, ExprPtr, LabelPtr, LowerCtx, PatPtr},
+    data::adt::StructKind,
     db::DefDatabase,
-    expr::{
-        dummy_expr_id, Array, Binding, BindingAnnotation, BindingId, ClosureKind, Expr, ExprId,
-        FloatTypeWrapper, Label, LabelId, Literal, MatchArm, Movability, Pat, PatId,
-        RecordFieldPat, RecordLitField, Statement,
+    hir::{
+        dummy_expr_id, Array, Binding, BindingAnnotation, BindingId, CaptureBy, ClosureKind, Expr,
+        ExprId, Label, LabelId, Literal, MatchArm, Movability, Pat, PatId, RecordFieldPat,
+        RecordLitField, Statement,
     },
     item_scope::BuiltinShadowMode,
-    item_tree::ItemTree,
     lang_item::LangItem,
     path::{GenericArgs, Path},
     type_ref::{Mutability, Rawness, TypeRef},
     AdtId, BlockId, BlockLoc, ModuleDefId, UnresolvedMacro,
 };
 
-pub struct LowerCtx<'a> {
-    pub db: &'a dyn DefDatabase,
-    hygiene: Hygiene,
-    ast_id_map: Option<(HirFileId, OnceCell<Arc<AstIdMap>>)>,
-}
-
-impl<'a> LowerCtx<'a> {
-    pub fn new(db: &'a dyn DefDatabase, file_id: HirFileId) -> Self {
-        LowerCtx {
-            db,
-            hygiene: Hygiene::new(db.upcast(), file_id),
-            ast_id_map: Some((file_id, OnceCell::new())),
-        }
-    }
-
-    pub fn with_hygiene(db: &'a dyn DefDatabase, hygiene: &Hygiene) -> Self {
-        LowerCtx { db, hygiene: hygiene.clone(), ast_id_map: None }
-    }
-
-    pub(crate) fn hygiene(&self) -> &Hygiene {
-        &self.hygiene
-    }
-
-    pub(crate) fn lower_path(&self, ast: ast::Path) -> Option<Path> {
-        Path::from_src(ast, self)
-    }
-
-    pub(crate) fn ast_id<N: AstNode>(&self, item: &N) -> Option<AstId<N>> {
-        let &(file_id, ref ast_id_map) = self.ast_id_map.as_ref()?;
-        let ast_id_map = ast_id_map.get_or_init(|| self.db.ast_id_map(file_id));
-        Some(InFile::new(file_id, ast_id_map.ast_id(item)))
-    }
-}
-
 pub(super) fn lower(
     db: &dyn DefDatabase,
     expander: Expander,
@@ -102,9 +63,11 @@ pub(super) fn lower(
             _c: Count::new(),
         },
         expander,
-        current_try_block: None,
+        current_try_block_label: None,
         is_lowering_assignee_expr: false,
         is_lowering_generator: false,
+        label_ribs: Vec::new(),
+        current_binding_owner: None,
     }
     .collect(params, body, is_async_fn)
 }
@@ -113,12 +76,57 @@ struct ExprCollector<'a> {
     db: &'a dyn DefDatabase,
     expander: Expander,
     ast_id_map: Arc<AstIdMap>,
-    body: Body,
     krate: CrateId,
+    body: Body,
     source_map: BodySourceMap,
-    current_try_block: Option<LabelId>,
+
     is_lowering_assignee_expr: bool,
     is_lowering_generator: bool,
+
+    current_try_block_label: Option<LabelId>,
+    // points to the expression that a try expression will target (replaces current_try_block_label)
+    // catch_scope: Option<ExprId>,
+    // points to the expression that an unlabeled control flow will target
+    // loop_scope: Option<ExprId>,
+    // needed to diagnose non label control flow in while conditions
+    // is_in_loop_condition: bool,
+
+    // resolution
+    label_ribs: Vec<LabelRib>,
+    current_binding_owner: Option<ExprId>,
+}
+
+#[derive(Clone, Debug)]
+struct LabelRib {
+    kind: RibKind,
+    // Once we handle macro hygiene this will need to be a map
+    label: Option<(Name, LabelId)>,
+}
+
+impl LabelRib {
+    fn new(kind: RibKind) -> Self {
+        LabelRib { kind, label: None }
+    }
+    fn new_normal(label: (Name, LabelId)) -> Self {
+        LabelRib { kind: RibKind::Normal, label: Some(label) }
+    }
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+enum RibKind {
+    Normal,
+    Closure,
+    Constant,
+}
+
+impl RibKind {
+    /// This rib forbids referring to labels defined in upwards ribs.
+    fn is_label_barrier(self) -> bool {
+        match self {
+            RibKind::Normal => false,
+            RibKind::Closure | RibKind::Constant => true,
+        }
+    }
 }
 
 #[derive(Debug, Default)]
@@ -162,97 +170,35 @@ impl ExprCollector<'_> {
                 self.body.params.push(param_pat);
             }
 
-            for pat in param_list
-                .params()
-                .zip(attr_enabled)
-                .filter_map(|(param, enabled)| param.pat().filter(|_| enabled))
+            for (param, _) in param_list.params().zip(attr_enabled).filter(|(_, enabled)| *enabled)
             {
-                let param_pat = self.collect_pat(pat);
+                let param_pat = self.collect_pat_top(param.pat());
                 self.body.params.push(param_pat);
             }
         };
+        self.body.body_expr = self.with_label_rib(RibKind::Closure, |this| {
+            if is_async_fn {
+                match body {
+                    Some(e) => {
+                        let expr = this.collect_expr(e);
+                        this.alloc_expr_desugared(Expr::Async {
+                            id: None,
+                            statements: Box::new([]),
+                            tail: Some(expr),
+                        })
+                    }
+                    None => this.missing_expr(),
+                }
+            } else {
+                this.collect_expr_opt(body)
+            }
+        });
 
-        self.body.body_expr = if is_async_fn {
-            self.current_try_block =
-                Some(self.alloc_label_desugared(Label { name: Name::generate_new_name() }));
-            let expr = self.collect_expr_opt(body);
-            let expr = self.alloc_expr_desugared(Expr::Block {
-                id: None,
-                statements: Box::new([]),
-                tail: Some(expr),
-                label: self.current_try_block,
-            });
-            let expr = self.alloc_expr_desugared(Expr::Async {
-                id: None,
-                statements: Box::new([]),
-                tail: Some(expr),
-            });
-            expr
-        } else {
-            self.collect_expr_opt(body)
-        };
         (self.body, self.source_map)
     }
 
     fn ctx(&self) -> LowerCtx<'_> {
-        LowerCtx::new(self.db, self.expander.current_file_id)
-    }
-
-    fn alloc_expr(&mut self, expr: Expr, ptr: ExprPtr) -> ExprId {
-        let src = self.expander.to_source(ptr);
-        let id = self.make_expr(expr, src.clone());
-        self.source_map.expr_map.insert(src, id);
-        id
-    }
-    // FIXME: desugared exprs don't have ptr, that's wrong and should be fixed somehow.
-    fn alloc_expr_desugared(&mut self, expr: Expr) -> ExprId {
-        self.body.exprs.alloc(expr)
-    }
-    fn missing_expr(&mut self) -> ExprId {
-        self.alloc_expr_desugared(Expr::Missing)
-    }
-    fn make_expr(&mut self, expr: Expr, src: ExprSource) -> ExprId {
-        let id = self.body.exprs.alloc(expr);
-        self.source_map.expr_map_back.insert(id, src);
-        id
-    }
-
-    fn alloc_binding(&mut self, name: Name, mode: BindingAnnotation) -> BindingId {
-        self.body.bindings.alloc(Binding { name, mode, definitions: SmallVec::new() })
-    }
-    fn alloc_pat(&mut self, pat: Pat, ptr: PatPtr) -> PatId {
-        let src = self.expander.to_source(ptr);
-        let id = self.make_pat(pat, src.clone());
-        self.source_map.pat_map.insert(src, id);
-        id
-    }
-    // FIXME: desugared pats don't have ptr, that's wrong and should be fixed somehow.
-    fn alloc_pat_desugared(&mut self, pat: Pat) -> PatId {
-        self.body.pats.alloc(pat)
-    }
-    fn missing_pat(&mut self) -> PatId {
-        self.body.pats.alloc(Pat::Missing)
-    }
-    fn make_pat(&mut self, pat: Pat, src: PatSource) -> PatId {
-        let id = self.body.pats.alloc(pat);
-        self.source_map.pat_map_back.insert(id, src);
-        id
-    }
-
-    fn alloc_label(&mut self, label: Label, ptr: LabelPtr) -> LabelId {
-        let src = self.expander.to_source(ptr);
-        let id = self.make_label(label, src.clone());
-        self.source_map.label_map.insert(src, id);
-        id
-    }
-    // FIXME: desugared labels don't have ptr, that's wrong and should be fixed somehow.
-    fn alloc_label_desugared(&mut self, label: Label) -> LabelId {
-        self.body.labels.alloc(label)
-    }
-    fn make_label(&mut self, label: Label, src: LabelSource) -> LabelId {
-        let id = self.body.labels.alloc(label);
-        self.source_map.label_map_back.insert(id, src);
-        id
+        self.expander.ctx(self.db)
     }
 
     fn collect_expr(&mut self, expr: ast::Expr) -> ExprId {
@@ -264,6 +210,7 @@ impl ExprCollector<'_> {
         let syntax_ptr = AstPtr::new(&expr);
         self.check_cfg(&expr)?;
 
+        // FIXME: Move some of these arms out into separate methods for clarity
         Some(match expr {
             ast::Expr::IfExpr(e) => {
                 let then_branch = self.collect_block_opt(e.then_branch());
@@ -281,12 +228,12 @@ impl ExprCollector<'_> {
                 self.alloc_expr(Expr::If { condition, then_branch, else_branch }, syntax_ptr)
             }
             ast::Expr::LetExpr(e) => {
-                let pat = self.collect_pat_opt(e.pat());
+                let pat = self.collect_pat_top(e.pat());
                 let expr = self.collect_expr_opt(e.expr());
                 self.alloc_expr(Expr::Let { pat, expr }, syntax_ptr)
             }
             ast::Expr::BlockExpr(e) => match e.modifier() {
-                Some(ast::BlockModifier::Try(_)) => self.collect_try_block(e),
+                Some(ast::BlockModifier::Try(_)) => self.desugar_try_block(e),
                 Some(ast::BlockModifier::Unsafe(_)) => {
                     self.collect_block_(e, |id, statements, tail| Expr::Unsafe {
                         id,
@@ -296,28 +243,48 @@ impl ExprCollector<'_> {
                 }
                 Some(ast::BlockModifier::Label(label)) => {
                     let label = self.collect_label(label);
-                    self.collect_block_(e, |id, statements, tail| Expr::Block {
-                        id,
-                        statements,
-                        tail,
-                        label: Some(label),
+                    self.with_labeled_rib(label, |this| {
+                        this.collect_block_(e, |id, statements, tail| Expr::Block {
+                            id,
+                            statements,
+                            tail,
+                            label: Some(label),
+                        })
+                    })
+                }
+                Some(ast::BlockModifier::Async(_)) => {
+                    self.with_label_rib(RibKind::Closure, |this| {
+                        this.collect_block_(e, |id, statements, tail| Expr::Async {
+                            id,
+                            statements,
+                            tail,
+                        })
+                    })
+                }
+                Some(ast::BlockModifier::Const(_)) => {
+                    self.with_label_rib(RibKind::Constant, |this| {
+                        this.collect_as_a_binding_owner_bad(
+                            |this| {
+                                this.collect_block_(e, |id, statements, tail| Expr::Const {
+                                    id,
+                                    statements,
+                                    tail,
+                                })
+                            },
+                            syntax_ptr,
+                        )
                     })
                 }
-                Some(ast::BlockModifier::Async(_)) => self
-                    .collect_block_(e, |id, statements, tail| Expr::Async { id, statements, tail }),
-                Some(ast::BlockModifier::Const(_)) => self
-                    .collect_block_(e, |id, statements, tail| Expr::Const { id, statements, tail }),
                 None => self.collect_block(e),
             },
             ast::Expr::LoopExpr(e) => {
                 let label = e.label().map(|label| self.collect_label(label));
-                let body = self.collect_block_opt(e.loop_body());
+                let body = self.collect_labelled_block_opt(label, e.loop_body());
                 self.alloc_expr(Expr::Loop { body, label }, syntax_ptr)
             }
             ast::Expr::WhileExpr(e) => {
                 let label = e.label().map(|label| self.collect_label(label));
-                let body = self.collect_block_opt(e.loop_body());
-
+                let body = self.collect_labelled_block_opt(label, e.loop_body());
                 let condition = self.collect_expr_opt(e.condition());
 
                 self.alloc_expr(Expr::While { condition, body, label }, syntax_ptr)
@@ -325,8 +292,8 @@ impl ExprCollector<'_> {
             ast::Expr::ForExpr(e) => {
                 let label = e.label().map(|label| self.collect_label(label));
                 let iterable = self.collect_expr_opt(e.iterable());
-                let pat = self.collect_pat_opt(e.pat());
-                let body = self.collect_block_opt(e.loop_body());
+                let pat = self.collect_pat_top(e.pat());
+                let body = self.collect_labelled_block_opt(label, e.loop_body());
                 self.alloc_expr(Expr::For { iterable, pat, body, label }, syntax_ptr)
             }
             ast::Expr::CallExpr(e) => {
@@ -365,7 +332,7 @@ impl ExprCollector<'_> {
                         .arms()
                         .filter_map(|arm| {
                             self.check_cfg(&arm).map(|()| MatchArm {
-                                pat: self.collect_pat_opt(arm.pat()),
+                                pat: self.collect_pat_top(arm.pat()),
                                 expr: self.collect_expr_opt(arm.expr()),
                                 guard: arm
                                     .guard()
@@ -386,16 +353,20 @@ impl ExprCollector<'_> {
                     .unwrap_or(Expr::Missing);
                 self.alloc_expr(path, syntax_ptr)
             }
-            ast::Expr::ContinueExpr(e) => self.alloc_expr(
-                Expr::Continue { label: e.lifetime().map(|l| Name::new_lifetime(&l)) },
-                syntax_ptr,
-            ),
+            ast::Expr::ContinueExpr(e) => {
+                let label = self.resolve_label(e.lifetime()).unwrap_or_else(|e| {
+                    self.source_map.diagnostics.push(e);
+                    None
+                });
+                self.alloc_expr(Expr::Continue { label }, syntax_ptr)
+            }
             ast::Expr::BreakExpr(e) => {
+                let label = self.resolve_label(e.lifetime()).unwrap_or_else(|e| {
+                    self.source_map.diagnostics.push(e);
+                    None
+                });
                 let expr = e.expr().map(|e| self.collect_expr(e));
-                self.alloc_expr(
-                    Expr::Break { expr, label: e.lifetime().map(|l| Name::new_lifetime(&l)) },
-                    syntax_ptr,
-                )
+                self.alloc_expr(Expr::Break { expr, label }, syntax_ptr)
             }
             ast::Expr::ParenExpr(e) => {
                 let inner = self.collect_expr_opt(e.expr());
@@ -496,14 +467,16 @@ impl ExprCollector<'_> {
                     None => self.alloc_expr(Expr::Missing, syntax_ptr),
                 }
             }
-            ast::Expr::ClosureExpr(e) => {
+            ast::Expr::ClosureExpr(e) => self.with_label_rib(RibKind::Closure, |this| {
+                let (result_expr_id, prev_binding_owner) =
+                    this.initialize_binding_owner(syntax_ptr);
                 let mut args = Vec::new();
                 let mut arg_types = Vec::new();
                 if let Some(pl) = e.param_list() {
                     for param in pl.params() {
-                        let pat = self.collect_pat_opt(param.pat());
+                        let pat = this.collect_pat_top(param.pat());
                         let type_ref =
-                            param.ty().map(|it| Interned::new(TypeRef::from_ast(&self.ctx(), it)));
+                            param.ty().map(|it| Interned::new(TypeRef::from_ast(&this.ctx(), it)));
                         args.push(pat);
                         arg_types.push(type_ref);
                     }
@@ -511,14 +484,13 @@ impl ExprCollector<'_> {
                 let ret_type = e
                     .ret_type()
                     .and_then(|r| r.ty())
-                    .map(|it| Interned::new(TypeRef::from_ast(&self.ctx(), it)));
+                    .map(|it| Interned::new(TypeRef::from_ast(&this.ctx(), it)));
 
-                let prev_is_lowering_generator = self.is_lowering_generator;
-                self.is_lowering_generator = false;
+                let prev_is_lowering_generator = mem::take(&mut this.is_lowering_generator);
 
-                let body = self.collect_expr_opt(e.body());
+                let body = this.collect_expr_opt(e.body());
 
-                let closure_kind = if self.is_lowering_generator {
+                let closure_kind = if this.is_lowering_generator {
                     let movability = if e.static_token().is_some() {
                         Movability::Static
                     } else {
@@ -530,19 +502,21 @@ impl ExprCollector<'_> {
                 } else {
                     ClosureKind::Closure
                 };
-                self.is_lowering_generator = prev_is_lowering_generator;
-
-                self.alloc_expr(
-                    Expr::Closure {
-                        args: args.into(),
-                        arg_types: arg_types.into(),
-                        ret_type,
-                        body,
-                        closure_kind,
-                    },
-                    syntax_ptr,
-                )
-            }
+                this.is_lowering_generator = prev_is_lowering_generator;
+                let capture_by =
+                    if e.move_token().is_some() { CaptureBy::Value } else { CaptureBy::Ref };
+                this.is_lowering_generator = prev_is_lowering_generator;
+                this.current_binding_owner = prev_binding_owner;
+                this.body.exprs[result_expr_id] = Expr::Closure {
+                    args: args.into(),
+                    arg_types: arg_types.into(),
+                    ret_type,
+                    body,
+                    closure_kind,
+                    capture_by,
+                };
+                result_expr_id
+            }),
             ast::Expr::BinExpr(e) => {
                 let op = e.op_kind();
                 if let Some(ast::BinaryOp::Assignment { op: None }) = op {
@@ -581,7 +555,17 @@ impl ExprCollector<'_> {
                     }
                     ArrayExprKind::Repeat { initializer, repeat } => {
                         let initializer = self.collect_expr_opt(initializer);
-                        let repeat = self.collect_expr_opt(repeat);
+                        let repeat = self.with_label_rib(RibKind::Constant, |this| {
+                            if let Some(repeat) = repeat {
+                                let syntax_ptr = AstPtr::new(&repeat);
+                                this.collect_as_a_binding_owner_bad(
+                                    |this| this.collect_expr(repeat),
+                                    syntax_ptr,
+                                )
+                            } else {
+                                this.missing_expr()
+                            }
+                        });
                         self.alloc_expr(
                             Expr::Array(Array::Repeat { initializer, repeat }),
                             syntax_ptr,
@@ -627,23 +611,53 @@ impl ExprCollector<'_> {
         })
     }
 
+    fn initialize_binding_owner(
+        &mut self,
+        syntax_ptr: AstPtr<ast::Expr>,
+    ) -> (ExprId, Option<ExprId>) {
+        let result_expr_id = self.alloc_expr(Expr::Missing, syntax_ptr);
+        let prev_binding_owner = self.current_binding_owner.take();
+        self.current_binding_owner = Some(result_expr_id);
+        (result_expr_id, prev_binding_owner)
+    }
+
+    /// FIXME: This function is bad. It will produce a dangling `Missing` expr which wastes memory. Currently
+    /// it is used only for const blocks and repeat expressions, which are also hacky and ideally should have
+    /// their own body. Don't add more usage for this function so that we can remove this function after
+    /// separating those bodies.
+    fn collect_as_a_binding_owner_bad(
+        &mut self,
+        job: impl FnOnce(&mut ExprCollector<'_>) -> ExprId,
+        syntax_ptr: AstPtr<ast::Expr>,
+    ) -> ExprId {
+        let (id, prev_owner) = self.initialize_binding_owner(syntax_ptr);
+        let tmp = job(self);
+        self.body.exprs[id] = mem::replace(&mut self.body.exprs[tmp], Expr::Missing);
+        self.current_binding_owner = prev_owner;
+        id
+    }
+
     /// Desugar `try { <stmts>; <expr> }` into `'<new_label>: { <stmts>; ::std::ops::Try::from_output(<expr>) }`,
     /// `try { <stmts>; }` into `'<new_label>: { <stmts>; ::std::ops::Try::from_output(()) }`
     /// and save the `<new_label>` to use it as a break target for desugaring of the `?` operator.
-    fn collect_try_block(&mut self, e: BlockExpr) -> ExprId {
+    fn desugar_try_block(&mut self, e: BlockExpr) -> ExprId {
         let Some(try_from_output) = LangItem::TryTraitFromOutput.path(self.db, self.krate) else {
-            return self.alloc_expr_desugared(Expr::Missing);
+            return self.collect_block(e);
         };
-        let prev_try_block = self.current_try_block.take();
-        self.current_try_block =
-            Some(self.alloc_label_desugared(Label { name: Name::generate_new_name() }));
-        let expr_id = self.collect_block(e);
+        let label = self.alloc_label_desugared(Label { name: Name::generate_new_name() });
+        let old_label = self.current_try_block_label.replace(label);
+
+        let (btail, expr_id) = self.with_labeled_rib(label, |this| {
+            let mut btail = None;
+            let block = this.collect_block_(e, |id, statements, tail| {
+                btail = tail;
+                Expr::Block { id, statements, tail, label: Some(label) }
+            });
+            (btail, block)
+        });
+
         let callee = self.alloc_expr_desugared(Expr::Path(try_from_output));
-        let Expr::Block { label, tail, .. } = &mut self.body.exprs[expr_id] else {
-            unreachable!("It is the output of collect block");
-        };
-        *label = self.current_try_block;
-        let next_tail = match *tail {
+        let next_tail = match btail {
             Some(tail) => self.alloc_expr_desugared(Expr::Call {
                 callee,
                 args: Box::new([tail]),
@@ -662,10 +676,10 @@ impl ExprCollector<'_> {
             }
         };
         let Expr::Block { tail, .. } = &mut self.body.exprs[expr_id] else {
-            unreachable!("It is the output of collect block");
+            unreachable!("block was lowered to non-block");
         };
         *tail = Some(next_tail);
-        self.current_try_block = prev_try_block;
+        self.current_try_block_label = old_label;
         expr_id
     }
 
@@ -735,12 +749,13 @@ impl ExprCollector<'_> {
                     Expr::Call { callee, args: Box::new([x]), is_assignee_expr: false },
                     syntax_ptr.clone(),
                 );
-                if let Some(label) = self.current_try_block {
-                    let label = Some(self.body.labels[label].name.clone());
-                    self.alloc_expr(Expr::Break { expr: Some(result), label }, syntax_ptr.clone())
-                } else {
-                    self.alloc_expr(Expr::Return { expr: Some(result) }, syntax_ptr.clone())
-                }
+                self.alloc_expr(
+                    match self.current_try_block_label {
+                        Some(label) => Expr::Break { expr: Some(result), label: Some(label) },
+                        None => Expr::Return { expr: Some(result) },
+                    },
+                    syntax_ptr.clone(),
+                )
             },
         };
         let arms = Box::new([continue_arm, break_arm]);
@@ -785,7 +800,7 @@ impl ExprCollector<'_> {
                         krate: *krate,
                     });
                 }
-                Some(ExpandError::RecursionOverflowPosioned) => {
+                Some(ExpandError::RecursionOverflowPoisoned) => {
                     // Recursion limit has been reached in the macro expansion tree, but not in
                     // this very macro call. Don't add diagnostics to avoid duplication.
                 }
@@ -866,7 +881,7 @@ impl ExprCollector<'_> {
                 if self.check_cfg(&stmt).is_none() {
                     return;
                 }
-                let pat = self.collect_pat_opt(stmt.pat());
+                let pat = self.collect_pat_top(stmt.pat());
                 let type_ref =
                     stmt.ty().map(|it| Interned::new(TypeRef::from_ast(&self.ctx(), it)));
                 let initializer = stmt.initializer().map(|e| self.collect_expr(e));
@@ -911,10 +926,20 @@ impl ExprCollector<'_> {
         block: ast::BlockExpr,
         mk_block: impl FnOnce(Option<BlockId>, Box<[Statement]>, Option<ExprId>) -> Expr,
     ) -> ExprId {
-        let file_local_id = self.ast_id_map.ast_id(&block);
-        let ast_id = AstId::new(self.expander.current_file_id, file_local_id);
+        let block_has_items = {
+            let statement_has_item = block.statements().any(|stmt| match stmt {
+                ast::Stmt::Item(_) => true,
+                // Macro calls can be both items and expressions. The syntax library always treats
+                // them as expressions here, so we undo that.
+                ast::Stmt::ExprStmt(es) => matches!(es.expr(), Some(ast::Expr::MacroExpr(_))),
+                _ => false,
+            });
+            statement_has_item || matches!(block.tail_expr(), Some(ast::Expr::MacroExpr(_)))
+        };
 
-        let block_id = if ItemTree::block_has_items(self.db, ast_id.file_id, &block) {
+        let block_id = if block_has_items {
+            let file_local_id = self.ast_id_map.ast_id(&block);
+            let ast_id = AstId::new(self.expander.current_file_id, file_local_id);
             Some(self.db.intern_block(BlockLoc {
                 ast_id,
                 module: self.expander.def_map.module_id(self.expander.module),
@@ -966,32 +991,34 @@ impl ExprCollector<'_> {
         }
     }
 
-    fn collect_label(&mut self, ast_label: ast::Label) -> LabelId {
-        let label = Label {
-            name: ast_label.lifetime().as_ref().map_or_else(Name::missing, Name::new_lifetime),
-        };
-        self.alloc_label(label, AstPtr::new(&ast_label))
+    fn collect_labelled_block_opt(
+        &mut self,
+        label: Option<LabelId>,
+        expr: Option<ast::BlockExpr>,
+    ) -> ExprId {
+        match label {
+            Some(label) => self.with_labeled_rib(label, |this| this.collect_block_opt(expr)),
+            None => self.collect_block_opt(expr),
+        }
     }
 
-    fn collect_pat(&mut self, pat: ast::Pat) -> PatId {
-        self.collect_pat_(pat, &mut BindingList::default())
-    }
+    // region: patterns
 
-    fn collect_pat_opt(&mut self, pat: Option<ast::Pat>) -> PatId {
+    fn collect_pat_top(&mut self, pat: Option<ast::Pat>) -> PatId {
         match pat {
-            Some(pat) => self.collect_pat(pat),
+            Some(pat) => self.collect_pat(pat, &mut BindingList::default()),
             None => self.missing_pat(),
         }
     }
 
-    fn collect_pat_(&mut self, pat: ast::Pat, binding_list: &mut BindingList) -> PatId {
+    fn collect_pat(&mut self, pat: ast::Pat, binding_list: &mut BindingList) -> PatId {
         let pattern = match &pat {
             ast::Pat::IdentPat(bp) => {
                 let name = bp.name().map(|nr| nr.as_name()).unwrap_or_else(Name::missing);
 
                 let annotation =
                     BindingAnnotation::new(bp.mut_token().is_some(), bp.ref_token().is_some());
-                let subpat = bp.pat().map(|subpat| self.collect_pat_(subpat, binding_list));
+                let subpat = bp.pat().map(|subpat| self.collect_pat(subpat, binding_list));
 
                 let is_simple_ident_pat =
                     annotation == BindingAnnotation::Unannotated && subpat.is_none();
@@ -1045,7 +1072,7 @@ impl ExprCollector<'_> {
                 Pat::TupleStruct { path, args, ellipsis }
             }
             ast::Pat::RefPat(p) => {
-                let pat = self.collect_pat_opt_(p.pat(), binding_list);
+                let pat = self.collect_pat_opt(p.pat(), binding_list);
                 let mutability = Mutability::from_mutable(p.mut_token().is_some());
                 Pat::Ref { pat, mutability }
             }
@@ -1055,10 +1082,10 @@ impl ExprCollector<'_> {
                 path.map(Pat::Path).unwrap_or(Pat::Missing)
             }
             ast::Pat::OrPat(p) => {
-                let pats = p.pats().map(|p| self.collect_pat_(p, binding_list)).collect();
+                let pats = p.pats().map(|p| self.collect_pat(p, binding_list)).collect();
                 Pat::Or(pats)
             }
-            ast::Pat::ParenPat(p) => return self.collect_pat_opt_(p.pat(), binding_list),
+            ast::Pat::ParenPat(p) => return self.collect_pat_opt(p.pat(), binding_list),
             ast::Pat::TuplePat(p) => {
                 let (args, ellipsis) = self.collect_tuple_pat(p.fields(), binding_list);
                 Pat::Tuple { args, ellipsis }
@@ -1073,7 +1100,7 @@ impl ExprCollector<'_> {
                     .fields()
                     .filter_map(|f| {
                         let ast_pat = f.pat()?;
-                        let pat = self.collect_pat_(ast_pat, binding_list);
+                        let pat = self.collect_pat(ast_pat, binding_list);
                         let name = f.field_name()?.as_name();
                         Some(RecordFieldPat { name, pat })
                     })
@@ -1092,15 +1119,9 @@ impl ExprCollector<'_> {
 
                 // FIXME properly handle `RestPat`
                 Pat::Slice {
-                    prefix: prefix
-                        .into_iter()
-                        .map(|p| self.collect_pat_(p, binding_list))
-                        .collect(),
-                    slice: slice.map(|p| self.collect_pat_(p, binding_list)),
-                    suffix: suffix
-                        .into_iter()
-                        .map(|p| self.collect_pat_(p, binding_list))
-                        .collect(),
+                    prefix: prefix.into_iter().map(|p| self.collect_pat(p, binding_list)).collect(),
+                    slice: slice.map(|p| self.collect_pat(p, binding_list)),
+                    suffix: suffix.into_iter().map(|p| self.collect_pat(p, binding_list)).collect(),
                 }
             }
             #[rustfmt::skip] // https://github.com/rust-lang/rustfmt/issues/5676
@@ -1131,12 +1152,18 @@ impl ExprCollector<'_> {
                 Pat::Missing
             }
             ast::Pat::BoxPat(boxpat) => {
-                let inner = self.collect_pat_opt_(boxpat.pat(), binding_list);
+                let inner = self.collect_pat_opt(boxpat.pat(), binding_list);
                 Pat::Box { inner }
             }
             ast::Pat::ConstBlockPat(const_block_pat) => {
-                if let Some(expr) = const_block_pat.block_expr() {
-                    let expr_id = self.collect_block(expr);
+                if let Some(block) = const_block_pat.block_expr() {
+                    let expr_id = self.with_label_rib(RibKind::Constant, |this| {
+                        let syntax_ptr = AstPtr::new(&block.clone().into());
+                        this.collect_as_a_binding_owner_bad(
+                            |this| this.collect_block(block),
+                            syntax_ptr,
+                        )
+                    });
                     Pat::ConstBlock(expr_id)
                 } else {
                     Pat::Missing
@@ -1148,7 +1175,7 @@ impl ExprCollector<'_> {
                     let src = self.expander.to_source(Either::Left(AstPtr::new(&pat)));
                     let pat =
                         self.collect_macro_call(call, macro_ptr, true, |this, expanded_pat| {
-                            this.collect_pat_opt_(expanded_pat, binding_list)
+                            this.collect_pat_opt(expanded_pat, binding_list)
                         });
                     self.source_map.pat_map.insert(src, pat);
                     return pat;
@@ -1162,9 +1189,9 @@ impl ExprCollector<'_> {
         self.alloc_pat(pattern, Either::Left(ptr))
     }
 
-    fn collect_pat_opt_(&mut self, pat: Option<ast::Pat>, binding_list: &mut BindingList) -> PatId {
+    fn collect_pat_opt(&mut self, pat: Option<ast::Pat>, binding_list: &mut BindingList) -> PatId {
         match pat {
-            Some(pat) => self.collect_pat_(pat, binding_list),
+            Some(pat) => self.collect_pat(pat, binding_list),
             None => self.missing_pat(),
         }
     }
@@ -1180,12 +1207,14 @@ impl ExprCollector<'_> {
         // We want to skip the `..` pattern here, since we account for it above.
         let args = args
             .filter(|p| !matches!(p, ast::Pat::RestPat(_)))
-            .map(|p| self.collect_pat_(p, binding_list))
+            .map(|p| self.collect_pat(p, binding_list))
             .collect();
 
         (args, ellipsis)
     }
 
+    // endregion: patterns
+
     /// Returns `None` (and emits diagnostics) when `owner` if `#[cfg]`d out, and `Some(())` when
     /// not.
     fn check_cfg(&mut self, owner: &dyn ast::HasAttrs) -> Option<()> {
@@ -1213,42 +1242,118 @@ impl ExprCollector<'_> {
     fn add_definition_to_binding(&mut self, binding_id: BindingId, pat_id: PatId) {
         self.body.bindings[binding_id].definitions.push(pat_id);
     }
-}
 
-impl From<ast::LiteralKind> for Literal {
-    fn from(ast_lit_kind: ast::LiteralKind) -> Self {
-        match ast_lit_kind {
-            // FIXME: these should have actual values filled in, but unsure on perf impact
-            LiteralKind::IntNumber(lit) => {
-                if let builtin @ Some(_) = lit.suffix().and_then(BuiltinFloat::from_suffix) {
-                    Literal::Float(
-                        FloatTypeWrapper::new(lit.float_value().unwrap_or(Default::default())),
-                        builtin,
-                    )
-                } else if let builtin @ Some(_) = lit.suffix().and_then(BuiltinUint::from_suffix) {
-                    Literal::Uint(lit.value().unwrap_or(0), builtin)
-                } else {
-                    let builtin = lit.suffix().and_then(BuiltinInt::from_suffix);
-                    Literal::Int(lit.value().unwrap_or(0) as i128, builtin)
+    // region: labels
+
+    fn collect_label(&mut self, ast_label: ast::Label) -> LabelId {
+        let label = Label {
+            name: ast_label.lifetime().as_ref().map_or_else(Name::missing, Name::new_lifetime),
+        };
+        self.alloc_label(label, AstPtr::new(&ast_label))
+    }
+
+    fn resolve_label(
+        &self,
+        lifetime: Option<ast::Lifetime>,
+    ) -> Result<Option<LabelId>, BodyDiagnostic> {
+        let Some(lifetime) = lifetime else {
+            return Ok(None)
+        };
+        let name = Name::new_lifetime(&lifetime);
+
+        for (rib_idx, rib) in self.label_ribs.iter().enumerate().rev() {
+            if let Some((label_name, id)) = &rib.label {
+                if *label_name == name {
+                    return if self.is_label_valid_from_rib(rib_idx) {
+                        Ok(Some(*id))
+                    } else {
+                        Err(BodyDiagnostic::UnreachableLabel {
+                            name,
+                            node: InFile::new(
+                                self.expander.current_file_id,
+                                AstPtr::new(&lifetime),
+                            ),
+                        })
+                    };
                 }
             }
-            LiteralKind::FloatNumber(lit) => {
-                let ty = lit.suffix().and_then(BuiltinFloat::from_suffix);
-                Literal::Float(FloatTypeWrapper::new(lit.value().unwrap_or(Default::default())), ty)
-            }
-            LiteralKind::ByteString(bs) => {
-                let text = bs.value().map(Box::from).unwrap_or_else(Default::default);
-                Literal::ByteString(text)
-            }
-            LiteralKind::String(s) => {
-                let text = s.value().map(Box::from).unwrap_or_else(Default::default);
-                Literal::String(text)
-            }
-            LiteralKind::Byte(b) => {
-                Literal::Uint(b.value().unwrap_or_default() as u128, Some(BuiltinUint::U8))
-            }
-            LiteralKind::Char(c) => Literal::Char(c.value().unwrap_or_default()),
-            LiteralKind::Bool(val) => Literal::Bool(val),
         }
+
+        Err(BodyDiagnostic::UndeclaredLabel {
+            name,
+            node: InFile::new(self.expander.current_file_id, AstPtr::new(&lifetime)),
+        })
+    }
+
+    fn is_label_valid_from_rib(&self, rib_index: usize) -> bool {
+        !self.label_ribs[rib_index + 1..].iter().any(|rib| rib.kind.is_label_barrier())
+    }
+
+    fn with_label_rib<T>(&mut self, kind: RibKind, f: impl FnOnce(&mut Self) -> T) -> T {
+        self.label_ribs.push(LabelRib::new(kind));
+        let res = f(self);
+        self.label_ribs.pop();
+        res
+    }
+
+    fn with_labeled_rib<T>(&mut self, label: LabelId, f: impl FnOnce(&mut Self) -> T) -> T {
+        self.label_ribs.push(LabelRib::new_normal((self.body[label].name.clone(), label)));
+        let res = f(self);
+        self.label_ribs.pop();
+        res
+    }
+    // endregion: labels
+}
+
+impl ExprCollector<'_> {
+    fn alloc_expr(&mut self, expr: Expr, ptr: ExprPtr) -> ExprId {
+        let src = self.expander.to_source(ptr);
+        let id = self.body.exprs.alloc(expr);
+        self.source_map.expr_map_back.insert(id, src.clone());
+        self.source_map.expr_map.insert(src, id);
+        id
+    }
+    // FIXME: desugared exprs don't have ptr, that's wrong and should be fixed somehow.
+    fn alloc_expr_desugared(&mut self, expr: Expr) -> ExprId {
+        self.body.exprs.alloc(expr)
+    }
+    fn missing_expr(&mut self) -> ExprId {
+        self.alloc_expr_desugared(Expr::Missing)
+    }
+
+    fn alloc_binding(&mut self, name: Name, mode: BindingAnnotation) -> BindingId {
+        self.body.bindings.alloc(Binding {
+            name,
+            mode,
+            definitions: SmallVec::new(),
+            owner: self.current_binding_owner,
+        })
+    }
+
+    fn alloc_pat(&mut self, pat: Pat, ptr: PatPtr) -> PatId {
+        let src = self.expander.to_source(ptr);
+        let id = self.body.pats.alloc(pat);
+        self.source_map.pat_map_back.insert(id, src.clone());
+        self.source_map.pat_map.insert(src, id);
+        id
+    }
+    // FIXME: desugared pats don't have ptr, that's wrong and should be fixed somehow.
+    fn alloc_pat_desugared(&mut self, pat: Pat) -> PatId {
+        self.body.pats.alloc(pat)
+    }
+    fn missing_pat(&mut self) -> PatId {
+        self.body.pats.alloc(Pat::Missing)
+    }
+
+    fn alloc_label(&mut self, label: Label, ptr: LabelPtr) -> LabelId {
+        let src = self.expander.to_source(ptr);
+        let id = self.body.labels.alloc(label);
+        self.source_map.label_map_back.insert(id, src.clone());
+        self.source_map.label_map.insert(src, id);
+        id
+    }
+    // FIXME: desugared labels don't have ptr, that's wrong and should be fixed somehow.
+    fn alloc_label_desugared(&mut self, label: Label) -> LabelId {
+        self.body.labels.alloc(label)
     }
 }
diff --git a/crates/hir-def/src/body/pretty.rs b/crates/hir-def/src/body/pretty.rs
index 8c9d77620e1..c3bd99b9487 100644
--- a/crates/hir-def/src/body/pretty.rs
+++ b/crates/hir-def/src/body/pretty.rs
@@ -5,7 +5,9 @@ use std::fmt::{self, Write};
 use syntax::ast::HasName;
 
 use crate::{
-    expr::{Array, BindingAnnotation, BindingId, ClosureKind, Literal, Movability, Statement},
+    hir::{
+        Array, BindingAnnotation, BindingId, CaptureBy, ClosureKind, Literal, Movability, Statement,
+    },
     pretty::{print_generic_args, print_path, print_type_ref},
     type_ref::TypeRef,
 };
@@ -13,20 +15,16 @@ use crate::{
 use super::*;
 
 pub(super) fn print_body_hir(db: &dyn DefDatabase, body: &Body, owner: DefWithBodyId) -> String {
-    let needs_semi;
     let header = match owner {
         DefWithBodyId::FunctionId(it) => {
-            needs_semi = false;
             let item_tree_id = it.lookup(db).id;
-            format!("fn {}(…) ", item_tree_id.item_tree(db)[item_tree_id.value].name)
+            format!("fn {}", item_tree_id.item_tree(db)[item_tree_id.value].name)
         }
         DefWithBodyId::StaticId(it) => {
-            needs_semi = true;
             let item_tree_id = it.lookup(db).id;
             format!("static {} = ", item_tree_id.item_tree(db)[item_tree_id.value].name)
         }
         DefWithBodyId::ConstId(it) => {
-            needs_semi = true;
             let item_tree_id = it.lookup(db).id;
             let name = match &item_tree_id.item_tree(db)[item_tree_id.value].name {
                 Some(name) => name.to_string(),
@@ -35,7 +33,6 @@ pub(super) fn print_body_hir(db: &dyn DefDatabase, body: &Body, owner: DefWithBo
             format!("const {name} = ")
         }
         DefWithBodyId::VariantId(it) => {
-            needs_semi = false;
             let src = it.parent.child_source(db);
             let variant = &src.value[it.local_id];
             let name = match &variant.name() {
@@ -47,8 +44,18 @@ pub(super) fn print_body_hir(db: &dyn DefDatabase, body: &Body, owner: DefWithBo
     };
 
     let mut p = Printer { body, buf: header, indent_level: 0, needs_indent: false };
+    if let DefWithBodyId::FunctionId(it) = owner {
+        p.buf.push('(');
+        body.params.iter().zip(&db.function_data(it).params).for_each(|(&param, ty)| {
+            p.print_pat(param);
+            p.buf.push(':');
+            p.print_type_ref(ty);
+        });
+        p.buf.push(')');
+        p.buf.push(' ');
+    }
     p.print_expr(body.body_expr);
-    if needs_semi {
+    if matches!(owner, DefWithBodyId::StaticId(_) | DefWithBodyId::ConstId(_)) {
         p.buf.push(';');
     }
     p.buf
@@ -219,14 +226,14 @@ impl<'a> Printer<'a> {
             }
             Expr::Continue { label } => {
                 w!(self, "continue");
-                if let Some(label) = label {
-                    w!(self, " {}", label);
+                if let Some(lbl) = label {
+                    w!(self, " {}", self.body[*lbl].name);
                 }
             }
             Expr::Break { expr, label } => {
                 w!(self, "break");
-                if let Some(label) = label {
-                    w!(self, " {}", label);
+                if let Some(lbl) = label {
+                    w!(self, " {}", self.body[*lbl].name);
                 }
                 if let Some(expr) = expr {
                     self.whitespace();
@@ -355,7 +362,7 @@ impl<'a> Printer<'a> {
                 self.print_expr(*index);
                 w!(self, "]");
             }
-            Expr::Closure { args, arg_types, ret_type, body, closure_kind } => {
+            Expr::Closure { args, arg_types, ret_type, body, closure_kind, capture_by } => {
                 match closure_kind {
                     ClosureKind::Generator(Movability::Static) => {
                         w!(self, "static ");
@@ -365,6 +372,12 @@ impl<'a> Printer<'a> {
                     }
                     _ => (),
                 }
+                match capture_by {
+                    CaptureBy::Value => {
+                        w!(self, "move ");
+                    }
+                    CaptureBy::Ref => (),
+                }
                 w!(self, "|");
                 for (i, (pat, ty)) in args.iter().zip(arg_types.iter()).enumerate() {
                     if i != 0 {
diff --git a/crates/hir-def/src/body/scope.rs b/crates/hir-def/src/body/scope.rs
index 8fe20da7477..f3f9aa79461 100644
--- a/crates/hir-def/src/body/scope.rs
+++ b/crates/hir-def/src/body/scope.rs
@@ -8,7 +8,7 @@ use rustc_hash::FxHashMap;
 use crate::{
     body::Body,
     db::DefDatabase,
-    expr::{Binding, BindingId, Expr, ExprId, LabelId, Pat, PatId, Statement},
+    hir::{Binding, BindingId, Expr, ExprId, LabelId, Pat, PatId, Statement},
     BlockId, DefWithBodyId,
 };
 
diff --git a/crates/hir-def/src/child_by_source.rs b/crates/hir-def/src/child_by_source.rs
index 68b57acca2a..21180fcbdae 100644
--- a/crates/hir-def/src/child_by_source.rs
+++ b/crates/hir-def/src/child_by_source.rs
@@ -10,9 +10,8 @@ use syntax::ast::HasDocComments;
 
 use crate::{
     db::DefDatabase,
-    dyn_map::DynMap,
+    dyn_map::{keys, DynMap},
     item_scope::ItemScope,
-    keys,
     src::{HasChildSource, HasSource},
     AdtId, AssocItemId, DefWithBodyId, EnumId, EnumVariantId, FieldId, ImplId, Lookup, MacroId,
     ModuleDefId, ModuleId, TraitId, VariantId,
diff --git a/crates/hir-def/src/data.rs b/crates/hir-def/src/data.rs
index 431140a665d..98cf69c1689 100644
--- a/crates/hir-def/src/data.rs
+++ b/crates/hir-def/src/data.rs
@@ -1,5 +1,7 @@
 //! Contains basic data about various HIR declarations.
 
+pub mod adt;
+
 use std::sync::Arc;
 
 use hir_expand::{name::Name, AstId, ExpandResult, HirFileId, InFile, MacroCallId, MacroDefKind};
@@ -28,7 +30,7 @@ use crate::{
 #[derive(Debug, Clone, PartialEq, Eq)]
 pub struct FunctionData {
     pub name: Name,
-    pub params: Vec<(Option<Name>, Interned<TypeRef>)>,
+    pub params: Vec<Interned<TypeRef>>,
     pub ret_type: Interned<TypeRef>,
     pub attrs: Attrs,
     pub visibility: RawVisibility,
@@ -98,7 +100,7 @@ impl FunctionData {
             params: enabled_params
                 .clone()
                 .filter_map(|id| match &item_tree[id] {
-                    Param::Normal(name, ty) => Some((name.clone(), ty.clone())),
+                    Param::Normal(ty) => Some(ty.clone()),
                     Param::Varargs => None,
                 })
                 .collect(),
diff --git a/crates/hir-def/src/adt.rs b/crates/hir-def/src/data/adt.rs
index 7a5025aa6a5..0b6a51fe764 100644
--- a/crates/hir-def/src/adt.rs
+++ b/crates/hir-def/src/data/adt.rs
@@ -473,7 +473,7 @@ fn lower_struct(
     trace: &mut Trace<FieldData, Either<ast::TupleField, ast::RecordField>>,
     ast: &InFile<ast::StructKind>,
 ) -> StructKind {
-    let ctx = LowerCtx::new(db, ast.file_id);
+    let ctx = LowerCtx::new(db, &expander.hygiene(), ast.file_id);
 
     match &ast.value {
         ast::StructKind::Tuple(fl) => {
diff --git a/crates/hir-def/src/db.rs b/crates/hir-def/src/db.rs
index 8d236aca367..2dfe4b62648 100644
--- a/crates/hir-def/src/db.rs
+++ b/crates/hir-def/src/db.rs
@@ -9,10 +9,10 @@ use la_arena::ArenaMap;
 use syntax::{ast, AstPtr};
 
 use crate::{
-    adt::{EnumData, StructData},
     attr::{Attrs, AttrsWithOwner},
     body::{scope::ExprScopes, Body, BodySourceMap},
     data::{
+        adt::{EnumData, StructData},
         ConstData, FunctionData, ImplData, Macro2Data, MacroRulesData, ProcMacroData, StaticData,
         TraitAliasData, TraitData, TypeAliasData,
     },
diff --git a/crates/hir-def/src/dyn_map.rs b/crates/hir-def/src/dyn_map.rs
index 166aa04da04..63138aa6ad7 100644
--- a/crates/hir-def/src/dyn_map.rs
+++ b/crates/hir-def/src/dyn_map.rs
@@ -21,6 +21,8 @@
 //!
 //! This is a work of fiction. Any similarities to Kotlin's `BindingContext` are
 //! a coincidence.
+pub mod keys;
+
 use std::{
     hash::Hash,
     marker::PhantomData,
diff --git a/crates/hir-def/src/keys.rs b/crates/hir-def/src/dyn_map/keys.rs
index f30be6b64e3..f30be6b64e3 100644
--- a/crates/hir-def/src/keys.rs
+++ b/crates/hir-def/src/dyn_map/keys.rs
diff --git a/crates/hir-def/src/generics.rs b/crates/hir-def/src/generics.rs
index e4912fa8a64..30edaed1095 100644
--- a/crates/hir-def/src/generics.rs
+++ b/crates/hir-def/src/generics.rs
@@ -12,7 +12,6 @@ use hir_expand::{
 use intern::Interned;
 use la_arena::{Arena, ArenaMap, Idx};
 use once_cell::unsync::Lazy;
-use std::ops::DerefMut;
 use stdx::impl_from;
 use syntax::ast::{self, HasGenericParams, HasName, HasTypeBounds};
 
@@ -20,8 +19,7 @@ use crate::{
     body::{Expander, LowerCtx},
     child_by_source::ChildBySource,
     db::DefDatabase,
-    dyn_map::DynMap,
-    keys,
+    dyn_map::{keys, DynMap},
     src::{HasChildSource, HasSource},
     type_ref::{LifetimeRef, TypeBound, TypeRef},
     AdtId, ConstParamId, GenericDefId, HasModule, LifetimeParamId, LocalLifetimeParamId,
@@ -177,7 +175,7 @@ impl GenericParams {
                 // Don't create an `Expander` nor call `loc.source(db)` if not needed since this
                 // causes a reparse after the `ItemTree` has been created.
                 let mut expander = Lazy::new(|| Expander::new(db, loc.source(db).file_id, module));
-                for (_, param) in &func_data.params {
+                for param in &func_data.params {
                     generic_params.fill_implicit_impl_trait_args(db, &mut expander, param);
                 }
 
@@ -329,7 +327,7 @@ impl GenericParams {
     pub(crate) fn fill_implicit_impl_trait_args(
         &mut self,
         db: &dyn DefDatabase,
-        expander: &mut impl DerefMut<Target = Expander>,
+        expander: &mut Expander,
         type_ref: &TypeRef,
     ) {
         type_ref.walk(&mut |type_ref| {
@@ -351,7 +349,7 @@ impl GenericParams {
                 let macro_call = mc.to_node(db.upcast());
                 match expander.enter_expand::<ast::Type>(db, macro_call) {
                     Ok(ExpandResult { value: Some((mark, expanded)), .. }) => {
-                        let ctx = LowerCtx::new(db, expander.current_file_id());
+                        let ctx = expander.ctx(db);
                         let type_ref = TypeRef::from_ast(&ctx, expanded);
                         self.fill_implicit_impl_trait_args(db, expander, &type_ref);
                         expander.exit(db, mark);
diff --git a/crates/hir-def/src/expr.rs b/crates/hir-def/src/hir.rs
index 443594d2717..8709ad0e99b 100644
--- a/crates/hir-def/src/expr.rs
+++ b/crates/hir-def/src/hir.rs
@@ -12,12 +12,15 @@
 //!
 //! See also a neighboring `body` module.
 
+pub mod type_ref;
+
 use std::fmt;
 
 use hir_expand::name::Name;
 use intern::Interned;
 use la_arena::{Idx, RawIdx};
 use smallvec::SmallVec;
+use syntax::ast;
 
 use crate::{
     builtin_type::{BuiltinFloat, BuiltinInt, BuiltinUint},
@@ -28,10 +31,10 @@ use crate::{
 
 pub use syntax::ast::{ArithOp, BinaryOp, CmpOp, LogicOp, Ordering, RangeOp, UnaryOp};
 
-pub type ExprId = Idx<Expr>;
-
 pub type BindingId = Idx<Binding>;
 
+pub type ExprId = Idx<Expr>;
+
 /// FIXME: this is a hacky function which should be removed
 pub(crate) fn dummy_expr_id() -> ExprId {
     ExprId::from_raw(RawIdx::from(u32::MAX))
@@ -102,6 +105,45 @@ impl Literal {
     }
 }
 
+impl From<ast::LiteralKind> for Literal {
+    fn from(ast_lit_kind: ast::LiteralKind) -> Self {
+        use ast::LiteralKind;
+        match ast_lit_kind {
+            // FIXME: these should have actual values filled in, but unsure on perf impact
+            LiteralKind::IntNumber(lit) => {
+                if let builtin @ Some(_) = lit.suffix().and_then(BuiltinFloat::from_suffix) {
+                    Literal::Float(
+                        FloatTypeWrapper::new(lit.float_value().unwrap_or(Default::default())),
+                        builtin,
+                    )
+                } else if let builtin @ Some(_) = lit.suffix().and_then(BuiltinUint::from_suffix) {
+                    Literal::Uint(lit.value().unwrap_or(0), builtin)
+                } else {
+                    let builtin = lit.suffix().and_then(BuiltinInt::from_suffix);
+                    Literal::Int(lit.value().unwrap_or(0) as i128, builtin)
+                }
+            }
+            LiteralKind::FloatNumber(lit) => {
+                let ty = lit.suffix().and_then(BuiltinFloat::from_suffix);
+                Literal::Float(FloatTypeWrapper::new(lit.value().unwrap_or(Default::default())), ty)
+            }
+            LiteralKind::ByteString(bs) => {
+                let text = bs.value().map(Box::from).unwrap_or_else(Default::default);
+                Literal::ByteString(text)
+            }
+            LiteralKind::String(s) => {
+                let text = s.value().map(Box::from).unwrap_or_else(Default::default);
+                Literal::String(text)
+            }
+            LiteralKind::Byte(b) => {
+                Literal::Uint(b.value().unwrap_or_default() as u128, Some(BuiltinUint::U8))
+            }
+            LiteralKind::Char(c) => Literal::Char(c.value().unwrap_or_default()),
+            LiteralKind::Bool(val) => Literal::Bool(val),
+        }
+    }
+}
+
 #[derive(Debug, Clone, Eq, PartialEq)]
 pub enum Expr {
     /// This is produced if the syntax tree does not have a required expression piece.
@@ -168,11 +210,11 @@ pub enum Expr {
         arms: Box<[MatchArm]>,
     },
     Continue {
-        label: Option<Name>,
+        label: Option<LabelId>,
     },
     Break {
         expr: Option<ExprId>,
-        label: Option<Name>,
+        label: Option<LabelId>,
     },
     Return {
         expr: Option<ExprId>,
@@ -233,6 +275,7 @@ pub enum Expr {
         ret_type: Option<Interned<TypeRef>>,
         body: ExprId,
         closure_kind: ClosureKind,
+        capture_by: CaptureBy,
     },
     Tuple {
         exprs: Box<[ExprId]>,
@@ -251,6 +294,14 @@ pub enum ClosureKind {
 }
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum CaptureBy {
+    /// `move |x| y + x`.
+    Value,
+    /// `move` keyword was not specified.
+    Ref,
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
 pub enum Movability {
     Static,
     Movable,
@@ -442,6 +493,22 @@ pub struct Binding {
     pub name: Name,
     pub mode: BindingAnnotation,
     pub definitions: SmallVec<[PatId; 1]>,
+    /// Id of the closure/generator that owns this binding. If it is owned by the
+    /// top level expression, this field would be `None`.
+    pub owner: Option<ExprId>,
+}
+
+impl Binding {
+    pub fn is_upvar(&self, relative_to: ExprId) -> bool {
+        match self.owner {
+            Some(x) => {
+                // We assign expression ids in a way that outer closures will recieve
+                // a lower id
+                x.into_raw() < relative_to.into_raw()
+            }
+            None => true,
+        }
+    }
 }
 
 #[derive(Debug, Clone, Eq, PartialEq)]
diff --git a/crates/hir-def/src/type_ref.rs b/crates/hir-def/src/hir/type_ref.rs
index 8e30f429a9f..0e2c0d864dc 100644
--- a/crates/hir-def/src/type_ref.rs
+++ b/crates/hir-def/src/hir/type_ref.rs
@@ -13,7 +13,7 @@ use syntax::ast::{self, HasName};
 use crate::{
     body::LowerCtx,
     builtin_type::{BuiltinInt, BuiltinType, BuiltinUint},
-    expr::Literal,
+    hir::Literal,
     path::Path,
 };
 
diff --git a/crates/hir-def/src/item_tree.rs b/crates/hir-def/src/item_tree.rs
index d5b973751dd..8546d36d798 100644
--- a/crates/hir-def/src/item_tree.rs
+++ b/crates/hir-def/src/item_tree.rs
@@ -152,14 +152,6 @@ impl ItemTree {
         &self.top_level
     }
 
-    pub fn block_has_items(
-        db: &dyn DefDatabase,
-        file_id: HirFileId,
-        block: &ast::BlockExpr,
-    ) -> bool {
-        lower::Ctx::new(db, file_id).block_has_items(block)
-    }
-
     /// Returns the inner attributes of the source file.
     pub fn top_level_attrs(&self, db: &dyn DefDatabase, krate: CrateId) -> Attrs {
         Attrs::filter(
@@ -614,7 +606,7 @@ pub struct Function {
 
 #[derive(Debug, Clone, Eq, PartialEq)]
 pub enum Param {
-    Normal(Option<Name>, Interned<TypeRef>),
+    Normal(Interned<TypeRef>),
     Varargs,
 }
 
diff --git a/crates/hir-def/src/item_tree/lower.rs b/crates/hir-def/src/item_tree/lower.rs
index 5bbf8e52e8b..c67c8bb4401 100644
--- a/crates/hir-def/src/item_tree/lower.rs
+++ b/crates/hir-def/src/item_tree/lower.rs
@@ -29,7 +29,7 @@ impl<'a> Ctx<'a> {
             db,
             tree: ItemTree::default(),
             source_ast_id_map: db.ast_id_map(file),
-            body_ctx: crate::body::LowerCtx::new(db, file),
+            body_ctx: crate::body::LowerCtx::with_file_id(db, file),
         }
     }
 
@@ -101,34 +101,6 @@ impl<'a> Ctx<'a> {
         self.tree
     }
 
-    pub(super) fn block_has_items(mut self, block: &ast::BlockExpr) -> bool {
-        let statement_has_item = block
-            .statements()
-            .find_map(|stmt| match stmt {
-                ast::Stmt::Item(item) => self.lower_mod_item(&item),
-                // Macro calls can be both items and expressions. The syntax library always treats
-                // them as expressions here, so we undo that.
-                ast::Stmt::ExprStmt(es) => match es.expr()? {
-                    ast::Expr::MacroExpr(expr) => self.lower_mod_item(&expr.macro_call()?.into()),
-                    _ => None,
-                },
-                _ => None,
-            })
-            .is_some();
-        if statement_has_item {
-            return true;
-        }
-
-        if let Some(ast::Expr::MacroExpr(expr)) = block.tail_expr() {
-            if let Some(call) = expr.macro_call() {
-                if let Some(_) = self.lower_mod_item(&call.into()) {
-                    return true;
-                }
-            }
-        }
-        false
-    }
-
     fn data(&mut self) -> &mut ItemTreeData {
         self.tree.data_mut()
     }
@@ -321,7 +293,7 @@ impl<'a> Ctx<'a> {
                     }
                 };
                 let ty = Interned::new(self_type);
-                let idx = self.data().params.alloc(Param::Normal(None, ty));
+                let idx = self.data().params.alloc(Param::Normal(ty));
                 self.add_attrs(
                     idx.into(),
                     RawAttrs::new(self.db.upcast(), &self_param, self.hygiene()),
@@ -334,19 +306,7 @@ impl<'a> Ctx<'a> {
                     None => {
                         let type_ref = TypeRef::from_ast_opt(&self.body_ctx, param.ty());
                         let ty = Interned::new(type_ref);
-                        let mut pat = param.pat();
-                        // FIXME: This really shouldn't be here, in fact FunctionData/ItemTree's function shouldn't know about
-                        // pattern names at all
-                        let name = 'name: loop {
-                            match pat {
-                                Some(ast::Pat::RefPat(ref_pat)) => pat = ref_pat.pat(),
-                                Some(ast::Pat::IdentPat(ident)) => {
-                                    break 'name ident.name().map(|it| it.as_name())
-                                }
-                                _ => break 'name None,
-                            }
-                        };
-                        self.data().params.alloc(Param::Normal(name, ty))
+                        self.data().params.alloc(Param::Normal(ty))
                     }
                 };
                 self.add_attrs(idx.into(), RawAttrs::new(self.db.upcast(), &param, self.hygiene()));
diff --git a/crates/hir-def/src/item_tree/pretty.rs b/crates/hir-def/src/item_tree/pretty.rs
index edd5c3b1151..94c5157386e 100644
--- a/crates/hir-def/src/item_tree/pretty.rs
+++ b/crates/hir-def/src/item_tree/pretty.rs
@@ -257,21 +257,15 @@ impl<'a> Printer<'a> {
                 w!(self, "(");
                 if !params.is_empty() {
                     self.indented(|this| {
-                        for (i, param) in params.clone().enumerate() {
+                        for param in params.clone() {
                             this.print_attrs_of(param);
                             match &this.tree[param] {
-                                Param::Normal(name, ty) => {
-                                    match name {
-                                        Some(name) => w!(this, "{}: ", name),
-                                        None => w!(this, "_: "),
+                                Param::Normal(ty) => {
+                                    if flags.contains(FnFlags::HAS_SELF_PARAM) {
+                                        w!(this, "self: ");
                                     }
                                     this.print_type_ref(ty);
-                                    w!(this, ",");
-                                    if flags.contains(FnFlags::HAS_SELF_PARAM) && i == 0 {
-                                        wln!(this, "  // self");
-                                    } else {
-                                        wln!(this);
-                                    }
+                                    wln!(this, ",");
                                 }
                                 Param::Varargs => {
                                     wln!(this, "...");
diff --git a/crates/hir-def/src/item_tree/tests.rs b/crates/hir-def/src/item_tree/tests.rs
index e30d9652bb5..1b7564f7a99 100644
--- a/crates/hir-def/src/item_tree/tests.rs
+++ b/crates/hir-def/src/item_tree/tests.rs
@@ -165,7 +165,7 @@ trait Tr: SuperTrait + 'lifetime {
     fn method(&self);
 }
         "#,
-        expect![[r##"
+        expect![[r#"
             pub static mut ST: () = _;
 
             pub(self) const _: Anon = _;
@@ -174,8 +174,8 @@ trait Tr: SuperTrait + 'lifetime {
             #[inner_attr_in_fn]
             pub(self) fn f(
                 #[attr]
-                arg: u8,
-                _: (),
+                u8,
+                (),
             ) -> () { ... }
 
             pub(self) trait Tr<Self>
@@ -186,10 +186,10 @@ trait Tr: SuperTrait + 'lifetime {
                 pub(self) type Assoc: AssocBound = Default;
 
                 pub(self) fn method(
-                    _: &Self,  // self
+                    self: &Self,
                 ) -> ();
             }
-        "##]],
+        "#]],
     );
 }
 
@@ -336,7 +336,7 @@ trait Tr<'a, T: 'a>: Super where Self: for<'a> Tr<'a, T> {}
                 T: 'b
             {
                 pub(self) fn f<G>(
-                    arg: impl Copy,
+                    impl Copy,
                 ) -> impl Copy
                 where
                     G: 'a { ... }
diff --git a/crates/hir-def/src/layout.rs b/crates/hir-def/src/layout.rs
index 49b1190ad46..873936b5b76 100644
--- a/crates/hir-def/src/layout.rs
+++ b/crates/hir-def/src/layout.rs
@@ -92,6 +92,7 @@ pub enum LayoutError {
     SizeOverflow,
     TargetLayoutNotAvailable,
     HasPlaceholder,
+    HasErrorType,
     NotImplemented,
     Unknown,
 }
diff --git a/crates/hir-def/src/lib.rs b/crates/hir-def/src/lib.rs
index 8c2e93f0905..65c33322b1d 100644
--- a/crates/hir-def/src/lib.rs
+++ b/crates/hir-def/src/lib.rs
@@ -18,24 +18,21 @@ pub mod db;
 
 pub mod attr;
 pub mod path;
-pub mod type_ref;
 pub mod builtin_type;
-pub mod builtin_attr;
 pub mod per_ns;
 pub mod item_scope;
 
 pub mod dyn_map;
-pub mod keys;
 
 pub mod item_tree;
 
-pub mod adt;
 pub mod data;
 pub mod generics;
 pub mod lang_item;
 pub mod layout;
 
-pub mod expr;
+pub mod hir;
+pub use self::hir::type_ref;
 pub mod body;
 pub mod resolver;
 
@@ -82,8 +79,8 @@ use syntax::ast;
 use ::tt::token_id as tt;
 
 use crate::{
-    adt::VariantData,
     builtin_type::BuiltinType,
+    data::adt::VariantData,
     item_tree::{
         Const, Enum, Function, Impl, ItemTreeId, ItemTreeNode, MacroDef, MacroRules, ModItem,
         Static, Struct, Trait, TraitAlias, TypeAlias, Union,
@@ -236,7 +233,7 @@ pub struct EnumVariantId {
     pub local_id: LocalEnumVariantId,
 }
 
-pub type LocalEnumVariantId = Idx<adt::EnumVariantData>;
+pub type LocalEnumVariantId = Idx<data::adt::EnumVariantData>;
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 pub struct FieldId {
@@ -244,7 +241,7 @@ pub struct FieldId {
     pub local_id: LocalFieldId,
 }
 
-pub type LocalFieldId = Idx<adt::FieldData>;
+pub type LocalFieldId = Idx<data::adt::FieldData>;
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 pub struct ConstId(salsa::InternId);
diff --git a/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs b/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs
index fafcde25ae7..37cf348c92d 100644
--- a/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs
+++ b/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs
@@ -16,7 +16,7 @@ struct Foo;
 #[derive(Copy)]
 struct Foo;
 
-impl < > core::marker::Copy for Foo< > {}"#]],
+impl < > core::marker::Copy for Foo< > where {}"#]],
     );
 }
 
@@ -41,7 +41,7 @@ macro Copy {}
 #[derive(Copy)]
 struct Foo;
 
-impl < > crate ::marker::Copy for Foo< > {}"#]],
+impl < > crate ::marker::Copy for Foo< > where {}"#]],
     );
 }
 
@@ -57,7 +57,7 @@ struct Foo<A, B>;
 #[derive(Copy)]
 struct Foo<A, B>;
 
-impl <T0: core::marker::Copy, T1: core::marker::Copy, > core::marker::Copy for Foo<T0, T1, > {}"#]],
+impl <A: core::marker::Copy, B: core::marker::Copy, > core::marker::Copy for Foo<A, B, > where {}"#]],
     );
 }
 
@@ -74,7 +74,7 @@ struct Foo<A, B, 'a, 'b>;
 #[derive(Copy)]
 struct Foo<A, B, 'a, 'b>;
 
-impl <T0: core::marker::Copy, T1: core::marker::Copy, > core::marker::Copy for Foo<T0, T1, > {}"#]],
+impl <A: core::marker::Copy, B: core::marker::Copy, > core::marker::Copy for Foo<A, B, > where {}"#]],
     );
 }
 
@@ -90,7 +90,7 @@ struct Foo<A, B>;
 #[derive(Clone)]
 struct Foo<A, B>;
 
-impl <T0: core::clone::Clone, T1: core::clone::Clone, > core::clone::Clone for Foo<T0, T1, > {}"#]],
+impl <A: core::clone::Clone, B: core::clone::Clone, > core::clone::Clone for Foo<A, B, > where {}"#]],
     );
 }
 
@@ -106,6 +106,6 @@ struct Foo<const X: usize, T>(u32);
 #[derive(Clone)]
 struct Foo<const X: usize, T>(u32);
 
-impl <const T0: usize, T1: core::clone::Clone, > core::clone::Clone for Foo<T0, T1, > {}"#]],
+impl <const X: usize, T: core::clone::Clone, > core::clone::Clone for Foo<X, T, > where {}"#]],
     );
 }
diff --git a/crates/hir-def/src/macro_expansion_tests.rs b/crates/hir-def/src/macro_expansion_tests/mod.rs
index 314bf22b95e..314bf22b95e 100644
--- a/crates/hir-def/src/macro_expansion_tests.rs
+++ b/crates/hir-def/src/macro_expansion_tests/mod.rs
diff --git a/crates/hir-def/src/nameres/attr_resolution.rs b/crates/hir-def/src/nameres/attr_resolution.rs
index 79cabeb0fb7..6fad01ec2f7 100644
--- a/crates/hir-def/src/nameres/attr_resolution.rs
+++ b/crates/hir-def/src/nameres/attr_resolution.rs
@@ -4,7 +4,8 @@ use hir_expand::{attrs::Attr, MacroCallId};
 use syntax::{ast, SmolStr};
 
 use crate::{
-    attr_macro_as_call_id, builtin_attr,
+    attr::builtin::{find_builtin_attr_idx, TOOL_MODULES},
+    attr_macro_as_call_id,
     db::DefDatabase,
     item_scope::BuiltinShadowMode,
     macro_id_to_def_id,
@@ -76,19 +77,15 @@ impl DefMap {
             let pred = |n: &_| *n == name;
 
             let registered = self.registered_tools.iter().map(SmolStr::as_str);
-            let is_tool = builtin_attr::TOOL_MODULES.iter().copied().chain(registered).any(pred);
+            let is_tool = TOOL_MODULES.iter().copied().chain(registered).any(pred);
             // FIXME: tool modules can be shadowed by actual modules
             if is_tool {
                 return true;
             }
 
             if segments.len() == 1 {
-                let registered = self.registered_attrs.iter().map(SmolStr::as_str);
-                let is_inert = builtin_attr::INERT_ATTRIBUTES
-                    .iter()
-                    .map(|it| it.name)
-                    .chain(registered)
-                    .any(pred);
+                let mut registered = self.registered_attrs.iter().map(SmolStr::as_str);
+                let is_inert = find_builtin_attr_idx(&name).is_some() || registered.any(pred);
                 return is_inert;
             }
         }
diff --git a/crates/hir-def/src/nameres/path_resolution.rs b/crates/hir-def/src/nameres/path_resolution.rs
index 25478481dd0..8299d9684bb 100644
--- a/crates/hir-def/src/nameres/path_resolution.rs
+++ b/crates/hir-def/src/nameres/path_resolution.rs
@@ -331,11 +331,11 @@ impl DefMap {
                         Some(local_id) => {
                             let variant = EnumVariantId { parent: e, local_id };
                             match &*enum_data.variants[local_id].variant_data {
-                                crate::adt::VariantData::Record(_) => {
+                                crate::data::adt::VariantData::Record(_) => {
                                     PerNs::types(variant.into(), Visibility::Public)
                                 }
-                                crate::adt::VariantData::Tuple(_)
-                                | crate::adt::VariantData::Unit => {
+                                crate::data::adt::VariantData::Tuple(_)
+                                | crate::data::adt::VariantData::Unit => {
                                     PerNs::both(variant.into(), variant.into(), Visibility::Public)
                                 }
                             }
diff --git a/crates/hir-def/src/resolver.rs b/crates/hir-def/src/resolver.rs
index 9afe07932ec..670495e4d16 100644
--- a/crates/hir-def/src/resolver.rs
+++ b/crates/hir-def/src/resolver.rs
@@ -12,8 +12,8 @@ use crate::{
     body::scope::{ExprScopes, ScopeId},
     builtin_type::BuiltinType,
     db::DefDatabase,
-    expr::{BindingId, ExprId, LabelId},
     generics::{GenericParams, TypeOrConstParamData},
+    hir::{BindingId, ExprId, LabelId},
     item_scope::{BuiltinShadowMode, BUILTIN_SCOPE},
     lang_item::LangItemTarget,
     nameres::DefMap,
diff --git a/crates/hir-expand/src/builtin_derive_macro.rs b/crates/hir-expand/src/builtin_derive_macro.rs
index 5c1a75132ee..7e753663c01 100644
--- a/crates/hir-expand/src/builtin_derive_macro.rs
+++ b/crates/hir-expand/src/builtin_derive_macro.rs
@@ -1,11 +1,12 @@
 //! Builtin derives.
 
 use base_db::{CrateOrigin, LangCrateOrigin};
+use std::collections::HashSet;
 use tracing::debug;
 
 use crate::tt::{self, TokenId};
 use syntax::{
-    ast::{self, AstNode, HasGenericParams, HasModuleItem, HasName},
+    ast::{self, AstNode, HasGenericParams, HasModuleItem, HasName, HasTypeBounds, PathType},
     match_ast,
 };
 
@@ -60,8 +61,11 @@ pub fn find_builtin_derive(ident: &name::Name) -> Option<BuiltinDeriveExpander>
 
 struct BasicAdtInfo {
     name: tt::Ident,
-    /// `Some(ty)` if it's a const param of type `ty`, `None` if it's a type param.
-    param_types: Vec<Option<tt::Subtree>>,
+    /// first field is the name, and
+    /// second field is `Some(ty)` if it's a const param of type `ty`, `None` if it's a type param.
+    /// third fields is where bounds, if any
+    param_types: Vec<(tt::Subtree, Option<tt::Subtree>, Option<tt::Subtree>)>,
+    associated_types: Vec<tt::Subtree>,
 }
 
 fn parse_adt(tt: &tt::Subtree) -> Result<BasicAdtInfo, ExpandError> {
@@ -86,18 +90,28 @@ fn parse_adt(tt: &tt::Subtree) -> Result<BasicAdtInfo, ExpandError> {
             },
         }
     };
-    let name = name.ok_or_else(|| {
-        debug!("parsed item has no name");
-        ExpandError::Other("missing name".into())
-    })?;
-    let name_token_id =
-        token_map.token_by_range(name.syntax().text_range()).unwrap_or_else(TokenId::unspecified);
-    let name_token = tt::Ident { span: name_token_id, text: name.text().into() };
+    let mut param_type_set: HashSet<String> = HashSet::new();
     let param_types = params
         .into_iter()
         .flat_map(|param_list| param_list.type_or_const_params())
         .map(|param| {
-            if let ast::TypeOrConstParam::Const(param) = param {
+            let name = {
+                let this = param.name();
+                match this {
+                    Some(x) => {
+                        param_type_set.insert(x.to_string());
+                        mbe::syntax_node_to_token_tree(x.syntax()).0
+                    }
+                    None => tt::Subtree::empty(),
+                }
+            };
+            let bounds = match &param {
+                ast::TypeOrConstParam::Type(x) => {
+                    x.type_bound_list().map(|x| mbe::syntax_node_to_token_tree(x.syntax()).0)
+                }
+                ast::TypeOrConstParam::Const(_) => None,
+            };
+            let ty = if let ast::TypeOrConstParam::Const(param) = param {
                 let ty = param
                     .ty()
                     .map(|ty| mbe::syntax_node_to_token_tree(ty.syntax()).0)
@@ -105,27 +119,97 @@ fn parse_adt(tt: &tt::Subtree) -> Result<BasicAdtInfo, ExpandError> {
                 Some(ty)
             } else {
                 None
-            }
+            };
+            (name, ty, bounds)
         })
         .collect();
-    Ok(BasicAdtInfo { name: name_token, param_types })
+    let is_associated_type = |p: &PathType| {
+        if let Some(p) = p.path() {
+            if let Some(parent) = p.qualifier() {
+                if let Some(x) = parent.segment() {
+                    if let Some(x) = x.path_type() {
+                        if let Some(x) = x.path() {
+                            if let Some(pname) = x.as_single_name_ref() {
+                                if param_type_set.contains(&pname.to_string()) {
+                                    // <T as Trait>::Assoc
+                                    return true;
+                                }
+                            }
+                        }
+                    }
+                }
+                if let Some(pname) = parent.as_single_name_ref() {
+                    if param_type_set.contains(&pname.to_string()) {
+                        // T::Assoc
+                        return true;
+                    }
+                }
+            }
+        }
+        false
+    };
+    let associated_types = node
+        .descendants()
+        .filter_map(PathType::cast)
+        .filter(is_associated_type)
+        .map(|x| mbe::syntax_node_to_token_tree(x.syntax()).0)
+        .collect::<Vec<_>>();
+    let name = name.ok_or_else(|| {
+        debug!("parsed item has no name");
+        ExpandError::Other("missing name".into())
+    })?;
+    let name_token_id =
+        token_map.token_by_range(name.syntax().text_range()).unwrap_or_else(TokenId::unspecified);
+    let name_token = tt::Ident { span: name_token_id, text: name.text().into() };
+    Ok(BasicAdtInfo { name: name_token, param_types, associated_types })
 }
 
+/// Given that we are deriving a trait `DerivedTrait` for a type like:
+///
+/// ```ignore (only-for-syntax-highlight)
+/// struct Struct<'a, ..., 'z, A, B: DeclaredTrait, C, ..., Z> where C: WhereTrait {
+///     a: A,
+///     b: B::Item,
+///     b1: <B as DeclaredTrait>::Item,
+///     c1: <C as WhereTrait>::Item,
+///     c2: Option<<C as WhereTrait>::Item>,
+///     ...
+/// }
+/// ```
+///
+/// create an impl like:
+///
+/// ```ignore (only-for-syntax-highlight)
+/// impl<'a, ..., 'z, A, B: DeclaredTrait, C, ... Z> where
+///     C:                       WhereTrait,
+///     A: DerivedTrait + B1 + ... + BN,
+///     B: DerivedTrait + B1 + ... + BN,
+///     C: DerivedTrait + B1 + ... + BN,
+///     B::Item:                 DerivedTrait + B1 + ... + BN,
+///     <C as WhereTrait>::Item: DerivedTrait + B1 + ... + BN,
+///     ...
+/// {
+///     ...
+/// }
+/// ```
+///
+/// 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(tt: &tt::Subtree, trait_path: tt::Subtree) -> ExpandResult<tt::Subtree> {
     let info = match parse_adt(tt) {
         Ok(info) => info,
         Err(e) => return ExpandResult::with_err(tt::Subtree::empty(), e),
     };
+    let mut where_block = vec![];
     let (params, args): (Vec<_>, Vec<_>) = info
         .param_types
         .into_iter()
-        .enumerate()
-        .map(|(idx, param_ty)| {
-            let ident = tt::Leaf::Ident(tt::Ident {
-                span: tt::TokenId::unspecified(),
-                text: format!("T{idx}").into(),
-            });
+        .map(|(ident, param_ty, bound)| {
             let ident_ = ident.clone();
+            if let Some(b) = bound {
+                let ident = ident.clone();
+                where_block.push(quote! { #ident : #b , });
+            }
             if let Some(ty) = param_ty {
                 (quote! { const #ident : #ty , }, quote! { #ident_ , })
             } else {
@@ -134,9 +218,16 @@ fn expand_simple_derive(tt: &tt::Subtree, trait_path: tt::Subtree) -> ExpandResu
             }
         })
         .unzip();
+
+    where_block.extend(info.associated_types.iter().map(|x| {
+        let x = x.clone();
+        let bound = trait_path.clone();
+        quote! { #x : #bound , }
+    }));
+
     let name = info.name;
     let expanded = quote! {
-        impl < ##params > #trait_path for #name < ##args > {}
+        impl < ##params > #trait_path for #name < ##args > where ##where_block {}
     };
     ExpandResult::ok(expanded)
 }
diff --git a/crates/hir-expand/src/lib.rs b/crates/hir-expand/src/lib.rs
index 980de3c2685..929dabcaf62 100644
--- a/crates/hir-expand/src/lib.rs
+++ b/crates/hir-expand/src/lib.rs
@@ -55,7 +55,7 @@ pub type ExpandResult<T> = ValueResult<T, ExpandError>;
 pub enum ExpandError {
     UnresolvedProcMacro(CrateId),
     Mbe(mbe::ExpandError),
-    RecursionOverflowPosioned,
+    RecursionOverflowPoisoned,
     Other(Box<str>),
 }
 
@@ -70,7 +70,7 @@ impl fmt::Display for ExpandError {
         match self {
             ExpandError::UnresolvedProcMacro(_) => f.write_str("unresolved proc-macro"),
             ExpandError::Mbe(it) => it.fmt(f),
-            ExpandError::RecursionOverflowPosioned => {
+            ExpandError::RecursionOverflowPoisoned => {
                 f.write_str("overflow expanding the original macro")
             }
             ExpandError::Other(it) => f.write_str(it),
diff --git a/crates/hir-expand/src/name.rs b/crates/hir-expand/src/name.rs
index 8099c20b027..4e688c431ae 100644
--- a/crates/hir-expand/src/name.rs
+++ b/crates/hir-expand/src/name.rs
@@ -120,8 +120,7 @@ impl Name {
         use std::sync::atomic::{AtomicUsize, Ordering};
         static CNT: AtomicUsize = AtomicUsize::new(0);
         let c = CNT.fetch_add(1, Ordering::Relaxed);
-        // FIXME: Currently a `__RA_generated_name` in user code will break our analysis
-        Name::new_text(format!("__RA_geneated_name_{c}").into())
+        Name::new_text(format!("<ra@gennew>{c}").into())
     }
 
     /// Returns the tuple index this name represents if it is a tuple field.
diff --git a/crates/hir-ty/src/chalk_db.rs b/crates/hir-ty/src/chalk_db.rs
index 68375f9e1eb..c30a99e06ca 100644
--- a/crates/hir-ty/src/chalk_db.rs
+++ b/crates/hir-ty/src/chalk_db.rs
@@ -9,7 +9,7 @@ use chalk_solve::rust_ir::{self, OpaqueTyDatumBound, WellKnownTrait};
 
 use base_db::CrateId;
 use hir_def::{
-    expr::Movability,
+    hir::Movability,
     lang_item::{lang_attr, LangItem, LangItemTarget},
     AssocItemId, BlockId, GenericDefId, HasModule, ItemContainerId, Lookup, TypeAliasId,
 };
@@ -18,9 +18,10 @@ use hir_expand::name::name;
 use crate::{
     db::HirDatabase,
     display::HirDisplay,
-    from_assoc_type_id, from_chalk_trait_id, make_binders, make_single_type_binders,
+    from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id, make_binders,
+    make_single_type_binders,
     mapping::{from_chalk, ToChalk, TypeAliasAsValue},
-    method_resolution::{TyFingerprint, ALL_FLOAT_FPS, ALL_INT_FPS},
+    method_resolution::{TraitImpls, TyFingerprint, ALL_FLOAT_FPS, ALL_INT_FPS},
     to_assoc_type_id, to_chalk_trait_id,
     traits::ChalkContext,
     utils::generics,
@@ -106,6 +107,19 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
             _ => self_ty_fp.as_ref().map(std::slice::from_ref).unwrap_or(&[]),
         };
 
+        let trait_module = trait_.module(self.db.upcast());
+        let type_module = match self_ty_fp {
+            Some(TyFingerprint::Adt(adt_id)) => Some(adt_id.module(self.db.upcast())),
+            Some(TyFingerprint::ForeignType(type_id)) => {
+                Some(from_foreign_def_id(type_id).module(self.db.upcast()))
+            }
+            Some(TyFingerprint::Dyn(trait_id)) => Some(trait_id.module(self.db.upcast())),
+            _ => None,
+        };
+
+        let mut def_blocks =
+            [trait_module.containing_block(), type_module.and_then(|it| it.containing_block())];
+
         // Note: Since we're using impls_for_trait, only impls where the trait
         // can be resolved should ever reach Chalk. impl_datum relies on that
         // and will panic if the trait can't be resolved.
@@ -120,6 +134,14 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
                 .and_then(|map| map.parent())
                 .and_then(|module| module.containing_block())
         })
+        .inspect(|&block_id| {
+            // make sure we don't search the same block twice
+            def_blocks.iter_mut().for_each(|block| {
+                if *block == Some(block_id) {
+                    *block = None;
+                }
+            });
+        })
         .filter_map(|block_id| self.db.trait_impls_in_block(block_id));
 
         let id_to_chalk = |id: hir_def::ImplId| id.to_chalk(self.db);
@@ -127,18 +149,27 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
         match fps {
             [] => {
                 debug!("Unrestricted search for {:?} impls...", trait_);
-                impl_maps.into_iter().chain(block_impls).for_each(|impls| {
+                let mut f = |impls: Arc<TraitImpls>| {
                     result.extend(impls.for_trait(trait_).map(id_to_chalk));
-                });
+                };
+                impl_maps.into_iter().chain(block_impls).for_each(&mut f);
+                def_blocks
+                    .into_iter()
+                    .filter_map(|it| self.db.trait_impls_in_block(it?))
+                    .for_each(f);
             }
             fps => {
-                impl_maps.into_iter().chain(block_impls).for_each(|impls| {
-                    result.extend(
-                        fps.iter().flat_map(|fp| {
+                let mut f =
+                    |impls: Arc<TraitImpls>| {
+                        result.extend(fps.iter().flat_map(|fp| {
                             impls.for_trait_and_self_ty(trait_, *fp).map(id_to_chalk)
-                        }),
-                    );
-                });
+                        }));
+                    };
+                impl_maps.into_iter().chain(block_impls).for_each(&mut f);
+                def_blocks
+                    .into_iter()
+                    .filter_map(|it| self.db.trait_impls_in_block(it?))
+                    .for_each(f);
             }
         }
 
@@ -384,8 +415,8 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
         let input_output = crate::make_type_and_const_binders(it, input_output);
 
         let movability = match self.db.body(parent)[expr] {
-            hir_def::expr::Expr::Closure {
-                closure_kind: hir_def::expr::ClosureKind::Generator(movability),
+            hir_def::hir::Expr::Closure {
+                closure_kind: hir_def::hir::ClosureKind::Generator(movability),
                 ..
             } => movability,
             _ => unreachable!("non generator expression interned as generator"),
diff --git a/crates/hir-ty/src/chalk_ext.rs b/crates/hir-ty/src/chalk_ext.rs
index 2141894922f..d6a56124856 100644
--- a/crates/hir-ty/src/chalk_ext.rs
+++ b/crates/hir-ty/src/chalk_ext.rs
@@ -12,8 +12,9 @@ use hir_def::{
 use crate::{
     db::HirDatabase, from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id,
     from_placeholder_idx, to_chalk_trait_id, utils::generics, AdtId, AliasEq, AliasTy, Binders,
-    CallableDefId, CallableSig, DynTy, FnPointer, ImplTraitId, Interner, Lifetime, ProjectionTy,
-    QuantifiedWhereClause, Substitution, TraitRef, Ty, TyBuilder, TyKind, TypeFlags, WhereClause,
+    CallableDefId, CallableSig, ClosureId, DynTy, FnPointer, ImplTraitId, Interner, Lifetime,
+    ProjectionTy, QuantifiedWhereClause, Substitution, TraitRef, Ty, TyBuilder, TyKind, TypeFlags,
+    WhereClause,
 };
 
 pub trait TyExt {
@@ -28,6 +29,7 @@ pub trait TyExt {
     fn as_adt(&self) -> Option<(hir_def::AdtId, &Substitution)>;
     fn as_builtin(&self) -> Option<BuiltinType>;
     fn as_tuple(&self) -> Option<&Substitution>;
+    fn as_closure(&self) -> Option<ClosureId>;
     fn as_fn_def(&self, db: &dyn HirDatabase) -> Option<FunctionId>;
     fn as_reference(&self) -> Option<(&Ty, Lifetime, Mutability)>;
     fn as_reference_or_ptr(&self) -> Option<(&Ty, Rawness, Mutability)>;
@@ -128,6 +130,13 @@ impl TyExt for Ty {
         }
     }
 
+    fn as_closure(&self) -> Option<ClosureId> {
+        match self.kind(Interner) {
+            TyKind::Closure(id, _) => Some(*id),
+            _ => None,
+        }
+    }
+
     fn as_fn_def(&self, db: &dyn HirDatabase) -> Option<FunctionId> {
         match self.callable_def(db) {
             Some(CallableDefId::FunctionId(func)) => Some(func),
diff --git a/crates/hir-ty/src/consteval.rs b/crates/hir-ty/src/consteval.rs
index 7e69971fee7..78033b4e89b 100644
--- a/crates/hir-ty/src/consteval.rs
+++ b/crates/hir-ty/src/consteval.rs
@@ -3,7 +3,7 @@
 use base_db::CrateId;
 use chalk_ir::{BoundVar, DebruijnIndex, GenericArgData};
 use hir_def::{
-    expr::Expr,
+    hir::Expr,
     path::Path,
     resolver::{Resolver, ValueNs},
     type_ref::ConstRef,
diff --git a/crates/hir-ty/src/consteval/tests.rs b/crates/hir-ty/src/consteval/tests.rs
index a0efc7541e3..d987f41c706 100644
--- a/crates/hir-ty/src/consteval/tests.rs
+++ b/crates/hir-ty/src/consteval/tests.rs
@@ -1106,6 +1106,81 @@ fn try_block() {
 }
 
 #[test]
+fn closures() {
+    check_number(
+        r#"
+    //- minicore: fn, copy
+    const GOAL: i32 = {
+        let y = 5;
+        let c = |x| x + y;
+        c(2)
+    };
+        "#,
+        7,
+    );
+    check_number(
+        r#"
+    //- minicore: fn, copy
+    const GOAL: i32 = {
+        let y = 5;
+        let c = |(a, b): &(i32, i32)| *a + *b + y;
+        c(&(2, 3))
+    };
+        "#,
+        10,
+    );
+    check_number(
+        r#"
+    //- minicore: fn, copy
+    const GOAL: i32 = {
+        let mut y = 5;
+        let c = |x| {
+            y = y + x;
+        };
+        c(2);
+        c(3);
+        y
+    };
+        "#,
+        10,
+    );
+    check_number(
+        r#"
+    //- minicore: fn, copy
+    struct X(i32);
+    impl X {
+        fn mult(&mut self, n: i32) {
+            self.0 = self.0 * n
+        }
+    }
+    const GOAL: i32 = {
+        let x = X(1);
+        let c = || {
+            x.mult(2);
+            || {
+                x.mult(3);
+                || {
+                    || {
+                        x.mult(4);
+                        || {
+                            x.mult(x.0);
+                            || {
+                                x.0
+                            }
+                        }
+                    }
+                }
+            }
+        };
+        let r = c()()()()()();
+        r + x.0
+    };
+        "#,
+        24 * 24 * 2,
+    );
+}
+
+#[test]
 fn or_pattern() {
     check_number(
         r#"
diff --git a/crates/hir-ty/src/db.rs b/crates/hir-ty/src/db.rs
index 56f5d90bb35..3a8fb665c44 100644
--- a/crates/hir-ty/src/db.rs
+++ b/crates/hir-ty/src/db.rs
@@ -6,7 +6,7 @@ use std::sync::Arc;
 use base_db::{impl_intern_key, salsa, CrateId, Upcast};
 use hir_def::{
     db::DefDatabase,
-    expr::ExprId,
+    hir::ExprId,
     layout::{Layout, LayoutError, TargetDataLayout},
     AdtId, BlockId, ConstId, ConstParamId, DefWithBodyId, EnumVariantId, FunctionId, GenericDefId,
     ImplId, LifetimeParamId, LocalFieldId, TypeOrConstParamId, VariantId,
@@ -19,9 +19,9 @@ use crate::{
     consteval::ConstEvalError,
     method_resolution::{InherentImpls, TraitImpls, TyFingerprint},
     mir::{BorrowckResult, MirBody, MirLowerError},
-    Binders, CallableDefId, Const, FnDefId, GenericArg, ImplTraitId, InferenceResult, Interner,
-    PolyFnSig, QuantifiedWhereClause, ReturnTypeImplTraits, Substitution, TraitRef, Ty, TyDefId,
-    ValueTyDefId,
+    Binders, CallableDefId, ClosureId, Const, FnDefId, GenericArg, ImplTraitId, InferenceResult,
+    Interner, PolyFnSig, QuantifiedWhereClause, ReturnTypeImplTraits, Substitution, TraitRef, Ty,
+    TyDefId, ValueTyDefId,
 };
 use hir_expand::name::Name;
 
@@ -38,8 +38,11 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
     #[salsa::cycle(crate::mir::mir_body_recover)]
     fn mir_body(&self, def: DefWithBodyId) -> Result<Arc<MirBody>, MirLowerError>;
 
+    #[salsa::invoke(crate::mir::mir_body_for_closure_query)]
+    fn mir_body_for_closure(&self, def: ClosureId) -> Result<Arc<MirBody>, MirLowerError>;
+
     #[salsa::invoke(crate::mir::borrowck_query)]
-    fn borrowck(&self, def: DefWithBodyId) -> Result<Arc<BorrowckResult>, MirLowerError>;
+    fn borrowck(&self, def: DefWithBodyId) -> Result<Arc<[BorrowckResult]>, MirLowerError>;
 
     #[salsa::invoke(crate::lower::ty_query)]
     #[salsa::cycle(crate::lower::ty_recover)]
diff --git a/crates/hir-ty/src/diagnostics/decl_check.rs b/crates/hir-ty/src/diagnostics/decl_check.rs
index d36b93e3bdd..b3a699e2d1b 100644
--- a/crates/hir-ty/src/diagnostics/decl_check.rs
+++ b/crates/hir-ty/src/diagnostics/decl_check.rs
@@ -16,8 +16,8 @@ use std::fmt;
 
 use base_db::CrateId;
 use hir_def::{
-    adt::VariantData,
-    expr::{Pat, PatId},
+    data::adt::VariantData,
+    hir::{Pat, PatId},
     src::HasSource,
     AdtId, AttrDefId, ConstId, EnumId, FunctionId, ItemContainerId, Lookup, ModuleDefId, StaticId,
     StructId,
diff --git a/crates/hir-ty/src/diagnostics/expr.rs b/crates/hir-ty/src/diagnostics/expr.rs
index 2e9066788cf..d39e077b716 100644
--- a/crates/hir-ty/src/diagnostics/expr.rs
+++ b/crates/hir-ty/src/diagnostics/expr.rs
@@ -27,7 +27,7 @@ use crate::{
 
 pub(crate) use hir_def::{
     body::Body,
-    expr::{Expr, ExprId, MatchArm, Pat, PatId},
+    hir::{Expr, ExprId, MatchArm, Pat, PatId},
     LocalFieldId, VariantId,
 };
 
diff --git a/crates/hir-ty/src/diagnostics/match_check.rs b/crates/hir-ty/src/diagnostics/match_check.rs
index fe1cda71dd5..202f4aa66bc 100644
--- a/crates/hir-ty/src/diagnostics/match_check.rs
+++ b/crates/hir-ty/src/diagnostics/match_check.rs
@@ -1,6 +1,6 @@
 //! Validation of matches.
 //!
-//! This module provides lowering from [hir_def::expr::Pat] to [self::Pat] and match
+//! This module provides lowering from [hir_def::hir::Pat] to [self::Pat] and match
 //! checking algorithm.
 //!
 //! It is modeled on the rustc module `rustc_mir_build::thir::pattern`.
@@ -12,7 +12,7 @@ pub(crate) mod usefulness;
 
 use chalk_ir::Mutability;
 use hir_def::{
-    adt::VariantData, body::Body, expr::PatId, AdtId, EnumVariantId, LocalFieldId, VariantId,
+    body::Body, data::adt::VariantData, hir::PatId, AdtId, EnumVariantId, LocalFieldId, VariantId,
 };
 use hir_expand::name::Name;
 use stdx::{always, never};
@@ -125,15 +125,15 @@ impl<'a> PatCtxt<'a> {
         let variant = self.infer.variant_resolution_for_pat(pat);
 
         let kind = match self.body[pat] {
-            hir_def::expr::Pat::Wild => PatKind::Wild,
+            hir_def::hir::Pat::Wild => PatKind::Wild,
 
-            hir_def::expr::Pat::Lit(expr) => self.lower_lit(expr),
+            hir_def::hir::Pat::Lit(expr) => self.lower_lit(expr),
 
-            hir_def::expr::Pat::Path(ref path) => {
+            hir_def::hir::Pat::Path(ref path) => {
                 return self.lower_path(pat, path);
             }
 
-            hir_def::expr::Pat::Tuple { ref args, ellipsis } => {
+            hir_def::hir::Pat::Tuple { ref args, ellipsis } => {
                 let arity = match *ty.kind(Interner) {
                     TyKind::Tuple(arity, _) => arity,
                     _ => {
@@ -146,7 +146,7 @@ impl<'a> PatCtxt<'a> {
                 PatKind::Leaf { subpatterns }
             }
 
-            hir_def::expr::Pat::Bind { id, subpat, .. } => {
+            hir_def::hir::Pat::Bind { id, subpat, .. } => {
                 let bm = self.infer.pat_binding_modes[&pat];
                 let name = &self.body.bindings[id].name;
                 match (bm, ty.kind(Interner)) {
@@ -161,13 +161,13 @@ impl<'a> PatCtxt<'a> {
                 PatKind::Binding { name: name.clone(), subpattern: self.lower_opt_pattern(subpat) }
             }
 
-            hir_def::expr::Pat::TupleStruct { ref args, ellipsis, .. } if variant.is_some() => {
+            hir_def::hir::Pat::TupleStruct { ref args, ellipsis, .. } if variant.is_some() => {
                 let expected_len = variant.unwrap().variant_data(self.db.upcast()).fields().len();
                 let subpatterns = self.lower_tuple_subpats(args, expected_len, ellipsis);
                 self.lower_variant_or_leaf(pat, ty, subpatterns)
             }
 
-            hir_def::expr::Pat::Record { ref args, .. } if variant.is_some() => {
+            hir_def::hir::Pat::Record { ref args, .. } if variant.is_some() => {
                 let variant_data = variant.unwrap().variant_data(self.db.upcast());
                 let subpatterns = args
                     .iter()
@@ -187,12 +187,12 @@ impl<'a> PatCtxt<'a> {
                     }
                 }
             }
-            hir_def::expr::Pat::TupleStruct { .. } | hir_def::expr::Pat::Record { .. } => {
+            hir_def::hir::Pat::TupleStruct { .. } | hir_def::hir::Pat::Record { .. } => {
                 self.errors.push(PatternError::UnresolvedVariant);
                 PatKind::Wild
             }
 
-            hir_def::expr::Pat::Or(ref pats) => PatKind::Or { pats: self.lower_patterns(pats) },
+            hir_def::hir::Pat::Or(ref pats) => PatKind::Or { pats: self.lower_patterns(pats) },
 
             _ => {
                 self.errors.push(PatternError::Unimplemented);
@@ -279,8 +279,8 @@ impl<'a> PatCtxt<'a> {
         }
     }
 
-    fn lower_lit(&mut self, expr: hir_def::expr::ExprId) -> PatKind {
-        use hir_def::expr::{Expr, Literal::Bool};
+    fn lower_lit(&mut self, expr: hir_def::hir::ExprId) -> PatKind {
+        use hir_def::hir::{Expr, Literal::Bool};
 
         match self.body[expr] {
             Expr::Literal(Bool(value)) => PatKind::LiteralBool { value },
diff --git a/crates/hir-ty/src/diagnostics/unsafe_check.rs b/crates/hir-ty/src/diagnostics/unsafe_check.rs
index 664822ee6fb..7c38e6583a7 100644
--- a/crates/hir-ty/src/diagnostics/unsafe_check.rs
+++ b/crates/hir-ty/src/diagnostics/unsafe_check.rs
@@ -3,7 +3,7 @@
 
 use hir_def::{
     body::Body,
-    expr::{Expr, ExprId, UnaryOp},
+    hir::{Expr, ExprId, UnaryOp},
     resolver::{resolver_for_expr, ResolveValueResult, ValueNs},
     DefWithBodyId,
 };
diff --git a/crates/hir-ty/src/display.rs b/crates/hir-ty/src/display.rs
index bd3eccfe43d..f892a815197 100644
--- a/crates/hir-ty/src/display.rs
+++ b/crates/hir-ty/src/display.rs
@@ -7,8 +7,8 @@ use std::fmt::{self, Debug};
 use base_db::CrateId;
 use chalk_ir::{BoundVar, TyKind};
 use hir_def::{
-    adt::VariantData,
     body,
+    data::adt::VariantData,
     db::DefDatabase,
     find_path,
     generics::{TypeOrConstParamData, TypeParamProvenance},
@@ -23,6 +23,7 @@ use hir_expand::{hygiene::Hygiene, name::Name};
 use intern::{Internable, Interned};
 use itertools::Itertools;
 use smallvec::SmallVec;
+use stdx::never;
 
 use crate::{
     db::HirDatabase,
@@ -64,6 +65,7 @@ pub struct HirFormatter<'a> {
     curr_size: usize,
     pub(crate) max_size: Option<usize>,
     omit_verbose_types: bool,
+    closure_style: ClosureStyle,
     display_target: DisplayTarget,
 }
 
@@ -87,6 +89,7 @@ pub trait HirDisplay {
         max_size: Option<usize>,
         omit_verbose_types: bool,
         display_target: DisplayTarget,
+        closure_style: ClosureStyle,
     ) -> HirDisplayWrapper<'a, Self>
     where
         Self: Sized,
@@ -95,7 +98,14 @@ pub trait HirDisplay {
             !matches!(display_target, DisplayTarget::SourceCode { .. }),
             "HirDisplayWrapper cannot fail with DisplaySourceCodeError, use HirDisplay::hir_fmt directly instead"
         );
-        HirDisplayWrapper { db, t: self, max_size, omit_verbose_types, display_target }
+        HirDisplayWrapper {
+            db,
+            t: self,
+            max_size,
+            omit_verbose_types,
+            display_target,
+            closure_style,
+        }
     }
 
     /// Returns a `Display`able type that is human-readable.
@@ -109,6 +119,7 @@ pub trait HirDisplay {
             t: self,
             max_size: None,
             omit_verbose_types: false,
+            closure_style: ClosureStyle::ImplFn,
             display_target: DisplayTarget::Diagnostics,
         }
     }
@@ -128,6 +139,7 @@ pub trait HirDisplay {
             t: self,
             max_size,
             omit_verbose_types: true,
+            closure_style: ClosureStyle::ImplFn,
             display_target: DisplayTarget::Diagnostics,
         }
     }
@@ -147,6 +159,7 @@ pub trait HirDisplay {
             curr_size: 0,
             max_size: None,
             omit_verbose_types: false,
+            closure_style: ClosureStyle::ImplFn,
             display_target: DisplayTarget::SourceCode { module_id },
         }) {
             Ok(()) => {}
@@ -166,6 +179,7 @@ pub trait HirDisplay {
             t: self,
             max_size: None,
             omit_verbose_types: false,
+            closure_style: ClosureStyle::ImplFn,
             display_target: DisplayTarget::Test,
         }
     }
@@ -253,7 +267,6 @@ impl DisplayTarget {
 pub enum DisplaySourceCodeError {
     PathNotFound,
     UnknownType,
-    Closure,
     Generator,
 }
 
@@ -274,9 +287,23 @@ pub struct HirDisplayWrapper<'a, T> {
     t: &'a T,
     max_size: Option<usize>,
     omit_verbose_types: bool,
+    closure_style: ClosureStyle,
     display_target: DisplayTarget,
 }
 
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+pub enum ClosureStyle {
+    /// `impl FnX(i32, i32) -> i32`, where `FnX` is the most special trait between `Fn`, `FnMut`, `FnOnce` that the
+    /// closure implements. This is the default.
+    ImplFn,
+    /// `|i32, i32| -> i32`
+    RANotation,
+    /// `{closure#14825}`, useful for some diagnostics (like type mismatch) and internal usage.
+    ClosureWithId,
+    /// `…`, which is the `TYPE_HINT_TRUNCATION`
+    Hide,
+}
+
 impl<T: HirDisplay> HirDisplayWrapper<'_, T> {
     pub fn write_to<F: HirWrite>(&self, f: &mut F) -> Result<(), HirDisplayError> {
         self.t.hir_fmt(&mut HirFormatter {
@@ -287,8 +314,14 @@ impl<T: HirDisplay> HirDisplayWrapper<'_, T> {
             max_size: self.max_size,
             omit_verbose_types: self.omit_verbose_types,
             display_target: self.display_target,
+            closure_style: self.closure_style,
         })
     }
+
+    pub fn with_closure_style(mut self, c: ClosureStyle) -> Self {
+        self.closure_style = c;
+        self
+    }
 }
 
 impl<'a, T> fmt::Display for HirDisplayWrapper<'a, T>
@@ -919,26 +952,42 @@ impl HirDisplay for Ty {
                     }
                 }
             }
-            TyKind::Closure(.., substs) => {
-                if f.display_target.is_source_code() {
-                    return Err(HirDisplayError::DisplaySourceCodeError(
-                        DisplaySourceCodeError::Closure,
-                    ));
+            TyKind::Closure(id, substs) => {
+                if f.display_target.is_source_code() && f.closure_style != ClosureStyle::ImplFn {
+                    never!("Only `impl Fn` is valid for displaying closures in source code");
+                }
+                match f.closure_style {
+                    ClosureStyle::Hide => return write!(f, "{TYPE_HINT_TRUNCATION}"),
+                    ClosureStyle::ClosureWithId => {
+                        return write!(f, "{{closure#{:?}}}", id.0.as_u32())
+                    }
+                    _ => (),
                 }
                 let sig = substs.at(Interner, 0).assert_ty_ref(Interner).callable_sig(db);
                 if let Some(sig) = sig {
+                    let (def, _) = db.lookup_intern_closure((*id).into());
+                    let infer = db.infer(def);
+                    let (_, kind) = infer.closure_info(id);
+                    match f.closure_style {
+                        ClosureStyle::ImplFn => write!(f, "impl {kind:?}(")?,
+                        ClosureStyle::RANotation => write!(f, "|")?,
+                        _ => unreachable!(),
+                    }
                     if sig.params().is_empty() {
-                        write!(f, "||")?;
                     } else if f.should_truncate() {
-                        write!(f, "|{TYPE_HINT_TRUNCATION}|")?;
+                        write!(f, "{TYPE_HINT_TRUNCATION}")?;
                     } else {
-                        write!(f, "|")?;
                         f.write_joined(sig.params(), ", ")?;
-                        write!(f, "|")?;
                     };
-
-                    write!(f, " -> ")?;
-                    sig.ret().hir_fmt(f)?;
+                    match f.closure_style {
+                        ClosureStyle::ImplFn => write!(f, ")")?,
+                        ClosureStyle::RANotation => write!(f, "|")?,
+                        _ => unreachable!(),
+                    }
+                    if f.closure_style == ClosureStyle::RANotation || !sig.ret().is_unit() {
+                        write!(f, " -> ")?;
+                        sig.ret().hir_fmt(f)?;
+                    }
                 } else {
                     write!(f, "{{closure}}")?;
                 }
diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs
index 0c41b92026a..d90ca77b55c 100644
--- a/crates/hir-ty/src/infer.rs
+++ b/crates/hir-ty/src/infer.rs
@@ -18,11 +18,12 @@ use std::{convert::identity, ops::Index};
 
 use chalk_ir::{cast::Cast, DebruijnIndex, Mutability, Safety, Scalar, TypeFlags};
 use either::Either;
+use hir_def::hir::LabelId;
 use hir_def::{
     body::Body,
     builtin_type::{BuiltinInt, BuiltinType, BuiltinUint},
     data::{ConstData, StaticData},
-    expr::{BindingAnnotation, BindingId, ExprId, ExprOrPatId, PatId},
+    hir::{BindingAnnotation, BindingId, ExprId, ExprOrPatId, PatId},
     lang_item::{LangItem, LangItemTarget},
     layout::Integer,
     path::{ModPath, Path},
@@ -32,15 +33,15 @@ use hir_def::{
     TraitId, TypeAliasId, VariantId,
 };
 use hir_expand::name::{name, Name};
-use la_arena::ArenaMap;
+use la_arena::{ArenaMap, Entry};
 use rustc_hash::{FxHashMap, FxHashSet};
 use stdx::{always, never};
 
 use crate::{
     db::HirDatabase, fold_tys, infer::coerce::CoerceMany, lower::ImplTraitLoweringMode,
-    static_lifetime, to_assoc_type_id, AliasEq, AliasTy, DomainGoal, GenericArg, Goal, ImplTraitId,
-    InEnvironment, Interner, ProjectionTy, RpitId, Substitution, TraitRef, Ty, TyBuilder, TyExt,
-    TyKind,
+    static_lifetime, to_assoc_type_id, traits::FnTrait, AliasEq, AliasTy, ClosureId, DomainGoal,
+    GenericArg, Goal, ImplTraitId, InEnvironment, Interner, ProjectionTy, RpitId, Substitution,
+    TraitRef, Ty, TyBuilder, TyExt, TyKind,
 };
 
 // This lint has a false positive here. See the link below for details.
@@ -51,6 +52,8 @@ pub use coerce::could_coerce;
 #[allow(unreachable_pub)]
 pub use unify::could_unify;
 
+pub(crate) use self::closure::{CaptureKind, CapturedItem, CapturedItemWithoutTy};
+
 pub(crate) mod unify;
 mod path;
 mod expr;
@@ -102,6 +105,8 @@ pub(crate) fn infer_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc<Infer
 
     ctx.infer_mut_body();
 
+    ctx.infer_closures();
+
     Arc::new(ctx.resolve_all())
 }
 
@@ -188,7 +193,7 @@ pub enum InferenceDiagnostic {
         /// Contains the type the field resolves to
         field_with_same_name: Option<Ty>,
     },
-    // FIXME: Make this proper
+    // FIXME: This should be emitted in body lowering
     BreakOutsideOfLoop {
         expr: ExprId,
         is_break: bool,
@@ -311,6 +316,13 @@ pub enum AutoBorrow {
     RawPtr(Mutability),
 }
 
+impl AutoBorrow {
+    fn mutability(self) -> Mutability {
+        let (AutoBorrow::Ref(m) | AutoBorrow::RawPtr(m)) = self;
+        m
+    }
+}
+
 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
 pub enum PointerCast {
     /// Go from a fn-item type to a fn-pointer type.
@@ -372,6 +384,9 @@ pub struct InferenceResult {
     pub pat_adjustments: FxHashMap<PatId, Vec<Ty>>,
     pub pat_binding_modes: FxHashMap<PatId, BindingMode>,
     pub expr_adjustments: FxHashMap<ExprId, Vec<Adjustment>>,
+    pub(crate) closure_info: FxHashMap<ClosureId, (Vec<CapturedItem>, FnTrait)>,
+    // FIXME: remove this field
+    pub mutated_bindings_in_closure: FxHashSet<BindingId>,
 }
 
 impl InferenceResult {
@@ -408,6 +423,9 @@ impl InferenceResult {
             _ => None,
         })
     }
+    pub(crate) fn closure_info(&self, closure: &ClosureId) -> &(Vec<CapturedItem>, FnTrait) {
+        self.closure_info.get(closure).unwrap()
+    }
 }
 
 impl Index<ExprId> for InferenceResult {
@@ -459,6 +477,14 @@ pub(crate) struct InferenceContext<'a> {
     resume_yield_tys: Option<(Ty, Ty)>,
     diverges: Diverges,
     breakables: Vec<BreakableContext>,
+
+    // fields related to closure capture
+    current_captures: Vec<CapturedItemWithoutTy>,
+    current_closure: Option<ClosureId>,
+    /// Stores the list of closure ids that need to be analyzed before this closure. See the
+    /// comment on `InferenceContext::sort_closures`
+    closure_dependecies: FxHashMap<ClosureId, Vec<ClosureId>>,
+    deferred_closures: FxHashMap<ClosureId, Vec<(Ty, Ty, Vec<Ty>, ExprId)>>,
 }
 
 #[derive(Clone, Debug)]
@@ -468,7 +494,7 @@ struct BreakableContext {
     /// The coercion target of the context.
     coerce: Option<CoerceMany>,
     /// The optional label of the context.
-    label: Option<name::Name>,
+    label: Option<LabelId>,
     kind: BreakableKind,
 }
 
@@ -483,21 +509,21 @@ enum BreakableKind {
 
 fn find_breakable<'c>(
     ctxs: &'c mut [BreakableContext],
-    label: Option<&name::Name>,
+    label: Option<LabelId>,
 ) -> Option<&'c mut BreakableContext> {
     let mut ctxs = ctxs
         .iter_mut()
         .rev()
         .take_while(|it| matches!(it.kind, BreakableKind::Block | BreakableKind::Loop));
     match label {
-        Some(_) => ctxs.find(|ctx| ctx.label.as_ref() == label),
+        Some(_) => ctxs.find(|ctx| ctx.label == label),
         None => ctxs.find(|ctx| matches!(ctx.kind, BreakableKind::Loop)),
     }
 }
 
 fn find_continuable<'c>(
     ctxs: &'c mut [BreakableContext],
-    label: Option<&name::Name>,
+    label: Option<LabelId>,
 ) -> Option<&'c mut BreakableContext> {
     match label {
         Some(_) => find_breakable(ctxs, label).filter(|it| matches!(it.kind, BreakableKind::Loop)),
@@ -526,6 +552,10 @@ impl<'a> InferenceContext<'a> {
             resolver,
             diverges: Diverges::Maybe,
             breakables: Vec::new(),
+            current_captures: vec![],
+            current_closure: None,
+            deferred_closures: FxHashMap::default(),
+            closure_dependecies: FxHashMap::default(),
         }
     }
 
@@ -617,7 +647,7 @@ impl<'a> InferenceContext<'a> {
         let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver)
             .with_impl_trait_mode(ImplTraitLoweringMode::Param);
         let mut param_tys =
-            data.params.iter().map(|(_, type_ref)| ctx.lower_ty(type_ref)).collect::<Vec<_>>();
+            data.params.iter().map(|type_ref| ctx.lower_ty(type_ref)).collect::<Vec<_>>();
         // Check if function contains a va_list, if it does then we append it to the parameter types
         // that are collected from the function data
         if data.is_varargs() {
@@ -646,36 +676,16 @@ impl<'a> InferenceContext<'a> {
         let return_ty = if let Some(rpits) = self.db.return_type_impl_traits(func) {
             // RPIT opaque types use substitution of their parent function.
             let fn_placeholders = TyBuilder::placeholder_subst(self.db, func);
-            fold_tys(
-                return_ty,
-                |ty, _| {
-                    let opaque_ty_id = match ty.kind(Interner) {
-                        TyKind::OpaqueType(opaque_ty_id, _) => *opaque_ty_id,
-                        _ => return ty,
-                    };
-                    let idx = match self.db.lookup_intern_impl_trait_id(opaque_ty_id.into()) {
-                        ImplTraitId::ReturnTypeImplTrait(_, idx) => idx,
-                        _ => unreachable!(),
-                    };
-                    let bounds = (*rpits).map_ref(|rpits| {
-                        rpits.impl_traits[idx].bounds.map_ref(|it| it.into_iter())
-                    });
-                    let var = self.table.new_type_var();
-                    let var_subst = Substitution::from1(Interner, var.clone());
-                    for bound in bounds {
-                        let predicate =
-                            bound.map(|it| it.cloned()).substitute(Interner, &fn_placeholders);
-                        let (var_predicate, binders) = predicate
-                            .substitute(Interner, &var_subst)
-                            .into_value_and_skipped_binders();
-                        always!(binders.is_empty(Interner)); // quantified where clauses not yet handled
-                        self.push_obligation(var_predicate.cast(Interner));
-                    }
-                    self.result.type_of_rpit.insert(idx, var.clone());
-                    var
-                },
-                DebruijnIndex::INNERMOST,
-            )
+            let result =
+                self.insert_inference_vars_for_rpit(return_ty, rpits.clone(), fn_placeholders);
+            let rpits = rpits.skip_binders();
+            for (id, _) in rpits.impl_traits.iter() {
+                if let Entry::Vacant(e) = self.result.type_of_rpit.entry(id) {
+                    never!("Missed RPIT in `insert_inference_vars_for_rpit`");
+                    e.insert(TyKind::Error.intern(Interner));
+                }
+            }
+            result
         } else {
             return_ty
         };
@@ -684,6 +694,50 @@ impl<'a> InferenceContext<'a> {
         self.return_coercion = Some(CoerceMany::new(self.return_ty.clone()));
     }
 
+    fn insert_inference_vars_for_rpit<T>(
+        &mut self,
+        t: T,
+        rpits: Arc<chalk_ir::Binders<crate::ReturnTypeImplTraits>>,
+        fn_placeholders: Substitution,
+    ) -> T
+    where
+        T: crate::HasInterner<Interner = Interner> + crate::TypeFoldable<Interner>,
+    {
+        fold_tys(
+            t,
+            |ty, _| {
+                let opaque_ty_id = match ty.kind(Interner) {
+                    TyKind::OpaqueType(opaque_ty_id, _) => *opaque_ty_id,
+                    _ => return ty,
+                };
+                let idx = match self.db.lookup_intern_impl_trait_id(opaque_ty_id.into()) {
+                    ImplTraitId::ReturnTypeImplTrait(_, idx) => idx,
+                    _ => unreachable!(),
+                };
+                let bounds = (*rpits)
+                    .map_ref(|rpits| rpits.impl_traits[idx].bounds.map_ref(|it| it.into_iter()));
+                let var = self.table.new_type_var();
+                let var_subst = Substitution::from1(Interner, var.clone());
+                for bound in bounds {
+                    let predicate =
+                        bound.map(|it| it.cloned()).substitute(Interner, &fn_placeholders);
+                    let (var_predicate, binders) =
+                        predicate.substitute(Interner, &var_subst).into_value_and_skipped_binders();
+                    always!(binders.is_empty(Interner)); // quantified where clauses not yet handled
+                    let var_predicate = self.insert_inference_vars_for_rpit(
+                        var_predicate,
+                        rpits.clone(),
+                        fn_placeholders.clone(),
+                    );
+                    self.push_obligation(var_predicate.cast(Interner));
+                }
+                self.result.type_of_rpit.insert(idx, var.clone());
+                var
+            },
+            DebruijnIndex::INNERMOST,
+        )
+    }
+
     fn infer_body(&mut self) {
         match self.return_coercion {
             Some(_) => self.infer_return(self.body.body_expr),
diff --git a/crates/hir-ty/src/infer/closure.rs b/crates/hir-ty/src/infer/closure.rs
index a6449d019ff..28cb301f3e4 100644
--- a/crates/hir-ty/src/infer/closure.rs
+++ b/crates/hir-ty/src/infer/closure.rs
@@ -1,12 +1,29 @@
 //! Inference of closure parameter types based on the closure's expected type.
 
-use chalk_ir::{cast::Cast, AliasEq, AliasTy, FnSubst, WhereClause};
-use hir_def::{expr::ExprId, HasModule};
+use std::{cmp, collections::HashMap, convert::Infallible, mem};
+
+use chalk_ir::{cast::Cast, AliasEq, AliasTy, FnSubst, Mutability, TyKind, WhereClause};
+use hir_def::{
+    hir::{
+        Array, BinaryOp, BindingAnnotation, BindingId, CaptureBy, Expr, ExprId, Pat, PatId,
+        Statement, UnaryOp,
+    },
+    lang_item::LangItem,
+    resolver::{resolver_for_expr, ResolveValueResult, ValueNs},
+    FieldId, HasModule, VariantId,
+};
+use hir_expand::name;
+use rustc_hash::FxHashMap;
 use smallvec::SmallVec;
+use stdx::never;
 
 use crate::{
-    to_chalk_trait_id, utils, ChalkTraitId, DynTy, FnPointer, FnSig, Interner, Substitution, Ty,
-    TyExt, TyKind,
+    mir::{BorrowKind, ProjectionElem},
+    static_lifetime, to_chalk_trait_id,
+    traits::FnTrait,
+    utils::{self, pattern_matching_dereference_count},
+    Adjust, Adjustment, Canonical, CanonicalVarKinds, ChalkTraitId, ClosureId, DynTy, FnPointer,
+    FnSig, InEnvironment, Interner, Substitution, Ty, TyBuilder, TyExt,
 };
 
 use super::{Expectation, InferenceContext};
@@ -86,3 +103,700 @@ impl InferenceContext<'_> {
         None
     }
 }
+
+// The below functions handle capture and closure kind (Fn, FnMut, ..)
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub(crate) struct HirPlace {
+    pub(crate) local: BindingId,
+    pub(crate) projections: Vec<ProjectionElem<Infallible, Ty>>,
+}
+impl HirPlace {
+    fn ty(&self, ctx: &mut InferenceContext<'_>) -> Ty {
+        let mut ty = ctx.table.resolve_completely(ctx.result[self.local].clone());
+        for p in &self.projections {
+            ty = p.projected_ty(ty, ctx.db, |_, _| {
+                unreachable!("Closure field only happens in MIR");
+            });
+        }
+        ty.clone()
+    }
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+pub(crate) enum CaptureKind {
+    ByRef(BorrowKind),
+    ByValue,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub(crate) struct CapturedItem {
+    pub(crate) place: HirPlace,
+    pub(crate) kind: CaptureKind,
+    pub(crate) ty: Ty,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub(crate) struct CapturedItemWithoutTy {
+    pub(crate) place: HirPlace,
+    pub(crate) kind: CaptureKind,
+}
+
+impl CapturedItemWithoutTy {
+    fn with_ty(self, ctx: &mut InferenceContext<'_>) -> CapturedItem {
+        let ty = self.place.ty(ctx).clone();
+        let ty = match &self.kind {
+            CaptureKind::ByValue => ty,
+            CaptureKind::ByRef(bk) => {
+                let m = match bk {
+                    BorrowKind::Mut { .. } => Mutability::Mut,
+                    _ => Mutability::Not,
+                };
+                TyKind::Ref(m, static_lifetime(), ty).intern(Interner)
+            }
+        };
+        CapturedItem { place: self.place, kind: self.kind, ty }
+    }
+}
+
+impl InferenceContext<'_> {
+    fn place_of_expr(&mut self, tgt_expr: ExprId) -> Option<HirPlace> {
+        let r = self.place_of_expr_without_adjust(tgt_expr)?;
+        let default = vec![];
+        let adjustments = self.result.expr_adjustments.get(&tgt_expr).unwrap_or(&default);
+        apply_adjusts_to_place(r, adjustments)
+    }
+
+    fn place_of_expr_without_adjust(&mut self, tgt_expr: ExprId) -> Option<HirPlace> {
+        match &self.body[tgt_expr] {
+            Expr::Path(p) => {
+                let resolver = resolver_for_expr(self.db.upcast(), self.owner, tgt_expr);
+                if let Some(r) = resolver.resolve_path_in_value_ns(self.db.upcast(), p) {
+                    if let ResolveValueResult::ValueNs(v) = r {
+                        if let ValueNs::LocalBinding(b) = v {
+                            return Some(HirPlace { local: b, projections: vec![] });
+                        }
+                    }
+                }
+            }
+            Expr::Field { expr, name } => {
+                let mut place = self.place_of_expr(*expr)?;
+                if let TyKind::Tuple(..) = self.expr_ty(*expr).kind(Interner) {
+                    let index = name.as_tuple_index()?;
+                    place.projections.push(ProjectionElem::TupleOrClosureField(index))
+                } else {
+                    let field = self.result.field_resolution(tgt_expr)?;
+                    place.projections.push(ProjectionElem::Field(field));
+                }
+                return Some(place);
+            }
+            _ => (),
+        }
+        None
+    }
+
+    fn push_capture(&mut self, capture: CapturedItemWithoutTy) {
+        self.current_captures.push(capture);
+    }
+
+    fn ref_expr(&mut self, expr: ExprId) {
+        if let Some(place) = self.place_of_expr(expr) {
+            self.add_capture(place, CaptureKind::ByRef(BorrowKind::Shared));
+        }
+        self.walk_expr(expr);
+    }
+
+    fn add_capture(&mut self, place: HirPlace, kind: CaptureKind) {
+        if self.is_upvar(&place) {
+            self.push_capture(CapturedItemWithoutTy { place, kind });
+        }
+    }
+
+    fn mutate_expr(&mut self, expr: ExprId) {
+        if let Some(place) = self.place_of_expr(expr) {
+            self.add_capture(
+                place,
+                CaptureKind::ByRef(BorrowKind::Mut { allow_two_phase_borrow: false }),
+            );
+        }
+        self.walk_expr(expr);
+    }
+
+    fn consume_expr(&mut self, expr: ExprId) {
+        if let Some(place) = self.place_of_expr(expr) {
+            self.consume_place(place);
+        }
+        self.walk_expr(expr);
+    }
+
+    fn consume_place(&mut self, place: HirPlace) {
+        if self.is_upvar(&place) {
+            let ty = place.ty(self).clone();
+            let kind = if self.is_ty_copy(ty) {
+                CaptureKind::ByRef(BorrowKind::Shared)
+            } else {
+                CaptureKind::ByValue
+            };
+            self.push_capture(CapturedItemWithoutTy { place, kind });
+        }
+    }
+
+    fn walk_expr_with_adjust(&mut self, tgt_expr: ExprId, adjustment: &[Adjustment]) {
+        if let Some((last, rest)) = adjustment.split_last() {
+            match last.kind {
+                Adjust::NeverToAny | Adjust::Deref(None) | Adjust::Pointer(_) => {
+                    self.walk_expr_with_adjust(tgt_expr, rest)
+                }
+                Adjust::Deref(Some(m)) => match m.0 {
+                    Some(m) => {
+                        self.ref_capture_with_adjusts(m, tgt_expr, rest);
+                    }
+                    None => unreachable!(),
+                },
+                Adjust::Borrow(b) => {
+                    self.ref_capture_with_adjusts(b.mutability(), tgt_expr, rest);
+                }
+            }
+        } else {
+            self.walk_expr_without_adjust(tgt_expr);
+        }
+    }
+
+    fn ref_capture_with_adjusts(&mut self, m: Mutability, tgt_expr: ExprId, rest: &[Adjustment]) {
+        let capture_kind = match m {
+            Mutability::Mut => {
+                CaptureKind::ByRef(BorrowKind::Mut { allow_two_phase_borrow: false })
+            }
+            Mutability::Not => CaptureKind::ByRef(BorrowKind::Shared),
+        };
+        if let Some(place) = self.place_of_expr_without_adjust(tgt_expr) {
+            if let Some(place) = apply_adjusts_to_place(place, rest) {
+                if self.is_upvar(&place) {
+                    self.push_capture(CapturedItemWithoutTy { place, kind: capture_kind });
+                }
+            }
+        }
+        self.walk_expr_with_adjust(tgt_expr, rest);
+    }
+
+    fn walk_expr(&mut self, tgt_expr: ExprId) {
+        if let Some(x) = self.result.expr_adjustments.get_mut(&tgt_expr) {
+            // FIXME: this take is completely unneeded, and just is here to make borrow checker
+            // happy. Remove it if you can.
+            let x_taken = mem::take(x);
+            self.walk_expr_with_adjust(tgt_expr, &x_taken);
+            *self.result.expr_adjustments.get_mut(&tgt_expr).unwrap() = x_taken;
+        } else {
+            self.walk_expr_without_adjust(tgt_expr);
+        }
+    }
+
+    fn walk_expr_without_adjust(&mut self, tgt_expr: ExprId) {
+        match &self.body[tgt_expr] {
+            Expr::If { condition, then_branch, else_branch } => {
+                self.consume_expr(*condition);
+                self.consume_expr(*then_branch);
+                if let &Some(expr) = else_branch {
+                    self.consume_expr(expr);
+                }
+            }
+            Expr::Async { statements, tail, .. }
+            | Expr::Const { statements, tail, .. }
+            | Expr::Unsafe { statements, tail, .. }
+            | Expr::Block { statements, tail, .. } => {
+                for s in statements.iter() {
+                    match s {
+                        Statement::Let { pat, type_ref: _, initializer, else_branch } => {
+                            if let Some(else_branch) = else_branch {
+                                self.consume_expr(*else_branch);
+                                if let Some(initializer) = initializer {
+                                    self.consume_expr(*initializer);
+                                }
+                                return;
+                            }
+                            if let Some(initializer) = initializer {
+                                self.walk_expr(*initializer);
+                                if let Some(place) = self.place_of_expr(*initializer) {
+                                    let ty = self.expr_ty(*initializer);
+                                    self.consume_with_pat(
+                                        place,
+                                        ty,
+                                        BindingAnnotation::Unannotated,
+                                        *pat,
+                                    );
+                                }
+                            }
+                        }
+                        Statement::Expr { expr, has_semi: _ } => {
+                            self.consume_expr(*expr);
+                        }
+                    }
+                }
+                if let Some(tail) = tail {
+                    self.consume_expr(*tail);
+                }
+            }
+            Expr::While { condition, body, label: _ }
+            | Expr::For { iterable: condition, pat: _, body, label: _ } => {
+                self.consume_expr(*condition);
+                self.consume_expr(*body);
+            }
+            Expr::Call { callee, args, is_assignee_expr: _ } => {
+                self.consume_expr(*callee);
+                self.consume_exprs(args.iter().copied());
+            }
+            Expr::MethodCall { receiver, args, .. } => {
+                self.consume_expr(*receiver);
+                self.consume_exprs(args.iter().copied());
+            }
+            Expr::Match { expr, arms } => {
+                self.consume_expr(*expr);
+                for arm in arms.iter() {
+                    self.consume_expr(arm.expr);
+                }
+            }
+            Expr::Break { expr, label: _ }
+            | Expr::Return { expr }
+            | Expr::Yield { expr }
+            | Expr::Yeet { expr } => {
+                if let &Some(expr) = expr {
+                    self.consume_expr(expr);
+                }
+            }
+            Expr::RecordLit { fields, spread, .. } => {
+                if let &Some(expr) = spread {
+                    self.consume_expr(expr);
+                }
+                self.consume_exprs(fields.iter().map(|x| x.expr));
+            }
+            Expr::Field { expr, name: _ } => self.select_from_expr(*expr),
+            Expr::UnaryOp { expr, op: UnaryOp::Deref } => {
+                if let Some((f, _)) = self.result.method_resolution(tgt_expr) {
+                    let mutability = 'b: {
+                        if let Some(deref_trait) =
+                            self.resolve_lang_item(LangItem::DerefMut).and_then(|x| x.as_trait())
+                        {
+                            if let Some(deref_fn) =
+                                self.db.trait_data(deref_trait).method_by_name(&name![deref_mut])
+                            {
+                                break 'b deref_fn == f;
+                            }
+                        }
+                        false
+                    };
+                    if mutability {
+                        self.mutate_expr(*expr);
+                    } else {
+                        self.ref_expr(*expr);
+                    }
+                } else {
+                    self.select_from_expr(*expr);
+                }
+            }
+            Expr::UnaryOp { expr, op: _ }
+            | Expr::Array(Array::Repeat { initializer: expr, repeat: _ })
+            | Expr::Await { expr }
+            | Expr::Loop { body: expr, label: _ }
+            | Expr::Let { pat: _, expr }
+            | Expr::Box { expr }
+            | Expr::Cast { expr, type_ref: _ } => {
+                self.consume_expr(*expr);
+            }
+            Expr::Ref { expr, rawness: _, mutability } => match mutability {
+                hir_def::type_ref::Mutability::Shared => self.ref_expr(*expr),
+                hir_def::type_ref::Mutability::Mut => self.mutate_expr(*expr),
+            },
+            Expr::BinaryOp { lhs, rhs, op } => {
+                let Some(op) = op else {
+                    return;
+                };
+                if matches!(op, BinaryOp::Assignment { .. }) {
+                    self.mutate_expr(*lhs);
+                    self.consume_expr(*rhs);
+                    return;
+                }
+                self.consume_expr(*lhs);
+                self.consume_expr(*rhs);
+            }
+            Expr::Range { lhs, rhs, range_type: _ } => {
+                if let &Some(expr) = lhs {
+                    self.consume_expr(expr);
+                }
+                if let &Some(expr) = rhs {
+                    self.consume_expr(expr);
+                }
+            }
+            Expr::Index { base, index } => {
+                self.select_from_expr(*base);
+                self.consume_expr(*index);
+            }
+            Expr::Closure { .. } => {
+                let ty = self.expr_ty(tgt_expr);
+                let TyKind::Closure(id, _) = ty.kind(Interner) else {
+                    never!("closure type is always closure");
+                    return;
+                };
+                let (captures, _) =
+                    self.result.closure_info.get(id).expect(
+                        "We sort closures, so we should always have data for inner closures",
+                    );
+                let mut cc = mem::take(&mut self.current_captures);
+                cc.extend(
+                    captures
+                        .iter()
+                        .filter(|x| self.is_upvar(&x.place))
+                        .map(|x| CapturedItemWithoutTy { place: x.place.clone(), kind: x.kind }),
+                );
+                self.current_captures = cc;
+            }
+            Expr::Array(Array::ElementList { elements: exprs, is_assignee_expr: _ })
+            | Expr::Tuple { exprs, is_assignee_expr: _ } => {
+                self.consume_exprs(exprs.iter().copied())
+            }
+            Expr::Missing
+            | Expr::Continue { .. }
+            | Expr::Path(_)
+            | Expr::Literal(_)
+            | Expr::Underscore => (),
+        }
+    }
+
+    fn expr_ty(&mut self, expr: ExprId) -> Ty {
+        self.result[expr].clone()
+    }
+
+    fn is_upvar(&self, place: &HirPlace) -> bool {
+        let b = &self.body[place.local];
+        if let Some(c) = self.current_closure {
+            let (_, root) = self.db.lookup_intern_closure(c.into());
+            return b.is_upvar(root);
+        }
+        false
+    }
+
+    fn is_ty_copy(&self, ty: Ty) -> bool {
+        if let TyKind::Closure(id, _) = ty.kind(Interner) {
+            // FIXME: We handle closure as a special case, since chalk consider every closure as copy. We
+            // should probably let chalk know which closures are copy, but I don't know how doing it
+            // without creating query cycles.
+            return self.result.closure_info.get(id).map(|x| x.1 == FnTrait::Fn).unwrap_or(true);
+        }
+        let crate_id = self.owner.module(self.db.upcast()).krate();
+        let Some(copy_trait) = self.db.lang_item(crate_id, LangItem::Copy).and_then(|x| x.as_trait()) else {
+            return false;
+        };
+        let trait_ref = TyBuilder::trait_ref(self.db, copy_trait).push(ty).build();
+        let env = self.db.trait_environment_for_body(self.owner);
+        let goal = Canonical {
+            value: InEnvironment::new(&env.env, trait_ref.cast(Interner)),
+            binders: CanonicalVarKinds::empty(Interner),
+        };
+        self.db.trait_solve(crate_id, None, goal).is_some()
+    }
+
+    fn select_from_expr(&mut self, expr: ExprId) {
+        self.walk_expr(expr);
+    }
+
+    fn adjust_for_move_closure(&mut self) {
+        for capture in &mut self.current_captures {
+            if let Some(first_deref) =
+                capture.place.projections.iter().position(|proj| *proj == ProjectionElem::Deref)
+            {
+                capture.place.projections.truncate(first_deref);
+            }
+            capture.kind = CaptureKind::ByValue;
+        }
+    }
+
+    fn minimize_captures(&mut self) {
+        self.current_captures.sort_by_key(|x| x.place.projections.len());
+        let mut hash_map = HashMap::<HirPlace, usize>::new();
+        let result = mem::take(&mut self.current_captures);
+        for item in result {
+            let mut lookup_place = HirPlace { local: item.place.local, projections: vec![] };
+            let mut it = item.place.projections.iter();
+            let prev_index = loop {
+                if let Some(k) = hash_map.get(&lookup_place) {
+                    break Some(*k);
+                }
+                match it.next() {
+                    Some(x) => lookup_place.projections.push(x.clone()),
+                    None => break None,
+                }
+            };
+            match prev_index {
+                Some(p) => {
+                    self.current_captures[p].kind =
+                        cmp::max(item.kind, self.current_captures[p].kind);
+                }
+                None => {
+                    hash_map.insert(item.place.clone(), self.current_captures.len());
+                    self.current_captures.push(item);
+                }
+            }
+        }
+    }
+
+    fn consume_with_pat(
+        &mut self,
+        mut place: HirPlace,
+        mut ty: Ty,
+        mut bm: BindingAnnotation,
+        pat: PatId,
+    ) {
+        match &self.body[pat] {
+            Pat::Missing | Pat::Wild => (),
+            Pat::Tuple { args, ellipsis } => {
+                pattern_matching_dereference(&mut ty, &mut bm, &mut place);
+                let (al, ar) = args.split_at(ellipsis.unwrap_or(args.len()));
+                let subst = match ty.kind(Interner) {
+                    TyKind::Tuple(_, s) => s,
+                    _ => return,
+                };
+                let fields = subst.iter(Interner).map(|x| x.assert_ty_ref(Interner)).enumerate();
+                let it = al.iter().zip(fields.clone()).chain(ar.iter().rev().zip(fields.rev()));
+                for (arg, (i, ty)) in it {
+                    let mut p = place.clone();
+                    p.projections.push(ProjectionElem::TupleOrClosureField(i));
+                    self.consume_with_pat(p, ty.clone(), bm, *arg);
+                }
+            }
+            Pat::Or(pats) => {
+                for pat in pats.iter() {
+                    self.consume_with_pat(place.clone(), ty.clone(), bm, *pat);
+                }
+            }
+            Pat::Record { args, .. } => {
+                pattern_matching_dereference(&mut ty, &mut bm, &mut place);
+                let subst = match ty.kind(Interner) {
+                    TyKind::Adt(_, s) => s,
+                    _ => return,
+                };
+                let Some(variant) = self.result.variant_resolution_for_pat(pat) else {
+                    return;
+                };
+                match variant {
+                    VariantId::EnumVariantId(_) | VariantId::UnionId(_) => {
+                        self.consume_place(place)
+                    }
+                    VariantId::StructId(s) => {
+                        let vd = &*self.db.struct_data(s).variant_data;
+                        let field_types = self.db.field_types(variant);
+                        for field_pat in args.iter() {
+                            let arg = field_pat.pat;
+                            let Some(local_id) = vd.field(&field_pat.name) else {
+                                continue;
+                            };
+                            let mut p = place.clone();
+                            p.projections.push(ProjectionElem::Field(FieldId {
+                                parent: variant.into(),
+                                local_id,
+                            }));
+                            self.consume_with_pat(
+                                p,
+                                field_types[local_id].clone().substitute(Interner, subst),
+                                bm,
+                                arg,
+                            );
+                        }
+                    }
+                }
+            }
+            Pat::Range { .. }
+            | Pat::Slice { .. }
+            | Pat::ConstBlock(_)
+            | Pat::Path(_)
+            | Pat::Lit(_) => self.consume_place(place),
+            Pat::Bind { id, subpat: _ } => {
+                let mode = self.body.bindings[*id].mode;
+                if matches!(mode, BindingAnnotation::Ref | BindingAnnotation::RefMut) {
+                    bm = mode;
+                }
+                let capture_kind = match bm {
+                    BindingAnnotation::Unannotated | BindingAnnotation::Mutable => {
+                        self.consume_place(place);
+                        return;
+                    }
+                    BindingAnnotation::Ref => BorrowKind::Shared,
+                    BindingAnnotation::RefMut => BorrowKind::Mut { allow_two_phase_borrow: false },
+                };
+                self.add_capture(place, CaptureKind::ByRef(capture_kind));
+            }
+            Pat::TupleStruct { path: _, args, ellipsis } => {
+                pattern_matching_dereference(&mut ty, &mut bm, &mut place);
+                let subst = match ty.kind(Interner) {
+                    TyKind::Adt(_, s) => s,
+                    _ => return,
+                };
+                let Some(variant) = self.result.variant_resolution_for_pat(pat) else {
+                    return;
+                };
+                match variant {
+                    VariantId::EnumVariantId(_) | VariantId::UnionId(_) => {
+                        self.consume_place(place)
+                    }
+                    VariantId::StructId(s) => {
+                        let vd = &*self.db.struct_data(s).variant_data;
+                        let (al, ar) = args.split_at(ellipsis.unwrap_or(args.len()));
+                        let fields = vd.fields().iter();
+                        let it =
+                            al.iter().zip(fields.clone()).chain(ar.iter().rev().zip(fields.rev()));
+                        let field_types = self.db.field_types(variant);
+                        for (arg, (i, _)) in it {
+                            let mut p = place.clone();
+                            p.projections.push(ProjectionElem::Field(FieldId {
+                                parent: variant.into(),
+                                local_id: i,
+                            }));
+                            self.consume_with_pat(
+                                p,
+                                field_types[i].clone().substitute(Interner, subst),
+                                bm,
+                                *arg,
+                            );
+                        }
+                    }
+                }
+            }
+            Pat::Ref { pat, mutability: _ } => {
+                if let Some((inner, _, _)) = ty.as_reference() {
+                    ty = inner.clone();
+                    place.projections.push(ProjectionElem::Deref);
+                    self.consume_with_pat(place, ty, bm, *pat)
+                }
+            }
+            Pat::Box { .. } => (), // not supported
+        }
+    }
+
+    fn consume_exprs(&mut self, exprs: impl Iterator<Item = ExprId>) {
+        for expr in exprs {
+            self.consume_expr(expr);
+        }
+    }
+
+    fn closure_kind(&self) -> FnTrait {
+        let mut r = FnTrait::Fn;
+        for x in &self.current_captures {
+            r = cmp::min(
+                r,
+                match &x.kind {
+                    CaptureKind::ByRef(BorrowKind::Unique | BorrowKind::Mut { .. }) => {
+                        FnTrait::FnMut
+                    }
+                    CaptureKind::ByRef(BorrowKind::Shallow | BorrowKind::Shared) => FnTrait::Fn,
+                    CaptureKind::ByValue => FnTrait::FnOnce,
+                },
+            )
+        }
+        r
+    }
+
+    fn analyze_closure(&mut self, closure: ClosureId) -> FnTrait {
+        let (_, root) = self.db.lookup_intern_closure(closure.into());
+        self.current_closure = Some(closure);
+        let Expr::Closure { body, capture_by, .. } = &self.body[root] else {
+            unreachable!("Closure expression id is always closure");
+        };
+        self.consume_expr(*body);
+        for item in &self.current_captures {
+            if matches!(item.kind, CaptureKind::ByRef(BorrowKind::Mut { .. })) {
+                // FIXME: remove the `mutated_bindings_in_closure` completely and add proper fake reads in
+                // MIR. I didn't do that due duplicate diagnostics.
+                self.result.mutated_bindings_in_closure.insert(item.place.local);
+            }
+        }
+        // closure_kind should be done before adjust_for_move_closure
+        let closure_kind = self.closure_kind();
+        match capture_by {
+            CaptureBy::Value => self.adjust_for_move_closure(),
+            CaptureBy::Ref => (),
+        }
+        self.minimize_captures();
+        let result = mem::take(&mut self.current_captures);
+        let captures = result.into_iter().map(|x| x.with_ty(self)).collect::<Vec<_>>();
+        self.result.closure_info.insert(closure, (captures, closure_kind));
+        closure_kind
+    }
+
+    pub(crate) fn infer_closures(&mut self) {
+        let deferred_closures = self.sort_closures();
+        for (closure, exprs) in deferred_closures.into_iter().rev() {
+            self.current_captures = vec![];
+            let kind = self.analyze_closure(closure);
+
+            for (derefed_callee, callee_ty, params, expr) in exprs {
+                if let &Expr::Call { callee, .. } = &self.body[expr] {
+                    let mut adjustments =
+                        self.result.expr_adjustments.remove(&callee).unwrap_or_default();
+                    self.write_fn_trait_method_resolution(
+                        kind,
+                        &derefed_callee,
+                        &mut adjustments,
+                        &callee_ty,
+                        &params,
+                        expr,
+                    );
+                    self.result.expr_adjustments.insert(callee, adjustments);
+                }
+            }
+        }
+    }
+
+    /// We want to analyze some closures before others, to have a correct analysis:
+    /// * We should analyze nested closures before the parent, since the parent should capture some of
+    ///   the things that its children captures.
+    /// * If a closure calls another closure, we need to analyze the callee, to find out how we should
+    ///   capture it (e.g. by move for FnOnce)
+    ///
+    /// These dependencies are collected in the main inference. We do a topological sort in this function. It
+    /// will consume the `deferred_closures` field and return its content in a sorted vector.
+    fn sort_closures(&mut self) -> Vec<(ClosureId, Vec<(Ty, Ty, Vec<Ty>, ExprId)>)> {
+        let mut deferred_closures = mem::take(&mut self.deferred_closures);
+        let mut dependents_count: FxHashMap<ClosureId, usize> =
+            deferred_closures.keys().map(|x| (*x, 0)).collect();
+        for (_, deps) in &self.closure_dependecies {
+            for dep in deps {
+                *dependents_count.entry(*dep).or_default() += 1;
+            }
+        }
+        let mut queue: Vec<_> =
+            deferred_closures.keys().copied().filter(|x| dependents_count[x] == 0).collect();
+        let mut result = vec![];
+        while let Some(x) = queue.pop() {
+            if let Some(d) = deferred_closures.remove(&x) {
+                result.push((x, d));
+            }
+            for dep in self.closure_dependecies.get(&x).into_iter().flat_map(|x| x.iter()) {
+                let cnt = dependents_count.get_mut(dep).unwrap();
+                *cnt -= 1;
+                if *cnt == 0 {
+                    queue.push(*dep);
+                }
+            }
+        }
+        result
+    }
+}
+
+fn apply_adjusts_to_place(mut r: HirPlace, adjustments: &[Adjustment]) -> Option<HirPlace> {
+    for adj in adjustments {
+        match &adj.kind {
+            Adjust::Deref(None) => {
+                r.projections.push(ProjectionElem::Deref);
+            }
+            _ => return None,
+        }
+    }
+    Some(r)
+}
+
+fn pattern_matching_dereference(
+    cond_ty: &mut Ty,
+    binding_mode: &mut BindingAnnotation,
+    cond_place: &mut HirPlace,
+) {
+    let cnt = pattern_matching_dereference_count(cond_ty, binding_mode);
+    cond_place.projections.extend((0..cnt).map(|_| ProjectionElem::Deref));
+}
diff --git a/crates/hir-ty/src/infer/coerce.rs b/crates/hir-ty/src/infer/coerce.rs
index 96c35bbb8c4..2249d84edbf 100644
--- a/crates/hir-ty/src/infer/coerce.rs
+++ b/crates/hir-ty/src/infer/coerce.rs
@@ -7,9 +7,9 @@
 
 use std::{iter, sync::Arc};
 
-use chalk_ir::{cast::Cast, BoundVar, Goal, Mutability, TyVariableKind};
+use chalk_ir::{cast::Cast, BoundVar, Goal, Mutability, TyKind, TyVariableKind};
 use hir_def::{
-    expr::ExprId,
+    hir::ExprId,
     lang_item::{LangItem, LangItemTarget},
 };
 use stdx::always;
@@ -22,7 +22,7 @@ use crate::{
         TypeError, TypeMismatch,
     },
     static_lifetime, Canonical, DomainGoal, FnPointer, FnSig, Guidance, InEnvironment, Interner,
-    Solution, Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, TyKind,
+    Solution, Substitution, TraitEnvironment, Ty, TyBuilder, TyExt,
 };
 
 use super::unify::InferenceTable;
@@ -111,6 +111,8 @@ impl CoerceMany {
         // pointers to have a chance at getting a match. See
         // https://github.com/rust-lang/rust/blob/7b805396bf46dce972692a6846ce2ad8481c5f85/src/librustc_typeck/check/coercion.rs#L877-L916
         let sig = match (self.merged_ty().kind(Interner), expr_ty.kind(Interner)) {
+            (TyKind::FnDef(x, _), TyKind::FnDef(y, _)) if x == y => None,
+            (TyKind::Closure(x, _), TyKind::Closure(y, _)) if x == y => None,
             (TyKind::FnDef(..) | TyKind::Closure(..), TyKind::FnDef(..) | TyKind::Closure(..)) => {
                 // FIXME: we're ignoring safety here. To be more correct, if we have one FnDef and one Closure,
                 // we should be coercing the closure to a fn pointer of the safety of the FnDef
diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs
index 035f61fc180..64f37c761e9 100644
--- a/crates/hir-ty/src/infer/expr.rs
+++ b/crates/hir-ty/src/infer/expr.rs
@@ -10,10 +10,10 @@ use chalk_ir::{
     cast::Cast, fold::Shift, DebruijnIndex, GenericArgData, Mutability, TyKind, TyVariableKind,
 };
 use hir_def::{
-    expr::{
+    generics::TypeOrConstParamData,
+    hir::{
         ArithOp, Array, BinaryOp, ClosureKind, Expr, ExprId, LabelId, Literal, Statement, UnaryOp,
     },
-    generics::TypeOrConstParamData,
     lang_item::LangItem,
     path::{GenericArg, GenericArgs},
     BlockId, ConstParamId, FieldId, ItemContainerId, Lookup,
@@ -86,10 +86,10 @@ impl<'a> InferenceContext<'a> {
         }
     }
 
-    pub(super) fn infer_expr_coerce_never(&mut self, expr: ExprId, expected: &Expectation) -> Ty {
+    fn infer_expr_coerce_never(&mut self, expr: ExprId, expected: &Expectation) -> Ty {
         let ty = self.infer_expr_inner(expr, expected);
         // While we don't allow *arbitrary* coercions here, we *do* allow
-        // coercions from ! to `expected`.
+        // coercions from `!` to `expected`.
         if ty.is_never() {
             if let Some(adjustments) = self.result.expr_adjustments.get(&expr) {
                 return if let [Adjustment { kind: Adjust::NeverToAny, target }] = &**adjustments {
@@ -99,13 +99,22 @@ impl<'a> InferenceContext<'a> {
                 };
             }
 
-            let adj_ty = self.table.new_type_var();
-            self.write_expr_adj(
-                expr,
-                vec![Adjustment { kind: Adjust::NeverToAny, target: adj_ty.clone() }],
-            );
-            adj_ty
+            if let Some(target) = expected.only_has_type(&mut self.table) {
+                self.coerce(Some(expr), &ty, &target)
+                    .expect("never-to-any coercion should always succeed")
+            } else {
+                ty
+            }
         } else {
+            if let Some(expected_ty) = expected.only_has_type(&mut self.table) {
+                let could_unify = self.unify(&ty, &expected_ty);
+                if !could_unify {
+                    self.result.type_mismatches.insert(
+                        expr.into(),
+                        TypeMismatch { expected: expected_ty, actual: ty.clone() },
+                    );
+                }
+            }
             ty
         }
     }
@@ -212,7 +221,7 @@ impl<'a> InferenceContext<'a> {
                 self.diverges = Diverges::Maybe;
                 TyBuilder::unit()
             }
-            Expr::Closure { body, args, ret_type, arg_types, closure_kind } => {
+            Expr::Closure { body, args, ret_type, arg_types, closure_kind, capture_by: _ } => {
                 assert_eq!(args.len(), arg_types.len());
 
                 let mut sig_tys = Vec::with_capacity(arg_types.len() + 1);
@@ -247,7 +256,7 @@ impl<'a> InferenceContext<'a> {
                 })
                 .intern(Interner);
 
-                let (ty, resume_yield_tys) = match closure_kind {
+                let (id, ty, resume_yield_tys) = match closure_kind {
                     ClosureKind::Generator(_) => {
                         // FIXME: report error when there are more than 1 parameter.
                         let resume_ty = match sig_tys.first() {
@@ -267,7 +276,7 @@ impl<'a> InferenceContext<'a> {
                         let generator_id = self.db.intern_generator((self.owner, tgt_expr)).into();
                         let generator_ty = TyKind::Generator(generator_id, subst).intern(Interner);
 
-                        (generator_ty, Some((resume_ty, yield_ty)))
+                        (None, generator_ty, Some((resume_ty, yield_ty)))
                     }
                     ClosureKind::Closure | ClosureKind::Async => {
                         let closure_id = self.db.intern_closure((self.owner, tgt_expr)).into();
@@ -276,8 +285,11 @@ impl<'a> InferenceContext<'a> {
                             Substitution::from1(Interner, sig_ty.clone()),
                         )
                         .intern(Interner);
-
-                        (closure_ty, None)
+                        self.deferred_closures.entry(closure_id).or_default();
+                        if let Some(c) = self.current_closure {
+                            self.closure_dependecies.entry(c).or_default().push(closure_id);
+                        }
+                        (Some(closure_id), closure_ty, None)
                     }
                 };
 
@@ -293,6 +305,7 @@ impl<'a> InferenceContext<'a> {
 
                 // FIXME: lift these out into a struct
                 let prev_diverges = mem::replace(&mut self.diverges, Diverges::Maybe);
+                let prev_closure = mem::replace(&mut self.current_closure, id);
                 let prev_ret_ty = mem::replace(&mut self.return_ty, ret_ty.clone());
                 let prev_ret_coercion =
                     mem::replace(&mut self.return_coercion, Some(CoerceMany::new(ret_ty)));
@@ -306,6 +319,7 @@ impl<'a> InferenceContext<'a> {
                 self.diverges = prev_diverges;
                 self.return_ty = prev_ret_ty;
                 self.return_coercion = prev_ret_coercion;
+                self.current_closure = prev_closure;
                 self.resume_yield_tys = prev_resume_yield_tys;
 
                 ty
@@ -331,43 +345,28 @@ impl<'a> InferenceContext<'a> {
                 let (param_tys, ret_ty) = match res {
                     Some((func, params, ret_ty)) => {
                         let mut adjustments = auto_deref_adjust_steps(&derefs);
-                        if let Some(fn_x) = func {
-                            match fn_x {
-                                FnTrait::FnOnce => (),
-                                FnTrait::FnMut => {
-                                    if !matches!(
-                                        derefed_callee.kind(Interner),
-                                        TyKind::Ref(Mutability::Mut, _, _)
-                                    ) {
-                                        adjustments.push(Adjustment::borrow(
-                                            Mutability::Mut,
-                                            derefed_callee.clone(),
-                                        ));
-                                    }
-                                }
-                                FnTrait::Fn => {
-                                    if !matches!(
-                                        derefed_callee.kind(Interner),
-                                        TyKind::Ref(Mutability::Not, _, _)
-                                    ) {
-                                        adjustments.push(Adjustment::borrow(
-                                            Mutability::Not,
-                                            derefed_callee.clone(),
-                                        ));
-                                    }
-                                }
-                            }
-                            let trait_ = fn_x
-                                .get_id(self.db, self.table.trait_env.krate)
-                                .expect("We just used it");
-                            let trait_data = self.db.trait_data(trait_);
-                            if let Some(func) = trait_data.method_by_name(&fn_x.method_name()) {
-                                let subst = TyBuilder::subst_for_def(self.db, trait_, None)
-                                    .push(callee_ty.clone())
-                                    .push(TyBuilder::tuple_with(params.iter().cloned()))
-                                    .build();
-                                self.write_method_resolution(tgt_expr, func, subst)
+                        if let TyKind::Closure(c, _) =
+                            self.table.resolve_completely(callee_ty.clone()).kind(Interner)
+                        {
+                            if let Some(par) = self.current_closure {
+                                self.closure_dependecies.entry(par).or_default().push(*c);
                             }
+                            self.deferred_closures.entry(*c).or_default().push((
+                                derefed_callee.clone(),
+                                callee_ty.clone(),
+                                params.clone(),
+                                tgt_expr,
+                            ));
+                        }
+                        if let Some(fn_x) = func {
+                            self.write_fn_trait_method_resolution(
+                                fn_x,
+                                &derefed_callee,
+                                &mut adjustments,
+                                &callee_ty,
+                                &params,
+                                tgt_expr,
+                            );
                         }
                         self.write_expr_adj(*callee, adjustments);
                         (params, ret_ty)
@@ -459,8 +458,8 @@ impl<'a> InferenceContext<'a> {
                 self.resolver.reset_to_guard(g);
                 ty
             }
-            Expr::Continue { label } => {
-                if let None = find_continuable(&mut self.breakables, label.as_ref()) {
+            &Expr::Continue { label } => {
+                if let None = find_continuable(&mut self.breakables, label) {
                     self.push_diagnostic(InferenceDiagnostic::BreakOutsideOfLoop {
                         expr: tgt_expr,
                         is_break: false,
@@ -469,9 +468,9 @@ impl<'a> InferenceContext<'a> {
                 };
                 self.result.standard_types.never.clone()
             }
-            Expr::Break { expr, label } => {
-                let val_ty = if let Some(expr) = *expr {
-                    let opt_coerce_to = match find_breakable(&mut self.breakables, label.as_ref()) {
+            &Expr::Break { expr, label } => {
+                let val_ty = if let Some(expr) = expr {
+                    let opt_coerce_to = match find_breakable(&mut self.breakables, label) {
                         Some(ctxt) => match &ctxt.coerce {
                             Some(coerce) => coerce.expected_ty(),
                             None => {
@@ -490,13 +489,13 @@ impl<'a> InferenceContext<'a> {
                     TyBuilder::unit()
                 };
 
-                match find_breakable(&mut self.breakables, label.as_ref()) {
+                match find_breakable(&mut self.breakables, label) {
                     Some(ctxt) => match ctxt.coerce.take() {
                         Some(mut coerce) => {
-                            coerce.coerce(self, *expr, &val_ty);
+                            coerce.coerce(self, expr, &val_ty);
 
                             // Avoiding borrowck
-                            let ctxt = find_breakable(&mut self.breakables, label.as_ref())
+                            let ctxt = find_breakable(&mut self.breakables, label)
                                 .expect("breakable stack changed during coercion");
                             ctxt.may_break = true;
                             ctxt.coerce = Some(coerce);
@@ -897,6 +896,41 @@ impl<'a> InferenceContext<'a> {
         TyKind::OpaqueType(opaque_ty_id, Substitution::from1(Interner, inner_ty)).intern(Interner)
     }
 
+    pub(crate) fn write_fn_trait_method_resolution(
+        &mut self,
+        fn_x: FnTrait,
+        derefed_callee: &Ty,
+        adjustments: &mut Vec<Adjustment>,
+        callee_ty: &Ty,
+        params: &Vec<Ty>,
+        tgt_expr: ExprId,
+    ) {
+        match fn_x {
+            FnTrait::FnOnce => (),
+            FnTrait::FnMut => {
+                if !matches!(derefed_callee.kind(Interner), TyKind::Ref(Mutability::Mut, _, _)) {
+                    adjustments.push(Adjustment::borrow(Mutability::Mut, derefed_callee.clone()));
+                }
+            }
+            FnTrait::Fn => {
+                if !matches!(derefed_callee.kind(Interner), TyKind::Ref(Mutability::Not, _, _)) {
+                    adjustments.push(Adjustment::borrow(Mutability::Not, derefed_callee.clone()));
+                }
+            }
+        }
+        let Some(trait_) = fn_x.get_id(self.db, self.table.trait_env.krate) else {
+            return;
+        };
+        let trait_data = self.db.trait_data(trait_);
+        if let Some(func) = trait_data.method_by_name(&fn_x.method_name()) {
+            let subst = TyBuilder::subst_for_def(self.db, trait_, None)
+                .push(callee_ty.clone())
+                .push(TyBuilder::tuple_with(params.iter().cloned()))
+                .build();
+            self.write_method_resolution(tgt_expr, func, subst.clone());
+        }
+    }
+
     fn infer_expr_array(
         &mut self,
         array: &Array,
@@ -1900,7 +1934,6 @@ impl<'a> InferenceContext<'a> {
         cb: impl FnOnce(&mut Self) -> T,
     ) -> (Option<Ty>, T) {
         self.breakables.push({
-            let label = label.map(|label| self.body[label].name.clone());
             BreakableContext { kind, may_break: false, coerce: ty.map(CoerceMany::new), label }
         });
         let res = cb(self);
diff --git a/crates/hir-ty/src/infer/mutability.rs b/crates/hir-ty/src/infer/mutability.rs
index 6f5ca2cf95d..f344b0610c9 100644
--- a/crates/hir-ty/src/infer/mutability.rs
+++ b/crates/hir-ty/src/infer/mutability.rs
@@ -3,7 +3,7 @@
 
 use chalk_ir::Mutability;
 use hir_def::{
-    expr::{Array, BindingAnnotation, Expr, ExprId, PatId, Statement, UnaryOp},
+    hir::{Array, BinaryOp, BindingAnnotation, Expr, ExprId, PatId, Statement, UnaryOp},
     lang_item::LangItem,
 };
 use hir_expand::name;
@@ -80,6 +80,9 @@ impl<'a> InferenceContext<'a> {
                 self.infer_mut_expr(*expr, m);
                 for arm in arms.iter() {
                     self.infer_mut_expr(arm.expr, Mutability::Not);
+                    if let Some(g) = arm.guard {
+                        self.infer_mut_expr(g, Mutability::Not);
+                    }
                 }
             }
             Expr::Yield { expr }
@@ -158,14 +161,19 @@ impl<'a> InferenceContext<'a> {
                 let mutability = lower_to_chalk_mutability(*mutability);
                 self.infer_mut_expr(*expr, mutability);
             }
+            Expr::BinaryOp { lhs, rhs, op: Some(BinaryOp::Assignment { .. }) } => {
+                self.infer_mut_expr(*lhs, Mutability::Mut);
+                self.infer_mut_expr(*rhs, Mutability::Not);
+            }
             Expr::Array(Array::Repeat { initializer: lhs, repeat: rhs })
             | Expr::BinaryOp { lhs, rhs, op: _ }
             | Expr::Range { lhs: Some(lhs), rhs: Some(rhs), range_type: _ } => {
                 self.infer_mut_expr(*lhs, Mutability::Not);
                 self.infer_mut_expr(*rhs, Mutability::Not);
             }
-            // not implemented
-            Expr::Closure { .. } => (),
+            Expr::Closure { body, .. } => {
+                self.infer_mut_expr(*body, Mutability::Not);
+            }
             Expr::Tuple { exprs, is_assignee_expr: _ }
             | Expr::Array(Array::ElementList { elements: exprs, is_assignee_expr: _ }) => {
                 self.infer_mut_not_expr_iter(exprs.iter().copied());
diff --git a/crates/hir-ty/src/infer/pat.rs b/crates/hir-ty/src/infer/pat.rs
index 5f839fc307a..ce179210d34 100644
--- a/crates/hir-ty/src/infer/pat.rs
+++ b/crates/hir-ty/src/infer/pat.rs
@@ -5,7 +5,7 @@ use std::iter::repeat_with;
 use chalk_ir::Mutability;
 use hir_def::{
     body::Body,
-    expr::{Binding, BindingAnnotation, BindingId, Expr, ExprId, ExprOrPatId, Literal, Pat, PatId},
+    hir::{Binding, BindingAnnotation, BindingId, Expr, ExprId, ExprOrPatId, Literal, Pat, PatId},
     path::Path,
 };
 use hir_expand::name::Name;
diff --git a/crates/hir-ty/src/inhabitedness.rs b/crates/hir-ty/src/inhabitedness.rs
index 39324d27306..25b226bb3b5 100644
--- a/crates/hir-ty/src/inhabitedness.rs
+++ b/crates/hir-ty/src/inhabitedness.rs
@@ -6,8 +6,8 @@ use chalk_ir::{
     DebruijnIndex,
 };
 use hir_def::{
-    adt::VariantData, attr::Attrs, visibility::Visibility, AdtId, EnumVariantId, HasModule, Lookup,
-    ModuleId, VariantId,
+    attr::Attrs, data::adt::VariantData, visibility::Visibility, AdtId, EnumVariantId, HasModule,
+    Lookup, ModuleId, VariantId,
 };
 use rustc_hash::FxHashSet;
 
diff --git a/crates/hir-ty/src/lang_items.rs b/crates/hir-ty/src/lang_items.rs
index b0ad4fc946a..adfdb01e72a 100644
--- a/crates/hir-ty/src/lang_items.rs
+++ b/crates/hir-ty/src/lang_items.rs
@@ -1,6 +1,6 @@
 //! Functions to detect special lang items
 
-use hir_def::{adt::StructFlags, lang_item::LangItem, AdtId};
+use hir_def::{data::adt::StructFlags, lang_item::LangItem, AdtId};
 use hir_expand::name::Name;
 
 use crate::db::HirDatabase;
diff --git a/crates/hir-ty/src/layout.rs b/crates/hir-ty/src/layout.rs
index b95bb01fcef..23cad5e6fd3 100644
--- a/crates/hir-ty/src/layout.rs
+++ b/crates/hir-ty/src/layout.rs
@@ -229,11 +229,24 @@ pub fn layout_of_ty(db: &dyn HirDatabase, ty: &Ty, krate: CrateId) -> Result<Lay
                 }
             }
         }
-        TyKind::Closure(_, _) | TyKind::Generator(_, _) | TyKind::GeneratorWitness(_, _) => {
+        TyKind::Closure(c, _) => {
+            let (def, _) = db.lookup_intern_closure((*c).into());
+            let infer = db.infer(def);
+            let (captures, _) = infer.closure_info(c);
+            let fields = captures
+                .iter()
+                .map(|x| layout_of_ty(db, &x.ty, krate))
+                .collect::<Result<Vec<_>, _>>()?;
+            let fields = fields.iter().collect::<Vec<_>>();
+            let fields = fields.iter().collect::<Vec<_>>();
+            cx.univariant(dl, &fields, &ReprOptions::default(), StructKind::AlwaysSized)
+                .ok_or(LayoutError::Unknown)?
+        }
+        TyKind::Generator(_, _) | TyKind::GeneratorWitness(_, _) => {
             return Err(LayoutError::NotImplemented)
         }
+        TyKind::Error => return Err(LayoutError::HasErrorType),
         TyKind::AssociatedType(_, _)
-        | TyKind::Error
         | TyKind::Alias(_)
         | TyKind::Placeholder(_)
         | TyKind::BoundVar(_)
diff --git a/crates/hir-ty/src/layout/adt.rs b/crates/hir-ty/src/layout/adt.rs
index 18199f327d1..3f3017deee9 100644
--- a/crates/hir-ty/src/layout/adt.rs
+++ b/crates/hir-ty/src/layout/adt.rs
@@ -3,7 +3,7 @@
 use std::ops::Bound;
 
 use hir_def::{
-    adt::VariantData,
+    data::adt::VariantData,
     layout::{Integer, IntegerExt, Layout, LayoutCalculator, LayoutError, RustcEnumVariantIdx},
     AdtId, EnumVariantId, HasModule, LocalEnumVariantId, VariantId,
 };
diff --git a/crates/hir-ty/src/layout/tests.rs b/crates/hir-ty/src/layout/tests.rs
index a8971fde3c2..a0ffab55180 100644
--- a/crates/hir-ty/src/layout/tests.rs
+++ b/crates/hir-ty/src/layout/tests.rs
@@ -11,6 +11,8 @@ use crate::{db::HirDatabase, test_db::TestDB, Interner, Substitution};
 
 use super::layout_of_ty;
 
+mod closure;
+
 fn current_machine_data_layout() -> String {
     project_model::target_data_layout::get(None, None, &HashMap::default()).unwrap()
 }
@@ -81,8 +83,8 @@ fn check_size_and_align(ra_fixture: &str, minicore: &str, size: u64, align: u64)
 #[track_caller]
 fn check_size_and_align_expr(ra_fixture: &str, minicore: &str, size: u64, align: u64) {
     let l = eval_expr(ra_fixture, minicore).unwrap();
-    assert_eq!(l.size.bytes(), size);
-    assert_eq!(l.align.abi.bytes(), align);
+    assert_eq!(l.size.bytes(), size, "size mismatch");
+    assert_eq!(l.align.abi.bytes(), align, "align mismatch");
 }
 
 #[track_caller]
@@ -118,13 +120,31 @@ macro_rules! size_and_align {
     };
 }
 
+#[macro_export]
 macro_rules! size_and_align_expr {
+    (minicore: $($x:tt),*; stmts: [$($s:tt)*] $($t:tt)*) => {
+        {
+            #[allow(dead_code)]
+            #[allow(unused_must_use)]
+            #[allow(path_statements)]
+            {
+                $($s)*
+                let val = { $($t)* };
+                $crate::layout::tests::check_size_and_align_expr(
+                    &format!("{{ {} let val = {{ {} }}; val }}", stringify!($($s)*), stringify!($($t)*)),
+                    &format!("//- minicore: {}\n", stringify!($($x),*)),
+                    ::std::mem::size_of_val(&val) as u64,
+                    ::std::mem::align_of_val(&val) as u64,
+                );
+            }
+        }
+    };
     ($($t:tt)*) => {
         {
             #[allow(dead_code)]
             {
                 let val = { $($t)* };
-                check_size_and_align_expr(
+                $crate::layout::tests::check_size_and_align_expr(
                     stringify!($($t)*),
                     "",
                     ::std::mem::size_of_val(&val) as u64,
@@ -213,6 +233,45 @@ fn return_position_impl_trait() {
         foo()
     }
     size_and_align_expr! {
+        minicore: iterators;
+        stmts: []
+        trait Tr {}
+        impl Tr for i32 {}
+        fn foo() -> impl Iterator<Item = impl Tr> {
+            [1, 2, 3].into_iter()
+        }
+        let mut iter = foo();
+        let item = iter.next();
+        (iter, item)
+    }
+    size_and_align_expr! {
+        minicore: future;
+        stmts: []
+        use core::{future::Future, task::{Poll, Context}, pin::pin};
+        use std::{task::Wake, sync::Arc};
+        trait Tr {}
+        impl Tr for i32 {}
+        async fn f() -> impl Tr {
+            2
+        }
+        fn unwrap_fut<T>(inp: impl Future<Output = T>) -> Poll<T> {
+            // In a normal test we could use `loop {}` or `panic!()` here,
+            // but rustc actually runs this code.
+            let pinned = pin!(inp);
+            struct EmptyWaker;
+            impl Wake for EmptyWaker {
+                fn wake(self: Arc<Self>) {
+                }
+            }
+            let waker = Arc::new(EmptyWaker).into();
+            let mut context = Context::from_waker(&waker);
+            let x = pinned.poll(&mut context);
+            x
+        }
+        let x = unwrap_fut(f());
+        x
+    }
+    size_and_align_expr! {
         struct Foo<T>(T, T, (T, T));
         trait T {}
         impl T for Foo<i32> {}
diff --git a/crates/hir-ty/src/layout/tests/closure.rs b/crates/hir-ty/src/layout/tests/closure.rs
new file mode 100644
index 00000000000..31b6765a7a2
--- /dev/null
+++ b/crates/hir-ty/src/layout/tests/closure.rs
@@ -0,0 +1,175 @@
+use crate::size_and_align_expr;
+
+#[test]
+fn zero_capture_simple() {
+    size_and_align_expr! {
+        |x: i32| x + 2
+    }
+}
+
+#[test]
+fn move_simple() {
+    size_and_align_expr! {
+        minicore: copy;
+        stmts: []
+        let y: i32 = 5;
+        move |x: i32| {
+            x + y
+        }
+    }
+}
+
+#[test]
+fn ref_simple() {
+    size_and_align_expr! {
+        minicore: copy;
+        stmts: [
+            let y: i32 = 5;
+        ]
+        |x: i32| {
+            x + y
+        }
+    }
+    size_and_align_expr! {
+        minicore: copy;
+        stmts: [
+            let mut y: i32 = 5;
+        ]
+        |x: i32| {
+            y = y + x;
+            y
+        }
+    }
+    size_and_align_expr! {
+        minicore: copy;
+        stmts: [
+            struct X(i32, i64);
+            let x: X = X(2, 6);
+        ]
+        || {
+            x
+        }
+    }
+}
+
+#[test]
+fn ref_then_mut_then_move() {
+    size_and_align_expr! {
+        minicore: copy;
+        stmts: [
+            struct X(i32, i64);
+            let mut x: X = X(2, 6);
+        ]
+        || {
+            &x;
+            &mut x;
+            x;
+        }
+    }
+}
+
+#[test]
+fn nested_closures() {
+    size_and_align_expr! {
+        || {
+            || {
+                || {
+                    let x = 2;
+                    move || {
+                        move || {
+                            x
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
+
+#[test]
+fn capture_specific_fields() {
+    size_and_align_expr! {
+        struct X(i64, i32, (u8, i128));
+        let y: X = X(2, 5, (7, 3));
+        move |x: i64| {
+            y.0 + x + (y.2 .0 as i64)
+        }
+    }
+    size_and_align_expr! {
+        struct X(i64, i32, (u8, i128));
+        let y: X = X(2, 5, (7, 3));
+        move |x: i64| {
+            let _ = &y;
+            y.0 + x + (y.2 .0 as i64)
+        }
+    }
+    size_and_align_expr! {
+        minicore: copy;
+        stmts: [
+            struct X(i64, i32, (u8, i128));
+            let y: X = X(2, 5, (7, 3));
+        ]
+        let y = &y;
+        move |x: i64| {
+            y.0 + x + (y.2 .0 as i64)
+        }
+    }
+    size_and_align_expr! {
+        struct X(i64, i32, (u8, i128));
+        let y: X = X(2, 5, (7, 3));
+        move |x: i64| {
+            let X(a, _, (b, _)) = y;
+            a + x + (b as i64)
+        }
+    }
+    size_and_align_expr! {
+        struct X(i64, i32, (u8, i128));
+        let y = &&X(2, 5, (7, 3));
+        move |x: i64| {
+            let X(a, _, (b, _)) = y;
+            *a + x + (*b as i64)
+        }
+    }
+    size_and_align_expr! {
+        struct X(i64, i32, (u8, i128));
+        let y: X = X(2, 5, (7, 3));
+        move |x: i64| {
+            match y {
+                X(a, _, (b, _)) => a + x + (b as i64),
+            }
+        }
+    }
+    size_and_align_expr! {
+        struct X(i64, i32, (u8, i128));
+        let y: X = X(2, 5, (7, 3));
+        move |x: i64| {
+            let X(a @ 2, _, (b, _)) = y else { return 5 };
+            a + x + (b as i64)
+        }
+    }
+}
+
+#[test]
+fn ellipsis_pattern() {
+    size_and_align_expr! {
+        struct X(i8, u16, i32, u64, i128, u8);
+        let y: X = X(1, 2, 3, 4, 5, 6);
+        move |_: i64| {
+            let X(_a, .., _b, _c) = y;
+        }
+    }
+    size_and_align_expr! {
+        struct X { a: i32, b: u8, c: i128}
+        let y: X = X { a: 1, b: 2, c: 3 };
+        move |_: i64| {
+            let X { a, b, .. } = y;
+            _ = (a, b);
+        }
+    }
+    size_and_align_expr! {
+        let y: (&&&(i8, u16, i32, u64, i128, u8), u16, i32, u64, i128, u8) = (&&&(1, 2, 3, 4, 5, 6), 2, 3, 4, 5, 6);
+        move |_: i64| {
+            let ((_a, .., _b, _c), .., _e, _f) = y;
+        }
+    }
+}
diff --git a/crates/hir-ty/src/lib.rs b/crates/hir-ty/src/lib.rs
index c36fad614f8..50fe5a724d0 100644
--- a/crates/hir-ty/src/lib.rs
+++ b/crates/hir-ty/src/lib.rs
@@ -44,7 +44,7 @@ use chalk_ir::{
     NoSolution, TyData,
 };
 use either::Either;
-use hir_def::{expr::ExprId, type_ref::Rawness, TypeOrConstParamId};
+use hir_def::{hir::ExprId, type_ref::Rawness, TypeOrConstParamId};
 use hir_expand::name;
 use la_arena::{Arena, Idx};
 use mir::MirEvalError;
diff --git a/crates/hir-ty/src/lower.rs b/crates/hir-ty/src/lower.rs
index adadbb888ba..b37fe1d63d5 100644
--- a/crates/hir-ty/src/lower.rs
+++ b/crates/hir-ty/src/lower.rs
@@ -18,9 +18,9 @@ use chalk_ir::{
 
 use either::Either;
 use hir_def::{
-    adt::StructKind,
-    body::{Expander, LowerCtx},
+    body::Expander,
     builtin_type::BuiltinType,
+    data::adt::StructKind,
     generics::{
         TypeOrConstParamData, TypeParamProvenance, WherePredicate, WherePredicateTypeTarget,
     },
@@ -380,7 +380,7 @@ impl<'a> TyLoweringContext<'a> {
                     let macro_call = macro_call.to_node(self.db.upcast());
                     match expander.enter_expand::<ast::Type>(self.db.upcast(), macro_call) {
                         Ok(ExpandResult { value: Some((mark, expanded)), .. }) => {
-                            let ctx = LowerCtx::new(self.db.upcast(), expander.current_file_id());
+                            let ctx = expander.ctx(self.db.upcast());
                             let type_ref = TypeRef::from_ast(&ctx, expanded);
 
                             drop(expander);
@@ -988,7 +988,7 @@ impl<'a> TyLoweringContext<'a> {
                         // ignore `T: Drop` or `T: Destruct` bounds.
                         // - `T: ~const Drop` has a special meaning in Rust 1.61 that we don't implement.
                         //   (So ideally, we'd only ignore `~const Drop` here)
-                        // - `Destruct` impls are built-in in 1.62 (current nightlies as of 08-04-2022), so until
+                        // - `Destruct` impls are built-in in 1.62 (current nightly as of 08-04-2022), so until
                         //   the builtin impls are supported by Chalk, we ignore them here.
                         if let Some(lang) = lang_attr(self.db.upcast(), tr.hir_trait_id()) {
                             if matches!(lang, LangItem::Drop | LangItem::Destruct) {
@@ -1082,23 +1082,23 @@ impl<'a> TyLoweringContext<'a> {
                     associated_ty_id: to_assoc_type_id(associated_ty),
                     substitution,
                 };
-                let mut preds: SmallVec<[_; 1]> = SmallVec::with_capacity(
+                let mut predicates: SmallVec<[_; 1]> = SmallVec::with_capacity(
                     binding.type_ref.as_ref().map_or(0, |_| 1) + binding.bounds.len(),
                 );
                 if let Some(type_ref) = &binding.type_ref {
                     let ty = self.lower_ty(type_ref);
                     let alias_eq =
                         AliasEq { alias: AliasTy::Projection(projection_ty.clone()), ty };
-                    preds.push(crate::wrap_empty_binders(WhereClause::AliasEq(alias_eq)));
+                    predicates.push(crate::wrap_empty_binders(WhereClause::AliasEq(alias_eq)));
                 }
                 for bound in binding.bounds.iter() {
-                    preds.extend(self.lower_type_bound(
+                    predicates.extend(self.lower_type_bound(
                         bound,
                         TyKind::Alias(AliasTy::Projection(projection_ty.clone())).intern(Interner),
                         false,
                     ));
                 }
-                preds
+                predicates
             })
     }
 
@@ -1165,7 +1165,7 @@ impl<'a> TyLoweringContext<'a> {
                 return None;
             }
 
-            // As multiple occurrences of the same auto traits *are* permitted, we dedulicate the
+            // As multiple occurrences of the same auto traits *are* permitted, we deduplicate the
             // bounds. We shouldn't have repeated elements besides auto traits at this point.
             bounds.dedup();
 
@@ -1634,7 +1634,7 @@ fn fn_sig_for_fn(db: &dyn HirDatabase, def: FunctionId) -> PolyFnSig {
     let ctx_params = TyLoweringContext::new(db, &resolver)
         .with_impl_trait_mode(ImplTraitLoweringMode::Variable)
         .with_type_param_mode(ParamLoweringMode::Variable);
-    let params = data.params.iter().map(|(_, tr)| ctx_params.lower_ty(tr)).collect::<Vec<_>>();
+    let params = data.params.iter().map(|tr| ctx_params.lower_ty(tr)).collect::<Vec<_>>();
     let ctx_ret = TyLoweringContext::new(db, &resolver)
         .with_impl_trait_mode(ImplTraitLoweringMode::Opaque)
         .with_type_param_mode(ParamLoweringMode::Variable);
diff --git a/crates/hir-ty/src/method_resolution.rs b/crates/hir-ty/src/method_resolution.rs
index 5d06906aa3a..740b6ddc27a 100644
--- a/crates/hir-ty/src/method_resolution.rs
+++ b/crates/hir-ty/src/method_resolution.rs
@@ -7,9 +7,11 @@ use std::{ops::ControlFlow, sync::Arc};
 use base_db::{CrateId, Edition};
 use chalk_ir::{cast::Cast, Mutability, TyKind, UniverseIndex, WhereClause};
 use hir_def::{
-    adt::StructFlags, data::ImplData, item_scope::ItemScope, nameres::DefMap, AssocItemId, BlockId,
-    ConstId, FunctionId, HasModule, ImplId, ItemContainerId, Lookup, ModuleDefId, ModuleId,
-    TraitId,
+    data::{adt::StructFlags, ImplData},
+    item_scope::ItemScope,
+    nameres::DefMap,
+    AssocItemId, BlockId, ConstId, FunctionId, HasModule, ImplId, ItemContainerId, Lookup,
+    ModuleDefId, ModuleId, TraitId,
 };
 use hir_expand::name::Name;
 use rustc_hash::{FxHashMap, FxHashSet};
@@ -952,7 +954,14 @@ fn iterate_method_candidates_with_autoref(
         )
     };
 
-    iterate_method_candidates_by_receiver(receiver_ty, first_adjustment.clone())?;
+    let mut maybe_reborrowed = first_adjustment.clone();
+    if let Some((_, _, m)) = receiver_ty.value.as_reference() {
+        // Prefer reborrow of references to move
+        maybe_reborrowed.autoref = Some(m);
+        maybe_reborrowed.autoderefs += 1;
+    }
+
+    iterate_method_candidates_by_receiver(receiver_ty, maybe_reborrowed)?;
 
     let refed = Canonical {
         value: TyKind::Ref(Mutability::Not, static_lifetime(), receiver_ty.value.clone())
diff --git a/crates/hir-ty/src/mir.rs b/crates/hir-ty/src/mir.rs
index 7c1cbbdf53d..2fa1bf2b7e6 100644
--- a/crates/hir-ty/src/mir.rs
+++ b/crates/hir-ty/src/mir.rs
@@ -3,11 +3,12 @@
 use std::{fmt::Display, iter};
 
 use crate::{
-    infer::PointerCast, Const, ConstScalar, InferenceResult, Interner, MemoryMap, Substitution, Ty,
+    db::HirDatabase, infer::PointerCast, ClosureId, Const, ConstScalar, InferenceResult, Interner,
+    MemoryMap, Substitution, Ty, TyKind,
 };
 use chalk_ir::Mutability;
 use hir_def::{
-    expr::{BindingId, Expr, ExprId, Ordering, PatId},
+    hir::{BindingId, Expr, ExprId, Ordering, PatId},
     DefWithBodyId, FieldId, UnionId, VariantId,
 };
 use la_arena::{Arena, ArenaMap, Idx, RawIdx};
@@ -19,9 +20,11 @@ mod pretty;
 
 pub use borrowck::{borrowck_query, BorrowckResult, MutabilityReason};
 pub use eval::{interpret_mir, pad16, Evaluator, MirEvalError};
-pub use lower::{lower_to_mir, mir_body_query, mir_body_recover, MirLowerError};
+pub use lower::{
+    lower_to_mir, mir_body_for_closure_query, mir_body_query, mir_body_recover, MirLowerError,
+};
 use smallvec::{smallvec, SmallVec};
-use stdx::impl_from;
+use stdx::{impl_from, never};
 
 use super::consteval::{intern_const_scalar, try_const_usize};
 
@@ -89,11 +92,12 @@ impl Operand {
     }
 }
 
-#[derive(Debug, PartialEq, Eq, Clone)]
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub enum ProjectionElem<V, T> {
     Deref,
     Field(FieldId),
-    TupleField(usize),
+    // FIXME: get rid of this, and use FieldId for tuples and closures
+    TupleOrClosureField(usize),
     Index(V),
     ConstantIndex { offset: u64, min_length: u64, from_end: bool },
     Subslice { from: u64, to: u64, from_end: bool },
@@ -101,6 +105,63 @@ pub enum ProjectionElem<V, T> {
     OpaqueCast(T),
 }
 
+impl<V, T> ProjectionElem<V, T> {
+    pub fn projected_ty(
+        &self,
+        base: Ty,
+        db: &dyn HirDatabase,
+        closure_field: impl FnOnce(ClosureId, usize) -> Ty,
+    ) -> Ty {
+        match self {
+            ProjectionElem::Deref => match &base.data(Interner).kind {
+                TyKind::Raw(_, inner) | TyKind::Ref(_, _, inner) => inner.clone(),
+                _ => {
+                    never!("Overloaded deref is not a projection");
+                    return TyKind::Error.intern(Interner);
+                }
+            },
+            ProjectionElem::Field(f) => match &base.data(Interner).kind {
+                TyKind::Adt(_, subst) => {
+                    db.field_types(f.parent)[f.local_id].clone().substitute(Interner, subst)
+                }
+                _ => {
+                    never!("Only adt has field");
+                    return TyKind::Error.intern(Interner);
+                }
+            },
+            ProjectionElem::TupleOrClosureField(f) => match &base.data(Interner).kind {
+                TyKind::Tuple(_, subst) => subst
+                    .as_slice(Interner)
+                    .get(*f)
+                    .map(|x| x.assert_ty_ref(Interner))
+                    .cloned()
+                    .unwrap_or_else(|| {
+                        never!("Out of bound tuple field");
+                        TyKind::Error.intern(Interner)
+                    }),
+                TyKind::Closure(id, _) => closure_field(*id, *f),
+                _ => {
+                    never!("Only tuple or closure has tuple or closure field");
+                    return TyKind::Error.intern(Interner);
+                }
+            },
+            ProjectionElem::Index(_) => match &base.data(Interner).kind {
+                TyKind::Array(inner, _) | TyKind::Slice(inner) => inner.clone(),
+                _ => {
+                    never!("Overloaded index is not a projection");
+                    return TyKind::Error.intern(Interner);
+                }
+            },
+            ProjectionElem::ConstantIndex { .. }
+            | ProjectionElem::Subslice { .. }
+            | ProjectionElem::OpaqueCast(_) => {
+                never!("We don't emit these yet");
+                return TyKind::Error.intern(Interner);
+            }
+        }
+    }
+}
+
 type PlaceElem = ProjectionElem<LocalId, Ty>;
 
 #[derive(Debug, Clone, PartialEq, Eq)]
@@ -123,7 +184,7 @@ pub enum AggregateKind {
     Tuple(Ty),
     Adt(VariantId, Substitution),
     Union(UnionId, FieldId),
-    //Closure(LocalDefId, SubstsRef),
+    Closure(Ty),
     //Generator(LocalDefId, SubstsRef, Movability),
 }
 
@@ -418,7 +479,7 @@ pub enum Terminator {
     },
 }
 
-#[derive(Debug, PartialEq, Eq, Clone)]
+#[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord)]
 pub enum BorrowKind {
     /// Data must be immutable and is aliasable.
     Shared,
@@ -588,32 +649,32 @@ impl Display for BinOp {
     }
 }
 
-impl From<hir_def::expr::ArithOp> for BinOp {
-    fn from(value: hir_def::expr::ArithOp) -> Self {
+impl From<hir_def::hir::ArithOp> for BinOp {
+    fn from(value: hir_def::hir::ArithOp) -> Self {
         match value {
-            hir_def::expr::ArithOp::Add => BinOp::Add,
-            hir_def::expr::ArithOp::Mul => BinOp::Mul,
-            hir_def::expr::ArithOp::Sub => BinOp::Sub,
-            hir_def::expr::ArithOp::Div => BinOp::Div,
-            hir_def::expr::ArithOp::Rem => BinOp::Rem,
-            hir_def::expr::ArithOp::Shl => BinOp::Shl,
-            hir_def::expr::ArithOp::Shr => BinOp::Shr,
-            hir_def::expr::ArithOp::BitXor => BinOp::BitXor,
-            hir_def::expr::ArithOp::BitOr => BinOp::BitOr,
-            hir_def::expr::ArithOp::BitAnd => BinOp::BitAnd,
+            hir_def::hir::ArithOp::Add => BinOp::Add,
+            hir_def::hir::ArithOp::Mul => BinOp::Mul,
+            hir_def::hir::ArithOp::Sub => BinOp::Sub,
+            hir_def::hir::ArithOp::Div => BinOp::Div,
+            hir_def::hir::ArithOp::Rem => BinOp::Rem,
+            hir_def::hir::ArithOp::Shl => BinOp::Shl,
+            hir_def::hir::ArithOp::Shr => BinOp::Shr,
+            hir_def::hir::ArithOp::BitXor => BinOp::BitXor,
+            hir_def::hir::ArithOp::BitOr => BinOp::BitOr,
+            hir_def::hir::ArithOp::BitAnd => BinOp::BitAnd,
         }
     }
 }
 
-impl From<hir_def::expr::CmpOp> for BinOp {
-    fn from(value: hir_def::expr::CmpOp) -> Self {
+impl From<hir_def::hir::CmpOp> for BinOp {
+    fn from(value: hir_def::hir::CmpOp) -> Self {
         match value {
-            hir_def::expr::CmpOp::Eq { negated: false } => BinOp::Eq,
-            hir_def::expr::CmpOp::Eq { negated: true } => BinOp::Ne,
-            hir_def::expr::CmpOp::Ord { ordering: Ordering::Greater, strict: false } => BinOp::Ge,
-            hir_def::expr::CmpOp::Ord { ordering: Ordering::Greater, strict: true } => BinOp::Gt,
-            hir_def::expr::CmpOp::Ord { ordering: Ordering::Less, strict: false } => BinOp::Le,
-            hir_def::expr::CmpOp::Ord { ordering: Ordering::Less, strict: true } => BinOp::Lt,
+            hir_def::hir::CmpOp::Eq { negated: false } => BinOp::Eq,
+            hir_def::hir::CmpOp::Eq { negated: true } => BinOp::Ne,
+            hir_def::hir::CmpOp::Ord { ordering: Ordering::Greater, strict: false } => BinOp::Ge,
+            hir_def::hir::CmpOp::Ord { ordering: Ordering::Greater, strict: true } => BinOp::Gt,
+            hir_def::hir::CmpOp::Ord { ordering: Ordering::Less, strict: false } => BinOp::Le,
+            hir_def::hir::CmpOp::Ord { ordering: Ordering::Less, strict: true } => BinOp::Lt,
         }
     }
 }
@@ -847,6 +908,87 @@ pub struct MirBody {
     pub arg_count: usize,
     pub binding_locals: ArenaMap<BindingId, LocalId>,
     pub param_locals: Vec<LocalId>,
+    /// This field stores the closures directly owned by this body. It is used
+    /// in traversing every mir body.
+    pub closures: Vec<ClosureId>,
+}
+
+impl MirBody {
+    fn walk_places(&mut self, mut f: impl FnMut(&mut Place)) {
+        fn for_operand(op: &mut Operand, f: &mut impl FnMut(&mut Place)) {
+            match op {
+                Operand::Copy(p) | Operand::Move(p) => {
+                    f(p);
+                }
+                Operand::Constant(_) => (),
+            }
+        }
+        for (_, block) in self.basic_blocks.iter_mut() {
+            for statement in &mut block.statements {
+                match &mut statement.kind {
+                    StatementKind::Assign(p, r) => {
+                        f(p);
+                        match r {
+                            Rvalue::ShallowInitBox(o, _)
+                            | Rvalue::UnaryOp(_, o)
+                            | Rvalue::Cast(_, o, _)
+                            | Rvalue::Use(o) => for_operand(o, &mut f),
+                            Rvalue::CopyForDeref(p)
+                            | Rvalue::Discriminant(p)
+                            | Rvalue::Len(p)
+                            | Rvalue::Ref(_, p) => f(p),
+                            Rvalue::CheckedBinaryOp(_, o1, o2) => {
+                                for_operand(o1, &mut f);
+                                for_operand(o2, &mut f);
+                            }
+                            Rvalue::Aggregate(_, ops) => {
+                                for op in ops {
+                                    for_operand(op, &mut f);
+                                }
+                            }
+                        }
+                    }
+                    StatementKind::Deinit(p) => f(p),
+                    StatementKind::StorageLive(_)
+                    | StatementKind::StorageDead(_)
+                    | StatementKind::Nop => (),
+                }
+            }
+            match &mut block.terminator {
+                Some(x) => match x {
+                    Terminator::SwitchInt { discr, .. } => for_operand(discr, &mut f),
+                    Terminator::FalseEdge { .. }
+                    | Terminator::FalseUnwind { .. }
+                    | Terminator::Goto { .. }
+                    | Terminator::Resume
+                    | Terminator::GeneratorDrop
+                    | Terminator::Abort
+                    | Terminator::Return
+                    | Terminator::Unreachable => (),
+                    Terminator::Drop { place, .. } => {
+                        f(place);
+                    }
+                    Terminator::DropAndReplace { place, value, .. } => {
+                        f(place);
+                        for_operand(value, &mut f);
+                    }
+                    Terminator::Call { func, args, destination, .. } => {
+                        for_operand(func, &mut f);
+                        args.iter_mut().for_each(|x| for_operand(x, &mut f));
+                        f(destination);
+                    }
+                    Terminator::Assert { cond, .. } => {
+                        for_operand(cond, &mut f);
+                    }
+                    Terminator::Yield { value, resume_arg, .. } => {
+                        for_operand(value, &mut f);
+                        f(resume_arg);
+                    }
+                },
+                None => (),
+            }
+        }
+    }
 }
 
 fn const_as_usize(c: &Const) -> usize {
diff --git a/crates/hir-ty/src/mir/borrowck.rs b/crates/hir-ty/src/mir/borrowck.rs
index c8729af86a9..5b2bca955f5 100644
--- a/crates/hir-ty/src/mir/borrowck.rs
+++ b/crates/hir-ty/src/mir/borrowck.rs
@@ -3,13 +3,13 @@
 // Currently it is an ad-hoc implementation, only useful for mutability analysis. Feel free to remove all of these
 // if needed for implementing a proper borrow checker.
 
-use std::sync::Arc;
+use std::{iter, sync::Arc};
 
 use hir_def::DefWithBodyId;
 use la_arena::ArenaMap;
 use stdx::never;
 
-use crate::db::HirDatabase;
+use crate::{db::HirDatabase, ClosureId};
 
 use super::{
     BasicBlockId, BorrowKind, LocalId, MirBody, MirLowerError, MirSpan, Place, ProjectionElem,
@@ -29,14 +29,48 @@ pub struct BorrowckResult {
     pub mutability_of_locals: ArenaMap<LocalId, MutabilityReason>,
 }
 
+fn all_mir_bodies(
+    db: &dyn HirDatabase,
+    def: DefWithBodyId,
+) -> Box<dyn Iterator<Item = Result<Arc<MirBody>, MirLowerError>> + '_> {
+    fn for_closure(
+        db: &dyn HirDatabase,
+        c: ClosureId,
+    ) -> Box<dyn Iterator<Item = Result<Arc<MirBody>, MirLowerError>> + '_> {
+        match db.mir_body_for_closure(c) {
+            Ok(body) => {
+                let closures = body.closures.clone();
+                Box::new(
+                    iter::once(Ok(body))
+                        .chain(closures.into_iter().flat_map(|x| for_closure(db, x))),
+                )
+            }
+            Err(e) => Box::new(iter::once(Err(e))),
+        }
+    }
+    match db.mir_body(def) {
+        Ok(body) => {
+            let closures = body.closures.clone();
+            Box::new(
+                iter::once(Ok(body)).chain(closures.into_iter().flat_map(|x| for_closure(db, x))),
+            )
+        }
+        Err(e) => Box::new(iter::once(Err(e))),
+    }
+}
+
 pub fn borrowck_query(
     db: &dyn HirDatabase,
     def: DefWithBodyId,
-) -> Result<Arc<BorrowckResult>, MirLowerError> {
+) -> Result<Arc<[BorrowckResult]>, MirLowerError> {
     let _p = profile::span("borrowck_query");
-    let body = db.mir_body(def)?;
-    let r = BorrowckResult { mutability_of_locals: mutability_of_locals(&body), mir_body: body };
-    Ok(Arc::new(r))
+    let r = all_mir_bodies(db, def)
+        .map(|body| {
+            let body = body?;
+            Ok(BorrowckResult { mutability_of_locals: mutability_of_locals(&body), mir_body: body })
+        })
+        .collect::<Result<Vec<_>, MirLowerError>>()?;
+    Ok(r.into())
 }
 
 fn is_place_direct(lvalue: &Place) -> bool {
@@ -60,7 +94,7 @@ fn place_case(lvalue: &Place) -> ProjectionCase {
             ProjectionElem::ConstantIndex { .. }
             | ProjectionElem::Subslice { .. }
             | ProjectionElem::Field(_)
-            | ProjectionElem::TupleField(_)
+            | ProjectionElem::TupleOrClosureField(_)
             | ProjectionElem::Index(_) => {
                 is_part_of = true;
             }
diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs
index 84b59b5eeb9..8c911b7f7b7 100644
--- a/crates/hir-ty/src/mir/eval.rs
+++ b/crates/hir-ty/src/mir/eval.rs
@@ -25,8 +25,8 @@ use crate::{
     mapping::from_chalk,
     method_resolution::{is_dyn_method, lookup_impl_method},
     traits::FnTrait,
-    CallableDefId, Const, ConstScalar, FnDefId, GenericArgData, Interner, MemoryMap, Substitution,
-    TraitEnvironment, Ty, TyBuilder, TyExt,
+    CallableDefId, ClosureId, Const, ConstScalar, FnDefId, GenericArgData, Interner, MemoryMap,
+    Substitution, TraitEnvironment, Ty, TyBuilder, TyExt,
 };
 
 use super::{
@@ -92,6 +92,7 @@ pub struct Evaluator<'a> {
 enum Address {
     Stack(usize),
     Heap(usize),
+    Invalid(usize),
 }
 
 use Address::*;
@@ -169,8 +170,10 @@ impl Address {
     fn from_usize(x: usize) -> Self {
         if x > usize::MAX / 2 {
             Stack(x - usize::MAX / 2)
+        } else if x > usize::MAX / 4 {
+            Heap(x - usize::MAX / 4)
         } else {
-            Heap(x)
+            Invalid(x)
         }
     }
 
@@ -181,7 +184,8 @@ impl Address {
     fn to_usize(&self) -> usize {
         let as_num = match self {
             Stack(x) => *x + usize::MAX / 2,
-            Heap(x) => *x,
+            Heap(x) => *x + usize::MAX / 4,
+            Invalid(x) => *x,
         };
         as_num
     }
@@ -190,6 +194,7 @@ impl Address {
         match self {
             Stack(x) => Stack(f(*x)),
             Heap(x) => Heap(f(*x)),
+            Invalid(x) => Invalid(f(*x)),
         }
     }
 
@@ -209,6 +214,7 @@ pub enum MirEvalError {
     UndefinedBehavior(&'static str),
     Panic(String),
     MirLowerError(FunctionId, MirLowerError),
+    MirLowerErrorForClosure(ClosureId, MirLowerError),
     TypeIsUnsized(Ty, &'static str),
     NotSupported(String),
     InvalidConst(Const),
@@ -238,6 +244,9 @@ impl std::fmt::Debug for MirEvalError {
             Self::MirLowerError(arg0, arg1) => {
                 f.debug_tuple("MirLowerError").field(arg0).field(arg1).finish()
             }
+            Self::MirLowerErrorForClosure(arg0, arg1) => {
+                f.debug_tuple("MirLowerError").field(arg0).field(arg1).finish()
+            }
             Self::InvalidVTableId(arg0) => f.debug_tuple("InvalidVTableId").field(arg0).finish(),
             Self::NotSupported(arg0) => f.debug_tuple("NotSupported").field(arg0).finish(),
             Self::InvalidConst(arg0) => {
@@ -355,16 +364,15 @@ impl Evaluator<'_> {
             self.ty_filler(&locals.body.locals[p.local].ty, locals.subst, locals.body.owner)?;
         let mut metadata = None; // locals are always sized
         for proj in &p.projection {
+            let prev_ty = ty.clone();
+            ty = proj.projected_ty(ty, self.db, |c, f| {
+                let (def, _) = self.db.lookup_intern_closure(c.into());
+                let infer = self.db.infer(def);
+                let (captures, _) = infer.closure_info(&c);
+                captures.get(f).expect("broken closure field").ty.clone()
+            });
             match proj {
                 ProjectionElem::Deref => {
-                    ty = match &ty.data(Interner).kind {
-                        TyKind::Raw(_, inner) | TyKind::Ref(_, _, inner) => inner.clone(),
-                        _ => {
-                            return Err(MirEvalError::TypeError(
-                                "Overloaded deref in MIR is disallowed",
-                            ))
-                        }
-                    };
                     metadata = if self.size_of(&ty, locals)?.is_none() {
                         Some(Interval { addr: addr.offset(self.ptr_size()), size: self.ptr_size() })
                     } else {
@@ -377,78 +385,41 @@ impl Evaluator<'_> {
                     let offset =
                         from_bytes!(usize, self.read_memory(locals.ptr[*op], self.ptr_size())?);
                     metadata = None; // Result of index is always sized
-                    match &ty.data(Interner).kind {
-                        TyKind::Ref(_, _, inner) => match &inner.data(Interner).kind {
-                            TyKind::Slice(inner) => {
-                                ty = inner.clone();
-                                let ty_size = self.size_of_sized(
-                                    &ty,
-                                    locals,
-                                    "slice inner type should be sized",
-                                )?;
-                                let value = self.read_memory(addr, self.ptr_size() * 2)?;
-                                addr = Address::from_bytes(&value[0..8])?.offset(ty_size * offset);
-                            }
-                            x => not_supported!("MIR index for ref type {x:?}"),
-                        },
-                        TyKind::Array(inner, _) | TyKind::Slice(inner) => {
-                            ty = inner.clone();
-                            let ty_size = self.size_of_sized(
-                                &ty,
-                                locals,
-                                "array inner type should be sized",
-                            )?;
-                            addr = addr.offset(ty_size * offset);
+                    let ty_size =
+                        self.size_of_sized(&ty, locals, "array inner type should be sized")?;
+                    addr = addr.offset(ty_size * offset);
+                }
+                &ProjectionElem::TupleOrClosureField(f) => {
+                    let layout = self.layout(&prev_ty)?;
+                    let offset = layout.fields.offset(f).bytes_usize();
+                    addr = addr.offset(offset);
+                    metadata = None; // tuple field is always sized
+                }
+                ProjectionElem::Field(f) => {
+                    let layout = self.layout(&prev_ty)?;
+                    let variant_layout = match &layout.variants {
+                        Variants::Single { .. } => &layout,
+                        Variants::Multiple { variants, .. } => {
+                            &variants[match f.parent {
+                                hir_def::VariantId::EnumVariantId(x) => {
+                                    RustcEnumVariantIdx(x.local_id)
+                                }
+                                _ => {
+                                    return Err(MirEvalError::TypeError(
+                                        "Multivariant layout only happens for enums",
+                                    ))
+                                }
+                            }]
                         }
-                        x => not_supported!("MIR index for type {x:?}"),
-                    }
+                    };
+                    let offset = variant_layout
+                        .fields
+                        .offset(u32::from(f.local_id.into_raw()) as usize)
+                        .bytes_usize();
+                    addr = addr.offset(offset);
+                    // FIXME: support structs with unsized fields
+                    metadata = None;
                 }
-                &ProjectionElem::TupleField(f) => match &ty.data(Interner).kind {
-                    TyKind::Tuple(_, subst) => {
-                        let layout = self.layout(&ty)?;
-                        ty = subst
-                            .as_slice(Interner)
-                            .get(f)
-                            .ok_or(MirEvalError::TypeError("not enough tuple fields"))?
-                            .assert_ty_ref(Interner)
-                            .clone();
-                        let offset = layout.fields.offset(f).bytes_usize();
-                        addr = addr.offset(offset);
-                        metadata = None; // tuple field is always sized
-                    }
-                    _ => return Err(MirEvalError::TypeError("Only tuple has tuple fields")),
-                },
-                ProjectionElem::Field(f) => match &ty.data(Interner).kind {
-                    TyKind::Adt(adt, subst) => {
-                        let layout = self.layout_adt(adt.0, subst.clone())?;
-                        let variant_layout = match &layout.variants {
-                            Variants::Single { .. } => &layout,
-                            Variants::Multiple { variants, .. } => {
-                                &variants[match f.parent {
-                                    hir_def::VariantId::EnumVariantId(x) => {
-                                        RustcEnumVariantIdx(x.local_id)
-                                    }
-                                    _ => {
-                                        return Err(MirEvalError::TypeError(
-                                            "Multivariant layout only happens for enums",
-                                        ))
-                                    }
-                                }]
-                            }
-                        };
-                        ty = self.db.field_types(f.parent)[f.local_id]
-                            .clone()
-                            .substitute(Interner, subst);
-                        let offset = variant_layout
-                            .fields
-                            .offset(u32::from(f.local_id.into_raw()) as usize)
-                            .bytes_usize();
-                        addr = addr.offset(offset);
-                        // FIXME: support structs with unsized fields
-                        metadata = None;
-                    }
-                    _ => return Err(MirEvalError::TypeError("Only adt has fields")),
-                },
                 ProjectionElem::ConstantIndex { .. } => {
                     not_supported!("constant index")
                 }
@@ -845,6 +816,15 @@ impl Evaluator<'_> {
                             values.iter().copied(),
                         )?)
                     }
+                    AggregateKind::Closure(ty) => {
+                        let layout = self.layout(&ty)?;
+                        Owned(self.make_by_layout(
+                            layout.size.bytes_usize(),
+                            &layout,
+                            None,
+                            values.iter().copied(),
+                        )?)
+                    }
                 }
             }
             Rvalue::Cast(kind, operand, target_ty) => match kind {
@@ -1065,6 +1045,9 @@ impl Evaluator<'_> {
         let (mem, pos) = match addr {
             Stack(x) => (&self.stack, x),
             Heap(x) => (&self.heap, x),
+            Invalid(_) => {
+                return Err(MirEvalError::UndefinedBehavior("read invalid memory address"))
+            }
         };
         mem.get(pos..pos + size).ok_or(MirEvalError::UndefinedBehavior("out of bound memory read"))
     }
@@ -1073,6 +1056,9 @@ impl Evaluator<'_> {
         let (mem, pos) = match addr {
             Stack(x) => (&mut self.stack, x),
             Heap(x) => (&mut self.heap, x),
+            Invalid(_) => {
+                return Err(MirEvalError::UndefinedBehavior("write invalid memory address"))
+            }
         };
         mem.get_mut(pos..pos + r.len())
             .ok_or(MirEvalError::UndefinedBehavior("out of bound memory write"))?
@@ -1394,6 +1380,25 @@ impl Evaluator<'_> {
         Ok(())
     }
 
+    fn exec_closure(
+        &mut self,
+        closure: ClosureId,
+        closure_data: Interval,
+        generic_args: &Substitution,
+        destination: Interval,
+        args: &[IntervalAndTy],
+    ) -> Result<()> {
+        let mir_body = self
+            .db
+            .mir_body_for_closure(closure)
+            .map_err(|x| MirEvalError::MirLowerErrorForClosure(closure, x))?;
+        let arg_bytes = iter::once(Ok(closure_data.get(self)?.to_owned()))
+            .chain(args.iter().map(|x| Ok(x.get(&self)?.to_owned())))
+            .collect::<Result<Vec<_>>>()?;
+        let bytes = self.interpret_mir(&mir_body, arg_bytes.into_iter(), generic_args.clone())?;
+        destination.write_from_bytes(self, &bytes)
+    }
+
     fn exec_fn_def(
         &mut self,
         def: FnDefId,
@@ -1546,6 +1551,9 @@ impl Evaluator<'_> {
             TyKind::Function(_) => {
                 self.exec_fn_pointer(func_data, destination, &args[1..], locals)?;
             }
+            TyKind::Closure(closure, subst) => {
+                self.exec_closure(*closure, func_data, subst, destination, &args[1..])?;
+            }
             x => not_supported!("Call FnTrait methods with type {x:?}"),
         }
         Ok(())
diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs
index 1821796be33..78a2d90f7fb 100644
--- a/crates/hir-ty/src/mir/lower.rs
+++ b/crates/hir-ty/src/mir/lower.rs
@@ -4,9 +4,9 @@ use std::{iter, mem, sync::Arc};
 
 use chalk_ir::{BoundVar, ConstData, DebruijnIndex, TyKind};
 use hir_def::{
-    adt::{StructKind, VariantData},
     body::Body,
-    expr::{
+    data::adt::{StructKind, VariantData},
+    hir::{
         Array, BindingAnnotation, BindingId, ExprId, LabelId, Literal, MatchArm, Pat, PatId,
         RecordFieldPat, RecordLitField,
     },
@@ -21,9 +21,16 @@ use la_arena::ArenaMap;
 use rustc_hash::FxHashMap;
 
 use crate::{
-    consteval::ConstEvalError, db::HirDatabase, display::HirDisplay, infer::TypeMismatch,
-    inhabitedness::is_ty_uninhabited_from, layout::layout_of_ty, mapping::ToChalk, static_lifetime,
-    utils::generics, Adjust, Adjustment, AutoBorrow, CallableDefId, TyBuilder, TyExt,
+    consteval::ConstEvalError,
+    db::HirDatabase,
+    display::HirDisplay,
+    infer::{CaptureKind, CapturedItem, TypeMismatch},
+    inhabitedness::is_ty_uninhabited_from,
+    layout::layout_of_ty,
+    mapping::ToChalk,
+    static_lifetime,
+    utils::generics,
+    Adjust, Adjustment, AutoBorrow, CallableDefId, TyBuilder, TyExt,
 };
 
 use super::*;
@@ -47,7 +54,7 @@ struct MirLowerCtx<'a> {
     current_loop_blocks: Option<LoopBlocks>,
     // FIXME: we should resolve labels in HIR lowering and always work with label id here, not
     // with raw names.
-    labeled_loop_blocks: FxHashMap<Name, LoopBlocks>,
+    labeled_loop_blocks: FxHashMap<LabelId, LoopBlocks>,
     discr_temp: Option<Place>,
     db: &'a dyn HirDatabase,
     body: &'a Body,
@@ -74,10 +81,12 @@ pub enum MirLowerError {
     BreakWithoutLoop,
     Loop,
     /// Something that should never happen and is definitely a bug, but we don't want to panic if it happened
-    ImplementationError(&'static str),
+    ImplementationError(String),
     LangItemNotFound(LangItem),
     MutatingRvalue,
     UnresolvedLabel,
+    UnresolvedUpvar(Place),
+    UnaccessableLocal,
 }
 
 macro_rules! not_supported {
@@ -88,8 +97,8 @@ macro_rules! not_supported {
 
 macro_rules! implementation_error {
     ($x: expr) => {{
-        ::stdx::never!("MIR lower implementation bug: {}", $x);
-        return Err(MirLowerError::ImplementationError($x));
+        ::stdx::never!("MIR lower implementation bug: {}", format!($x));
+        return Err(MirLowerError::ImplementationError(format!($x)));
     }};
 }
 
@@ -116,7 +125,44 @@ impl MirLowerError {
 
 type Result<T> = std::result::Result<T, MirLowerError>;
 
-impl MirLowerCtx<'_> {
+impl<'ctx> MirLowerCtx<'ctx> {
+    fn new(
+        db: &'ctx dyn HirDatabase,
+        owner: DefWithBodyId,
+        body: &'ctx Body,
+        infer: &'ctx InferenceResult,
+    ) -> Self {
+        let mut basic_blocks = Arena::new();
+        let start_block = basic_blocks.alloc(BasicBlock {
+            statements: vec![],
+            terminator: None,
+            is_cleanup: false,
+        });
+        let locals = Arena::new();
+        let binding_locals: ArenaMap<BindingId, LocalId> = ArenaMap::new();
+        let mir = MirBody {
+            basic_blocks,
+            locals,
+            start_block,
+            binding_locals,
+            param_locals: vec![],
+            owner,
+            arg_count: body.params.len(),
+            closures: vec![],
+        };
+        let ctx = MirLowerCtx {
+            result: mir,
+            db,
+            infer,
+            body,
+            owner,
+            current_loop_blocks: None,
+            labeled_loop_blocks: Default::default(),
+            discr_temp: None,
+        };
+        ctx
+    }
+
     fn temp(&mut self, ty: Ty) -> Result<LocalId> {
         if matches!(ty.kind(Interner), TyKind::Slice(_) | TyKind::Dyn(_)) {
             implementation_error!("unsized temporaries");
@@ -268,7 +314,7 @@ impl MirLowerCtx<'_> {
                         self.push_assignment(
                             current,
                             place,
-                            Operand::Copy(self.result.binding_locals[pat_id].into()).into(),
+                            Operand::Copy(self.binding_local(pat_id)?.into()).into(),
                             expr_id.into(),
                         );
                         Ok(Some(current))
@@ -579,19 +625,19 @@ impl MirLowerCtx<'_> {
                     Ok(None)
                 }
             },
-            Expr::Break { expr, label } => {
+            &Expr::Break { expr, label } => {
                 if let Some(expr) = expr {
                     let loop_data = match label {
-                        Some(l) => self.labeled_loop_blocks.get(l).ok_or(MirLowerError::UnresolvedLabel)?,
+                        Some(l) => self.labeled_loop_blocks.get(&l).ok_or(MirLowerError::UnresolvedLabel)?,
                         None => self.current_loop_blocks.as_ref().ok_or(MirLowerError::BreakWithoutLoop)?,
                     };
-                    let Some(c) = self.lower_expr_to_place(*expr, loop_data.place.clone(), current)? else {
+                    let Some(c) = self.lower_expr_to_place(expr, loop_data.place.clone(), current)? else {
                         return Ok(None);
                     };
                     current = c;
                 }
                 let end = match label {
-                    Some(l) => self.labeled_loop_blocks.get(l).ok_or(MirLowerError::UnresolvedLabel)?.end.expect("We always generate end for labeled loops"),
+                    Some(l) => self.labeled_loop_blocks.get(&l).ok_or(MirLowerError::UnresolvedLabel)?.end.expect("We always generate end for labeled loops"),
                     None => self.current_loop_end()?,
                 };
                 self.set_goto(current, end);
@@ -713,20 +759,20 @@ impl MirLowerCtx<'_> {
                 Ok(Some(current))
             }
             Expr::Box { .. } => not_supported!("box expression"),
-            Expr::Field { .. } | Expr::Index { .. } | Expr::UnaryOp { op: hir_def::expr::UnaryOp::Deref, .. } => {
+            Expr::Field { .. } | Expr::Index { .. } | Expr::UnaryOp { op: hir_def::hir::UnaryOp::Deref, .. } => {
                 let Some((p, current)) = self.lower_expr_as_place_without_adjust(current, expr_id, true)? else {
                     return Ok(None);
                 };
                 self.push_assignment(current, place, Operand::Copy(p).into(), expr_id.into());
                 Ok(Some(current))
             }
-            Expr::UnaryOp { expr, op: op @ (hir_def::expr::UnaryOp::Not | hir_def::expr::UnaryOp::Neg) } => {
+            Expr::UnaryOp { expr, op: op @ (hir_def::hir::UnaryOp::Not | hir_def::hir::UnaryOp::Neg) } => {
                 let Some((operand, current)) = self.lower_expr_to_some_operand(*expr, current)? else {
                     return Ok(None);
                 };
                 let operation = match op {
-                    hir_def::expr::UnaryOp::Not => UnOp::Not,
-                    hir_def::expr::UnaryOp::Neg => UnOp::Neg,
+                    hir_def::hir::UnaryOp::Not => UnOp::Not,
+                    hir_def::hir::UnaryOp::Neg => UnOp::Neg,
                     _ => unreachable!(),
                 };
                 self.push_assignment(
@@ -739,7 +785,7 @@ impl MirLowerCtx<'_> {
             },
             Expr::BinaryOp { lhs, rhs, op } => {
                 let op = op.ok_or(MirLowerError::IncompleteExpr)?;
-                if let hir_def::expr::BinaryOp::Assignment { op } = op {
+                if let hir_def::hir::BinaryOp::Assignment { op } = op {
                     if op.is_some() {
                         not_supported!("assignment with arith op (like +=)");
                     }
@@ -765,13 +811,13 @@ impl MirLowerCtx<'_> {
                     place,
                     Rvalue::CheckedBinaryOp(
                         match op {
-                            hir_def::expr::BinaryOp::LogicOp(op) => match op {
-                                hir_def::expr::LogicOp::And => BinOp::BitAnd, // FIXME: make these short circuit
-                                hir_def::expr::LogicOp::Or => BinOp::BitOr,
+                            hir_def::hir::BinaryOp::LogicOp(op) => match op {
+                                hir_def::hir::LogicOp::And => BinOp::BitAnd, // FIXME: make these short circuit
+                                hir_def::hir::LogicOp::Or => BinOp::BitOr,
                             },
-                            hir_def::expr::BinaryOp::ArithOp(op) => BinOp::from(op),
-                            hir_def::expr::BinaryOp::CmpOp(op) => BinOp::from(op),
-                            hir_def::expr::BinaryOp::Assignment { .. } => unreachable!(), // handled above
+                            hir_def::hir::BinaryOp::ArithOp(op) => BinOp::from(op),
+                            hir_def::hir::BinaryOp::CmpOp(op) => BinOp::from(op),
+                            hir_def::hir::BinaryOp::Assignment { .. } => unreachable!(), // handled above
                         },
                         lhs_op,
                         rhs_op,
@@ -823,7 +869,51 @@ impl MirLowerCtx<'_> {
                 );
                 Ok(Some(current))
             },
-            Expr::Closure { .. } => not_supported!("closure"),
+            Expr::Closure { .. } => {
+                let ty = self.expr_ty(expr_id);
+                let TyKind::Closure(id, _) = ty.kind(Interner) else {
+                    not_supported!("closure with non closure type");
+                };
+                self.result.closures.push(*id);
+                let (captures, _) = self.infer.closure_info(id);
+                let mut operands = vec![];
+                for capture in captures.iter() {
+                    let p = Place {
+                        local: self.binding_local(capture.place.local)?,
+                        projection: capture.place.projections.clone().into_iter().map(|x| {
+                            match x {
+                                ProjectionElem::Deref => ProjectionElem::Deref,
+                                ProjectionElem::Field(x) => ProjectionElem::Field(x),
+                                ProjectionElem::TupleOrClosureField(x) => ProjectionElem::TupleOrClosureField(x),
+                                ProjectionElem::ConstantIndex { offset, min_length, from_end } => ProjectionElem::ConstantIndex { offset, min_length, from_end },
+                                ProjectionElem::Subslice { from, to, from_end } => ProjectionElem::Subslice { from, to, from_end },
+                                ProjectionElem::OpaqueCast(x) => ProjectionElem::OpaqueCast(x),
+                                ProjectionElem::Index(x) => match x { },
+                            }
+                        }).collect(),
+                    };
+                    match &capture.kind {
+                        CaptureKind::ByRef(bk) => {
+                            let tmp: Place = self.temp(capture.ty.clone())?.into();
+                            self.push_assignment(
+                                current,
+                                tmp.clone(),
+                                Rvalue::Ref(bk.clone(), p),
+                                expr_id.into(),
+                            );
+                            operands.push(Operand::Move(tmp));
+                        },
+                        CaptureKind::ByValue => operands.push(Operand::Move(p)),
+                    }
+                }
+                self.push_assignment(
+                    current,
+                    place,
+                    Rvalue::Aggregate(AggregateKind::Closure(ty), operands),
+                    expr_id.into(),
+                );
+                Ok(Some(current))
+            },
             Expr::Tuple { exprs, is_assignee_expr: _ } => {
                 let Some(values) = exprs
                         .iter()
@@ -893,7 +983,7 @@ impl MirLowerCtx<'_> {
                 let index = name
                     .as_tuple_index()
                     .ok_or(MirLowerError::TypeError("named field on tuple"))?;
-                place.projection.push(ProjectionElem::TupleField(index))
+                place.projection.push(ProjectionElem::TupleOrClosureField(index))
             } else {
                 let field =
                     self.infer.field_resolution(expr_id).ok_or(MirLowerError::UnresolvedField)?;
@@ -910,7 +1000,7 @@ impl MirLowerCtx<'_> {
             .size
             .bytes_usize();
         let bytes = match l {
-            hir_def::expr::Literal::String(b) => {
+            hir_def::hir::Literal::String(b) => {
                 let b = b.as_bytes();
                 let mut data = vec![];
                 data.extend(0usize.to_le_bytes());
@@ -919,7 +1009,7 @@ impl MirLowerCtx<'_> {
                 mm.insert(0, b.to_vec());
                 return Ok(Operand::from_concrete_const(data, mm, ty));
             }
-            hir_def::expr::Literal::ByteString(b) => {
+            hir_def::hir::Literal::ByteString(b) => {
                 let mut data = vec![];
                 data.extend(0usize.to_le_bytes());
                 data.extend(b.len().to_le_bytes());
@@ -927,11 +1017,11 @@ impl MirLowerCtx<'_> {
                 mm.insert(0, b.to_vec());
                 return Ok(Operand::from_concrete_const(data, mm, ty));
             }
-            hir_def::expr::Literal::Char(c) => u32::from(*c).to_le_bytes().into(),
-            hir_def::expr::Literal::Bool(b) => vec![*b as u8],
-            hir_def::expr::Literal::Int(x, _) => x.to_le_bytes()[0..size].into(),
-            hir_def::expr::Literal::Uint(x, _) => x.to_le_bytes()[0..size].into(),
-            hir_def::expr::Literal::Float(f, _) => match size {
+            hir_def::hir::Literal::Char(c) => u32::from(*c).to_le_bytes().into(),
+            hir_def::hir::Literal::Bool(b) => vec![*b as u8],
+            hir_def::hir::Literal::Int(x, _) => x.to_le_bytes()[0..size].into(),
+            hir_def::hir::Literal::Uint(x, _) => x.to_le_bytes()[0..size].into(),
+            hir_def::hir::Literal::Float(f, _) => match size {
                 8 => f.into_f64().to_le_bytes().into(),
                 4 => f.into_f32().to_le_bytes().into(),
                 _ => {
@@ -1119,19 +1209,18 @@ impl MirLowerCtx<'_> {
             // bad as we may emit end (unneccessary unreachable block) for unterminating loop, but
             // it should not affect correctness.
             self.current_loop_end()?;
-            self.labeled_loop_blocks.insert(
-                self.body.labels[label].name.clone(),
-                self.current_loop_blocks.as_ref().unwrap().clone(),
-            )
+            self.labeled_loop_blocks
+                .insert(label, self.current_loop_blocks.as_ref().unwrap().clone())
         } else {
             None
         };
         self.set_goto(prev_block, begin);
         f(self, begin)?;
-        let my = mem::replace(&mut self.current_loop_blocks, prev)
-            .ok_or(MirLowerError::ImplementationError("current_loop_blocks is corrupt"))?;
+        let my = mem::replace(&mut self.current_loop_blocks, prev).ok_or(
+            MirLowerError::ImplementationError("current_loop_blocks is corrupt".to_string()),
+        )?;
         if let Some(prev) = prev_label {
-            self.labeled_loop_blocks.insert(self.body.labels[label.unwrap()].name.clone(), prev);
+            self.labeled_loop_blocks.insert(label.unwrap(), prev);
         }
         Ok(my.end)
     }
@@ -1161,7 +1250,9 @@ impl MirLowerCtx<'_> {
         let r = match self
             .current_loop_blocks
             .as_mut()
-            .ok_or(MirLowerError::ImplementationError("Current loop access out of loop"))?
+            .ok_or(MirLowerError::ImplementationError(
+                "Current loop access out of loop".to_string(),
+            ))?
             .end
         {
             Some(x) => x,
@@ -1169,7 +1260,9 @@ impl MirLowerCtx<'_> {
                 let s = self.new_basic_block();
                 self.current_loop_blocks
                     .as_mut()
-                    .ok_or(MirLowerError::ImplementationError("Current loop access out of loop"))?
+                    .ok_or(MirLowerError::ImplementationError(
+                        "Current loop access out of loop".to_string(),
+                    ))?
                     .end = Some(s);
                 s
             }
@@ -1183,7 +1276,7 @@ impl MirLowerCtx<'_> {
 
     /// This function push `StorageLive` statement for the binding, and applies changes to add `StorageDead` in
     /// the appropriated places.
-    fn push_storage_live(&mut self, b: BindingId, current: BasicBlockId) {
+    fn push_storage_live(&mut self, b: BindingId, current: BasicBlockId) -> Result<()> {
         // Current implementation is wrong. It adds no `StorageDead` at the end of scope, and before each break
         // and continue. It just add a `StorageDead` before the `StorageLive`, which is not wrong, but unneeeded in
         // the proper implementation. Due this limitation, implementing a borrow checker on top of this mir will falsely
@@ -1208,9 +1301,10 @@ impl MirLowerCtx<'_> {
             .copied()
             .map(MirSpan::PatId)
             .unwrap_or(MirSpan::Unknown);
-        let l = self.result.binding_locals[b];
+        let l = self.binding_local(b)?;
         self.push_statement(current, StatementKind::StorageDead(l).with_span(span));
         self.push_statement(current, StatementKind::StorageLive(l).with_span(span));
+        Ok(())
     }
 
     fn resolve_lang_item(&self, item: LangItem) -> Result<LangItemTarget> {
@@ -1220,14 +1314,14 @@ impl MirLowerCtx<'_> {
 
     fn lower_block_to_place(
         &mut self,
-        statements: &[hir_def::expr::Statement],
+        statements: &[hir_def::hir::Statement],
         mut current: BasicBlockId,
         tail: Option<ExprId>,
         place: Place,
     ) -> Result<Option<Idx<BasicBlock>>> {
         for statement in statements.iter() {
             match statement {
-                hir_def::expr::Statement::Let { pat, initializer, else_branch, type_ref: _ } => {
+                hir_def::hir::Statement::Let { pat, initializer, else_branch, type_ref: _ } => {
                     if let Some(expr_id) = initializer {
                         let else_block;
                         let Some((init_place, c)) =
@@ -1258,12 +1352,18 @@ impl MirLowerCtx<'_> {
                             }
                         }
                     } else {
+                        let mut err = None;
                         self.body.walk_bindings_in_pat(*pat, |b| {
-                            self.push_storage_live(b, current);
+                            if let Err(e) = self.push_storage_live(b, current) {
+                                err = Some(e);
+                            }
                         });
+                        if let Some(e) = err {
+                            return Err(e);
+                        }
                     }
                 }
-                hir_def::expr::Statement::Expr { expr, has_semi: _ } => {
+                hir_def::hir::Statement::Expr { expr, has_semi: _ } => {
                     let Some((_, c)) = self.lower_expr_as_place(current, *expr, true)? else {
                         return Ok(None);
                     };
@@ -1276,6 +1376,67 @@ impl MirLowerCtx<'_> {
             None => Ok(Some(current)),
         }
     }
+
+    fn lower_params_and_bindings(
+        &mut self,
+        params: impl Iterator<Item = (PatId, Ty)> + Clone,
+        pick_binding: impl Fn(BindingId) -> bool,
+    ) -> Result<BasicBlockId> {
+        let base_param_count = self.result.param_locals.len();
+        self.result.param_locals.extend(params.clone().map(|(x, ty)| {
+            let local_id = self.result.locals.alloc(Local { ty });
+            if let Pat::Bind { id, subpat: None } = self.body[x] {
+                if matches!(
+                    self.body.bindings[id].mode,
+                    BindingAnnotation::Unannotated | BindingAnnotation::Mutable
+                ) {
+                    self.result.binding_locals.insert(id, local_id);
+                }
+            }
+            local_id
+        }));
+        // and then rest of bindings
+        for (id, _) in self.body.bindings.iter() {
+            if !pick_binding(id) {
+                continue;
+            }
+            if !self.result.binding_locals.contains_idx(id) {
+                self.result
+                    .binding_locals
+                    .insert(id, self.result.locals.alloc(Local { ty: self.infer[id].clone() }));
+            }
+        }
+        let mut current = self.result.start_block;
+        for ((param, _), local) in
+            params.zip(self.result.param_locals.clone().into_iter().skip(base_param_count))
+        {
+            if let Pat::Bind { id, .. } = self.body[param] {
+                if local == self.binding_local(id)? {
+                    continue;
+                }
+            }
+            let r = self.pattern_match(
+                current,
+                None,
+                local.into(),
+                self.result.locals[local].ty.clone(),
+                param,
+                BindingAnnotation::Unannotated,
+            )?;
+            if let Some(b) = r.1 {
+                self.set_terminator(b, Terminator::Unreachable);
+            }
+            current = r.0;
+        }
+        Ok(current)
+    }
+
+    fn binding_local(&self, b: BindingId) -> Result<LocalId> {
+        match self.result.binding_locals.get(b) {
+            Some(x) => Ok(*x),
+            None => Err(MirLowerError::UnaccessableLocal),
+        }
+    }
 }
 
 fn cast_kind(source_ty: &Ty, target_ty: &Ty) -> Result<CastKind> {
@@ -1299,6 +1460,87 @@ fn cast_kind(source_ty: &Ty, target_ty: &Ty) -> Result<CastKind> {
     })
 }
 
+pub fn mir_body_for_closure_query(
+    db: &dyn HirDatabase,
+    closure: ClosureId,
+) -> Result<Arc<MirBody>> {
+    let (owner, expr) = db.lookup_intern_closure(closure.into());
+    let body = db.body(owner);
+    let infer = db.infer(owner);
+    let Expr::Closure { args, body: root, .. } = &body[expr] else {
+        implementation_error!("closure expression is not closure");
+    };
+    let TyKind::Closure(_, substs) = &infer[expr].kind(Interner) else {
+        implementation_error!("closure expression is not closure");
+    };
+    let (captures, _) = infer.closure_info(&closure);
+    let mut ctx = MirLowerCtx::new(db, owner, &body, &infer);
+    ctx.result.arg_count = args.len() + 1;
+    // 0 is return local
+    ctx.result.locals.alloc(Local { ty: infer[*root].clone() });
+    ctx.result.locals.alloc(Local { ty: infer[expr].clone() });
+    let Some(sig) = substs.at(Interner, 0).assert_ty_ref(Interner).callable_sig(db) else {
+        implementation_error!("closure has not callable sig");
+    };
+    let current = ctx.lower_params_and_bindings(
+        args.iter().zip(sig.params().iter()).map(|(x, y)| (*x, y.clone())),
+        |_| true,
+    )?;
+    if let Some(b) = ctx.lower_expr_to_place(*root, return_slot().into(), current)? {
+        ctx.set_terminator(b, Terminator::Return);
+    }
+    let mut upvar_map: FxHashMap<LocalId, Vec<(&CapturedItem, usize)>> = FxHashMap::default();
+    for (i, capture) in captures.iter().enumerate() {
+        let local = ctx.binding_local(capture.place.local)?;
+        upvar_map.entry(local).or_default().push((capture, i));
+    }
+    let mut err = None;
+    let closure_local = ctx.result.locals.iter().nth(1).unwrap().0;
+    ctx.result.walk_places(|p| {
+        if let Some(x) = upvar_map.get(&p.local) {
+            let r = x.iter().find(|x| {
+                if p.projection.len() < x.0.place.projections.len() {
+                    return false;
+                }
+                for (x, y) in p.projection.iter().zip(x.0.place.projections.iter()) {
+                    match (x, y) {
+                        (ProjectionElem::Deref, ProjectionElem::Deref) => (),
+                        (ProjectionElem::Field(x), ProjectionElem::Field(y)) if x == y => (),
+                        (
+                            ProjectionElem::TupleOrClosureField(x),
+                            ProjectionElem::TupleOrClosureField(y),
+                        ) if x == y => (),
+                        _ => return false,
+                    }
+                }
+                true
+            });
+            match r {
+                Some(x) => {
+                    p.local = closure_local;
+                    let prev_projs =
+                        mem::replace(&mut p.projection, vec![PlaceElem::TupleOrClosureField(x.1)]);
+                    if x.0.kind != CaptureKind::ByValue {
+                        p.projection.push(ProjectionElem::Deref);
+                    }
+                    p.projection.extend(prev_projs.into_iter().skip(x.0.place.projections.len()));
+                }
+                None => err = Some(p.clone()),
+            }
+        }
+    });
+    ctx.result.binding_locals = ctx
+        .result
+        .binding_locals
+        .into_iter()
+        .filter(|x| ctx.body[x.0].owner == Some(expr))
+        .collect();
+    if let Some(err) = err {
+        return Err(MirLowerError::UnresolvedUpvar(err));
+    }
+    Ok(Arc::new(ctx.result))
+}
+
 pub fn mir_body_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Result<Arc<MirBody>> {
     let _p = profile::span("mir_body_query").detail(|| match def {
         DefWithBodyId::FunctionId(it) => db.function_data(it).name.to_string(),
@@ -1336,86 +1578,29 @@ pub fn lower_to_mir(
     if let Some((_, x)) = infer.type_mismatches().next() {
         return Err(MirLowerError::TypeMismatch(x.clone()));
     }
-    let mut basic_blocks = Arena::new();
-    let start_block =
-        basic_blocks.alloc(BasicBlock { statements: vec![], terminator: None, is_cleanup: false });
-    let mut locals = Arena::new();
+    let mut ctx = MirLowerCtx::new(db, owner, body, infer);
     // 0 is return local
-    locals.alloc(Local { ty: infer[root_expr].clone() });
-    let mut binding_locals: ArenaMap<BindingId, LocalId> = ArenaMap::new();
+    ctx.result.locals.alloc(Local { ty: infer[root_expr].clone() });
+    let binding_picker = |b: BindingId| {
+        if root_expr == body.body_expr {
+            body[b].owner.is_none()
+        } else {
+            body[b].owner == Some(root_expr)
+        }
+    };
     // 1 to param_len is for params
-    let param_locals: Vec<LocalId> = if let DefWithBodyId::FunctionId(fid) = owner {
+    let current = if let DefWithBodyId::FunctionId(fid) = owner {
         let substs = TyBuilder::placeholder_subst(db, fid);
         let callable_sig = db.callable_item_signature(fid.into()).substitute(Interner, &substs);
-        body.params
-            .iter()
-            .zip(callable_sig.params().iter())
-            .map(|(&x, ty)| {
-                let local_id = locals.alloc(Local { ty: ty.clone() });
-                if let Pat::Bind { id, subpat: None } = body[x] {
-                    if matches!(
-                        body.bindings[id].mode,
-                        BindingAnnotation::Unannotated | BindingAnnotation::Mutable
-                    ) {
-                        binding_locals.insert(id, local_id);
-                    }
-                }
-                local_id
-            })
-            .collect()
+        ctx.lower_params_and_bindings(
+            body.params.iter().zip(callable_sig.params().iter()).map(|(x, y)| (*x, y.clone())),
+            binding_picker,
+        )?
     } else {
-        if !body.params.is_empty() {
-            return Err(MirLowerError::TypeError("Unexpected parameter for non function body"));
-        }
-        vec![]
+        ctx.lower_params_and_bindings([].into_iter(), binding_picker)?
     };
-    // and then rest of bindings
-    for (id, _) in body.bindings.iter() {
-        if !binding_locals.contains_idx(id) {
-            binding_locals.insert(id, locals.alloc(Local { ty: infer[id].clone() }));
-        }
-    }
-    let mir = MirBody {
-        basic_blocks,
-        locals,
-        start_block,
-        binding_locals,
-        param_locals,
-        owner,
-        arg_count: body.params.len(),
-    };
-    let mut ctx = MirLowerCtx {
-        result: mir,
-        db,
-        infer,
-        body,
-        owner,
-        current_loop_blocks: None,
-        labeled_loop_blocks: Default::default(),
-        discr_temp: None,
-    };
-    let mut current = start_block;
-    for (&param, local) in body.params.iter().zip(ctx.result.param_locals.clone().into_iter()) {
-        if let Pat::Bind { id, .. } = body[param] {
-            if local == ctx.result.binding_locals[id] {
-                continue;
-            }
-        }
-        let r = ctx.pattern_match(
-            current,
-            None,
-            local.into(),
-            ctx.result.locals[local].ty.clone(),
-            param,
-            BindingAnnotation::Unannotated,
-        )?;
-        if let Some(b) = r.1 {
-            ctx.set_terminator(b, Terminator::Unreachable);
-        }
-        current = r.0;
-    }
     if let Some(b) = ctx.lower_expr_to_place(root_expr, return_slot().into(), current)? {
-        ctx.result.basic_blocks[b].terminator = Some(Terminator::Return);
+        ctx.set_terminator(b, Terminator::Return);
     }
     Ok(ctx.result)
 }
diff --git a/crates/hir-ty/src/mir/lower/as_place.rs b/crates/hir-ty/src/mir/lower/as_place.rs
index 91163e5bd61..a1574f55936 100644
--- a/crates/hir-ty/src/mir/lower/as_place.rs
+++ b/crates/hir-ty/src/mir/lower/as_place.rs
@@ -141,7 +141,7 @@ impl MirLowerCtx<'_> {
                 }
             }
             Expr::UnaryOp { expr, op } => match op {
-                hir_def::expr::UnaryOp::Deref => {
+                hir_def::hir::UnaryOp::Deref => {
                     if !matches!(
                         self.expr_ty(*expr).kind(Interner),
                         TyKind::Ref(..) | TyKind::Raw(..)
diff --git a/crates/hir-ty/src/mir/lower/pattern_matching.rs b/crates/hir-ty/src/mir/lower/pattern_matching.rs
index c3ced82aab7..12a77715e9c 100644
--- a/crates/hir-ty/src/mir/lower/pattern_matching.rs
+++ b/crates/hir-ty/src/mir/lower/pattern_matching.rs
@@ -1,5 +1,7 @@
 //! MIR lowering for patterns
 
+use crate::utils::pattern_matching_dereference_count;
+
 use super::*;
 
 macro_rules! not_supported {
@@ -52,7 +54,7 @@ impl MirLowerCtx<'_> {
                     args,
                     *ellipsis,
                     subst.iter(Interner).enumerate().map(|(i, x)| {
-                        (PlaceElem::TupleField(i), x.assert_ty_ref(Interner).clone())
+                        (PlaceElem::TupleOrClosureField(i), x.assert_ty_ref(Interner).clone())
                     }),
                     &cond_place,
                     binding_mode,
@@ -142,7 +144,7 @@ impl MirLowerCtx<'_> {
                 if matches!(mode, BindingAnnotation::Ref | BindingAnnotation::RefMut) {
                     binding_mode = mode;
                 }
-                self.push_storage_live(*id, current);
+                self.push_storage_live(*id, current)?;
                 self.push_assignment(
                     current,
                     target_place.into(),
@@ -387,13 +389,6 @@ fn pattern_matching_dereference(
     binding_mode: &mut BindingAnnotation,
     cond_place: &mut Place,
 ) {
-    while let Some((ty, _, mu)) = cond_ty.as_reference() {
-        if mu == Mutability::Mut && *binding_mode != BindingAnnotation::Ref {
-            *binding_mode = BindingAnnotation::RefMut;
-        } else {
-            *binding_mode = BindingAnnotation::Ref;
-        }
-        *cond_ty = ty.clone();
-        cond_place.projection.push(ProjectionElem::Deref);
-    }
+    let cnt = pattern_matching_dereference_count(cond_ty, binding_mode);
+    cond_place.projection.extend((0..cnt).map(|_| ProjectionElem::Deref));
 }
diff --git a/crates/hir-ty/src/mir/pretty.rs b/crates/hir-ty/src/mir/pretty.rs
index 9ec2913dce9..3e1f2ecef1b 100644
--- a/crates/hir-ty/src/mir/pretty.rs
+++ b/crates/hir-ty/src/mir/pretty.rs
@@ -1,8 +1,11 @@
 //! A pretty-printer for MIR.
 
-use std::fmt::{Debug, Display, Write};
+use std::{
+    fmt::{Debug, Display, Write},
+    mem,
+};
 
-use hir_def::{body::Body, expr::BindingId};
+use hir_def::{body::Body, hir::BindingId};
 use hir_expand::name::Name;
 use la_arena::ArenaMap;
 
@@ -20,7 +23,7 @@ impl MirBody {
     pub fn pretty_print(&self, db: &dyn HirDatabase) -> String {
         let hir_body = db.body(self.owner);
         let mut ctx = MirPrettyCtx::new(self, &hir_body, db);
-        ctx.for_body();
+        ctx.for_body(ctx.body.owner);
         ctx.result
     }
 
@@ -42,7 +45,7 @@ struct MirPrettyCtx<'a> {
     hir_body: &'a Body,
     db: &'a dyn HirDatabase,
     result: String,
-    ident: String,
+    indent: String,
     local_to_binding: ArenaMap<LocalId, BindingId>,
 }
 
@@ -88,22 +91,43 @@ impl Display for LocalName {
 }
 
 impl<'a> MirPrettyCtx<'a> {
-    fn for_body(&mut self) {
-        wln!(self, "// {:?}", self.body.owner);
+    fn for_body(&mut self, name: impl Debug) {
+        wln!(self, "// {:?}", name);
         self.with_block(|this| {
             this.locals();
             wln!(this);
             this.blocks();
         });
+        for &closure in &self.body.closures {
+            let body = match self.db.mir_body_for_closure(closure) {
+                Ok(x) => x,
+                Err(e) => {
+                    wln!(self, "// error in {closure:?}: {e:?}");
+                    continue;
+                }
+            };
+            let result = mem::take(&mut self.result);
+            let indent = mem::take(&mut self.indent);
+            let mut ctx = MirPrettyCtx {
+                body: &body,
+                local_to_binding: body.binding_locals.iter().map(|(x, y)| (*y, x)).collect(),
+                result,
+                indent,
+                ..*self
+            };
+            ctx.for_body(closure);
+            self.result = ctx.result;
+            self.indent = ctx.indent;
+        }
     }
 
     fn with_block(&mut self, f: impl FnOnce(&mut MirPrettyCtx<'_>)) {
-        self.ident += "    ";
+        self.indent += "    ";
         wln!(self, "{{");
         f(self);
         for _ in 0..4 {
             self.result.pop();
-            self.ident.pop();
+            self.indent.pop();
         }
         wln!(self, "}}");
     }
@@ -114,7 +138,7 @@ impl<'a> MirPrettyCtx<'a> {
             body,
             db,
             result: String::new(),
-            ident: String::new(),
+            indent: String::new(),
             local_to_binding,
             hir_body,
         }
@@ -122,7 +146,7 @@ impl<'a> MirPrettyCtx<'a> {
 
     fn write_line(&mut self) {
         self.result.push('\n');
-        self.result += &self.ident;
+        self.result += &self.indent;
     }
 
     fn write(&mut self, line: &str) {
@@ -247,7 +271,7 @@ impl<'a> MirPrettyCtx<'a> {
                         }
                     }
                 }
-                ProjectionElem::TupleField(x) => {
+                ProjectionElem::TupleOrClosureField(x) => {
                     f(this, local, head);
                     w!(this, ".{}", x);
                 }
@@ -302,6 +326,11 @@ impl<'a> MirPrettyCtx<'a> {
                 self.operand_list(x);
                 w!(self, ")");
             }
+            Rvalue::Aggregate(AggregateKind::Closure(_), x) => {
+                w!(self, "Closure(");
+                self.operand_list(x);
+                w!(self, ")");
+            }
             Rvalue::Aggregate(AggregateKind::Union(_, _), x) => {
                 w!(self, "Union(");
                 self.operand_list(x);
diff --git a/crates/hir-ty/src/tests.rs b/crates/hir-ty/src/tests.rs
index 83d31f002a1..1e46bb1d040 100644
--- a/crates/hir-ty/src/tests.rs
+++ b/crates/hir-ty/src/tests.rs
@@ -17,7 +17,7 @@ use expect_test::Expect;
 use hir_def::{
     body::{Body, BodySourceMap, SyntheticSyntax},
     db::{DefDatabase, InternDatabase},
-    expr::{ExprId, PatId},
+    hir::{ExprId, PatId},
     item_scope::ItemScope,
     nameres::DefMap,
     src::HasSource,
@@ -198,8 +198,8 @@ fn check_impl(ra_fixture: &str, allow_none: bool, only_types: bool, display_sour
 
         for (expr_or_pat, mismatch) in inference_result.type_mismatches() {
             let Some(node) = (match expr_or_pat {
-                hir_def::expr::ExprOrPatId::ExprId(expr) => expr_node(&body_source_map, expr, &db),
-                hir_def::expr::ExprOrPatId::PatId(pat) => pat_node(&body_source_map, pat, &db),
+                hir_def::hir::ExprOrPatId::ExprId(expr) => expr_node(&body_source_map, expr, &db),
+                hir_def::hir::ExprOrPatId::PatId(pat) => pat_node(&body_source_map, pat, &db),
             }) else { continue; };
             let range = node.as_ref().original_file_range(&db);
             let actual = format!(
diff --git a/crates/hir-ty/src/tests/coercion.rs b/crates/hir-ty/src/tests/coercion.rs
index e63a674a3fd..16e5ef85d09 100644
--- a/crates/hir-ty/src/tests/coercion.rs
+++ b/crates/hir-ty/src/tests/coercion.rs
@@ -575,7 +575,7 @@ fn two_closures_lub() {
 fn foo(c: i32) {
     let add = |a: i32, b: i32| a + b;
     let sub = |a, b| a - b;
-            //^^^^^^^^^^^^ |i32, i32| -> i32
+            //^^^^^^^^^^^^ impl Fn(i32, i32) -> i32
     if c > 42 { add } else { sub };
   //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ fn(i32, i32) -> i32
 }
@@ -875,6 +875,16 @@ fn test() {
 fn adjust_index() {
     check_no_mismatches(
         r"
+//- minicore: index, slice, coerce_unsized
+fn test() {
+    let x = [1, 2, 3];
+    x[2] = 6;
+ // ^ adjustments: Borrow(Ref(Mut))
+}
+    ",
+    );
+    check_no_mismatches(
+        r"
 //- minicore: index
 struct Struct;
 impl core::ops::Index<usize> for Struct {
@@ -902,3 +912,32 @@ fn test() {
 }",
     );
 }
+
+#[test]
+fn regression_14443_dyn_coercion_block_impls() {
+    check_no_mismatches(
+        r#"
+//- minicore: coerce_unsized
+trait T {}
+
+fn dyn_t(d: &dyn T) {}
+
+fn main() {
+    struct A;
+    impl T for A {}
+
+    let a = A;
+
+    let b = {
+        struct B;
+        impl T for B {}
+
+        B
+    };
+
+    dyn_t(&a);
+    dyn_t(&b);
+}
+"#,
+    )
+}
diff --git a/crates/hir-ty/src/tests/macros.rs b/crates/hir-ty/src/tests/macros.rs
index 8b75ec842a4..d45edf730ad 100644
--- a/crates/hir-ty/src/tests/macros.rs
+++ b/crates/hir-ty/src/tests/macros.rs
@@ -198,7 +198,7 @@ fn expr_macro_def_expanded_in_various_places() {
             100..119 'for _ ...!() {}': ()
             104..105 '_': {unknown}
             117..119 '{}': ()
-            124..134 '|| spam!()': || -> isize
+            124..134 '|| spam!()': impl Fn() -> isize
             140..156 'while ...!() {}': ()
             154..156 '{}': ()
             161..174 'break spam!()': !
@@ -279,7 +279,7 @@ fn expr_macro_rules_expanded_in_various_places() {
             114..133 'for _ ...!() {}': ()
             118..119 '_': {unknown}
             131..133 '{}': ()
-            138..148 '|| spam!()': || -> isize
+            138..148 '|| spam!()': impl Fn() -> isize
             154..170 'while ...!() {}': ()
             168..170 '{}': ()
             175..188 'break spam!()': !
diff --git a/crates/hir-ty/src/tests/method_resolution.rs b/crates/hir-ty/src/tests/method_resolution.rs
index f3ca93672d9..f62a3cfabe8 100644
--- a/crates/hir-ty/src/tests/method_resolution.rs
+++ b/crates/hir-ty/src/tests/method_resolution.rs
@@ -389,6 +389,24 @@ mod bar_test {
 }
 
 #[test]
+fn infer_trait_method_multiple_mutable_reference() {
+    check_types(
+        r#"
+trait Trait {
+    fn method(&mut self) -> i32 { 5 }
+}
+struct S;
+impl Trait for &mut &mut S {}
+fn test() {
+    let s = &mut &mut &mut S;
+    s.method();
+  //^^^^^^^^^^ i32
+}
+        "#,
+    );
+}
+
+#[test]
 fn infer_trait_method_generic_1() {
     // the trait implementation is intentionally incomplete -- it shouldn't matter
     check_types(
@@ -1722,7 +1740,7 @@ fn test() {
     Foo.foo();
   //^^^ adjustments: Borrow(Ref(Not))
     (&Foo).foo();
-  // ^^^^ adjustments: ,
+  // ^^^^ adjustments: Deref(None), Borrow(Ref(Not))
 }
 "#,
     );
diff --git a/crates/hir-ty/src/tests/patterns.rs b/crates/hir-ty/src/tests/patterns.rs
index 74bcab6caa9..c8c31bdea5c 100644
--- a/crates/hir-ty/src/tests/patterns.rs
+++ b/crates/hir-ty/src/tests/patterns.rs
@@ -70,8 +70,8 @@ fn infer_pattern() {
             228..233 '&true': &bool
             229..233 'true': bool
             234..236 '{}': ()
-            246..252 'lambda': |u64, u64, i32| -> i32
-            255..287 '|a: u6...b; c }': |u64, u64, i32| -> i32
+            246..252 'lambda': impl Fn(u64, u64, i32) -> i32
+            255..287 '|a: u6...b; c }': impl Fn(u64, u64, i32) -> i32
             256..257 'a': u64
             264..265 'b': u64
             267..268 'c': i32
@@ -476,7 +476,7 @@ fn infer_adt_pattern() {
             183..184 'x': usize
             190..191 'x': usize
             201..205 'E::B': E
-            209..212 'foo': {unknown}
+            209..212 'foo': bool
             216..217 '1': usize
             227..231 'E::B': E
             235..237 '10': usize
@@ -677,25 +677,25 @@ fn test() {
             51..58 'loop {}': !
             56..58 '{}': ()
             72..171 '{     ... x); }': ()
-            78..81 'foo': fn foo<&(i32, &str), i32, |&(i32, &str)| -> i32>(&(i32, &str), |&(i32, &str)| -> i32) -> i32
+            78..81 'foo': fn foo<&(i32, &str), i32, impl Fn(&(i32, &str)) -> i32>(&(i32, &str), impl Fn(&(i32, &str)) -> i32) -> i32
             78..105 'foo(&(...y)| x)': i32
             82..91 '&(1, "a")': &(i32, &str)
             83..91 '(1, "a")': (i32, &str)
             84..85 '1': i32
             87..90 '"a"': &str
-            93..104 '|&(x, y)| x': |&(i32, &str)| -> i32
+            93..104 '|&(x, y)| x': impl Fn(&(i32, &str)) -> i32
             94..101 '&(x, y)': &(i32, &str)
             95..101 '(x, y)': (i32, &str)
             96..97 'x': i32
             99..100 'y': &str
             103..104 'x': i32
-            142..145 'foo': fn foo<&(i32, &str), &i32, |&(i32, &str)| -> &i32>(&(i32, &str), |&(i32, &str)| -> &i32) -> &i32
+            142..145 'foo': fn foo<&(i32, &str), &i32, impl Fn(&(i32, &str)) -> &i32>(&(i32, &str), impl Fn(&(i32, &str)) -> &i32) -> &i32
             142..168 'foo(&(...y)| x)': &i32
             146..155 '&(1, "a")': &(i32, &str)
             147..155 '(1, "a")': (i32, &str)
             148..149 '1': i32
             151..154 '"a"': &str
-            157..167 '|(x, y)| x': |&(i32, &str)| -> &i32
+            157..167 '|(x, y)| x': impl Fn(&(i32, &str)) -> &i32
             158..164 '(x, y)': (i32, &str)
             159..160 'x': &i32
             162..163 'y': &&str
diff --git a/crates/hir-ty/src/tests/regression.rs b/crates/hir-ty/src/tests/regression.rs
index 8911dd318c4..28b87689ecd 100644
--- a/crates/hir-ty/src/tests/regression.rs
+++ b/crates/hir-ty/src/tests/regression.rs
@@ -270,7 +270,7 @@ fn infer_std_crash_5() {
             61..320 '{     ...     }': ()
             75..79 'name': &{unknown}
             82..166 'if doe...     }': &{unknown}
-            85..98 'doesnt_matter': {unknown}
+            85..98 'doesnt_matter': bool
             99..128 '{     ...     }': &{unknown}
             113..118 'first': &{unknown}
             134..166 '{     ...     }': &{unknown}
@@ -279,7 +279,7 @@ fn infer_std_crash_5() {
             181..188 'content': &{unknown}
             191..313 'if ICE...     }': &{unknown}
             194..231 'ICE_RE..._VALUE': {unknown}
-            194..247 'ICE_RE...&name)': {unknown}
+            194..247 'ICE_RE...&name)': bool
             241..246 '&name': &&{unknown}
             242..246 'name': &{unknown}
             248..276 '{     ...     }': &{unknown}
@@ -805,19 +805,19 @@ fn issue_4966() {
             225..229 'iter': T
             244..246 '{}': Vec<A>
             258..402 '{     ...r(); }': ()
-            268..273 'inner': Map<|&f64| -> f64>
-            276..300 'Map { ... 0.0 }': Map<|&f64| -> f64>
-            285..298 '|_: &f64| 0.0': |&f64| -> f64
+            268..273 'inner': Map<impl Fn(&f64) -> f64>
+            276..300 'Map { ... 0.0 }': Map<impl Fn(&f64) -> f64>
+            285..298 '|_: &f64| 0.0': impl Fn(&f64) -> f64
             286..287 '_': &f64
             295..298 '0.0': f64
-            311..317 'repeat': Repeat<Map<|&f64| -> f64>>
-            320..345 'Repeat...nner }': Repeat<Map<|&f64| -> f64>>
-            338..343 'inner': Map<|&f64| -> f64>
-            356..359 'vec': Vec<IntoIterator::Item<Repeat<Map<|&f64| -> f64>>>>
-            362..371 'from_iter': fn from_iter<IntoIterator::Item<Repeat<Map<|&f64| -> f64>>>, Repeat<Map<|&f64| -> f64>>>(Repeat<Map<|&f64| -> f64>>) -> Vec<IntoIterator::Item<Repeat<Map<|&f64| -> f64>>>>
-            362..379 'from_i...epeat)': Vec<IntoIterator::Item<Repeat<Map<|&f64| -> f64>>>>
-            372..378 'repeat': Repeat<Map<|&f64| -> f64>>
-            386..389 'vec': Vec<IntoIterator::Item<Repeat<Map<|&f64| -> f64>>>>
+            311..317 'repeat': Repeat<Map<impl Fn(&f64) -> f64>>
+            320..345 'Repeat...nner }': Repeat<Map<impl Fn(&f64) -> f64>>
+            338..343 'inner': Map<impl Fn(&f64) -> f64>
+            356..359 'vec': Vec<IntoIterator::Item<Repeat<Map<impl Fn(&f64) -> f64>>>>
+            362..371 'from_iter': fn from_iter<IntoIterator::Item<Repeat<Map<impl Fn(&f64) -> f64>>>, Repeat<Map<impl Fn(&f64) -> f64>>>(Repeat<Map<impl Fn(&f64) -> f64>>) -> Vec<IntoIterator::Item<Repeat<Map<impl Fn(&f64) -> f64>>>>
+            362..379 'from_i...epeat)': Vec<IntoIterator::Item<Repeat<Map<impl Fn(&f64) -> f64>>>>
+            372..378 'repeat': Repeat<Map<impl Fn(&f64) -> f64>>
+            386..389 'vec': Vec<IntoIterator::Item<Repeat<Map<impl Fn(&f64) -> f64>>>>
             386..399 'vec.foo_bar()': {unknown}
         "#]],
     );
@@ -852,7 +852,7 @@ fn main() {
             123..126 'S()': S<i32>
             132..133 's': S<i32>
             132..144 's.g(|_x| {})': ()
-            136..143 '|_x| {}': |&i32| -> ()
+            136..143 '|_x| {}': impl Fn(&i32)
             137..139 '_x': &i32
             141..143 '{}': ()
             150..151 's': S<i32>
@@ -1068,6 +1068,23 @@ fn parse_arule() {
 }
 
 #[test]
+fn nested_closure() {
+    check_types(
+        r#"
+//- minicore: fn, option
+
+fn map<T, U>(o: Option<T>, f: impl FnOnce(T) -> U) -> Option<U> { loop {} }
+
+fn test() {
+    let o = Some(Some(2));
+    map(o, |s| map(s, |x| x));
+                    // ^ i32
+}
+        "#,
+    );
+}
+
+#[test]
 fn call_expected_type_closure() {
     check_types(
         r#"
@@ -1759,13 +1776,14 @@ const C: usize = 2 + 2;
 
 #[test]
 fn regression_14456() {
-    check_no_mismatches(
+    check_types(
         r#"
 //- minicore: future
 async fn x() {}
 fn f() {
     let fut = x();
-    let t = [0u8; 2 + 2];
+    let t = [0u8; { let a = 2 + 2; a }];
+      //^ [u8; 4]
 }
 "#,
     );
@@ -1802,3 +1820,21 @@ where
 "#,
     );
 }
+
+#[test]
+fn match_ergonomics_with_binding_modes_interaction() {
+    check_types(
+        r"
+enum E { A }
+fn foo() {
+    match &E::A {
+        b @ (x @ E::A | x) => {
+            b;
+          //^ &E
+            x;
+          //^ &E
+        }
+    }
+}",
+    );
+}
diff --git a/crates/hir-ty/src/tests/simple.rs b/crates/hir-ty/src/tests/simple.rs
index 0c037a39ec1..2b14a28a672 100644
--- a/crates/hir-ty/src/tests/simple.rs
+++ b/crates/hir-ty/src/tests/simple.rs
@@ -1906,8 +1906,8 @@ fn closure_return() {
         "#,
         expect![[r#"
             16..58 '{     ...; }; }': u32
-            26..27 'x': || -> usize
-            30..55 '|| -> ...n 1; }': || -> usize
+            26..27 'x': impl Fn() -> usize
+            30..55 '|| -> ...n 1; }': impl Fn() -> usize
             42..55 '{ return 1; }': usize
             44..52 'return 1': !
             51..52 '1': usize
@@ -1925,8 +1925,8 @@ fn closure_return_unit() {
         "#,
         expect![[r#"
             16..47 '{     ...; }; }': u32
-            26..27 'x': || -> ()
-            30..44 '|| { return; }': || -> ()
+            26..27 'x': impl Fn()
+            30..44 '|| { return; }': impl Fn()
             33..44 '{ return; }': ()
             35..41 'return': !
         "#]],
@@ -1943,8 +1943,8 @@ fn closure_return_inferred() {
         "#,
         expect![[r#"
             16..46 '{     ..." }; }': u32
-            26..27 'x': || -> &str
-            30..43 '|| { "test" }': || -> &str
+            26..27 'x': impl Fn() -> &str
+            30..43 '|| { "test" }': impl Fn() -> &str
             33..43 '{ "test" }': &str
             35..41 '"test"': &str
         "#]],
@@ -2050,7 +2050,7 @@ fn fn_pointer_return() {
             47..120 '{     ...hod; }': ()
             57..63 'vtable': Vtable
             66..90 'Vtable...| {} }': Vtable
-            83..88 '|| {}': || -> ()
+            83..88 '|| {}': impl Fn()
             86..88 '{}': ()
             100..101 'm': fn()
             104..110 'vtable': Vtable
@@ -2142,9 +2142,9 @@ fn main() {
             149..151 'Ok': Ok<(), ()>(()) -> Result<(), ()>
             149..155 'Ok(())': Result<(), ()>
             152..154 '()': ()
-            167..171 'test': fn test<(), (), || -> impl Future<Output = Result<(), ()>>, impl Future<Output = Result<(), ()>>>(|| -> impl Future<Output = Result<(), ()>>)
+            167..171 'test': fn test<(), (), impl Fn() -> impl Future<Output = Result<(), ()>>, impl Future<Output = Result<(), ()>>>(impl Fn() -> impl Future<Output = Result<(), ()>>)
             167..228 'test(|...    })': ()
-            172..227 '|| asy...     }': || -> impl Future<Output = Result<(), ()>>
+            172..227 '|| asy...     }': impl Fn() -> impl Future<Output = Result<(), ()>>
             175..227 'async ...     }': impl Future<Output = Result<(), ()>>
             191..205 'return Err(())': !
             198..201 'Err': Err<(), ()>(()) -> Result<(), ()>
@@ -2270,8 +2270,8 @@ fn infer_labelled_break_with_val() {
         "#,
         expect![[r#"
             9..335 '{     ...  }; }': ()
-            19..21 '_x': || -> bool
-            24..332 '|| 'ou...     }': || -> bool
+            19..21 '_x': impl Fn() -> bool
+            24..332 '|| 'ou...     }': impl Fn() -> bool
             27..332 ''outer...     }': bool
             40..332 '{     ...     }': ()
             54..59 'inner': i8
@@ -2696,6 +2696,179 @@ impl B for Astruct {}
 }
 
 #[test]
+fn capture_kinds_simple() {
+    check_types(
+        r#"
+struct S;
+
+impl S {
+    fn read(&self) -> &S { self }
+    fn write(&mut self) -> &mut S { self }
+    fn consume(self) -> S { self }
+}
+
+fn f() {
+    let x = S;
+    let c1 = || x.read();
+      //^^ impl Fn() -> &S
+    let c2 = || x.write();
+      //^^ impl FnMut() -> &mut S
+    let c3 = || x.consume();
+      //^^ impl FnOnce() -> S
+    let c3 = || x.consume().consume().consume();
+      //^^ impl FnOnce() -> S
+    let c3 = || x.consume().write().read();
+      //^^ impl FnOnce() -> &S
+    let x = &mut x;
+    let c1 = || x.write();
+      //^^ impl FnMut() -> &mut S
+    let x = S;
+    let c1 = || { let ref t = x; t };
+      //^^ impl Fn() -> &S
+    let c2 = || { let ref mut t = x; t };
+      //^^ impl FnMut() -> &mut S
+    let c3 = || { let t = x; t };
+      //^^ impl FnOnce() -> S
+}
+    "#,
+    )
+}
+
+#[test]
+fn capture_kinds_closure() {
+    check_types(
+        r#"
+//- minicore: copy, fn
+fn f() {
+    let mut x = 2;
+    x = 5;
+    let mut c1 = || { x = 3; x };
+      //^^^^^^ impl FnMut() -> i32
+    let mut c2 = || { c1() };
+      //^^^^^^ impl FnMut() -> i32
+    let mut c1 = || { x };
+      //^^^^^^ impl Fn() -> i32
+    let mut c2 = || { c1() };
+      //^^^^^^ impl Fn() -> i32
+    struct X;
+    let x = X;
+    let mut c1 = || { x };
+      //^^^^^^ impl FnOnce() -> X
+    let mut c2 = || { c1() };
+      //^^^^^^ impl FnOnce() -> X
+}
+        "#,
+    );
+}
+
+#[test]
+fn capture_kinds_overloaded_deref() {
+    check_types(
+        r#"
+//- minicore: fn, deref_mut
+use core::ops::{Deref, DerefMut};
+
+struct Foo;
+impl Deref for Foo {
+    type Target = (i32, u8);
+    fn deref(&self) -> &(i32, u8) {
+        &(5, 2)
+    }
+}
+impl DerefMut for Foo {
+    fn deref_mut(&mut self) -> &mut (i32, u8) {
+        &mut (5, 2)
+    }
+}
+fn test() {
+    let mut x = Foo;
+    let c1 = || *x;
+      //^^ impl Fn() -> (i32, u8)
+    let c2 = || { *x = (2, 5); };
+      //^^ impl FnMut()
+    let c3 = || { x.1 };
+      //^^ impl Fn() -> u8
+    let c4 = || { x.1 = 6; };
+      //^^ impl FnMut()
+}
+       "#,
+    );
+}
+
+#[test]
+fn capture_kinds_with_copy_types() {
+    check_types(
+        r#"
+//- minicore: copy, clone, derive
+#[derive(Clone, Copy)]
+struct Copy;
+struct NotCopy;
+#[derive(Clone, Copy)]
+struct Generic<T>(T);
+
+trait Tr {
+    type Assoc;
+}
+
+impl Tr for Copy {
+    type Assoc = NotCopy;
+}
+
+#[derive(Clone, Copy)]
+struct AssocGeneric<T: Tr>(T::Assoc);
+
+fn f() {
+    let a = Copy;
+    let b = NotCopy;
+    let c = Generic(Copy);
+    let d = Generic(NotCopy);
+    let e: AssocGeneric<Copy> = AssocGeneric(NotCopy);
+    let c1 = || a;
+      //^^ impl Fn() -> Copy
+    let c2 = || b;
+      //^^ impl FnOnce() -> NotCopy
+    let c3 = || c;
+      //^^ impl Fn() -> Generic<Copy>
+    let c3 = || d;
+      //^^ impl FnOnce() -> Generic<NotCopy>
+    let c3 = || e;
+      //^^ impl FnOnce() -> AssocGeneric<Copy>
+}
+    "#,
+    )
+}
+
+#[test]
+fn derive_macro_should_work_for_associated_type() {
+    check_types(
+        r#"
+//- minicore: copy, clone, derive
+#[derive(Clone)]
+struct X;
+#[derive(Clone)]
+struct Y;
+
+trait Tr {
+    type Assoc;
+}
+
+impl Tr for X {
+    type Assoc = Y;
+}
+
+#[derive(Clone)]
+struct AssocGeneric<T: Tr>(T::Assoc);
+
+fn f() {
+    let e: AssocGeneric<X> = AssocGeneric(Y);
+    let e_clone = e.clone();
+      //^^^^^^^ AssocGeneric<X>
+}
+    "#,
+    )
+}
+
+#[test]
 fn cfgd_out_assoc_items() {
     check_types(
         r#"
@@ -3292,6 +3465,19 @@ fn f<T>(t: Ark<T>) {
 }
 
 #[test]
+fn const_dependent_on_local() {
+    check_types(
+        r#"
+fn main() {
+    let s = 5;
+    let t = [2; s];
+      //^ [i32; _]
+}
+"#,
+    );
+}
+
+#[test]
 fn issue_14275() {
     // FIXME: evaluate const generic
     check_types(
diff --git a/crates/hir-ty/src/tests/traits.rs b/crates/hir-ty/src/tests/traits.rs
index 17a0c12c3ef..fdfae44c2d1 100644
--- a/crates/hir-ty/src/tests/traits.rs
+++ b/crates/hir-ty/src/tests/traits.rs
@@ -90,7 +90,7 @@ fn infer_async_closure() {
 async fn test() {
     let f = async move |x: i32| x + 42;
     f;
-//  ^ |i32| -> impl Future<Output = i32>
+//  ^ impl Fn(i32) -> impl Future<Output = i32>
     let a = f(4);
     a;
 //  ^ impl Future<Output = i32>
@@ -99,7 +99,7 @@ async fn test() {
 //  ^ i32
     let f = async move || 42;
     f;
-//  ^ || -> impl Future<Output = i32>
+//  ^ impl Fn() -> impl Future<Output = i32>
     let a = f();
     a;
 //  ^ impl Future<Output = i32>
@@ -116,7 +116,7 @@ async fn test() {
     };
     let _: Option<u64> = c().await;
     c;
-//  ^ || -> impl Future<Output = Option<u64>>
+//  ^ impl Fn() -> impl Future<Output = Option<u64>>
 }
 "#,
     );
@@ -550,7 +550,7 @@ fn test() -> u64 {
             53..54 'a': S
             57..58 'S': S(fn(u32) -> u64) -> S
             57..74 'S(|i| ...s u64)': S
-            59..73 '|i| 2*i as u64': |u32| -> u64
+            59..73 '|i| 2*i as u64': impl Fn(u32) -> u64
             60..61 'i': u32
             63..64 '2': u64
             63..73 '2*i as u64': u64
@@ -1333,9 +1333,9 @@ fn foo<const C: u8, T>() -> (impl FnOnce(&str, T), impl Trait<u8>) {
 }
 "#,
         expect![[r#"
-            134..165 '{     ...(C)) }': (|&str, T| -> (), Bar<u8>)
-            140..163 '(|inpu...ar(C))': (|&str, T| -> (), Bar<u8>)
-            141..154 '|input, t| {}': |&str, T| -> ()
+            134..165 '{     ...(C)) }': (impl Fn(&str, T), Bar<u8>)
+            140..163 '(|inpu...ar(C))': (impl Fn(&str, T), Bar<u8>)
+            141..154 '|input, t| {}': impl Fn(&str, T)
             142..147 'input': &str
             149..150 't': T
             152..154 '{}': ()
@@ -1506,8 +1506,8 @@ fn main() {
             71..105 '{     ...()); }': ()
             77..78 'f': fn f(&dyn Fn(S))
             77..102 'f(&|nu...foo())': ()
-            79..101 '&|numb....foo()': &|S| -> ()
-            80..101 '|numbe....foo()': |S| -> ()
+            79..101 '&|numb....foo()': &impl Fn(S)
+            80..101 '|numbe....foo()': impl Fn(S)
             81..87 'number': S
             89..95 'number': S
             89..101 'number.foo()': ()
@@ -1912,13 +1912,13 @@ fn test() {
             131..132 'f': F
             151..153 '{}': Lazy<T, F>
             251..497 '{     ...o(); }': ()
-            261..266 'lazy1': Lazy<Foo, || -> Foo>
-            283..292 'Lazy::new': fn new<Foo, || -> Foo>(|| -> Foo) -> Lazy<Foo, || -> Foo>
-            283..300 'Lazy::...| Foo)': Lazy<Foo, || -> Foo>
-            293..299 '|| Foo': || -> Foo
+            261..266 'lazy1': Lazy<Foo, impl Fn() -> Foo>
+            283..292 'Lazy::new': fn new<Foo, impl Fn() -> Foo>(impl Fn() -> Foo) -> Lazy<Foo, impl Fn() -> Foo>
+            283..300 'Lazy::...| Foo)': Lazy<Foo, impl Fn() -> Foo>
+            293..299 '|| Foo': impl Fn() -> Foo
             296..299 'Foo': Foo
             310..312 'r1': usize
-            315..320 'lazy1': Lazy<Foo, || -> Foo>
+            315..320 'lazy1': Lazy<Foo, impl Fn() -> Foo>
             315..326 'lazy1.foo()': usize
             368..383 'make_foo_fn_ptr': fn() -> Foo
             399..410 'make_foo_fn': fn make_foo_fn() -> Foo
@@ -1963,20 +1963,20 @@ fn test() {
             163..167 '1u32': u32
             174..175 'x': Option<u32>
             174..190 'x.map(...v + 1)': Option<u32>
-            180..189 '|v| v + 1': |u32| -> u32
+            180..189 '|v| v + 1': impl Fn(u32) -> u32
             181..182 'v': u32
             184..185 'v': u32
             184..189 'v + 1': u32
             188..189 '1': u32
             196..197 'x': Option<u32>
             196..212 'x.map(... 1u64)': Option<u64>
-            202..211 '|_v| 1u64': |u32| -> u64
+            202..211 '|_v| 1u64': impl Fn(u32) -> u64
             203..205 '_v': u32
             207..211 '1u64': u64
             222..223 'y': Option<i64>
             239..240 'x': Option<u32>
             239..252 'x.map(|_v| 1)': Option<i64>
-            245..251 '|_v| 1': |u32| -> i64
+            245..251 '|_v| 1': impl Fn(u32) -> i64
             246..248 '_v': u32
             250..251 '1': i64
         "#]],
@@ -2005,11 +2005,11 @@ fn test<F: FnOnce(u32) -> u64>(f: F) {
   //^^^^ u64
     let g = |v| v + 1;
               //^^^^^ u64
-          //^^^^^^^^^ |u64| -> u64
+          //^^^^^^^^^ impl Fn(u64) -> u64
     g(1u64);
   //^^^^^^^ u64
     let h = |v| 1u128 + v;
-          //^^^^^^^^^^^^^ |u128| -> u128
+          //^^^^^^^^^^^^^ impl Fn(u128) -> u128
 }"#,
     );
 }
@@ -2062,17 +2062,17 @@ fn test() {
             312..314 '{}': ()
             330..489 '{     ... S); }': ()
             340..342 'x1': u64
-            345..349 'foo1': fn foo1<S, u64, |S| -> u64>(S, |S| -> u64) -> u64
+            345..349 'foo1': fn foo1<S, u64, impl Fn(S) -> u64>(S, impl Fn(S) -> u64) -> u64
             345..368 'foo1(S...hod())': u64
             350..351 'S': S
-            353..367 '|s| s.method()': |S| -> u64
+            353..367 '|s| s.method()': impl Fn(S) -> u64
             354..355 's': S
             357..358 's': S
             357..367 's.method()': u64
             378..380 'x2': u64
-            383..387 'foo2': fn foo2<S, u64, |S| -> u64>(|S| -> u64, S) -> u64
+            383..387 'foo2': fn foo2<S, u64, impl Fn(S) -> u64>(impl Fn(S) -> u64, S) -> u64
             383..406 'foo2(|...(), S)': u64
-            388..402 '|s| s.method()': |S| -> u64
+            388..402 '|s| s.method()': impl Fn(S) -> u64
             389..390 's': S
             392..393 's': S
             392..402 's.method()': u64
@@ -2081,14 +2081,14 @@ fn test() {
             421..422 'S': S
             421..446 'S.foo1...hod())': u64
             428..429 'S': S
-            431..445 '|s| s.method()': |S| -> u64
+            431..445 '|s| s.method()': impl Fn(S) -> u64
             432..433 's': S
             435..436 's': S
             435..445 's.method()': u64
             456..458 'x4': u64
             461..462 'S': S
             461..486 'S.foo2...(), S)': u64
-            468..482 '|s| s.method()': |S| -> u64
+            468..482 '|s| s.method()': impl Fn(S) -> u64
             469..470 's': S
             472..473 's': S
             472..482 's.method()': u64
@@ -2562,9 +2562,9 @@ fn main() {
             72..74 '_v': F
             117..120 '{ }': ()
             132..163 '{     ... }); }': ()
-            138..148 'f::<(), _>': fn f<(), |&()| -> ()>(|&()| -> ())
+            138..148 'f::<(), _>': fn f<(), impl Fn(&())>(impl Fn(&()))
             138..160 'f::<()... z; })': ()
-            149..159 '|z| { z; }': |&()| -> ()
+            149..159 '|z| { z; }': impl Fn(&())
             150..151 'z': &()
             153..159 '{ z; }': ()
             155..156 'z': &()
@@ -2721,9 +2721,9 @@ fn main() {
             983..998 'Vec::<i32>::new': fn new<i32>() -> Vec<i32>
             983..1000 'Vec::<...:new()': Vec<i32>
             983..1012 'Vec::<...iter()': IntoIter<i32>
-            983..1075 'Vec::<...one })': FilterMap<IntoIter<i32>, |i32| -> Option<u32>>
+            983..1075 'Vec::<...one })': FilterMap<IntoIter<i32>, impl Fn(i32) -> Option<u32>>
             983..1101 'Vec::<... y; })': ()
-            1029..1074 '|x| if...None }': |i32| -> Option<u32>
+            1029..1074 '|x| if...None }': impl Fn(i32) -> Option<u32>
             1030..1031 'x': i32
             1033..1074 'if x >...None }': Option<u32>
             1036..1037 'x': i32
@@ -2736,7 +2736,7 @@ fn main() {
             1049..1057 'x as u32': u32
             1066..1074 '{ None }': Option<u32>
             1068..1072 'None': Option<u32>
-            1090..1100 '|y| { y; }': |u32| -> ()
+            1090..1100 '|y| { y; }': impl Fn(u32)
             1091..1092 'y': u32
             1094..1100 '{ y; }': ()
             1096..1097 'y': u32
@@ -2979,13 +2979,13 @@ fn foo() {
             52..126 '{     ...)(s) }': ()
             62..63 's': Option<i32>
             66..78 'Option::None': Option<i32>
-            88..89 'f': |Option<i32>| -> ()
-            92..111 '|x: Op...2>| {}': |Option<i32>| -> ()
+            88..89 'f': impl Fn(Option<i32>)
+            92..111 '|x: Op...2>| {}': impl Fn(Option<i32>)
             93..94 'x': Option<i32>
             109..111 '{}': ()
             117..124 '(&f)(s)': ()
-            118..120 '&f': &|Option<i32>| -> ()
-            119..120 'f': |Option<i32>| -> ()
+            118..120 '&f': &impl Fn(Option<i32>)
+            119..120 'f': impl Fn(Option<i32>)
             122..123 's': Option<i32>
         "#]],
     );
@@ -3072,15 +3072,15 @@ fn foo() {
             228..229 's': Option<i32>
             232..236 'None': Option<i32>
             246..247 'f': Box<dyn FnOnce(&Option<i32>)>
-            281..294 'box (|ps| {})': Box<|&Option<i32>| -> ()>
-            286..293 '|ps| {}': |&Option<i32>| -> ()
+            281..294 'box (|ps| {})': Box<impl Fn(&Option<i32>)>
+            286..293 '|ps| {}': impl Fn(&Option<i32>)
             287..289 'ps': &Option<i32>
             291..293 '{}': ()
             300..301 'f': Box<dyn FnOnce(&Option<i32>)>
             300..305 'f(&s)': ()
             302..304 '&s': &Option<i32>
             303..304 's': Option<i32>
-            281..294: expected Box<dyn FnOnce(&Option<i32>)>, got Box<|&Option<i32>| -> ()>
+            281..294: expected Box<dyn FnOnce(&Option<i32>)>, got Box<impl Fn(&Option<i32>)>
         "#]],
     );
 }
@@ -3812,6 +3812,35 @@ fn f() {
 }
 
 #[test]
+fn regression_14443_trait_solve() {
+    check_no_mismatches(
+        r#"
+trait T {
+    fn f(&self) {}
+}
+
+
+fn main() {
+    struct A;
+    impl T for A {}
+
+    let a = A;
+
+    let b = {
+        struct B;
+        impl T for B {}
+
+        B
+    };
+
+    a.f();
+    b.f();
+}
+"#,
+    )
+}
+
+#[test]
 fn associated_type_sized_bounds() {
     check_infer(
         r#"
@@ -4286,3 +4315,63 @@ impl Trait for () {
     "#,
     );
 }
+
+#[test]
+fn derive_macro_bounds() {
+    check_types(
+        r#"
+        //- minicore: clone, derive
+        #[derive(Clone)]
+        struct Copy;
+        struct NotCopy;
+        #[derive(Clone)]
+        struct Generic<T>(T);
+        trait Tr {
+            type Assoc;
+        }
+        impl Tr for Copy {
+            type Assoc = NotCopy;
+        }
+        #[derive(Clone)]
+        struct AssocGeneric<T: Tr>(T::Assoc);
+
+        #[derive(Clone)]
+        struct AssocGeneric2<T: Tr>(<T as Tr>::Assoc);
+
+        #[derive(Clone)]
+        struct AssocGeneric3<T: Tr>(Generic<T::Assoc>);
+
+        #[derive(Clone)]
+        struct Vec<T>();
+
+        #[derive(Clone)]
+        struct R1(Vec<R2>);
+        #[derive(Clone)]
+        struct R2(R1);
+
+        fn f() {
+            let x = (&Copy).clone();
+              //^ Copy
+            let x = (&NotCopy).clone();
+              //^ &NotCopy
+            let x = (&Generic(Copy)).clone();
+              //^ Generic<Copy>
+            let x = (&Generic(NotCopy)).clone();
+              //^ &Generic<NotCopy>
+            let x: &AssocGeneric<Copy> = &AssocGeneric(NotCopy);
+            let x = x.clone();
+              //^ &AssocGeneric<Copy>
+            let x: &AssocGeneric2<Copy> = &AssocGeneric2(NotCopy);
+            let x = x.clone();
+              //^ &AssocGeneric2<Copy>
+            let x: &AssocGeneric3<Copy> = &AssocGeneric3(Generic(NotCopy));
+            let x = x.clone();
+              //^ &AssocGeneric3<Copy>
+            let x = (&R1(Vec())).clone();
+              //^ R1
+            let x = (&R2(R1(Vec()))).clone();
+              //^ R2
+        }
+        "#,
+    );
+}
diff --git a/crates/hir-ty/src/traits.rs b/crates/hir-ty/src/traits.rs
index deb6ce56773..8bc38aca472 100644
--- a/crates/hir-ty/src/traits.rs
+++ b/crates/hir-ty/src/traits.rs
@@ -4,7 +4,7 @@ use std::{env::var, sync::Arc};
 
 use chalk_ir::GoalData;
 use chalk_recursive::Cache;
-use chalk_solve::{logging_db::LoggingRustIrDatabase, Solver};
+use chalk_solve::{logging_db::LoggingRustIrDatabase, rust_ir, Solver};
 
 use base_db::CrateId;
 use hir_def::{
@@ -177,8 +177,10 @@ fn is_chalk_print() -> bool {
     std::env::var("CHALK_PRINT").is_ok()
 }
 
-#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
 pub enum FnTrait {
+    // Warning: Order is important. If something implements `x` it should also implement
+    // `y` if `y <= x`.
     FnOnce,
     FnMut,
     Fn,
@@ -193,6 +195,14 @@ impl FnTrait {
         }
     }
 
+    pub const fn to_chalk_ir(self) -> rust_ir::ClosureKind {
+        match self {
+            FnTrait::FnOnce => rust_ir::ClosureKind::FnOnce,
+            FnTrait::FnMut => rust_ir::ClosureKind::FnMut,
+            FnTrait::Fn => rust_ir::ClosureKind::Fn,
+        }
+    }
+
     pub fn method_name(self) -> Name {
         match self {
             FnTrait::FnOnce => name!(call_once),
diff --git a/crates/hir-ty/src/utils.rs b/crates/hir-ty/src/utils.rs
index 9d97ab84a56..3b2a726688d 100644
--- a/crates/hir-ty/src/utils.rs
+++ b/crates/hir-ty/src/utils.rs
@@ -4,7 +4,7 @@
 use std::iter;
 
 use base_db::CrateId;
-use chalk_ir::{cast::Cast, fold::Shift, BoundVar, DebruijnIndex};
+use chalk_ir::{cast::Cast, fold::Shift, BoundVar, DebruijnIndex, Mutability};
 use either::Either;
 use hir_def::{
     db::DefDatabase,
@@ -12,6 +12,7 @@ use hir_def::{
         GenericParams, TypeOrConstParamData, TypeParamProvenance, WherePredicate,
         WherePredicateTypeTarget,
     },
+    hir::BindingAnnotation,
     lang_item::LangItem,
     resolver::{HasResolver, TypeNs},
     type_ref::{TraitBoundModifier, TypeRef},
@@ -24,7 +25,8 @@ use rustc_hash::FxHashSet;
 use smallvec::{smallvec, SmallVec};
 
 use crate::{
-    db::HirDatabase, ChalkTraitId, Interner, Substitution, TraitRef, TraitRefExt, WhereClause,
+    db::HirDatabase, ChalkTraitId, Interner, Substitution, TraitRef, TraitRefExt, Ty, TyExt,
+    WhereClause,
 };
 
 pub(crate) fn fn_traits(
@@ -352,3 +354,20 @@ pub fn is_fn_unsafe_to_call(db: &dyn HirDatabase, func: FunctionId) -> bool {
         _ => false,
     }
 }
+
+pub(crate) fn pattern_matching_dereference_count(
+    cond_ty: &mut Ty,
+    binding_mode: &mut BindingAnnotation,
+) -> usize {
+    let mut r = 0;
+    while let Some((ty, _, mu)) = cond_ty.as_reference() {
+        if mu == Mutability::Mut && *binding_mode != BindingAnnotation::Ref {
+            *binding_mode = BindingAnnotation::RefMut;
+        } else {
+            *binding_mode = BindingAnnotation::Ref;
+        }
+        *cond_ty = ty.clone();
+        r += 1;
+    }
+    r
+}
diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs
index 253d62dafc6..f756832f0ff 100644
--- a/crates/hir/src/diagnostics.rs
+++ b/crates/hir/src/diagnostics.rs
@@ -50,7 +50,9 @@ diagnostics![
     PrivateField,
     ReplaceFilterMapNextWithFindMap,
     TypeMismatch,
+    UndeclaredLabel,
     UnimplementedBuiltinMacro,
+    UnreachableLabel,
     UnresolvedExternCrate,
     UnresolvedField,
     UnresolvedImport,
@@ -62,6 +64,13 @@ diagnostics![
 ];
 
 #[derive(Debug)]
+pub struct BreakOutsideOfLoop {
+    pub expr: InFile<AstPtr<ast::Expr>>,
+    pub is_break: bool,
+    pub bad_value_break: bool,
+}
+
+#[derive(Debug)]
 pub struct UnresolvedModule {
     pub decl: InFile<AstPtr<ast::Module>>,
     pub candidates: Box<[String]>,
@@ -84,6 +93,17 @@ pub struct UnresolvedMacroCall {
     pub path: ModPath,
     pub is_bang: bool,
 }
+#[derive(Debug, Clone, Eq, PartialEq)]
+pub struct UnreachableLabel {
+    pub node: InFile<AstPtr<ast::Lifetime>>,
+    pub name: Name,
+}
+
+#[derive(Debug, Clone, Eq, PartialEq)]
+pub struct UndeclaredLabel {
+    pub node: InFile<AstPtr<ast::Lifetime>>,
+    pub name: Name,
+}
 
 #[derive(Debug, Clone, Eq, PartialEq)]
 pub struct InactiveCode {
@@ -167,13 +187,6 @@ pub struct PrivateField {
 }
 
 #[derive(Debug)]
-pub struct BreakOutsideOfLoop {
-    pub expr: InFile<AstPtr<ast::Expr>>,
-    pub is_break: bool,
-    pub bad_value_break: bool,
-}
-
-#[derive(Debug)]
 pub struct MissingUnsafe {
     pub expr: InFile<AstPtr<ast::Expr>>,
 }
diff --git a/crates/hir/src/display.rs b/crates/hir/src/display.rs
index 5aae92efd19..49165ca70ec 100644
--- a/crates/hir/src/display.rs
+++ b/crates/hir/src/display.rs
@@ -1,6 +1,6 @@
 //! HirDisplay implementations for various hir types.
 use hir_def::{
-    adt::VariantData,
+    data::adt::VariantData,
     generics::{
         TypeOrConstParamData, TypeParamProvenance, WherePredicate, WherePredicateTypeTarget,
     },
@@ -8,6 +8,7 @@ use hir_def::{
     type_ref::{TypeBound, TypeRef},
     AdtId, GenericDefId,
 };
+use hir_expand::name;
 use hir_ty::{
     display::{
         write_bounds_like_dyn_trait_with_prefix, write_visibility, HirDisplay, HirDisplayError,
@@ -76,22 +77,22 @@ impl HirDisplay for Function {
         };
 
         let mut first = true;
-        for (name, type_ref) in &data.params {
+        // FIXME: Use resolved `param.ty` once we no longer discard lifetimes
+        for (type_ref, param) in data.params.iter().zip(self.assoc_fn_params(db)) {
+            let local = param.as_local(db).map(|it| it.name(db));
             if !first {
                 f.write_str(", ")?;
             } else {
                 first = false;
-                if data.has_self_param() {
+                if local == Some(name!(self)) {
                     write_self_param(type_ref, f)?;
                     continue;
                 }
             }
-            match name {
+            match local {
                 Some(name) => write!(f, "{name}: ")?,
                 None => f.write_str("_: ")?,
             }
-            // FIXME: Use resolved `param.ty` or raw `type_ref`?
-            // The former will ignore lifetime arguments currently.
             type_ref.hir_fmt(f)?;
         }
 
diff --git a/crates/hir/src/from_id.rs b/crates/hir/src/from_id.rs
index aaaa7abf386..883e6a29b06 100644
--- a/crates/hir/src/from_id.rs
+++ b/crates/hir/src/from_id.rs
@@ -4,7 +4,7 @@
 //! are splitting the hir.
 
 use hir_def::{
-    expr::{BindingId, LabelId},
+    hir::{BindingId, LabelId},
     AdtId, AssocItemId, DefWithBodyId, EnumVariantId, FieldId, GenericDefId, GenericParamId,
     ModuleDefId, VariantId,
 };
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index 624ee1fd30a..db923cb0fe2 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -39,10 +39,10 @@ use arrayvec::ArrayVec;
 use base_db::{CrateDisplayName, CrateId, CrateOrigin, Edition, FileId, ProcMacroKind};
 use either::Either;
 use hir_def::{
-    adt::VariantData,
     body::{BodyDiagnostic, SyntheticSyntax},
-    expr::{BindingAnnotation, BindingId, ExprOrPatId, LabelId, Pat},
+    data::adt::VariantData,
     generics::{LifetimeParamData, TypeOrConstParamData, TypeParamProvenance},
+    hir::{BindingAnnotation, BindingId, ExprOrPatId, LabelId, Pat},
     item_tree::ItemTreeNode,
     lang_item::{LangItem, LangItemTarget},
     layout::{Layout, LayoutError, ReprOptions},
@@ -88,9 +88,10 @@ pub use crate::{
         AnyDiagnostic, BreakOutsideOfLoop, ExpectedFunction, InactiveCode, IncoherentImpl,
         IncorrectCase, InvalidDeriveTarget, MacroError, MalformedDerive, MismatchedArgCount,
         MissingFields, MissingMatchArms, MissingUnsafe, NeedMut, NoSuchField, PrivateAssocItem,
-        PrivateField, ReplaceFilterMapNextWithFindMap, TypeMismatch, UnimplementedBuiltinMacro,
-        UnresolvedExternCrate, UnresolvedField, UnresolvedImport, UnresolvedMacroCall,
-        UnresolvedMethodCall, UnresolvedModule, UnresolvedProcMacro, UnusedMut,
+        PrivateField, ReplaceFilterMapNextWithFindMap, TypeMismatch, UndeclaredLabel,
+        UnimplementedBuiltinMacro, UnreachableLabel, UnresolvedExternCrate, UnresolvedField,
+        UnresolvedImport, UnresolvedMacroCall, UnresolvedMethodCall, UnresolvedModule,
+        UnresolvedProcMacro, UnusedMut,
     },
     has_source::HasSource,
     semantics::{PathResolution, Semantics, SemanticsScope, TypeInfo, VisibleTraits},
@@ -108,9 +109,8 @@ pub use crate::{
 pub use {
     cfg::{CfgAtom, CfgExpr, CfgOptions},
     hir_def::{
-        adt::StructKind,
-        attr::{Attrs, AttrsWithOwner, Documentation},
-        builtin_attr::AttributeTemplate,
+        attr::{builtin::AttributeTemplate, Attrs, AttrsWithOwner, Documentation},
+        data::adt::StructKind,
         find_path::PrefixKind,
         import_map,
         nameres::ModuleSource,
@@ -129,7 +129,7 @@ pub use {
         ExpandResult, HirFileId, InFile, MacroFile, Origin,
     },
     hir_ty::{
-        display::{HirDisplay, HirDisplayError, HirWrite},
+        display::{ClosureStyle, HirDisplay, HirDisplayError, HirWrite},
         mir::MirEvalError,
         PointerCast, Safety,
     },
@@ -1393,6 +1393,12 @@ impl DefWithBody {
                     }
                     .into(),
                 ),
+                BodyDiagnostic::UnreachableLabel { node, name } => {
+                    acc.push(UnreachableLabel { node: node.clone(), name: name.clone() }.into())
+                }
+                BodyDiagnostic::UndeclaredLabel { node, name } => {
+                    acc.push(UndeclaredLabel { node: node.clone(), name: name.clone() }.into())
+                }
             }
         }
 
@@ -1405,14 +1411,6 @@ impl DefWithBody {
                     let field = source_map.field_syntax(expr);
                     acc.push(NoSuchField { field }.into())
                 }
-                &hir_ty::InferenceDiagnostic::BreakOutsideOfLoop {
-                    expr,
-                    is_break,
-                    bad_value_break,
-                } => {
-                    let expr = expr_syntax(expr);
-                    acc.push(BreakOutsideOfLoop { expr, is_break, bad_value_break }.into())
-                }
                 &hir_ty::InferenceDiagnostic::MismatchedArgCount { call_expr, expected, found } => {
                     acc.push(
                         MismatchedArgCount { call_expr: expr_syntax(call_expr), expected, found }
@@ -1484,6 +1482,14 @@ impl DefWithBody {
                         .into(),
                     )
                 }
+                &hir_ty::InferenceDiagnostic::BreakOutsideOfLoop {
+                    expr,
+                    is_break,
+                    bad_value_break,
+                } => {
+                    let expr = expr_syntax(expr);
+                    acc.push(BreakOutsideOfLoop { expr, is_break, bad_value_break }.into())
+                }
             }
         }
         for (pat_or_expr, mismatch) in infer.type_mismatches() {
@@ -1524,35 +1530,44 @@ impl DefWithBody {
 
         let hir_body = db.body(self.into());
 
-        if let Ok(borrowck_result) = db.borrowck(self.into()) {
-            let mir_body = &borrowck_result.mir_body;
-            let mol = &borrowck_result.mutability_of_locals;
-            for (binding_id, _) in hir_body.bindings.iter() {
-                let need_mut = &mol[mir_body.binding_locals[binding_id]];
-                let local = Local { parent: self.into(), binding_id };
-                match (need_mut, local.is_mut(db)) {
-                    (mir::MutabilityReason::Mut { .. }, true)
-                    | (mir::MutabilityReason::Not, false) => (),
-                    (mir::MutabilityReason::Mut { spans }, false) => {
-                        for span in spans {
-                            let span: InFile<SyntaxNodePtr> = match span {
-                                mir::MirSpan::ExprId(e) => match source_map.expr_syntax(*e) {
-                                    Ok(s) => s.map(|x| x.into()),
-                                    Err(_) => continue,
-                                },
-                                mir::MirSpan::PatId(p) => match source_map.pat_syntax(*p) {
-                                    Ok(s) => s.map(|x| match x {
-                                        Either::Left(e) => e.into(),
-                                        Either::Right(e) => e.into(),
-                                    }),
-                                    Err(_) => continue,
-                                },
-                                mir::MirSpan::Unknown => continue,
-                            };
-                            acc.push(NeedMut { local, span }.into());
+        if let Ok(borrowck_results) = db.borrowck(self.into()) {
+            for borrowck_result in borrowck_results.iter() {
+                let mir_body = &borrowck_result.mir_body;
+                let mol = &borrowck_result.mutability_of_locals;
+                for (binding_id, _) in hir_body.bindings.iter() {
+                    let Some(&local) = mir_body.binding_locals.get(binding_id) else {
+                        continue;
+                    };
+                    let need_mut = &mol[local];
+                    let local = Local { parent: self.into(), binding_id };
+                    match (need_mut, local.is_mut(db)) {
+                        (mir::MutabilityReason::Mut { .. }, true)
+                        | (mir::MutabilityReason::Not, false) => (),
+                        (mir::MutabilityReason::Mut { spans }, false) => {
+                            for span in spans {
+                                let span: InFile<SyntaxNodePtr> = match span {
+                                    mir::MirSpan::ExprId(e) => match source_map.expr_syntax(*e) {
+                                        Ok(s) => s.map(|x| x.into()),
+                                        Err(_) => continue,
+                                    },
+                                    mir::MirSpan::PatId(p) => match source_map.pat_syntax(*p) {
+                                        Ok(s) => s.map(|x| match x {
+                                            Either::Left(e) => e.into(),
+                                            Either::Right(e) => e.into(),
+                                        }),
+                                        Err(_) => continue,
+                                    },
+                                    mir::MirSpan::Unknown => continue,
+                                };
+                                acc.push(NeedMut { local, span }.into());
+                            }
+                        }
+                        (mir::MutabilityReason::Not, true) => {
+                            if !infer.mutated_bindings_in_closure.contains(&binding_id) {
+                                acc.push(UnusedMut { local }.into())
+                            }
                         }
                     }
-                    (mir::MutabilityReason::Not, true) => acc.push(UnusedMut { local }.into()),
                 }
             }
         }
@@ -1838,7 +1853,7 @@ impl Param {
     }
 
     pub fn name(&self, db: &dyn HirDatabase) -> Option<Name> {
-        db.function_data(self.func.id).params[self.idx].0.clone()
+        Some(self.as_local(db)?.name(db))
     }
 
     pub fn as_local(&self, db: &dyn HirDatabase) -> Option<Local> {
@@ -1879,7 +1894,7 @@ impl SelfParam {
         func_data
             .params
             .first()
-            .map(|(_, param)| match &**param {
+            .map(|param| match &**param {
                 TypeRef::Reference(.., mutability) => match mutability {
                     hir_def::type_ref::Mutability::Shared => Access::Shared,
                     hir_def::type_ref::Mutability::Mut => Access::Exclusive,
@@ -2690,9 +2705,7 @@ impl BuiltinAttr {
     }
 
     fn builtin(name: &str) -> Option<Self> {
-        hir_def::builtin_attr::INERT_ATTRIBUTES
-            .iter()
-            .position(|tool| tool.name == name)
+        hir_def::attr::builtin::find_builtin_attr_idx(name)
             .map(|idx| BuiltinAttr { krate: None, idx: idx as u32 })
     }
 
@@ -2700,14 +2713,14 @@ impl BuiltinAttr {
         // FIXME: Return a `Name` here
         match self.krate {
             Some(krate) => db.crate_def_map(krate).registered_attrs()[self.idx as usize].clone(),
-            None => SmolStr::new(hir_def::builtin_attr::INERT_ATTRIBUTES[self.idx as usize].name),
+            None => SmolStr::new(hir_def::attr::builtin::INERT_ATTRIBUTES[self.idx as usize].name),
         }
     }
 
     pub fn template(&self, _: &dyn HirDatabase) -> Option<AttributeTemplate> {
         match self.krate {
             Some(_) => None,
-            None => Some(hir_def::builtin_attr::INERT_ATTRIBUTES[self.idx as usize].template),
+            None => Some(hir_def::attr::builtin::INERT_ATTRIBUTES[self.idx as usize].template),
         }
     }
 }
@@ -2730,7 +2743,7 @@ impl ToolModule {
     }
 
     fn builtin(name: &str) -> Option<Self> {
-        hir_def::builtin_attr::TOOL_MODULES
+        hir_def::attr::builtin::TOOL_MODULES
             .iter()
             .position(|&tool| tool == name)
             .map(|idx| ToolModule { krate: None, idx: idx as u32 })
@@ -2740,7 +2753,7 @@ impl ToolModule {
         // FIXME: Return a `Name` here
         match self.krate {
             Some(krate) => db.crate_def_map(krate).registered_tools()[self.idx as usize].clone(),
-            None => SmolStr::new(hir_def::builtin_attr::TOOL_MODULES[self.idx as usize]),
+            None => SmolStr::new(hir_def::attr::builtin::TOOL_MODULES[self.idx as usize]),
         }
     }
 }
@@ -3379,7 +3392,12 @@ impl Type {
     }
 
     pub fn as_callable(&self, db: &dyn HirDatabase) -> Option<Callable> {
+        let mut the_ty = &self.ty;
         let callee = match self.ty.kind(Interner) {
+            TyKind::Ref(_, _, ty) if ty.as_closure().is_some() => {
+                the_ty = ty;
+                Callee::Closure(ty.as_closure().unwrap())
+            }
             TyKind::Closure(id, _) => Callee::Closure(*id),
             TyKind::Function(_) => Callee::FnPtr,
             TyKind::FnDef(..) => Callee::Def(self.ty.callable_def(db)?),
@@ -3394,7 +3412,7 @@ impl Type {
             }
         };
 
-        let sig = self.ty.callable_sig(db)?;
+        let sig = the_ty.callable_sig(db)?;
         Some(Callable { ty: self.clone(), sig, callee, is_bound_method: false })
     }
 
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index 2b2a2966c1c..ffd0b0ad7ad 100644
--- a/crates/hir/src/semantics.rs
+++ b/crates/hir/src/semantics.rs
@@ -8,7 +8,7 @@ use base_db::{FileId, FileRange};
 use either::Either;
 use hir_def::{
     body,
-    expr::Expr,
+    hir::Expr,
     macro_id_to_def_id,
     resolver::{self, HasResolver, Resolver, TypeNs},
     type_ref::Mutability,
@@ -1065,7 +1065,7 @@ impl<'db> SemanticsImpl<'db> {
 
     fn resolve_type(&self, ty: &ast::Type) -> Option<Type> {
         let analyze = self.analyze(ty.syntax())?;
-        let ctx = body::LowerCtx::new(self.db.upcast(), analyze.file_id);
+        let ctx = body::LowerCtx::with_file_id(self.db.upcast(), analyze.file_id);
         let ty = hir_ty::TyLoweringContext::new(self.db, &analyze.resolver)
             .lower_ty(&crate::TypeRef::from_ast(&ctx, ty.clone()));
         Some(Type::new_with_resolver(self.db, &analyze.resolver, ty))
@@ -1672,7 +1672,7 @@ impl<'a> SemanticsScope<'a> {
     /// Resolve a path as-if it was written at the given scope. This is
     /// necessary a heuristic, as it doesn't take hygiene into account.
     pub fn speculative_resolve(&self, path: &ast::Path) -> Option<PathResolution> {
-        let ctx = body::LowerCtx::new(self.db.upcast(), self.file_id);
+        let ctx = body::LowerCtx::with_file_id(self.db.upcast(), self.file_id);
         let path = Path::from_src(path.clone(), &ctx)?;
         resolve_hir_path(self.db, &self.resolver, &path)
     }
diff --git a/crates/hir/src/semantics/source_to_def.rs b/crates/hir/src/semantics/source_to_def.rs
index f6f8c9a250f..c50ffa4f8b7 100644
--- a/crates/hir/src/semantics/source_to_def.rs
+++ b/crates/hir/src/semantics/source_to_def.rs
@@ -14,7 +14,7 @@
 //! expression, an item definition.
 //!
 //! Knowing only the syntax gives us relatively little info. For example,
-//! looking at the syntax of the function we can realise that it is a part of an
+//! looking at the syntax of the function we can realize that it is a part of an
 //! `impl` block, but we won't be able to tell what trait function the current
 //! function overrides, and whether it does that correctly. For that, we need to
 //! go from [`ast::Fn`] to [`crate::Function`], and that's exactly what this
@@ -88,9 +88,11 @@
 use base_db::FileId;
 use hir_def::{
     child_by_source::ChildBySource,
-    dyn_map::DynMap,
-    expr::{BindingId, LabelId},
-    keys::{self, Key},
+    dyn_map::{
+        keys::{self, Key},
+        DynMap,
+    },
+    hir::{BindingId, LabelId},
     AdtId, ConstId, ConstParamId, DefWithBodyId, EnumId, EnumVariantId, FieldId, FunctionId,
     GenericDefId, GenericParamId, ImplId, LifetimeParamId, MacroId, ModuleId, StaticId, StructId,
     TraitAliasId, TraitId, TypeAliasId, TypeParamId, UnionId, VariantId,
diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs
index 4d33c71fdd8..2a7a0ae4c43 100644
--- a/crates/hir/src/source_analyzer.rs
+++ b/crates/hir/src/source_analyzer.rs
@@ -17,7 +17,7 @@ use hir_def::{
         scope::{ExprScopes, ScopeId},
         Body, BodySourceMap,
     },
-    expr::{ExprId, Pat, PatId},
+    hir::{ExprId, Pat, PatId},
     lang_item::LangItem,
     macro_id_to_def_id,
     path::{ModPath, Path, PathKind},
@@ -463,7 +463,7 @@ impl SourceAnalyzer {
         db: &dyn HirDatabase,
         macro_call: InFile<&ast::MacroCall>,
     ) -> Option<Macro> {
-        let ctx = body::LowerCtx::new(db.upcast(), macro_call.file_id);
+        let ctx = body::LowerCtx::with_file_id(db.upcast(), macro_call.file_id);
         let path = macro_call.value.path().and_then(|ast| Path::from_src(ast, &ctx))?;
         self.resolver.resolve_path_as_macro(db.upcast(), path.mod_path()?).map(|it| it.into())
     }
diff --git a/crates/ide-assists/src/handlers/convert_nested_function_to_closure.rs b/crates/ide-assists/src/handlers/convert_nested_function_to_closure.rs
new file mode 100644
index 00000000000..399f87c8f50
--- /dev/null
+++ b/crates/ide-assists/src/handlers/convert_nested_function_to_closure.rs
@@ -0,0 +1,209 @@
+use ide_db::assists::{AssistId, AssistKind};
+use syntax::ast::{self, HasGenericParams, HasName};
+use syntax::{AstNode, SyntaxKind};
+
+use crate::assist_context::{AssistContext, Assists};
+
+// Assist: convert_nested_function_to_closure
+//
+// Converts a function that is defined within the body of another function into a closure.
+//
+// ```
+// fn main() {
+//     fn fo$0o(label: &str, number: u64) {
+//         println!("{}: {}", label, number);
+//     }
+//
+//     foo("Bar", 100);
+// }
+// ```
+// ->
+// ```
+// fn main() {
+//     let foo = |label: &str, number: u64| {
+//         println!("{}: {}", label, number);
+//     };
+//
+//     foo("Bar", 100);
+// }
+// ```
+pub(crate) fn convert_nested_function_to_closure(
+    acc: &mut Assists,
+    ctx: &AssistContext<'_>,
+) -> Option<()> {
+    let name = ctx.find_node_at_offset::<ast::Name>()?;
+    let function = name.syntax().parent().and_then(ast::Fn::cast)?;
+
+    if !is_nested_function(&function) || is_generic(&function) || has_modifiers(&function) {
+        return None;
+    }
+
+    let target = function.syntax().text_range();
+    let body = function.body()?;
+    let name = function.name()?;
+    let param_list = function.param_list()?;
+
+    acc.add(
+        AssistId("convert_nested_function_to_closure", AssistKind::RefactorRewrite),
+        "Convert nested function to closure",
+        target,
+        |edit| {
+            let params = &param_list.syntax().text().to_string();
+            let params = params.strip_prefix("(").unwrap_or(params);
+            let params = params.strip_suffix(")").unwrap_or(params);
+
+            let mut body = body.to_string();
+            if !has_semicolon(&function) {
+                body.push(';');
+            }
+            edit.replace(target, format!("let {name} = |{params}| {body}"));
+        },
+    )
+}
+
+/// Returns whether the given function is nested within the body of another function.
+fn is_nested_function(function: &ast::Fn) -> bool {
+    function.syntax().ancestors().skip(1).find_map(ast::Item::cast).map_or(false, |it| {
+        matches!(it, ast::Item::Fn(_) | ast::Item::Static(_) | ast::Item::Const(_))
+    })
+}
+
+/// Returns whether the given nested function has generic parameters.
+fn is_generic(function: &ast::Fn) -> bool {
+    function.generic_param_list().is_some()
+}
+
+/// Returns whether the given nested function has any modifiers:
+///
+/// - `async`,
+/// - `const` or
+/// - `unsafe`
+fn has_modifiers(function: &ast::Fn) -> bool {
+    function.async_token().is_some()
+        || function.const_token().is_some()
+        || function.unsafe_token().is_some()
+}
+
+/// Returns whether the given nested function has a trailing semicolon.
+fn has_semicolon(function: &ast::Fn) -> bool {
+    function
+        .syntax()
+        .next_sibling_or_token()
+        .map(|t| t.kind() == SyntaxKind::SEMICOLON)
+        .unwrap_or(false)
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::tests::{check_assist, check_assist_not_applicable};
+
+    use super::convert_nested_function_to_closure;
+
+    #[test]
+    fn convert_nested_function_to_closure_works() {
+        check_assist(
+            convert_nested_function_to_closure,
+            r#"
+fn main() {
+    fn $0foo(a: u64, b: u64) -> u64 {
+        2 * (a + b)
+    }
+
+    _ = foo(3, 4);
+}
+            "#,
+            r#"
+fn main() {
+    let foo = |a: u64, b: u64| {
+        2 * (a + b)
+    };
+
+    _ = foo(3, 4);
+}
+            "#,
+        );
+    }
+
+    #[test]
+    fn convert_nested_function_to_closure_works_with_existing_semicolon() {
+        check_assist(
+            convert_nested_function_to_closure,
+            r#"
+fn main() {
+    fn foo$0(a: u64, b: u64) -> u64 {
+        2 * (a + b)
+    };
+
+    _ = foo(3, 4);
+}
+            "#,
+            r#"
+fn main() {
+    let foo = |a: u64, b: u64| {
+        2 * (a + b)
+    };
+
+    _ = foo(3, 4);
+}
+            "#,
+        );
+    }
+
+    #[test]
+    fn convert_nested_function_to_closure_is_not_suggested_on_top_level_function() {
+        check_assist_not_applicable(
+            convert_nested_function_to_closure,
+            r#"
+fn ma$0in() {}
+            "#,
+        );
+    }
+
+    #[test]
+    fn convert_nested_function_to_closure_is_not_suggested_when_cursor_off_name() {
+        check_assist_not_applicable(
+            convert_nested_function_to_closure,
+            r#"
+fn main() {
+    fn foo(a: u64, $0b: u64) -> u64 {
+        2 * (a + b)
+    }
+
+    _ = foo(3, 4);
+}
+            "#,
+        );
+    }
+
+    #[test]
+    fn convert_nested_function_to_closure_is_not_suggested_if_function_has_generic_params() {
+        check_assist_not_applicable(
+            convert_nested_function_to_closure,
+            r#"
+fn main() {
+    fn fo$0o<S: Into<String>>(s: S) -> String {
+        s.into()
+    }
+
+    _ = foo("hello");
+}
+            "#,
+        );
+    }
+
+    #[test]
+    fn convert_nested_function_to_closure_is_not_suggested_if_function_has_modifier() {
+        check_assist_not_applicable(
+            convert_nested_function_to_closure,
+            r#"
+fn main() {
+    const fn fo$0o(s: String) -> String {
+        s
+    }
+
+    _ = foo("hello");
+}
+            "#,
+        );
+    }
+}
diff --git a/crates/ide-assists/src/handlers/extract_function.rs b/crates/ide-assists/src/handlers/extract_function.rs
index 0b90c9ba34f..bfa2890676b 100644
--- a/crates/ide-assists/src/handlers/extract_function.rs
+++ b/crates/ide-assists/src/handlers/extract_function.rs
@@ -70,6 +70,11 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
     }
 
     let node = ctx.covering_element();
+    if matches!(node.kind(), T!['{'] | T!['}'] | T!['('] | T![')'] | T!['['] | T![']']) {
+        cov_mark::hit!(extract_function_in_braces_is_not_applicable);
+        return None;
+    }
+
     if node.kind() == COMMENT {
         cov_mark::hit!(extract_function_in_comment_is_not_applicable);
         return None;
@@ -5800,4 +5805,40 @@ fn $0fun_name() -> ControlFlow<()> {
 "#,
         );
     }
+
+    #[test]
+    fn in_left_curly_is_not_applicable() {
+        cov_mark::check!(extract_function_in_braces_is_not_applicable);
+        check_assist_not_applicable(extract_function, r"fn foo() { $0}$0");
+    }
+
+    #[test]
+    fn in_right_curly_is_not_applicable() {
+        cov_mark::check!(extract_function_in_braces_is_not_applicable);
+        check_assist_not_applicable(extract_function, r"fn foo() $0{$0 }");
+    }
+
+    #[test]
+    fn in_left_paren_is_not_applicable() {
+        cov_mark::check!(extract_function_in_braces_is_not_applicable);
+        check_assist_not_applicable(extract_function, r"fn foo( $0)$0 { }");
+    }
+
+    #[test]
+    fn in_right_paren_is_not_applicable() {
+        cov_mark::check!(extract_function_in_braces_is_not_applicable);
+        check_assist_not_applicable(extract_function, r"fn foo $0($0 ) { }");
+    }
+
+    #[test]
+    fn in_left_brack_is_not_applicable() {
+        cov_mark::check!(extract_function_in_braces_is_not_applicable);
+        check_assist_not_applicable(extract_function, r"fn foo(arr: &mut [i32$0]$0) {}");
+    }
+
+    #[test]
+    fn in_right_brack_is_not_applicable() {
+        cov_mark::check!(extract_function_in_braces_is_not_applicable);
+        check_assist_not_applicable(extract_function, r"fn foo(arr: &mut $0[$0i32]) {}");
+    }
 }
diff --git a/crates/ide-assists/src/handlers/extract_variable.rs b/crates/ide-assists/src/handlers/extract_variable.rs
index 16356141288..014c23197fa 100644
--- a/crates/ide-assists/src/handlers/extract_variable.rs
+++ b/crates/ide-assists/src/handlers/extract_variable.rs
@@ -1,3 +1,4 @@
+use hir::TypeInfo;
 use stdx::format_to;
 use syntax::{
     ast::{self, AstNode},
@@ -46,21 +47,24 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
         .take_while(|it| ctx.selection_trimmed().contains_range(it.text_range()))
         .find_map(valid_target_expr)?;
 
-    if let Some(ty_info) = ctx.sema.type_of_expr(&to_extract) {
-        if ty_info.adjusted().is_unit() {
-            return None;
-        }
+    let ty = ctx.sema.type_of_expr(&to_extract).map(TypeInfo::adjusted);
+    if matches!(&ty, Some(ty_info) if ty_info.is_unit()) {
+        return None;
     }
 
-    let reference_modifier = match get_receiver_type(ctx, &to_extract) {
+    let parent = to_extract.syntax().parent().and_then(ast::Expr::cast);
+    let needs_adjust = parent
+        .as_ref()
+        .map_or(false, |it| matches!(it, ast::Expr::FieldExpr(_) | ast::Expr::MethodCallExpr(_)));
+
+    let reference_modifier = match ty.filter(|_| needs_adjust) {
         Some(receiver_type) if receiver_type.is_mutable_reference() => "&mut ",
         Some(receiver_type) if receiver_type.is_reference() => "&",
         _ => "",
     };
 
-    let parent_ref_expr = to_extract.syntax().parent().and_then(ast::RefExpr::cast);
-    let var_modifier = match parent_ref_expr {
-        Some(expr) if expr.mut_token().is_some() => "mut ",
+    let var_modifier = match parent {
+        Some(ast::Expr::RefExpr(expr)) if expr.mut_token().is_some() => "mut ",
         _ => "",
     };
 
@@ -164,22 +168,6 @@ fn valid_target_expr(node: SyntaxNode) -> Option<ast::Expr> {
     }
 }
 
-fn get_receiver_type(ctx: &AssistContext<'_>, expression: &ast::Expr) -> Option<hir::Type> {
-    let receiver = get_receiver(expression.clone())?;
-    Some(ctx.sema.type_of_expr(&receiver)?.original())
-}
-
-/// In the expression `a.b.c.x()`, find `a`
-fn get_receiver(expression: ast::Expr) -> Option<ast::Expr> {
-    match expression {
-        ast::Expr::FieldExpr(field) if field.expr().is_some() => {
-            let nested_expression = &field.expr()?;
-            get_receiver(nested_expression.to_owned())
-        }
-        _ => Some(expression),
-    }
-}
-
 #[derive(Debug)]
 enum Anchor {
     Before(SyntaxNode),
@@ -944,6 +932,11 @@ struct S {
     vec: Vec<u8>
 }
 
+struct Vec<T>;
+impl<T> Vec<T> {
+    fn push(&mut self, _:usize) {}
+}
+
 fn foo(s: &mut S) {
     $0s.vec$0.push(0);
 }"#,
@@ -952,6 +945,11 @@ struct S {
     vec: Vec<u8>
 }
 
+struct Vec<T>;
+impl<T> Vec<T> {
+    fn push(&mut self, _:usize) {}
+}
+
 fn foo(s: &mut S) {
     let $0vec = &mut s.vec;
     vec.push(0);
@@ -973,6 +971,10 @@ struct X {
 struct S {
     vec: Vec<u8>
 }
+struct Vec<T>;
+impl<T> Vec<T> {
+    fn push(&mut self, _:usize) {}
+}
 
 fn foo(f: &mut Y) {
     $0f.field.field.vec$0.push(0);
@@ -987,6 +989,10 @@ struct X {
 struct S {
     vec: Vec<u8>
 }
+struct Vec<T>;
+impl<T> Vec<T> {
+    fn push(&mut self, _:usize) {}
+}
 
 fn foo(f: &mut Y) {
     let $0vec = &mut f.field.field.vec;
@@ -1123,7 +1129,7 @@ struct S {
 }
 
 fn foo(s: S) {
-    let $0x = s.sub;
+    let $0x = &s.sub;
     x.do_thing();
 }"#,
         );
@@ -1189,7 +1195,7 @@ impl X {
 
 fn foo() {
     let local = &mut S::new();
-    let $0x = &mut local.sub;
+    let $0x = &local.sub;
     x.do_thing();
 }"#,
         );
diff --git a/crates/ide-assists/src/handlers/generate_function.rs b/crates/ide-assists/src/handlers/generate_function.rs
index 0768389281c..2372fe28e19 100644
--- a/crates/ide-assists/src/handlers/generate_function.rs
+++ b/crates/ide-assists/src/handlers/generate_function.rs
@@ -1910,7 +1910,6 @@ fn bar(new: fn) ${0:-> _} {
 
     #[test]
     fn add_function_with_closure_arg() {
-        // FIXME: The argument in `bar` is wrong.
         check_assist(
             generate_function,
             r"
@@ -1925,7 +1924,7 @@ fn foo() {
     bar(closure)
 }
 
-fn bar(closure: _) {
+fn bar(closure: impl Fn(i64) -> i64) {
     ${0:todo!()}
 }
 ",
diff --git a/crates/ide-assists/src/handlers/reorder_impl_items.rs b/crates/ide-assists/src/handlers/reorder_impl_items.rs
index 208c3e109dd..af96950761e 100644
--- a/crates/ide-assists/src/handlers/reorder_impl_items.rs
+++ b/crates/ide-assists/src/handlers/reorder_impl_items.rs
@@ -21,7 +21,7 @@ use crate::{AssistContext, AssistId, AssistKind, Assists};
 // }
 //
 // struct Bar;
-// $0impl Foo for Bar {
+// $0impl Foo for Bar$0 {
 //     const B: u8 = 17;
 //     fn c() {}
 //     type A = String;
@@ -45,6 +45,16 @@ use crate::{AssistContext, AssistId, AssistKind, Assists};
 pub(crate) fn reorder_impl_items(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
     let impl_ast = ctx.find_node_at_offset::<ast::Impl>()?;
     let items = impl_ast.assoc_item_list()?;
+
+    // restrict the range
+    // if cursor is in assoc_items, abort
+    let assoc_range = items.syntax().text_range();
+    let cursor_position = ctx.offset();
+    if assoc_range.contains_inclusive(cursor_position) {
+        cov_mark::hit!(not_applicable_editing_assoc_items);
+        return None;
+    }
+
     let assoc_items = items.assoc_items().collect::<Vec<_>>();
 
     let path = impl_ast
@@ -264,9 +274,9 @@ trait Bar {
 }
 
 struct Foo;
-impl Bar for Foo {
+$0impl Bar for Foo {
     type Fooo = ();
-    type Foo = ();$0
+    type Foo = ();
 }"#,
             r#"
 trait Bar {
@@ -281,4 +291,29 @@ impl Bar for Foo {
 }"#,
         )
     }
+
+    #[test]
+    fn not_applicable_editing_assoc_items() {
+        cov_mark::check!(not_applicable_editing_assoc_items);
+        check_assist_not_applicable(
+            reorder_impl_items,
+            r#"
+trait Bar {
+    type T;
+    const C: ();
+    fn a() {}
+    fn z() {}
+    fn b() {}
+}
+struct Foo;
+impl Bar for Foo {
+    type T = ();$0
+    const C: () = ();
+    fn z() {}
+    fn a() {}
+    fn b() {}
+}
+        "#,
+        )
+    }
 }
diff --git a/crates/ide-assists/src/handlers/sort_items.rs b/crates/ide-assists/src/handlers/sort_items.rs
index a93704b3947..63e350e155f 100644
--- a/crates/ide-assists/src/handlers/sort_items.rs
+++ b/crates/ide-assists/src/handlers/sort_items.rs
@@ -116,9 +116,11 @@ trait AddRewrite {
         new: Vec<T>,
         target: TextRange,
     ) -> Option<()>;
+    fn yeet() {}
 }
 
 impl AddRewrite for Assists {
+    fn yeet() {}
     fn add_rewrite<T: AstNode>(
         &mut self,
         label: &str,
diff --git a/crates/ide-assists/src/lib.rs b/crates/ide-assists/src/lib.rs
index 8b07e29a587..fc03903e593 100644
--- a/crates/ide-assists/src/lib.rs
+++ b/crates/ide-assists/src/lib.rs
@@ -122,6 +122,7 @@ mod handlers {
     mod convert_iter_for_each_to_for;
     mod convert_let_else_to_match;
     mod convert_match_to_let_else;
+    mod convert_nested_function_to_closure;
     mod convert_tuple_struct_to_named_struct;
     mod convert_named_struct_to_tuple_struct;
     mod convert_to_guarded_return;
@@ -228,8 +229,9 @@ mod handlers {
             convert_iter_for_each_to_for::convert_iter_for_each_to_for,
             convert_iter_for_each_to_for::convert_for_loop_with_for_each,
             convert_let_else_to_match::convert_let_else_to_match,
-            convert_named_struct_to_tuple_struct::convert_named_struct_to_tuple_struct,
             convert_match_to_let_else::convert_match_to_let_else,
+            convert_named_struct_to_tuple_struct::convert_named_struct_to_tuple_struct,
+            convert_nested_function_to_closure::convert_nested_function_to_closure,
             convert_to_guarded_return::convert_to_guarded_return,
             convert_tuple_struct_to_named_struct::convert_tuple_struct_to_named_struct,
             convert_two_arm_bool_match_to_matches_macro::convert_two_arm_bool_match_to_matches_macro,
diff --git a/crates/ide-assists/src/tests/generated.rs b/crates/ide-assists/src/tests/generated.rs
index e2c71d2565c..0096254ecb7 100644
--- a/crates/ide-assists/src/tests/generated.rs
+++ b/crates/ide-assists/src/tests/generated.rs
@@ -495,6 +495,31 @@ impl Point {
 }
 
 #[test]
+fn doctest_convert_nested_function_to_closure() {
+    check_doc_test(
+        "convert_nested_function_to_closure",
+        r#####"
+fn main() {
+    fn fo$0o(label: &str, number: u64) {
+        println!("{}: {}", label, number);
+    }
+
+    foo("Bar", 100);
+}
+"#####,
+        r#####"
+fn main() {
+    let foo = |label: &str, number: u64| {
+        println!("{}: {}", label, number);
+    };
+
+    foo("Bar", 100);
+}
+"#####,
+    )
+}
+
+#[test]
 fn doctest_convert_to_guarded_return() {
     check_doc_test(
         "convert_to_guarded_return",
@@ -2116,7 +2141,7 @@ trait Foo {
 }
 
 struct Bar;
-$0impl Foo for Bar {
+$0impl Foo for Bar$0 {
     const B: u8 = 17;
     fn c() {}
     type A = String;
diff --git a/crates/ide-assists/src/tests/sourcegen.rs b/crates/ide-assists/src/tests/sourcegen.rs
index b4f50c7fb26..3da90e9052f 100644
--- a/crates/ide-assists/src/tests/sourcegen.rs
+++ b/crates/ide-assists/src/tests/sourcegen.rs
@@ -90,8 +90,6 @@ impl Assist {
             let comment_blocks = sourcegen::CommentBlock::extract("Assist", &text);
 
             for block in comment_blocks {
-                // FIXME: doesn't support blank lines yet, need to tweak
-                // `extract_comment_blocks` for that.
                 let id = block.id;
                 assert!(
                     id.chars().all(|it| it.is_ascii_lowercase() || it == '_'),
diff --git a/crates/ide-completion/src/completions.rs b/crates/ide-completion/src/completions.rs
index b94bbc40652..bc7a94cd337 100644
--- a/crates/ide-completion/src/completions.rs
+++ b/crates/ide-completion/src/completions.rs
@@ -23,7 +23,7 @@ pub(crate) mod env_vars;
 
 use std::iter;
 
-use hir::{known, ScopeDef, Variant};
+use hir::{known, HasAttrs, ScopeDef, Variant};
 use ide_db::{imports::import_assets::LocatedImport, SymbolKind};
 use syntax::ast;
 
@@ -173,6 +173,9 @@ impl Completions {
         resolution: hir::ScopeDef,
         doc_aliases: Vec<syntax::SmolStr>,
     ) {
+        if !ctx.check_stability(resolution.attrs(ctx.db).as_deref()) {
+            return;
+        }
         let is_private_editable = match ctx.def_is_visible(&resolution) {
             Visible::Yes => false,
             Visible::Editable => true,
@@ -198,6 +201,9 @@ impl Completions {
         local_name: hir::Name,
         resolution: hir::ScopeDef,
     ) {
+        if !ctx.check_stability(resolution.attrs(ctx.db).as_deref()) {
+            return;
+        }
         let is_private_editable = match ctx.def_is_visible(&resolution) {
             Visible::Yes => false,
             Visible::Editable => true,
@@ -220,6 +226,9 @@ impl Completions {
         path_ctx: &PathCompletionCtx,
         e: hir::Enum,
     ) {
+        if !ctx.check_stability(Some(&e.attrs(ctx.db))) {
+            return;
+        }
         e.variants(ctx.db)
             .into_iter()
             .for_each(|variant| self.add_enum_variant(ctx, path_ctx, variant, None));
@@ -233,6 +242,9 @@ impl Completions {
         local_name: hir::Name,
         doc_aliases: Vec<syntax::SmolStr>,
     ) {
+        if !ctx.check_stability(Some(&module.attrs(ctx.db))) {
+            return;
+        }
         self.add_path_resolution(
             ctx,
             path_ctx,
@@ -249,6 +261,9 @@ impl Completions {
         mac: hir::Macro,
         local_name: hir::Name,
     ) {
+        if !ctx.check_stability(Some(&mac.attrs(ctx.db))) {
+            return;
+        }
         let is_private_editable = match ctx.is_visible(&mac) {
             Visible::Yes => false,
             Visible::Editable => true,
@@ -272,6 +287,9 @@ impl Completions {
         func: hir::Function,
         local_name: Option<hir::Name>,
     ) {
+        if !ctx.check_stability(Some(&func.attrs(ctx.db))) {
+            return;
+        }
         let is_private_editable = match ctx.is_visible(&func) {
             Visible::Yes => false,
             Visible::Editable => true,
@@ -296,6 +314,9 @@ impl Completions {
         receiver: Option<hir::Name>,
         local_name: Option<hir::Name>,
     ) {
+        if !ctx.check_stability(Some(&func.attrs(ctx.db))) {
+            return;
+        }
         let is_private_editable = match ctx.is_visible(&func) {
             Visible::Yes => false,
             Visible::Editable => true,
@@ -320,6 +341,9 @@ impl Completions {
         func: hir::Function,
         import: LocatedImport,
     ) {
+        if !ctx.check_stability(Some(&func.attrs(ctx.db))) {
+            return;
+        }
         let is_private_editable = match ctx.is_visible(&func) {
             Visible::Yes => false,
             Visible::Editable => true,
@@ -340,6 +364,9 @@ impl Completions {
     }
 
     pub(crate) fn add_const(&mut self, ctx: &CompletionContext<'_>, konst: hir::Const) {
+        if !ctx.check_stability(Some(&konst.attrs(ctx.db))) {
+            return;
+        }
         let is_private_editable = match ctx.is_visible(&konst) {
             Visible::Yes => false,
             Visible::Editable => true,
@@ -356,6 +383,9 @@ impl Completions {
         ctx: &CompletionContext<'_>,
         type_alias: hir::TypeAlias,
     ) {
+        if !ctx.check_stability(Some(&type_alias.attrs(ctx.db))) {
+            return;
+        }
         let is_private_editable = match ctx.is_visible(&type_alias) {
             Visible::Yes => false,
             Visible::Editable => true,
@@ -372,6 +402,9 @@ impl Completions {
         ctx: &CompletionContext<'_>,
         type_alias: hir::TypeAlias,
     ) {
+        if !ctx.check_stability(Some(&type_alias.attrs(ctx.db))) {
+            return;
+        }
         self.add_opt(render_type_alias_with_eq(RenderContext::new(ctx), type_alias));
     }
 
@@ -382,6 +415,9 @@ impl Completions {
         variant: hir::Variant,
         path: hir::ModPath,
     ) {
+        if !ctx.check_stability(Some(&variant.attrs(ctx.db))) {
+            return;
+        }
         if let Some(builder) =
             render_variant_lit(RenderContext::new(ctx), path_ctx, None, variant, Some(path))
         {
@@ -396,6 +432,9 @@ impl Completions {
         variant: hir::Variant,
         local_name: Option<hir::Name>,
     ) {
+        if !ctx.check_stability(Some(&variant.attrs(ctx.db))) {
+            return;
+        }
         if let PathCompletionCtx { kind: PathKind::Pat { pat_ctx }, .. } = path_ctx {
             cov_mark::hit!(enum_variant_pattern_path);
             self.add_variant_pat(ctx, pat_ctx, Some(path_ctx), variant, local_name);
@@ -417,6 +456,9 @@ impl Completions {
         field: hir::Field,
         ty: &hir::Type,
     ) {
+        if !ctx.check_stability(Some(&field.attrs(ctx.db))) {
+            return;
+        }
         let is_private_editable = match ctx.is_visible(&field) {
             Visible::Yes => false,
             Visible::Editable => true,
@@ -441,6 +483,9 @@ impl Completions {
         path: Option<hir::ModPath>,
         local_name: Option<hir::Name>,
     ) {
+        if !ctx.check_stability(Some(&strukt.attrs(ctx.db))) {
+            return;
+        }
         if let Some(builder) =
             render_struct_literal(RenderContext::new(ctx), path_ctx, strukt, path, local_name)
         {
@@ -455,6 +500,9 @@ impl Completions {
         path: Option<hir::ModPath>,
         local_name: Option<hir::Name>,
     ) {
+        if !ctx.check_stability(Some(&un.attrs(ctx.db))) {
+            return;
+        }
         let item = render_union_literal(RenderContext::new(ctx), un, path, local_name);
         self.add_opt(item);
     }
@@ -466,6 +514,8 @@ impl Completions {
         field: usize,
         ty: &hir::Type,
     ) {
+        // Only used for (unnamed) tuples, whose all fields *are* stable. No need to check
+        // stability here.
         let item = render_tuple_field(RenderContext::new(ctx), receiver, field, ty);
         self.add(item);
     }
@@ -487,6 +537,9 @@ impl Completions {
         variant: hir::Variant,
         local_name: Option<hir::Name>,
     ) {
+        if !ctx.check_stability(Some(&variant.attrs(ctx.db))) {
+            return;
+        }
         self.add_opt(render_variant_pat(
             RenderContext::new(ctx),
             pattern_ctx,
@@ -504,6 +557,9 @@ impl Completions {
         variant: hir::Variant,
         path: hir::ModPath,
     ) {
+        if !ctx.check_stability(Some(&variant.attrs(ctx.db))) {
+            return;
+        }
         let path = Some(&path);
         self.add_opt(render_variant_pat(
             RenderContext::new(ctx),
@@ -522,6 +578,9 @@ impl Completions {
         strukt: hir::Struct,
         local_name: Option<hir::Name>,
     ) {
+        if !ctx.check_stability(Some(&strukt.attrs(ctx.db))) {
+            return;
+        }
         self.add_opt(render_struct_pat(RenderContext::new(ctx), pattern_ctx, strukt, local_name));
     }
 }
diff --git a/crates/ide-completion/src/completions/dot.rs b/crates/ide-completion/src/completions/dot.rs
index 77246379e7b..ba766340161 100644
--- a/crates/ide-completion/src/completions/dot.rs
+++ b/crates/ide-completion/src/completions/dot.rs
@@ -173,6 +173,43 @@ fn foo(s: S) { s.$0 }
     }
 
     #[test]
+    fn no_unstable_method_on_stable() {
+        check(
+            r#"
+//- /main.rs crate:main deps:std
+fn foo(s: std::S) { s.$0 }
+//- /std.rs crate:std
+pub struct S;
+impl S {
+    #[unstable]
+    pub fn bar(&self) {}
+}
+"#,
+            expect![""],
+        );
+    }
+
+    #[test]
+    fn unstable_method_on_nightly() {
+        check(
+            r#"
+//- toolchain:nightly
+//- /main.rs crate:main deps:std
+fn foo(s: std::S) { s.$0 }
+//- /std.rs crate:std
+pub struct S;
+impl S {
+    #[unstable]
+    pub fn bar(&self) {}
+}
+"#,
+            expect![[r#"
+                me bar() fn(&self)
+            "#]],
+        );
+    }
+
+    #[test]
     fn test_struct_field_completion_self() {
         check(
             r#"
diff --git a/crates/ide-completion/src/completions/env_vars.rs b/crates/ide-completion/src/completions/env_vars.rs
index 1002be21131..c525e0c8076 100644
--- a/crates/ide-completion/src/completions/env_vars.rs
+++ b/crates/ide-completion/src/completions/env_vars.rs
@@ -37,9 +37,9 @@ pub(crate) fn complete_cargo_env_vars(
     guard_env_macro(expanded, &ctx.sema)?;
     let range = expanded.text_range_between_quotes()?;
 
-    CARGO_DEFINED_VARS.iter().for_each(|(var, detail)| {
+    CARGO_DEFINED_VARS.into_iter().for_each(|&(var, detail)| {
         let mut item = CompletionItem::new(CompletionItemKind::Keyword, range, var);
-        item.detail(*detail);
+        item.detail(detail);
         item.add_to(acc);
     });
 
diff --git a/crates/ide-completion/src/completions/flyimport.rs b/crates/ide-completion/src/completions/flyimport.rs
index 6d023e12bca..1edf636aea0 100644
--- a/crates/ide-completion/src/completions/flyimport.rs
+++ b/crates/ide-completion/src/completions/flyimport.rs
@@ -262,8 +262,10 @@ fn import_on_the_fly(
         .into_iter()
         .filter(ns_filter)
         .filter(|import| {
+            let original_item = &import.original_item;
             !ctx.is_item_hidden(&import.item_to_import)
-                && !ctx.is_item_hidden(&import.original_item)
+                && !ctx.is_item_hidden(original_item)
+                && ctx.check_stability(original_item.attrs(ctx.db).as_deref())
         })
         .sorted_by_key(|located_import| {
             compute_fuzzy_completion_order_key(&located_import.import_path, &user_input_lowercased)
@@ -302,8 +304,10 @@ fn import_on_the_fly_pat_(
         .into_iter()
         .filter(ns_filter)
         .filter(|import| {
+            let original_item = &import.original_item;
             !ctx.is_item_hidden(&import.item_to_import)
-                && !ctx.is_item_hidden(&import.original_item)
+                && !ctx.is_item_hidden(original_item)
+                && ctx.check_stability(original_item.attrs(ctx.db).as_deref())
         })
         .sorted_by_key(|located_import| {
             compute_fuzzy_completion_order_key(&located_import.import_path, &user_input_lowercased)
diff --git a/crates/ide-completion/src/completions/item_list/trait_impl.rs b/crates/ide-completion/src/completions/item_list/trait_impl.rs
index 889d90095fa..e82908a361e 100644
--- a/crates/ide-completion/src/completions/item_list/trait_impl.rs
+++ b/crates/ide-completion/src/completions/item_list/trait_impl.rs
@@ -150,21 +150,24 @@ fn complete_trait_impl(
     impl_def: &ast::Impl,
 ) {
     if let Some(hir_impl) = ctx.sema.to_def(impl_def) {
-        get_missing_assoc_items(&ctx.sema, impl_def).into_iter().for_each(|item| {
-            use self::ImplCompletionKind::*;
-            match (item, kind) {
-                (hir::AssocItem::Function(func), All | Fn) => {
-                    add_function_impl(acc, ctx, replacement_range, func, hir_impl)
+        get_missing_assoc_items(&ctx.sema, impl_def)
+            .into_iter()
+            .filter(|item| ctx.check_stability(Some(&item.attrs(ctx.db))))
+            .for_each(|item| {
+                use self::ImplCompletionKind::*;
+                match (item, kind) {
+                    (hir::AssocItem::Function(func), All | Fn) => {
+                        add_function_impl(acc, ctx, replacement_range, func, hir_impl)
+                    }
+                    (hir::AssocItem::TypeAlias(type_alias), All | TypeAlias) => {
+                        add_type_alias_impl(acc, ctx, replacement_range, type_alias, hir_impl)
+                    }
+                    (hir::AssocItem::Const(const_), All | Const) => {
+                        add_const_impl(acc, ctx, replacement_range, const_, hir_impl)
+                    }
+                    _ => {}
                 }
-                (hir::AssocItem::TypeAlias(type_alias), All | TypeAlias) => {
-                    add_type_alias_impl(acc, ctx, replacement_range, type_alias, hir_impl)
-                }
-                (hir::AssocItem::Const(const_), All | Const) => {
-                    add_const_impl(acc, ctx, replacement_range, const_, hir_impl)
-                }
-                _ => {}
-            }
-        });
+            });
     }
 }
 
diff --git a/crates/ide-completion/src/completions/use_.rs b/crates/ide-completion/src/completions/use_.rs
index 546a1f4c499..45be4fb2054 100644
--- a/crates/ide-completion/src/completions/use_.rs
+++ b/crates/ide-completion/src/completions/use_.rs
@@ -52,6 +52,9 @@ pub(crate) fn complete_use_path(
                         )
                     };
                     for (name, def) in module_scope {
+                        if !ctx.check_stability(def.attrs(ctx.db).as_deref()) {
+                            continue;
+                        }
                         let is_name_already_imported = name
                             .as_text()
                             .map_or(false, |text| already_imported_names.contains(text.as_str()));
diff --git a/crates/ide-completion/src/context.rs b/crates/ide-completion/src/context.rs
index 6089f18ac6a..7b145f3c14e 100644
--- a/crates/ide-completion/src/context.rs
+++ b/crates/ide-completion/src/context.rs
@@ -367,6 +367,8 @@ pub(crate) struct CompletionContext<'a> {
     pub(super) krate: hir::Crate,
     /// The module of the `scope`.
     pub(super) module: hir::Module,
+    /// Whether nightly toolchain is used. Cached since this is looked up a lot.
+    is_nightly: bool,
 
     /// The expected name of what we are completing.
     /// This is usually the parameter name of the function argument we are completing.
@@ -386,7 +388,7 @@ pub(crate) struct CompletionContext<'a> {
     pub(super) depth_from_crate_root: usize,
 }
 
-impl<'a> CompletionContext<'a> {
+impl CompletionContext<'_> {
     /// The range of the identifier that is being completed.
     pub(crate) fn source_range(&self) -> TextRange {
         let kind = self.original_token.kind();
@@ -459,6 +461,12 @@ impl<'a> CompletionContext<'a> {
         }
     }
 
+    /// Checks whether this item should be listed in regards to stability. Returns `true` if we should.
+    pub(crate) fn check_stability(&self, attrs: Option<&hir::Attrs>) -> bool {
+        let Some(attrs) = attrs else { return true; };
+        !attrs.is_unstable() || self.is_nightly
+    }
+
     /// Whether the given trait is an operator trait or not.
     pub(crate) fn is_ops_trait(&self, trait_: hir::Trait) -> bool {
         match trait_.attrs(self.db).lang() {
@@ -632,6 +640,11 @@ impl<'a> CompletionContext<'a> {
         let krate = scope.krate();
         let module = scope.module();
 
+        let toolchain = db.crate_graph()[krate.into()].channel;
+        // `toolchain == None` means we're in some detached files. Since we have no information on
+        // the toolchain being used, let's just allow unstable items to be listed.
+        let is_nightly = matches!(toolchain, Some(base_db::ReleaseChannel::Nightly) | None);
+
         let mut locals = FxHashMap::default();
         scope.process_all_names(&mut |name, scope| {
             if let ScopeDef::Local(local) = scope {
@@ -651,6 +664,7 @@ impl<'a> CompletionContext<'a> {
             token,
             krate,
             module,
+            is_nightly,
             expected_name,
             expected_type,
             qualifier_ctx,
diff --git a/crates/ide-completion/src/tests.rs b/crates/ide-completion/src/tests.rs
index 05e6aaf09d9..79c1f98f3bd 100644
--- a/crates/ide-completion/src/tests.rs
+++ b/crates/ide-completion/src/tests.rs
@@ -23,6 +23,7 @@ mod type_pos;
 mod use_tree;
 mod visibility;
 
+use expect_test::Expect;
 use hir::PrefixKind;
 use ide_db::{
     base_db::{fixture::ChangeFixture, FileLoader, FilePosition},
@@ -215,6 +216,11 @@ pub(crate) fn check_edit_with_config(
     assert_eq_text!(&ra_fixture_after, &actual)
 }
 
+fn check_empty(ra_fixture: &str, expect: Expect) {
+    let actual = completion_list(ra_fixture);
+    expect.assert_eq(&actual);
+}
+
 pub(crate) fn get_all_items(
     config: CompletionConfig,
     code: &str,
diff --git a/crates/ide-completion/src/tests/expression.rs b/crates/ide-completion/src/tests/expression.rs
index c1c6a689eb1..36465be23d8 100644
--- a/crates/ide-completion/src/tests/expression.rs
+++ b/crates/ide-completion/src/tests/expression.rs
@@ -1,18 +1,13 @@
 //! Completion tests for expressions.
 use expect_test::{expect, Expect};
 
-use crate::tests::{check_edit, completion_list, BASE_ITEMS_FIXTURE};
+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)
 }
 
-fn check_empty(ra_fixture: &str, expect: Expect) {
-    let actual = completion_list(ra_fixture);
-    expect.assert_eq(&actual);
-}
-
 #[test]
 fn complete_literal_struct_with_a_private_field() {
     // `FooDesc.bar` is private, the completion should not be triggered.
@@ -997,3 +992,105 @@ fn foo() { if foo {} el$0 { let x = 92; } }
         "#]],
     );
 }
+
+#[test]
+fn expr_no_unstable_item_on_stable() {
+    check_empty(
+        r#"
+//- /main.rs crate:main deps:std
+use std::*;
+fn main() {
+    $0
+}
+//- /std.rs crate:std
+#[unstable]
+pub struct UnstableThisShouldNotBeListed;
+"#,
+        expect![[r#"
+            fn main()      fn()
+            md std
+            bt u32
+            kw const
+            kw crate::
+            kw enum
+            kw extern
+            kw false
+            kw fn
+            kw for
+            kw if
+            kw if let
+            kw impl
+            kw let
+            kw loop
+            kw match
+            kw mod
+            kw return
+            kw self::
+            kw static
+            kw struct
+            kw trait
+            kw true
+            kw type
+            kw union
+            kw unsafe
+            kw use
+            kw while
+            kw while let
+            sn macro_rules
+            sn pd
+            sn ppd
+        "#]],
+    );
+}
+
+#[test]
+fn expr_unstable_item_on_nightly() {
+    check_empty(
+        r#"
+//- toolchain:nightly
+//- /main.rs crate:main deps:std
+use std::*;
+fn main() {
+    $0
+}
+//- /std.rs crate:std
+#[unstable]
+pub struct UnstableButWeAreOnNightlyAnyway;
+"#,
+        expect![[r#"
+            fn main()                 fn()
+            md std
+            st UnstableButWeAreOnNightlyAnyway
+            bt u32
+            kw const
+            kw crate::
+            kw enum
+            kw extern
+            kw false
+            kw fn
+            kw for
+            kw if
+            kw if let
+            kw impl
+            kw let
+            kw loop
+            kw match
+            kw mod
+            kw return
+            kw self::
+            kw static
+            kw struct
+            kw trait
+            kw true
+            kw type
+            kw union
+            kw unsafe
+            kw use
+            kw while
+            kw while let
+            sn macro_rules
+            sn pd
+            sn ppd
+        "#]],
+    );
+}
diff --git a/crates/ide-completion/src/tests/flyimport.rs b/crates/ide-completion/src/tests/flyimport.rs
index 0b485eb776d..d727320b514 100644
--- a/crates/ide-completion/src/tests/flyimport.rs
+++ b/crates/ide-completion/src/tests/flyimport.rs
@@ -1108,6 +1108,41 @@ fn function() {
 }
 
 #[test]
+fn flyimport_pattern_no_unstable_item_on_stable() {
+    check(
+        r#"
+//- /main.rs crate:main deps:std
+fn function() {
+    let foo$0
+}
+//- /std.rs crate:std
+#[unstable]
+pub struct FooStruct {}
+"#,
+        expect![""],
+    );
+}
+
+#[test]
+fn flyimport_pattern_unstable_item_on_nightly() {
+    check(
+        r#"
+//- toolchain:nightly
+//- /main.rs crate:main deps:std
+fn function() {
+    let foo$0
+}
+//- /std.rs crate:std
+#[unstable]
+pub struct FooStruct {}
+"#,
+        expect![[r#"
+            st FooStruct (use std::FooStruct)
+        "#]],
+    );
+}
+
+#[test]
 fn flyimport_item_name() {
     check(
         r#"
diff --git a/crates/ide-completion/src/tests/item_list.rs b/crates/ide-completion/src/tests/item_list.rs
index 9fc731bb11d..2b5b4dd773c 100644
--- a/crates/ide-completion/src/tests/item_list.rs
+++ b/crates/ide-completion/src/tests/item_list.rs
@@ -1,7 +1,7 @@
 //! Completion tests for item list position.
 use expect_test::{expect, Expect};
 
-use crate::tests::{check_edit, completion_list, BASE_ITEMS_FIXTURE};
+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}"));
@@ -298,6 +298,58 @@ impl Test for () {
 }
 
 #[test]
+fn in_trait_impl_no_unstable_item_on_stable() {
+    check_empty(
+        r#"
+trait Test {
+    #[unstable]
+    type Type;
+    #[unstable]
+    const CONST: ();
+    #[unstable]
+    fn function();
+}
+
+impl Test for () {
+    $0
+}
+"#,
+        expect![[r#"
+            kw crate::
+            kw self::
+        "#]],
+    );
+}
+
+#[test]
+fn in_trait_impl_unstable_item_on_nightly() {
+    check_empty(
+        r#"
+//- toolchain:nightly
+trait Test {
+    #[unstable]
+    type Type;
+    #[unstable]
+    const CONST: ();
+    #[unstable]
+    fn function();
+}
+
+impl Test for () {
+    $0
+}
+"#,
+        expect![[r#"
+            ct const CONST: () =
+            fn fn function()
+            ta type Type =
+            kw crate::
+            kw self::
+        "#]],
+    );
+}
+
+#[test]
 fn after_unit_struct() {
     check(
         r#"struct S; f$0"#,
diff --git a/crates/ide-completion/src/tests/pattern.rs b/crates/ide-completion/src/tests/pattern.rs
index c0e485c36fd..8af6cce98f6 100644
--- a/crates/ide-completion/src/tests/pattern.rs
+++ b/crates/ide-completion/src/tests/pattern.rs
@@ -1,12 +1,7 @@
 //! Completion tests for pattern position.
 use expect_test::{expect, Expect};
 
-use crate::tests::{check_edit, completion_list, BASE_ITEMS_FIXTURE};
-
-fn check_empty(ra_fixture: &str, expect: Expect) {
-    let actual = completion_list(ra_fixture);
-    expect.assert_eq(&actual)
-}
+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}"));
@@ -742,3 +737,56 @@ fn f(x: EnumAlias<u8>) {
         "#]],
     );
 }
+
+#[test]
+fn pat_no_unstable_item_on_stable() {
+    check_empty(
+        r#"
+//- /main.rs crate:main deps:std
+use std::*;
+fn foo() {
+    let a$0
+}
+//- /std.rs crate:std
+#[unstable]
+pub struct S;
+#[unstable]
+pub enum Enum {
+    Variant
+}
+"#,
+        expect![[r#"
+            md std
+            kw mut
+            kw ref
+        "#]],
+    );
+}
+
+#[test]
+fn pat_unstable_item_on_nightly() {
+    check_empty(
+        r#"
+//- toolchain:nightly
+//- /main.rs crate:main deps:std
+use std::*;
+fn foo() {
+    let a$0
+}
+//- /std.rs crate:std
+#[unstable]
+pub struct S;
+#[unstable]
+pub enum Enum {
+    Variant
+}
+"#,
+        expect![[r#"
+            en Enum
+            md std
+            st S
+            kw mut
+            kw ref
+        "#]],
+    );
+}
diff --git a/crates/ide-completion/src/tests/predicate.rs b/crates/ide-completion/src/tests/predicate.rs
index 2656a4d545e..789ad66345b 100644
--- a/crates/ide-completion/src/tests/predicate.rs
+++ b/crates/ide-completion/src/tests/predicate.rs
@@ -1,7 +1,7 @@
 //! Completion tests for predicates and bounds.
 use expect_test::{expect, Expect};
 
-use crate::tests::{completion_list, BASE_ITEMS_FIXTURE};
+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}"));
@@ -129,3 +129,43 @@ impl Record {
         "#]],
     );
 }
+
+#[test]
+fn pred_no_unstable_item_on_stable() {
+    check_empty(
+        r#"
+//- /main.rs crate:main deps:std
+use std::*;
+struct Foo<T> where T: $0 {}
+//- /std.rs crate:std
+#[unstable]
+pub trait Trait {}
+"#,
+        expect![[r#"
+            md std
+            kw crate::
+            kw self::
+        "#]],
+    );
+}
+
+#[test]
+fn pred_unstable_item_on_nightly() {
+    check_empty(
+        r#"
+//- toolchain:nightly
+//- /main.rs crate:main deps:std
+use std::*;
+struct Foo<T> where T: $0 {}
+//- /std.rs crate:std
+#[unstable]
+pub trait Trait {}
+"#,
+        expect![[r#"
+            md std
+            tt Trait
+            kw crate::
+            kw self::
+        "#]],
+    );
+}
diff --git a/crates/ide-completion/src/tests/type_pos.rs b/crates/ide-completion/src/tests/type_pos.rs
index c3f4fb4d181..8cb1ff4a125 100644
--- a/crates/ide-completion/src/tests/type_pos.rs
+++ b/crates/ide-completion/src/tests/type_pos.rs
@@ -1,7 +1,7 @@
 //! Completion tests for type position.
 use expect_test::{expect, Expect};
 
-use crate::tests::{completion_list, BASE_ITEMS_FIXTURE};
+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}"));
@@ -669,3 +669,53 @@ fn f(t: impl MyTrait<Item1 = u8, Item2 = $0
         "#]],
     );
 }
+
+#[test]
+fn type_pos_no_unstable_type_on_stable() {
+    check_empty(
+        r#"
+//- /main.rs crate:main deps:std
+use std::*;
+struct Foo {
+    f: $0
+}
+//- /std.rs crate:std
+#[unstable]
+pub struct S;
+"#,
+        expect![[r#"
+            md std
+            sp Self
+            st Foo
+            bt u32
+            kw crate::
+            kw self::
+        "#]],
+    )
+}
+
+#[test]
+fn type_pos_unstable_type_on_nightly() {
+    check_empty(
+        r#"
+//- toolchain:nightly
+//- /main.rs crate:main deps:std
+use std::*;
+struct Foo {
+    f: $0
+}
+//- /std.rs crate:std
+#[unstable]
+pub struct S;
+"#,
+        expect![[r#"
+            md std
+            sp Self
+            st Foo
+            st S
+            bt u32
+            kw crate::
+            kw self::
+        "#]],
+    )
+}
diff --git a/crates/ide-completion/src/tests/use_tree.rs b/crates/ide-completion/src/tests/use_tree.rs
index 037d7dce52e..ba2e047999e 100644
--- a/crates/ide-completion/src/tests/use_tree.rs
+++ b/crates/ide-completion/src/tests/use_tree.rs
@@ -382,3 +382,51 @@ use self::foo::impl$0
         "#]],
     );
 }
+
+#[test]
+fn use_tree_no_unstable_items_on_stable() {
+    check(
+        r#"
+//- toolchain:stable
+//- /lib.rs crate:main deps:std
+use std::$0
+//- /std.rs crate:std
+#[unstable]
+pub mod simd {}
+#[unstable]
+pub struct S;
+#[unstable]
+pub fn foo() {}
+#[unstable]
+#[macro_export]
+marco_rules! m { () => {} }
+"#,
+        expect![""],
+    );
+}
+
+#[test]
+fn use_tree_unstable_items_on_nightly() {
+    check(
+        r#"
+//- toolchain:nightly
+//- /lib.rs crate:main deps:std
+use std::$0
+//- /std.rs crate:std
+#[unstable]
+pub mod simd {}
+#[unstable]
+pub struct S;
+#[unstable]
+pub fn foo() {}
+#[unstable]
+#[macro_export]
+marco_rules! m { () => {} }
+"#,
+        expect![[r#"
+            fn foo  fn()
+            md simd
+            st S
+        "#]],
+    );
+}
diff --git a/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs b/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs
index 114face2dca..89aa437d75d 100644
--- a/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs
+++ b/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs
@@ -31,12 +31,8 @@ mod tests {
 fn foo() {
     break;
   //^^^^^ error: break outside of loop
-    break 'a;
-  //^^^^^^^^ error: break outside of loop
     continue;
   //^^^^^^^^ error: continue outside of loop
-    continue 'a;
-  //^^^^^^^^^^^ error: continue outside of loop
 }
 "#,
         );
@@ -51,12 +47,8 @@ fn foo() {
         async {
                 break;
               //^^^^^ error: break outside of loop
-                break 'a;
-              //^^^^^^^^ error: break outside of loop
                 continue;
               //^^^^^^^^ error: continue outside of loop
-                continue 'a;
-              //^^^^^^^^^^^ error: continue outside of loop
         };
     }
 }
@@ -73,12 +65,8 @@ fn foo() {
         || {
                 break;
               //^^^^^ error: break outside of loop
-                break 'a;
-              //^^^^^^^^ error: break outside of loop
                 continue;
               //^^^^^^^^ error: continue outside of loop
-                continue 'a;
-              //^^^^^^^^^^^ error: continue outside of loop
         };
     }
 }
@@ -94,9 +82,7 @@ fn foo() {
     'a: loop {
         {
             break;
-            break 'a;
             continue;
-            continue 'a;
         }
     }
 }
@@ -112,9 +98,7 @@ fn foo() {
     'a: loop {
         try {
                 break;
-                break 'a;
                 continue;
-                continue 'a;
         };
     }
 }
@@ -130,11 +114,8 @@ fn foo() {
     'a: {
         break;
       //^^^^^ error: break outside of loop
-        break 'a;
         continue;
       //^^^^^^^^ error: continue outside of loop
-        continue 'a;
-      //^^^^^^^^^^^ error: continue outside of loop
     }
 }
 "#,
diff --git a/crates/ide-diagnostics/src/handlers/mutability_errors.rs b/crates/ide-diagnostics/src/handlers/mutability_errors.rs
index 564b756402d..8c4ca23e06e 100644
--- a/crates/ide-diagnostics/src/handlers/mutability_errors.rs
+++ b/crates/ide-diagnostics/src/handlers/mutability_errors.rs
@@ -772,6 +772,88 @@ fn fn_once(mut x: impl FnOnce(u8) -> u8) -> u8 {
     }
 
     #[test]
+    fn closure() {
+        // FIXME: Diagnositc spans are too large
+        check_diagnostics(
+            r#"
+        //- minicore: copy, fn
+        struct X;
+
+        impl X {
+            fn mutate(&mut self) {}
+        }
+
+        fn f() {
+            let x = 5;
+            let closure1 = || { x = 2; };
+                         //^^^^^^^^^^^^^ 💡 error: cannot mutate immutable variable `x`
+            let _ = closure1();
+                  //^^^^^^^^ 💡 error: cannot mutate immutable variable `closure1`
+            let closure2 = || { x = x; };
+                         //^^^^^^^^^^^^^ 💡 error: cannot mutate immutable variable `x`
+            let closure3 = || {
+                let x = 2;
+                x = 5;
+              //^^^^^ 💡 error: cannot mutate immutable variable `x`
+                x
+            };
+            let x = X;
+            let closure4 = || { x.mutate(); };
+                         //^^^^^^^^^^^^^^^^^^ 💡 error: cannot mutate immutable variable `x`
+        }
+                    "#,
+        );
+        check_diagnostics(
+            r#"
+        //- minicore: copy, fn
+        fn f() {
+            let mut x = 5;
+              //^^^^^ 💡 weak: variable does not need to be mutable
+            let mut y = 2;
+            y = 7;
+            let closure = || {
+                let mut z = 8;
+                z = 3;
+                let mut k = z;
+                  //^^^^^ 💡 weak: variable does not need to be mutable
+            };
+        }
+                    "#,
+        );
+        check_diagnostics(
+            r#"
+//- minicore: copy, fn
+fn f() {
+    let closure = || {
+        || {
+            || {
+                let x = 2;
+                || { || { x = 5; } }
+              //^^^^^^^^^^^^^^^^^^^^ 💡 error: cannot mutate immutable variable `x`
+            }
+        }
+    };
+}
+            "#,
+        );
+        check_diagnostics(
+            r#"
+//- minicore: copy, fn
+fn f() {
+    struct X;
+    let mut x = X;
+      //^^^^^ 💡 weak: variable does not need to be mutable
+    let c1 = || x;
+    let mut x = X;
+    let c2 = || { x = X; x };
+    let mut x = X;
+    let c2 = move || { x = X; };
+}
+            "#,
+        );
+    }
+
+    #[test]
     fn respect_allow_unused_mut() {
         // FIXME: respect
         check_diagnostics(
diff --git a/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs b/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs
index 9b1c65983e6..738339cfa6b 100644
--- a/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs
+++ b/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs
@@ -115,7 +115,7 @@ fn foo() {
             r#"
 //- minicore: iterators
 fn foo() {
-    let m = core::iter::repeat(())
+    let mut m = core::iter::repeat(())
         .filter_map(|()| Some(92));
     let n = m.next();
 }
diff --git a/crates/ide-diagnostics/src/handlers/type_mismatch.rs b/crates/ide-diagnostics/src/handlers/type_mismatch.rs
index 4abc25a28fb..c5fa1cb027e 100644
--- a/crates/ide-diagnostics/src/handlers/type_mismatch.rs
+++ b/crates/ide-diagnostics/src/handlers/type_mismatch.rs
@@ -1,5 +1,5 @@
 use either::Either;
-use hir::{db::ExpandDatabase, HirDisplay, InFile, Type};
+use hir::{db::ExpandDatabase, ClosureStyle, HirDisplay, InFile, Type};
 use ide_db::{famous_defs::FamousDefs, source_change::SourceChange};
 use syntax::{
     ast::{self, BlockExpr, ExprStmt},
@@ -32,8 +32,8 @@ pub(crate) fn type_mismatch(ctx: &DiagnosticsContext<'_>, d: &hir::TypeMismatch)
         "type-mismatch",
         format!(
             "expected {}, found {}",
-            d.expected.display(ctx.sema.db),
-            d.actual.display(ctx.sema.db)
+            d.expected.display(ctx.sema.db).with_closure_style(ClosureStyle::ClosureWithId),
+            d.actual.display(ctx.sema.db).with_closure_style(ClosureStyle::ClosureWithId),
         ),
         display_range,
     )
@@ -597,6 +597,19 @@ fn test() -> String {
     }
 
     #[test]
+    fn closure_mismatch_show_different_type() {
+        check_diagnostics(
+            r#"
+fn f() {
+    let mut x = (|| 1, 2);
+    x = (|| 3, 4);
+       //^^^^ error: expected {closure#0}, found {closure#1}
+}
+            "#,
+        );
+    }
+
+    #[test]
     fn type_mismatch_on_block() {
         cov_mark::check!(type_mismatch_on_block);
         check_diagnostics(
diff --git a/crates/ide-diagnostics/src/handlers/undeclared_label.rs b/crates/ide-diagnostics/src/handlers/undeclared_label.rs
new file mode 100644
index 00000000000..dbedf1e6c1e
--- /dev/null
+++ b/crates/ide-diagnostics/src/handlers/undeclared_label.rs
@@ -0,0 +1,63 @@
+use crate::{Diagnostic, DiagnosticsContext};
+
+// Diagnostic: undeclared-label
+pub(crate) fn undeclared_label(
+    ctx: &DiagnosticsContext<'_>,
+    d: &hir::UndeclaredLabel,
+) -> Diagnostic {
+    let name = &d.name;
+    Diagnostic::new(
+        "undeclared-label",
+        format!("use of undeclared label `{name}`"),
+        ctx.sema.diagnostics_display_range(d.node.clone().map(|it| it.into())).range,
+    )
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::tests::check_diagnostics;
+
+    #[test]
+    fn smoke_test() {
+        check_diagnostics(
+            r#"
+fn foo() {
+    break 'a;
+  //^^^^^^^^ error: break outside of loop
+        //^^ error: use of undeclared label `'a`
+    continue 'a;
+  //^^^^^^^^^^^ error: continue outside of loop
+           //^^ error: use of undeclared label `'a`
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn try_operator_desugar_works() {
+        check_diagnostics(
+            r#"
+//- minicore: option, try
+fn foo() {
+    None?;
+}
+"#,
+        );
+        check_diagnostics(
+            r#"
+//- minicore: option, try, future
+async fn foo() {
+    None?;
+}
+"#,
+        );
+        check_diagnostics(
+            r#"
+//- minicore: option, try, future, fn
+async fn foo() {
+    || None?;
+}
+"#,
+        );
+    }
+}
diff --git a/crates/ide-diagnostics/src/handlers/unlinked_file.rs b/crates/ide-diagnostics/src/handlers/unlinked_file.rs
index 145e18c5c48..b9c5384ea1e 100644
--- a/crates/ide-diagnostics/src/handlers/unlinked_file.rs
+++ b/crates/ide-diagnostics/src/handlers/unlinked_file.rs
@@ -10,7 +10,7 @@ use ide_db::{
 };
 use syntax::{
     ast::{self, edit::IndentLevel, HasModuleItem, HasName},
-    AstNode, TextRange, TextSize,
+    AstNode, TextRange,
 };
 use text_edit::TextEdit;
 
@@ -27,14 +27,28 @@ pub(crate) fn unlinked_file(
 ) {
     // Limit diagnostic to the first few characters in the file. This matches how VS Code
     // renders it with the full span, but on other editors, and is less invasive.
+    let fixes = fixes(ctx, file_id);
+    // FIXME: This is a hack for the vscode extension to notice whether there is an autofix or not before having to resolve diagnostics.
+    // This is to prevent project linking popups from appearing when there is an autofix. https://github.com/rust-lang/rust-analyzer/issues/14523
+    let message = if fixes.is_none() {
+        "file not included in crate hierarchy"
+    } else {
+        "file not included in module tree"
+    };
+
     let range = ctx.sema.db.parse(file_id).syntax_node().text_range();
-    // FIXME: This is wrong if one of the first three characters is not ascii: `//Ы`.
-    let range = range.intersect(TextRange::up_to(TextSize::of("..."))).unwrap_or(range);
+    let range = FileLoader::file_text(ctx.sema.db, file_id)
+        .char_indices()
+        .take(3)
+        .last()
+        .map(|(i, _)| i)
+        .map(|i| TextRange::up_to(i.try_into().unwrap()))
+        .unwrap_or(range);
 
     acc.push(
-        Diagnostic::new("unlinked-file", "file not included in module tree", range)
+        Diagnostic::new("unlinked-file", message, range)
             .severity(Severity::WeakWarning)
-            .with_fixes(fixes(ctx, file_id)),
+            .with_fixes(fixes),
     );
 }
 
diff --git a/crates/ide-diagnostics/src/handlers/unreachable_label.rs b/crates/ide-diagnostics/src/handlers/unreachable_label.rs
new file mode 100644
index 00000000000..5933a9b694a
--- /dev/null
+++ b/crates/ide-diagnostics/src/handlers/unreachable_label.rs
@@ -0,0 +1,91 @@
+use crate::{Diagnostic, DiagnosticsContext};
+
+// Diagnostic: unreachable-label
+pub(crate) fn unreachable_label(
+    ctx: &DiagnosticsContext<'_>,
+    d: &hir::UnreachableLabel,
+) -> Diagnostic {
+    let name = &d.name;
+    Diagnostic::new(
+        "unreachable-label",
+        format!("use of unreachable label `{name}`"),
+        ctx.sema.diagnostics_display_range(d.node.clone().map(|it| it.into())).range,
+    )
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::tests::check_diagnostics;
+
+    #[test]
+    fn async_blocks_are_borders() {
+        check_diagnostics(
+            r#"
+fn foo() {
+    'a: loop {
+        async {
+            break 'a;
+          //^^^^^^^^ error: break outside of loop
+               // ^^ error: use of unreachable label `'a`
+            continue 'a;
+          //^^^^^^^^^^^ error: continue outside of loop
+                  // ^^ error: use of unreachable label `'a`
+        };
+    }
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn closures_are_borders() {
+        check_diagnostics(
+            r#"
+fn foo() {
+    'a: loop {
+        || {
+            break 'a;
+          //^^^^^^^^ error: break outside of loop
+               // ^^ error: use of unreachable label `'a`
+            continue 'a;
+          //^^^^^^^^^^^ error: continue outside of loop
+                  // ^^ error: use of unreachable label `'a`
+        };
+    }
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn blocks_pass_through() {
+        check_diagnostics(
+            r#"
+fn foo() {
+    'a: loop {
+        {
+          break 'a;
+          continue 'a;
+        }
+    }
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn try_blocks_pass_through() {
+        check_diagnostics(
+            r#"
+fn foo() {
+    'a: loop {
+        try {
+            break 'a;
+            continue 'a;
+        };
+    }
+}
+"#,
+        );
+    }
+}
diff --git a/crates/ide-diagnostics/src/lib.rs b/crates/ide-diagnostics/src/lib.rs
index 0dc5343f942..70116f15a75 100644
--- a/crates/ide-diagnostics/src/lib.rs
+++ b/crates/ide-diagnostics/src/lib.rs
@@ -52,6 +52,8 @@ mod handlers {
     pub(crate) mod unresolved_macro_call;
     pub(crate) mod unresolved_module;
     pub(crate) mod unresolved_proc_macro;
+    pub(crate) mod undeclared_label;
+    pub(crate) mod unreachable_label;
 
     // The handlers below are unusual, the implement the diagnostics as well.
     pub(crate) mod field_shorthand;
@@ -253,36 +255,38 @@ pub fn diagnostics(
     for diag in diags {
         #[rustfmt::skip]
         let d = match diag {
-            AnyDiagnostic::BreakOutsideOfLoop(d) => handlers::break_outside_of_loop::break_outside_of_loop(&ctx, &d),
             AnyDiagnostic::ExpectedFunction(d) => handlers::expected_function::expected_function(&ctx, &d),
-            AnyDiagnostic::IncorrectCase(d) => handlers::incorrect_case::incorrect_case(&ctx, &d),
+            AnyDiagnostic::InactiveCode(d) => match handlers::inactive_code::inactive_code(&ctx, &d) {
+                Some(it) => it,
+                None => continue,
+            }
             AnyDiagnostic::IncoherentImpl(d) => handlers::incoherent_impl::incoherent_impl(&ctx, &d),
+            AnyDiagnostic::IncorrectCase(d) => handlers::incorrect_case::incorrect_case(&ctx, &d),
+            AnyDiagnostic::InvalidDeriveTarget(d) => handlers::invalid_derive_target::invalid_derive_target(&ctx, &d),
             AnyDiagnostic::MacroError(d) => handlers::macro_error::macro_error(&ctx, &d),
             AnyDiagnostic::MalformedDerive(d) => handlers::malformed_derive::malformed_derive(&ctx, &d),
             AnyDiagnostic::MismatchedArgCount(d) => handlers::mismatched_arg_count::mismatched_arg_count(&ctx, &d),
             AnyDiagnostic::MissingFields(d) => handlers::missing_fields::missing_fields(&ctx, &d),
             AnyDiagnostic::MissingMatchArms(d) => handlers::missing_match_arms::missing_match_arms(&ctx, &d),
             AnyDiagnostic::MissingUnsafe(d) => handlers::missing_unsafe::missing_unsafe(&ctx, &d),
+            AnyDiagnostic::NeedMut(d) => handlers::mutability_errors::need_mut(&ctx, &d),
             AnyDiagnostic::NoSuchField(d) => handlers::no_such_field::no_such_field(&ctx, &d),
             AnyDiagnostic::PrivateAssocItem(d) => handlers::private_assoc_item::private_assoc_item(&ctx, &d),
             AnyDiagnostic::PrivateField(d) => handlers::private_field::private_field(&ctx, &d),
             AnyDiagnostic::ReplaceFilterMapNextWithFindMap(d) => handlers::replace_filter_map_next_with_find_map::replace_filter_map_next_with_find_map(&ctx, &d),
             AnyDiagnostic::TypeMismatch(d) => handlers::type_mismatch::type_mismatch(&ctx, &d),
+            AnyDiagnostic::UndeclaredLabel(d) => handlers::undeclared_label::undeclared_label(&ctx, &d),
             AnyDiagnostic::UnimplementedBuiltinMacro(d) => handlers::unimplemented_builtin_macro::unimplemented_builtin_macro(&ctx, &d),
+            AnyDiagnostic::UnreachableLabel(d) => handlers::unreachable_label:: unreachable_label(&ctx, &d),
             AnyDiagnostic::UnresolvedExternCrate(d) => handlers::unresolved_extern_crate::unresolved_extern_crate(&ctx, &d),
+            AnyDiagnostic::UnresolvedField(d) => handlers::unresolved_field::unresolved_field(&ctx, &d),
             AnyDiagnostic::UnresolvedImport(d) => handlers::unresolved_import::unresolved_import(&ctx, &d),
             AnyDiagnostic::UnresolvedMacroCall(d) => handlers::unresolved_macro_call::unresolved_macro_call(&ctx, &d),
+            AnyDiagnostic::UnresolvedMethodCall(d) => handlers::unresolved_method::unresolved_method(&ctx, &d),
             AnyDiagnostic::UnresolvedModule(d) => handlers::unresolved_module::unresolved_module(&ctx, &d),
             AnyDiagnostic::UnresolvedProcMacro(d) => handlers::unresolved_proc_macro::unresolved_proc_macro(&ctx, &d, config.proc_macros_enabled, config.proc_attr_macros_enabled),
-            AnyDiagnostic::InvalidDeriveTarget(d) => handlers::invalid_derive_target::invalid_derive_target(&ctx, &d),
-            AnyDiagnostic::UnresolvedField(d) => handlers::unresolved_field::unresolved_field(&ctx, &d),
-            AnyDiagnostic::UnresolvedMethodCall(d) => handlers::unresolved_method::unresolved_method(&ctx, &d),
-            AnyDiagnostic::NeedMut(d) => handlers::mutability_errors::need_mut(&ctx, &d),
             AnyDiagnostic::UnusedMut(d) => handlers::mutability_errors::unused_mut(&ctx, &d),
-            AnyDiagnostic::InactiveCode(d) => match handlers::inactive_code::inactive_code(&ctx, &d) {
-                Some(it) => it,
-                None => continue,
-            }
+            AnyDiagnostic::BreakOutsideOfLoop(d) => handlers::break_outside_of_loop::break_outside_of_loop(&ctx, &d),
         };
         res.push(d)
     }
diff --git a/crates/ide/src/doc_links/tests.rs b/crates/ide/src/doc_links/tests.rs
index 104181a33e6..b6b46c45088 100644
--- a/crates/ide/src/doc_links/tests.rs
+++ b/crates/ide/src/doc_links/tests.rs
@@ -116,7 +116,7 @@ fn external_docs_doc_url_std_crate() {
 //- /main.rs crate:std
 use self$0;
 "#,
-        expect![[r#"https://doc.rust-lang.org/nightly/std/index.html"#]],
+        expect!["https://doc.rust-lang.org/stable/std/index.html"],
     );
 }
 
diff --git a/crates/ide/src/expand_macro.rs b/crates/ide/src/expand_macro.rs
index 4382af43438..91af5716ca5 100644
--- a/crates/ide/src/expand_macro.rs
+++ b/crates/ide/src/expand_macro.rs
@@ -471,7 +471,7 @@ struct Foo {}
 "#,
             expect![[r#"
                 Clone
-                impl < >core::clone::Clone for Foo< >{}
+                impl < >core::clone::Clone for Foo< >where{}
             "#]],
         );
     }
@@ -488,7 +488,7 @@ struct Foo {}
 "#,
             expect![[r#"
                 Copy
-                impl < >core::marker::Copy for Foo< >{}
+                impl < >core::marker::Copy for Foo< >where{}
             "#]],
         );
     }
@@ -504,7 +504,7 @@ struct Foo {}
 "#,
             expect![[r#"
                 Copy
-                impl < >core::marker::Copy for Foo< >{}
+                impl < >core::marker::Copy for Foo< >where{}
             "#]],
         );
         check(
@@ -516,7 +516,7 @@ struct Foo {}
 "#,
             expect![[r#"
                 Clone
-                impl < >core::clone::Clone for Foo< >{}
+                impl < >core::clone::Clone for Foo< >where{}
             "#]],
         );
     }
diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs
index 70ec915e967..73ab3d2e16f 100644
--- a/crates/ide/src/hover/tests.rs
+++ b/crates/ide/src/hover/tests.rs
@@ -225,7 +225,7 @@ fn main() {
                 *iter*
 
                 ```rust
-                let mut iter: Iter<Scan<OtherStruct<OtherStruct<i32>>, |&mut u32, &u32, &mut u32| -> Option<u32>, u32>>
+                let mut iter: Iter<Scan<OtherStruct<OtherStruct<i32>>, impl Fn(&mut u32, &u32, &mut u32) -> Option<u32>, u32>>
                 ```
             "#]],
     );
@@ -4242,7 +4242,7 @@ fn foo() {
 /// [threads]: ../book/ch16-01-threads.html#using-move-closures-with-threads
 mod move_keyword {}
 "#,
-        expect![[r##"
+        expect![[r#"
             *move*
 
             ```rust
@@ -4251,11 +4251,11 @@ mod move_keyword {}
 
             ---
 
-            [closure](https://doc.rust-lang.org/nightly/book/ch13-01-closures.html)
-            [closures](https://doc.rust-lang.org/nightly/book/ch13-01-closures.html)
-            [threads](https://doc.rust-lang.org/nightly/book/ch16-01-threads.html#using-move-closures-with-threads)
+            [closure](https://doc.rust-lang.org/stable/book/ch13-01-closures.html)
+            [closures](https://doc.rust-lang.org/stable/book/ch13-01-closures.html)
+            [threads](https://doc.rust-lang.org/stable/book/ch16-01-threads.html#using-move-closures-with-threads)
             <https://doc.rust-lang.org/nightly/book/ch13-01-closures.html>
-        "##]],
+        "#]],
     );
 }
 
diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs
index ac477339ec2..e6360bc6ec1 100644
--- a/crates/ide/src/inlay_hints.rs
+++ b/crates/ide/src/inlay_hints.rs
@@ -5,7 +5,8 @@ use std::{
 
 use either::Either;
 use hir::{
-    known, HasVisibility, HirDisplay, HirDisplayError, HirWrite, ModuleDef, ModuleDefId, Semantics,
+    known, ClosureStyle, HasVisibility, HirDisplay, HirDisplayError, HirWrite, ModuleDef,
+    ModuleDefId, Semantics,
 };
 use ide_db::{base_db::FileRange, famous_defs::FamousDefs, RootDatabase};
 use itertools::Itertools;
@@ -45,6 +46,7 @@ pub struct InlayHintsConfig {
     pub param_names_for_lifetime_elision_hints: bool,
     pub hide_named_constructor_hints: bool,
     pub hide_closure_initialization_hints: bool,
+    pub closure_style: ClosureStyle,
     pub max_length: Option<usize>,
     pub closing_brace_hints_min_lines: Option<usize>,
 }
@@ -291,6 +293,7 @@ fn label_of_ty(
         mut max_length: Option<usize>,
         ty: hir::Type,
         label_builder: &mut InlayHintLabelBuilder<'_>,
+        config: &InlayHintsConfig,
     ) -> Result<(), HirDisplayError> {
         let iter_item_type = hint_iterator(sema, famous_defs, &ty);
         match iter_item_type {
@@ -321,11 +324,14 @@ fn label_of_ty(
                 label_builder.write_str(LABEL_ITEM)?;
                 label_builder.end_location_link();
                 label_builder.write_str(LABEL_MIDDLE2)?;
-                rec(sema, famous_defs, max_length, ty, label_builder)?;
+                rec(sema, famous_defs, max_length, ty, label_builder, config)?;
                 label_builder.write_str(LABEL_END)?;
                 Ok(())
             }
-            None => ty.display_truncated(sema.db, max_length).write_to(label_builder),
+            None => ty
+                .display_truncated(sema.db, max_length)
+                .with_closure_style(config.closure_style)
+                .write_to(label_builder),
         }
     }
 
@@ -335,7 +341,7 @@ fn label_of_ty(
         location: None,
         result: InlayHintLabel::default(),
     };
-    let _ = rec(sema, famous_defs, config.max_length, ty, &mut label_builder);
+    let _ = rec(sema, famous_defs, config.max_length, ty, &mut label_builder, config);
     let r = label_builder.finish();
     Some(r)
 }
@@ -481,6 +487,7 @@ fn closure_has_block_body(closure: &ast::ClosureExpr) -> bool {
 #[cfg(test)]
 mod tests {
     use expect_test::Expect;
+    use hir::ClosureStyle;
     use itertools::Itertools;
     use test_utils::extract_annotations;
 
@@ -504,6 +511,7 @@ mod tests {
         binding_mode_hints: false,
         hide_named_constructor_hints: false,
         hide_closure_initialization_hints: false,
+        closure_style: ClosureStyle::ImplFn,
         param_names_for_lifetime_elision_hints: false,
         max_length: None,
         closing_brace_hints_min_lines: None,
diff --git a/crates/ide/src/inlay_hints/adjustment.rs b/crates/ide/src/inlay_hints/adjustment.rs
index f279c91a9e9..0b146091956 100644
--- a/crates/ide/src/inlay_hints/adjustment.rs
+++ b/crates/ide/src/inlay_hints/adjustment.rs
@@ -315,6 +315,8 @@ fn main() {
     (&Struct).consume();
    //^^^^^^^*
     (&Struct).by_ref();
+   //^^^^^^^&
+   //^^^^^^^*
 
     (&mut Struct).consume();
    //^^^^^^^^^^^*
@@ -322,6 +324,8 @@ fn main() {
    //^^^^^^^^^^^&
    //^^^^^^^^^^^*
     (&mut Struct).by_ref_mut();
+   //^^^^^^^^^^^&mut $
+   //^^^^^^^^^^^*
 
     // Check that block-like expressions don't duplicate hints
     let _: &mut [u32] = (&mut []);
@@ -414,6 +418,10 @@ fn main() {
    //^^^^^^^)
    //^^^^^^^.*
     (&Struct).by_ref();
+   //^^^^^^^(
+   //^^^^^^^)
+   //^^^^^^^.*
+   //^^^^^^^.&
 
     (&mut Struct).consume();
    //^^^^^^^^^^^(
@@ -425,6 +433,10 @@ fn main() {
    //^^^^^^^^^^^.*
    //^^^^^^^^^^^.&
     (&mut Struct).by_ref_mut();
+   //^^^^^^^^^^^(
+   //^^^^^^^^^^^)
+   //^^^^^^^^^^^.*
+   //^^^^^^^^^^^.&mut
 
     // Check that block-like expressions don't duplicate hints
     let _: &mut [u32] = (&mut []);
diff --git a/crates/ide/src/inlay_hints/bind_pat.rs b/crates/ide/src/inlay_hints/bind_pat.rs
index 6a50927333d..5f571d04482 100644
--- a/crates/ide/src/inlay_hints/bind_pat.rs
+++ b/crates/ide/src/inlay_hints/bind_pat.rs
@@ -176,6 +176,7 @@ fn pat_is_enum_variant(db: &RootDatabase, bind_pat: &ast::IdentPat, pat_ty: &hir
 mod tests {
     // This module also contains tests for super::closure_ret
 
+    use hir::ClosureStyle;
     use syntax::{TextRange, TextSize};
     use test_utils::extract_annotations;
 
@@ -235,7 +236,7 @@ fn main() {
     let zz_ref = &zz;
       //^^^^^^ &Test<i32>
     let test = || zz;
-      //^^^^ || -> Test<i32>
+      //^^^^ impl FnOnce() -> Test<i32>
 }"#,
         );
     }
@@ -753,7 +754,7 @@ fn main() {
     let func = times2;
     //  ^^^^ fn times2(i32) -> i32
     let closure = |x: i32| x * 2;
-    //  ^^^^^^^ |i32| -> i32
+    //  ^^^^^^^ impl Fn(i32) -> i32
 }
 
 fn fallible() -> ControlFlow<()> {
@@ -821,7 +822,7 @@ fn main() {
                    //^^^^^^^^^ i32
 
     let multiply =
-      //^^^^^^^^ |i32, i32| -> i32
+      //^^^^^^^^ impl Fn(i32, i32) -> i32
       | a,     b| a * b
       //^ i32  ^ i32
 
@@ -830,10 +831,10 @@ fn main() {
     let _: i32 = multiply(1,  2);
                         //^ a ^ b
     let multiply_ref = &multiply;
-      //^^^^^^^^^^^^ &|i32, i32| -> i32
+      //^^^^^^^^^^^^ &impl Fn(i32, i32) -> i32
 
     let return_42 = || 42;
-      //^^^^^^^^^ || -> i32
+      //^^^^^^^^^ impl Fn() -> i32
       || { 42 };
     //^^ i32
 }"#,
@@ -858,6 +859,94 @@ fn main() {
     }
 
     #[test]
+    fn closure_style() {
+        check_with_config(
+            InlayHintsConfig { type_hints: true, ..DISABLED_CONFIG },
+            r#"
+//- minicore: fn
+fn main() {
+    let x = || 2;
+      //^ impl Fn() -> i32
+    let y = |t: i32| x() + t;
+      //^ impl Fn(i32) -> i32
+    let mut t = 5;
+          //^ i32
+    let z = |k: i32| { t += k; };
+      //^ impl FnMut(i32)
+    let p = (y, z);
+      //^ (impl Fn(i32) -> i32, impl FnMut(i32))
+}
+            "#,
+        );
+        check_with_config(
+            InlayHintsConfig {
+                type_hints: true,
+                closure_style: ClosureStyle::RANotation,
+                ..DISABLED_CONFIG
+            },
+            r#"
+//- minicore: fn
+fn main() {
+    let x = || 2;
+      //^ || -> i32
+    let y = |t: i32| x() + t;
+      //^ |i32| -> i32
+    let mut t = 5;
+          //^ i32
+    let z = |k: i32| { t += k; };
+      //^ |i32| -> ()
+    let p = (y, z);
+      //^ (|i32| -> i32, |i32| -> ())
+}
+            "#,
+        );
+        check_with_config(
+            InlayHintsConfig {
+                type_hints: true,
+                closure_style: ClosureStyle::ClosureWithId,
+                ..DISABLED_CONFIG
+            },
+            r#"
+//- minicore: fn
+fn main() {
+    let x = || 2;
+      //^ {closure#0}
+    let y = |t: i32| x() + t;
+      //^ {closure#1}
+    let mut t = 5;
+          //^ i32
+    let z = |k: i32| { t += k; };
+      //^ {closure#2}
+    let p = (y, z);
+      //^ ({closure#1}, {closure#2})
+}
+            "#,
+        );
+        check_with_config(
+            InlayHintsConfig {
+                type_hints: true,
+                closure_style: ClosureStyle::Hide,
+                ..DISABLED_CONFIG
+            },
+            r#"
+//- minicore: fn
+fn main() {
+    let x = || 2;
+      //^ …
+    let y = |t: i32| x() + t;
+      //^ …
+    let mut t = 5;
+          //^ i32
+    let z = |k: i32| { t += k; };
+      //^ …
+    let p = (y, z);
+      //^ (…, …)
+}
+            "#,
+        );
+    }
+
+    #[test]
     fn skip_closure_type_hints() {
         check_with_config(
             InlayHintsConfig {
@@ -871,13 +960,13 @@ fn main() {
     let multiple_2 = |x: i32| { x * 2 };
 
     let multiple_2 = |x: i32| x * 2;
-    //  ^^^^^^^^^^ |i32| -> i32
+    //  ^^^^^^^^^^ impl Fn(i32) -> i32
 
     let (not) = (|x: bool| { !x });
-    //   ^^^ |bool| -> bool
+    //   ^^^ impl Fn(bool) -> bool
 
     let (is_zero, _b) = (|x: usize| { x == 0 }, false);
-    //   ^^^^^^^ |usize| -> bool
+    //   ^^^^^^^ impl Fn(usize) -> bool
     //            ^^ bool
 
     let plus_one = |x| { x + 1 };
diff --git a/crates/ide/src/signature_help.rs b/crates/ide/src/signature_help.rs
index 4b2c139f6f4..cdee705cbfd 100644
--- a/crates/ide/src/signature_help.rs
+++ b/crates/ide/src/signature_help.rs
@@ -1228,6 +1228,24 @@ fn main() {
     }
 
     #[test]
+    fn call_info_for_fn_def_over_reference() {
+        check(
+            r#"
+struct S;
+fn foo(s: S) -> i32 { 92 }
+fn main() {
+    let bar = &&&&&foo;
+    bar($0);
+}
+        "#,
+            expect![[r#"
+                fn foo(s: S) -> i32
+                       ^^^^
+            "#]],
+        )
+    }
+
+    #[test]
     fn call_info_for_fn_ptr() {
         check(
             r#"
diff --git a/crates/ide/src/static_index.rs b/crates/ide/src/static_index.rs
index c97691b14a5..774b07775b9 100644
--- a/crates/ide/src/static_index.rs
+++ b/crates/ide/src/static_index.rs
@@ -118,6 +118,7 @@ impl StaticIndex<'_> {
                     adjustment_hints_hide_outside_unsafe: false,
                     hide_named_constructor_hints: false,
                     hide_closure_initialization_hints: false,
+                    closure_style: hir::ClosureStyle::ImplFn,
                     param_names_for_lifetime_elision_hints: false,
                     binding_mode_hints: false,
                     max_length: Some(25),
diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs
index 454a250f3de..5821bb1202f 100644
--- a/crates/ide/src/syntax_highlighting.rs
+++ b/crates/ide/src/syntax_highlighting.rs
@@ -21,8 +21,11 @@ use syntax::{
 
 use crate::{
     syntax_highlighting::{
-        escape::highlight_escape_string, format::highlight_format_string, highlights::Highlights,
-        macro_::MacroHighlighter, tags::Highlight,
+        escape::{highlight_escape_char, highlight_escape_string},
+        format::highlight_format_string,
+        highlights::Highlights,
+        macro_::MacroHighlighter,
+        tags::Highlight,
     },
     FileId, HlMod, HlOperator, HlPunct, HlTag,
 };
@@ -427,6 +430,14 @@ fn traverse(
                 if let Some(byte_string) = ast::ByteString::cast(token) {
                     highlight_escape_string(hl, &byte_string, range.start());
                 }
+            } else if ast::Char::can_cast(token.kind())
+                && ast::Char::can_cast(descended_token.kind())
+            {
+                let Some(char) = ast::Char::cast(token) else {
+                    continue;
+                };
+
+                highlight_escape_char(hl, &char, range.start())
             }
         }
 
diff --git a/crates/ide/src/syntax_highlighting/escape.rs b/crates/ide/src/syntax_highlighting/escape.rs
index 6a1236c793b..211e3588095 100644
--- a/crates/ide/src/syntax_highlighting/escape.rs
+++ b/crates/ide/src/syntax_highlighting/escape.rs
@@ -1,8 +1,8 @@
 //! Syntax highlighting for escape sequences
 use crate::syntax_highlighting::highlights::Highlights;
 use crate::{HlRange, HlTag};
-use syntax::ast::IsString;
-use syntax::TextSize;
+use syntax::ast::{Char, IsString};
+use syntax::{AstToken, TextRange, TextSize};
 
 pub(super) fn highlight_escape_string<T: IsString>(
     stack: &mut Highlights,
@@ -23,3 +23,23 @@ pub(super) fn highlight_escape_string<T: IsString>(
         }
     });
 }
+
+pub(super) fn highlight_escape_char(stack: &mut Highlights, char: &Char, start: TextSize) {
+    if char.value().is_none() {
+        return;
+    }
+
+    let text = char.text();
+    if !text.starts_with('\'') || !text.ends_with('\'') {
+        return;
+    }
+
+    let text = &text[1..text.len() - 1];
+    if !text.starts_with('\\') {
+        return;
+    }
+
+    let range =
+        TextRange::new(start + TextSize::from(1), start + TextSize::from(text.len() as u32 + 1));
+    stack.add(HlRange { range, highlight: HlTag::EscapeSequence.into(), binding_hash: None })
+}
diff --git a/crates/ide/src/syntax_highlighting/highlight.rs b/crates/ide/src/syntax_highlighting/highlight.rs
index 2111baad74d..936362914ae 100644
--- a/crates/ide/src/syntax_highlighting/highlight.rs
+++ b/crates/ide/src/syntax_highlighting/highlight.rs
@@ -675,14 +675,12 @@ fn is_consumed_lvalue(node: &SyntaxNode, local: &hir::Local, db: &RootDatabase)
 
 /// Returns true if the parent nodes of `node` all match the `SyntaxKind`s in `kinds` exactly.
 fn parents_match(mut node: NodeOrToken<SyntaxNode, SyntaxToken>, mut kinds: &[SyntaxKind]) -> bool {
-    while let (Some(parent), [kind, rest @ ..]) = (&node.parent(), kinds) {
+    while let (Some(parent), [kind, rest @ ..]) = (node.parent(), kinds) {
         if parent.kind() != *kind {
             return false;
         }
 
-        // FIXME: Would be nice to get parent out of the match, but binding by-move and by-value
-        // in the same pattern is unstable: rust-lang/rust#68354.
-        node = node.parent().unwrap().into();
+        node = parent.into();
         kinds = rest;
     }
 
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html b/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
index a626cda3fe8..d34f5cffbf1 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
@@ -93,6 +93,16 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 <span class="brace">}</span>
 
 <span class="keyword">fn</span> <span class="function declaration">main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
+    <span class="keyword">let</span> <span class="variable declaration">a</span> <span class="operator">=</span> <span class="char_literal">'</span><span class="escape_sequence">\n</span><span class="char_literal">'</span><span class="semicolon">;</span>
+    <span class="keyword">let</span> <span class="variable declaration">a</span> <span class="operator">=</span> <span class="char_literal">'</span><span class="escape_sequence">\t</span><span class="char_literal">'</span><span class="semicolon">;</span>
+    <span class="keyword">let</span> <span class="variable declaration">a</span> <span class="operator">=</span> <span class="char_literal">'\e'</span><span class="semicolon">;</span> <span class="comment">// invalid escape</span>
+    <span class="keyword">let</span> <span class="variable declaration">a</span> <span class="operator">=</span> <span class="char_literal">'e'</span><span class="semicolon">;</span>
+    <span class="keyword">let</span> <span class="variable declaration">a</span> <span class="operator">=</span> <span class="char_literal">' '</span><span class="semicolon">;</span>
+    <span class="keyword">let</span> <span class="variable declaration">a</span> <span class="operator">=</span> <span class="char_literal">'</span><span class="escape_sequence">\u{48}</span><span class="char_literal">'</span><span class="semicolon">;</span>
+    <span class="keyword">let</span> <span class="variable declaration">a</span> <span class="operator">=</span> <span class="char_literal">'</span><span class="escape_sequence">\u{4823}</span><span class="char_literal">'</span><span class="semicolon">;</span>
+    <span class="keyword">let</span> <span class="variable declaration">a</span> <span class="operator">=</span> <span class="char_literal">'</span><span class="escape_sequence">\x65</span><span class="char_literal">'</span><span class="semicolon">;</span>
+    <span class="keyword">let</span> <span class="variable declaration">a</span> <span class="operator">=</span> <span class="char_literal">'</span><span class="escape_sequence">\x00</span><span class="char_literal">'</span><span class="semicolon">;</span>
+
     <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="escape_sequence">{{</span><span class="string_literal">Hello</span><span class="escape_sequence">}}</span><span class="string_literal">"</span><span class="parenthesis">)</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">(</span><span class="string_literal">"Hello"</span><span class="parenthesis">)</span><span class="semicolon">;</span>                 <span class="comment">// =&gt; "Hello"</span>
diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs
index 5cc3bad04be..12205d47e5d 100644
--- a/crates/ide/src/syntax_highlighting/tests.rs
+++ b/crates/ide/src/syntax_highlighting/tests.rs
@@ -439,6 +439,16 @@ macro_rules! toho {
 }
 
 fn main() {
+    let a = '\n';
+    let a = '\t';
+    let a = '\e'; // invalid escape
+    let a = 'e';
+    let a = ' ';
+    let a = '\u{48}';
+    let a = '\u{4823}';
+    let a = '\x65';
+    let a = '\x00';
+
     println!("Hello {{Hello}}");
     // from https://doc.rust-lang.org/std/fmt/index.html
     println!("Hello");                 // => "Hello"
diff --git a/crates/proc-macro-srv-cli/Cargo.toml b/crates/proc-macro-srv-cli/Cargo.toml
index c402bc02253..8f03c6ec7b5 100644
--- a/crates/proc-macro-srv-cli/Cargo.toml
+++ b/crates/proc-macro-srv-cli/Cargo.toml
@@ -10,6 +10,7 @@ rust-version.workspace = true
 
 [dependencies]
 proc-macro-srv.workspace = true
+proc-macro-api.workspace = true
 
 [features]
 sysroot-abi = ["proc-macro-srv/sysroot-abi"]
diff --git a/crates/proc-macro-srv/src/cli.rs b/crates/proc-macro-srv-cli/src/lib.rs
index 05168feb629..b307701e19f 100644
--- a/crates/proc-macro-srv/src/cli.rs
+++ b/crates/proc-macro-srv-cli/src/lib.rs
@@ -3,10 +3,9 @@ use std::io;
 
 use proc_macro_api::msg::{self, Message};
 
-use crate::ProcMacroSrv;
-
+#[cfg(feature = "sysroot-abi")]
 pub fn run() -> io::Result<()> {
-    let mut srv = ProcMacroSrv::default();
+    let mut srv = proc_macro_srv::ProcMacroSrv::default();
     let mut buf = String::new();
 
     while let Some(req) = read_request(&mut buf)? {
@@ -24,6 +23,27 @@ pub fn run() -> io::Result<()> {
 
     Ok(())
 }
+#[cfg(not(feature = "sysroot-abi"))]
+pub fn run() -> io::Result<()> {
+    let mut buf = String::new();
+
+    while let Some(req) = read_request(&mut buf)? {
+        let res = match req {
+            msg::Request::ListMacros { .. } => {
+                msg::Response::ListMacros(Err("server is built without sysroot support".to_owned()))
+            }
+            msg::Request::ExpandMacro(..) => msg::Response::ExpandMacro(Err(msg::PanicMessage(
+                "server is built without sysroot support".to_owned(),
+            ))),
+            msg::Request::ApiVersionCheck {} => {
+                msg::Response::ApiVersionCheck(proc_macro_api::msg::CURRENT_API_VERSION)
+            }
+        };
+        write_response(res)?
+    }
+
+    Ok(())
+}
 
 fn read_request(buf: &mut String) -> io::Result<Option<msg::Request>> {
     msg::Request::read(&mut io::stdin().lock(), buf)
diff --git a/crates/proc-macro-srv-cli/src/main.rs b/crates/proc-macro-srv-cli/src/main.rs
index ac9fa9f5a4c..cc83c60f15c 100644
--- a/crates/proc-macro-srv-cli/src/main.rs
+++ b/crates/proc-macro-srv-cli/src/main.rs
@@ -1,6 +1,5 @@
 //! A standalone binary for `proc-macro-srv`.
-
-use proc_macro_srv::cli;
+//! Driver for proc macro server
 
 fn main() -> std::io::Result<()> {
     let v = std::env::var("RUST_ANALYZER_INTERNALS_DO_NOT_USE");
@@ -15,5 +14,5 @@ fn main() -> std::io::Result<()> {
         }
     }
 
-    cli::run()
+    proc_macro_srv_cli::run()
 }
diff --git a/crates/proc-macro-srv/src/abis/abi_1_63/mod.rs b/crates/proc-macro-srv/src/abis/abi_1_63/mod.rs
deleted file mode 100644
index 93805c89354..00000000000
--- a/crates/proc-macro-srv/src/abis/abi_1_63/mod.rs
+++ /dev/null
@@ -1,106 +0,0 @@
-//! Macro ABI for version 1.63 of rustc
-
-#[allow(dead_code)]
-#[doc(hidden)]
-mod proc_macro;
-
-#[allow(dead_code)]
-#[doc(hidden)]
-mod ra_server;
-
-use libloading::Library;
-use proc_macro_api::ProcMacroKind;
-
-use super::tt;
-use super::PanicMessage;
-
-pub use ra_server::TokenStream;
-
-pub(crate) struct Abi {
-    exported_macros: Vec<proc_macro::bridge::client::ProcMacro>,
-}
-
-impl From<proc_macro::bridge::PanicMessage> for PanicMessage {
-    fn from(p: proc_macro::bridge::PanicMessage) -> Self {
-        Self { message: p.as_str().map(|s| s.to_string()) }
-    }
-}
-
-impl Abi {
-    pub unsafe fn from_lib(lib: &Library, symbol_name: String) -> Result<Abi, libloading::Error> {
-        let macros: libloading::Symbol<'_, &&[proc_macro::bridge::client::ProcMacro]> =
-            lib.get(symbol_name.as_bytes())?;
-        Ok(Self { exported_macros: macros.to_vec() })
-    }
-
-    pub fn expand(
-        &self,
-        macro_name: &str,
-        macro_body: &tt::Subtree,
-        attributes: Option<&tt::Subtree>,
-    ) -> Result<tt::Subtree, PanicMessage> {
-        let parsed_body = TokenStream::with_subtree(macro_body.clone());
-
-        let parsed_attributes =
-            attributes.map_or(TokenStream::new(), |attr| TokenStream::with_subtree(attr.clone()));
-
-        for proc_macro in &self.exported_macros {
-            match proc_macro {
-                proc_macro::bridge::client::ProcMacro::CustomDerive {
-                    trait_name, client, ..
-                } if *trait_name == macro_name => {
-                    let res = client.run(
-                        &proc_macro::bridge::server::SameThread,
-                        ra_server::RustAnalyzer::default(),
-                        parsed_body,
-                        true,
-                    );
-                    return res.map(|it| it.into_subtree()).map_err(PanicMessage::from);
-                }
-                proc_macro::bridge::client::ProcMacro::Bang { name, client }
-                    if *name == macro_name =>
-                {
-                    let res = client.run(
-                        &proc_macro::bridge::server::SameThread,
-                        ra_server::RustAnalyzer::default(),
-                        parsed_body,
-                        true,
-                    );
-                    return res.map(|it| it.into_subtree()).map_err(PanicMessage::from);
-                }
-                proc_macro::bridge::client::ProcMacro::Attr { name, client }
-                    if *name == macro_name =>
-                {
-                    let res = client.run(
-                        &proc_macro::bridge::server::SameThread,
-                        ra_server::RustAnalyzer::default(),
-                        parsed_attributes,
-                        parsed_body,
-                        true,
-                    );
-                    return res.map(|it| it.into_subtree()).map_err(PanicMessage::from);
-                }
-                _ => continue,
-            }
-        }
-
-        Err(proc_macro::bridge::PanicMessage::String("Nothing to expand".to_string()).into())
-    }
-
-    pub fn list_macros(&self) -> Vec<(String, ProcMacroKind)> {
-        self.exported_macros
-            .iter()
-            .map(|proc_macro| match proc_macro {
-                proc_macro::bridge::client::ProcMacro::CustomDerive { trait_name, .. } => {
-                    (trait_name.to_string(), ProcMacroKind::CustomDerive)
-                }
-                proc_macro::bridge::client::ProcMacro::Bang { name, .. } => {
-                    (name.to_string(), ProcMacroKind::FuncLike)
-                }
-                proc_macro::bridge::client::ProcMacro::Attr { name, .. } => {
-                    (name.to_string(), ProcMacroKind::Attr)
-                }
-            })
-            .collect()
-    }
-}
diff --git a/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/buffer.rs b/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/buffer.rs
deleted file mode 100644
index 48030f8d82d..00000000000
--- a/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/buffer.rs
+++ /dev/null
@@ -1,156 +0,0 @@
-//! Buffer management for same-process client<->server communication.
-
-use std::io::{self, Write};
-use std::mem;
-use std::ops::{Deref, DerefMut};
-use std::slice;
-
-#[repr(C)]
-pub struct Buffer {
-    data: *mut u8,
-    len: usize,
-    capacity: usize,
-    reserve: extern "C" fn(Buffer, usize) -> Buffer,
-    drop: extern "C" fn(Buffer),
-}
-
-unsafe impl Sync for Buffer {}
-unsafe impl Send for Buffer {}
-
-impl Default for Buffer {
-    #[inline]
-    fn default() -> Self {
-        Self::from(vec![])
-    }
-}
-
-impl Deref for Buffer {
-    type Target = [u8];
-    #[inline]
-    fn deref(&self) -> &[u8] {
-        unsafe { slice::from_raw_parts(self.data as *const u8, self.len) }
-    }
-}
-
-impl DerefMut for Buffer {
-    #[inline]
-    fn deref_mut(&mut self) -> &mut [u8] {
-        unsafe { slice::from_raw_parts_mut(self.data, self.len) }
-    }
-}
-
-impl Buffer {
-    #[inline]
-    pub(super) fn new() -> Self {
-        Self::default()
-    }
-
-    #[inline]
-    pub(super) fn clear(&mut self) {
-        self.len = 0;
-    }
-
-    #[inline]
-    pub(super) fn take(&mut self) -> Self {
-        mem::take(self)
-    }
-
-    // We have the array method separate from extending from a slice. This is
-    // because in the case of small arrays, codegen can be more efficient
-    // (avoiding a memmove call). With extend_from_slice, LLVM at least
-    // currently is not able to make that optimization.
-    #[inline]
-    pub(super) fn extend_from_array<const N: usize>(&mut self, xs: &[u8; N]) {
-        if xs.len() > (self.capacity - self.len) {
-            let b = self.take();
-            *self = (b.reserve)(b, xs.len());
-        }
-        unsafe {
-            xs.as_ptr().copy_to_nonoverlapping(self.data.add(self.len), xs.len());
-            self.len += xs.len();
-        }
-    }
-
-    #[inline]
-    pub(super) fn extend_from_slice(&mut self, xs: &[u8]) {
-        if xs.len() > (self.capacity - self.len) {
-            let b = self.take();
-            *self = (b.reserve)(b, xs.len());
-        }
-        unsafe {
-            xs.as_ptr().copy_to_nonoverlapping(self.data.add(self.len), xs.len());
-            self.len += xs.len();
-        }
-    }
-
-    #[inline]
-    pub(super) fn push(&mut self, v: u8) {
-        // The code here is taken from Vec::push, and we know that reserve()
-        // will panic if we're exceeding isize::MAX bytes and so there's no need
-        // to check for overflow.
-        if self.len == self.capacity {
-            let b = self.take();
-            *self = (b.reserve)(b, 1);
-        }
-        unsafe {
-            *self.data.add(self.len) = v;
-            self.len += 1;
-        }
-    }
-}
-
-impl Write for Buffer {
-    #[inline]
-    fn write(&mut self, xs: &[u8]) -> io::Result<usize> {
-        self.extend_from_slice(xs);
-        Ok(xs.len())
-    }
-
-    #[inline]
-    fn write_all(&mut self, xs: &[u8]) -> io::Result<()> {
-        self.extend_from_slice(xs);
-        Ok(())
-    }
-
-    #[inline]
-    fn flush(&mut self) -> io::Result<()> {
-        Ok(())
-    }
-}
-
-impl Drop for Buffer {
-    #[inline]
-    fn drop(&mut self) {
-        let b = self.take();
-        (b.drop)(b);
-    }
-}
-
-impl From<Vec<u8>> for Buffer {
-    fn from(mut v: Vec<u8>) -> Self {
-        let (data, len, capacity) = (v.as_mut_ptr(), v.len(), v.capacity());
-        mem::forget(v);
-
-        // This utility function is nested in here because it can *only*
-        // be safely called on `Buffer`s created by *this* `proc_macro`.
-        fn to_vec(b: Buffer) -> Vec<u8> {
-            unsafe {
-                let Buffer { data, len, capacity, .. } = b;
-                mem::forget(b);
-                Vec::from_raw_parts(data, len, capacity)
-            }
-        }
-
-        extern "C" fn reserve(b: Buffer, additional: usize) -> Buffer {
-            let mut v = to_vec(b);
-            v.reserve(additional);
-            Buffer::from(v)
-        }
-
-        extern "C" fn drop(b: Buffer) {
-            mem::drop(to_vec(b));
-        }
-
-        Buffer { data, len, capacity, reserve, drop }
-    }
-}
diff --git a/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/client.rs b/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/client.rs
deleted file mode 100644
index b346c2c1896..00000000000
--- a/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/client.rs
+++ /dev/null
@@ -1,510 +0,0 @@
-//! Client-side types.
-
-use super::*;
-
-use std::marker::PhantomData;
-
-macro_rules! define_handles {
-    (
-        'owned: $($oty:ident,)*
-        'interned: $($ity:ident,)*
-    ) => {
-        #[repr(C)]
-        #[allow(non_snake_case)]
-        pub struct HandleCounters {
-            $($oty: AtomicUsize,)*
-            $($ity: AtomicUsize,)*
-        }
-
-        impl HandleCounters {
-            // FIXME(eddyb) use a reference to the `static COUNTERS`, instead of
-            // a wrapper `fn` pointer, once `const fn` can reference `static`s.
-            extern "C" fn get() -> &'static Self {
-                static COUNTERS: HandleCounters = HandleCounters {
-                    $($oty: AtomicUsize::new(1),)*
-                    $($ity: AtomicUsize::new(1),)*
-                };
-                &COUNTERS
-            }
-        }
-
-        // FIXME(eddyb) generate the definition of `HandleStore` in `server.rs`.
-        #[repr(C)]
-        #[allow(non_snake_case)]
-        pub(super) struct HandleStore<S: server::Types> {
-            $($oty: handle::OwnedStore<S::$oty>,)*
-            $($ity: handle::InternedStore<S::$ity>,)*
-        }
-
-        impl<S: server::Types> HandleStore<S> {
-            pub(super) fn new(handle_counters: &'static HandleCounters) -> Self {
-                HandleStore {
-                    $($oty: handle::OwnedStore::new(&handle_counters.$oty),)*
-                    $($ity: handle::InternedStore::new(&handle_counters.$ity),)*
-                }
-            }
-        }
-
-        $(
-            #[repr(C)]
-            pub(crate) struct $oty {
-                handle: handle::Handle,
-                // Prevent Send and Sync impls. `!Send`/`!Sync` is the usual
-                // way of doing this, but that requires unstable features.
-                // rust-analyzer uses this code and avoids unstable features.
-                _marker: PhantomData<*mut ()>,
-            }
-
-            // Forward `Drop::drop` to the inherent `drop` method.
-            impl Drop for $oty {
-                fn drop(&mut self) {
-                    $oty {
-                        handle: self.handle,
-                        _marker: PhantomData,
-                    }.drop();
-                }
-            }
-
-            impl<S> Encode<S> for $oty {
-                fn encode(self, w: &mut Writer, s: &mut S) {
-                    let handle = self.handle;
-                    mem::forget(self);
-                    handle.encode(w, s);
-                }
-            }
-
-            impl<S: server::Types> DecodeMut<'_, '_, HandleStore<server::MarkedTypes<S>>>
-                for Marked<S::$oty, $oty>
-            {
-                fn decode(r: &mut Reader<'_>, s: &mut HandleStore<server::MarkedTypes<S>>) -> Self {
-                    s.$oty.take(handle::Handle::decode(r, &mut ()))
-                }
-            }
-
-            impl<S> Encode<S> for &$oty {
-                fn encode(self, w: &mut Writer, s: &mut S) {
-                    self.handle.encode(w, s);
-                }
-            }
-
-            impl<'s, S: server::Types> Decode<'_, 's, HandleStore<server::MarkedTypes<S>>>
-                for &'s Marked<S::$oty, $oty>
-            {
-                fn decode(r: &mut Reader<'_>, s: &'s HandleStore<server::MarkedTypes<S>>) -> Self {
-                    &s.$oty[handle::Handle::decode(r, &mut ())]
-                }
-            }
-
-            impl<S> Encode<S> for &mut $oty {
-                fn encode(self, w: &mut Writer, s: &mut S) {
-                    self.handle.encode(w, s);
-                }
-            }
-
-            impl<'s, S: server::Types> DecodeMut<'_, 's, HandleStore<server::MarkedTypes<S>>>
-                for &'s mut Marked<S::$oty, $oty>
-            {
-                fn decode(
-                    r: &mut Reader<'_>,
-                    s: &'s mut HandleStore<server::MarkedTypes<S>>
-                ) -> Self {
-                    &mut s.$oty[handle::Handle::decode(r, &mut ())]
-                }
-            }
-
-            impl<S: server::Types> Encode<HandleStore<server::MarkedTypes<S>>>
-                for Marked<S::$oty, $oty>
-            {
-                fn encode(self, w: &mut Writer, s: &mut HandleStore<server::MarkedTypes<S>>) {
-                    s.$oty.alloc(self).encode(w, s);
-                }
-            }
-
-            impl<S> DecodeMut<'_, '_, S> for $oty {
-                fn decode(r: &mut Reader<'_>, s: &mut S) -> Self {
-                    $oty {
-                        handle: handle::Handle::decode(r, s),
-                        _marker: PhantomData,
-                    }
-                }
-            }
-        )*
-
-        $(
-            #[repr(C)]
-            #[derive(Copy, Clone, PartialEq, Eq, Hash)]
-            pub(crate) struct $ity {
-                handle: handle::Handle,
-                // Prevent Send and Sync impls. `!Send`/`!Sync` is the usual
-                // way of doing this, but that requires unstable features.
-                // rust-analyzer uses this code and avoids unstable features.
-                _marker: PhantomData<*mut ()>,
-            }
-
-            impl<S> Encode<S> for $ity {
-                fn encode(self, w: &mut Writer, s: &mut S) {
-                    self.handle.encode(w, s);
-                }
-            }
-
-            impl<S: server::Types> DecodeMut<'_, '_, HandleStore<server::MarkedTypes<S>>>
-                for Marked<S::$ity, $ity>
-            {
-                fn decode(r: &mut Reader<'_>, s: &mut HandleStore<server::MarkedTypes<S>>) -> Self {
-                    s.$ity.copy(handle::Handle::decode(r, &mut ()))
-                }
-            }
-
-            impl<S: server::Types> Encode<HandleStore<server::MarkedTypes<S>>>
-                for Marked<S::$ity, $ity>
-            {
-                fn encode(self, w: &mut Writer, s: &mut HandleStore<server::MarkedTypes<S>>) {
-                    s.$ity.alloc(self).encode(w, s);
-                }
-            }
-
-            impl<S> DecodeMut<'_, '_, S> for $ity {
-                fn decode(r: &mut Reader<'_>, s: &mut S) -> Self {
-                    $ity {
-                        handle: handle::Handle::decode(r, s),
-                        _marker: PhantomData,
-                    }
-                }
-            }
-        )*
-    }
-}
-define_handles! {
-    'owned:
-    FreeFunctions,
-    TokenStream,
-    Group,
-    Literal,
-    SourceFile,
-    MultiSpan,
-    Diagnostic,
-
-    'interned:
-    Punct,
-    Ident,
-    Span,
-}
-
-// FIXME(eddyb) generate these impls by pattern-matching on the
-// names of methods - also could use the presence of `fn drop`
-// to distinguish between 'owned and 'interned, above.
-// Alternatively, special "modes" could be listed of types in with_api
-// instead of pattern matching on methods, here and in server decl.
-
-impl Clone for TokenStream {
-    fn clone(&self) -> Self {
-        self.clone()
-    }
-}
-
-impl Clone for Group {
-    fn clone(&self) -> Self {
-        self.clone()
-    }
-}
-
-impl Clone for Literal {
-    fn clone(&self) -> Self {
-        self.clone()
-    }
-}
-
-impl fmt::Debug for Literal {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.debug_struct("Literal")
-            // format the kind without quotes, as in `kind: Float`
-            .field("kind", &format_args!("{}", &self.debug_kind()))
-            .field("symbol", &self.symbol())
-            // format `Some("...")` on one line even in {:#?} mode
-            .field("suffix", &format_args!("{:?}", &self.suffix()))
-            .field("span", &self.span())
-            .finish()
-    }
-}
-
-impl Clone for SourceFile {
-    fn clone(&self) -> Self {
-        self.clone()
-    }
-}
-
-impl fmt::Debug for Span {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.write_str(&self.debug())
-    }
-}
-
-macro_rules! define_client_side {
-    ($($name:ident {
-        $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)*;)*
-    }),* $(,)?) => {
-        $(impl $name {
-            $(pub(crate) fn $method($($arg: $arg_ty),*) $(-> $ret_ty)* {
-                Bridge::with(|bridge| {
-                    let mut buf = bridge.cached_buffer.take();
-
-                    buf.clear();
-                    api_tags::Method::$name(api_tags::$name::$method).encode(&mut buf, &mut ());
-                    reverse_encode!(buf; $($arg),*);
-
-                    buf = bridge.dispatch.call(buf);
-
-                    let r = Result::<_, PanicMessage>::decode(&mut &buf[..], &mut ());
-
-                    bridge.cached_buffer = buf;
-
-                    r.unwrap_or_else(|e| panic::resume_unwind(e.into()))
-                })
-            })*
-        })*
-    }
-}
-with_api!(self, self, define_client_side);
-
-enum BridgeState<'a> {
-    /// No server is currently connected to this client.
-    NotConnected,
-
-    /// A server is connected and available for requests.
-    Connected(Bridge<'a>),
-
-    /// Access to the bridge is being exclusively acquired
-    /// (e.g., during `BridgeState::with`).
-    InUse,
-}
-
-enum BridgeStateL {}
-
-impl<'a> scoped_cell::ApplyL<'a> for BridgeStateL {
-    type Out = BridgeState<'a>;
-}
-
-thread_local! {
-    static BRIDGE_STATE: scoped_cell::ScopedCell<BridgeStateL> =
-        scoped_cell::ScopedCell::new(BridgeState::NotConnected);
-}
-
-impl BridgeState<'_> {
-    /// Take exclusive control of the thread-local
-    /// `BridgeState`, and pass it to `f`, mutably.
-    /// The state will be restored after `f` exits, even
-    /// by panic, including modifications made to it by `f`.
-    ///
-    /// N.B., while `f` is running, the thread-local state
-    /// is `BridgeState::InUse`.
-    fn with<R>(f: impl FnOnce(&mut BridgeState<'_>) -> R) -> R {
-        BRIDGE_STATE.with(|state| {
-            state.replace(BridgeState::InUse, |mut state| {
-                // FIXME(#52812) pass `f` directly to `replace` when `RefMutL` is gone
-                f(&mut state)
-            })
-        })
-    }
-}
-
-impl Bridge<'_> {
-    pub(crate) fn is_available() -> bool {
-        BridgeState::with(|state| match state {
-            BridgeState::Connected(_) | BridgeState::InUse => true,
-            BridgeState::NotConnected => false,
-        })
-    }
-
-    fn enter<R>(self, f: impl FnOnce() -> R) -> R {
-        let force_show_panics = self.force_show_panics;
-        // Hide the default panic output within `proc_macro` expansions.
-        // NB. the server can't do this because it may use a different libstd.
-        static HIDE_PANICS_DURING_EXPANSION: Once = Once::new();
-        HIDE_PANICS_DURING_EXPANSION.call_once(|| {
-            let prev = panic::take_hook();
-            panic::set_hook(Box::new(move |info| {
-                let show = BridgeState::with(|state| match state {
-                    BridgeState::NotConnected => true,
-                    BridgeState::Connected(_) | BridgeState::InUse => force_show_panics,
-                });
-                if show {
-                    prev(info)
-                }
-            }));
-        });
-
-        BRIDGE_STATE.with(|state| state.set(BridgeState::Connected(self), f))
-    }
-
-    fn with<R>(f: impl FnOnce(&mut Bridge<'_>) -> R) -> R {
-        BridgeState::with(|state| match state {
-            BridgeState::NotConnected => {
-                panic!("procedural macro API is used outside of a procedural macro");
-            }
-            BridgeState::InUse => {
-                panic!("procedural macro API is used while it's already in use");
-            }
-            BridgeState::Connected(bridge) => f(bridge),
-        })
-    }
-}
-
-/// A client-side RPC entry-point, which may be using a different `proc_macro`
-/// from the one used by the server, but can be invoked compatibly.
-///
-/// Note that the (phantom) `I` ("input") and `O` ("output") type parameters
-/// decorate the `Client<I, O>` with the RPC "interface" of the entry-point, but
-/// do not themselves participate in ABI, at all, only facilitate type-checking.
-///
-/// E.g. `Client<TokenStream, TokenStream>` is the common proc macro interface,
-/// used for `#[proc_macro] fn foo(input: TokenStream) -> TokenStream`,
-/// indicating that the RPC input and output will be serialized token streams,
-/// and forcing the use of APIs that take/return `S::TokenStream`, server-side.
-#[repr(C)]
-pub struct Client<I, O> {
-    // FIXME(eddyb) use a reference to the `static COUNTERS`, instead of
-    // a wrapper `fn` pointer, once `const fn` can reference `static`s.
-    pub(super) get_handle_counters: extern "C" fn() -> &'static HandleCounters,
-
-    pub(super) run: extern "C" fn(Bridge<'_>) -> Buffer,
-
-    pub(super) _marker: PhantomData<fn(I) -> O>,
-}
-
-impl<I, O> Copy for Client<I, O> {}
-impl<I, O> Clone for Client<I, O> {
-    fn clone(&self) -> Self {
-        *self
-    }
-}
-
-/// Client-side helper for handling client panics, entering the bridge,
-/// deserializing input and serializing output.
-// FIXME(eddyb) maybe replace `Bridge::enter` with this?
-fn run_client<A: for<'a, 's> DecodeMut<'a, 's, ()>, R: Encode<()>>(
-    mut bridge: Bridge<'_>,
-    f: impl FnOnce(A) -> R,
-) -> Buffer {
-    // The initial `cached_buffer` contains the input.
-    let mut buf = bridge.cached_buffer.take();
-
-    panic::catch_unwind(panic::AssertUnwindSafe(|| {
-        bridge.enter(|| {
-            let reader = &mut &buf[..];
-            let input = A::decode(reader, &mut ());
-
-            // Put the `cached_buffer` back in the `Bridge`, for requests.
-            Bridge::with(|bridge| bridge.cached_buffer = buf.take());
-
-            let output = f(input);
-
-            // Take the `cached_buffer` back out, for the output value.
-            buf = Bridge::with(|bridge| bridge.cached_buffer.take());
-
-            // HACK(eddyb) Separate encoding a success value (`Ok(output)`)
-            // from encoding a panic (`Err(e: PanicMessage)`) to avoid
-            // having handles outside the `bridge.enter(|| ...)` scope, and
-            // to catch panics that could happen while encoding the success.
-            //
-            // Note that panics should be impossible beyond this point, but
-            // this is defensively trying to avoid any accidental panicking
-            // reaching the `extern "C"` (which should `abort` but might not
-            // at the moment, so this is also potentially preventing UB).
-            buf.clear();
-            Ok::<_, ()>(output).encode(&mut buf, &mut ());
-        })
-    }))
-    .map_err(PanicMessage::from)
-    .unwrap_or_else(|e| {
-        buf.clear();
-        Err::<(), _>(e).encode(&mut buf, &mut ());
-    });
-    buf
-}
-
-impl Client<super::super::TokenStream, super::super::TokenStream> {
-    pub const fn expand1(
-        f: impl Fn(super::super::TokenStream) -> super::super::TokenStream + Copy,
-    ) -> Self {
-        Client {
-            get_handle_counters: HandleCounters::get,
-            run: super::selfless_reify::reify_to_extern_c_fn_hrt_bridge(move |bridge| {
-                run_client(bridge, |input| f(super::super::TokenStream(input)).0)
-            }),
-            _marker: PhantomData,
-        }
-    }
-}
-
-impl Client<(super::super::TokenStream, super::super::TokenStream), super::super::TokenStream> {
-    pub const fn expand2(
-        f: impl Fn(super::super::TokenStream, super::super::TokenStream) -> super::super::TokenStream
-            + Copy,
-    ) -> Self {
-        Client {
-            get_handle_counters: HandleCounters::get,
-            run: super::selfless_reify::reify_to_extern_c_fn_hrt_bridge(move |bridge| {
-                run_client(bridge, |(input, input2)| {
-                    f(super::super::TokenStream(input), super::super::TokenStream(input2)).0
-                })
-            }),
-            _marker: PhantomData,
-        }
-    }
-}
-
-#[repr(C)]
-#[derive(Copy, Clone)]
-pub enum ProcMacro {
-    CustomDerive {
-        trait_name: &'static str,
-        attributes: &'static [&'static str],
-        client: Client<super::super::TokenStream, super::super::TokenStream>,
-    },
-
-    Attr {
-        name: &'static str,
-        client: Client<
-            (super::super::TokenStream, super::super::TokenStream),
-            super::super::TokenStream,
-        >,
-    },
-
-    Bang {
-        name: &'static str,
-        client: Client<super::super::TokenStream, super::super::TokenStream>,
-    },
-}
-
-impl ProcMacro {
-    pub fn name(&self) -> &'static str {
-        match self {
-            ProcMacro::CustomDerive { trait_name, .. } => trait_name,
-            ProcMacro::Attr { name, .. } => name,
-            ProcMacro::Bang { name, .. } => name,
-        }
-    }
-
-    pub const fn custom_derive(
-        trait_name: &'static str,
-        attributes: &'static [&'static str],
-        expand: impl Fn(super::super::TokenStream) -> super::super::TokenStream + Copy,
-    ) -> Self {
-        ProcMacro::CustomDerive { trait_name, attributes, client: Client::expand1(expand) }
-    }
-
-    pub const fn attr(
-        name: &'static str,
-        expand: impl Fn(super::super::TokenStream, super::super::TokenStream) -> super::super::TokenStream
-            + Copy,
-    ) -> Self {
-        ProcMacro::Attr { name, client: Client::expand2(expand) }
-    }
-
-    pub const fn bang(
-        name: &'static str,
-        expand: impl Fn(super::super::TokenStream) -> super::super::TokenStream + Copy,
-    ) -> Self {
-        ProcMacro::Bang { name, client: Client::expand1(expand) }
-    }
-}
diff --git a/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/closure.rs b/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/closure.rs
deleted file mode 100644
index d371ae3cea0..00000000000
--- a/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/closure.rs
+++ /dev/null
@@ -1,32 +0,0 @@
-//! Closure type (equivalent to `&mut dyn FnMut(A) -> R`) that's `repr(C)`.
-
-use std::marker::PhantomData;
-
-#[repr(C)]
-pub struct Closure<'a, A, R> {
-    call: unsafe extern "C" fn(*mut Env, A) -> R,
-    env: *mut Env,
-    // Prevent Send and Sync impls. `!Send`/`!Sync` is the usual way of doing
-    // this, but that requires unstable features. rust-analyzer uses this code
-    // and avoids unstable features.
-    //
-    // The `'a` lifetime parameter represents the lifetime of `Env`.
-    _marker: PhantomData<*mut &'a mut ()>,
-}
-
-struct Env;
-
-impl<'a, A, R, F: FnMut(A) -> R> From<&'a mut F> for Closure<'a, A, R> {
-    fn from(f: &'a mut F) -> Self {
-        unsafe extern "C" fn call<A, R, F: FnMut(A) -> R>(env: *mut Env, arg: A) -> R {
-            (*(env as *mut _ as *mut F))(arg)
-        }
-        Closure { call: call::<A, R, F>, env: f as *mut _ as *mut Env, _marker: PhantomData }
-    }
-}
-
-impl<'a, A, R> Closure<'a, A, R> {
-    pub fn call(&mut self, arg: A) -> R {
-        unsafe { (self.call)(self.env, arg) }
-    }
-}
diff --git a/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/handle.rs b/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/handle.rs
deleted file mode 100644
index c219a9465d3..00000000000
--- a/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/handle.rs
+++ /dev/null
@@ -1,89 +0,0 @@
-//! Server-side handles and storage for per-handle data.
-
-use std::collections::{BTreeMap, HashMap};
-use std::hash::{BuildHasher, Hash};
-use std::num::NonZeroU32;
-use std::ops::{Index, IndexMut};
-use std::sync::atomic::{AtomicUsize, Ordering};
-
-pub(super) type Handle = NonZeroU32;
-
-/// A store that associates values of type `T` with numeric handles. A value can
-/// be looked up using its handle.
-pub(super) struct OwnedStore<T: 'static> {
-    counter: &'static AtomicUsize,
-    data: BTreeMap<Handle, T>,
-}
-
-impl<T> OwnedStore<T> {
-    pub(super) fn new(counter: &'static AtomicUsize) -> Self {
-        // Ensure the handle counter isn't 0, which would panic later,
-        // when `NonZeroU32::new` (aka `Handle::new`) is called in `alloc`.
-        assert_ne!(counter.load(Ordering::SeqCst), 0);
-
-        OwnedStore { counter, data: BTreeMap::new() }
-    }
-}
-
-impl<T> OwnedStore<T> {
-    pub(super) fn alloc(&mut self, x: T) -> Handle {
-        let counter = self.counter.fetch_add(1, Ordering::SeqCst);
-        let handle = Handle::new(counter as u32).expect("`proc_macro` handle counter overflowed");
-        assert!(self.data.insert(handle, x).is_none());
-        handle
-    }
-
-    pub(super) fn take(&mut self, h: Handle) -> T {
-        self.data.remove(&h).expect("use-after-free in `proc_macro` handle")
-    }
-}
-
-impl<T> Index<Handle> for OwnedStore<T> {
-    type Output = T;
-    fn index(&self, h: Handle) -> &T {
-        self.data.get(&h).expect("use-after-free in `proc_macro` handle")
-    }
-}
-
-impl<T> IndexMut<Handle> for OwnedStore<T> {
-    fn index_mut(&mut self, h: Handle) -> &mut T {
-        self.data.get_mut(&h).expect("use-after-free in `proc_macro` handle")
-    }
-}
-
-// HACK(eddyb) deterministic `std::collections::hash_map::RandomState` replacement
-// that doesn't require adding any dependencies to `proc_macro` (like `rustc-hash`).
-#[derive(Clone)]
-struct NonRandomState;
-
-impl BuildHasher for NonRandomState {
-    type Hasher = std::collections::hash_map::DefaultHasher;
-    #[inline]
-    fn build_hasher(&self) -> Self::Hasher {
-        Self::Hasher::new()
-    }
-}
-
-/// Like `OwnedStore`, but avoids storing any value more than once.
-pub(super) struct InternedStore<T: 'static> {
-    owned: OwnedStore<T>,
-    interner: HashMap<T, Handle, NonRandomState>,
-}
-
-impl<T: Copy + Eq + Hash> InternedStore<T> {
-    pub(super) fn new(counter: &'static AtomicUsize) -> Self {
-        InternedStore {
-            owned: OwnedStore::new(counter),
-            interner: HashMap::with_hasher(NonRandomState),
-        }
-    }
-
-    pub(super) fn alloc(&mut self, x: T) -> Handle {
-        let owned = &mut self.owned;
-        *self.interner.entry(x).or_insert_with(|| owned.alloc(x))
-    }
-
-    pub(super) fn copy(&mut self, h: Handle) -> T {
-        self.owned[h]
-    }
-}
diff --git a/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/mod.rs b/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/mod.rs
deleted file mode 100644
index 4967da4931a..00000000000
--- a/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/mod.rs
+++ /dev/null
@@ -1,451 +0,0 @@
-//! Internal interface for communicating between a `proc_macro` client
-//! (a proc macro crate) and a `proc_macro` server (a compiler front-end).
-//!
-//! Serialization (with C ABI buffers) and unique integer handles are employed
-//! to allow safely interfacing between two copies of `proc_macro` built
-//! (from the same source) by different compilers with potentially mismatching
-//! Rust ABIs (e.g., stage0/bin/rustc vs stage1/bin/rustc during bootstrap).
-
-#![deny(unsafe_code)]
-
-pub use super::{Delimiter, Level, LineColumn, Spacing};
-use std::fmt;
-use std::hash::Hash;
-use std::marker;
-use std::mem;
-use std::ops::Bound;
-use std::panic;
-use std::sync::atomic::AtomicUsize;
-use std::sync::Once;
-use std::thread;
-
-/// Higher-order macro describing the server RPC API, allowing automatic
-/// generation of type-safe Rust APIs, both client-side and server-side.
-///
-/// `with_api!(MySelf, my_self, my_macro)` expands to:
-/// ```rust,ignore (pseudo-code)
-/// my_macro! {
-///     // ...
-///     Literal {
-///         // ...
-///         fn character(ch: char) -> MySelf::Literal;
-///         // ...
-///         fn span(my_self: &MySelf::Literal) -> MySelf::Span;
-///         fn set_span(my_self: &mut MySelf::Literal, span: MySelf::Span);
-///     },
-///     // ...
-/// }
-/// ```
-///
-/// The first two arguments serve to customize the arguments names
-/// and argument/return types, to enable several different usecases:
-///
-/// If `my_self` is just `self`, then each `fn` signature can be used
-/// as-is for a method. If it's anything else (`self_` in practice),
-/// then the signatures don't have a special `self` argument, and
-/// can, therefore, have a different one introduced.
-///
-/// If `MySelf` is just `Self`, then the types are only valid inside
-/// a trait or a trait impl, where the trait has associated types
-/// for each of the API types. If non-associated types are desired,
-/// a module name (`self` in practice) can be used instead of `Self`.
-macro_rules! with_api {
-    ($S:ident, $self:ident, $m:ident) => {
-        $m! {
-            FreeFunctions {
-                fn drop($self: $S::FreeFunctions);
-                fn track_env_var(var: &str, value: Option<&str>);
-                fn track_path(path: &str);
-            },
-            TokenStream {
-                fn drop($self: $S::TokenStream);
-                fn clone($self: &$S::TokenStream) -> $S::TokenStream;
-                fn is_empty($self: &$S::TokenStream) -> bool;
-                fn expand_expr($self: &$S::TokenStream) -> Result<$S::TokenStream, ()>;
-                fn from_str(src: &str) -> $S::TokenStream;
-                fn to_string($self: &$S::TokenStream) -> String;
-                fn from_token_tree(
-                    tree: TokenTree<$S::Group, $S::Punct, $S::Ident, $S::Literal>,
-                ) -> $S::TokenStream;
-                fn concat_trees(
-                    base: Option<$S::TokenStream>,
-                    trees: Vec<TokenTree<$S::Group, $S::Punct, $S::Ident, $S::Literal>>,
-                ) -> $S::TokenStream;
-                fn concat_streams(
-                    base: Option<$S::TokenStream>,
-                    streams: Vec<$S::TokenStream>,
-                ) -> $S::TokenStream;
-                fn into_trees(
-                    $self: $S::TokenStream
-                ) -> Vec<TokenTree<$S::Group, $S::Punct, $S::Ident, $S::Literal>>;
-            },
-            Group {
-                fn drop($self: $S::Group);
-                fn clone($self: &$S::Group) -> $S::Group;
-                fn new(delimiter: Delimiter, stream: Option<$S::TokenStream>) -> $S::Group;
-                fn delimiter($self: &$S::Group) -> Delimiter;
-                fn stream($self: &$S::Group) -> $S::TokenStream;
-                fn span($self: &$S::Group) -> $S::Span;
-                fn span_open($self: &$S::Group) -> $S::Span;
-                fn span_close($self: &$S::Group) -> $S::Span;
-                fn set_span($self: &mut $S::Group, span: $S::Span);
-            },
-            Punct {
-                fn new(ch: char, spacing: Spacing) -> $S::Punct;
-                fn as_char($self: $S::Punct) -> char;
-                fn spacing($self: $S::Punct) -> Spacing;
-                fn span($self: $S::Punct) -> $S::Span;
-                fn with_span($self: $S::Punct, span: $S::Span) -> $S::Punct;
-            },
-            Ident {
-                fn new(string: &str, span: $S::Span, is_raw: bool) -> $S::Ident;
-                fn span($self: $S::Ident) -> $S::Span;
-                fn with_span($self: $S::Ident, span: $S::Span) -> $S::Ident;
-            },
-            Literal {
-                fn drop($self: $S::Literal);
-                fn clone($self: &$S::Literal) -> $S::Literal;
-                fn from_str(s: &str) -> Result<$S::Literal, ()>;
-                fn to_string($self: &$S::Literal) -> String;
-                fn debug_kind($self: &$S::Literal) -> String;
-                fn symbol($self: &$S::Literal) -> String;
-                fn suffix($self: &$S::Literal) -> Option<String>;
-                fn integer(n: &str) -> $S::Literal;
-                fn typed_integer(n: &str, kind: &str) -> $S::Literal;
-                fn float(n: &str) -> $S::Literal;
-                fn f32(n: &str) -> $S::Literal;
-                fn f64(n: &str) -> $S::Literal;
-                fn string(string: &str) -> $S::Literal;
-                fn character(ch: char) -> $S::Literal;
-                fn byte_string(bytes: &[u8]) -> $S::Literal;
-                fn span($self: &$S::Literal) -> $S::Span;
-                fn set_span($self: &mut $S::Literal, span: $S::Span);
-                fn subspan(
-                    $self: &$S::Literal,
-                    start: Bound<usize>,
-                    end: Bound<usize>,
-                ) -> Option<$S::Span>;
-            },
-            SourceFile {
-                fn drop($self: $S::SourceFile);
-                fn clone($self: &$S::SourceFile) -> $S::SourceFile;
-                fn eq($self: &$S::SourceFile, other: &$S::SourceFile) -> bool;
-                fn path($self: &$S::SourceFile) -> String;
-                fn is_real($self: &$S::SourceFile) -> bool;
-            },
-            MultiSpan {
-                fn drop($self: $S::MultiSpan);
-                fn new() -> $S::MultiSpan;
-                fn push($self: &mut $S::MultiSpan, span: $S::Span);
-            },
-            Diagnostic {
-                fn drop($self: $S::Diagnostic);
-                fn new(level: Level, msg: &str, span: $S::MultiSpan) -> $S::Diagnostic;
-                fn sub(
-                    $self: &mut $S::Diagnostic,
-                    level: Level,
-                    msg: &str,
-                    span: $S::MultiSpan,
-                );
-                fn emit($self: $S::Diagnostic);
-            },
-            Span {
-                fn debug($self: $S::Span) -> String;
-                fn def_site() -> $S::Span;
-                fn call_site() -> $S::Span;
-                fn mixed_site() -> $S::Span;
-                fn source_file($self: $S::Span) -> $S::SourceFile;
-                fn parent($self: $S::Span) -> Option<$S::Span>;
-                fn source($self: $S::Span) -> $S::Span;
-                fn start($self: $S::Span) -> LineColumn;
-                fn end($self: $S::Span) -> LineColumn;
-                fn before($self: $S::Span) -> $S::Span;
-                fn after($self: $S::Span) -> $S::Span;
-                fn join($self: $S::Span, other: $S::Span) -> Option<$S::Span>;
-                fn resolved_at($self: $S::Span, at: $S::Span) -> $S::Span;
-                fn source_text($self: $S::Span) -> Option<String>;
-                fn save_span($self: $S::Span) -> usize;
-                fn recover_proc_macro_span(id: usize) -> $S::Span;
-            },
-        }
-    };
-}
-
-// FIXME(eddyb) this calls `encode` for each argument, but in reverse,
-// to match the ordering in `reverse_decode`.
-macro_rules! reverse_encode {
-    ($writer:ident;) => {};
-    ($writer:ident; $first:ident $(, $rest:ident)*) => {
-        reverse_encode!($writer; $($rest),*);
-        $first.encode(&mut $writer, &mut ());
-    }
-}
-
-// FIXME(eddyb) this calls `decode` for each argument, but in reverse,
-// to avoid borrow conflicts from borrows started by `&mut` arguments.
-macro_rules! reverse_decode {
-    ($reader:ident, $s:ident;) => {};
-    ($reader:ident, $s:ident; $first:ident: $first_ty:ty $(, $rest:ident: $rest_ty:ty)*) => {
-        reverse_decode!($reader, $s; $($rest: $rest_ty),*);
-        let $first = <$first_ty>::decode(&mut $reader, $s);
-    }
-}
-
-#[allow(unsafe_code)]
-mod buffer;
-#[forbid(unsafe_code)]
-pub mod client;
-#[allow(unsafe_code)]
-mod closure;
-#[forbid(unsafe_code)]
-mod handle;
-#[macro_use]
-#[forbid(unsafe_code)]
-mod rpc;
-#[allow(unsafe_code)]
-mod scoped_cell;
-#[allow(unsafe_code)]
-mod selfless_reify;
-#[forbid(unsafe_code)]
-pub mod server;
-
-use buffer::Buffer;
-pub use rpc::PanicMessage;
-use rpc::{Decode, DecodeMut, Encode, Reader, Writer};
-
-/// An active connection between a server and a client.
-/// The server creates the bridge (`Bridge::run_server` in `server.rs`),
-/// then passes it to the client through the function pointer in the `run`
-/// field of `client::Client`. The client holds its copy of the `Bridge`
-/// in TLS during its execution (`Bridge::{enter, with}` in `client.rs`).
-#[repr(C)]
-pub struct Bridge<'a> {
-    /// Reusable buffer (only `clear`-ed, never shrunk), primarily
-    /// used for making requests, but also for passing input to client.
-    cached_buffer: Buffer,
-
-    /// Server-side function that the client uses to make requests.
-    dispatch: closure::Closure<'a, Buffer, Buffer>,
-
-    /// If 'true', always invoke the default panic hook
-    force_show_panics: bool,
-
-    // Prevent Send and Sync impls. `!Send`/`!Sync` is the usual way of doing
-    // this, but that requires unstable features. rust-analyzer uses this code
-    // and avoids unstable features.
-    _marker: marker::PhantomData<*mut ()>,
-}
-
-#[forbid(unsafe_code)]
-#[allow(non_camel_case_types)]
-mod api_tags {
-    use super::rpc::{DecodeMut, Encode, Reader, Writer};
-
-    macro_rules! declare_tags {
-        ($($name:ident {
-            $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)*;)*
-        }),* $(,)?) => {
-            $(
-                pub(super) enum $name {
-                    $($method),*
-                }
-                rpc_encode_decode!(enum $name { $($method),* });
-            )*
-
-            pub(super) enum Method {
-                $($name($name)),*
-            }
-            rpc_encode_decode!(enum Method { $($name(m)),* });
-        }
-    }
-    with_api!(self, self, declare_tags);
-}
-
-/// Helper to wrap associated types to allow trait impl dispatch.
-/// That is, normally a pair of impls for `T::Foo` and `T::Bar`
-/// can overlap, but if the impls are, instead, on types like
-/// `Marked<T::Foo, Foo>` and `Marked<T::Bar, Bar>`, they can't.
-trait Mark {
-    type Unmarked;
-    fn mark(unmarked: Self::Unmarked) -> Self;
-}
-
-/// Unwrap types wrapped by `Mark::mark` (see `Mark` for details).
-trait Unmark {
-    type Unmarked;
-    fn unmark(self) -> Self::Unmarked;
-}
-
-#[derive(Copy, Clone, PartialEq, Eq, Hash)]
-struct Marked<T, M> {
-    value: T,
-    _marker: marker::PhantomData<M>,
-}
-
-impl<T, M> Mark for Marked<T, M> {
-    type Unmarked = T;
-    fn mark(unmarked: Self::Unmarked) -> Self {
-        Marked { value: unmarked, _marker: marker::PhantomData }
-    }
-}
-impl<T, M> Unmark for Marked<T, M> {
-    type Unmarked = T;
-    fn unmark(self) -> Self::Unmarked {
-        self.value
-    }
-}
-impl<'a, T, M> Unmark for &'a Marked<T, M> {
-    type Unmarked = &'a T;
-    fn unmark(self) -> Self::Unmarked {
-        &self.value
-    }
-}
-impl<'a, T, M> Unmark for &'a mut Marked<T, M> {
-    type Unmarked = &'a mut T;
-    fn unmark(self) -> Self::Unmarked {
-        &mut self.value
-    }
-}
-
-impl<T: Mark> Mark for Vec<T> {
-    type Unmarked = Vec<T::Unmarked>;
-    fn mark(unmarked: Self::Unmarked) -> Self {
-        // Should be a no-op due to std's in-place collect optimizations.
-        unmarked.into_iter().map(T::mark).collect()
-    }
-}
-impl<T: Unmark> Unmark for Vec<T> {
-    type Unmarked = Vec<T::Unmarked>;
-    fn unmark(self) -> Self::Unmarked {
-        // Should be a no-op due to std's in-place collect optimizations.
-        self.into_iter().map(T::unmark).collect()
-    }
-}
-
-macro_rules! mark_noop {
-    ($($ty:ty),* $(,)?) => {
-        $(
-            impl Mark for $ty {
-                type Unmarked = Self;
-                fn mark(unmarked: Self::Unmarked) -> Self {
-                    unmarked
-                }
-            }
-            impl Unmark for $ty {
-                type Unmarked = Self;
-                fn unmark(self) -> Self::Unmarked {
-                    self
-                }
-            }
-        )*
-    }
-}
-mark_noop! {
-    (),
-    bool,
-    char,
-    &'_ [u8],
-    &'_ str,
-    String,
-    usize,
-    Delimiter,
-    Level,
-    LineColumn,
-    Spacing,
-}
-
-rpc_encode_decode!(
-    enum Delimiter {
-        Parenthesis,
-        Brace,
-        Bracket,
-        None,
-    }
-);
-rpc_encode_decode!(
-    enum Level {
-        Error,
-        Warning,
-        Note,
-        Help,
-    }
-);
-rpc_encode_decode!(struct LineColumn { line, column });
-rpc_encode_decode!(
-    enum Spacing {
-        Alone,
-        Joint,
-    }
-);
-
-macro_rules! mark_compound {
-    (enum $name:ident <$($T:ident),+> { $($variant:ident $(($field:ident))?),* $(,)? }) => {
-        impl<$($T: Mark),+> Mark for $name <$($T),+> {
-            type Unmarked = $name <$($T::Unmarked),+>;
-            fn mark(unmarked: Self::Unmarked) -> Self {
-                match unmarked {
-                    $($name::$variant $(($field))? => {
-                        $name::$variant $((Mark::mark($field)))?
-                    })*
-                }
-            }
-        }
-
-        impl<$($T: Unmark),+> Unmark for $name <$($T),+> {
-            type Unmarked = $name <$($T::Unmarked),+>;
-            fn unmark(self) -> Self::Unmarked {
-                match self {
-                    $($name::$variant $(($field))? => {
-                        $name::$variant $((Unmark::unmark($field)))?
-                    })*
-                }
-            }
-        }
-    }
-}
-
-macro_rules! compound_traits {
-    ($($t:tt)*) => {
-        rpc_encode_decode!($($t)*);
-        mark_compound!($($t)*);
-    };
-}
-
-compound_traits!(
-    enum Bound<T> {
-        Included(x),
-        Excluded(x),
-        Unbounded,
-    }
-);
-
-compound_traits!(
-    enum Option<T> {
-        Some(t),
-        None,
-    }
-);
-
-compound_traits!(
-    enum Result<T, E> {
-        Ok(t),
-        Err(e),
-    }
-);
-
-#[derive(Clone)]
-pub enum TokenTree<G, P, I, L> {
-    Group(G),
-    Punct(P),
-    Ident(I),
-    Literal(L),
-}
-
-compound_traits!(
-    enum TokenTree<G, P, I, L> {
-        Group(tt),
-        Punct(tt),
-        Ident(tt),
-        Literal(tt),
-    }
-);
diff --git a/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/rpc.rs b/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/rpc.rs
deleted file mode 100644
index e9d7a46c06f..00000000000
--- a/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/rpc.rs
+++ /dev/null
@@ -1,304 +0,0 @@
-//! Serialization for client-server communication.
-
-use std::any::Any;
-use std::char;
-use std::io::Write;
-use std::num::NonZeroU32;
-use std::str;
-
-pub(super) type Writer = super::buffer::Buffer;
-
-pub(super) trait Encode<S>: Sized {
-    fn encode(self, w: &mut Writer, s: &mut S);
-}
-
-pub(super) type Reader<'a> = &'a [u8];
-
-pub(super) trait Decode<'a, 's, S>: Sized {
-    fn decode(r: &mut Reader<'a>, s: &'s S) -> Self;
-}
-
-pub(super) trait DecodeMut<'a, 's, S>: Sized {
-    fn decode(r: &mut Reader<'a>, s: &'s mut S) -> Self;
-}
-
-macro_rules! rpc_encode_decode {
-    (le $ty:ty) => {
-        impl<S> Encode<S> for $ty {
-            fn encode(self, w: &mut Writer, _: &mut S) {
-                w.extend_from_array(&self.to_le_bytes());
-            }
-        }
-
-        impl<S> DecodeMut<'_, '_, S> for $ty {
-            fn decode(r: &mut Reader<'_>, _: &mut S) -> Self {
-                const N: usize = ::std::mem::size_of::<$ty>();
-
-                let mut bytes = [0; N];
-                bytes.copy_from_slice(&r[..N]);
-                *r = &r[N..];
-
-                Self::from_le_bytes(bytes)
-            }
-        }
-    };
-    (struct $name:ident $(<$($T:ident),+>)? { $($field:ident),* $(,)? }) => {
-        impl<S, $($($T: Encode<S>),+)?> Encode<S> for $name $(<$($T),+>)? {
-            fn encode(self, w: &mut Writer, s: &mut S) {
-                $(self.$field.encode(w, s);)*
-            }
-        }
-
-        impl<'a, S, $($($T: for<'s> DecodeMut<'a, 's, S>),+)?> DecodeMut<'a, '_, S>
-            for $name $(<$($T),+>)?
-        {
-            fn decode(r: &mut Reader<'a>, s: &mut S) -> Self {
-                $name {
-                    $($field: DecodeMut::decode(r, s)),*
-                }
-            }
-        }
-    };
-    (enum $name:ident $(<$($T:ident),+>)? { $($variant:ident $(($field:ident))*),* $(,)? }) => {
-        impl<S, $($($T: Encode<S>),+)?> Encode<S> for $name $(<$($T),+>)? {
-            fn encode(self, w: &mut Writer, s: &mut S) {
-                // HACK(eddyb): `Tag` enum duplicated between the
-                // two impls as there's no other place to stash it.
-                #[allow(non_upper_case_globals)]
-                mod tag {
-                    #[repr(u8)] enum Tag { $($variant),* }
-
-                    $(pub const $variant: u8 = Tag::$variant as u8;)*
-                }
-
-                match self {
-                    $($name::$variant $(($field))* => {
-                        tag::$variant.encode(w, s);
-                        $($field.encode(w, s);)*
-                    })*
-                }
-            }
-        }
-
-        impl<'a, S, $($($T: for<'s> DecodeMut<'a, 's, S>),+)?> DecodeMut<'a, '_, S>
-            for $name $(<$($T),+>)?
-        {
-            fn decode(r: &mut Reader<'a>, s: &mut S) -> Self {
-                // HACK(eddyb): `Tag` enum duplicated between the
-                // two impls as there's no other place to stash it.
-                #[allow(non_upper_case_globals)]
-                mod tag {
-                    #[repr(u8)] enum Tag { $($variant),* }
-
-                    $(pub const $variant: u8 = Tag::$variant as u8;)*
-                }
-
-                match u8::decode(r, s) {
-                    $(tag::$variant => {
-                        $(let $field = DecodeMut::decode(r, s);)*
-                        $name::$variant $(($field))*
-                    })*
-                    _ => unreachable!(),
-                }
-            }
-        }
-    }
-}
-
-impl<S> Encode<S> for () {
-    fn encode(self, _: &mut Writer, _: &mut S) {}
-}
-
-impl<S> DecodeMut<'_, '_, S> for () {
-    fn decode(_: &mut Reader<'_>, _: &mut S) -> Self {}
-}
-
-impl<S> Encode<S> for u8 {
-    fn encode(self, w: &mut Writer, _: &mut S) {
-        w.push(self);
-    }
-}
-
-impl<S> DecodeMut<'_, '_, S> for u8 {
-    fn decode(r: &mut Reader<'_>, _: &mut S) -> Self {
-        let x = r[0];
-        *r = &r[1..];
-        x
-    }
-}
-
-rpc_encode_decode!(le u32);
-rpc_encode_decode!(le usize);
-
-impl<S> Encode<S> for bool {
-    fn encode(self, w: &mut Writer, s: &mut S) {
-        (self as u8).encode(w, s);
-    }
-}
-
-impl<S> DecodeMut<'_, '_, S> for bool {
-    fn decode(r: &mut Reader<'_>, s: &mut S) -> Self {
-        match u8::decode(r, s) {
-            0 => false,
-            1 => true,
-            _ => unreachable!(),
-        }
-    }
-}
-
-impl<S> Encode<S> for char {
-    fn encode(self, w: &mut Writer, s: &mut S) {
-        (self as u32).encode(w, s);
-    }
-}
-
-impl<S> DecodeMut<'_, '_, S> for char {
-    fn decode(r: &mut Reader<'_>, s: &mut S) -> Self {
-        char::from_u32(u32::decode(r, s)).unwrap()
-    }
-}
-
-impl<S> Encode<S> for NonZeroU32 {
-    fn encode(self, w: &mut Writer, s: &mut S) {
-        self.get().encode(w, s);
-    }
-}
-
-impl<S> DecodeMut<'_, '_, S> for NonZeroU32 {
-    fn decode(r: &mut Reader<'_>, s: &mut S) -> Self {
-        Self::new(u32::decode(r, s)).unwrap()
-    }
-}
-
-impl<S, A: Encode<S>, B: Encode<S>> Encode<S> for (A, B) {
-    fn encode(self, w: &mut Writer, s: &mut S) {
-        self.0.encode(w, s);
-        self.1.encode(w, s);
-    }
-}
-
-impl<'a, S, A: for<'s> DecodeMut<'a, 's, S>, B: for<'s> DecodeMut<'a, 's, S>> DecodeMut<'a, '_, S>
-    for (A, B)
-{
-    fn decode(r: &mut Reader<'a>, s: &mut S) -> Self {
-        (DecodeMut::decode(r, s), DecodeMut::decode(r, s))
-    }
-}
-
-impl<S> Encode<S> for &[u8] {
-    fn encode(self, w: &mut Writer, s: &mut S) {
-        self.len().encode(w, s);
-        w.write_all(self).unwrap();
-    }
-}
-
-impl<'a, S> DecodeMut<'a, '_, S> for &'a [u8] {
-    fn decode(r: &mut Reader<'a>, s: &mut S) -> Self {
-        let len = usize::decode(r, s);
-        let xs = &r[..len];
-        *r = &r[len..];
-        xs
-    }
-}
-
-impl<S> Encode<S> for &str {
-    fn encode(self, w: &mut Writer, s: &mut S) {
-        self.as_bytes().encode(w, s);
-    }
-}
-
-impl<'a, S> DecodeMut<'a, '_, S> for &'a str {
-    fn decode(r: &mut Reader<'a>, s: &mut S) -> Self {
-        str::from_utf8(<&[u8]>::decode(r, s)).unwrap()
-    }
-}
-
-impl<S> Encode<S> for String {
-    fn encode(self, w: &mut Writer, s: &mut S) {
-        self[..].encode(w, s);
-    }
-}
-
-impl<S> DecodeMut<'_, '_, S> for String {
-    fn decode(r: &mut Reader<'_>, s: &mut S) -> Self {
-        <&str>::decode(r, s).to_string()
-    }
-}
-
-impl<S, T: Encode<S>> Encode<S> for Vec<T> {
-    fn encode(self, w: &mut Writer, s: &mut S) {
-        self.len().encode(w, s);
-        for x in self {
-            x.encode(w, s);
-        }
-    }
-}
-
-impl<'a, S, T: for<'s> DecodeMut<'a, 's, S>> DecodeMut<'a, '_, S> for Vec<T> {
-    fn decode(r: &mut Reader<'a>, s: &mut S) -> Self {
-        let len = usize::decode(r, s);
-        let mut vec = Vec::with_capacity(len);
-        for _ in 0..len {
-            vec.push(T::decode(r, s));
-        }
-        vec
-    }
-}
-
-/// Simplified version of panic payloads, ignoring
-/// types other than `&'static str` and `String`.
-pub enum PanicMessage {
-    StaticStr(&'static str),
-    String(String),
-    Unknown,
-}
-
-impl From<Box<dyn Any + Send>> for PanicMessage {
-    fn from(payload: Box<dyn Any + Send + 'static>) -> Self {
-        if let Some(s) = payload.downcast_ref::<&'static str>() {
-            return PanicMessage::StaticStr(s);
-        }
-        if let Ok(s) = payload.downcast::<String>() {
-            return PanicMessage::String(*s);
-        }
-        PanicMessage::Unknown
-    }
-}
-
-impl Into<Box<dyn Any + Send>> for PanicMessage {
-    fn into(self) -> Box<dyn Any + Send> {
-        match self {
-            PanicMessage::StaticStr(s) => Box::new(s),
-            PanicMessage::String(s) => Box::new(s),
-            PanicMessage::Unknown => {
-                struct UnknownPanicMessage;
-                Box::new(UnknownPanicMessage)
-            }
-        }
-    }
-}
-
-impl PanicMessage {
-    pub fn as_str(&self) -> Option<&str> {
-        match self {
-            PanicMessage::StaticStr(s) => Some(s),
-            PanicMessage::String(s) => Some(s),
-            PanicMessage::Unknown => None,
-        }
-    }
-}
-
-impl<S> Encode<S> for PanicMessage {
-    fn encode(self, w: &mut Writer, s: &mut S) {
-        self.as_str().encode(w, s);
-    }
-}
-
-impl<S> DecodeMut<'_, '_, S> for PanicMessage {
-    fn decode(r: &mut Reader<'_>, s: &mut S) -> Self {
-        match Option::<String>::decode(r, s) {
-            Some(s) => PanicMessage::String(s),
-            None => PanicMessage::Unknown,
-        }
-    }
-}
diff --git a/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/scoped_cell.rs b/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/scoped_cell.rs
deleted file mode 100644
index 2cde1f65adf..00000000000
--- a/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/scoped_cell.rs
+++ /dev/null
@@ -1,81 +0,0 @@
-//! `Cell` variant for (scoped) existential lifetimes.
-
-use std::cell::Cell;
-use std::mem;
-use std::ops::{Deref, DerefMut};
-
-/// Type lambda application, with a lifetime.
-#[allow(unused_lifetimes)]
-pub trait ApplyL<'a> {
-    type Out;
-}
-
-/// Type lambda taking a lifetime, i.e., `Lifetime -> Type`.
-pub trait LambdaL: for<'a> ApplyL<'a> {}
-
-impl<T: for<'a> ApplyL<'a>> LambdaL for T {}
-
-// HACK(eddyb) work around projection limitations with a newtype
-// FIXME(#52812) replace with `&'a mut <T as ApplyL<'b>>::Out`
-pub struct RefMutL<'a, 'b, T: LambdaL>(&'a mut <T as ApplyL<'b>>::Out);
-
-impl<'a, 'b, T: LambdaL> Deref for RefMutL<'a, 'b, T> {
-    type Target = <T as ApplyL<'b>>::Out;
-    fn deref(&self) -> &Self::Target {
-        self.0
-    }
-}
-
-impl<'a, 'b, T: LambdaL> DerefMut for RefMutL<'a, 'b, T> {
-    fn deref_mut(&mut self) -> &mut Self::Target {
-        self.0
-    }
-}
-
-pub struct ScopedCell<T: LambdaL>(Cell<<T as ApplyL<'static>>::Out>);
-
-impl<T: LambdaL> ScopedCell<T> {
-    pub const fn new(value: <T as ApplyL<'static>>::Out) -> Self {
-        ScopedCell(Cell::new(value))
-    }
-
-    /// Sets the value in `self` to `replacement` while
-    /// running `f`, which gets the old value, mutably.
-    /// The old value will be restored after `f` exits, even
-    /// by panic, including modifications made to it by `f`.
-    pub fn replace<'a, R>(
-        &self,
-        replacement: <T as ApplyL<'a>>::Out,
-        f: impl for<'b, 'c> FnOnce(RefMutL<'b, 'c, T>) -> R,
-    ) -> R {
-        /// Wrapper that ensures that the cell always gets filled
-        /// (with the original state, optionally changed by `f`),
-        /// even if `f` had panicked.
-        struct PutBackOnDrop<'a, T: LambdaL> {
-            cell: &'a ScopedCell<T>,
-            value: Option<<T as ApplyL<'static>>::Out>,
-        }
-
-        impl<'a, T: LambdaL> Drop for PutBackOnDrop<'a, T> {
-            fn drop(&mut self) {
-                self.cell.0.set(self.value.take().unwrap());
-            }
-        }
-
-        let mut put_back_on_drop = PutBackOnDrop {
-            cell: self,
-            value: Some(self.0.replace(unsafe {
-                let erased = mem::transmute_copy(&replacement);
-                mem::forget(replacement);
-                erased
-            })),
-        };
-
-        f(RefMutL(put_back_on_drop.value.as_mut().unwrap()))
-    }
-
-    /// Sets the value in `self` to `value` while running `f`.
-    pub fn set<R>(&self, value: <T as ApplyL<'_>>::Out, f: impl FnOnce() -> R) -> R {
-        self.replace(value, |_| f())
-    }
-}
diff --git a/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/selfless_reify.rs b/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/selfless_reify.rs
deleted file mode 100644
index 4ee4bb87c2b..00000000000
--- a/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/selfless_reify.rs
+++ /dev/null
@@ -1,83 +0,0 @@
-//! Abstraction for creating `fn` pointers from any callable that *effectively*
-//! has the equivalent of implementing `Default`, even if the compiler neither
-//! provides `Default` nor allows reifying closures (i.e. creating `fn` pointers)
-//! other than those with absolutely no captures.
-//!
-//! More specifically, for a closure-like type to be "effectively `Default`":
-//! * it must be a ZST (zero-sized type): no information contained within, so
-//!   that `Default`'s return value (if it were implemented) is unambiguous
-//! * it must be `Copy`: no captured "unique ZST tokens" or any other similar
-//!   types that would make duplicating values at will unsound
-//!   * combined with the ZST requirement, this confers a kind of "telecopy"
-//!     ability: similar to `Copy`, but without keeping the value around, and
-//!     instead "reconstructing" it (a noop given it's a ZST) when needed
-//! * it must be *provably* inhabited: no captured uninhabited types or any
-//!   other types that cannot be constructed by the user of this abstraction
-//!   * the proof is a value of the closure-like type itself, in a sense the
-//!     "seed" for the "telecopy" process made possible by ZST + `Copy`
-//!   * this requirement is the only reason an abstraction limited to a specific
-//!     usecase is required: ZST + `Copy` can be checked with *at worst* a panic
-//!     at the "attempted `::default()` call" time, but that doesn't guarantee
-//!     that the value can be soundly created, and attempting to use the typical
-//!     "proof ZST token" approach leads yet again to having a ZST + `Copy` type
-//!     that is not proof of anything without a value (i.e. isomorphic to a
-//!     newtype of the type it's trying to prove the inhabitation of)
-//!
-//! A more flexible (and safer) solution to the general problem could exist once
-//! `const`-generic parameters can have type parameters in their types:
-//!
-//! ```rust,ignore (needs future const-generics)
-//! extern "C" fn ffi_wrapper<
-//!     A, R,
-//!     F: Fn(A) -> R,
-//!     const f: F, // <-- this `const`-generic is not yet allowed
-//! >(arg: A) -> R {
-//!     f(arg)
-//! }
-//! ```
-
-use std::mem;
-
-// FIXME(eddyb) this could be `trait` impls except for the `const fn` requirement.
-macro_rules! define_reify_functions {
-    ($(
-        fn $name:ident $(<$($param:ident),*>)?
-            for $(extern $abi:tt)? fn($($arg:ident: $arg_ty:ty),*) -> $ret_ty:ty;
-    )+) => {
-        $(pub const fn $name<
-            $($($param,)*)?
-            F: Fn($($arg_ty),*) -> $ret_ty + Copy
-        >(f: F) -> $(extern $abi)? fn($($arg_ty),*) -> $ret_ty {
-            // FIXME(eddyb) describe the `F` type (e.g. via `type_name::<F>`) once panic
-            // formatting becomes possible in `const fn`.
-            assert!(mem::size_of::<F>() == 0, "selfless_reify: closure must be zero-sized");
-
-            $(extern $abi)? fn wrapper<
-                $($($param,)*)?
-                F: Fn($($arg_ty),*) -> $ret_ty + Copy
-            >($($arg: $arg_ty),*) -> $ret_ty {
-                let f = unsafe {
-                    // SAFETY: `F` satisfies all criteria for "out of thin air"
-                    // reconstructability (see module-level doc comment).
-                    mem::MaybeUninit::<F>::uninit().assume_init()
-                };
-                f($($arg),*)
-            }
-            let _f_proof = f;
-            wrapper::<
-                $($($param,)*)?
-                F
-            >
-        })+
-    }
-}
-
-define_reify_functions! {
-    fn _reify_to_extern_c_fn_unary<A, R> for extern "C" fn(arg: A) -> R;
-
-    // HACK(eddyb) this abstraction is used with `for<'a> fn(Bridge<'a>) -> T`
-    // but that doesn't work with just `reify_to_extern_c_fn_unary` because of
-    // the `fn` pointer type being "higher-ranked" (i.e. the `for<'a>` binder).
-    // FIXME(eddyb) try to remove the lifetime from `Bridge`, that'd help.
-    fn reify_to_extern_c_fn_hrt_bridge<R> for extern "C" fn(bridge: super::Bridge<'_>) -> R;
-}
diff --git a/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/server.rs b/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/server.rs
deleted file mode 100644
index 0fb3c698589..00000000000
--- a/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/server.rs
+++ /dev/null
@@ -1,332 +0,0 @@
-//! Server-side traits.
-
-use super::*;
-
-// FIXME(eddyb) generate the definition of `HandleStore` in `server.rs`.
-use super::client::HandleStore;
-
-pub trait Types {
-    type FreeFunctions: 'static;
-    type TokenStream: 'static + Clone;
-    type Group: 'static + Clone;
-    type Punct: 'static + Copy + Eq + Hash;
-    type Ident: 'static + Copy + Eq + Hash;
-    type Literal: 'static + Clone;
-    type SourceFile: 'static + Clone;
-    type MultiSpan: 'static;
-    type Diagnostic: 'static;
-    type Span: 'static + Copy + Eq + Hash;
-}
-
-/// Declare an associated fn of one of the traits below, adding necessary
-/// default bodies.
-macro_rules! associated_fn {
-    (fn drop(&mut self, $arg:ident: $arg_ty:ty)) =>
-        (fn drop(&mut self, $arg: $arg_ty) { mem::drop($arg) });
-
-    (fn clone(&mut self, $arg:ident: $arg_ty:ty) -> $ret_ty:ty) =>
-        (fn clone(&mut self, $arg: $arg_ty) -> $ret_ty { $arg.clone() });
-
-    ($($item:tt)*) => ($($item)*;)
-}
-
-macro_rules! declare_server_traits {
-    ($($name:ident {
-        $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)?;)*
-    }),* $(,)?) => {
-        $(pub trait $name: Types {
-            $(associated_fn!(fn $method(&mut self, $($arg: $arg_ty),*) $(-> $ret_ty)?);)*
-        })*
-
-        pub trait Server: Types $(+ $name)* {}
-        impl<S: Types $(+ $name)*> Server for S {}
-    }
-}
-with_api!(Self, self_, declare_server_traits);
-
-pub(super) struct MarkedTypes<S: Types>(S);
-
-macro_rules! define_mark_types_impls {
-    ($($name:ident {
-        $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)?;)*
-    }),* $(,)?) => {
-        impl<S: Types> Types for MarkedTypes<S> {
-            $(type $name = Marked<S::$name, client::$name>;)*
-        }
-
-        $(impl<S: $name> $name for MarkedTypes<S> {
-            $(fn $method(&mut self, $($arg: $arg_ty),*) $(-> $ret_ty)? {
-                <_>::mark($name::$method(&mut self.0, $($arg.unmark()),*))
-            })*
-        })*
-    }
-}
-with_api!(Self, self_, define_mark_types_impls);
-
-struct Dispatcher<S: Types> {
-    handle_store: HandleStore<S>,
-    server: S,
-}
-
-macro_rules! define_dispatcher_impl {
-    ($($name:ident {
-        $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)?;)*
-    }),* $(,)?) => {
-        // FIXME(eddyb) `pub` only for `ExecutionStrategy` below.
-        pub trait DispatcherTrait {
-            // HACK(eddyb) these are here to allow `Self::$name` to work below.
-            $(type $name;)*
-            fn dispatch(&mut self, buf: Buffer) -> Buffer;
-        }
-
-        impl<S: Server> DispatcherTrait for Dispatcher<MarkedTypes<S>> {
-            $(type $name = <MarkedTypes<S> as Types>::$name;)*
-            fn dispatch(&mut self, mut buf: Buffer) -> Buffer {
-                let Dispatcher { handle_store, server } = self;
-
-                let mut reader = &buf[..];
-                match api_tags::Method::decode(&mut reader, &mut ()) {
-                    $(api_tags::Method::$name(m) => match m {
-                        $(api_tags::$name::$method => {
-                            let mut call_method = || {
-                                reverse_decode!(reader, handle_store; $($arg: $arg_ty),*);
-                                $name::$method(server, $($arg),*)
-                            };
-                            // HACK(eddyb) don't use `panic::catch_unwind` in a panic.
-                            // If client and server happen to use the same `libstd`,
-                            // `catch_unwind` asserts that the panic counter was 0,
-                            // even when the closure passed to it didn't panic.
-                            let r = if thread::panicking() {
-                                Ok(call_method())
-                            } else {
-                                panic::catch_unwind(panic::AssertUnwindSafe(call_method))
-                                    .map_err(PanicMessage::from)
-                            };
-
-                            buf.clear();
-                            r.encode(&mut buf, handle_store);
-                        })*
-                    }),*
-                }
-                buf
-            }
-        }
-    }
-}
-with_api!(Self, self_, define_dispatcher_impl);
-
-pub trait ExecutionStrategy {
-    fn run_bridge_and_client(
-        &self,
-        dispatcher: &mut impl DispatcherTrait,
-        input: Buffer,
-        run_client: extern "C" fn(Bridge<'_>) -> Buffer,
-        force_show_panics: bool,
-    ) -> Buffer;
-}
-
-pub struct SameThread;
-
-impl ExecutionStrategy for SameThread {
-    fn run_bridge_and_client(
-        &self,
-        dispatcher: &mut impl DispatcherTrait,
-        input: Buffer,
-        run_client: extern "C" fn(Bridge<'_>) -> Buffer,
-        force_show_panics: bool,
-    ) -> Buffer {
-        let mut dispatch = |buf| dispatcher.dispatch(buf);
-
-        run_client(Bridge {
-            cached_buffer: input,
-            dispatch: (&mut dispatch).into(),
-            force_show_panics,
-            _marker: marker::PhantomData,
-        })
-    }
-}
-
-// NOTE(eddyb) Two implementations are provided, the second one is a bit
-// faster but neither is anywhere near as fast as same-thread execution.
-
-pub struct CrossThread1;
-
-impl ExecutionStrategy for CrossThread1 {
-    fn run_bridge_and_client(
-        &self,
-        dispatcher: &mut impl DispatcherTrait,
-        input: Buffer,
-        run_client: extern "C" fn(Bridge<'_>) -> Buffer,
-        force_show_panics: bool,
-    ) -> Buffer {
-        use std::sync::mpsc::channel;
-
-        let (req_tx, req_rx) = channel();
-        let (res_tx, res_rx) = channel();
-
-        let join_handle = thread::spawn(move || {
-            let mut dispatch = |buf| {
-                req_tx.send(buf).unwrap();
-                res_rx.recv().unwrap()
-            };
-
-            run_client(Bridge {
-                cached_buffer: input,
-                dispatch: (&mut dispatch).into(),
-                force_show_panics,
-                _marker: marker::PhantomData,
-            })
-        });
-
-        for b in req_rx {
-            res_tx.send(dispatcher.dispatch(b)).unwrap();
-        }
-
-        join_handle.join().unwrap()
-    }
-}
-
-pub struct CrossThread2;
-
-impl ExecutionStrategy for CrossThread2 {
-    fn run_bridge_and_client(
-        &self,
-        dispatcher: &mut impl DispatcherTrait,
-        input: Buffer,
-        run_client: extern "C" fn(Bridge<'_>) -> Buffer,
-        force_show_panics: bool,
-    ) -> Buffer {
-        use std::sync::{Arc, Mutex};
-
-        enum State<T> {
-            Req(T),
-            Res(T),
-        }
-
-        let mut state = Arc::new(Mutex::new(State::Res(Buffer::new())));
-
-        let server_thread = thread::current();
-        let state2 = state.clone();
-        let join_handle = thread::spawn(move || {
-            let mut dispatch = |b| {
-                *state2.lock().unwrap() = State::Req(b);
-                server_thread.unpark();
-                loop {
-                    thread::park();
-                    if let State::Res(b) = &mut *state2.lock().unwrap() {
-                        break b.take();
-                    }
-                }
-            };
-
-            let r = run_client(Bridge {
-                cached_buffer: input,
-                dispatch: (&mut dispatch).into(),
-                force_show_panics,
-                _marker: marker::PhantomData,
-            });
-
-            // Wake up the server so it can exit the dispatch loop.
-            drop(state2);
-            server_thread.unpark();
-
-            r
-        });
-
-        // Check whether `state2` was dropped, to know when to stop.
-        while Arc::get_mut(&mut state).is_none() {
-            thread::park();
-            let mut b = match &mut *state.lock().unwrap() {
-                State::Req(b) => b.take(),
-                _ => continue,
-            };
-            b = dispatcher.dispatch(b.take());
-            *state.lock().unwrap() = State::Res(b);
-            join_handle.thread().unpark();
-        }
-
-        join_handle.join().unwrap()
-    }
-}
-
-fn run_server<
-    S: Server,
-    I: Encode<HandleStore<MarkedTypes<S>>>,
-    O: for<'a, 's> DecodeMut<'a, 's, HandleStore<MarkedTypes<S>>>,
->(
-    strategy: &impl ExecutionStrategy,
-    handle_counters: &'static client::HandleCounters,
-    server: S,
-    input: I,
-    run_client: extern "C" fn(Bridge<'_>) -> Buffer,
-    force_show_panics: bool,
-) -> Result<O, PanicMessage> {
-    let mut dispatcher =
-        Dispatcher { handle_store: HandleStore::new(handle_counters), server: MarkedTypes(server) };
-
-    let mut buf = Buffer::new();
-    input.encode(&mut buf, &mut dispatcher.handle_store);
-
-    buf = strategy.run_bridge_and_client(&mut dispatcher, buf, run_client, force_show_panics);
-
-    Result::decode(&mut &buf[..], &mut dispatcher.handle_store)
-}
-
-impl client::Client<super::super::TokenStream, super::super::TokenStream> {
-    pub fn run<S>(
-        &self,
-        strategy: &impl ExecutionStrategy,
-        server: S,
-        input: S::TokenStream,
-        force_show_panics: bool,
-    ) -> Result<S::TokenStream, PanicMessage>
-    where
-        S: Server,
-        S::TokenStream: Default,
-    {
-        let client::Client { get_handle_counters, run, _marker } = *self;
-        run_server(
-            strategy,
-            get_handle_counters(),
-            server,
-            <MarkedTypes<S> as Types>::TokenStream::mark(input),
-            run,
-            force_show_panics,
-        )
-        .map(|s| <Option<<MarkedTypes<S> as Types>::TokenStream>>::unmark(s).unwrap_or_default())
-    }
-}
-
-impl
-    client::Client<
-        (super::super::TokenStream, super::super::TokenStream),
-        super::super::TokenStream,
-    >
-{
-    pub fn run<S>(
-        &self,
-        strategy: &impl ExecutionStrategy,
-        server: S,
-        input: S::TokenStream,
-        input2: S::TokenStream,
-        force_show_panics: bool,
-    ) -> Result<S::TokenStream, PanicMessage>
-    where
-        S: Server,
-        S::TokenStream: Default,
-    {
-        let client::Client { get_handle_counters, run, _marker } = *self;
-        run_server(
-            strategy,
-            get_handle_counters(),
-            server,
-            (
-                <MarkedTypes<S> as Types>::TokenStream::mark(input),
-                <MarkedTypes<S> as Types>::TokenStream::mark(input2),
-            ),
-            run,
-            force_show_panics,
-        )
-        .map(|s| <Option<<MarkedTypes<S> as Types>::TokenStream>>::unmark(s).unwrap_or_default())
-    }
-}
diff --git a/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/diagnostic.rs b/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/diagnostic.rs
deleted file mode 100644
index 3fade2dc4f9..00000000000
--- a/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/diagnostic.rs
+++ /dev/null
@@ -1,166 +0,0 @@
-//! lib-proc-macro diagnostic
-//!
-//! Copy from <https://github.com/rust-lang/rust/blob/e45d9973b2665897a768312e971b82cc62633103/src/libproc_macro/diagnostic.rs>
-//! augmented with removing unstable features
-
-use super::Span;
-
-/// An enum representing a diagnostic level.
-#[derive(Copy, Clone, Debug)]
-#[non_exhaustive]
-pub enum Level {
-    /// An error.
-    Error,
-    /// A warning.
-    Warning,
-    /// A note.
-    Note,
-    /// A help message.
-    Help,
-}
-
-/// Trait implemented by types that can be converted into a set of `Span`s.
-pub trait MultiSpan {
-    /// Converts `self` into a `Vec<Span>`.
-    fn into_spans(self) -> Vec<Span>;
-}
-
-impl MultiSpan for Span {
-    fn into_spans(self) -> Vec<Span> {
-        vec![self]
-    }
-}
-
-impl MultiSpan for Vec<Span> {
-    fn into_spans(self) -> Vec<Span> {
-        self
-    }
-}
-
-impl<'a> MultiSpan for &'a [Span] {
-    fn into_spans(self) -> Vec<Span> {
-        self.to_vec()
-    }
-}
-
-/// A structure representing a diagnostic message and associated children
-/// messages.
-#[derive(Clone, Debug)]
-pub struct Diagnostic {
-    level: Level,
-    message: String,
-    spans: Vec<Span>,
-    children: Vec<Diagnostic>,
-}
-
-macro_rules! diagnostic_child_methods {
-    ($spanned:ident, $regular:ident, $level:expr) => {
-        #[doc = concat!("Adds a new child diagnostics message to `self` with the [`",
-                        stringify!($level), "`] level, and the given `spans` and `message`.")]
-        pub fn $spanned<S, T>(mut self, spans: S, message: T) -> Diagnostic
-        where
-            S: MultiSpan,
-            T: Into<String>,
-        {
-            self.children.push(Diagnostic::spanned(spans, $level, message));
-            self
-        }
-
-        #[doc = concat!("Adds a new child diagnostic message to `self` with the [`",
-                        stringify!($level), "`] level, and the given `message`.")]
-        pub fn $regular<T: Into<String>>(mut self, message: T) -> Diagnostic {
-            self.children.push(Diagnostic::new($level, message));
-            self
-        }
-    };
-}
-
-/// Iterator over the children diagnostics of a `Diagnostic`.
-#[derive(Debug, Clone)]
-pub struct Children<'a>(std::slice::Iter<'a, Diagnostic>);
-
-impl<'a> Iterator for Children<'a> {
-    type Item = &'a Diagnostic;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        self.0.next()
-    }
-}
-
-impl Diagnostic {
-    /// Creates a new diagnostic with the given `level` and `message`.
-    pub fn new<T: Into<String>>(level: Level, message: T) -> Diagnostic {
-        Diagnostic { level, message: message.into(), spans: vec![], children: vec![] }
-    }
-
-    /// Creates a new diagnostic with the given `level` and `message` pointing to
-    /// the given set of `spans`.
-    pub fn spanned<S, T>(spans: S, level: Level, message: T) -> Diagnostic
-    where
-        S: MultiSpan,
-        T: Into<String>,
-    {
-        Diagnostic { level, message: message.into(), spans: spans.into_spans(), children: vec![] }
-    }
-
-    diagnostic_child_methods!(span_error, error, Level::Error);
-    diagnostic_child_methods!(span_warning, warning, Level::Warning);
-    diagnostic_child_methods!(span_note, note, Level::Note);
-    diagnostic_child_methods!(span_help, help, Level::Help);
-
-    /// Returns the diagnostic `level` for `self`.
-    pub fn level(&self) -> Level {
-        self.level
-    }
-
-    /// Sets the level in `self` to `level`.
-    pub fn set_level(&mut self, level: Level) {
-        self.level = level;
-    }
-
-    /// Returns the message in `self`.
-    pub fn message(&self) -> &str {
-        &self.message
-    }
-
-    /// Sets the message in `self` to `message`.
-    pub fn set_message<T: Into<String>>(&mut self, message: T) {
-        self.message = message.into();
-    }
-
-    /// Returns the `Span`s in `self`.
-    pub fn spans(&self) -> &[Span] {
-        &self.spans
-    }
-
-    /// Sets the `Span`s in `self` to `spans`.
-    pub fn set_spans<S: MultiSpan>(&mut self, spans: S) {
-        self.spans = spans.into_spans();
-    }
-
-    /// Returns an iterator over the children diagnostics of `self`.
-    pub fn children(&self) -> Children<'_> {
-        Children(self.children.iter())
-    }
-
-    /// Emit the diagnostic.
-    pub fn emit(self) {
-        fn to_internal(spans: Vec<Span>) -> super::bridge::client::MultiSpan {
-            let mut multi_span = super::bridge::client::MultiSpan::new();
-            for span in spans {
-                multi_span.push(span.0);
-            }
-            multi_span
-        }
-
-        let mut diag = super::bridge::client::Diagnostic::new(
-            self.level,
-            &self.message[..],
-            to_internal(self.spans),
-        );
-        for c in self.children {
-            diag.sub(c.level, &c.message[..], to_internal(c.spans));
-        }
-        diag.emit();
-    }
-}
diff --git a/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/mod.rs b/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/mod.rs
deleted file mode 100644
index 89bd10da5e4..00000000000
--- a/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/mod.rs
+++ /dev/null
@@ -1,1106 +0,0 @@
-//! A support library for macro authors when defining new macros.
-//!
-//! This library, provided by the standard distribution, provides the types
-//! consumed in the interfaces of procedurally defined macro definitions such as
-//! function-like macros `#[proc_macro]`, macro attributes `#[proc_macro_attribute]` and
-//! custom derive attributes`#[proc_macro_derive]`.
-//!
-//! See [the book] for more.
-//!
-//! [the book]: ../book/ch19-06-macros.html#procedural-macros-for-generating-code-from-attributes
-
-#[doc(hidden)]
-pub mod bridge;
-
-mod diagnostic;
-
-pub use diagnostic::{Diagnostic, Level, MultiSpan};
-
-use std::cmp::Ordering;
-use std::ops::RangeBounds;
-use std::path::PathBuf;
-use std::str::FromStr;
-use std::{error, fmt, iter, mem};
-
-/// Determines whether proc_macro has been made accessible to the currently
-/// running program.
-///
-/// The proc_macro crate is only intended for use inside the implementation of
-/// procedural macros. All the functions in this crate panic if invoked from
-/// outside of a procedural macro, such as from a build script or unit test or
-/// ordinary Rust binary.
-///
-/// With consideration for Rust libraries that are designed to support both
-/// macro and non-macro use cases, `proc_macro::is_available()` provides a
-/// non-panicking way to detect whether the infrastructure required to use the
-/// API of proc_macro is presently available. Returns true if invoked from
-/// inside of a procedural macro, false if invoked from any other binary.
-pub fn is_available() -> bool {
-    bridge::Bridge::is_available()
-}
-
-/// The main type provided by this crate, representing an abstract stream of
-/// tokens, or, more specifically, a sequence of token trees.
-/// The type provide interfaces for iterating over those token trees and, conversely,
-/// collecting a number of token trees into one stream.
-///
-/// This is both the input and output of `#[proc_macro]`, `#[proc_macro_attribute]`
-/// and `#[proc_macro_derive]` definitions.
-#[derive(Clone)]
-pub struct TokenStream(Option<bridge::client::TokenStream>);
-
-/// Error returned from `TokenStream::from_str`.
-#[non_exhaustive]
-#[derive(Debug)]
-pub struct LexError;
-
-impl fmt::Display for LexError {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.write_str("cannot parse string into token stream")
-    }
-}
-
-impl error::Error for LexError {}
-
-/// Error returned from `TokenStream::expand_expr`.
-#[non_exhaustive]
-#[derive(Debug)]
-pub struct ExpandError;
-
-impl fmt::Display for ExpandError {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.write_str("macro expansion failed")
-    }
-}
-
-impl error::Error for ExpandError {}
-
-impl TokenStream {
-    /// Returns an empty `TokenStream` containing no token trees.
-    pub fn new() -> TokenStream {
-        TokenStream(None)
-    }
-
-    /// Checks if this `TokenStream` is empty.
-    pub fn is_empty(&self) -> bool {
-        self.0.as_ref().map(|h| h.is_empty()).unwrap_or(true)
-    }
-
-    /// Parses this `TokenStream` as an expression and attempts to expand any
-    /// macros within it. Returns the expanded `TokenStream`.
-    ///
-    /// Currently only expressions expanding to literals will succeed, although
-    /// this may be relaxed in the future.
-    ///
-    /// NOTE: In error conditions, `expand_expr` may leave macros unexpanded,
-    /// report an error, failing compilation, and/or return an `Err(..)`. The
-    /// specific behavior for any error condition, and what conditions are
-    /// considered errors, is unspecified and may change in the future.
-    pub fn expand_expr(&self) -> Result<TokenStream, ExpandError> {
-        let stream = self.0.as_ref().ok_or(ExpandError)?;
-        match bridge::client::TokenStream::expand_expr(stream) {
-            Ok(stream) => Ok(TokenStream(Some(stream))),
-            Err(_) => Err(ExpandError),
-        }
-    }
-}
-
-/// Attempts to break the string into tokens and parse those tokens into a token stream.
-/// May fail for a number of reasons, for example, if the string contains unbalanced delimiters
-/// or characters not existing in the language.
-/// All tokens in the parsed stream get `Span::call_site()` spans.
-///
-/// NOTE: some errors may cause panics instead of returning `LexError`. We reserve the right to
-/// change these errors into `LexError`s later.
-impl FromStr for TokenStream {
-    type Err = LexError;
-
-    fn from_str(src: &str) -> Result<TokenStream, LexError> {
-        Ok(TokenStream(Some(bridge::client::TokenStream::from_str(src))))
-    }
-}
-
-/// Prints the token stream as a string that is supposed to be losslessly convertible back
-/// into the same token stream (modulo spans), except for possibly `TokenTree::Group`s
-/// with `Delimiter::None` delimiters and negative numeric literals.
-impl fmt::Display for TokenStream {
-    fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        unimplemented!()
-    }
-}
-
-/// Prints token in a form convenient for debugging.
-impl fmt::Debug for TokenStream {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.write_str("TokenStream ")?;
-        f.debug_list().entries(self.clone()).finish()
-    }
-}
-
-impl Default for TokenStream {
-    fn default() -> Self {
-        TokenStream::new()
-    }
-}
-
-pub use quote::{quote, quote_span};
-
-fn tree_to_bridge_tree(
-    tree: TokenTree,
-) -> bridge::TokenTree<
-    bridge::client::Group,
-    bridge::client::Punct,
-    bridge::client::Ident,
-    bridge::client::Literal,
-> {
-    match tree {
-        TokenTree::Group(tt) => bridge::TokenTree::Group(tt.0),
-        TokenTree::Punct(tt) => bridge::TokenTree::Punct(tt.0),
-        TokenTree::Ident(tt) => bridge::TokenTree::Ident(tt.0),
-        TokenTree::Literal(tt) => bridge::TokenTree::Literal(tt.0),
-    }
-}
-
-/// Creates a token stream containing a single token tree.
-impl From<TokenTree> for TokenStream {
-    fn from(tree: TokenTree) -> TokenStream {
-        TokenStream(Some(bridge::client::TokenStream::from_token_tree(tree_to_bridge_tree(tree))))
-    }
-}
-
-/// Non-generic helper for implementing `FromIterator<TokenStream>` and
-/// `Extend<TokenStream>` with less monomorphization in calling crates.
-struct ConcatStreamsHelper {
-    streams: Vec<bridge::client::TokenStream>,
-}
-
-impl ConcatStreamsHelper {
-    fn new(capacity: usize) -> Self {
-        ConcatStreamsHelper { streams: Vec::with_capacity(capacity) }
-    }
-
-    fn push(&mut self, stream: TokenStream) {
-        if let Some(stream) = stream.0 {
-            self.streams.push(stream);
-        }
-    }
-
-    fn build(mut self) -> TokenStream {
-        if self.streams.len() <= 1 {
-            TokenStream(self.streams.pop())
-        } else {
-            TokenStream(Some(bridge::client::TokenStream::concat_streams(None, self.streams)))
-        }
-    }
-
-    fn append_to(mut self, stream: &mut TokenStream) {
-        if self.streams.is_empty() {
-            return;
-        }
-        let base = stream.0.take();
-        if base.is_none() && self.streams.len() == 1 {
-            stream.0 = self.streams.pop();
-        } else {
-            stream.0 = Some(bridge::client::TokenStream::concat_streams(base, self.streams));
-        }
-    }
-}
-
-/// Collects a number of token trees into a single stream.
-impl FromIterator<TokenTree> for TokenStream {
-    fn from_iter<I: IntoIterator<Item = TokenTree>>(trees: I) -> Self {
-        trees.into_iter().map(TokenStream::from).collect()
-    }
-}
-
-/// A "flattening" operation on token streams, collects token trees
-/// from multiple token streams into a single stream.
-impl FromIterator<TokenStream> for TokenStream {
-    fn from_iter<I: IntoIterator<Item = TokenStream>>(streams: I) -> Self {
-        let iter = streams.into_iter();
-        let mut builder = ConcatStreamsHelper::new(iter.size_hint().0);
-        iter.for_each(|stream| builder.push(stream));
-        builder.build()
-    }
-}
-
-impl Extend<TokenTree> for TokenStream {
-    fn extend<I: IntoIterator<Item = TokenTree>>(&mut self, trees: I) {
-        self.extend(trees.into_iter().map(TokenStream::from));
-    }
-}
-
-impl Extend<TokenStream> for TokenStream {
-    fn extend<I: IntoIterator<Item = TokenStream>>(&mut self, streams: I) {
-        // FIXME(eddyb) Use an optimized implementation if/when possible.
-        *self = iter::once(mem::replace(self, Self::new())).chain(streams).collect();
-    }
-}
-
-/// Public implementation details for the `TokenStream` type, such as iterators.
-pub mod token_stream {
-    use super::{bridge, Group, Ident, Literal, Punct, TokenStream, TokenTree};
-
-    /// An iterator over `TokenStream`'s `TokenTree`s.
-    /// The iteration is "shallow", e.g., the iterator doesn't recurse into delimited groups,
-    /// and returns whole groups as token trees.
-    #[derive(Clone)]
-    pub struct IntoIter(
-        std::vec::IntoIter<
-            bridge::TokenTree<
-                bridge::client::Group,
-                bridge::client::Punct,
-                bridge::client::Ident,
-                bridge::client::Literal,
-            >,
-        >,
-    );
-
-    impl Iterator for IntoIter {
-        type Item = TokenTree;
-
-        fn next(&mut self) -> Option<TokenTree> {
-            self.0.next().map(|tree| match tree {
-                bridge::TokenTree::Group(tt) => TokenTree::Group(Group(tt)),
-                bridge::TokenTree::Punct(tt) => TokenTree::Punct(Punct(tt)),
-                bridge::TokenTree::Ident(tt) => TokenTree::Ident(Ident(tt)),
-                bridge::TokenTree::Literal(tt) => TokenTree::Literal(Literal(tt)),
-            })
-        }
-    }
-
-    impl IntoIterator for TokenStream {
-        type Item = TokenTree;
-        type IntoIter = IntoIter;
-
-        fn into_iter(self) -> IntoIter {
-            IntoIter(self.0.map(|v| v.into_trees()).unwrap_or_default().into_iter())
-        }
-    }
-}
-
-#[doc(hidden)]
-mod quote;
-
-/// A region of source code, along with macro expansion information.
-#[derive(Copy, Clone)]
-pub struct Span(bridge::client::Span);
-
-macro_rules! diagnostic_method {
-    ($name:ident, $level:expr) => {
-        /// Creates a new `Diagnostic` with the given `message` at the span
-        /// `self`.
-        pub fn $name<T: Into<String>>(self, message: T) -> Diagnostic {
-            Diagnostic::spanned(self, $level, message)
-        }
-    };
-}
-
-impl Span {
-    /// A span that resolves at the macro definition site.
-    pub fn def_site() -> Span {
-        Span(bridge::client::Span::def_site())
-    }
-
-    /// The span of the invocation of the current procedural macro.
-    /// Identifiers created with this span will be resolved as if they were written
-    /// directly at the macro call location (call-site hygiene) and other code
-    /// at the macro call site will be able to refer to them as well.
-    pub fn call_site() -> Span {
-        Span(bridge::client::Span::call_site())
-    }
-
-    /// A span that represents `macro_rules` hygiene, and sometimes resolves at the macro
-    /// definition site (local variables, labels, `$crate`) and sometimes at the macro
-    /// call site (everything else).
-    /// The span location is taken from the call-site.
-    pub fn mixed_site() -> Span {
-        Span(bridge::client::Span::mixed_site())
-    }
-
-    /// The original source file into which this span points.
-    pub fn source_file(&self) -> SourceFile {
-        SourceFile(self.0.source_file())
-    }
-
-    /// The `Span` for the tokens in the previous macro expansion from which
-    /// `self` was generated from, if any.
-    pub fn parent(&self) -> Option<Span> {
-        self.0.parent().map(Span)
-    }
-
-    /// The span for the origin source code that `self` was generated from. If
-    /// this `Span` wasn't generated from other macro expansions then the return
-    /// value is the same as `*self`.
-    pub fn source(&self) -> Span {
-        Span(self.0.source())
-    }
-
-    /// Gets the starting line/column in the source file for this span.
-    pub fn start(&self) -> LineColumn {
-        self.0.start().add_1_to_column()
-    }
-
-    /// Gets the ending line/column in the source file for this span.
-    pub fn end(&self) -> LineColumn {
-        self.0.end().add_1_to_column()
-    }
-
-    /// Creates an empty span pointing to directly before this span.
-    pub fn before(&self) -> Span {
-        Span(self.0.before())
-    }
-
-    /// Creates an empty span pointing to directly after this span.
-    pub fn after(&self) -> Span {
-        Span(self.0.after())
-    }
-
-    /// Creates a new span encompassing `self` and `other`.
-    ///
-    /// Returns `None` if `self` and `other` are from different files.
-    pub fn join(&self, other: Span) -> Option<Span> {
-        self.0.join(other.0).map(Span)
-    }
-
-    /// Creates a new span with the same line/column information as `self` but
-    /// that resolves symbols as though it were at `other`.
-    pub fn resolved_at(&self, other: Span) -> Span {
-        Span(self.0.resolved_at(other.0))
-    }
-
-    /// Creates a new span with the same name resolution behavior as `self` but
-    /// with the line/column information of `other`.
-    pub fn located_at(&self, other: Span) -> Span {
-        other.resolved_at(*self)
-    }
-
-    /// Compares to spans to see if they're equal.
-    pub fn eq(&self, other: &Span) -> bool {
-        self.0 == other.0
-    }
-
-    /// Returns the source text behind a span. This preserves the original source
-    /// code, including spaces and comments. It only returns a result if the span
-    /// corresponds to real source code.
-    ///
-    /// Note: The observable result of a macro should only rely on the tokens and
-    /// not on this source text. The result of this function is a best effort to
-    /// be used for diagnostics only.
-    pub fn source_text(&self) -> Option<String> {
-        self.0.source_text()
-    }
-
-    // Used by the implementation of `Span::quote`
-    #[doc(hidden)]
-    pub fn save_span(&self) -> usize {
-        self.0.save_span()
-    }
-
-    // Used by the implementation of `Span::quote`
-    #[doc(hidden)]
-    pub fn recover_proc_macro_span(id: usize) -> Span {
-        Span(bridge::client::Span::recover_proc_macro_span(id))
-    }
-
-    diagnostic_method!(error, Level::Error);
-    diagnostic_method!(warning, Level::Warning);
-    diagnostic_method!(note, Level::Note);
-    diagnostic_method!(help, Level::Help);
-}
-
-/// Prints a span in a form convenient for debugging.
-impl fmt::Debug for Span {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        self.0.fmt(f)
-    }
-}
-
-/// A line-column pair representing the start or end of a `Span`.
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
-pub struct LineColumn {
-    /// The 1-indexed line in the source file on which the span starts or ends (inclusive).
-    pub line: usize,
-    /// The 1-indexed column (number of bytes in UTF-8 encoding) in the source
-    /// file on which the span starts or ends (inclusive).
-    pub column: usize,
-}
-
-impl LineColumn {
-    fn add_1_to_column(self) -> Self {
-        LineColumn { line: self.line, column: self.column + 1 }
-    }
-}
-
-impl Ord for LineColumn {
-    fn cmp(&self, other: &Self) -> Ordering {
-        self.line.cmp(&other.line).then(self.column.cmp(&other.column))
-    }
-}
-
-impl PartialOrd for LineColumn {
-    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
-        Some(self.cmp(other))
-    }
-}
-
-/// The source file of a given `Span`.
-#[derive(Clone)]
-pub struct SourceFile(bridge::client::SourceFile);
-
-impl SourceFile {
-    /// Gets the path to this source file.
-    ///
-    /// ### Note
-    /// If the code span associated with this `SourceFile` was generated by an external macro, this
-    /// macro, this might not be an actual path on the filesystem. Use [`is_real`] to check.
-    ///
-    /// Also note that even if `is_real` returns `true`, if `--remap-path-prefix` was passed on
-    /// the command line, the path as given might not actually be valid.
-    ///
-    /// [`is_real`]: Self::is_real
-    pub fn path(&self) -> PathBuf {
-        PathBuf::from(self.0.path())
-    }
-
-    /// Returns `true` if this source file is a real source file, and not generated by an external
-    /// macro's expansion.
-    pub fn is_real(&self) -> bool {
-        // This is a hack until intercrate spans are implemented and we can have real source files
-        // for spans generated in external macros.
-        // https://github.com/rust-lang/rust/pull/43604#issuecomment-333334368
-        self.0.is_real()
-    }
-}
-
-impl fmt::Debug for SourceFile {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.debug_struct("SourceFile")
-            .field("path", &self.path())
-            .field("is_real", &self.is_real())
-            .finish()
-    }
-}
-
-impl PartialEq for SourceFile {
-    fn eq(&self, other: &Self) -> bool {
-        self.0.eq(&other.0)
-    }
-}
-
-impl Eq for SourceFile {}
-
-/// A single token or a delimited sequence of token trees (e.g., `[1, (), ..]`).
-#[derive(Clone)]
-pub enum TokenTree {
-    /// A token stream surrounded by bracket delimiters.
-    Group(Group),
-    /// An identifier.
-    Ident(Ident),
-    /// A single punctuation character (`+`, `,`, `$`, etc.).
-    Punct(Punct),
-    /// A literal character (`'a'`), string (`"hello"`), number (`2.3`), etc.
-    Literal(Literal),
-}
-
-impl TokenTree {
-    /// Returns the span of this tree, delegating to the `span` method of
-    /// the contained token or a delimited stream.
-    pub fn span(&self) -> Span {
-        match *self {
-            TokenTree::Group(ref t) => t.span(),
-            TokenTree::Ident(ref t) => t.span(),
-            TokenTree::Punct(ref t) => t.span(),
-            TokenTree::Literal(ref t) => t.span(),
-        }
-    }
-
-    /// Configures the span for *only this token*.
-    ///
-    /// Note that if this token is a `Group` then this method will not configure
-    /// the span of each of the internal tokens, this will simply delegate to
-    /// the `set_span` method of each variant.
-    pub fn set_span(&mut self, span: Span) {
-        match *self {
-            TokenTree::Group(ref mut t) => t.set_span(span),
-            TokenTree::Ident(ref mut t) => t.set_span(span),
-            TokenTree::Punct(ref mut t) => t.set_span(span),
-            TokenTree::Literal(ref mut t) => t.set_span(span),
-        }
-    }
-}
-
-/// Prints token tree in a form convenient for debugging.
-impl fmt::Debug for TokenTree {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        // Each of these has the name in the struct type in the derived debug,
-        // so don't bother with an extra layer of indirection
-        match *self {
-            TokenTree::Group(ref tt) => tt.fmt(f),
-            TokenTree::Ident(ref tt) => tt.fmt(f),
-            TokenTree::Punct(ref tt) => tt.fmt(f),
-            TokenTree::Literal(ref tt) => tt.fmt(f),
-        }
-    }
-}
-
-impl From<Group> for TokenTree {
-    fn from(g: Group) -> TokenTree {
-        TokenTree::Group(g)
-    }
-}
-
-impl From<Ident> for TokenTree {
-    fn from(g: Ident) -> TokenTree {
-        TokenTree::Ident(g)
-    }
-}
-
-impl From<Punct> for TokenTree {
-    fn from(g: Punct) -> TokenTree {
-        TokenTree::Punct(g)
-    }
-}
-
-impl From<Literal> for TokenTree {
-    fn from(g: Literal) -> TokenTree {
-        TokenTree::Literal(g)
-    }
-}
-
-/// Prints the token tree as a string that is supposed to be losslessly convertible back
-/// into the same token tree (modulo spans), except for possibly `TokenTree::Group`s
-/// with `Delimiter::None` delimiters and negative numeric literals.
-impl fmt::Display for TokenTree {
-    fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        unimplemented!()
-    }
-}
-
-/// A delimited token stream.
-///
-/// A `Group` internally contains a `TokenStream` which is surrounded by `Delimiter`s.
-#[derive(Clone)]
-pub struct Group(bridge::client::Group);
-
-/// Describes how a sequence of token trees is delimited.
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
-pub enum Delimiter {
-    /// `( ... )`
-    Parenthesis,
-    /// `{ ... }`
-    Brace,
-    /// `[ ... ]`
-    Bracket,
-    /// `Ø ... Ø`
-    /// An invisible delimiter, that may, for example, appear around tokens coming from a
-    /// "macro variable" `$var`. It is important to preserve operator priorities in cases like
-    /// `$var * 3` where `$var` is `1 + 2`.
-    /// Invisible delimiters might not survive roundtrip of a token stream through a string.
-    None,
-}
-
-impl Group {
-    /// Creates a new `Group` with the given delimiter and token stream.
-    ///
-    /// This constructor will set the span for this group to
-    /// `Span::call_site()`. To change the span you can use the `set_span`
-    /// method below.
-    pub fn new(delimiter: Delimiter, stream: TokenStream) -> Group {
-        Group(bridge::client::Group::new(delimiter, stream.0))
-    }
-
-    /// Returns the delimiter of this `Group`
-    pub fn delimiter(&self) -> Delimiter {
-        self.0.delimiter()
-    }
-
-    /// Returns the `TokenStream` of tokens that are delimited in this `Group`.
-    ///
-    /// Note that the returned token stream does not include the delimiter
-    /// returned above.
-    pub fn stream(&self) -> TokenStream {
-        TokenStream(Some(self.0.stream()))
-    }
-
-    /// Returns the span for the delimiters of this token stream, spanning the
-    /// entire `Group`.
-    ///
-    /// ```text
-    /// pub fn span(&self) -> Span {
-    ///            ^^^^^^^
-    /// ```
-    pub fn span(&self) -> Span {
-        Span(self.0.span())
-    }
-
-    /// Returns the span pointing to the opening delimiter of this group.
-    ///
-    /// ```text
-    /// pub fn span_open(&self) -> Span {
-    ///                 ^
-    /// ```
-    pub fn span_open(&self) -> Span {
-        Span(self.0.span_open())
-    }
-
-    /// Returns the span pointing to the closing delimiter of this group.
-    ///
-    /// ```text
-    /// pub fn span_close(&self) -> Span {
-    ///                        ^
-    /// ```
-    pub fn span_close(&self) -> Span {
-        Span(self.0.span_close())
-    }
-
-    /// Configures the span for this `Group`'s delimiters, but not its internal
-    /// tokens.
-    ///
-    /// This method will **not** set the span of all the internal tokens spanned
-    /// by this group, but rather it will only set the span of the delimiter
-    /// tokens at the level of the `Group`.
-    pub fn set_span(&mut self, span: Span) {
-        self.0.set_span(span.0);
-    }
-}
-
-/// Prints the group as a string that should be losslessly convertible back
-/// into the same group (modulo spans), except for possibly `TokenTree::Group`s
-/// with `Delimiter::None` delimiters.
-impl fmt::Display for Group {
-    fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        unimplemented!()
-    }
-}
-
-impl fmt::Debug for Group {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.debug_struct("Group")
-            .field("delimiter", &self.delimiter())
-            .field("stream", &self.stream())
-            .field("span", &self.span())
-            .finish()
-    }
-}
-
-/// A `Punct` is a single punctuation character such as `+`, `-` or `#`.
-///
-/// Multi-character operators like `+=` are represented as two instances of `Punct` with different
-/// forms of `Spacing` returned.
-#[derive(Clone)]
-pub struct Punct(bridge::client::Punct);
-
-/// Describes whether a `Punct` is followed immediately by another `Punct` ([`Spacing::Joint`]) or
-/// by a different token or whitespace ([`Spacing::Alone`]).
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
-pub enum Spacing {
-    /// A `Punct` is not immediately followed by another `Punct`.
-    /// E.g. `+` is `Alone` in `+ =`, `+ident` and `+()`.
-    Alone,
-    /// A `Punct` is immediately followed by another `Punct`.
-    /// E.g. `+` is `Joint` in `+=` and `++`.
-    ///
-    /// Additionally, single quote `'` can join with identifiers to form lifetimes: `'ident`.
-    Joint,
-}
-
-impl Punct {
-    /// Creates a new `Punct` from the given character and spacing.
-    /// The `ch` argument must be a valid punctuation character permitted by the language,
-    /// otherwise the function will panic.
-    ///
-    /// The returned `Punct` will have the default span of `Span::call_site()`
-    /// which can be further configured with the `set_span` method below.
-    pub fn new(ch: char, spacing: Spacing) -> Punct {
-        Punct(bridge::client::Punct::new(ch, spacing))
-    }
-
-    /// Returns the value of this punctuation character as `char`.
-    pub fn as_char(&self) -> char {
-        self.0.as_char()
-    }
-
-    /// Returns the spacing of this punctuation character, indicating whether it's immediately
-    /// followed by another `Punct` in the token stream, so they can potentially be combined into
-    /// a multi-character operator (`Joint`), or it's followed by some other token or whitespace
-    /// (`Alone`) so the operator has certainly ended.
-    pub fn spacing(&self) -> Spacing {
-        self.0.spacing()
-    }
-
-    /// Returns the span for this punctuation character.
-    pub fn span(&self) -> Span {
-        Span(self.0.span())
-    }
-
-    /// Configure the span for this punctuation character.
-    pub fn set_span(&mut self, span: Span) {
-        self.0 = self.0.with_span(span.0);
-    }
-}
-
-/// Prints the punctuation character as a string that should be losslessly convertible
-/// back into the same character.
-impl fmt::Display for Punct {
-    fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        unimplemented!()
-    }
-}
-
-impl fmt::Debug for Punct {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.debug_struct("Punct")
-            .field("ch", &self.as_char())
-            .field("spacing", &self.spacing())
-            .field("span", &self.span())
-            .finish()
-    }
-}
-
-impl PartialEq<char> for Punct {
-    fn eq(&self, rhs: &char) -> bool {
-        self.as_char() == *rhs
-    }
-}
-
-impl PartialEq<Punct> for char {
-    fn eq(&self, rhs: &Punct) -> bool {
-        *self == rhs.as_char()
-    }
-}
-
-/// An identifier (`ident`).
-#[derive(Clone)]
-pub struct Ident(bridge::client::Ident);
-
-impl Ident {
-    /// Creates a new `Ident` with the given `string` as well as the specified
-    /// `span`.
-    /// The `string` argument must be a valid identifier permitted by the
-    /// language (including keywords, e.g. `self` or `fn`). Otherwise, the function will panic.
-    ///
-    /// Note that `span`, currently in rustc, configures the hygiene information
-    /// for this identifier.
-    ///
-    /// As of this time `Span::call_site()` explicitly opts-in to "call-site" hygiene
-    /// meaning that identifiers created with this span will be resolved as if they were written
-    /// directly at the location of the macro call, and other code at the macro call site will be
-    /// able to refer to them as well.
-    ///
-    /// Later spans like `Span::def_site()` will allow to opt-in to "definition-site" hygiene
-    /// meaning that identifiers created with this span will be resolved at the location of the
-    /// macro definition and other code at the macro call site will not be able to refer to them.
-    ///
-    /// Due to the current importance of hygiene this constructor, unlike other
-    /// tokens, requires a `Span` to be specified at construction.
-    pub fn new(string: &str, span: Span) -> Ident {
-        Ident(bridge::client::Ident::new(string, span.0, false))
-    }
-
-    /// Same as `Ident::new`, but creates a raw identifier (`r#ident`).
-    /// The `string` argument be a valid identifier permitted by the language
-    /// (including keywords, e.g. `fn`). Keywords which are usable in path segments
-    /// (e.g. `self`, `super`) are not supported, and will cause a panic.
-    pub fn new_raw(string: &str, span: Span) -> Ident {
-        Ident(bridge::client::Ident::new(string, span.0, true))
-    }
-
-    /// Returns the span of this `Ident`, encompassing the entire string returned
-    /// by [`to_string`](Self::to_string).
-    pub fn span(&self) -> Span {
-        Span(self.0.span())
-    }
-
-    /// Configures the span of this `Ident`, possibly changing its hygiene context.
-    pub fn set_span(&mut self, span: Span) {
-        self.0 = self.0.with_span(span.0);
-    }
-}
-
-/// Prints the identifier as a string that should be losslessly convertible
-/// back into the same identifier.
-impl fmt::Display for Ident {
-    fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        unimplemented!()
-    }
-}
-
-impl fmt::Debug for Ident {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.debug_struct("Ident")
-            .field("ident", &self.to_string())
-            .field("span", &self.span())
-            .finish()
-    }
-}
-
-/// A literal string (`"hello"`), byte string (`b"hello"`),
-/// character (`'a'`), byte character (`b'a'`), an integer or floating point number
-/// with or without a suffix (`1`, `1u8`, `2.3`, `2.3f32`).
-/// Boolean literals like `true` and `false` do not belong here, they are `Ident`s.
-#[derive(Clone)]
-pub struct Literal(bridge::client::Literal);
-
-macro_rules! suffixed_int_literals {
-    ($($name:ident => $kind:ident,)*) => ($(
-        /// Creates a new suffixed integer literal with the specified value.
-        ///
-        /// This function will create an integer like `1u32` where the integer
-        /// value specified is the first part of the token and the integral is
-        /// also suffixed at the end.
-        /// Literals created from negative numbers might not survive round-trips through
-        /// `TokenStream` or strings and may be broken into two tokens (`-` and positive literal).
-        ///
-        /// Literals created through this method have the `Span::call_site()`
-        /// span by default, which can be configured with the `set_span` method
-        /// below.
-        pub fn $name(n: $kind) -> Literal {
-            Literal(bridge::client::Literal::typed_integer(&n.to_string(), stringify!($kind)))
-        }
-    )*)
-}
-
-macro_rules! unsuffixed_int_literals {
-    ($($name:ident => $kind:ident,)*) => ($(
-        /// Creates a new unsuffixed integer literal with the specified value.
-        ///
-        /// This function will create an integer like `1` where the integer
-        /// value specified is the first part of the token. No suffix is
-        /// specified on this token, meaning that invocations like
-        /// `Literal::i8_unsuffixed(1)` are equivalent to
-        /// `Literal::u32_unsuffixed(1)`.
-        /// Literals created from negative numbers might not survive rountrips through
-        /// `TokenStream` or strings and may be broken into two tokens (`-` and positive literal).
-        ///
-        /// Literals created through this method have the `Span::call_site()`
-        /// span by default, which can be configured with the `set_span` method
-        /// below.
-        pub fn $name(n: $kind) -> Literal {
-            Literal(bridge::client::Literal::integer(&n.to_string()))
-        }
-    )*)
-}
-
-impl Literal {
-    suffixed_int_literals! {
-        u8_suffixed => u8,
-        u16_suffixed => u16,
-        u32_suffixed => u32,
-        u64_suffixed => u64,
-        u128_suffixed => u128,
-        usize_suffixed => usize,
-        i8_suffixed => i8,
-        i16_suffixed => i16,
-        i32_suffixed => i32,
-        i64_suffixed => i64,
-        i128_suffixed => i128,
-        isize_suffixed => isize,
-    }
-
-    unsuffixed_int_literals! {
-        u8_unsuffixed => u8,
-        u16_unsuffixed => u16,
-        u32_unsuffixed => u32,
-        u64_unsuffixed => u64,
-        u128_unsuffixed => u128,
-        usize_unsuffixed => usize,
-        i8_unsuffixed => i8,
-        i16_unsuffixed => i16,
-        i32_unsuffixed => i32,
-        i64_unsuffixed => i64,
-        i128_unsuffixed => i128,
-        isize_unsuffixed => isize,
-    }
-
-    /// Creates a new unsuffixed floating-point literal.
-    ///
-    /// This constructor is similar to those like `Literal::i8_unsuffixed` where
-    /// the float's value is emitted directly into the token but no suffix is
-    /// used, so it may be inferred to be a `f64` later in the compiler.
-    /// Literals created from negative numbers might not survive rountrips through
-    /// `TokenStream` or strings and may be broken into two tokens (`-` and positive literal).
-    ///
-    /// # Panics
-    ///
-    /// This function requires that the specified float is finite, for
-    /// example if it is infinity or NaN this function will panic.
-    pub fn f32_unsuffixed(n: f32) -> Literal {
-        if !n.is_finite() {
-            panic!("Invalid float literal {n}");
-        }
-        let mut repr = n.to_string();
-        if !repr.contains('.') {
-            repr.push_str(".0");
-        }
-        Literal(bridge::client::Literal::float(&repr))
-    }
-
-    /// Creates a new suffixed floating-point literal.
-    ///
-    /// This constructor will create a literal like `1.0f32` where the value
-    /// specified is the preceding part of the token and `f32` is the suffix of
-    /// the token. This token will always be inferred to be an `f32` in the
-    /// compiler.
-    /// Literals created from negative numbers might not survive rountrips through
-    /// `TokenStream` or strings and may be broken into two tokens (`-` and positive literal).
-    ///
-    /// # Panics
-    ///
-    /// This function requires that the specified float is finite, for
-    /// example if it is infinity or NaN this function will panic.
-    pub fn f32_suffixed(n: f32) -> Literal {
-        if !n.is_finite() {
-            panic!("Invalid float literal {n}");
-        }
-        Literal(bridge::client::Literal::f32(&n.to_string()))
-    }
-
-    /// Creates a new unsuffixed floating-point literal.
-    ///
-    /// This constructor is similar to those like `Literal::i8_unsuffixed` where
-    /// the float's value is emitted directly into the token but no suffix is
-    /// used, so it may be inferred to be a `f64` later in the compiler.
-    /// Literals created from negative numbers might not survive rountrips through
-    /// `TokenStream` or strings and may be broken into two tokens (`-` and positive literal).
-    ///
-    /// # Panics
-    ///
-    /// This function requires that the specified float is finite, for
-    /// example if it is infinity or NaN this function will panic.
-    pub fn f64_unsuffixed(n: f64) -> Literal {
-        if !n.is_finite() {
-            panic!("Invalid float literal {n}");
-        }
-        let mut repr = n.to_string();
-        if !repr.contains('.') {
-            repr.push_str(".0");
-        }
-        Literal(bridge::client::Literal::float(&repr))
-    }
-
-    /// Creates a new suffixed floating-point literal.
-    ///
-    /// This constructor will create a literal like `1.0f64` where the value
-    /// specified is the preceding part of the token and `f64` is the suffix of
-    /// the token. This token will always be inferred to be an `f64` in the
-    /// compiler.
-    /// Literals created from negative numbers might not survive rountrips through
-    /// `TokenStream` or strings and may be broken into two tokens (`-` and positive literal).
-    ///
-    /// # Panics
-    ///
-    /// This function requires that the specified float is finite, for
-    /// example if it is infinity or NaN this function will panic.
-    pub fn f64_suffixed(n: f64) -> Literal {
-        if !n.is_finite() {
-            panic!("Invalid float literal {n}");
-        }
-        Literal(bridge::client::Literal::f64(&n.to_string()))
-    }
-
-    /// String literal.
-    pub fn string(string: &str) -> Literal {
-        Literal(bridge::client::Literal::string(string))
-    }
-
-    /// Character literal.
-    pub fn character(ch: char) -> Literal {
-        Literal(bridge::client::Literal::character(ch))
-    }
-
-    /// Byte string literal.
-    pub fn byte_string(bytes: &[u8]) -> Literal {
-        Literal(bridge::client::Literal::byte_string(bytes))
-    }
-
-    /// Returns the span encompassing this literal.
-    pub fn span(&self) -> Span {
-        Span(self.0.span())
-    }
-
-    /// Configures the span associated for this literal.
-    pub fn set_span(&mut self, span: Span) {
-        self.0.set_span(span.0);
-    }
-
-    /// Returns a `Span` that is a subset of `self.span()` containing only the
-    /// source bytes in range `range`. Returns `None` if the would-be trimmed
-    /// span is outside the bounds of `self`.
-    // FIXME(SergioBenitez): check that the byte range starts and ends at a
-    // UTF-8 boundary of the source. otherwise, it's likely that a panic will
-    // occur elsewhere when the source text is printed.
-    // FIXME(SergioBenitez): there is no way for the user to know what
-    // `self.span()` actually maps to, so this method can currently only be
-    // called blindly. For example, `to_string()` for the character 'c' returns
-    // "'\u{63}'"; there is no way for the user to know whether the source text
-    // was 'c' or whether it was '\u{63}'.
-    pub fn subspan<R: RangeBounds<usize>>(&self, range: R) -> Option<Span> {
-        self.0.subspan(range.start_bound().cloned(), range.end_bound().cloned()).map(Span)
-    }
-}
-
-/// Parse a single literal from its stringified representation.
-///
-/// In order to parse successfully, the input string must not contain anything
-/// but the literal token. Specifically, it must not contain whitespace or
-/// comments in addition to the literal.
-///
-/// The resulting literal token will have a `Span::call_site()` span.
-///
-/// NOTE: some errors may cause panics instead of returning `LexError`. We
-/// reserve the right to change these errors into `LexError`s later.
-impl FromStr for Literal {
-    type Err = LexError;
-
-    fn from_str(src: &str) -> Result<Self, LexError> {
-        match bridge::client::Literal::from_str(src) {
-            Ok(literal) => Ok(Literal(literal)),
-            Err(()) => Err(LexError),
-        }
-    }
-}
-
-/// Prints the literal as a string that should be losslessly convertible
-/// back into the same literal (except for possible rounding for floating point literals).
-impl fmt::Display for Literal {
-    fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        unimplemented!()
-    }
-}
-
-impl fmt::Debug for Literal {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        self.0.fmt(f)
-    }
-}
-
-/// Tracked access to environment variables.
-pub mod tracked_env {
-    use std::env::{self, VarError};
-    use std::ffi::OsStr;
-
-    /// Retrieve an environment variable and add it to build dependency info.
-    /// Build system executing the compiler will know that the variable was accessed during
-    /// compilation, and will be able to rerun the build when the value of that variable changes.
-    /// Besides the dependency tracking this function should be equivalent to `env::var` from the
-    /// standard library, except that the argument must be UTF-8.
-    pub fn var<K: AsRef<OsStr> + AsRef<str>>(key: K) -> Result<String, VarError> {
-        let key: &str = key.as_ref();
-        let value = env::var(key);
-        super::bridge::client::FreeFunctions::track_env_var(key, value.as_deref().ok());
-        value
-    }
-}
-
-/// Tracked access to additional files.
-pub mod tracked_path {
-
-    /// Track a file explicitly.
-    ///
-    /// Commonly used for tracking asset preprocessing.
-    pub fn path<P: AsRef<str>>(path: P) {
-        let path: &str = path.as_ref();
-        super::bridge::client::FreeFunctions::track_path(path);
-    }
-}
diff --git a/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/quote.rs b/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/quote.rs
deleted file mode 100644
index 39309faa412..00000000000
--- a/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/quote.rs
+++ /dev/null
@@ -1,139 +0,0 @@
-//! # Quasiquoter
-//! This file contains the implementation internals of the quasiquoter provided by `quote!`.
-
-//! This quasiquoter uses macros 2.0 hygiene to reliably access
-//! items from `proc_macro`, to build a `proc_macro::TokenStream`.
-
-use super::{Delimiter, Group, Ident, Literal, Punct, Spacing, Span, TokenStream, TokenTree};
-
-macro_rules! quote_tt {
-    (($($t:tt)*)) => { Group::new(Delimiter::Parenthesis, quote!($($t)*)) };
-    ([$($t:tt)*]) => { Group::new(Delimiter::Bracket, quote!($($t)*)) };
-    ({$($t:tt)*}) => { Group::new(Delimiter::Brace, quote!($($t)*)) };
-    (,) => { Punct::new(',', Spacing::Alone) };
-    (.) => { Punct::new('.', Spacing::Alone) };
-    (;) => { Punct::new(';', Spacing::Alone) };
-    (!) => { Punct::new('!', Spacing::Alone) };
-    (<) => { Punct::new('<', Spacing::Alone) };
-    (>) => { Punct::new('>', Spacing::Alone) };
-    (&) => { Punct::new('&', Spacing::Alone) };
-    (=) => { Punct::new('=', Spacing::Alone) };
-    ($i:ident) => { Ident::new(stringify!($i), Span::def_site()) };
-}
-
-macro_rules! quote_ts {
-    ((@ $($t:tt)*)) => { $($t)* };
-    (::) => {
-        [
-            TokenTree::from(Punct::new(':', Spacing::Joint)),
-            TokenTree::from(Punct::new(':', Spacing::Alone)),
-        ].iter()
-            .cloned()
-            .map(|mut x| {
-                x.set_span(Span::def_site());
-                x
-            })
-            .collect::<TokenStream>()
-    };
-    ($t:tt) => { TokenTree::from(quote_tt!($t)) };
-}
-
-/// Simpler version of the real `quote!` macro, implemented solely
-/// through `macro_rules`, for bootstrapping the real implementation
-/// (see the `quote` function), which does not have access to the
-/// real `quote!` macro due to the `proc_macro` crate not being
-/// able to depend on itself.
-///
-/// Note: supported tokens are a subset of the real `quote!`, but
-/// unquoting is different: instead of `$x`, this uses `(@ expr)`.
-macro_rules! quote {
-    () => { TokenStream::new() };
-    ($($t:tt)*) => {
-        [
-            $(TokenStream::from(quote_ts!($t)),)*
-        ].iter().cloned().collect::<TokenStream>()
-    };
-}
-
-/// Quote a `TokenStream` into a `TokenStream`.
-/// This is the actual implementation of the `quote!()` proc macro.
-///
-/// It is loaded by the compiler in `register_builtin_macros`.
-pub fn quote(stream: TokenStream) -> TokenStream {
-    if stream.is_empty() {
-        return quote!(super::TokenStream::new());
-    }
-    let proc_macro_crate = quote!(crate);
-    let mut after_dollar = false;
-    let tokens = stream
-        .into_iter()
-        .filter_map(|tree| {
-            if after_dollar {
-                after_dollar = false;
-                match tree {
-                    TokenTree::Ident(_) => {
-                        return Some(quote!(Into::<super::TokenStream>::into(
-                        Clone::clone(&(@ tree))),));
-                    }
-                    TokenTree::Punct(ref tt) if tt.as_char() == '$' => {}
-                    _ => panic!("`$` must be followed by an ident or `$` in `quote!`"),
-                }
-            } else if let TokenTree::Punct(ref tt) = tree {
-                if tt.as_char() == '$' {
-                    after_dollar = true;
-                    return None;
-                }
-            }
-
-            Some(quote!(super::TokenStream::from((@ match tree {
-                TokenTree::Punct(tt) => quote!(super::TokenTree::Punct(super::Punct::new(
-                    (@ TokenTree::from(Literal::character(tt.as_char()))),
-                    (@ match tt.spacing() {
-                        Spacing::Alone => quote!(super::Spacing::Alone),
-                        Spacing::Joint => quote!(super::Spacing::Joint),
-                    }),
-                ))),
-                TokenTree::Group(tt) => quote!(super::TokenTree::Group(super::Group::new(
-                    (@ match tt.delimiter() {
-                        Delimiter::Parenthesis => quote!(super::Delimiter::Parenthesis),
-                        Delimiter::Brace => quote!(super::Delimiter::Brace),
-                        Delimiter::Bracket => quote!(super::Delimiter::Bracket),
-                        Delimiter::None => quote!(super::Delimiter::None),
-                    }),
-                    (@ quote(tt.stream())),
-                ))),
-                TokenTree::Ident(tt) => quote!(super::TokenTree::Ident(super::Ident::new(
-                    (@ TokenTree::from(Literal::string(&tt.to_string()))),
-                    (@ quote_span(proc_macro_crate.clone(), tt.span())),
-                ))),
-                TokenTree::Literal(tt) => quote!(super::TokenTree::Literal({
-                    let mut iter = (@ TokenTree::from(Literal::string(&tt.to_string())))
-                        .parse::<super::TokenStream>()
-                        .unwrap()
-                        .into_iter();
-                    if let (Some(super::TokenTree::Literal(mut lit)), None) =
-                        (iter.next(), iter.next())
-                    {
-                        lit.set_span((@ quote_span(proc_macro_crate.clone(), tt.span())));
-                        lit
-                    } else {
-                        unreachable!()
-                    }
-                }))
-            })),))
-        })
-        .collect::<TokenStream>();
-
-    if after_dollar {
-        panic!("unexpected trailing `$` in `quote!`");
-    }
-
-    quote!([(@ tokens)].iter().cloned().collect::<super::TokenStream>())
-}
-
-/// Quote a `Span` into a `TokenStream`.
-/// This is needed to implement a custom quoter.
-pub fn quote_span(proc_macro_crate: TokenStream, span: Span) -> TokenStream {
-    let id = span.save_span();
-    quote!((@ proc_macro_crate ) ::Span::recover_proc_macro_span((@ TokenTree::from(Literal::usize_unsuffixed(id)))))
-}
diff --git a/crates/proc-macro-srv/src/abis/abi_1_63/ra_server.rs b/crates/proc-macro-srv/src/abis/abi_1_63/ra_server.rs
deleted file mode 100644
index 30baf3a13f5..00000000000
--- a/crates/proc-macro-srv/src/abis/abi_1_63/ra_server.rs
+++ /dev/null
@@ -1,840 +0,0 @@
-//! Rustc proc-macro server implementation with tt
-//!
-//! Based on idea from <https://github.com/fedochet/rust-proc-macro-expander>
-//! The lib-proc-macro server backend is `TokenStream`-agnostic, such that
-//! we could provide any TokenStream implementation.
-//! The original idea from fedochet is using proc-macro2 as backend,
-//! we use tt instead for better integration with RA.
-//!
-//! FIXME: No span and source file information is implemented yet
-
-use super::proc_macro::bridge::{self, server};
-
-use std::collections::HashMap;
-use std::hash::Hash;
-use std::ops::Bound;
-use std::{ascii, vec::IntoIter};
-
-use crate::tt;
-
-type Group = tt::Subtree;
-type TokenTree = tt::TokenTree;
-type Punct = tt::Punct;
-type Spacing = tt::Spacing;
-type Literal = tt::Literal;
-type Span = tt::TokenId;
-
-#[derive(Debug, Default, Clone)]
-pub struct TokenStream {
-    pub token_trees: Vec<TokenTree>,
-}
-
-impl TokenStream {
-    pub fn new() -> Self {
-        TokenStream::default()
-    }
-
-    pub fn with_subtree(subtree: tt::Subtree) -> Self {
-        if subtree.delimiter.kind != tt::DelimiterKind::Invisible {
-            TokenStream { token_trees: vec![TokenTree::Subtree(subtree)] }
-        } else {
-            TokenStream { token_trees: subtree.token_trees }
-        }
-    }
-
-    pub fn into_subtree(self) -> tt::Subtree {
-        tt::Subtree { delimiter: tt::Delimiter::UNSPECIFIED, token_trees: self.token_trees }
-    }
-
-    pub fn is_empty(&self) -> bool {
-        self.token_trees.is_empty()
-    }
-}
-
-/// Creates a token stream containing a single token tree.
-impl From<TokenTree> for TokenStream {
-    fn from(tree: TokenTree) -> TokenStream {
-        TokenStream { token_trees: vec![tree] }
-    }
-}
-
-/// Collects a number of token trees into a single stream.
-impl FromIterator<TokenTree> for TokenStream {
-    fn from_iter<I: IntoIterator<Item = TokenTree>>(trees: I) -> Self {
-        trees.into_iter().map(TokenStream::from).collect()
-    }
-}
-
-/// A "flattening" operation on token streams, collects token trees
-/// from multiple token streams into a single stream.
-impl FromIterator<TokenStream> for TokenStream {
-    fn from_iter<I: IntoIterator<Item = TokenStream>>(streams: I) -> Self {
-        let mut builder = TokenStreamBuilder::new();
-        streams.into_iter().for_each(|stream| builder.push(stream));
-        builder.build()
-    }
-}
-
-impl Extend<TokenTree> for TokenStream {
-    fn extend<I: IntoIterator<Item = TokenTree>>(&mut self, trees: I) {
-        self.extend(trees.into_iter().map(TokenStream::from));
-    }
-}
-
-impl Extend<TokenStream> for TokenStream {
-    fn extend<I: IntoIterator<Item = TokenStream>>(&mut self, streams: I) {
-        for item in streams {
-            for tkn in item {
-                match tkn {
-                    tt::TokenTree::Subtree(subtree)
-                        if subtree.delimiter.kind == tt::DelimiterKind::Invisible =>
-                    {
-                        self.token_trees.extend(subtree.token_trees);
-                    }
-                    _ => {
-                        self.token_trees.push(tkn);
-                    }
-                }
-            }
-        }
-    }
-}
-
-#[derive(Clone)]
-pub struct SourceFile {
-    // FIXME stub
-}
-
-type Level = super::proc_macro::Level;
-type LineColumn = super::proc_macro::LineColumn;
-
-/// A structure representing a diagnostic message and associated children
-/// messages.
-#[derive(Clone, Debug)]
-pub struct Diagnostic {
-    level: Level,
-    message: String,
-    spans: Vec<Span>,
-    children: Vec<Diagnostic>,
-}
-
-impl Diagnostic {
-    /// Creates a new diagnostic with the given `level` and `message`.
-    pub fn new<T: Into<String>>(level: Level, message: T) -> Diagnostic {
-        Diagnostic { level, message: message.into(), spans: vec![], children: vec![] }
-    }
-}
-
-// Rustc Server Ident has to be `Copyable`
-// We use a stub here for bypassing
-#[derive(Hash, Eq, PartialEq, Copy, Clone)]
-pub struct IdentId(u32);
-
-#[derive(Clone, Hash, Eq, PartialEq)]
-struct IdentData(tt::Ident);
-
-#[derive(Default)]
-struct IdentInterner {
-    idents: HashMap<IdentData, u32>,
-    ident_data: Vec<IdentData>,
-}
-
-impl IdentInterner {
-    fn intern(&mut self, data: &IdentData) -> u32 {
-        if let Some(index) = self.idents.get(data) {
-            return *index;
-        }
-
-        let index = self.idents.len() as u32;
-        self.ident_data.push(data.clone());
-        self.idents.insert(data.clone(), index);
-        index
-    }
-
-    fn get(&self, index: u32) -> &IdentData {
-        &self.ident_data[index as usize]
-    }
-
-    #[allow(unused)]
-    fn get_mut(&mut self, index: u32) -> &mut IdentData {
-        self.ident_data.get_mut(index as usize).expect("Should be consistent")
-    }
-}
-
-pub struct TokenStreamBuilder {
-    acc: TokenStream,
-}
-
-/// Public implementation details for the `TokenStream` type, such as iterators.
-pub mod token_stream {
-    use std::str::FromStr;
-
-    use super::{tt, TokenStream, TokenTree};
-
-    /// An iterator over `TokenStream`'s `TokenTree`s.
-    /// The iteration is "shallow", e.g., the iterator doesn't recurse into delimited groups,
-    /// and returns whole groups as token trees.
-    impl IntoIterator for TokenStream {
-        type Item = TokenTree;
-        type IntoIter = super::IntoIter<TokenTree>;
-
-        fn into_iter(self) -> Self::IntoIter {
-            self.token_trees.into_iter()
-        }
-    }
-
-    type LexError = String;
-
-    /// Attempts to break the string into tokens and parse those tokens into a token stream.
-    /// May fail for a number of reasons, for example, if the string contains unbalanced delimiters
-    /// or characters not existing in the language.
-    /// All tokens in the parsed stream get `Span::call_site()` spans.
-    ///
-    /// NOTE: some errors may cause panics instead of returning `LexError`. We reserve the right to
-    /// change these errors into `LexError`s later.
-    impl FromStr for TokenStream {
-        type Err = LexError;
-
-        fn from_str(src: &str) -> Result<TokenStream, LexError> {
-            let (subtree, _token_map) =
-                mbe::parse_to_token_tree(src).ok_or("Failed to parse from mbe")?;
-
-            let subtree = subtree_replace_token_ids_with_unspecified(subtree);
-            Ok(TokenStream::with_subtree(subtree))
-        }
-    }
-
-    impl ToString for TokenStream {
-        fn to_string(&self) -> String {
-            ::tt::pretty(&self.token_trees)
-        }
-    }
-
-    fn subtree_replace_token_ids_with_unspecified(subtree: tt::Subtree) -> tt::Subtree {
-        tt::Subtree {
-            delimiter: tt::Delimiter {
-                open: tt::TokenId::UNSPECIFIED,
-                close: tt::TokenId::UNSPECIFIED,
-                ..subtree.delimiter
-            },
-            token_trees: subtree
-                .token_trees
-                .into_iter()
-                .map(token_tree_replace_token_ids_with_unspecified)
-                .collect(),
-        }
-    }
-
-    fn token_tree_replace_token_ids_with_unspecified(tt: tt::TokenTree) -> tt::TokenTree {
-        match tt {
-            tt::TokenTree::Leaf(leaf) => {
-                tt::TokenTree::Leaf(leaf_replace_token_ids_with_unspecified(leaf))
-            }
-            tt::TokenTree::Subtree(subtree) => {
-                tt::TokenTree::Subtree(subtree_replace_token_ids_with_unspecified(subtree))
-            }
-        }
-    }
-
-    fn leaf_replace_token_ids_with_unspecified(leaf: tt::Leaf) -> tt::Leaf {
-        match leaf {
-            tt::Leaf::Literal(lit) => {
-                tt::Leaf::Literal(tt::Literal { span: tt::TokenId::unspecified(), ..lit })
-            }
-            tt::Leaf::Punct(punct) => {
-                tt::Leaf::Punct(tt::Punct { span: tt::TokenId::unspecified(), ..punct })
-            }
-            tt::Leaf::Ident(ident) => {
-                tt::Leaf::Ident(tt::Ident { span: tt::TokenId::unspecified(), ..ident })
-            }
-        }
-    }
-}
-
-impl TokenStreamBuilder {
-    fn new() -> TokenStreamBuilder {
-        TokenStreamBuilder { acc: TokenStream::new() }
-    }
-
-    fn push(&mut self, stream: TokenStream) {
-        self.acc.extend(stream.into_iter())
-    }
-
-    fn build(self) -> TokenStream {
-        self.acc
-    }
-}
-
-pub struct FreeFunctions;
-
-#[derive(Clone)]
-pub struct TokenStreamIter {
-    trees: IntoIter<TokenTree>,
-}
-
-#[derive(Default)]
-pub struct RustAnalyzer {
-    ident_interner: IdentInterner,
-    // FIXME: store span information here.
-}
-
-impl server::Types for RustAnalyzer {
-    type FreeFunctions = FreeFunctions;
-    type TokenStream = TokenStream;
-    type Group = Group;
-    type Punct = Punct;
-    type Ident = IdentId;
-    type Literal = Literal;
-    type SourceFile = SourceFile;
-    type Diagnostic = Diagnostic;
-    type Span = Span;
-    type MultiSpan = Vec<Span>;
-}
-
-impl server::FreeFunctions for RustAnalyzer {
-    fn track_env_var(&mut self, _var: &str, _value: Option<&str>) {
-        // FIXME: track env var accesses
-        // https://github.com/rust-lang/rust/pull/71858
-    }
-    fn track_path(&mut self, _path: &str) {}
-}
-
-impl server::TokenStream for RustAnalyzer {
-    fn is_empty(&mut self, stream: &Self::TokenStream) -> bool {
-        stream.is_empty()
-    }
-    fn from_str(&mut self, src: &str) -> Self::TokenStream {
-        use std::str::FromStr;
-
-        Self::TokenStream::from_str(src).expect("cannot parse string")
-    }
-    fn to_string(&mut self, stream: &Self::TokenStream) -> String {
-        stream.to_string()
-    }
-    fn from_token_tree(
-        &mut self,
-        tree: bridge::TokenTree<Self::Group, Self::Punct, Self::Ident, Self::Literal>,
-    ) -> Self::TokenStream {
-        match tree {
-            bridge::TokenTree::Group(group) => {
-                let tree = TokenTree::from(group);
-                Self::TokenStream::from_iter(vec![tree])
-            }
-
-            bridge::TokenTree::Ident(IdentId(index)) => {
-                let IdentData(ident) = self.ident_interner.get(index).clone();
-                let ident: tt::Ident = ident;
-                let leaf = tt::Leaf::from(ident);
-                let tree = TokenTree::from(leaf);
-                Self::TokenStream::from_iter(vec![tree])
-            }
-
-            bridge::TokenTree::Literal(literal) => {
-                let leaf = tt::Leaf::from(literal);
-                let tree = TokenTree::from(leaf);
-                Self::TokenStream::from_iter(vec![tree])
-            }
-
-            bridge::TokenTree::Punct(p) => {
-                let leaf = tt::Leaf::from(p);
-                let tree = TokenTree::from(leaf);
-                Self::TokenStream::from_iter(vec![tree])
-            }
-        }
-    }
-
-    fn expand_expr(&mut self, self_: &Self::TokenStream) -> Result<Self::TokenStream, ()> {
-        Ok(self_.clone())
-    }
-
-    fn concat_trees(
-        &mut self,
-        base: Option<Self::TokenStream>,
-        trees: Vec<bridge::TokenTree<Self::Group, Self::Punct, Self::Ident, Self::Literal>>,
-    ) -> Self::TokenStream {
-        let mut builder = TokenStreamBuilder::new();
-        if let Some(base) = base {
-            builder.push(base);
-        }
-        for tree in trees {
-            builder.push(self.from_token_tree(tree));
-        }
-        builder.build()
-    }
-
-    fn concat_streams(
-        &mut self,
-        base: Option<Self::TokenStream>,
-        streams: Vec<Self::TokenStream>,
-    ) -> Self::TokenStream {
-        let mut builder = TokenStreamBuilder::new();
-        if let Some(base) = base {
-            builder.push(base);
-        }
-        for stream in streams {
-            builder.push(stream);
-        }
-        builder.build()
-    }
-
-    fn into_trees(
-        &mut self,
-        stream: Self::TokenStream,
-    ) -> Vec<bridge::TokenTree<Self::Group, Self::Punct, Self::Ident, Self::Literal>> {
-        stream
-            .into_iter()
-            .map(|tree| match tree {
-                tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) => {
-                    bridge::TokenTree::Ident(IdentId(self.ident_interner.intern(&IdentData(ident))))
-                }
-                tt::TokenTree::Leaf(tt::Leaf::Literal(lit)) => bridge::TokenTree::Literal(lit),
-                tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) => bridge::TokenTree::Punct(punct),
-                tt::TokenTree::Subtree(subtree) => bridge::TokenTree::Group(subtree),
-            })
-            .collect()
-    }
-}
-
-fn delim_to_internal(d: bridge::Delimiter) -> tt::Delimiter {
-    let kind = match d {
-        bridge::Delimiter::Parenthesis => tt::DelimiterKind::Parenthesis,
-        bridge::Delimiter::Brace => tt::DelimiterKind::Brace,
-        bridge::Delimiter::Bracket => tt::DelimiterKind::Bracket,
-        bridge::Delimiter::None => tt::DelimiterKind::Invisible,
-    };
-    tt::Delimiter { open: tt::TokenId::unspecified(), close: tt::TokenId::unspecified(), kind }
-}
-
-fn delim_to_external(d: tt::Delimiter) -> bridge::Delimiter {
-    match d.kind {
-        tt::DelimiterKind::Parenthesis => bridge::Delimiter::Parenthesis,
-        tt::DelimiterKind::Brace => bridge::Delimiter::Brace,
-        tt::DelimiterKind::Bracket => bridge::Delimiter::Bracket,
-        tt::DelimiterKind::Invisible => bridge::Delimiter::None,
-    }
-}
-
-fn spacing_to_internal(spacing: bridge::Spacing) -> Spacing {
-    match spacing {
-        bridge::Spacing::Alone => Spacing::Alone,
-        bridge::Spacing::Joint => Spacing::Joint,
-    }
-}
-
-fn spacing_to_external(spacing: Spacing) -> bridge::Spacing {
-    match spacing {
-        Spacing::Alone => bridge::Spacing::Alone,
-        Spacing::Joint => bridge::Spacing::Joint,
-    }
-}
-
-impl server::Group for RustAnalyzer {
-    fn new(
-        &mut self,
-        delimiter: bridge::Delimiter,
-        stream: Option<Self::TokenStream>,
-    ) -> Self::Group {
-        Self::Group {
-            delimiter: delim_to_internal(delimiter),
-            token_trees: stream.unwrap_or_default().token_trees,
-        }
-    }
-    fn delimiter(&mut self, group: &Self::Group) -> bridge::Delimiter {
-        delim_to_external(group.delimiter)
-    }
-
-    // NOTE: Return value of do not include delimiter
-    fn stream(&mut self, group: &Self::Group) -> Self::TokenStream {
-        TokenStream { token_trees: group.token_trees.clone() }
-    }
-
-    fn span(&mut self, group: &Self::Group) -> Self::Span {
-        group.delimiter.open
-    }
-
-    fn set_span(&mut self, group: &mut Self::Group, span: Self::Span) {
-        group.delimiter.open = span;
-    }
-
-    fn span_open(&mut self, group: &Self::Group) -> Self::Span {
-        group.delimiter.open
-    }
-
-    fn span_close(&mut self, group: &Self::Group) -> Self::Span {
-        group.delimiter.close
-    }
-}
-
-impl server::Punct for RustAnalyzer {
-    fn new(&mut self, ch: char, spacing: bridge::Spacing) -> Self::Punct {
-        tt::Punct {
-            char: ch,
-            spacing: spacing_to_internal(spacing),
-            span: tt::TokenId::unspecified(),
-        }
-    }
-    fn as_char(&mut self, punct: Self::Punct) -> char {
-        punct.char
-    }
-    fn spacing(&mut self, punct: Self::Punct) -> bridge::Spacing {
-        spacing_to_external(punct.spacing)
-    }
-    fn span(&mut self, punct: Self::Punct) -> Self::Span {
-        punct.span
-    }
-    fn with_span(&mut self, punct: Self::Punct, span: Self::Span) -> Self::Punct {
-        tt::Punct { span: span, ..punct }
-    }
-}
-
-impl server::Ident for RustAnalyzer {
-    fn new(&mut self, string: &str, span: Self::Span, is_raw: bool) -> Self::Ident {
-        IdentId(self.ident_interner.intern(&IdentData(tt::Ident {
-            text: if is_raw { ::tt::SmolStr::from_iter(["r#", string]) } else { string.into() },
-            span,
-        })))
-    }
-
-    fn span(&mut self, ident: Self::Ident) -> Self::Span {
-        self.ident_interner.get(ident.0).0.span
-    }
-    fn with_span(&mut self, ident: Self::Ident, span: Self::Span) -> Self::Ident {
-        let data = self.ident_interner.get(ident.0);
-        let new = IdentData(tt::Ident { span: span, ..data.0.clone() });
-        IdentId(self.ident_interner.intern(&new))
-    }
-}
-
-impl server::Literal for RustAnalyzer {
-    fn debug_kind(&mut self, _literal: &Self::Literal) -> String {
-        // r-a: debug_kind and suffix are unsupported; corresponding client code has been changed to not call these.
-        // They must still be present to be ABI-compatible and work with upstream proc_macro.
-        "".to_owned()
-    }
-    fn from_str(&mut self, s: &str) -> Result<Self::Literal, ()> {
-        Ok(Literal { text: s.into(), span: tt::TokenId::unspecified() })
-    }
-    fn symbol(&mut self, literal: &Self::Literal) -> String {
-        literal.text.to_string()
-    }
-    fn suffix(&mut self, _literal: &Self::Literal) -> Option<String> {
-        None
-    }
-
-    fn to_string(&mut self, literal: &Self::Literal) -> String {
-        literal.to_string()
-    }
-
-    fn integer(&mut self, n: &str) -> Self::Literal {
-        let n = match n.parse::<i128>() {
-            Ok(n) => n.to_string(),
-            Err(_) => n.parse::<u128>().unwrap().to_string(),
-        };
-        Literal { text: n.into(), span: tt::TokenId::unspecified() }
-    }
-
-    fn typed_integer(&mut self, n: &str, kind: &str) -> Self::Literal {
-        macro_rules! def_suffixed_integer {
-            ($kind:ident, $($ty:ty),*) => {
-                match $kind {
-                    $(
-                        stringify!($ty) => {
-                            let n: $ty = n.parse().unwrap();
-                            format!(concat!("{}", stringify!($ty)), n)
-                        }
-                    )*
-                    _ => unimplemented!("unknown args for typed_integer: n {}, kind {}", n, $kind),
-                }
-            }
-        }
-
-        let text = def_suffixed_integer! {kind, u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize};
-
-        Literal { text: text.into(), span: tt::TokenId::unspecified() }
-    }
-
-    fn float(&mut self, n: &str) -> Self::Literal {
-        let n: f64 = n.parse().unwrap();
-        let mut text = f64::to_string(&n);
-        if !text.contains('.') {
-            text += ".0"
-        }
-        Literal { text: text.into(), span: tt::TokenId::unspecified() }
-    }
-
-    fn f32(&mut self, n: &str) -> Self::Literal {
-        let n: f32 = n.parse().unwrap();
-        let text = format!("{n}f32");
-        Literal { text: text.into(), span: tt::TokenId::unspecified() }
-    }
-
-    fn f64(&mut self, n: &str) -> Self::Literal {
-        let n: f64 = n.parse().unwrap();
-        let text = format!("{n}f64");
-        Literal { text: text.into(), span: tt::TokenId::unspecified() }
-    }
-
-    fn string(&mut self, string: &str) -> Self::Literal {
-        let mut escaped = String::new();
-        for ch in string.chars() {
-            escaped.extend(ch.escape_debug());
-        }
-        Literal { text: format!("\"{escaped}\"").into(), span: tt::TokenId::unspecified() }
-    }
-
-    fn character(&mut self, ch: char) -> Self::Literal {
-        Literal { text: format!("'{ch}'").into(), span: tt::TokenId::unspecified() }
-    }
-
-    fn byte_string(&mut self, bytes: &[u8]) -> Self::Literal {
-        let string = bytes
-            .iter()
-            .cloned()
-            .flat_map(ascii::escape_default)
-            .map(Into::<char>::into)
-            .collect::<String>();
-
-        Literal { text: format!("b\"{string}\"").into(), span: tt::TokenId::unspecified() }
-    }
-
-    fn span(&mut self, literal: &Self::Literal) -> Self::Span {
-        literal.span
-    }
-
-    fn set_span(&mut self, literal: &mut Self::Literal, span: Self::Span) {
-        literal.span = span;
-    }
-
-    fn subspan(
-        &mut self,
-        _literal: &Self::Literal,
-        _start: Bound<usize>,
-        _end: Bound<usize>,
-    ) -> Option<Self::Span> {
-        // FIXME handle span
-        None
-    }
-}
-
-impl server::SourceFile for RustAnalyzer {
-    // FIXME these are all stubs
-    fn eq(&mut self, _file1: &Self::SourceFile, _file2: &Self::SourceFile) -> bool {
-        true
-    }
-    fn path(&mut self, _file: &Self::SourceFile) -> String {
-        String::new()
-    }
-    fn is_real(&mut self, _file: &Self::SourceFile) -> bool {
-        true
-    }
-}
-
-impl server::Diagnostic for RustAnalyzer {
-    fn new(&mut self, level: Level, msg: &str, spans: Self::MultiSpan) -> Self::Diagnostic {
-        let mut diag = Diagnostic::new(level, msg);
-        diag.spans = spans;
-        diag
-    }
-
-    fn sub(
-        &mut self,
-        _diag: &mut Self::Diagnostic,
-        _level: Level,
-        _msg: &str,
-        _spans: Self::MultiSpan,
-    ) {
-        // FIXME handle diagnostic
-        //
-    }
-
-    fn emit(&mut self, _diag: Self::Diagnostic) {
-        // FIXME handle diagnostic
-        // diag.emit()
-    }
-}
-
-impl server::Span for RustAnalyzer {
-    fn debug(&mut self, span: Self::Span) -> String {
-        format!("{:?}", span.0)
-    }
-    fn def_site(&mut self) -> Self::Span {
-        // MySpan(self.span_interner.intern(&MySpanData(Span::def_site())))
-        // FIXME handle span
-        tt::TokenId::unspecified()
-    }
-    fn call_site(&mut self) -> Self::Span {
-        // MySpan(self.span_interner.intern(&MySpanData(Span::call_site())))
-        // FIXME handle span
-        tt::TokenId::unspecified()
-    }
-    fn source_file(&mut self, _span: Self::Span) -> Self::SourceFile {
-        SourceFile {}
-    }
-    fn save_span(&mut self, _span: Self::Span) -> usize {
-        // FIXME stub
-        0
-    }
-    fn recover_proc_macro_span(&mut self, _id: usize) -> Self::Span {
-        // FIXME stub
-        tt::TokenId::unspecified()
-    }
-    /// Recent feature, not yet in the proc_macro
-    ///
-    /// See PR:
-    /// https://github.com/rust-lang/rust/pull/55780
-    fn source_text(&mut self, _span: Self::Span) -> Option<String> {
-        None
-    }
-
-    fn parent(&mut self, _span: Self::Span) -> Option<Self::Span> {
-        // FIXME handle span
-        None
-    }
-    fn source(&mut self, span: Self::Span) -> Self::Span {
-        // FIXME handle span
-        span
-    }
-    fn start(&mut self, _span: Self::Span) -> LineColumn {
-        // FIXME handle span
-        LineColumn { line: 0, column: 0 }
-    }
-    fn end(&mut self, _span: Self::Span) -> LineColumn {
-        // FIXME handle span
-        LineColumn { line: 0, column: 0 }
-    }
-    fn join(&mut self, first: Self::Span, _second: Self::Span) -> Option<Self::Span> {
-        // Just return the first span again, because some macros will unwrap the result.
-        Some(first)
-    }
-    fn resolved_at(&mut self, _span: Self::Span, _at: Self::Span) -> Self::Span {
-        // FIXME handle span
-        tt::TokenId::unspecified()
-    }
-
-    fn mixed_site(&mut self) -> Self::Span {
-        // FIXME handle span
-        tt::TokenId::unspecified()
-    }
-
-    fn after(&mut self, _self_: Self::Span) -> Self::Span {
-        tt::TokenId::unspecified()
-    }
-
-    fn before(&mut self, _self_: Self::Span) -> Self::Span {
-        tt::TokenId::unspecified()
-    }
-}
-
-impl server::MultiSpan for RustAnalyzer {
-    fn new(&mut self) -> Self::MultiSpan {
-        // FIXME handle span
-        vec![]
-    }
-
-    fn push(&mut self, other: &mut Self::MultiSpan, span: Self::Span) {
-        //TODP
-        other.push(span)
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use super::super::proc_macro::bridge::server::Literal;
-    use super::*;
-
-    #[test]
-    fn test_ra_server_literals() {
-        let mut srv = RustAnalyzer { ident_interner: IdentInterner::default() };
-        assert_eq!(srv.integer("1234").text, "1234");
-
-        assert_eq!(srv.typed_integer("12", "u8").text, "12u8");
-        assert_eq!(srv.typed_integer("255", "u16").text, "255u16");
-        assert_eq!(srv.typed_integer("1234", "u32").text, "1234u32");
-        assert_eq!(srv.typed_integer("15846685", "u64").text, "15846685u64");
-        assert_eq!(srv.typed_integer("15846685258", "u128").text, "15846685258u128");
-        assert_eq!(srv.typed_integer("156788984", "usize").text, "156788984usize");
-        assert_eq!(srv.typed_integer("127", "i8").text, "127i8");
-        assert_eq!(srv.typed_integer("255", "i16").text, "255i16");
-        assert_eq!(srv.typed_integer("1234", "i32").text, "1234i32");
-        assert_eq!(srv.typed_integer("15846685", "i64").text, "15846685i64");
-        assert_eq!(srv.typed_integer("15846685258", "i128").text, "15846685258i128");
-        assert_eq!(srv.float("0").text, "0.0");
-        assert_eq!(srv.float("15684.5867").text, "15684.5867");
-        assert_eq!(srv.f32("15684.58").text, "15684.58f32");
-        assert_eq!(srv.f64("15684.58").text, "15684.58f64");
-
-        assert_eq!(srv.string("hello_world").text, "\"hello_world\"");
-        assert_eq!(srv.character('c').text, "'c'");
-        assert_eq!(srv.byte_string(b"1234586\x88").text, "b\"1234586\\x88\"");
-
-        // u128::max
-        assert_eq!(
-            srv.integer("340282366920938463463374607431768211455").text,
-            "340282366920938463463374607431768211455"
-        );
-        // i128::min
-        assert_eq!(
-            srv.integer("-170141183460469231731687303715884105728").text,
-            "-170141183460469231731687303715884105728"
-        );
-    }
-
-    #[test]
-    fn test_ra_server_to_string() {
-        let s = TokenStream {
-            token_trees: vec![
-                tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident {
-                    text: "struct".into(),
-                    span: tt::TokenId::unspecified(),
-                })),
-                tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident {
-                    text: "T".into(),
-                    span: tt::TokenId::unspecified(),
-                })),
-                tt::TokenTree::Subtree(tt::Subtree {
-                    delimiter: tt::Delimiter {
-                        open: tt::TokenId::unspecified(),
-                        close: tt::TokenId::unspecified(),
-                        kind: tt::DelimiterKind::Brace,
-                    },
-                    token_trees: vec![],
-                }),
-            ],
-        };
-
-        assert_eq!(s.to_string(), "struct T {}");
-    }
-
-    #[test]
-    fn test_ra_server_from_str() {
-        use std::str::FromStr;
-        let subtree_paren_a = tt::TokenTree::Subtree(tt::Subtree {
-            delimiter: tt::Delimiter {
-                open: tt::TokenId::unspecified(),
-                close: tt::TokenId::unspecified(),
-                kind: tt::DelimiterKind::Parenthesis,
-            },
-            token_trees: vec![tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident {
-                text: "a".into(),
-                span: tt::TokenId::unspecified(),
-            }))],
-        });
-
-        let t1 = TokenStream::from_str("(a)").unwrap();
-        assert_eq!(t1.token_trees.len(), 1);
-        assert_eq!(t1.token_trees[0], subtree_paren_a);
-
-        let t2 = TokenStream::from_str("(a);").unwrap();
-        assert_eq!(t2.token_trees.len(), 2);
-        assert_eq!(t2.token_trees[0], subtree_paren_a);
-
-        let underscore = TokenStream::from_str("_").unwrap();
-        assert_eq!(
-            underscore.token_trees[0],
-            tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident {
-                text: "_".into(),
-                span: tt::TokenId::unspecified(),
-            }))
-        );
-    }
-}
diff --git a/crates/proc-macro-srv/src/abis/mod.rs b/crates/proc-macro-srv/src/abis/mod.rs
deleted file mode 100644
index 04be39cffa4..00000000000
--- a/crates/proc-macro-srv/src/abis/mod.rs
+++ /dev/null
@@ -1,146 +0,0 @@
-//! Procedural macros are implemented by compiling the macro providing crate
-//! to a dynamic library with a particular ABI which the compiler uses to expand
-//! macros. Unfortunately this ABI is not specified and can change from version
-//! to version of the compiler. To support this we copy the ABI from the rust
-//! compiler into submodules of this module (e.g proc_macro_srv::abis::abi_1_47).
-//!
-//! All of these ABIs are subsumed in the `Abi` enum, which exposes a simple
-//! interface the rest of rust-analyzer can use to talk to the macro
-//! provider.
-//!
-//! # Adding a new ABI
-//!
-//! To add a new ABI you'll need to copy the source of the target proc_macro
-//! crate from the source tree of the Rust compiler into this directory tree.
-//! Then you'll need to modify it
-//! - Remove any feature! or other things which won't compile on stable
-//! - change any absolute imports to relative imports within the ABI tree
-//!
-//! Then you'll need to add a branch to the `Abi` enum and an implementation of
-//! `Abi::expand`, `Abi::list_macros` and `Abi::from_lib` for the new ABI. See
-//! `proc_macro_srv/src/abis/abi_1_47/mod.rs` for an example. Finally you'll
-//! need to update the conditionals in `Abi::from_lib` to return your new ABI
-//! for the relevant versions of the rust compiler
-//!
-
-mod abi_1_63;
-#[cfg(feature = "sysroot-abi")]
-mod abi_sysroot;
-
-// see `build.rs`
-include!(concat!(env!("OUT_DIR"), "/rustc_version.rs"));
-
-// Used by `test/utils.rs`
-#[cfg(all(test, feature = "sysroot-abi"))]
-pub(crate) use abi_sysroot::TokenStream as TestTokenStream;
-
-use super::dylib::LoadProcMacroDylibError;
-pub(crate) use abi_1_63::Abi as Abi_1_63;
-#[cfg(feature = "sysroot-abi")]
-pub(crate) use abi_sysroot::Abi as Abi_Sysroot;
-use libloading::Library;
-use proc_macro_api::{ProcMacroKind, RustCInfo};
-
-use crate::tt;
-
-pub struct PanicMessage {
-    message: Option<String>,
-}
-
-impl PanicMessage {
-    pub fn as_str(&self) -> Option<String> {
-        self.message.clone()
-    }
-}
-
-pub(crate) enum Abi {
-    Abi1_63(Abi_1_63),
-    #[cfg(feature = "sysroot-abi")]
-    AbiSysroot(Abi_Sysroot),
-}
-
-impl Abi {
-    /// Load a new ABI.
-    ///
-    /// # Arguments
-    ///
-    /// *`lib` - The dynamic library containing the macro implementations
-    /// *`symbol_name` - The symbol name the macros can be found attributes
-    /// *`info` - RustCInfo about the compiler that was used to compile the
-    ///           macro crate. This is the information we use to figure out
-    ///           which ABI to return
-    pub fn from_lib(
-        lib: &Library,
-        symbol_name: String,
-        info: RustCInfo,
-    ) -> Result<Abi, LoadProcMacroDylibError> {
-        // the sysroot ABI relies on `extern proc_macro` with unstable features,
-        // instead of a snapshot of the proc macro bridge's source code. it's only
-        // enabled if we have an exact version match.
-        #[cfg(feature = "sysroot-abi")]
-        {
-            if info.version_string == RUSTC_VERSION_STRING {
-                let inner = unsafe { Abi_Sysroot::from_lib(lib, symbol_name) }?;
-                return Ok(Abi::AbiSysroot(inner));
-            }
-
-            // if we reached this point, versions didn't match. in testing, we
-            // want that to panic - this could mean that the format of `rustc
-            // --version` no longer matches the format of the version string
-            // stored in the `.rustc` section, and we want to catch that in-tree
-            // with `x.py test`
-            #[cfg(test)]
-            {
-                let allow_mismatch = std::env::var("PROC_MACRO_SRV_ALLOW_SYSROOT_MISMATCH");
-                if let Ok("1") = allow_mismatch.as_deref() {
-                    // only used by rust-analyzer developers, when working on the
-                    // sysroot ABI from the rust-analyzer repository - which should
-                    // only happen pre-subtree. this can be removed later.
-                } else {
-                    panic!(
-                        "sysroot ABI mismatch: dylib rustc version (read from .rustc section): {:?} != proc-macro-srv version (read from 'rustc --version'): {:?}",
-                        info.version_string, RUSTC_VERSION_STRING
-                    );
-                }
-            }
-        }
-
-        // FIXME: this should use exclusive ranges when they're stable
-        // https://github.com/rust-lang/rust/issues/37854
-        match (info.version.0, info.version.1) {
-            (1, 63) => {
-                let inner = unsafe { Abi_1_63::from_lib(lib, symbol_name) }?;
-                Ok(Abi::Abi1_63(inner))
-            }
-            _ => Err(LoadProcMacroDylibError::UnsupportedABI(info.version_string)),
-        }
-    }
-
-    pub fn expand(
-        &self,
-        macro_name: &str,
-        macro_body: &tt::Subtree,
-        attributes: Option<&tt::Subtree>,
-    ) -> Result<tt::Subtree, PanicMessage> {
-        match self {
-            Self::Abi1_63(abi) => abi.expand(macro_name, macro_body, attributes),
-            #[cfg(feature = "sysroot-abi")]
-            Self::AbiSysroot(abi) => abi.expand(macro_name, macro_body, attributes),
-        }
-    }
-
-    pub fn list_macros(&self) -> Vec<(String, ProcMacroKind)> {
-        match self {
-            Self::Abi1_63(abi) => abi.list_macros(),
-            #[cfg(feature = "sysroot-abi")]
-            Self::AbiSysroot(abi) => abi.list_macros(),
-        }
-    }
-}
-
-#[test]
-fn test_version_check() {
-    let path = paths::AbsPathBuf::assert(crate::proc_macro_test_dylib_path());
-    let info = proc_macro_api::read_dylib_info(&path).unwrap();
-    assert!(info.version.1 >= 50);
-}
diff --git a/crates/proc-macro-srv/src/dylib.rs b/crates/proc-macro-srv/src/dylib.rs
index 89ffd1f493e..dd05e250c2d 100644
--- a/crates/proc-macro-srv/src/dylib.rs
+++ b/crates/proc-macro-srv/src/dylib.rs
@@ -13,10 +13,6 @@ use object::Object;
 use paths::AbsPath;
 use proc_macro_api::{read_dylib_info, ProcMacroKind};
 
-use crate::tt;
-
-use super::abis::Abi;
-
 const NEW_REGISTRAR_SYMBOL: &str = "_rustc_proc_macro_decls_";
 
 fn invalid_data_err(e: impl Into<Box<dyn std::error::Error + Send + Sync>>) -> io::Error {
@@ -82,14 +78,17 @@ fn load_library(file: &Path) -> Result<Library, libloading::Error> {
 pub enum LoadProcMacroDylibError {
     Io(io::Error),
     LibLoading(libloading::Error),
-    UnsupportedABI(String),
+    AbiMismatch(String),
 }
 
 impl fmt::Display for LoadProcMacroDylibError {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         match self {
             Self::Io(e) => e.fmt(f),
-            Self::UnsupportedABI(v) => write!(f, "unsupported ABI `{v}`"),
+            Self::AbiMismatch(v) => {
+                use crate::RUSTC_VERSION_STRING;
+                write!(f, "mismatched ABI expected: `{RUSTC_VERSION_STRING}`, got `{v}`")
+            }
             Self::LibLoading(e) => e.fmt(f),
         }
     }
@@ -110,7 +109,7 @@ impl From<libloading::Error> for LoadProcMacroDylibError {
 struct ProcMacroLibraryLibloading {
     // Hold on to the library so it doesn't unload
     _lib: Library,
-    abi: Abi,
+    proc_macros: crate::proc_macros::ProcMacros,
 }
 
 impl ProcMacroLibraryLibloading {
@@ -125,8 +124,9 @@ impl ProcMacroLibraryLibloading {
         let version_info = read_dylib_info(abs_file)?;
 
         let lib = load_library(file).map_err(invalid_data_err)?;
-        let abi = Abi::from_lib(&lib, symbol_name, version_info)?;
-        Ok(ProcMacroLibraryLibloading { _lib: lib, abi })
+        let proc_macros =
+            crate::proc_macros::ProcMacros::from_lib(&lib, symbol_name, version_info)?;
+        Ok(ProcMacroLibraryLibloading { _lib: lib, proc_macros })
     }
 }
 
@@ -150,15 +150,15 @@ impl Expander {
     pub fn expand(
         &self,
         macro_name: &str,
-        macro_body: &tt::Subtree,
-        attributes: Option<&tt::Subtree>,
-    ) -> Result<tt::Subtree, String> {
-        let result = self.inner.abi.expand(macro_name, macro_body, attributes);
+        macro_body: &crate::tt::Subtree,
+        attributes: Option<&crate::tt::Subtree>,
+    ) -> Result<crate::tt::Subtree, String> {
+        let result = self.inner.proc_macros.expand(macro_name, macro_body, attributes);
         result.map_err(|e| e.as_str().unwrap_or_else(|| "<unknown error>".to_string()))
     }
 
     pub fn list_macros(&self) -> Vec<(String, ProcMacroKind)> {
-        self.inner.abi.list_macros()
+        self.inner.proc_macros.list_macros()
     }
 }
 
diff --git a/crates/proc-macro-srv/src/lib.rs b/crates/proc-macro-srv/src/lib.rs
index ee70fe7d4f5..2aac3790313 100644
--- a/crates/proc-macro-srv/src/lib.rs
+++ b/crates/proc-macro-srv/src/lib.rs
@@ -10,17 +10,16 @@
 //! * By **copying** the whole rustc `lib_proc_macro` code, we are able to build this with `stable`
 //!   rustc rather than `unstable`. (Although in general ABI compatibility is still an issue)…
 
+#![cfg(feature = "sysroot-abi")]
+#![feature(proc_macro_internals, proc_macro_diagnostic, proc_macro_span)]
 #![warn(rust_2018_idioms, unused_lifetimes, semicolon_in_expressions_from_macros)]
-#![cfg_attr(
-    feature = "sysroot-abi",
-    feature(proc_macro_internals, proc_macro_diagnostic, proc_macro_span)
-)]
 #![allow(unreachable_pub)]
 
-mod dylib;
-mod abis;
+extern crate proc_macro;
 
-pub mod cli;
+mod dylib;
+mod server;
+mod proc_macros;
 
 use std::{
     collections::{hash_map::Entry, HashMap},
@@ -32,25 +31,25 @@ use std::{
     time::SystemTime,
 };
 
-use proc_macro_api::{
-    msg::{ExpandMacro, FlatTree, PanicMessage},
-    ProcMacroKind,
-};
+use proc_macro_api::{msg, ProcMacroKind};
 
 use ::tt::token_id as tt;
 
+// see `build.rs`
+include!(concat!(env!("OUT_DIR"), "/rustc_version.rs"));
+
 #[derive(Default)]
-pub(crate) struct ProcMacroSrv {
+pub struct ProcMacroSrv {
     expanders: HashMap<(PathBuf, SystemTime), dylib::Expander>,
 }
 
 const EXPANDER_STACK_SIZE: usize = 8 * 1024 * 1024;
 
 impl ProcMacroSrv {
-    pub fn expand(&mut self, task: ExpandMacro) -> Result<FlatTree, PanicMessage> {
+    pub fn expand(&mut self, task: msg::ExpandMacro) -> Result<msg::FlatTree, msg::PanicMessage> {
         let expander = self.expander(task.lib.as_ref()).map_err(|err| {
             debug_assert!(false, "should list macros before asking to expand");
-            PanicMessage(format!("failed to load macro: {err}"))
+            msg::PanicMessage(format!("failed to load macro: {err}"))
         })?;
 
         let prev_env = EnvSnapshot::new();
@@ -77,7 +76,7 @@ impl ProcMacroSrv {
                 .spawn_scoped(s, || {
                     expander
                         .expand(&task.macro_name, &macro_body, attributes.as_ref())
-                        .map(|it| FlatTree::new(&it))
+                        .map(|it| msg::FlatTree::new(&it))
                 });
             let res = match thread {
                 Ok(handle) => handle.join(),
@@ -102,10 +101,10 @@ impl ProcMacroSrv {
             }
         }
 
-        result.map_err(PanicMessage)
+        result.map_err(msg::PanicMessage)
     }
 
-    pub(crate) fn list_macros(
+    pub fn list_macros(
         &mut self,
         dylib_path: &Path,
     ) -> Result<Vec<(String, ProcMacroKind)>, String> {
@@ -129,6 +128,16 @@ impl ProcMacroSrv {
     }
 }
 
+pub struct PanicMessage {
+    message: Option<String>,
+}
+
+impl PanicMessage {
+    pub fn as_str(&self) -> Option<String> {
+        self.message.clone()
+    }
+}
+
 struct EnvSnapshot {
     vars: HashMap<OsString, OsString>,
 }
@@ -138,10 +147,13 @@ impl EnvSnapshot {
         EnvSnapshot { vars: env::vars_os().collect() }
     }
 
-    fn rollback(self) {
-        let mut old_vars = self.vars;
+    fn rollback(self) {}
+}
+
+impl Drop for EnvSnapshot {
+    fn drop(&mut self) {
         for (name, value) in env::vars_os() {
-            let old_value = old_vars.remove(&name);
+            let old_value = self.vars.remove(&name);
             if old_value != Some(value) {
                 match old_value {
                     None => env::remove_var(name),
@@ -149,13 +161,13 @@ impl EnvSnapshot {
                 }
             }
         }
-        for (name, old_value) in old_vars {
+        for (name, old_value) in self.vars.drain() {
             env::set_var(name, old_value)
         }
     }
 }
 
-#[cfg(all(feature = "sysroot-abi", test))]
+#[cfg(test)]
 mod tests;
 
 #[cfg(test)]
diff --git a/crates/proc-macro-srv/src/abis/abi_sysroot/mod.rs b/crates/proc-macro-srv/src/proc_macros.rs
index 0a3b8866a7f..3c6f3203319 100644
--- a/crates/proc-macro-srv/src/abis/abi_sysroot/mod.rs
+++ b/crates/proc-macro-srv/src/proc_macros.rs
@@ -1,45 +1,55 @@
 //! Proc macro ABI
 
-extern crate proc_macro;
-
-#[allow(dead_code)]
-#[doc(hidden)]
-mod ra_server;
-
 use libloading::Library;
-use proc_macro_api::ProcMacroKind;
+use proc_macro_api::{ProcMacroKind, RustCInfo};
 
-use super::{tt, PanicMessage};
+use crate::{dylib::LoadProcMacroDylibError, server::SYMBOL_INTERNER, tt};
 
-pub use ra_server::TokenStream;
-
-pub(crate) struct Abi {
+pub(crate) struct ProcMacros {
     exported_macros: Vec<proc_macro::bridge::client::ProcMacro>,
 }
 
-impl From<proc_macro::bridge::PanicMessage> for PanicMessage {
+impl From<proc_macro::bridge::PanicMessage> for crate::PanicMessage {
     fn from(p: proc_macro::bridge::PanicMessage) -> Self {
         Self { message: p.as_str().map(|s| s.to_string()) }
     }
 }
 
-impl Abi {
-    pub unsafe fn from_lib(lib: &Library, symbol_name: String) -> Result<Abi, libloading::Error> {
-        let macros: libloading::Symbol<'_, &&[proc_macro::bridge::client::ProcMacro]> =
-            lib.get(symbol_name.as_bytes())?;
-        Ok(Self { exported_macros: macros.to_vec() })
+impl ProcMacros {
+    /// Load a new ABI.
+    ///
+    /// # Arguments
+    ///
+    /// *`lib` - The dynamic library containing the macro implementations
+    /// *`symbol_name` - The symbol name the macros can be found attributes
+    /// *`info` - RustCInfo about the compiler that was used to compile the
+    ///           macro crate. This is the information we use to figure out
+    ///           which ABI to return
+    pub(crate) fn from_lib(
+        lib: &Library,
+        symbol_name: String,
+        info: RustCInfo,
+    ) -> Result<ProcMacros, LoadProcMacroDylibError> {
+        if info.version_string == crate::RUSTC_VERSION_STRING {
+            let macros = unsafe {
+                lib.get::<&&[proc_macro::bridge::client::ProcMacro]>(symbol_name.as_bytes())
+            }?;
+
+            return Ok(Self { exported_macros: macros.to_vec() });
+        }
+        Err(LoadProcMacroDylibError::AbiMismatch(info.version_string))
     }
 
-    pub fn expand(
+    pub(crate) fn expand(
         &self,
         macro_name: &str,
         macro_body: &tt::Subtree,
         attributes: Option<&tt::Subtree>,
-    ) -> Result<tt::Subtree, PanicMessage> {
-        let parsed_body = ra_server::TokenStream::with_subtree(macro_body.clone());
+    ) -> Result<tt::Subtree, crate::PanicMessage> {
+        let parsed_body = crate::server::TokenStream::with_subtree(macro_body.clone());
 
-        let parsed_attributes = attributes.map_or(ra_server::TokenStream::new(), |attr| {
-            ra_server::TokenStream::with_subtree(attr.clone())
+        let parsed_attributes = attributes.map_or(crate::server::TokenStream::new(), |attr| {
+            crate::server::TokenStream::with_subtree(attr.clone())
         });
 
         for proc_macro in &self.exported_macros {
@@ -49,34 +59,34 @@ impl Abi {
                 } if *trait_name == macro_name => {
                     let res = client.run(
                         &proc_macro::bridge::server::SameThread,
-                        ra_server::RustAnalyzer::default(),
+                        crate::server::RustAnalyzer { interner: &SYMBOL_INTERNER },
                         parsed_body,
                         true,
                     );
-                    return res.map(|it| it.into_subtree()).map_err(PanicMessage::from);
+                    return res.map(|it| it.into_subtree()).map_err(crate::PanicMessage::from);
                 }
                 proc_macro::bridge::client::ProcMacro::Bang { name, client }
                     if *name == macro_name =>
                 {
                     let res = client.run(
                         &proc_macro::bridge::server::SameThread,
-                        ra_server::RustAnalyzer::default(),
+                        crate::server::RustAnalyzer { interner: &SYMBOL_INTERNER },
                         parsed_body,
                         true,
                     );
-                    return res.map(|it| it.into_subtree()).map_err(PanicMessage::from);
+                    return res.map(|it| it.into_subtree()).map_err(crate::PanicMessage::from);
                 }
                 proc_macro::bridge::client::ProcMacro::Attr { name, client }
                     if *name == macro_name =>
                 {
                     let res = client.run(
                         &proc_macro::bridge::server::SameThread,
-                        ra_server::RustAnalyzer::default(),
+                        crate::server::RustAnalyzer { interner: &SYMBOL_INTERNER },
                         parsed_attributes,
                         parsed_body,
                         true,
                     );
-                    return res.map(|it| it.into_subtree()).map_err(PanicMessage::from);
+                    return res.map(|it| it.into_subtree()).map_err(crate::PanicMessage::from);
                 }
                 _ => continue,
             }
@@ -85,7 +95,7 @@ impl Abi {
         Err(proc_macro::bridge::PanicMessage::String("Nothing to expand".to_string()).into())
     }
 
-    pub fn list_macros(&self) -> Vec<(String, ProcMacroKind)> {
+    pub(crate) fn list_macros(&self) -> Vec<(String, ProcMacroKind)> {
         self.exported_macros
             .iter()
             .map(|proc_macro| match proc_macro {
@@ -102,3 +112,16 @@ impl Abi {
             .collect()
     }
 }
+
+#[test]
+fn test_version_check() {
+    let path = paths::AbsPathBuf::assert(crate::proc_macro_test_dylib_path());
+    let info = proc_macro_api::read_dylib_info(&path).unwrap();
+    assert_eq!(
+        info.version_string,
+        crate::RUSTC_VERSION_STRING,
+        "sysroot ABI mismatch: dylib rustc version (read from .rustc section): {:?} != proc-macro-srv version (read from 'rustc --version'): {:?}",
+        info.version_string,
+        crate::RUSTC_VERSION_STRING,
+    );
+}
diff --git a/crates/proc-macro-srv/src/abis/abi_sysroot/ra_server.rs b/crates/proc-macro-srv/src/server.rs
index d258a024729..5b659ab8498 100644
--- a/crates/proc-macro-srv/src/abis/abi_sysroot/ra_server.rs
+++ b/crates/proc-macro-srv/src/server.rs
@@ -8,9 +8,9 @@
 //!
 //! FIXME: No span and source file information is implemented yet
 
-use super::proc_macro::{
-    self,
+use proc_macro::{
     bridge::{self, server},
+    LineColumn,
 };
 
 mod token_stream;
@@ -26,8 +26,10 @@ use crate::tt;
 
 type Group = tt::Subtree;
 type TokenTree = tt::TokenTree;
+#[allow(unused)]
 type Punct = tt::Punct;
 type Spacing = tt::Spacing;
+#[allow(unused)]
 type Literal = tt::Literal;
 type Span = tt::TokenId;
 
@@ -36,14 +38,11 @@ pub struct SourceFile {
     // FIXME stub
 }
 
-type Level = super::proc_macro::Level;
-type LineColumn = super::proc_macro::LineColumn;
-
 pub struct FreeFunctions;
 
-#[derive(Default)]
 pub struct RustAnalyzer {
     // FIXME: store span information here.
+    pub(crate) interner: SymbolInternerRef,
 }
 
 impl server::Types for RustAnalyzer {
@@ -68,7 +67,7 @@ impl server::FreeFunctions for RustAnalyzer {
         // FIXME: keep track of LitKind and Suffix
         Ok(bridge::Literal {
             kind: bridge::LitKind::Err,
-            symbol: Symbol::intern(s),
+            symbol: Symbol::intern(self.interner, s),
             suffix: None,
             span: tt::TokenId::unspecified(),
         })
@@ -109,7 +108,7 @@ impl server::TokenStream for RustAnalyzer {
             }
 
             bridge::TokenTree::Ident(ident) => {
-                let text = ident.sym.text();
+                let text = ident.sym.text(self.interner);
                 let text =
                     if ident.is_raw { ::tt::SmolStr::from_iter(["r#", &text]) } else { text };
                 let ident: tt::Ident = tt::Ident { text, span: ident.span };
@@ -120,8 +119,9 @@ impl server::TokenStream for RustAnalyzer {
 
             bridge::TokenTree::Literal(literal) => {
                 let literal = LiteralFormatter(literal);
-                let text = literal
-                    .with_stringify_parts(|parts| ::tt::SmolStr::from_iter(parts.iter().copied()));
+                let text = literal.with_stringify_parts(self.interner, |parts| {
+                    ::tt::SmolStr::from_iter(parts.iter().copied())
+                });
 
                 let literal = tt::Literal { text, span: literal.0.span };
                 let leaf = tt::Leaf::from(literal);
@@ -185,7 +185,7 @@ impl server::TokenStream for RustAnalyzer {
             .map(|tree| match tree {
                 tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) => {
                     bridge::TokenTree::Ident(bridge::Ident {
-                        sym: Symbol::intern(ident.text.trim_start_matches("r#")),
+                        sym: Symbol::intern(self.interner, ident.text.trim_start_matches("r#")),
                         is_raw: ident.text.starts_with("r#"),
                         span: ident.span,
                     })
@@ -194,7 +194,7 @@ impl server::TokenStream for RustAnalyzer {
                     bridge::TokenTree::Literal(bridge::Literal {
                         // FIXME: handle literal kinds
                         kind: bridge::LitKind::Err,
-                        symbol: Symbol::intern(&lit.text),
+                        symbol: Symbol::intern(self.interner, &lit.text),
                         // FIXME: handle suffixes
                         suffix: None,
                         span: lit.span,
@@ -240,6 +240,7 @@ fn delim_to_external(d: tt::Delimiter) -> proc_macro::Delimiter {
     }
 }
 
+#[allow(unused)]
 fn spacing_to_internal(spacing: proc_macro::Spacing) -> Spacing {
     match spacing {
         proc_macro::Spacing::Alone => Spacing::Alone,
@@ -247,6 +248,7 @@ fn spacing_to_internal(spacing: proc_macro::Spacing) -> Spacing {
     }
 }
 
+#[allow(unused)]
 fn spacing_to_external(spacing: Spacing) -> proc_macro::Spacing {
     match spacing {
         Spacing::Alone => proc_macro::Spacing::Alone,
@@ -350,11 +352,13 @@ impl server::Server for RustAnalyzer {
     }
 
     fn intern_symbol(ident: &str) -> Self::Symbol {
-        Symbol::intern(&::tt::SmolStr::from(ident))
+        // FIXME: should be self.interner once the proc-macro api allows is
+        Symbol::intern(&SYMBOL_INTERNER, &::tt::SmolStr::from(ident))
     }
 
     fn with_symbol_string(symbol: &Self::Symbol, f: impl FnOnce(&str)) {
-        f(symbol.text().as_str())
+        // FIXME: should be self.interner once the proc-macro api allows is
+        f(symbol.text(&SYMBOL_INTERNER).as_str())
     }
 }
 
@@ -365,7 +369,11 @@ impl LiteralFormatter {
     /// literal's representation. This is done to allow the `ToString` and
     /// `Display` implementations to borrow references to symbol values, and
     /// both be optimized to reduce overhead.
-    fn with_stringify_parts<R>(&self, f: impl FnOnce(&[&str]) -> R) -> R {
+    fn with_stringify_parts<R>(
+        &self,
+        interner: SymbolInternerRef,
+        f: impl FnOnce(&[&str]) -> R,
+    ) -> R {
         /// Returns a string containing exactly `num` '#' characters.
         /// Uses a 256-character source string literal which is always safe to
         /// index with a `u8` index.
@@ -380,7 +388,7 @@ impl LiteralFormatter {
             &HASHES[..num as usize]
         }
 
-        self.with_symbol_and_suffix(|symbol, suffix| match self.0.kind {
+        self.with_symbol_and_suffix(interner, |symbol, suffix| match self.0.kind {
             bridge::LitKind::Byte => f(&["b'", symbol, "'", suffix]),
             bridge::LitKind::Char => f(&["'", symbol, "'", suffix]),
             bridge::LitKind::Str => f(&["\"", symbol, "\"", suffix]),
@@ -397,9 +405,13 @@ impl LiteralFormatter {
         })
     }
 
-    fn with_symbol_and_suffix<R>(&self, f: impl FnOnce(&str, &str) -> R) -> R {
-        let symbol = self.0.symbol.text();
-        let suffix = self.0.suffix.map(|s| s.text()).unwrap_or_default();
+    fn with_symbol_and_suffix<R>(
+        &self,
+        interner: SymbolInternerRef,
+        f: impl FnOnce(&str, &str) -> R,
+    ) -> R {
+        let symbol = self.0.symbol.text(interner);
+        let suffix = self.0.suffix.map(|s| s.text(interner)).unwrap_or_default();
         f(symbol.as_str(), suffix.as_str())
     }
 }
diff --git a/crates/proc-macro-srv/src/abis/abi_sysroot/ra_server/symbol.rs b/crates/proc-macro-srv/src/server/symbol.rs
index 51dfba2ea9f..540d06457f2 100644
--- a/crates/proc-macro-srv/src/abis/abi_sysroot/ra_server/symbol.rs
+++ b/crates/proc-macro-srv/src/server/symbol.rs
@@ -1,28 +1,30 @@
 //! Symbol interner for proc-macro-srv
 
-use std::{cell::RefCell, collections::HashMap};
+use std::{cell::RefCell, collections::HashMap, thread::LocalKey};
 use tt::SmolStr;
 
 thread_local! {
-    static SYMBOL_INTERNER: RefCell<SymbolInterner> = Default::default();
+    pub(crate) static SYMBOL_INTERNER: RefCell<SymbolInterner> = Default::default();
 }
 
 // ID for an interned symbol.
 #[derive(Hash, Eq, PartialEq, Copy, Clone)]
 pub struct Symbol(u32);
 
+pub(crate) type SymbolInternerRef = &'static LocalKey<RefCell<SymbolInterner>>;
+
 impl Symbol {
-    pub fn intern(data: &str) -> Symbol {
-        SYMBOL_INTERNER.with(|i| i.borrow_mut().intern(data))
+    pub(super) fn intern(interner: SymbolInternerRef, data: &str) -> Symbol {
+        interner.with(|i| i.borrow_mut().intern(data))
     }
 
-    pub fn text(&self) -> SmolStr {
-        SYMBOL_INTERNER.with(|i| i.borrow().get(self).clone())
+    pub(super) fn text(&self, interner: SymbolInternerRef) -> SmolStr {
+        interner.with(|i| i.borrow().get(self).clone())
     }
 }
 
 #[derive(Default)]
-struct SymbolInterner {
+pub(crate) struct SymbolInterner {
     idents: HashMap<SmolStr, u32>,
     ident_data: Vec<SmolStr>,
 }
diff --git a/crates/proc-macro-srv/src/abis/abi_sysroot/ra_server/token_stream.rs b/crates/proc-macro-srv/src/server/token_stream.rs
index d091d431900..2589d8b64d4 100644
--- a/crates/proc-macro-srv/src/abis/abi_sysroot/ra_server/token_stream.rs
+++ b/crates/proc-macro-srv/src/server/token_stream.rs
@@ -4,15 +4,15 @@ use crate::tt::{self, TokenTree};
 
 #[derive(Debug, Default, Clone)]
 pub struct TokenStream {
-    pub token_trees: Vec<TokenTree>,
+    pub(super) token_trees: Vec<TokenTree>,
 }
 
 impl TokenStream {
-    pub fn new() -> Self {
+    pub(crate) fn new() -> Self {
         TokenStream::default()
     }
 
-    pub fn with_subtree(subtree: tt::Subtree) -> Self {
+    pub(crate) fn with_subtree(subtree: tt::Subtree) -> Self {
         if subtree.delimiter.kind != tt::DelimiterKind::Invisible {
             TokenStream { token_trees: vec![TokenTree::Subtree(subtree)] }
         } else {
@@ -20,11 +20,11 @@ impl TokenStream {
         }
     }
 
-    pub fn into_subtree(self) -> tt::Subtree {
+    pub(crate) fn into_subtree(self) -> tt::Subtree {
         tt::Subtree { delimiter: tt::Delimiter::UNSPECIFIED, token_trees: self.token_trees }
     }
 
-    pub fn is_empty(&self) -> bool {
+    pub(super) fn is_empty(&self) -> bool {
         self.token_trees.is_empty()
     }
 }
@@ -78,12 +78,12 @@ impl Extend<TokenStream> for TokenStream {
     }
 }
 
-pub struct TokenStreamBuilder {
+pub(super) struct TokenStreamBuilder {
     acc: TokenStream,
 }
 
-/// Public implementation details for the `TokenStream` type, such as iterators.
-pub mod token_stream {
+/// pub(super)lic implementation details for the `TokenStream` type, such as iterators.
+pub(super) mod token_stream {
     use std::str::FromStr;
 
     use super::{tt, TokenStream, TokenTree};
diff --git a/crates/proc-macro-srv/src/tests/utils.rs b/crates/proc-macro-srv/src/tests/utils.rs
index efbeb90ca9d..49b4d973b63 100644
--- a/crates/proc-macro-srv/src/tests/utils.rs
+++ b/crates/proc-macro-srv/src/tests/utils.rs
@@ -5,14 +5,14 @@ use std::str::FromStr;
 
 use crate::{dylib, proc_macro_test_dylib_path, ProcMacroSrv};
 
-fn parse_string(code: &str) -> Option<crate::abis::TestTokenStream> {
+fn parse_string(code: &str) -> Option<crate::server::TokenStream> {
     // This is a bit strange. We need to parse a string into a token stream into
     // order to create a tt::SubTree from it in fixtures. `into_subtree` is
     // implemented by all the ABIs we have so we arbitrarily choose one ABI to
     // write a `parse_string` function for and use that. The tests don't really
     // care which ABI we're using as the `into_subtree` function isn't part of
     // the ABI and shouldn't change between ABI versions.
-    crate::abis::TestTokenStream::from_str(code).ok()
+    crate::server::TokenStream::from_str(code).ok()
 }
 
 pub fn assert_expand(macro_name: &str, ra_fixture: &str, expect: Expect) {
diff --git a/crates/project-model/src/build_scripts.rs b/crates/project-model/src/build_scripts.rs
index 4e5d640f175..fe4cbfc8867 100644
--- a/crates/project-model/src/build_scripts.rs
+++ b/crates/project-model/src/build_scripts.rs
@@ -415,7 +415,6 @@ impl WorkspaceBuildScripts {
                     let dir_entry = entry.ok()?;
                     if dir_entry.file_type().ok()?.is_file() {
                         let path = dir_entry.path();
-                        tracing::info!("p{:?}", path);
                         let extension = path.extension()?;
                         if extension == std::env::consts::DLL_EXTENSION {
                             let name = path.file_stem()?.to_str()?.split_once('-')?.0.to_owned();
diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml
index 1a4155d3bbb..3f2cfed806f 100644
--- a/crates/rust-analyzer/Cargo.toml
+++ b/crates/rust-analyzer/Cargo.toml
@@ -67,7 +67,7 @@ ide-db.workspace = true
 ide-ssr.workspace = true
 ide.workspace = true
 proc-macro-api.workspace = true
-proc-macro-srv.workspace = true
+proc-macro-srv-cli.workspace = true
 profile.workspace = true
 project-model.workspace = true
 stdx.workspace = true
@@ -95,8 +95,9 @@ mbe.workspace = true
 [features]
 jemalloc = ["jemallocator", "profile/jemalloc"]
 force-always-assert = ["always-assert/force"]
+sysroot-abi = ["proc-macro-srv-cli/sysroot-abi"]
 in-rust-tree = [
-    "proc-macro-srv/sysroot-abi",
+    "sysroot-abi",
     "ide/in-rust-tree",
     "syntax/in-rust-tree",
 ]
diff --git a/crates/rust-analyzer/src/bin/main.rs b/crates/rust-analyzer/src/bin/main.rs
index 4de022b6ed6..3506bed2c74 100644
--- a/crates/rust-analyzer/src/bin/main.rs
+++ b/crates/rust-analyzer/src/bin/main.rs
@@ -77,7 +77,7 @@ fn try_main(flags: flags::RustAnalyzer) -> Result<()> {
             with_extra_thread("LspServer", run_server)?;
         }
         flags::RustAnalyzerCmd::ProcMacro(flags::ProcMacro) => {
-            with_extra_thread("MacroExpander", || proc_macro_srv::cli::run().map_err(Into::into))?;
+            with_extra_thread("MacroExpander", || proc_macro_srv_cli::run().map_err(Into::into))?;
         }
         flags::RustAnalyzerCmd::Parse(cmd) => cmd.run()?,
         flags::RustAnalyzerCmd::Symbols(cmd) => cmd.run()?,
diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs
index e1504743bfd..bd477775717 100644
--- a/crates/rust-analyzer/src/cli/analysis_stats.rs
+++ b/crates/rust-analyzer/src/cli/analysis_stats.rs
@@ -13,7 +13,7 @@ use hir::{
 };
 use hir_def::{
     body::{BodySourceMap, SyntheticSyntax},
-    expr::{ExprId, PatId},
+    hir::{ExprId, PatId},
     FunctionId,
 };
 use hir_ty::{Interner, TyExt, TypeFlags};
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index 06fb95515ae..27445d1f712 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -338,6 +338,8 @@ config_data! {
         inlayHints_closingBraceHints_minLines: usize               = "25",
         /// Whether to show inlay type hints for return types of closures.
         inlayHints_closureReturnTypeHints_enable: ClosureReturnTypeHintsDef  = "\"never\"",
+        /// Closure notation in type and chaining inaly hints.
+        inlayHints_closureStyle: ClosureStyle                                = "\"impl_fn\"",
         /// Whether to show enum variant discriminant hints.
         inlayHints_discriminantHints_enable: DiscriminantHintsDef            = "\"never\"",
         /// Whether to show inlay hints for type adjustments.
@@ -1301,6 +1303,12 @@ impl Config {
             hide_closure_initialization_hints: self
                 .data
                 .inlayHints_typeHints_hideClosureInitialization,
+            closure_style: match self.data.inlayHints_closureStyle {
+                ClosureStyle::ImplFn => hir::ClosureStyle::ImplFn,
+                ClosureStyle::RustAnalyzer => hir::ClosureStyle::RANotation,
+                ClosureStyle::WithId => hir::ClosureStyle::ClosureWithId,
+                ClosureStyle::Hide => hir::ClosureStyle::Hide,
+            },
             adjustment_hints: match self.data.inlayHints_expressionAdjustmentHints_enable {
                 AdjustmentHintsDef::Always => ide::AdjustmentHints::Always,
                 AdjustmentHintsDef::Never => match self.data.inlayHints_reborrowHints_enable {
@@ -1808,6 +1816,15 @@ enum ClosureReturnTypeHintsDef {
 }
 
 #[derive(Deserialize, Debug, Clone)]
+#[serde(rename_all = "snake_case")]
+enum ClosureStyle {
+    ImplFn,
+    RustAnalyzer,
+    WithId,
+    Hide,
+}
+
+#[derive(Deserialize, Debug, Clone)]
 #[serde(untagged)]
 enum ReborrowHintsDef {
     #[serde(deserialize_with = "true_or_always")]
@@ -2288,6 +2305,16 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json
                 },
             ],
         },
+        "ClosureStyle" => set! {
+            "type": "string",
+            "enum": ["impl_fn", "rust_analyzer", "with_id", "hide"],
+            "enumDescriptions": [
+                "`impl_fn`: `impl FnMut(i32, u64) -> i8`",
+                "`rust_analyzer`: `|i32, u64| -> i8`",
+                "`with_id`: `{closure#14352}`, where that id is the unique number of the closure in r-a internals",
+                "`hide`: Shows `...` for every closure type",
+            ],
+        },
         _ => panic!("missing entry for {ty}: {default}"),
     }
 
diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs
index 899c31d2063..ddf130e08dd 100644
--- a/crates/rust-analyzer/src/reload.rs
+++ b/crates/rust-analyzer/src/reload.rs
@@ -114,6 +114,10 @@ impl GlobalState {
             status.health = lsp_ext::Health::Warning;
             message.push_str("Failed to run build scripts of some packages.\n\n");
         }
+        if self.proc_macro_clients.iter().any(|it| it.is_err()) {
+            status.health = lsp_ext::Health::Warning;
+            message.push_str("Failed to spawn one or more proc-macro servers.\n\n");
+        }
         if !self.config.cargo_autoreload()
             && self.is_quiescent()
             && self.fetch_workspaces_queue.op_requested()
@@ -384,18 +388,23 @@ impl GlobalState {
                     .workspaces
                     .iter()
                     .map(|ws| {
-                        let (path, args): (_, &[_]) = if path_manually_set {
+                        let path = if path_manually_set {
                             tracing::debug!(
                                 "Pro-macro server path explicitly set: {}",
                                 path.display()
                             );
-                            (path.clone(), &[])
+                            path.clone()
                         } else {
                             match ws.find_sysroot_proc_macro_srv() {
-                                Some(server_path) => (server_path, &[]),
-                                None => (path.clone(), &["proc-macro"]),
+                                Some(server_path) => server_path,
+                                None => path.clone(),
                             }
                         };
+                        let args: &[_] = if path.file_stem() == Some("rust-analyzer".as_ref()) {
+                            &["proc-macro"]
+                        } else {
+                            &[]
+                        };
 
                         tracing::info!(?args, "Using proc-macro server at {}", path.display(),);
                         ProcMacroServer::spawn(path.clone(), args).map_err(|err| {
@@ -447,7 +456,7 @@ impl GlobalState {
             let mut proc_macros = Vec::default();
             for ws in &**self.workspaces {
                 let (other, mut crate_proc_macros) =
-                    ws.to_crate_graph(&mut load, &self.config.cargo().extra_env);
+                    ws.to_crate_graph(&mut load, &self.config.extra_env());
                 crate_graph.extend(other, &mut crate_proc_macros);
                 proc_macros.push(crate_proc_macros);
             }
diff --git a/crates/rust-analyzer/tests/slow-tests/main.rs b/crates/rust-analyzer/tests/slow-tests/main.rs
index 587d640969a..54a91df23e5 100644
--- a/crates/rust-analyzer/tests/slow-tests/main.rs
+++ b/crates/rust-analyzer/tests/slow-tests/main.rs
@@ -59,7 +59,7 @@ use std::collections::Spam;
 "#,
     )
     .with_config(serde_json::json!({
-        "cargo": { "sysroot": "discover" }
+        "cargo": { "sysroot": "discover" },
     }))
     .server()
     .wait_until_workspace_is_loaded();
@@ -508,7 +508,7 @@ fn main() {}
 #[test]
 fn test_missing_module_code_action_in_json_project() {
     if skip_slow_tests() {
-        // return;
+        return;
     }
 
     let tmp_dir = TestDir::new();
@@ -612,7 +612,7 @@ fn main() {{}}
 "#
     ))
     .with_config(serde_json::json!({
-        "cargo": { "sysroot": "discover" }
+        "cargo": { "sysroot": "discover" },
     }))
     .server()
     .wait_until_workspace_is_loaded();
@@ -685,7 +685,7 @@ version = \"0.0.0\"
 #[test]
 fn out_dirs_check() {
     if skip_slow_tests() {
-        // return;
+        return;
     }
 
     let server = Project::with_fixture(
@@ -711,10 +711,21 @@ fn main() {
     println!("cargo:rerun-if-changed=build.rs");
 }
 //- /src/main.rs
-#[rustc_builtin_macro] macro_rules! include {}
-#[rustc_builtin_macro] macro_rules! include_str {}
-#[rustc_builtin_macro] macro_rules! concat {}
-#[rustc_builtin_macro] macro_rules! env {}
+#![allow(warnings)]
+#![feature(rustc_attrs)]
+#[rustc_builtin_macro] macro_rules! include {
+    ($file:expr $(,)?) => {{ /* compiler built-in */ }};
+}
+#[rustc_builtin_macro] macro_rules! include_str {
+    ($file:expr $(,)?) => {{ /* compiler built-in */ }};
+}
+#[rustc_builtin_macro] macro_rules! concat {
+    ($($e:ident),+ $(,)?) => {{ /* compiler built-in */ }};
+}
+#[rustc_builtin_macro] macro_rules! env {
+    ($name:expr $(,)?) => {{ /* compiler built-in */ }};
+    ($name:expr, $error_msg:expr $(,)?) => {{ /* compiler built-in */ }};
+}
 
 include!(concat!(env!("OUT_DIR"), "/hello.rs"));
 
@@ -749,7 +760,7 @@ fn main() {
     let res = server.send_request::<HoverRequest>(HoverParams {
         text_document_position_params: TextDocumentPositionParams::new(
             server.doc_id("src/main.rs"),
-            Position::new(19, 10),
+            Position::new(30, 10),
         ),
         work_done_progress_params: Default::default(),
     });
@@ -758,7 +769,7 @@ fn main() {
     let res = server.send_request::<HoverRequest>(HoverParams {
         text_document_position_params: TextDocumentPositionParams::new(
             server.doc_id("src/main.rs"),
-            Position::new(20, 10),
+            Position::new(31, 10),
         ),
         work_done_progress_params: Default::default(),
     });
@@ -768,23 +779,23 @@ fn main() {
         GotoDefinitionParams {
             text_document_position_params: TextDocumentPositionParams::new(
                 server.doc_id("src/main.rs"),
-                Position::new(17, 9),
+                Position::new(28, 9),
             ),
             work_done_progress_params: Default::default(),
             partial_result_params: Default::default(),
         },
         json!([{
             "originSelectionRange": {
-                "end": { "character": 10, "line": 17 },
-                "start": { "character": 8, "line": 17 }
+                "end": { "character": 10, "line": 28 },
+                "start": { "character": 8, "line": 28 }
             },
             "targetRange": {
-                "end": { "character": 9, "line": 8 },
-                "start": { "character": 0, "line": 7 }
+                "end": { "character": 9, "line": 19 },
+                "start": { "character": 0, "line": 18 }
             },
             "targetSelectionRange": {
-                "end": { "character": 8, "line": 8 },
-                "start": { "character": 7, "line": 8 }
+                "end": { "character": 8, "line": 19 },
+                "start": { "character": 7, "line": 19 }
             },
             "targetUri": "file:///[..]src/main.rs"
         }]),
@@ -794,23 +805,23 @@ fn main() {
         GotoDefinitionParams {
             text_document_position_params: TextDocumentPositionParams::new(
                 server.doc_id("src/main.rs"),
-                Position::new(18, 9),
+                Position::new(29, 9),
             ),
             work_done_progress_params: Default::default(),
             partial_result_params: Default::default(),
         },
         json!([{
             "originSelectionRange": {
-                "end": { "character": 10, "line": 18 },
-                "start": { "character": 8, "line": 18 }
+                "end": { "character": 10, "line": 29 },
+                "start": { "character": 8, "line": 29 }
             },
             "targetRange": {
-                "end": { "character": 9, "line": 12 },
-                "start": { "character": 0, "line":11 }
+                "end": { "character": 9, "line": 23 },
+                "start": { "character": 0, "line": 22 }
             },
             "targetSelectionRange": {
-                "end": { "character": 8, "line": 12 },
-                "start": { "character": 7, "line": 12 }
+                "end": { "character": 8, "line": 23 },
+                "start": { "character": 7, "line": 23 }
             },
             "targetUri": "file:///[..]src/main.rs"
         }]),
@@ -818,8 +829,7 @@ fn main() {
 }
 
 #[test]
-// FIXME: Re-enable once we can run proc-macro tests on rust-lang/rust-analyzer again
-#[cfg(any())]
+#[cfg(feature = "sysroot-abi")]
 fn resolve_proc_macro() {
     use expect_test::expect;
     if skip_slow_tests() {
@@ -837,6 +847,7 @@ edition = "2021"
 bar = {path = "../bar"}
 
 //- /foo/src/main.rs
+#![feature(rustc_attrs, decl_macro)]
 use bar::Bar;
 
 #[rustc_builtin_macro]
@@ -913,7 +924,7 @@ pub fn foo(_input: TokenStream) -> TokenStream {
     let res = server.send_request::<HoverRequest>(HoverParams {
         text_document_position_params: TextDocumentPositionParams::new(
             server.doc_id("foo/src/main.rs"),
-            Position::new(10, 9),
+            Position::new(11, 9),
         ),
         work_done_progress_params: Default::default(),
     });
@@ -1083,10 +1094,18 @@ version = "0.0.0"
 
 //- /bar/src/lib.rs
 pub fn bar() {}
+
+//- /baz/Cargo.toml
+[package]
+name = "baz"
+version = "0.0.0"
+
+//- /baz/src/lib.rs
 "#,
     )
     .root("foo")
     .root("bar")
+    .root("baz")
     .with_config(json!({
        "files": {
            "excludeDirs": ["foo", "bar"]
diff --git a/crates/rust-analyzer/tests/slow-tests/support.rs b/crates/rust-analyzer/tests/slow-tests/support.rs
index 037fc89ace0..df62dcd7ddc 100644
--- a/crates/rust-analyzer/tests/slow-tests/support.rs
+++ b/crates/rust-analyzer/tests/slow-tests/support.rs
@@ -13,7 +13,7 @@ use project_model::ProjectManifest;
 use rust_analyzer::{config::Config, lsp_ext, main_loop};
 use serde::Serialize;
 use serde_json::{json, to_string_pretty, Value};
-use test_utils::Fixture;
+use test_utils::FixtureWithProjectMeta;
 use vfs::AbsPathBuf;
 
 use crate::testdir::TestDir;
@@ -37,8 +37,12 @@ impl<'a> Project<'a> {
                     "sysroot": null,
                     // Can't use test binary as rustc wrapper.
                     "buildScripts": {
-                        "useRustcWrapper": false
+                        "useRustcWrapper": false,
+                        "enable": false,
                     },
+                },
+                "procMacro": {
+                    "enable": false,
                 }
             }),
         }
@@ -80,10 +84,12 @@ impl<'a> Project<'a> {
             profile::init_from(crate::PROFILE);
         });
 
-        let (mini_core, proc_macros, fixtures) = Fixture::parse(self.fixture);
-        assert!(proc_macros.is_empty());
+        let FixtureWithProjectMeta { fixture, mini_core, proc_macro_names, toolchain } =
+            FixtureWithProjectMeta::parse(self.fixture);
+        assert!(proc_macro_names.is_empty());
         assert!(mini_core.is_none());
-        for entry in fixtures {
+        assert!(toolchain.is_none());
+        for entry in fixture {
             let path = tmp_dir.path().join(&entry.path['/'.len_utf8()..]);
             fs::create_dir_all(path.parent().unwrap()).unwrap();
             fs::write(path.as_path(), entry.text.as_bytes()).unwrap();
@@ -251,6 +257,9 @@ impl Server {
                     .clone()
                     .extract::<lsp_ext::ServerStatusParams>("experimental/serverStatus")
                     .unwrap();
+                if status.health != lsp_ext::Health::Ok {
+                    panic!("server errored/warned while loading workspace: {:?}", status.message);
+                }
                 status.quiescent
             }
             _ => false,
diff --git a/crates/sourcegen/src/lib.rs b/crates/sourcegen/src/lib.rs
index 72d26635c33..c5da6ceb4d1 100644
--- a/crates/sourcegen/src/lib.rs
+++ b/crates/sourcegen/src/lib.rs
@@ -58,21 +58,19 @@ impl CommentBlock {
         assert!(tag.starts_with(char::is_uppercase));
 
         let tag = format!("{tag}:");
-        // Would be nice if we had `.retain_mut` here!
-        CommentBlock::extract_untagged(text)
-            .into_iter()
-            .filter_map(|mut block| {
-                let first = block.contents.remove(0);
-                first.strip_prefix(&tag).map(|id| {
-                    if block.is_doc {
-                        panic!("Use plain (non-doc) comments with tags like {tag}:\n    {first}");
-                    }
+        let mut blocks = CommentBlock::extract_untagged(text);
+        blocks.retain_mut(|block| {
+            let first = block.contents.remove(0);
+            let Some(id) = first.strip_prefix(&tag) else { return false; };
+
+            if block.is_doc {
+                panic!("Use plain (non-doc) comments with tags like {tag}:\n    {first}");
+            }
 
-                    block.id = id.trim().to_string();
-                    block
-                })
-            })
-            .collect()
+            block.id = id.trim().to_string();
+            true
+        });
+        blocks
     }
 
     pub fn extract_untagged(text: &str) -> Vec<CommentBlock> {
diff --git a/crates/syntax/Cargo.toml b/crates/syntax/Cargo.toml
index 7200df0a573..9109507f75e 100644
--- a/crates/syntax/Cargo.toml
+++ b/crates/syntax/Cargo.toml
@@ -21,7 +21,7 @@ rustc_lexer = { version = "727.0.0", package = "rustc-ap-rustc_lexer" }
 rustc-hash = "1.1.0"
 once_cell = "1.17.0"
 indexmap = "1.9.1"
-smol_str = "0.1.23"
+smol_str.workspace = true
 
 parser.workspace = true
 profile.workspace = true
diff --git a/crates/syntax/src/token_text.rs b/crates/syntax/src/token_text.rs
index 913b24d42bc..09c080c0c23 100644
--- a/crates/syntax/src/token_text.rs
+++ b/crates/syntax/src/token_text.rs
@@ -3,6 +3,7 @@
 use std::{cmp::Ordering, fmt, ops};
 
 use rowan::GreenToken;
+use smol_str::SmolStr;
 
 pub struct TokenText<'a>(pub(crate) Repr<'a>);
 
@@ -47,6 +48,12 @@ impl From<TokenText<'_>> for String {
     }
 }
 
+impl From<TokenText<'_>> for SmolStr {
+    fn from(token_text: TokenText<'_>) -> Self {
+        SmolStr::new(token_text.as_str())
+    }
+}
+
 impl PartialEq<&'_ str> for TokenText<'_> {
     fn eq(&self, other: &&str) -> bool {
         self.as_str() == *other
diff --git a/crates/test-utils/src/fixture.rs b/crates/test-utils/src/fixture.rs
index cd1235fa6dc..dffc7fccdab 100644
--- a/crates/test-utils/src/fixture.rs
+++ b/crates/test-utils/src/fixture.rs
@@ -86,7 +86,14 @@ pub struct MiniCore {
     valid_flags: Vec<String>,
 }
 
-impl Fixture {
+pub struct FixtureWithProjectMeta {
+    pub fixture: Vec<Fixture>,
+    pub mini_core: Option<MiniCore>,
+    pub proc_macro_names: Vec<String>,
+    pub toolchain: Option<String>,
+}
+
+impl FixtureWithProjectMeta {
     /// Parses text which looks like this:
     ///
     ///  ```not_rust
@@ -96,37 +103,40 @@ impl Fixture {
     ///  //- other meta
     ///  ```
     ///
-    /// Fixture can also start with a proc_macros and minicore declaration(in that order):
+    /// Fixture can also start with a proc_macros and minicore declaration (in that order):
     ///
     /// ```
+    /// //- toolchain: nightly
     /// //- proc_macros: identity
     /// //- minicore: sized
     /// ```
     ///
     /// That will include predefined proc macros and a subset of `libcore` into the fixture, see
     /// `minicore.rs` for what's available.
-    pub fn parse(ra_fixture: &str) -> (Option<MiniCore>, Vec<String>, Vec<Fixture>) {
+    pub fn parse(ra_fixture: &str) -> Self {
         let fixture = trim_indent(ra_fixture);
         let mut fixture = fixture.as_str();
+        let mut toolchain = None;
         let mut mini_core = None;
         let mut res: Vec<Fixture> = Vec::new();
-        let mut test_proc_macros = vec![];
-
-        if fixture.starts_with("//- proc_macros:") {
-            let first_line = fixture.split_inclusive('\n').next().unwrap();
-            test_proc_macros = first_line
-                .strip_prefix("//- proc_macros:")
-                .unwrap()
-                .split(',')
-                .map(|it| it.trim().to_string())
-                .collect();
-            fixture = &fixture[first_line.len()..];
+        let mut proc_macro_names = vec![];
+
+        if let Some(meta) = fixture.strip_prefix("//- toolchain:") {
+            let (meta, remain) = meta.split_once('\n').unwrap();
+            toolchain = Some(meta.trim().to_string());
+            fixture = remain;
         }
 
-        if fixture.starts_with("//- minicore:") {
-            let first_line = fixture.split_inclusive('\n').next().unwrap();
-            mini_core = Some(MiniCore::parse(first_line));
-            fixture = &fixture[first_line.len()..];
+        if let Some(meta) = fixture.strip_prefix("//- proc_macros:") {
+            let (meta, remain) = meta.split_once('\n').unwrap();
+            proc_macro_names = meta.split(',').map(|it| it.trim().to_string()).collect();
+            fixture = remain;
+        }
+
+        if let Some(meta) = fixture.strip_prefix("//- minicore:") {
+            let (meta, remain) = meta.split_once('\n').unwrap();
+            mini_core = Some(MiniCore::parse(meta));
+            fixture = remain;
         }
 
         let default = if fixture.contains("//-") { None } else { Some("//- /main.rs") };
@@ -142,7 +152,7 @@ impl Fixture {
             }
 
             if line.starts_with("//-") {
-                let meta = Fixture::parse_meta_line(line);
+                let meta = Self::parse_meta_line(line);
                 res.push(meta);
             } else {
                 if line.starts_with("// ")
@@ -160,7 +170,7 @@ impl Fixture {
             }
         }
 
-        (mini_core, test_proc_macros, res)
+        Self { fixture: res, mini_core, proc_macro_names, toolchain }
     }
 
     //- /lib.rs crate:foo deps:bar,baz cfg:foo=a,bar=b env:OUTDIR=path/to,OTHER=foo
@@ -257,8 +267,7 @@ impl MiniCore {
     fn parse(line: &str) -> MiniCore {
         let mut res = MiniCore { activated_flags: Vec::new(), valid_flags: Vec::new() };
 
-        let line = line.strip_prefix("//- minicore:").unwrap().trim();
-        for entry in line.split(", ") {
+        for entry in line.trim().split(", ") {
             if res.has_flag(entry) {
                 panic!("duplicate minicore flag: {entry:?}");
             }
@@ -372,7 +381,7 @@ impl MiniCore {
 #[test]
 #[should_panic]
 fn parse_fixture_checks_further_indented_metadata() {
-    Fixture::parse(
+    FixtureWithProjectMeta::parse(
         r"
         //- /lib.rs
           mod bar;
@@ -386,15 +395,18 @@ fn parse_fixture_checks_further_indented_metadata() {
 
 #[test]
 fn parse_fixture_gets_full_meta() {
-    let (mini_core, proc_macros, parsed) = Fixture::parse(
-        r#"
+    let FixtureWithProjectMeta { fixture: parsed, mini_core, proc_macro_names, toolchain } =
+        FixtureWithProjectMeta::parse(
+            r#"
+//- toolchain: nightly
 //- proc_macros: identity
 //- minicore: coerce_unsized
 //- /lib.rs crate:foo deps:bar,baz cfg:foo=a,bar=b,atom env:OUTDIR=path/to,OTHER=foo
 mod m;
 "#,
-    );
-    assert_eq!(proc_macros, vec!["identity".to_string()]);
+        );
+    assert_eq!(toolchain, Some("nightly".to_string()));
+    assert_eq!(proc_macro_names, vec!["identity".to_string()]);
     assert_eq!(mini_core.unwrap().activated_flags, vec!["coerce_unsized".to_string()]);
     assert_eq!(1, parsed.len());
 
diff --git a/crates/test-utils/src/lib.rs b/crates/test-utils/src/lib.rs
index a7a52e08e75..5abadaad629 100644
--- a/crates/test-utils/src/lib.rs
+++ b/crates/test-utils/src/lib.rs
@@ -27,7 +27,7 @@ pub use rustc_hash::FxHashMap;
 
 pub use crate::{
     assert_linear::AssertLinear,
-    fixture::{Fixture, MiniCore},
+    fixture::{Fixture, FixtureWithProjectMeta, MiniCore},
 };
 
 pub const CURSOR_MARKER: &str = "$0";
diff --git a/crates/test-utils/src/minicore.rs b/crates/test-utils/src/minicore.rs
index 118b9ad631b..308dc5892e5 100644
--- a/crates/test-utils/src/minicore.rs
+++ b/crates/test-utils/src/minicore.rs
@@ -143,6 +143,12 @@ pub mod clone {
     pub trait Clone: Sized {
         fn clone(&self) -> Self;
     }
+
+    impl<T> Clone for &T {
+        fn clone(&self) -> Self {
+            *self
+        }
+    }
     // region:derive
     #[rustc_builtin_macro]
     pub macro Clone($item:item) {}
@@ -885,12 +891,19 @@ pub mod iter {
                     self
                 }
             }
-            pub struct IntoIter<T, const N: usize>([T; N]);
+            struct IndexRange {
+                start: usize,
+                end: usize,
+            }
+            pub struct IntoIter<T, const N: usize> {
+                data: [T; N],
+                range: IndexRange,
+            }
             impl<T, const N: usize> IntoIterator for [T; N] {
                 type Item = T;
                 type IntoIter = IntoIter<T, N>;
                 fn into_iter(self) -> I {
-                    IntoIter(self)
+                    IntoIter { data: self, range: IndexRange { start: 0, end: self.len() } }
                 }
             }
             impl<T, const N: usize> Iterator for IntoIter<T, N> {
diff --git a/crates/tt/Cargo.toml b/crates/tt/Cargo.toml
index b8469383183..a28ee5f1ca2 100644
--- a/crates/tt/Cargo.toml
+++ b/crates/tt/Cargo.toml
@@ -12,6 +12,6 @@ rust-version.workspace = true
 doctest = false
 
 [dependencies]
-smol_str = "0.1.23"
+smol_str.workspace = true
 
 stdx.workspace = true
diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc
index 12dfe394f1d..e92e6ae92cc 100644
--- a/docs/user/generated_config.adoc
+++ b/docs/user/generated_config.adoc
@@ -474,6 +474,11 @@ to always show them).
 --
 Whether to show inlay type hints for return types of closures.
 --
+[[rust-analyzer.inlayHints.closureStyle]]rust-analyzer.inlayHints.closureStyle (default: `"impl_fn"`)::
++
+--
+Closure notation in type and chaining inaly hints.
+--
 [[rust-analyzer.inlayHints.discriminantHints.enable]]rust-analyzer.inlayHints.discriminantHints.enable (default: `"never"`)::
 +
 --
diff --git a/editors/code/package.json b/editors/code/package.json
index 81fa97269a9..087fd1296b3 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -1028,6 +1028,23 @@
                         "Only show type hints for return types of closures with blocks."
                     ]
                 },
+                "rust-analyzer.inlayHints.closureStyle": {
+                    "markdownDescription": "Closure notation in type and chaining inaly hints.",
+                    "default": "impl_fn",
+                    "type": "string",
+                    "enum": [
+                        "impl_fn",
+                        "rust_analyzer",
+                        "with_id",
+                        "hide"
+                    ],
+                    "enumDescriptions": [
+                        "`impl_fn`: `impl FnMut(i32, u64) -> i8`",
+                        "`rust_analyzer`: `|i32, u64| -> i8`",
+                        "`with_id`: `{closure#14352}`, where that id is the unique number of the closure in r-a internals",
+                        "`hide`: Shows `...` for every closure type"
+                    ]
+                },
                 "rust-analyzer.inlayHints.discriminantHints.enable": {
                     "markdownDescription": "Whether to show enum variant discriminant hints.",
                     "default": "never",
diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts
index e76710cb247..b27d9f54943 100644
--- a/editors/code/src/client.ts
+++ b/editors/code/src/client.ts
@@ -125,7 +125,11 @@ export async function createClient(
                         typeof diag.code === "string" || typeof diag.code === "number"
                             ? diag.code
                             : diag.code?.value;
-                    if (value === "unlinked-file" && !unlinkedFiles.includes(uri)) {
+                    if (
+                        value === "unlinked-file" &&
+                        !unlinkedFiles.includes(uri) &&
+                        diag.message !== "file not included in module tree"
+                    ) {
                         const config = vscode.workspace.getConfiguration("rust-analyzer");
                         if (config.get("showUnlinkedFileNotification")) {
                             unlinkedFiles.push(uri);
diff --git a/editors/code/src/commands.ts b/editors/code/src/commands.ts
index 7a8490e4767..4438d475ad9 100644
--- a/editors/code/src/commands.ts
+++ b/editors/code/src/commands.ts
@@ -761,12 +761,13 @@ export function addProject(ctx: CtxInit): Cmd {
         }
 
         const workspaces: JsonProject[] = await Promise.all(
-            vscode.workspace.workspaceFolders!.map(async (folder): Promise<JsonProject> => {
-                const rustDocuments = vscode.workspace.textDocuments.filter(isRustDocument);
-                return discoverWorkspace(rustDocuments, discoverProjectCommand, {
-                    cwd: folder.uri.fsPath,
-                });
-            })
+            vscode.workspace.textDocuments
+                .filter(isRustDocument)
+                .map(async (file): Promise<JsonProject> => {
+                    return discoverWorkspace([file], discoverProjectCommand, {
+                        cwd: path.dirname(file.uri.fsPath),
+                    });
+                })
         );
 
         ctx.addToDiscoveredWorkspaces(workspaces);
diff --git a/editors/code/src/ctx.ts b/editors/code/src/ctx.ts
index dd74b31cc71..0ffa42c2a5a 100644
--- a/editors/code/src/ctx.ts
+++ b/editors/code/src/ctx.ts
@@ -1,6 +1,7 @@
 import * as vscode from "vscode";
 import * as lc from "vscode-languageclient/node";
 import * as ra from "./lsp_ext";
+import * as path from "path";
 
 import { Config, prepareVSCodeConfig } from "./config";
 import { createClient } from "./client";
@@ -192,12 +193,13 @@ export class Ctx {
             const discoverProjectCommand = this.config.discoverProjectCommand;
             if (discoverProjectCommand) {
                 const workspaces: JsonProject[] = await Promise.all(
-                    vscode.workspace.workspaceFolders!.map(async (folder): Promise<JsonProject> => {
-                        const rustDocuments = vscode.workspace.textDocuments.filter(isRustDocument);
-                        return discoverWorkspace(rustDocuments, discoverProjectCommand, {
-                            cwd: folder.uri.fsPath,
-                        });
-                    })
+                    vscode.workspace.textDocuments
+                        .filter(isRustDocument)
+                        .map(async (file): Promise<JsonProject> => {
+                            return discoverWorkspace([file], discoverProjectCommand, {
+                                cwd: path.dirname(file.uri.fsPath),
+                            });
+                        })
                 );
 
                 this.addToDiscoveredWorkspaces(workspaces);