Merge pull request #22994 from A4-Tacks/useless-ty-term-search-assist

fix: term_search exclude useless target type
diff --git a/Cargo.lock b/Cargo.lock
index 7a2e2d4..19bdd0c 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -453,7 +453,7 @@
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 2.0.117",
 ]
 
 [[package]]
@@ -464,7 +464,7 @@
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 2.0.117",
 ]
 
 [[package]]
@@ -524,7 +524,7 @@
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 2.0.117",
 ]
 
 [[package]]
@@ -698,9 +698,9 @@
 
 [[package]]
 name = "gen-lsp-types"
-version = "0.10.0"
+version = "0.11.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4cd635c5206acd03ea024d6b5902539e5c903de3afa220fdb5c94b583af77f4f"
+checksum = "b64887ac3a8083427ae935a7296db876871582cd57eac077564f8bc18fa49116"
 dependencies = [
  "serde",
  "serde_json",
@@ -1471,7 +1471,7 @@
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 2.0.117",
  "synstructure",
 ]
 
@@ -1857,7 +1857,7 @@
 checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b"
 dependencies = [
  "proc-macro2",
- "syn",
+ "syn 2.0.117",
 ]
 
 [[package]]
@@ -2104,7 +2104,7 @@
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 2.0.117",
 ]
 
 [[package]]
@@ -2183,7 +2183,7 @@
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 2.0.117",
  "synstructure",
 ]
 
@@ -2296,9 +2296,9 @@
 
 [[package]]
 name = "rowan"
-version = "0.15.19"
+version = "0.17.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a2441aaccb50f4267d4f0f58b21e0138e96a449f361ed57a2673a83c9bca0772"
+checksum = "14b574c58582fa59fa43a2feb6608b8744184659f08a2e0117e4b8224d95ed61"
 dependencies = [
  "countme",
  "hashbrown 0.14.5",
@@ -2447,9 +2447,9 @@
 
 [[package]]
 name = "salsa"
-version = "0.27.2"
+version = "0.28.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ffbaab832e2ea754afda4a738f987dd1e8bd30c9e5d8c981ee6a3934386095e2"
+checksum = "cf0e374215cd2db2b5c75d7b3a99cb0cc052c0595335dfdefc03d4eb08f4aa81"
 dependencies = [
  "boxcar",
  "crossbeam-queue",
@@ -2474,20 +2474,19 @@
 
 [[package]]
 name = "salsa-macro-rules"
-version = "0.27.2"
+version = "0.28.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "de6872462ac73d39969a836273c24163e6a26a4e08f5114fcd80e25af30ea9c6"
+checksum = "85f4b7d4405540bbd6d4ffa52d4322d983f3781954d3073067ac1bdb028459b3"
 
 [[package]]
 name = "salsa-macros"
-version = "0.27.2"
+version = "0.28.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "76bc78ffaf65b1a9175818592c5130aa10b1bb245a905722fd4db87cea8a8457"
+checksum = "445be2bfbb2f67cb663225ecd7bc5a25370c0250fca30f9d8cbad9a913650370"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
- "synstructure",
+ "syn 3.0.3",
 ]
 
 [[package]]
@@ -2551,7 +2550,7 @@
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 2.0.117",
 ]
 
 [[package]]
@@ -2696,6 +2695,17 @@
 ]
 
 [[package]]
+name = "syn"
+version = "3.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
 name = "synstructure"
 version = "0.13.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2703,7 +2713,7 @@
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 2.0.117",
 ]
 
 [[package]]
@@ -2714,6 +2724,7 @@
  "expect-test",
  "itertools 0.15.0",
  "parser",
+ "ra-ap-rustc_lexer",
  "rayon",
  "rowan",
  "rustc-hash 2.1.2",
@@ -2842,7 +2853,7 @@
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 2.0.117",
 ]
 
 [[package]]
@@ -2853,7 +2864,7 @@
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 2.0.117",
 ]
 
 [[package]]
@@ -2999,6 +3010,7 @@
 version = "0.0.0"
 dependencies = [
  "camino",
+ "semver",
 ]
 
 [[package]]
@@ -3020,7 +3032,7 @@
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 2.0.117",
 ]
 
 [[package]]
@@ -3282,7 +3294,7 @@
  "bumpalo",
  "proc-macro2",
  "quote",
- "syn",
+ "syn 2.0.117",
  "wasm-bindgen-shared",
 ]
 
@@ -3401,7 +3413,7 @@
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 2.0.117",
 ]
 
 [[package]]
@@ -3412,7 +3424,7 @@
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 2.0.117",
 ]
 
 [[package]]
@@ -3589,7 +3601,7 @@
  "heck",
  "indexmap",
  "prettyplease",
- "syn",
+ "syn 2.0.117",
  "wasm-metadata",
  "wit-bindgen-core",
  "wit-component",
@@ -3605,7 +3617,7 @@
  "prettyplease",
  "proc-macro2",
  "quote",
- "syn",
+ "syn 2.0.117",
  "wit-bindgen-core",
  "wit-bindgen-rust",
 ]
@@ -3728,7 +3740,7 @@
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 2.0.117",
  "synstructure",
 ]
 
@@ -3749,7 +3761,7 @@
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 2.0.117",
 ]
 
 [[package]]
@@ -3769,7 +3781,7 @@
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 2.0.117",
  "synstructure",
 ]
 
@@ -3803,7 +3815,7 @@
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 2.0.117",
 ]
 
 [[package]]
diff --git a/Cargo.toml b/Cargo.toml
index 4ef92c5..e7f2b35 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -123,17 +123,17 @@
 pulldown-cmark-to-cmark = "10.0.4"
 pulldown-cmark = { version = "0.9.6", default-features = false }
 rayon = "1.10.0"
-rowan = "=0.15.19"
+rowan = "0.17.0"
 # Ideally we'd not enable the macros feature but unfortunately the `tracked` attribute does not work
 # on impls without it
-salsa = { version = "0.27.0", default-features = false, features = [
+salsa = { version = "0.28.2", default-features = false, features = [
     "rayon",
     "salsa_unstable",
     "macros",
     "inventory",
     "triomphe"
 ] }
-salsa-macros = "0.27.0"
+salsa-macros = "0.28.2"
 semver = "1.0.26"
 serde = { version = "1.0.219" }
 serde_derive = { version = "1.0.219" }
diff --git a/crates/base-db/src/editioned_file_id.rs b/crates/base-db/src/editioned_file_id.rs
index f674c5d..6b32469 100644
--- a/crates/base-db/src/editioned_file_id.rs
+++ b/crates/base-db/src/editioned_file_id.rs
@@ -10,15 +10,16 @@
 
 use crate::SourceDatabase;
 
-#[salsa::interned(debug, constructor = from_span_file_id, no_lifetime)]
+#[salsa::interned(debug, constructor = from_span_file_id, unsafe(no_lifetime), revisions = usize::MAX)]
 #[derive(PartialOrd, Ord)]
 pub struct EditionedFileId {
+    #[returns(copy)]
     field: span::EditionedFileId,
 }
 
 #[salsa::tracked]
 impl EditionedFileId {
-    #[salsa::tracked(lru = 128)]
+    #[salsa::tracked(lru = 128, returns(clone))]
     pub fn parse(self, db: &dyn SourceDatabase) -> syntax::Parse<ast::SourceFile> {
         let _p = tracing::info_span!("parse", ?self).entered();
         let (file_id, edition) = self.unpack(db);
diff --git a/crates/base-db/src/lib.rs b/crates/base-db/src/lib.rs
index 4beeae9..37f617b 100644
--- a/crates/base-db/src/lib.rs
+++ b/crates/base-db/src/lib.rs
@@ -6,40 +6,7 @@
 #[cfg(feature = "in-rust-tree")]
 extern crate rustc_driver as _;
 
-pub mod salsa {
-    pub use salsa::*;
-
-    // Adjusted from `salsa::update_fallback` to work with database owned T
-    /// "Fallback" for maybe-update that is suitable for database owned T
-    /// that implement `Eq`. In this version, we update only if the new value
-    /// is not `Eq` to the old one. Note that given `Eq` impls that are not just
-    /// structurally comparing fields, this may cause us not to update even if
-    /// the value has changed (presumably because this change is not semantically
-    /// significant).
-    ///
-    /// # Safety
-    ///
-    /// See `Update::maybe_update`, additionally, `'db` is required to be `'static` or the lifetime
-    /// of the database `T` belongs to.
-    pub unsafe fn update_fallback_db<'db, T>(old_pointer: *mut T, new_value: T) -> bool
-    where
-        T: 'db + PartialEq,
-    {
-        // SAFETY: Because everything is owned, this ref is simply a valid `&mut`
-        let old_ref: &mut T = unsafe { &mut *old_pointer };
-
-        if *old_ref != new_value {
-            *old_ref = new_value;
-            true
-        } else {
-            // Subtle but important: Eq impls can be buggy or define equality
-            // in surprising ways. If it says that the value has not changed,
-            // we do not modify the existing value, and thus do not have to
-            // update the revision, as downstream code will not see the new value.
-            false
-        }
-    }
-}
+pub use salsa;
 pub use salsa_macros;
 use span::TextSize;
 
@@ -79,7 +46,7 @@
 #[macro_export]
 macro_rules! impl_intern_key {
     ($id:ident, $loc:ident) => {
-        #[salsa::interned(no_lifetime, revisions = usize::MAX)]
+        #[salsa::interned(unsafe(no_lifetime), revisions = usize::MAX)]
         #[derive(PartialOrd, Ord)]
         pub struct $id {
             #[returns(ref)]
@@ -255,11 +222,13 @@
 
 #[salsa::input(debug)]
 pub struct FileSourceRootInput {
+    #[returns(copy)]
     pub source_root_id: SourceRootId,
 }
 
 #[salsa::input(debug)]
 pub struct SourceRootInput {
+    #[returns(clone)]
     pub source_root: Arc<SourceRoot>,
 }
 
@@ -363,6 +332,7 @@
 
 #[salsa::input(singleton, debug)]
 struct AllCrates {
+    #[returns(clone)]
     crates: std::sync::Arc<[Crate]>,
 }
 
@@ -389,6 +359,7 @@
 #[doc(hidden)]
 #[salsa::interned]
 pub struct InternedSourceRootId {
+    #[returns(copy)]
     pub id: SourceRootId,
 }
 
diff --git a/crates/hir-def/src/attrs.rs b/crates/hir-def/src/attrs.rs
index d55509e..d71cad4 100644
--- a/crates/hir-def/src/attrs.rs
+++ b/crates/hir-def/src/attrs.rs
@@ -201,7 +201,7 @@
                 let segment4 = segment4.and_then(|it| it.segment()?.name_ref());
                 segment1.text() == "test"
                     && segment3.is_none_or(|it| it.text() == "prelude")
-                    && segment4.is_none_or(|it| matches!(&*it.text(), "core" | "std"))
+                    && segment4.is_none_or(|it| matches!(it.text(), "core" | "std"))
             });
             if is_test {
                 attr_flags.insert(AttrFlags::IS_TEST);
@@ -593,7 +593,7 @@
 
 #[salsa::tracked]
 impl AttrFlags {
-    #[salsa::tracked]
+    #[salsa::tracked(returns(copy))]
     pub fn query(db: &dyn SourceDatabase, owner: AttrDefId) -> AttrFlags {
         let mut attr_flags = AttrFlags::empty();
         collect_attrs(db, owner, |attr| match_attr_flags(&mut attr_flags, attr));
@@ -740,7 +740,7 @@
 
         return lang_item(db, owner);
 
-        #[salsa::tracked]
+        #[salsa::tracked(returns(clone))]
         fn lang_item(db: &dyn SourceDatabase, owner: AttrDefId) -> Option<Symbol> {
             collect_attrs(db, owner, |attr| {
                 if let ast::Meta::KeyValueMeta(attr) = attr
@@ -769,7 +769,7 @@
     ///
     /// Prefer [`AttrFlags::repr()`] in non-perf-sensitive places as it also has a check that
     /// that the ADT has repr.
-    #[salsa::tracked]
+    #[salsa::tracked(returns(copy))]
     pub fn repr_assume_has(db: &dyn SourceDatabase, owner: AdtId) -> Option<ReprOptions> {
         let mut result = None;
         collect_attrs::<Infallible>(db, owner.into(), |attr| {
@@ -922,7 +922,7 @@
 
         return rustc_layout_scalar_valid_range(db, owner);
 
-        #[salsa::tracked]
+        #[salsa::tracked(returns(copy))]
         fn rustc_layout_scalar_valid_range(
             db: &dyn SourceDatabase,
             owner: AdtId,
@@ -1049,7 +1049,7 @@
         }
         return doc_keyword(db, owner);
 
-        #[salsa::tracked]
+        #[salsa::tracked(returns(clone))]
         fn doc_keyword(db: &dyn SourceDatabase, owner: ModuleId) -> Option<Symbol> {
             collect_attrs(db, AttrDefId::ModuleId(owner), |attr| {
                 if let ast::Meta::TokenTreeMeta(attr) = attr
@@ -1166,7 +1166,7 @@
 
         return unstable_feature(db, owner);
 
-        #[salsa::tracked]
+        #[salsa::tracked(returns(clone))]
         fn unstable_feature(db: &dyn SourceDatabase, owner: AttrDefId) -> Option<Symbol> {
             collect_attrs(db, owner, |attr| {
                 if let ast::Meta::TokenTreeMeta(attr) = attr
diff --git a/crates/hir-def/src/attrs/docs.rs b/crates/hir-def/src/attrs/docs.rs
index 55cb6f8..0dd20c1 100644
--- a/crates/hir-def/src/attrs/docs.rs
+++ b/crates/hir-def/src/attrs/docs.rs
@@ -325,8 +325,8 @@
 struct DocMacroExpander<'db> {
     db: &'db dyn SourceDatabase,
     krate: Crate,
-    recursion_depth: usize,
-    recursion_limit: usize,
+    macro_depth: u32,
+    recursion_limit: u32,
     resolver: Resolver<'db>,
     file_id: HirFileId,
     ast_id_map: &'db AstIdMap,
@@ -357,7 +357,7 @@
     expander: &mut DocMacroExpander<'db>,
     macro_call: ast::MacroCall,
 ) -> Option<String> {
-    if expander.recursion_depth >= expander.recursion_limit {
+    if expander.macro_depth >= expander.recursion_limit {
         return None;
     }
 
@@ -374,6 +374,7 @@
         call_site.ctx,
         ExpandTo::Expr,
         expander.krate,
+        expander.macro_depth + 1,
         |path| {
             expander.resolver.resolve_path_as_macro_def(expander.db, path, Some(MacroSubNs::Bang))
         },
@@ -394,14 +395,14 @@
         std::mem::replace(&mut expander.span_map, SpanMap::ExpansionSpanMap(span_map));
     let old_ast_id_map =
         std::mem::replace(&mut expander.ast_id_map, expansion_file_id.ast_id_map(expander.db));
-    expander.recursion_depth += 1;
+    expander.macro_depth += 1;
 
     let expansion = expand_doc_expr_via_macro_pipeline(expander, expr);
 
     expander.file_id = old_file_id;
     expander.span_map = old_span_map;
     expander.ast_id_map = old_ast_id_map;
-    expander.recursion_depth -= 1;
+    expander.macro_depth -= 1;
 
     expansion
 }
@@ -444,11 +445,11 @@
                                 let exp = expander.get_or_insert_with(|| {
                                     let resolver = make_resolver();
                                     let def_map = resolver.top_level_def_map();
-                                    let recursion_limit = def_map.recursion_limit() as usize;
+                                    let recursion_limit = def_map.recursion_limit();
                                     DocMacroExpander {
                                         db,
                                         krate,
-                                        recursion_depth: 0,
+                                        macro_depth: file_id.macro_expansion_depth(db),
                                         recursion_limit,
                                         resolver,
                                         file_id,
diff --git a/crates/hir-def/src/expr_store.rs b/crates/hir-def/src/expr_store.rs
index 32d1695..5e6633d 100644
--- a/crates/hir-def/src/expr_store.rs
+++ b/crates/hir-def/src/expr_store.rs
@@ -117,7 +117,7 @@
     pats: Arena<Pat>,
     bindings: Arena<Binding>,
     labels: Arena<Label>,
-    /// Id of the closure/coroutine that owns the corresponding binding. If a binding is owned by the
+    /// Id of the closure/coroutine/anon const that owns the corresponding binding. If a binding is owned by the
     /// top level expression, it will not be listed in here.
     binding_owners: FxHashMap<BindingId, ExprId>,
     /// Block expressions in this store that may contain inner items.
@@ -613,8 +613,10 @@
                 visitor.on_expr_opt(*start);
                 visitor.on_expr_opt(*end);
             }
-            Pat::Lit(expr) | Pat::ConstBlock(expr) | Pat::Expr(expr) => visitor.on_expr(*expr),
-            Pat::Path(_) | Pat::Wild | Pat::Missing | Pat::Rest | Pat::NotNull => {}
+            Pat::Lit(expr) | Pat::Expr(expr) => visitor.on_expr(*expr),
+            Pat::ConstBlock(expr) => visitor.on_anon_const_expr(*expr),
+            Pat::Path(path) => visitor.on_path(path),
+            Pat::Wild | Pat::Missing | Pat::Rest | Pat::NotNull => {}
             &Pat::Bind { subpat, id: _ } => visitor.on_pat_opt(subpat),
             Pat::Or(args) | Pat::Tuple { args, ellipsis: _ } => visitor.on_pats(args),
             Pat::TupleStruct { args, ellipsis: _, path } => {
@@ -652,18 +654,6 @@
         self.walk_pats_shallow(pat_id, |p| self.walk_pats(p, f));
     }
 
-    pub fn is_binding_upvar(&self, binding: BindingId, relative_to: ExprId) -> bool {
-        let Some(expr_only) = &self.expr_only else { return false };
-        match expr_only.binding_owners.get(&binding) {
-            Some(it) => {
-                // We assign expression ids in a way that outer closures will receive
-                // a higher id (allocated after their body is collected)
-                it.into_raw() > relative_to.into_raw()
-            }
-            None => true,
-        }
-    }
-
     #[inline]
     pub fn binding_owner(&self, id: BindingId) -> Option<ExprId> {
         self.expr_only.as_ref()?.binding_owners.get(&id).copied()
@@ -775,10 +765,8 @@
             Expr::Field { expr, name: _ }
             | Expr::Await { expr }
             | Expr::Ref { expr, mutability: _, rawness: _ }
-            | Expr::UnaryOp { expr, op: _ }
-            | Expr::Const(expr) => {
-                visitor.on_expr(*expr);
-            }
+            | Expr::UnaryOp { expr, op: _ } => visitor.on_expr(*expr),
+            Expr::Const(expr) => visitor.on_anon_const_expr(*expr),
             Expr::Tuple { exprs } => visitor.on_exprs(exprs),
             Expr::Array(a) => match a {
                 Array::ElementList { elements } => visitor.on_exprs(elements),
@@ -1003,7 +991,7 @@
     }
 }
 
-trait StoreVisitorExt: StoreVisitor {
+pub trait StoreVisitorExt: StoreVisitor {
     fn on_type_bound(&mut self, bound: &TypeBound) {
         match bound {
             TypeBound::Path(path_id, _) => self.on_type(path_id.type_ref()),
diff --git a/crates/hir-def/src/expr_store/expander.rs b/crates/hir-def/src/expr_store/expander.rs
index c40f244..a974815 100644
--- a/crates/hir-def/src/expr_store/expander.rs
+++ b/crates/hir-def/src/expr_store/expander.rs
@@ -24,9 +24,9 @@
     span_map: SpanMap<'db>,
     current_file_id: HirFileId,
     ast_id_map: &'db AstIdMap,
-    /// `recursion_depth == usize::MAX` indicates that the recursion limit has been reached.
-    recursion_depth: u32,
-    recursion_limit: usize,
+    /// `recursion_depth == u32::MAX` indicates that the recursion limit has been reached.
+    macro_depth: u32,
+    recursion_limit: u32,
 }
 
 impl<'db> Expander<'db> {
@@ -35,7 +35,7 @@
         current_file_id: HirFileId,
         def_map: &'db DefMap,
     ) -> Expander<'db> {
-        let recursion_limit = def_map.recursion_limit() as usize;
+        let recursion_limit = def_map.recursion_limit();
         let recursion_limit = if cfg!(test) {
             // Without this, `body::tests::your_stack_belongs_to_me` stack-overflows in debug
             std::cmp::min(32, recursion_limit)
@@ -44,7 +44,7 @@
         };
         Expander {
             current_file_id,
-            recursion_depth: 0,
+            macro_depth: current_file_id.macro_expansion_depth(db),
             recursion_limit,
             span_map: current_file_id.span_map(db),
             ast_id_map: current_file_id.ast_id_map(db),
@@ -111,6 +111,7 @@
                 call_site.ctx,
                 expands_to,
                 krate,
+                this.macro_depth,
                 |path| resolver(path).map(|it| it.definition(db)),
                 eager_callback,
             ) {
@@ -137,14 +138,14 @@
         self.span_map = span_map;
         self.current_file_id = file_id;
         self.ast_id_map = ast_id_map;
-        if self.recursion_depth == u32::MAX {
+        if self.macro_depth == u32::MAX {
             // Recursion limit has been reached somewhere in the macro expansion tree. Reset the
             // depth only when we get out of the tree.
             if !self.current_file_id.is_macro() {
-                self.recursion_depth = 0;
+                self.macro_depth = 0;
             }
         } else {
-            self.recursion_depth -= 1;
+            self.macro_depth -= 1;
         }
         bomb.defuse();
     }
@@ -165,7 +166,7 @@
     where
         F: FnOnce(&mut Self) -> ExpandResult<Option<MacroCallId>>,
     {
-        if self.recursion_depth == u32::MAX {
+        if self.macro_depth == u32::MAX {
             // Recursion limit has been reached somewhere in the macro expansion tree. We should
             // stop expanding other macro calls in this tree, or else this may result in
             // exponential number of macro expansions, leading to a hang.
@@ -180,8 +181,8 @@
         let Some(call_id) = value else {
             return ExpandResult { value: None, err };
         };
-        if self.recursion_depth as usize > self.recursion_limit {
-            self.recursion_depth = u32::MAX;
+        if self.macro_depth > self.recursion_limit {
+            self.macro_depth = u32::MAX;
             cov_mark::hit!(your_stack_belongs_to_me);
             return ExpandResult::only_err(ExpandError::new(
                 call_id.macro_arg_considering_derives(db, &call_id.lookup(db).kind).2,
@@ -196,7 +197,7 @@
             value: {
                 let parse = res.value.0.clone().cast::<T>();
 
-                self.recursion_depth += 1;
+                self.macro_depth += 1;
                 let old_file_id = std::mem::replace(&mut self.current_file_id, call_id.into());
                 let old_span_map =
                     std::mem::replace(&mut self.span_map, SpanMap::ExpansionSpanMap(&res.value.1));
diff --git a/crates/hir-def/src/expr_store/lower.rs b/crates/hir-def/src/expr_store/lower.rs
index df4fc6e..1e5d9c2 100644
--- a/crates/hir-def/src/expr_store/lower.rs
+++ b/crates/hir-def/src/expr_store/lower.rs
@@ -665,7 +665,7 @@
         lifetime: ast::Lifetime,
     ) -> LifetimeRefId {
         // FIXME: Keyword check?
-        let lifetime_ref = match &*lifetime.text() {
+        let lifetime_ref = match lifetime.text() {
             "" | "'" => LifetimeRef::Error,
             "'static" => LifetimeRef::Static,
             "'_" => LifetimeRef::Placeholder,
@@ -1295,7 +1295,7 @@
         match binder.generic_param_list() {
             Some(gpl) => gpl
                 .lifetime_params()
-                .flat_map(|lp| lp.lifetime().map(|lt| Name::new_lifetime(&lt.text())))
+                .flat_map(|lp| lp.lifetime().map(|lt| Name::new_lifetime(lt.text())))
                 .collect(),
             None => ThinVec::default(),
         }
@@ -3175,7 +3175,7 @@
             name: ast_label
                 .lifetime()
                 .as_ref()
-                .map_or_else(Name::missing, |lt| Name::new_lifetime(&lt.text())),
+                .map_or_else(Name::missing, |lt| Name::new_lifetime(lt.text())),
         };
         self.alloc_label(label, AstPtr::new(&ast_label))
     }
@@ -3195,7 +3195,7 @@
                 (hygiene_id.syntax_context().parent(self.db), expansion.def)
             })
         };
-        let name = Name::new_lifetime(&lifetime.text());
+        let name = Name::new_lifetime(lifetime.text());
 
         for (rib_idx, rib) in self.label_ribs.iter().enumerate().rev() {
             match &rib.kind {
@@ -3331,7 +3331,7 @@
     Some((hir_lit, ast_lit))
 }
 
-impl ExprCollector<'_> {
+impl<'db> ExprCollector<'db> {
     fn with_fresh_binding_expr_root(&mut self, f: impl FnOnce(&mut Self) -> ExprId) -> ExprId {
         self.with_expr_root(|this| this.with_binding_owner(f))
     }
@@ -3482,19 +3482,16 @@
         }
     }
 
-    fn extend_type_alias_lifetime(&mut self, lifetimes: impl Iterator<Item = Name>) {
-        self.named_lifetime_store.lifetimes_constrained_by_input.extend(lifetimes);
-    }
-
     fn is_argument_lt_bound_scope(&mut self) -> bool {
         matches!(self.named_lifetime_store.lifetime_bound_scope, Some(LifetimeBoundScope::Argument))
     }
 
-    fn get_constrained_lifetimes_if_type_alias(
+    fn get_constrained_lifetimes_if_type_alias<'a>(
         &mut self,
-        mod_path: &intern::Interned<ModPath>,
-        generic_args: Option<&GenericArgs>,
-    ) -> Option<FxIndexSet<Name>> {
+        mod_path: &'a ModPath,
+        generic_args: Option<&'a GenericArgs>,
+    ) -> Option<impl Iterator<Item = LifetimeRefId> + use<'a, 'db>> {
+        let generic_args = generic_args?;
         let r_path = self.def_map.resolve_path(
             self.local_def_map,
             self.db,
@@ -3503,33 +3500,20 @@
             BuiltinShadowMode::Module,
             None,
         );
-        let def_id = r_path.0.types.map(|item| item.def)?;
-        let res = if let crate::ModuleDefId::TypeAliasId(id) = def_id {
-            let Some(generic_args) = generic_args else { return Some(FxIndexSet::default()) };
+        let ModuleDefId::TypeAliasId(id) = r_path.0.take_types()? else { return None };
 
-            let constrained_lt_indices = get_constrained_lifetimes(self.db, id);
-            let res = constrained_lt_indices
+        let constrained_lt_indices = get_constrained_lifetimes(self.db, id);
+        let res = constrained_lt_indices.iter().filter_map(|&idx| {
+            generic_args
+                .args
                 .iter()
-                .filter_map(|&idx| {
-                    let lt_ref = generic_args
-                        .args
-                        .iter()
-                        .filter_map(|arg| match arg {
-                            &GenericArg::Lifetime(lt_ref) => Some(lt_ref),
-                            GenericArg::Type(_) | GenericArg::Const(_) => None,
-                        })
-                        .nth(idx as usize)?;
-                    match &self.store.lifetimes[lt_ref] {
-                        LifetimeRef::Named(name) => Some(name.clone()),
-                        _ => None,
-                    }
+                .filter_map(|arg| match arg {
+                    &GenericArg::Lifetime(lt_ref) => Some(lt_ref),
+                    GenericArg::Type(_) | GenericArg::Const(_) => None,
                 })
-                .collect();
-            Some(res)
-        } else {
-            None
-        };
-        return res;
+                .nth(idx as usize)
+        });
+        return Some(res);
 
         #[salsa::tracked(returns(deref), cycle_result = get_constrained_lifetimes_cycle_result)]
         fn get_constrained_lifetimes(
diff --git a/crates/hir-def/src/expr_store/lower/asm.rs b/crates/hir-def/src/expr_store/lower/asm.rs
index 63a0594..fb0a5b0 100644
--- a/crates/hir-def/src/expr_store/lower/asm.rs
+++ b/crates/hir-def/src/expr_store/lower/asm.rs
@@ -39,7 +39,7 @@
                     Some(InlineAsmRegOrRegClass::Reg(Symbol::intern(string.text())))
                 } else {
                     reg.name_ref().map(|name_ref| {
-                        InlineAsmRegOrRegClass::RegClass(Symbol::intern(&name_ref.text()))
+                        InlineAsmRegOrRegClass::RegClass(Symbol::intern(name_ref.text()))
                     })
                 }
             };
@@ -69,7 +69,7 @@
                     continue;
                 }
                 ast::AsmPiece::AsmOperandNamed(op) => {
-                    let name = op.name().map(|name| Symbol::intern(&name.text()));
+                    let name = op.name().map(|name| Symbol::intern(name.text()));
                     if let Some(name) = &name {
                         named_args.insert(name.clone(), slot);
                         named_pos.insert(slot, name.clone());
diff --git a/crates/hir-def/src/expr_store/lower/format_args.rs b/crates/hir-def/src/expr_store/lower/format_args.rs
index 5552213..ad548c6 100644
--- a/crates/hir-def/src/expr_store/lower/format_args.rs
+++ b/crates/hir-def/src/expr_store/lower/format_args.rs
@@ -1,10 +1,13 @@
 //! Lowering of `format_args!()`.
 
 use base_db::FxIndexSet;
-use hir_expand::name::Name;
+use hir_expand::name::{AsName, Name};
 use intern::{Symbol, sym};
 use span::SyntaxContext;
-use syntax::{AstPtr, AstToken as _, ast};
+use syntax::{
+    AstPtr, AstToken as _,
+    ast::{self, HasName},
+};
 
 use crate::{
     expr_store::{HygieneId, lower::ExprCollector, path::Path},
@@ -29,8 +32,8 @@
         f.args().for_each(|arg| {
             let expr = arg.expr();
             args.add(FormatArgument {
-                kind: match arg.arg_name() {
-                    Some(name) => FormatArgumentKind::Named(Name::new_root(name.name().text())),
+                kind: match arg.name() {
+                    Some(name) => FormatArgumentKind::Named(name.as_name()),
                     None => FormatArgumentKind::Normal,
                 },
                 syntax: expr.as_ref().map(AstPtr::new),
diff --git a/crates/hir-def/src/expr_store/lower/generics.rs b/crates/hir-def/src/expr_store/lower/generics.rs
index ce6e736..65877fb 100644
--- a/crates/hir-def/src/expr_store/lower/generics.rs
+++ b/crates/hir-def/src/expr_store/lower/generics.rs
@@ -181,7 +181,7 @@
                             .map(|lifetime_param| {
                                 lifetime_param
                                     .lifetime()
-                                    .map_or_else(Name::missing, |lt| Name::new_lifetime(&lt.text()))
+                                    .map_or_else(Name::missing, |lt| Name::new_lifetime(lt.text()))
                             })
                             .collect()
                     });
diff --git a/crates/hir-def/src/expr_store/lower/path.rs b/crates/hir-def/src/expr_store/lower/path.rs
index 5d45a4f..8f71ea8 100644
--- a/crates/hir-def/src/expr_store/lower/path.rs
+++ b/crates/hir-def/src/expr_store/lower/path.rs
@@ -5,9 +5,12 @@
 
 use std::iter;
 
-use crate::expr_store::{
-    lower::{ExprCollector, generics::ImplTraitLowerFn},
-    path::NormalPath,
+use crate::{
+    expr_store::{
+        lower::{ExprCollector, generics::ImplTraitLowerFn},
+        path::NormalPath,
+    },
+    type_ref::LifetimeRef,
 };
 
 use hir_expand::{
@@ -255,15 +258,20 @@
 
     let mod_path = Interned::new(ModPath::from_segments(kind, segments));
 
-    let type_alias_constrained_lifetimes = collector.get_constrained_lifetimes_if_type_alias(
-        &mod_path,
-        generic_args.last().and_then(|g| g.as_ref()),
-    );
     if let Some(old_lifetimes_constrained_by_input) = old_lifetimes_constrained_by_input {
+        let type_alias_constrained_lifetimes = collector.get_constrained_lifetimes_if_type_alias(
+            &mod_path,
+            generic_args.last().and_then(|g| g.as_ref()),
+        );
         if let Some(lifetimes) = type_alias_constrained_lifetimes {
             collector.named_lifetime_store.lifetimes_constrained_by_input =
                 old_lifetimes_constrained_by_input;
-            collector.extend_type_alias_lifetime(lifetimes.into_iter());
+            collector.named_lifetime_store.lifetimes_constrained_by_input.extend(
+                lifetimes.filter_map(|lt_ref| match &collector.store.lifetimes[lt_ref] {
+                    LifetimeRef::Named(name) => Some(name.clone()),
+                    _ => None,
+                }),
+            );
         } else {
             collector
                 .named_lifetime_store
diff --git a/crates/hir-def/src/expr_store/scope.rs b/crates/hir-def/src/expr_store/scope.rs
index d6568b8..f0a4b5a 100644
--- a/crates/hir-def/src/expr_store/scope.rs
+++ b/crates/hir-def/src/expr_store/scope.rs
@@ -1,13 +1,15 @@
 //! Name resolution for expressions.
+use std::mem;
+
 use base_db::SourceDatabase;
 use hir_expand::{MacroDefId, name::Name};
 use la_arena::{Arena, ArenaMap, Idx, IdxRange, RawIdx};
 
 use crate::{
     BlockId, DefWithBodyId, ExpressionStoreOwnerId, GenericDefId, VariantId,
-    expr_store::{Body, ExpressionStore, HygieneId, StoreVisitor, body::Param},
+    expr_store::{Body, ExpressionStore, HygieneId, StoreVisitor, StoreVisitorExt, body::Param},
     hir::{
-        Array, Binding, BindingId, Expr, ExprId, Item, LabelId, Pat, PatId, Statement,
+        Binding, BindingId, Expr, ExprId, Item, LabelId, Pat, PatId, Statement,
         generics::GenericParams,
     },
     signatures::VariantFields,
@@ -162,12 +164,14 @@
                 body.expr_only.as_ref().map_or(0, |it| it.exprs.len()),
             ),
         };
-        let mut root = scopes.root_scope();
+        let root = scopes.root_scope();
         if let Some(Param { formal: self_param, user_written: _ }) = body.self_param {
             scopes.add_bindings(body, root, self_param, body.binding_hygiene(self_param));
         }
-        body.params.iter().for_each(|param| scopes.add_pat_bindings(body, root, param.formal));
-        compute_expr_scopes(body.root_expr(), body, &mut scopes, &mut { root }, &mut root);
+        let mut visitor =
+            ExprScopeVisitor { store: body, scopes: &mut scopes, scope: root, const_scope: root };
+        body.params.iter().for_each(|param| visitor.on_pat(param.formal));
+        visitor.on_expr(body.root_expr());
         scopes
     }
 
@@ -181,8 +185,9 @@
         };
         let root = scopes.root_scope();
         for root_expr in roots {
-            let mut scope = scopes.new_scope(root);
-            compute_expr_scopes(root_expr, store, &mut scopes, &mut { scope }, &mut scope);
+            let scope = scopes.new_scope(root);
+            ExprScopeVisitor { store, scopes: &mut scopes, scope, const_scope: scope }
+                .on_expr(root_expr);
         }
         scopes
     }
@@ -254,15 +259,6 @@
             IdxRange::new_inclusive(self.scopes[scope].entries.start()..=entry);
     }
 
-    fn add_pat_bindings(&mut self, store: &ExpressionStore, scope: ScopeId, pat: PatId) {
-        let pattern = &store[pat];
-        if let Pat::Bind { id, .. } = *pattern {
-            self.add_bindings(store, scope, id, store.binding_hygiene(id));
-        }
-
-        pattern.walk_child_pats(|pat| self.add_pat_bindings(store, scope, pat));
-    }
-
     fn set_scope(&mut self, node: ExprId, scope: ScopeId) {
         self.scope_by_expr.insert(node, scope);
     }
@@ -278,180 +274,131 @@
 struct ExprScopeVisitor<'a> {
     store: &'a ExpressionStore,
     scopes: &'a mut ExprScopes,
-    scope: &'a mut ScopeId,
-    const_scope: &'a mut ScopeId,
+    scope: ScopeId,
+    const_scope: ScopeId,
+}
+
+impl ExprScopeVisitor<'_> {
+    fn with_scope(&mut self, scope: ScopeId, f: impl FnOnce(&mut Self)) {
+        let old_scope = mem::replace(&mut self.scope, scope);
+        f(self);
+        self.scope = old_scope;
+    }
+
+    fn visit_block(
+        &mut self,
+        expr: ExprId,
+        id: Option<BlockId>,
+        statements: &[Statement],
+        tail: Option<ExprId>,
+        label: Option<LabelId>,
+    ) {
+        let scope = self.scopes.new_block_scope(self.scope, id, label);
+        self.with_scope(scope, |this| {
+            let old_const_scope = if id.is_some() {
+                let const_scope = this.scopes.new_block_scope(this.const_scope, id, None);
+                mem::replace(&mut this.const_scope, const_scope)
+            } else {
+                // We don't need to allocate a new scope, since only items matter to us.
+                this.const_scope
+            };
+            // Overwrite the old scope for the block expr, so that every block scope can be found
+            // via the block itthis (important for blocks that only contain items, no expressions).
+            this.scopes.set_scope(expr, this.scope);
+
+            for stmt in statements {
+                match stmt {
+                    Statement::Let { pat, initializer, else_branch, type_ref } => {
+                        this.on_type_opt(*type_ref);
+                        this.on_expr_opt(*initializer);
+                        this.on_expr_opt(*else_branch);
+                        this.scope = this.scopes.new_scope(this.scope);
+                        this.on_pat(*pat);
+                    }
+                    Statement::Expr { expr, has_semi: _ } => this.on_expr(*expr),
+                    Statement::Item(Item::MacroDef(macro_id)) => {
+                        this.scope = this.scopes.new_macro_def_scope(this.scope, macro_id.clone());
+                        this.const_scope =
+                            this.scopes.new_macro_def_scope(this.const_scope, macro_id.clone());
+                    }
+                    Statement::Item(Item::Other) => (),
+                }
+            }
+            this.on_expr_opt(tail);
+
+            this.const_scope = old_const_scope;
+        });
+    }
 }
 
 impl StoreVisitor for ExprScopeVisitor<'_> {
     fn on_expr(&mut self, expr: ExprId) {
-        compute_expr_scopes(expr, self.store, self.scopes, self.scope, self.const_scope);
+        self.scopes.set_scope(expr, self.scope);
+        match &self.store[expr] {
+            Expr::Block { statements, tail, id, label } => {
+                self.visit_block(expr, *id, statements, *tail, *label);
+            }
+            Expr::Unsafe { id, statements, tail } => {
+                self.visit_block(expr, *id, statements, *tail, None);
+            }
+            Expr::Loop { body, label, source: _ } => {
+                let scope = self.scopes.new_labeled_scope(self.scope, *label);
+                self.with_scope(scope, |this| this.on_expr(*body));
+            }
+            Expr::Closure { args, arg_types, ret_type, body, capture_by: _, closure_kind: _ } => {
+                arg_types.iter().for_each(|type_ref| self.on_type_opt(*type_ref));
+                self.on_type_opt(*ret_type);
+                let scope = self.scopes.new_scope(self.scope);
+                self.with_scope(scope, |this| {
+                    this.on_pats(args);
+                    this.on_expr(*body);
+                });
+            }
+            Expr::Match { expr, arms } => {
+                self.on_expr(*expr);
+                for arm in arms.iter() {
+                    let scope = self.scopes.new_scope(self.scope);
+                    self.with_scope(scope, |this| {
+                        this.on_pat(arm.pat);
+                        this.on_expr_opt(arm.guard);
+                        this.on_expr(arm.expr);
+                    });
+                }
+            }
+            &Expr::If { condition, then_branch, else_branch } => {
+                let then_branch_scope = self.scopes.new_scope(self.scope);
+                self.with_scope(then_branch_scope, |this| {
+                    this.on_expr(condition);
+                    this.on_expr(then_branch);
+                });
+                self.on_expr_opt(else_branch);
+            }
+            &Expr::Let { pat, expr } => {
+                self.on_expr(expr);
+                self.scope = self.scopes.new_scope(self.scope);
+                self.on_pat(pat);
+            }
+            _ => self.store.visit_expr_children(expr, self),
+        }
     }
 
     fn on_anon_const_expr(&mut self, expr: ExprId) {
-        let mut scope = *self.const_scope;
-        compute_expr_scopes(expr, self.store, self.scopes, &mut scope, self.const_scope);
+        self.with_scope(self.const_scope, |this| this.on_expr(expr));
     }
 
     fn on_pat(&mut self, pat: PatId) {
-        self.store.visit_pat_children(pat, &mut *self);
+        if let Pat::Bind { id, .. } = self.store[pat] {
+            self.scopes.add_bindings(self.store, self.scope, id, self.store.binding_hygiene(id));
+        }
+
+        self.store.visit_pat_children(pat, self);
     }
 
     fn on_type(&mut self, ty: TypeRefId) {
-        self.store.visit_type_ref_children(ty, &mut *self);
+        self.with_scope(self.const_scope, |this| self.store.visit_type_ref_children(ty, this));
     }
 }
 
-fn compute_type_scopes(
-    ty: TypeRefId,
-    store: &ExpressionStore,
-    scopes: &mut ExprScopes,
-    const_scope: &mut ScopeId,
-) {
-    let mut scope = *const_scope;
-    ExprScopeVisitor { store, scopes, scope: &mut scope, const_scope }.on_type(ty);
-}
-
-fn compute_block_scopes(
-    statements: &[Statement],
-    tail: Option<ExprId>,
-    store: &ExpressionStore,
-    scopes: &mut ExprScopes,
-    scope: &mut ScopeId,
-    const_scope: &mut ScopeId,
-) {
-    for stmt in statements {
-        match stmt {
-            Statement::Let { pat, initializer, else_branch, type_ref } => {
-                if let Some(type_ref) = type_ref {
-                    compute_type_scopes(*type_ref, store, scopes, const_scope);
-                }
-                if let Some(expr) = initializer {
-                    compute_expr_scopes(*expr, store, scopes, scope, const_scope);
-                }
-                if let Some(expr) = else_branch {
-                    compute_expr_scopes(*expr, store, scopes, scope, const_scope);
-                }
-
-                *scope = scopes.new_scope(*scope);
-                scopes.add_pat_bindings(store, *scope, *pat);
-            }
-            Statement::Expr { expr, .. } => {
-                compute_expr_scopes(*expr, store, scopes, scope, const_scope);
-            }
-            Statement::Item(Item::MacroDef(macro_id)) => {
-                *scope = scopes.new_macro_def_scope(*scope, macro_id.clone());
-                *const_scope = scopes.new_macro_def_scope(*const_scope, macro_id.clone());
-            }
-            Statement::Item(Item::Other) => (),
-        }
-    }
-    if let Some(expr) = tail {
-        compute_expr_scopes(expr, store, scopes, scope, const_scope);
-    }
-}
-
-fn compute_expr_scopes(
-    expr: ExprId,
-    store: &ExpressionStore,
-    scopes: &mut ExprScopes,
-    scope: &mut ScopeId,
-    const_scope: &mut ScopeId,
-) {
-    let compute_expr_scopes =
-        |scopes: &mut ExprScopes, expr: ExprId, scope: &mut ScopeId, const_scope: &mut ScopeId| {
-            compute_expr_scopes(expr, store, scopes, scope, const_scope)
-        };
-    let handle_block = |id,
-                        statements,
-                        tail,
-                        label,
-                        scopes: &mut ExprScopes,
-                        scope: &mut ScopeId,
-                        const_scope: &mut ScopeId| {
-        let mut scope = scopes.new_block_scope(*scope, id, label);
-        let mut const_scope = if id.is_some() {
-            scopes.new_block_scope(*const_scope, id, None)
-        } else {
-            // We don't need to allocate a new scope, since only items matter to us.
-            *const_scope
-        };
-        // Overwrite the old scope for the block expr, so that every block scope can be found
-        // via the block itself (important for blocks that only contain items, no expressions).
-        scopes.set_scope(expr, scope);
-        compute_block_scopes(statements, tail, store, scopes, &mut scope, &mut const_scope);
-    };
-
-    scopes.set_scope(expr, *scope);
-    match &store[expr] {
-        Expr::Block { statements, tail, id, label } => {
-            handle_block(*id, statements, *tail, *label, scopes, scope, const_scope);
-        }
-        Expr::Const(id) => {
-            let mut scope = *const_scope;
-            compute_expr_scopes(scopes, *id, &mut scope, const_scope);
-        }
-        Expr::Array(Array::Repeat { initializer, repeat }) => {
-            compute_expr_scopes(scopes, *initializer, scope, const_scope);
-            let mut repeat_scope = *const_scope;
-            compute_expr_scopes(scopes, *repeat, &mut repeat_scope, const_scope);
-        }
-        Expr::Unsafe { id, statements, tail } => {
-            handle_block(*id, statements, *tail, None, scopes, scope, const_scope);
-        }
-        Expr::Loop { body: body_expr, label, source: _ } => {
-            let mut scope = scopes.new_labeled_scope(*scope, *label);
-            compute_expr_scopes(scopes, *body_expr, &mut scope, const_scope);
-        }
-        Expr::Closure {
-            args,
-            arg_types,
-            ret_type,
-            body: body_expr,
-            capture_by: _,
-            closure_kind: _,
-        } => {
-            arg_types
-                .iter()
-                .flatten()
-                .for_each(|type_ref| compute_type_scopes(*type_ref, store, scopes, const_scope));
-            if let Some(type_ref) = ret_type {
-                compute_type_scopes(*type_ref, store, scopes, const_scope);
-            }
-            let mut scope = scopes.new_scope(*scope);
-            args.iter().for_each(|arg| scopes.add_pat_bindings(store, scope, *arg));
-            compute_expr_scopes(scopes, *body_expr, &mut scope, const_scope);
-        }
-        Expr::Match { expr, arms } => {
-            compute_expr_scopes(scopes, *expr, scope, const_scope);
-            for arm in arms.iter() {
-                let mut scope = scopes.new_scope(*scope);
-                scopes.add_pat_bindings(store, scope, arm.pat);
-                if let Some(guard) = arm.guard {
-                    scope = scopes.new_scope(scope);
-                    compute_expr_scopes(scopes, guard, &mut scope, const_scope);
-                }
-                compute_expr_scopes(scopes, arm.expr, &mut scope, const_scope);
-            }
-        }
-        &Expr::If { condition, then_branch, else_branch } => {
-            let mut then_branch_scope = scopes.new_scope(*scope);
-            compute_expr_scopes(scopes, condition, &mut then_branch_scope, const_scope);
-            compute_expr_scopes(scopes, then_branch, &mut then_branch_scope, const_scope);
-            if let Some(else_branch) = else_branch {
-                compute_expr_scopes(scopes, else_branch, scope, const_scope);
-            }
-        }
-        &Expr::Let { pat, expr } => {
-            compute_expr_scopes(scopes, expr, scope, const_scope);
-            *scope = scopes.new_scope(*scope);
-            scopes.add_pat_bindings(store, *scope, pat);
-        }
-        _ => {
-            store.visit_expr_children(expr, ExprScopeVisitor { store, scopes, scope, const_scope })
-        }
-    };
-}
-
 #[cfg(test)]
 mod tests {
     use hir_expand::{InFile, name::AsName};
@@ -497,16 +444,27 @@
         let (file_id, _) = editioned_file_id.unpack(&db);
 
         let file_syntax = editioned_file_id.parse(&db).syntax_node();
-        let marker: ast::PathExpr = find_node_at_offset(&file_syntax, offset).unwrap();
+        let marker: Option<ast::PathExpr> = find_node_at_offset(&file_syntax, offset);
         let function = find_function(&db, file_id);
 
         let scopes = ExprScopes::of(&db, DefWithBodyId::from(function));
-        let (_body, source_map) = Body::with_source_map(&db, function.into());
+        let (body, source_map) = Body::with_source_map(&db, function.into());
 
-        let expr_id = source_map
-            .node_expr(InFile { file_id: editioned_file_id.into(), value: &marker.into() })
-            .unwrap()
-            .as_expr()
+        let expr_id = marker
+            .and_then(|marker| {
+                source_map
+                    .node_expr(InFile { file_id: editioned_file_id.into(), value: &marker.into() })
+                    .and_then(|expr| expr.as_expr())
+            })
+            .or_else(|| {
+                body.exprs().find_map(|(expr, value)| {
+                    let crate::hir::Expr::Path(path) = value else { return None };
+                    path.mod_path()
+                        .and_then(|path| path.as_ident())
+                        .is_some_and(|name| name.as_str() == "marker")
+                        .then_some(expr)
+                })
+            })
             .unwrap();
         let scope = scopes.scope_for(expr_id);
 
@@ -534,6 +492,58 @@
     }
 
     #[test]
+    fn pattern_type_expr_scope() {
+        do_check(
+            r#"
+fn f(param: usize) {
+    let local = 0;
+    let _: builtin#pattern_type (usize is 0..=$0) = 0;
+}
+"#,
+            &["param"],
+        );
+    }
+
+    #[test]
+    fn closure_pattern_type_expr_scope() {
+        do_check(
+            r#"
+fn f(param: usize) {
+    let local = 0;
+    let _ = |_: builtin#pattern_type (usize is 0..=$0)| {};
+}
+"#,
+            &["param"],
+        );
+    }
+
+    #[test]
+    fn array_repeat_expr_scope() {
+        do_check(
+            r#"
+fn f(param: usize) {
+    let local = 0;
+    let _ = [(); $0];
+}
+"#,
+            &["param"],
+        );
+    }
+
+    #[test]
+    fn inline_const_expr_scope() {
+        do_check(
+            r#"
+fn f(param: usize) {
+    let local = 0;
+    let _ = const { $0 };
+}
+"#,
+            &["param"],
+        );
+    }
+
+    #[test]
     fn test_lambda_scope() {
         do_check(
             r"
@@ -811,4 +821,66 @@
             100,
         );
     }
+    #[test]
+    fn pattern_const_block_expressions_have_scopes() {
+        do_check(
+            r#"
+fn foo() {
+    match () {
+        const { |x: i32| { let y = x; $0 } } => (),
+    }
+}
+"#,
+            &["y", "x"],
+        );
+    }
+
+    #[test]
+    fn let_pattern_expr_scope() {
+        do_check(
+            r#"
+fn foo(param: usize) {
+    let local = 0;
+    let const { $0 } = ();
+}
+"#,
+            &["param"],
+        );
+    }
+
+    #[test]
+    fn closure_param_pattern_expr_scope() {
+        do_check(
+            r#"
+fn foo(param: usize) {
+    let local = 0;
+    let _ = |const { $0 }: ()| ();
+}
+"#,
+            &["param"],
+        );
+    }
+
+    #[test]
+    fn fn_param_pattern_expr_scope() {
+        do_check(
+            r#"
+fn foo(param: usize, const { $0 }: ()) {}
+"#,
+            &["param"],
+        );
+    }
+
+    #[test]
+    fn if_let_pattern_expr_scope() {
+        do_check(
+            r#"
+fn foo(param: usize) {
+    let local = 0;
+    if let const { $0 } = () {}
+}
+"#,
+            &["param"],
+        );
+    }
 }
diff --git a/crates/hir-def/src/expr_store/tests/body/block.rs b/crates/hir-def/src/expr_store/tests/body/block.rs
index a39a00b..850919e 100644
--- a/crates/hir-def/src/expr_store/tests/body/block.rs
+++ b/crates/hir-def/src/expr_store/tests/body/block.rs
@@ -190,13 +190,13 @@
     "#,
         expect![[r#"
             ModuleIdLt {
-                [salsa id]: Id(2c03),
+                [salsa id]: Id(583),
                 krate: Crate(
-                    Id(1c00),
+                    Id(380),
                 ),
                 block: Some(
                     BlockId(
-                        Id(3c01),
+                        Id(781),
                     ),
                 ),
                 containing_module_inside_def_map: None,
diff --git a/crates/hir-def/src/hir.rs b/crates/hir-def/src/hir.rs
index 7e56282..a275780 100644
--- a/crates/hir-def/src/hir.rs
+++ b/crates/hir-def/src/hir.rs
@@ -74,7 +74,7 @@
     }
 }
 
-#[derive(Copy, Clone, Hash, PartialEq, Eq, salsa::Update)]
+#[derive(Copy, Clone, Hash, PartialEq, Eq, salsa::SalsaValue)]
 pub struct ExprOrPatIdPacked(u32);
 
 const _: () = assert!(mem::size_of::<ExprOrPatIdPacked>() == mem::size_of::<u32>());
diff --git a/crates/hir-def/src/hir/generics.rs b/crates/hir-def/src/hir/generics.rs
index 039b229..2e0b6f1 100644
--- a/crates/hir-def/src/hir/generics.rs
+++ b/crates/hir-def/src/hir/generics.rs
@@ -4,6 +4,7 @@
 use base_db::SourceDatabase;
 use hir_expand::name::Name;
 use la_arena::{Arena, Idx, RawIdx};
+use salsa::SalsaValue;
 use stdx::impl_from;
 use thin_vec::ThinVec;
 
@@ -63,7 +64,7 @@
     ArgumentImplTrait,
 }
 
-#[derive(Clone, PartialEq, Eq, Debug, Hash)]
+#[derive(Clone, PartialEq, Eq, Debug, Hash, SalsaValue)]
 pub enum TypeOrConstParamData {
     TypeParamData(TypeParamData),
     ConstParamData(ConstParamData),
diff --git a/crates/hir-def/src/item_scope.rs b/crates/hir-def/src/item_scope.rs
index 14f1065..61d578b 100644
--- a/crates/hir-def/src/item_scope.rs
+++ b/crates/hir-def/src/item_scope.rs
@@ -161,7 +161,7 @@
     /// Module scoped macros will be inserted into `items` instead of here.
     // FIXME: Macro shadowing in one module is not properly handled. Non-item place macros will
     // be all resolved to the last one defined if shadowing happens.
-    legacy_macros: FxHashMap<Name, SmallVec<[MacroId; 2]>>,
+    legacy_macros: FxHashMap<Name, SmallVec<[MacroId; 1]>>,
     /// The attribute macro invocations in this scope.
     attr_macros: FxHashMap<AstId<ast::Item>, MacroCallId>,
     /// The macro invocations in this scope.
diff --git a/crates/hir-def/src/item_tree.rs b/crates/hir-def/src/item_tree.rs
index 423ffd5..63ce6bc 100644
--- a/crates/hir-def/src/item_tree.rs
+++ b/crates/hir-def/src/item_tree.rs
@@ -46,6 +46,7 @@
 use ast::{AstNode, StructKind};
 use base_db::{Crate, SourceDatabase};
 use cfg::CfgOptions;
+use either::Either;
 use hir_expand::{
     ExpandTo, HirFileId,
     mod_path::{ModPath, PathKind},
@@ -53,7 +54,6 @@
 };
 use intern::Interned;
 use la_arena::{Idx, RawIdx};
-use rustc_hash::FxHashMap;
 use span::{
     AstIdNode, Edition, FileAstId, NO_DOWNMAP_ERASED_FILE_AST_ID_MARKER, Span, SpanAnchor,
     SyntaxContext,
@@ -128,10 +128,9 @@
             static EMPTY: OnceLock<ItemTree> = OnceLock::new();
             EMPTY.get_or_init(|| ItemTree {
                 top_level: Box::new([]),
-                attrs: FxHashMap::default(),
-                small_data: FxHashMap::default(),
-                big_data: FxHashMap::default(),
-                top_attrs: AttrsOrCfg::empty(),
+                attrs: ThinVec::new(),
+                small_data: ThinVec::new(),
+                big_data: ThinVec::new(),
                 vis: ItemVisibilities { arena: ThinVec::new() },
             })
         }
@@ -146,7 +145,7 @@
 ) -> Option<Box<ItemTree>> {
     let _p = tracing::info_span!("file_item_tree_query", ?file_id).entered();
 
-    let ctx = lower::Ctx::new(db, file_id, krate);
+    let mut ctx = lower::Ctx::new(db, file_id, krate);
     let syntax = file_id.parse_or_expand(db);
     let mut item_tree = match_ast! {
         match syntax {
@@ -162,9 +161,8 @@
                     Some(attrs @ AttrsOrCfg::CfgDisabled(_)) => attrs,
                     None => ctx.lower_attrs(&file)
                 };
-                let mut item_tree = ctx.lower_module_items(&file);
-                item_tree.top_attrs = top_attrs;
-                item_tree
+                ctx.add_attrs(ModItemId::TOP_OWNER, top_attrs);
+                ctx.lower_module_items(&file)
             },
             ast::MacroItems(items) => {
                 ctx.lower_module_items(&items)
@@ -182,17 +180,10 @@
             },
         }
     };
-    let ItemTree { top_level, top_attrs, attrs, vis, big_data, small_data } = &item_tree;
-    if small_data.is_empty()
-        && big_data.is_empty()
-        && top_level.is_empty()
-        && attrs.is_empty()
-        && top_attrs.is_empty()
-        && vis.arena.is_empty()
-    {
+    if item_tree.is_empty() {
         None
     } else {
-        item_tree.shrink_to_fit();
+        item_tree.finalize();
         Some(Box::new(item_tree))
     }
 }
@@ -209,7 +200,7 @@
 
     let ctx = lower::Ctx::new(db, ast_id.file_id, krate);
     let mut item_tree = ctx.lower_block(&block);
-    item_tree.shrink_to_fit();
+    item_tree.finalize();
     item_tree
 }
 
@@ -217,11 +208,11 @@
 #[derive(Debug, Default, Eq, PartialEq)]
 pub struct ItemTree {
     top_level: Box<[ModItemId]>,
-    top_attrs: AttrsOrCfg,
-    attrs: FxHashMap<FileAstId<ast::Item>, AttrsOrCfg>,
+    /// Sorted by the id. The last item, if it has [`ModItemId::TOP_OWNER`], is the top level attrs.
+    attrs: ThinVec<(ModItemId, AttrsOrCfg)>,
     vis: ItemVisibilities,
-    big_data: FxHashMap<FileAstId<ast::Item>, BigModItem>,
-    small_data: FxHashMap<FileAstId<ast::Item>, SmallModItem>,
+    big_data: ThinVec<(FileAstId<ast::Item>, BigModItem)>,
+    small_data: ThinVec<(FileAstId<ast::Item>, SmallModItem)>,
 }
 
 impl ItemTree {
@@ -232,12 +223,14 @@
     }
 
     /// Returns the inner attributes of the source file.
-    pub(crate) fn top_level_attrs(&self) -> &AttrsOrCfg {
-        &self.top_attrs
+    #[inline]
+    pub(crate) fn top_level_attrs(&self) -> Option<&AttrsOrCfg> {
+        self.attrs.last().filter(|(id, _)| *id == ModItemId::TOP_OWNER).map(|(_, attrs)| attrs)
     }
 
-    pub(crate) fn attrs(&self, of: FileAstId<ast::Item>) -> Option<&AttrsOrCfg> {
-        self.attrs.get(&of)
+    #[inline]
+    pub(crate) fn attrs(&self, of: ModItemId) -> Option<&AttrsOrCfg> {
+        self.attrs.binary_search_by_key(&of, |(id, _)| *id).ok().map(|index| &self.attrs[index].1)
     }
 
     /// Returns a count of a few, expensive items.
@@ -249,7 +242,7 @@
         let mut mods = 0;
         let mut macro_calls = 0;
         let mut macro_rules = 0;
-        for item in self.small_data.values() {
+        for (_, item) in &self.small_data {
             match item {
                 SmallModItem::Trait(_) => traits += 1,
                 SmallModItem::Impl(_) => impls += 1,
@@ -258,7 +251,7 @@
                 _ => {}
             }
         }
-        for item in self.big_data.values() {
+        for (_, item) in &self.big_data {
             match item {
                 BigModItem::Mod(_) => mods += 1,
                 _ => {}
@@ -271,11 +264,21 @@
         pretty::print_item_tree(db, self, edition)
     }
 
-    fn shrink_to_fit(&mut self) {
-        let ItemTree { top_level: _, attrs, big_data, small_data, vis: _, top_attrs: _ } = self;
+    fn is_empty(&self) -> bool {
+        let ItemTree { top_level, attrs, vis: _, big_data: _, small_data: _ } = self;
+        // We don't need to check the rest since if those are empty, everything is empty.
+        // `attrs` might contain the top-level attrs so we need to check it separately.
+        top_level.is_empty() && attrs.is_empty()
+    }
+
+    fn finalize(&mut self) {
+        self.attrs.sort_unstable_by_key(|(id, _)| *id);
+
+        let ItemTree { top_level: _, attrs, big_data, small_data, vis } = self;
         attrs.shrink_to_fit();
         big_data.shrink_to_fit();
         small_data.shrink_to_fit();
+        vis.arena.shrink_to_fit();
     }
 }
 
@@ -366,71 +369,105 @@
     }
 }
 
+#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, PartialOrd, Ord)]
+pub struct ModItemId {
+    /// The LSB is 0 for small_data and 1 for big_data.
+    index: u32,
+}
+
+impl ModItemId {
+    const TOP_OWNER: ModItemId = ModItemId { index: u32::MAX };
+
+    #[inline]
+    fn new_small(index: u32) -> Self {
+        debug_assert!(
+            (index & 0x80_00_00_00) == 0 && index != (ModItemId::TOP_OWNER.index >> 1),
+            "too big index"
+        );
+        Self { index: index << 1 }
+    }
+    #[inline]
+    fn new_big(index: u32) -> Self {
+        debug_assert!(
+            (index & 0x80_00_00_00) == 0 && index != (ModItemId::TOP_OWNER.index >> 1),
+            "too big index"
+        );
+        Self { index: (index << 1) | 0b1 }
+    }
+
+    #[inline]
+    fn get(
+        self,
+        item_tree: &ItemTree,
+    ) -> (FileAstId<ast::Item>, Either<&SmallModItem, &BigModItem>) {
+        let index = self.index >> 1;
+        match self.index & 0b1 {
+            0 => {
+                let (ast_id, data) = &item_tree.small_data[index as usize];
+                (*ast_id, Either::Left(data))
+            }
+            1 => {
+                let (ast_id, data) = &item_tree.big_data[index as usize];
+                (*ast_id, Either::Right(data))
+            }
+            _ => unreachable!(),
+        }
+    }
+
+    #[inline]
+    pub(crate) fn ast_id(self, item_tree: &ItemTree) -> FileAstId<ast::Item> {
+        self.get(item_tree).0
+    }
+}
+
 macro_rules! mod_items {
-    ($mod_item:ident -> $( $typ:ident in $fld:ident -> $ast:ty ),+ $(,)? ) => {
-        #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
-        pub(crate) enum $mod_item {
+    ($mod_item:ident -> $( $typ:ident by $either:ident -> $ast:ty ),+ $(,)? ) => {
+        #[derive(Debug, Copy, Clone)]
+        pub(crate) enum $mod_item<'a> {
             $(
-                $typ(FileAstId<$ast>),
+                $typ(FileAstId<$ast>, &'a $typ),
             )+
         }
 
-        impl $mod_item {
-            pub(crate) fn ast_id(self) -> FileAstId<ast::Item> {
-                match self {
-                    $($mod_item::$typ(it) => it.upcast()),+
+        impl ItemTree {
+            #[inline]
+            pub(crate) fn index(&self, id: ModItemId) -> $mod_item<'_> {
+                use BigModItem::*;
+                use SmallModItem::*;
+
+                let (ast_id, item) = id.get(self);
+                match item {
+                    $( Either::$either($typ(item)) => $mod_item::$typ(ast_id.downcast_unchecked(), item), )+
                 }
             }
         }
 
         $(
-            impl From<FileAstId<$ast>> for $mod_item {
-                fn from(id: FileAstId<$ast>) -> $mod_item {
-                    ModItemId::$typ(id)
-                }
-            }
-        )+
-
-        $(
             impl ItemTreeNode for $typ {
                 type Source = $ast;
             }
-
-            impl Index<FileAstId<$ast>> for ItemTree {
-                type Output = $typ;
-
-                #[allow(unused_imports)]
-                fn index(&self, index: FileAstId<$ast>) -> &Self::Output {
-                    use BigModItem::*;
-                    use SmallModItem::*;
-                    match &self.$fld[&index.upcast()] {
-                        $typ(item) => item,
-                        _ => panic!("expected item of type `{}` at index `{:?}`", stringify!($typ), index),
-                    }
-                }
-            }
         )+
     };
 }
 
 mod_items! {
-ModItemId ->
-    Const in small_data -> ast::Const,
-    Enum in small_data -> ast::Enum,
-    ExternBlock in small_data -> ast::ExternBlock,
-    ExternCrate in big_data -> ast::ExternCrate,
-    Function in small_data -> ast::Fn,
-    Impl in small_data -> ast::Impl,
-    Macro2 in small_data -> ast::MacroDef,
-    MacroCall in small_data -> ast::MacroCall,
-    MacroRules in small_data -> ast::MacroRules,
-    Mod in big_data -> ast::Module,
-    Static in small_data -> ast::Static,
-    Struct in small_data -> ast::Struct,
-    Trait in small_data -> ast::Trait,
-    TypeAlias in small_data -> ast::TypeAlias,
-    Union in small_data -> ast::Union,
-    Use in big_data -> ast::Use,
+ModItemKind ->
+    Const by Left -> ast::Const,
+    Enum by Left -> ast::Enum,
+    ExternBlock by Left -> ast::ExternBlock,
+    ExternCrate by Right -> ast::ExternCrate,
+    Function by Left -> ast::Fn,
+    Impl by Left -> ast::Impl,
+    Macro2 by Left -> ast::MacroDef,
+    MacroCall by Left -> ast::MacroCall,
+    MacroRules by Left -> ast::MacroRules,
+    Mod by Right -> ast::Module,
+    Static by Left -> ast::Static,
+    Struct by Left -> ast::Struct,
+    Trait by Left -> ast::Trait,
+    TypeAlias by Left -> ast::TypeAlias,
+    Union by Left -> ast::Union,
+    Use by Right -> ast::Use,
 }
 
 impl Index<RawVisibilityId> for ItemTree {
diff --git a/crates/hir-def/src/item_tree/attrs.rs b/crates/hir-def/src/item_tree/attrs.rs
index 9671ceb..4ee2f56 100644
--- a/crates/hir-def/src/item_tree/attrs.rs
+++ b/crates/hir-def/src/item_tree/attrs.rs
@@ -254,12 +254,7 @@
 
 impl AttrsOrCfg {
     #[inline]
-    pub(super) fn empty() -> Self {
-        AttrsOrCfg::Enabled { attrs: AttrsOwned(Box::new([])) }
-    }
-
-    #[inline]
     pub(super) fn is_empty(&self) -> bool {
-        matches!(self, AttrsOrCfg::Enabled { attrs } if attrs.as_ref().is_empty())
+        matches!(self, AttrsOrCfg::Enabled { attrs } if attrs.0.is_empty())
     }
 }
diff --git a/crates/hir-def/src/item_tree/lower.rs b/crates/hir-def/src/item_tree/lower.rs
index 6f916c8..85fc0e4 100644
--- a/crates/hir-def/src/item_tree/lower.rs
+++ b/crates/hir-def/src/item_tree/lower.rs
@@ -14,9 +14,9 @@
 
 use crate::item_tree::{
     BigModItem, Const, Enum, ExternBlock, ExternCrate, FieldsShape, Function, Impl, ImportAlias,
-    Interned, ItemTree, ItemTreeAstId, Macro2, MacroCall, MacroRules, Mod, ModItemId, ModKind,
-    ModPath, RawVisibility, RawVisibilityId, SmallModItem, Static, Struct, StructKind, Trait,
-    TypeAlias, Union, Use, UseTree, UseTreeKind, VisibilityExplicitness, attrs::AttrsOrCfg,
+    Interned, ItemTree, Macro2, MacroCall, MacroRules, Mod, ModItemId, ModKind, ModPath,
+    RawVisibility, RawVisibilityId, SmallModItem, Static, Struct, StructKind, Trait, TypeAlias,
+    Union, Use, UseTree, UseTreeKind, VisibilityExplicitness, attrs::AttrsOrCfg,
 };
 
 pub(super) struct Ctx<'db> {
@@ -98,7 +98,7 @@
     }
 
     pub(super) fn lower_block(mut self, block: &ast::BlockExpr) -> ItemTree {
-        self.tree.top_attrs = self.lower_attrs(block);
+        self.add_attrs(ModItemId::TOP_OWNER, self.lower_attrs(block));
         self.top_level = block
             .statements()
             .filter_map(|stmt| match stmt {
@@ -124,68 +124,76 @@
     }
 
     fn lower_mod_item(&mut self, item: &ast::Item) -> Option<ModItemId> {
-        let mod_item: ModItemId = match item {
-            ast::Item::Struct(ast) => self.lower_struct(ast)?.into(),
-            ast::Item::Union(ast) => self.lower_union(ast)?.into(),
-            ast::Item::Enum(ast) => self.lower_enum(ast)?.into(),
-            ast::Item::Fn(ast) => self.lower_function(ast)?.into(),
-            ast::Item::TypeAlias(ast) => self.lower_type_alias(ast)?.into(),
-            ast::Item::Static(ast) => self.lower_static(ast)?.into(),
-            ast::Item::Const(ast) => self.lower_const(ast).into(),
-            ast::Item::Module(ast) => self.lower_module(ast)?.into(),
-            ast::Item::Trait(ast) => self.lower_trait(ast)?.into(),
-            ast::Item::Impl(ast) => self.lower_impl(ast).into(),
-            ast::Item::Use(ast) => self.lower_use(ast)?.into(),
-            ast::Item::ExternCrate(ast) => self.lower_extern_crate(ast)?.into(),
-            ast::Item::MacroCall(ast) => self.lower_macro_call(ast)?.into(),
-            ast::Item::MacroRules(ast) => self.lower_macro_rules(ast)?.into(),
-            ast::Item::MacroDef(ast) => self.lower_macro_def(ast)?.into(),
-            ast::Item::ExternBlock(ast) => self.lower_extern_block(ast).into(),
+        let mod_item = match item {
+            ast::Item::Struct(ast) => self.lower_struct(ast)?,
+            ast::Item::Union(ast) => self.lower_union(ast)?,
+            ast::Item::Enum(ast) => self.lower_enum(ast)?,
+            ast::Item::Fn(ast) => self.lower_function(ast)?,
+            ast::Item::TypeAlias(ast) => self.lower_type_alias(ast)?,
+            ast::Item::Static(ast) => self.lower_static(ast)?,
+            ast::Item::Const(ast) => self.lower_const(ast),
+            ast::Item::Module(ast) => self.lower_module(ast)?,
+            ast::Item::Trait(ast) => self.lower_trait(ast)?,
+            ast::Item::Impl(ast) => self.lower_impl(ast),
+            ast::Item::Use(ast) => self.lower_use(ast)?,
+            ast::Item::ExternCrate(ast) => self.lower_extern_crate(ast)?,
+            ast::Item::MacroCall(ast) => self.lower_macro_call(ast)?,
+            ast::Item::MacroRules(ast) => self.lower_macro_rules(ast)?,
+            ast::Item::MacroDef(ast) => self.lower_macro_def(ast)?,
+            ast::Item::ExternBlock(ast) => self.lower_extern_block(ast),
             // FIXME: Handle `global_asm!()`.
             ast::Item::AsmExpr(_) => return None,
         };
         let attrs = self.lower_attrs(item);
-        self.add_attrs(mod_item.ast_id(), attrs);
+        self.add_attrs(mod_item, attrs);
 
         Some(mod_item)
     }
 
-    fn add_attrs(&mut self, item: FileAstId<ast::Item>, attrs: AttrsOrCfg) {
+    pub(super) fn add_attrs(&mut self, item: ModItemId, attrs: AttrsOrCfg) {
         if !attrs.is_empty() {
-            self.tree.attrs.insert(item, attrs);
+            self.tree.attrs.push((item, attrs));
         }
     }
 
-    fn lower_struct(&mut self, strukt: &ast::Struct) -> Option<ItemTreeAstId<Struct>> {
+    fn alloc_small(&mut self, ast_id: FileAstId<ast::Item>, item: SmallModItem) -> ModItemId {
+        let index = self.tree.small_data.len() as u32;
+        self.tree.small_data.push((ast_id, item));
+        ModItemId::new_small(index)
+    }
+
+    fn alloc_big(&mut self, ast_id: FileAstId<ast::Item>, item: BigModItem) -> ModItemId {
+        let index = self.tree.big_data.len() as u32;
+        self.tree.big_data.push((ast_id, item));
+        ModItemId::new_big(index)
+    }
+
+    fn lower_struct(&mut self, strukt: &ast::Struct) -> Option<ModItemId> {
         let visibility = self.lower_visibility(strukt);
         let name = strukt.name()?.as_name();
         let ast_id = self.source_ast_id_map.ast_id(strukt);
         let shape = adt_shape(strukt.kind());
         let res = Struct { name, visibility, shape };
-        self.tree.small_data.insert(ast_id.upcast(), SmallModItem::Struct(res));
-
-        Some(ast_id)
+        Some(self.alloc_small(ast_id.upcast(), SmallModItem::Struct(res)))
     }
 
-    fn lower_union(&mut self, union: &ast::Union) -> Option<ItemTreeAstId<Union>> {
+    fn lower_union(&mut self, union: &ast::Union) -> Option<ModItemId> {
         let visibility = self.lower_visibility(union);
         let name = union.name()?.as_name();
         let ast_id = self.source_ast_id_map.ast_id(union);
         let res = Union { name, visibility };
-        self.tree.small_data.insert(ast_id.upcast(), SmallModItem::Union(res));
-        Some(ast_id)
+        Some(self.alloc_small(ast_id.upcast(), SmallModItem::Union(res)))
     }
 
-    fn lower_enum(&mut self, enum_: &ast::Enum) -> Option<ItemTreeAstId<Enum>> {
+    fn lower_enum(&mut self, enum_: &ast::Enum) -> Option<ModItemId> {
         let visibility = self.lower_visibility(enum_);
         let name = enum_.name()?.as_name();
         let ast_id = self.source_ast_id_map.ast_id(enum_);
         let res = Enum { name, visibility };
-        self.tree.small_data.insert(ast_id.upcast(), SmallModItem::Enum(res));
-        Some(ast_id)
+        Some(self.alloc_small(ast_id.upcast(), SmallModItem::Enum(res)))
     }
 
-    fn lower_function(&mut self, func: &ast::Fn) -> Option<ItemTreeAstId<Function>> {
+    fn lower_function(&mut self, func: &ast::Fn) -> Option<ModItemId> {
         let visibility = self.lower_visibility(func);
         let name = func.name()?.as_name();
 
@@ -193,41 +201,34 @@
 
         let res = Function { name, visibility };
 
-        self.tree.small_data.insert(ast_id.upcast(), SmallModItem::Function(res));
-        Some(ast_id)
+        Some(self.alloc_small(ast_id.upcast(), SmallModItem::Function(res)))
     }
 
-    fn lower_type_alias(
-        &mut self,
-        type_alias: &ast::TypeAlias,
-    ) -> Option<ItemTreeAstId<TypeAlias>> {
+    fn lower_type_alias(&mut self, type_alias: &ast::TypeAlias) -> Option<ModItemId> {
         let name = type_alias.name()?.as_name();
         let visibility = self.lower_visibility(type_alias);
         let ast_id = self.source_ast_id_map.ast_id(type_alias);
         let res = TypeAlias { name, visibility };
-        self.tree.small_data.insert(ast_id.upcast(), SmallModItem::TypeAlias(res));
-        Some(ast_id)
+        Some(self.alloc_small(ast_id.upcast(), SmallModItem::TypeAlias(res)))
     }
 
-    fn lower_static(&mut self, static_: &ast::Static) -> Option<ItemTreeAstId<Static>> {
+    fn lower_static(&mut self, static_: &ast::Static) -> Option<ModItemId> {
         let name = static_.name()?.as_name();
         let visibility = self.lower_visibility(static_);
         let ast_id = self.source_ast_id_map.ast_id(static_);
         let res = Static { name, visibility };
-        self.tree.small_data.insert(ast_id.upcast(), SmallModItem::Static(res));
-        Some(ast_id)
+        Some(self.alloc_small(ast_id.upcast(), SmallModItem::Static(res)))
     }
 
-    fn lower_const(&mut self, konst: &ast::Const) -> ItemTreeAstId<Const> {
+    fn lower_const(&mut self, konst: &ast::Const) -> ModItemId {
         let name = konst.name().map(|it| it.as_name());
         let visibility = self.lower_visibility(konst);
         let ast_id = self.source_ast_id_map.ast_id(konst);
         let res = Const { name, visibility };
-        self.tree.small_data.insert(ast_id.upcast(), SmallModItem::Const(res));
-        ast_id
+        self.alloc_small(ast_id.upcast(), SmallModItem::Const(res))
     }
 
-    fn lower_module(&mut self, module: &ast::Module) -> Option<ItemTreeAstId<Mod>> {
+    fn lower_module(&mut self, module: &ast::Module) -> Option<ModItemId> {
         let name = module.name()?.as_name();
         let visibility = self.lower_visibility(module);
         let kind = if module.semicolon_token().is_some() {
@@ -247,30 +248,27 @@
         };
         let ast_id = self.source_ast_id_map.ast_id(module);
         let res = Mod { name, visibility, kind };
-        self.tree.big_data.insert(ast_id.upcast(), BigModItem::Mod(res));
-        Some(ast_id)
+        Some(self.alloc_big(ast_id.upcast(), BigModItem::Mod(res)))
     }
 
-    fn lower_trait(&mut self, trait_def: &ast::Trait) -> Option<ItemTreeAstId<Trait>> {
+    fn lower_trait(&mut self, trait_def: &ast::Trait) -> Option<ModItemId> {
         let name = trait_def.name()?.as_name();
         let visibility = self.lower_visibility(trait_def);
         let ast_id = self.source_ast_id_map.ast_id(trait_def);
 
         let def = Trait { name, visibility };
-        self.tree.small_data.insert(ast_id.upcast(), SmallModItem::Trait(def));
-        Some(ast_id)
+        Some(self.alloc_small(ast_id.upcast(), SmallModItem::Trait(def)))
     }
 
-    fn lower_impl(&mut self, impl_def: &ast::Impl) -> ItemTreeAstId<Impl> {
+    fn lower_impl(&mut self, impl_def: &ast::Impl) -> ModItemId {
         let ast_id = self.source_ast_id_map.ast_id(impl_def);
         // Note that trait impls don't get implicit `Self` unlike traits, because here they are a
         // type alias rather than a type parameter, so this is handled by the resolver.
         let res = Impl { is_trait_impl: impl_def.trait_().is_some() };
-        self.tree.small_data.insert(ast_id.upcast(), SmallModItem::Impl(res));
-        ast_id
+        self.alloc_small(ast_id.upcast(), SmallModItem::Impl(res))
     }
 
-    fn lower_use(&mut self, use_item: &ast::Use) -> Option<ItemTreeAstId<Use>> {
+    fn lower_use(&mut self, use_item: &ast::Use) -> Option<ModItemId> {
         let visibility = self.lower_visibility(use_item);
         let ast_id = self.source_ast_id_map.ast_id(use_item);
         let (use_tree, _) = lower_use_tree(self.db, use_item.use_tree()?, &mut |range| {
@@ -278,14 +276,10 @@
         })?;
 
         let res = Use { visibility, use_tree };
-        self.tree.big_data.insert(ast_id.upcast(), BigModItem::Use(res));
-        Some(ast_id)
+        Some(self.alloc_big(ast_id.upcast(), BigModItem::Use(res)))
     }
 
-    fn lower_extern_crate(
-        &mut self,
-        extern_crate: &ast::ExternCrate,
-    ) -> Option<ItemTreeAstId<ExternCrate>> {
+    fn lower_extern_crate(&mut self, extern_crate: &ast::ExternCrate) -> Option<ModItemId> {
         let name = extern_crate.name_ref()?.as_name();
         let alias = extern_crate.rename().map(|a| {
             a.name().map(|it| it.as_name()).map_or(ImportAlias::Underscore, ImportAlias::Alias)
@@ -294,11 +288,10 @@
         let ast_id = self.source_ast_id_map.ast_id(extern_crate);
 
         let res = ExternCrate { name, alias, visibility };
-        self.tree.big_data.insert(ast_id.upcast(), BigModItem::ExternCrate(res));
-        Some(ast_id)
+        Some(self.alloc_big(ast_id.upcast(), BigModItem::ExternCrate(res)))
     }
 
-    fn lower_macro_call(&mut self, m: &ast::MacroCall) -> Option<ItemTreeAstId<MacroCall>> {
+    fn lower_macro_call(&mut self, m: &ast::MacroCall) -> Option<ModItemId> {
         let span_map = self.span_map();
         let path = m.path()?;
         let range = path.syntax().text_range();
@@ -308,31 +301,28 @@
         let ast_id = self.source_ast_id_map.ast_id(m);
         let expand_to = hir_expand::ExpandTo::from_call_site(m);
         let res = MacroCall { path, expand_to, ctxt: span_map.span_for_range(range).ctx };
-        self.tree.small_data.insert(ast_id.upcast(), SmallModItem::MacroCall(res));
-        Some(ast_id)
+        Some(self.alloc_small(ast_id.upcast(), SmallModItem::MacroCall(res)))
     }
 
-    fn lower_macro_rules(&mut self, m: &ast::MacroRules) -> Option<ItemTreeAstId<MacroRules>> {
+    fn lower_macro_rules(&mut self, m: &ast::MacroRules) -> Option<ModItemId> {
         let name = m.name()?;
         let ast_id = self.source_ast_id_map.ast_id(m);
 
         let res = MacroRules { name: name.as_name() };
-        self.tree.small_data.insert(ast_id.upcast(), SmallModItem::MacroRules(res));
-        Some(ast_id)
+        Some(self.alloc_small(ast_id.upcast(), SmallModItem::MacroRules(res)))
     }
 
-    fn lower_macro_def(&mut self, m: &ast::MacroDef) -> Option<ItemTreeAstId<Macro2>> {
+    fn lower_macro_def(&mut self, m: &ast::MacroDef) -> Option<ModItemId> {
         let name = m.name()?;
 
         let ast_id = self.source_ast_id_map.ast_id(m);
         let visibility = self.lower_visibility(m);
 
         let res = Macro2 { name: name.as_name(), visibility };
-        self.tree.small_data.insert(ast_id.upcast(), SmallModItem::Macro2(res));
-        Some(ast_id)
+        Some(self.alloc_small(ast_id.upcast(), SmallModItem::Macro2(res)))
     }
 
-    fn lower_extern_block(&mut self, block: &ast::ExternBlock) -> ItemTreeAstId<ExternBlock> {
+    fn lower_extern_block(&mut self, block: &ast::ExternBlock) -> ModItemId {
         let ast_id = self.source_ast_id_map.ast_id(block);
         let children: Box<[_]> = block.extern_item_list().map_or(Box::new([]), |list| {
             list.extern_items()
@@ -341,22 +331,21 @@
                     // (in other words, the knowledge that they're in an extern block must not be used).
                     // This is because an extern block can contain macros whose ItemTree's top-level items
                     // should be considered to be in an extern block too.
-                    let mod_item: ModItemId = match &item {
-                        ast::ExternItem::Fn(ast) => self.lower_function(ast)?.into(),
-                        ast::ExternItem::Static(ast) => self.lower_static(ast)?.into(),
-                        ast::ExternItem::TypeAlias(ty) => self.lower_type_alias(ty)?.into(),
-                        ast::ExternItem::MacroCall(call) => self.lower_macro_call(call)?.into(),
+                    let mod_item = match &item {
+                        ast::ExternItem::Fn(ast) => self.lower_function(ast)?,
+                        ast::ExternItem::Static(ast) => self.lower_static(ast)?,
+                        ast::ExternItem::TypeAlias(ty) => self.lower_type_alias(ty)?,
+                        ast::ExternItem::MacroCall(call) => self.lower_macro_call(call)?,
                     };
                     let attrs = self.lower_attrs(&item);
-                    self.add_attrs(mod_item.ast_id(), attrs);
+                    self.add_attrs(mod_item, attrs);
                     Some(mod_item)
                 })
                 .collect()
         });
 
         let res = ExternBlock { children };
-        self.tree.small_data.insert(ast_id.upcast(), SmallModItem::ExternBlock(res));
-        ast_id
+        self.alloc_small(ast_id.upcast(), SmallModItem::ExternBlock(res))
     }
 
     fn lower_visibility(&mut self, item: &dyn ast::HasVisibility) -> RawVisibilityId {
diff --git a/crates/hir-def/src/item_tree/pretty.rs b/crates/hir-def/src/item_tree/pretty.rs
index fc62468..9fefe8b 100644
--- a/crates/hir-def/src/item_tree/pretty.rs
+++ b/crates/hir-def/src/item_tree/pretty.rs
@@ -7,8 +7,9 @@
 use crate::{
     item_tree::{
         Const, Enum, ExternBlock, ExternCrate, FieldsShape, Function, Impl, ItemTree, Macro2,
-        MacroCall, MacroRules, Mod, ModItemId, ModKind, RawVisibilityId, SourceDatabase, Static,
-        Struct, Trait, TypeAlias, Union, Use, UseTree, UseTreeKind, attrs::AttrsOrCfg,
+        MacroCall, MacroRules, Mod, ModItemId, ModItemKind, ModKind, RawVisibilityId,
+        SourceDatabase, Static, Struct, Trait, TypeAlias, Union, Use, UseTree, UseTreeKind,
+        attrs::AttrsOrCfg,
     },
     visibility::RawVisibility,
 };
@@ -21,8 +22,10 @@
     let mut p =
         Printer { db, tree, buf: String::new(), indent_level: 0, needs_indent: true, edition };
 
-    p.print_attrs(&tree.top_attrs, true, "\n");
-    p.blank();
+    if let Some(top_attrs) = tree.top_level_attrs() {
+        p.print_attrs(top_attrs, true, "\n");
+        p.blank();
+    }
 
     for item in tree.top_level_items() {
         p.print_mod_item(*item);
@@ -111,7 +114,7 @@
     }
 
     fn print_attrs_of(&mut self, of: ModItemId, separated_by: &str) {
-        if let Some(attrs) = self.tree.attrs.get(&of.ast_id()) {
+        if let Some(attrs) = self.tree.attrs(of) {
             self.print_attrs(attrs, false, separated_by);
         }
     }
@@ -173,17 +176,17 @@
     fn print_mod_item(&mut self, item: ModItemId) {
         self.print_attrs_of(item, "\n");
 
-        match item {
-            ModItemId::Use(ast_id) => {
-                let Use { visibility, use_tree } = &self.tree[ast_id];
+        match self.tree.index(item) {
+            ModItemKind::Use(ast_id, item) => {
+                let Use { visibility, use_tree } = item;
                 self.print_ast_id(ast_id.erase());
                 self.print_visibility(*visibility);
                 w!(self, "use ");
                 self.print_use_tree(use_tree);
                 wln!(self, ";");
             }
-            ModItemId::ExternCrate(ast_id) => {
-                let ExternCrate { name, alias, visibility } = &self.tree[ast_id];
+            ModItemKind::ExternCrate(ast_id, item) => {
+                let ExternCrate { name, alias, visibility } = item;
                 self.print_ast_id(ast_id.erase());
                 self.print_visibility(*visibility);
                 w!(self, "extern crate {}", name.display(self.db, self.edition));
@@ -192,8 +195,8 @@
                 }
                 wln!(self, ";");
             }
-            ModItemId::ExternBlock(ast_id) => {
-                let ExternBlock { children } = &self.tree[ast_id];
+            ModItemKind::ExternBlock(ast_id, item) => {
+                let ExternBlock { children } = item;
                 self.print_ast_id(ast_id.erase());
                 w!(self, "extern {{");
                 self.indented(|this| {
@@ -203,14 +206,14 @@
                 });
                 wln!(self, "}}");
             }
-            ModItemId::Function(ast_id) => {
-                let Function { name, visibility } = &self.tree[ast_id];
+            ModItemKind::Function(ast_id, item) => {
+                let Function { name, visibility } = item;
                 self.print_ast_id(ast_id.erase());
                 self.print_visibility(*visibility);
                 wln!(self, "fn {};", name.display(self.db, self.edition));
             }
-            ModItemId::Struct(ast_id) => {
-                let Struct { visibility, name, shape: kind } = &self.tree[ast_id];
+            ModItemKind::Struct(ast_id, item) => {
+                let Struct { visibility, name, shape: kind } = item;
                 self.print_ast_id(ast_id.erase());
                 self.print_visibility(*visibility);
                 w!(self, "struct {}", name.display(self.db, self.edition));
@@ -221,22 +224,22 @@
                     wln!(self, ";");
                 }
             }
-            ModItemId::Union(ast_id) => {
-                let Union { name, visibility } = &self.tree[ast_id];
+            ModItemKind::Union(ast_id, item) => {
+                let Union { name, visibility } = item;
                 self.print_ast_id(ast_id.erase());
                 self.print_visibility(*visibility);
                 w!(self, "union {}", name.display(self.db, self.edition));
                 self.print_fields(FieldsShape::Record);
                 wln!(self);
             }
-            ModItemId::Enum(ast_id) => {
-                let Enum { name, visibility } = &self.tree[ast_id];
+            ModItemKind::Enum(ast_id, item) => {
+                let Enum { name, visibility } = item;
                 self.print_ast_id(ast_id.erase());
                 self.print_visibility(*visibility);
                 w!(self, "enum {} {{ ... }}", name.display(self.db, self.edition));
             }
-            ModItemId::Const(ast_id) => {
-                let Const { name, visibility } = &self.tree[ast_id];
+            ModItemKind::Const(ast_id, item) => {
+                let Const { name, visibility } = item;
                 self.print_ast_id(ast_id.erase());
                 self.print_visibility(*visibility);
                 w!(self, "const ");
@@ -246,8 +249,8 @@
                 }
                 wln!(self, " = _;");
             }
-            ModItemId::Static(ast_id) => {
-                let Static { name, visibility } = &self.tree[ast_id];
+            ModItemKind::Static(ast_id, item) => {
+                let Static { name, visibility } = item;
                 self.print_ast_id(ast_id.erase());
                 self.print_visibility(*visibility);
                 w!(self, "static ");
@@ -255,27 +258,27 @@
                 w!(self, " = _;");
                 wln!(self);
             }
-            ModItemId::Trait(ast_id) => {
-                let Trait { name, visibility } = &self.tree[ast_id];
+            ModItemKind::Trait(ast_id, item) => {
+                let Trait { name, visibility } = item;
                 self.print_ast_id(ast_id.erase());
                 self.print_visibility(*visibility);
                 w!(self, "trait {} {{ ... }}", name.display(self.db, self.edition));
             }
-            ModItemId::Impl(ast_id) => {
-                let Impl { is_trait_impl: _ } = &self.tree[ast_id];
+            ModItemKind::Impl(ast_id, item) => {
+                let Impl { is_trait_impl: _ } = item;
                 self.print_ast_id(ast_id.erase());
                 w!(self, "impl {{ ... }}");
             }
-            ModItemId::TypeAlias(ast_id) => {
-                let TypeAlias { name, visibility } = &self.tree[ast_id];
+            ModItemKind::TypeAlias(ast_id, item) => {
+                let TypeAlias { name, visibility } = item;
                 self.print_ast_id(ast_id.erase());
                 self.print_visibility(*visibility);
                 w!(self, "type {}", name.display(self.db, self.edition));
                 w!(self, ";");
                 wln!(self);
             }
-            ModItemId::Mod(ast_id) => {
-                let Mod { name, visibility, kind } = &self.tree[ast_id];
+            ModItemKind::Mod(ast_id, item) => {
+                let Mod { name, visibility, kind } = item;
                 self.print_ast_id(ast_id.erase());
                 self.print_visibility(*visibility);
                 w!(self, "mod {}", name.display(self.db, self.edition));
@@ -294,8 +297,8 @@
                     }
                 }
             }
-            ModItemId::MacroCall(ast_id) => {
-                let MacroCall { path, expand_to, ctxt } = &self.tree[ast_id];
+            ModItemKind::MacroCall(ast_id, item) => {
+                let MacroCall { path, expand_to, ctxt } = item;
                 let _ = writeln!(
                     self,
                     "// AstId: {:#?}, SyntaxContextId: {}, ExpandTo: {:?}",
@@ -305,13 +308,13 @@
                 );
                 wln!(self, "{}!(...);", path.display(self.db, self.edition));
             }
-            ModItemId::MacroRules(ast_id) => {
-                let MacroRules { name } = &self.tree[ast_id];
+            ModItemKind::MacroRules(ast_id, item) => {
+                let MacroRules { name } = item;
                 self.print_ast_id(ast_id.erase());
                 wln!(self, "macro_rules! {} {{ ... }}", name.display(self.db, self.edition));
             }
-            ModItemId::Macro2(ast_id) => {
-                let Macro2 { name, visibility } = &self.tree[ast_id];
+            ModItemKind::Macro2(ast_id, item) => {
+                let Macro2 { name, visibility } = item;
                 self.print_ast_id(ast_id.erase());
                 self.print_visibility(*visibility);
                 wln!(self, "macro {} {{ ... }}", name.display(self.db, self.edition));
diff --git a/crates/hir-def/src/lib.rs b/crates/hir-def/src/lib.rs
index bed38d3..288fefd 100644
--- a/crates/hir-def/src/lib.rs
+++ b/crates/hir-def/src/lib.rs
@@ -354,7 +354,7 @@
     pub derive_index: u32,
 }
 
-#[salsa::interned(debug, no_lifetime)]
+#[salsa::interned(debug, unsafe(no_lifetime), revisions = usize::MAX)]
 #[derive(PartialOrd, Ord)]
 pub struct BuiltinDeriveImplId {
     #[returns(ref)]
@@ -465,7 +465,7 @@
 impl_intern!(ProcMacroId, ProcMacroLoc);
 impl_loc!(ProcMacroLoc, id: Fn, container: ModuleId);
 
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, salsa::Update)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, salsa::SalsaValue)]
 pub enum LoweringMode {
     Analysis,
     Ide,
@@ -495,8 +495,10 @@
 #[salsa::tracked(constructor = new_)]
 #[derive(PartialOrd, Ord)]
 pub struct BlockIdLt<'db> {
+    #[returns(copy)]
     pub ast_id: AstId<ast::BlockExpr>,
     /// The containing module.
+    #[returns(copy)]
     pub module: ModuleIdLt<'db>,
 }
 pub type BlockId = BlockIdLt<'static>;
@@ -538,15 +540,18 @@
 #[derive(PartialOrd, Ord)]
 pub struct ModuleIdLt<'db> {
     /// The crate this module belongs to.
+    #[returns(copy)]
     pub krate: Crate,
     /// If this `ModuleId` was derived from a `DefMap` for a block expression, this stores the
     /// `BlockId` of that block expression. If `None`, this module is part of the crate-level
     /// `DefMap` of `krate`.
+    #[returns(copy)]
     pub block: Option<BlockIdLt<'db>>,
     /// The parent module of this module, or `None` if this is the root module inside the def
     /// map (including for block def maps).
     pub containing_module_inside_def_map: Option<ModuleIdLt<'db>>,
     /// The name of this module, or [`sym::__empty`] for the root module.
+    #[returns(clone)]
     name_or_empty: Name,
 }
 pub type ModuleId = ModuleIdLt<'static>;
@@ -620,17 +625,17 @@
     }
 }
 
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, salsa::Update)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, salsa::SalsaValue)]
 pub struct FieldId {
     // FIXME: Store this as an erased `salsa::Id` to save space
     pub parent: VariantId,
     pub local_id: LocalFieldId,
 }
 
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, salsa::Update)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, salsa::SalsaValue)]
 pub struct TupleId(pub u32);
 
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, salsa::Update)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, salsa::SalsaValue)]
 pub struct TupleFieldId {
     pub tuple: TupleId,
     pub index: u32,
@@ -754,7 +759,7 @@
 #[salsa::tracked]
 impl MacroId {
     /// Turns a MacroId into a MacroDefId, describing the macro's definition post name resolution.
-    #[salsa::tracked]
+    #[salsa::tracked(returns(copy))]
     pub fn definition(self, db: &dyn SourceDatabase) -> MacroDefId {
         let kind = |expander, file_id, m| {
             let in_file = InFile::new(file_id, m);
@@ -1124,7 +1129,7 @@
 );
 
 #[derive(
-    Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, salsa::Supertype, salsa::Update,
+    Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, salsa::Supertype, salsa::SalsaValue,
 )]
 pub enum VariantId {
     EnumVariantId(EnumVariantId),
@@ -1435,6 +1440,7 @@
     call_site: SyntaxContext,
     expand_to: ExpandTo,
     krate: Crate,
+    macro_depth: u32,
     resolver: impl Fn(&ModPath) -> Option<MacroDefId> + Copy,
     eager_callback: &mut dyn FnMut(
         InFile<(syntax::AstPtr<ast::MacroCall>, span::FileAstId<ast::MacroCall>)>,
@@ -1451,6 +1457,7 @@
             ast_id,
             def,
             call_site,
+            macro_depth,
             &|path| resolver(path).filter(MacroDefId::is_fn_like),
             eager_callback,
         ),
@@ -1460,6 +1467,7 @@
                 krate,
                 MacroCallKind::FnLike { ast_id, expand_to, eager: None },
                 call_site,
+                macro_depth,
             )),
             err: None,
         },
diff --git a/crates/hir-def/src/macro_expansion_tests/mbe.rs b/crates/hir-def/src/macro_expansion_tests/mbe.rs
index 9a46ddb..e290601 100644
--- a/crates/hir-def/src/macro_expansion_tests/mbe.rs
+++ b/crates/hir-def/src/macro_expansion_tests/mbe.rs
@@ -35,9 +35,9 @@
     };
 }
 
-struct#0:MacroRules[BE8F, 0]@58..64#15360# MyTraitMap2#0:MacroCall[BE8F, 0]@31..42#ROOT2024# {#0:MacroRules[BE8F, 0]@72..73#15360#
-    map#0:MacroRules[BE8F, 0]@86..89#15360#:#0:MacroRules[BE8F, 0]@89..90#15360# #0:MacroRules[BE8F, 0]@89..90#15360#::#0:MacroRules[BE8F, 0]@91..93#15360#std#0:MacroRules[BE8F, 0]@93..96#15360#::#0:MacroRules[BE8F, 0]@96..98#15360#collections#0:MacroRules[BE8F, 0]@98..109#15360#::#0:MacroRules[BE8F, 0]@109..111#15360#HashSet#0:MacroRules[BE8F, 0]@111..118#15360#<#0:MacroRules[BE8F, 0]@118..119#15360#(#0:MacroRules[BE8F, 0]@119..120#15360#)#0:MacroRules[BE8F, 0]@120..121#15360#>#0:MacroRules[BE8F, 0]@121..122#15360#,#0:MacroRules[BE8F, 0]@122..123#15360#
-}#0:MacroRules[BE8F, 0]@132..133#15360#
+struct#0:MacroRules[BE8F, 0]@58..64#1920# MyTraitMap2#0:MacroCall[BE8F, 0]@31..42#ROOT2024# {#0:MacroRules[BE8F, 0]@72..73#1920#
+    map#0:MacroRules[BE8F, 0]@86..89#1920#:#0:MacroRules[BE8F, 0]@89..90#1920# #0:MacroRules[BE8F, 0]@89..90#1920#::#0:MacroRules[BE8F, 0]@91..93#1920#std#0:MacroRules[BE8F, 0]@93..96#1920#::#0:MacroRules[BE8F, 0]@96..98#1920#collections#0:MacroRules[BE8F, 0]@98..109#1920#::#0:MacroRules[BE8F, 0]@109..111#1920#HashSet#0:MacroRules[BE8F, 0]@111..118#1920#<#0:MacroRules[BE8F, 0]@118..119#1920#(#0:MacroRules[BE8F, 0]@119..120#1920#)#0:MacroRules[BE8F, 0]@120..121#1920#>#0:MacroRules[BE8F, 0]@121..122#1920#,#0:MacroRules[BE8F, 0]@122..123#1920#
+}#0:MacroRules[BE8F, 0]@132..133#1920#
 "#]],
     );
 }
@@ -197,7 +197,7 @@
 #[macro_use]
 mod foo;
 
-struct#1:MacroRules[DB0C, 0]@59..65#15360# Foo#0:MacroCall[DB0C, 0]@32..35#ROOT2024#(#1:MacroRules[DB0C, 0]@70..71#15360#u32#0:MacroCall[DB0C, 0]@41..44#ROOT2024#)#1:MacroRules[DB0C, 0]@74..75#15360#;#1:MacroRules[DB0C, 0]@75..76#15360#
+struct#1:MacroRules[DB0C, 0]@59..65#1920# Foo#0:MacroCall[DB0C, 0]@32..35#ROOT2024#(#1:MacroRules[DB0C, 0]@70..71#1920#u32#0:MacroCall[DB0C, 0]@41..44#ROOT2024#)#1:MacroRules[DB0C, 0]@74..75#1920#;#1:MacroRules[DB0C, 0]@75..76#1920#
 "#]],
     );
 }
@@ -423,10 +423,10 @@
 macro_rules! m {
     ($($i:ident),*) => ( impl Bar { $(fn $i() {})* } );
 }
-impl#\15360# Bar#\15360# {#\15360#
-    fn#\15360# foo#\ROOT2024#(#\15360#)#\15360# {#\15360#}#\15360#
-    fn#\15360# bar#\ROOT2024#(#\15360#)#\15360# {#\15360#}#\15360#
-}#\15360#
+impl#\1920# Bar#\1920# {#\1920#
+    fn#\1920# foo#\ROOT2024#(#\1920#)#\1920# {#\1920#}#\1920#
+    fn#\1920# bar#\ROOT2024#(#\1920#)#\1920# {#\1920#}#\1920#
+}#\1920#
 "#]],
     );
 }
diff --git a/crates/hir-def/src/nameres/assoc.rs b/crates/hir-def/src/nameres/assoc.rs
index 9560d78..8dea9a4 100644
--- a/crates/hir-def/src/nameres/assoc.rs
+++ b/crates/hir-def/src/nameres/assoc.rs
@@ -144,7 +144,7 @@
     diagnostics: Vec<DefDiagnostic>,
     container: ItemContainerId,
 
-    depth: usize,
+    macro_depth: u32,
     items: Vec<(Name, AssocItemId)>,
     macro_calls: ThinVec<(AstId<ast::Item>, MacroCallId)>,
 }
@@ -169,7 +169,7 @@
             container,
             items: Vec::new(),
 
-            depth: 0,
+            macro_depth: file_id.macro_expansion_depth(db),
             macro_calls: ThinVec::new(),
             diagnostics: Vec::new(),
         }
@@ -215,6 +215,7 @@
                 ast_id_with_path,
                 attr,
                 attr_id,
+                self.macro_depth + 1,
             ) {
                 Ok(ResolvedAttr::Macro(call_id)) => {
                     let loc = call_id.loc(self.db);
@@ -321,6 +322,7 @@
                     ctxt,
                     ExpandTo::Items,
                     self.module_id.krate(self.db),
+                    self.macro_depth + 1,
                     resolver,
                     &mut |ptr, call_id| {
                         self.macro_calls.push((ptr.map(|(_, it)| it.upcast()), call_id))
@@ -351,7 +353,7 @@
     }
 
     fn collect_macro_items(&mut self, macro_call_id: MacroCallId) {
-        if self.depth > self.def_map.recursion_limit() as usize {
+        if self.macro_depth > self.def_map.recursion_limit() {
             tracing::warn!("macro expansion is too deep");
             return;
         }
@@ -360,7 +362,7 @@
         let old_file_id = mem::replace(&mut self.file_id, macro_call_id.into());
         let old_ast_id_map = mem::replace(&mut self.ast_id_map, self.file_id.ast_id_map(self.db));
         let old_span_map = mem::replace(&mut self.span_map, SpanMap::ExpansionSpanMap(span_map));
-        self.depth += 1;
+        self.macro_depth += 1;
 
         let items = ast::MacroItems::cast(syntax.syntax_node()).expect("not `MacroItems`");
         for item in items.items() {
@@ -375,7 +377,7 @@
             self.collect_item(item);
         }
 
-        self.depth -= 1;
+        self.macro_depth -= 1;
         self.file_id = old_file_id;
         self.ast_id_map = old_ast_id_map;
         self.span_map = old_span_map;
diff --git a/crates/hir-def/src/nameres/attr_resolution.rs b/crates/hir-def/src/nameres/attr_resolution.rs
index e78c38b..411b80f 100644
--- a/crates/hir-def/src/nameres/attr_resolution.rs
+++ b/crates/hir-def/src/nameres/attr_resolution.rs
@@ -35,6 +35,7 @@
         ast_id: AstIdWithPath<ast::Item>,
         attr: &Attr,
         attr_id: AttrId,
+        macro_depth: u32,
     ) -> Result<ResolvedAttr, UnresolvedMacro> {
         // NB: does not currently work for derive helpers as they aren't recorded in the `DefMap`
 
@@ -73,6 +74,7 @@
             AttrMacroAttrIds::from_one(attr_id),
             self.krate,
             def.definition(db),
+            macro_depth,
         )))
     }
 
@@ -108,6 +110,7 @@
     censored_attr_ids: AttrMacroAttrIds,
     krate: Crate,
     def: MacroDefId,
+    macro_depth: u32,
 ) -> MacroCallId {
     let arg = match macro_attr.input.as_deref() {
         Some(AttrInput::TokenTree(tt)) => {
@@ -128,6 +131,7 @@
             censored_attr_ids,
         },
         macro_attr.ctxt,
+        macro_depth,
     )
 }
 
@@ -140,6 +144,7 @@
     krate: Crate,
     resolver: impl Fn(&ModPath) -> Option<(MacroId, MacroDefId)>,
     derive_macro_id: MacroCallId,
+    macro_depth: u32,
 ) -> Result<(MacroId, MacroDefId, MacroCallId), UnresolvedMacro> {
     let (macro_id, def_id) = resolver(&item_attr.path)
         .filter(|(_, def_id)| def_id.is_derive())
@@ -154,6 +159,7 @@
             derive_macro_id,
         },
         call_site,
+        macro_depth,
     );
     Ok((macro_id, def_id, call_id))
 }
diff --git a/crates/hir-def/src/nameres/collector.rs b/crates/hir-def/src/nameres/collector.rs
index bb61ceb..97450c8 100644
--- a/crates/hir-def/src/nameres/collector.rs
+++ b/crates/hir-def/src/nameres/collector.rs
@@ -37,7 +37,7 @@
     item_scope::{GlobId, ImportId, ImportOrExternCrate, PerNsGlobImports},
     item_tree::{
         self, Attrs, AttrsOrCfg, FieldsShape, ImportAlias, ImportKind, ItemTree, ItemTreeAstId,
-        Macro2, MacroCall, MacroRules, Mod, ModItemId, ModKind, TreeId,
+        Macro2, MacroCall, MacroRules, Mod, ModItemId, ModItemKind, ModKind, TreeId, Use,
     },
     macro_call_as_call_id,
     nameres::{
@@ -157,14 +157,13 @@
 impl Import {
     fn from_use(
         tree: &ItemTree,
-        item: FileAstId<ast::Use>,
+        item: &Use,
         id: UseId,
         is_prelude: bool,
         mut cb: impl FnMut(Self),
     ) {
-        let it = &tree[item];
-        let visibility = &tree[it.visibility];
-        it.expand(|idx, path, kind, alias| {
+        let visibility = &tree[item.visibility];
+        item.expand(|idx, path, kind, alias| {
             cb(Self {
                 path,
                 alias,
@@ -186,7 +185,7 @@
 #[derive(Clone, Debug, Eq, PartialEq)]
 struct MacroDirective<'db> {
     module_id: ModuleId,
-    depth: usize,
+    depth: u32,
     kind: MacroDirectiveKind<'db>,
     container: ItemContainerId,
 }
@@ -221,7 +220,7 @@
     call_id: MacroCallId,
     derive: BuiltinDeriveExpander,
     module_id: ModuleId,
-    depth: usize,
+    depth: u32,
     container: ItemContainerId,
     derive_attr_id: AttrId,
     derive_index: u32,
@@ -261,7 +260,7 @@
     /// This also stores the attributes to skip when we resolve derive helpers and non-macro
     /// non-builtin attributes in general.
     // FIXME: There has to be a better way to do this
-    skip_attrs: FxHashMap<AstId<ast::Item>, AttrId>,
+    skip_attrs: FxHashMap<InFile<ModItemId>, AttrId>,
     /// When we expand attributes, we need to censor all previous active attributes
     /// on the same item. Therefore, this holds all active attributes that we already
     /// expanded.
@@ -280,8 +279,9 @@
         let file_id = self.def_map.krate.root_file_id(self.db);
         let item_tree = file_item_tree(self.db, file_id.into(), self.def_map.krate);
         let attrs = match item_tree.top_level_attrs() {
-            AttrsOrCfg::Enabled { attrs } => attrs.as_ref(),
-            AttrsOrCfg::CfgDisabled(it) => it.1.as_ref(),
+            Some(AttrsOrCfg::Enabled { attrs }) => attrs.as_ref(),
+            None => Attrs::EMPTY,
+            Some(AttrsOrCfg::CfgDisabled(it)) => it.1.as_ref(),
         };
         let crate_data = Arc::get_mut(&mut self.def_map.data).unwrap();
 
@@ -368,7 +368,7 @@
 
         self.inject_prelude();
 
-        if let AttrsOrCfg::CfgDisabled(attrs) = item_tree.top_level_attrs() {
+        if let Some(AttrsOrCfg::CfgDisabled(attrs)) = item_tree.top_level_attrs() {
             let (cfg_expr, _) = &**attrs;
             self.def_map.diagnostics.push(DefDiagnostic::unconfigured_code(
                 self.def_map.root,
@@ -394,14 +394,19 @@
 
     fn seed_with_inner(&mut self, tree_id: TreeId) {
         let item_tree = tree_id.item_tree(self.db, self.def_map.krate);
-        let is_cfg_enabled = matches!(item_tree.top_level_attrs(), AttrsOrCfg::Enabled { .. });
+        let is_cfg_enabled =
+            matches!(item_tree.top_level_attrs(), Some(AttrsOrCfg::Enabled { .. }) | None);
         if is_cfg_enabled {
             self.inject_prelude();
 
             let module_id = self.def_map.root;
+            // A block `DefMap` is collected from scratch, but it may itself live inside a macro
+            // expansion. Continue that chain's budget instead of restarting it, or a macro
+            // expanding to an item that invokes the macro again never reaches the limit.
+            let macro_depth = tree_id.file_id().macro_expansion_depth(self.db);
             ModCollector {
                 def_collector: self,
-                macro_depth: 0,
+                macro_depth,
                 module_id,
                 tree_id,
                 item_tree,
@@ -498,7 +503,7 @@
                         (*attr.path).clone(),
                     ));
 
-                    self.skip_attrs.insert(ast_id.ast_id.with_value(mod_item.ast_id()), *attr_id);
+                    self.skip_attrs.insert(ast_id.ast_id.with_value(*mod_item), *attr_id);
 
                     Some((idx, directive, *mod_item, *tree, *item_tree))
                 }
@@ -1348,6 +1353,7 @@
                         *call_site,
                         *expand_to,
                         self.def_map.krate,
+                        directive.depth,
                         resolver_def_id,
                         &mut |ptr, call_id| {
                             eager_callback_buffer.push((directive.module_id, ptr, call_id));
@@ -1384,6 +1390,7 @@
                         self.def_map.krate,
                         |path| resolver(&self.def_map, path),
                         *derive_macro_id,
+                        directive.depth,
                     );
 
                     if let Ok((macro_id, def_id, call_id)) = id {
@@ -1429,9 +1436,7 @@
                     let mut recollect_without = |collector: &mut Self| {
                         // Remove the original directive since we resolved it.
                         let mod_dir = collector.mod_dirs[&directive.module_id].clone();
-                        collector
-                            .skip_attrs
-                            .insert(InFile::new(file_id, mod_item.ast_id()), *attr_id);
+                        collector.skip_attrs.insert(InFile::new(file_id, *mod_item), *attr_id);
 
                         ModCollector {
                             def_collector: collector,
@@ -1489,6 +1494,7 @@
                             AttrMacroAttrIds::from_many(active_attrs),
                             self.def_map.krate,
                             def,
+                            directive.depth,
                         )
                     };
                     if matches!(def,
@@ -1499,10 +1505,10 @@
                         // normal (as that would just be an identity expansion with extra output)
                         // Instead we treat derive attributes special and apply them separately.
 
-                        let ast_adt_id: FileAstId<ast::Adt> = match *mod_item {
-                            ModItemId::Struct(ast_id) => ast_id.upcast(),
-                            ModItemId::Union(ast_id) => ast_id.upcast(),
-                            ModItemId::Enum(ast_id) => ast_id.upcast(),
+                        let ast_adt_id: FileAstId<ast::Adt> = match item_tree.index(*mod_item) {
+                            ModItemKind::Struct(ast_id, _) => ast_id.upcast(),
+                            ModItemKind::Union(ast_id, _) => ast_id.upcast(),
+                            ModItemKind::Enum(ast_id, _) => ast_id.upcast(),
                             _ => {
                                 let diag = DefDiagnostic::invalid_derive_target(
                                     directive.module_id,
@@ -1541,6 +1547,7 @@
                                         self.def_map.krate,
                                         |path| resolver(&self.def_map, path),
                                         call_id,
+                                        directive.depth,
                                     );
 
                                     let ast_id_without_path = ast_id.ast_id;
@@ -1703,11 +1710,11 @@
         &mut self,
         module_id: ModuleId,
         macro_call_id: MacroCallId,
-        depth: usize,
+        depth: u32,
         container: ItemContainerId,
         attr_macro_item: Option<AstId<ast::Item>>,
     ) {
-        if depth > self.def_map.recursion_limit() as usize {
+        if depth > self.def_map.recursion_limit() {
             cov_mark::hit!(macro_expansion_overflow);
             tracing::warn!("macro expansion is too deep");
             return;
@@ -1738,7 +1745,7 @@
             for item in item_tree.top_level_items() {
                 self.def_map
                     .derive_helpers_in_scope
-                    .entry(InFile::new(file_id, item.ast_id()))
+                    .entry(InFile::new(file_id, item.ast_id(item_tree)))
                     .or_default()
                     .extend(derive_helpers.iter().cloned());
             }
@@ -1776,6 +1783,7 @@
                         *call_site,
                         *expand_to,
                         self.def_map.krate,
+                        directive.depth,
                         |path| {
                             let resolved_res = self.def_map.resolve_path_fp_with_macro(
                                 self.crate_local_def_map.unwrap_or(&self.local_def_map),
@@ -1864,7 +1872,7 @@
 /// Walks a single module, populating defs, imports and macros
 struct ModCollector<'a, 'db> {
     def_collector: &'a mut DefCollector<'db>,
-    macro_depth: usize,
+    macro_depth: u32,
     module_id: ModuleId,
     tree_id: TreeId,
     item_tree: &'db ItemTree,
@@ -1944,18 +1952,20 @@
                 .unwrap_or(Visibility::Public)
         };
 
-        let mut process_mod_item = |item: ModItemId| {
-            let attrs = match self.item_tree.attrs(item.ast_id()) {
+        let item_tree = self.item_tree;
+
+        let mut process_mod_item = |item_id: ModItemId| {
+            let attrs = match self.item_tree.attrs(item_id) {
                 Some(AttrsOrCfg::Enabled { attrs }) => attrs.as_ref(),
                 None => Attrs::EMPTY,
                 Some(AttrsOrCfg::CfgDisabled(cfg)) => {
-                    let ast_id = item.ast_id().erase();
+                    let ast_id = item_id.ast_id(self.item_tree).erase();
                     self.emit_unconfigured_diagnostic(InFile::new(self.file_id(), ast_id), &cfg.0);
                     return;
                 }
             };
 
-            if let Err(()) = self.resolve_attributes(attrs, item, container) {
+            if let Err(()) = self.resolve_attributes(attrs, item_id, container) {
                 // Do not process the item. It has at least one non-builtin attribute, so the
                 // fixed-point algorithm is required to resolve the rest of them.
                 return;
@@ -1965,16 +1975,14 @@
             let local_def_map =
                 self.def_collector.crate_local_def_map.unwrap_or(&self.def_collector.local_def_map);
 
-            match item {
-                ModItemId::Mod(m) => self.collect_module(m, attrs),
-                ModItemId::Use(item_tree_id) => {
-                    let id = UseLoc {
-                        container: module_id,
-                        id: InFile::new(self.file_id(), item_tree_id),
-                    }
-                    .intern(db);
+            match self.item_tree.index(item_id) {
+                ModItemKind::Mod(ast_id, module) => self.collect_module(ast_id, module, attrs),
+                ModItemKind::Use(ast_id, use_) => {
+                    let id =
+                        UseLoc { container: module_id, id: InFile::new(self.file_id(), ast_id) }
+                            .intern(db);
                     let is_prelude = attrs.by_key(sym::prelude_import).exists();
-                    Import::from_use(self.item_tree, item_tree_id, id, is_prelude, |import| {
+                    Import::from_use(self.item_tree, use_, id, is_prelude, |import| {
                         self.def_collector.unresolved_imports.push(ImportDirective {
                             module_id: self.module_id,
                             import,
@@ -1982,13 +1990,12 @@
                         });
                     })
                 }
-                ModItemId::ExternCrate(item_tree_id) => {
-                    let item_tree::ExternCrate { name, visibility, alias } =
-                        &self.item_tree[item_tree_id];
+                ModItemKind::ExternCrate(ast_id, item) => {
+                    let item_tree::ExternCrate { name, visibility, alias } = item;
 
                     let id = ExternCrateLoc {
                         container: module_id,
-                        id: InFile::new(self.tree_id.file_id(), item_tree_id),
+                        id: InFile::new(self.tree_id.file_id(), ast_id),
                     }
                     .intern(db);
                     def_map.modules[self.module_id].scope.define_extern_crate_decl(id);
@@ -2051,40 +2058,42 @@
                         self.def_collector.def_map.diagnostics.push(
                             DefDiagnostic::unresolved_extern_crate(
                                 module_id,
-                                InFile::new(self.file_id(), item_tree_id),
+                                InFile::new(self.file_id(), ast_id),
                             ),
                         );
                     }
                 }
-                ModItemId::ExternBlock(block) => {
+                ModItemKind::ExternBlock(ast_id, block) => {
                     let extern_block_id = ExternBlockLoc {
                         container: module_id,
-                        id: InFile::new(self.file_id(), block),
+                        id: InFile::new(self.file_id(), ast_id),
                     }
                     .intern(db);
                     self.def_collector.def_map.modules[self.module_id]
                         .scope
                         .define_extern_block(extern_block_id);
-                    self.collect(
-                        &self.item_tree[block].children,
-                        ItemContainerId::ExternBlockId(extern_block_id),
-                    )
+                    self.collect(&block.children, ItemContainerId::ExternBlockId(extern_block_id))
                 }
-                ModItemId::MacroCall(mac) => self.collect_macro_call(mac, container),
-                ModItemId::MacroRules(id) => self.collect_macro_rules(id, module_id),
-                ModItemId::Macro2(id) => self.collect_macro_def(id, module_id),
-                ModItemId::Impl(imp) => {
+                ModItemKind::MacroCall(ast_id, mac) => {
+                    self.collect_macro_call(ast_id, mac, container)
+                }
+                ModItemKind::MacroRules(ast_id, mac) => {
+                    self.collect_macro_rules(attrs, ast_id, mac, module_id)
+                }
+                ModItemKind::Macro2(ast_id, mac) => {
+                    self.collect_macro_def(attrs, ast_id, mac, module_id)
+                }
+                ModItemKind::Impl(ast_id, imp) => {
                     let impl_id =
-                        ImplLoc { container: module_id, id: InFile::new(self.file_id(), imp) }
+                        ImplLoc { container: module_id, id: InFile::new(self.file_id(), ast_id) }
                             .intern(db);
                     self.def_collector.def_map.modules[self.module_id]
                         .scope
-                        .define_impl(impl_id, self.item_tree[imp].is_trait_impl)
+                        .define_impl(impl_id, imp.is_trait_impl)
                 }
-                ModItemId::Function(id) => {
-                    let it = &self.item_tree[id];
+                ModItemKind::Function(ast_id, it) => {
                     let fn_id =
-                        FunctionLoc { container, id: InFile::new(self.tree_id.file_id(), id) }
+                        FunctionLoc { container, id: InFile::new(self.tree_id.file_id(), ast_id) }
                             .intern(db);
 
                     let vis = resolve_vis(def_map, local_def_map, &self.item_tree[it.visibility]);
@@ -2098,7 +2107,7 @@
                     {
                         self.def_collector.export_proc_macro(
                             proc_macro,
-                            InFile::new(self.file_id(), id),
+                            InFile::new(self.file_id(), ast_id),
                             fn_id,
                         );
 
@@ -2110,19 +2119,17 @@
                             .remove_from_value_ns(&it.name, fn_id.into());
                     }
                 }
-                ModItemId::Struct(id) => {
-                    let it = &self.item_tree[id];
-
+                ModItemKind::Struct(ast_id, it) => {
                     let vis = resolve_vis(def_map, local_def_map, &self.item_tree[it.visibility]);
                     let interned = StructLoc {
                         container: module_id,
-                        id: InFile::new(self.tree_id.file_id(), id),
+                        id: InFile::new(self.tree_id.file_id(), ast_id),
                     }
                     .intern(db);
                     consider_deferred_derives(
                         self.tree_id.file_id(),
                         &mut self.def_collector.deferred_builtin_derives,
-                        id.upcast(),
+                        ast_id.upcast(),
                         interned.into(),
                         def_map,
                     );
@@ -2134,46 +2141,42 @@
                         !matches!(it.shape, FieldsShape::Record),
                     );
                 }
-                ModItemId::Union(id) => {
-                    let it = &self.item_tree[id];
-
+                ModItemKind::Union(ast_id, it) => {
                     let vis = resolve_vis(def_map, local_def_map, &self.item_tree[it.visibility]);
                     let interned = UnionLoc {
                         container: module_id,
-                        id: InFile::new(self.tree_id.file_id(), id),
+                        id: InFile::new(self.tree_id.file_id(), ast_id),
                     }
                     .intern(db);
                     consider_deferred_derives(
                         self.tree_id.file_id(),
                         &mut self.def_collector.deferred_builtin_derives,
-                        id.upcast(),
+                        ast_id.upcast(),
                         interned.into(),
                         def_map,
                     );
                     update_def(self.def_collector, interned.into(), &it.name, vis, false);
                 }
-                ModItemId::Enum(id) => {
-                    let it = &self.item_tree[id];
+                ModItemKind::Enum(ast_id, it) => {
                     let enum_ = EnumLoc {
                         container: module_id,
-                        id: InFile::new(self.tree_id.file_id(), id),
+                        id: InFile::new(self.tree_id.file_id(), ast_id),
                     }
                     .intern(db);
 
                     consider_deferred_derives(
                         self.tree_id.file_id(),
                         &mut self.def_collector.deferred_builtin_derives,
-                        id.upcast(),
+                        ast_id.upcast(),
                         enum_.into(),
                         def_map,
                     );
                     let vis = resolve_vis(def_map, local_def_map, &self.item_tree[it.visibility]);
                     update_def(self.def_collector, enum_.into(), &it.name, vis, false);
                 }
-                ModItemId::Const(id) => {
-                    let it = &self.item_tree[id];
+                ModItemKind::Const(ast_id, it) => {
                     let const_id =
-                        ConstLoc { container, id: InFile::new(self.tree_id.file_id(), id) }
+                        ConstLoc { container, id: InFile::new(self.tree_id.file_id(), ast_id) }
                             .intern(db);
 
                     match &it.name {
@@ -2190,13 +2193,11 @@
                         }
                     }
                 }
-                ModItemId::Static(id) => {
-                    let it = &self.item_tree[id];
-
+                ModItemKind::Static(ast_id, it) => {
                     let vis = resolve_vis(def_map, local_def_map, &self.item_tree[it.visibility]);
                     update_def(
                         self.def_collector,
-                        StaticLoc { container, id: InFile::new(self.file_id(), id) }
+                        StaticLoc { container, id: InFile::new(self.file_id(), ast_id) }
                             .intern(db)
                             .into(),
                         &it.name,
@@ -2204,13 +2205,11 @@
                         false,
                     );
                 }
-                ModItemId::Trait(id) => {
-                    let it = &self.item_tree[id];
-
+                ModItemKind::Trait(ast_id, it) => {
                     let vis = resolve_vis(def_map, local_def_map, &self.item_tree[it.visibility]);
                     update_def(
                         self.def_collector,
-                        TraitLoc { container: module_id, id: InFile::new(self.file_id(), id) }
+                        TraitLoc { container: module_id, id: InFile::new(self.file_id(), ast_id) }
                             .intern(db)
                             .into(),
                         &it.name,
@@ -2218,13 +2217,11 @@
                         false,
                     );
                 }
-                ModItemId::TypeAlias(id) => {
-                    let it = &self.item_tree[id];
-
+                ModItemKind::TypeAlias(ast_id, it) => {
                     let vis = resolve_vis(def_map, local_def_map, &self.item_tree[it.visibility]);
                     update_def(
                         self.def_collector,
-                        TypeAliasLoc { container, id: InFile::new(self.file_id(), id) }
+                        TypeAliasLoc { container, id: InFile::new(self.file_id(), ast_id) }
                             .intern(db)
                             .into(),
                         &it.name,
@@ -2241,12 +2238,12 @@
         if is_crate_root {
             items
                 .iter()
-                .filter(|it| matches!(it, ModItemId::ExternCrate(..)))
+                .filter(move |&&it| matches!(item_tree.index(it), ModItemKind::ExternCrate(..)))
                 .copied()
                 .for_each(&mut process_mod_item);
             items
                 .iter()
-                .filter(|it| !matches!(it, ModItemId::ExternCrate(..)))
+                .filter(move |&&it| !matches!(item_tree.index(it), ModItemKind::ExternCrate(..)))
                 .copied()
                 .for_each(process_mod_item);
         } else {
@@ -2287,10 +2284,14 @@
         );
     }
 
-    fn collect_module(&mut self, module_ast_id: ItemTreeAstId<Mod>, attrs: Attrs<'_>) {
+    fn collect_module(
+        &mut self,
+        module_ast_id: ItemTreeAstId<Mod>,
+        module: &Mod,
+        attrs: Attrs<'_>,
+    ) {
         let path_attr = attrs.by_key(sym::path).string_value_unescape();
         let is_macro_use = attrs.by_key(sym::macro_use).exists();
-        let module = &self.item_tree[module_ast_id];
         match &module.kind {
             // inline module, just recurse
             ModKind::Inline { items } => {
@@ -2332,14 +2333,15 @@
                     Ok((file_id, is_mod_rs, mod_dir)) => {
                         let item_tree =
                             file_item_tree(db, file_id.into(), self.def_collector.def_map.krate);
-                        match item_tree.top_level_attrs() {
-                            AttrsOrCfg::CfgDisabled(cfg) => {
+                        let top_level_attrs = item_tree.top_level_attrs();
+                        match top_level_attrs {
+                            Some(AttrsOrCfg::CfgDisabled(cfg)) => {
                                 self.emit_unconfigured_diagnostic(
                                     InFile::new(self.file_id(), module_ast_id.erase()),
                                     &cfg.0,
                                 );
                             }
-                            AttrsOrCfg::Enabled { attrs } => {
+                            Some(AttrsOrCfg::Enabled { .. }) | None => {
                                 let module_id = self.push_child_module(
                                     module.name.clone(),
                                     ast_id.value,
@@ -2355,8 +2357,8 @@
                                     mod_dir,
                                 }
                                 .collect_in_top_module(item_tree.top_level_items());
-                                let is_macro_use =
-                                    is_macro_use || attrs.as_ref().by_key(sym::macro_use).exists();
+                                let is_macro_use = is_macro_use
+                                    || matches!(top_level_attrs, Some(AttrsOrCfg::Enabled { attrs }) if attrs.as_ref().by_key(sym::macro_use).exists());
                                 if is_macro_use {
                                     self.import_all_legacy_macros(module_id);
                                 }
@@ -2455,11 +2457,8 @@
         mod_item: ModItemId,
         container: ItemContainerId,
     ) -> Result<(), ()> {
-        let ignore_up_to = self
-            .def_collector
-            .skip_attrs
-            .get(&InFile::new(self.file_id(), mod_item.ast_id()))
-            .copied();
+        let ignore_up_to =
+            self.def_collector.skip_attrs.get(&InFile::new(self.file_id(), mod_item)).copied();
         for (attr_id, attr) in attrs.iter_after(ignore_up_to) {
             if self.def_collector.def_map.is_builtin_or_registered_attr(&attr.path) {
                 continue;
@@ -2469,7 +2468,11 @@
                 attr.path.display(self.def_collector.db, Edition::LATEST)
             );
 
-            let ast_id = AstIdWithPath::new(self.file_id(), mod_item.ast_id(), attr.path.clone());
+            let ast_id = AstIdWithPath::new(
+                self.file_id(),
+                mod_item.ast_id(self.item_tree),
+                attr.path.clone(),
+            );
             self.def_collector.unresolved_macros.push(MacroDirective {
                 module_id: self.module_id,
                 depth: self.macro_depth + 1,
@@ -2490,16 +2493,14 @@
         Ok(())
     }
 
-    fn collect_macro_rules(&mut self, ast_id: ItemTreeAstId<MacroRules>, module: ModuleId) {
+    fn collect_macro_rules(
+        &mut self,
+        attrs: Attrs<'_>,
+        ast_id: ItemTreeAstId<MacroRules>,
+        mac: &MacroRules,
+        module: ModuleId,
+    ) {
         let krate = self.def_collector.def_map.krate;
-        let mac = &self.item_tree[ast_id];
-        let attrs = match self.item_tree.attrs(ast_id.upcast()) {
-            Some(AttrsOrCfg::Enabled { attrs }) => attrs.as_ref(),
-            None => Attrs::EMPTY,
-            Some(AttrsOrCfg::CfgDisabled(_)) => {
-                unreachable!("we only get here if the macro is not cfg'ed out")
-            }
-        };
         let f_ast_id = InFile::new(self.file_id(), ast_id.upcast());
 
         let export_attr = || attrs.by_key(sym::macro_export);
@@ -2574,16 +2575,14 @@
         );
     }
 
-    fn collect_macro_def(&mut self, ast_id: ItemTreeAstId<Macro2>, module: ModuleId) {
+    fn collect_macro_def(
+        &mut self,
+        attrs: Attrs<'_>,
+        ast_id: ItemTreeAstId<Macro2>,
+        mac: &Macro2,
+        module: ModuleId,
+    ) {
         let krate = self.def_collector.def_map.krate;
-        let mac = &self.item_tree[ast_id];
-        let attrs = match self.item_tree.attrs(ast_id.upcast()) {
-            Some(AttrsOrCfg::Enabled { attrs }) => attrs.as_ref(),
-            None => Attrs::EMPTY,
-            Some(AttrsOrCfg::CfgDisabled(_)) => {
-                unreachable!("we only get here if the macro is not cfg'ed out")
-            }
-        };
         let f_ast_id = InFile::new(self.file_id(), ast_id.upcast());
 
         // Case 1: builtin macros
@@ -2659,9 +2658,10 @@
     fn collect_macro_call(
         &mut self,
         ast_id: FileAstId<ast::MacroCall>,
+        macro_call: &MacroCall,
         container: ItemContainerId,
     ) {
-        let &MacroCall { ref path, expand_to, ctxt } = &self.item_tree[ast_id];
+        let &MacroCall { ref path, expand_to, ctxt } = macro_call;
         let ast_id = AstIdWithPath::new(self.file_id(), ast_id, path.clone());
         let db = self.def_collector.db;
 
@@ -2678,6 +2678,7 @@
             ctxt,
             expand_to,
             self.def_collector.def_map.krate,
+            self.macro_depth + 1,
             |path| {
                 path.as_ident().and_then(|name| {
                     let def_map = &self.def_collector.def_map;
diff --git a/crates/hir-def/src/nameres/tests/imports.rs b/crates/hir-def/src/nameres/tests/imports.rs
index b1960b7..9fb685c 100644
--- a/crates/hir-def/src/nameres/tests/imports.rs
+++ b/crates/hir-def/src/nameres/tests/imports.rs
@@ -41,6 +41,26 @@
 }

 

 #[test]

+fn trailing_self_import() {

+    check(

+        r#"

+mod fmt {

+    pub struct Thing;

+}

+pub use fmt::self as alias;

+"#,

+        expect![[r#"

+            crate

+            - alias : type (import)

+            - fmt : type

+

+            crate::fmt

+            - Thing : type value

+        "#]],

+    );

+}

+

+#[test]

 fn primitive_reexport() {

     check(

         r#"

diff --git a/crates/hir-def/src/signatures.rs b/crates/hir-def/src/signatures.rs
index ab5d377..b46d258 100644
--- a/crates/hir-def/src/signatures.rs
+++ b/crates/hir-def/src/signatures.rs
@@ -1152,7 +1152,7 @@
     }
 }
 
-#[salsa::tracked]
+#[salsa::tracked(returns(copy))]
 pub(crate) fn extern_block_abi(db: &dyn SourceDatabase, extern_block: ExternBlockId) -> ExternAbi {
     let source = extern_block.lookup(db).source(db);
     source
diff --git a/crates/hir-def/src/visibility.rs b/crates/hir-def/src/visibility.rs
index 317be34..2a406cf 100644
--- a/crates/hir-def/src/visibility.rs
+++ b/crates/hir-def/src/visibility.rs
@@ -278,6 +278,11 @@
             }
         }
     }
+
+    #[inline]
+    pub fn is_public(&self) -> bool {
+        matches!(self, Visibility::Public)
+    }
 }
 
 #[salsa::tracked]
@@ -324,7 +329,7 @@
 #[salsa::tracked]
 impl AssocItemId {
     /// Resolve visibility of an assoc item.
-    #[salsa::tracked]
+    #[salsa::tracked(returns(copy))]
     pub fn assoc_visibility(self, db: &dyn SourceDatabase) -> Visibility {
         match self {
             AssocItemId::FunctionId(function_id) => {
diff --git a/crates/hir-expand/src/builtin/derive_macro.rs b/crates/hir-expand/src/builtin/derive_macro.rs
index 5e85a71..63e5764 100644
--- a/crates/hir-expand/src/builtin/derive_macro.rs
+++ b/crates/hir-expand/src/builtin/derive_macro.rs
@@ -412,7 +412,7 @@
     })?;
     let span = token_map.span_at(name.syntax().text_range().start());
 
-    let name_token = tt::Ident::new(name.text().as_ref(), span);
+    let name_token = tt::Ident::new(name.text(), span);
     Ok(name_token)
 }
 
@@ -1184,7 +1184,7 @@
                 let new_bounds = bounds.bounds().filter_map(|bound| {
                     let new_bound = substitute_type_bound(
                         bound.clone(),
-                        &pointee_param_name.text(),
+                        pointee_param_name.text(),
                         ADDED_PARAM,
                     );
 
@@ -1197,7 +1197,7 @@
                 let new_bounds_target = if is_pointee {
                     make.name_ref(ADDED_PARAM)
                 } else {
-                    make.name_ref(&param_name.text())
+                    make.name_ref(param_name.text())
                 };
                 new_predicates.push(make.where_pred(
                     Either::Right(
@@ -1240,12 +1240,12 @@
             // If the target type references the pointee, duplicate the bound as whole.
             // Otherwise, duplicate only bounds that mention the pointee.
             if let Some(predicate_with_substituted_target) =
-                substitute_where_pred(&predicate, &pointee_param_name.text(), ADDED_PARAM)
+                substitute_where_pred(&predicate, pointee_param_name.text(), ADDED_PARAM)
             {
                 new_predicates.push(predicate_with_substituted_target);
             } else if let Some(bounds) = predicate.type_bound_list() {
                 let new_bounds = bounds.bounds().filter_map(|bound| {
-                    substitute_type_bound(bound, &pointee_param_name.text(), ADDED_PARAM)
+                    substitute_type_bound(bound, pointee_param_name.text(), ADDED_PARAM)
                 });
                 new_predicates.push(make.where_pred(Either::Right(pred_target), new_bounds));
             }
@@ -1259,7 +1259,7 @@
         new_predicates.push(
             make.where_pred(
                 Either::Right(make.ty_path_from_segments(
-                    [make.path_segment(make.name_ref(&pointee_param_name.text()))],
+                    [make.path_segment(make.name_ref(pointee_param_name.text()))],
                     false,
                 )),
                 [make.type_bound(
@@ -1294,7 +1294,7 @@
             .filter_map(|param| {
                 Some(match param {
                     ast::GenericParam::ConstParam(param) => {
-                        ast::GenericArg::ConstArg(make.expr_const_value(&param.name()?.text()))
+                        ast::GenericArg::ConstArg(make.expr_const_value(param.name()?.text()))
                     }
                     ast::GenericParam::LifetimeParam(param) => {
                         make.lifetime_arg(param.lifetime()?).into()
@@ -1303,7 +1303,7 @@
                         let name = if pointee_param_idx == type_param_idx {
                             make.name_ref(ADDED_PARAM)
                         } else {
-                            make.name_ref(&param.name()?.text())
+                            make.name_ref(param.name()?.text())
                         };
                         type_param_idx += 1;
                         make.type_arg(make.ty_path_from_segments([make.path_segment(name)], false))
@@ -1314,7 +1314,7 @@
 
         make.path_from_segments(
             [make.generic_ty_path_segment(
-                make.name_ref(&struct_name.text()),
+                make.name_ref(struct_name.text()),
                 self_params_for_traits,
             )],
             false,
diff --git a/crates/hir-expand/src/eager.rs b/crates/hir-expand/src/eager.rs
index 22413e7..7002b70 100644
--- a/crates/hir-expand/src/eager.rs
+++ b/crates/hir-expand/src/eager.rs
@@ -44,6 +44,7 @@
     ast_id: AstId<ast::MacroCall>,
     def: MacroDefId,
     call_site: SyntaxContext,
+    macro_depth: u32,
     resolver: &dyn Fn(&ModPath) -> Option<MacroDefId>,
     eager_callback: EagerCallBackFn<'_>,
 ) -> ExpandResult<Option<MacroCallId>> {
@@ -58,6 +59,7 @@
         krate,
         kind: MacroCallKind::FnLike { ast_id, expand_to: ExpandTo::Expr, eager: None },
         ctxt: call_site,
+        macro_depth,
     };
     let arg_id = MacroCallId::new(db, loc);
     #[allow(deprecated)] // builtin eager macros are never derives
@@ -76,6 +78,7 @@
             InFile::new(arg_id.into(), arg_exp.syntax_node()),
             krate,
             call_site,
+            macro_depth,
             resolver,
             eager_callback,
         )
@@ -112,6 +115,7 @@
             })),
         },
         ctxt: call_site,
+        macro_depth,
     };
 
     ExpandResult { value: Some(MacroCallId::new(db, loc)), err }
@@ -124,6 +128,7 @@
     ast_id: AstId<ast::MacroCall>,
     krate: Crate,
     call_site: SyntaxContext,
+    macro_depth: u32,
     eager_callback: EagerCallBackFn<'_>,
 ) -> ExpandResult<(InFile<Parse<SyntaxNode>>, &'db ExpansionSpanMap)> {
     let expand_to = ExpandTo::from_call_site(macro_call);
@@ -132,6 +137,7 @@
         krate,
         MacroCallKind::FnLike { ast_id, expand_to, eager: None },
         call_site,
+        macro_depth,
     );
     eager_callback(ast_id.map(|ast_id| (AstPtr::new(macro_call), ast_id)), id);
 
@@ -148,6 +154,7 @@
     curr: InFile<SyntaxNode>,
     krate: Crate,
     call_site: SyntaxContext,
+    macro_depth: u32,
     macro_resolver: &dyn Fn(&ModPath) -> Option<MacroDefId>,
     eager_callback: EagerCallBackFn<'_>,
 ) -> ExpandResult<Option<(SyntaxNode, TextSize)>> {
@@ -215,6 +222,7 @@
                     curr.with_value(ast_id),
                     def,
                     call_site,
+                    macro_depth + 1,
                     macro_resolver,
                     eager_callback,
                 );
@@ -254,6 +262,7 @@
                     curr.with_value(ast_id),
                     krate,
                     call_site,
+                    macro_depth,
                     eager_callback,
                 );
 
@@ -267,6 +276,7 @@
                     parse.as_ref().map(|it| it.syntax_node()),
                     krate,
                     call_site,
+                    macro_depth + 1,
                     macro_resolver,
                     eager_callback,
                 );
diff --git a/crates/hir-expand/src/lib.rs b/crates/hir-expand/src/lib.rs
index 58ab0e5..f7ac3f1 100644
--- a/crates/hir-expand/src/lib.rs
+++ b/crates/hir-expand/src/lib.rs
@@ -241,6 +241,11 @@
     pub krate: Crate,
     pub kind: MacroCallKind,
     pub ctxt: SyntaxContext,
+    /// The macro recursion depth of this expansion.
+    ///
+    /// Because macro expansions can cross `DefMap` boundaries, we must track this inside the expansion
+    /// to not recurse infinitely.
+    pub macro_depth: u32,
 }
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@@ -960,7 +965,7 @@
 /// parse queries being LRU cached. If they weren't the invalidations would only happen if the
 /// user wrote in the file that defines the proc-macro.
 fn proc_macro_span(db: &dyn SourceDatabase, ast: AstId<ast::Fn>) -> Span {
-    #[salsa::tracked]
+    #[salsa::tracked(returns(copy))]
     fn proc_macro_span(db: &dyn SourceDatabase, ast: AstId<ast::Fn>, _: ()) -> Span {
         let (parse, span_map) = ast.file_id.parse_with_map(db);
         let root = parse.syntax_node();
@@ -1014,8 +1019,9 @@
         krate: Crate,
         kind: MacroCallKind,
         ctxt: SyntaxContext,
+        macro_depth: u32,
     ) -> MacroCallId {
-        MacroCallId::new(db, MacroCallLoc { def: self, krate, kind, ctxt })
+        MacroCallId::new(db, MacroCallLoc { def: self, krate, kind, ctxt, macro_depth })
     }
 
     pub fn definition_range(&self, db: &dyn SourceDatabase) -> InFile<TextRange> {
@@ -1544,7 +1550,7 @@
 ///
 /// We encode macro definitions into ids of macro calls, this what allows us
 /// to be incremental.
-#[salsa::interned(no_lifetime, debug, revisions = usize::MAX)]
+#[salsa::interned(unsafe(no_lifetime), debug, revisions = usize::MAX)]
 #[doc(alias = "MacroFileId")]
 pub struct MacroCallId {
     #[returns(ref)]
@@ -1722,6 +1728,24 @@
             }
         }
     }
+
+    /// How many macro expansions separate this file from the real file it originates from.
+    ///
+    /// `Expander` and `DefCollector` seed their expansion depth counters from this. Both otherwise
+    /// start at zero for every body and every block, so a macro expanding to an item that invokes
+    /// the macro again advances neither counter past one and never reaches the recursion limit.
+    ///
+    /// Written recursively rather than as a loop so that salsa memoizes each file's depth. Callers
+    /// ask for this once per body, and the bodies of one expansion chain sit at depths 1..n, so a
+    /// loop would walk 1 + 2 + ... + n steps for that chain. Reusing the parent's memoized depth
+    /// makes each file O(1) once computed, and the chain linear.
+    #[inline]
+    pub fn macro_expansion_depth(self, db: &dyn SourceDatabase) -> u32 {
+        match self {
+            HirFileId::MacroFile(macro_call) => macro_call.loc(db).macro_depth,
+            HirFileId::FileId(_) => 0,
+        }
+    }
 }
 
 #[salsa::tracked]
diff --git a/crates/hir-expand/src/mod_path.rs b/crates/hir-expand/src/mod_path.rs
index 59578ba..4cc1944 100644
--- a/crates/hir-expand/src/mod_path.rs
+++ b/crates/hir-expand/src/mod_path.rs
@@ -315,6 +315,7 @@
     for segment in segments {
         let name = match segment.kind()? {
             ast::PathSegmentKind::Name(name) => name.as_name(),
+            ast::PathSegmentKind::SelfKw => continue,
             _ => return None,
         };
         mod_path.segments.push(name);
diff --git a/crates/hir-expand/src/name.rs b/crates/hir-expand/src/name.rs
index d91b0f3..7968ada 100644
--- a/crates/hir-expand/src/name.rs
+++ b/crates/hir-expand/src/name.rs
@@ -246,14 +246,14 @@
     fn as_name(&self) -> Name {
         match self.as_tuple_field() {
             Some(idx) => Name::new_tuple_field(idx),
-            None => Name::new_root(&self.text()),
+            None => Name::new_root(self.text()),
         }
     }
 }
 
 impl AsName for ast::Name {
     fn as_name(&self) -> Name {
-        Name::new_root(&self.text())
+        Name::new_root(self.text())
     }
 }
 
diff --git a/crates/hir-ty/src/consteval.rs b/crates/hir-ty/src/consteval.rs
index 15dd530..1e631ca 100644
--- a/crates/hir-ty/src/consteval.rs
+++ b/crates/hir-ty/src/consteval.rs
@@ -16,8 +16,7 @@
 use rustc_apfloat::Float;
 use rustc_ast_ir::Mutability;
 use rustc_type_ir::inherent::{Const as _, GenericArgs as _, IntoKind, Ty as _};
-use salsa::Update;
-use stdx::never;
+use salsa::SalsaValue;
 
 use crate::{
     ParamEnvAndCrate, Span,
@@ -36,7 +35,7 @@
 
 use super::mir::interpret_mir;
 
-#[derive(Debug, Clone, PartialEq, Eq, Update)]
+#[derive(Debug, Clone, PartialEq, Eq, SalsaValue)]
 pub enum ConstEvalError<'db> {
     MirLowerError(MirLowerError<'db>),
     MirEvalError(MirEvalError<'db>),
@@ -81,15 +80,15 @@
     interner: DbInterner<'db>,
     value: &Literal,
     ty: Ty<'db>,
-) -> Result<Const<'db>, CreateConstError<'db>> {
+) -> Option<Result<Const<'db>, CreateConstError<'db>>> {
     let Ok(data_layout) = interner.db.target_data_layout(interner.expect_crate()) else {
-        return Ok(Const::error(interner));
+        return Some(Ok(Const::error(interner)));
     };
     let valtree = match (ty.kind(), value) {
         (TyKind::Uint(uint), Literal::Uint(value, _)) => {
             let size = uint.bit_width().map(Size::from_bits).unwrap_or(data_layout.pointer_size());
             let Some(scalar) = ScalarInt::try_from_uint(*value, size) else {
-                return Ok(Const::error(interner));
+                return Some(Ok(Const::error(interner)));
             };
             ValTreeKind::Leaf(scalar)
         }
@@ -97,14 +96,14 @@
             // `Literal::Int` is the default, so we also need to account for the type being uint.
             let size = uint.bit_width().map(Size::from_bits).unwrap_or(data_layout.pointer_size());
             let Some(scalar) = ScalarInt::try_from_uint(*value as u128, size) else {
-                return Ok(Const::error(interner));
+                return Some(Ok(Const::error(interner)));
             };
             ValTreeKind::Leaf(scalar)
         }
         (TyKind::Int(int), Literal::Int(value, _)) => {
             let size = int.bit_width().map(Size::from_bits).unwrap_or(data_layout.pointer_size());
             let Some(scalar) = ScalarInt::try_from_int(*value, size) else {
-                return Ok(Const::error(interner));
+                return Some(Ok(Const::error(interner)));
             };
             ValTreeKind::Leaf(scalar)
         }
@@ -121,26 +120,21 @@
             let scalar = ScalarInt::try_from_uint(value, size).unwrap();
             ValTreeKind::Leaf(scalar)
         }
-        (_, Literal::String(value)) => {
+        (TyKind::Ref(_, inner_ty, _), Literal::String(value))
+            if matches!(inner_ty.kind(), TyKind::Str) =>
+        {
             let u8_values = &interner.default_types().consts.u8_values;
             ValTreeKind::Branch(Consts::new_from_iter(
                 interner,
                 value.as_str().as_bytes().iter().map(|&byte| u8_values[usize::from(byte)]),
             ))
         }
-        (_, Literal::ByteString(value)) => {
-            let u8_values = &interner.default_types().consts.u8_values;
-            ValTreeKind::Branch(Consts::new_from_iter(
-                interner,
-                value.iter().map(|&byte| u8_values[usize::from(byte)]),
-            ))
-        }
-        (_, Literal::CString(_)) => {
-            // FIXME:
-            return Ok(Const::error(interner));
+        (_, Literal::ByteString(_) | Literal::CString(_)) => {
+            // This literals are complicated to construct and/or are possible to coerce.
+            // So we just allocate an anon const for them, they should be rare so it's not a problem.
+            return None;
         }
         _ => {
-            never!("mismatching type for literal");
             let actual = literal_ty(
                 interner,
                 value,
@@ -148,10 +142,10 @@
                 |types| types.types.u32,
                 |types| types.types.f64,
             );
-            return Err(CreateConstError::TypeMismatch { actual });
+            return Some(Err(CreateConstError::TypeMismatch { actual }));
         }
     };
-    Ok(Const::new_valtree(interner, ty, valtree))
+    Some(Ok(Const::new_valtree(interner, ty, valtree)))
 }
 
 pub(crate) fn literal_ty<'db>(
@@ -361,7 +355,7 @@
     interner: DbInterner<'db>,
     owner: ExpressionStoreOwnerId,
     store: &ExpressionStore,
-    expr: ExprId,
+    expr_id: ExprId,
     resolver: &Resolver<'db>,
     expected_ty: Ty<'db>,
     generics: &dyn Fn() -> &'a Generics<'db>,
@@ -369,10 +363,23 @@
     lowering_mode: LoweringMode,
     forbid_params_after: Option<u32>,
 ) -> Result<Const<'db>, CreateConstError<'db>> {
-    match &store[expr] {
-        Expr::Literal(literal) => intern_const_ref(interner, literal, expected_ty),
+    let mut expr = &store[expr_id];
+    if let Expr::Block { statements, tail: Some(tail), .. } = expr
+        && statements.is_empty()
+    {
+        // rustc unwraps *one* layer of blocks, so we do too (this impacts whether the const can use generic parameters.
+        // Anon consts sometimes cannot while bare paths can). mGCA allows arbitrarily many blocks, but we don't implement
+        // it yet.
+        expr = &store[*tail];
+    }
+    match expr {
+        Expr::Literal(literal)
+            if let Some(literal) = intern_const_ref(interner, literal, expected_ty) =>
+        {
+            literal
+        }
         Expr::Underscore => match create_var {
-            Some(create_var) => Ok(create_var(expr.into())),
+            Some(create_var) => Ok(create_var(expr_id.into())),
             None => Err(CreateConstError::UnderscoreExpr),
         },
         Expr::Path(path)
@@ -395,7 +402,7 @@
                 interner.db,
                 AnonConstLoc {
                     owner,
-                    expr,
+                    expr: expr_id,
                     ty: StoredEarlyBinder::bind(expected_ty.store()),
                     allow_using_generic_params,
                 },
@@ -414,7 +421,7 @@
     }
 }
 
-#[salsa::tracked(cycle_result = const_eval_discriminant_cycle_result)]
+#[salsa::tracked(cycle_result = const_eval_discriminant_cycle_result, returns(clone))]
 pub(crate) fn const_eval_discriminant_variant<'db>(
     db: &'db dyn HirDatabase,
     variant_id: EnumVariantId,
diff --git a/crates/hir-ty/src/db.rs b/crates/hir-ty/src/db.rs
index 8e7e55a..d005d62 100644
--- a/crates/hir-ty/src/db.rs
+++ b/crates/hir-ty/src/db.rs
@@ -10,13 +10,13 @@
     StaticId, TraitId, TypeAliasId, VariantId,
     builtin_derive::BuiltinDeriveImplMethod,
     expr_store::ExpressionStore,
-    hir::{ClosureKind, ExprId, generics::LocalTypeOrConstParamId},
+    hir::{ClosureKind, ExprId},
     layout::TargetDataLayout,
     resolver::{HasResolver, Resolver},
     signatures::{ConstSignature, StaticSignature},
 };
 use la_arena::ArenaMap;
-use salsa::Update;
+use salsa::SalsaValue;
 use span::Edition;
 use stdx::impl_from;
 use triomphe::Arc;
@@ -27,7 +27,10 @@
     consteval::ConstEvalError,
     dyn_compatibility::DynCompatibilityViolation,
     layout::{Layout, LayoutError},
-    lower::{GenericDefaults, TrackedStructToken, TypeAliasBounds},
+    lower::{
+        ConstParamTypes, FieldTypes, GenericDefaults, TrackedStructToken, TypeAliasBounds,
+        WithDefinedOpaques,
+    },
     mir::{MirBody, MirLowerError},
     next_solver::{
         Allocation, Clause, EarlyBinder, GenericArgs, ParamEnv, PolyFnSig, StoredClauses,
@@ -172,7 +175,7 @@
     fn type_for_type_alias_with_diagnostics<'db>(
         &'db self,
         def: TypeAliasId,
-    ) -> &'db TyLoweringResult<'db, StoredEarlyBinder<StoredTy>> {
+    ) -> &'db TyLoweringResult<'db, WithDefinedOpaques<StoredEarlyBinder<StoredTy>>> {
         let db = self.as_dyn();
         crate::lower::type_for_type_alias_with_diagnostics(db, def)
     }
@@ -226,12 +229,12 @@
     fn const_param_types_with_diagnostics<'db>(
         &'db self,
         def: GenericDefId,
-    ) -> &'db TyLoweringResult<'db, ArenaMap<LocalTypeOrConstParamId, StoredTy>> {
+    ) -> &'db TyLoweringResult<'db, ConstParamTypes> {
         let db = self.as_dyn();
         crate::lower::const_param_types_with_diagnostics(db, def)
     }
 
-    fn const_param_types(&self, def: GenericDefId) -> &ArenaMap<LocalTypeOrConstParamId, StoredTy> {
+    fn const_param_types(&self, def: GenericDefId) -> &ConstParamTypes {
         let db = self.as_dyn();
         crate::lower::const_param_types(db, def)
     }
@@ -257,7 +260,7 @@
     fn field_types_with_diagnostics<'db>(
         &'db self,
         var: VariantId,
-    ) -> &'db TyLoweringResult<'db, ArenaMap<LocalFieldId, FieldType>> {
+    ) -> &'db TyLoweringResult<'db, FieldTypes> {
         let db = self.as_dyn();
         crate::lower::field_types_with_diagnostics(db, var)
     }
@@ -275,12 +278,12 @@
         crate::lower::callable_item_signature(db, def)
     }
 
-    fn callable_item_signature_with_diagnostics<'db>(
+    fn fn_sig_for_fn_with_diagnostics<'db>(
         &'db self,
-        def: CallableDefId,
-    ) -> &'db TyLoweringResult<'db, StoredEarlyBinder<StoredPolyFnSig>> {
+        def: FunctionId,
+    ) -> &'db TyLoweringResult<'db, WithDefinedOpaques<StoredEarlyBinder<StoredPolyFnSig>>> {
         let db = self.as_dyn();
-        crate::lower::callable_item_signature_with_diagnostics(db, def)
+        crate::lower::fn_sig_for_fn(db, def)
     }
 
     fn trait_environment<'db>(&'db self, def: GenericDefId) -> ParamEnv<'db> {
@@ -349,10 +352,11 @@
 #[salsa::interned(debug, revisions = usize::MAX)]
 #[derive(PartialOrd, Ord)]
 pub struct InternedOpaqueTyId {
+    #[returns(copy)]
     pub loc: ImplTraitId,
 }
 
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Update)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, SalsaValue)]
 pub struct InternedClosure<'db> {
     pub owner: InferBodyId<'db>,
     pub expr: ExprId,
@@ -362,6 +366,7 @@
 #[salsa::interned(constructor = new_impl, debug, revisions = usize::MAX)]
 #[derive(PartialOrd, Ord)]
 pub struct InternedClosureId<'db> {
+    #[returns(copy)]
     pub loc: InternedClosure<'db>,
 }
 
@@ -390,6 +395,7 @@
 #[salsa::interned(constructor = new_impl, debug, revisions = usize::MAX)]
 #[derive(PartialOrd, Ord)]
 pub struct InternedCoroutineId<'db> {
+    #[returns(copy)]
     pub loc: InternedClosure<'db>,
 }
 
@@ -419,6 +425,7 @@
 #[salsa::interned(constructor = new_impl, debug, revisions = usize::MAX)]
 #[derive(PartialOrd, Ord)]
 pub struct InternedCoroutineClosureId<'db> {
+    #[returns(copy)]
     pub loc: InternedClosure<'db>,
 }
 
@@ -513,8 +520,9 @@
                 result.push(db.type_for_type_alias_with_diagnostics(id).defined_anon_consts());
                 result.push(db.type_alias_bounds_with_diagnostics(id).defined_anon_consts());
             }
-            GenericDefId::FunctionId(id) => result
-                .push(db.callable_item_signature_with_diagnostics(id.into()).defined_anon_consts()),
+            GenericDefId::FunctionId(id) => {
+                result.push(db.fn_sig_for_fn_with_diagnostics(id).defined_anon_consts())
+            }
             GenericDefId::ConstId(def) => {
                 result.push(db.type_for_const_with_diagnostics(def).defined_anon_consts())
             }
diff --git a/crates/hir-ty/src/dyn_compatibility.rs b/crates/hir-ty/src/dyn_compatibility.rs
index bf7970d..4fd6539 100644
--- a/crates/hir-ty/src/dyn_compatibility.rs
+++ b/crates/hir-ty/src/dyn_compatibility.rs
@@ -122,7 +122,7 @@
     ControlFlow::Continue(())
 }
 
-#[salsa::tracked]
+#[salsa::tracked(returns(clone))]
 pub fn dyn_compatibility_of_trait_query(
     db: &dyn HirDatabase,
     trait_: TraitId,
diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs
index a5a8220..1dd6f95 100644
--- a/crates/hir-ty/src/infer.rs
+++ b/crates/hir-ty/src/infer.rs
@@ -65,7 +65,7 @@
     AliasTyKind, TypeFoldable, TypeVisitableExt,
     inherent::{GenericArgs as _, IntoKind, Ty as _},
 };
-use salsa::Update;
+use salsa::SalsaValue;
 use smallvec::SmallVec;
 use span::Edition;
 use stdx::never;
@@ -757,7 +757,7 @@
 /// When you add a field that stores types (including `Substitution` and the like), don't forget
 /// `resolve_completely()`'ing  them in `InferenceContext::resolve_all()`. Inference variables must
 /// not appear in the final inference result.
-#[derive(Clone, PartialEq, Eq, Debug, Update)]
+#[derive(Clone, PartialEq, Eq, Debug, SalsaValue)]
 pub struct InferenceResult<'db> {
     /// For each method call expr, records the function it resolves to.
     method_resolutions: FxHashMap<ExprId, (FunctionId, StoredGenericArgs)>,
@@ -1085,10 +1085,7 @@
     fn for_body(db: &dyn HirDatabase, def: DefWithBodyId) -> InferenceResult<'_> {
         infer_query(db, def)
     }
-}
 
-#[salsa::tracked]
-impl<'db> InferenceResult<'db> {
     /// Infer types for all const expressions in an item's signature.
     ///
     /// Returns an `InferenceResult` containing type information for array lengths,
@@ -1371,9 +1368,6 @@
     breakables: Vec<BreakableContext<'db>>,
     types: &'db crate::next_solver::DefaultAny<'db>,
 
-    /// Whether we are inside the pattern of a destructuring assignment.
-    inside_assignment: bool,
-
     deferred_cast_checks: Vec<CastCheck<'db>>,
 
     /// The key is an expression defining a closure or a coroutine closure.
@@ -1441,7 +1435,7 @@
         lowering_mode: LoweringMode,
     ) -> Self {
         let trait_env = db.trait_environment(generic_def);
-        let table = unify::InferenceTable::new(db, trait_env, resolver.krate(), store_owner);
+        let table = unify::InferenceTable::new(db, trait_env, resolver.krate(), owner);
         let types = crate::next_solver::default_types(db);
         InferenceContext {
             result: InferenceResult::new(types.types.error),
@@ -1468,7 +1462,6 @@
             diverges: Diverges::Maybe,
             breakables: Vec::new(),
             deferred_cast_checks: Vec::new(),
-            inside_assignment: false,
             diagnostics: Diagnostics::default(),
             vars_emitted_type_must_be_known_for: FxHashSet::default(),
             deferred_call_resolutions: FxHashMap::default(),
diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs
index 570df6b..96c48fe 100644
--- a/crates/hir-ty/src/infer/expr.rs
+++ b/crates/hir-ty/src/infer/expr.rs
@@ -220,15 +220,26 @@
         result
     }
 
-    fn is_syntactic_place_expr(&self, expr: ExprId) -> bool {
+    fn is_syntactic_place_expr(&mut self, expr: ExprId) -> bool {
         match &self.store[expr] {
             // Lang item paths cannot currently be local variables or statics.
             Expr::Path(Path::LangItem(_, _)) => false,
             Expr::Path(Path::Normal(path)) => path.type_anchor.is_none(),
-            Expr::Path(path) => self
-                .resolver
-                .resolve_path_in_value_ns_fully(self.db, path, self.store.expr_path_hygiene(expr))
-                .is_none_or(|res| matches!(res, ValueNs::LocalBinding(_) | ValueNs::StaticId(_))),
+            Expr::Path(path) => {
+                let guard = self.resolver.update_to_inner_scope(self.db, self.store_owner, expr);
+                let is_place = self
+                    .resolver
+                    .resolve_path_in_value_ns_fully(
+                        self.db,
+                        path,
+                        self.store.expr_path_hygiene(expr),
+                    )
+                    .is_none_or(|res| {
+                        matches!(res, ValueNs::LocalBinding(_) | ValueNs::StaticId(_))
+                    });
+                self.resolver.reset_to_guard(guard);
+                is_place
+            }
             Expr::Underscore => true,
             Expr::UnaryOp { op: UnaryOp::Deref, .. } => true,
             Expr::Field { .. } | Expr::Index { .. } => true,
@@ -266,7 +277,7 @@
         }
     }
 
-    pub(crate) fn check_lhs_assignable(&self, lhs: ExprId) {
+    pub(crate) fn check_lhs_assignable(&mut self, lhs: ExprId) {
         if self.is_syntactic_place_expr(lhs) {
             return;
         }
@@ -647,13 +658,15 @@
                     self.write_pat_ty(target, lhs_ty);
                     self.infer_expr_coerce(value, &Expectation::has_type(lhs_ty), ExprIsRead::Yes);
                 } else {
-                    let rhs_ty = self.infer_expr(value, &Expectation::none(), ExprIsRead::Yes);
-                    let resolver_guard =
-                        self.resolver.update_to_inner_scope(self.db, self.store_owner, tgt_expr);
-                    self.inside_assignment = true;
+                    // Let the target pattern constrain the RHS coercion target. In particular,
+                    // this allows a diverging RHS to coerce to the pattern's type.
+                    let expected_ty = self.table.next_ty_var(target.into());
+                    let rhs_ty = self.infer_expr_coerce(
+                        value,
+                        &Expectation::has_type(expected_ty),
+                        ExprIsRead::Yes,
+                    );
                     self.infer_top_pat(target, rhs_ty, PatOrigin::DestructuringAssignment);
-                    self.inside_assignment = false;
-                    self.resolver.reset_to_guard(resolver_guard);
                 }
                 if is_destructuring_assignment && self.diverges.is_always() {
                     // Ordinary assignments always return `()`, even when they diverge.
@@ -1887,7 +1900,12 @@
                     )),
                     None => field_with_same_name_exists.and_then(|field_ty| {
                         let callable_sig = field_ty.callable_sig(self.interner())?;
-                        Some((field_ty, callable_sig.skip_binder(), false))
+                        let callable_sig = self.infcx().instantiate_binder_with_fresh_vars(
+                            tgt_expr.into(),
+                            BoundRegionConversionTime::FnCall,
+                            callable_sig,
+                        );
+                        Some((field_ty, callable_sig, false))
                     }),
                 };
                 match recovered {
diff --git a/crates/hir-ty/src/infer/unify.rs b/crates/hir-ty/src/infer/unify.rs
index 6157f51..7b589ef 100644
--- a/crates/hir-ty/src/infer/unify.rs
+++ b/crates/hir-ty/src/infer/unify.rs
@@ -3,7 +3,7 @@
 use std::fmt;
 
 use base_db::Crate;
-use hir_def::{ExpressionStoreOwnerId, GenericParamId, TraitId};
+use hir_def::{GenericParamId, TraitId};
 use rustc_hash::FxHashSet;
 use rustc_type_ir::{
     TyVid, TypeFoldable, TypeVisitableExt,
@@ -14,7 +14,7 @@
 use thin_vec::ThinVec;
 
 use crate::{
-    InferenceDiagnostic, Span,
+    InferBodyId, InferenceDiagnostic, Span,
     db::HirDatabase,
     next_solver::{
         Canonical, ClauseKind, Const, ConstKind, DbInterner, ErrorGuaranteed, GenericArg,
@@ -144,7 +144,7 @@
         db: &'db dyn HirDatabase,
         trait_env: ParamEnv<'db>,
         krate: Crate,
-        owner: ExpressionStoreOwnerId,
+        owner: InferBodyId<'db>,
     ) -> Self {
         let interner = DbInterner::new_with(db, krate);
         let typing_mode = TypingMode::typeck_for_body(interner, owner.into());
diff --git a/crates/hir-ty/src/layout.rs b/crates/hir-ty/src/layout.rs
index db1f7b8..1fbf150 100644
--- a/crates/hir-ty/src/layout.rs
+++ b/crates/hir-ty/src/layout.rs
@@ -160,7 +160,7 @@
     Ok(Arc::new(cx.calc.simd_type(e_ly, e_len, repr_packed)?))
 }
 
-#[salsa::tracked(cycle_result = layout_of_ty_cycle_result)]
+#[salsa::tracked(cycle_result = layout_of_ty_cycle_result, returns(clone))]
 pub fn layout_of_ty_query(
     db: &dyn HirDatabase,
     ty: StoredTy,
diff --git a/crates/hir-ty/src/layout/adt.rs b/crates/hir-ty/src/layout/adt.rs
index 777d080..47a960e 100644
--- a/crates/hir-ty/src/layout/adt.rs
+++ b/crates/hir-ty/src/layout/adt.rs
@@ -19,7 +19,7 @@
     traits::StoredParamEnvAndCrate,
 };
 
-#[salsa::tracked(cycle_result = layout_of_adt_cycle_result)]
+#[salsa::tracked(cycle_result = layout_of_adt_cycle_result, returns(clone))]
 pub fn layout_of_adt_query(
     db: &dyn HirDatabase,
     def: AdtId,
diff --git a/crates/hir-ty/src/layout/tests.rs b/crates/hir-ty/src/layout/tests.rs
index b5db24e..72befc5 100644
--- a/crates/hir-ty/src/layout/tests.rs
+++ b/crates/hir-ty/src/layout/tests.rs
@@ -30,6 +30,7 @@
         QueryConfig::Rustc(&Sysroot::empty(), &std::env::current_dir().unwrap()),
         None,
         &FxHashMap::default(),
+        None,
     )
     .unwrap()
 }
diff --git a/crates/hir-ty/src/lib.rs b/crates/hir-ty/src/lib.rs
index 0dd5588..21e68a6 100644
--- a/crates/hir-ty/src/lib.rs
+++ b/crates/hir-ty/src/lib.rs
@@ -81,7 +81,7 @@
     BoundVarIndexKind, TypeSuperVisitable, TypeVisitableExt,
     inherent::{IntoKind, Ty as _},
 };
-use salsa::Update;
+use salsa::SalsaValue;
 use stdx::impl_from;
 use syntax::ast::{ConstArg, make};
 use traits::FnTrait;
@@ -111,9 +111,9 @@
     infer_query_with_inspect,
 };
 pub use lower::{
-    FieldType, GenericDefaults, GenericDefaultsRef, GenericPredicates, ImplTraits,
-    LifetimeElisionKind, LifetimeLoweringMode, LoweringMode, TyDefId, TyLoweringContext,
-    TyLoweringInferVarsCtx, TyLoweringResult, ValueTyDefId, diagnostics::*,
+    FieldType, GenericDefaults, GenericDefaultsRef, GenericPredicates, LifetimeElisionKind,
+    LifetimeLoweringMode, LoweringMode, TyDefId, TyLoweringContext, TyLoweringInferVarsCtx,
+    TyLoweringResult, ValueTyDefId, diagnostics::*,
 };
 pub use next_solver::interner::{attach_db, attach_db_allow_change, with_attached_db};
 pub use target_feature::TargetFeatures;
@@ -554,7 +554,9 @@
 }
 
 /// A [`DefWithBodyId`], or an anon const.
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, salsa::Supertype, Update)]
+#[derive(
+    Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, salsa::Supertype, SalsaValue,
+)]
 pub enum InferBodyId<'db> {
     DefWithBodyId(DefWithBodyId),
     AnonConstId(AnonConstId<'db>),
diff --git a/crates/hir-ty/src/lower.rs b/crates/hir-ty/src/lower.rs
index bdb882b..7b28689 100644
--- a/crates/hir-ty/src/lower.rs
+++ b/crates/hir-ty/src/lower.rs
@@ -8,9 +8,8 @@
 pub(crate) mod diagnostics;
 pub(crate) mod path;
 
-use std::{cell::OnceCell, iter, mem, sync::OnceLock};
+use std::{cell::OnceCell, iter, mem, ops::Deref, sync::OnceLock};
 
-use base_db::salsa::update_fallback_db;
 use either::Either;
 use hir_def::{
     AdtId, AssocItemId, CallableDefId, ConstId, ConstParamId, EnumId, EnumVariantId,
@@ -51,7 +50,7 @@
     UpcastFrom, elaborate,
     inherent::{Clause as _, GenericArgs as _, IntoKind as _, Region as _, Ty as _},
 };
-use salsa::Update;
+use salsa::SalsaValue;
 use smallvec::SmallVec;
 use stdx::{impl_from, never};
 use thin_vec::ThinVec;
@@ -79,14 +78,15 @@
 
 pub(crate) struct PathDiagnosticCallbackData(pub(crate) TypeRefId);
 
-#[derive(PartialEq, Eq, Debug, Hash)]
-pub struct ImplTraits {
-    pub(crate) impl_traits: Arena<ImplTrait>,
+#[derive(PartialEq, Eq, Debug, Hash, SalsaValue)]
+pub struct WithDefinedOpaques<T> {
+    value: T,
+    impl_traits: Option<Box<Arena<ImplTrait>>>,
 }
 
 #[derive(PartialEq, Eq, Debug, Hash)]
 pub struct ImplTrait {
-    pub(crate) predicates: StoredClauses,
+    pub(crate) predicates: StoredEarlyBinder<StoredClauses>,
     pub(crate) assoc_ty_bounds_start: u32,
 }
 
@@ -417,6 +417,15 @@
 
         BoundVarKinds::new_from_iter(interner, args)
     }
+
+    fn take_defined_opaques(&mut self) -> Option<Box<Arena<ImplTrait>>> {
+        if self.impl_trait_mode.opaque_type_data.is_empty() {
+            None
+        } else {
+            self.impl_trait_mode.opaque_type_data.shrink_to_fit();
+            Some(Box::new(mem::take(&mut self.impl_trait_mode.opaque_type_data)))
+        }
+    }
 }
 
 #[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
@@ -631,7 +640,7 @@
                         // place even if we encounter more opaque types while
                         // lowering the bounds
                         let idx = self.impl_trait_mode.opaque_type_data.alloc(ImplTrait {
-                            predicates: Clauses::empty(interner).store(),
+                            predicates: StoredEarlyBinder::bind(Clauses::empty(interner).store()),
                             assoc_ty_bounds_start: 0,
                         });
 
@@ -1319,7 +1328,7 @@
 
         self.is_lowering_impl_trait_bounds = prev_is_lowering_impl_trait_bounds;
         ImplTrait {
-            predicates: Clauses::new_from_slice(&predicates).store(),
+            predicates: StoredEarlyBinder::bind(Clauses::new_from_slice(&predicates).store()),
             assoc_ty_bounds_start,
         }
     }
@@ -1361,15 +1370,13 @@
     }
 }
 
-#[derive(Clone, PartialEq, Eq, Update)]
+#[derive(Clone, PartialEq, Eq, SalsaValue)]
 pub struct TyLoweringResult<'db, T> {
-    #[update(fallback)]
     pub value: T,
-    #[update(bounds(TyLoweringResultInfo<'db>: Update), unsafe(with(update_fallback_db::<'db, _>)))]
     info: Option<Box<TyLoweringResultInfo<'db>>>,
 }
 
-#[derive(Clone, PartialEq, Eq, Update)]
+#[derive(Clone, PartialEq, Eq, SalsaValue)]
 struct TyLoweringResultInfo<'db> {
     diagnostics: ThinVec<TyLoweringDiagnostic>,
     anon_consts: ThinVec<AnonConstId<'db>>,
@@ -1497,19 +1504,21 @@
 
 impl ImplTraitId {
     #[inline]
-    pub fn predicates<'db>(self, db: &'db dyn HirDatabase) -> EarlyBinder<'db, &'db [Clause<'db>]> {
+    fn data(self, db: &dyn HirDatabase) -> &ImplTrait {
         let (impl_traits, idx) = match self {
             ImplTraitId::ReturnTypeImplTrait(owner, idx) => {
-                (ImplTraits::return_type_impl_traits(db, owner), idx)
+                (ImplTrait::return_type_impl_traits(db, owner), idx)
             }
             ImplTraitId::TypeAliasImplTrait(owner, idx) => {
-                (ImplTraits::type_alias_impl_traits(db, owner), idx)
+                (ImplTrait::type_alias_impl_traits(db, owner), idx)
             }
         };
-        impl_traits
-            .as_deref()
-            .expect("owner should have opaque type")
-            .get_with(|it| it.impl_traits[idx].predicates.as_ref().as_slice())
+        &impl_traits[idx]
+    }
+
+    #[inline]
+    pub fn predicates<'db>(self, db: &'db dyn HirDatabase) -> EarlyBinder<'db, &'db [Clause<'db>]> {
+        self.data(db).predicates.get().map_bound(|it| it.as_slice())
     }
 
     #[inline]
@@ -1517,24 +1526,8 @@
         self,
         db: &'db dyn HirDatabase,
     ) -> EarlyBinder<'db, &'db [Clause<'db>]> {
-        let (impl_traits, idx) = match self {
-            ImplTraitId::ReturnTypeImplTrait(owner, idx) => {
-                (ImplTraits::return_type_impl_traits(db, owner), idx)
-            }
-            ImplTraitId::TypeAliasImplTrait(owner, idx) => {
-                (ImplTraits::type_alias_impl_traits(db, owner), idx)
-            }
-        };
-        let predicates =
-            impl_traits.as_deref().expect("owner should have opaque type").get_with(|it| {
-                let impl_trait = &it.impl_traits[idx];
-                (
-                    impl_trait.predicates.as_ref().as_slice(),
-                    impl_trait.assoc_ty_bounds_start as usize,
-                )
-            });
-
-        predicates.map_bound(|(preds, len)| &preds[..len])
+        let data = self.data(db);
+        data.predicates.get().map_bound(|it| &it.as_slice()[..data.assoc_ty_bounds_start as usize])
     }
 }
 
@@ -1553,71 +1546,25 @@
     }
 }
 
-#[salsa::tracked]
-impl ImplTraits {
-    #[salsa::tracked(returns(ref))]
+impl ImplTrait {
+    #[inline]
     pub(crate) fn return_type_impl_traits(
         db: &dyn HirDatabase,
-        def: hir_def::FunctionId,
-    ) -> Option<Box<StoredEarlyBinder<ImplTraits>>> {
-        // FIXME unify with fn_sig_for_fn instead of doing lowering twice, maybe
-        let data = FunctionSignature::of(db, def);
-        let resolver = def.resolver(db);
-        let generics = OnceCell::new();
-        let mut ctx_ret = TyLoweringContext::new(
-            db,
-            &resolver,
-            &data.store,
-            ExpressionStoreOwnerId::Signature(def.into()),
-            def.into(),
-            &generics,
-            LifetimeElisionKind::Infer,
-            LifetimeLoweringMode::Bound,
-        )
-        .with_impl_trait_mode(ImplTraitLoweringMode::Opaque);
-        if let Some(ret_type) = data.ret_type {
-            let _ret = ctx_ret.lower_ty(ret_type);
-        }
-        let mut return_type_impl_traits =
-            ImplTraits { impl_traits: ctx_ret.impl_trait_mode.opaque_type_data };
-        if return_type_impl_traits.impl_traits.is_empty() {
-            None
-        } else {
-            return_type_impl_traits.impl_traits.shrink_to_fit();
-            Some(Box::new(StoredEarlyBinder::bind(return_type_impl_traits)))
-        }
+        def: FunctionId,
+    ) -> &Arena<ImplTrait> {
+        fn_sig_for_fn(db, def).value.impl_traits.as_deref().unwrap_or(const { &Arena::new() })
     }
 
-    #[salsa::tracked(returns(ref))]
+    #[inline]
     pub(crate) fn type_alias_impl_traits(
         db: &dyn HirDatabase,
-        def: hir_def::TypeAliasId,
-    ) -> Option<Box<StoredEarlyBinder<ImplTraits>>> {
-        let data = TypeAliasSignature::of(db, def);
-        let resolver = def.resolver(db);
-        let generics = OnceCell::new();
-        let mut ctx = TyLoweringContext::new(
-            db,
-            &resolver,
-            &data.store,
-            ExpressionStoreOwnerId::Signature(def.into()),
-            def.into(),
-            &generics,
-            LifetimeElisionKind::AnonymousReportError,
-            LifetimeLoweringMode::Bound,
-        )
-        .with_impl_trait_mode(ImplTraitLoweringMode::Opaque);
-        if let Some(type_ref) = data.ty {
-            let _ty = ctx.lower_ty(type_ref);
-        }
-        let mut type_alias_impl_traits =
-            ImplTraits { impl_traits: ctx.impl_trait_mode.opaque_type_data };
-        if type_alias_impl_traits.impl_traits.is_empty() {
-            None
-        } else {
-            type_alias_impl_traits.impl_traits.shrink_to_fit();
-            Some(Box::new(StoredEarlyBinder::bind(type_alias_impl_traits)))
-        }
+        def: TypeAliasId,
+    ) -> &Arena<ImplTrait> {
+        type_for_type_alias_with_diagnostics(db, def)
+            .value
+            .impl_traits
+            .as_deref()
+            .unwrap_or(const { &Arena::new() })
     }
 }
 
@@ -1666,7 +1613,7 @@
             it,
             GenericArgs::identity_for_item(interner, it.into()),
         )),
-        TyDefId::TypeAliasId(it) => db.type_for_type_alias_with_diagnostics(it).value.get(),
+        TyDefId::TypeAliasId(it) => db.type_for_type_alias_with_diagnostics(it).value.value.get(),
     }
 }
 
@@ -1804,13 +1751,14 @@
 pub(crate) fn type_for_type_alias_with_diagnostics<'db>(
     db: &'db dyn HirDatabase,
     t: TypeAliasId,
-) -> TyLoweringResult<'db, StoredEarlyBinder<StoredTy>> {
+) -> TyLoweringResult<'db, WithDefinedOpaques<StoredEarlyBinder<StoredTy>>> {
     let type_alias_data = TypeAliasSignature::of(db, t);
     let interner = DbInterner::new_no_crate(db);
     if type_alias_data.flags.contains(TypeAliasFlags::IS_EXTERN) {
-        TyLoweringResult::empty(StoredEarlyBinder::bind(
-            Ty::new_foreign(interner, t.into()).store(),
-        ))
+        TyLoweringResult::empty(WithDefinedOpaques {
+            value: StoredEarlyBinder::bind(Ty::new_foreign(interner, t.into()).store()),
+            impl_traits: None,
+        })
     } else {
         let resolver = t.resolver(db);
         let generics = OnceCell::new();
@@ -1832,7 +1780,10 @@
                 .unwrap_or_else(|| Ty::new_error(interner, ErrorGuaranteed))
                 .store(),
         );
-        TyLoweringResult::from_ctx(res, ctx)
+        TyLoweringResult::from_ctx(
+            WithDefinedOpaques { value: res, impl_traits: ctx.take_defined_opaques() },
+            ctx,
+        )
     }
 }
 
@@ -1840,10 +1791,13 @@
     db: &'db dyn HirDatabase,
     _: salsa::Id,
     _adt: TypeAliasId,
-) -> TyLoweringResult<'db, StoredEarlyBinder<StoredTy>> {
-    TyLoweringResult::empty(StoredEarlyBinder::bind(
-        Ty::new_error(DbInterner::new_no_crate(db), ErrorGuaranteed).store(),
-    ))
+) -> TyLoweringResult<'db, WithDefinedOpaques<StoredEarlyBinder<StoredTy>>> {
+    TyLoweringResult::empty(WithDefinedOpaques {
+        value: StoredEarlyBinder::bind(
+            Ty::new_error(DbInterner::new_no_crate(db), ErrorGuaranteed).store(),
+        ),
+        impl_traits: None,
+    })
 }
 
 pub(crate) fn impl_self_ty_query<'db>(
@@ -1894,10 +1848,23 @@
     }
 }
 
-pub(crate) fn const_param_types(
-    db: &dyn HirDatabase,
-    def: GenericDefId,
-) -> &ArenaMap<LocalTypeOrConstParamId, StoredTy> {
+/// Wrapper struct around `ArenaMap` which implements [`SalsaValue`].
+///
+/// Required to make the `SalsaValue` derive for [`TyLoweringResult`] work.
+#[derive(Default, PartialEq, Eq, SalsaValue)]
+pub struct ConstParamTypes {
+    map: ArenaMap<LocalTypeOrConstParamId, StoredTy>,
+}
+
+impl Deref for ConstParamTypes {
+    type Target = ArenaMap<LocalTypeOrConstParamId, StoredTy>;
+
+    fn deref(&self) -> &Self::Target {
+        &self.map
+    }
+}
+
+pub(crate) fn const_param_types(db: &dyn HirDatabase, def: GenericDefId) -> &ConstParamTypes {
     &const_param_types_with_diagnostics(db, def).value
 }
 
@@ -1905,7 +1872,7 @@
 pub(crate) fn const_param_types_with_diagnostics<'db>(
     db: &'db dyn HirDatabase,
     def: GenericDefId,
-) -> TyLoweringResult<'db, ArenaMap<LocalTypeOrConstParamId, StoredTy>> {
+) -> TyLoweringResult<'db, ConstParamTypes> {
     let mut result = ArenaMap::new();
     let (data, store) = GenericParams::with_store(db, def);
     let resolver = def.resolver(db);
@@ -1927,21 +1894,34 @@
         }
     }
     result.shrink_to_fit();
-    TyLoweringResult::from_ctx(result, ctx)
+    TyLoweringResult::from_ctx(ConstParamTypes { map: result }, ctx)
 }
 
 fn const_param_types_with_diagnostics_cycle_result<'db>(
     _db: &'db dyn HirDatabase,
     _: salsa::Id,
     _def: GenericDefId,
-) -> TyLoweringResult<'db, ArenaMap<LocalTypeOrConstParamId, StoredTy>> {
-    TyLoweringResult::empty(ArenaMap::default())
+) -> TyLoweringResult<'db, ConstParamTypes> {
+    TyLoweringResult::empty(ConstParamTypes::default())
 }
 
-pub(crate) fn field_types_query(
-    db: &dyn HirDatabase,
-    variant_id: VariantId,
-) -> &ArenaMap<LocalFieldId, FieldType> {
+/// Wrapper struct around `ArenaMap` which implements [`SalsaValue`].
+///
+/// Required to make the `SalsaValue` derive for [`TyLoweringResult`] work.
+#[derive(Default, PartialEq, Eq, SalsaValue)]
+pub struct FieldTypes {
+    map: ArenaMap<LocalFieldId, FieldType>,
+}
+
+impl Deref for FieldTypes {
+    type Target = ArenaMap<LocalFieldId, FieldType>;
+
+    fn deref(&self) -> &Self::Target {
+        &self.map
+    }
+}
+
+pub(crate) fn field_types_query(db: &dyn HirDatabase, variant_id: VariantId) -> &FieldTypes {
     &field_types_with_diagnostics(db, variant_id).value
 }
 
@@ -1968,11 +1948,11 @@
 pub(crate) fn field_types_with_diagnostics<'db>(
     db: &'db dyn HirDatabase,
     variant_id: VariantId,
-) -> TyLoweringResult<'db, ArenaMap<LocalFieldId, FieldType>> {
+) -> TyLoweringResult<'db, FieldTypes> {
     let var_data = variant_id.fields(db);
     let fields = var_data.fields();
     if fields.is_empty() {
-        return TyLoweringResult::empty(ArenaMap::default());
+        return TyLoweringResult::empty(FieldTypes::default());
     }
 
     let (resolver, generic_def): (_, GenericDefId) = match variant_id {
@@ -2003,7 +1983,7 @@
             },
         );
     }
-    TyLoweringResult::from_ctx(res, ctx)
+    TyLoweringResult::from_ctx(FieldTypes { map: res }, ctx)
 }
 
 #[derive(Debug, PartialEq, Eq, Default)]
@@ -2286,7 +2266,7 @@
     predicates.get().map_bound(|it| &it.as_slice()[..*assoc_ty_bounds_start as usize])
 }
 
-#[derive(PartialEq, Eq, Debug, Hash)]
+#[derive(PartialEq, Eq, Debug, Hash, SalsaValue)]
 pub struct TypeAliasBounds<T> {
     predicates: T,
     assoc_ty_bounds_start: u32,
@@ -2352,7 +2332,7 @@
     )
 }
 
-#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+#[derive(Debug, Clone, PartialEq, Eq, Hash, SalsaValue)]
 pub struct GenericPredicates {
     // The order is the following:
     //
@@ -2717,7 +2697,7 @@
     }
 }
 
-#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+#[derive(Debug, Clone, PartialEq, Eq, Hash, SalsaValue)]
 pub struct GenericDefaults(ThinVec<Option<StoredEarlyBinder<StoredGenericArg>>>);
 
 impl GenericDefaults {
@@ -2822,27 +2802,18 @@
     db: &'db dyn HirDatabase,
     def: CallableDefId,
 ) -> EarlyBinder<'db, PolyFnSig<'db>> {
-    callable_item_signature_with_diagnostics(db, def).value.get()
-}
-
-#[salsa::tracked(returns(ref))]
-pub(crate) fn callable_item_signature_with_diagnostics<'db>(
-    db: &'db dyn HirDatabase,
-    def: CallableDefId,
-) -> TyLoweringResult<'db, StoredEarlyBinder<StoredPolyFnSig>> {
     match def {
-        CallableDefId::FunctionId(f) => fn_sig_for_fn(db, f),
-        CallableDefId::StructId(s) => TyLoweringResult::empty(fn_sig_for_struct_constructor(db, s)),
-        CallableDefId::EnumVariantId(e) => {
-            TyLoweringResult::empty(fn_sig_for_enum_variant_constructor(db, e))
-        }
+        CallableDefId::FunctionId(f) => fn_sig_for_fn(db, f).value.value.get(),
+        CallableDefId::StructId(s) => fn_sig_for_struct_constructor(db, s).get(),
+        CallableDefId::EnumVariantId(e) => fn_sig_for_enum_variant_constructor(db, e).get(),
     }
 }
 
-fn fn_sig_for_fn<'db>(
+#[salsa::tracked(returns(ref))]
+pub(crate) fn fn_sig_for_fn<'db>(
     db: &'db dyn HirDatabase,
     def: FunctionId,
-) -> TyLoweringResult<'db, StoredEarlyBinder<StoredPolyFnSig>> {
+) -> TyLoweringResult<'db, WithDefinedOpaques<StoredEarlyBinder<StoredPolyFnSig>>> {
     let data = FunctionSignature::of(db, def);
     let resolver = def.resolver(db);
     let interner = DbInterner::new_no_crate(db);
@@ -2874,6 +2845,7 @@
         Some(ret_type) => ctx_ret.lower_ty(ret_type),
         None => Ty::new_unit(interner),
     };
+    let impl_traits = ctx_ret.take_defined_opaques();
 
     let inputs_and_output = Tys::new_from_iter(interner, params.chain(Some(ret)));
     ctx_params.diagnostics.extend(ctx_ret.diagnostics);
@@ -2891,7 +2863,7 @@
         },
         binder,
     )));
-    TyLoweringResult::from_ctx(result, ctx_params)
+    TyLoweringResult::from_ctx(WithDefinedOpaques { value: result, impl_traits }, ctx_params)
 }
 
 fn type_for_adt<'db>(db: &'db dyn HirDatabase, adt: AdtId) -> EarlyBinder<'db, Ty<'db>> {
@@ -2901,13 +2873,14 @@
     EarlyBinder::bind(ty)
 }
 
-fn fn_sig_for_struct_constructor(
+fn ctor_signature(
     db: &dyn HirDatabase,
-    def: StructId,
+    variant: VariantId,
+    adt: AdtId,
 ) -> StoredEarlyBinder<StoredPolyFnSig> {
-    let field_tys = db.field_types(def.into());
+    let field_tys = db.field_types(variant);
     let params = field_tys.iter().map(|(_, field)| field.ty().skip_binder());
-    let ret = type_for_adt(db, def.into()).skip_binder();
+    let ret = type_for_adt(db, adt).skip_binder();
 
     let inputs_and_output =
         Tys::new_from_iter(DbInterner::new_no_crate(db), params.chain(Some(ret)));
@@ -2917,21 +2890,20 @@
     })))
 }
 
+#[salsa::tracked(returns(ref))]
+fn fn_sig_for_struct_constructor(
+    db: &dyn HirDatabase,
+    def: StructId,
+) -> StoredEarlyBinder<StoredPolyFnSig> {
+    ctor_signature(db, def.into(), def.into())
+}
+
+#[salsa::tracked(returns(ref))]
 fn fn_sig_for_enum_variant_constructor(
     db: &dyn HirDatabase,
     def: EnumVariantId,
 ) -> StoredEarlyBinder<StoredPolyFnSig> {
-    let field_tys = db.field_types(def.into());
-    let params = field_tys.iter().map(|(_, field)| field.ty().skip_binder());
-    let parent = def.lookup(db).parent;
-    let ret = type_for_adt(db, parent.into()).skip_binder();
-
-    let inputs_and_output =
-        Tys::new_from_iter(DbInterner::new_no_crate(db), params.chain(Some(ret)));
-    StoredEarlyBinder::bind(StoredPolyFnSig::new(Binder::dummy(FnSig {
-        fn_sig_kind: FnSigKind::new(ExternAbi::Rust, Safety::Safe, false),
-        inputs_and_output,
-    })))
+    ctor_signature(db, def.into(), def.lookup(db).parent.into())
 }
 
 // FIXME: Remove this.
diff --git a/crates/hir-ty/src/method_resolution.rs b/crates/hir-ty/src/method_resolution.rs
index 97e9d8b..07ff9ac 100644
--- a/crates/hir-ty/src/method_resolution.rs
+++ b/crates/hir-ty/src/method_resolution.rs
@@ -11,11 +11,11 @@
 
 use either::Either;
 use hir_expand::name::Name;
-use salsa::Update;
+use salsa::SalsaValue;
 use span::Edition;
 use tracing::{debug, instrument};
 
-use base_db::{Crate, salsa::update_fallback_db};
+use base_db::Crate;
 use hir_def::{
     AssocItemId, BlockIdLt, BuiltinDeriveImplId, ConstId, FunctionId, GenericParamId, HasModule,
     ImplId, ItemContainerId, ModuleId, TraitId,
@@ -34,6 +34,7 @@
     TypeVisitableExt, VisitorResult,
     fast_reject::{TreatParams, simplify_type},
     inherent::{BoundExistentialPredicates, IntoKind},
+    try_visit,
 };
 use stdx::impl_from;
 use triomphe::Arc;
@@ -54,7 +55,6 @@
         obligation_ctxt::ObligationCtxt,
         util::clauses_as_obligations,
     },
-    ret,
     traits::ParamEnvAndCrate,
 };
 
@@ -73,7 +73,7 @@
     pub receiver_span: Span,
 }
 
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, salsa::Update)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, salsa::SalsaValue)]
 pub enum CandidateId {
     FunctionId(FunctionId),
     ConstId(ConstId),
@@ -558,9 +558,12 @@
     }
 }
 
-#[derive(Debug, PartialEq, Eq, Update)]
+#[derive(Debug, PartialEq, Eq, SalsaValue)]
 pub struct InherentImpls<'db> {
-    #[update(bounds(SolverDefId<'db>: Update), unsafe(with(update_fallback_db::<'db, _>)))]
+    // SAFETY: necessary due to `SimplifiedType<'db>`.
+    // It's safe to retain, as it only contains `SolverDefId<'db>` (which is `SalsaValue`),
+    // and no `&'db` references.
+    #[salsa_value(unsafe(prove(SolverDefId<'db>: SalsaValue)))]
     map: FxHashMap<SimplifiedType<'db>, Box<[ImplId]>>,
 }
 
@@ -644,9 +647,12 @@
     }
 }
 
-#[derive(Debug, PartialEq, Update)]
+#[derive(Debug, PartialEq, SalsaValue)]
 struct OneTraitImpls<'db> {
-    #[update(bounds(SolverDefId<'db>: Update), unsafe(with(update_fallback_db::<'db, _>)))]
+    // SAFETY: necessary due to `SimplifiedType<'db>`.
+    // It's safe to retain, as it only contains `SolverDefId<'db>` (which is `SalsaValue`),
+    // and no `&'db` references.
+    #[salsa_value(unsafe(prove(SolverDefId<'db>: SalsaValue)))]
     non_blanket_impls: FxHashMap<SimplifiedType<'db>, (Box<[ImplId]>, Box<[BuiltinDeriveImplId]>)>,
     blanket_impls: Box<[ImplId]>,
 }
@@ -672,7 +678,7 @@
     }
 }
 
-#[derive(Debug, PartialEq, Update)]
+#[derive(Debug, PartialEq, SalsaValue)]
 pub struct TraitImpls<'db> {
     map: FxHashMap<TraitId, OneTraitImpls<'db>>,
 }
@@ -844,10 +850,10 @@
     ) -> R {
         let blocks = std::iter::successors(block, |block| block.module(db).block(db));
         for impl_ in blocks.filter_map(|block| Self::for_block(db, block)) {
-            ret!(for_each(impl_));
+            try_visit!(for_each(impl_));
         }
         for impl_ in Self::for_crate_and_deps(db, krate) {
-            ret!(for_each(impl_));
+            try_visit!(for_each(impl_));
         }
         R::output()
     }
@@ -862,7 +868,7 @@
     ) -> R {
         let in_self_and_deps = TraitImpls::for_crate_and_deps(db, krate);
         for impl_ in in_self_and_deps {
-            ret!(for_each(impl_));
+            try_visit!(for_each(impl_));
         }
 
         // We must not provide duplicate impls to the solver. Therefore we work with the following strategy:
@@ -885,14 +891,14 @@
             for impl_ in
                 blocks_iter(trait_block).filter_map(|block| TraitImpls::for_block(db, block))
             {
-                ret!(for_each(impl_));
+                try_visit!(for_each(impl_));
             }
         } else {
             for impl_ in for_each_block(trait_block, type_block) {
-                ret!(for_each(impl_));
+                try_visit!(for_each(impl_));
             }
             for impl_ in for_each_block(type_block, trait_block) {
-                ret!(for_each(impl_));
+                try_visit!(for_each(impl_));
             }
         }
         R::output()
diff --git a/crates/hir-ty/src/mir.rs b/crates/hir-ty/src/mir.rs
index fe4b383..6bfc99b 100644
--- a/crates/hir-ty/src/mir.rs
+++ b/crates/hir-ty/src/mir.rs
@@ -15,7 +15,7 @@
     CollectAndApply, GenericTypeVisitable,
     inherent::{GenericArgs as _, IntoKind, Ty as _},
 };
-use salsa::Update;
+use salsa::SalsaValue;
 use smallvec::{SmallVec, smallvec};
 use stdx::impl_from;
 
@@ -32,13 +32,11 @@
     },
 };
 
-mod borrowck;
 mod eval;
 mod lower;
 mod monomorphization;
 mod pretty;
 
-pub use borrowck::{BorrowckResult, MutabilityReason};
 pub use eval::{
     Evaluator, MirEvalError, VTableMap, interpret_mir, pad16, render_const_using_debug_impl,
 };
@@ -148,7 +146,7 @@
 
 /// The index of a field (whether of a struct/enum variant, tuple, or closure).
 /// For a struct/enum it converts from and to the LocalFieldId, for a tuple or closure it's simply the index.
-#[derive(Copy, Clone, PartialEq, Eq, Hash, salsa::Update, PartialOrd, Ord, Debug)]
+#[derive(Copy, Clone, PartialEq, Eq, Hash, salsa::SalsaValue, PartialOrd, Ord, Debug)]
 pub struct FieldIndex(pub u32);
 
 impl FieldIndex {
@@ -1055,7 +1053,7 @@
     pub is_cleanup: bool,
 }
 
-#[derive(Debug, Clone, PartialEq, Eq, Update)]
+#[derive(Debug, Clone, PartialEq, Eq, SalsaValue)]
 pub struct MirBody<'db> {
     pub basic_blocks: Arena<BasicBlock>,
     pub locals: Arena<Local>,
@@ -1180,7 +1178,7 @@
     }
 }
 
-#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+#[derive(Debug, PartialEq, Eq, Clone, Copy, SalsaValue)]
 pub enum MirSpan {
     ExprId(ExprId),
     PatId(PatId),
diff --git a/crates/hir-ty/src/mir/borrowck.rs b/crates/hir-ty/src/mir/borrowck.rs
deleted file mode 100644
index e860ae8..0000000
--- a/crates/hir-ty/src/mir/borrowck.rs
+++ /dev/null
@@ -1,696 +0,0 @@
-//! MIR borrow checker, which is used in diagnostics like `unused_mut`
-
-// 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::iter;
-
-use either::Either;
-use hir_def::HasModule;
-use la_arena::ArenaMap;
-use rustc_hash::FxHashMap;
-use salsa::Update;
-use stdx::never;
-
-use crate::{
-    InferBodyId,
-    closure_analysis::ProjectionKind as HirProjectionKind,
-    db::{HirDatabase, InternedClosureId},
-    display::DisplayTarget,
-    mir::{OperandKind, PlaceTy},
-    next_solver::{
-        DbInterner, ParamEnv, StoredTy, TypingMode,
-        infer::{DbInternerInferExt, InferCtxt},
-    },
-};
-
-use super::{
-    BasicBlockId, BorrowKind, LocalId, MirBody, MirLowerError, MirSpan, MutBorrowKind, Operand,
-    Place, ProjectionElem, Rvalue, StatementKind, TerminatorKind,
-};
-
-#[derive(Debug, Clone, PartialEq, Eq)]
-/// Stores spans which implies that the local should be mutable.
-pub enum MutabilityReason {
-    Mut { spans: Vec<MirSpan> },
-    Not,
-    Unused,
-}
-
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct MovedOutOfRef {
-    pub ty: StoredTy,
-    pub span: MirSpan,
-}
-
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct PartiallyMoved {
-    pub ty: StoredTy,
-    pub span: MirSpan,
-    pub local: LocalId,
-}
-
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct BorrowRegion {
-    pub local: LocalId,
-    pub kind: BorrowKind,
-    pub places: Vec<MirSpan>,
-}
-
-#[derive(Debug, Clone, PartialEq, Eq, Update)]
-pub struct BorrowckResult<'db> {
-    owner: Either<InferBodyId<'db>, InternedClosureId<'db>>,
-    pub mutability_of_locals: ArenaMap<LocalId, MutabilityReason>,
-    pub moved_out_of_ref: Vec<MovedOutOfRef>,
-    pub partially_moved: Vec<PartiallyMoved>,
-    pub borrow_regions: Vec<BorrowRegion>,
-}
-
-impl<'db> BorrowckResult<'db> {
-    pub fn mir_body(&self, db: &'db dyn HirDatabase) -> &'db MirBody<'db> {
-        match self.owner {
-            Either::Left(it) => db.mir_body(it).unwrap(),
-            Either::Right(it) => db.mir_body_for_closure(it).unwrap(),
-        }
-    }
-}
-
-fn all_mir_bodies<'db>(
-    db: &'db dyn HirDatabase,
-    def: InferBodyId<'db>,
-    mut cb: impl FnMut(
-        &'db MirBody<'db>,
-        Either<InferBodyId<'db>, InternedClosureId<'db>>,
-    ) -> BorrowckResult<'db>,
-    mut merge_from_closures: impl FnMut(
-        (&mut BorrowckResult<'db>, &'db MirBody<'db>),
-        (&BorrowckResult<'db>, &'db MirBody<'db>),
-    ),
-) -> Result<Box<[BorrowckResult<'db>]>, MirLowerError<'db>> {
-    fn for_closure<'db>(
-        db: &'db dyn HirDatabase,
-        c: InternedClosureId<'db>,
-        results: &mut Vec<(BorrowckResult<'db>, &'db MirBody<'db>)>,
-        cb: &mut impl FnMut(
-            &'db MirBody<'db>,
-            Either<InferBodyId<'db>, InternedClosureId<'db>>,
-        ) -> BorrowckResult<'db>,
-        merge_from_closures: &mut impl FnMut(
-            (&mut BorrowckResult<'db>, &'db MirBody<'db>),
-            (&BorrowckResult<'db>, &'db MirBody<'db>),
-        ),
-    ) -> Result<(), MirLowerError<'db>> {
-        match db.mir_body_for_closure(c) {
-            Ok(body) => {
-                let parent_index = results.len();
-                results.push((cb(body, Either::Right(c)), body));
-                body.closures
-                    .iter()
-                    .try_for_each(|&it| for_closure(db, it, results, cb, merge_from_closures))?;
-                merge(results, merge_from_closures, parent_index);
-                Ok(())
-            }
-            Err(e) => Err(e.clone()),
-        }
-    }
-
-    fn merge<'db>(
-        results: &mut [(BorrowckResult<'db>, &'db MirBody<'db>)],
-        merge: &mut impl FnMut(
-            (&mut BorrowckResult<'db>, &'db MirBody<'db>),
-            (&BorrowckResult<'db>, &'db MirBody<'db>),
-        ),
-        parent_index: usize,
-    ) {
-        let (parent_and_before, children) = results.split_at_mut(parent_index + 1);
-        let (parent, parent_mir_body) = &mut parent_and_before[parent_and_before.len() - 1];
-        children.iter().for_each(|(child, child_mir_body)| {
-            merge((parent, parent_mir_body), (child, child_mir_body))
-        });
-    }
-
-    let mut results = Vec::new();
-    match db.mir_body(def) {
-        Ok(body) => {
-            results.push((cb(body, Either::Left(def)), body));
-            body.closures.iter().try_for_each(|&it| {
-                for_closure(db, it, &mut results, &mut cb, &mut merge_from_closures)
-            })?;
-            merge(&mut results, &mut merge_from_closures, 0);
-            Ok(results.into_iter().map(|(it, _)| it).collect())
-        }
-        Err(e) => Err(e.clone()),
-    }
-}
-
-impl<'db> InferBodyId<'db> {
-    pub fn borrowck(
-        self,
-        db: &'db dyn HirDatabase,
-    ) -> Result<&'db [BorrowckResult<'db>], MirLowerError<'db>> {
-        return borrowck_query(db, self).map_err(|e| e.clone());
-
-        #[salsa::tracked(returns(as_deref), lru = 2024)]
-        fn borrowck_query<'db>(
-            db: &'db dyn HirDatabase,
-            def: InferBodyId<'db>,
-        ) -> Result<Box<[BorrowckResult<'db>]>, MirLowerError<'db>> {
-            let _p = tracing::info_span!("InferBodyId::borrowck").entered();
-            let module = def.module(db);
-            let interner = DbInterner::new_with(db, module.krate(db));
-            let env = db.trait_environment(def.generic_def(db));
-            // This calculates opaques defining scope which is a bit costly therefore is put outside `all_mir_bodies()`.
-            let typing_mode = TypingMode::borrowck(interner, def.into());
-            all_mir_bodies(
-                db,
-                def,
-                |body, owner| {
-                    // FIXME(next-solver): Opaques.
-                    let infcx = interner.infer_ctxt().build(typing_mode);
-                    BorrowckResult {
-                        owner,
-                        mutability_of_locals: mutability_of_locals(&infcx, env, body),
-                        moved_out_of_ref: moved_out_of_ref(&infcx, env, body),
-                        partially_moved: partially_moved(&infcx, env, body),
-                        borrow_regions: borrow_regions(db, body),
-                    }
-                },
-                |(parent, parent_mir_body), (child, child_mir_body)| {
-                    for (upvar, child_locals) in &child_mir_body.upvar_locals {
-                        let Some(&parent_local) = parent_mir_body.binding_locals.get(*upvar) else {
-                            continue;
-                        };
-                        for (child_local, capture_place) in child_locals {
-                            if !capture_place
-                                .projections
-                                .iter()
-                                .any(|proj| matches!(proj.kind, HirProjectionKind::Deref))
-                            {
-                                let parent_mol = &mut parent.mutability_of_locals[parent_local];
-                                match (&*parent_mol, &child.mutability_of_locals[*child_local]) {
-                                    (MutabilityReason::Mut { .. }, _) => {}
-                                    (_, MutabilityReason::Mut { .. }) => {
-                                        // FIXME: Fix the child spans.
-                                        *parent_mol = MutabilityReason::Mut { spans: Vec::new() }
-                                    }
-                                    (MutabilityReason::Not, _) => {}
-                                    (_, MutabilityReason::Not) => {
-                                        *parent_mol = MutabilityReason::Not
-                                    }
-                                    (MutabilityReason::Unused, MutabilityReason::Unused) => {}
-                                }
-                            }
-                        }
-                    }
-                },
-            )
-        }
-    }
-}
-
-fn moved_out_of_ref<'db>(
-    infcx: &InferCtxt<'db>,
-    env: ParamEnv<'db>,
-    body: &MirBody<'db>,
-) -> Vec<MovedOutOfRef> {
-    let db = infcx.interner.db;
-    let mut result = vec![];
-    let mut for_operand = |op: &Operand, span: MirSpan| match &op.kind {
-        OperandKind::Copy(p) | OperandKind::Move(p) => {
-            let mut ty = PlaceTy::from_ty(body.locals[p.local].ty.as_ref());
-            let mut is_dereference_of_ref = false;
-            for proj in p.projection.lookup() {
-                if *proj == ProjectionElem::Deref && ty.ty.as_reference().is_some() {
-                    is_dereference_of_ref = true;
-                }
-                ty = ty.projection_ty(infcx, proj, env);
-            }
-            if is_dereference_of_ref
-                && !infcx.type_is_copy_modulo_regions(env, ty.ty)
-                && !ty.ty.references_non_lt_error()
-            {
-                result.push(MovedOutOfRef { span: op.span.unwrap_or(span), ty: ty.ty.store() });
-            }
-        }
-        OperandKind::Constant { .. } | OperandKind::Static(_) | OperandKind::Allocation { .. } => {}
-    };
-    for (_, block) in body.basic_blocks.iter() {
-        db.unwind_if_revision_cancelled();
-        for statement in &block.statements {
-            match &statement.kind {
-                StatementKind::Assign(_, r) => match r {
-                    Rvalue::UnaryOp(_, o)
-                    | Rvalue::Cast(_, o, _)
-                    | Rvalue::Repeat(o, _)
-                    | Rvalue::Use(o) => for_operand(o, statement.span),
-                    Rvalue::CopyForDeref(_)
-                    | Rvalue::Discriminant(_)
-                    | Rvalue::Len(_)
-                    | Rvalue::Ref(_, _) => (),
-                    Rvalue::CheckedBinaryOp(_, o1, o2) => {
-                        for_operand(o1, statement.span);
-                        for_operand(o2, statement.span);
-                    }
-                    Rvalue::Aggregate(_, ops) => {
-                        for op in ops.iter() {
-                            for_operand(op, statement.span);
-                        }
-                    }
-                    Rvalue::ThreadLocalRef(n)
-                    | Rvalue::AddressOf(n)
-                    | Rvalue::BinaryOp(n)
-                    | Rvalue::NullaryOp(n) => match *n {},
-                },
-                StatementKind::FakeRead(_)
-                | StatementKind::Deinit(_)
-                | StatementKind::StorageLive(_)
-                | StatementKind::StorageDead(_)
-                | StatementKind::Nop => (),
-            }
-        }
-        match &block.terminator {
-            Some(terminator) => match &terminator.kind {
-                TerminatorKind::SwitchInt { discr, .. } => for_operand(discr, terminator.span),
-                TerminatorKind::FalseEdge { .. }
-                | TerminatorKind::FalseUnwind { .. }
-                | TerminatorKind::Goto { .. }
-                | TerminatorKind::UnwindResume
-                | TerminatorKind::CoroutineDrop
-                | TerminatorKind::Abort
-                | TerminatorKind::Return
-                | TerminatorKind::Unreachable
-                | TerminatorKind::Drop { .. } => (),
-                TerminatorKind::DropAndReplace { value, .. } => {
-                    for_operand(value, terminator.span);
-                }
-                TerminatorKind::Call { func, args, .. } => {
-                    for_operand(func, terminator.span);
-                    args.iter().for_each(|it| for_operand(it, terminator.span));
-                }
-                TerminatorKind::Assert { cond, .. } => {
-                    for_operand(cond, terminator.span);
-                }
-                TerminatorKind::Yield { value, .. } => {
-                    for_operand(value, terminator.span);
-                }
-            },
-            None => (),
-        }
-    }
-    result.shrink_to_fit();
-    result
-}
-
-fn partially_moved<'db>(
-    infcx: &InferCtxt<'db>,
-    env: ParamEnv<'db>,
-    body: &MirBody<'db>,
-) -> Vec<PartiallyMoved> {
-    let db = infcx.interner.db;
-    let mut result = vec![];
-    let mut for_operand = |op: &Operand, span: MirSpan| match &op.kind {
-        OperandKind::Copy(p) | OperandKind::Move(p) => {
-            let ty = p.as_ref().ty(body, infcx, env).ty;
-            if !infcx.type_is_copy_modulo_regions(env, ty) && !ty.references_non_lt_error() {
-                result.push(PartiallyMoved { span, ty: ty.store(), local: p.local });
-            }
-        }
-        OperandKind::Constant { .. } | OperandKind::Static(_) | OperandKind::Allocation { .. } => {}
-    };
-    for (_, block) in body.basic_blocks.iter() {
-        db.unwind_if_revision_cancelled();
-        for statement in &block.statements {
-            match &statement.kind {
-                StatementKind::Assign(_, r) => match r {
-                    Rvalue::UnaryOp(_, o)
-                    | Rvalue::Cast(_, o, _)
-                    | Rvalue::Repeat(o, _)
-                    | Rvalue::Use(o) => for_operand(o, statement.span),
-                    Rvalue::CopyForDeref(_)
-                    | Rvalue::Discriminant(_)
-                    | Rvalue::Len(_)
-                    | Rvalue::Ref(_, _) => (),
-                    Rvalue::CheckedBinaryOp(_, o1, o2) => {
-                        for_operand(o1, statement.span);
-                        for_operand(o2, statement.span);
-                    }
-                    Rvalue::Aggregate(_, ops) => {
-                        for op in ops.iter() {
-                            for_operand(op, statement.span);
-                        }
-                    }
-                    Rvalue::ThreadLocalRef(n)
-                    | Rvalue::AddressOf(n)
-                    | Rvalue::BinaryOp(n)
-                    | Rvalue::NullaryOp(n) => match *n {},
-                },
-                StatementKind::FakeRead(_)
-                | StatementKind::Deinit(_)
-                | StatementKind::StorageLive(_)
-                | StatementKind::StorageDead(_)
-                | StatementKind::Nop => (),
-            }
-        }
-        match &block.terminator {
-            Some(terminator) => match &terminator.kind {
-                TerminatorKind::SwitchInt { discr, .. } => for_operand(discr, terminator.span),
-                TerminatorKind::FalseEdge { .. }
-                | TerminatorKind::FalseUnwind { .. }
-                | TerminatorKind::Goto { .. }
-                | TerminatorKind::UnwindResume
-                | TerminatorKind::CoroutineDrop
-                | TerminatorKind::Abort
-                | TerminatorKind::Return
-                | TerminatorKind::Unreachable
-                | TerminatorKind::Drop { .. } => (),
-                TerminatorKind::DropAndReplace { value, .. } => {
-                    for_operand(value, terminator.span);
-                }
-                TerminatorKind::Call { func, args, .. } => {
-                    for_operand(func, terminator.span);
-                    args.iter().for_each(|it| for_operand(it, terminator.span));
-                }
-                TerminatorKind::Assert { cond, .. } => {
-                    for_operand(cond, terminator.span);
-                }
-                TerminatorKind::Yield { value, .. } => {
-                    for_operand(value, terminator.span);
-                }
-            },
-            None => (),
-        }
-    }
-    result.shrink_to_fit();
-    result
-}
-
-fn borrow_regions<'db>(db: &'db dyn HirDatabase, body: &MirBody<'db>) -> Vec<BorrowRegion> {
-    let mut borrows = FxHashMap::default();
-    for (_, block) in body.basic_blocks.iter() {
-        db.unwind_if_revision_cancelled();
-        for statement in &block.statements {
-            if let StatementKind::Assign(_, Rvalue::Ref(kind, p)) = &statement.kind {
-                borrows
-                    .entry(p.local)
-                    .and_modify(|it: &mut BorrowRegion| {
-                        it.places.push(statement.span);
-                    })
-                    .or_insert_with(|| BorrowRegion {
-                        local: p.local,
-                        kind: *kind,
-                        places: vec![statement.span],
-                    });
-            }
-        }
-        match &block.terminator {
-            Some(terminator) => match &terminator.kind {
-                TerminatorKind::FalseEdge { .. }
-                | TerminatorKind::FalseUnwind { .. }
-                | TerminatorKind::Goto { .. }
-                | TerminatorKind::UnwindResume
-                | TerminatorKind::CoroutineDrop
-                | TerminatorKind::Abort
-                | TerminatorKind::Return
-                | TerminatorKind::Unreachable
-                | TerminatorKind::Drop { .. } => (),
-                TerminatorKind::DropAndReplace { .. } => {}
-                TerminatorKind::Call { .. } => {}
-                _ => (),
-            },
-            None => (),
-        }
-    }
-
-    borrows.into_values().collect()
-}
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-enum ProjectionCase {
-    /// Projection is a local
-    Direct,
-    /// Projection is some field or slice of a local
-    DirectPart,
-    /// Projection is deref of something
-    Indirect,
-}
-
-fn place_case<'db>(
-    infcx: &InferCtxt<'db>,
-    env: ParamEnv<'db>,
-    body: &MirBody<'db>,
-    lvalue: &Place,
-) -> ProjectionCase {
-    let mut is_part_of = false;
-    let mut ty = PlaceTy::from_ty(body.locals[lvalue.local].ty.as_ref());
-    for proj in lvalue.projection.lookup().iter() {
-        match proj {
-            ProjectionElem::Deref if ty.ty.as_adt().is_none() => return ProjectionCase::Indirect, // It's indirect in case of reference and raw
-            ProjectionElem::Deref // It's direct in case of `Box<T>`
-            | ProjectionElem::ConstantIndex { .. }
-            | ProjectionElem::Subslice { .. }
-            | ProjectionElem::Field(_)
-            | ProjectionElem::Index(_) => {
-                is_part_of = true;
-            }
-            ProjectionElem::Downcast(_) => (),
-        }
-        ty = ty.projection_ty(infcx, proj, env);
-    }
-    if is_part_of { ProjectionCase::DirectPart } else { ProjectionCase::Direct }
-}
-
-/// Returns a map from basic blocks to the set of locals that might be ever initialized before
-/// the start of the block. Only `StorageDead` can remove something from this map, and we ignore
-/// `Uninit` and `drop` and similar after initialization.
-fn ever_initialized_map(
-    db: &dyn HirDatabase,
-    body: &MirBody<'_>,
-) -> ArenaMap<BasicBlockId, ArenaMap<LocalId, bool>> {
-    let mut result: ArenaMap<BasicBlockId, ArenaMap<LocalId, bool>> =
-        body.basic_blocks.iter().map(|it| (it.0, ArenaMap::default())).collect();
-    fn dfs(
-        db: &dyn HirDatabase,
-        body: &MirBody<'_>,
-        l: LocalId,
-        stack: &mut Vec<BasicBlockId>,
-        result: &mut ArenaMap<BasicBlockId, ArenaMap<LocalId, bool>>,
-    ) {
-        while let Some(b) = stack.pop() {
-            let mut is_ever_initialized = result[b][l]; // It must be filled, as we use it as mark for dfs
-            let block = &body.basic_blocks[b];
-            for statement in &block.statements {
-                match &statement.kind {
-                    StatementKind::Assign(p, _) => {
-                        if p.projection.is_empty() && p.local == l {
-                            is_ever_initialized = true;
-                        }
-                    }
-                    StatementKind::StorageDead(p) => {
-                        if *p == l {
-                            is_ever_initialized = false;
-                        }
-                    }
-                    StatementKind::Deinit(_)
-                    | StatementKind::FakeRead(_)
-                    | StatementKind::Nop
-                    | StatementKind::StorageLive(_) => (),
-                }
-            }
-            let Some(terminator) = &block.terminator else {
-                never!(
-                    "Terminator should be none only in construction.\nThe body:\n{}",
-                    body.pretty_print(db, DisplayTarget::from_crate(db, body.owner.krate(db)))
-                );
-                return;
-            };
-            let mut process = |target, is_ever_initialized| {
-                if !result[target].contains_idx(l) || !result[target][l] && is_ever_initialized {
-                    result[target].insert(l, is_ever_initialized);
-                    stack.push(target);
-                }
-            };
-            match &terminator.kind {
-                TerminatorKind::Goto { target } => process(*target, is_ever_initialized),
-                TerminatorKind::SwitchInt { targets, .. } => {
-                    targets.all_targets().iter().for_each(|&it| process(it, is_ever_initialized));
-                }
-                TerminatorKind::UnwindResume
-                | TerminatorKind::Abort
-                | TerminatorKind::Return
-                | TerminatorKind::Unreachable => (),
-                TerminatorKind::Call { target, cleanup, destination, .. } => {
-                    if destination.projection.is_empty() && destination.local == l {
-                        is_ever_initialized = true;
-                    }
-                    target.iter().chain(cleanup).for_each(|&it| process(it, is_ever_initialized));
-                }
-                TerminatorKind::Drop { target, unwind, place: _ } => {
-                    iter::once(target)
-                        .chain(unwind)
-                        .for_each(|&it| process(it, is_ever_initialized));
-                }
-                TerminatorKind::DropAndReplace { .. }
-                | TerminatorKind::Assert { .. }
-                | TerminatorKind::Yield { .. }
-                | TerminatorKind::CoroutineDrop
-                | TerminatorKind::FalseEdge { .. }
-                | TerminatorKind::FalseUnwind { .. } => {
-                    never!("We don't emit these MIR terminators yet");
-                }
-            }
-        }
-    }
-    let mut stack = Vec::new();
-    for &l in &body.param_locals {
-        result[body.start_block].insert(l, true);
-        stack.clear();
-        stack.push(body.start_block);
-        dfs(db, body, l, &mut stack, &mut result);
-    }
-    for l in body.locals.iter().map(|it| it.0) {
-        db.unwind_if_revision_cancelled();
-        if !result[body.start_block].contains_idx(l) {
-            result[body.start_block].insert(l, false);
-            stack.clear();
-            stack.push(body.start_block);
-            dfs(db, body, l, &mut stack, &mut result);
-        }
-    }
-    result
-}
-
-fn push_mut_span(local: LocalId, span: MirSpan, result: &mut ArenaMap<LocalId, MutabilityReason>) {
-    match &mut result[local] {
-        MutabilityReason::Mut { spans } => spans.push(span),
-        it @ (MutabilityReason::Not | MutabilityReason::Unused) => {
-            *it = MutabilityReason::Mut { spans: vec![span] }
-        }
-    };
-}
-
-fn record_usage(local: LocalId, result: &mut ArenaMap<LocalId, MutabilityReason>) {
-    if let it @ MutabilityReason::Unused = &mut result[local] {
-        *it = MutabilityReason::Not;
-    };
-}
-
-fn record_usage_for_operand(arg: &Operand, result: &mut ArenaMap<LocalId, MutabilityReason>) {
-    if let OperandKind::Copy(p) | OperandKind::Move(p) = &arg.kind {
-        record_usage(p.local, result);
-    }
-}
-
-fn mutability_of_locals<'db>(
-    infcx: &InferCtxt<'db>,
-    env: ParamEnv<'db>,
-    body: &MirBody<'db>,
-) -> ArenaMap<LocalId, MutabilityReason> {
-    let db = infcx.interner.db;
-    let mut result: ArenaMap<LocalId, MutabilityReason> =
-        body.locals.iter().map(|it| (it.0, MutabilityReason::Unused)).collect();
-
-    let ever_init_maps = ever_initialized_map(db, body);
-    for (block_id, mut ever_init_map) in ever_init_maps.into_iter() {
-        let block = &body.basic_blocks[block_id];
-        for statement in &block.statements {
-            match &statement.kind {
-                StatementKind::Assign(place, value) => {
-                    match place_case(infcx, env, body, place) {
-                        ProjectionCase::Direct => {
-                            if ever_init_map.get(place.local).copied().unwrap_or_default() {
-                                push_mut_span(place.local, statement.span, &mut result);
-                            } else {
-                                ever_init_map.insert(place.local, true);
-                            }
-                        }
-                        ProjectionCase::DirectPart => {
-                            // Partial initialization is not supported, so it is definitely `mut`
-                            push_mut_span(place.local, statement.span, &mut result);
-                        }
-                        ProjectionCase::Indirect => {
-                            record_usage(place.local, &mut result);
-                        }
-                    }
-                    match value {
-                        Rvalue::CopyForDeref(p)
-                        | Rvalue::Discriminant(p)
-                        | Rvalue::Len(p)
-                        | Rvalue::Ref(_, p) => {
-                            record_usage(p.local, &mut result);
-                        }
-                        Rvalue::Use(o)
-                        | Rvalue::Repeat(o, _)
-                        | Rvalue::Cast(_, o, _)
-                        | Rvalue::UnaryOp(_, o) => record_usage_for_operand(o, &mut result),
-                        Rvalue::CheckedBinaryOp(_, o1, o2) => {
-                            for o in [o1, o2] {
-                                record_usage_for_operand(o, &mut result);
-                            }
-                        }
-                        Rvalue::Aggregate(_, args) => {
-                            for arg in args.iter() {
-                                record_usage_for_operand(arg, &mut result);
-                            }
-                        }
-                        Rvalue::ThreadLocalRef(n)
-                        | Rvalue::AddressOf(n)
-                        | Rvalue::BinaryOp(n)
-                        | Rvalue::NullaryOp(n) => match *n {},
-                    }
-                    if let Rvalue::Ref(
-                        BorrowKind::Mut {
-                            kind: MutBorrowKind::Default | MutBorrowKind::TwoPhasedBorrow,
-                        },
-                        p,
-                    ) = value
-                        && place_case(infcx, env, body, p) != ProjectionCase::Indirect
-                    {
-                        push_mut_span(p.local, statement.span, &mut result);
-                    }
-                }
-                StatementKind::FakeRead(p) => {
-                    record_usage(p.local, &mut result);
-                }
-                StatementKind::StorageDead(p) => {
-                    ever_init_map.insert(*p, false);
-                }
-                StatementKind::Deinit(_) | StatementKind::StorageLive(_) | StatementKind::Nop => (),
-            }
-        }
-        let Some(terminator) = &block.terminator else {
-            never!("Terminator should be none only in construction");
-            continue;
-        };
-        match &terminator.kind {
-            TerminatorKind::Goto { .. }
-            | TerminatorKind::UnwindResume
-            | TerminatorKind::Abort
-            | TerminatorKind::Return
-            | TerminatorKind::Unreachable
-            | TerminatorKind::FalseEdge { .. }
-            | TerminatorKind::FalseUnwind { .. }
-            | TerminatorKind::CoroutineDrop
-            | TerminatorKind::Drop { .. }
-            | TerminatorKind::DropAndReplace { .. }
-            | TerminatorKind::Assert { .. }
-            | TerminatorKind::Yield { .. } => (),
-            TerminatorKind::SwitchInt { discr, targets: _ } => {
-                record_usage_for_operand(discr, &mut result);
-            }
-            TerminatorKind::Call { destination, args, func, .. } => {
-                record_usage_for_operand(func, &mut result);
-                for arg in args.iter() {
-                    record_usage_for_operand(arg, &mut result);
-                }
-                if destination.projection.is_empty() {
-                    if ever_init_map.get(destination.local).copied().unwrap_or_default() {
-                        push_mut_span(destination.local, terminator.span, &mut result);
-                    } else {
-                        ever_init_map.insert(destination.local, true);
-                    }
-                }
-            }
-        }
-    }
-    result
-}
diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs
index e968da5..f849571 100644
--- a/crates/hir-ty/src/mir/eval.rs
+++ b/crates/hir-ty/src/mir/eval.rs
@@ -2,7 +2,7 @@
 
 use std::{borrow::Cow, cell::RefCell, fmt::Write, iter, mem, ops::Range};
 
-use base_db::{Crate, salsa::update_fallback_db, target::TargetLoadError};
+use base_db::{Crate, target::TargetLoadError};
 use either::Either;
 use hir_def::{
     AdtId, DefWithBodyId, EnumVariantId, FunctionId, HasModule, ItemContainerId, Lookup, StaticId,
@@ -31,7 +31,7 @@
     AliasTyKind,
     inherent::{GenericArgs as _, IntoKind, Region as _, SliceLike, Ty as _},
 };
-use salsa::Update;
+use salsa::SalsaValue;
 use span::FileId;
 use stdx::never;
 use syntax::{SyntaxNodePtr, TextRange};
@@ -349,7 +349,7 @@
     }
 }
 
-#[derive(Clone, PartialEq, Eq, Update)]
+#[derive(Clone, PartialEq, Eq, SalsaValue)]
 pub enum MirEvalError<'db> {
     ConstEvalError(String, Box<ConstEvalError<'db>>),
     LayoutError(LayoutError, StoredTy),
@@ -366,8 +366,7 @@
     InvalidConst,
     InFunction(
         Box<MirEvalError<'db>>,
-        #[update(bounds(InternedClosureId<'db>: Update), unsafe(with(update_fallback_db::<'db, _>)))]
-         Vec<(Either<FunctionId, InternedClosureId<'db>>, MirSpan, InferBodyId<'db>)>,
+        Vec<(Either<FunctionId, InternedClosureId<'db>>, MirSpan, InferBodyId<'db>)>,
     ),
     ExecutionLimitExceeded,
     StackOverflow,
@@ -2548,7 +2547,6 @@
         ty: Ty<'db>,
         locals: &Locals<'a, 'db>,
     ) -> Result<'db, ()> {
-        // FIXME: support indirect references
         let layout = self.layout(ty)?;
         let my_size = self.size_of_sized(ty, locals, "value to patch address")?;
         use rustc_type_ir::TyKind;
@@ -2574,9 +2572,30 @@
                         )?;
                     }
                     None => {
-                        let current = from_bytes!(usize, self.read_memory(addr, my_size / 2)?);
-                        if let Some(it) = patch_map.get(&current) {
-                            self.write_memory(addr, &it.to_le_bytes())?;
+                        let bytes = self.read_memory(addr, my_size)?;
+                        let (current, metadata) = bytes.split_at(my_size / 2);
+                        let metadata = metadata.to_vec();
+                        let current = from_bytes!(usize, current);
+                        let patched = match patch_map.get(&current) {
+                            Some(it) => {
+                                self.write_memory(addr, &it.to_le_bytes())?;
+                                *it
+                            }
+                            None => current,
+                        };
+                        let patched = Address::from_usize(patched);
+                        if let TyKind::Slice(inner) = t.kind() {
+                            let len = from_bytes!(usize, metadata);
+                            let size = self.size_of_sized(inner, locals, "slice item to patch")?;
+                            for i in 0..len {
+                                self.patch_addresses(
+                                    patch_map,
+                                    ty_of_bytes,
+                                    patched.offset(i * size),
+                                    inner,
+                                    locals,
+                                )?;
+                            }
                         }
                     }
                 }
diff --git a/crates/hir-ty/src/mir/eval/shim.rs b/crates/hir-ty/src/mir/eval/shim.rs
index e569b32..177b2c8 100644
--- a/crates/hir-ty/src/mir/eval/shim.rs
+++ b/crates/hir-ty/src/mir/eval/shim.rs
@@ -1012,6 +1012,43 @@
                 let dst = Interval { addr: dst, size };
                 dst.write_from_interval(self, src)
             }
+            "slice_get_unchecked" => {
+                let [slice_ptr, index] = args else {
+                    return Err(MirEvalError::InternalError(
+                        "slice_get_unchecked args are not provided".into(),
+                    ));
+                };
+                let Some(ty) = generic_args.as_slice().get(2).and_then(|it| it.ty()) else {
+                    return Err(MirEvalError::InternalError(
+                        "slice_get_unchecked item type is not provided".into(),
+                    ));
+                };
+                let slice_ptr = slice_ptr.get(self)?;
+                let ptr_size = self.ptr_size();
+                let Some(data) = slice_ptr.get(..ptr_size) else {
+                    return Err(MirEvalError::InternalError(
+                        "slice_get_unchecked slice pointer is too small".into(),
+                    ));
+                };
+                let Some(len) = slice_ptr.get(ptr_size..2 * ptr_size) else {
+                    return Err(MirEvalError::InternalError(
+                        "slice_get_unchecked slice metadata is missing".into(),
+                    ));
+                };
+                let slice_ptr = Address::from_bytes(data)?;
+                let len = from_bytes!(usize, len);
+                let index = from_bytes!(usize, index.get(self)?);
+                if index >= len {
+                    return Err(MirEvalError::UndefinedBehavior(format!(
+                        "slice_get_unchecked index {index} is out of bounds for slice of length {len}"
+                    )));
+                }
+                let size = self.size_of_sized(ty, locals, "slice_get_unchecked item type")?;
+                let offset = index* size;
+                let addr = slice_ptr.to_usize() + offset;
+                let addr = Address::from_usize(addr);
+                destination.write_from_bytes(self, &addr.to_bytes()[..destination.size])
+            }
             "offset" | "arith_offset" => {
                 let [ptr, offset] = args else {
                     return Err(MirEvalError::InternalError("offset args are not provided".into()));
diff --git a/crates/hir-ty/src/mir/eval/tests.rs b/crates/hir-ty/src/mir/eval/tests.rs
index 68d1976..7431ac8 100644
--- a/crates/hir-ty/src/mir/eval/tests.rs
+++ b/crates/hir-ty/src/mir/eval/tests.rs
@@ -1115,6 +1115,109 @@
 }
 
 #[test]
+fn slice_get_unchecked_intrinsic() {
+    check_pass(
+        r#"
+//- minicore: panic
+#[rustc_intrinsic]
+unsafe fn slice_get_unchecked<ItemPtr, SlicePtr, T>(
+    slice_ptr: SlicePtr,
+    index: usize,
+) -> ItemPtr;
+
+fn should_not_reach() { panic!() }
+
+fn main() {
+    let values = [10, 20, 30];
+    let slice_ptr = &values as *const [i32];
+    let item_ptr = unsafe {
+        slice_get_unchecked::<*const i32, *const [i32], i32>(slice_ptr, 1)
+    };
+    if unsafe { *item_ptr } != 20 {
+        should_not_reach();
+    }
+}
+"#,
+    );
+}
+
+#[test]
+fn slice_get_unchecked_out_of_bounds() {
+    check_error_with(
+        r#"
+#[rustc_intrinsic]
+unsafe fn slice_get_unchecked<ItemPtr, SlicePtr, T>(
+    slice_ptr: SlicePtr,
+    index: usize,
+) -> ItemPtr;
+
+fn main() {
+    let values = [()];
+    let slice_ptr = &values as *const [()];
+    let _item = unsafe {
+        slice_get_unchecked::<*const (), *const [()], ()>(slice_ptr, 1)
+    };
+}
+"#,
+        |e| {
+            let mut err = &e;
+            while let MirEvalError::InFunction(inner, _) = err {
+                err = inner;
+            }
+            matches!(err, MirEvalError::UndefinedBehavior(_))
+        },
+    );
+}
+
+#[test]
+fn slice_get_unchecked_const_slice() {
+    check_pass(
+        r#"
+//- minicore: panic
+#[rustc_intrinsic]
+unsafe fn slice_get_unchecked<ItemPtr, SlicePtr, T>(
+    slice_ptr: SlicePtr,
+    index: usize,
+) -> ItemPtr;
+
+struct Flag {
+    name: &'static str,
+    value: u16,
+}
+
+const PURE: &str = "PURE";
+const NOMEM: &str = "NOMEM";
+const READONLY: &str = "READONLY";
+const PRESERVES_FLAGS: &str = "PRESERVES_FLAGS";
+const NORETURN: &str = "NORETURN";
+const NOSTACK: &str = "NOSTACK";
+const ATT_SYNTAX: &str = "ATT_SYNTAX";
+const FLAGS: &[Flag] = &[
+    Flag { name: PURE, value: 1 },
+    Flag { name: NOMEM, value: 2 },
+    Flag { name: READONLY, value: 4 },
+    Flag { name: PRESERVES_FLAGS, value: 8 },
+    Flag { name: NORETURN, value: 16 },
+    Flag { name: NOSTACK, value: 32 },
+    Flag { name: ATT_SYNTAX, value: 64 },
+];
+
+fn should_not_reach() { panic!() }
+
+fn main() {
+    let flag = unsafe {
+        slice_get_unchecked::<&Flag, &[Flag], Flag>(FLAGS, 6)
+    };
+    let name = flag.name as *const str as *const u8;
+    if unsafe { *name } != b'A' || flag.value != 64 {
+        should_not_reach();
+    }
+}
+"#,
+    );
+}
+
+#[test]
 fn unreachable_intrinsic() {
     check_error_with(
         r#"
diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs
index 620d768..2537f64 100644
--- a/crates/hir-ty/src/mir/lower.rs
+++ b/crates/hir-ty/src/mir/lower.rs
@@ -23,7 +23,7 @@
 use rustc_apfloat::Float;
 use rustc_hash::FxHashMap;
 use rustc_type_ir::inherent::{Const as _, GenericArgs as _, IntoKind, Ty as _};
-use salsa::Update;
+use salsa::SalsaValue;
 use span::{Edition, FileId};
 use syntax::TextRange;
 
@@ -97,7 +97,7 @@
 }
 
 // FIXME: Make this smaller, its stored in database queries
-#[derive(Debug, Clone, PartialEq, Eq, Update)]
+#[derive(Debug, Clone, PartialEq, Eq, SalsaValue)]
 pub enum MirLowerError<'db> {
     ConstEvalError(Box<str>, Box<ConstEvalError<'db>>),
     LayoutError(LayoutError),
diff --git a/crates/hir-ty/src/mir/lower/tests.rs b/crates/hir-ty/src/mir/lower/tests.rs
index fdd67fc..1f6aa5c 100644
--- a/crates/hir-ty/src/mir/lower/tests.rs
+++ b/crates/hir-ty/src/mir/lower/tests.rs
@@ -1,7 +1,6 @@
-use hir_def::DefWithBodyId;
 use test_fixture::WithFixture;
 
-use crate::{InferBodyId, db::HirDatabase, setup_tracing, test_db::TestDB};
+use crate::{db::HirDatabase, setup_tracing, test_db::TestDB};
 
 fn lower_mir(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
     let _tracing = setup_tracing();
@@ -50,131 +49,3 @@
     "#,
     );
 }
-
-fn check_borrowck(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
-    let _tracing = setup_tracing();
-    let (db, file_ids) = TestDB::with_many_files(ra_fixture);
-    crate::attach_db(db.as_dyn(), || {
-        let file_id = *file_ids.last().unwrap();
-        let module_id = db.module_for_file(file_id.file_id(&db));
-        let def_map = module_id.def_map(&db);
-        let scope = &def_map[module_id].scope;
-
-        let mut bodies: Vec<DefWithBodyId> = Vec::new();
-
-        for decl in scope.declarations() {
-            if let hir_def::ModuleDefId::FunctionId(f) = decl {
-                bodies.push(f.into());
-            }
-        }
-
-        for impl_id in scope.impls() {
-            let impl_items = impl_id.impl_items(&db);
-            for (_, item) in impl_items.items.iter() {
-                if let hir_def::AssocItemId::FunctionId(f) = item {
-                    bodies.push((*f).into());
-                }
-            }
-        }
-
-        for body in bodies {
-            let _ = InferBodyId::from(body).borrowck(db.as_dyn());
-        }
-    })
-}
-
-#[test]
-fn regression_21173_const_generic_impl_with_assoc_type() {
-    check_borrowck(
-        r#"
-pub trait Tr {
-    type Assoc;
-    fn f(&self, handle: Self::Assoc) -> i32;
-}
-
-pub struct ConstGeneric<const N: usize>;
-
-impl<const N: usize> Tr for &ConstGeneric<N> {
-    type Assoc = AssocTy;
-
-    fn f(&self, a: Self::Assoc) -> i32 {
-        a.x
-    }
-}
-
-pub struct AssocTy {
-    x: i32,
-}
-    "#,
-    );
-}
-
-#[test]
-fn borrowck_tuple_field_projection_recovery_does_not_panic() {
-    check_borrowck(
-        r#"
-//- minicore: sized
-fn tuple_field() {
-    let t = (1,);
-    let x = t.1;
-}
-    "#,
-    );
-}
-
-#[test]
-fn borrowck_alias_projection_recovery_does_not_panic() {
-    check_borrowck(
-        r#"
-//- minicore: sized
-trait Tr { type A; }
-fn alias<T: Tr>(x: T::A) {
-    let (a, b) = x;
-}
-    "#,
-    );
-}
-
-#[test]
-fn borrowck_opaque_downcast_recovery_does_not_panic() {
-    check_borrowck(
-        r#"
-//- minicore: option, sized
-struct PathBuf;
-fn opaque<T>(path: T) -> impl Sized {
-    Some(path)
-}
-fn caller(path: &PathBuf) {
-    let Some(value) = opaque(path) else { return };
-}
-    "#,
-    );
-}
-
-#[test]
-fn borrowck_hrtb_closure_argument_does_not_panic() {
-    check_borrowck(
-        r#"
-//- minicore: fn, copy
-enum Res<T, E> {
-    Ok(T),
-    Err(E),
-}
-
-struct S;
-
-impl S {
-    fn set<F>(&mut self, _: F)
-    where
-        F: for<'a> Fn(&mut (), &'a [u8]) -> Res<(), ()>,
-    {
-    }
-}
-
-fn main() {
-    let mut s = S;
-    s.set(|_, _| Res::Err(()));
-}
-    "#,
-    );
-}
diff --git a/crates/hir-ty/src/next_solver/binder.rs b/crates/hir-ty/src/next_solver/binder.rs
index 351d0c4..bc2f155 100644
--- a/crates/hir-ty/src/next_solver/binder.rs
+++ b/crates/hir-ty/src/next_solver/binder.rs
@@ -1,6 +1,6 @@
 use hir_def::TraitId;
 use macros::{TypeFoldable, TypeVisitable};
-use salsa::Update;
+use salsa::SalsaValue;
 
 use crate::next_solver::{
     Binder, Clauses, DbInterner, EarlyBinder, FnSig, FnSigKind, GenericArg, PolyFnSig,
@@ -8,7 +8,7 @@
     TraitRef, Ty,
 };
 
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Update)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, SalsaValue)]
 pub struct StoredEarlyBinder<T>(T);
 
 impl<T> StoredEarlyBinder<T> {
@@ -68,7 +68,7 @@
     }
 }
 
-#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+#[derive(Debug, Clone, PartialEq, Eq, Hash, SalsaValue)]
 pub struct StoredPolyFnSig {
     bound_vars: StoredBoundVarKinds,
     sig: StoredFnSig,
@@ -113,7 +113,7 @@
     }
 }
 
-#[derive(Debug, Clone, PartialEq, Eq, Hash, TypeVisitable, TypeFoldable, Update)]
+#[derive(Debug, Clone, PartialEq, Eq, Hash, TypeVisitable, TypeFoldable, SalsaValue)]
 pub struct StoredTraitRef {
     #[type_visitable(ignore)]
     def_id: TraitId,
diff --git a/crates/hir-ty/src/next_solver/def_id.rs b/crates/hir-ty/src/next_solver/def_id.rs
index b122a8c..c45387b 100644
--- a/crates/hir-ty/src/next_solver/def_id.rs
+++ b/crates/hir-ty/src/next_solver/def_id.rs
@@ -10,7 +10,7 @@
     },
 };
 use rustc_type_ir::inherent;
-use salsa::Update;
+use salsa::SalsaValue;
 use stdx::impl_from;
 
 use crate::{
@@ -29,7 +29,7 @@
     Enum(EnumVariantId),
 }
 
-#[derive(PartialOrd, Ord, Clone, Copy, PartialEq, Eq, Hash, Update)]
+#[derive(PartialOrd, Ord, Clone, Copy, PartialEq, Eq, Hash, SalsaValue)]
 pub enum SolverDefId<'db> {
     AdtId(AdtId),
     ConstId(ConstId),
diff --git a/crates/hir-ty/src/next_solver/interner.rs b/crates/hir-ty/src/next_solver/interner.rs
index 7554ca6..05e1559 100644
--- a/crates/hir-ty/src/next_solver/interner.rs
+++ b/crates/hir-ty/src/next_solver/interner.rs
@@ -36,6 +36,7 @@
     inherent::{self, Const as _, GenericsOf, IntoKind, SliceLike as _, Span as _, Ty as _},
     lang_items::{SolverAdtLangItem, SolverProjectionLangItem, SolverTraitLangItem},
     solve::{AdtDestructorKind, SizedTraitKind},
+    try_visit,
 };
 
 use crate::{
@@ -54,7 +55,6 @@
         TraitAssocTyId, TraitIdWrapper, TypeAliasIdWrapper, UnevaluatedConst, Unnormalized,
         util::{explicit_item_bounds, explicit_item_self_bounds},
     },
-    ret,
 };
 
 use super::{
@@ -211,12 +211,7 @@
         }
 
         // SAFETY: It is safe to store this type in queries (but not `$name`).
-        unsafe impl salsa::Update for $stored_name {
-            unsafe fn maybe_update(old_pointer: *mut Self, new_value: Self) -> bool {
-                // SAFETY: Comparing by (pointer) equality is safe.
-                unsafe { salsa::update_fallback(old_pointer, new_value) }
-            }
-        }
+        unsafe impl salsa::SalsaValue for $stored_name {}
 
         impl std::fmt::Debug for $stored_name {
             fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
@@ -296,7 +291,7 @@
 
 macro_rules! impl_stored_interned {
     ( $storage:ident, $name:ident, $stored_name:ident $(,)? ) => {
-        #[derive(Clone, PartialEq, Eq, Hash)]
+        #[derive(Clone, PartialEq, Eq, Hash, ::salsa::SalsaValue)]
         pub struct $stored_name {
             interned: ::intern::Interned<$storage>,
         }
@@ -314,12 +309,6 @@
             }
         }
 
-        unsafe impl salsa::Update for $stored_name {
-            unsafe fn maybe_update(old_pointer: *mut Self, new_value: Self) -> bool {
-                unsafe { salsa::update_fallback(old_pointer, new_value) }
-            }
-        }
-
         impl std::fmt::Debug for $stored_name {
             fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
                 self.as_ref().fmt(f)
@@ -1671,10 +1660,10 @@
                     let (regular_impls, builtin_derive_impls) =
                         impls.for_trait_and_self_ty(trait_def_id.0, &simp);
                     for &impl_ in regular_impls {
-                        ret!(f(impl_.into()));
+                        try_visit!(f(impl_.into()));
                     }
                     for &impl_ in builtin_derive_impls {
-                        ret!(f(impl_.into()));
+                        try_visit!(f(impl_.into()));
                     }
                     R::output()
                 },
@@ -1707,7 +1696,7 @@
                 let simp =
                     fast_reject::simplify_type(self, self_ty, fast_reject::TreatParams::AsRigid)
                         .unwrap();
-                ret!(consider_impls_for_simplified_type(simp));
+                try_visit!(consider_impls_for_simplified_type(simp));
             }
 
             // HACK: For integer and float variables we have to manually look at all impls
@@ -1735,7 +1724,7 @@
                     SimplifiedType::Uint(Usize),
                 ];
                 for simp in possible_integers {
-                    ret!(consider_impls_for_simplified_type(simp));
+                    try_visit!(consider_impls_for_simplified_type(simp));
                 }
             }
 
@@ -1750,7 +1739,7 @@
                 ];
 
                 for simp in possible_floats {
-                    ret!(consider_impls_for_simplified_type(simp));
+                    try_visit!(consider_impls_for_simplified_type(simp));
                 }
             }
 
@@ -1791,7 +1780,7 @@
 
         TraitImpls::for_each_crate_and_block(self.db, krate, block, &mut |impls| {
             for &impl_ in impls.blanket_impls(trait_def_id.0) {
-                ret!(f(impl_.into()));
+                try_visit!(f(impl_.into()));
             }
             R::output()
         })
@@ -2085,13 +2074,19 @@
         opaque: Self::LocalOpaqueTyId,
     ) -> EarlyBinder<Self, Self::Ty> {
         let impl_trait_id = opaque.0.loc(self.db);
-        match impl_trait_id {
+        // The entry is missing when this call cycles back into the still-running inference
+        // of the defining body, as the cycle fallback is an empty result.
+        let hidden_type = match impl_trait_id {
             crate::ImplTraitId::ReturnTypeImplTrait(func, idx) => {
-                crate::opaques::rpit_hidden_types(self.db, func)[idx].get()
+                crate::opaques::rpit_hidden_types(self.db, func).get(idx)
             }
             crate::ImplTraitId::TypeAliasImplTrait(type_alias, idx) => {
-                crate::opaques::tait_hidden_types(self.db, type_alias)[idx].get()
+                crate::opaques::tait_hidden_types(self.db, type_alias).get(idx)
             }
+        };
+        match hidden_type {
+            Some(hidden_type) => hidden_type.get(),
+            None => EarlyBinder::bind(Ty::new_error(self, ErrorGuaranteed)),
         }
     }
 
diff --git a/crates/hir-ty/src/next_solver/util.rs b/crates/hir-ty/src/next_solver/util.rs
index e384ac4..7e40e3c 100644
--- a/crates/hir-ty/src/next_solver/util.rs
+++ b/crates/hir-ty/src/next_solver/util.rs
@@ -723,15 +723,3 @@
         recursion_depth: 0,
     })
 }
-
-/// Copied from
-/// <https://github.com/jdonszelmann/rust/blob/180725cff61d00dd1b9c35fc720a8befaec5d46b/compiler/rustc_middle/src/ty/context/impl_interner.rs#L534-L541>
-#[macro_export]
-macro_rules! ret {
-    ($e: expr) => {
-        match $e.branch() {
-            ::std::ops::ControlFlow::Break(b) => return R::from_residual(b),
-            ::std::ops::ControlFlow::Continue(()) => {}
-        }
-    };
-}
diff --git a/crates/hir-ty/src/opaques.rs b/crates/hir-ty/src/opaques.rs
index 9cb0022..194c866 100644
--- a/crates/hir-ty/src/opaques.rs
+++ b/crates/hir-ty/src/opaques.rs
@@ -5,14 +5,14 @@
     signatures::ImplSignature,
 };
 use hir_expand::name::Name;
-use la_arena::ArenaMap;
+use la_arena::{Arena, ArenaMap};
 use rustc_type_ir::inherent::Ty as _;
 use syntax::ast;
 
 use crate::{
     ImplTraitId, InferBodyId, InferenceResult,
     db::{HirDatabase, InternedOpaqueTyId},
-    lower::{ImplTraitIdx, ImplTraits},
+    lower::{ImplTrait, ImplTraitIdx},
     next_solver::{
         DbInterner, ErrorGuaranteed, SolverDefId, StoredEarlyBinder, StoredTy, Ty, TypingMode,
         infer::{DbInternerInferExt, traits::ObligationCause},
@@ -29,7 +29,7 @@
         // A function may define its own RPITs.
         extend_with_opaques(
             db,
-            ImplTraits::return_type_impl_traits(db, func),
+            ImplTrait::return_type_impl_traits(db, func),
             |opaque_idx| ImplTraitId::ReturnTypeImplTrait(func, opaque_idx),
             result,
         );
@@ -38,7 +38,7 @@
     let extend_with_taits = |type_alias| {
         extend_with_opaques(
             db,
-            ImplTraits::type_alias_impl_traits(db, type_alias),
+            ImplTrait::type_alias_impl_traits(db, type_alias),
             |opaque_idx| ImplTraitId::TypeAliasImplTrait(type_alias, opaque_idx),
             result,
         );
@@ -81,15 +81,13 @@
 
     fn extend_with_opaques<'db>(
         db: &'db dyn HirDatabase,
-        opaques: &Option<Box<StoredEarlyBinder<ImplTraits>>>,
+        opaques: &Arena<ImplTrait>,
         mut make_impl_trait: impl FnMut(ImplTraitIdx) -> ImplTraitId,
         result: &mut Vec<SolverDefId<'db>>,
     ) {
-        if let Some(opaques) = opaques {
-            for (opaque_idx, _) in (**opaques).as_ref().skip_binder().impl_traits.iter() {
-                let opaque_id = InternedOpaqueTyId::new(db, make_impl_trait(opaque_idx));
-                result.push(opaque_id.into());
-            }
+        for (opaque_idx, _) in opaques.iter() {
+            let opaque_id = InternedOpaqueTyId::new(db, make_impl_trait(opaque_idx));
+            result.push(opaque_id.into());
         }
     }
 }
@@ -116,12 +114,7 @@
     type_alias: TypeAliasId,
 ) -> ArenaMap<ImplTraitIdx, StoredEarlyBinder<StoredTy>> {
     // Call this first, to not perform redundant work if there are no TAITs.
-    let Some(taits_count) = ImplTraits::type_alias_impl_traits(db, type_alias)
-        .as_deref()
-        .map(|taits| taits.as_ref().skip_binder().impl_traits.len())
-    else {
-        return ArenaMap::new();
-    };
+    let taits_count = ImplTrait::type_alias_impl_traits(db, type_alias).len();
 
     let loc = type_alias.loc(db);
     let module = loc.module(db);
diff --git a/crates/hir-ty/src/representability.rs b/crates/hir-ty/src/representability.rs
index 828af20..9a31443 100644
--- a/crates/hir-ty/src/representability.rs
+++ b/crates/hir-ty/src/representability.rs
@@ -23,7 +23,7 @@
     };
 }
 
-#[salsa::tracked(cycle_result = representability_cycle)]
+#[salsa::tracked(cycle_result = representability_cycle, returns(copy))]
 pub(crate) fn representability(db: &dyn HirDatabase, id: AdtId) -> Representability {
     match id {
         AdtId::StructId(id) => variant_representability(db, id.into()),
diff --git a/crates/hir-ty/src/specialization.rs b/crates/hir-ty/src/specialization.rs
index 2d206fe..c3543cd 100644
--- a/crates/hir-ty/src/specialization.rs
+++ b/crates/hir-ty/src/specialization.rs
@@ -39,7 +39,7 @@
 /// `parent_impl_def_id` is a const impl (conditionally based off of some `[const]`
 /// bounds), then `specializing_impl_def_id` must also be const for the same
 /// set of types.
-#[salsa::tracked(cycle_result = specializes_query_cycle)]
+#[salsa::tracked(cycle_result = specializes_query_cycle, returns(copy))]
 fn specializes_query(
     db: &dyn HirDatabase,
     specializing_impl_def_id: ImplId,
diff --git a/crates/hir-ty/src/tests/incremental.rs b/crates/hir-ty/src/tests/incremental.rs
index 08efef1..4574e09 100644
--- a/crates/hir-ty/src/tests/incremental.rs
+++ b/crates/hir-ty/src/tests/incremental.rs
@@ -47,7 +47,7 @@
                 "lang_items",
                 "crate_lang_items",
                 "GenericPredicates::query_with_diagnostics_",
-                "ImplTraits::return_type_impl_traits_",
+                "fn_sig_for_fn",
                 "ExprScopes::body_expr_scopes_",
                 "body_upvars_mentioned",
             ]
@@ -136,7 +136,7 @@
                 "lang_items",
                 "crate_lang_items",
                 "GenericPredicates::query_with_diagnostics_",
-                "ImplTraits::return_type_impl_traits_",
+                "fn_sig_for_fn",
                 "ExprScopes::body_expr_scopes_",
                 "body_upvars_mentioned",
                 "InferenceResult < 'db >::for_body_",
@@ -147,7 +147,7 @@
                 "Body::with_source_map_",
                 "trait_environment_query",
                 "GenericPredicates::query_with_diagnostics_",
-                "ImplTraits::return_type_impl_traits_",
+                "fn_sig_for_fn",
                 "ExprScopes::body_expr_scopes_",
                 "body_upvars_mentioned",
                 "InferenceResult < 'db >::for_body_",
@@ -158,7 +158,7 @@
                 "Body::with_source_map_",
                 "trait_environment_query",
                 "GenericPredicates::query_with_diagnostics_",
-                "ImplTraits::return_type_impl_traits_",
+                "fn_sig_for_fn",
                 "ExprScopes::body_expr_scopes_",
                 "body_upvars_mentioned",
             ]
@@ -599,21 +599,20 @@
                 "crate_lang_items",
                 "GenericPredicates::query_with_diagnostics_",
                 "GenericPredicates::query_with_diagnostics_",
-                "ImplTraits::return_type_impl_traits_",
+                "fn_sig_for_fn",
                 "body_upvars_mentioned",
                 "InferenceResult < 'db >::for_body_",
                 "FunctionSignature::of_",
                 "FunctionSignature::with_source_map_",
                 "trait_environment_query",
                 "GenericPredicates::query_with_diagnostics_",
-                "ImplTraits::return_type_impl_traits_",
+                "fn_sig_for_fn",
                 "ExprScopes::body_expr_scopes_",
                 "StructSignature::of_",
                 "StructSignature::with_source_map_",
                 "AttrFlags::query_",
                 "GenericPredicates::query_with_diagnostics_",
                 "InherentImpls < 'db >::for_crate_",
-                "callable_item_signature_with_diagnostics",
                 "TraitImpls < 'db >::for_crate_and_deps_",
                 "TraitImpls < 'db >::for_crate_",
                 "impl_trait_with_diagnostics",
@@ -692,18 +691,17 @@
                 "crate_lang_items",
                 "GenericPredicates::query_with_diagnostics_",
                 "GenericPredicates::query_with_diagnostics_",
-                "ImplTraits::return_type_impl_traits_",
+                "fn_sig_for_fn",
                 "body_upvars_mentioned",
                 "InferenceResult < 'db >::for_body_",
                 "FunctionSignature::with_source_map_",
                 "GenericPredicates::query_with_diagnostics_",
-                "ImplTraits::return_type_impl_traits_",
+                "fn_sig_for_fn",
                 "ExprScopes::body_expr_scopes_",
                 "StructSignature::with_source_map_",
                 "AttrFlags::query_",
                 "GenericPredicates::query_with_diagnostics_",
                 "InherentImpls < 'db >::for_crate_",
-                "callable_item_signature_with_diagnostics",
                 "TraitImpls < 'db >::for_crate_",
                 "ImplSignature::with_source_map_",
                 "ImplSignature::of_",
diff --git a/crates/hir-ty/src/tests/never_type.rs b/crates/hir-ty/src/tests/never_type.rs
index 9d98218..5cc4156 100644
--- a/crates/hir-ty/src/tests/never_type.rs
+++ b/crates/hir-ty/src/tests/never_type.rs
@@ -888,6 +888,17 @@
 }
 
 #[test]
+fn diverging_destructuring_assignment_coerces_rhs() {
+    check_no_mismatches(
+        r#"
+fn main() {
+    if (() = return) {}
+}
+"#,
+    );
+}
+
+#[test]
 fn never_coercion_in_struct_update_syntax() {
     check_no_mismatches(
         r#"
diff --git a/crates/hir-ty/src/tests/regression.rs b/crates/hir-ty/src/tests/regression.rs
index 2836f97..6724243 100644
--- a/crates/hir-ty/src/tests/regression.rs
+++ b/crates/hir-ty/src/tests/regression.rs
@@ -3028,3 +3028,97 @@
     "#,
     );
 }
+
+#[test]
+fn braced_const_path() {
+    check_types(
+        r#"
+//- minicore: default, builtin_impls
+trait ToNum {
+    type Num;
+}
+trait Bar {
+    type Ty;
+}
+struct Gen<const B: bool>;
+struct Int<const B: bool>;
+
+impl<const B: bool> ToNum for Gen<{ B }> {
+    type Num = Int<B>;
+}
+
+impl Bar for Int<true> {
+    type Ty = i32;
+}
+impl Bar for Int<false> {
+    type Ty = f32;
+}
+
+type A = <<Gen<true> as ToNum>::Num as Bar>::Ty;
+
+fn main() {
+    let x = A::default();
+     // ^ i32
+}
+    "#,
+    );
+}
+
+#[test]
+fn regression_22820() {
+    check_no_mismatches(
+        r#"
+//- minicore: copy
+trait MyTrait: Copy {
+    const ASSOC: usize;
+}
+
+const fn output<T: MyTrait>(_: T) -> usize {
+    <T as MyTrait>::ASSOC
+}
+
+const fn yeet() -> impl Clone {
+    let x = [0u8; output(yeet())];
+}
+    "#,
+    );
+}
+
+#[test]
+fn rpit_function_with_non_trivial_anon_const() {
+    check_no_mismatches(
+        r#"
+fn f() -> impl Sized {
+    let x = [0u8; 1 + 2];
+}
+    "#,
+    );
+}
+
+#[test]
+fn regression_22836() {
+    check(
+        r#"
+fn main() {
+  match () {
+    const {
+      async | v | ()
+   // ^^^^^^^^^^^^^^ expected (), got impl AsyncFn({unknown})
+    }
+  }
+}
+    "#,
+    );
+}
+
+#[test]
+fn regression_22986() {
+    check_no_mismatches(
+        r#"
+fn main() {
+    let _: &[u8; 0] = b"\
+        ";
+}
+    "#,
+    );
+}
diff --git a/crates/hir-ty/src/tests/regression/new_solver.rs b/crates/hir-ty/src/tests/regression/new_solver.rs
index 97f83ca..59563bd 100644
--- a/crates/hir-ty/src/tests/regression/new_solver.rs
+++ b/crates/hir-ty/src/tests/regression/new_solver.rs
@@ -1,5 +1,5 @@
 use expect_test::expect;
-use hir_def::ModuleDefId;
+use hir_def::{AdtId, ModuleDefId};
 use rustc_type_ir::inherent::IntoKind as _;
 use test_fixture::WithFixture;
 
@@ -424,6 +424,41 @@
 }
 
 #[test]
+fn invalid_string_array_len_is_error() {
+    let (db, file_id) = TestDB::with_single_file(
+        r#"
+pub union U {
+    foo: [usize; ""],
+}
+"#,
+    );
+
+    crate::attach_db(&db, || {
+        let module_id = db.module_for_file(file_id.file_id(&db));
+        let def_map = module_id.def_map(&db);
+        let union_id = def_map[module_id]
+            .scope
+            .declarations()
+            .find_map(|decl| match decl {
+                ModuleDefId::AdtId(AdtId::UnionId(id)) => Some(id),
+                _ => None,
+            })
+            .unwrap();
+        let field_ty = db
+            .field_types(union_id.into())
+            .iter()
+            .next()
+            .unwrap()
+            .1
+            .ty()
+            .instantiate_identity()
+            .skip_norm_wip();
+        let TyKind::Array(_, len) = field_ty.kind() else { unreachable!() };
+        assert!(len.is_error());
+    });
+}
+
+#[test]
 fn another_20654_case() {
     check_no_mismatches(
         r#"
@@ -914,3 +949,31 @@
         "#]],
     );
 }
+
+#[test]
+fn infer_method_call_recovery_hrtb_does_not_panic() {
+    check_no_mismatches(
+        r#"
+//- minicore: coerce_unsized, dispatch_from_dyn, fn, option, phantom_data, sized
+use core::marker::PhantomData;
+
+trait Any {}
+
+impl<T: 'static> Any for T {}
+
+struct Bar<'a>(PhantomData<&'a ()>);
+
+type FooFn = for<'a> fn(&'a dyn Any) -> Option<Bar<'a>>;
+
+struct Foo {
+    bar: FooFn,
+}
+
+impl Foo {
+    fn baz<'a, T: 'static>(&self, x: &'a T) -> Option<Bar<'a>> {
+        self.bar(x)
+    }
+}
+"#,
+    );
+}
diff --git a/crates/hir-ty/src/tests/simple.rs b/crates/hir-ty/src/tests/simple.rs
index e8f378d..3cdfe4e 100644
--- a/crates/hir-ty/src/tests/simple.rs
+++ b/crates/hir-ty/src/tests/simple.rs
@@ -4201,8 +4201,6 @@
             248..282 'LazyLo..._LOCK)': &'? [u32; 0]
             264..281 '&VALUE...Y_LOCK': &'? LazyLock<[u32; 0]>
             265..281 'VALUES...Y_LOCK': LazyLock<[u32; 0]>
-            197..202 '{ 0 }': usize
-            199..200 '0': usize
         "#]],
     );
 }
@@ -4308,9 +4306,8 @@
 }
     "#,
         expect![[r#"
-            29..34 '{ 2 }': usize
-            31..32 '2': usize
-        "#]],
+
+"#]],
     );
 }
 
diff --git a/crates/hir-ty/src/traits.rs b/crates/hir-ty/src/traits.rs
index 108e3e0..c485af5 100644
--- a/crates/hir-ty/src/traits.rs
+++ b/crates/hir-ty/src/traits.rs
@@ -83,7 +83,7 @@
     ty.replace_infer_with_error(infcx.interner)
 }
 
-#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, salsa::Update)]
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, salsa::SalsaValue)]
 pub enum FnTrait {
     // Warning: Order is important. If something implements `x` it should also implement
     // `y` if `y <= x`.
diff --git a/crates/hir-ty/src/upvars.rs b/crates/hir-ty/src/upvars.rs
index 6dcd8b5..5cc7d81 100644
--- a/crates/hir-ty/src/upvars.rs
+++ b/crates/hir-ty/src/upvars.rs
@@ -2,9 +2,10 @@
 
 use hir_def::{
     DefWithBodyId, ExpressionStoreOwnerId, GenericDefId, VariantId,
-    expr_store::{ExpressionStore, path::Path},
-    hir::{BindingId, Expr, ExprId, ExprOrPatId},
+    expr_store::{ExpressionStore, StoreVisitor, StoreVisitorExt, path::Path},
+    hir::{BindingId, Expr, ExprId, PatId},
     resolver::{HasResolver, Resolver, ValueNs},
+    type_ref::TypeRefId,
 };
 use hir_expand::mod_path::PathKind;
 use rustc_hash::{FxHashMap, FxHashSet};
@@ -111,140 +112,114 @@
 ) -> Option<Box<FxHashMap<ExprId, Upvars>>> {
     let store = ExpressionStore::of(db, owner);
     store.expr_roots().next()?;
-    let mut resolver = owner.resolver(db);
-    let mut result = FxHashMap::default();
-    for root_expr in store.expr_roots() {
-        handle_expr_outside_closure(db, &mut resolver, owner, store, root_expr, &mut result);
-    }
-    return if result.is_empty() {
+    let resolver = owner.resolver(db);
+    let mut visitor = UpvarsMentionedVisitor {
+        db,
+        resolver,
+        owner,
+        store,
+        closures_map: FxHashMap::default(),
+        current_closure: None,
+    };
+    visitor.on_exprs(store.expr_roots());
+    let mut result = visitor.closures_map;
+    if result.is_empty() {
         None
     } else {
         result.shrink_to_fit();
         Some(Box::new(result))
-    };
-
-    fn handle_expr_outside_closure<'db>(
-        db: &'db dyn HirDatabase,
-        resolver: &mut Resolver<'db>,
-        owner: ExpressionStoreOwnerId,
-        body: &ExpressionStore,
-        expr: ExprId,
-        closures_map: &mut FxHashMap<ExprId, Upvars>,
-    ) {
-        match &body[expr] {
-            &Expr::Closure { body: body_expr, .. } => {
-                let mut upvars = FxHashSet::default();
-                handle_expr_inside_closure(
-                    db,
-                    resolver,
-                    owner,
-                    body,
-                    expr,
-                    body_expr,
-                    &mut upvars,
-                    closures_map,
-                );
-                if !upvars.is_empty() {
-                    closures_map.insert(expr, Upvars::new(&upvars));
-                }
-            }
-            _ => body.walk_child_exprs(expr, |expr| {
-                handle_expr_outside_closure(db, resolver, owner, body, expr, closures_map)
-            }),
-        }
-    }
-
-    fn handle_expr_inside_closure<'db>(
-        db: &'db dyn HirDatabase,
-        resolver: &mut Resolver<'db>,
-        owner: ExpressionStoreOwnerId,
-        body: &ExpressionStore,
-        current_closure: ExprId,
-        expr: ExprId,
-        upvars: &mut FxHashSet<BindingId>,
-        closures_map: &mut FxHashMap<ExprId, Upvars>,
-    ) {
-        match &body[expr] {
-            Expr::Path(path) => {
-                resolve_maybe_upvar(
-                    db,
-                    resolver,
-                    owner,
-                    body,
-                    current_closure,
-                    expr,
-                    expr.into(),
-                    upvars,
-                    path,
-                );
-            }
-            &Expr::Closure { body: body_expr, .. } => {
-                let mut closure_upvars = FxHashSet::default();
-                handle_expr_inside_closure(
-                    db,
-                    resolver,
-                    owner,
-                    body,
-                    expr,
-                    body_expr,
-                    &mut closure_upvars,
-                    closures_map,
-                );
-                if !closure_upvars.is_empty() {
-                    closures_map.insert(expr, Upvars::new(&closure_upvars));
-                    // All nested closure's upvars are also upvars of the parent closure.
-                    upvars.extend(
-                        closure_upvars
-                            .iter()
-                            .copied()
-                            .filter(|local| body.binding_owner(*local) != Some(current_closure)),
-                    );
-                }
-                return;
-            }
-            _ => {}
-        }
-        body.walk_child_exprs(expr, |expr| {
-            handle_expr_inside_closure(
-                db,
-                resolver,
-                owner,
-                body,
-                current_closure,
-                expr,
-                upvars,
-                closures_map,
-            )
-        });
     }
 }
 
-fn resolve_maybe_upvar<'db>(
+struct UpvarsMentionedVisitor<'db> {
     db: &'db dyn HirDatabase,
-    resolver: &mut Resolver<'db>,
+    resolver: Resolver<'db>,
     owner: ExpressionStoreOwnerId,
-    body: &ExpressionStore,
-    current_closure: ExprId,
-    expr: ExprId,
-    id: ExprOrPatId,
-    upvars: &mut FxHashSet<BindingId>,
-    path: &Path,
-) {
-    if let Path::BarePath(mod_path) = path
+    store: &'db ExpressionStore,
+    closures_map: FxHashMap<ExprId, Upvars>,
+    current_closure: Option<(ExprId, FxHashSet<BindingId>)>,
+}
+
+impl UpvarsMentionedVisitor<'_> {
+    fn resolve_maybe_upvar(&mut self, expr: ExprId, path: &Path) {
+        let Some((current_closure, upvars)) = &mut self.current_closure else { return };
+
+        if let Path::BarePath(mod_path) = path
         && matches!(mod_path.kind, PathKind::Plain | PathKind::SELF)
         // `self` is length zero.
         && mod_path.segments().len() <= 1
-    {
-        // Could be a variable.
-        let guard = resolver.update_to_inner_scope(db, owner, expr);
-        let resolution =
-            resolver.resolve_path_in_value_ns_fully(db, path, body.expr_or_pat_path_hygiene(id));
-        if let Some(ValueNs::LocalBinding(local)) = resolution
-            && body.binding_owner(local) != Some(current_closure)
         {
-            upvars.insert(local);
+            // Could be a variable.
+            let guard = self.resolver.update_to_inner_scope(self.db, self.owner, expr);
+            let resolution = self.resolver.resolve_path_in_value_ns_fully(
+                self.db,
+                path,
+                self.store.expr_or_pat_path_hygiene(expr.into()),
+            );
+            if let Some(ValueNs::LocalBinding(local)) = resolution
+                && self.store.binding_owner(local) != Some(*current_closure)
+            {
+                upvars.insert(local);
+            }
+            self.resolver.reset_to_guard(guard);
         }
-        resolver.reset_to_guard(guard);
+    }
+}
+
+impl StoreVisitor for UpvarsMentionedVisitor<'_> {
+    fn on_expr(&mut self, expr: ExprId) {
+        match &self.store[expr] {
+            Expr::Path(path) => {
+                self.resolve_maybe_upvar(expr, path);
+                self.on_path(path);
+            }
+            Expr::Closure {
+                body: body_expr,
+                args,
+                arg_types,
+                ret_type,
+                capture_by: _,
+                closure_kind: _,
+            } => {
+                self.on_pats(args);
+                arg_types.iter().for_each(|arg_ty| self.on_type_opt(*arg_ty));
+                self.on_type_opt(*ret_type);
+
+                let mut old_current_closure =
+                    self.current_closure.replace((expr, FxHashSet::default()));
+                self.on_expr(*body_expr);
+                let closure_upvars = self.current_closure.take().unwrap().1;
+                if !closure_upvars.is_empty() {
+                    self.closures_map.insert(expr, Upvars::new(&closure_upvars));
+                    // All nested closure's upvars are also upvars of the parent closure.
+                    if let Some((current_closure, upvars)) = &mut old_current_closure {
+                        upvars.extend(closure_upvars.iter().copied().filter(|local| {
+                            self.store.binding_owner(*local) != Some(*current_closure)
+                        }));
+                    }
+                }
+                self.current_closure = old_current_closure;
+            }
+            _ => self.store.visit_expr_children(expr, self),
+        }
+    }
+
+    fn on_anon_const_expr(&mut self, expr: ExprId) {
+        // Anon consts don't contribute upvars.
+        let old_current_closure = self.current_closure.take();
+        self.on_expr(expr);
+        self.current_closure = old_current_closure;
+    }
+
+    fn on_pat(&mut self, pat: PatId) {
+        self.store.visit_pat_children(pat, self);
+    }
+
+    fn on_type(&mut self, ty: TypeRefId) {
+        // Anon consts don't contribute upvars.
+        let old_current_closure = self.current_closure.take();
+        self.store.visit_type_ref_children(ty, &mut *self);
+        self.current_closure = old_current_closure;
     }
 }
 
@@ -394,4 +369,21 @@
             expect!["56..65: self"],
         );
     }
+
+    #[test]
+    fn const_block() {
+        check(
+            r#"
+fn main() {
+    || {
+        const {
+            let v = ();
+            v
+        }
+    };
+}
+        "#,
+            expect![""],
+        );
+    }
 }
diff --git a/crates/hir/src/attrs.rs b/crates/hir/src/attrs.rs
index 9a61885..210d6bd 100644
--- a/crates/hir/src/attrs.rs
+++ b/crates/hir/src/attrs.rs
@@ -100,6 +100,8 @@
         }
     }
 
+    /// **Do not** use this to detect visibility of the macro, this does not account for macros 2.0.
+    /// Use this only if you're interested in `#[macro_export]` literally (for example, because it exports under the crate root).
     #[inline]
     pub fn is_macro_export(&self) -> bool {
         self.attrs.contains(AttrFlags::IS_MACRO_EXPORT)
diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs
index 0c4191c..f6df18a 100644
--- a/crates/hir/src/diagnostics.rs
+++ b/crates/hir/src/diagnostics.rs
@@ -32,7 +32,7 @@
 };
 use triomphe::Arc;
 
-use crate::{AssocItem, Field, Function, GenericDef, Local, Trait, Type, TypeOwnerId, Variant};
+use crate::{AssocItem, Field, Function, GenericDef, Trait, Type, TypeOwnerId, Variant};
 
 pub use hir_def::VariantId;
 pub use hir_ty::{
@@ -134,10 +134,8 @@
     MissingFields,
     MissingMatchArms,
     MissingUnsafe,
-    MovedOutOfRef<'db>,
     MutRefInImmRefPat,
     MutableRefBinding,
-    NeedMut<'db>,
     NonExhaustiveLet,
     NonExhaustiveRecordExpr,
     NonExhaustiveRecordPat,
@@ -168,8 +166,6 @@
     UnresolvedMethodCall<'db>,
     UnresolvedModule,
     UnresolvedIdent,
-    UnusedMut<'db>,
-    UnusedVariable<'db>,
     GenericArgsProhibited,
     ParenthesizedGenericArgsWithoutFnTrait,
     BadRtn,
@@ -476,28 +472,6 @@
     pub actual: Type<'db>,
 }
 
-#[derive(Debug)]
-pub struct NeedMut<'db> {
-    pub local: Local<'db>,
-    pub span: InFile<SyntaxNodePtr>,
-}
-
-#[derive(Debug)]
-pub struct UnusedMut<'db> {
-    pub local: Local<'db>,
-}
-
-#[derive(Debug)]
-pub struct UnusedVariable<'db> {
-    pub local: Local<'db>,
-}
-
-#[derive(Debug)]
-pub struct MovedOutOfRef<'db> {
-    pub ty: Type<'db>,
-    pub span: InFile<SyntaxNodePtr>,
-}
-
 #[derive(Debug, PartialEq, Eq)]
 pub struct IncoherentImpl {
     pub file_id: HirFileId,
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index 70e2d3f..474f4bb 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -2098,121 +2098,6 @@
             }
         }
 
-        if let Ok(borrowck_results) = InferBodyId::from(id).borrowck(db) {
-            for borrowck_result in borrowck_results {
-                let mir_body = borrowck_result.mir_body(db);
-                for moof in &borrowck_result.moved_out_of_ref {
-                    let span: InFile<SyntaxNodePtr> = match moof.span {
-                        mir::MirSpan::ExprId(e) => match source_map.expr_syntax(e) {
-                            Ok(s) => s.map(|it| it.into()),
-                            Err(_) => continue,
-                        },
-                        mir::MirSpan::PatId(p) => match source_map.pat_syntax(p) {
-                            Ok(s) => s.map(|it| it.into()),
-                            Err(_) => continue,
-                        },
-                        mir::MirSpan::SelfParam => match source_map.self_param_syntax() {
-                            Some(s) => s.map(|it| it.into()),
-                            None => continue,
-                        },
-                        mir::MirSpan::BindingId(b) => {
-                            match source_map
-                                .patterns_for_binding(b)
-                                .iter()
-                                .find_map(|p| source_map.pat_syntax(*p).ok())
-                            {
-                                Some(s) => s.map(|it| it.into()),
-                                None => continue,
-                            }
-                        }
-                        mir::MirSpan::Unknown => continue,
-                    };
-                    acc.push(
-                        MovedOutOfRef {
-                            ty: Type { owner: type_owner, ty: EarlyBinder::bind(moof.ty.as_ref()) },
-                            span,
-                        }
-                        .into(),
-                    )
-                }
-                let mol = &borrowck_result.mutability_of_locals;
-                for (binding_id, binding_data) in body.bindings() {
-                    if binding_data.problems.is_some() {
-                        // We should report specific diagnostics for these problems, not `need-mut` and `unused-mut`.
-                        continue;
-                    }
-                    let Some(&local) = mir_body.binding_locals.get(binding_id) else {
-                        continue;
-                    };
-                    if source_map
-                        .patterns_for_binding(binding_id)
-                        .iter()
-                        .any(|&pat| source_map.pat_syntax(pat).is_err())
-                    {
-                        // Skip synthetic bindings
-                        continue;
-                    }
-                    let mut need_mut = &mol[local];
-                    if body[binding_id].name == sym::self_
-                        && need_mut == &mir::MutabilityReason::Unused
-                    {
-                        need_mut = &mir::MutabilityReason::Not;
-                    }
-                    let local =
-                        Local { parent: id.into(), parent_infer: mir_body.owner, binding_id };
-                    let is_mut = body[binding_id].mode == BindingAnnotation::Mutable;
-
-                    match (need_mut, is_mut) {
-                        (mir::MutabilityReason::Unused, _) => {
-                            let should_ignore = body[binding_id].name.as_str().starts_with('_');
-                            if !should_ignore {
-                                acc.push(UnusedVariable { local }.into())
-                            }
-                        }
-                        (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(|it| it.into()),
-                                        Err(_) => continue,
-                                    },
-                                    mir::MirSpan::PatId(p) => match source_map.pat_syntax(*p) {
-                                        Ok(s) => s.map(|it| it.into()),
-                                        Err(_) => continue,
-                                    },
-                                    mir::MirSpan::BindingId(b) => {
-                                        match source_map
-                                            .patterns_for_binding(*b)
-                                            .iter()
-                                            .find_map(|p| source_map.pat_syntax(*p).ok())
-                                        {
-                                            Some(s) => s.map(|it| it.into()),
-                                            None => continue,
-                                        }
-                                    }
-                                    mir::MirSpan::SelfParam => match source_map.self_param_syntax()
-                                    {
-                                        Some(s) => s.map(|it| it.into()),
-                                        None => continue,
-                                    },
-                                    mir::MirSpan::Unknown => continue,
-                                };
-                                acc.push(NeedMut { local, span }.into());
-                            }
-                        }
-                        (mir::MutabilityReason::Not, true) => {
-                            let should_ignore = body[binding_id].name.as_str().starts_with('_');
-                            if !should_ignore {
-                                acc.push(UnusedMut { local }.into())
-                            }
-                        }
-                    }
-                }
-            }
-        }
-
         for diagnostic in BodyValidationDiagnostic::collect(db, id, style_lints) {
             acc.extend(AnyDiagnostic::body_validation_diagnostic(db, diagnostic, source_map));
         }
@@ -3310,10 +3195,6 @@
         }
     }
 
-    pub fn is_macro_export(self, db: &dyn HirDatabase) -> bool {
-        matches!(self.id, MacroId::MacroRulesId(_) if AttrFlags::query(db, self.id.into()).contains(AttrFlags::IS_MACRO_EXPORT))
-    }
-
     pub fn is_proc_macro(self) -> bool {
         matches!(self.id, MacroId::ProcMacroId(_))
     }
@@ -3454,7 +3335,13 @@
                 let source = loc.source(db);
                 visibility_from_ast(db, id, source.map(|src| src.visibility()))
             }
-            MacroId::MacroRulesId(_) => Visibility::Public,
+            MacroId::MacroRulesId(id) => {
+                if AttrFlags::query(db, id.into()).contains(AttrFlags::IS_MACRO_EXPORT) {
+                    Visibility::Public
+                } else {
+                    Visibility::PubCrate(self.krate(db).id)
+                }
+            }
             MacroId::ProcMacroId(_) => Visibility::Public,
         }
     }
diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs
index e805676..2090916 100644
--- a/crates/hir/src/source_analyzer.rs
+++ b/crates/hir/src/source_analyzer.rs
@@ -1320,7 +1320,7 @@
                     .first_segment()
                     .and_then(|it| it.name_ref())
                     .and_then(|name_ref| {
-                        ToolModule::by_name(db, self.resolver.krate().into(), &name_ref.text())
+                        ToolModule::by_name(db, self.resolver.krate().into(), name_ref.text())
                             .map(PathResolution::ToolModule)
                     })
                     .map(|it| (it, None)),
@@ -1361,7 +1361,7 @@
             // in this case we have to check for inert/builtin attributes and tools and prioritize
             // resolution of attributes over other namespaces
             if let Some(name_ref) = path.as_single_name_ref() {
-                let builtin = BuiltinAttr::builtin(&name_ref.text());
+                let builtin = BuiltinAttr::builtin(name_ref.text());
                 if builtin.is_some() {
                     return builtin.map(|it| (PathResolution::BuiltinAttr(it), None));
                 }
@@ -1411,7 +1411,7 @@
                     .first_segment()
                     .and_then(|it| it.name_ref())
                     .and_then(|name_ref| {
-                        ToolModule::by_name(db, self.resolver.krate().into(), &name_ref.text())
+                        ToolModule::by_name(db, self.resolver.krate().into(), name_ref.text())
                             .map(PathResolution::ToolModule)
                     })
                     .map(|it| (it, None)),
diff --git a/crates/hir/src/symbols.rs b/crates/hir/src/symbols.rs
index 79a075e..b458dc0 100644
--- a/crates/hir/src/symbols.rs
+++ b/crates/hir/src/symbols.rs
@@ -2,7 +2,7 @@
 
 use std::marker::PhantomData;
 
-use base_db::{FxIndexSet, salsa::Update};
+use base_db::{FxIndexSet, salsa::SalsaValue};
 use either::Either;
 use hir_def::{
     AdtId, AssocItemId, AstIdLoc, Complete, DefWithBodyId, ExternCrateId, HasModule, ImplId,
@@ -28,7 +28,7 @@
 
 /// The actual data that is stored in the index. It should be as compact as
 /// possible.
-#[derive(Clone, PartialEq, Eq, Hash, Update)]
+#[derive(Clone, PartialEq, Eq, Hash, SalsaValue)]
 pub struct FileSymbol<'db> {
     pub name: Symbol,
     pub def: ModuleDef,
diff --git a/crates/hir/src/term_search.rs b/crates/hir/src/term_search.rs
index 1cc6766..0a5d95d 100644
--- a/crates/hir/src/term_search.rs
+++ b/crates/hir/src/term_search.rs
@@ -228,8 +228,6 @@
 /// Configuration options for the term search
 #[derive(Debug, Clone, Copy)]
 pub struct TermSearchConfig {
-    /// Enable borrow checking, this guarantees the outputs of the `term_search` to borrow-check
-    pub enable_borrowcheck: bool,
     /// Indicate when to squash multiple trees to `Many` as there are too many to keep track
     pub many_alternatives_threshold: usize,
     /// Fuel for term search in "units of work"
@@ -238,7 +236,7 @@
 
 impl Default for TermSearchConfig {
     fn default() -> Self {
-        Self { enable_borrowcheck: true, many_alternatives_threshold: 1, fuel: 1200 }
+        Self { many_alternatives_threshold: 1, fuel: 1200 }
     }
 }
 
diff --git a/crates/hir/src/term_search/tactics.rs b/crates/hir/src/term_search/tactics.rs
index c0cfe18..b89921d 100644
--- a/crates/hir/src/term_search/tactics.rs
+++ b/crates/hir/src/term_search/tactics.rs
@@ -10,7 +10,7 @@
 
 use std::iter;
 
-use hir_ty::{db::HirDatabase, mir::BorrowKind};
+use hir_ty::db::HirDatabase;
 use itertools::Itertools;
 use rustc_hash::FxHashSet;
 
@@ -46,42 +46,17 @@
             ScopeDef::ModuleDef(ModuleDef::Const(it)) => Some(Expr::Const(*it)),
             ScopeDef::ModuleDef(ModuleDef::Static(it)) => Some(Expr::Static(*it)),
             ScopeDef::GenericParam(GenericParam::ConstParam(it)) => Some(Expr::ConstParam(*it)),
-            ScopeDef::Local(it) => {
-                if ctx.config.enable_borrowcheck {
-                    let borrowck = it.parent_infer.borrowck(db).ok()?;
-
-                    let invalid = borrowck.iter().any(|b| {
-                        let mir_body = b.mir_body(ctx.sema.db);
-                        b.partially_moved.iter().any(|moved| {
-                            Some(&moved.local) == mir_body.binding_locals.get(it.binding_id)
-                        }) || b.borrow_regions.iter().any(|region| {
-                            // Shared borrows are fine
-                            Some(&region.local) == mir_body.binding_locals.get(it.binding_id)
-                                && region.kind != BorrowKind::Shared
-                        })
-                    });
-
-                    if invalid {
-                        return None;
-                    }
-                }
-
-                Some(Expr::Local(*it))
-            }
+            ScopeDef::Local(it) => Some(Expr::Local(*it)),
             _ => None,
         }?;
 
         let ty = expr.ty(db);
-        lookup.insert(ty.clone(), std::iter::once(expr.clone()));
-
-        // Don't suggest local references as they are not valid for return
-        if matches!(expr, Expr::Local(_))
-            && ty.contains_reference(db)
-            && ctx.config.enable_borrowcheck
-        {
+        if ty.contains_unknown() {
             return None;
         }
 
+        lookup.insert(ty.clone(), std::iter::once(expr.clone()));
+
         ty.instantiate_with_errors().could_unify_with_deeply(db, &ctx.goal).then_some(expr)
     })
 }
@@ -182,10 +157,6 @@
                     return None;
                 }
 
-                // Ignore types that have something to do with lifetimes
-                if ctx.config.enable_borrowcheck && ty.contains_reference(db) {
-                    return None;
-                }
                 let fields = strukt.fields(db);
                 // Check if all fields are visible, otherwise we cannot fill them
                 if fields.iter().any(|it| !it.is_visible_from(db, module)) {
@@ -234,11 +205,6 @@
                     return None;
                 }
 
-                // Ignore types that have something to do with lifetimes
-                if ctx.config.enable_borrowcheck && ty.contains_reference(db) {
-                    return None;
-                }
-
                 let generics: Vec<_> = ty.type_arguments().collect();
                 let exprs = enum_
                     .variants(db)
@@ -372,7 +338,6 @@
                                 crate::Crate::from(ctx.scope.resolver().krate()).edition(db),
                             )
                             || it.is_unstable(db)
-                            || ctx.config.enable_borrowcheck && ret_ty.contains_reference(db)
                             || ret_ty.is_raw_ptr()
                         {
                             return None;
@@ -495,11 +460,6 @@
             }
 
             let ret_ty = it.ret_type(db).instantiate(ty.type_arguments());
-            // Filter out functions that return references
-            if ctx.config.enable_borrowcheck && ret_ty.contains_reference(db) || ret_ty.is_raw_ptr()
-            {
-                return None;
-            }
 
             // Ignore functions that do not change the type
             if ty.instantiate_with_errors().could_unify_with_deeply(db, &ret_ty) {
@@ -695,11 +655,6 @@
             }
 
             let ret_ty = it.ret_type(db).instantiate(ty.type_arguments());
-            // Filter out functions that return references
-            if ctx.config.enable_borrowcheck && ret_ty.contains_reference(db) || ret_ty.is_raw_ptr()
-            {
-                return None;
-            }
 
             // Early exit if some param cannot be filled from lookup
             let param_exprs: Vec<Vec<Expr<'_>>> = it
@@ -763,11 +718,6 @@
                 return None;
             }
 
-            // Ignore types that have something to do with lifetimes
-            if ctx.config.enable_borrowcheck && ty.contains_reference(db) {
-                return None;
-            }
-
             // Early exit if some param cannot be filled from lookup
             let param_exprs: Vec<Vec<Expr<'db>>> =
                 ty.type_arguments().map(|field| lookup.find(db, &field)).collect::<Option<_>>()?;
diff --git a/crates/ide-assists/src/assist_config.rs b/crates/ide-assists/src/assist_config.rs
index 636acf3..c7d69d8 100644
--- a/crates/ide-assists/src/assist_config.rs
+++ b/crates/ide-assists/src/assist_config.rs
@@ -24,7 +24,6 @@
     pub prefer_absolute: bool,
     pub assist_emit_must_use: bool,
     pub term_search_fuel: u64,
-    pub term_search_borrowck: bool,
     pub code_action_grouping: bool,
     pub expr_fill_default: ExprFillDefaultMode,
     pub prefer_self_ty: bool,
diff --git a/crates/ide-assists/src/handlers/apply_demorgan.rs b/crates/ide-assists/src/handlers/apply_demorgan.rs
index 1026244..e2c1048 100644
--- a/crates/ide-assists/src/handlers/apply_demorgan.rs
+++ b/crates/ide-assists/src/handlers/apply_demorgan.rs
@@ -206,7 +206,7 @@
     let closure_body = closure_expr.body()?;
 
     let op_range = method_call.syntax().text_range();
-    let label = format!("Apply De Morgan's law to `Iterator::{}`", name.text().as_str());
+    let label = format!("Apply De Morgan's law to `Iterator::{}`", name.text());
     acc.add_group(
         &GroupLabel("Apply De Morgan's law".to_owned()),
         AssistId::refactor_rewrite("apply_demorgan_iterator"),
@@ -216,7 +216,7 @@
             let editor = builder.make_editor(method_call.syntax());
             let make = editor.make();
             // replace the method name
-            let new_name = match name.text().as_str() {
+            let new_name = match name.text() {
                 "all" => make.name_ref("any"),
                 "any" => make.name_ref("all"),
                 "is_some_and" => make.name_ref("is_none_or"),
diff --git a/crates/ide-assists/src/handlers/convert_closure_to_fn.rs b/crates/ide-assists/src/handlers/convert_closure_to_fn.rs
index c9f5e0a..83effa1 100644
--- a/crates/ide-assists/src/handlers/convert_closure_to_fn.rs
+++ b/crates/ide-assists/src/handlers/convert_closure_to_fn.rs
@@ -507,7 +507,7 @@
     capture_kind: CaptureKind,
     is_ref: bool,
 ) -> ast::Expr {
-    let capture_name = make.expr_path(make.path_from_text(&capture_name.text()));
+    let capture_name = make.expr_path(make.path_from_text(capture_name.text()));
     if capture_kind == CaptureKind::Move || is_ref {
         return capture_name;
     }
diff --git a/crates/ide-assists/src/handlers/convert_into_to_from.rs b/crates/ide-assists/src/handlers/convert_into_to_from.rs
index a01a66e..c8de14b 100644
--- a/crates/ide-assists/src/handlers/convert_into_to_from.rs
+++ b/crates/ide-assists/src/handlers/convert_into_to_from.rs
@@ -109,7 +109,7 @@
             editor.replace(into_fn_name.syntax(), make.name("from").syntax());
 
             for s in selfs {
-                match s.text().as_ref() {
+                match s.text() {
                     "self" => editor.replace(s.syntax(), make.name_ref("val").syntax()),
                     "Self" => {
                         if let Some(path_segment) =
diff --git a/crates/ide-assists/src/handlers/convert_match_to_let_else.rs b/crates/ide-assists/src/handlers/convert_match_to_let_else.rs
index 9dffdf3..db084c6 100644
--- a/crates/ide-assists/src/handlers/convert_match_to_let_else.rs
+++ b/crates/ide-assists/src/handlers/convert_match_to_let_else.rs
@@ -148,8 +148,7 @@
             if let Some(name_ref) = record_pat_field.field_name() {
                 editor.replace(
                     record_pat_field.syntax(),
-                    make.record_pat_field(make.name_ref(&name_ref.text()), binding.clone())
-                        .syntax(),
+                    make.record_pat_field(make.name_ref(name_ref.text()), binding.clone()).syntax(),
                 );
             }
         } else {
diff --git a/crates/ide-assists/src/handlers/convert_range_for_to_while.rs b/crates/ide-assists/src/handlers/convert_range_for_to_while.rs
index 7026b5b..ae8f626 100644
--- a/crates/ide-assists/src/handlers/convert_range_for_to_while.rs
+++ b/crates/ide-assists/src/handlers/convert_range_for_to_while.rs
@@ -74,7 +74,7 @@
 
             let mut elements = vec![];
 
-            let var_expr = make.expr_path(make.ident_path(&name.text()));
+            let var_expr = make.expr_path(make.ident_path(name.text()));
             let op = ast::BinaryOp::CmpOp(ast::CmpOp::Ord {
                 ordering: ast::Ordering::Less,
                 strict: !inclusive,
diff --git a/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs b/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs
index eb74e91..0bb9bf1 100644
--- a/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs
+++ b/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs
@@ -194,7 +194,7 @@
                     let range = ctx.sema.original_range_opt(pat.syntax())?.range;
                     let place = cover_edit_range(source.syntax(), range);
                     let elements = vec![
-                        make.name_ref(&name.text()).syntax().clone().into(),
+                        make.name_ref(name.text()).syntax().clone().into(),
                         make.token(T![:]).into(),
                         make.whitespace(" ").into(),
                     ];
@@ -237,7 +237,7 @@
                     let range = ctx.sema.original_range_opt(expr.syntax())?.range;
                     let place = cover_edit_range(source.syntax(), range);
                     let elements = vec![
-                        make.name_ref(&name.text()).syntax().clone().into(),
+                        make.name_ref(name.text()).syntax().clone().into(),
                         make.token(T![:]).into(),
                         make.whitespace(" ").into(),
                     ];
diff --git a/crates/ide-assists/src/handlers/extract_function.rs b/crates/ide-assists/src/handlers/extract_function.rs
index c2eb49d..46333ed 100644
--- a/crates/ide-assists/src/handlers/extract_function.rs
+++ b/crates/ide-assists/src/handlers/extract_function.rs
@@ -934,7 +934,7 @@
         };
 
         // FIXME: make trait arguments
-        let trait_name = trait_name.map(|name| make.ty_path(make.ident_path(&name.text())).into());
+        let trait_name = trait_name.map(|name| make.ty_path(make.ident_path(name.text())).into());
 
         let parent = self.parent()?;
         let parents = generic_parents(&parent);
@@ -1561,7 +1561,7 @@
     old_indent: IndentLevel,
     make: &SyntaxFactory,
 ) -> ast::Fn {
-    let fun_name = make.name(&fun.name.text());
+    let fun_name = make.name(fun.name.text());
     let params = fun.make_param_list(make, ctx, module, fun.mods.edition);
     let ret_ty = fun.make_ret_ty(make, ctx, module);
     let body = make_body(make, ctx, old_indent, fun);
diff --git a/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs b/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs
index 5e6e74b..c1ac4f1 100644
--- a/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs
+++ b/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs
@@ -241,7 +241,7 @@
                     if matches!(token.kind(), T![lifetime_ident]) =>
                 {
                     if let Some(lt) = lt.lifetime()
-                        && lt.text().as_str() == token.text()
+                        && lt.text() == token.text()
                     {
                         *tag = true;
                         tagged_one = true;
@@ -250,18 +250,15 @@
                 }
                 param if matches!(token.kind(), T![ident]) => {
                     if match param {
-                        ast::GenericParam::ConstParam(konst) => konst
-                            .name()
-                            .map(|name| name.text().as_str() == token.text())
-                            .unwrap_or_default(),
-                        ast::GenericParam::TypeParam(ty) => ty
-                            .name()
-                            .map(|name| name.text().as_str() == token.text())
-                            .unwrap_or_default(),
-                        ast::GenericParam::LifetimeParam(lt) => lt
-                            .lifetime()
-                            .map(|lt| lt.text().as_str() == token.text())
-                            .unwrap_or_default(),
+                        ast::GenericParam::ConstParam(konst) => {
+                            konst.name().map(|name| name.text() == token.text()).unwrap_or_default()
+                        }
+                        ast::GenericParam::TypeParam(ty) => {
+                            ty.name().map(|name| name.text() == token.text()).unwrap_or_default()
+                        }
+                        ast::GenericParam::LifetimeParam(lt) => {
+                            lt.lifetime().map(|lt| lt.text() == token.text()).unwrap_or_default()
+                        }
                     } {
                         *tag = true;
                         tagged_one = true;
@@ -338,7 +335,7 @@
     // FIXME: replace with a `ast::make` constructor
     let ty = match generic_args {
         Some(generic_args) => make.ty(&format!("{name}{generic_args}")),
-        None => make.ty(&name.text()),
+        None => make.ty(name.text()),
     };
 
     // change from a record to a tuple field list
diff --git a/crates/ide-assists/src/handlers/extract_type_alias.rs b/crates/ide-assists/src/handlers/extract_type_alias.rs
index 329f832..a378256 100644
--- a/crates/ide-assists/src/handlers/extract_type_alias.rs
+++ b/crates/ide-assists/src/handlers/extract_type_alias.rs
@@ -145,7 +145,7 @@
                             .filter_map(|it| match it {
                                 ast::GenericArg::LifetimeArg(lt) => {
                                     let lt = lt.lifetime()?;
-                                    known_generics.iter().find(find_lifetime(&lt.text()))
+                                    known_generics.iter().find(find_lifetime(lt.text()))
                                 }
                                 _ => None,
                             }),
@@ -157,7 +157,7 @@
                     generics.extend(
                         it.bounds()
                             .filter_map(|it| it.lifetime())
-                            .filter_map(|lt| known_generics.iter().find(find_lifetime(&lt.text()))),
+                            .filter_map(|lt| known_generics.iter().find(find_lifetime(lt.text()))),
                     );
                 }
             }
@@ -166,13 +166,12 @@
                     generics.extend(
                         it.bounds()
                             .filter_map(|it| it.lifetime())
-                            .filter_map(|lt| known_generics.iter().find(find_lifetime(&lt.text()))),
+                            .filter_map(|lt| known_generics.iter().find(find_lifetime(lt.text()))),
                     );
                 }
             }
             ast::Type::RefType(ref_) => generics.extend(
-                ref_.lifetime()
-                    .and_then(|lt| known_generics.iter().find(find_lifetime(&lt.text()))),
+                ref_.lifetime().and_then(|lt| known_generics.iter().find(find_lifetime(lt.text()))),
             ),
             ast::Type::ArrayType(ar) => {
                 if let Some(ast::Expr::PathExpr(p)) = ar.const_arg().and_then(|x| x.expr())
diff --git a/crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs b/crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs
index acd98ae..738f461 100644
--- a/crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs
+++ b/crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs
@@ -75,12 +75,12 @@
         |builder| {
             let editor = builder.make_editor(traitd.syntax());
             let make = editor.make();
-            let namety = make.ty_path(make.path_from_text(&name.text()));
+            let namety = make.ty_path(make.path_from_text(name.text()));
             let trait_where_clause = traitd.where_clause().map(|it| it.reset_indent());
             let bounds = traitd.type_bound_list().and_then(|list| exclude_sized(make, list));
             let is_unsafe = traitd.unsafe_token().is_some();
             let thisname = this_name(make, &traitd);
-            let thisty = make.ty_path(make.path_from_text(&thisname.text()));
+            let thisty = make.ty_path(make.path_from_text(thisname.text()));
             let indent = traitd.indent_level();
 
             let gendecl = make.generic_param_list([GenericParam::TypeParam(make.type_param(
diff --git a/crates/ide-assists/src/handlers/generate_default_from_enum_variant.rs b/crates/ide-assists/src/handlers/generate_default_from_enum_variant.rs
index 713d6a3..07c191b 100644
--- a/crates/ide-assists/src/handlers/generate_default_from_enum_variant.rs
+++ b/crates/ide-assists/src/handlers/generate_default_from_enum_variant.rs
@@ -76,7 +76,7 @@
 
 fn default_impl(variant_name: ast::Name, adt: &ast::Adt, make: &SyntaxFactory) -> ast::Impl {
     let impl_ = utils::generate_trait_impl_intransitive(make, adt, make.ty("Default"));
-    let fn_ = default_fn(&variant_name.text(), make);
+    let fn_ = default_fn(variant_name.text(), make);
     let (impl_editor, impl_) = SyntaxEditor::with_ast_node(&impl_);
 
     impl_
diff --git a/crates/ide-assists/src/handlers/generate_enum_is_method.rs b/crates/ide-assists/src/handlers/generate_enum_is_method.rs
index 53e77b4..5e2ee77 100644
--- a/crates/ide-assists/src/handlers/generate_enum_is_method.rs
+++ b/crates/ide-assists/src/handlers/generate_enum_is_method.rs
@@ -141,7 +141,7 @@
         };
 
         let variant_name = variant.name()?;
-        let fn_name = format!("is_{}", to_lower_snake_case(&variant_name.text()));
+        let fn_name = format!("is_{}", to_lower_snake_case(variant_name.text()));
         Some(Method { pattern_suffix, fn_name, variant_name })
     }
 }
diff --git a/crates/ide-assists/src/handlers/generate_enum_projection_method.rs b/crates/ide-assists/src/handlers/generate_enum_projection_method.rs
index 8a194ae..479143c 100644
--- a/crates/ide-assists/src/handlers/generate_enum_projection_method.rs
+++ b/crates/ide-assists/src/handlers/generate_enum_projection_method.rs
@@ -219,7 +219,7 @@
     fn new(variant: &ast::Variant, fn_name_prefix: &str) -> Option<Self> {
         use itertools::Itertools as _;
         let variant_name = variant.name()?;
-        let fn_name = format!("{fn_name_prefix}_{}", to_lower_snake_case(&variant_name.text()));
+        let fn_name = format!("{fn_name_prefix}_{}", to_lower_snake_case(variant_name.text()));
 
         match variant.kind() {
             ast::StructKind::Record(record) => {
diff --git a/crates/ide-assists/src/handlers/generate_enum_variant.rs b/crates/ide-assists/src/handlers/generate_enum_variant.rs
index fb43e3e..73837f4 100644
--- a/crates/ide-assists/src/handlers/generate_enum_variant.rs
+++ b/crates/ide-assists/src/handlers/generate_enum_variant.rs
@@ -62,7 +62,7 @@
         let editor = builder.make_editor(enum_node.syntax());
         let make = editor.make();
         let field_list = parent.make_field_list(ctx, make);
-        let variant = make.variant(None, make.name(&name_ref.text()), field_list, None);
+        let variant = make.variant(None, make.name(name_ref.text()), field_list, None);
         if let Some(it) = enum_node.variant_list() {
             it.add_variant(&editor, &variant);
         }
diff --git a/crates/ide-assists/src/handlers/generate_function.rs b/crates/ide-assists/src/handlers/generate_function.rs
index 3c3fde8..13096c6 100644
--- a/crates/ide-assists/src/handlers/generate_function.rs
+++ b/crates/ide-assists/src/handlers/generate_function.rs
@@ -67,7 +67,7 @@
         return None;
     }
 
-    let fn_name = &*name_ref.text();
+    let fn_name = name_ref.text();
     let TargetInfo { target_module, adt_info, target, file } =
         fn_target_info(ctx, path, &call, fn_name)?;
 
@@ -159,7 +159,7 @@
     let (impl_, file) = if let Some(impl_) = cursor_impl {
         (Some(impl_), ctx.vfs_file_id())
     } else {
-        get_adt_source(ctx, &adt, fn_name.text().as_str())?
+        get_adt_source(ctx, &adt, fn_name.text())?
     };
     let target = get_method_target(ctx, &impl_, &adt)?;
 
@@ -269,7 +269,7 @@
         // If generated function has the name "new" and is an associated function, we generate fn body
         // as a constructor and assume a "Self" return type.
         if let Some(body) =
-            make_fn_body_as_new_function(make, ctx, &fn_name.text(), adt_info, target_edition)
+            make_fn_body_as_new_function(make, ctx, fn_name.text(), adt_info, target_edition)
         {
             ret_type = Some(make.ret_type(make.ty_path(make.ident_path("Self")).into()));
             should_focus_return_type = false;
diff --git a/crates/ide-assists/src/handlers/generate_getter_or_setter.rs b/crates/ide-assists/src/handlers/generate_getter_or_setter.rs
index 7e5d5ce..b21e608 100644
--- a/crates/ide-assists/src/handlers/generate_getter_or_setter.rs
+++ b/crates/ide-assists/src/handlers/generate_getter_or_setter.rs
@@ -225,7 +225,7 @@
         (
             make.ty_ref(record_field_info.field_ty.clone(), true),
             make.expr_ref(
-                make.expr_field(self_expr, &record_field_info.field_name.text()).into(),
+                make.expr_field(self_expr, record_field_info.field_name.text()).into(),
                 true,
             ),
         )
@@ -250,7 +250,7 @@
                 make.expr_ref(
                     make.expr_field(
                         make.expr_path(make.ident_path("self")),
-                        &record_field_info.field_name.text(),
+                        record_field_info.field_name.text(),
                     )
                     .into(),
                     false,
diff --git a/crates/ide-assists/src/handlers/generate_impl.rs b/crates/ide-assists/src/handlers/generate_impl.rs
index ab0eb56..ecff626 100644
--- a/crates/ide-assists/src/handlers/generate_impl.rs
+++ b/crates/ide-assists/src/handlers/generate_impl.rs
@@ -185,7 +185,7 @@
                     None,
                     None,
                     false,
-                    make.ty(&name.text()),
+                    make.ty(name.text()),
                     make.ty_placeholder(),
                     None,
                     None,
diff --git a/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs b/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs
index fd095dd..6858b62 100644
--- a/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs
+++ b/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs
@@ -210,7 +210,7 @@
 }
 
 fn process_method_name(name: ast::Name) -> Option<(ast::Name, &'static str)> {
-    let new_name = match &*name.text() {
+    let new_name = match name.text() {
         "index" => "index_mut",
         "as_ref" => "as_mut",
         "borrow" => "borrow_mut",
diff --git a/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs b/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs
index d5629e2..242712f 100644
--- a/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs
+++ b/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs
@@ -4,14 +4,11 @@
     RootDatabase, famous_defs::FamousDefs, helpers::mod_path_to_ast_with_factory,
     imports::import_assets::item_for_path_search,
 };
-use syntax::syntax_editor::{Position, SyntaxEditor};
-use syntax::{
-    TokenText,
-    ast::{
-        self, AstNode, HasAttrs, HasGenericParams, HasName, edit::AstNodeEdit,
-        syntax_factory::SyntaxFactory,
-    },
+use syntax::ast::{
+    self, AstNode, HasAttrs, HasGenericParams, HasName, edit::AstNodeEdit,
+    syntax_factory::SyntaxFactory,
 };
+use syntax::syntax_editor::{Position, SyntaxEditor};
 
 use crate::{
     AssistId,
@@ -71,8 +68,7 @@
         return None;
     }
 
-    let main_field_name =
-        names.as_ref().map_or(TokenText::borrowed("value"), |names| names[main_field_i].text());
+    let main_field_name = names.as_ref().map_or("value", |names| names[main_field_i].text());
     let main_field_ty = types[main_field_i].clone();
 
     acc.add(
@@ -92,10 +88,10 @@
                 false,
             ));
 
-            let ty = make.ty(&strukt_name.text());
+            let ty = make.ty(strukt_name.text());
 
             let constructor =
-                make_adt_constructor(names.as_deref(), constructors, &main_field_name, make);
+                make_adt_constructor(names.as_deref(), constructors, main_field_name, make);
             let body = make.block_expr([], Some(constructor));
 
             let fn_ = make
@@ -108,7 +104,7 @@
                     make.param_list(
                         None,
                         [make.param(
-                            make.path_pat(make.path_from_text(&main_field_name)),
+                            make.path_pat(make.path_from_text(main_field_name)),
                             main_field_ty,
                         )],
                     ),
@@ -161,12 +157,12 @@
 fn make_adt_constructor(
     names: Option<&[ast::Name]>,
     constructors: Vec<Option<ast::Expr>>,
-    main_field_name: &TokenText<'_>,
+    main_field_name: &str,
     make: &SyntaxFactory,
 ) -> ast::Expr {
     if let Some(names) = names {
         let fields = make.record_expr_field_list(names.iter().zip(constructors).map(
-            |(name, initializer)| make.record_expr_field(make.name_ref(&name.text()), initializer),
+            |(name, initializer)| make.record_expr_field(make.name_ref(name.text()), initializer),
         ));
         make.record_expr(make.path_from_text("Self"), fields).into()
     } else {
diff --git a/crates/ide-assists/src/handlers/generate_trait_from_impl.rs b/crates/ide-assists/src/handlers/generate_trait_from_impl.rs
index 12afd9a..354447c 100644
--- a/crates/ide-assists/src/handlers/generate_trait_from_impl.rs
+++ b/crates/ide-assists/src/handlers/generate_trait_from_impl.rs
@@ -117,7 +117,7 @@
             let params = used_params(&impl_ast, make, ctx);
             let trait_ast = make.trait_(
                 false,
-                &trait_name(&impl_assoc_items, make).text(),
+                trait_name(&impl_assoc_items, make).text(),
                 params.clone(),
                 impl_ast.where_clause(),
                 trait_items,
@@ -204,7 +204,7 @@
     fn_names
         .next()
         .and_then(|name| {
-            fn_names.next().is_none().then(|| make.name(&stdx::to_camel_case(&name.text())))
+            fn_names.next().is_none().then(|| make.name(&stdx::to_camel_case(name.text())))
         })
         .unwrap_or_else(|| make.name("NewTrait"))
 }
diff --git a/crates/ide-assists/src/handlers/inline_type_alias.rs b/crates/ide-assists/src/handlers/inline_type_alias.rs
index bb76e27..f5d5400 100644
--- a/crates/ide-assists/src/handlers/inline_type_alias.rs
+++ b/crates/ide-assists/src/handlers/inline_type_alias.rs
@@ -140,7 +140,7 @@
                     let src = adt.source(ctx.db())?.value;
                     let name = src.name()?;
                     let generic_params = src.generic_param_list();
-                    let name_ref = make.name_ref(&name.text());
+                    let name_ref = make.name_ref(name.text());
                     let segment = match generic_params {
                         Some(params) => {
                             make.path_segment_generics(name_ref, params.to_generic_args(&make))
diff --git a/crates/ide-assists/src/handlers/merge_match_arms.rs b/crates/ide-assists/src/handlers/merge_match_arms.rs
index f417691..5060886 100644
--- a/crates/ide-assists/src/handlers/merge_match_arms.rs
+++ b/crates/ide-assists/src/handlers/merge_match_arms.rs
@@ -165,7 +165,7 @@
                     {
                         let pat_type = ctx.sema.type_of_binding_in_pat(ident_pat);
 
-                        map.insert(name.text().to_string(), pat_type);
+                        map.insert(name.text().to_owned(), pat_type);
                     }
                 }
                 _ => (),
diff --git a/crates/ide-assists/src/handlers/remove_unused_imports.rs b/crates/ide-assists/src/handlers/remove_unused_imports.rs
index d6582a9..aeffb8a 100644
--- a/crates/ide-assists/src/handlers/remove_unused_imports.rs
+++ b/crates/ide-assists/src/handlers/remove_unused_imports.rs
@@ -8,7 +8,7 @@
 use ide_db::{
     FxHashMap, RootDatabase,
     defs::Definition,
-    search::{FileReference, ReferenceCategory, SearchScope},
+    search::{ReferenceCategory, SearchScope},
 };
 use syntax::{
     AstNode,
@@ -139,7 +139,7 @@
 fn is_path_per_ns_unused_in_scope(
     ctx: &AssistContext<'_, '_>,
     u: &ast::UseTree,
-    scope: &mut Vec<SearchScope>,
+    scope: &[SearchScope],
     path: &PathResolutionPerNs<'_>,
 ) -> bool {
     if let Some(PathResolution::Def(ModuleDef::Trait(ref t))) = path.type_ns {
@@ -158,7 +158,7 @@
 fn is_path_unused_in_scope(
     ctx: &AssistContext<'_, '_>,
     u: &ast::UseTree,
-    scope: &mut Vec<SearchScope>,
+    scope: &[SearchScope],
     path: &[Option<PathResolution<'_>>],
 ) -> bool {
     !path
@@ -174,7 +174,7 @@
 fn is_trait_unused_in_scope(
     ctx: &AssistContext<'_, '_>,
     u: &ast::UseTree,
-    scope: &mut Vec<SearchScope>,
+    scope: &[SearchScope],
     t: &hir::Trait,
 ) -> bool {
     !std::iter::once((Definition::Trait(*t), u.rename()))
@@ -186,30 +186,16 @@
     ctx: &AssistContext<'_, 'db>,
     def: Definition<'db>,
     rename: Option<Rename>,
-    scopes: &Vec<SearchScope>,
+    scopes: &[SearchScope],
 ) -> bool {
-    let mut found = false;
-
-    for scope in scopes {
-        let mut search_non_import = |_, r: FileReference| {
-            // The import itself is a use; we must skip that.
-            if !r.category.contains(ReferenceCategory::IMPORT) {
-                found = true;
-                true
-            } else {
-                false
-            }
-        };
+    scopes.iter().any(|scope| {
+        // The import itself is a use; we must skip that.
         def.usages(&ctx.sema)
+            .set_included_categories(ReferenceCategory::IMPORT.complement())
             .in_scope(scope)
             .with_rename(rename.as_ref())
-            .search(&mut search_non_import);
-        if found {
-            break;
-        }
-    }
-
-    found
+            .at_least_one()
+    })
 }
 
 /// Build a search scope spanning the given module but none of its submodules.
diff --git a/crates/ide-assists/src/handlers/reorder_impl_items.rs b/crates/ide-assists/src/handlers/reorder_impl_items.rs
index 658947a..ed5a372 100644
--- a/crates/ide-assists/src/handlers/reorder_impl_items.rs
+++ b/crates/ide-assists/src/handlers/reorder_impl_items.rs
@@ -82,7 +82,7 @@
                 ast::AssocItem::MacroCall(_) => None,
             };
 
-            name.and_then(|n| ranks.get(n.text().as_str().trim_start_matches("r#")).copied())
+            name.and_then(|n| ranks.get(n.text().trim_start_matches("r#")).copied())
                 .unwrap_or(usize::MAX)
         })
         .collect();
diff --git a/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs b/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs
index 17ee859..a414db0 100644
--- a/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs
+++ b/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs
@@ -39,7 +39,7 @@
     let (_, receiver_ty) = callable.receiver_param(ctx.sema.db)?;
     let n_params = callable.n_params() + 1;
 
-    let method_name_lazy = lazy_method_name(&method_name.text());
+    let method_name_lazy = lazy_method_name(method_name.text());
 
     receiver_ty.iterate_method_candidates_with_traits(
         ctx.sema.db,
@@ -156,7 +156,7 @@
     }
 
     let method_name_text = method_name.text();
-    let method_name_eager = eager_method_name(&method_name_text)?;
+    let method_name_eager = eager_method_name(method_name_text)?;
 
     receiver_ty.iterate_method_candidates_with_traits(
         ctx.sema.db,
diff --git a/crates/ide-assists/src/handlers/term_search.rs b/crates/ide-assists/src/handlers/term_search.rs
index 9036f195..5025f60 100644
--- a/crates/ide-assists/src/handlers/term_search.rs
+++ b/crates/ide-assists/src/handlers/term_search.rs
@@ -39,11 +39,7 @@
         sema: &ctx.sema,
         scope: &scope,
         goal: target_ty,
-        config: TermSearchConfig {
-            fuel: ctx.config.term_search_fuel,
-            enable_borrowcheck: ctx.config.term_search_borrowck,
-            ..Default::default()
-        },
+        config: TermSearchConfig { fuel: ctx.config.term_search_fuel, ..Default::default() },
     };
     let paths = hir::term_search::term_search(&term_search_ctx);
 
diff --git a/crates/ide-assists/src/tests.rs b/crates/ide-assists/src/tests.rs
index 3624099..220b0c3 100644
--- a/crates/ide-assists/src/tests.rs
+++ b/crates/ide-assists/src/tests.rs
@@ -34,7 +34,6 @@
     prefer_absolute: false,
     assist_emit_must_use: false,
     term_search_fuel: 400,
-    term_search_borrowck: true,
     code_action_grouping: true,
     expr_fill_default: ExprFillDefaultMode::Todo,
     prefer_self_ty: false,
@@ -56,7 +55,6 @@
     prefer_absolute: false,
     assist_emit_must_use: false,
     term_search_fuel: 400,
-    term_search_borrowck: true,
     code_action_grouping: false,
     expr_fill_default: ExprFillDefaultMode::Todo,
     prefer_self_ty: false,
@@ -78,7 +76,6 @@
     prefer_absolute: false,
     assist_emit_must_use: false,
     term_search_fuel: 400,
-    term_search_borrowck: true,
     code_action_grouping: true,
     expr_fill_default: ExprFillDefaultMode::Todo,
     prefer_self_ty: false,
@@ -100,7 +97,6 @@
     prefer_absolute: false,
     assist_emit_must_use: false,
     term_search_fuel: 400,
-    term_search_borrowck: true,
     code_action_grouping: true,
     expr_fill_default: ExprFillDefaultMode::Todo,
     prefer_self_ty: false,
@@ -500,6 +496,35 @@
 }
 
 #[test]
+fn assist_order_term_search_unknown_type() {
+    let (db, frange) = RootDatabase::with_range(
+        r#"
+//- minicore: todo, unimplemented
+fn foo(_: u32) -> i32 { 2 }
+fn main() {
+    let unknown;
+    let _: i32 = $0todo!()$0;
+}
+"#,
+    );
+
+    let assists = assists(
+        &db,
+        &TEST_CONFIG,
+        AssistResolveStrategy::None,
+        FileRange { file_id: frange.file_id.file_id(&db), range: frange.range },
+    );
+    let expected = labels(&assists);
+
+    expect![[r#"
+        Inline macro
+        Extract into...
+        Extract Module
+    "#]]
+    .assert_eq(&expected);
+}
+
+#[test]
 fn assist_filter_works() {
     let (db, frange) = RootDatabase::with_range(
         r#"
diff --git a/crates/ide-assists/src/utils.rs b/crates/ide-assists/src/utils.rs
index 670a030..388aac1 100644
--- a/crates/ide-assists/src/utils.rs
+++ b/crates/ide-assists/src/utils.rs
@@ -344,7 +344,7 @@
             let method = mce.name_ref()?;
             let arg_list = mce.arg_list()?;
 
-            let method = match method.text().as_str() {
+            let method = match method.text() {
                 "is_some" => "is_none",
                 "is_none" => "is_some",
                 "is_ok" => "is_err",
@@ -550,7 +550,7 @@
         for item in il.assoc_items() {
             if let ast::AssocItem::Fn(f) = item
                 && let Some(name) = f.name()
-                && names.iter().any(|n| n.eq_ignore_ascii_case(&name.text()))
+                && names.iter().any(|n| n.eq_ignore_ascii_case(name.text()))
             {
                 return true;
             }
@@ -664,7 +664,7 @@
         .zip(generic_params.as_ref())
         .and_then(|(trait_, params)| generic_param_associated_bounds(make, adt, trait_, params));
 
-    let ty: ast::Type = make.ty_path(make.ident_path(&adt.name().unwrap().text())).into();
+    let ty: ast::Type = make.ty_path(make.ident_path(adt.name().unwrap().text())).into();
 
     let cfg_attrs = adt.attrs().filter(|attr| matches!(attr.meta(), Some(ast::Meta::CfgMeta(_))));
     match trait_ {
diff --git a/crates/ide-assists/src/utils/gen_trait_fn_body.rs b/crates/ide-assists/src/utils/gen_trait_fn_body.rs
index c0ddcb9..277b5bd 100644
--- a/crates/ide-assists/src/utils/gen_trait_fn_body.rs
+++ b/crates/ide-assists/src/utils/gen_trait_fn_body.rs
@@ -21,7 +21,7 @@
     trait_ref: Option<TraitRef<'_>>,
 ) -> Option<ast::BlockExpr> {
     let _ = func.body()?;
-    match trait_path.segment()?.name_ref()?.text().as_str() {
+    match trait_path.segment()?.name_ref()?.text() {
         "Clone" => {
             stdx::always!(func.name().is_some_and(|name| name.text() == "clone"));
             gen_clone_impl(make, adt)
diff --git a/crates/ide-completion/src/completions/attribute.rs b/crates/ide-completion/src/completions/attribute.rs
index 109ebce..c2d7cb9 100644
--- a/crates/ide-completion/src/completions/attribute.rs
+++ b/crates/ide-completion/src/completions/attribute.rs
@@ -40,7 +40,6 @@
     let path = attribute.path()?;
     let segments = path.segments().map(|s| s.name_ref()).collect::<Option<Vec<_>>>()?;
     let segments = segments.iter().map(|n| n.text()).collect::<Vec<_>>();
-    let segments = segments.iter().map(|t| t.as_str()).collect::<Vec<_>>();
     let tt = attribute.token_tree()?;
 
     match segments.as_slice() {
diff --git a/crates/ide-completion/src/completions/attribute/repr.rs b/crates/ide-completion/src/completions/attribute/repr.rs
index 63cddb3..73ba847 100644
--- a/crates/ide-completion/src/completions/attribute/repr.rs
+++ b/crates/ide-completion/src/completions/attribute/repr.rs
@@ -23,7 +23,7 @@
             })
             .any(|it| {
                 let text = it.text();
-                lookup.unwrap_or(label) == text || collides.contains(&text.as_str())
+                lookup.unwrap_or(label) == text || collides.contains(&text)
             });
         if repr_already_annotated {
             continue;
diff --git a/crates/ide-completion/src/completions/expr.rs b/crates/ide-completion/src/completions/expr.rs
index 889beea..3e95128 100644
--- a/crates/ide-completion/src/completions/expr.rs
+++ b/crates/ide-completion/src/completions/expr.rs
@@ -484,7 +484,6 @@
             scope: &ctx.scope,
             goal: ty.clone(),
             config: hir::term_search::TermSearchConfig {
-                enable_borrowcheck: false,
                 many_alternatives_threshold: 1,
                 fuel: 200,
             },
diff --git a/crates/ide-completion/src/completions/postfix.rs b/crates/ide-completion/src/completions/postfix.rs
index 5a3a3ac..34b53e5 100644
--- a/crates/ide-completion/src/completions/postfix.rs
+++ b/crates/ide-completion/src/completions/postfix.rs
@@ -365,7 +365,7 @@
     match receiver {
         ast::Expr::PathExpr(path) => {
             if let Some(name) = path.path().and_then(|it| it.as_single_name_ref()) {
-                return placeholder(name.text().as_str());
+                return placeholder(name.text());
             }
         }
         ast::Expr::RefExpr(it) => {
diff --git a/crates/ide-completion/src/render.rs b/crates/ide-completion/src/render.rs
index 9f8a2f7..43b2a53 100644
--- a/crates/ide-completion/src/render.rs
+++ b/crates/ide-completion/src/render.rs
@@ -3226,9 +3226,7 @@
     foo($0);
 }
             "#,
-            // FIXME: term_search exclude ssss.0 (field.ty().is_unknown())
             expect![[r#"
-                ex ssss.0  [type_could_unify]
                 lc ssss S<{unknown}> [local]
                 st S S<T> []
                 md core::  []
diff --git a/crates/ide-completion/src/render/function.rs b/crates/ide-completion/src/render/function.rs
index 4f70a90..2fa8a9d 100644
--- a/crates/ide-completion/src/render/function.rs
+++ b/crates/ide-completion/src/render/function.rs
@@ -233,7 +233,8 @@
                         Some(n) => {
                             let smol_str = n.display_no_db(ctx.edition).to_smolstr();
                             let text = smol_str.as_str().trim_start_matches('_');
-                            let ref_ = ref_of_param(ctx, text, param.ty());
+                            let ref_ =
+                                ref_of_param(ctx, text, &param.ty().instantiate_with_errors());
                             f(&format_args!("${{{}:{ref_}{text}}}", index + offset))
                         }
                         None => {
@@ -288,14 +289,16 @@
 }
 
 fn ref_of_param(ctx: &CompletionContext<'_, '_>, arg: &str, ty: &hir::Type<'_>) -> &'static str {
-    if let Some(derefed_ty) = ty.as_reference_inner() {
+    if let Some((_, mutability)) = ty.as_reference() {
+        let ref_prefix = if mutability.is_mut() { "&mut " } else { "&" };
+
         for (name, local) in ctx.locals.iter().sorted_by_key(|&(k, _)| k.clone()) {
             if name.as_str() == arg {
-                return if local.ty(ctx.db) == derefed_ty {
-                    if ty.is_mutable_reference() { "&mut " } else { "&" }
-                } else {
-                    ""
-                };
+                let local_ty = local.ty(ctx.db);
+                let added_ref = local_ty.add_reference(ctx.db, mutability);
+                let needs_ref =
+                    !local_ty.could_coerce_to(ctx.db, ty) && added_ref.could_coerce_to(ctx.db, ty);
+                return if needs_ref { ref_prefix } else { "" };
             }
         }
     }
@@ -333,7 +336,7 @@
     let mut detail = String::with_capacity(signature.len());
 
     for segment in signature.split_whitespace() {
-        if !detail.is_empty() {
+        if !detail.is_empty() && !detail.ends_with('(') && !segment.starts_with(')') {
             detail.push(' ');
         }
 
@@ -473,7 +476,7 @@
             r#"
 struct S {}
 impl S {
-    fn foo(&self, x: i32) {
+    fn foo(&self, x: i32, y: &i32) {
         $0
     }
 }
@@ -481,8 +484,8 @@
             r#"
 struct S {}
 impl S {
-    fn foo(&self, x: i32) {
-        self.foo(${1:x});$0
+    fn foo(&self, x: i32, y: &i32) {
+        self.foo(${1:x}, ${2:y});$0
     }
 }
 "#,
@@ -563,6 +566,24 @@
 }
 "#,
         );
+        check_edit(
+            "ref_arg",
+            r#"
+//- minicore: coerce_unsized
+fn ref_arg(x: &[i32]) {}
+fn main() {
+    let x = [2];
+    ref_ar$0
+}
+"#,
+            r#"
+fn ref_arg(x: &[i32]) {}
+fn main() {
+    let x = [2];
+    ref_arg(${1:&x});$0
+}
+"#,
+        );
     }
 
     #[test]
diff --git a/crates/ide-completion/src/tests/special.rs b/crates/ide-completion/src/tests/special.rs
index bc7b727..6c2502a4 100644
--- a/crates/ide-completion/src/tests/special.rs
+++ b/crates/ide-completion/src/tests/special.rs
@@ -1617,6 +1617,19 @@
         expect!("const fn(&'foo mut self, &Foo) -> !"),
         expect!("pub const fn baz<'foo>(&'foo mut self, x: &'foo Foo) -> !"),
     );
+
+    check_signatures(
+        r#"
+struct Foo;
+impl Foo {
+    pub fn method(&self, first: bool, second: bool, third: bool, fourth: bool) {}
+}
+fn main() { Foo.m$0 }
+"#,
+        CompletionItemKind::SymbolKind(SymbolKind::Method),
+        expect!("fn(&self, bool, bool, bool, bool)"),
+        expect!("pub fn method(&self, first: bool, second: bool, third: bool, fourth: bool)"),
+    );
 }
 
 #[test]
diff --git a/crates/ide-db/src/helpers.rs b/crates/ide-db/src/helpers.rs
index 00e81b3..08e4b12 100644
--- a/crates/ide-db/src/helpers.rs
+++ b/crates/ide-db/src/helpers.rs
@@ -3,7 +3,7 @@
 use std::collections::VecDeque;
 
 use base_db::SourceDatabase;
-use hir::{Crate, ItemInNs, ModuleDef, Name, Semantics};
+use hir::{Crate, HasAttrs, ItemInNs, ModuleDef, Name, Semantics};
 use span::{Edition, FileId};
 use syntax::{
     AstToken, SyntaxKind, SyntaxToken, ToSmolStr, TokenAtOffset,
@@ -110,7 +110,7 @@
         .legacy_macros(db)
         .into_iter()
         // don't show legacy macros declared in the crate-root that were already covered in declarations earlier
-        .filter(|it| !(is_root && it.is_macro_export(db)))
+        .filter(|it| !(is_root && it.attrs(db).is_macro_export()))
         .for_each(|mac| cb(mac.into()));
 }
 
diff --git a/crates/ide-db/src/imports/import_assets.rs b/crates/ide-db/src/imports/import_assets.rs
index f5dff47..422648c 100644
--- a/crates/ide-db/src/imports/import_assets.rs
+++ b/crates/ide-db/src/imports/import_assets.rs
@@ -979,7 +979,7 @@
             return None;
         }
         let after = std::iter::successors(path.parent_path(), |it| it.parent_path())
-            .map(|seg| seg.segment()?.name_ref().map(|name| Name::new_root(&name.text())))
+            .map(|seg| seg.segment()?.name_ref().map(|name| Name::new_root(name.text())))
             .collect::<Option<_>>()?;
         path_import_candidate(
             sema,
@@ -993,7 +993,7 @@
     fn for_name(sema: &Semantics<'db, RootDatabase>, name: &ast::Name) -> Option<Self> {
         if sema
             .scope(name.syntax())?
-            .speculative_resolve(&make::ext::ident_path(&name.text()))
+            .speculative_resolve(&make::ext::ident_path(name.text()))
             .is_some()
         {
             return None;
@@ -1033,7 +1033,7 @@
                 if qualifier.first_qualifier().is_none_or(|it| sema.resolve_path(&it).is_none()) {
                     let qualifier = qualifier
                         .segments()
-                        .map(|seg| seg.name_ref().map(|name| Name::new_root(&name.text())))
+                        .map(|seg| seg.name_ref().map(|name| Name::new_root(name.text())))
                         .collect::<Option<Vec<_>>>()?;
                     ImportCandidate::Path(PathImportCandidate {
                         qualifier,
diff --git a/crates/ide-db/src/imports/insert_use.rs b/crates/ide-db/src/imports/insert_use.rs
index 27e3ed6..0235389 100644
--- a/crates/ide-db/src/imports/insert_use.rs
+++ b/crates/ide-db/src/imports/insert_use.rs
@@ -314,7 +314,7 @@
             PathSegmentKind::SelfKw => ImportGroup::ThisModule,
             PathSegmentKind::SuperKw => ImportGroup::SuperModule,
             PathSegmentKind::CrateKw => ImportGroup::ThisCrate,
-            PathSegmentKind::Name(name) => match name.text().as_str() {
+            PathSegmentKind::Name(name) => match name.text() {
                 "std" => ImportGroup::Std,
                 "core" => ImportGroup::Std,
                 _ => ImportGroup::ExternCrate,
diff --git a/crates/ide-db/src/imports/merge_imports.rs b/crates/ide-db/src/imports/merge_imports.rs
index 5909905..f925145 100644
--- a/crates/ide-db/src/imports/merge_imports.rs
+++ b/crates/ide-db/src/imports/merge_imports.rs
@@ -582,8 +582,8 @@
                 (Some(_), None) => Ordering::Greater,
                 (None, Some(_)) => Ordering::Less,
                 (Some(a_name), Some(b_name)) => {
-                    let a_text = a_name.as_str().trim_start_matches("r#");
-                    let b_text = b_name.as_str().trim_start_matches("r#");
+                    let a_text = a_name.trim_start_matches("r#");
+                    let b_text = b_name.trim_start_matches("r#");
                     version_sort::version_sort(a_text, b_text)
                 }
             }
@@ -614,15 +614,13 @@
                 .name()
                 .as_ref()
                 .map(ast::Name::text)
-                .as_ref()
-                .map_or("_", |a_name| a_name.as_str().trim_start_matches("r#"))
+                .map_or("_", |a_name| a_name.trim_start_matches("r#"))
                 .cmp(
                     b_rename
                         .name()
                         .as_ref()
                         .map(ast::Name::text)
-                        .as_ref()
-                        .map_or("_", |b_name| b_name.as_str().trim_start_matches("r#")),
+                        .map_or("_", |b_name| b_name.trim_start_matches("r#")),
                 ),
         },
     };
diff --git a/crates/ide-db/src/lib.rs b/crates/ide-db/src/lib.rs
index 930cb99..e37c2f0 100644
--- a/crates/ide-db/src/lib.rs
+++ b/crates/ide-db/src/lib.rs
@@ -256,6 +256,7 @@
 pub fn line_index(db: &dyn SourceDatabase, file_id: FileId) -> &Arc<LineIndex> {
     #[salsa::interned]
     pub struct InternedFileId {
+        #[returns(copy)]
         id: FileId,
     }
     #[salsa::tracked(returns(ref))]
diff --git a/crates/ide-db/src/path_transform.rs b/crates/ide-db/src/path_transform.rs
index 101046c..ff32bad 100644
--- a/crates/ide-db/src/path_transform.rs
+++ b/crates/ide-db/src/path_transform.rs
@@ -536,7 +536,7 @@
         let name = ident_pat.name()?;
         let make = editor.make();
 
-        let temp_path = make.path_from_text(&name.text());
+        let temp_path = make.path_from_text(name.text());
 
         let resolution = self.source_scope.speculative_resolve(&temp_path)?;
 
@@ -651,7 +651,7 @@
         });
 
         for name in names {
-            if assoc_item_name.as_str() == name.as_str() {
+            if assoc_item_name == name.as_str() {
                 // It is fine to return the first match because in case of
                 // multiple possibilities, the exact trait must be disambiguated
                 // in the definition of trait being implemented, so this search
diff --git a/crates/ide-db/src/ra_fixture.rs b/crates/ide-db/src/ra_fixture.rs
index c8607a8..09a270c 100644
--- a/crates/ide-db/src/ra_fixture.rs
+++ b/crates/ide-db/src/ra_fixture.rs
@@ -102,8 +102,7 @@
                 else {
                     return false;
                 };
-                segment1.text_non_mutable() == "rust_analyzer"
-                    && segment2.text_non_mutable() == "rust_fixture"
+                segment1.text() == "rust_analyzer" && segment2.text() == "rust_fixture"
             })
         });
         if !has_rust_fixture_attr {
diff --git a/crates/ide-db/src/rename.rs b/crates/ide-db/src/rename.rs
index b89c2fd..16224ae 100644
--- a/crates/ide-db/src/rename.rs
+++ b/crates/ide-db/src/rename.rs
@@ -459,7 +459,7 @@
             };
             expr.record_expr_field_list()?.fields().find_map(|record_field| {
                 if record_field.name_ref().is_none()
-                    && Name::new_root(&record_field.field_name()?.text()) == old_name
+                    && Name::new_root(record_field.field_name()?.text()) == old_name
                     && let ast::Expr::PathExpr(field_name) = record_field.expr()?
                 {
                     field_name.path()
@@ -734,7 +734,7 @@
                 // special cases required for renaming fields/locals in Record patterns
                 if let Some(pat_field) = pat.syntax().parent().and_then(ast::RecordPatField::cast) {
                     if let Some(name_ref) = pat_field.name_ref() {
-                        if new_name.as_str() == name_ref.text().as_str().trim_start_matches("r#")
+                        if new_name.as_str() == name_ref.text().trim_start_matches("r#")
                             && pat.at_token().is_none()
                         {
                             // Foo { field: ref mut local } -> Foo { ref mut field }
@@ -747,7 +747,7 @@
                                     .text_range()
                                     .cover_offset(pat.syntax().text_range().start()),
                             );
-                            edit.replace(name_range, name_ref.text().to_string());
+                            edit.replace(name_range, name_ref.text().to_owned());
                         } else {
                             // Foo { field: ref mut local @ local 2} -> Foo { field: ref mut new_name @ local2 }
                             // Foo { field: ref mut local } -> Foo { field: ref mut new_name }
diff --git a/crates/ide-db/src/search.rs b/crates/ide-db/src/search.rs
index b688cb1..c2cc9d7 100644
--- a/crates/ide-db/src/search.rs
+++ b/crates/ide-db/src/search.rs
@@ -11,8 +11,8 @@
 use either::Either;
 use hir::{
     Adt, AsAssocItem, DefWithBody, EditionedFileId, ExpressionStoreOwner, FileRange,
-    FileRangeWrapper, HasAttrs, HasContainer, HasSource, InFile, InFileWrapper, InRealFile,
-    InlineAsmOperand, ItemContainer, ModuleSource, PathResolution, Semantics, Visibility,
+    FileRangeWrapper, HasContainer, HasSource, InFile, InFileWrapper, InRealFile, InlineAsmOperand,
+    ItemContainer, ModuleSource, PathResolution, Semantics, Visibility,
 };
 use memchr::memmem::Finder;
 use parser::SyntaxKind;
@@ -119,13 +119,13 @@
             _ => None,
         }
     }
-    pub fn text(&self) -> syntax::TokenText<'_> {
+    pub fn text(&self) -> &str {
         match self {
             FileReferenceNode::NameRef(name_ref) => name_ref.text(),
             FileReferenceNode::Name(name) => name.text(),
             FileReferenceNode::Lifetime(lifetime) => lifetime.text(),
             FileReferenceNode::FormatStringEntry(it, range) => {
-                syntax::TokenText::borrowed(&it.text()[*range - it.syntax().text_range().start()])
+                &it.text()[*range - it.syntax().text_range().start()]
             }
         }
     }
@@ -397,24 +397,6 @@
             };
         }
 
-        if let Definition::Macro(macro_def) = self {
-            return match macro_def.kind(db) {
-                hir::MacroKind::Declarative => {
-                    if macro_def.attrs(db).is_macro_export() {
-                        SearchScope::reverse_dependencies(db, module.krate(db))
-                    } else {
-                        SearchScope::krate(db, module.krate(db))
-                    }
-                }
-                hir::MacroKind::AttrBuiltIn
-                | hir::MacroKind::DeriveBuiltIn
-                | hir::MacroKind::DeclarativeBuiltIn => SearchScope::crate_graph(db),
-                hir::MacroKind::Derive | hir::MacroKind::Attr | hir::MacroKind::ProcMacro => {
-                    SearchScope::reverse_dependencies(db, module.krate(db))
-                }
-            };
-        }
-
         if let Definition::DeriveHelper(_) = self {
             return SearchScope::reverse_dependencies(db, module.krate(db));
         }
@@ -751,7 +733,7 @@
                                     insert_type_alias(
                                         sema.db,
                                         &mut to_process,
-                                        name.text().as_str(),
+                                        name.text(),
                                         def.into(),
                                     );
                                 } else {
@@ -814,7 +796,7 @@
                                             insert_type_alias(
                                                 sema.db,
                                                 &mut to_process,
-                                                name.text().as_str(),
+                                                name.text(),
                                                 def.into(),
                                             );
                                         } else {
diff --git a/crates/ide-db/src/symbol_index.rs b/crates/ide-db/src/symbol_index.rs
index edec862..9de7cd9 100644
--- a/crates/ide-db/src/symbol_index.rs
+++ b/crates/ide-db/src/symbol_index.rs
@@ -29,7 +29,7 @@
 
 use base_db::{
     CrateOrigin, InternedSourceRootId, LangCrateOrigin, LibraryRoots, LocalRoots, SourceRootId,
-    salsa::Update, source_root_crates,
+    salsa::SalsaValue, source_root_crates,
 };
 use fst::{Automaton, Streamer, raw::IndexedValue};
 use hir::{
@@ -359,31 +359,12 @@
     candidate_modules.into_iter().map(|(module, _)| module).collect()
 }
 
-#[derive(Default)]
+#[derive(Default, SalsaValue)]
 pub struct SymbolIndex<'db> {
     symbols: Box<[FileSymbol<'db>]>,
     map: fst::Map<Vec<u8>>,
 }
 
-// SAFETY:
-// - It is safe to compare a `SymbolIndex` from a previous revision to a new one.
-// - FileSymbol<'db>: Update
-unsafe impl<'db> Update for SymbolIndex<'db>
-where
-    FileSymbol<'db>: Update,
-{
-    unsafe fn maybe_update(old_pointer: *mut Self, new_value: Self) -> bool {
-        // SAFETY: Safe to dereference as per `salsa::Update` contract.
-        let this = unsafe { &mut *old_pointer };
-        if *this != new_value {
-            *this = new_value;
-            true
-        } else {
-            false
-        }
-    }
-}
-
 impl<'db> SymbolIndex<'db> {
     /// The symbol index for a given source root within library_roots.
     pub fn library_symbols(
diff --git a/crates/ide-db/src/test_data/test_doc_alias.txt b/crates/ide-db/src/test_data/test_doc_alias.txt
index 7a59abf..9b97b28 100644
--- a/crates/ide-db/src/test_data/test_doc_alias.txt
+++ b/crates/ide-db/src/test_data/test_doc_alias.txt
@@ -2,7 +2,7 @@
     (
         Module {
             id: ModuleIdLt {
-                [salsa id]: Id(2800),
+                [salsa id]: Id(500),
             },
         },
         [
@@ -12,7 +12,7 @@
                     Struct(
                         Struct {
                             id: StructId(
-                                3001,
+                                0601,
                             ),
                         },
                     ),
@@ -20,7 +20,7 @@
                 loc: DeclarationLocation {
                     hir_file_id: FileId(
                         EditionedFileId(
-                            Id(2400),
+                            Id(480),
                         ),
                     ),
                     ptr: SyntaxNodePtr {
@@ -48,7 +48,7 @@
                     Struct(
                         Struct {
                             id: StructId(
-                                3000,
+                                0600,
                             ),
                         },
                     ),
@@ -56,7 +56,7 @@
                 loc: DeclarationLocation {
                     hir_file_id: FileId(
                         EditionedFileId(
-                            Id(2400),
+                            Id(480),
                         ),
                     ),
                     ptr: SyntaxNodePtr {
@@ -84,7 +84,7 @@
                     Struct(
                         Struct {
                             id: StructId(
-                                3000,
+                                0600,
                             ),
                         },
                     ),
@@ -92,7 +92,7 @@
                 loc: DeclarationLocation {
                     hir_file_id: FileId(
                         EditionedFileId(
-                            Id(2400),
+                            Id(480),
                         ),
                     ),
                     ptr: SyntaxNodePtr {
@@ -120,7 +120,7 @@
                     Struct(
                         Struct {
                             id: StructId(
-                                3000,
+                                0600,
                             ),
                         },
                     ),
@@ -128,7 +128,7 @@
                 loc: DeclarationLocation {
                     hir_file_id: FileId(
                         EditionedFileId(
-                            Id(2400),
+                            Id(480),
                         ),
                     ),
                     ptr: SyntaxNodePtr {
@@ -156,7 +156,7 @@
                     Struct(
                         Struct {
                             id: StructId(
-                                3000,
+                                0600,
                             ),
                         },
                     ),
@@ -164,7 +164,7 @@
                 loc: DeclarationLocation {
                     hir_file_id: FileId(
                         EditionedFileId(
-                            Id(2400),
+                            Id(480),
                         ),
                     ),
                     ptr: SyntaxNodePtr {
@@ -192,7 +192,7 @@
                     Struct(
                         Struct {
                             id: StructId(
-                                3001,
+                                0601,
                             ),
                         },
                     ),
@@ -200,7 +200,7 @@
                 loc: DeclarationLocation {
                     hir_file_id: FileId(
                         EditionedFileId(
-                            Id(2400),
+                            Id(480),
                         ),
                     ),
                     ptr: SyntaxNodePtr {
@@ -228,7 +228,7 @@
                     Struct(
                         Struct {
                             id: StructId(
-                                3000,
+                                0600,
                             ),
                         },
                     ),
@@ -236,7 +236,7 @@
                 loc: DeclarationLocation {
                     hir_file_id: FileId(
                         EditionedFileId(
-                            Id(2400),
+                            Id(480),
                         ),
                     ),
                     ptr: SyntaxNodePtr {
diff --git a/crates/ide-db/src/test_data/test_symbol_index_collection.txt b/crates/ide-db/src/test_data/test_symbol_index_collection.txt
index d061976..1cf6042 100644
--- a/crates/ide-db/src/test_data/test_symbol_index_collection.txt
+++ b/crates/ide-db/src/test_data/test_symbol_index_collection.txt
@@ -2,7 +2,7 @@
     (
         Module {
             id: ModuleIdLt {
-                [salsa id]: Id(2800),
+                [salsa id]: Id(500),
             },
         },
         [
@@ -11,14 +11,14 @@
                 def: EnumVariant(
                     EnumVariant {
                         id: EnumVariantId(
-                            7000,
+                            0e00,
                         ),
                     },
                 ),
                 loc: DeclarationLocation {
                     hir_file_id: FileId(
                         EditionedFileId(
-                            Id(2400),
+                            Id(480),
                         ),
                     ),
                     ptr: SyntaxNodePtr {
@@ -47,14 +47,14 @@
                 def: TypeAlias(
                     TypeAlias {
                         id: TypeAliasId(
-                            6400,
+                            0c80,
                         ),
                     },
                 ),
                 loc: DeclarationLocation {
                     hir_file_id: FileId(
                         EditionedFileId(
-                            Id(2400),
+                            Id(480),
                         ),
                     ),
                     ptr: SyntaxNodePtr {
@@ -81,14 +81,14 @@
                 def: EnumVariant(
                     EnumVariant {
                         id: EnumVariantId(
-                            7001,
+                            0e01,
                         ),
                     },
                 ),
                 loc: DeclarationLocation {
                     hir_file_id: FileId(
                         EditionedFileId(
-                            Id(2400),
+                            Id(480),
                         ),
                     ),
                     ptr: SyntaxNodePtr {
@@ -117,14 +117,14 @@
                 def: Const(
                     Const {
                         id: ConstId(
-                            5c00,
+                            0b80,
                         ),
                     },
                 ),
                 loc: DeclarationLocation {
                     hir_file_id: FileId(
                         EditionedFileId(
-                            Id(2400),
+                            Id(480),
                         ),
                     ),
                     ptr: SyntaxNodePtr {
@@ -151,14 +151,14 @@
                 def: Const(
                     Const {
                         id: ConstId(
-                            5c02,
+                            0b82,
                         ),
                     },
                 ),
                 loc: DeclarationLocation {
                     hir_file_id: FileId(
                         EditionedFileId(
-                            Id(2400),
+                            Id(480),
                         ),
                     ),
                     ptr: SyntaxNodePtr {
@@ -186,7 +186,7 @@
                     Enum(
                         Enum {
                             id: EnumId(
-                                4800,
+                                0900,
                             ),
                         },
                     ),
@@ -194,7 +194,7 @@
                 loc: DeclarationLocation {
                     hir_file_id: FileId(
                         EditionedFileId(
-                            Id(2400),
+                            Id(480),
                         ),
                     ),
                     ptr: SyntaxNodePtr {
@@ -222,7 +222,7 @@
                     Macro {
                         id: Macro2Id(
                             Macro2Id(
-                                4400,
+                                0880,
                             ),
                         ),
                     },
@@ -230,7 +230,7 @@
                 loc: DeclarationLocation {
                     hir_file_id: FileId(
                         EditionedFileId(
-                            Id(2400),
+                            Id(480),
                         ),
                     ),
                     ptr: SyntaxNodePtr {
@@ -258,7 +258,7 @@
                     Macro {
                         id: Macro2Id(
                             Macro2Id(
-                                4400,
+                                0880,
                             ),
                         ),
                     },
@@ -266,7 +266,7 @@
                 loc: DeclarationLocation {
                     hir_file_id: FileId(
                         EditionedFileId(
-                            Id(2400),
+                            Id(480),
                         ),
                     ),
                     ptr: SyntaxNodePtr {
@@ -293,14 +293,14 @@
                 def: Static(
                     Static {
                         id: StaticId(
-                            6000,
+                            0c00,
                         ),
                     },
                 ),
                 loc: DeclarationLocation {
                     hir_file_id: FileId(
                         EditionedFileId(
-                            Id(2400),
+                            Id(480),
                         ),
                     ),
                     ptr: SyntaxNodePtr {
@@ -328,7 +328,7 @@
                     Struct(
                         Struct {
                             id: StructId(
-                                4001,
+                                0801,
                             ),
                         },
                     ),
@@ -336,7 +336,7 @@
                 loc: DeclarationLocation {
                     hir_file_id: FileId(
                         EditionedFileId(
-                            Id(2400),
+                            Id(480),
                         ),
                     ),
                     ptr: SyntaxNodePtr {
@@ -364,7 +364,7 @@
                     Struct(
                         Struct {
                             id: StructId(
-                                4000,
+                                0800,
                             ),
                         },
                     ),
@@ -372,7 +372,7 @@
                 loc: DeclarationLocation {
                     hir_file_id: MacroFile(
                         MacroCallId(
-                            Id(3800),
+                            Id(700),
                         ),
                     ),
                     ptr: SyntaxNodePtr {
@@ -400,7 +400,7 @@
                     Struct(
                         Struct {
                             id: StructId(
-                                4005,
+                                0805,
                             ),
                         },
                     ),
@@ -408,7 +408,7 @@
                 loc: DeclarationLocation {
                     hir_file_id: FileId(
                         EditionedFileId(
-                            Id(2400),
+                            Id(480),
                         ),
                     ),
                     ptr: SyntaxNodePtr {
@@ -438,7 +438,7 @@
                     Struct(
                         Struct {
                             id: StructId(
-                                4006,
+                                0806,
                             ),
                         },
                     ),
@@ -446,7 +446,7 @@
                 loc: DeclarationLocation {
                     hir_file_id: FileId(
                         EditionedFileId(
-                            Id(2400),
+                            Id(480),
                         ),
                     ),
                     ptr: SyntaxNodePtr {
@@ -476,7 +476,7 @@
                     Struct(
                         Struct {
                             id: StructId(
-                                4007,
+                                0807,
                             ),
                         },
                     ),
@@ -484,7 +484,7 @@
                 loc: DeclarationLocation {
                     hir_file_id: FileId(
                         EditionedFileId(
-                            Id(2400),
+                            Id(480),
                         ),
                     ),
                     ptr: SyntaxNodePtr {
@@ -512,7 +512,7 @@
                     Struct(
                         Struct {
                             id: StructId(
-                                4002,
+                                0802,
                             ),
                         },
                     ),
@@ -520,7 +520,7 @@
                 loc: DeclarationLocation {
                     hir_file_id: FileId(
                         EditionedFileId(
-                            Id(2400),
+                            Id(480),
                         ),
                     ),
                     ptr: SyntaxNodePtr {
@@ -547,14 +547,14 @@
                 def: Trait(
                     Trait {
                         id: TraitId(
-                            5400,
+                            0a80,
                         ),
                     },
                 ),
                 loc: DeclarationLocation {
                     hir_file_id: FileId(
                         EditionedFileId(
-                            Id(2400),
+                            Id(480),
                         ),
                     ),
                     ptr: SyntaxNodePtr {
@@ -582,7 +582,7 @@
                     Macro {
                         id: Macro2Id(
                             Macro2Id(
-                                4400,
+                                0880,
                             ),
                         ),
                     },
@@ -590,7 +590,7 @@
                 loc: DeclarationLocation {
                     hir_file_id: FileId(
                         EditionedFileId(
-                            Id(2400),
+                            Id(480),
                         ),
                     ),
                     ptr: SyntaxNodePtr {
@@ -618,7 +618,7 @@
                     Union(
                         Union {
                             id: UnionId(
-                                4c00,
+                                0980,
                             ),
                         },
                     ),
@@ -626,7 +626,7 @@
                 loc: DeclarationLocation {
                     hir_file_id: FileId(
                         EditionedFileId(
-                            Id(2400),
+                            Id(480),
                         ),
                     ),
                     ptr: SyntaxNodePtr {
@@ -653,14 +653,14 @@
                 def: Module(
                     Module {
                         id: ModuleIdLt {
-                            [salsa id]: Id(2801),
+                            [salsa id]: Id(501),
                         },
                     },
                 ),
                 loc: DeclarationLocation {
                     hir_file_id: FileId(
                         EditionedFileId(
-                            Id(2400),
+                            Id(480),
                         ),
                     ),
                     ptr: SyntaxNodePtr {
@@ -687,14 +687,14 @@
                 def: Module(
                     Module {
                         id: ModuleIdLt {
-                            [salsa id]: Id(2802),
+                            [salsa id]: Id(502),
                         },
                     },
                 ),
                 loc: DeclarationLocation {
                     hir_file_id: FileId(
                         EditionedFileId(
-                            Id(2400),
+                            Id(480),
                         ),
                     ),
                     ptr: SyntaxNodePtr {
@@ -722,7 +722,7 @@
                     Macro {
                         id: MacroRulesId(
                             MacroRulesId(
-                                3401,
+                                0681,
                             ),
                         ),
                     },
@@ -730,7 +730,7 @@
                 loc: DeclarationLocation {
                     hir_file_id: FileId(
                         EditionedFileId(
-                            Id(2400),
+                            Id(480),
                         ),
                     ),
                     ptr: SyntaxNodePtr {
@@ -757,14 +757,14 @@
                 def: Function(
                     FunctionId(
                         FunctionId(
-                            5802,
+                            0b02,
                         ),
                     ),
                 ),
                 loc: DeclarationLocation {
                     hir_file_id: FileId(
                         EditionedFileId(
-                            Id(2400),
+                            Id(480),
                         ),
                     ),
                     ptr: SyntaxNodePtr {
@@ -793,14 +793,14 @@
                 def: Function(
                     FunctionId(
                         FunctionId(
-                            5801,
+                            0b01,
                         ),
                     ),
                 ),
                 loc: DeclarationLocation {
                     hir_file_id: FileId(
                         EditionedFileId(
-                            Id(2400),
+                            Id(480),
                         ),
                     ),
                     ptr: SyntaxNodePtr {
@@ -830,7 +830,7 @@
                     Macro {
                         id: MacroRulesId(
                             MacroRulesId(
-                                3400,
+                                0680,
                             ),
                         ),
                     },
@@ -838,7 +838,7 @@
                 loc: DeclarationLocation {
                     hir_file_id: FileId(
                         EditionedFileId(
-                            Id(2400),
+                            Id(480),
                         ),
                     ),
                     ptr: SyntaxNodePtr {
@@ -865,14 +865,14 @@
                 def: Function(
                     FunctionId(
                         FunctionId(
-                            5800,
+                            0b00,
                         ),
                     ),
                 ),
                 loc: DeclarationLocation {
                     hir_file_id: FileId(
                         EditionedFileId(
-                            Id(2400),
+                            Id(480),
                         ),
                     ),
                     ptr: SyntaxNodePtr {
@@ -900,7 +900,7 @@
                     Macro {
                         id: MacroRulesId(
                             MacroRulesId(
-                                3401,
+                                0681,
                             ),
                         ),
                     },
@@ -908,7 +908,7 @@
                 loc: DeclarationLocation {
                     hir_file_id: FileId(
                         EditionedFileId(
-                            Id(2400),
+                            Id(480),
                         ),
                     ),
                     ptr: SyntaxNodePtr {
@@ -935,14 +935,14 @@
                 def: Function(
                     FunctionId(
                         FunctionId(
-                            5803,
+                            0b03,
                         ),
                     ),
                 ),
                 loc: DeclarationLocation {
                     hir_file_id: FileId(
                         EditionedFileId(
-                            Id(2400),
+                            Id(480),
                         ),
                     ),
                     ptr: SyntaxNodePtr {
@@ -971,7 +971,7 @@
     (
         Module {
             id: ModuleIdLt {
-                [salsa id]: Id(2801),
+                [salsa id]: Id(501),
             },
         },
         [
@@ -981,7 +981,7 @@
                     Struct(
                         Struct {
                             id: StructId(
-                                4003,
+                                0803,
                             ),
                         },
                     ),
@@ -989,7 +989,7 @@
                 loc: DeclarationLocation {
                     hir_file_id: FileId(
                         EditionedFileId(
-                            Id(2400),
+                            Id(480),
                         ),
                     ),
                     ptr: SyntaxNodePtr {
@@ -1016,7 +1016,7 @@
     (
         Module {
             id: ModuleIdLt {
-                [salsa id]: Id(2802),
+                [salsa id]: Id(502),
             },
         },
         [
@@ -1025,14 +1025,14 @@
                 def: Trait(
                     Trait {
                         id: TraitId(
-                            5400,
+                            0a80,
                         ),
                     },
                 ),
                 loc: DeclarationLocation {
                     hir_file_id: FileId(
                         EditionedFileId(
-                            Id(2401),
+                            Id(481),
                         ),
                     ),
                     ptr: SyntaxNodePtr {
@@ -1060,7 +1060,7 @@
                     Macro {
                         id: Macro2Id(
                             Macro2Id(
-                                4400,
+                                0880,
                             ),
                         ),
                     },
@@ -1068,7 +1068,7 @@
                 loc: DeclarationLocation {
                     hir_file_id: FileId(
                         EditionedFileId(
-                            Id(2401),
+                            Id(481),
                         ),
                     ),
                     ptr: SyntaxNodePtr {
@@ -1096,7 +1096,7 @@
                     Struct(
                         Struct {
                             id: StructId(
-                                4004,
+                                0804,
                             ),
                         },
                     ),
@@ -1104,7 +1104,7 @@
                 loc: DeclarationLocation {
                     hir_file_id: FileId(
                         EditionedFileId(
-                            Id(2401),
+                            Id(481),
                         ),
                     ),
                     ptr: SyntaxNodePtr {
@@ -1132,7 +1132,7 @@
                     Macro {
                         id: Macro2Id(
                             Macro2Id(
-                                4400,
+                                0880,
                             ),
                         ),
                     },
@@ -1140,7 +1140,7 @@
                 loc: DeclarationLocation {
                     hir_file_id: FileId(
                         EditionedFileId(
-                            Id(2401),
+                            Id(481),
                         ),
                     ),
                     ptr: SyntaxNodePtr {
@@ -1168,7 +1168,7 @@
                     Struct(
                         Struct {
                             id: StructId(
-                                4004,
+                                0804,
                             ),
                         },
                     ),
@@ -1176,7 +1176,7 @@
                 loc: DeclarationLocation {
                     hir_file_id: FileId(
                         EditionedFileId(
-                            Id(2401),
+                            Id(481),
                         ),
                     ),
                     ptr: SyntaxNodePtr {
diff --git a/crates/ide-db/src/test_data/test_symbols_exclude_imports.txt b/crates/ide-db/src/test_data/test_symbols_exclude_imports.txt
index 20b162b..c70e63c 100644
--- a/crates/ide-db/src/test_data/test_symbols_exclude_imports.txt
+++ b/crates/ide-db/src/test_data/test_symbols_exclude_imports.txt
@@ -5,7 +5,7 @@
             Struct(
                 Struct {
                     id: StructId(
-                        3400,
+                        0680,
                     ),
                 },
             ),
@@ -13,7 +13,7 @@
         loc: DeclarationLocation {
             hir_file_id: FileId(
                 EditionedFileId(
-                    Id(2401),
+                    Id(481),
                 ),
             ),
             ptr: SyntaxNodePtr {
diff --git a/crates/ide-db/src/test_data/test_symbols_with_imports.txt b/crates/ide-db/src/test_data/test_symbols_with_imports.txt
index 1d475ab..ac29c72 100644
--- a/crates/ide-db/src/test_data/test_symbols_with_imports.txt
+++ b/crates/ide-db/src/test_data/test_symbols_with_imports.txt
@@ -5,7 +5,7 @@
             Struct(
                 Struct {
                     id: StructId(
-                        3400,
+                        0680,
                     ),
                 },
             ),
@@ -13,7 +13,7 @@
         loc: DeclarationLocation {
             hir_file_id: FileId(
                 EditionedFileId(
-                    Id(2401),
+                    Id(481),
                 ),
             ),
             ptr: SyntaxNodePtr {
@@ -41,7 +41,7 @@
             Struct(
                 Struct {
                     id: StructId(
-                        3400,
+                        0680,
                     ),
                 },
             ),
@@ -49,7 +49,7 @@
         loc: DeclarationLocation {
             hir_file_id: FileId(
                 EditionedFileId(
-                    Id(2400),
+                    Id(480),
                 ),
             ),
             ptr: SyntaxNodePtr {
diff --git a/crates/ide-diagnostics/src/handlers/invalid_lhs_of_assignment.rs b/crates/ide-diagnostics/src/handlers/invalid_lhs_of_assignment.rs
index 225d3e0..6f0981b 100644
--- a/crates/ide-diagnostics/src/handlers/invalid_lhs_of_assignment.rs
+++ b/crates/ide-diagnostics/src/handlers/invalid_lhs_of_assignment.rs
@@ -38,6 +38,19 @@
     }
 
     #[test]
+    fn local_binding_shadows_function() {
+        check_diagnostics(
+            r#"
+//- minicore: add
+fn f() {
+    let mut f = 1;
+    f += 1;
+}
+        "#,
+        );
+    }
+
+    #[test]
     fn struct_literal() {
         check_diagnostics(
             r#"
diff --git a/crates/ide-diagnostics/src/handlers/macro_error.rs b/crates/ide-diagnostics/src/handlers/macro_error.rs
index 7acbcad..3c7d9f9 100644
--- a/crates/ide-diagnostics/src/handlers/macro_error.rs
+++ b/crates/ide-diagnostics/src/handlers/macro_error.rs
@@ -329,6 +329,47 @@
     }
 
     #[test]
+    fn no_stack_overflow_for_recursive_macro() {
+        check_diagnostics(
+            r#"
+#![recursion_limit = "4"]
+macro_rules! test {
+    () => {
+        fn my_test() {
+            test!();
+        }
+    };
+}
+mod tests {
+    test!();
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn no_stack_overflow_for_recursive_exported_macro() {
+        check_diagnostics(
+            r#"
+#![recursion_limit = "4"]
+#[macro_export]
+macro_rules! test {
+    () => {
+        fn my_test() {
+            $crate::test!();
+        }
+    };
+}
+
+mod tests {
+    use super::*;
+    test!();
+}
+"#,
+        );
+    }
+
+    #[test]
     fn unimplemented_builtin_macro() {
         check_diagnostics(
             r#"
diff --git a/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs b/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs
deleted file mode 100644
index e61719a..0000000
--- a/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs
+++ /dev/null
@@ -1,260 +0,0 @@
-use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
-use hir::HirDisplay;
-
-// Diagnostic: moved-out-of-ref
-//
-// This diagnostic is triggered on moving non copy things out of references.
-pub(crate) fn moved_out_of_ref(
-    ctx: &DiagnosticsContext<'_, '_>,
-    d: &hir::MovedOutOfRef<'_>,
-) -> Diagnostic {
-    Diagnostic::new_with_syntax_node_ptr(
-        ctx,
-        DiagnosticCode::RustcHardError("E0507"),
-        format!("cannot move `{}` out of reference", d.ty.display(ctx.sema.db, ctx.display_target)),
-        d.span,
-    )
-    // spans are broken, and I'm not sure how precise we can detect copy types
-}
-
-#[cfg(test)]
-mod tests {
-    use crate::tests::check_diagnostics;
-
-    #[test]
-    fn operand_field_span_respected() {
-        check_diagnostics(
-            r#"
-struct NotCopy;
-struct S {
-    field: NotCopy,
-}
-
-fn f(s: &S) -> S {
-    S { field: s.field }
-             //^^^^^^^ error: cannot move `NotCopy` out of reference
-}
-            "#,
-        );
-    }
-
-    #[test]
-    fn move_by_explicit_deref() {
-        check_diagnostics(
-            r#"
-struct X;
-fn main() {
-    let a = &X;
-    let b = *a;
-      //^ error: cannot move `X` out of reference
-    _ = b;
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn move_out_of_field() {
-        check_diagnostics(
-            r#"
-//- minicore: copy
-struct X;
-struct Y(X, i32);
-fn main() {
-    let a = &Y(X, 5);
-    let b = a.0;
-      //^ error: cannot move `X` out of reference
-    let y = a.1;
-    _ = (b, y);
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn move_out_of_static() {
-        check_diagnostics(
-            r#"
-//- minicore: copy
-struct X;
-fn main() {
-    static S: X = X;
-    let _s = S;
-      //^^ error: cannot move `X` out of reference
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn generic_types() {
-        check_diagnostics(
-            r#"
-//- minicore: derive, copy
-
-#[derive(Copy)]
-struct X<T>(T);
-struct Y;
-
-fn consume<T>(_: X<T>) {
-
-}
-
-fn main() {
-    let a = &X(Y);
-    consume(*a);
-          //^^ error: cannot move `X<Y>` out of reference
-    let a = &X(5);
-    consume(*a);
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn no_false_positive_simple() {
-        check_diagnostics(
-            r#"
-//- minicore: copy
-fn f(_: i32) {}
-fn main() {
-    let x = &2;
-    f(*x);
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn no_false_positive_unknown_type() {
-        check_diagnostics(
-            r#"
-//- minicore: derive, copy
-fn f(x: &Unknown) -> Unknown {
-    *x
-}
-
-#[derive(Copy)]
-struct X<T>(T);
-
-struct Y<T>(T);
-
-fn g(x: &X<Unknown>) -> X<Unknown> {
-    *x
-}
-
-fn h(x: &Y<Unknown>) -> Y<Unknown> {
-    // FIXME: we should show error for this, as `Y` is not copy
-    // regardless of its generic parameter.
-    *x
-}
-
-"#,
-        );
-    }
-
-    #[test]
-    fn no_false_positive_dyn_fn() {
-        check_diagnostics(
-            r#"
-//- minicore: copy, fn, dispatch_from_dyn
-fn f(x: &mut &mut dyn Fn()) {
-    x();
-}
-
-struct X<'a> {
-    field: &'a mut dyn Fn(),
-}
-
-fn g(x: &mut X<'_>) {
-    (x.field)();
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn no_false_positive_match_and_closure_capture() {
-        check_diagnostics(
-            r#"
-//- minicore: copy, fn
-enum X {
-    Foo(u16),
-    Bar,
-}
-
-fn main() {
-    let x = &X::Bar;
-    let _c = || match *x {
-        X::Foo(t) => t,
-        _ => 5,
-    };
-}
-            "#,
-        );
-    }
-
-    #[test]
-    fn regression_15787() {
-        check_diagnostics(
-            r#"
-//- minicore: coerce_unsized, slice, copy
-fn foo(mut slice: &[u32]) -> usize {
-    slice = match slice {
-        [0, rest @ ..] | rest => rest,
-    };
-    slice.len()
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn regression_16564() {
-        check_diagnostics(
-            r#"
-//- minicore: copy
-fn test() {
-    let _x = (&(&mut (),)).0 as *const ();
-}
-            "#,
-        )
-    }
-
-    #[test]
-    fn regression_18201() {
-        check_diagnostics(
-            r#"
-//- minicore: copy
-struct NotCopy;
-struct S(NotCopy);
-impl S {
-    fn f(&mut self) {
-        || {
-            if let ref mut _cb = self.0 {
-            }
-        };
-    }
-}
-"#,
-        )
-    }
-
-    #[test]
-    fn regression_20155() {
-        check_diagnostics(
-            r#"
-//- minicore: copy, option
-struct Box(i32);
-fn test() {
-    let b = Some(Box(0));
-    || {
-        if let Some(b) = b {
-            let _move = b;
-        }
-    };
-}
-"#,
-        )
-    }
-}
diff --git a/crates/ide-diagnostics/src/handlers/mutability_errors.rs b/crates/ide-diagnostics/src/handlers/mutability_errors.rs
deleted file mode 100644
index fa9a69c..0000000
--- a/crates/ide-diagnostics/src/handlers/mutability_errors.rs
+++ /dev/null
@@ -1,1332 +0,0 @@
-use ide_db::source_change::SourceChange;
-use ide_db::text_edit::TextEdit;
-use syntax::{AstNode, SyntaxKind, SyntaxNode, SyntaxNodePtr, SyntaxToken, T, ast};
-
-use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, fix};
-
-// Diagnostic: need-mut
-//
-// This diagnostic is triggered on mutating an immutable variable.
-pub(crate) fn need_mut(
-    ctx: &DiagnosticsContext<'_, '_>,
-    d: &hir::NeedMut<'_>,
-) -> Option<Diagnostic> {
-    let root = d.span.file_id.parse_or_expand(ctx.sema.db);
-    let node = d.span.value.to_node(&root);
-    let mut span = d.span;
-    if let Some(parent) = node.parent()
-        && ast::BinExpr::can_cast(parent.kind())
-    {
-        // In case of an assignment, the diagnostic is provided on the variable name.
-        // We want to expand it to include the whole assignment, but only when this
-        // is an ordinary assignment, not a destructuring assignment. So, the direct
-        // parent is an assignment expression.
-        span = d.span.with_value(SyntaxNodePtr::new(&parent));
-    };
-
-    let fixes = (|| {
-        if d.local.is_ref(ctx.sema.db) {
-            // There is no simple way to add `mut` to `ref x` and `ref mut x`
-            return None;
-        }
-        let file_id = span.file_id.file_id()?;
-        let mut edit_builder = TextEdit::builder();
-        let use_range = span.value.text_range();
-        for source in d.local.sources(ctx.sema.db) {
-            let Some(ast) = source.name() else { continue };
-            // FIXME: macros
-            edit_builder.insert(ast.value.syntax().text_range().start(), "mut ".to_owned());
-        }
-        let edit = edit_builder.finish();
-        Some(vec![fix(
-            "add_mut",
-            "Change it to be mutable",
-            SourceChange::from_text_edit(file_id.file_id(ctx.sema.db), edit),
-            use_range,
-        )])
-    })();
-
-    Some(
-        Diagnostic::new_with_syntax_node_ptr(
-            ctx,
-            // FIXME: `E0384` is not the only error that this diagnostic handles
-            DiagnosticCode::RustcHardError("E0384"),
-            format!(
-                "cannot mutate immutable variable `{}`",
-                d.local.name(ctx.sema.db).display(ctx.sema.db, ctx.edition)
-            ),
-            span,
-        )
-        .stable()
-        .with_fixes(fixes),
-    )
-}
-
-// Diagnostic: unused-mut
-//
-// This diagnostic is triggered when a mutable variable isn't actually mutated.
-pub(crate) fn unused_mut(
-    ctx: &DiagnosticsContext<'_, '_>,
-    d: &hir::UnusedMut<'_>,
-) -> Option<Diagnostic> {
-    let ast = d.local.primary_source(ctx.sema.db).syntax_ptr();
-    let fixes = (|| {
-        let file_id = ast.file_id.file_id()?;
-        let mut edit_builder = TextEdit::builder();
-        let use_range = ast.value.text_range();
-        for source in d.local.sources(ctx.sema.db) {
-            let ast = source.syntax();
-            let Some(mut_token) = token(ast, T![mut]) else { continue };
-            edit_builder.delete(mut_token.text_range());
-            if let Some(token) = mut_token.next_token()
-                && token.kind() == SyntaxKind::WHITESPACE
-            {
-                edit_builder.delete(token.text_range());
-            }
-        }
-        let edit = edit_builder.finish();
-        Some(vec![fix(
-            "remove_mut",
-            "Remove unnecessary `mut`",
-            SourceChange::from_text_edit(file_id.file_id(ctx.sema.db), edit),
-            use_range,
-        )])
-    })();
-    Some(
-        Diagnostic::new_with_syntax_node_ptr(
-            ctx,
-            DiagnosticCode::RustcLint("unused_mut"),
-            "variable does not need to be mutable",
-            ast,
-        )
-        // Not supporting `#[allow(unused_mut)]` in proc macros leads to false positive, hence not stable.
-        .with_fixes(fixes),
-    )
-}
-
-pub(super) fn token(parent: &SyntaxNode, kind: SyntaxKind) -> Option<SyntaxToken> {
-    parent.children_with_tokens().filter_map(|it| it.into_token()).find(|it| it.kind() == kind)
-}
-
-#[cfg(test)]
-mod tests {
-    use crate::tests::{check_diagnostics, check_diagnostics_with_disabled, check_fix};
-
-    #[test]
-    fn unused_mut_simple() {
-        check_diagnostics(
-            r#"
-fn f(_: i32) {}
-fn main() {
-    let mut x = 2;
-      //^^^^^ 💡 warn: variable does not need to be mutable
-    f(x);
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn no_false_positive_simple() {
-        check_diagnostics(
-            r#"
-fn f(_: i32) {}
-fn main() {
-    let x = 2;
-    f(x);
-}
-"#,
-        );
-        check_diagnostics(
-            r#"
-fn f(_: i32) {}
-fn main() {
-    let mut x = 2;
-    x = 5;
-    f(x);
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn multiple_errors_for_single_variable() {
-        check_diagnostics(
-            r#"
-fn f(_: i32) {}
-fn main() {
-    let x = 2;
-    x = 10;
-  //^^^^^^ 💡 error: cannot mutate immutable variable `x`
-    x = 5;
-  //^^^^^ 💡 error: cannot mutate immutable variable `x`
-    &mut x;
-  //^^^^^^ 💡 error: cannot mutate immutable variable `x`
-    f(x);
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn unused_mut_fix() {
-        check_fix(
-            r#"
-fn f(_: i32) {}
-fn main() {
-    let mu$0t x = 2;
-    f(x);
-}
-"#,
-            r#"
-fn f(_: i32) {}
-fn main() {
-    let x = 2;
-    f(x);
-}
-"#,
-        );
-        check_fix(
-            r#"
-fn f(_: i32) {}
-fn main() {
-    let ((mu$0t x, _) | (_, mut x)) = (2, 3);
-    f(x);
-}
-"#,
-            r#"
-fn f(_: i32) {}
-fn main() {
-    let ((x, _) | (_, x)) = (2, 3);
-    f(x);
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn need_mut_fix() {
-        check_fix(
-            r#"
-fn f(_: i32) {}
-fn main() {
-    let x = 2;
-    x$0 = 5;
-    f(x);
-}
-"#,
-            r#"
-fn f(_: i32) {}
-fn main() {
-    let mut x = 2;
-    x = 5;
-    f(x);
-}
-"#,
-        );
-        check_fix(
-            r#"
-fn f(_: i32) {}
-fn main() {
-    let ((x, _) | (_, x)) = (2, 3);
-    x =$0 4;
-    f(x);
-}
-"#,
-            r#"
-fn f(_: i32) {}
-fn main() {
-    let ((mut x, _) | (_, mut x)) = (2, 3);
-    x = 4;
-    f(x);
-}
-"#,
-        );
-
-        check_fix(
-            r#"
-struct Foo(i32);
-
-impl Foo {
-    fn foo(self) {
-        self = Fo$0o(5);
-    }
-}
-"#,
-            r#"
-struct Foo(i32);
-
-impl Foo {
-    fn foo(mut self) {
-        self = Foo(5);
-    }
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn need_mut_fix_not_applicable_on_ref() {
-        check_diagnostics(
-            r#"
-fn main() {
-    let ref x = 2;
-    x = &5;
-  //^^^^^^ error: cannot mutate immutable variable `x`
-}
-"#,
-        );
-        check_diagnostics(
-            r#"
-fn main() {
-    let ref mut x = 2;
-    x = &mut 5;
-  //^^^^^^^^^^ error: cannot mutate immutable variable `x`
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn field_mutate() {
-        check_diagnostics(
-            r#"
-fn f(_: i32) {}
-fn main() {
-    let mut x = (2, 7);
-      //^^^^^ 💡 warn: variable does not need to be mutable
-    f(x.1);
-}
-"#,
-        );
-        check_diagnostics(
-            r#"
-fn f(_: i32) {}
-fn main() {
-    let mut x = (2, 7);
-    x.0 = 5;
-    f(x.1);
-}
-"#,
-        );
-        check_diagnostics(
-            r#"
-fn f(_: i32) {}
-fn main() {
-    let x = (2, 7);
-    x.0 = 5;
-  //^^^^^^^ 💡 error: cannot mutate immutable variable `x`
-    f(x.1);
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn mutable_reference() {
-        check_diagnostics(
-            r#"
-fn main() {
-    let mut x = &mut 2;
-      //^^^^^ 💡 warn: variable does not need to be mutable
-    *x = 5;
-}
-"#,
-        );
-        check_diagnostics(
-            r#"
-fn main() {
-    let x = 2;
-    &mut x;
-  //^^^^^^ 💡 error: cannot mutate immutable variable `x`
-}
-"#,
-        );
-        check_diagnostics(
-            r#"
-fn main() {
-    let x_own = 2;
-    let ref mut x_ref = x_own;
-      //^^^^^^^^^^^^^ 💡 error: cannot mutate immutable variable `x_own`
-    _ = x_ref;
-}
-"#,
-        );
-        check_diagnostics(
-            r#"
-struct Foo;
-impl Foo {
-    fn method(&mut self, _x: i32) {}
-}
-fn main() {
-    let x = Foo;
-    x.method(2);
-  //^ 💡 error: cannot mutate immutable variable `x`
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn regression_14310() {
-        check_diagnostics(
-            r#"
-            //- minicore: copy, builtin_impls
-            fn clone(mut i: &!) -> ! {
-                   //^^^^^ 💡 warn: variable does not need to be mutable
-                *i
-            }
-        "#,
-        );
-    }
-
-    #[test]
-    fn match_closure_capture() {
-        check_diagnostics(
-            r#"
-//- minicore: option
-fn main() {
-    let mut v = &mut Some(2);
-      //^^^^^ 💡 warn: variable does not need to be mutable
-    let _ = || match v {
-        Some(k) => {
-            *k = 5;
-        }
-        None => {}
-    };
-    let v = &mut Some(2);
-    let _ = || match v {
-                   //^ 💡 error: cannot mutate immutable variable `v`
-        ref mut k => {
-            *k = &mut Some(5);
-        }
-    };
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn match_bindings() {
-        check_diagnostics(
-            r#"
-fn main() {
-    match (2, 3) {
-        (x, mut y) => {
-          //^^^^^ 💡 warn: variable does not need to be mutable
-            x = 7;
-          //^^^^^ 💡 error: cannot mutate immutable variable `x`
-            _ = y;
-        }
-    }
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn mutation_in_dead_code() {
-        // This one is interesting. Dead code is not represented at all in the MIR, so
-        // there would be no mutability error for locals in dead code. Rustc tries to
-        // not emit `unused_mut` in this case, but since it works without `mut`, and
-        // special casing it is not trivial, we emit it.
-
-        // Update: now MIR based `unused-variable` is taking over `unused-mut` for the same reason.
-        check_diagnostics(
-            r#"
-fn main() {
-    return;
-    let mut x = 2;
-      //^^^^^ 💡 warn: unused variable
-    &mut x;
-}
-"#,
-        );
-        check_diagnostics(
-            r#"
-fn main() {
-    loop {}
-    let mut x = 2;
-      //^^^^^ 💡 warn: unused variable
-    &mut x;
-}
-"#,
-        );
-        check_diagnostics_with_disabled(
-            r#"
-enum X {}
-fn g() -> X {
-    loop {}
-}
-fn f() -> ! {
-    loop {}
-}
-fn main(b: bool) {
-    if b {
-        f();
-    } else {
-        g();
-    }
-    let mut x = 2;
-      //^^^^^ 💡 warn: unused variable
-    &mut x;
-}
-"#,
-            &["remove-unnecessary-else"],
-        );
-        check_diagnostics_with_disabled(
-            r#"
-fn main(b: bool) {
-    if b {
-        loop {}
-    } else {
-        return;
-    }
-    let mut x = 2;
-      //^^^^^ 💡 warn: unused variable
-    &mut x;
-}
-"#,
-            &["remove-unnecessary-else"],
-        );
-    }
-
-    #[test]
-    fn initialization_is_not_mutation() {
-        check_diagnostics(
-            r#"
-fn f(_: i32) {}
-fn main() {
-    let mut x;
-      //^^^^^ 💡 warn: variable does not need to be mutable
-    x = 5;
-    f(x);
-}
-"#,
-        );
-        check_diagnostics(
-            r#"
-fn f(_: i32) {}
-fn main(b: bool) {
-    let mut x;
-      //^^^^^ 💡 warn: variable does not need to be mutable
-    if b {
-        x = 1;
-    } else {
-        x = 3;
-    }
-    f(x);
-}
-"#,
-        );
-        check_diagnostics(
-            r#"
-fn f(_: i32) {}
-fn main(b: bool) {
-    let x;
-    if b {
-        x = 1;
-    }
-    x = 3;
-  //^^^^^ 💡 error: cannot mutate immutable variable `x`
-    f(x);
-}
-"#,
-        );
-        check_diagnostics(
-            r#"
-fn f(_: i32) {}
-fn main() {
-    let x;
-    loop {
-        x = 1;
-      //^^^^^ 💡 error: cannot mutate immutable variable `x`
-        f(x);
-    }
-}
-"#,
-        );
-        check_diagnostics(
-            r#"
-fn check(_: i32) -> bool {
-    false
-}
-fn main() {
-    loop {
-        let x = 1;
-        if check(x) {
-            break;
-        }
-        let y = (1, 2);
-        if check(y.1) {
-            return;
-        }
-        let z = (1, 2);
-        match z {
-            (k @ 5, ref mut t) if { continue; } => {
-                  //^^^^^^^^^ 💡 error: cannot mutate immutable variable `z`
-                *t = 5;
-                _ = k;
-            }
-            _ => {
-                let y = (1, 2);
-                if check(y.1) {
-                    return;
-                }
-            }
-        }
-    }
-}
-"#,
-        );
-        check_diagnostics(
-            r#"
-fn f(_: i32) {}
-fn main() {
-    loop {
-        let mut x = 1;
-          //^^^^^ 💡 warn: variable does not need to be mutable
-        f(x);
-        if let mut y = 2 {
-             //^^^^^ 💡 warn: variable does not need to be mutable
-            f(y);
-        }
-        match 3 {
-            mut z => f(z),
-          //^^^^^ 💡 warn: variable does not need to be mutable
-        }
-    }
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn initialization_is_not_mutation_in_loop() {
-        check_diagnostics(
-            r#"
-fn main() {
-    let a;
-    loop {
-        let c @ (
-            mut b,
-          //^^^^^ 💡 warn: variable does not need to be mutable
-            mut d
-          //^^^^^ 💡 warn: variable does not need to be mutable
-        );
-        a = 1;
-      //^^^^^ 💡 error: cannot mutate immutable variable `a`
-        b = 1;
-        c = (2, 3);
-        d = 3;
-        _ = (c, b, d);
-    }
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn function_arguments_are_initialized() {
-        check_diagnostics(
-            r#"
-fn f(mut x: i32) {
-   //^^^^^ 💡 warn: variable does not need to be mutable
-   f(x + 2);
-}
-"#,
-        );
-        check_diagnostics(
-            r#"
-fn f(x: i32) {
-   x = 5;
- //^^^^^ 💡 error: cannot mutate immutable variable `x`
-}
-"#,
-        );
-        check_diagnostics(
-            r#"
-fn f((x, y): (i32, i32)) {
-    let t = [0; 2];
-    x = 5;
-  //^^^^^ 💡 error: cannot mutate immutable variable `x`
-    _ = x;
-    _ = y;
-    _ = t;
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn no_diagnostics_in_case_of_multiple_bounds() {
-        check_diagnostics(
-            r#"
-fn f() {
-    let (b, a, b) = (2, 3, 5);
-    a = 8;
-  //^^^^^ 💡 error: cannot mutate immutable variable `a`
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn for_loop() {
-        check_diagnostics(
-            r#"
-//- minicore: iterators, copy
-fn f(x: [(i32, u8); 10]) {
-    for (a, mut b) in x {
-          //^^^^^ 💡 warn: variable does not need to be mutable
-        a = 2;
-      //^^^^^ 💡 error: cannot mutate immutable variable `a`
-        _ = b;
-    }
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn while_let() {
-        check_diagnostics(
-            r#"
-//- minicore: iterators, copy
-fn f(x: [(i32, u8); 10]) {
-    let mut it = x.into_iter();
-    while let Some((a, mut b)) = it.next() {
-                     //^^^^^ 💡 warn: variable does not need to be mutable
-        while let Some((c, mut d)) = it.next() {
-                         //^^^^^ 💡 warn: variable does not need to be mutable
-            a = 2;
-          //^^^^^ 💡 error: cannot mutate immutable variable `a`
-            c = 2;
-          //^^^^^ 💡 error: cannot mutate immutable variable `c`
-            _ = (b, d);
-        }
-    }
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn index() {
-        check_diagnostics(
-            r#"
-//- minicore: coerce_unsized, index, slice
-fn f() {
-    let x = [1, 2, 3];
-    x[2] = 5;
-  //^^^^^^^^ 💡 error: cannot mutate immutable variable `x`
-    let x = &mut x;
-          //^^^^^^ 💡 error: cannot mutate immutable variable `x`
-    let mut x = x;
-      //^^^^^ 💡 warn: variable does not need to be mutable
-    x[2] = 5;
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn overloaded_index() {
-        check_diagnostics(
-            r#"
-//- minicore: index, copy
-use core::ops::{Index, IndexMut};
-
-struct Foo;
-impl Index<usize> for Foo {
-    type Output = (i32, u8);
-    fn index(&self, _index: usize) -> &(i32, u8) {
-        &(5, 2)
-    }
-}
-impl IndexMut<usize> for Foo {
-    fn index_mut(&mut self, _index: usize) -> &mut (i32, u8) {
-        &mut (5, 2)
-    }
-}
-fn f() {
-    let mut x = Foo;
-      //^^^^^ 💡 warn: variable does not need to be mutable
-    let y = &x[2];
-    _ = (x, y);
-    let x = Foo;
-    let y = &mut x[2];
-               //^💡 error: cannot mutate immutable variable `x`
-    _ = (x, y);
-    let mut x = &mut Foo;
-      //^^^^^ 💡 warn: variable does not need to be mutable
-    let y: &mut (i32, u8) = &mut x[2];
-    _ = (x, y);
-    let x = Foo;
-    let ref mut y = x[7];
-                  //^ 💡 error: cannot mutate immutable variable `x`
-    _ = (x, y);
-    let (ref mut y, _) = x[3];
-                       //^ 💡 error: cannot mutate immutable variable `x`
-    _ = y;
-    match x[10] {
-        //^ 💡 error: cannot mutate immutable variable `x`
-        (ref y, 5) => _ = y,
-        (_, ref mut y) => _ = y,
-    }
-    let mut x = Foo;
-    let mut i = 5;
-      //^^^^^ 💡 warn: variable does not need to be mutable
-    let y = &mut x[i];
-    _ = y;
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn overloaded_deref() {
-        check_diagnostics(
-            r#"
-//- minicore: deref_mut, copy
-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 f() {
-    let mut x = Foo;
-      //^^^^^ 💡 warn: variable does not need to be mutable
-    let y = &*x;
-    _ = (x, y);
-    let x = Foo;
-    let y = &mut *x;
-               // ^ 💡 error: cannot mutate immutable variable `x`
-    _ = (x, y);
-    let x = Foo;
-      //^ 💡 warn: unused variable
-    let x = Foo;
-    let y: &mut (i32, u8) = &mut x;
-                          //^^^^^^ 💡 error: cannot mutate immutable variable `x`
-    _ = (x, y);
-    let ref mut y = *x;
-                  // ^ 💡 error: cannot mutate immutable variable `x`
-    _ = y;
-    let (ref mut y, _) = *x;
-                       // ^ 💡 error: cannot mutate immutable variable `x`
-    _ = y;
-    match *x {
-        // ^ 💡 error: cannot mutate immutable variable `x`
-        (ref y, 5) => _ = y,
-        (_, ref mut y) => _ = y,
-    }
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn or_pattern() {
-        check_diagnostics(
-            r#"
-//- minicore: option
-fn f(_: i32) {}
-fn main() {
-    let ((Some(mut x), None) | (_, Some(mut x))) = (None, Some(7)) else { return };
-             //^^^^^ 💡 warn: variable does not need to be mutable
-
-    f(x);
-}
-"#,
-        );
-        check_diagnostics(
-            r#"
-struct Foo(i32);
-
-const X: Foo = Foo(5);
-const Y: Foo = Foo(12);
-
-const fn f(mut a: Foo) -> bool {
-         //^^^^^ 💡 warn: variable does not need to be mutable
-    match a {
-        X | Y => true,
-        _ => false,
-    }
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn or_pattern_no_terminator() {
-        check_diagnostics(
-            r#"
-enum Foo {
-    A, B, C, D
-}
-
-use Foo::*;
-
-fn f(inp: (Foo, Foo, Foo, Foo)) {
-    let ((A, B, _, x) | (B, C | D, x, _)) = inp else {
-        return;
-    };
-    x = B;
-  //^^^^^ 💡 error: cannot mutate immutable variable `x`
-}
-"#,
-        );
-    }
-
-    #[test]
-    // FIXME: We should have tests for `is_ty_uninhabited_from`
-    fn regression_14421() {
-        check_diagnostics(
-            r#"
-pub enum Tree {
-    Node(TreeNode),
-    Leaf(TreeLeaf),
-}
-
-struct Box<T>(&T);
-
-pub struct TreeNode {
-    pub depth: usize,
-    pub children: [Box<Tree>; 8]
-}
-
-pub struct TreeLeaf {
-    pub depth: usize,
-    pub data: u8
-}
-
-pub fn test() {
-    let mut tree = Tree::Leaf(
-      //^^^^^^^^ 💡 warn: variable does not need to be mutable
-        TreeLeaf {
-            depth: 0,
-            data: 0
-        }
-    );
-    _ = tree;
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn fn_traits() {
-        check_diagnostics(
-            r#"
-//- minicore: fn
-fn fn_ref(mut x: impl Fn(u8) -> u8) -> u8 {
-        //^^^^^ 💡 warn: variable does not need to be mutable
-    x(2)
-}
-fn fn_mut(x: impl FnMut(u8) -> u8) -> u8 {
-    x(2)
-  //^ 💡 error: cannot mutate immutable variable `x`
-}
-fn fn_borrow_mut(mut x: &mut impl FnMut(u8) -> u8) -> u8 {
-               //^^^^^ 💡 warn: variable does not need to be mutable
-    x(2)
-}
-fn fn_once(mut x: impl FnOnce(u8) -> u8) -> u8 {
-         //^^^^^ 💡 warn: variable does not need to be mutable
-    x(2)
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn closure() {
-        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`
-            _ = (closure2, closure3, closure4);
-        }
-                    "#,
-        );
-        check_diagnostics(
-            r#"
-        //- minicore: copy, fn
-        fn f() {
-            let mut x = 5;
-              //^^^^^ 💡 warn: 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;
-                  //^^^^^ 💡 warn: variable does not need to be mutable
-                _ = k;
-            };
-            _ = (x, closure);
-        }
-                    "#,
-        );
-        check_diagnostics(
-            r#"
-//- minicore: copy, fn
-fn f() {
-    let closure = || {
-        || {
-            || {
-                let x = 2;
-                || { || { x = 5; } }
-                        //^^^^^ 💡 error: cannot mutate immutable variable `x`
-            }
-        }
-    };
-    _ = closure;
-}
-            "#,
-        );
-        check_diagnostics(
-            r#"
-//- minicore: copy, fn
-fn f() {
-    struct X;
-    let mut x = X;
-      //^^^^^ 💡 warn: variable does not need to be mutable
-    let c1 = || x;
-    let mut x = X;
-    let c2 = || { x = X; x };
-    let mut x = X;
-    let c3 = move || { x = X; };
-    _ = (c1, c2, c3);
-}
-            "#,
-        );
-        check_diagnostics(
-            r#"
-        //- minicore: copy, fn, deref_mut
-        struct X(i32, i64);
-
-        fn f() {
-            let mut x = &mut 5;
-              //^^^^^ 💡 warn: variable does not need to be mutable
-            let closure1 = || { *x = 2; };
-            let _ = closure1();
-                  //^^^^^^^^ 💡 error: cannot mutate immutable variable `closure1`
-            let mut x = &mut 5;
-              //^^^^^ 💡 warn: variable does not need to be mutable
-            let closure1 = || { *x = 2; &x; };
-            let _ = closure1();
-                  //^^^^^^^^ 💡 error: cannot mutate immutable variable `closure1`
-            let mut x = &mut 5;
-            let closure1 = || { *x = 2; &x; x = &mut 3; };
-            let _ = closure1();
-                  //^^^^^^^^ 💡 error: cannot mutate immutable variable `closure1`
-            let mut x = &mut 5;
-              //^^^^^ 💡 warn: variable does not need to be mutable
-            let closure1 = move || { *x = 2; };
-            let _ = closure1();
-                  //^^^^^^^^ 💡 error: cannot mutate immutable variable `closure1`
-            let mut x = &mut X(1, 2);
-              //^^^^^ 💡 warn: variable does not need to be mutable
-            let closure1 = || { x.0 = 2; };
-            let _ = closure1();
-                  //^^^^^^^^ 💡 error: cannot mutate immutable variable `closure1`
-        }
-                    "#,
-        );
-    }
-
-    #[test]
-    fn slice_pattern() {
-        check_diagnostics(
-            r#"
-//- minicore: coerce_unsized, deref_mut, slice, copy
-fn x(t: &[u8]) {
-    match t {
-        &[a, mut b] | &[a, _, mut b] => {
-           //^^^^^ 💡 warn: variable does not need to be mutable
-
-            a = 2;
-          //^^^^^ 💡 error: cannot mutate immutable variable `a`
-            _ = b;
-        }
-        _ => {}
-    }
-}
-            "#,
-        );
-    }
-
-    #[test]
-    fn boxes() {
-        check_diagnostics(
-            r#"
-//- minicore: coerce_unsized, deref_mut, slice
-use core::ops::{Deref, DerefMut};
-use core::{marker::Unsize, ops::CoerceUnsized};
-
-#[rustc_intrinsic]
-#[rustc_intrinsic_must_be_overridden]
-pub fn box_new<T>(_x: T) -> Box<T>;
-
-#[lang = "owned_box"]
-pub struct Box<T: ?Sized> {
-    inner: *mut T,
-}
-impl<T> Box<T> {
-    fn new(t: T) -> Self {
-        box_new(t)
-    }
-}
-
-impl<T: ?Sized> Deref for Box<T> {
-    type Target = T;
-
-    fn deref(&self) -> &T {
-        &**self
-    }
-}
-
-impl<T: ?Sized> DerefMut for Box<T> {
-    fn deref_mut(&mut self) -> &mut T {
-        &mut **self
-    }
-}
-
-fn f() {
-    let x = Box::new(5);
-    x = Box::new(7);
-  //^^^^^^^^^^^^^^^ 💡 error: cannot mutate immutable variable `x`
-    let x = Box::new(5);
-    *x = 7;
-  //^^^^^^ 💡 error: cannot mutate immutable variable `x`
-    let mut y = Box::new(5);
-      //^^^^^ 💡 warn: variable does not need to be mutable
-    *x = *y;
-  //^^^^^^^ 💡 error: cannot mutate immutable variable `x`
-    let x = Box::new(5);
-    let closure = || *x = 2;
-                   //^^^^^^ 💡 error: cannot mutate immutable variable `x`
-    _ = closure;
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn regression_15143() {
-        check_diagnostics(
-            r#"
-        trait Tr {
-            type Ty;
-        }
-
-        struct A;
-
-        impl Tr for A {
-            type Ty = (u32, i64);
-        }
-
-        struct B<T: Tr> {
-            f: <T as Tr>::Ty,
-        }
-
-        fn main(b: B<A>) {
-            let f = b.f.0;
-            f = 5;
-          //^^^^^ 💡 error: cannot mutate immutable variable `f`
-        }
-            "#,
-        );
-    }
-
-    #[test]
-    fn allow_unused_mut_for_identifiers_starting_with_underline() {
-        check_diagnostics(
-            r#"
-fn f(_: i32) {}
-fn main() {
-    let mut _x = 2;
-    f(_x);
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn respect_lint_attributes_for_unused_mut() {
-        check_diagnostics(
-            r#"
-fn f(_: i32) {}
-fn main() {
-    #[allow(unused_mut)]
-    let mut x = 2;
-    f(x);
-}
-
-fn main2() {
-    #[deny(unused_mut)]
-    let mut x = 2;
-      //^^^^^ 💡 error: variable does not need to be mutable
-    f(x);
-}
-"#,
-        );
-        check_diagnostics(
-            r#"
-macro_rules! mac {
-    ($($x:expr),*$(,)*) => ({
-        #[allow(unused_mut)]
-        let mut vec = 2;
-        vec
-    });
-}
-
-fn main2() {
-    let mut x = mac![];
-      //^^^^^ 💡 warn: variable does not need to be mutable
-    _ = x;
-}
-        "#,
-        );
-    }
-
-    #[test]
-    fn regression_15099() {
-        check_diagnostics(
-            r#"
-//- minicore: iterator, range
-fn f() {
-    loop {}
-    for _ in 0..2 {}
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn regression_15623() {
-        check_diagnostics(
-            r#"
-//- minicore: fn
-
-struct Foo;
-
-impl Foo {
-    fn needs_mut(&mut self) {}
-}
-
-fn foo(mut foo: Foo) {
-    let mut call_me = || {
-        let 0 = 1 else { return };
-        foo.needs_mut();
-    };
-    call_me();
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn regression_15670() {
-        check_diagnostics(
-            r#"
-//- minicore: fn
-
-pub struct A {}
-pub unsafe fn foo(a: *mut A) {
-    let mut b = || -> *mut A { unsafe { &mut *a } };
-      //^^^^^ 💡 warn: variable does not need to be mutable
-    let _ = b();
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn regression_15799() {
-        check_diagnostics(
-            r#"
-//- minicore: deref_mut
-struct WrapPtr(*mut u32);
-
-impl core::ops::Deref for WrapPtr {
-    type Target = *mut u32;
-    fn deref(&self) -> &Self::Target {
-        &self.0
-    }
-}
-
-fn main() {
-    let mut x = 0u32;
-    let wrap = WrapPtr(&mut x);
-    unsafe {
-        **wrap = 6;
-    }
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn destructuring_assignment_needs_mut() {
-        check_diagnostics(
-            r#"
-//- minicore: fn
-
-fn main() {
-	let mut var = 1;
-	let mut func = || (var,) = (2,);
-	func();
-}
-        "#,
-        );
-    }
-
-    #[test]
-    fn regression_20662() {
-        check_diagnostics(
-            r#"
-//- minicore: index, slice
-pub trait A: core::ops::IndexMut<usize> {
-    type T: A;
-}
-
-fn func(a: &mut impl A, b: &mut [i32]) {
-    b[0] += 1;
-}
-        "#,
-        );
-    }
-}
diff --git a/crates/ide-diagnostics/src/handlers/type_mismatch.rs b/crates/ide-diagnostics/src/handlers/type_mismatch.rs
index b16be50..df815d6 100644
--- a/crates/ide-diagnostics/src/handlers/type_mismatch.rs
+++ b/crates/ide-diagnostics/src/handlers/type_mismatch.rs
@@ -45,18 +45,31 @@
         cov_mark::hit!(type_mismatch_range_adjustment);
         Some(salient_token_range)
     });
+
+    let expected = d
+        .expected
+        .display(ctx.db(), ctx.display_target)
+        .with_closure_style(ClosureStyle::ClosureWithId)
+        .to_string();
+    let actual = d
+        .actual
+        .display(ctx.db(), ctx.display_target)
+        .with_closure_style(ClosureStyle::ClosureWithId)
+        .to_string();
+
+    // The types differ (that's why we're here), yet they render the same, e.g. `foo::S` and
+    // `bar::S` both render as `S`. Retry with qualified paths so the message isn't a useless
+    // "expected S, found S".
+    let (expected, actual) = if expected == actual {
+        qualified_display(ctx, d).unwrap_or((expected, actual))
+    } else {
+        (expected, actual)
+    };
+
     Some(
         Diagnostic::new(
             DiagnosticCode::RustcHardError("E0308"),
-            format!(
-                "expected {}, found {}",
-                d.expected
-                    .display(ctx.db(), ctx.display_target)
-                    .with_closure_style(ClosureStyle::ClosureWithId),
-                d.actual
-                    .display(ctx.db(), ctx.display_target)
-                    .with_closure_style(ClosureStyle::ClosureWithId),
-            ),
+            format!("expected {expected}, found {actual}"),
             display_range,
         )
         .stable()
@@ -64,6 +77,20 @@
     )
 }
 
+/// Renders both sides of the mismatch with qualified paths, for when their plain names collide.
+/// Returns `None` if either side has no renderable path, in which case both keep their plain
+/// names — mixing a qualified and an unqualified name would be more confusing, not less.
+fn qualified_display(
+    ctx: &DiagnosticsContext<'_, '_>,
+    d: &hir::TypeMismatch<'_>,
+) -> Option<(String, String)> {
+    let root = d.expr_or_pat.file_id.parse_or_expand(ctx.db());
+    let module = ctx.sema.scope(d.expr_or_pat.value.to_node(&root).syntax())?.module();
+    let expected = d.expected.display_source_code(ctx.db(), module.into(), true).ok()?;
+    let actual = d.actual.display_source_code(ctx.db(), module.into(), true).ok()?;
+    Some((expected, actual))
+}
+
 fn fixes(ctx: &DiagnosticsContext<'_, '_>, d: &hir::TypeMismatch<'_>) -> Option<Vec<Assist>> {
     let mut fixes = Vec::new();
 
@@ -1838,4 +1865,88 @@
 "#,
         );
     }
+
+    // Tests for qualified paths on name collision (issue #22331)
+
+    #[test]
+    fn type_mismatch_collision_basic_structs() {
+        check_diagnostics(
+            r#"
+mod foo {
+    pub struct S;
+}
+mod bar {
+    pub struct S;
+}
+fn test(_: foo::S) {
+    test(bar::S);
+       //^^^^^^ error: expected foo::S, found bar::S
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn type_mismatch_collision_in_generic() {
+        check_diagnostics(
+            r#"
+//- minicore: option
+mod foo {
+    pub struct S;
+}
+mod bar {
+    pub struct S;
+}
+fn make() -> Option<bar::S> { loop {} }
+fn test(_: Option<foo::S>) {
+    test(make());
+       //^^^^^^ error: expected Option<foo::S>, found Option<bar::S>
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn type_mismatch_no_collision_unchanged() {
+        check_diagnostics(
+            r#"
+mod foo {
+    pub struct S;
+}
+mod bar {
+    pub struct T;
+}
+fn test(_: foo::S) {
+    test(bar::T);
+       //^^^^^^ error: expected S, found T
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn type_mismatch_multiple_collisions() {
+        check_diagnostics(
+            r#"
+//- minicore: result
+mod foo {
+    pub struct T;
+}
+mod bar {
+    pub struct T;
+}
+mod baz {
+    pub struct E;
+}
+mod qux {
+    pub struct E;
+}
+fn make() -> Result<bar::T, qux::E> { loop {} }
+fn test(_: Result<foo::T, baz::E>) {
+    test(make());
+       //^^^^^^ error: expected Result<foo::T, baz::E>, found Result<bar::T, qux::E>
+}
+"#,
+        );
+    }
 }
diff --git a/crates/ide-diagnostics/src/handlers/type_must_be_known.rs b/crates/ide-diagnostics/src/handlers/type_must_be_known.rs
index ae4e6c6..5a338de 100644
--- a/crates/ide-diagnostics/src/handlers/type_must_be_known.rs
+++ b/crates/ide-diagnostics/src/handlers/type_must_be_known.rs
@@ -64,11 +64,9 @@
             r#"
 fn foo() {
     let var = loop {};
-     // ^^^ 💡 warn: unused variable
     var();
  // ^^^ error: type annotations needed; type must be known at this point
     let var = loop {};
-     // ^^^ 💡 warn: unused variable
     var[0];
  // ^^^ error: type annotations needed; type must be known at this point
 }
diff --git a/crates/ide-diagnostics/src/handlers/typed_hole.rs b/crates/ide-diagnostics/src/handlers/typed_hole.rs
index 4c59482..e2d3150 100644
--- a/crates/ide-diagnostics/src/handlers/typed_hole.rs
+++ b/crates/ide-diagnostics/src/handlers/typed_hole.rs
@@ -54,12 +54,7 @@
         sema: &ctx.sema,
         scope: &scope,
         goal: d.expected.clone(),
-        config: TermSearchConfig {
-            fuel: ctx.config.term_search_fuel,
-            enable_borrowcheck: ctx.config.term_search_borrowck,
-
-            ..Default::default()
-        },
+        config: TermSearchConfig { fuel: ctx.config.term_search_fuel, ..Default::default() },
     };
     let paths = term_search(&term_search_ctx);
 
diff --git a/crates/ide-diagnostics/src/handlers/unlinked_file.rs b/crates/ide-diagnostics/src/handlers/unlinked_file.rs
index dc6ae6f..376fc4fb 100644
--- a/crates/ide-diagnostics/src/handlers/unlinked_file.rs
+++ b/crates/ide-diagnostics/src/handlers/unlinked_file.rs
@@ -3,14 +3,15 @@
 use std::iter;
 
 use hir::crate_def_map;
-use hir::{InFile, ModuleSource};
+use hir::{EditionedFileId, InFile, ModuleSource};
 use ide_db::text_edit::TextEdit;
 use ide_db::{FileId, FileRange, base_db::SourceDatabase, source_change::SourceChange};
 use ide_db::{base_db, line_index};
 use paths::Utf8Component;
 use syntax::{
-    AstNode, TextRange,
+    AstNode, Edition, TextRange,
     ast::{self, HasModuleItem, HasName, edit::IndentLevel},
+    utils::{is_identifier, is_raw_identifier},
 };
 
 use crate::{Assist, Diagnostic, DiagnosticCode, DiagnosticsContext, Severity, fix};
@@ -22,10 +23,11 @@
 pub(crate) fn unlinked_file(
     ctx: &DiagnosticsContext<'_, '_>,
     acc: &mut Vec<Diagnostic>,
-    file_id: FileId,
+    editioned_file_id: EditionedFileId,
 ) {
+    let file_id = editioned_file_id.file_id(ctx.sema.db);
     let mut range = TextRange::up_to(line_index(ctx.sema.db, file_id).len());
-    let fixes = fixes(ctx, file_id, range);
+    let fixes = fixes(ctx, editioned_file_id, range);
     // 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() {
@@ -74,13 +76,15 @@
 
 fn fixes(
     ctx: &DiagnosticsContext<'_, '_>,
-    file_id: FileId,
+    editioned_file_id: EditionedFileId,
     trigger_range: TextRange,
 ) -> Option<Vec<Assist>> {
     // If there's an existing module that could add `mod` or `pub mod` items to include the unlinked file,
     // suggest that as a fix.
 
     let db = ctx.sema.db;
+    let file_id = editioned_file_id.file_id(db);
+    let edition = editioned_file_id.edition(db);
 
     let source_root = ctx.sema.db.file_source_root(file_id).source_root_id(db);
     let source_root = ctx.sema.db.source_root(source_root).source_root(db);
@@ -136,6 +140,7 @@
         return make_fixes(
             parent_file_id.file_id(ctx.sema.db),
             source,
+            edition,
             &module_name,
             trigger_range,
         );
@@ -169,6 +174,7 @@
             return make_fixes(
                 parent_id,
                 module.definition_source(ctx.sema.db).value,
+                edition,
                 &module_name,
                 trigger_range,
             );
@@ -193,6 +199,7 @@
             return make_fixes(
                 parent_file_id.file_id(ctx.sema.db),
                 source,
+                edition,
                 &module_name,
                 trigger_range,
             );
@@ -202,9 +209,26 @@
     None
 }
 
+/// Convert a module name along with its visibility to code.
+/// In most cases, this just adds the visibility and keyword beforehand,
+/// but the exceptions are non-identifiers and keywords.
+fn format_mod_name(mod_name: &str, visibility_and_keyword: &str, edition: Edition) -> String {
+    if is_identifier(mod_name, edition) {
+        format!("{visibility_and_keyword} {mod_name};")
+    } else {
+        if is_raw_identifier(mod_name, edition) {
+            format!("{visibility_and_keyword} r#{mod_name};")
+        } else {
+            let file_name = format!("{mod_name}.rs");
+            format!("#[path = {file_name:?}]\n{visibility_and_keyword} mod_name;")
+        }
+    }
+}
+
 fn make_fixes(
     parent_file_id: FileId,
     source: ModuleSource,
+    edition: Edition,
     new_mod_name: &str,
     trigger_range: TextRange,
 ) -> Option<Vec<Assist>> {
@@ -212,9 +236,9 @@
         matches!(item, ast::Item::Module(m) if m.item_list().is_none())
     }
 
-    let mod_decl = format!("mod {new_mod_name};");
-    let pub_mod_decl = format!("pub mod {new_mod_name};");
-    let pub_crate_mod_decl = format!("pub(crate) mod {new_mod_name};");
+    let mod_decl = format_mod_name(new_mod_name, "mod", edition);
+    let pub_mod_decl = format_mod_name(new_mod_name, "pub mod", edition);
+    let pub_crate_mod_decl = format_mod_name(new_mod_name, "pub(crate) mod", edition);
 
     let mut mod_decl_builder = TextEdit::builder();
     let mut pub_mod_decl_builder = TextEdit::builder();
@@ -548,4 +572,88 @@
 "#,
         );
     }
+
+    #[test]
+    fn unlinked_file_with_strict_keyword_move() {
+        check_fix(
+            r#"
+//- /main.rs
+//- /move.rs
+$0
+"#,
+            r#"
+mod r#move;
+"#,
+        );
+    }
+
+    #[test]
+    fn unlinked_file_with_weak_keyword_safe() {
+        check_fix(
+            r#"
+//- /main.rs
+//- /safe.rs
+$0
+"#,
+            r#"
+mod safe;
+"#,
+        );
+    }
+
+    #[test]
+    fn unlinked_file_with_reserved_keyword_abstract() {
+        check_fix(
+            r#"
+//- /main.rs
+//- /abstract.rs
+$0
+"#,
+            r#"
+mod r#abstract;
+"#,
+        );
+    }
+
+    #[test]
+    fn unlinked_file_with_unescaped_keyword_crate() {
+        check_fix(
+            r#"
+//- /main.rs
+//- /crate.rs
+$0
+"#,
+            r#"#[path = "crate.rs"]
+mod mod_name;
+"#,
+        );
+    }
+
+    #[test]
+    fn unlinked_invalid_symbol_in_module_name() {
+        check_fix(
+            r#"
+//- /main.rs
+//- /my-file.rs
+$0
+"#,
+            r#"#[path = "my-file.rs"]
+mod mod_name;
+"#,
+        );
+    }
+
+    #[test]
+    fn unlinked_numeric_module_name() {
+        check_fix(
+            r#"
+//- /main.rs
+//- /0000.rs
+$0
+"#,
+            r#"#[path = "0000.rs"]
+mod mod_name;
+"#,
+        );
+    }
 }
diff --git a/crates/ide-diagnostics/src/handlers/unused_variables.rs b/crates/ide-diagnostics/src/handlers/unused_variables.rs
deleted file mode 100644
index 9e5150c..0000000
--- a/crates/ide-diagnostics/src/handlers/unused_variables.rs
+++ /dev/null
@@ -1,420 +0,0 @@
-use hir::Name;
-use ide_db::text_edit::TextEdit;
-use ide_db::{
-    FileRange, RootDatabase,
-    assists::{Assist, AssistId},
-    label::Label,
-    source_change::SourceChange,
-};
-use syntax::{AstNode, Edition, TextRange, ToSmolStr};
-
-use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
-
-// Diagnostic: unused-variables
-//
-// This diagnostic is triggered when a local variable is not used.
-pub(crate) fn unused_variables(
-    ctx: &DiagnosticsContext<'_, '_>,
-    d: &hir::UnusedVariable<'_>,
-) -> Option<Diagnostic> {
-    let ast = d.local.primary_source(ctx.sema.db).syntax_ptr();
-    if ast.file_id.macro_file().is_some() {
-        // FIXME: Our infra can't handle allow from within macro expansions rn
-        return None;
-    }
-    let diagnostic_range = ctx.sema.diagnostics_display_range(ast);
-    // The range for the Actual Name. We don't want to replace the entire declaration. Using the diagnostic range causes issues within in Array Destructuring.
-    let primary_source = d.local.primary_source(ctx.sema.db);
-    let name_range = primary_source
-        .name()
-        .map(|v| v.syntax().original_file_range_rooted(ctx.sema.db))
-        .filter(|it| {
-            Some(it.file_id) == ast.file_id.file_id()
-                && diagnostic_range.range.contains_range(it.range)
-        });
-    let is_shorthand_field = primary_source
-        .source
-        .value
-        .left()
-        .and_then(|name| name.syntax().parent())
-        .and_then(syntax::ast::RecordPatField::cast)
-        .is_some_and(|field| field.colon_token().is_none());
-    let var_name = d.local.name(ctx.sema.db);
-    Some(
-        Diagnostic::new_with_syntax_node_ptr(
-            ctx,
-            DiagnosticCode::RustcLint("unused_variables"),
-            "unused variable",
-            ast,
-        )
-        .with_fixes(name_range.and_then(|it| {
-            fixes(
-                ctx.sema.db,
-                var_name,
-                it.range,
-                diagnostic_range,
-                ast.file_id.is_macro(),
-                is_shorthand_field,
-                ctx.edition,
-            )
-        })),
-    )
-}
-
-fn fixes(
-    db: &RootDatabase,
-    var_name: Name,
-    name_range: TextRange,
-    diagnostic_range: FileRange,
-    is_in_marco: bool,
-    is_shorthand_field: bool,
-    edition: Edition,
-) -> Option<Vec<Assist>> {
-    if is_in_marco {
-        return None;
-    }
-    let name = var_name.display(db, edition).to_smolstr();
-    let name = name.strip_prefix("r#").unwrap_or(&name);
-    let new_name = if is_shorthand_field { format!("{name}: _{name}") } else { format!("_{name}") };
-
-    Some(vec![Assist {
-        id: AssistId::quick_fix("unscore_unused_variable_name"),
-        label: Label::new(format!("Rename unused {name} to {new_name}")),
-        group: None,
-        target: diagnostic_range.range,
-        source_change: Some(SourceChange::from_text_edit(
-            diagnostic_range.file_id,
-            TextEdit::replace(name_range, new_name),
-        )),
-        command: None,
-    }])
-}
-
-#[cfg(test)]
-mod tests {
-    use crate::tests::{check_diagnostics, check_fix};
-
-    #[test]
-    fn unused_variables_simple() {
-        check_diagnostics(
-            r#"
-//- minicore: fn
-struct Foo { f1: i32, f2: i64 }
-
-fn f(kkk: i32) {}
-   //^^^ 💡 warn: unused variable
-fn main() {
-    let a = 2;
-      //^ 💡 warn: unused variable
-    let b = 5;
-    // note: `unused variable` implies `unused mut`, so we should not emit both at the same time.
-    let mut c = f(b);
-      //^^^^^ 💡 warn: unused variable
-    let (d, e) = (3, 5);
-       //^ 💡 warn: unused variable
-    let _ = e;
-    let f1 = 2;
-    let f2 = 5;
-    let f = Foo { f1, f2 };
-    match f {
-        Foo { f1, f2 } => {
-            //^^ 💡 warn: unused variable
-            _ = f2;
-        }
-    }
-    let g = false;
-    if g {}
-    let h: fn() -> i32 = || 2;
-    let i = h();
-      //^ 💡 warn: unused variable
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn unused_self() {
-        check_diagnostics(
-            r#"
-struct S {
-}
-impl S {
-    fn owned_self(self, u: i32) {}
-                      //^ 💡 warn: unused variable
-    fn ref_self(&self, u: i32) {}
-                     //^ 💡 warn: unused variable
-    fn ref_mut_self(&mut self, u: i32) {}
-                             //^ 💡 warn: unused variable
-    fn owned_mut_self(mut self) {}
-                    //^^^^^^^^ 💡 warn: variable does not need to be mutable
-
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn allow_unused_variables_for_identifiers_starting_with_underline() {
-        check_diagnostics(
-            r#"
-fn main() {
-    let _x = 2;
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn respect_lint_attributes_for_unused_variables() {
-        check_diagnostics(
-            r#"
-fn main() {
-    #[allow(unused_variables)]
-    let x = 2;
-}
-
-#[deny(unused)]
-fn main2() {
-    let x = 2;
-      //^ 💡 error: unused variable
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn apply_last_lint_attribute_when_multiple_are_present() {
-        check_diagnostics(
-            r#"
-#![allow(unused_variables)]
-#![warn(unused_variables)]
-#![deny(unused_variables)]
-
-fn main() {
-    let x = 2;
-      //^ 💡 error: unused variable
-
-    #[deny(unused_variables)]
-    #[warn(unused_variables)]
-    #[allow(unused_variables)]
-    let y = 0;
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn prefer_closest_ancestor_lint_attribute() {
-        check_diagnostics(
-            r#"
-#![allow(unused_variables)]
-
-fn main() {
-    #![warn(unused_variables)]
-
-    #[deny(unused_variables)]
-    let x = 2;
-      //^ 💡 error: unused variable
-}
-
-#[warn(unused_variables)]
-fn main2() {
-    #[deny(unused_variables)]
-    let x = 2;
-      //^ 💡 error: unused variable
-}
-
-#[warn(unused_variables)]
-fn main3() {
-    let x = 2;
-      //^ 💡 warn: unused variable
-}
-
-fn main4() {
-    let x = 2;
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn fix_unused_variable() {
-        check_fix(
-            r#"
-fn main() {
-    let x$0 = 2;
-}
-"#,
-            r#"
-fn main() {
-    let _x = 2;
-}
-"#,
-        );
-
-        check_fix(
-            r#"
-fn main() {
-    let ($0d, _e) = (3, 5);
-}
-"#,
-            r#"
-fn main() {
-    let (_d, _e) = (3, 5);
-}
-"#,
-        );
-
-        check_fix(
-            r#"
-struct Foo { f1: i32, f2: i64 }
-fn main() {
-    let f = Foo { f1: 0, f2: 0 };
-    match f {
-        Foo { f1$0, f2 } => {
-            _ = f2;
-        }
-    }
-}
-"#,
-            r#"
-struct Foo { f1: i32, f2: i64 }
-fn main() {
-    let f = Foo { f1: 0, f2: 0 };
-    match f {
-        Foo { f1: _f1, f2 } => {
-            _ = f2;
-        }
-    }
-}
-"#,
-        );
-
-        check_fix(
-            r#"
-fn main() {
-    let $0r#type = 2;
-}
-"#,
-            r#"
-fn main() {
-    let _type = 2;
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn no_fix_for_marco() {
-        check_diagnostics(
-            r#"
-macro_rules! my_macro {
-    () => {
-        let x = 3;
-    };
-}
-
-fn main() {
-    my_macro!();
-}
-"#,
-        );
-    }
-    #[test]
-    fn unused_variable_in_array_destructure() {
-        check_fix(
-            r#"
-fn main() {
-    let arr = [1, 2, 3, 4, 5];
-    let [_x, y$0 @ ..] = arr;
-}
-"#,
-            r#"
-fn main() {
-    let arr = [1, 2, 3, 4, 5];
-    let [_x, _y @ ..] = arr;
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn unused_variable_in_record_field() {
-        check_fix(
-            r#"
-struct S { field : u32 }
-fn main() {
-    let s = S { field : 2 };
-    let S { field: $0x } = s
-}
-"#,
-            r#"
-struct S { field : u32 }
-fn main() {
-    let s = S { field : 2 };
-    let S { field: _x } = s
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn unused_variable_in_shorthand_record_field() {
-        check_fix(
-            r#"
-struct S { field : u32 }
-fn main() {
-    let s = S { field : 2 };
-    let S { $0field } = s
-}
-"#,
-            r#"
-struct S { field : u32 }
-fn main() {
-    let s = S { field : 2 };
-    let S { field: _field } = s
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn crate_attrs_lint_smoke_test() {
-        check_diagnostics(
-            r#"
-//- /lib.rs crate:foo crate-attr:deny(unused_variables)
-fn main() {
-    let x = 2;
-      //^ 💡 error: unused variable
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn crate_attrs_should_not_override_lints_in_source() {
-        check_diagnostics(
-            r#"
-//- /lib.rs crate:foo crate-attr:allow(unused_variables)
-#![deny(unused_variables)]
-fn main() {
-    let x = 2;
-      //^ 💡 error: unused variable
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn crate_attrs_should_preserve_lint_order() {
-        check_diagnostics(
-            r#"
-//- /lib.rs crate:foo crate-attr:allow(unused_variables) crate-attr:warn(unused_variables)
-fn main() {
-    let x = 2;
-      //^ 💡 warn: unused variable
-}
-"#,
-        );
-    }
-}
diff --git a/crates/ide-diagnostics/src/lib.rs b/crates/ide-diagnostics/src/lib.rs
index 26e4a84..8ba59ed 100644
--- a/crates/ide-diagnostics/src/lib.rs
+++ b/crates/ide-diagnostics/src/lib.rs
@@ -64,9 +64,7 @@
     pub(crate) mod missing_lifetime;
     pub(crate) mod missing_match_arms;
     pub(crate) mod missing_unsafe;
-    pub(crate) mod moved_out_of_ref;
     pub(crate) mod mut_ref_in_imm_ref_pat;
-    pub(crate) mod mutability_errors;
     pub(crate) mod mutable_ref;
     pub(crate) mod no_such_field;
     pub(crate) mod non_exhaustive_let;
@@ -103,7 +101,6 @@
     pub(crate) mod unresolved_method;
     pub(crate) mod unresolved_module;
     pub(crate) mod unused_must_use;
-    pub(crate) mod unused_variables;
     pub(crate) mod yield_outside_coroutine;
 
     // The handlers below are unusual, the implement the diagnostics as well.
@@ -286,7 +283,6 @@
     pub prefer_prelude: bool,
     pub prefer_absolute: bool,
     pub term_search_fuel: u64,
-    pub term_search_borrowck: bool,
     pub show_rename_conflicts: bool,
 }
 
@@ -315,7 +311,6 @@
             prefer_prelude: true,
             prefer_absolute: false,
             term_search_fuel: 400,
-            term_search_borrowck: true,
             show_rename_conflicts: true,
         }
     }
@@ -435,9 +430,7 @@
                 m.diagnostics(db, &mut diags, config.style_lints);
             }
         }
-        None => {
-            handlers::unlinked_file::unlinked_file(&ctx, &mut res, editioned_file_id.file_id(db))
-        }
+        None => handlers::unlinked_file::unlinked_file(&ctx, &mut res, editioned_file_id),
     }
 
     for diag in diags {
@@ -484,13 +477,8 @@
             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::MovedOutOfRef(d) => handlers::moved_out_of_ref::moved_out_of_ref(&ctx, &d),
             AnyDiagnostic::MutRefInImmRefPat(d) => handlers::mut_ref_in_imm_ref_pat::mut_ref_in_imm_ref_pat(&ctx, &d),
             AnyDiagnostic::MutableRefBinding(d) => handlers::mutable_ref::mutable_ref_binding(&ctx, &d),
-            AnyDiagnostic::NeedMut(d) => match handlers::mutability_errors::need_mut(&ctx, &d) {
-                Some(it) => it,
-                None => continue,
-            },
             AnyDiagnostic::NonExhaustiveLet(d) => handlers::non_exhaustive_let::non_exhaustive_let(&ctx, &d),
             AnyDiagnostic::NonExhaustiveRecordExpr(d) => {
                 handlers::non_exhaustive_record_expr::non_exhaustive_record_expr(&ctx, &d)
@@ -524,14 +512,6 @@
             AnyDiagnostic::UnresolvedMethodCall(d) => handlers::unresolved_method::unresolved_method(&ctx, &d),
             AnyDiagnostic::UnresolvedModule(d) => handlers::unresolved_module::unresolved_module(&ctx, &d),
             AnyDiagnostic::UnusedMustUse(d) => handlers::unused_must_use::unused_must_use(&ctx, &d),
-            AnyDiagnostic::UnusedMut(d) => match handlers::mutability_errors::unused_mut(&ctx, &d) {
-                Some(it) => it,
-                None => continue,
-            },
-            AnyDiagnostic::UnusedVariable(d) => match handlers::unused_variables::unused_variables(&ctx, &d) {
-                Some(it) => it,
-                None => continue,
-            },
             AnyDiagnostic::BreakOutsideOfLoop(d) => handlers::break_outside_of_loop::break_outside_of_loop(&ctx, &d),
             AnyDiagnostic::MismatchedTupleStructPatArgCount(d) => handlers::mismatched_arg_count::mismatched_tuple_struct_pat_arg_count(&ctx, &d),
             AnyDiagnostic::RemoveTrailingReturn(d) => match handlers::remove_trailing_return::remove_trailing_return(&ctx, &d) {
diff --git a/crates/ide-ssr/src/resolving.rs b/crates/ide-ssr/src/resolving.rs
index 3dbba0f..9d60792 100644
--- a/crates/ide-ssr/src/resolving.rs
+++ b/crates/ide-ssr/src/resolving.rs
@@ -155,7 +155,7 @@
     fn path_contains_placeholder(&self, path: &ast::Path) -> bool {
         if let Some(segment) = path.segment()
             && let Some(name_ref) = segment.name_ref()
-            && self.placeholders_by_stand_in.contains_key(name_ref.text().as_str())
+            && self.placeholders_by_stand_in.contains_key(name_ref.text())
         {
             return true;
         }
diff --git a/crates/ide/src/doc_links.rs b/crates/ide/src/doc_links.rs
index 70d05cd..2f29fc3 100644
--- a/crates/ide/src/doc_links.rs
+++ b/crates/ide/src/doc_links.rs
@@ -282,7 +282,7 @@
             ast::Comment(comment) => TextSize::try_from(comment.prefix().len()).ok(),
             ast::String(string) => {
                 doc_token.parent_ancestors().find_map(ast::Attr::cast).filter(|attr| attr.simple_name().as_deref() == Some("doc"))?;
-                if doc_token.parent_ancestors().find_map(ast::MacroCall::cast).filter(|mac| mac.path().and_then(|p| p.segment()?.name_ref()).as_ref().map(|n| n.text()).as_deref() == Some("include_str")).is_some() {
+                if doc_token.parent_ancestors().find_map(ast::MacroCall::cast).filter(|mac| mac.path().and_then(|p| p.segment()?.name_ref()).as_ref().map(|n| n.text()) == Some("include_str")).is_some() {
                     return None;
                 }
                 string.open_quote_text_range().map(|it| it.len())
@@ -387,7 +387,7 @@
 
     let (mut web_url, mut local_url) = get_doc_base_urls(db, target, target_dir, sysroot);
 
-    let append_mod = !matches!(def, Definition::Macro(m) if m.is_macro_export(db));
+    let append_mod = !matches!(def, Definition::Macro(m) if m.attrs(db).is_macro_export());
     if append_mod && let Some(path) = mod_path_of_def(db, target) {
         web_url = join_url(web_url, &path);
         local_url = join_url(local_url, &path);
diff --git a/crates/ide/src/file_structure.rs b/crates/ide/src/file_structure.rs
index 21254fc..1a85342 100644
--- a/crates/ide/src/file_structure.rs
+++ b/crates/ide/src/file_structure.rs
@@ -106,7 +106,7 @@
 
         Some(StructureNode {
             parent: None,
-            label: name.text().to_string(),
+            label: name.text().to_owned(),
             navigation_range: name.syntax().text_range(),
             node_range: node.syntax().text_range(),
             kind,
diff --git a/crates/ide/src/hover/render.rs b/crates/ide/src/hover/render.rs
index f26b992..f70783f 100644
--- a/crates/ide/src/hover/render.rs
+++ b/crates/ide/src/hover/render.rs
@@ -509,7 +509,7 @@
                         Some(if it >= 10 { format!("{it} ({it:#X})") } else { format!("{it}") })
                     }
                     Err(err) => {
-                        let res = it.value(db).map(|it| format!("{it:?}"));
+                        let res = it.value(db).map(|it| it.to_string());
                         if env::var_os("RA_DEV").is_some() {
                             let res = res.as_deref().unwrap_or("");
                             Some(format!(
diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs
index 89f1cf2..9dbf113 100644
--- a/crates/ide/src/hover/tests.rs
+++ b/crates/ide/src/hover/tests.rs
@@ -5620,6 +5620,30 @@
             This is a doc
         "#]],
     );
+    // const eval failed test
+    check(
+        r#"
+#[repr(u8)]
+enum E {
+    A$0 = {},
+}
+"#,
+        expect![[r#"
+            *A*
+
+            ```rust
+            ra_test_fixture::E
+            ```
+
+            ```rust
+            A = {}
+            ```
+
+            ---
+
+            size = 1, align = 1, no Drop
+        "#]],
+    );
 }
 
 #[test]
@@ -9508,7 +9532,7 @@
             *x*
 
             ```rust
-            x: impl Tr<{const}> + ?Sized
+            x: impl Tr<0> + ?Sized
             ```
 
             ---
diff --git a/crates/ide/src/inlay_hints/lifetime.rs b/crates/ide/src/inlay_hints/lifetime.rs
index 7a8a6eb..89a2d9f 100644
--- a/crates/ide/src/inlay_hints/lifetime.rs
+++ b/crates/ide/src/inlay_hints/lifetime.rs
@@ -204,7 +204,7 @@
     mut is_trivial: bool,
 ) -> Option<()> {
     let is_elided = |lt: &Option<ast::Lifetime>| match lt {
-        Some(lt) => matches!(lt.text().as_str(), "'_"),
+        Some(lt) => matches!(lt.text(), "'_"),
         None => true,
     };
     let self_param = self_param.and_then(|it| {
@@ -298,12 +298,12 @@
         potential_lt_refs.for_each(|(name, ..)| {
             let name = match name {
                 Some(it) if config.param_names_for_lifetime_elision_hints => {
-                    if let Some(c) = used_names.get_mut(it.text().as_str()) {
+                    if let Some(c) = used_names.get_mut(it.text()) {
                         *c += 1;
-                        format_smolstr!("'{}{c}", it.text().as_str())
+                        format_smolstr!("'{}{c}", it.text())
                     } else {
-                        used_names.insert(it.text().as_str().into(), 0);
-                        format_smolstr!("'{}", it.text().as_str())
+                        used_names.insert(it.text().into(), 0);
+                        format_smolstr!("'{}", it.text())
                     }
                 }
                 _ => gen_idx_name(),
@@ -316,7 +316,7 @@
     let output = match potential_lt_refs.as_slice() {
         [(_, _, lifetime, _), ..] if self_param.is_some() || potential_lt_refs.len() == 1 => {
             match lifetime {
-                Some(lt) => match lt.text().as_str() {
+                Some(lt) => match lt.text() {
                     "'_" => allocated_lifetimes.first().cloned(),
                     "'static" => None,
                     name => Some(name.into()),
diff --git a/crates/ide/src/inlay_hints/param_name.rs b/crates/ide/src/inlay_hints/param_name.rs
index 5da8f2e..fcf1304 100644
--- a/crates/ide/src/inlay_hints/param_name.rs
+++ b/crates/ide/src/inlay_hints/param_name.rs
@@ -294,7 +294,7 @@
     debug_assert!(!argument.is_empty());
     debug_assert!(!param_name.is_empty());
     let param_name = param_name.split('_');
-    let argument = argument.iter().flat_map(|it| it.text_non_mutable().split('_'));
+    let argument = argument.iter().flat_map(|it| it.text().split('_'));
     let argument = argument.map(|it| it.strip_prefix("r#").unwrap_or(it));
 
     let prefix_match = zip(argument.clone(), param_name.clone())
@@ -313,7 +313,7 @@
             let receiver =
                 method_call_expr.receiver().and_then(|expr| get_segment_representation(&expr));
             let name_ref = method_call_expr.name_ref()?;
-            if INSIGNIFICANT_METHOD_NAMES.contains(&name_ref.text().as_str()) {
+            if INSIGNIFICANT_METHOD_NAMES.contains(&name_ref.text()) {
                 return receiver;
             }
             Some(Either::Left(match receiver {
@@ -375,16 +375,16 @@
 ) -> bool {
     (|| match sema.resolve_path(path)? {
         hir::PathResolution::Def(hir::ModuleDef::Adt(_)) => {
-            Some(to_lower_snake_case(&path.segment()?.name_ref()?.text()) == param_name)
+            Some(to_lower_snake_case(path.segment()?.name_ref()?.text()) == param_name)
         }
         hir::PathResolution::Def(hir::ModuleDef::Function(_) | hir::ModuleDef::EnumVariant(_)) => {
-            if to_lower_snake_case(&path.segment()?.name_ref()?.text()) == param_name {
+            if to_lower_snake_case(path.segment()?.name_ref()?.text()) == param_name {
                 return Some(true);
             }
             let qual = path.qualifier()?;
             match sema.resolve_path(&qual)? {
                 hir::PathResolution::Def(hir::ModuleDef::Adt(_)) => {
-                    Some(to_lower_snake_case(&qual.segment()?.name_ref()?.text()) == param_name)
+                    Some(to_lower_snake_case(qual.segment()?.name_ref()?.text()) == param_name)
                 }
                 _ => None,
             }
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs
index c4a3ec1..bd09cb1 100644
--- a/crates/ide/src/lib.rs
+++ b/crates/ide/src/lib.rs
@@ -65,17 +65,16 @@
 use cfg::CfgOptions;
 use fetch_crates::CrateInfo;
 use hir::{ChangeWithProcMacros, EditionedFileId, crate_def_map, sym};
-use ide_db::base_db::relevant_crates;
-use ide_db::base_db::salsa::Durability;
-use ide_db::line_index;
-use ide_db::ra_fixture::RaFixtureAnalysis;
 use ide_db::{
     FxHashMap, FxIndexSet,
     base_db::{
         AbsPathBuf, CrateOrigin, CrateWorkspaceData, Env, FileSet, SourceDatabase, VfsPath,
-        salsa::{Cancelled, Database},
+        relevant_crates,
+        salsa::{Cancelled, Database, Durability},
     },
-    prime_caches, symbol_index,
+    line_index, prime_caches,
+    ra_fixture::RaFixtureAnalysis,
+    symbol_index,
 };
 use macros::UpmapFromRaFixture;
 use syntax::{AstNode, SourceFile, ast};
diff --git a/crates/ide/src/navigation_target.rs b/crates/ide/src/navigation_target.rs
index 125b2f4..c3d620a 100644
--- a/crates/ide/src/navigation_target.rs
+++ b/crates/ide/src/navigation_target.rs
@@ -190,7 +190,7 @@
         kind: SymbolKind,
     ) -> UpmappingResult<NavigationTarget> {
         let name =
-            value.name().map(|it| Symbol::intern(&it.text())).unwrap_or_else(|| sym::underscore);
+            value.name().map(|it| Symbol::intern(it.text())).unwrap_or_else(|| sym::underscore);
 
         orig_range_with_focus(db, file_id, value.syntax(), value.name()).map(
             |(FileRange { file_id, range: full_range }, focus_range)| {
diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs
index bb79fc2..2bf39ec 100644
--- a/crates/ide/src/references.rs
+++ b/crates/ide/src/references.rs
@@ -3358,4 +3358,22 @@
             "#]],
         );
     }
+
+    #[test]
+    fn pub_macro_2_scope() {
+        check(
+            r#"
+//- /foo.rs crate:foo
+pub macro m$0() {}
+
+//- /bar.rs new_source_root:local crate:bar deps:foo
+foo::m!();
+        "#,
+            expect![[r#"
+                m Macro FileId(0) 0..16 10..11
+
+                FileId(1) 5..6
+            "#]],
+        );
+    }
 }
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_block_mod_items.html b/crates/ide/src/syntax_highlighting/test_data/highlight_block_mod_items.html
index 982e89d..10a9478 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_block_mod_items.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_block_mod_items.html
@@ -41,7 +41,7 @@
 .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; }
 .unresolved_reference    { color: #FC5555; text-decoration: wavy underline; }
 </style>
-<pre><code><span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration public">foo</span> <span class="brace">{</span>
+<pre><code><span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">foo</span> <span class="brace">{</span>
     <span class="parenthesis">(</span><span class="punctuation">$</span>foo<span class="colon">:</span>ident<span class="parenthesis">)</span> <span class="operator">=</span><span class="operator">&gt;</span> <span class="brace">{</span>
         <span class="keyword">mod</span> y <span class="brace">{</span>
             <span class="keyword">pub</span> <span class="keyword">struct</span> <span class="punctuation">$</span>foo<span class="semicolon">;</span>
@@ -49,9 +49,9 @@
     <span class="brace">}</span><span class="semicolon">;</span>
 <span class="brace">}</span>
 <span class="keyword">fn</span> <span class="function declaration">main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
-    <span class="macro public">foo</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="struct declaration macro public">Foo</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+    <span class="macro">foo</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="struct declaration macro public">Foo</span><span class="parenthesis">)</span><span class="semicolon">;</span>
     <span class="keyword">mod</span> <span class="module declaration">module</span> <span class="brace">{</span>
-        <span class="macro public">foo</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="struct declaration macro public">Bar</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+        <span class="macro">foo</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="struct declaration macro public">Bar</span><span class="parenthesis">)</span><span class="semicolon">;</span>
         <span class="keyword">fn</span> <span class="function declaration">func</span><span class="parenthesis">(</span><span class="punctuation">_</span><span class="colon">:</span> <span class="module">y</span><span class="operator">::</span><span class="struct public">Bar</span><span class="parenthesis">)</span> <span class="brace">{</span>
             <span class="keyword">mod</span> <span class="module declaration">inner</span> <span class="brace">{</span>
                 <span class="keyword">struct</span> <span class="struct declaration">Innerest</span><span class="angle">&lt;</span><span class="keyword const">const</span> <span class="const_param const declaration">C</span><span class="colon">:</span> <span class="builtin_type">usize</span><span class="angle">&gt;</span> <span class="brace">{</span> <span class="field declaration">field</span><span class="colon">:</span> <span class="bracket">[</span><span class="builtin_type">u32</span><span class="semicolon">;</span> <span class="brace">{</span><span class="const_param const">C</span><span class="brace">}</span><span class="bracket">]</span><span class="comma">,</span> <span class="field declaration">field2</span><span class="colon">:</span> <span class="punctuation">&</span><span class="struct">Innerest</span> <span class="brace">}</span>
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_const.html b/crates/ide/src/syntax_highlighting/test_data/highlight_const.html
index 5d89147..6280ed2 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_const.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_const.html
@@ -41,7 +41,7 @@
 .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; }
 .unresolved_reference    { color: #FC5555; text-decoration: wavy underline; }
 </style>
-<pre><code><span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration public">id</span> <span class="brace">{</span>
+<pre><code><span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">id</span> <span class="brace">{</span>
     <span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>tt<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="operator">&gt;</span> <span class="brace">{</span>
         <span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span>
     <span class="brace">}</span><span class="semicolon">;</span>
@@ -53,7 +53,7 @@
     <span class="keyword const">const</span> <span class="brace">{</span>
         <span class="keyword const">const</span> <span class="punctuation">|</span><span class="punctuation">|</span> <span class="brace">{</span><span class="brace">}</span>
     <span class="brace">}</span>
-    <span class="macro public">id</span><span class="macro_bang">!</span><span class="parenthesis">(</span>
+    <span class="macro">id</span><span class="macro_bang">!</span><span class="parenthesis">(</span>
         <span class="constant const macro">CONST_ITEM</span><span class="semicolon macro">;</span>
         <span class="const_param const macro">CONST_PARAM</span><span class="semicolon macro">;</span>
         <span class="keyword const macro">const</span> <span class="brace macro">{</span>
@@ -74,7 +74,7 @@
     <span class="keyword const">const</span> <span class="keyword">fn</span> <span class="function associated const declaration static trait">assoc_const_fn</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
 <span class="brace">}</span>
 
-<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration public">unsafe_deref</span> <span class="brace">{</span>
+<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">unsafe_deref</span> <span class="brace">{</span>
     <span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="operator">&gt;</span> <span class="brace">{</span>
         <span class="punctuation">*</span><span class="parenthesis">(</span><span class="punctuation">&</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="keyword">as</span> <span class="punctuation">*</span><span class="keyword const">const</span> <span class="parenthesis">(</span><span class="parenthesis">)</span><span class="parenthesis">)</span>
     <span class="brace">}</span><span class="semicolon">;</span>
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_deprecated.html b/crates/ide/src/syntax_highlighting/test_data/highlight_deprecated.html
index 5287aff..6cbc7f0 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_deprecated.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_deprecated.html
@@ -45,7 +45,7 @@
 <span class="keyword">use</span> <span class="keyword deprecated public">crate</span> <span class="keyword">as</span> <span class="punctuation">_</span><span class="semicolon">;</span>
 <span class="keyword">extern</span> <span class="keyword">crate</span> <span class="crate_root deprecated library">bar</span><span class="semicolon">;</span>
 <span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute">deprecated</span><span class="attribute_bracket attribute">]</span>
-<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration deprecated public">macro_</span> <span class="brace">{</span>
+<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration deprecated">macro_</span> <span class="brace">{</span>
     <span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="operator">&gt;</span> <span class="brace">{</span><span class="brace">}</span><span class="semicolon">;</span>
 <span class="brace">}</span>
 <span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute">deprecated</span><span class="attribute_bracket attribute">]</span>
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
index dcfe4dd..c95b36a 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
@@ -151,10 +151,10 @@
 <span class="brace">}</span>
 
 <span class="comment documentation">/// ```</span>
-<span class="comment documentation">/// </span><span class="keyword injected">macro_rules</span><span class="macro_bang injected">!</span><span class="none injected"> </span><span class="macro declaration injected public">noop</span><span class="none injected"> </span><span class="brace injected">{</span><span class="none injected"> </span><span class="parenthesis injected">(</span><span class="punctuation injected">$</span><span class="none injected">expr</span><span class="colon injected">:</span><span class="none injected">expr</span><span class="parenthesis injected">)</span><span class="none injected"> </span><span class="operator injected">=</span><span class="operator injected">&gt;</span><span class="none injected"> </span><span class="brace injected">{</span><span class="none injected"> </span><span class="punctuation injected">$</span><span class="none injected">expr</span><span class="none injected"> </span><span class="brace injected">}</span><span class="brace injected">}</span>
-<span class="comment documentation">/// </span><span class="macro injected public">noop</span><span class="macro_bang injected">!</span><span class="parenthesis injected">(</span><span class="numeric_literal injected macro">1</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span>
+<span class="comment documentation">/// </span><span class="keyword injected">macro_rules</span><span class="macro_bang injected">!</span><span class="none injected"> </span><span class="macro declaration injected">noop</span><span class="none injected"> </span><span class="brace injected">{</span><span class="none injected"> </span><span class="parenthesis injected">(</span><span class="punctuation injected">$</span><span class="none injected">expr</span><span class="colon injected">:</span><span class="none injected">expr</span><span class="parenthesis injected">)</span><span class="none injected"> </span><span class="operator injected">=</span><span class="operator injected">&gt;</span><span class="none injected"> </span><span class="brace injected">{</span><span class="none injected"> </span><span class="punctuation injected">$</span><span class="none injected">expr</span><span class="none injected"> </span><span class="brace injected">}</span><span class="brace injected">}</span>
+<span class="comment documentation">/// </span><span class="macro injected">noop</span><span class="macro_bang injected">!</span><span class="parenthesis injected">(</span><span class="numeric_literal injected macro">1</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span>
 <span class="comment documentation">/// ```</span>
-<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration public">noop</span> <span class="brace">{</span>
+<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">noop</span> <span class="brace">{</span>
     <span class="parenthesis">(</span><span class="punctuation">$</span>expr<span class="colon">:</span>expr<span class="parenthesis">)</span> <span class="operator">=</span><span class="operator">&gt;</span> <span class="brace">{</span>
         <span class="punctuation">$</span>expr
     <span class="brace">}</span>
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2015.html b/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2015.html
index 6366cba..a6a51f6 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2015.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2015.html
@@ -49,22 +49,22 @@
     <span class="keyword">use</span> <span class="keyword public">super</span><span class="operator">::</span><span class="punctuation">*</span><span class="semicolon">;</span>
 <span class="brace">}</span>
 
-<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration public">void</span> <span class="brace">{</span>
+<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">void</span> <span class="brace">{</span>
     <span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>tt<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="operator">&gt;</span> <span class="brace">{</span>discard<span class="punctuation">!</span><span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>tt<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span><span class="parenthesis">)</span><span class="brace">}</span>
 <span class="brace">}</span>
 
 <span class="keyword">struct</span> <span class="struct declaration">__</span> <span class="keyword">where</span> <span class="self_type_keyword">Self</span><span class="colon">:</span><span class="semicolon">;</span>
 <span class="keyword">fn</span> <span class="function declaration">__</span><span class="parenthesis">(</span><span class="punctuation">_</span><span class="colon">:</span> <span class="unresolved_reference">Self</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
-<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="keyword macro">Self</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="keyword macro">Self</span><span class="parenthesis">)</span><span class="semicolon">;</span>
 
 <span class="comment">// edition dependent</span>
-<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="none macro">try</span> <span class="none macro">async</span> <span class="none macro">await</span> <span class="none macro">gen</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="none macro">try</span> <span class="none macro">async</span> <span class="none macro">await</span> <span class="none macro">gen</span><span class="parenthesis">)</span><span class="semicolon">;</span>
 <span class="comment">// edition and context dependent</span>
-<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="none macro">dyn</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="none macro">dyn</span><span class="parenthesis">)</span><span class="semicolon">;</span>
 <span class="comment">// builtin custom syntax</span>
-<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="none macro">builtin</span> <span class="none macro">offset_of</span> <span class="none macro">format_args</span> <span class="none macro">asm</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="none macro">builtin</span> <span class="none macro">offset_of</span> <span class="none macro">format_args</span> <span class="none macro">asm</span><span class="parenthesis">)</span><span class="semicolon">;</span>
 <span class="comment">// contextual</span>
-<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="none macro">macro_rules</span><span class="comma macro">,</span> <span class="none macro">union</span><span class="comma macro">,</span> <span class="none macro">default</span><span class="comma macro">,</span> <span class="none macro">raw</span><span class="comma macro">,</span> <span class="none macro">auto</span><span class="comma macro">,</span> <span class="none macro">yeet</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="none macro">macro_rules</span><span class="comma macro">,</span> <span class="none macro">union</span><span class="comma macro">,</span> <span class="none macro">default</span><span class="comma macro">,</span> <span class="none macro">raw</span><span class="comma macro">,</span> <span class="none macro">auto</span><span class="comma macro">,</span> <span class="none macro">yeet</span><span class="parenthesis">)</span><span class="semicolon">;</span>
 <span class="comment">// reserved</span>
-<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="keyword macro">abstract</span> <span class="keyword macro">become</span> <span class="keyword macro">box</span> <span class="keyword macro">do</span> <span class="keyword macro">final</span> <span class="keyword macro">macro</span> <span class="keyword macro">override</span> <span class="keyword macro">priv</span> <span class="keyword macro">typeof</span> <span class="keyword macro">unsized</span> <span class="keyword macro">virtual</span> <span class="keyword control macro">yield</span><span class="parenthesis">)</span><span class="semicolon">;</span>
-<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span>'static 'self 'unsafe<span class="parenthesis">)</span></code></pre>
\ No newline at end of file
+<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="keyword macro">abstract</span> <span class="keyword macro">become</span> <span class="keyword macro">box</span> <span class="keyword macro">do</span> <span class="keyword macro">final</span> <span class="keyword macro">macro</span> <span class="keyword macro">override</span> <span class="keyword macro">priv</span> <span class="keyword macro">typeof</span> <span class="keyword macro">unsized</span> <span class="keyword macro">virtual</span> <span class="keyword control macro">yield</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span>'static 'self 'unsafe<span class="parenthesis">)</span></code></pre>
\ No newline at end of file
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2018.html b/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2018.html
index a89e819..ff2b2c3 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2018.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2018.html
@@ -49,22 +49,22 @@
     <span class="keyword">use</span> <span class="keyword public">super</span><span class="operator">::</span><span class="punctuation">*</span><span class="semicolon">;</span>
 <span class="brace">}</span>
 
-<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration public">void</span> <span class="brace">{</span>
+<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">void</span> <span class="brace">{</span>
     <span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>tt<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="operator">&gt;</span> <span class="brace">{</span>discard<span class="punctuation">!</span><span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>tt<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span><span class="parenthesis">)</span><span class="brace">}</span>
 <span class="brace">}</span>
 
 <span class="keyword">struct</span> <span class="struct declaration">__</span> <span class="keyword">where</span> <span class="self_type_keyword">Self</span><span class="colon">:</span><span class="semicolon">;</span>
 <span class="keyword">fn</span> <span class="function declaration">__</span><span class="parenthesis">(</span><span class="punctuation">_</span><span class="colon">:</span> <span class="unresolved_reference">Self</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
-<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="keyword macro">Self</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="keyword macro">Self</span><span class="parenthesis">)</span><span class="semicolon">;</span>
 
 <span class="comment">// edition dependent</span>
-<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="keyword macro">try</span> <span class="keyword async macro">async</span> <span class="keyword async control macro">await</span> <span class="none macro">gen</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="keyword macro">try</span> <span class="keyword async macro">async</span> <span class="keyword async control macro">await</span> <span class="none macro">gen</span><span class="parenthesis">)</span><span class="semicolon">;</span>
 <span class="comment">// edition and context dependent</span>
-<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="keyword macro">dyn</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="keyword macro">dyn</span><span class="parenthesis">)</span><span class="semicolon">;</span>
 <span class="comment">// builtin custom syntax</span>
-<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="none macro">builtin</span> <span class="none macro">offset_of</span> <span class="none macro">format_args</span> <span class="none macro">asm</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="none macro">builtin</span> <span class="none macro">offset_of</span> <span class="none macro">format_args</span> <span class="none macro">asm</span><span class="parenthesis">)</span><span class="semicolon">;</span>
 <span class="comment">// contextual</span>
-<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="none macro">macro_rules</span><span class="comma macro">,</span> <span class="none macro">union</span><span class="comma macro">,</span> <span class="none macro">default</span><span class="comma macro">,</span> <span class="none macro">raw</span><span class="comma macro">,</span> <span class="none macro">auto</span><span class="comma macro">,</span> <span class="none macro">yeet</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="none macro">macro_rules</span><span class="comma macro">,</span> <span class="none macro">union</span><span class="comma macro">,</span> <span class="none macro">default</span><span class="comma macro">,</span> <span class="none macro">raw</span><span class="comma macro">,</span> <span class="none macro">auto</span><span class="comma macro">,</span> <span class="none macro">yeet</span><span class="parenthesis">)</span><span class="semicolon">;</span>
 <span class="comment">// reserved</span>
-<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="keyword macro">abstract</span> <span class="keyword macro">become</span> <span class="keyword macro">box</span> <span class="keyword macro">do</span> <span class="keyword macro">final</span> <span class="keyword macro">macro</span> <span class="keyword macro">override</span> <span class="keyword macro">priv</span> <span class="keyword macro">typeof</span> <span class="keyword macro">unsized</span> <span class="keyword macro">virtual</span> <span class="keyword control macro">yield</span><span class="parenthesis">)</span><span class="semicolon">;</span>
-<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span>'static 'self 'unsafe<span class="parenthesis">)</span></code></pre>
\ No newline at end of file
+<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="keyword macro">abstract</span> <span class="keyword macro">become</span> <span class="keyword macro">box</span> <span class="keyword macro">do</span> <span class="keyword macro">final</span> <span class="keyword macro">macro</span> <span class="keyword macro">override</span> <span class="keyword macro">priv</span> <span class="keyword macro">typeof</span> <span class="keyword macro">unsized</span> <span class="keyword macro">virtual</span> <span class="keyword control macro">yield</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span>'static 'self 'unsafe<span class="parenthesis">)</span></code></pre>
\ No newline at end of file
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2021.html b/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2021.html
index a89e819..ff2b2c3 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2021.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2021.html
@@ -49,22 +49,22 @@
     <span class="keyword">use</span> <span class="keyword public">super</span><span class="operator">::</span><span class="punctuation">*</span><span class="semicolon">;</span>
 <span class="brace">}</span>
 
-<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration public">void</span> <span class="brace">{</span>
+<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">void</span> <span class="brace">{</span>
     <span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>tt<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="operator">&gt;</span> <span class="brace">{</span>discard<span class="punctuation">!</span><span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>tt<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span><span class="parenthesis">)</span><span class="brace">}</span>
 <span class="brace">}</span>
 
 <span class="keyword">struct</span> <span class="struct declaration">__</span> <span class="keyword">where</span> <span class="self_type_keyword">Self</span><span class="colon">:</span><span class="semicolon">;</span>
 <span class="keyword">fn</span> <span class="function declaration">__</span><span class="parenthesis">(</span><span class="punctuation">_</span><span class="colon">:</span> <span class="unresolved_reference">Self</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
-<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="keyword macro">Self</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="keyword macro">Self</span><span class="parenthesis">)</span><span class="semicolon">;</span>
 
 <span class="comment">// edition dependent</span>
-<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="keyword macro">try</span> <span class="keyword async macro">async</span> <span class="keyword async control macro">await</span> <span class="none macro">gen</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="keyword macro">try</span> <span class="keyword async macro">async</span> <span class="keyword async control macro">await</span> <span class="none macro">gen</span><span class="parenthesis">)</span><span class="semicolon">;</span>
 <span class="comment">// edition and context dependent</span>
-<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="keyword macro">dyn</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="keyword macro">dyn</span><span class="parenthesis">)</span><span class="semicolon">;</span>
 <span class="comment">// builtin custom syntax</span>
-<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="none macro">builtin</span> <span class="none macro">offset_of</span> <span class="none macro">format_args</span> <span class="none macro">asm</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="none macro">builtin</span> <span class="none macro">offset_of</span> <span class="none macro">format_args</span> <span class="none macro">asm</span><span class="parenthesis">)</span><span class="semicolon">;</span>
 <span class="comment">// contextual</span>
-<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="none macro">macro_rules</span><span class="comma macro">,</span> <span class="none macro">union</span><span class="comma macro">,</span> <span class="none macro">default</span><span class="comma macro">,</span> <span class="none macro">raw</span><span class="comma macro">,</span> <span class="none macro">auto</span><span class="comma macro">,</span> <span class="none macro">yeet</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="none macro">macro_rules</span><span class="comma macro">,</span> <span class="none macro">union</span><span class="comma macro">,</span> <span class="none macro">default</span><span class="comma macro">,</span> <span class="none macro">raw</span><span class="comma macro">,</span> <span class="none macro">auto</span><span class="comma macro">,</span> <span class="none macro">yeet</span><span class="parenthesis">)</span><span class="semicolon">;</span>
 <span class="comment">// reserved</span>
-<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="keyword macro">abstract</span> <span class="keyword macro">become</span> <span class="keyword macro">box</span> <span class="keyword macro">do</span> <span class="keyword macro">final</span> <span class="keyword macro">macro</span> <span class="keyword macro">override</span> <span class="keyword macro">priv</span> <span class="keyword macro">typeof</span> <span class="keyword macro">unsized</span> <span class="keyword macro">virtual</span> <span class="keyword control macro">yield</span><span class="parenthesis">)</span><span class="semicolon">;</span>
-<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span>'static 'self 'unsafe<span class="parenthesis">)</span></code></pre>
\ No newline at end of file
+<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="keyword macro">abstract</span> <span class="keyword macro">become</span> <span class="keyword macro">box</span> <span class="keyword macro">do</span> <span class="keyword macro">final</span> <span class="keyword macro">macro</span> <span class="keyword macro">override</span> <span class="keyword macro">priv</span> <span class="keyword macro">typeof</span> <span class="keyword macro">unsized</span> <span class="keyword macro">virtual</span> <span class="keyword control macro">yield</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span>'static 'self 'unsafe<span class="parenthesis">)</span></code></pre>
\ No newline at end of file
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2024.html b/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2024.html
index aa1500b..0505d41 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2024.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2024.html
@@ -49,22 +49,22 @@
     <span class="keyword">use</span> <span class="keyword public">super</span><span class="operator">::</span><span class="punctuation">*</span><span class="semicolon">;</span>
 <span class="brace">}</span>
 
-<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration public">void</span> <span class="brace">{</span>
+<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">void</span> <span class="brace">{</span>
     <span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>tt<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="operator">&gt;</span> <span class="brace">{</span>discard<span class="punctuation">!</span><span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>tt<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span><span class="parenthesis">)</span><span class="brace">}</span>
 <span class="brace">}</span>
 
 <span class="keyword">struct</span> <span class="struct declaration">__</span> <span class="keyword">where</span> <span class="self_type_keyword">Self</span><span class="colon">:</span><span class="semicolon">;</span>
 <span class="keyword">fn</span> <span class="function declaration">__</span><span class="parenthesis">(</span><span class="punctuation">_</span><span class="colon">:</span> <span class="unresolved_reference">Self</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
-<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="keyword macro">Self</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="keyword macro">Self</span><span class="parenthesis">)</span><span class="semicolon">;</span>
 
 <span class="comment">// edition dependent</span>
-<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="keyword macro">try</span> <span class="keyword async macro">async</span> <span class="keyword async control macro">await</span> <span class="keyword macro">gen</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="keyword macro">try</span> <span class="keyword async macro">async</span> <span class="keyword async control macro">await</span> <span class="keyword macro">gen</span><span class="parenthesis">)</span><span class="semicolon">;</span>
 <span class="comment">// edition and context dependent</span>
-<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="keyword macro">dyn</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="keyword macro">dyn</span><span class="parenthesis">)</span><span class="semicolon">;</span>
 <span class="comment">// builtin custom syntax</span>
-<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="none macro">builtin</span> <span class="none macro">offset_of</span> <span class="none macro">format_args</span> <span class="none macro">asm</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="none macro">builtin</span> <span class="none macro">offset_of</span> <span class="none macro">format_args</span> <span class="none macro">asm</span><span class="parenthesis">)</span><span class="semicolon">;</span>
 <span class="comment">// contextual</span>
-<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="none macro">macro_rules</span><span class="comma macro">,</span> <span class="none macro">union</span><span class="comma macro">,</span> <span class="none macro">default</span><span class="comma macro">,</span> <span class="none macro">raw</span><span class="comma macro">,</span> <span class="none macro">auto</span><span class="comma macro">,</span> <span class="none macro">yeet</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="none macro">macro_rules</span><span class="comma macro">,</span> <span class="none macro">union</span><span class="comma macro">,</span> <span class="none macro">default</span><span class="comma macro">,</span> <span class="none macro">raw</span><span class="comma macro">,</span> <span class="none macro">auto</span><span class="comma macro">,</span> <span class="none macro">yeet</span><span class="parenthesis">)</span><span class="semicolon">;</span>
 <span class="comment">// reserved</span>
-<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="keyword macro">abstract</span> <span class="keyword macro">become</span> <span class="keyword macro">box</span> <span class="keyword macro">do</span> <span class="keyword macro">final</span> <span class="keyword macro">macro</span> <span class="keyword macro">override</span> <span class="keyword macro">priv</span> <span class="keyword macro">typeof</span> <span class="keyword macro">unsized</span> <span class="keyword macro">virtual</span> <span class="keyword control macro">yield</span><span class="parenthesis">)</span><span class="semicolon">;</span>
-<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span>'static 'self 'unsafe<span class="parenthesis">)</span></code></pre>
\ No newline at end of file
+<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="keyword macro">abstract</span> <span class="keyword macro">become</span> <span class="keyword macro">box</span> <span class="keyword macro">do</span> <span class="keyword macro">final</span> <span class="keyword macro">macro</span> <span class="keyword macro">override</span> <span class="keyword macro">priv</span> <span class="keyword macro">typeof</span> <span class="keyword macro">unsized</span> <span class="keyword macro">virtual</span> <span class="keyword control macro">yield</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span>'static 'self 'unsafe<span class="parenthesis">)</span></code></pre>
\ No newline at end of file
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html b/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html
index 740a627..5fc199f 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html
@@ -50,34 +50,34 @@
         <span class="comma macro proc_macro">,</span><span class="builtin_type macro proc_macro">i32</span> <span class="colon macro proc_macro">:</span><span class="field declaration macro proc_macro public">y</span> <span class="keyword macro proc_macro">pub</span>
     <span class="brace macro proc_macro">}</span> <span class="struct declaration macro proc_macro">Foo</span> <span class="keyword macro proc_macro">struct</span>
 <span class="brace">}</span>
-<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration public">def_fn</span> <span class="brace">{</span>
+<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">def_fn</span> <span class="brace">{</span>
     <span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>tt<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="operator">&gt;</span> <span class="brace">{</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span><span class="brace">}</span>
 <span class="brace">}</span>
 
-<span class="macro public">def_fn</span><span class="macro_bang">!</span> <span class="brace">{</span>
+<span class="macro">def_fn</span><span class="macro_bang">!</span> <span class="brace">{</span>
     <span class="keyword macro">fn</span> <span class="function declaration macro">bar</span><span class="parenthesis macro">(</span><span class="parenthesis macro">)</span> <span class="operator macro">-</span><span class="operator macro">&gt;</span> <span class="builtin_type macro">u32</span> <span class="brace macro">{</span>
         <span class="numeric_literal macro">100</span>
     <span class="brace macro">}</span>
 <span class="brace">}</span>
 
-<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration public">dont_color_me_braces</span> <span class="brace">{</span>
+<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">dont_color_me_braces</span> <span class="brace">{</span>
     <span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="operator">&gt;</span> <span class="brace">{</span><span class="numeric_literal">0</span><span class="brace">}</span>
 <span class="brace">}</span>
 
-<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration public">noop</span> <span class="brace">{</span>
+<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">noop</span> <span class="brace">{</span>
     <span class="parenthesis">(</span><span class="punctuation">$</span>expr<span class="colon">:</span>expr<span class="parenthesis">)</span> <span class="operator">=</span><span class="operator">&gt;</span> <span class="brace">{</span>
         <span class="punctuation">$</span>expr
     <span class="brace">}</span>
 <span class="brace">}</span>
 
 <span class="comment documentation">/// textually shadow previous definition</span>
-<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration public">noop</span> <span class="brace">{</span>
+<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">noop</span> <span class="brace">{</span>
     <span class="parenthesis">(</span><span class="punctuation">$</span>expr<span class="colon">:</span>expr<span class="parenthesis">)</span> <span class="operator">=</span><span class="operator">&gt;</span> <span class="brace">{</span>
         <span class="punctuation">$</span>expr
     <span class="brace">}</span>
 <span class="brace">}</span>
 
-<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration public">keyword_frag</span> <span class="brace">{</span>
+<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">keyword_frag</span> <span class="brace">{</span>
     <span class="parenthesis">(</span><span class="punctuation">$</span>type<span class="colon">:</span>ty<span class="parenthesis">)</span> <span class="operator">=</span><span class="operator">&gt;</span> <span class="parenthesis">(</span><span class="punctuation">$</span>type<span class="parenthesis">)</span>
 <span class="brace">}</span>
 
@@ -91,7 +91,7 @@
     <span class="brace">}</span>
 <span class="brace">}</span>
 
-<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration public">id</span> <span class="brace">{</span>
+<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">id</span> <span class="brace">{</span>
     <span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>tt<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="operator">&gt;</span> <span class="brace">{</span>
         <span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span>
     <span class="brace">}</span><span class="semicolon">;</span>
@@ -103,10 +103,10 @@
 <span class="keyword">fn</span> <span class="function declaration">main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
     <span class="keyword">struct</span> <span class="struct declaration">TestLocal</span><span class="semicolon">;</span>
     <span class="comment">// regression test, TestLocal here used to not resolve</span>
-    <span class="keyword">let</span> <span class="punctuation">_</span><span class="colon">:</span> <span class="struct">S</span><span class="angle">&lt;</span><span class="macro public">id</span><span class="macro_bang">!</span><span class="bracket">[</span><span class="struct macro">TestLocal</span><span class="bracket">]</span><span class="angle">&gt;</span><span class="semicolon">;</span>
+    <span class="keyword">let</span> <span class="punctuation">_</span><span class="colon">:</span> <span class="struct">S</span><span class="angle">&lt;</span><span class="macro">id</span><span class="macro_bang">!</span><span class="bracket">[</span><span class="struct macro">TestLocal</span><span class="bracket">]</span><span class="angle">&gt;</span><span class="semicolon">;</span>
 
     <span class="macro default_library library">format_args</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"Hello, </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="parenthesis macro">(</span><span class="numeric_literal macro">92</span><span class="comma macro">,</span><span class="parenthesis macro">)</span><span class="operator macro">.</span><span class="field library macro">0</span><span class="parenthesis">)</span><span class="semicolon">;</span>
-    <span class="macro public">dont_color_me_braces</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
-    <span class="macro public">noop</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="macro macro public">noop</span><span class="macro_bang macro">!</span><span class="parenthesis macro">(</span><span class="numeric_literal macro">1</span><span class="parenthesis macro">)</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+    <span class="macro">dont_color_me_braces</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+    <span class="macro">noop</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="macro macro">noop</span><span class="macro_bang macro">!</span><span class="parenthesis macro">(</span><span class="numeric_literal macro">1</span><span class="parenthesis macro">)</span><span class="parenthesis">)</span><span class="semicolon">;</span>
 <span class="brace">}</span>
 </code></pre>
\ No newline at end of file
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_raw_string_format_specifiers.html b/crates/ide/src/syntax_highlighting/test_data/highlight_raw_string_format_specifiers.html
new file mode 100644
index 0000000..4497e83
--- /dev/null
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_raw_string_format_specifiers.html
@@ -0,0 +1,49 @@
+
+<style>
+body                { margin: 0; }
+pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; }
+
+.lifetime           { color: #DFAF8F; font-style: italic; }
+.label              { color: #DFAF8F; font-style: italic; }
+.comment            { color: #7F9F7F; }
+.documentation      { color: #629755; }
+.intra_doc_link     { font-style: italic; }
+.injected           { opacity: 0.65 ; }
+.struct, .enum      { color: #7CB8BB; }
+.enum_variant       { color: #BDE0F3; }
+.string_literal     { color: #CC9393; }
+.field              { color: #94BFF3; }
+.function           { color: #93E0E3; }
+.parameter          { color: #94BFF3; }
+.text               { color: #DCDCCC; }
+.type               { color: #7CB8BB; }
+.builtin_type       { color: #8CD0D3; }
+.type_param         { color: #DFAF8F; }
+.attribute          { color: #94BFF3; }
+.numeric_literal    { color: #BFEBBF; }
+.bool_literal       { color: #BFE6EB; }
+.macro              { color: #94BFF3; }
+.proc_macro         { color: #94BFF3; text-decoration: underline; }
+.derive             { color: #94BFF3; font-style: italic; }
+.module             { color: #AFD8AF; }
+.value_param        { color: #DCDCCC; }
+.variable           { color: #DCDCCC; }
+.format_specifier   { color: #CC696B; }
+.mutable            { text-decoration: underline; }
+.escape_sequence    { color: #94BFF3; }
+.keyword            { color: #F0DFAF; font-weight: bold; }
+.control            { font-style: italic; }
+.reference          { font-style: italic; font-weight: bold; }
+.const              { font-weight: bolder; }
+.unsafe             { color: #BC8383; }
+.deprecated         { text-decoration: line-through; }
+
+.invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; }
+.unresolved_reference    { color: #FC5555; text-decoration: wavy underline; }
+</style>
+<pre><code><span class="keyword">fn</span> <span class="function declaration">main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
+    <span class="keyword">let</span> <span class="variable declaration">here</span> <span class="operator">=</span> <span class="numeric_literal">1</span><span class="semicolon">;</span>
+    <span class="macro default_library library">format_args</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">r"backslash \</span><span class="format_specifier">{</span><span class="variable">here</span><span class="format_specifier">}</span><span class="string_literal macro"> arg"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+    <span class="macro default_library library">format_args</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">r#"hashed \</span><span class="format_specifier">{</span><span class="variable">here</span><span class="format_specifier">}</span><span class="string_literal macro"> arg"#</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+    <span class="macro default_library library">format_args</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"plain </span><span class="format_specifier">{</span><span class="variable">here</span><span class="format_specifier">}</span><span class="string_literal macro"> arg"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+<span class="brace">}</span></code></pre>
\ No newline at end of file
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 4e3822c..b5ace72 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
@@ -41,7 +41,7 @@
 .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; }
 .unresolved_reference    { color: #FC5555; text-decoration: wavy underline; }
 </style>
-<pre><code><span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration public">println</span> <span class="brace">{</span>
+<pre><code><span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">println</span> <span class="brace">{</span>
     <span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>arg<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="operator">&gt;</span> <span class="parenthesis">(</span><span class="brace">{</span>
         <span class="punctuation">$</span>crate<span class="colon">:</span><span class="colon">:</span>io<span class="colon">:</span><span class="colon">:</span>_print<span class="parenthesis">(</span>format_args_nl<span class="punctuation">!</span><span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>arg<span class="parenthesis">)</span><span class="punctuation">*</span><span class="parenthesis">)</span><span class="parenthesis">)</span><span class="semicolon">;</span>
     <span class="brace">}</span><span class="parenthesis">)</span>
@@ -69,12 +69,12 @@
     <span class="brace">}</span>
 <span class="brace">}</span>
 
-<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration public">toho</span> <span class="brace">{</span>
+<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">toho</span> <span class="brace">{</span>
     <span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="operator">&gt;</span> <span class="parenthesis">(</span><span class="punctuation">$</span>crate<span class="colon">:</span><span class="colon">:</span>panic<span class="punctuation">!</span><span class="parenthesis">(</span><span class="string_literal">"not yet implemented"</span><span class="parenthesis">)</span><span class="parenthesis">)</span><span class="semicolon">;</span>
     <span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>arg<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">+</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="operator">&gt;</span> <span class="parenthesis">(</span><span class="punctuation">$</span>crate<span class="colon">:</span><span class="colon">:</span>panic<span class="punctuation">!</span><span class="parenthesis">(</span><span class="string_literal">"not yet implemented: {}"</span><span class="comma">,</span> format_args<span class="punctuation">!</span><span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>arg<span class="parenthesis">)</span><span class="punctuation">+</span><span class="parenthesis">)</span><span class="parenthesis">)</span><span class="parenthesis">)</span><span class="semicolon">;</span>
 <span class="brace">}</span>
 
-<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration public">reuse_twice</span> <span class="brace">{</span>
+<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">reuse_twice</span> <span class="brace">{</span>
     <span class="parenthesis">(</span><span class="punctuation">$</span>literal<span class="colon">:</span>literal<span class="parenthesis">)</span> <span class="operator">=</span><span class="operator">&gt;</span> <span class="brace">{</span><span class="brace">{</span>stringify<span class="punctuation">!</span><span class="parenthesis">(</span><span class="punctuation">$</span>literal<span class="parenthesis">)</span><span class="semicolon">;</span> format_args<span class="punctuation">!</span><span class="parenthesis">(</span><span class="punctuation">$</span>literal<span class="parenthesis">)</span><span class="brace">}</span><span class="brace">}</span><span class="semicolon">;</span>
 <span class="brace">}</span>
 
@@ -95,74 +95,74 @@
 
     <span class="keyword">let</span> <span class="variable declaration">a</span> <span class="operator">=</span> <span class="byte_literal">b'</span><span class="escape_sequence">\xFF</span><span class="byte_literal">'</span><span class="semicolon">;</span>
 
-    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"Hello </span><span class="escape_sequence">{{</span><span class="string_literal macro">Hello</span><span class="escape_sequence">}}</span><span class="string_literal macro">"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"Hello </span><span class="escape_sequence">{{</span><span class="string_literal macro">Hello</span><span class="escape_sequence">}}</span><span class="string_literal macro">"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
     <span class="comment">// from https://doc.rust-lang.org/std/fmt/index.html</span>
-    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"Hello"</span><span class="parenthesis">)</span><span class="semicolon">;</span>                 <span class="comment">// =&gt; "Hello"</span>
-    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"Hello, </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"world"</span><span class="parenthesis">)</span><span class="semicolon">;</span>   <span class="comment">// =&gt; "Hello, world!"</span>
-    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"The number is </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="parenthesis">)</span><span class="semicolon">;</span>   <span class="comment">// =&gt; "The number is 1"</span>
-    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">?</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="parenthesis macro">(</span><span class="numeric_literal macro">3</span><span class="comma macro">,</span> <span class="numeric_literal macro">4</span><span class="parenthesis macro">)</span><span class="parenthesis">)</span><span class="semicolon">;</span>          <span class="comment">// =&gt; "(3, 4)"</span>
-    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">value</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="none macro">value</span><span class="operator macro">=</span><span class="numeric_literal macro">4</span><span class="parenthesis">)</span><span class="semicolon">;</span>      <span class="comment">// =&gt; "4"</span>
-    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="comma macro">,</span> <span class="numeric_literal macro">2</span><span class="parenthesis">)</span><span class="semicolon">;</span>           <span class="comment">// =&gt; "1 2"</span>
-    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="numeric_literal">4</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">42</span><span class="parenthesis">)</span><span class="semicolon">;</span>             <span class="comment">// =&gt; "0042" with leading zerosV</span>
-    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="comma macro">,</span> <span class="numeric_literal macro">2</span><span class="parenthesis">)</span><span class="semicolon">;</span>   <span class="comment">// =&gt; "2 1 1 2"</span>
-    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">argument</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="none macro">argument</span> <span class="operator macro">=</span> <span class="string_literal macro">"test"</span><span class="parenthesis">)</span><span class="semicolon">;</span>   <span class="comment">// =&gt; "test"</span>
-    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="comma macro">,</span> <span class="none macro">name</span> <span class="operator macro">=</span> <span class="numeric_literal macro">2</span><span class="parenthesis">)</span><span class="semicolon">;</span>          <span class="comment">// =&gt; "2 1"</span>
-    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">a</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="variable">c</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="variable">b</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="none macro">a</span><span class="operator macro">=</span><span class="string_literal macro">"a"</span><span class="comma macro">,</span> <span class="none macro">b</span><span class="operator macro">=</span><span class="char_literal macro">'b'</span><span class="comma macro">,</span> <span class="none macro">c</span><span class="operator macro">=</span><span class="numeric_literal macro">3</span><span class="parenthesis">)</span><span class="semicolon">;</span>  <span class="comment">// =&gt; "a 3 b"</span>
-    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="escape_sequence">{{</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="escape_sequence">}}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">2</span><span class="parenthesis">)</span><span class="semicolon">;</span>                       <span class="comment">// =&gt; "{2}"</span>
-    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
-    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">1</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="parenthesis">)</span><span class="semicolon">;</span>
-    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
-    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="variable">width</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="none macro">width</span> <span class="operator macro">=</span> <span class="numeric_literal macro">5</span><span class="parenthesis">)</span><span class="semicolon">;</span>
-    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">&lt;</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
-    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">-</span><span class="format_specifier">&lt;</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
-    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">^</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
-    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">&gt;</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
-    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">+</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="parenthesis">)</span><span class="semicolon">;</span>
-    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">#</span><span class="variable">x</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="numeric_literal macro">27</span><span class="parenthesis">)</span><span class="semicolon">;</span>
-    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="parenthesis">)</span><span class="semicolon">;</span>
-    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="numeric_literal macro">-</span><span class="numeric_literal macro">5</span><span class="parenthesis">)</span><span class="semicolon">;</span>
-    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">#</span><span class="numeric_literal">0</span><span class="numeric_literal">10</span><span class="variable">x</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="numeric_literal macro">27</span><span class="parenthesis">)</span><span class="semicolon">;</span>
-    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0.01</span><span class="parenthesis">)</span><span class="semicolon">;</span>
-    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="numeric_literal">2</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="numeric_literal">0</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0.01</span><span class="parenthesis">)</span><span class="semicolon">;</span>
-    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="numeric_literal">2</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="numeric_literal">1</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="numeric_literal macro">0.01</span><span class="parenthesis">)</span><span class="semicolon">;</span>
-    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span>    <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="numeric_literal macro">0.01</span><span class="parenthesis">)</span><span class="semicolon">;</span>
-    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="numeric_literal">2</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span>   <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="numeric_literal macro">0.01</span><span class="parenthesis">)</span><span class="semicolon">;</span>
-    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="variable">number</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="variable">prec</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="none macro">prec</span> <span class="operator macro">=</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="none macro">number</span> <span class="operator macro">=</span> <span class="numeric_literal macro">0.01</span><span class="parenthesis">)</span><span class="semicolon">;</span>
-    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">` has 3 fractional digits"</span><span class="comma macro">,</span> <span class="string_literal macro">"Hello"</span><span class="comma macro">,</span> <span class="numeric_literal macro">3</span><span class="comma macro">,</span> <span class="none macro">name</span><span class="operator macro">=</span><span class="numeric_literal macro">1234.56</span><span class="parenthesis">)</span><span class="semicolon">;</span>
-    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">` has 3 characters"</span><span class="comma macro">,</span> <span class="string_literal macro">"Hello"</span><span class="comma macro">,</span> <span class="numeric_literal macro">3</span><span class="comma macro">,</span> <span class="none macro">name</span><span class="operator macro">=</span><span class="string_literal macro">"1234.56"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
-    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">&gt;</span><span class="numeric_literal">8</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">` has 3 right-aligned characters"</span><span class="comma macro">,</span> <span class="string_literal macro">"Hello"</span><span class="comma macro">,</span> <span class="numeric_literal macro">3</span><span class="comma macro">,</span> <span class="none macro">name</span><span class="operator macro">=</span><span class="string_literal macro">"1234.56"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"Hello"</span><span class="parenthesis">)</span><span class="semicolon">;</span>                 <span class="comment">// =&gt; "Hello"</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"Hello, </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"world"</span><span class="parenthesis">)</span><span class="semicolon">;</span>   <span class="comment">// =&gt; "Hello, world!"</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"The number is </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="parenthesis">)</span><span class="semicolon">;</span>   <span class="comment">// =&gt; "The number is 1"</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">?</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="parenthesis macro">(</span><span class="numeric_literal macro">3</span><span class="comma macro">,</span> <span class="numeric_literal macro">4</span><span class="parenthesis macro">)</span><span class="parenthesis">)</span><span class="semicolon">;</span>          <span class="comment">// =&gt; "(3, 4)"</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">value</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="variable declaration macro">value</span><span class="operator macro">=</span><span class="numeric_literal macro">4</span><span class="parenthesis">)</span><span class="semicolon">;</span>      <span class="comment">// =&gt; "4"</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="comma macro">,</span> <span class="numeric_literal macro">2</span><span class="parenthesis">)</span><span class="semicolon">;</span>           <span class="comment">// =&gt; "1 2"</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="numeric_literal">4</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">42</span><span class="parenthesis">)</span><span class="semicolon">;</span>             <span class="comment">// =&gt; "0042" with leading zerosV</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="comma macro">,</span> <span class="numeric_literal macro">2</span><span class="parenthesis">)</span><span class="semicolon">;</span>   <span class="comment">// =&gt; "2 1 1 2"</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">argument</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="variable declaration macro">argument</span> <span class="operator macro">=</span> <span class="string_literal macro">"test"</span><span class="parenthesis">)</span><span class="semicolon">;</span>   <span class="comment">// =&gt; "test"</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="comma macro">,</span> <span class="variable declaration macro">name</span> <span class="operator macro">=</span> <span class="numeric_literal macro">2</span><span class="parenthesis">)</span><span class="semicolon">;</span>          <span class="comment">// =&gt; "2 1"</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">a</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="variable">c</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="variable">b</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="variable declaration macro">a</span><span class="operator macro">=</span><span class="string_literal macro">"a"</span><span class="comma macro">,</span> <span class="variable declaration macro">b</span><span class="operator macro">=</span><span class="char_literal macro">'b'</span><span class="comma macro">,</span> <span class="variable declaration macro">c</span><span class="operator macro">=</span><span class="numeric_literal macro">3</span><span class="parenthesis">)</span><span class="semicolon">;</span>  <span class="comment">// =&gt; "a 3 b"</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="escape_sequence">{{</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="escape_sequence">}}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">2</span><span class="parenthesis">)</span><span class="semicolon">;</span>                       <span class="comment">// =&gt; "{2}"</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">1</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="variable">width</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="variable declaration macro">width</span> <span class="operator macro">=</span> <span class="numeric_literal macro">5</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">&lt;</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">-</span><span class="format_specifier">&lt;</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">^</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">&gt;</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">+</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">#</span><span class="variable">x</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="numeric_literal macro">27</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="numeric_literal macro">-</span><span class="numeric_literal macro">5</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">#</span><span class="numeric_literal">0</span><span class="numeric_literal">10</span><span class="variable">x</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="numeric_literal macro">27</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0.01</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="numeric_literal">2</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="numeric_literal">0</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0.01</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="numeric_literal">2</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="numeric_literal">1</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="numeric_literal macro">0.01</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span>    <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="numeric_literal macro">0.01</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="numeric_literal">2</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span>   <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="numeric_literal macro">0.01</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="variable">number</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="variable">prec</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="variable declaration macro">prec</span> <span class="operator macro">=</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="variable declaration macro">number</span> <span class="operator macro">=</span> <span class="numeric_literal macro">0.01</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">` has 3 fractional digits"</span><span class="comma macro">,</span> <span class="string_literal macro">"Hello"</span><span class="comma macro">,</span> <span class="numeric_literal macro">3</span><span class="comma macro">,</span> <span class="variable declaration macro">name</span><span class="operator macro">=</span><span class="numeric_literal macro">1234.56</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">` has 3 characters"</span><span class="comma macro">,</span> <span class="string_literal macro">"Hello"</span><span class="comma macro">,</span> <span class="numeric_literal macro">3</span><span class="comma macro">,</span> <span class="variable declaration macro">name</span><span class="operator macro">=</span><span class="string_literal macro">"1234.56"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">&gt;</span><span class="numeric_literal">8</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">` has 3 right-aligned characters"</span><span class="comma macro">,</span> <span class="string_literal macro">"Hello"</span><span class="comma macro">,</span> <span class="numeric_literal macro">3</span><span class="comma macro">,</span> <span class="variable declaration macro">name</span><span class="operator macro">=</span><span class="string_literal macro">"1234.56"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
 
     <span class="keyword">let</span> <span class="punctuation">_</span> <span class="operator">=</span> <span class="string_literal">"{}"</span>
     <span class="keyword">let</span> <span class="punctuation">_</span> <span class="operator">=</span> <span class="string_literal">"{{}}"</span><span class="semicolon">;</span>
 
-    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"Hello </span><span class="escape_sequence">{{</span><span class="escape_sequence">}}</span><span class="string_literal macro">"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
-    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="escape_sequence">{{</span><span class="string_literal macro"> Hello"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
-    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"Hello </span><span class="escape_sequence">}}</span><span class="string_literal macro">"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
-    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="escape_sequence">{{</span><span class="string_literal macro">Hello</span><span class="escape_sequence">}}</span><span class="string_literal macro">"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
-    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="escape_sequence">{{</span><span class="string_literal macro"> Hello </span><span class="escape_sequence">}}</span><span class="string_literal macro">"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
-    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="escape_sequence">{{</span><span class="string_literal macro">Hello </span><span class="escape_sequence">}}</span><span class="string_literal macro">"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
-    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="escape_sequence">{{</span><span class="string_literal macro"> Hello</span><span class="escape_sequence">}}</span><span class="string_literal macro">"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"Hello </span><span class="escape_sequence">{{</span><span class="escape_sequence">}}</span><span class="string_literal macro">"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="escape_sequence">{{</span><span class="string_literal macro"> Hello"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"Hello </span><span class="escape_sequence">}}</span><span class="string_literal macro">"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="escape_sequence">{{</span><span class="string_literal macro">Hello</span><span class="escape_sequence">}}</span><span class="string_literal macro">"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="escape_sequence">{{</span><span class="string_literal macro"> Hello </span><span class="escape_sequence">}}</span><span class="string_literal macro">"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="escape_sequence">{{</span><span class="string_literal macro">Hello </span><span class="escape_sequence">}}</span><span class="string_literal macro">"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="escape_sequence">{{</span><span class="string_literal macro"> Hello</span><span class="escape_sequence">}}</span><span class="string_literal macro">"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
 
-    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">r"Hello, </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"world"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">r"Hello, </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"world"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
 
     <span class="comment">// escape sequences</span>
-    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"Hello</span><span class="escape_sequence">\n</span><span class="string_literal macro">World"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
-    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="escape_sequence">\u{48}</span><span class="escape_sequence">\x65</span><span class="escape_sequence">\x6C</span><span class="escape_sequence">\x6C</span><span class="escape_sequence">\x6F</span><span class="string_literal macro"> World"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"Hello</span><span class="escape_sequence">\n</span><span class="string_literal macro">World"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="escape_sequence">\u{48}</span><span class="escape_sequence">\x65</span><span class="escape_sequence">\x6C</span><span class="escape_sequence">\x6C</span><span class="escape_sequence">\x6F</span><span class="string_literal macro"> World"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
 
     <span class="keyword">let</span> <span class="punctuation">_</span> <span class="operator">=</span> <span class="string_literal">"</span><span class="escape_sequence">\x28</span><span class="escape_sequence">\x28</span><span class="escape_sequence">\x00</span><span class="escape_sequence">\x63</span><span class="invalid_escape_sequence">\xFF</span><span class="escape_sequence">\u{FF}</span><span class="escape_sequence">\n</span><span class="string_literal">"</span><span class="semicolon">;</span> <span class="comment">// invalid non-UTF8 escape sequences</span>
     <span class="keyword">let</span> <span class="punctuation">_</span> <span class="operator">=</span> <span class="string_literal">b"</span><span class="escape_sequence">\x28</span><span class="escape_sequence">\x28</span><span class="escape_sequence">\x00</span><span class="escape_sequence">\x63</span><span class="escape_sequence">\xFF</span><span class="invalid_escape_sequence">\u{FF}</span><span class="escape_sequence">\n</span><span class="string_literal">"</span><span class="semicolon">;</span> <span class="comment">// valid bytes, invalid unicodes</span>
     <span class="keyword">let</span> <span class="punctuation">_</span> <span class="operator">=</span> <span class="string_literal">c"</span><span class="escape_sequence">\u{FF}</span><span class="escape_sequence">\xFF</span><span class="string_literal">"</span><span class="semicolon">;</span> <span class="comment">// valid bytes, valid unicodes</span>
     <span class="keyword">let</span> <span class="variable declaration reference">backslash</span> <span class="operator">=</span> <span class="string_literal">r"\\"</span><span class="semicolon">;</span>
 
-    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="escape_sequence">\x41</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="none macro">A</span> <span class="operator macro">=</span> <span class="numeric_literal macro">92</span><span class="parenthesis">)</span><span class="semicolon">;</span>
-    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">ничоси</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="none macro">ничоси</span> <span class="operator macro">=</span> <span class="numeric_literal macro">92</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="escape_sequence">\x41</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="variable declaration macro">A</span> <span class="operator macro">=</span> <span class="numeric_literal macro">92</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">ничоси</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="variable declaration macro">ничоси</span> <span class="operator macro">=</span> <span class="numeric_literal macro">92</span><span class="parenthesis">)</span><span class="semicolon">;</span>
 
-    <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="variable">x</span><span class="format_specifier">?</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> "</span><span class="comma macro">,</span> <span class="unresolved_reference macro">thingy</span><span class="comma macro">,</span> <span class="unresolved_reference macro">n2</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+    <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="variable">x</span><span class="format_specifier">?</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> "</span><span class="comma macro">,</span> <span class="unresolved_reference macro">thingy</span><span class="comma macro">,</span> <span class="unresolved_reference macro">n2</span><span class="parenthesis">)</span><span class="semicolon">;</span>
     <span class="macro default_library library">panic</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"{}"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0</span><span class="parenthesis">)</span><span class="semicolon">;</span>
     <span class="macro default_library library">panic</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"more </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="parenthesis">)</span><span class="semicolon">;</span>
     <span class="macro default_library library">assert</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="bool_literal macro">true</span><span class="comma">,</span> <span class="string_literal macro">"{}"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="parenthesis">)</span><span class="semicolon">;</span>
     <span class="macro default_library library">assert</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="bool_literal macro">true</span><span class="comma">,</span> <span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> asdasd"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="parenthesis">)</span><span class="semicolon">;</span>
-    <span class="macro public">toho</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"{}fmt"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+    <span class="macro">toho</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"{}fmt"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0</span><span class="parenthesis">)</span><span class="semicolon">;</span>
     <span class="keyword">let</span> <span class="variable declaration">i</span><span class="colon">:</span> <span class="builtin_type">u64</span> <span class="operator">=</span> <span class="numeric_literal">3</span><span class="semicolon">;</span>
     <span class="keyword">let</span> <span class="variable declaration">o</span><span class="colon">:</span> <span class="builtin_type">u64</span><span class="semicolon">;</span>
     <span class="crate_root default_library library">core</span><span class="operator">::</span><span class="module default_library library">arch</span><span class="operator">::</span><span class="macro default_library library unsafe">asm</span><span class="macro_bang unsafe">!</span><span class="parenthesis unsafe">(</span>
@@ -175,6 +175,6 @@
     <span class="keyword const">const</span> <span class="constant const declaration">CONSTANT</span><span class="colon">:</span> <span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">=</span> <span class="parenthesis">(</span><span class="parenthesis">)</span><span class="colon">:</span>
     <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">m</span> <span class="operator">=</span> <span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
     <span class="macro default_library library">format_args</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="macro default_library library macro">concat</span><span class="macro_bang macro">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"{}"</span><span class="parenthesis macro">)</span><span class="comma macro">,</span> <span class="string_literal macro">"{}"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
-    <span class="macro default_library library">format_args</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="variable">backslash</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="variable">CONSTANT</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="variable">m</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="variable macro reference">backslash</span><span class="comma macro">,</span> <span class="macro default_library library macro">format_args</span><span class="macro_bang macro">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0</span><span class="parenthesis macro">)</span><span class="comma macro">,</span> <span class="unresolved_reference macro">foo</span><span class="comma macro">,</span> <span class="string_literal macro">"bar"</span><span class="comma macro">,</span> <span class="macro macro public">toho</span><span class="macro_bang macro">!</span><span class="parenthesis macro">(</span><span class="parenthesis macro">)</span><span class="comma macro">,</span> <span class="variable macro reference">backslash</span><span class="parenthesis">)</span><span class="semicolon">;</span>
-    <span class="macro public">reuse_twice</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">backslash</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+    <span class="macro default_library library">format_args</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="variable">backslash</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="variable">CONSTANT</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="variable">m</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="variable macro reference">backslash</span><span class="comma macro">,</span> <span class="macro default_library library macro">format_args</span><span class="macro_bang macro">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0</span><span class="parenthesis macro">)</span><span class="comma macro">,</span> <span class="unresolved_reference macro">foo</span><span class="comma macro">,</span> <span class="string_literal macro">"bar"</span><span class="comma macro">,</span> <span class="macro macro">toho</span><span class="macro_bang macro">!</span><span class="parenthesis macro">(</span><span class="parenthesis macro">)</span><span class="comma macro">,</span> <span class="variable macro reference">backslash</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+    <span class="macro">reuse_twice</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">backslash</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
 <span class="brace">}</span></code></pre>
\ No newline at end of file
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html b/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
index 008987d..2e889f8 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
@@ -41,12 +41,12 @@
 .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; }
 .unresolved_reference    { color: #FC5555; text-decoration: wavy underline; }
 </style>
-<pre><code><span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration public">id</span> <span class="brace">{</span>
+<pre><code><span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">id</span> <span class="brace">{</span>
     <span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>tt<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="operator">&gt;</span> <span class="brace">{</span>
         <span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span>
     <span class="brace">}</span><span class="semicolon">;</span>
 <span class="brace">}</span>
-<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration public">unsafe_deref</span> <span class="brace">{</span>
+<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">unsafe_deref</span> <span class="brace">{</span>
     <span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="operator">&gt;</span> <span class="brace">{</span>
         <span class="punctuation">*</span><span class="parenthesis">(</span><span class="punctuation">&</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="keyword">as</span> <span class="punctuation">*</span><span class="keyword const">const</span> <span class="parenthesis">(</span><span class="parenthesis">)</span><span class="parenthesis">)</span>
     <span class="brace">}</span><span class="semicolon">;</span>
@@ -80,14 +80,14 @@
     <span class="keyword">let</span> <span class="variable declaration">u</span><span class="colon">:</span> <span class="union">Union</span><span class="semicolon">;</span>
 
     <span class="comment">// id should be safe here, but unsafe_deref should not</span>
-    <span class="macro public">id</span><span class="macro_bang">!</span> <span class="brace">{</span>
-        <span class="keyword macro unsafe">unsafe</span> <span class="brace macro">{</span> <span class="macro macro public unsafe">unsafe_deref</span><span class="macro_bang macro unsafe">!</span><span class="parenthesis macro unsafe">(</span><span class="parenthesis macro unsafe">)</span> <span class="brace macro">}</span>
+    <span class="macro">id</span><span class="macro_bang">!</span> <span class="brace">{</span>
+        <span class="keyword macro unsafe">unsafe</span> <span class="brace macro">{</span> <span class="macro macro unsafe">unsafe_deref</span><span class="macro_bang macro unsafe">!</span><span class="parenthesis macro unsafe">(</span><span class="parenthesis macro unsafe">)</span> <span class="brace macro">}</span>
     <span class="brace">}</span><span class="semicolon">;</span>
 
     <span class="keyword unsafe">unsafe</span> <span class="brace">{</span>
         <span class="comment">// unsafe macro calls</span>
-        <span class="macro public unsafe">unsafe_deref</span><span class="macro_bang unsafe">!</span><span class="parenthesis unsafe">(</span><span class="parenthesis unsafe">)</span><span class="semicolon">;</span>
-        <span class="macro public unsafe">id</span><span class="macro_bang unsafe">!</span> <span class="brace unsafe">{</span> <span class="macro macro public unsafe">unsafe_deref</span><span class="macro_bang macro unsafe">!</span><span class="parenthesis macro unsafe">(</span><span class="parenthesis macro unsafe">)</span> <span class="brace unsafe">}</span><span class="semicolon">;</span>
+        <span class="macro unsafe">unsafe_deref</span><span class="macro_bang unsafe">!</span><span class="parenthesis unsafe">(</span><span class="parenthesis unsafe">)</span><span class="semicolon">;</span>
+        <span class="macro unsafe">id</span><span class="macro_bang unsafe">!</span> <span class="brace unsafe">{</span> <span class="macro macro unsafe">unsafe_deref</span><span class="macro_bang macro unsafe">!</span><span class="parenthesis macro unsafe">(</span><span class="parenthesis macro unsafe">)</span> <span class="brace unsafe">}</span><span class="semicolon">;</span>
 
         <span class="comment">// unsafe fn and method calls</span>
         <span class="function unsafe">unsafe_fn</span><span class="parenthesis unsafe">(</span><span class="parenthesis unsafe">)</span><span class="semicolon">;</span>
diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs
index 6cb323b..7a3d71f 100644
--- a/crates/ide/src/syntax_highlighting/tests.rs
+++ b/crates/ide/src/syntax_highlighting/tests.rs
@@ -453,6 +453,23 @@
 }
 
 #[test]
+fn test_raw_string_format_specifiers() {
+    check_highlighting(
+        r####"
+//- minicore: fmt
+fn main() {
+    let here = 1;
+    format_args!(r"backslash \{here} arg");
+    format_args!(r#"hashed \{here} arg"#);
+    format_args!("plain {here} arg");
+}
+"####,
+        expect_file!["./test_data/highlight_raw_string_format_specifiers.html"],
+        false,
+    );
+}
+
+#[test]
 fn test_string_highlighting() {
     // The format string detection is based on macro-expansion,
     // thus, we have to copy the macro definition from `std`
diff --git a/crates/ide/src/view_crate_graph.rs b/crates/ide/src/view_crate_graph.rs
index ecfdd09..cb75c59 100644
--- a/crates/ide/src/view_crate_graph.rs
+++ b/crates/ide/src/view_crate_graph.rs
@@ -1,9 +1,10 @@
 use dot::{Id, LabelText};
-use ide_db::base_db::all_crates;
-use ide_db::base_db::salsa::plumbing::AsId;
 use ide_db::{
     FxHashMap, RootDatabase,
-    base_db::{BuiltCrateData, BuiltDependency, Crate, ExtraCrateData, SourceDatabase},
+    base_db::{
+        BuiltCrateData, BuiltDependency, Crate, ExtraCrateData, SourceDatabase, all_crates,
+        salsa::plumbing::AsId,
+    },
 };
 
 // Feature: View Crate Graph
diff --git a/crates/parser/src/grammar.rs b/crates/parser/src/grammar.rs
index 0623e7e..0305733 100644
--- a/crates/parser/src/grammar.rs
+++ b/crates/parser/src/grammar.rs
@@ -343,6 +343,12 @@
     name_r(p, TokenSet::EMPTY);
 }
 
+fn name_any_identifier(p: &mut Parser<'_>) {
+    let m = p.start();
+    p.bump_remap_any_ident();
+    m.complete(p, NAME);
+}
+
 fn name_ref_or_self(p: &mut Parser<'_>) {
     if matches!(p.current(), T![ident] | T![self]) {
         let m = p.start();
diff --git a/crates/parser/src/grammar/expressions.rs b/crates/parser/src/grammar/expressions.rs
index 3f341c2..3df47bf 100644
--- a/crates/parser/src/grammar/expressions.rs
+++ b/crates/parser/src/grammar/expressions.rs
@@ -36,8 +36,7 @@
 /// It needs to be parsed with lower precedence than `&&`, so that
 /// `if let true = true && false` is parsed as `if (let true = true) && (true)`
 /// and not `if let true = (true && true)`.
-fn expr_let(p: &mut Parser<'_>) {
-    let r = Restrictions { forbid_structs: true, prefer_stmt: false };
+fn expr_let(p: &mut Parser<'_>, r: Restrictions) {
     expr_bp(p, None, r, 5);
 }
 
@@ -299,8 +298,16 @@
             //     match 1.. { _ => () };
             //     match a.b()..S { _ => () };
             // }
+
+            // test closure_postfix_range_method_call
+            // fn foo() {
+            //     || 1.. .method();
+            //     || 1.. .field;
+            // }
+            let has_access_after = p.at(T![.]) && p.nth_at(1, SyntaxKind::IDENT);
+            let struct_forbidden = r.forbid_structs && p.at(T!['{']);
             let has_trailing_expression =
-                p.at_ts(EXPR_FIRST) && !(r.forbid_structs && p.at(T!['{']));
+                p.at_ts(EXPR_FIRST) && !has_access_after && !struct_forbidden;
             if !has_trailing_expression {
                 // no RHS
                 lhs = m.complete(p, RANGE_EXPR);
@@ -382,6 +389,7 @@
                     // }
                     let has_access_after = p.at(T![.]) && p.nth_at(1, SyntaxKind::IDENT);
                     let struct_forbidden = r.forbid_structs && p.at(T!['{']);
+                    // NOTE: Similar logic `is_range` flag in expr_bp()
                     if p.at_ts(EXPR_FIRST) && !has_access_after && !struct_forbidden {
                         expr_bp(p, None, r, 2);
                     }
diff --git a/crates/parser/src/grammar/expressions/atom.rs b/crates/parser/src/grammar/expressions/atom.rs
index 7ce1fc2..275a79b 100644
--- a/crates/parser/src/grammar/expressions/atom.rs
+++ b/crates/parser/src/grammar/expressions/atom.rs
@@ -88,7 +88,7 @@
         T!['('] => tuple_expr(p),
         T!['['] => array_expr(p),
         T![if] => if_expr(p),
-        T![let] => let_expr(p),
+        T![let] => let_expr(p, r),
         T![_] => {
             // test destructuring_assignment_wildcard_pat
             // fn foo() {
@@ -291,10 +291,8 @@
             while !p.at(EOF) && !p.at(T![')']) {
                 let m = p.start();
                 if p.current().is_any_identifier() && p.nth_at(1, T![=]) && !p.nth_at(2, T![=]) {
-                    let m = p.start();
-                    p.bump_any();
+                    name_any_identifier(p);
                     p.bump(T![=]);
-                    m.complete(p, FORMAT_ARGS_ARG_NAME);
                 }
                 if expr(p).is_none() {
                     m.abandon(p);
@@ -376,8 +374,12 @@
         }
 
         // Parse operand names
-        if p.at(T![ident]) && p.nth_at(1, T![=]) {
-            name(p);
+        if p.current().is_any_identifier() && p.nth_at(1, T![=]) {
+            // test asm_keyword_name
+            // fn foo() {
+            //     builtin # asm("", fn = const 0);
+            // }
+            name_any_identifier(p);
             p.bump(T![=]);
             allow_templates = false;
         }
@@ -719,12 +721,12 @@
 //     if let Some(_) = None && true {}
 //     while 1 == 5 && (let None = None) {}
 // }
-fn let_expr(p: &mut Parser<'_>) -> CompletedMarker {
+fn let_expr(p: &mut Parser<'_>, r: Restrictions) -> CompletedMarker {
     let m = p.start();
     p.bump(T![let]);
     patterns::pattern(p);
     p.expect(T![=]);
-    expr_let(p);
+    expr_let(p, r);
     m.complete(p, LET_EXPR)
 }
 
@@ -860,6 +862,12 @@
     if p.at(T![=]) {
         p.error("expected expression");
     } else {
+        // test guard_let_struct
+        // fn foo() {
+        //     match () {
+        //         () if let Foo {} = Foo {} => {}
+        //     }
+        // }
         expr(p);
     }
     m.complete(p, MATCH_GUARD)
diff --git a/crates/parser/src/parser.rs b/crates/parser/src/parser.rs
index 57c501e..11a3a53 100644
--- a/crates/parser/src/parser.rs
+++ b/crates/parser/src/parser.rs
@@ -154,6 +154,12 @@
         true
     }
 
+    /// This consumes the next token as an identifier, even if it is a strict keyword.
+    pub(crate) fn bump_remap_any_ident(&mut self) {
+        assert!(self.current().is_any_identifier());
+        self.bump_remap(T![ident]);
+    }
+
     fn at_composite2(&self, n: usize, k1: SyntaxKind, k2: SyntaxKind) -> bool {
         self.inp.kind(self.pos + n) == k1
             && self.inp.kind(self.pos + n + 1) == k2
diff --git a/crates/parser/src/syntax_kind/generated.rs b/crates/parser/src/syntax_kind/generated.rs
index 1c4fcf0..5604da5 100644
--- a/crates/parser/src/syntax_kind/generated.rs
+++ b/crates/parser/src/syntax_kind/generated.rs
@@ -214,7 +214,6 @@
     FN,
     FN_PTR_TYPE,
     FORMAT_ARGS_ARG,
-    FORMAT_ARGS_ARG_NAME,
     FORMAT_ARGS_EXPR,
     FOR_BINDER,
     FOR_EXPR,
@@ -402,7 +401,6 @@
             | FN
             | FN_PTR_TYPE
             | FORMAT_ARGS_ARG
-            | FORMAT_ARGS_ARG_NAME
             | FORMAT_ARGS_EXPR
             | FOR_BINDER
             | FOR_EXPR
diff --git a/crates/parser/test_data/generated/runner.rs b/crates/parser/test_data/generated/runner.rs
index 8104d28..520e736 100644
--- a/crates/parser/test_data/generated/runner.rs
+++ b/crates/parser/test_data/generated/runner.rs
@@ -21,6 +21,10 @@
     #[test]
     fn asm_expr() { run_and_expect_no_errors("test_data/parser/inline/ok/asm_expr.rs"); }
     #[test]
+    fn asm_keyword_name() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/asm_keyword_name.rs");
+    }
+    #[test]
     fn asm_kinds() { run_and_expect_no_errors("test_data/parser/inline/ok/asm_kinds.rs"); }
     #[test]
     fn asm_label() { run_and_expect_no_errors("test_data/parser/inline/ok/asm_label.rs"); }
@@ -121,6 +125,10 @@
         run_and_expect_no_errors("test_data/parser/inline/ok/closure_params.rs");
     }
     #[test]
+    fn closure_postfix_range_method_call() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/closure_postfix_range_method_call.rs");
+    }
+    #[test]
     fn closure_range_method_call() {
         run_and_expect_no_errors("test_data/parser/inline/ok/closure_range_method_call.rs");
     }
@@ -336,6 +344,10 @@
     #[test]
     fn global_asm() { run_and_expect_no_errors("test_data/parser/inline/ok/global_asm.rs"); }
     #[test]
+    fn guard_let_struct() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/guard_let_struct.rs");
+    }
+    #[test]
     fn half_open_range_pat() {
         run_and_expect_no_errors("test_data/parser/inline/ok/half_open_range_pat.rs");
     }
diff --git a/crates/parser/test_data/parser/inline/ok/asm_keyword_name.rast b/crates/parser/test_data/parser/inline/ok/asm_keyword_name.rast
new file mode 100644
index 0000000..4c97562
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/ok/asm_keyword_name.rast
@@ -0,0 +1,42 @@
+SOURCE_FILE
+  FN
+    FN_KW "fn"
+    WHITESPACE " "
+    NAME
+      IDENT "foo"
+    PARAM_LIST
+      L_PAREN "("
+      R_PAREN ")"
+    WHITESPACE " "
+    BLOCK_EXPR
+      STMT_LIST
+        L_CURLY "{"
+        WHITESPACE "\n    "
+        EXPR_STMT
+          ASM_EXPR
+            BUILTIN_KW "builtin"
+            WHITESPACE " "
+            POUND "#"
+            WHITESPACE " "
+            ASM_KW "asm"
+            L_PAREN "("
+            LITERAL
+              STRING "\"\""
+            COMMA ","
+            WHITESPACE " "
+            ASM_OPERAND_NAMED
+              NAME
+                IDENT "fn"
+              WHITESPACE " "
+              EQ "="
+              WHITESPACE " "
+              ASM_CONST
+                CONST_KW "const"
+                WHITESPACE " "
+                LITERAL
+                  INT_NUMBER "0"
+            R_PAREN ")"
+          SEMICOLON ";"
+        WHITESPACE "\n"
+        R_CURLY "}"
+  WHITESPACE "\n"
diff --git a/crates/parser/test_data/parser/inline/ok/asm_keyword_name.rs b/crates/parser/test_data/parser/inline/ok/asm_keyword_name.rs
new file mode 100644
index 0000000..a8f1ab2
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/ok/asm_keyword_name.rs
@@ -0,0 +1,3 @@
+fn foo() {
+    builtin # asm("", fn = const 0);
+}
diff --git a/crates/parser/test_data/parser/inline/ok/builtin_expr.rast b/crates/parser/test_data/parser/inline/ok/builtin_expr.rast
index 78c3bee..19a84ac 100644
--- a/crates/parser/test_data/parser/inline/ok/builtin_expr.rast
+++ b/crates/parser/test_data/parser/inline/ok/builtin_expr.rast
@@ -44,10 +44,10 @@
             COMMA ","
             WHITESPACE " "
             FORMAT_ARGS_ARG
-              FORMAT_ARGS_ARG_NAME
+              NAME
                 IDENT "a"
-                WHITESPACE " "
-                EQ "="
+              WHITESPACE " "
+              EQ "="
               WHITESPACE " "
               BIN_EXPR
                 LITERAL
diff --git a/crates/parser/test_data/parser/inline/ok/closure_postfix_range_method_call.rast b/crates/parser/test_data/parser/inline/ok/closure_postfix_range_method_call.rast
new file mode 100644
index 0000000..555d312
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/ok/closure_postfix_range_method_call.rast
@@ -0,0 +1,53 @@
+SOURCE_FILE
+  FN
+    FN_KW "fn"
+    WHITESPACE " "
+    NAME
+      IDENT "foo"
+    PARAM_LIST
+      L_PAREN "("
+      R_PAREN ")"
+    WHITESPACE " "
+    BLOCK_EXPR
+      STMT_LIST
+        L_CURLY "{"
+        WHITESPACE "\n    "
+        EXPR_STMT
+          METHOD_CALL_EXPR
+            CLOSURE_EXPR
+              PARAM_LIST
+                PIPE "|"
+                PIPE "|"
+              WHITESPACE " "
+              RANGE_EXPR
+                LITERAL
+                  INT_NUMBER "1"
+                DOT2 ".."
+            WHITESPACE " "
+            DOT "."
+            NAME_REF
+              IDENT "method"
+            ARG_LIST
+              L_PAREN "("
+              R_PAREN ")"
+          SEMICOLON ";"
+        WHITESPACE "\n    "
+        EXPR_STMT
+          FIELD_EXPR
+            CLOSURE_EXPR
+              PARAM_LIST
+                PIPE "|"
+                PIPE "|"
+              WHITESPACE " "
+              RANGE_EXPR
+                LITERAL
+                  INT_NUMBER "1"
+                DOT2 ".."
+            WHITESPACE " "
+            DOT "."
+            NAME_REF
+              IDENT "field"
+          SEMICOLON ";"
+        WHITESPACE "\n"
+        R_CURLY "}"
+  WHITESPACE "\n"
diff --git a/crates/parser/test_data/parser/inline/ok/closure_postfix_range_method_call.rs b/crates/parser/test_data/parser/inline/ok/closure_postfix_range_method_call.rs
new file mode 100644
index 0000000..71429be
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/ok/closure_postfix_range_method_call.rs
@@ -0,0 +1,4 @@
+fn foo() {
+    || 1.. .method();
+    || 1.. .field;
+}
diff --git a/crates/parser/test_data/parser/inline/ok/format_args_named_arg_keyword.rast b/crates/parser/test_data/parser/inline/ok/format_args_named_arg_keyword.rast
index 03bc2ec..50b9208 100644
--- a/crates/parser/test_data/parser/inline/ok/format_args_named_arg_keyword.rast
+++ b/crates/parser/test_data/parser/inline/ok/format_args_named_arg_keyword.rast
@@ -23,9 +23,9 @@
             COMMA ","
             WHITESPACE " "
             FORMAT_ARGS_ARG
-              FORMAT_ARGS_ARG_NAME
-                TYPE_KW "type"
-                EQ "="
+              NAME
+                IDENT "type"
+              EQ "="
               LITERAL
                 INT_NUMBER "1"
             R_PAREN ")"
diff --git a/crates/parser/test_data/parser/inline/ok/guard_let_struct.rast b/crates/parser/test_data/parser/inline/ok/guard_let_struct.rast
new file mode 100644
index 0000000..33754e6
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/ok/guard_let_struct.rast
@@ -0,0 +1,68 @@
+SOURCE_FILE
+  FN
+    FN_KW "fn"
+    WHITESPACE " "
+    NAME
+      IDENT "foo"
+    PARAM_LIST
+      L_PAREN "("
+      R_PAREN ")"
+    WHITESPACE " "
+    BLOCK_EXPR
+      STMT_LIST
+        L_CURLY "{"
+        WHITESPACE "\n    "
+        MATCH_EXPR
+          MATCH_KW "match"
+          WHITESPACE " "
+          TUPLE_EXPR
+            L_PAREN "("
+            R_PAREN ")"
+          WHITESPACE " "
+          MATCH_ARM_LIST
+            L_CURLY "{"
+            WHITESPACE "\n        "
+            MATCH_ARM
+              TUPLE_PAT
+                L_PAREN "("
+                R_PAREN ")"
+              WHITESPACE " "
+              MATCH_GUARD
+                IF_KW "if"
+                WHITESPACE " "
+                LET_EXPR
+                  LET_KW "let"
+                  WHITESPACE " "
+                  RECORD_PAT
+                    PATH
+                      PATH_SEGMENT
+                        NAME_REF
+                          IDENT "Foo"
+                    WHITESPACE " "
+                    RECORD_PAT_FIELD_LIST
+                      L_CURLY "{"
+                      R_CURLY "}"
+                  WHITESPACE " "
+                  EQ "="
+                  WHITESPACE " "
+                  RECORD_EXPR
+                    PATH
+                      PATH_SEGMENT
+                        NAME_REF
+                          IDENT "Foo"
+                    WHITESPACE " "
+                    RECORD_EXPR_FIELD_LIST
+                      L_CURLY "{"
+                      R_CURLY "}"
+              WHITESPACE " "
+              FAT_ARROW "=>"
+              WHITESPACE " "
+              BLOCK_EXPR
+                STMT_LIST
+                  L_CURLY "{"
+                  R_CURLY "}"
+            WHITESPACE "\n    "
+            R_CURLY "}"
+        WHITESPACE "\n"
+        R_CURLY "}"
+  WHITESPACE "\n"
diff --git a/crates/parser/test_data/parser/inline/ok/guard_let_struct.rs b/crates/parser/test_data/parser/inline/ok/guard_let_struct.rs
new file mode 100644
index 0000000..bd84d57
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/ok/guard_let_struct.rs
@@ -0,0 +1,5 @@
+fn foo() {
+    match () {
+        () if let Foo {} = Foo {} => {}
+    }
+}
diff --git a/crates/project-model/src/build_dependencies.rs b/crates/project-model/src/build_dependencies.rs
index 9f84f63..8da0a57 100644
--- a/crates/project-model/src/build_dependencies.rs
+++ b/crates/project-model/src/build_dependencies.rs
@@ -462,9 +462,7 @@
                     cmd.arg(target_dir.as_ref());
                 }
 
-                if let Some(target) = &config.target {
-                    cmd.args(["--target", target]);
-                }
+                toolchain::cargo_use_targets(toolchain, &mut cmd, config.target.as_slice());
                 let mut lockfile_copy = None;
                 if let Some(toolchain) = toolchain {
                     let lockfile_path =
diff --git a/crates/project-model/src/cargo_workspace.rs b/crates/project-model/src/cargo_workspace.rs
index 97375fe..a0a9815 100644
--- a/crates/project-model/src/cargo_workspace.rs
+++ b/crates/project-model/src/cargo_workspace.rs
@@ -704,9 +704,18 @@
         }
 
         if !config.targets.is_empty() {
-            other_options.extend(
-                config.targets.iter().flat_map(|it| ["--filter-platform".to_owned(), it.clone()]),
-            );
+            let mut has_json_target = false;
+            other_options.extend(config.targets.iter().flat_map(|target| {
+                has_json_target |= target.ends_with(".json");
+                ["--filter-platform".to_owned(), target.clone()]
+            }));
+            if has_json_target
+                && config.toolchain_version.as_ref().is_some_and(|version| {
+                    *version >= toolchain::MINIMUM_TOOLCHAIN_VERSION_REQUIRING_JSON_TARGET_SPEC_FLAG
+                })
+            {
+                other_options.push("-Zjson-target-spec".to_owned());
+            }
         }
 
         command.other_options(other_options.clone());
diff --git a/crates/project-model/src/toolchain_info/target_data.rs b/crates/project-model/src/toolchain_info/target_data.rs
index b815c0b..18ceac6 100644
--- a/crates/project-model/src/toolchain_info/target_data.rs
+++ b/crates/project-model/src/toolchain_info/target_data.rs
@@ -39,6 +39,7 @@
     config: QueryConfig<'_>,
     target: Option<&str>,
     extra_env: &FxHashMap<String, Option<String>>,
+    version: Option<&semver::Version>,
 ) -> anyhow::Result<target::TargetData> {
     const RUSTC_ARGS: [&str; 2] = ["--print", "target-spec-json"];
     let process = |output: String| {
@@ -55,9 +56,7 @@
             let mut cmd = sysroot.tool(Tool::Cargo, cargo_toml.parent(), extra_env);
             cmd.env("RUSTC_BOOTSTRAP", "1");
             cmd.args(["rustc", "-Z", "unstable-options"]).args(RUSTC_ARGS);
-            if let Some(target) = target {
-                cmd.args(["--target", target]);
-            }
+            toolchain::cargo_use_targets(version, &mut cmd, target.as_slice());
             cmd.args(["--", "-Z", "unstable-options"]);
             match utf8_stdout(&mut cmd) {
                 Ok(output) => return process(output),
@@ -95,13 +94,17 @@
         let manifest_path =
             ManifestPath::try_from(AbsPathBuf::assert(Utf8PathBuf::from(manifest_path))).unwrap();
         let cfg = QueryConfig::Cargo(&sysroot, &manifest_path, &None);
-        assert!(get(cfg, None, &FxHashMap::default()).is_ok());
+        let extra_env = &FxHashMap::default();
+        let Ok(version) = super::super::version::get(cfg, extra_env) else { return };
+        assert!(get(cfg, None, extra_env, version.as_ref()).is_ok());
     }
 
     #[test]
     fn rustc() {
         let sysroot = Sysroot::empty();
         let cfg = QueryConfig::Rustc(&sysroot, env!("CARGO_MANIFEST_DIR").as_ref());
-        assert!(get(cfg, None, &FxHashMap::default()).is_ok());
+        let extra_env = &FxHashMap::default();
+        let Ok(version) = super::super::version::get(cfg, extra_env) else { return };
+        assert!(get(cfg, None, extra_env, version.as_ref()).is_ok());
     }
 }
diff --git a/crates/project-model/src/toolchain_info/version.rs b/crates/project-model/src/toolchain_info/version.rs
index 357053d..c97a687 100644
--- a/crates/project-model/src/toolchain_info/version.rs
+++ b/crates/project-model/src/toolchain_info/version.rs
@@ -7,7 +7,7 @@
 
 use crate::{toolchain_info::QueryConfig, utf8_stdout};
 
-pub(crate) fn get(
+pub fn get(
     config: QueryConfig<'_>,
     extra_env: &FxHashMap<String, Option<String>>,
 ) -> Result<Option<Version>, anyhow::Error> {
diff --git a/crates/project-model/src/workspace.rs b/crates/project-model/src/workspace.rs
index 9002b0c..9010ba8 100644
--- a/crates/project-model/src/workspace.rs
+++ b/crates/project-model/src/workspace.rs
@@ -316,13 +316,18 @@
             let target_data = Builder::new()
                 .name("ProjectWorkspace::target_data".to_owned())
                 .spawn_scoped(s, || {
-                    target_data::get(toolchain_config, targets.first().map(Deref::deref), extra_env)
-                        .inspect_err(|e| {
-                            tracing::error!(%e,
-                                "failed fetching data layout for \
-                                {cargo_toml:?} workspace"
-                            )
-                        })
+                    target_data::get(
+                        toolchain_config,
+                        targets.first().map(Deref::deref),
+                        extra_env,
+                        toolchain.as_ref(),
+                    )
+                    .inspect_err(|e| {
+                        tracing::error!(%e,
+                            "failed fetching data layout for \
+                            {cargo_toml:?} workspace"
+                        )
+                    })
                 })
                 .expect("failed to spawn thread");
 
@@ -495,7 +500,12 @@
                 rustc_cfg::get(query_config, targets.first().map(Deref::deref), &config.extra_env)
             });
             let data_layout = s.spawn(|| {
-                target_data::get(query_config, targets.first().map(Deref::deref), &config.extra_env)
+                target_data::get(
+                    query_config,
+                    targets.first().map(Deref::deref),
+                    &config.extra_env,
+                    toolchain.as_ref(),
+                )
             });
             let loaded_sysroot = s.spawn(|| {
                 if let Some(sysroot_project) = sysroot_project {
@@ -564,7 +574,8 @@
         let targets = target_tuple::get(query_config, config.target.as_deref(), &config.extra_env)
             .unwrap_or_default();
         let rustc_cfg = rustc_cfg::get(query_config, None, &config.extra_env);
-        let target_data = target_data::get(query_config, None, &config.extra_env);
+        let target_data =
+            target_data::get(query_config, None, &config.extra_env, toolchain.as_ref());
 
         let loaded_sysroot = sysroot.load_workspace(
             &RustSourceWorkspaceConfig::CargoMetadata(sysroot_metadata_config(
diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml
index ae0c957..5ec0a89 100644
--- a/crates/rust-analyzer/Cargo.toml
+++ b/crates/rust-analyzer/Cargo.toml
@@ -29,7 +29,7 @@
 indexmap.workspace = true
 itertools.workspace = true
 scip = "0.7.1"
-lsp-types = { version = "0.10.0", package = "gen-lsp-types", features = ["url"] }
+lsp-types = { version = "0.11.0", package = "gen-lsp-types", features = ["url"] }
 parking_lot = "0.12.4"
 xflags = "0.3.2"
 oorandom = "11.1.5"
diff --git a/crates/rust-analyzer/src/bin/main.rs b/crates/rust-analyzer/src/bin/main.rs
index 6bd27c2..c3678e0 100644
--- a/crates/rust-analyzer/src/bin/main.rs
+++ b/crates/rust-analyzer/src/bin/main.rs
@@ -72,7 +72,7 @@
         flags::RustAnalyzerCmd::Symbols(cmd) => cmd.run()?,
         flags::RustAnalyzerCmd::Highlight(cmd) => cmd.run()?,
         flags::RustAnalyzerCmd::AnalysisStats(cmd) => cmd.run(verbosity)?,
-        flags::RustAnalyzerCmd::Diagnostics(cmd) => cmd.run()?,
+        flags::RustAnalyzerCmd::Diagnostics(cmd) => cmd.run(verbosity)?,
         flags::RustAnalyzerCmd::UnresolvedReferences(cmd) => cmd.run()?,
         flags::RustAnalyzerCmd::Ssr(cmd) => cmd.run()?,
         flags::RustAnalyzerCmd::Search(cmd) => cmd.run()?,
@@ -166,8 +166,6 @@
     Ok(())
 }
 
-const STACK_SIZE: usize = 1024 * 1024 * 8;
-
 /// Parts of rust-analyzer can use a lot of stack space, and some operating systems only give us
 /// 1 MB by default (eg. Windows), so this spawns a new thread with hopefully sufficient stack
 /// space.
@@ -176,8 +174,7 @@
     thread_intent: stdx::thread::ThreadIntent,
     f: impl FnOnce() -> anyhow::Result<()> + Send + 'static,
 ) -> anyhow::Result<()> {
-    let handle =
-        stdx::thread::Builder::new(thread_intent, thread_name).stack_size(STACK_SIZE).spawn(f)?;
+    let handle = stdx::thread::Builder::new(thread_intent, thread_name).spawn(f)?;
 
     handle.join()?;
 
@@ -189,6 +186,7 @@
 
     rayon::ThreadPoolBuilder::new()
         .thread_name(|ix| format!("RayonWorker{}", ix))
+        .stack_size(stdx::thread::DEFAULT_STACK_SIZE)
         .build_global()
         .unwrap();
 
diff --git a/crates/rust-analyzer/src/cli.rs b/crates/rust-analyzer/src/cli.rs
index cc0efe9..0355ade 100644
--- a/crates/rust-analyzer/src/cli.rs
+++ b/crates/rust-analyzer/src/cli.rs
@@ -42,6 +42,9 @@
     pub fn is_spammy(self) -> bool {
         matches!(self, Verbosity::Spammy)
     }
+    pub fn is_quiet(self) -> bool {
+        matches!(self, Verbosity::Quiet)
+    }
 }
 
 fn read_stdin() -> anyhow::Result<String> {
diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs
index 073f287..6cbb0c7 100644
--- a/crates/rust-analyzer/src/cli/analysis_stats.rs
+++ b/crates/rust-analyzer/src/cli/analysis_stats.rs
@@ -53,6 +53,14 @@
 
 impl flags::AnalysisStats {
     pub fn run(self, verbosity: Verbosity) -> anyhow::Result<()> {
+        let mut rayon_pool = rayon::ThreadPoolBuilder::new()
+            .thread_name(|ix| format!("RayonWorker{}", ix))
+            .stack_size(stdx::thread::DEFAULT_STACK_SIZE);
+        if !self.parallel {
+            rayon_pool = rayon_pool.num_threads(1);
+        }
+        rayon_pool.build_global().unwrap();
+
         let mut rng = {
             let seed = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_millis() as u64;
             Rand32::new(seed)
@@ -576,10 +584,7 @@
                     sema: &sema,
                     scope: &scope,
                     goal: target_ty,
-                    config: hir::term_search::TermSearchConfig {
-                        enable_borrowcheck: true,
-                        ..Default::default()
-                    },
+                    config: hir::term_search::TermSearchConfig::default(),
                 };
                 let found_terms = hir::term_search::term_search(&ctx);
 
@@ -1367,7 +1372,6 @@
                     prefer_absolute: false,
                     style_lints: false,
                     term_search_fuel: 400,
-                    term_search_borrowck: true,
                     show_rename_conflicts: true,
                 },
                 ide::AssistResolveStrategy::All,
diff --git a/crates/rust-analyzer/src/cli/diagnostics.rs b/crates/rust-analyzer/src/cli/diagnostics.rs
index e50e1c2..fe1a58f 100644
--- a/crates/rust-analyzer/src/cli/diagnostics.rs
+++ b/crates/rust-analyzer/src/cli/diagnostics.rs
@@ -9,23 +9,20 @@
 use ide_db::{base_db::SourceDatabase, line_index};
 use load_cargo::{LoadCargoConfig, ProcMacroServerChoice, load_workspace_at};
 
-use crate::cli::{flags, progress_report::ProgressReport};
+use crate::cli::{Verbosity, flags, progress_report::ProgressReport};
 
 impl flags::Diagnostics {
-    pub fn run(self) -> anyhow::Result<()> {
-        const STACK_SIZE: usize = 1024 * 1024 * 8;
-
+    pub fn run(self, verbosity: Verbosity) -> anyhow::Result<()> {
         let handle = stdx::thread::Builder::new(
             stdx::thread::ThreadIntent::LatencySensitive,
             "BIG_STACK_THREAD",
         )
-        .stack_size(STACK_SIZE)
-        .spawn(|| self.run_())
+        .spawn(move || self.run_(verbosity))
         .unwrap();
 
         handle.join()
     }
-    fn run_(self) -> anyhow::Result<()> {
+    fn run_(self, verbosity: Verbosity) -> anyhow::Result<()> {
         let cargo_config = CargoConfig {
             sysroot: Some(RustLibSource::Discover),
             all_targets: true,
@@ -64,7 +61,11 @@
             })
             .collect::<Vec<_>>();
 
-        let mut bar = ProgressReport::new(work.len());
+        let mut bar = if verbosity.is_quiet() {
+            ProgressReport::hidden()
+        } else {
+            ProgressReport::new(work.len())
+        };
         for module in work {
             let file_id = module.definition_source_file_id(db).original_file(db);
             if !visited_files.contains(&file_id) {
diff --git a/crates/rust-analyzer/src/cli/progress_report.rs b/crates/rust-analyzer/src/cli/progress_report.rs
index aff2c0d..7c634cf 100644
--- a/crates/rust-analyzer/src/cli/progress_report.rs
+++ b/crates/rust-analyzer/src/cli/progress_report.rs
@@ -1,7 +1,7 @@
 //! A simple progress bar
 //!
 //! A single thread non-optimized progress bar
-use std::io::{self, Write};
+use std::io::{self, IsTerminal, Write};
 
 /// A Simple ASCII Progress Bar
 pub(crate) struct ProgressReport<'a> {
@@ -16,7 +16,8 @@
 
 impl<'a> ProgressReport<'a> {
     pub(crate) fn new(len: usize) -> ProgressReport<'a> {
-        ProgressReport { curr: 0.0, text: String::new(), hidden: false, len, pos: 0, msg: None }
+        let is_tty = io::stdout().is_terminal();
+        ProgressReport { curr: 0.0, text: String::new(), hidden: !is_tty, len, pos: 0, msg: None }
     }
 
     pub(crate) fn hidden() -> ProgressReport<'a> {
diff --git a/crates/rust-analyzer/src/cli/rustc_tests.rs b/crates/rust-analyzer/src/cli/rustc_tests.rs
index 69405b4..5225c49c 100644
--- a/crates/rust-analyzer/src/cli/rustc_tests.rs
+++ b/crates/rust-analyzer/src/cli/rustc_tests.rs
@@ -10,7 +10,7 @@
 use ide_db::base_db;
 use itertools::Either;
 use profile::StopWatch;
-use project_model::toolchain_info::{QueryConfig, target_data};
+use project_model::toolchain_info::{QueryConfig, target_data, version};
 use project_model::{
     CargoConfig, ManifestPath, ProjectWorkspace, ProjectWorkspaceKind, RustLibSource,
     RustSourceWorkspaceConfig, Sysroot,
@@ -81,10 +81,13 @@
             sysroot.set_workspace(loaded_sysroot);
         }
 
+        let query_config = QueryConfig::Rustc(&sysroot, tmp_file.parent().unwrap().as_ref());
+        let toolchain_version = version::get(query_config, &cargo_config.extra_env).ok().flatten();
         let target_data = target_data::get(
-            QueryConfig::Rustc(&sysroot, tmp_file.parent().unwrap().as_ref()),
+            query_config,
             None,
             &cargo_config.extra_env,
+            toolchain_version.as_ref(),
         );
 
         let workspace = ProjectWorkspace {
diff --git a/crates/rust-analyzer/src/cli/unresolved_references.rs b/crates/rust-analyzer/src/cli/unresolved_references.rs
index f8eacbb..d9a5609 100644
--- a/crates/rust-analyzer/src/cli/unresolved_references.rs
+++ b/crates/rust-analyzer/src/cli/unresolved_references.rs
@@ -11,13 +11,10 @@
 
 impl flags::UnresolvedReferences {
     pub fn run(self) -> anyhow::Result<()> {
-        const STACK_SIZE: usize = 1024 * 1024 * 8;
-
         let handle = stdx::thread::Builder::new(
             stdx::thread::ThreadIntent::LatencySensitive,
             "BIG_STACK_THREAD",
         )
-        .stack_size(STACK_SIZE)
         .spawn(|| self.run_())
         .unwrap();
 
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index 3ba0e47..1e7c825 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -623,10 +623,6 @@
         /// Prefer to use `Self` over the type name when inserting a type (e.g. in "fill match arms" assist).
         assist_preferSelf: bool = false,
 
-        /// Enable borrow checking for term search code assists. If set to false, also there will be
-        /// more suggestions, but some of them may not borrow-check.
-        assist_termSearch_borrowcheck: bool = true,
-
         /// Term search fuel in "units of work" for assists (Defaults to 1800).
         assist_termSearch_fuel: usize = 1800,
 
@@ -1868,7 +1864,6 @@
             prefer_prelude: self.imports_preferPrelude(source_root).to_owned(),
             prefer_absolute: self.imports_prefixExternPrelude(source_root).to_owned(),
             term_search_fuel: self.assist_termSearch_fuel(source_root).to_owned() as u64,
-            term_search_borrowck: self.assist_termSearch_borrowcheck(source_root).to_owned(),
             code_action_grouping: self.code_action_group(),
             expr_fill_default: match self.assist_expressionFillDefault(source_root) {
                 ExprFillDefaultDef::Todo => ExprFillDefaultMode::Todo,
@@ -1998,7 +1993,6 @@
             prefer_absolute: self.imports_prefixExternPrelude(source_root).to_owned(),
             style_lints: self.diagnostics_styleLints_enable(source_root).to_owned(),
             term_search_fuel: self.assist_termSearch_fuel(source_root).to_owned() as u64,
-            term_search_borrowck: self.assist_termSearch_borrowcheck(source_root).to_owned(),
             show_rename_conflicts: *self.rename_showConflicts(source_root),
         }
     }
@@ -4034,7 +4028,9 @@
             "enum": ["per_workspace", "once"],
             "enumDescriptions": [
                 "The command will be executed for each Rust workspace with the workspace as the working directory.",
-                "The command will be executed once with the opened project as the working directory."
+                "The command will be executed once with the opened project as the working directory.\
+                \n\n**Warning:** This can cause errors when the Cargo configuration of the opened workspaces is different \
+                or even only different than the default. Enabling this is not recommended."
             ],
         },
         "Option<CheckOnSaveTargets>" => set! {
diff --git a/crates/rust-analyzer/src/flycheck.rs b/crates/rust-analyzer/src/flycheck.rs
index b927a11..99e640a 100644
--- a/crates/rust-analyzer/src/flycheck.rs
+++ b/crates/rust-analyzer/src/flycheck.rs
@@ -69,10 +69,9 @@
         cmd: &mut Command,
         ws_target_dir: Option<&Utf8Path>,
         package_repr: Option<&str>,
+        toolchain_version: Option<&semver::Version>,
     ) {
-        for target in &self.target_tuples {
-            cmd.args(["--target", target.as_str()]);
-        }
+        toolchain::cargo_use_targets(toolchain_version, cmd, &self.target_tuples);
         if self.all_targets {
             if self.set_test {
                 cmd.arg("--all-targets");
@@ -227,6 +226,7 @@
         workspace_root: AbsPathBuf,
         manifest_path: Option<AbsPathBuf>,
         ws_target_dir: Option<Utf8PathBuf>,
+        toolchain_version: Option<semver::Version>,
     ) -> FlycheckHandle {
         let actor = FlycheckActor::new(
             id,
@@ -238,6 +238,7 @@
             workspace_root,
             manifest_path,
             ws_target_dir,
+            toolchain_version,
         );
         let (sender, receiver) = unbounded::<StateChange>();
         let thread =
@@ -445,6 +446,7 @@
     command_receiver: Option<Receiver<CheckMessage>>,
     diagnostics_cleared_for: FxHashSet<PackageSpecifier>,
     diagnostics_received: DiagnosticsReceived,
+    toolchain_version: Option<semver::Version>,
 }
 
 #[derive(PartialEq, Debug)]
@@ -531,6 +533,7 @@
         workspace_root: AbsPathBuf,
         manifest_path: Option<AbsPathBuf>,
         ws_target_dir: Option<Utf8PathBuf>,
+        toolchain_version: Option<semver::Version>,
     ) -> FlycheckActor {
         tracing::info!(%id, ?workspace_root, "Spawning flycheck");
         FlycheckActor {
@@ -548,6 +551,7 @@
             command_receiver: None,
             diagnostics_cleared_for: Default::default(),
             diagnostics_received: DiagnosticsReceived::NotYet,
+            toolchain_version,
         }
     }
 
@@ -958,6 +962,7 @@
                     &mut cmd,
                     self.ws_target_dir.as_ref().map(Utf8PathBuf::as_path),
                     package_repr,
+                    self.toolchain_version.as_ref(),
                 );
                 cmd.args(&cargo_options.extra_args);
                 Some((cmd, FlycheckCommandOrigin::Cargo))
diff --git a/crates/rust-analyzer/src/handlers/request.rs b/crates/rust-analyzer/src/handlers/request.rs
index 41e3db7..d88eb35 100644
--- a/crates/rust-analyzer/src/handlers/request.rs
+++ b/crates/rust-analyzer/src/handlers/request.rs
@@ -287,6 +287,7 @@
                     Some(cargo.target_directory().as_ref()),
                     target,
                     state.test_run_sender.clone(),
+                    ws.toolchain.as_ref(),
                 )?;
                 handles.push(handle);
             }
@@ -780,10 +781,7 @@
     let source_changes: Vec<SourceChange> = params
         .files
         .into_iter()
-        .filter_map(|file_rename| {
-            let from = Uri::parse(&file_rename.old_uri).ok()?;
-            let to = Uri::parse(&file_rename.new_uri).ok()?;
-
+        .filter_map(|lsp_types::FileRename { new_uri: to, old_uri: from }| {
             let from_path = from.to_file_path().ok()?;
             let to_path = to.to_file_path().ok()?;
 
@@ -2726,6 +2724,7 @@
                     ResourceOperationKind::Create => "create",
                     ResourceOperationKind::Rename => "rename",
                     ResourceOperationKind::Delete => "delete",
+                    ResourceOperationKind::Custom(_) => unreachable!(),
                 }
             ),
         )
diff --git a/crates/rust-analyzer/src/integrated_benchmarks.rs b/crates/rust-analyzer/src/integrated_benchmarks.rs
index bd3574a..93fa872 100644
--- a/crates/rust-analyzer/src/integrated_benchmarks.rs
+++ b/crates/rust-analyzer/src/integrated_benchmarks.rs
@@ -281,7 +281,6 @@
         prefer_prelude: false,
         prefer_absolute: false,
         term_search_fuel: 400,
-        term_search_borrowck: true,
         show_rename_conflicts: true,
     };
     host.analysis()
diff --git a/crates/rust-analyzer/src/lsp/to_proto.rs b/crates/rust-analyzer/src/lsp/to_proto.rs
index ce71685..7fa3317 100644
--- a/crates/rust-analyzer/src/lsp/to_proto.rs
+++ b/crates/rust-analyzer/src/lsp/to_proto.rs
@@ -183,18 +183,21 @@
 
 pub(crate) fn completion_text_edit(
     line_index: &LineIndex,
-    insert_replace_support: Option<lsp_types::Position>,
+    cursor_position: lsp_types::Position,
+    insert_replace_support: bool,
     indel: Indel,
 ) -> lsp_types::CompletionItemTextEdit {
     let text_edit = text_edit(line_index, indel);
-    match insert_replace_support {
-        Some(cursor_pos) => lsp_types::InsertReplaceEdit {
+    let insert = lsp_types::Range { start: text_edit.range.start, end: cursor_position };
+    if insert_replace_support {
+        lsp_types::InsertReplaceEdit {
             new_text: text_edit.new_text,
-            insert: lsp_types::Range { start: text_edit.range.start, end: cursor_pos },
+            insert,
             replace: text_edit.range,
         }
-        .into(),
-        None => text_edit.into(),
+        .into()
+    } else {
+        lsp_types::TextEdit { range: insert, new_text: text_edit.new_text }.into()
     }
 }
 
@@ -296,7 +299,7 @@
     completion_trigger_character: Option<char>,
     item: CompletionItem,
 ) {
-    let insert_replace_support = config.insert_replace_support().then_some(tdpp.position);
+    let insert_replace_support = config.insert_replace_support();
     let ref_match = item.ref_match();
 
     let mut additional_text_edits = Vec::new();
@@ -321,7 +324,12 @@
             if indel.delete.contains_range(source_range) {
                 // Extract this indel as the main edit
                 text_edit = Some(if indel.delete == source_range {
-                    self::completion_text_edit(line_index, insert_replace_support, indel.clone())
+                    self::completion_text_edit(
+                        line_index,
+                        tdpp.position,
+                        insert_replace_support,
+                        indel.clone(),
+                    )
                 } else {
                     assert!(source_range.end() == indel.delete.end());
                     let range1 = TextRange::new(indel.delete.start(), source_range.start());
@@ -329,7 +337,12 @@
                     let indel1 = Indel::delete(range1);
                     let indel2 = Indel::replace(range2, indel.insert.clone());
                     additional_text_edits.push(self::text_edit(line_index, indel1));
-                    self::completion_text_edit(line_index, insert_replace_support, indel2)
+                    self::completion_text_edit(
+                        line_index,
+                        tdpp.position,
+                        insert_replace_support,
+                        indel2,
+                    )
                 })
             } else {
                 assert!(source_range.intersect(indel.delete).is_none());
diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs
index 3bf3cd5..173409b 100644
--- a/crates/rust-analyzer/src/reload.rs
+++ b/crates/rust-analyzer/src/reload.rs
@@ -901,6 +901,7 @@
                     self.config.default_root_path().clone(),
                     None,
                     None,
+                    None,
                 )]
             }
             crate::flycheck::InvocationStrategy::PerWorkspace => {
@@ -942,21 +943,30 @@
                                 ProjectWorkspaceKind::DetachedFile { .. } => return None,
                             },
                             ws.sysroot.root().map(ToOwned::to_owned),
+                            ws.toolchain.clone(),
                         ))
                     })
-                    .map(|(id, (config_json, root, manifest_path, target_dir), sysroot_root)| {
-                        FlycheckHandle::spawn(
+                    .map(
+                        |(
                             id,
-                            generation.clone(),
-                            sender.clone(),
-                            config.clone(),
-                            config_json,
+                            (config_json, root, manifest_path, target_dir),
                             sysroot_root,
-                            root.to_path_buf(),
-                            manifest_path.map(|it| it.to_path_buf()),
-                            target_dir.map(|it| AsRef::<Utf8Path>::as_ref(it).to_path_buf()),
-                        )
-                    })
+                            toolchain,
+                        )| {
+                            FlycheckHandle::spawn(
+                                id,
+                                generation.clone(),
+                                sender.clone(),
+                                config.clone(),
+                                config_json,
+                                sysroot_root,
+                                root.to_path_buf(),
+                                manifest_path.map(|it| it.to_path_buf()),
+                                target_dir.map(|it| AsRef::<Utf8Path>::as_ref(it).to_path_buf()),
+                                toolchain,
+                            )
+                        },
+                    )
                     .collect()
             }
         }
diff --git a/crates/rust-analyzer/src/test_runner.rs b/crates/rust-analyzer/src/test_runner.rs
index c6f8a7c..62d242f 100644
--- a/crates/rust-analyzer/src/test_runner.rs
+++ b/crates/rust-analyzer/src/test_runner.rs
@@ -108,6 +108,7 @@
         ws_target_dir: Option<&Utf8Path>,
         test_target: TestTarget,
         sender: Sender<CargoTestMessage>,
+        toolchain_version: Option<&semver::Version>,
     ) -> anyhow::Result<Self> {
         let mut cmd = toolchain::command(Tool::Cargo.path(), root, &options.extra_env);
         cmd.env("RUSTC_BOOTSTRAP", "1");
@@ -131,7 +132,12 @@
         cmd.arg("--no-fail-fast");
         cmd.arg("--manifest-path");
         cmd.arg(root.join("Cargo.toml"));
-        options.apply_on_command(&mut cmd, ws_target_dir, Some(&test_target.package));
+        options.apply_on_command(
+            &mut cmd,
+            ws_target_dir,
+            Some(&test_target.package),
+            toolchain_version,
+        );
         cmd.arg("--");
         if let Some(path) = path {
             cmd.arg(path);
diff --git a/crates/rust-analyzer/tests/slow-tests/main.rs b/crates/rust-analyzer/tests/slow-tests/main.rs
index 9edb1df..56629f7 100644
--- a/crates/rust-analyzer/tests/slow-tests/main.rs
+++ b/crates/rust-analyzer/tests/slow-tests/main.rs
@@ -30,7 +30,8 @@
     FileRename, FormattingOptions, HoverParams, HoverRequest, InlayHint, InlayHintParams,
     InlayHintRequest, InlayHintResolveRequest, Label, LanguageKind, PartialResultParams, Position,
     Range, RenameFilesParams, TextDocumentItem, TextDocumentPositionParams, TypeDefinitionParams,
-    TypeDefinitionRequest, WillRenameFilesRequest, WorkDoneProgressParams, WorkspaceSymbolRequest,
+    TypeDefinitionRequest, Uri, WillRenameFilesRequest, WorkDoneProgressParams,
+    WorkspaceSymbolRequest,
 };
 use rust_analyzer::lsp::ext::{OnEnterRequest, RunnablesParams, RunnablesRequest};
 use serde_json::json;
@@ -1304,8 +1305,8 @@
     server.request::<WillRenameFilesRequest>(
         RenameFilesParams {
             files: vec![FileRename {
-                old_uri: base_path.join("src/old_file.rs").to_str().unwrap().to_owned(),
-                new_uri: base_path.join("src/new_file.rs").to_str().unwrap().to_owned(),
+                old_uri: Uri::parse(base_path.join("src/old_file.rs").to_str().unwrap()).unwrap(),
+                new_uri: Uri::parse(base_path.join("src/new_file.rs").to_str().unwrap()).unwrap(),
             }],
         },
         json!({
@@ -1339,8 +1340,10 @@
     server.request::<WillRenameFilesRequest>(
         RenameFilesParams {
             files: vec![FileRename {
-                old_uri: base_path.join("src/from_mod/mod.rs").to_str().unwrap().to_owned(),
-                new_uri: base_path.join("src/from_mod/foo.rs").to_str().unwrap().to_owned(),
+                old_uri: Uri::parse(base_path.join("src/from_mod/mod.rs").to_str().unwrap())
+                    .unwrap(),
+                new_uri: Uri::parse(base_path.join("src/from_mod/foo.rs").to_str().unwrap())
+                    .unwrap(),
             }],
         },
         json!(null),
@@ -1350,8 +1353,8 @@
     server.request::<WillRenameFilesRequest>(
         RenameFilesParams {
             files: vec![FileRename {
-                old_uri: base_path.join("src/to_mod/foo.rs").to_str().unwrap().to_owned(),
-                new_uri: base_path.join("src/to_mod/mod.rs").to_str().unwrap().to_owned(),
+                old_uri: Uri::parse(base_path.join("src/to_mod/foo.rs").to_str().unwrap()).unwrap(),
+                new_uri: Uri::parse(base_path.join("src/to_mod/mod.rs").to_str().unwrap()).unwrap(),
             }],
         },
         json!(null),
@@ -1361,8 +1364,8 @@
     server.request::<WillRenameFilesRequest>(
         RenameFilesParams {
             files: vec![FileRename {
-                old_uri: base_path.join("src/old_folder").to_str().unwrap().to_owned(),
-                new_uri: base_path.join("src/new_folder").to_str().unwrap().to_owned(),
+                old_uri: Uri::parse(base_path.join("src/old_folder").to_str().unwrap()).unwrap(),
+                new_uri: Uri::parse(base_path.join("src/new_folder").to_str().unwrap()).unwrap(),
             }],
         },
         json!({
diff --git a/crates/span/src/ast_id.rs b/crates/span/src/ast_id.rs
index 83a6748..e6b89e7 100644
--- a/crates/span/src/ast_id.rs
+++ b/crates/span/src/ast_id.rs
@@ -59,53 +59,13 @@
 
 impl fmt::Debug for ErasedFileAstId {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        let kind = self.kind();
-        macro_rules! kind {
-            ($($kind:ident),* $(,)?) => {
-                if false {
-                    // Ensure we covered all variants.
-                    match ErasedFileAstIdKind::Root {
-                        $( ErasedFileAstIdKind::$kind => {} )*
-                    }
-                    unreachable!()
-                }
-                $( else if kind == ErasedFileAstIdKind::$kind as u32 {
-                    stringify!($kind)
-                } )*
-                else {
-                    "Unknown"
-                }
-            };
-        }
-        let kind = kind!(
-            Root,
-            Enum,
-            Struct,
-            Union,
-            ExternCrate,
-            MacroDef,
-            MacroRules,
-            Module,
-            Static,
-            Trait,
-            Variant,
-            Const,
-            Fn,
-            MacroCall,
-            TypeAlias,
-            ExternBlock,
-            Use,
-            Impl,
-            BlockExpr,
-            AsmExpr,
-            Fixup,
-            NoDownmap,
-        );
+        let kind =
+            self.kind_typed().map_or_else(|| "Unknown".to_owned(), |kind| format!("{kind:?}"));
         if f.alternate() {
             write!(f, "{kind}[{:04X}, {}]", self.hash_value(), self.index())
         } else {
             f.debug_struct("ErasedFileAstId")
-                .field("kind", &format_args!("{kind}"))
+                .field("kind", &kind)
                 .field("index", &self.index())
                 .field("hash", &format_args!("{:04X}", self.hash_value()))
                 .finish()
@@ -161,6 +121,35 @@
     Root,
 }
 
+impl ErasedFileAstIdKind {
+    fn syntax_kind(self) -> Option<SyntaxKind> {
+        match self {
+            ErasedFileAstIdKind::Enum => Some(SyntaxKind::ENUM),
+            ErasedFileAstIdKind::Struct => Some(SyntaxKind::STRUCT),
+            ErasedFileAstIdKind::Union => Some(SyntaxKind::UNION),
+            ErasedFileAstIdKind::ExternCrate => Some(SyntaxKind::EXTERN_CRATE),
+            ErasedFileAstIdKind::MacroDef => Some(SyntaxKind::MACRO_DEF),
+            ErasedFileAstIdKind::MacroRules => Some(SyntaxKind::MACRO_RULES),
+            ErasedFileAstIdKind::Module => Some(SyntaxKind::MODULE),
+            ErasedFileAstIdKind::Static => Some(SyntaxKind::STATIC),
+            ErasedFileAstIdKind::Trait => Some(SyntaxKind::TRAIT),
+            ErasedFileAstIdKind::Variant => Some(SyntaxKind::VARIANT),
+            ErasedFileAstIdKind::Const => Some(SyntaxKind::CONST),
+            ErasedFileAstIdKind::Fn => Some(SyntaxKind::FN),
+            ErasedFileAstIdKind::MacroCall => Some(SyntaxKind::MACRO_CALL),
+            ErasedFileAstIdKind::TypeAlias => Some(SyntaxKind::TYPE_ALIAS),
+            ErasedFileAstIdKind::ExternBlock => Some(SyntaxKind::EXTERN_BLOCK),
+            ErasedFileAstIdKind::Use => Some(SyntaxKind::USE),
+            ErasedFileAstIdKind::Impl => Some(SyntaxKind::IMPL),
+            ErasedFileAstIdKind::BlockExpr => Some(SyntaxKind::BLOCK_EXPR),
+            ErasedFileAstIdKind::AsmExpr => Some(SyntaxKind::ASM_EXPR),
+            ErasedFileAstIdKind::Fixup
+            | ErasedFileAstIdKind::NoDownmap
+            | ErasedFileAstIdKind::Root => None,
+        }
+    }
+}
+
 // First hash, then index, then kind.
 const HASH_BITS: u32 = 16;
 const INDEX_BITS: u32 = 11;
@@ -206,6 +195,51 @@
         self.0 >> (HASH_BITS + INDEX_BITS)
     }
 
+    fn kind_typed(self) -> Option<ErasedFileAstIdKind> {
+        let kind = self.kind();
+        macro_rules! kind {
+            ($($kind:ident),* $(,)?) => {
+                if false {
+                    // Ensure we covered all variants.
+                    match ErasedFileAstIdKind::Root {
+                        $( ErasedFileAstIdKind::$kind => {} )*
+                    }
+                    unreachable!()
+                }
+                $( else if kind == ErasedFileAstIdKind::$kind as u32 {
+                    Some(ErasedFileAstIdKind::$kind)
+                } )*
+                else {
+                    None
+                }
+            };
+        }
+        kind!(
+            Root,
+            Enum,
+            Struct,
+            Union,
+            ExternCrate,
+            MacroDef,
+            MacroRules,
+            Module,
+            Static,
+            Trait,
+            Variant,
+            Const,
+            Fn,
+            MacroCall,
+            TypeAlias,
+            ExternBlock,
+            Use,
+            Impl,
+            BlockExpr,
+            AsmExpr,
+            Fixup,
+            NoDownmap,
+        )
+    }
+
     #[inline]
     pub fn is_root(self) -> bool {
         self.kind() == ErasedFileAstIdKind::Root as u32
@@ -294,6 +328,20 @@
     }
 
     #[inline]
+    pub fn downcast_unchecked<M: AstIdNode + Into<N>>(self) -> FileAstId<M> {
+        debug_assert!(
+            self.erase()
+                .kind_typed()
+                .and_then(|kind| kind.syntax_kind())
+                .is_some_and(|kind| M::can_cast(kind)),
+            "`FileAstId::<{}>::downcast_unchecked::<{}>()` is invalid",
+            std::any::type_name::<N>(),
+            std::any::type_name::<M>(),
+        );
+        FileAstId { raw: self.raw, _marker: PhantomData }
+    }
+
+    #[inline]
     pub fn erase(self) -> ErasedFileAstId {
         self.raw
     }
@@ -381,8 +429,8 @@
         let self_ty_name = type_as_name(node.self_ty());
         let trait_name = type_as_name(node.trait_());
         let data = ImplFileAstId {
-            self_ty_name: self_ty_name.as_ref().map(|it| it.text_non_mutable()),
-            trait_name: trait_name.as_ref().map(|it| it.text_non_mutable()),
+            self_ty_name: self_ty_name.as_ref().map(|it| it.text()),
+            trait_name: trait_name.as_ref().map(|it| it.text()),
         };
         Some(index_map.new_id(ErasedFileAstIdKind::Impl, data))
     } else {
@@ -473,7 +521,7 @@
                     $(
                         ast::$ident(node) => {
                             let name = node.$name_method();
-                            let name = name.as_ref().map_or("", |it| it.text_non_mutable());
+                            let name = name.as_ref().map_or("", |it| it.text());
                             let result = ErasedHasNameFileAstId {
                                 name,
                             };
@@ -519,7 +567,7 @@
                     $(
                         ast::$ident(node) => {
                             let name = $name_callback(node);
-                            let name = name.as_ref().map_or("", |it| it.text_non_mutable());
+                            let name = name.as_ref().map_or("", |it| it.text());
                             let properties = ErasedHasNameFileAstId {
                                 name,
                             };
diff --git a/crates/span/src/hygiene.rs b/crates/span/src/hygiene.rs
index 70b0447..64ce120 100644
--- a/crates/span/src/hygiene.rs
+++ b/crates/span/src/hygiene.rs
@@ -103,7 +103,9 @@
                 && zalsa_::HashEqLike::<T3>::eq(&self.parent, &data.3)
         }
     }
-    impl zalsa_struct_::Configuration for SyntaxContext {
+
+    // SAFETY: `Self::Fields`, i.e. `SyntaxContextData`, doesn't contain any lifetimes.
+    unsafe impl zalsa_struct_::Configuration for SyntaxContext {
         const LOCATION: salsa::plumbing::Location =
             salsa::plumbing::Location { file: file!(), line: line!() };
         const DEBUG_NAME: &'static str = "SyntaxContextData";
@@ -133,13 +135,9 @@
             static CACHE: zalsa_::IngredientCache<zalsa_struct_::IngredientImpl<SyntaxContext>> =
                 zalsa_::IngredientCache::new();
 
-            // SAFETY: `lookup_jar_by_type` returns a valid ingredient index, and the only
-            // ingredient created by our jar is the struct ingredient.
-            unsafe {
-                CACHE.get_or_create(zalsa, || {
-                    zalsa.lookup_jar_by_type::<zalsa_struct_::JarImpl<SyntaxContext>>()
-                })
-            }
+            // SAFETY: The ingredient at offset 0 in `JarImpl<SyntaxContext>` has type
+            // `IngredientImpl<SyntaxContext>`.
+            unsafe { CACHE.get_or_create::<zalsa_struct_::JarImpl<SyntaxContext>, 0>(zalsa) }
         }
     }
     impl zalsa_::AsId for SyntaxContext {
@@ -193,16 +191,7 @@
         }
     }
 
-    unsafe impl salsa::plumbing::Update for SyntaxContext {
-        unsafe fn maybe_update(old_pointer: *mut Self, new_value: Self) -> bool {
-            if unsafe { *old_pointer } != new_value {
-                unsafe { *old_pointer = new_value };
-                true
-            } else {
-                false
-            }
-        }
-    }
+    unsafe impl salsa::plumbing::SalsaValue for SyntaxContext {}
     impl<'db> SyntaxContext {
         pub fn new<
             Db,
diff --git a/crates/stdx/src/thread.rs b/crates/stdx/src/thread.rs
index 37b7a9f..0000fd1 100644
--- a/crates/stdx/src/thread.rs
+++ b/crates/stdx/src/thread.rs
@@ -34,6 +34,8 @@
     Builder::new(intent, name).spawn(f).expect("failed to spawn thread")
 }
 
+pub const DEFAULT_STACK_SIZE: usize = 16 * 1024 * 1024;
+
 pub struct Builder {
     intent: ThreadIntent,
     inner: jod_thread::Builder,
@@ -43,7 +45,11 @@
 impl Builder {
     #[must_use]
     pub fn new(intent: ThreadIntent, name: impl Into<String>) -> Self {
-        Self { intent, inner: jod_thread::Builder::new().name(name.into()), allow_leak: false }
+        Self {
+            intent,
+            inner: jod_thread::Builder::new().name(name.into()).stack_size(DEFAULT_STACK_SIZE),
+            allow_leak: false,
+        }
     }
 
     #[must_use]
diff --git a/crates/stdx/src/thread/pool.rs b/crates/stdx/src/thread/pool.rs
index 918b88d..1ef6954 100644
--- a/crates/stdx/src/thread/pool.rs
+++ b/crates/stdx/src/thread/pool.rs
@@ -45,7 +45,6 @@
     /// Panics if job panics
     #[must_use]
     pub fn new(threads: usize) -> Self {
-        const STACK_SIZE: usize = 8 * 1024 * 1024;
         const INITIAL_INTENT: ThreadIntent = ThreadIntent::Worker;
 
         let (job_sender, job_receiver) = crossbeam_channel::unbounded();
@@ -54,7 +53,6 @@
         let mut handles = Vec::with_capacity(threads);
         for idx in 0..threads {
             let handle = Builder::new(INITIAL_INTENT, format!("Worker{idx}",))
-                .stack_size(STACK_SIZE)
                 .allow_leak(true)
                 .spawn({
                     let extant_tasks = Arc::clone(&extant_tasks);
diff --git a/crates/syntax/Cargo.toml b/crates/syntax/Cargo.toml
index e65836e..a9df1ac 100644
--- a/crates/syntax/Cargo.toml
+++ b/crates/syntax/Cargo.toml
@@ -15,6 +15,7 @@
 [dependencies]
 either.workspace = true
 itertools.workspace = true
+ra-ap-rustc_lexer.workspace = true
 rowan.workspace = true
 rustc-hash.workspace = true
 rustc-literal-escaper.workspace = true
diff --git a/crates/syntax/rust.ungram b/crates/syntax/rust.ungram
index 05df8b9..7a24b32 100644
--- a/crates/syntax/rust.ungram
+++ b/crates/syntax/rust.ungram
@@ -488,10 +488,7 @@
   ')'
 
 FormatArgsArg =
-  arg_name:FormatArgsArgName? Expr
-
-FormatArgsArgName =
-  '=' // This also has a name, but it's any token and we can't put it here
+  (Name '=')? Expr
 
 MacroExpr =
   MacroCall
diff --git a/crates/syntax/src/ast/edit.rs b/crates/syntax/src/ast/edit.rs
index 080f9a7..852b13f 100644
--- a/crates/syntax/src/ast/edit.rs
+++ b/crates/syntax/src/ast/edit.rs
@@ -360,7 +360,7 @@
             // shorthand `{ x }` → expand to `{ x: expr }`
             let new_field = editor
                 .make()
-                .record_expr_field(editor.make().name_ref(&name_ref.text()), Some(expr));
+                .record_expr_field(editor.make().name_ref(name_ref.text()), Some(expr));
             editor.replace(self.syntax(), new_field.syntax());
         }
     }
diff --git a/crates/syntax/src/ast/generated/nodes.rs b/crates/syntax/src/ast/generated/nodes.rs
index e0992ab..5fa56cf 100644
--- a/crates/syntax/src/ast/generated/nodes.rs
+++ b/crates/syntax/src/ast/generated/nodes.rs
@@ -723,16 +723,10 @@
 pub struct FormatArgsArg {
     pub(crate) syntax: SyntaxNode,
 }
+impl ast::HasName for FormatArgsArg {}
 impl FormatArgsArg {
     #[inline]
-    pub fn arg_name(&self) -> Option<FormatArgsArgName> { support::child(&self.syntax) }
-    #[inline]
     pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) }
-}
-pub struct FormatArgsArgName {
-    pub(crate) syntax: SyntaxNode,
-}
-impl FormatArgsArgName {
     #[inline]
     pub fn eq_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![=]) }
 }
@@ -4105,38 +4099,6 @@
         f.debug_struct("FormatArgsArg").field("syntax", &self.syntax).finish()
     }
 }
-impl AstNode for FormatArgsArgName {
-    #[inline]
-    fn kind() -> SyntaxKind
-    where
-        Self: Sized,
-    {
-        FORMAT_ARGS_ARG_NAME
-    }
-    #[inline]
-    fn can_cast(kind: SyntaxKind) -> bool { kind == FORMAT_ARGS_ARG_NAME }
-    #[inline]
-    fn cast(syntax: SyntaxNode) -> Option<Self> {
-        if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
-    }
-    #[inline]
-    fn syntax(&self) -> &SyntaxNode { &self.syntax }
-}
-impl hash::Hash for FormatArgsArgName {
-    fn hash<H: hash::Hasher>(&self, state: &mut H) { self.syntax.hash(state); }
-}
-impl Eq for FormatArgsArgName {}
-impl PartialEq for FormatArgsArgName {
-    fn eq(&self, other: &Self) -> bool { self.syntax == other.syntax }
-}
-impl Clone for FormatArgsArgName {
-    fn clone(&self) -> Self { Self { syntax: self.syntax.clone() } }
-}
-impl fmt::Debug for FormatArgsArgName {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.debug_struct("FormatArgsArgName").field("syntax", &self.syntax).finish()
-    }
-}
 impl AstNode for FormatArgsExpr {
     #[inline]
     fn kind() -> SyntaxKind
@@ -9810,6 +9772,7 @@
                 | CONST_PARAM
                 | ENUM
                 | FN
+                | FORMAT_ARGS_ARG
                 | IDENT_PAT
                 | MACRO_DEF
                 | MACRO_RULES
@@ -9868,6 +9831,10 @@
     #[inline]
     fn from(node: Fn) -> AnyHasName { AnyHasName { syntax: node.syntax } }
 }
+impl From<FormatArgsArg> for AnyHasName {
+    #[inline]
+    fn from(node: FormatArgsArg) -> AnyHasName { AnyHasName { syntax: node.syntax } }
+}
 impl From<IdentPat> for AnyHasName {
     #[inline]
     fn from(node: IdentPat) -> AnyHasName { AnyHasName { syntax: node.syntax } }
@@ -10434,11 +10401,6 @@
         std::fmt::Display::fmt(self.syntax(), f)
     }
 }
-impl std::fmt::Display for FormatArgsArgName {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        std::fmt::Display::fmt(self.syntax(), f)
-    }
-}
 impl std::fmt::Display for FormatArgsExpr {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         std::fmt::Display::fmt(self.syntax(), f)
diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs
index 1eb658f..672e2fd 100644
--- a/crates/syntax/src/ast/node_ext.rs
+++ b/crates/syntax/src/ast/node_ext.rs
@@ -3,7 +3,7 @@
 //!
 //! These methods should only do simple, shallow tasks related to the syntax of the node itself.
 
-use std::{borrow::Cow, fmt, iter::successors};
+use std::{fmt, iter::successors};
 
 use itertools::Itertools;
 use parser::SyntaxKind;
@@ -11,7 +11,7 @@
 use smallvec::{SmallVec, smallvec};
 
 use crate::{
-    NodeOrToken, SmolStr, SyntaxElement, SyntaxElementChildren, SyntaxToken, T, TokenText,
+    NodeOrToken, SmolStr, SyntaxElement, SyntaxElementChildren, SyntaxToken, T,
     ast::{
         self, AstNode, AstToken, HasAttrs, HasGenericArgs, HasGenericParams, HasName,
         HasTypeBounds, SyntaxNode, support,
@@ -22,41 +22,21 @@
 use super::{GenericParam, RangeItem, RangeOp};
 
 impl ast::Lifetime {
-    pub fn text(&self) -> TokenText<'_> {
+    pub fn text(&self) -> &str {
         text_of_first_token(self.syntax())
     }
 }
 
 impl ast::Name {
-    pub fn text(&self) -> TokenText<'_> {
+    pub fn text(&self) -> &str {
         text_of_first_token(self.syntax())
     }
-    pub fn text_non_mutable(&self) -> &str {
-        fn first_token(green_ref: &GreenNodeData) -> &GreenTokenData {
-            green_ref.children().next().and_then(NodeOrToken::into_token).unwrap()
-        }
-
-        match self.syntax().green() {
-            Cow::Borrowed(green_ref) => first_token(green_ref).text(),
-            Cow::Owned(_) => unreachable!(),
-        }
-    }
 }
 
 impl ast::NameRef {
-    pub fn text(&self) -> TokenText<'_> {
+    pub fn text(&self) -> &str {
         text_of_first_token(self.syntax())
     }
-    pub fn text_non_mutable(&self) -> &str {
-        fn first_token(green_ref: &GreenNodeData) -> &GreenTokenData {
-            green_ref.children().next().and_then(NodeOrToken::into_token).unwrap()
-        }
-
-        match self.syntax().green() {
-            Cow::Borrowed(green_ref) => first_token(green_ref).text(),
-            Cow::Owned(_) => unreachable!(),
-        }
-    }
 
     pub fn as_tuple_field(&self) -> Option<usize> {
         self.text().parse().ok()
@@ -67,15 +47,12 @@
     }
 }
 
-fn text_of_first_token(node: &SyntaxNode) -> TokenText<'_> {
+fn text_of_first_token(node: &SyntaxNode) -> &str {
     fn first_token(green_ref: &GreenNodeData) -> &GreenTokenData {
         green_ref.children().next().and_then(NodeOrToken::into_token).unwrap()
     }
 
-    match node.green() {
-        Cow::Borrowed(green_ref) => TokenText::borrowed(first_token(green_ref).text()),
-        Cow::Owned(green) => TokenText::owned(first_token(&green).to_owned()),
-    }
+    first_token(node.green()).text()
 }
 
 fn into_comma(it: NodeOrToken<SyntaxNode, SyntaxToken>) -> Option<SyntaxToken> {
@@ -619,7 +596,7 @@
             _ => None,
         }
     }
-    pub fn text(&self) -> TokenText<'_> {
+    pub fn text(&self) -> &str {
         match self {
             NameLike::NameRef(name_ref) => name_ref.text(),
             NameLike::Name(name) => name.text(),
@@ -691,7 +668,7 @@
 }
 
 impl NameOrNameRef {
-    pub fn text(&self) -> TokenText<'_> {
+    pub fn text(&self) -> &str {
         match self {
             NameOrNameRef::Name(name) => name.text(),
             NameOrNameRef::NameRef(name_ref) => name_ref.text(),
@@ -1192,15 +1169,6 @@
     }
 }
 
-impl ast::FormatArgsArgName {
-    /// This is not a [`ast::Name`], because the name may be a keyword.
-    pub fn name(&self) -> SyntaxToken {
-        let name = self.syntax.first_token().unwrap();
-        assert!(name.kind().is_any_identifier());
-        name
-    }
-}
-
 impl ast::OrPat {
     pub fn leading_pipe(&self) -> Option<SyntaxToken> {
         self.syntax
diff --git a/crates/syntax/src/ast/token_ext.rs b/crates/syntax/src/ast/token_ext.rs
index b5c4e1a..bb0f53d 100644
--- a/crates/syntax/src/ast/token_ext.rs
+++ b/crates/syntax/src/ast/token_ext.rs
@@ -183,6 +183,16 @@
         let text = &self.text()[text_range_no_quotes - start];
         let offset = text_range_no_quotes.start() - start;
 
+        if self.is_raw() {
+            let mut pos = offset;
+            for c in text.chars() {
+                let len = TextSize::of(c);
+                cb(TextRange::at(pos, len), Ok(c));
+                pos += len;
+            }
+            return;
+        }
+
         self.unescape(text, &mut |range: Range<usize>, unescaped_char| {
             if let Some((s, e)) = range.start.try_into().ok().zip(range.end.try_into().ok()) {
                 cb(TextRange::new(s, e) + offset, unescaped_char);
@@ -285,7 +295,7 @@
 
         match (has_error, buf.capacity() == 0) {
             (Some(e), _) => Err(e),
-            (None, true) => Ok(Cow::Borrowed(text.as_bytes())),
+            (None, true) => Ok(Cow::Borrowed(&text.as_bytes()[..prev_end])),
             (None, false) => Ok(Cow::Owned(buf)),
         }
     }
@@ -641,6 +651,10 @@
             r"a\
 bcde", b"abcde",
         );
+        check_byte_string_value(
+            r"\
+    ", b"",
+        );
     }
 
     #[test]
diff --git a/crates/syntax/src/lib.rs b/crates/syntax/src/lib.rs
index 6146785..dc7d7af 100644
--- a/crates/syntax/src/lib.rs
+++ b/crates/syntax/src/lib.rs
@@ -21,8 +21,12 @@
 
 #![cfg_attr(feature = "in-rust-tree", feature(rustc_private))]
 
+#[cfg(not(feature = "in-rust-tree"))]
+extern crate ra_ap_rustc_lexer as rustc_lexer;
 #[cfg(feature = "in-rust-tree")]
 extern crate rustc_driver as _;
+#[cfg(feature = "in-rust-tree")]
+extern crate rustc_lexer;
 
 mod parsing;
 mod ptr;
@@ -30,7 +34,6 @@
 mod syntax_node;
 #[cfg(test)]
 mod tests;
-mod token_text;
 mod validation;
 
 pub mod algo;
@@ -54,7 +57,6 @@
         PreorderWithTokens, RustLanguage, SyntaxElement, SyntaxElementChildren, SyntaxNode,
         SyntaxNodeChildren, SyntaxToken, SyntaxTreeBuilder,
     },
-    token_text::TokenText,
 };
 pub use parser::{Edition, SyntaxKind, T};
 pub use rowan::{
diff --git a/crates/syntax/src/syntax_editor/edit_algo.rs b/crates/syntax/src/syntax_editor/edit_algo.rs
index d24d9b1..24e7016 100644
--- a/crates/syntax/src/syntax_editor/edit_algo.rs
+++ b/crates/syntax/src/syntax_editor/edit_algo.rs
@@ -449,7 +449,7 @@
 
         let parent = parent_path.resolve(&self.root).and_then(SyntaxElement::into_node).unwrap();
         let green = rowan::GreenNodeData::splice_children(
-            parent.green().as_ref(),
+            parent.green(),
             deleted.clone(),
             inserted.into_iter().map(PreparedElement::into_green),
         );
@@ -466,7 +466,7 @@
         let NodeOrToken::Node(node) = replacement.syntax else {
             panic!("root node replacement should be a node")
         };
-        self.root = SyntaxNode::new_root(node.green().into_owned());
+        self.root = SyntaxNode::new_root(node.green().to_owned());
         self.changed.clear();
         if track_as_changed {
             self.changed.push(SyntaxPath { child_indices: Vec::new() });
@@ -543,7 +543,7 @@
 impl PreparedElement {
     fn into_green(self) -> rowan::NodeOrToken<rowan::GreenNode, rowan::GreenToken> {
         match self.syntax {
-            SyntaxElement::Node(node) => NodeOrToken::Node(node.green().into_owned()),
+            SyntaxElement::Node(node) => NodeOrToken::Node(node.green().to_owned()),
             SyntaxElement::Token(token) => NodeOrToken::Token(token.green().to_owned()),
         }
     }
diff --git a/crates/syntax/src/token_text.rs b/crates/syntax/src/token_text.rs
deleted file mode 100644
index e69deb4..0000000
--- a/crates/syntax/src/token_text.rs
+++ /dev/null
@@ -1,102 +0,0 @@
-//! Yet another version of owned string, backed by a syntax tree token.
-
-use std::{cmp::Ordering, fmt, ops};
-
-use rowan::GreenToken;
-use smol_str::SmolStr;
-
-pub struct TokenText<'a>(pub(crate) Repr<'a>);
-
-pub(crate) enum Repr<'a> {
-    Borrowed(&'a str),
-    Owned(GreenToken),
-}
-
-impl<'a> TokenText<'a> {
-    pub fn borrowed(text: &'a str) -> Self {
-        TokenText(Repr::Borrowed(text))
-    }
-
-    pub(crate) fn owned(green: GreenToken) -> Self {
-        TokenText(Repr::Owned(green))
-    }
-
-    pub fn as_str(&self) -> &str {
-        match &self.0 {
-            &Repr::Borrowed(it) => it,
-            Repr::Owned(green) => green.text(),
-        }
-    }
-}
-
-impl ops::Deref for TokenText<'_> {
-    type Target = str;
-
-    fn deref(&self) -> &str {
-        self.as_str()
-    }
-}
-impl AsRef<str> for TokenText<'_> {
-    fn as_ref(&self) -> &str {
-        self.as_str()
-    }
-}
-
-impl From<TokenText<'_>> for String {
-    fn from(token_text: TokenText<'_>) -> Self {
-        token_text.as_str().into()
-    }
-}
-
-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
-    }
-}
-impl PartialEq<TokenText<'_>> for &'_ str {
-    fn eq(&self, other: &TokenText<'_>) -> bool {
-        other == self
-    }
-}
-impl PartialEq<String> for TokenText<'_> {
-    fn eq(&self, other: &String) -> bool {
-        self.as_str() == other.as_str()
-    }
-}
-impl PartialEq<TokenText<'_>> for String {
-    fn eq(&self, other: &TokenText<'_>) -> bool {
-        other == self
-    }
-}
-impl PartialEq for TokenText<'_> {
-    fn eq(&self, other: &TokenText<'_>) -> bool {
-        self.as_str() == other.as_str()
-    }
-}
-impl Eq for TokenText<'_> {}
-impl Ord for TokenText<'_> {
-    fn cmp(&self, other: &Self) -> Ordering {
-        self.as_str().cmp(other.as_str())
-    }
-}
-impl PartialOrd for TokenText<'_> {
-    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
-        Some(self.cmp(other))
-    }
-}
-impl fmt::Display for TokenText<'_> {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        fmt::Display::fmt(self.as_str(), f)
-    }
-}
-impl fmt::Debug for TokenText<'_> {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        fmt::Debug::fmt(self.as_str(), f)
-    }
-}
diff --git a/crates/syntax/src/utils.rs b/crates/syntax/src/utils.rs
index d1f60f0..9538ae2 100644
--- a/crates/syntax/src/utils.rs
+++ b/crates/syntax/src/utils.rs
@@ -1,6 +1,25 @@
 //! A set of utils methods to reuse on other abstraction levels
 
 use crate::SyntaxKind;
+use rustc_lexer;
+
+#[inline]
+/// Checks that the name is an identifier.
+/// This also means that it is not a strict keyword.
+/// But it may be a weak keyword.
+pub fn is_identifier(name: &str, edition: parser::Edition) -> bool {
+    if rustc_lexer::is_ident(name) {
+        if let Some(syntax_kind) = SyntaxKind::from_keyword(name, edition)
+            && syntax_kind.is_strict_keyword(edition)
+        {
+            false
+        } else {
+            true
+        }
+    } else {
+        false
+    }
+}
 
 #[inline]
 pub fn is_raw_identifier(name: &str, edition: parser::Edition) -> bool {
diff --git a/crates/syntax/src/validation.rs b/crates/syntax/src/validation.rs
index 4622590..acc5d70 100644
--- a/crates/syntax/src/validation.rs
+++ b/crates/syntax/src/validation.rs
@@ -285,11 +285,11 @@
 fn validate_path_keywords(segment: ast::PathSegment, errors: &mut Vec<SyntaxError>) {
     let path = segment.parent_path();
     let is_path_start = segment.coloncolon_token().is_none() && path.qualifier().is_none();
-
     if let Some(token) = segment.self_token() {
-        if !is_path_start {
+        let is_last_segment = path.parent_path().is_none();
+        if !is_path_start && !is_last_segment {
             errors.push(SyntaxError::new(
-                "The `self` keyword is only allowed as the first segment of a path",
+                "The `self` keyword is only allowed at the start or at the end of a path",
                 token.text_range(),
             ));
         }
diff --git a/crates/syntax/src/validation/block.rs b/crates/syntax/src/validation/block.rs
index 1f815f6..a140153 100644
--- a/crates/syntax/src/validation/block.rs
+++ b/crates/syntax/src/validation/block.rs
@@ -9,7 +9,9 @@
 pub(crate) fn validate_block_expr(block: ast::BlockExpr, errors: &mut Vec<SyntaxError>) {
     if let Some(parent) = block.syntax().parent() {
         match parent.kind() {
-            FN | EXPR_STMT | STMT_LIST | MACRO_STMTS => return,
+            FN | EXPR_STMT | STMT_LIST | MACRO_STMTS | LOOP_EXPR | WHILE_EXPR | FOR_EXPR => {
+                return;
+            }
             _ => {}
         }
     }
diff --git a/crates/syntax/test_data/parser/validation/0031_block_inner_attrs.rast b/crates/syntax/test_data/parser/validation/0031_block_inner_attrs.rast
index 5fdde93..f56af1a 100644
--- a/crates/syntax/test_data/parser/validation/0031_block_inner_attrs.rast
+++ b/crates/syntax/test_data/parser/validation/0031_block_inner_attrs.rast
@@ -1,5 +1,5 @@
-SOURCE_FILE@0..350
-  FN@0..349
+SOURCE_FILE@0..611
+  FN@0..610
     FN_KW@0..2 "fn"
     WHITESPACE@2..3 " "
     NAME@3..8
@@ -8,8 +8,8 @@
       L_PAREN@8..9 "("
       R_PAREN@9..10 ")"
     WHITESPACE@10..11 " "
-    BLOCK_EXPR@11..349
-      STMT_LIST@11..349
+    BLOCK_EXPR@11..610
+      STMT_LIST@11..610
         L_CURLY@11..12 "{"
         WHITESPACE@12..17 "\n    "
         LET_STMT@17..129
@@ -90,38 +90,103 @@
                 WHITESPACE@251..256 "\n    "
                 R_CURLY@256..257 "}"
         WHITESPACE@257..262 "\n    "
-        WHILE_EXPR@262..347
-          WHILE_KW@262..267 "while"
-          WHITESPACE@267..268 " "
-          LITERAL@268..272
-            TRUE_KW@268..272 "true"
-          WHITESPACE@272..273 " "
-          BLOCK_EXPR@273..347
-            STMT_LIST@273..347
-              L_CURLY@273..274 "{"
-              WHITESPACE@274..283 "\n        "
-              ATTR@283..302
-                POUND@283..284 "#"
-                BANG@284..285 "!"
-                L_BRACK@285..286 "["
-                TOKEN_TREE_META@286..301
-                  PATH@286..289
-                    PATH_SEGMENT@286..289
-                      NAME_REF@286..289
-                        IDENT@286..289 "doc"
-                  TOKEN_TREE@289..301
-                    L_PAREN@289..290 "("
-                    STRING@290..300 "\"Nor here\""
-                    R_PAREN@300..301 ")"
-                R_BRACK@301..302 "]"
-              WHITESPACE@302..311 "\n        "
-              COMMENT@311..341 "//! Nor are ModuleDoc ..."
-              WHITESPACE@341..346 "\n    "
-              R_CURLY@346..347 "}"
-        WHITESPACE@347..348 "\n"
-        R_CURLY@348..349 "}"
-  WHITESPACE@349..350 "\n"
+        EXPR_STMT@262..347
+          WHILE_EXPR@262..347
+            WHILE_KW@262..267 "while"
+            WHITESPACE@267..268 " "
+            LITERAL@268..272
+              TRUE_KW@268..272 "true"
+            WHITESPACE@272..273 " "
+            BLOCK_EXPR@273..347
+              STMT_LIST@273..347
+                L_CURLY@273..274 "{"
+                WHITESPACE@274..283 "\n        "
+                ATTR@283..302
+                  POUND@283..284 "#"
+                  BANG@284..285 "!"
+                  L_BRACK@285..286 "["
+                  TOKEN_TREE_META@286..301
+                    PATH@286..289
+                      PATH_SEGMENT@286..289
+                        NAME_REF@286..289
+                          IDENT@286..289 "doc"
+                    TOKEN_TREE@289..301
+                      L_PAREN@289..290 "("
+                      STRING@290..300 "\"Nor here\""
+                      R_PAREN@300..301 ")"
+                  R_BRACK@301..302 "]"
+                WHITESPACE@302..311 "\n        "
+                COMMENT@311..341 "//! Nor are ModuleDoc ..."
+                WHITESPACE@341..346 "\n    "
+                R_CURLY@346..347 "}"
+        WHITESPACE@347..353 "\n     "
+        EXPR_STMT@353..474
+          LOOP_EXPR@353..474
+            LOOP_KW@353..357 "loop"
+            WHITESPACE@357..358 " "
+            BLOCK_EXPR@358..474
+              STMT_LIST@358..474
+                L_CURLY@358..359 "{"
+                WHITESPACE@359..368 "\n        "
+                ATTR@368..430
+                  POUND@368..369 "#"
+                  BANG@369..370 "!"
+                  L_BRACK@370..371 "["
+                  TOKEN_TREE_META@371..429
+                    PATH@371..374
+                      PATH_SEGMENT@371..374
+                        NAME_REF@371..374
+                          IDENT@371..374 "doc"
+                    TOKEN_TREE@374..429
+                      L_PAREN@374..375 "("
+                      STRING@375..428 "\"This is fine, `loop` ..."
+                      R_PAREN@428..429 ")"
+                  R_BRACK@429..430 "]"
+                WHITESPACE@430..439 "\n        "
+                COMMENT@439..468 "//! So are ModuleDoc  ..."
+                WHITESPACE@468..473 "\n    "
+                R_CURLY@473..474 "}"
+        WHITESPACE@474..479 "\n    "
+        FOR_EXPR@479..608
+          FOR_KW@479..482 "for"
+          WHITESPACE@482..483 " "
+          WILDCARD_PAT@483..484
+            UNDERSCORE@483..484 "_"
+          WHITESPACE@484..485 " "
+          IN_KW@485..487 "in"
+          WHITESPACE@487..488 " "
+          RANGE_EXPR@488..492
+            LITERAL@488..489
+              INT_NUMBER@488..489 "0"
+            DOT2@489..491 ".."
+            LITERAL@491..492
+              INT_NUMBER@491..492 "1"
+          WHITESPACE@492..493 " "
+          BLOCK_EXPR@493..608
+            STMT_LIST@493..608
+              L_CURLY@493..494 "{"
+              WHITESPACE@494..503 "\n        "
+              ATTR@503..564
+                POUND@503..504 "#"
+                BANG@504..505 "!"
+                L_BRACK@505..506 "["
+                TOKEN_TREE_META@506..563
+                  PATH@506..509
+                    PATH_SEGMENT@506..509
+                      NAME_REF@506..509
+                        IDENT@506..509 "doc"
+                  TOKEN_TREE@509..563
+                    L_PAREN@509..510 "("
+                    STRING@510..562 "\"This is fine, `for`  ..."
+                    R_PAREN@562..563 ")"
+                R_BRACK@563..564 "]"
+              WHITESPACE@564..573 "\n        "
+              COMMENT@573..602 "//! So are ModuleDoc  ..."
+              WHITESPACE@602..607 "\n    "
+              R_CURLY@607..608 "}"
+        WHITESPACE@608..609 "\n"
+        R_CURLY@609..610 "}"
+  WHITESPACE@610..611 "\n"
 error 39..83: A block in this position cannot accept inner attributes
 error 152..171: A block in this position cannot accept inner attributes
 error 180..212: A block in this position cannot accept inner attributes
-error 283..302: A block in this position cannot accept inner attributes
diff --git a/crates/syntax/test_data/parser/validation/0031_block_inner_attrs.rs b/crates/syntax/test_data/parser/validation/0031_block_inner_attrs.rs
index 6a04f2d..65bb7e1 100644
--- a/crates/syntax/test_data/parser/validation/0031_block_inner_attrs.rs
+++ b/crates/syntax/test_data/parser/validation/0031_block_inner_attrs.rs
@@ -12,4 +12,12 @@
         #![doc("Nor here")]
         //! Nor are ModuleDoc comments
     }
+     loop {
+        #![doc("This is fine, `loop` bodies accept inner attributes")]
+        //! So are ModuleDoc comments
+    }
+    for _ in 0..1 {
+        #![doc("This is fine, `for` bodies accept inner attributes")]
+        //! So are ModuleDoc comments
+    }
 }
diff --git a/crates/syntax/test_data/parser/validation/0041_illegal_self_keyword_location.rast b/crates/syntax/test_data/parser/validation/0041_illegal_self_keyword_location.rast
index 01f6010..92f7469 100644
--- a/crates/syntax/test_data/parser/validation/0041_illegal_self_keyword_location.rast
+++ b/crates/syntax/test_data/parser/validation/0041_illegal_self_keyword_location.rast
@@ -1,4 +1,4 @@
-SOURCE_FILE@0..25
+SOURCE_FILE@0..28
   USE@0..11
     USE_KW@0..3 "use"
     WHITESPACE@3..4 " "
@@ -10,20 +10,24 @@
             SELF_KW@6..10 "self"
     SEMICOLON@10..11 ";"
   WHITESPACE@11..12 "\n"
-  USE@12..24
+  USE@12..27
     USE_KW@12..15 "use"
     WHITESPACE@15..16 " "
-    USE_TREE@16..23
-      PATH@16..23
-        PATH@16..17
-          PATH_SEGMENT@16..17
-            NAME_REF@16..17
-              IDENT@16..17 "a"
-        COLON2@17..19 "::"
-        PATH_SEGMENT@19..23
-          NAME_REF@19..23
-            SELF_KW@19..23 "self"
-    SEMICOLON@23..24 ";"
-  WHITESPACE@24..25 "\n"
-error 6..10: The `self` keyword is only allowed as the first segment of a path
-error 19..23: The `self` keyword is only allowed as the first segment of a path
+    USE_TREE@16..26
+      PATH@16..26
+        PATH@16..23
+          PATH@16..17
+            PATH_SEGMENT@16..17
+              NAME_REF@16..17
+                IDENT@16..17 "a"
+          COLON2@17..19 "::"
+          PATH_SEGMENT@19..23
+            NAME_REF@19..23
+              SELF_KW@19..23 "self"
+        COLON2@23..25 "::"
+        PATH_SEGMENT@25..26
+          NAME_REF@25..26
+            IDENT@25..26 "b"
+    SEMICOLON@26..27 ";"
+  WHITESPACE@27..28 "\n"
+error 19..23: The `self` keyword is only allowed at the start or at the end of a path
diff --git a/crates/syntax/test_data/parser/validation/0041_illegal_self_keyword_location.rs b/crates/syntax/test_data/parser/validation/0041_illegal_self_keyword_location.rs
index b9e1d7d..9a9429c 100644
--- a/crates/syntax/test_data/parser/validation/0041_illegal_self_keyword_location.rs
+++ b/crates/syntax/test_data/parser/validation/0041_illegal_self_keyword_location.rs
@@ -1,2 +1,2 @@
 use ::self;
-use a::self;
+use a::self::b;
diff --git a/crates/toolchain/Cargo.toml b/crates/toolchain/Cargo.toml
index d0d5840..8ba63ae 100644
--- a/crates/toolchain/Cargo.toml
+++ b/crates/toolchain/Cargo.toml
@@ -14,6 +14,7 @@
 
 [dependencies]
 camino.workspace = true
+semver.workspace = true
 
 [lints]
 workspace = true
diff --git a/crates/toolchain/src/lib.rs b/crates/toolchain/src/lib.rs
index ea08f7a..6bed98f 100644
--- a/crates/toolchain/src/lib.rs
+++ b/crates/toolchain/src/lib.rs
@@ -143,3 +143,33 @@
     };
     iter::once(path).chain(with_extension).find(|it| it.is_file())
 }
+
+pub const MINIMUM_TOOLCHAIN_VERSION_REQUIRING_JSON_TARGET_SPEC_FLAG: semver::Version =
+    semver::Version {
+        major: 1,
+        minor: 95,
+        patch: 0,
+        pre: semver::Prerelease::EMPTY,
+        build: semver::BuildMetadata::EMPTY,
+    };
+
+/// Uses targets in a Cargo process.
+pub fn cargo_use_targets(
+    toolchain_version: Option<&semver::Version>,
+    cmd: &mut Command,
+    targets: impl IntoIterator<Item = impl AsRef<str>>,
+) {
+    let mut has_json_target = false;
+    for target in targets {
+        let target = target.as_ref();
+        cmd.args(["--target", target]);
+        has_json_target |= target.ends_with(".json");
+    }
+    if has_json_target
+        && toolchain_version.is_some_and(|version| {
+            *version >= MINIMUM_TOOLCHAIN_VERSION_REQUIRING_JSON_TARGET_SPEC_FLAG
+        })
+    {
+        cmd.arg("-Zjson-target-spec");
+    }
+}
diff --git a/docs/book/src/configuration_generated.md b/docs/book/src/configuration_generated.md
index 4df17d7..1c46427 100644
--- a/docs/book/src/configuration_generated.md
+++ b/docs/book/src/configuration_generated.md
@@ -19,14 +19,6 @@
 Prefer to use `Self` over the type name when inserting a type (e.g. in "fill match arms" assist).
 
 
-## rust-analyzer.assist.termSearch.borrowcheck {#assist.termSearch.borrowcheck}
-
-Default: `true`
-
-Enable borrow checking for term search code assists. If set to false, also there will be
-more suggestions, but some of them may not borrow-check.
-
-
 ## rust-analyzer.assist.termSearch.fuel {#assist.termSearch.fuel}
 
 Default: `1800`
diff --git a/editors/code/package.json b/editors/code/package.json
index d152cfb..de9f17b 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -816,16 +816,6 @@
             {
                 "title": "Assist",
                 "properties": {
-                    "rust-analyzer.assist.termSearch.borrowcheck": {
-                        "markdownDescription": "Enable borrow checking for term search code assists. If set to false, also there will be\nmore suggestions, but some of them may not borrow-check.",
-                        "default": true,
-                        "type": "boolean"
-                    }
-                }
-            },
-            {
-                "title": "Assist",
-                "properties": {
                     "rust-analyzer.assist.termSearch.fuel": {
                         "markdownDescription": "Term search fuel in \"units of work\" for assists (Defaults to 1800).",
                         "default": 1800,
@@ -914,7 +904,7 @@
                         ],
                         "enumDescriptions": [
                             "The command will be executed for each Rust workspace with the workspace as the working directory.",
-                            "The command will be executed once with the opened project as the working directory."
+                            "The command will be executed once with the opened project as the working directory.\n\n**Warning:** This can cause errors when the Cargo configuration of the opened workspaces is different or even only different than the default. Enabling this is not recommended."
                         ]
                     }
                 }
@@ -1247,7 +1237,7 @@
                         ],
                         "enumDescriptions": [
                             "The command will be executed for each Rust workspace with the workspace as the working directory.",
-                            "The command will be executed once with the opened project as the working directory."
+                            "The command will be executed once with the opened project as the working directory.\n\n**Warning:** This can cause errors when the Cargo configuration of the opened workspaces is different or even only different than the default. Enabling this is not recommended."
                         ]
                     }
                 }
diff --git a/editors/code/src/bootstrap.ts b/editors/code/src/bootstrap.ts
index ca5b7e3..440f21c 100644
--- a/editors/code/src/bootstrap.ts
+++ b/editors/code/src/bootstrap.ts
@@ -176,14 +176,24 @@
     );
 }
 
+// Captures the elements of a `components` array. They are matched with `[^\]]` rather than `.`
+// so that the array may be spread over several lines, which is just as valid TOML as keeping it
+// on one, while still stopping at the end of the array.
+const COMPONENTS_RE = /components\s*=\s*\[(?<components>[^\]]*)\]/;
+// TOML strings come in both quote flavours.
+const RA_COMPONENT_RE = /["']rust-analyzer["']/;
+
+function declaresRaComponent(toolchainFileContents: string): boolean {
+    const components = toolchainFileContents.match(COMPONENTS_RE)?.groups?.["components"];
+    return components !== undefined && RA_COMPONENT_RE.test(components);
+}
+
 async function hasToolchainFileWithRaDeclared(uri: vscode.Uri): Promise<boolean> {
     try {
         const toolchainFileContents = new TextDecoder().decode(
             await vscode.workspace.fs.readFile(uri),
         );
-        return (
-            toolchainFileContents.match(/components\s*=\s*\[.*"rust-analyzer".*\]/g)?.length === 1
-        );
+        return declaresRaComponent(toolchainFileContents);
     } catch (_) {
         return false;
     }
@@ -296,6 +306,7 @@
 }
 
 export const _private = {
+    declaresRaComponent,
     earliestToolchainPath,
     orderFromPath,
 };
diff --git a/editors/code/tests/unit/bootstrap.test.ts b/editors/code/tests/unit/bootstrap.test.ts
index baabf4f..259428d 100644
--- a/editors/code/tests/unit/bootstrap.test.ts
+++ b/editors/code/tests/unit/bootstrap.test.ts
@@ -93,4 +93,75 @@
             );
         });
     });
+
+    await ctx.suite("Bootstrap/Detect RA component in toolchain file", (suite) => {
+        suite.addTest("Single line components array", async () => {
+            assert.ok(
+                _private.declaresRaComponent(
+                    `[toolchain]
+channel = "1.88"
+components = ["cargo", "rust-analyzer", "rustfmt"]
+`,
+                ),
+            );
+        });
+
+        suite.addTest("Multi line components array", async () => {
+            assert.ok(
+                _private.declaresRaComponent(
+                    `[toolchain]
+channel = "1.88"
+components = [
+    "cargo",
+    "rust-analyzer",
+    "rustfmt",
+]
+profile = "default"
+`,
+                ),
+            );
+        });
+
+        suite.addTest("Components array with literal strings", async () => {
+            assert.ok(_private.declaresRaComponent(`components = ['cargo', 'rust-analyzer']`));
+        });
+
+        suite.addTest("Components array without RA", async () => {
+            assert.ok(
+                !_private.declaresRaComponent(
+                    `[toolchain]
+channel = "1.88"
+components = [
+    "cargo",
+    "rustfmt",
+]
+`,
+                ),
+            );
+        });
+
+        suite.addTest("No components array", async () => {
+            assert.ok(
+                !_private.declaresRaComponent(
+                    `[toolchain]
+channel = "1.88"
+`,
+                ),
+            );
+        });
+
+        suite.addTest("RA mentioned outside the components array", async () => {
+            assert.ok(
+                !_private.declaresRaComponent(
+                    `[toolchain]
+channel = "1.88"
+components = [
+    "cargo",
+]
+# add "rust-analyzer" here to use the matching server
+`,
+                ),
+            );
+        });
+    });
 }
diff --git a/lib/lsp-server/Cargo.toml b/lib/lsp-server/Cargo.toml
index 1a179ef..4913d6b 100644
--- a/lib/lsp-server/Cargo.toml
+++ b/lib/lsp-server/Cargo.toml
@@ -14,7 +14,7 @@
 crossbeam-channel.workspace = true
 
 [dev-dependencies]
-lsp-types = { version = "0.10.0", package = "gen-lsp-types", features = [
+lsp-types = { version = "0.11.0", package = "gen-lsp-types", features = [
     "url",
 ] }
 ctrlc = "3.4.7"