about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/body.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/data.rs14
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/db.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/find_path.rs143
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/lib.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs10
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs4
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/display.rs8
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer.rs164
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs6
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs4
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/layout.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/lib.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/lower.rs6
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs4
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests.rs1
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs113
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/type_alias_impl_traits.rs161
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/utils.rs7
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/display.rs8
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/lib.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions.rs8
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs14
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs21
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs8
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/config.rs9
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/lib.rs7
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/render.rs8
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs7
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs24
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs2
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/diff.rs53
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs5
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/dispatch.rs (renamed from src/tools/rust-analyzer/crates/rust-analyzer/src/dispatch.rs)0
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs2
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs45
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs6
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/lsp.rs2
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/capabilities.rs (renamed from src/tools/rust-analyzer/crates/rust-analyzer/src/capabilities.rs)0
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs3
42 files changed, 522 insertions, 373 deletions
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body.rs b/src/tools/rust-analyzer/crates/hir-def/src/body.rs
index 58812479ddf..a988317e046 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/body.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/body.rs
@@ -158,7 +158,7 @@ impl Body {
                             }),
                         )
                     });
-                    is_async_fn = data.has_async_kw();
+                    is_async_fn = data.is_async();
                     src.map(|it| it.body().map(ast::Expr::from))
                 }
                 DefWithBodyId::ConstId(c) => {
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/data.rs b/src/tools/rust-analyzer/crates/hir-def/src/data.rs
index c3c2e51fd03..d17ebd7ff92 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/data.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/data.rs
@@ -94,6 +94,12 @@ impl FunctionData {
             .filter(|it| !it.is_empty())
             .map(Box::new);
         let rustc_allow_incoherent_impl = attrs.by_key(&sym::rustc_allow_incoherent_impl).exists();
+        if flags.contains(FnFlags::HAS_UNSAFE_KW)
+            && !crate_graph[krate].edition.at_least_2024()
+            && attrs.by_key(&sym::rustc_deprecated_safe_2024).exists()
+        {
+            flags.remove(FnFlags::HAS_UNSAFE_KW);
+        }
 
         Arc::new(FunctionData {
             name: func.name.clone(),
@@ -126,19 +132,19 @@ impl FunctionData {
         self.flags.contains(FnFlags::HAS_SELF_PARAM)
     }
 
-    pub fn has_default_kw(&self) -> bool {
+    pub fn is_default(&self) -> bool {
         self.flags.contains(FnFlags::HAS_DEFAULT_KW)
     }
 
-    pub fn has_const_kw(&self) -> bool {
+    pub fn is_const(&self) -> bool {
         self.flags.contains(FnFlags::HAS_CONST_KW)
     }
 
-    pub fn has_async_kw(&self) -> bool {
+    pub fn is_async(&self) -> bool {
         self.flags.contains(FnFlags::HAS_ASYNC_KW)
     }
 
-    pub fn has_unsafe_kw(&self) -> bool {
+    pub fn is_unsafe(&self) -> bool {
         self.flags.contains(FnFlags::HAS_UNSAFE_KW)
     }
 
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/db.rs b/src/tools/rust-analyzer/crates/hir-def/src/db.rs
index 56feb0163e1..9ba99788fb2 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/db.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/db.rs
@@ -160,7 +160,7 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast<dyn ExpandDataba
     fn const_data(&self, konst: ConstId) -> Arc<ConstData>;
 
     #[salsa::invoke(StaticData::static_data_query)]
-    fn static_data(&self, konst: StaticId) -> Arc<StaticData>;
+    fn static_data(&self, statik: StaticId) -> Arc<StaticData>;
 
     #[salsa::invoke(Macro2Data::macro2_data_query)]
     fn macro2_data(&self, makro: Macro2Id) -> Arc<Macro2Data>;
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs b/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs
index 91594aecd04..5a3a3e91897 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs
@@ -50,13 +50,13 @@ pub fn find_path(
             prefix: prefix_kind,
             cfg,
             ignore_local_imports,
+            is_std_item: db.crate_graph()[item_module.krate()].origin.is_lang(),
             from,
             from_def_map: &from.def_map(db),
             fuel: Cell::new(FIND_PATH_FUEL),
         },
         item,
         MAX_PATH_LEN,
-        db.crate_graph()[item_module.krate()].origin.is_lang(),
     )
 }
 
@@ -98,20 +98,16 @@ struct FindPathCtx<'db> {
     prefix: PrefixKind,
     cfg: ImportPathConfig,
     ignore_local_imports: bool,
+    is_std_item: bool,
     from: ModuleId,
     from_def_map: &'db DefMap,
     fuel: Cell<usize>,
 }
 
 /// Attempts to find a path to refer to the given `item` visible from the `from` ModuleId
-fn find_path_inner(
-    ctx: &FindPathCtx<'_>,
-    item: ItemInNs,
-    max_len: usize,
-    is_std_item: bool,
-) -> Option<ModPath> {
+fn find_path_inner(ctx: &FindPathCtx<'_>, item: ItemInNs, max_len: usize) -> Option<ModPath> {
     // - if the item is a module, jump straight to module search
-    if !is_std_item {
+    if !ctx.is_std_item {
         if let ItemInNs::Types(ModuleDefId::ModuleId(module_id)) = item {
             return find_path_for_module(ctx, &mut FxHashSet::default(), module_id, true, max_len)
                 .map(|choice| choice.path);
@@ -138,12 +134,9 @@ fn find_path_inner(
 
     if let Some(ModuleDefId::EnumVariantId(variant)) = item.as_module_def_id() {
         // - if the item is an enum variant, refer to it via the enum
-        if let Some(mut path) = find_path_inner(
-            ctx,
-            ItemInNs::Types(variant.lookup(ctx.db).parent.into()),
-            max_len,
-            is_std_item,
-        ) {
+        if let Some(mut path) =
+            find_path_inner(ctx, ItemInNs::Types(variant.lookup(ctx.db).parent.into()), max_len)
+        {
             path.push_segment(ctx.db.enum_variant_data(variant).name.clone());
             return Some(path);
         }
@@ -152,16 +145,6 @@ fn find_path_inner(
         // variant somewhere
     }
 
-    if is_std_item {
-        // The item we are searching for comes from the sysroot libraries, so skip prefer looking in
-        // the sysroot libraries directly.
-        // We do need to fallback as the item in question could be re-exported by another crate
-        // while not being a transitive dependency of the current crate.
-        if let Some(choice) = find_in_sysroot(ctx, &mut FxHashSet::default(), item, max_len) {
-            return Some(choice.path);
-        }
-    }
-
     let mut best_choice = None;
     calculate_best_path(ctx, &mut FxHashSet::default(), item, max_len, &mut best_choice);
     best_choice.map(|choice| choice.path)
@@ -366,6 +349,12 @@ fn calculate_best_path(
         // Item was defined in the same crate that wants to import it. It cannot be found in any
         // dependency in this case.
         calculate_best_path_local(ctx, visited_modules, item, max_len, best_choice)
+    } else if ctx.is_std_item {
+        // The item we are searching for comes from the sysroot libraries, so skip prefer looking in
+        // the sysroot libraries directly.
+        // We do need to fallback as the item in question could be re-exported by another crate
+        // while not being a transitive dependency of the current crate.
+        find_in_sysroot(ctx, visited_modules, item, max_len, best_choice)
     } else {
         // Item was defined in some upstream crate. This means that it must be exported from one,
         // too (unless we can't name it at all). It could *also* be (re)exported by the same crate
@@ -382,10 +371,10 @@ fn find_in_sysroot(
     visited_modules: &mut FxHashSet<(ItemInNs, ModuleId)>,
     item: ItemInNs,
     max_len: usize,
-) -> Option<Choice> {
+    best_choice: &mut Option<Choice>,
+) {
     let crate_graph = ctx.db.crate_graph();
     let dependencies = &crate_graph[ctx.from.krate].dependencies;
-    let mut best_choice = None;
     let mut search = |lang, best_choice: &mut _| {
         if let Some(dep) = dependencies.iter().filter(|it| it.is_sysroot()).find(|dep| {
             match crate_graph[dep.crate_id].origin {
@@ -397,29 +386,31 @@ fn find_in_sysroot(
         }
     };
     if ctx.cfg.prefer_no_std {
-        search(LangCrateOrigin::Core, &mut best_choice);
+        search(LangCrateOrigin::Core, best_choice);
         if matches!(best_choice, Some(Choice { stability: Stable, .. })) {
-            return best_choice;
+            return;
         }
-        search(LangCrateOrigin::Std, &mut best_choice);
+        search(LangCrateOrigin::Std, best_choice);
         if matches!(best_choice, Some(Choice { stability: Stable, .. })) {
-            return best_choice;
+            return;
         }
     } else {
-        search(LangCrateOrigin::Std, &mut best_choice);
+        search(LangCrateOrigin::Std, best_choice);
         if matches!(best_choice, Some(Choice { stability: Stable, .. })) {
-            return best_choice;
+            return;
         }
-        search(LangCrateOrigin::Core, &mut best_choice);
+        search(LangCrateOrigin::Core, best_choice);
         if matches!(best_choice, Some(Choice { stability: Stable, .. })) {
-            return best_choice;
+            return;
         }
     }
-    let mut best_choice = None;
-    dependencies.iter().filter(|it| it.is_sysroot()).for_each(|dep| {
-        find_in_dep(ctx, visited_modules, item, max_len, &mut best_choice, dep.crate_id);
-    });
-    best_choice
+    dependencies
+        .iter()
+        .filter(|it| it.is_sysroot())
+        .chain(dependencies.iter().filter(|it| !it.is_sysroot()))
+        .for_each(|dep| {
+            find_in_dep(ctx, visited_modules, item, max_len, best_choice, dep.crate_id);
+        });
 }
 
 fn find_in_dep(
@@ -491,6 +482,7 @@ fn calculate_best_path_local(
     );
 }
 
+#[derive(Debug)]
 struct Choice {
     path: ModPath,
     /// The length in characters of the path
@@ -676,6 +668,7 @@ mod tests {
         path: &str,
         prefer_prelude: bool,
         prefer_absolute: bool,
+        prefer_no_std: bool,
         expect: Expect,
     ) {
         let (db, pos) = TestDB::with_position(ra_fixture);
@@ -717,7 +710,7 @@ mod tests {
                 module,
                 prefix,
                 ignore_local_imports,
-                ImportPathConfig { prefer_no_std: false, prefer_prelude, prefer_absolute },
+                ImportPathConfig { prefer_no_std, prefer_prelude, prefer_absolute },
             );
             format_to!(
                 res,
@@ -732,15 +725,19 @@ mod tests {
     }
 
     fn check_found_path(ra_fixture: &str, path: &str, expect: Expect) {
-        check_found_path_(ra_fixture, path, false, false, expect);
+        check_found_path_(ra_fixture, path, false, false, false, expect);
     }
 
     fn check_found_path_prelude(ra_fixture: &str, path: &str, expect: Expect) {
-        check_found_path_(ra_fixture, path, true, false, expect);
+        check_found_path_(ra_fixture, path, true, false, false, expect);
     }
 
     fn check_found_path_absolute(ra_fixture: &str, path: &str, expect: Expect) {
-        check_found_path_(ra_fixture, path, false, true, expect);
+        check_found_path_(ra_fixture, path, false, true, false, expect);
+    }
+
+    fn check_found_path_prefer_no_std(ra_fixture: &str, path: &str, expect: Expect) {
+        check_found_path_(ra_fixture, path, false, false, true, expect);
     }
 
     #[test]
@@ -1361,9 +1358,66 @@ pub mod sync {
             "#]],
         );
     }
+    #[test]
+    fn prefer_core_paths_over_std_for_mod_reexport() {
+        check_found_path_prefer_no_std(
+            r#"
+//- /main.rs crate:main deps:core,std
+
+$0
+
+//- /stdlib.rs crate:std deps:core
+
+pub use core::pin;
+
+//- /corelib.rs crate:core
+
+pub mod pin {
+    pub struct Pin;
+}
+            "#,
+            "std::pin::Pin",
+            expect![[r#"
+                Plain  (imports ✔): core::pin::Pin
+                Plain  (imports ✖): core::pin::Pin
+                ByCrate(imports ✔): core::pin::Pin
+                ByCrate(imports ✖): core::pin::Pin
+                BySelf (imports ✔): core::pin::Pin
+                BySelf (imports ✖): core::pin::Pin
+            "#]],
+        );
+    }
 
     #[test]
     fn prefer_core_paths_over_std() {
+        check_found_path_prefer_no_std(
+            r#"
+//- /main.rs crate:main deps:core,std
+
+$0
+
+//- /std.rs crate:std deps:core
+
+pub mod fmt {
+    pub use core::fmt::Error;
+}
+
+//- /zzz.rs crate:core
+
+pub mod fmt {
+    pub struct Error;
+}
+        "#,
+            "core::fmt::Error",
+            expect![[r#"
+                Plain  (imports ✔): core::fmt::Error
+                Plain  (imports ✖): core::fmt::Error
+                ByCrate(imports ✔): core::fmt::Error
+                ByCrate(imports ✖): core::fmt::Error
+                BySelf (imports ✔): core::fmt::Error
+                BySelf (imports ✖): core::fmt::Error
+            "#]],
+        );
         check_found_path(
             r#"
 //- /main.rs crate:main deps:core,std
@@ -1878,10 +1932,9 @@ pub mod ops {
 
     #[test]
     fn respect_unstable_modules() {
-        check_found_path(
+        check_found_path_prefer_no_std(
             r#"
 //- /main.rs crate:main deps:std,core
-#![no_std]
 extern crate std;
 $0
 //- /longer.rs crate:std deps:core
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs
index 66412b26a00..4ced30c81dc 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs
@@ -105,7 +105,7 @@ use crate::{
 
 type FxIndexMap<K, V> =
     indexmap::IndexMap<K, V, std::hash::BuildHasherDefault<rustc_hash::FxHasher>>;
-/// A wrapper around two booleans, [`ImportPathConfig::prefer_no_std`] and [`ImportPathConfig::prefer_prelude`].
+/// A wrapper around three booleans
 #[derive(Debug, Clone, PartialEq, Eq, Hash, Copy)]
 pub struct ImportPathConfig {
     /// If true, prefer to unconditionally use imports of the `core` and `alloc` crate
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs
index d970dbac1c2..debc5a44326 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs
@@ -56,7 +56,6 @@ use crate::{
 };
 
 static GLOB_RECURSION_LIMIT: Limit = Limit::new(100);
-static EXPANSION_DEPTH_LIMIT: Limit = Limit::new(128);
 static FIXED_POINT_LIMIT: Limit = Limit::new(8192);
 
 pub(super) fn collect_defs(db: &dyn DefDatabase, def_map: DefMap, tree_id: TreeId) -> DefMap {
@@ -1440,7 +1439,14 @@ impl DefCollector<'_> {
         depth: usize,
         container: ItemContainerId,
     ) {
-        if EXPANSION_DEPTH_LIMIT.check(depth).is_err() {
+        let recursion_limit = self.def_map.recursion_limit() as usize;
+        let recursion_limit = Limit::new(if cfg!(test) {
+            // Without this, `body::tests::your_stack_belongs_to_me` stack-overflows in debug
+            std::cmp::min(32, recursion_limit)
+        } else {
+            recursion_limit
+        });
+        if recursion_limit.check(depth).is_err() {
             cov_mark::hit!(macro_expansion_overflow);
             tracing::warn!("macro expansion is too deep");
             return;
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs
index d506e00ca12..a151ee01e64 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs
@@ -275,7 +275,7 @@ impl chalk_solve::RustIrDatabase<Interner> for ChalkContext<'_> {
                 };
                 chalk_ir::Binders::new(binders, bound)
             }
-            crate::ImplTraitId::AssociatedTypeImplTrait(alias, idx) => {
+            crate::ImplTraitId::TypeAliasImplTrait(alias, idx) => {
                 let datas = self
                     .db
                     .type_alias_impl_traits(alias)
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs
index 5765262b08b..302558162ac 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs
@@ -276,7 +276,7 @@ impl TyExt for Ty {
                             data.substitute(Interner, &subst).into_value_and_skipped_binders().0
                         })
                     }
-                    ImplTraitId::AssociatedTypeImplTrait(alias, idx) => {
+                    ImplTraitId::TypeAliasImplTrait(alias, idx) => {
                         db.type_alias_impl_traits(alias).map(|it| {
                             let data =
                                 (*it).as_ref().map(|rpit| rpit.impl_traits[idx].bounds.clone());
@@ -295,7 +295,7 @@ impl TyExt for Ty {
                             data.substitute(Interner, &opaque_ty.substitution)
                         })
                     }
-                    ImplTraitId::AssociatedTypeImplTrait(alias, idx) => {
+                    ImplTraitId::TypeAliasImplTrait(alias, idx) => {
                         db.type_alias_impl_traits(alias).map(|it| {
                             let data =
                                 (*it).as_ref().map(|rpit| rpit.impl_traits[idx].bounds.clone());
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs
index 22aa5c69bb0..3f54cdd20ce 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs
@@ -17,7 +17,7 @@ pub fn missing_unsafe(db: &dyn HirDatabase, def: DefWithBodyId) -> Vec<ExprId> {
 
     let mut res = Vec::new();
     let is_unsafe = match def {
-        DefWithBodyId::FunctionId(it) => db.function_data(it).has_unsafe_kw(),
+        DefWithBodyId::FunctionId(it) => db.function_data(it).is_unsafe(),
         DefWithBodyId::StaticId(_)
         | DefWithBodyId::ConstId(_)
         | DefWithBodyId::VariantId(_)
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs
index 47ea2f5347c..7c481958d1a 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs
@@ -1069,6 +1069,7 @@ impl HirDisplay for Ty {
                             module_id,
                             PrefixKind::Plain,
                             false,
+                            // FIXME: no_std Cfg?
                             ImportPathConfig {
                                 prefer_no_std: false,
                                 prefer_prelude: true,
@@ -1151,11 +1152,10 @@ impl HirDisplay for Ty {
                         )?;
                         // FIXME: it would maybe be good to distinguish this from the alias type (when debug printing), and to show the substitution
                     }
-                    ImplTraitId::AssociatedTypeImplTrait(alias, idx) => {
+                    ImplTraitId::TypeAliasImplTrait(alias, idx) => {
                         let datas =
                             db.type_alias_impl_traits(alias).expect("impl trait id without data");
-                        let data =
-                            (*datas).as_ref().map(|rpit| rpit.impl_traits[idx].bounds.clone());
+                        let data = (*datas).as_ref().map(|it| it.impl_traits[idx].bounds.clone());
                         let bounds = data.substitute(Interner, &parameters);
                         let krate = alias.krate(db.upcast());
                         write_bounds_like_dyn_trait_with_prefix(
@@ -1338,7 +1338,7 @@ impl HirDisplay for Ty {
                             SizedByDefault::Sized { anchor: krate },
                         )?;
                     }
-                    ImplTraitId::AssociatedTypeImplTrait(alias, idx) => {
+                    ImplTraitId::TypeAliasImplTrait(alias, idx) => {
                         let datas =
                             db.type_alias_impl_traits(alias).expect("impl trait id without data");
                         let data =
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs
index 804bc53905a..45d423d03c0 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs
@@ -36,15 +36,14 @@ use hir_def::{
     body::Body,
     builtin_type::{BuiltinInt, BuiltinType, BuiltinUint},
     data::{ConstData, StaticData},
-    hir::LabelId,
-    hir::{BindingAnnotation, BindingId, ExprId, ExprOrPatId, PatId},
+    hir::{BindingAnnotation, BindingId, ExprId, ExprOrPatId, LabelId, PatId},
     lang_item::{LangItem, LangItemTarget},
     layout::Integer,
     path::{ModPath, Path},
     resolver::{HasResolver, ResolveValueResult, Resolver, TypeNs, ValueNs},
     type_ref::{LifetimeRef, TypeRef},
-    AdtId, AssocItemId, DefWithBodyId, FieldId, FunctionId, ItemContainerId, Lookup, TraitId,
-    TupleFieldId, TupleId, TypeAliasId, VariantId,
+    AdtId, AssocItemId, DefWithBodyId, FieldId, FunctionId, ImplId, ItemContainerId, Lookup,
+    TraitId, TupleFieldId, TupleId, TypeAliasId, VariantId,
 };
 use hir_expand::name::Name;
 use indexmap::IndexSet;
@@ -785,14 +784,19 @@ impl<'a> InferenceContext<'a> {
     fn collect_const(&mut self, data: &ConstData) {
         let return_ty = self.make_ty(&data.type_ref);
 
-        // Constants might be associated items that define ATPITs.
-        self.insert_atpit_coercion_table(iter::once(&return_ty));
+        // Constants might be defining usage sites of TAITs.
+        self.make_tait_coercion_table(iter::once(&return_ty));
 
         self.return_ty = return_ty;
     }
 
     fn collect_static(&mut self, data: &StaticData) {
-        self.return_ty = self.make_ty(&data.type_ref);
+        let return_ty = self.make_ty(&data.type_ref);
+
+        // Statics might be defining usage sites of TAITs.
+        self.make_tait_coercion_table(iter::once(&return_ty));
+
+        self.return_ty = return_ty;
     }
 
     fn collect_fn(&mut self, func: FunctionId) {
@@ -857,11 +861,11 @@ impl<'a> InferenceContext<'a> {
         self.return_ty = self.normalize_associated_types_in(return_ty);
         self.return_coercion = Some(CoerceMany::new(self.return_ty.clone()));
 
-        // Functions might be associated items that define ATPITs.
-        // To define an ATPITs, that ATPIT must appear in the function's signatures.
+        // Functions might be defining usage sites of TAITs.
+        // To define an TAITs, that TAIT must appear in the function's signatures.
         // So, it suffices to check for params and return types.
         params_and_ret_tys.push(self.return_ty.clone());
-        self.insert_atpit_coercion_table(params_and_ret_tys.iter());
+        self.make_tait_coercion_table(params_and_ret_tys.iter());
     }
 
     fn insert_inference_vars_for_impl_trait<T>(&mut self, t: T, placeholders: Substitution) -> T
@@ -880,7 +884,7 @@ impl<'a> InferenceContext<'a> {
                         ImplTraitId::ReturnTypeImplTrait(def, idx) => {
                             (self.db.return_type_impl_traits(def), idx)
                         }
-                        ImplTraitId::AssociatedTypeImplTrait(def, idx) => {
+                        ImplTraitId::TypeAliasImplTrait(def, idx) => {
                             (self.db.type_alias_impl_traits(def), idx)
                         }
                         _ => unreachable!(),
@@ -909,23 +913,25 @@ impl<'a> InferenceContext<'a> {
     }
 
     /// The coercion of a non-inference var into an opaque type should fail,
-    /// but not in the defining sites of the ATPITs.
-    /// In such cases, we insert an proxy inference var for each ATPIT,
-    /// and coerce into it instead of ATPIT itself.
+    /// but not in the defining sites of the TAITs.
+    /// In such cases, we insert an proxy inference var for each TAIT,
+    /// and coerce into it instead of TAIT itself.
     ///
     /// The inference var stretagy is effective because;
     ///
-    /// - It can still unify types that coerced into ATPIT
+    /// - It can still unify types that coerced into TAITs
     /// - We are pushing `impl Trait` bounds into it
     ///
     /// This function inserts a map that maps the opaque type to that proxy inference var.
-    fn insert_atpit_coercion_table<'b>(&mut self, tys: impl Iterator<Item = &'b Ty>) {
-        struct OpaqueTyCollector<'a, 'b> {
+    fn make_tait_coercion_table<'b>(&mut self, tait_candidates: impl Iterator<Item = &'b Ty>) {
+        struct TypeAliasImplTraitCollector<'a, 'b> {
+            db: &'b dyn HirDatabase,
             table: &'b mut InferenceTable<'a>,
-            opaque_tys: FxHashMap<OpaqueTyId, Ty>,
+            assocs: FxHashMap<OpaqueTyId, (ImplId, Ty)>,
+            non_assocs: FxHashMap<OpaqueTyId, Ty>,
         }
 
-        impl<'a, 'b> TypeVisitor<Interner> for OpaqueTyCollector<'a, 'b> {
+        impl<'a, 'b> TypeVisitor<Interner> for TypeAliasImplTraitCollector<'a, 'b> {
             type BreakTy = ();
 
             fn as_dyn(&mut self) -> &mut dyn TypeVisitor<Interner, BreakTy = Self::BreakTy> {
@@ -944,59 +950,105 @@ impl<'a> InferenceContext<'a> {
                 let ty = self.table.resolve_ty_shallow(ty);
 
                 if let TyKind::OpaqueType(id, _) = ty.kind(Interner) {
-                    self.opaque_tys.insert(*id, ty.clone());
+                    if let ImplTraitId::TypeAliasImplTrait(alias_id, _) =
+                        self.db.lookup_intern_impl_trait_id((*id).into())
+                    {
+                        let loc = self.db.lookup_intern_type_alias(alias_id);
+                        match loc.container {
+                            ItemContainerId::ImplId(impl_id) => {
+                                self.assocs.insert(*id, (impl_id, ty.clone()));
+                            }
+                            ItemContainerId::ModuleId(..) | ItemContainerId::ExternBlockId(..) => {
+                                self.non_assocs.insert(*id, ty.clone());
+                            }
+                            _ => {}
+                        }
+                    }
                 }
 
                 ty.super_visit_with(self, outer_binder)
             }
         }
 
-        // Early return if this is not happening inside the impl block
-        let impl_id = if let Some(impl_id) = self.resolver.impl_def() {
-            impl_id
-        } else {
-            return;
+        let mut collector = TypeAliasImplTraitCollector {
+            db: self.db,
+            table: &mut self.table,
+            assocs: FxHashMap::default(),
+            non_assocs: FxHashMap::default(),
         };
-
-        let assoc_tys: FxHashSet<_> = self
-            .db
-            .impl_data(impl_id)
-            .items
-            .iter()
-            .filter_map(|item| match item {
-                AssocItemId::TypeAliasId(alias) => Some(*alias),
-                _ => None,
-            })
-            .collect();
-        if assoc_tys.is_empty() {
-            return;
+        for ty in tait_candidates {
+            ty.visit_with(collector.as_dyn(), DebruijnIndex::INNERMOST);
         }
 
-        let mut collector =
-            OpaqueTyCollector { table: &mut self.table, opaque_tys: FxHashMap::default() };
-        for ty in tys {
-            ty.visit_with(collector.as_dyn(), DebruijnIndex::INNERMOST);
+        // Non-assoc TAITs can be define-used everywhere as long as they are
+        // in function signatures or const types, etc
+        let mut taits = collector.non_assocs;
+
+        // assoc TAITs(ATPITs) can be only define-used inside their impl block.
+        // They cannot be define-used in inner items like in the following;
+        //
+        // ```
+        // impl Trait for Struct {
+        //     type Assoc = impl Default;
+        //
+        //     fn assoc_fn() -> Self::Assoc {
+        //         let foo: Self::Assoc = true; // Allowed here
+        //
+        //         fn inner() -> Self::Assoc {
+        //              false                   // Not allowed here
+        //         }
+        //
+        //         foo
+        //     }
+        // }
+        // ```
+        let impl_id = match self.owner {
+            DefWithBodyId::FunctionId(it) => {
+                let loc = self.db.lookup_intern_function(it);
+                if let ItemContainerId::ImplId(impl_id) = loc.container {
+                    Some(impl_id)
+                } else {
+                    None
+                }
+            }
+            DefWithBodyId::ConstId(it) => {
+                let loc = self.db.lookup_intern_const(it);
+                if let ItemContainerId::ImplId(impl_id) = loc.container {
+                    Some(impl_id)
+                } else {
+                    None
+                }
+            }
+            _ => None,
+        };
+
+        if let Some(impl_id) = impl_id {
+            taits.extend(collector.assocs.into_iter().filter_map(|(id, (impl_, ty))| {
+                if impl_ == impl_id {
+                    Some((id, ty))
+                } else {
+                    None
+                }
+            }));
         }
-        let atpit_coercion_table: FxHashMap<_, _> = collector
-            .opaque_tys
+
+        let tait_coercion_table: FxHashMap<_, _> = taits
             .into_iter()
-            .filter_map(|(opaque_ty_id, ty)| {
-                if let ImplTraitId::AssociatedTypeImplTrait(alias_id, _) =
-                    self.db.lookup_intern_impl_trait_id(opaque_ty_id.into())
+            .filter_map(|(id, ty)| {
+                if let ImplTraitId::TypeAliasImplTrait(alias_id, _) =
+                    self.db.lookup_intern_impl_trait_id(id.into())
                 {
-                    if assoc_tys.contains(&alias_id) {
-                        let alias_placeholders = TyBuilder::placeholder_subst(self.db, alias_id);
-                        let ty = self.insert_inference_vars_for_impl_trait(ty, alias_placeholders);
-                        return Some((opaque_ty_id, ty));
-                    }
+                    let subst = TyBuilder::placeholder_subst(self.db, alias_id);
+                    let ty = self.insert_inference_vars_for_impl_trait(ty, subst);
+                    Some((id, ty))
+                } else {
+                    None
                 }
-
-                None
             })
             .collect();
 
-        if !atpit_coercion_table.is_empty() {
-            self.table.atpit_coercion_table = Some(atpit_coercion_table);
+        if !tait_coercion_table.is_empty() {
+            self.table.tait_coercion_table = Some(tait_coercion_table);
         }
     }
 
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs
index 72928851f12..6f85a4a4247 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs
@@ -276,16 +276,16 @@ impl InferenceTable<'_> {
             return success(simple(Adjust::NeverToAny)(to_ty.clone()), to_ty.clone(), vec![]);
         }
 
-        // If we are coercing into an ATPIT, coerce into its proxy inference var, instead.
+        // If we are coercing into a TAIT, coerce into its proxy inference var, instead.
         let mut to_ty = to_ty;
         let _to;
-        if let Some(atpit_table) = &self.atpit_coercion_table {
+        if let Some(tait_table) = &self.tait_coercion_table {
             if let TyKind::OpaqueType(opaque_ty_id, _) = to_ty.kind(Interner) {
                 if !matches!(
                     from_ty.kind(Interner),
                     TyKind::InferenceVar(..) | TyKind::OpaqueType(..)
                 ) {
-                    if let Some(ty) = atpit_table.get(opaque_ty_id) {
+                    if let Some(ty) = tait_table.get(opaque_ty_id) {
                         _to = ty.clone();
                         to_ty = &_to;
                     }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs
index 7ee63af1c22..3e3578b9f9b 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs
@@ -224,7 +224,7 @@ type ChalkInferenceTable = chalk_solve::infer::InferenceTable<Interner>;
 pub(crate) struct InferenceTable<'a> {
     pub(crate) db: &'a dyn HirDatabase,
     pub(crate) trait_env: Arc<TraitEnvironment>,
-    pub(crate) atpit_coercion_table: Option<FxHashMap<OpaqueTyId, Ty>>,
+    pub(crate) tait_coercion_table: Option<FxHashMap<OpaqueTyId, Ty>>,
     var_unification_table: ChalkInferenceTable,
     type_variable_table: SmallVec<[TypeVariableFlags; 16]>,
     pending_obligations: Vec<Canonicalized<InEnvironment<Goal>>>,
@@ -244,7 +244,7 @@ impl<'a> InferenceTable<'a> {
         InferenceTable {
             db,
             trait_env,
-            atpit_coercion_table: None,
+            tait_coercion_table: None,
             var_unification_table: ChalkInferenceTable::new(),
             type_variable_table: SmallVec::new(),
             pending_obligations: Vec::new(),
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs
index 034b9c773c2..47cc2a2f1e6 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs
@@ -391,7 +391,7 @@ pub fn layout_of_ty_query(
                     let infer = db.infer(func.into());
                     return db.layout_of_ty(infer.type_of_rpit[idx].clone(), trait_env);
                 }
-                crate::ImplTraitId::AssociatedTypeImplTrait(..) => {
+                crate::ImplTraitId::TypeAliasImplTrait(..) => {
                     return Err(LayoutError::NotImplemented);
                 }
                 crate::ImplTraitId::AsyncBlockTypeImplTrait(_, _) => {
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs
index 2f93ce31816..4c9e0a1e118 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs
@@ -595,7 +595,7 @@ impl TypeFoldable<Interner> for CallableSig {
 #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
 pub enum ImplTraitId {
     ReturnTypeImplTrait(hir_def::FunctionId, ImplTraitIdx),
-    AssociatedTypeImplTrait(hir_def::TypeAliasId, ImplTraitIdx),
+    TypeAliasImplTrait(hir_def::TypeAliasId, ImplTraitIdx),
     AsyncBlockTypeImplTrait(hir_def::DefWithBodyId, ExprId),
 }
 impl InternValueTrivial for ImplTraitId {}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs
index 444628ff521..67cdb99744a 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs
@@ -341,7 +341,7 @@ impl<'a> TyLoweringContext<'a> {
 
                         let impl_trait_id = origin.either(
                             |f| ImplTraitId::ReturnTypeImplTrait(f, idx),
-                            |a| ImplTraitId::AssociatedTypeImplTrait(a, idx),
+                            |a| ImplTraitId::TypeAliasImplTrait(a, idx),
                         );
                         let opaque_ty_id = self.db.intern_impl_trait_id(impl_trait_id).into();
                         let generics =
@@ -1857,7 +1857,7 @@ fn fn_sig_for_fn(db: &dyn HirDatabase, def: FunctionId) -> PolyFnSig {
         params,
         ret,
         data.is_varargs(),
-        if data.has_unsafe_kw() { Safety::Unsafe } else { Safety::Safe },
+        if data.is_unsafe() { Safety::Unsafe } else { Safety::Safe },
         data.abi.as_ref().map_or(FnAbi::Rust, FnAbi::from_symbol),
     );
     make_binders(db, &generics, sig)
@@ -2131,7 +2131,6 @@ pub(crate) fn type_alias_impl_traits(
     if let Some(type_ref) = &data.type_ref {
         let _ty = ctx.lower_ty(type_ref);
     }
-    let generics = generics(db.upcast(), def.into());
     let type_alias_impl_traits = ImplTraits {
         impl_traits: match ctx.impl_trait_mode {
             ImplTraitLoweringState::Opaque(x) => x.into_inner(),
@@ -2141,6 +2140,7 @@ pub(crate) fn type_alias_impl_traits(
     if type_alias_impl_traits.impl_traits.is_empty() {
         None
     } else {
+        let generics = generics(db.upcast(), def.into());
         Some(Arc::new(make_binders(db, &generics, type_alias_impl_traits)))
     }
 }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs
index 172dea02e61..8f6582b7f80 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs
@@ -82,8 +82,8 @@ impl FallibleTypeFolder<Interner> for Filler<'_> {
                         };
                         filler.try_fold_ty(infer.type_of_rpit[idx].clone(), outer_binder)
                     }
-                    crate::ImplTraitId::AssociatedTypeImplTrait(..) => {
-                        not_supported!("associated type impl trait");
+                    crate::ImplTraitId::TypeAliasImplTrait(..) => {
+                        not_supported!("type alias impl trait");
                     }
                     crate::ImplTraitId::AsyncBlockTypeImplTrait(_, _) => {
                         not_supported!("async block impl trait");
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs
index 19619008e3d..0fcd789f761 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs
@@ -9,6 +9,7 @@ mod patterns;
 mod regression;
 mod simple;
 mod traits;
+mod type_alias_impl_traits;
 
 use std::env;
 
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs
index fb07e718d10..a98cff2a08b 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs
@@ -4692,119 +4692,6 @@ fn f<T: Send, U>() {
 }
 
 #[test]
-fn associated_type_impl_trait() {
-    check_types(
-        r#"
-trait Foo {}
-struct S1;
-impl Foo for S1 {}
-
-trait Bar {
-    type Item;
-    fn bar(&self) -> Self::Item;
-}
-struct S2;
-impl Bar for S2 {
-    type Item = impl Foo;
-    fn bar(&self) -> Self::Item {
-        S1
-    }
-}
-
-fn test() {
-    let x = S2.bar();
-      //^ impl Foo + ?Sized
-}
-        "#,
-    );
-}
-
-#[test]
-fn associated_type_impl_traits_complex() {
-    check_types(
-        r#"
-struct Unary<T>(T);
-struct Binary<T, U>(T, U);
-
-trait Foo {}
-struct S1;
-impl Foo for S1 {}
-
-trait Bar {
-    type Item;
-    fn bar(&self) -> Unary<Self::Item>;
-}
-struct S2;
-impl Bar for S2 {
-    type Item = Unary<impl Foo>;
-    fn bar(&self) -> Unary<<Self as Bar>::Item> {
-        Unary(Unary(S1))
-    }
-}
-
-trait Baz {
-    type Target1;
-    type Target2;
-    fn baz(&self) -> Binary<Self::Target1, Self::Target2>;
-}
-struct S3;
-impl Baz for S3 {
-    type Target1 = impl Foo;
-    type Target2 = Unary<impl Bar>;
-    fn baz(&self) -> Binary<Self::Target1, Self::Target2> {
-        Binary(S1, Unary(S2))
-    }
-}
-
-fn test() {
-    let x = S3.baz();
-      //^ Binary<impl Foo + ?Sized, Unary<impl Bar + ?Sized>>
-    let y = x.1.0.bar();
-      //^ Unary<Bar::Item<impl Bar + ?Sized>>
-}
-        "#,
-    );
-}
-
-#[test]
-fn associated_type_with_impl_trait_in_tuple() {
-    check_no_mismatches(
-        r#"
-pub trait Iterator {
-    type Item;
-}
-
-pub trait Value {}
-
-fn bar<I: Iterator<Item = (usize, impl Value)>>() {}
-
-fn foo() {
-    bar();
-}
-"#,
-    );
-}
-
-#[test]
-fn associated_type_with_impl_trait_in_nested_tuple() {
-    check_no_mismatches(
-        r#"
-pub trait Iterator {
-    type Item;
-}
-
-pub trait Value {}
-
-fn bar<I: Iterator<Item = ((impl Value, usize), u32)>>() {}
-
-fn foo() {
-    bar();
-}
-"#,
-    );
-}
-
-#[test]
 fn dyn_trait_with_lifetime_in_rpit() {
     check_types(
         r#"
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/type_alias_impl_traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/type_alias_impl_traits.rs
new file mode 100644
index 00000000000..e2b7bf379cc
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/type_alias_impl_traits.rs
@@ -0,0 +1,161 @@
+use expect_test::expect;
+
+use super::{check_infer_with_mismatches, check_no_mismatches, check_types};
+
+#[test]
+fn associated_type_impl_trait() {
+    check_types(
+        r#"
+trait Foo {}
+struct S1;
+impl Foo for S1 {}
+
+trait Bar {
+    type Item;
+    fn bar(&self) -> Self::Item;
+}
+struct S2;
+impl Bar for S2 {
+    type Item = impl Foo;
+    fn bar(&self) -> Self::Item {
+        S1
+    }
+}
+
+fn test() {
+    let x = S2.bar();
+      //^ impl Foo + ?Sized
+}
+        "#,
+    );
+}
+
+#[test]
+fn associated_type_impl_traits_complex() {
+    check_types(
+        r#"
+struct Unary<T>(T);
+struct Binary<T, U>(T, U);
+
+trait Foo {}
+struct S1;
+impl Foo for S1 {}
+
+trait Bar {
+    type Item;
+    fn bar(&self) -> Unary<Self::Item>;
+}
+struct S2;
+impl Bar for S2 {
+    type Item = Unary<impl Foo>;
+    fn bar(&self) -> Unary<<Self as Bar>::Item> {
+        Unary(Unary(S1))
+    }
+}
+
+trait Baz {
+    type Target1;
+    type Target2;
+    fn baz(&self) -> Binary<Self::Target1, Self::Target2>;
+}
+struct S3;
+impl Baz for S3 {
+    type Target1 = impl Foo;
+    type Target2 = Unary<impl Bar>;
+    fn baz(&self) -> Binary<Self::Target1, Self::Target2> {
+        Binary(S1, Unary(S2))
+    }
+}
+
+fn test() {
+    let x = S3.baz();
+      //^ Binary<impl Foo + ?Sized, Unary<impl Bar + ?Sized>>
+    let y = x.1.0.bar();
+      //^ Unary<Bar::Item<impl Bar + ?Sized>>
+}
+        "#,
+    );
+}
+
+#[test]
+fn associated_type_with_impl_trait_in_tuple() {
+    check_no_mismatches(
+        r#"
+pub trait Iterator {
+    type Item;
+}
+
+pub trait Value {}
+
+fn bar<I: Iterator<Item = (usize, impl Value)>>() {}
+
+fn foo() {
+    bar();
+}
+"#,
+    );
+}
+
+#[test]
+fn associated_type_with_impl_trait_in_nested_tuple() {
+    check_no_mismatches(
+        r#"
+pub trait Iterator {
+    type Item;
+}
+
+pub trait Value {}
+
+fn bar<I: Iterator<Item = ((impl Value, usize), u32)>>() {}
+
+fn foo() {
+    bar();
+}
+"#,
+    );
+}
+
+#[test]
+fn type_alias_impl_trait_simple() {
+    check_no_mismatches(
+        r#"
+trait Trait {}
+
+struct Struct;
+
+impl Trait for Struct {}
+
+type AliasTy = impl Trait;
+
+static ALIAS: AliasTy = {
+    let res: AliasTy = Struct;
+    res
+};
+"#,
+    );
+
+    check_infer_with_mismatches(
+        r#"
+trait Trait {}
+
+struct Struct;
+
+impl Trait for Struct {}
+
+type AliasTy = impl Trait;
+
+static ALIAS: i32 = {
+    // TATIs cannot be define-used if not in signature or type annotations
+    let _a: AliasTy = Struct;
+    5
+};
+"#,
+        expect![[r#"
+            106..220 '{     ...   5 }': i32
+            191..193 '_a': impl Trait + ?Sized
+            205..211 'Struct': Struct
+            217..218 '5': i32
+            205..211: expected impl Trait + ?Sized, got Struct
+        "#]],
+    )
+}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs b/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs
index fbec332885d..d1ce68da6d6 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs
@@ -253,12 +253,7 @@ impl<'a> ClosureSubst<'a> {
 
 pub fn is_fn_unsafe_to_call(db: &dyn HirDatabase, func: FunctionId) -> bool {
     let data = db.function_data(func);
-    if data.has_unsafe_kw() {
-        // Functions that are `#[rustc_deprecated_safe_2024]` are safe to call before 2024.
-        if db.attrs(func.into()).by_key(&sym::rustc_deprecated_safe_2024).exists() {
-            // FIXME: Properly check the caller span and mark it as unsafe after 2024.
-            return false;
-        }
+    if data.is_unsafe() {
         return true;
     }
 
diff --git a/src/tools/rust-analyzer/crates/hir/src/display.rs b/src/tools/rust-analyzer/crates/hir/src/display.rs
index 7def828e95f..12dd8b5bf4f 100644
--- a/src/tools/rust-analyzer/crates/hir/src/display.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/display.rs
@@ -69,13 +69,13 @@ impl HirDisplay for Function {
 
         write_visibility(module_id, self.visibility(db), f)?;
 
-        if data.has_default_kw() {
+        if data.is_default() {
             f.write_str("default ")?;
         }
-        if data.has_const_kw() {
+        if data.is_const() {
             f.write_str("const ")?;
         }
-        if data.has_async_kw() {
+        if data.is_async() {
             f.write_str("async ")?;
         }
         if self.is_unsafe_to_call(db) {
@@ -125,7 +125,7 @@ impl HirDisplay for Function {
         // `FunctionData::ret_type` will be `::core::future::Future<Output = ...>` for async fns.
         // Use ugly pattern match to strip the Future trait.
         // Better way?
-        let ret_type = if !data.has_async_kw() {
+        let ret_type = if !data.is_async() {
             &data.ret_type
         } else {
             match &*data.ret_type {
diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs
index 67fbe3b789c..1a3becdf50e 100644
--- a/src/tools/rust-analyzer/crates/hir/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs
@@ -2189,11 +2189,11 @@ impl Function {
     }
 
     pub fn is_const(self, db: &dyn HirDatabase) -> bool {
-        db.function_data(self.id).has_const_kw()
+        db.function_data(self.id).is_const()
     }
 
     pub fn is_async(self, db: &dyn HirDatabase) -> bool {
-        db.function_data(self.id).has_async_kw()
+        db.function_data(self.id).is_async()
     }
 
     /// Does this function have `#[test]` attribute?
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs
index 414b096ad47..58e9b724df2 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs
@@ -24,7 +24,7 @@ pub(crate) mod vis;
 
 use std::iter;
 
-use hir::{sym, HasAttrs, ImportPathConfig, Name, ScopeDef, Variant};
+use hir::{sym, HasAttrs, Name, ScopeDef, Variant};
 use ide_db::{imports::import_assets::LocatedImport, RootDatabase, SymbolKind};
 use syntax::{ast, SmolStr, ToSmolStr};
 
@@ -645,11 +645,7 @@ fn enum_variants_with_paths(
         if let Some(path) = ctx.module.find_path(
             ctx.db,
             hir::ModuleDef::from(variant),
-            ImportPathConfig {
-                prefer_no_std: ctx.config.prefer_no_std,
-                prefer_prelude: ctx.config.prefer_prelude,
-                prefer_absolute: ctx.config.prefer_absolute,
-            },
+            ctx.config.import_path_config(),
         ) {
             // Variants with trivial paths are already added by the existing completion logic,
             // so we should avoid adding these twice
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs
index 71ff6b5aea3..ff2c8da4213 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs
@@ -1,6 +1,6 @@
 //! Completion of names from the current scope in expression position.
 
-use hir::{sym, ImportPathConfig, Name, ScopeDef};
+use hir::{sym, Name, ScopeDef};
 use syntax::ast;
 
 use crate::{
@@ -174,11 +174,7 @@ pub(crate) fn complete_expr_path(
                             .find_path(
                                 ctx.db,
                                 hir::ModuleDef::from(strukt),
-                                ImportPathConfig {
-                                    prefer_no_std: ctx.config.prefer_no_std,
-                                    prefer_prelude: ctx.config.prefer_prelude,
-                                    prefer_absolute: ctx.config.prefer_absolute,
-                                },
+                                ctx.config.import_path_config(),
                             )
                             .filter(|it| it.len() > 1);
 
@@ -200,11 +196,7 @@ pub(crate) fn complete_expr_path(
                             .find_path(
                                 ctx.db,
                                 hir::ModuleDef::from(un),
-                                ImportPathConfig {
-                                    prefer_no_std: ctx.config.prefer_no_std,
-                                    prefer_prelude: ctx.config.prefer_prelude,
-                                    prefer_absolute: ctx.config.prefer_absolute,
-                                },
+                                ctx.config.import_path_config(),
                             )
                             .filter(|it| it.len() > 1);
 
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs
index e803072fa8f..fdce7c547a4 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs
@@ -1,5 +1,5 @@
 //! See [`import_on_the_fly`].
-use hir::{ImportPathConfig, ItemInNs, ModuleDef};
+use hir::{ItemInNs, ModuleDef};
 use ide_db::imports::{
     import_assets::{ImportAssets, LocatedImport},
     insert_use::ImportScope,
@@ -256,11 +256,7 @@ fn import_on_the_fly(
     };
     let user_input_lowercased = potential_import_name.to_lowercase();
 
-    let import_cfg = ImportPathConfig {
-        prefer_no_std: ctx.config.prefer_no_std,
-        prefer_prelude: ctx.config.prefer_prelude,
-        prefer_absolute: ctx.config.prefer_absolute,
-    };
+    let import_cfg = ctx.config.import_path_config();
 
     import_assets
         .search_for_imports(&ctx.sema, import_cfg, ctx.config.insert_use.prefix_kind)
@@ -306,12 +302,7 @@ fn import_on_the_fly_pat_(
         ItemInNs::Values(def) => matches!(def, hir::ModuleDef::Const(_)),
     };
     let user_input_lowercased = potential_import_name.to_lowercase();
-
-    let cfg = ImportPathConfig {
-        prefer_no_std: ctx.config.prefer_no_std,
-        prefer_prelude: ctx.config.prefer_prelude,
-        prefer_absolute: ctx.config.prefer_absolute,
-    };
+    let cfg = ctx.config.import_path_config();
 
     import_assets
         .search_for_imports(&ctx.sema, cfg, ctx.config.insert_use.prefix_kind)
@@ -353,11 +344,7 @@ fn import_on_the_fly_method(
 
     let user_input_lowercased = potential_import_name.to_lowercase();
 
-    let cfg = ImportPathConfig {
-        prefer_no_std: ctx.config.prefer_no_std,
-        prefer_prelude: ctx.config.prefer_prelude,
-        prefer_absolute: ctx.config.prefer_absolute,
-    };
+    let cfg = ctx.config.import_path_config();
 
     import_assets
         .search_for_imports(&ctx.sema, cfg, ctx.config.insert_use.prefix_kind)
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs
index d919609237a..977e0d80a4d 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs
@@ -2,7 +2,7 @@
 
 mod format_like;
 
-use hir::{ImportPathConfig, ItemInNs};
+use hir::ItemInNs;
 use ide_db::{
     documentation::{Documentation, HasDocs},
     imports::insert_use::ImportScope,
@@ -60,11 +60,7 @@ pub(crate) fn complete_postfix(
         None => return,
     };
 
-    let cfg = ImportPathConfig {
-        prefer_no_std: ctx.config.prefer_no_std,
-        prefer_prelude: ctx.config.prefer_prelude,
-        prefer_absolute: ctx.config.prefer_absolute,
-    };
+    let cfg = ctx.config.import_path_config();
 
     if let Some(drop_trait) = ctx.famous_defs().core_ops_Drop() {
         if receiver_ty.impls_trait(ctx.db, drop_trait, &[]) {
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/config.rs b/src/tools/rust-analyzer/crates/ide-completion/src/config.rs
index 7d062cb23e5..d885b82ec90 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/config.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/config.rs
@@ -4,6 +4,7 @@
 //! module, and we use to statically check that we only produce snippet
 //! completions if we are allowed to.
 
+use hir::ImportPathConfig;
 use ide_db::{imports::insert_use::InsertUseConfig, SnippetCap};
 
 use crate::snippet::Snippet;
@@ -45,4 +46,12 @@ impl CompletionConfig {
             .iter()
             .flat_map(|snip| snip.prefix_triggers.iter().map(move |trigger| (&**trigger, snip)))
     }
+
+    pub fn import_path_config(&self) -> ImportPathConfig {
+        ImportPathConfig {
+            prefer_no_std: self.prefer_no_std,
+            prefer_prelude: self.prefer_prelude,
+            prefer_absolute: self.prefer_absolute,
+        }
+    }
 }
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs b/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs
index 424f94457e3..90c1728074d 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs
@@ -10,7 +10,6 @@ mod snippet;
 #[cfg(test)]
 mod tests;
 
-use hir::ImportPathConfig;
 use ide_db::{
     helpers::mod_path_to_ast,
     imports::{
@@ -249,11 +248,7 @@ pub fn resolve_completion_edits(
     let new_ast = scope.clone_for_update();
     let mut import_insert = TextEdit::builder();
 
-    let cfg = ImportPathConfig {
-        prefer_no_std: config.prefer_no_std,
-        prefer_prelude: config.prefer_prelude,
-        prefer_absolute: config.prefer_absolute,
-    };
+    let cfg = config.import_path_config();
 
     imports.into_iter().for_each(|(full_import_path, imported_name)| {
         let items_with_name = items_locator::items_with_name(
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs
index abcff62341b..02d667c5205 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs
@@ -10,7 +10,7 @@ pub(crate) mod type_alias;
 pub(crate) mod union_literal;
 pub(crate) mod variant;
 
-use hir::{sym, AsAssocItem, HasAttrs, HirDisplay, ImportPathConfig, ModuleDef, ScopeDef, Type};
+use hir::{sym, AsAssocItem, HasAttrs, HirDisplay, ModuleDef, ScopeDef, Type};
 use ide_db::{
     documentation::{Documentation, HasDocs},
     helpers::item_name,
@@ -294,11 +294,7 @@ pub(crate) fn render_expr(
             .unwrap_or_else(|| String::from("..."))
     };
 
-    let cfg = ImportPathConfig {
-        prefer_no_std: ctx.config.prefer_no_std,
-        prefer_prelude: ctx.config.prefer_prelude,
-        prefer_absolute: ctx.config.prefer_absolute,
-    };
+    let cfg = ctx.config.import_path_config();
 
     let label = expr.gen_source_code(&ctx.scope, &mut label_formatter, cfg).ok()?;
 
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs b/src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs
index 1eb8c574bd1..5265aa8515b 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs
@@ -100,7 +100,6 @@
 // }
 // ----
 
-use hir::ImportPathConfig;
 use ide_db::imports::import_assets::LocatedImport;
 use itertools::Itertools;
 use syntax::{ast, AstNode, GreenNode, SyntaxNode};
@@ -169,11 +168,7 @@ impl Snippet {
 }
 
 fn import_edits(ctx: &CompletionContext<'_>, requires: &[GreenNode]) -> Option<Vec<LocatedImport>> {
-    let import_cfg = ImportPathConfig {
-        prefer_no_std: ctx.config.prefer_no_std,
-        prefer_prelude: ctx.config.prefer_prelude,
-        prefer_absolute: ctx.config.prefer_absolute,
-    };
+    let import_cfg = ctx.config.import_path_config();
 
     let resolve = |import: &GreenNode| {
         let path = ast::Path::cast(SyntaxNode::new_root(import.clone()))?;
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs
index 30dd26a118d..af8ac6005d7 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs
@@ -487,4 +487,28 @@ fn main() {
             "#,
         )
     }
+
+    #[test]
+    fn rustc_deprecated_safe_2024() {
+        check_diagnostics(
+            r#"
+//- /ed2021.rs crate:ed2021 edition:2021
+#[rustc_deprecated_safe_2024]
+unsafe fn safe() -> u8 {
+    0
+}
+//- /ed2024.rs crate:ed2024 edition:2024
+#[rustc_deprecated_safe_2024]
+unsafe fn not_safe() -> u8 {
+    0
+}
+//- /main.rs crate:main deps:ed2021,ed2024
+fn main() {
+    ed2021::safe();
+    ed2024::not_safe();
+  //^^^^^^^^^^^^^^^^^^💡 error: this operation is unsafe and requires an unsafe function or block
+}
+            "#,
+        )
+    }
 }
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
index 486046c47c7..02f5d75136e 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
@@ -34,9 +34,9 @@ use triomphe::Arc;
 use vfs::{AbsPath, AbsPathBuf, VfsPath};
 
 use crate::{
-    capabilities::ClientCapabilities,
     diagnostics::DiagnosticsMapConfig,
     flycheck::{CargoOptions, FlycheckConfig},
+    lsp::capabilities::ClientCapabilities,
     lsp_ext::{WorkspaceSymbolSearchKind, WorkspaceSymbolSearchScope},
 };
 
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/diff.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/diff.rs
deleted file mode 100644
index 3fcfb4a1b08..00000000000
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/diff.rs
+++ /dev/null
@@ -1,53 +0,0 @@
-//! Generate minimal `TextEdit`s from different text versions
-use dissimilar::Chunk;
-use ide::{TextEdit, TextRange, TextSize};
-
-pub(crate) fn diff(left: &str, right: &str) -> TextEdit {
-    let chunks = dissimilar::diff(left, right);
-    textedit_from_chunks(chunks)
-}
-
-fn textedit_from_chunks(chunks: Vec<dissimilar::Chunk<'_>>) -> TextEdit {
-    let mut builder = TextEdit::builder();
-    let mut pos = TextSize::default();
-
-    let mut chunks = chunks.into_iter().peekable();
-    while let Some(chunk) = chunks.next() {
-        if let (Chunk::Delete(deleted), Some(&Chunk::Insert(inserted))) = (chunk, chunks.peek()) {
-            chunks.next().unwrap();
-            let deleted_len = TextSize::of(deleted);
-            builder.replace(TextRange::at(pos, deleted_len), inserted.into());
-            pos += deleted_len;
-            continue;
-        }
-
-        match chunk {
-            Chunk::Equal(text) => {
-                pos += TextSize::of(text);
-            }
-            Chunk::Delete(deleted) => {
-                let deleted_len = TextSize::of(deleted);
-                builder.delete(TextRange::at(pos, deleted_len));
-                pos += deleted_len;
-            }
-            Chunk::Insert(inserted) => {
-                builder.insert(pos, inserted.into());
-            }
-        }
-    }
-    builder.finish()
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-
-    #[test]
-    fn diff_applies() {
-        let mut original = String::from("fn foo(a:u32){\n}");
-        let result = "fn foo(a: u32) {}";
-        let edit = diff(&original, result);
-        edit.apply(&mut original);
-        assert_eq!(original, result);
-    }
-}
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs
index 88ff25e5d12..7a7ec1d77e0 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs
@@ -459,6 +459,11 @@ impl GlobalState {
             }
         }
 
+        // FIXME: `workspace_structure_change` is computed from `should_refresh_for_change` which is
+        // path syntax based. That is not sufficient for all cases so we should lift that check out
+        // into a `QueuedTask`, see `handle_did_save_text_document`.
+        // Or maybe instead of replacing that check, kick off a semantic one if the syntactic one
+        // didn't find anything (to make up for the lack of precision).
         {
             if !matches!(&workspace_structure_change, Some((.., true))) {
                 _ = self
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/dispatch.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/dispatch.rs
index ebdc196a658..ebdc196a658 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/dispatch.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/dispatch.rs
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs
index a2f9229047e..de5d1f23136 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs
@@ -158,6 +158,8 @@ pub(crate) fn handle_did_save_text_document(
                 .map(|cfg| cfg.files_to_watch.iter().map(String::as_str).collect::<Vec<&str>>())
                 .unwrap_or_default();
 
+            // FIXME: We should move this check into a QueuedTask and do semantic resolution of
+            // the files. There is only so much we can tell syntactically from the path.
             if reload::should_refresh_for_change(path, ChangeKind::Modify, additional_files) {
                 state.fetch_workspaces_queue.request_op(
                     format!("workspace vfs file change saved {path}"),
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs
index a77d31167a7..34325ac7a93 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs
@@ -36,7 +36,6 @@ use vfs::{AbsPath, AbsPathBuf, FileId, VfsPath};
 
 use crate::{
     config::{Config, RustfmtConfig, WorkspaceSymbolConfig},
-    diff::diff,
     global_state::{FetchWorkspaceRequest, GlobalState, GlobalStateSnapshot},
     hack_recover_crate_name,
     line_index::LineEndings,
@@ -2370,3 +2369,47 @@ fn resolve_resource_op(op: &ResourceOp) -> ResourceOperationKind {
         ResourceOp::Delete(_) => ResourceOperationKind::Delete,
     }
 }
+
+pub(crate) fn diff(left: &str, right: &str) -> TextEdit {
+    use dissimilar::Chunk;
+
+    let chunks = dissimilar::diff(left, right);
+
+    let mut builder = TextEdit::builder();
+    let mut pos = TextSize::default();
+
+    let mut chunks = chunks.into_iter().peekable();
+    while let Some(chunk) = chunks.next() {
+        if let (Chunk::Delete(deleted), Some(&Chunk::Insert(inserted))) = (chunk, chunks.peek()) {
+            chunks.next().unwrap();
+            let deleted_len = TextSize::of(deleted);
+            builder.replace(TextRange::at(pos, deleted_len), inserted.into());
+            pos += deleted_len;
+            continue;
+        }
+
+        match chunk {
+            Chunk::Equal(text) => {
+                pos += TextSize::of(text);
+            }
+            Chunk::Delete(deleted) => {
+                let deleted_len = TextSize::of(deleted);
+                builder.delete(TextRange::at(pos, deleted_len));
+                pos += deleted_len;
+            }
+            Chunk::Insert(inserted) => {
+                builder.insert(pos, inserted.into());
+            }
+        }
+    }
+    builder.finish()
+}
+
+#[test]
+fn diff_smoke_test() {
+    let mut original = String::from("fn foo(a:u32){\n}");
+    let result = "fn foo(a: u32) {}";
+    let edit = diff(&original, result);
+    edit.apply(&mut original);
+    assert_eq!(original, result);
+}
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs
index 56eb420770e..714991e8116 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs
@@ -11,12 +11,9 @@
 
 pub mod cli;
 
-mod capabilities;
 mod command;
 mod diagnostics;
-mod diff;
 mod discover;
-mod dispatch;
 mod flycheck;
 mod hack_recover_crate_name;
 mod line_index;
@@ -30,6 +27,7 @@ mod test_runner;
 mod version;
 
 mod handlers {
+    pub(crate) mod dispatch;
     pub(crate) mod notification;
     pub(crate) mod request;
 }
@@ -51,7 +49,7 @@ mod integrated_benchmarks;
 use serde::de::DeserializeOwned;
 
 pub use crate::{
-    capabilities::server_capabilities, main_loop::main_loop, reload::ws_to_crate_graph,
+    lsp::capabilities::server_capabilities, main_loop::main_loop, reload::ws_to_crate_graph,
     version::version,
 };
 
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp.rs
index 9e0d42faed4..122ad20d65e 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp.rs
@@ -3,6 +3,8 @@
 use core::fmt;
 
 pub mod ext;
+
+pub(crate) mod capabilities;
 pub(crate) mod from_proto;
 pub(crate) mod semantic_tokens;
 pub(crate) mod to_proto;
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/capabilities.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/capabilities.rs
index 9610808c27e..9610808c27e 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/capabilities.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/capabilities.rs
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs
index 85e7d81fce3..e303765aab6 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs
@@ -20,10 +20,10 @@ use crate::{
     config::Config,
     diagnostics::{fetch_native_diagnostics, DiagnosticsGeneration, NativeDiagnosticsFetchKind},
     discover::{DiscoverArgument, DiscoverCommand, DiscoverProjectMessage},
-    dispatch::{NotificationDispatcher, RequestDispatcher},
     flycheck::{self, FlycheckMessage},
     global_state::{file_id_to_url, url_to_file_id, FetchWorkspaceRequest, GlobalState},
     hack_recover_crate_name,
+    handlers::dispatch::{NotificationDispatcher, RequestDispatcher},
     lsp::{
         from_proto, to_proto,
         utils::{notification_is, Progress},
@@ -105,6 +105,7 @@ pub(crate) enum Task {
     FetchWorkspace(ProjectWorkspaceProgress),
     FetchBuildData(BuildDataProgress),
     LoadProcMacros(ProcMacroProgress),
+    // FIXME: Remove this in favor of a more general QueuedTask, see `handle_did_save_text_document`
     BuildDepsHaveChanged,
 }