Repository: rust-analyzer/rust-analyzer
Pull request #5968 (2020-09-09 17:01:57)

Lookup ADT and associated type names for chalk debugging / tweak chalk interner
This PR improves the chalk program writing integration by looking up the names for ADTs and associated types, making the output much more readable.

There are also a few small changes to the interner, which gives some nice performance improvements. We clone `Ty`s and `ProgramClause`s relatively often in chalk, so wrapping them in `Arc`s is a perf win. This takes the time for performing type inference on the rust-analyzer codebase from 40s to 33s on my machine.


Patch:
diff --git a/Cargo.lock b/Cargo.lock
index 9ebb15a96a..5b93841491 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -129,9 +129,9 @@ checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de"
 
 [[package]]
 name = "cargo_metadata"
-version = "0.11.2"
+version = "0.11.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c990b1694d29f8e477f456db1b2fcd5dd1cd6e29d5be082df45213e8834eb39a"
+checksum = "89fec17b16f1ac67908af82e47d0a90a7afd0e1827b181cd77504323d3263d35"
 dependencies = [
  "semver",
  "serde",
@@ -162,9 +162,9 @@ checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
 
 [[package]]
 name = "chalk-derive"
-version = "0.25.0"
+version = "0.23.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "624e14d3f029186e6ffd97081ffa082f98ddd5df20655b6f0e8efb83dd8ac8b4"
+checksum = "c3cb438e961fd7f1183dc5e0bdcfd09253bf9b90592cf665d1ce6787d8a4908f"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -174,9 +174,9 @@ dependencies = [
 
 [[package]]
 name = "chalk-ir"
-version = "0.25.0"
+version = "0.23.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "118c68eccdda5604af50bbef84c94550f3854f76989cb03c36ffd36cc2ffe958"
+checksum = "bb332abfcb015b148c6fbab39b1d13282745b0f7f312019dd8e138f5f3f0855d"
 dependencies = [
  "chalk-derive",
  "lazy_static",
@@ -184,9 +184,9 @@ dependencies = [
 
 [[package]]
 name = "chalk-recursive"
-version = "0.25.0"
+version = "0.23.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5130de3065e3cdfd2ab6d7d70b02b917bafbc096f270c9a643c23da249053606"
+checksum = "e7c7673f10c5fa1acf7fa07d4f4c5917cbcf161ed3a952d14530c79950de32d2"
 dependencies = [
  "chalk-derive",
  "chalk-ir",
@@ -197,9 +197,9 @@ dependencies = [
 
 [[package]]
 name = "chalk-solve"
-version = "0.25.0"
+version = "0.23.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "45b235a1f568b28707f117b2d30eabbee9cbcfccaa0d6e9697300400c8ca0996"
+checksum = "802de4eff72e5a5d2828e6c07224c74d66949dc6308aff025d0ae2871a11b4eb"
 dependencies = [
  "chalk-derive",
  "chalk-ir",
@@ -252,12 +252,12 @@ dependencies = [
 
 [[package]]
 name = "crossbeam-channel"
-version = "0.4.4"
+version = "0.4.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b153fe7cbef478c567df0f972e02e6d736db11affe43dfc9c56a9374d1adfb87"
+checksum = "09ee0cc8804d5393478d743b035099520087a5186f3b93fa58cec08fa62407b6"
 dependencies = [
+ "cfg-if",
  "crossbeam-utils",
- "maybe-uninit",
 ]
 
 [[package]]
@@ -451,9 +451,12 @@ dependencies = [
 
 [[package]]
 name = "hashbrown"
-version = "0.9.0"
+version = "0.8.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "00d63df3d41950fb462ed38308eea019113ad1508da725bbedcd0fa5a85ef5f7"
+checksum = "e91b62f79061a0bc2e046024cb7ba44b08419ed238ecbd9adbd787434b9e8c25"
+dependencies = [
+ "autocfg",
+]
 
 [[package]]
 name = "heck"
@@ -632,9 +635,9 @@ dependencies = [
 
 [[package]]
 name = "indexmap"
-version = "1.6.0"
+version = "1.5.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "55e2e4c765aa53a0424761bf9f41aa7a6ac1efa87238f59560640e27fca028f2"
+checksum = "4e47a3566dd4fd4eec714ae6ceabdee0caec795be835c223d92c2d40f1e8cf1c"
 dependencies = [
  "autocfg",
  "hashbrown",
@@ -909,9 +912,9 @@ dependencies = [
 
 [[package]]
 name = "net2"
-version = "0.2.35"
+version = "0.2.34"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3ebc3ec692ed7c9a255596c67808dee269f64655d8baf7b4f0638e51ba1d6853"
+checksum = "2ba7c918ac76704fb42afcbbb43891e72731f3dcca3bef2a19786297baf14af7"
 dependencies = [
  "cfg-if",
  "libc",
@@ -1071,9 +1074,9 @@ checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6"
 
 [[package]]
 name = "proc-macro2"
-version = "1.0.20"
+version = "1.0.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "175c513d55719db99da20232b06cda8bab6b83ec2d04e3283edf0213c37c1a29"
+checksum = "04f5f085b5d71e2188cb8271e5da0161ad52c3f227a661a3c135fdf28e258b12"
 dependencies = [
  "unicode-xid",
 ]
@@ -1492,9 +1495,9 @@ version = "0.0.0"
 
 [[package]]
 name = "syn"
-version = "1.0.40"
+version = "1.0.39"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "963f7d3cc59b59b9325165add223142bbf1df27655d07789f109896d353d8350"
+checksum = "891d8d6567fe7c7f8835a3a98af4208f3846fba258c1bc3c31d6e506239f11f9"
 dependencies = [
  "proc-macro2",
  "quote",
diff --git a/crates/assists/src/ast_transform.rs b/crates/assists/src/ast_transform.rs
index bbcd2d4883..5216862ba5 100644
--- a/crates/assists/src/ast_transform.rs
+++ b/crates/assists/src/ast_transform.rs
@@ -166,7 +166,7 @@ impl<'a> QualifyPaths<'a> {
                     .map(|arg_list| apply(self, arg_list));
                 if let Some(type_args) = type_args {
                     let last_segment = path.segment().unwrap();
-                    path = path.with_segment(last_segment.with_generic_args(type_args))
+                    path = path.with_segment(last_segment.with_type_args(type_args))
                 }
 
                 Some(path.syntax().clone())
diff --git a/crates/assists/src/handlers/auto_import.rs b/crates/assists/src/handlers/auto_import.rs
index 66e8191548..c4770f3361 100644
--- a/crates/assists/src/handlers/auto_import.rs
+++ b/crates/assists/src/handlers/auto_import.rs
@@ -1,13 +1,11 @@
 use std::collections::BTreeSet;
 
-use ast::make;
 use either::Either;
 use hir::{
     AsAssocItem, AssocItemContainer, ModPath, Module, ModuleDef, PathResolution, Semantics, Trait,
     Type,
 };
 use ide_db::{imports_locator, RootDatabase};
-use insert_use::ImportScope;
 use rustc_hash::FxHashSet;
 use syntax::{
     ast::{self, AstNode},
@@ -15,8 +13,7 @@ use syntax::{
 };
 
 use crate::{
-    utils::{insert_use, MergeBehaviour},
-    AssistContext, AssistId, AssistKind, Assists, GroupLabel,
+    utils::insert_use_statement, AssistContext, AssistId, AssistKind, Assists, GroupLabel,
 };
 
 // Assist: auto_import
@@ -47,9 +44,6 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
 
     let range = ctx.sema.original_range(&auto_import_assets.syntax_under_caret).range;
     let group = auto_import_assets.get_import_group_message();
-    let scope =
-        ImportScope::find_insert_use_container(&auto_import_assets.syntax_under_caret, ctx)?;
-    let syntax = scope.as_syntax_node();
     for import in proposed_imports {
         acc.add_group(
             &group,
@@ -57,12 +51,12 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
             format!("Import `{}`", &import),
             range,
             |builder| {
-                let new_syntax = insert_use(
-                    &scope,
-                    make::path_from_text(&import.to_string()),
-                    Some(MergeBehaviour::Full),
+                insert_use_statement(
+                    &auto_import_assets.syntax_under_caret,
+                    &import.to_string(),
+                    ctx,
+                    builder.text_edit_builder(),
                 );
-                builder.replace(syntax.text_range(), new_syntax.to_string())
             },
         );
     }
@@ -364,7 +358,7 @@ mod tests {
             }
             ",
             r"
-            use PubMod::{PubStruct1, PubStruct2};
+            use PubMod::{PubStruct2, PubStruct1};
 
             struct Test {
                 test: PubStruct2<u8>,
diff --git a/crates/assists/src/handlers/extract_struct_from_enum_variant.rs b/crates/assists/src/handlers/extract_struct_from_enum_variant.rs
index 80c62d8bba..8ac20210ad 100644
--- a/crates/assists/src/handlers/extract_struct_from_enum_variant.rs
+++ b/crates/assists/src/handlers/extract_struct_from_enum_variant.rs
@@ -10,12 +10,9 @@ use syntax::{
 };
 
 use crate::{
-    assist_context::AssistBuilder,
-    utils::{insert_use, MergeBehaviour},
-    AssistContext, AssistId, AssistKind, Assists,
+    assist_context::AssistBuilder, utils::insert_use_statement, AssistContext, AssistId,
+    AssistKind, Assists,
 };
-use ast::make;
-use insert_use::ImportScope;
 
 // Assist: extract_struct_from_enum_variant
 //
@@ -97,7 +94,6 @@ fn existing_struct_def(db: &RootDatabase, variant_name: &str, variant: &EnumVari
         .any(|(name, _)| name.to_string() == variant_name.to_string())
 }
 
-#[allow(dead_code)]
 fn insert_import(
     ctx: &AssistContext,
     builder: &mut AssistBuilder,
@@ -111,16 +107,12 @@ fn insert_import(
     if let Some(mut mod_path) = mod_path {
         mod_path.segments.pop();
         mod_path.segments.push(variant_hir_name.clone());
-        let scope = ImportScope::find_insert_use_container(path.syntax(), ctx)?;
-        let syntax = scope.as_syntax_node();
-
-        let new_syntax = insert_use(
-            &scope,
-            make::path_from_text(&mod_path.to_string()),
-            Some(MergeBehaviour::Full),
+        insert_use_statement(
+            path.syntax(),
+            &mod_path.to_string(),
+            ctx,
+            builder.text_edit_builder(),
         );
-        // FIXME: this will currently panic as multiple imports will have overlapping text ranges
-        builder.replace(syntax.text_range(), new_syntax.to_string())
     }
     Some(())
 }
@@ -175,9 +167,9 @@ fn update_reference(
     builder: &mut AssistBuilder,
     reference: Reference,
     source_file: &SourceFile,
-    _enum_module_def: &ModuleDef,
-    _variant_hir_name: &Name,
-    _visited_modules_set: &mut FxHashSet<Module>,
+    enum_module_def: &ModuleDef,
+    variant_hir_name: &Name,
+    visited_modules_set: &mut FxHashSet<Module>,
 ) -> Option<()> {
     let path_expr: ast::PathExpr = find_node_at_offset::<ast::PathExpr>(
         source_file.syntax(),
@@ -186,14 +178,13 @@ fn update_reference(
     let call = path_expr.syntax().parent().and_then(ast::CallExpr::cast)?;
     let list = call.arg_list()?;
     let segment = path_expr.path()?.segment()?;
-    let _module = ctx.sema.scope(&path_expr.syntax()).module()?;
+    let module = ctx.sema.scope(&path_expr.syntax()).module()?;
     let list_range = list.syntax().text_range();
     let inside_list_range = TextRange::new(
         list_range.start().checked_add(TextSize::from(1))?,
         list_range.end().checked_sub(TextSize::from(1))?,
     );
     builder.edit_file(reference.file_range.file_id);
-    /* FIXME: this most likely requires AST-based editing, see `insert_import`
     if !visited_modules_set.contains(&module) {
         if insert_import(ctx, builder, &path_expr, &module, enum_module_def, variant_hir_name)
             .is_some()
@@ -201,7 +192,6 @@ fn update_reference(
             visited_modules_set.insert(module);
         }
     }
-    */
     builder.replace(inside_list_range, format!("{}{}", segment, list));
     Some(())
 }
@@ -260,7 +250,6 @@ pub enum A { One(One) }"#,
     }
 
     #[test]
-    #[ignore] // FIXME: this currently panics if `insert_import` is used
     fn test_extract_struct_with_complex_imports() {
         check_assist(
             extract_struct_from_enum_variant,
diff --git a/crates/assists/src/handlers/replace_impl_trait_with_generic.rs b/crates/assists/src/handlers/replace_impl_trait_with_generic.rs
deleted file mode 100644
index 6738bc1341..0000000000
--- a/crates/assists/src/handlers/replace_impl_trait_with_generic.rs
+++ /dev/null
@@ -1,168 +0,0 @@
-use syntax::ast::{self, edit::AstNodeEdit, make, AstNode, GenericParamsOwner};
-
-use crate::{AssistContext, AssistId, AssistKind, Assists};
-
-// Assist: replace_impl_trait_with_generic
-//
-// Replaces `impl Trait` function argument with the named generic.
-//
-// ```
-// fn foo(bar: <|>impl Bar) {}
-// ```
-// ->
-// ```
-// fn foo<B: Bar>(bar: B) {}
-// ```
-pub(crate) fn replace_impl_trait_with_generic(
-    acc: &mut Assists,
-    ctx: &AssistContext,
-) -> Option<()> {
-    let type_impl_trait = ctx.find_node_at_offset::<ast::ImplTraitType>()?;
-    let type_param = type_impl_trait.syntax().parent().and_then(ast::Param::cast)?;
-    let type_fn = type_param.syntax().ancestors().find_map(ast::Fn::cast)?;
-
-    let impl_trait_ty = type_impl_trait.type_bound_list()?;
-
-    let target = type_fn.syntax().text_range();
-    acc.add(
-        AssistId("replace_impl_trait_with_generic", AssistKind::RefactorRewrite),
-        "Replace impl trait with generic",
-        target,
-        |edit| {
-            let generic_letter = impl_trait_ty.to_string().chars().next().unwrap().to_string();
-
-            let generic_param_list = type_fn
-                .generic_param_list()
-                .unwrap_or_else(|| make::generic_param_list(None))
-                .append_param(make::generic_param(generic_letter.clone(), Some(impl_trait_ty)));
-
-            let new_type_fn = type_fn
-                .replace_descendant::<ast::Type>(type_impl_trait.into(), make::ty(&generic_letter))
-                .with_generic_param_list(generic_param_list);
-
-            edit.replace_ast(type_fn.clone(), new_type_fn);
-        },
-    )
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-
-    use crate::tests::check_assist;
-
-    #[test]
-    fn replace_impl_trait_with_generic_params() {
-        check_assist(
-            replace_impl_trait_with_generic,
-            r#"
-            fn foo<G>(bar: <|>impl Bar) {}
-            "#,
-            r#"
-            fn foo<G, B: Bar>(bar: B) {}
-            "#,
-        );
-    }
-
-    #[test]
-    fn replace_impl_trait_without_generic_params() {
-        check_assist(
-            replace_impl_trait_with_generic,
-            r#"
-            fn foo(bar: <|>impl Bar) {}
-            "#,
-            r#"
-            fn foo<B: Bar>(bar: B) {}
-            "#,
-        );
-    }
-
-    #[test]
-    fn replace_two_impl_trait_with_generic_params() {
-        check_assist(
-            replace_impl_trait_with_generic,
-            r#"
-            fn foo<G>(foo: impl Foo, bar: <|>impl Bar) {}
-            "#,
-            r#"
-            fn foo<G, B: Bar>(foo: impl Foo, bar: B) {}
-            "#,
-        );
-    }
-
-    #[test]
-    fn replace_impl_trait_with_empty_generic_params() {
-        check_assist(
-            replace_impl_trait_with_generic,
-            r#"
-            fn foo<>(bar: <|>impl Bar) {}
-            "#,
-            r#"
-            fn foo<B: Bar>(bar: B) {}
-            "#,
-        );
-    }
-
-    #[test]
-    fn replace_impl_trait_with_empty_multiline_generic_params() {
-        check_assist(
-            replace_impl_trait_with_generic,
-            r#"
-            fn foo<
-            >(bar: <|>impl Bar) {}
-            "#,
-            r#"
-            fn foo<B: Bar
-            >(bar: B) {}
-            "#,
-        );
-    }
-
-    #[test]
-    #[ignore = "This case is very rare but there is no simple solutions to fix it."]
-    fn replace_impl_trait_with_exist_generic_letter() {
-        check_assist(
-            replace_impl_trait_with_generic,
-            r#"
-            fn foo<B>(bar: <|>impl Bar) {}
-            "#,
-            r#"
-            fn foo<B, C: Bar>(bar: C) {}
-            "#,
-        );
-    }
-
-    #[test]
-    fn replace_impl_trait_with_multiline_generic_params() {
-        check_assist(
-            replace_impl_trait_with_generic,
-            r#"
-            fn foo<
-                G: Foo,
-                F,
-                H,
-            >(bar: <|>impl Bar) {}
-            "#,
-            r#"
-            fn foo<
-                G: Foo,
-                F,
-                H, B: Bar
-            >(bar: B) {}
-            "#,
-        );
-    }
-
-    #[test]
-    fn replace_impl_trait_multiple() {
-        check_assist(
-            replace_impl_trait_with_generic,
-            r#"
-            fn foo(bar: <|>impl Foo + Bar) {}
-            "#,
-            r#"
-            fn foo<F: Foo + Bar>(bar: F) {}
-            "#,
-        );
-    }
-}
diff --git a/crates/assists/src/handlers/replace_qualified_name_with_use.rs b/crates/assists/src/handlers/replace_qualified_name_with_use.rs
index 85c70d16b6..470e5f8ff7 100644
--- a/crates/assists/src/handlers/replace_qualified_name_with_use.rs
+++ b/crates/assists/src/handlers/replace_qualified_name_with_use.rs
@@ -2,10 +2,9 @@ use syntax::{algo::SyntaxRewriter, ast, match_ast, AstNode, SyntaxNode, TextRang
 use test_utils::mark;
 
 use crate::{
-    utils::{insert_use, ImportScope, MergeBehaviour},
+    utils::{find_insert_use_container, insert_use_statement},
     AssistContext, AssistId, AssistKind, Assists,
 };
-use ast::make;
 
 // Assist: replace_qualified_name_with_use
 //
@@ -33,7 +32,7 @@ pub(crate) fn replace_qualified_name_with_use(
         mark::hit!(dont_import_trivial_paths);
         return None;
     }
-    let path_to_import = path.to_string();
+    let path_to_import = path.to_string().clone();
     let path_to_import = match path.segment()?.generic_arg_list() {
         Some(generic_args) => {
             let generic_args_start =
@@ -44,26 +43,28 @@ pub(crate) fn replace_qualified_name_with_use(
     };
 
     let target = path.syntax().text_range();
-    let scope = ImportScope::find_insert_use_container(path.syntax(), ctx)?;
-    let syntax = scope.as_syntax_node();
     acc.add(
         AssistId("replace_qualified_name_with_use", AssistKind::RefactorRewrite),
         "Replace qualified path with use",
         target,
         |builder| {
+            let container = match find_insert_use_container(path.syntax(), ctx) {
+                Some(c) => c,
+                None => return,
+            };
+            insert_use_statement(
+                path.syntax(),
+                &path_to_import.to_string(),
+                ctx,
+                builder.text_edit_builder(),
+            );
+
             // Now that we've brought the name into scope, re-qualify all paths that could be
             // affected (that is, all paths inside the node we added the `use` to).
             let mut rewriter = SyntaxRewriter::default();
-            shorten_paths(&mut rewriter, syntax.clone(), path);
-            let rewritten_syntax = rewriter.rewrite(&syntax);
-            if let Some(ref import_scope) = ImportScope::from(rewritten_syntax) {
-                let new_syntax = insert_use(
-                    import_scope,
-                    make::path_from_text(path_to_import),
-                    Some(MergeBehaviour::Full),
-                );
-                builder.replace(syntax.text_range(), new_syntax.to_string())
-            }
+            let syntax = container.either(|l| l.syntax().clone(), |r| r.syntax().clone());
+            shorten_paths(&mut rewriter, syntax, path);
+            builder.rewrite(rewriter);
         },
     )
 }
@@ -219,9 +220,8 @@ impl std::fmt::Debug<|> for Foo {
 }
     ",
             r"
-use std::fmt::Debug;
-
 use stdx;
+use std::fmt::Debug;
 
 impl Debug for Foo {
 }
@@ -274,7 +274,7 @@ impl std::io<|> for Foo {
 }
     ",
             r"
-use std::{fmt, io};
+use std::{io, fmt};
 
 impl io for Foo {
 }
@@ -293,7 +293,7 @@ impl std::fmt::Debug<|> for Foo {
 }
     ",
             r"
-use std::fmt::{self, Debug};
+use std::fmt::{self, Debug, };
 
 impl Debug for Foo {
 }
@@ -312,7 +312,7 @@ impl std::fmt<|> for Foo {
 }
     ",
             r"
-use std::fmt::{Debug, self};
+use std::fmt::{self, Debug};
 
 impl fmt for Foo {
 }
@@ -330,9 +330,8 @@ use std::fmt::{Debug, nested::{Display}};
 impl std::fmt::nested<|> for Foo {
 }
 ",
-            // FIXME(veykril): should be nested::{self, Display} here
             r"
-use std::fmt::{Debug, nested::{Display}, nested};
+use std::fmt::{Debug, nested::{Display, self}};
 
 impl nested for Foo {
 }
@@ -350,9 +349,8 @@ use std::fmt::{Debug, nested::{self, Display}};
 impl std::fmt::nested<|> for Foo {
 }
 ",
-            // FIXME(veykril): nested is duplicated now
             r"
-use std::fmt::{Debug, nested::{self, Display}, nested};
+use std::fmt::{Debug, nested::{self, Display}};
 
 impl nested for Foo {
 }
@@ -371,7 +369,7 @@ impl std::fmt::nested::Debug<|> for Foo {
 }
 ",
             r"
-use std::fmt::{Debug, nested::{Display}, nested::Debug};
+use std::fmt::{Debug, nested::{Display, Debug}};
 
 impl Debug for Foo {
 }
@@ -390,7 +388,7 @@ impl std::fmt::nested::Display<|> for Foo {
 }
 ",
             r"
-use std::fmt::{Debug, nested::Display};
+use std::fmt::{nested::Display, Debug};
 
 impl Display for Foo {
 }
@@ -409,7 +407,7 @@ impl std::fmt::Display<|> for Foo {
 }
 ",
             r"
-use std::fmt::{nested::Debug, Display};
+use std::fmt::{Display, nested::Debug};
 
 impl Display for Foo {
 }
@@ -429,12 +427,11 @@ use crate::{
 
 fn foo() { crate::ty::lower<|>::trait_env() }
 ",
-            // FIXME(veykril): formatting broke here
             r"
 use crate::{
-    ty::{Substs, Ty},
+    ty::{Substs, Ty, lower},
     AssocItem,
-ty::lower};
+};
 
 fn foo() { lower::trait_env() }
 ",
@@ -454,8 +451,6 @@ impl foo::Debug<|> for Foo {
             r"
 use std::fmt as foo;
 
-use foo::Debug;
-
 impl Debug for Foo {
 }
 ",
@@ -520,7 +515,6 @@ fn main() {
     ",
             r"
 #![allow(dead_code)]
-
 use std::fmt::Debug;
 
 fn main() {
@@ -633,7 +627,7 @@ fn main() {
 }
     ",
             r"
-use std::fmt::{Display, self};
+use std::fmt::{self, Display};
 
 fn main() {
     fmt;
@@ -653,9 +647,10 @@ impl std::io<|> for Foo {
 }
     ",
             r"
-pub use std::fmt;
 use std::io;
 
+pub use std::fmt;
+
 impl io for Foo {
 }
     ",
@@ -673,9 +668,10 @@ impl std::io<|> for Foo {
 }
     ",
             r"
-pub(crate) use std::fmt;
 use std::io;
 
+pub(crate) use std::fmt;
+
 impl io for Foo {
 }
     ",
diff --git a/crates/assists/src/lib.rs b/crates/assists/src/lib.rs
index cbac53e711..2e0d191a60 100644
--- a/crates/assists/src/lib.rs
+++ b/crates/assists/src/lib.rs
@@ -155,7 +155,6 @@ mod handlers {
     mod remove_unused_param;
     mod reorder_fields;
     mod replace_if_let_with_match;
-    mod replace_impl_trait_with_generic;
     mod replace_let_with_if_let;
     mod replace_qualified_name_with_use;
     mod replace_unwrap_with_match;
@@ -203,7 +202,6 @@ mod handlers {
             remove_unused_param::remove_unused_param,
             reorder_fields::reorder_fields,
             replace_if_let_with_match::replace_if_let_with_match,
-            replace_impl_trait_with_generic::replace_impl_trait_with_generic,
             replace_let_with_if_let::replace_let_with_if_let,
             replace_qualified_name_with_use::replace_qualified_name_with_use,
             replace_unwrap_with_match::replace_unwrap_with_match,
diff --git a/crates/assists/src/utils.rs b/crates/assists/src/utils.rs
index 7559ddd638..daa7b64f7d 100644
--- a/crates/assists/src/utils.rs
+++ b/crates/assists/src/utils.rs
@@ -16,7 +16,7 @@ use syntax::{
 
 use crate::assist_config::SnippetCap;
 
-pub(crate) use insert_use::{insert_use, ImportScope, MergeBehaviour};
+pub(crate) use insert_use::{find_insert_use_container, insert_use_statement};
 
 pub(crate) fn unwrap_trivial_block(block: ast::BlockExpr) -> ast::Expr {
     extract_trivial_expression(&block)
diff --git a/crates/assists/src/utils/insert_use.rs b/crates/assists/src/utils/insert_use.rs
index 8a4c8520d7..49096a67c7 100644
--- a/crates/assists/src/utils/insert_use.rs
+++ b/crates/assists/src/utils/insert_use.rs
@@ -1,724 +1,546 @@
 //! Handle syntactic aspects of inserting a new `use`.
-use std::iter::{self, successors};
+// FIXME: rewrite according to the plan, outlined in
+// https://github.com/rust-analyzer/rust-analyzer/issues/3301#issuecomment-592931553
 
-use algo::skip_trivia_token;
-use ast::{
-    edit::{AstNodeEdit, IndentLevel},
-    PathSegmentKind, VisibilityOwner,
-};
+use std::iter::successors;
+
+use either::Either;
 use syntax::{
-    algo,
-    ast::{self, make, AstNode},
-    Direction, InsertPosition, SyntaxElement, SyntaxNode, T,
+    ast::{self, NameOwner, VisibilityOwner},
+    AstNode, AstToken, Direction, SmolStr,
+    SyntaxKind::{PATH, PATH_SEGMENT},
+    SyntaxNode, SyntaxToken, T,
 };
-use test_utils::mark;
+use text_edit::TextEditBuilder;
+
+use crate::assist_context::AssistContext;
+
+/// Determines the containing syntax node in which to insert a `use` statement affecting `position`.
+pub(crate) fn find_insert_use_container(
+    position: &SyntaxNode,
+    ctx: &AssistContext,
+) -> Option<Either<ast::ItemList, ast::SourceFile>> {
+    ctx.sema.ancestors_with_macros(position.clone()).find_map(|n| {
+        if let Some(module) = ast::Module::cast(n.clone()) {
+            return module.item_list().map(|it| Either::Left(it));
+        }
+        Some(Either::Right(ast::SourceFile::cast(n)?))
+    })
+}
 
-#[derive(Debug)]
-pub enum ImportScope {
-    File(ast::SourceFile),
-    Module(ast::ItemList),
+/// Creates and inserts a use statement for the given path to import.
+/// The use statement is inserted in the scope most appropriate to the
+/// the cursor position given, additionally merged with the existing use imports.
+pub(crate) fn insert_use_statement(
+    // Ideally the position of the cursor, used to
+    position: &SyntaxNode,
+    path_to_import: &str,
+    ctx: &AssistContext,
+    builder: &mut TextEditBuilder,
+) {
+    let target = path_to_import.split("::").map(SmolStr::new).collect::<Vec<_>>();
+    let container = find_insert_use_container(position, ctx);
+
+    if let Some(container) = container {
+        let syntax = container.either(|l| l.syntax().clone(), |r| r.syntax().clone());
+        let action = best_action_for_target(syntax, position.clone(), &target);
+        make_assist(&action, &target, builder);
+    }
 }
 
-impl ImportScope {
-    pub fn from(syntax: SyntaxNode) -> Option<Self> {
-        if let Some(module) = ast::Module::cast(syntax.clone()) {
-            module.item_list().map(ImportScope::Module)
-        } else if let this @ Some(_) = ast::SourceFile::cast(syntax.clone()) {
-            this.map(ImportScope::File)
-        } else {
-            ast::ItemList::cast(syntax).map(ImportScope::Module)
+fn collect_path_segments_raw(
+    segments: &mut Vec<ast::PathSegment>,
+    mut path: ast::Path,
+) -> Option<usize> {
+    let oldlen = segments.len();
+    loop {
+        let mut children = path.syntax().children_with_tokens();
+        let (first, second, third) = (
+            children.next().map(|n| (n.clone(), n.kind())),
+            children.next().map(|n| (n.clone(), n.kind())),
+            children.next().map(|n| (n.clone(), n.kind())),
+        );
+        match (first, second, third) {
+            (Some((subpath, PATH)), Some((_, T![::])), Some((segment, PATH_SEGMENT))) => {
+                path = ast::Path::cast(subpath.as_node()?.clone())?;
+                segments.push(ast::PathSegment::cast(segment.as_node()?.clone())?);
+            }
+            (Some((segment, PATH_SEGMENT)), _, _) => {
+                segments.push(ast::PathSegment::cast(segment.as_node()?.clone())?);
+                break;
+            }
+            (_, _, _) => return None,
         }
     }
+    // We need to reverse only the new added segments
+    let only_new_segments = segments.split_at_mut(oldlen).1;
+    only_new_segments.reverse();
+    Some(segments.len() - oldlen)
+}
 
-    /// Determines the containing syntax node in which to insert a `use` statement affecting `position`.
-    pub(crate) fn find_insert_use_container(
-        position: &SyntaxNode,
-        ctx: &crate::assist_context::AssistContext,
-    ) -> Option<Self> {
-        ctx.sema.ancestors_with_macros(position.clone()).find_map(Self::from)
+fn fmt_segments_raw(segments: &[SmolStr], buf: &mut String) {
+    let mut iter = segments.iter();
+    if let Some(s) = iter.next() {
+        buf.push_str(s);
+    }
+    for s in iter {
+        buf.push_str("::");
+        buf.push_str(s);
     }
+}
+
+/// Returns the number of common segments.
+fn compare_path_segments(left: &[SmolStr], right: &[ast::PathSegment]) -> usize {
+    left.iter().zip(right).take_while(|(l, r)| compare_path_segment(l, r)).count()
+}
 
-    pub(crate) fn as_syntax_node(&self) -> &SyntaxNode {
-        match self {
-            ImportScope::File(file) => file.syntax(),
-            ImportScope::Module(item_list) => item_list.syntax(),
+fn compare_path_segment(a: &SmolStr, b: &ast::PathSegment) -> bool {
+    if let Some(kb) = b.kind() {
+        match kb {
+            ast::PathSegmentKind::Name(nameref_b) => a == nameref_b.text(),
+            ast::PathSegmentKind::SelfKw => a == "self",
+            ast::PathSegmentKind::SuperKw => a == "super",
+            ast::PathSegmentKind::CrateKw => a == "crate",
+            ast::PathSegmentKind::Type { .. } => false, // not allowed in imports
         }
+    } else {
+        false
     }
+}
+
+fn compare_path_segment_with_name(a: &SmolStr, b: &ast::Name) -> bool {
+    a == b.text()
+}
+
+#[derive(Clone, Debug)]
+enum ImportAction {
+    Nothing,
+    // Add a brand new use statement.
+    AddNewUse {
+        anchor: Option<SyntaxNode>, // anchor node
+        add_after_anchor: bool,
+    },
+
+    // To split an existing use statement creating a nested import.
+    AddNestedImport {
+        // how may segments matched with the target path
+        common_segments: usize,
+        path_to_split: ast::Path,
+        // the first segment of path_to_split we want to add into the new nested list
+        first_segment_to_split: Option<ast::PathSegment>,
+        // Wether to add 'self' in addition to the target path
+        add_self: bool,
+    },
+    // To add the target path to an existing nested import tree list.
+    AddInTreeList {
+        common_segments: usize,
+        // The UseTreeList where to add the target path
+        tree_list: ast::UseTreeList,
+        add_self: bool,
+    },
+}
 
-    fn indent_level(&self) -> IndentLevel {
-        match self {
-            ImportScope::File(file) => file.indent_level(),
-            ImportScope::Module(item_list) => item_list.indent_level() + 1,
+impl ImportAction {
+    fn add_new_use(anchor: Option<SyntaxNode>, add_after_anchor: bool) -> Self {
+        ImportAction::AddNewUse { anchor, add_after_anchor }
+    }
+
+    fn add_nested_import(
+        common_segments: usize,
+        path_to_split: ast::Path,
+        first_segment_to_split: Option<ast::PathSegment>,
+        add_self: bool,
+    ) -> Self {
+        ImportAction::AddNestedImport {
+            common_segments,
+            path_to_split,
+            first_segment_to_split,
+            add_self,
         }
     }
 
-    fn first_insert_pos(&self) -> (InsertPosition<SyntaxElement>, AddBlankLine) {
-        match self {
-            ImportScope::File(_) => (InsertPosition::First, AddBlankLine::AfterTwice),
-            // don't insert the imports before the item list's opening curly brace
-            ImportScope::Module(item_list) => item_list
-                .l_curly_token()
-                .map(|b| (InsertPosition::After(b.into()), AddBlankLine::Around))
-                .unwrap_or((InsertPosition::First, AddBlankLine::AfterTwice)),
-        }
+    fn add_in_tree_list(
+        common_segments: usize,
+        tree_list: ast::UseTreeList,
+        add_self: bool,
+    ) -> Self {
+        ImportAction::AddInTreeList { common_segments, tree_list, add_self }
     }
 
-    fn insert_pos_after_inner_attribute(&self) -> (InsertPosition<SyntaxElement>, AddBlankLine) {
-        // check if the scope has inner attributes, we dont want to insert in front of them
-        match self
-            .as_syntax_node()
-            .children()
-            // no flat_map here cause we want to short circuit the iterator
-            .map(ast::Attr::cast)
-            .take_while(|attr| {
-                attr.as_ref().map(|attr| attr.kind() == ast::AttrKind::Inner).unwrap_or(false)
-            })
-            .last()
-            .flatten()
-        {
-            Some(attr) => {
-                (InsertPosition::After(attr.syntax().clone().into()), AddBlankLine::BeforeTwice)
-            }
-            None => self.first_insert_pos(),
+    fn better(left: ImportAction, right: ImportAction) -> ImportAction {
+        if left.is_better(&right) {
+            left
+        } else {
+            right
         }
     }
-}
 
-/// Insert an import path into the given file/node. A `merge` value of none indicates that no import merging is allowed to occur.
-pub(crate) fn insert_use(
-    scope: &ImportScope,
-    path: ast::Path,
-    merge: Option<MergeBehaviour>,
-) -> SyntaxNode {
-    let use_item = make::use_(make::use_tree(path.clone(), None, None, false));
-    // merge into existing imports if possible
-    if let Some(mb) = merge {
-        for existing_use in scope.as_syntax_node().children().filter_map(ast::Use::cast) {
-            if let Some(merged) = try_merge_imports(&existing_use, &use_item, mb) {
-                let to_delete: SyntaxElement = existing_use.syntax().clone().into();
-                let to_delete = to_delete.clone()..=to_delete;
-                let to_insert = iter::once(merged.syntax().clone().into());
-                return algo::replace_children(scope.as_syntax_node(), to_delete, to_insert);
-            }
+    fn is_better(&self, other: &ImportAction) -> bool {
+        match (self, other) {
+            (ImportAction::Nothing, _) => true,
+            (ImportAction::AddInTreeList { .. }, ImportAction::Nothing) => false,
+            (
+                ImportAction::AddNestedImport { common_segments: n, .. },
+                ImportAction::AddInTreeList { common_segments: m, .. },
+            )
+            | (
+                ImportAction::AddInTreeList { common_segments: n, .. },
+                ImportAction::AddNestedImport { common_segments: m, .. },
+            )
+            | (
+                ImportAction::AddInTreeList { common_segments: n, .. },
+                ImportAction::AddInTreeList { common_segments: m, .. },
+            )
+            | (
+                ImportAction::AddNestedImport { common_segments: n, .. },
+                ImportAction::AddNestedImport { common_segments: m, .. },
+            ) => n > m,
+            (ImportAction::AddInTreeList { .. }, _) => true,
+            (ImportAction::AddNestedImport { .. }, ImportAction::Nothing) => false,
+            (ImportAction::AddNestedImport { .. }, _) => true,
+            (ImportAction::AddNewUse { .. }, _) => false,
         }
     }
+}
 
-    // either we weren't allowed to merge or there is no import that fits the merge conditions
-    // so look for the place we have to insert to
-    let (insert_position, add_blank) = find_insert_position(scope, path);
+// Find out the best ImportAction to import target path against current_use_tree.
+// If current_use_tree has a nested import the function gets called recursively on every UseTree inside a UseTreeList.
+fn walk_use_tree_for_best_action(
+    current_path_segments: &mut Vec<ast::PathSegment>, // buffer containing path segments
+    current_parent_use_tree_list: Option<ast::UseTreeList>, // will be Some value if we are in a nested import
+    current_use_tree: ast::UseTree, // the use tree we are currently examinating
+    target: &[SmolStr],             // the path we want to import
+) -> ImportAction {
+    // We save the number of segments in the buffer so we can restore the correct segments
+    // before returning. Recursive call will add segments so we need to delete them.
+    let prev_len = current_path_segments.len();
+
+    let tree_list = current_use_tree.use_tree_list();
+    let alias = current_use_tree.rename();
+
+    let path = match current_use_tree.path() {
+        Some(path) => path,
+        None => {
+            // If the use item don't have a path, it means it's broken (syntax error)
+            return ImportAction::add_new_use(
+                current_use_tree
+                    .syntax()
+                    .ancestors()
+                    .find_map(ast::Use::cast)
+                    .map(|it| it.syntax().clone()),
+                true,
+            );
+        }
+    };
 
-    let to_insert: Vec<SyntaxElement> = {
-        let mut buf = Vec::new();
+    // This can happen only if current_use_tree is a direct child of a UseItem
+    if let Some(name) = alias.and_then(|it| it.name()) {
+        if compare_path_segment_with_name(&target[0], &name) {
+            return ImportAction::Nothing;
+        }
+    }
 
-        match add_blank {
-            AddBlankLine::Before | AddBlankLine::Around => {
-                buf.push(make::tokens::single_newline().into())
+    collect_path_segments_raw(current_path_segments, path.clone());
+
+    // We compare only the new segments added in the line just above.
+    // The first prev_len segments were already compared in 'parent' recursive calls.
+    let left = target.split_at(prev_len).1;
+    let right = current_path_segments.split_at(prev_len).1;
+    let common = compare_path_segments(left, &right);
+    let mut action = match common {
+        0 => ImportAction::add_new_use(
+            // e.g: target is std::fmt and we can have
+            // use foo::bar
+            // We add a brand new use statement
+            current_use_tree
+                .syntax()
+                .ancestors()
+                .find_map(ast::Use::cast)
+                .map(|it| it.syntax().clone()),
+            true,
+        ),
+        common if common == left.len() && left.len() == right.len() => {
+            // e.g: target is std::fmt and we can have
+            // 1- use std::fmt;
+            // 2- use std::fmt::{ ... }
+            if let Some(list) = tree_list {
+                // In case 2 we need to add self to the nested list
+                // unless it's already there
+                let has_self = list.use_trees().map(|it| it.path()).any(|p| {
+                    p.and_then(|it| it.segment())
+                        .and_then(|it| it.kind())
+                        .filter(|k| *k == ast::PathSegmentKind::SelfKw)
+                        .is_some()
+                });
+
+                if has_self {
+                    ImportAction::Nothing
+                } else {
+                    ImportAction::add_in_tree_list(current_path_segments.len(), list, true)
+                }
+            } else {
+                // Case 1
+                ImportAction::Nothing
             }
-            AddBlankLine::BeforeTwice => buf.push(make::tokens::blank_line().into()),
-            _ => (),
         }
-
-        if let ident_level @ 1..=usize::MAX = scope.indent_level().0 as usize {
-            // FIXME: this alone doesnt properly re-align all cases
-            buf.push(make::tokens::whitespace(&" ".repeat(4 * ident_level)).into());
+        common if common != left.len() && left.len() == right.len() => {
+            // e.g: target is std::fmt and we have
+            // use std::io;
+            // We need to split.
+            let segments_to_split = current_path_segments.split_at(prev_len + common).1;
+            ImportAction::add_nested_import(
+                prev_len + common,
+                path,
+                Some(segments_to_split[0].clone()),
+                false,
+            )
         }
-        buf.push(use_item.syntax().clone().into());
-
-        match add_blank {
-            AddBlankLine::After | AddBlankLine::Around => {
-                buf.push(make::tokens::single_newline().into())
+        common if common == right.len() && left.len() > right.len() => {
+            // e.g: target is std::fmt and we can have
+            // 1- use std;
+            // 2- use std::{ ... };
+
+            // fallback action
+            let mut better_action = ImportAction::add_new_use(
+                current_use_tree
+                    .syntax()
+                    .ancestors()
+                    .find_map(ast::Use::cast)
+                    .map(|it| it.syntax().clone()),
+                true,
+            );
+            if let Some(list) = tree_list {
+                // Case 2, check recursively if the path is already imported in the nested list
+                for u in list.use_trees() {
+                    let child_action = walk_use_tree_for_best_action(
+                        current_path_segments,
+                        Some(list.clone()),
+                        u,
+                        target,
+                    );
+                    if child_action.is_better(&better_action) {
+                        better_action = child_action;
+                        if let ImportAction::Nothing = better_action {
+                            return better_action;
+                        }
+                    }
+                }
+            } else {
+                // Case 1, split adding self
+                better_action = ImportAction::add_nested_import(prev_len + common, path, None, true)
             }
-            AddBlankLine::AfterTwice => buf.push(make::tokens::blank_line().into()),
-            _ => (),
+            better_action
+        }
+        common if common == left.len() && left.len() < right.len() => {
+            // e.g: target is std::fmt and we can have
+            // use std::fmt::Debug;
+            let segments_to_split = current_path_segments.split_at(prev_len + common).1;
+            ImportAction::add_nested_import(
+                prev_len + common,
+                path,
+                Some(segments_to_split[0].clone()),
+                true,
+            )
+        }
+        common if common < left.len() && common < right.len() => {
+            // e.g: target is std::fmt::nested::Debug
+            // use std::fmt::Display
+            let segments_to_split = current_path_segments.split_at(prev_len + common).1;
+            ImportAction::add_nested_import(
+                prev_len + common,
+                path,
+                Some(segments_to_split[0].clone()),
+                false,
+            )
         }
+        _ => unreachable!(),
+    };
 
-        buf
+    // If we are inside a UseTreeList adding a use statement become adding to the existing
+    // tree list.
+    action = match (current_parent_use_tree_list, action.clone()) {
+        (Some(use_tree_list), ImportAction::AddNewUse { .. }) => {
+            ImportAction::add_in_tree_list(prev_len, use_tree_list, false)
+        }
+        (_, _) => action,
     };
 
-    algo::insert_children(scope.as_syntax_node(), insert_position, to_insert)
+    // We remove the segments added
+    current_path_segments.truncate(prev_len);
+    action
 }
 
-fn try_merge_imports(
-    old: &ast::Use,
-    new: &ast::Use,
-    merge_behaviour: MergeBehaviour,
-) -> Option<ast::Use> {
-    // don't merge into re-exports
-    if old.visibility().and_then(|vis| vis.pub_token()).is_some() {
-        return None;
-    }
-    let old_tree = old.use_tree()?;
-    let new_tree = new.use_tree()?;
-    let merged = try_merge_trees(&old_tree, &new_tree, merge_behaviour)?;
-    Some(old.with_use_tree(merged))
-}
+fn best_action_for_target(
+    container: SyntaxNode,
+    anchor: SyntaxNode,
+    target: &[SmolStr],
+) -> ImportAction {
+    let mut storage = Vec::with_capacity(16); // this should be the only allocation
+    let best_action = container
+        .children()
+        .filter_map(ast::Use::cast)
+        .filter(|u| u.visibility().is_none())
+        .filter_map(|it| it.use_tree())
+        .map(|u| walk_use_tree_for_best_action(&mut storage, None, u, target))
+        .fold(None, |best, a| match best {
+            Some(best) => Some(ImportAction::better(best, a)),
+            None => Some(a),
+        });
 
-/// Simple function that checks if a UseTreeList is deeper than one level
-fn use_tree_list_is_nested(tl: &ast::UseTreeList) -> bool {
-    tl.use_trees().any(|use_tree| {
-        use_tree.use_tree_list().is_some() || use_tree.path().and_then(|p| p.qualifier()).is_some()
-    })
-}
+    match best_action {
+        Some(action) => action,
+        None => {
+            // We have no action and no UseItem was found in container so we find
+            // another item and we use it as anchor.
+            // If there are no items above, we choose the target path itself as anchor.
+            // todo: we should include even whitespace blocks as anchor candidates
+            let anchor = container.children().next().or_else(|| Some(anchor));
 
-// FIXME: currently this merely prepends the new tree into old, ideally it would insert the items in a sorted fashion
-pub fn try_merge_trees(
-    old: &ast::UseTree,
-    new: &ast::UseTree,
-    merge_behaviour: MergeBehaviour,
-) -> Option<ast::UseTree> {
-    let lhs_path = old.path()?;
-    let rhs_path = new.path()?;
-
-    let (lhs_prefix, rhs_prefix) = common_prefix(&lhs_path, &rhs_path)?;
-    let lhs = old.split_prefix(&lhs_prefix);
-    let rhs = new.split_prefix(&rhs_prefix);
-    let lhs_tl = lhs.use_tree_list()?;
-    let rhs_tl = rhs.use_tree_list()?;
-
-    // if we are only allowed to merge the last level check if the split off paths are only one level deep
-    if merge_behaviour == MergeBehaviour::Last
-        && (use_tree_list_is_nested(&lhs_tl) || use_tree_list_is_nested(&rhs_tl))
-    {
-        mark::hit!(test_last_merge_too_long);
-        return None;
+            let add_after_anchor = anchor
+                .clone()
+                .and_then(ast::Attr::cast)
+                .map(|attr| attr.kind() == ast::AttrKind::Inner)
+                .unwrap_or(false);
+            ImportAction::add_new_use(anchor, add_after_anchor)
+        }
     }
+}
 
-    let should_insert_comma = lhs_tl
-        .r_curly_token()
-        .and_then(|it| skip_trivia_token(it.prev_token()?, Direction::Prev))
-        .map(|it| it.kind())
-        != Some(T![,]);
-    let mut to_insert: Vec<SyntaxElement> = Vec::new();
-    if should_insert_comma {
-        to_insert.push(make::token(T![,]).into());
-        to_insert.push(make::tokens::single_space().into());
+fn make_assist(action: &ImportAction, target: &[SmolStr], edit: &mut TextEditBuilder) {
+    match action {
+        ImportAction::AddNewUse { anchor, add_after_anchor } => {
+            make_assist_add_new_use(anchor, *add_after_anchor, target, edit)
+        }
+        ImportAction::AddInTreeList { common_segments, tree_list, add_self } => {
+            // We know that the fist n segments already exists in the use statement we want
+            // to modify, so we want to add only the last target.len() - n segments.
+            let segments_to_add = target.split_at(*common_segments).1;
+            make_assist_add_in_tree_list(tree_list, segments_to_add, *add_self, edit)
+        }
+        ImportAction::AddNestedImport {
+            common_segments,
+            path_to_split,
+            first_segment_to_split,
+            add_self,
+        } => {
+            let segments_to_add = target.split_at(*common_segments).1;
+            make_assist_add_nested_import(
+                path_to_split,
+                first_segment_to_split,
+                segments_to_add,
+                *add_self,
+                edit,
+            )
+        }
+        _ => {}
     }
-    to_insert.extend(
-        rhs_tl.syntax().children_with_tokens().filter(|it| !matches!(it.kind(), T!['{'] | T!['}'])),
-    );
-    let pos = InsertPosition::Before(lhs_tl.r_curly_token()?.into());
-    let use_tree_list = lhs_tl.insert_children(pos, to_insert);
-    Some(lhs.with_use_tree_list(use_tree_list))
 }
 
-/// Traverses both paths until they differ, returning the common prefix of both.
-fn common_prefix(lhs: &ast::Path, rhs: &ast::Path) -> Option<(ast::Path, ast::Path)> {
-    let mut res = None;
-    let mut lhs_curr = first_path(&lhs);
-    let mut rhs_curr = first_path(&rhs);
-    loop {
-        match (lhs_curr.segment(), rhs_curr.segment()) {
-            (Some(lhs), Some(rhs)) if lhs.syntax().text() == rhs.syntax().text() => (),
-            _ => break,
+fn make_assist_add_new_use(
+    anchor: &Option<SyntaxNode>,
+    after: bool,
+    target: &[SmolStr],
+    edit: &mut TextEditBuilder,
+) {
+    if let Some(anchor) = anchor {
+        let indent = leading_indent(anchor);
+        let mut buf = String::new();
+        if after {
+            buf.push_str("\n");
+            if let Some(spaces) = &indent {
+                buf.push_str(spaces);
+            }
         }
-        res = Some((lhs_curr.clone(), rhs_curr.clone()));
-
-        match lhs_curr.parent_path().zip(rhs_curr.parent_path()) {
-            Some((lhs, rhs)) => {
-                lhs_curr = lhs;
-                rhs_curr = rhs;
+        buf.push_str("use ");
+        fmt_segments_raw(target, &mut buf);
+        buf.push_str(";");
+        if !after {
+            buf.push_str("\n\n");
+            if let Some(spaces) = &indent {
+                buf.push_str(&spaces);
             }
-            _ => break,
         }
+        let position = if after { anchor.text_range().end() } else { anchor.text_range().start() };
+        edit.insert(position, buf);
     }
-
-    res
-}
-
-/// What type of merges are allowed.
-#[derive(Copy, Clone, PartialEq, Eq)]
-pub enum MergeBehaviour {
-    /// Merge everything together creating deeply nested imports.
-    Full,
-    /// Only merge the last import level, doesn't allow import nesting.
-    Last,
 }
 
-#[derive(Eq, PartialEq, PartialOrd, Ord)]
-enum ImportGroup {
-    // the order here defines the order of new group inserts
-    Std,
-    ExternCrate,
-    ThisCrate,
-    ThisModule,
-    SuperModule,
-}
-
-impl ImportGroup {
-    fn new(path: &ast::Path) -> ImportGroup {
-        let default = ImportGroup::ExternCrate;
-
-        let first_segment = match first_segment(path) {
-            Some(it) => it,
-            None => return default,
+fn make_assist_add_in_tree_list(
+    tree_list: &ast::UseTreeList,
+    target: &[SmolStr],
+    add_self: bool,
+    edit: &mut TextEditBuilder,
+) {
+    let last = tree_list.use_trees().last();
+    if let Some(last) = last {
+        let mut buf = String::new();
+        let comma = last.syntax().siblings(Direction::Next).find(|n| n.kind() == T![,]);
+        let offset = if let Some(comma) = comma {
+            comma.text_range().end()
+        } else {
+            buf.push_str(",");
+            last.syntax().text_range().end()
         };
-
-        let kind = first_segment.kind().unwrap_or(PathSegmentKind::SelfKw);
-        match kind {
-            PathSegmentKind::SelfKw => ImportGroup::ThisModule,
-            PathSegmentKind::SuperKw => ImportGroup::SuperModule,
-            PathSegmentKind::CrateKw => ImportGroup::ThisCrate,
-            PathSegmentKind::Name(name) => match name.text().as_str() {
-                "std" => ImportGroup::Std,
-                "core" => ImportGroup::Std,
-                // FIXME: can be ThisModule as well
-                _ => ImportGroup::ExternCrate,
-            },
-            PathSegmentKind::Type { .. } => unreachable!(),
+        if add_self {
+            buf.push_str(" self")
+        } else {
+            buf.push_str(" ");
         }
+        fmt_segments_raw(target, &mut buf);
+        edit.insert(offset, buf);
+    } else {
     }
 }
 
-fn first_segment(path: &ast::Path) -> Option<ast::PathSegment> {
-    first_path(path).segment()
-}
-
-fn first_path(path: &ast::Path) -> ast::Path {
-    successors(Some(path.clone()), ast::Path::qualifier).last().unwrap()
-}
-
-fn segment_iter(path: &ast::Path) -> impl Iterator<Item = ast::PathSegment> + Clone {
-    path.syntax().children().flat_map(ast::PathSegment::cast)
-}
-
-#[derive(PartialEq, Eq)]
-enum AddBlankLine {
-    Before,
-    BeforeTwice,
-    Around,
-    After,
-    AfterTwice,
-}
+fn make_assist_add_nested_import(
+    path: &ast::Path,
+    first_segment_to_split: &Option<ast::PathSegment>,
+    target: &[SmolStr],
+    add_self: bool,
+    edit: &mut TextEditBuilder,
+) {
+    let use_tree = path.syntax().ancestors().find_map(ast::UseTree::cast);
+    if let Some(use_tree) = use_tree {
+        let (start, add_colon_colon) = if let Some(first_segment_to_split) = first_segment_to_split
+        {
+            (first_segment_to_split.syntax().text_range().start(), false)
+        } else {
+            (use_tree.syntax().text_range().end(), true)
+        };
+        let end = use_tree.syntax().text_range().end();
 
-fn find_insert_position(
-    scope: &ImportScope,
-    insert_path: ast::Path,
-) -> (InsertPosition<SyntaxElement>, AddBlankLine) {
-    let group = ImportGroup::new(&insert_path);
-    let path_node_iter = scope
-        .as_syntax_node()
-        .children()
-        .filter_map(|node| ast::Use::cast(node.clone()).zip(Some(node)))
-        .flat_map(|(use_, node)| use_.use_tree().and_then(|tree| tree.path()).zip(Some(node)));
-    // Iterator that discards anything thats not in the required grouping
-    // This implementation allows the user to rearrange their import groups as this only takes the first group that fits
-    let group_iter = path_node_iter
-        .clone()
-        .skip_while(|(path, _)| ImportGroup::new(path) != group)
-        .take_while(|(path, _)| ImportGroup::new(path) == group);
-
-    let segments = segment_iter(&insert_path);
-    // track the last element we iterated over, if this is still None after the iteration then that means we never iterated in the first place
-    let mut last = None;
-    // find the element that would come directly after our new import
-    let post_insert =
-        group_iter.inspect(|(_, node)| last = Some(node.clone())).find(|(path, _)| {
-            let check_segments = segment_iter(&path);
-            segments
-                .clone()
-                .zip(check_segments)
-                .flat_map(|(seg, seg2)| seg.name_ref().zip(seg2.name_ref()))
-                .all(|(l, r)| l.text() <= r.text())
-        });
-    match post_insert {
-        // insert our import before that element
-        Some((_, node)) => (InsertPosition::Before(node.into()), AddBlankLine::After),
-        // there is no element after our new import, so append it to the end of the group
-        None => match last {
-            Some(node) => (InsertPosition::After(node.into()), AddBlankLine::Before),
-            // the group we were looking for actually doesnt exist, so insert
-            None => {
-                // similar concept here to the `last` from above
-                let mut last = None;
-                // find the group that comes after where we want to insert
-                let post_group = path_node_iter
-                    .inspect(|(_, node)| last = Some(node.clone()))
-                    .find(|(p, _)| ImportGroup::new(p) > group);
-                match post_group {
-                    Some((_, node)) => {
-                        (InsertPosition::Before(node.into()), AddBlankLine::AfterTwice)
-                    }
-                    // there is no such group, so append after the last one
-                    None => match last {
-                        Some(node) => {
-                            (InsertPosition::After(node.into()), AddBlankLine::BeforeTwice)
-                        }
-                        // there are no imports in this file at all
-                        None => scope.insert_pos_after_inner_attribute(),
-                    },
-                }
-            }
-        },
+        let mut buf = String::new();
+        if add_colon_colon {
+            buf.push_str("::");
+        }
+        buf.push_str("{");
+        if add_self {
+            buf.push_str("self, ");
+        }
+        fmt_segments_raw(target, &mut buf);
+        if !target.is_empty() {
+            buf.push_str(", ");
+        }
+        edit.insert(start, buf);
+        edit.insert(end, "}".to_string());
     }
 }
 
-#[cfg(test)]
-mod tests {
-    use super::*;
-
-    use test_utils::assert_eq_text;
-
-    #[test]
-    fn insert_start() {
-        check_none(
-            "std::bar::AA",
-            r"
-use std::bar::B;
-use std::bar::D;
-use std::bar::F;
-use std::bar::G;",
-            r"
-use std::bar::AA;
-use std::bar::B;
-use std::bar::D;
-use std::bar::F;
-use std::bar::G;",
-        )
-    }
-
-    #[test]
-    fn insert_middle() {
-        check_none(
-            "std::bar::EE",
-            r"
-use std::bar::A;
-use std::bar::D;
-use std::bar::F;
-use std::bar::G;",
-            r"
-use std::bar::A;
-use std::bar::D;
-use std::bar::EE;
-use std::bar::F;
-use std::bar::G;",
-        )
-    }
-
-    #[test]
-    fn insert_end() {
-        check_none(
-            "std::bar::ZZ",
-            r"
-use std::bar::A;
-use std::bar::D;
-use std::bar::F;
-use std::bar::G;",
-            r"
-use std::bar::A;
-use std::bar::D;
-use std::bar::F;
-use std::bar::G;
-use std::bar::ZZ;",
-        )
-    }
-
-    #[test]
-    fn insert_middle_nested() {
-        check_none(
-            "std::bar::EE",
-            r"
-use std::bar::A;
-use std::bar::{D, Z}; // example of weird imports due to user
-use std::bar::F;
-use std::bar::G;",
-            r"
-use std::bar::A;
-use std::bar::EE;
-use std::bar::{D, Z}; // example of weird imports due to user
-use std::bar::F;
-use std::bar::G;",
-        )
-    }
-
-    #[test]
-    fn insert_middle_groups() {
-        check_none(
-            "foo::bar::GG",
-            r"
-use std::bar::A;
-use std::bar::D;
-
-use foo::bar::F;
-use foo::bar::H;",
-            r"
-use std::bar::A;
-use std::bar::D;
-
-use foo::bar::F;
-use foo::bar::GG;
-use foo::bar::H;",
-        )
-    }
-
-    #[test]
-    fn insert_first_matching_group() {
-        check_none(
-            "foo::bar::GG",
-            r"
-use foo::bar::A;
-use foo::bar::D;
-
-use std;
-
-use foo::bar::F;
-use foo::bar::H;",
-            r"
-use foo::bar::A;
-use foo::bar::D;
-use foo::bar::GG;
-
-use std;
-
-use foo::bar::F;
-use foo::bar::H;",
-        )
-    }
-
-    #[test]
-    fn insert_missing_group_std() {
-        check_none(
-            "std::fmt",
-            r"
-use foo::bar::A;
-use foo::bar::D;",
-            r"
-use std::fmt;
-
-use foo::bar::A;
-use foo::bar::D;",
-        )
-    }
-
-    #[test]
-    fn insert_missing_group_self() {
-        check_none(
-            "self::fmt",
-            r"
-use foo::bar::A;
-use foo::bar::D;",
-            r"
-use foo::bar::A;
-use foo::bar::D;
-
-use self::fmt;",
-        )
-    }
-
-    #[test]
-    fn insert_no_imports() {
-        check_full(
-            "foo::bar",
-            "fn main() {}",
-            r"use foo::bar;
-
-fn main() {}",
-        )
-    }
-
-    #[test]
-    fn insert_empty_file() {
-        // empty files will get two trailing newlines
-        // this is due to the test case insert_no_imports above
-        check_full(
-            "foo::bar",
-            "",
-            r"use foo::bar;
-
-",
-        )
-    }
-
-    #[test]
-    fn insert_after_inner_attr() {
-        check_full(
-            "foo::bar",
-            r"#![allow(unused_imports)]",
-            r"#![allow(unused_imports)]
-
-use foo::bar;",
-        )
-    }
-
-    #[test]
-    fn insert_after_inner_attr2() {
-        check_full(
-            "foo::bar",
-            r"#![allow(unused_imports)]
-
-fn main() {}",
-            r"#![allow(unused_imports)]
-
-use foo::bar;
-
-fn main() {}",
-        )
-    }
-
-    #[test]
-    fn merge_groups() {
-        check_last("std::io", r"use std::fmt;", r"use std::{fmt, io};")
-    }
-
-    #[test]
-    fn merge_groups_last() {
-        check_last(
-            "std::io",
-            r"use std::fmt::{Result, Display};",
-            r"use std::fmt::{Result, Display};
-use std::io;",
-        )
-    }
-
-    #[test]
-    fn merge_groups_full() {
-        check_full(
-            "std::io",
-            r"use std::fmt::{Result, Display};",
-            r"use std::{fmt::{Result, Display}, io};",
-        )
-    }
-
-    #[test]
-    fn merge_groups_long_full() {
-        check_full(
-            "std::foo::bar::Baz",
-            r"use std::foo::bar::Qux;",
-            r"use std::foo::bar::{Qux, Baz};",
-        )
-    }
-
-    #[test]
-    fn merge_groups_long_last() {
-        check_last(
-            "std::foo::bar::Baz",
-            r"use std::foo::bar::Qux;",
-            r"use std::foo::bar::{Qux, Baz};",
-        )
-    }
-
-    #[test]
-    fn merge_groups_long_full_list() {
-        check_full(
-            "std::foo::bar::Baz",
-            r"use std::foo::bar::{Qux, Quux};",
-            r"use std::foo::bar::{Qux, Quux, Baz};",
-        )
-    }
-
-    #[test]
-    fn merge_groups_long_last_list() {
-        check_last(
-            "std::foo::bar::Baz",
-            r"use std::foo::bar::{Qux, Quux};",
-            r"use std::foo::bar::{Qux, Quux, Baz};",
-        )
-    }
-
-    #[test]
-    fn merge_groups_long_full_nested() {
-        check_full(
-            "std::foo::bar::Baz",
-            r"use std::foo::bar::{Qux, quux::{Fez, Fizz}};",
-            r"use std::foo::bar::{Qux, quux::{Fez, Fizz}, Baz};",
-        )
-    }
-
-    #[test]
-    fn merge_groups_long_last_nested() {
-        check_last(
-            "std::foo::bar::Baz",
-            r"use std::foo::bar::{Qux, quux::{Fez, Fizz}};",
-            r"use std::foo::bar::Baz;
-use std::foo::bar::{Qux, quux::{Fez, Fizz}};",
-        )
-    }
-
-    #[test]
-    fn merge_groups_skip_pub() {
-        check_full(
-            "std::io",
-            r"pub use std::fmt::{Result, Display};",
-            r"pub use std::fmt::{Result, Display};
-use std::io;",
-        )
-    }
-
-    #[test]
-    fn merge_groups_skip_pub_crate() {
-        check_full(
-            "std::io",
-            r"pub(crate) use std::fmt::{Result, Display};",
-            r"pub(crate) use std::fmt::{Result, Display};
-use std::io;",
-        )
-    }
-
-    #[test]
-    #[ignore] // FIXME: Support this
-    fn split_out_merge() {
-        check_last(
-            "std::fmt::Result",
-            r"use std::{fmt, io};",
-            r"use std::{self, fmt::Result};
-use std::io;",
-        )
-    }
-
-    #[test]
-    fn merge_groups_self() {
-        check_full("std::fmt::Debug", r"use std::fmt;", r"use std::fmt::{self, Debug};")
-    }
-
-    #[test]
-    fn merge_self_glob() {
-        check_full(
-            "token::TokenKind",
-            r"use token::TokenKind::*;",
-            r"use token::TokenKind::{self::*, self};",
-        )
-    }
-
-    #[test]
-    fn merge_last_too_long() {
-        mark::check!(test_last_merge_too_long);
-        check_last(
-            "foo::bar",
-            r"use foo::bar::baz::Qux;",
-            r"use foo::bar::baz::Qux;
-use foo::bar;",
-        );
-    }
-
-    fn check(
-        path: &str,
-        ra_fixture_before: &str,
-        ra_fixture_after: &str,
-        mb: Option<MergeBehaviour>,
-    ) {
-        let file = super::ImportScope::from(
-            ast::SourceFile::parse(ra_fixture_before).tree().syntax().clone(),
-        )
-        .unwrap();
-        let path = ast::SourceFile::parse(&format!("use {};", path))
-            .tree()
-            .syntax()
-            .descendants()
-            .find_map(ast::Path::cast)
-            .unwrap();
-
-        let result = insert_use(&file, path, mb).to_string();
-        assert_eq_text!(&result, ra_fixture_after);
-    }
-
-    fn check_full(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) {
-        check(path, ra_fixture_before, ra_fixture_after, Some(MergeBehaviour::Full))
-    }
-
-    fn check_last(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) {
-        check(path, ra_fixture_before, ra_fixture_after, Some(MergeBehaviour::Last))
+/// If the node is on the beginning of the line, calculate indent.
+fn leading_indent(node: &SyntaxNode) -> Option<SmolStr> {
+    for token in prev_tokens(node.first_token()?) {
+        if let Some(ws) = ast::Whitespace::cast(token.clone()) {
+            let ws_text = ws.text();
+            if let Some(pos) = ws_text.rfind('\n') {
+                return Some(ws_text[pos + 1..].into());
+            }
+        }
+        if token.text().contains('\n') {
+            break;
+        }
     }
-
-    fn check_none(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) {
-        check(path, ra_fixture_before, ra_fixture_after, None)
+    return None;
+    fn prev_tokens(token: SyntaxToken) -> impl Iterator<Item = SyntaxToken> {
+        successors(token.prev_token(), |token| token.prev_token())
     }
 }
diff --git a/crates/hir_ty/Cargo.toml b/crates/hir_ty/Cargo.toml
index 7d02aaf955..47940c36e7 100644
--- a/crates/hir_ty/Cargo.toml
+++ b/crates/hir_ty/Cargo.toml
@@ -17,9 +17,9 @@ ena = "0.14.0"
 log = "0.4.8"
 rustc-hash = "1.1.0"
 scoped-tls = "1"
-chalk-solve = { version = "0.25.0" }
-chalk-ir = { version = "0.25.0" }
-chalk-recursive = { version = "0.25.0" }
+chalk-solve = { version = "0.23.0" }
+chalk-ir = { version = "0.23.0" }
+chalk-recursive = { version = "0.23.0" }
 
 stdx = { path = "../stdx", version = "0.0.0" }
 hir_def = { path = "../hir_def", version = "0.0.0" }
diff --git a/crates/hir_ty/src/traits/chalk.rs b/crates/hir_ty/src/traits/chalk.rs
index 17c83b6a46..01b5717a3f 100644
--- a/crates/hir_ty/src/traits/chalk.rs
+++ b/crates/hir_ty/src/traits/chalk.rs
@@ -244,13 +244,17 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
         let id = from_chalk(self.db, trait_id);
         self.db.trait_data(id).name.to_string()
     }
-    // FIXME: lookup names
-    fn adt_name(&self, struct_id: chalk_ir::AdtId<Interner>) -> String {
-        let datum = self.db.struct_datum(self.krate, struct_id);
-        format!("{:?}", datum.name(&Interner))
+    fn adt_name(&self, adt_id: chalk_ir::AdtId<Interner>) -> String {
+        let id = from_chalk(self.db, adt_id);
+        match id {
+            hir_def::AdtId::StructId(id) => self.db.struct_data(id).name.to_string(),
+            hir_def::AdtId::EnumId(id) => self.db.enum_data(id).name.to_string(),
+            hir_def::AdtId::UnionId(id) => self.db.union_data(id).name.to_string(),
+        }
     }
     fn assoc_type_name(&self, assoc_ty_id: chalk_ir::AssocTypeId<Interner>) -> String {
-        format!("Assoc_{}", assoc_ty_id.0)
+        let id = self.db.associated_ty_data(assoc_ty_id).name;
+        self.db.type_alias_data(id).name.to_string()
     }
     fn opaque_type_name(&self, opaque_ty_id: chalk_ir::OpaqueTyId<Interner>) -> String {
         format!("Opaque_{}", opaque_ty_id.0)
diff --git a/crates/hir_ty/src/traits/chalk/interner.rs b/crates/hir_ty/src/traits/chalk/interner.rs
index fc0f9c2019..eb35db3ffc 100644
--- a/crates/hir_ty/src/traits/chalk/interner.rs
+++ b/crates/hir_ty/src/traits/chalk/interner.rs
@@ -26,7 +26,7 @@ pub type OpaqueTyId = chalk_ir::OpaqueTyId<Interner>;
 pub type OpaqueTyDatum = chalk_solve::rust_ir::OpaqueTyDatum<Interner>;
 
 impl chalk_ir::interner::Interner for Interner {
-    type InternedType = Box<chalk_ir::TyData<Self>>; // FIXME use Arc?
+    type InternedType = Arc<chalk_ir::TyData<Self>>;
     type InternedLifetime = chalk_ir::LifetimeData<Self>;
     type InternedConst = Arc<chalk_ir::ConstData<Self>>;
     type InternedConcreteConst = ();
@@ -34,7 +34,7 @@ impl chalk_ir::interner::Interner for Interner {
     type InternedGoal = Arc<GoalData<Self>>;
     type InternedGoals = Vec<Goal<Self>>;
     type InternedSubstitution = Vec<GenericArg<Self>>;
-    type InternedProgramClause = chalk_ir::ProgramClauseData<Self>;
+    type InternedProgramClause = Arc<chalk_ir::ProgramClauseData<Self>>;
     type InternedProgramClauses = Arc<[chalk_ir::ProgramClause<Self>]>;
     type InternedQuantifiedWhereClauses = Vec<chalk_ir::QuantifiedWhereClause<Self>>;
     type InternedVariableKinds = Vec<chalk_ir::VariableKind<Self>>;
@@ -197,11 +197,11 @@ impl chalk_ir::interner::Interner for Interner {
         tls::with_current_program(|prog| Some(prog?.debug_quantified_where_clauses(clauses, fmt)))
     }
 
-    fn intern_ty(&self, ty: chalk_ir::TyData<Self>) -> Box<chalk_ir::TyData<Self>> {
-        Box::new(ty)
+    fn intern_ty(&self, ty: chalk_ir::TyData<Self>) -> Arc<chalk_ir::TyData<Self>> {
+        Arc::new(ty)
     }
 
-    fn ty_data<'a>(&self, ty: &'a Box<chalk_ir::TyData<Self>>) -> &'a chalk_ir::TyData<Self> {
+    fn ty_data<'a>(&self, ty: &'a Arc<chalk_ir::TyData<Self>>) -> &'a chalk_ir::TyData<Self> {
         ty
     }
 
@@ -230,7 +230,7 @@ impl chalk_ir::interner::Interner for Interner {
         constant
     }
 
-    fn const_eq(&self, _ty: &Box<chalk_ir::TyData<Self>>, _c1: &(), _c2: &()) -> bool {
+    fn const_eq(&self, _ty: &Arc<chalk_ir::TyData<Self>>, _c1: &(), _c2: &()) -> bool {
         true
     }
 
@@ -284,13 +284,13 @@ impl chalk_ir::interner::Interner for Interner {
     fn intern_program_clause(
         &self,
         data: chalk_ir::ProgramClauseData<Self>,
-    ) -> chalk_ir::ProgramClauseData<Self> {
-        data
+    ) -> Arc<chalk_ir::ProgramClauseData<Self>> {
+        Arc::new(data)
     }
 
     fn program_clause_data<'a>(
         &self,
-        clause: &'a chalk_ir::ProgramClauseData<Self>,
+        clause: &'a Arc<chalk_ir::ProgramClauseData<Self>>,
     ) -> &'a chalk_ir::ProgramClauseData<Self> {
         clause
     }
diff --git a/crates/hir_ty/src/traits/chalk/mapping.rs b/crates/hir_ty/src/traits/chalk/mapping.rs
index fe62f3fa7c..d6bacba1d3 100644
--- a/crates/hir_ty/src/traits/chalk/mapping.rs
+++ b/crates/hir_ty/src/traits/chalk/mapping.rs
@@ -464,6 +464,18 @@ impl ToChalk for hir_def::ImplId {
     }
 }
 
+impl ToChalk for hir_def::AdtId {
+    type Chalk = AdtId;
+
+    fn to_chalk(self, _db: &dyn HirDatabase) -> Self::Chalk {
+        chalk_ir::AdtId(self.into())
+    }
+
+    fn from_chalk(_db: &dyn HirDatabase, id: AdtId) -> Self {
+        id.0
+    }
+}
+
 impl ToChalk for CallableDefId {
     type Chalk = FnDefId;
 
diff --git a/crates/parser/src/syntax_kind/generated.rs b/crates/parser/src/syntax_kind/generated.rs
index 935bd2c5e9..192ecd864e 100644
--- a/crates/parser/src/syntax_kind/generated.rs
+++ b/crates/parser/src/syntax_kind/generated.rs
@@ -364,4 +364,4 @@ impl SyntaxKind {
     }
 }
 #[macro_export]
-macro_rules ! T { [;] => { $ crate :: SyntaxKind :: SEMICOLON } ; [,] => { $ crate :: SyntaxKind :: COMMA } ; ['('] => { $ crate :: SyntaxKind :: L_PAREN } ; [')'] => { $ crate :: SyntaxKind :: R_PAREN } ; ['{'] => { $ crate :: SyntaxKind :: L_CURLY } ; ['}'] => { $ crate :: SyntaxKind :: R_CURLY } ; ['['] => { $ crate :: SyntaxKind :: L_BRACK } ; [']'] => { $ crate :: SyntaxKind :: R_BRACK } ; [<] => { $ crate :: SyntaxKind :: L_ANGLE } ; [>] => { $ crate :: SyntaxKind :: R_ANGLE } ; [@] => { $ crate :: SyntaxKind :: AT } ; [#] => { $ crate :: SyntaxKind :: POUND } ; [~] => { $ crate :: SyntaxKind :: TILDE } ; [?] => { $ crate :: SyntaxKind :: QUESTION } ; [$] => { $ crate :: SyntaxKind :: DOLLAR } ; [&] => { $ crate :: SyntaxKind :: AMP } ; [|] => { $ crate :: SyntaxKind :: PIPE } ; [+] => { $ crate :: SyntaxKind :: PLUS } ; [*] => { $ crate :: SyntaxKind :: STAR } ; [/] => { $ crate :: SyntaxKind :: SLASH } ; [^] => { $ crate :: SyntaxKind :: CARET } ; [%] => { $ crate :: SyntaxKind :: PERCENT } ; [_] => { $ crate :: SyntaxKind :: UNDERSCORE } ; [.] => { $ crate :: SyntaxKind :: DOT } ; [..] => { $ crate :: SyntaxKind :: DOT2 } ; [...] => { $ crate :: SyntaxKind :: DOT3 } ; [..=] => { $ crate :: SyntaxKind :: DOT2EQ } ; [:] => { $ crate :: SyntaxKind :: COLON } ; [::] => { $ crate :: SyntaxKind :: COLON2 } ; [=] => { $ crate :: SyntaxKind :: EQ } ; [==] => { $ crate :: SyntaxKind :: EQ2 } ; [=>] => { $ crate :: SyntaxKind :: FAT_ARROW } ; [!] => { $ crate :: SyntaxKind :: BANG } ; [!=] => { $ crate :: SyntaxKind :: NEQ } ; [-] => { $ crate :: SyntaxKind :: MINUS } ; [->] => { $ crate :: SyntaxKind :: THIN_ARROW } ; [<=] => { $ crate :: SyntaxKind :: LTEQ } ; [>=] => { $ crate :: SyntaxKind :: GTEQ } ; [+=] => { $ crate :: SyntaxKind :: PLUSEQ } ; [-=] => { $ crate :: SyntaxKind :: MINUSEQ } ; [|=] => { $ crate :: SyntaxKind :: PIPEEQ } ; [&=] => { $ crate :: SyntaxKind :: AMPEQ } ; [^=] => { $ crate :: SyntaxKind :: CARETEQ } ; [/=] => { $ crate :: SyntaxKind :: SLASHEQ } ; [*=] => { $ crate :: SyntaxKind :: STAREQ } ; [%=] => { $ crate :: SyntaxKind :: PERCENTEQ } ; [&&] => { $ crate :: SyntaxKind :: AMP2 } ; [||] => { $ crate :: SyntaxKind :: PIPE2 } ; [<<] => { $ crate :: SyntaxKind :: SHL } ; [>>] => { $ crate :: SyntaxKind :: SHR } ; [<<=] => { $ crate :: SyntaxKind :: SHLEQ } ; [>>=] => { $ crate :: SyntaxKind :: SHREQ } ; [as] => { $ crate :: SyntaxKind :: AS_KW } ; [async] => { $ crate :: SyntaxKind :: ASYNC_KW } ; [await] => { $ crate :: SyntaxKind :: AWAIT_KW } ; [box] => { $ crate :: SyntaxKind :: BOX_KW } ; [break] => { $ crate :: SyntaxKind :: BREAK_KW } ; [const] => { $ crate :: SyntaxKind :: CONST_KW } ; [continue] => { $ crate :: SyntaxKind :: CONTINUE_KW } ; [crate] => { $ crate :: SyntaxKind :: CRATE_KW } ; [dyn] => { $ crate :: SyntaxKind :: DYN_KW } ; [else] => { $ crate :: SyntaxKind :: ELSE_KW } ; [enum] => { $ crate :: SyntaxKind :: ENUM_KW } ; [extern] => { $ crate :: SyntaxKind :: EXTERN_KW } ; [false] => { $ crate :: SyntaxKind :: FALSE_KW } ; [fn] => { $ crate :: SyntaxKind :: FN_KW } ; [for] => { $ crate :: SyntaxKind :: FOR_KW } ; [if] => { $ crate :: SyntaxKind :: IF_KW } ; [impl] => { $ crate :: SyntaxKind :: IMPL_KW } ; [in] => { $ crate :: SyntaxKind :: IN_KW } ; [let] => { $ crate :: SyntaxKind :: LET_KW } ; [loop] => { $ crate :: SyntaxKind :: LOOP_KW } ; [macro] => { $ crate :: SyntaxKind :: MACRO_KW } ; [match] => { $ crate :: SyntaxKind :: MATCH_KW } ; [mod] => { $ crate :: SyntaxKind :: MOD_KW } ; [move] => { $ crate :: SyntaxKind :: MOVE_KW } ; [mut] => { $ crate :: SyntaxKind :: MUT_KW } ; [pub] => { $ crate :: SyntaxKind :: PUB_KW } ; [ref] => { $ crate :: SyntaxKind :: REF_KW } ; [return] => { $ crate :: SyntaxKind :: RETURN_KW } ; [self] => { $ crate :: SyntaxKind :: SELF_KW } ; [static] => { $ crate :: SyntaxKind :: STATIC_KW } ; [struct] => { $ crate :: SyntaxKind :: STRUCT_KW } ; [super] => { $ crate :: SyntaxKind :: SUPER_KW } ; [trait] => { $ crate :: SyntaxKind :: TRAIT_KW } ; [true] => { $ crate :: SyntaxKind :: TRUE_KW } ; [try] => { $ crate :: SyntaxKind :: TRY_KW } ; [type] => { $ crate :: SyntaxKind :: TYPE_KW } ; [unsafe] => { $ crate :: SyntaxKind :: UNSAFE_KW } ; [use] => { $ crate :: SyntaxKind :: USE_KW } ; [where] => { $ crate :: SyntaxKind :: WHERE_KW } ; [while] => { $ crate :: SyntaxKind :: WHILE_KW } ; [auto] => { $ crate :: SyntaxKind :: AUTO_KW } ; [default] => { $ crate :: SyntaxKind :: DEFAULT_KW } ; [existential] => { $ crate :: SyntaxKind :: EXISTENTIAL_KW } ; [union] => { $ crate :: SyntaxKind :: UNION_KW } ; [raw] => { $ crate :: SyntaxKind :: RAW_KW } ; [lifetime] => { $ crate :: SyntaxKind :: LIFETIME } ; [ident] => { $ crate :: SyntaxKind :: IDENT } ; [shebang] => { $ crate :: SyntaxKind :: SHEBANG } ; }
+macro_rules ! T { [ ; ] => { $ crate :: SyntaxKind :: SEMICOLON } ; [ , ] => { $ crate :: SyntaxKind :: COMMA } ; [ '(' ] => { $ crate :: SyntaxKind :: L_PAREN } ; [ ')' ] => { $ crate :: SyntaxKind :: R_PAREN } ; [ '{' ] => { $ crate :: SyntaxKind :: L_CURLY } ; [ '}' ] => { $ crate :: SyntaxKind :: R_CURLY } ; [ '[' ] => { $ crate :: SyntaxKind :: L_BRACK } ; [ ']' ] => { $ crate :: SyntaxKind :: R_BRACK } ; [ < ] => { $ crate :: SyntaxKind :: L_ANGLE } ; [ > ] => { $ crate :: SyntaxKind :: R_ANGLE } ; [ @ ] => { $ crate :: SyntaxKind :: AT } ; [ # ] => { $ crate :: SyntaxKind :: POUND } ; [ ~ ] => { $ crate :: SyntaxKind :: TILDE } ; [ ? ] => { $ crate :: SyntaxKind :: QUESTION } ; [ $ ] => { $ crate :: SyntaxKind :: DOLLAR } ; [ & ] => { $ crate :: SyntaxKind :: AMP } ; [ | ] => { $ crate :: SyntaxKind :: PIPE } ; [ + ] => { $ crate :: SyntaxKind :: PLUS } ; [ * ] => { $ crate :: SyntaxKind :: STAR } ; [ / ] => { $ crate :: SyntaxKind :: SLASH } ; [ ^ ] => { $ crate :: SyntaxKind :: CARET } ; [ % ] => { $ crate :: SyntaxKind :: PERCENT } ; [ _ ] => { $ crate :: SyntaxKind :: UNDERSCORE } ; [ . ] => { $ crate :: SyntaxKind :: DOT } ; [ .. ] => { $ crate :: SyntaxKind :: DOT2 } ; [ ... ] => { $ crate :: SyntaxKind :: DOT3 } ; [ ..= ] => { $ crate :: SyntaxKind :: DOT2EQ } ; [ : ] => { $ crate :: SyntaxKind :: COLON } ; [ :: ] => { $ crate :: SyntaxKind :: COLON2 } ; [ = ] => { $ crate :: SyntaxKind :: EQ } ; [ == ] => { $ crate :: SyntaxKind :: EQ2 } ; [ => ] => { $ crate :: SyntaxKind :: FAT_ARROW } ; [ ! ] => { $ crate :: SyntaxKind :: BANG } ; [ != ] => { $ crate :: SyntaxKind :: NEQ } ; [ - ] => { $ crate :: SyntaxKind :: MINUS } ; [ -> ] => { $ crate :: SyntaxKind :: THIN_ARROW } ; [ <= ] => { $ crate :: SyntaxKind :: LTEQ } ; [ >= ] => { $ crate :: SyntaxKind :: GTEQ } ; [ += ] => { $ crate :: SyntaxKind :: PLUSEQ } ; [ -= ] => { $ crate :: SyntaxKind :: MINUSEQ } ; [ |= ] => { $ crate :: SyntaxKind :: PIPEEQ } ; [ &= ] => { $ crate :: SyntaxKind :: AMPEQ } ; [ ^= ] => { $ crate :: SyntaxKind :: CARETEQ } ; [ /= ] => { $ crate :: SyntaxKind :: SLASHEQ } ; [ *= ] => { $ crate :: SyntaxKind :: STAREQ } ; [ %= ] => { $ crate :: SyntaxKind :: PERCENTEQ } ; [ && ] => { $ crate :: SyntaxKind :: AMP2 } ; [ || ] => { $ crate :: SyntaxKind :: PIPE2 } ; [ << ] => { $ crate :: SyntaxKind :: SHL } ; [ >> ] => { $ crate :: SyntaxKind :: SHR } ; [ <<= ] => { $ crate :: SyntaxKind :: SHLEQ } ; [ >>= ] => { $ crate :: SyntaxKind :: SHREQ } ; [ as ] => { $ crate :: SyntaxKind :: AS_KW } ; [ async ] => { $ crate :: SyntaxKind :: ASYNC_KW } ; [ await ] => { $ crate :: SyntaxKind :: AWAIT_KW } ; [ box ] => { $ crate :: SyntaxKind :: BOX_KW } ; [ break ] => { $ crate :: SyntaxKind :: BREAK_KW } ; [ const ] => { $ crate :: SyntaxKind :: CONST_KW } ; [ continue ] => { $ crate :: SyntaxKind :: CONTINUE_KW } ; [ crate ] => { $ crate :: SyntaxKind :: CRATE_KW } ; [ dyn ] => { $ crate :: SyntaxKind :: DYN_KW } ; [ else ] => { $ crate :: SyntaxKind :: ELSE_KW } ; [ enum ] => { $ crate :: SyntaxKind :: ENUM_KW } ; [ extern ] => { $ crate :: SyntaxKind :: EXTERN_KW } ; [ false ] => { $ crate :: SyntaxKind :: FALSE_KW } ; [ fn ] => { $ crate :: SyntaxKind :: FN_KW } ; [ for ] => { $ crate :: SyntaxKind :: FOR_KW } ; [ if ] => { $ crate :: SyntaxKind :: IF_KW } ; [ impl ] => { $ crate :: SyntaxKind :: IMPL_KW } ; [ in ] => { $ crate :: SyntaxKind :: IN_KW } ; [ let ] => { $ crate :: SyntaxKind :: LET_KW } ; [ loop ] => { $ crate :: SyntaxKind :: LOOP_KW } ; [ macro ] => { $ crate :: SyntaxKind :: MACRO_KW } ; [ match ] => { $ crate :: SyntaxKind :: MATCH_KW } ; [ mod ] => { $ crate :: SyntaxKind :: MOD_KW } ; [ move ] => { $ crate :: SyntaxKind :: MOVE_KW } ; [ mut ] => { $ crate :: SyntaxKind :: MUT_KW } ; [ pub ] => { $ crate :: SyntaxKind :: PUB_KW } ; [ ref ] => { $ crate :: SyntaxKind :: REF_KW } ; [ return ] => { $ crate :: SyntaxKind :: RETURN_KW } ; [ self ] => { $ crate :: SyntaxKind :: SELF_KW } ; [ static ] => { $ crate :: SyntaxKind :: STATIC_KW } ; [ struct ] => { $ crate :: SyntaxKind :: STRUCT_KW } ; [ super ] => { $ crate :: SyntaxKind :: SUPER_KW } ; [ trait ] => { $ crate :: SyntaxKind :: TRAIT_KW } ; [ true ] => { $ crate :: SyntaxKind :: TRUE_KW } ; [ try ] => { $ crate :: SyntaxKind :: TRY_KW } ; [ type ] => { $ crate :: SyntaxKind :: TYPE_KW } ; [ unsafe ] => { $ crate :: SyntaxKind :: UNSAFE_KW } ; [ use ] => { $ crate :: SyntaxKind :: USE_KW } ; [ where ] => { $ crate :: SyntaxKind :: WHERE_KW } ; [ while ] => { $ crate :: SyntaxKind :: WHILE_KW } ; [ auto ] => { $ crate :: SyntaxKind :: AUTO_KW } ; [ default ] => { $ crate :: SyntaxKind :: DEFAULT_KW } ; [ existential ] => { $ crate :: SyntaxKind :: EXISTENTIAL_KW } ; [ union ] => { $ crate :: SyntaxKind :: UNION_KW } ; [ raw ] => { $ crate :: SyntaxKind :: RAW_KW } ; [ lifetime ] => { $ crate :: SyntaxKind :: LIFETIME } ; [ ident ] => { $ crate :: SyntaxKind :: IDENT } ; [ shebang ] => { $ crate :: SyntaxKind :: SHEBANG } ; }
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index 187273bbcb..99f7751acd 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -402,7 +402,7 @@ config_data! {
         cargo_noDefaultFeatures: bool    = false,
         cargo_target: Option<String>     = None,
 
-        checkOnSave_enable: bool                         = true,
+        checkOnSave_enable: bool                         = false,
         checkOnSave_allFeatures: Option<bool>            = None,
         checkOnSave_allTargets: bool                     = true,
         checkOnSave_command: String                      = "check".into(),
diff --git a/crates/syntax/src/ast/edit.rs b/crates/syntax/src/ast/edit.rs
index 8b1c65dd6f..060b209663 100644
--- a/crates/syntax/src/ast/edit.rs
+++ b/crates/syntax/src/ast/edit.rs
@@ -13,7 +13,7 @@ use crate::{
     ast::{
         self,
         make::{self, tokens},
-        AstNode, GenericParamsOwner, NameOwner, TypeBoundsOwner,
+        AstNode, TypeBoundsOwner,
     },
     AstToken, Direction, InsertPosition, SmolStr, SyntaxElement, SyntaxKind,
     SyntaxKind::{ATTR, COMMENT, WHITESPACE},
@@ -46,19 +46,6 @@ impl ast::Fn {
         to_insert.push(body.syntax().clone().into());
         self.replace_children(single_node(old_body_or_semi), to_insert)
     }
-
-    #[must_use]
-    pub fn with_generic_param_list(&self, generic_args: ast::GenericParamList) -> ast::Fn {
-        if let Some(old) = self.generic_param_list() {
-            return self.replace_descendant(old, generic_args);
-        }
-
-        let anchor = self.name().expect("The function must have a name").syntax().clone();
-
-        let mut to_insert: ArrayVec<[SyntaxElement; 1]> = ArrayVec::new();
-        to_insert.push(generic_args.syntax().clone().into());
-        self.insert_children(InsertPosition::After(anchor.into()), to_insert)
-    }
 }
 
 fn make_multiline<N>(node: N) -> N
@@ -273,16 +260,16 @@ impl ast::Path {
 
 impl ast::PathSegment {
     #[must_use]
-    pub fn with_generic_args(&self, type_args: ast::GenericArgList) -> ast::PathSegment {
-        self._with_generic_args(type_args, false)
+    pub fn with_type_args(&self, type_args: ast::GenericArgList) -> ast::PathSegment {
+        self._with_type_args(type_args, false)
     }
 
     #[must_use]
     pub fn with_turbo_fish(&self, type_args: ast::GenericArgList) -> ast::PathSegment {
-        self._with_generic_args(type_args, true)
+        self._with_type_args(type_args, true)
     }
 
-    fn _with_generic_args(&self, type_args: ast::GenericArgList, turbo: bool) -> ast::PathSegment {
+    fn _with_type_args(&self, type_args: ast::GenericArgList, turbo: bool) -> ast::PathSegment {
         if let Some(old) = self.generic_arg_list() {
             return self.replace_children(
                 single_node(old.syntax().clone()),
@@ -472,61 +459,6 @@ impl ast::MatchArmList {
     }
 }
 
-impl ast::GenericParamList {
-    #[must_use]
-    pub fn append_params(
-        &self,
-        params: impl IntoIterator<Item = ast::GenericParam>,
-    ) -> ast::GenericParamList {
-        let mut res = self.clone();
-        params.into_iter().for_each(|it| res = res.append_param(it));
-        res
-    }
-
-    #[must_use]
-    pub fn append_param(&self, item: ast::GenericParam) -> ast::GenericParamList {
-        let space = tokens::single_space();
-
-        let mut to_insert: ArrayVec<[SyntaxElement; 4]> = ArrayVec::new();
-        if self.generic_params().next().is_some() {
-            to_insert.push(space.into());
-        }
-        to_insert.push(item.syntax().clone().into());
-
-        macro_rules! after_l_angle {
-            () => {{
-                let anchor = match self.l_angle_token() {
-                    Some(it) => it.into(),
-                    None => return self.clone(),
-                };
-                InsertPosition::After(anchor)
-            }};
-        }
-
-        macro_rules! after_field {
-            ($anchor:expr) => {
-                if let Some(comma) = $anchor
-                    .syntax()
-                    .siblings_with_tokens(Direction::Next)
-                    .find(|it| it.kind() == T![,])
-                {
-                    InsertPosition::After(comma)
-                } else {
-                    to_insert.insert(0, make::token(T![,]).into());
-                    InsertPosition::After($anchor.syntax().clone().into())
-                }
-            };
-        };
-
-        let position = match self.generic_params().last() {
-            Some(it) => after_field!(it),
-            None => after_l_angle!(),
-        };
-
-        self.insert_children(position, to_insert)
-    }
-}
-
 #[must_use]
 pub fn remove_attrs_and_docs<N: ast::AttrsOwner>(node: &N) -> N {
     N::cast(remove_attrs_and_docs_inner(node.syntax().clone())).unwrap()
diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs
index 25e8a359d9..c2c938ad11 100644
--- a/crates/syntax/src/ast/make.rs
+++ b/crates/syntax/src/ast/make.rs
@@ -294,21 +294,6 @@ pub fn param_list(pats: impl IntoIterator<Item = ast::Param>) -> ast::ParamList
     ast_from_text(&format!("fn f({}) {{ }}", args))
 }
 
-pub fn generic_param(name: String, ty: Option<ast::TypeBoundList>) -> ast::GenericParam {
-    let bound = match ty {
-        Some(it) => format!(": {}", it),
-        None => String::new(),
-    };
-    ast_from_text(&format!("fn f<{}{}>() {{ }}", name, bound))
-}
-
-pub fn generic_param_list(
-    pats: impl IntoIterator<Item = ast::GenericParam>,
-) -> ast::GenericParamList {
-    let args = pats.into_iter().join(", ");
-    ast_from_text(&format!("fn f<{}>() {{ }}", args))
-}
-
 pub fn visibility_pub_crate() -> ast::Visibility {
     ast_from_text("pub(crate) struct S")
 }
@@ -354,7 +339,7 @@ pub mod tokens {
     use crate::{ast, AstNode, Parse, SourceFile, SyntaxKind::*, SyntaxToken};
 
     pub(super) static SOURCE_FILE: Lazy<Parse<SourceFile>> =
-        Lazy::new(|| SourceFile::parse("const C: <()>::Item = (1 != 1, 2 == 2, !true)\n;\n\n"));
+        Lazy::new(|| SourceFile::parse("const C: <()>::Item = (1 != 1, 2 == 2, !true)\n;"));
 
     pub fn single_space() -> SyntaxToken {
         SOURCE_FILE
@@ -394,16 +379,6 @@ pub mod tokens {
             .unwrap()
     }
 
-    pub fn blank_line() -> SyntaxToken {
-        SOURCE_FILE
-            .tree()
-            .syntax()
-            .descendants_with_tokens()
-            .filter_map(|it| it.into_token())
-            .find(|it| it.kind() == WHITESPACE && it.text().as_str() == "\n\n")
-            .unwrap()
-    }
-
     pub struct WsBuilder(SourceFile);
 
     impl WsBuilder {
diff --git a/crates/text_edit/src/lib.rs b/crates/text_edit/src/lib.rs
index eb3c8caa2a..e417e8ea62 100644
--- a/crates/text_edit/src/lib.rs
+++ b/crates/text_edit/src/lib.rs
@@ -119,7 +119,7 @@ impl TextEdit {
             return Err(other);
         }
         self.indels.extend(other.indels);
-        assert_disjoint(&mut self.indels);
+        assert!(check_disjoint(&mut self.indels));
         Ok(())
     }
 
@@ -169,7 +169,7 @@ impl TextEditBuilder {
     }
     pub fn finish(self) -> TextEdit {
         let mut indels = self.indels;
-        assert_disjoint(&mut indels);
+        assert!(check_disjoint(&mut indels));
         TextEdit { indels }
     }
     pub fn invalidates_offset(&self, offset: TextSize) -> bool {
@@ -178,14 +178,11 @@ impl TextEditBuilder {
     fn indel(&mut self, indel: Indel) {
         self.indels.push(indel);
         if self.indels.len() <= 16 {
-            assert_disjoint(&mut self.indels);
+            check_disjoint(&mut self.indels);
         }
     }
 }
 
-fn assert_disjoint(indels: &mut [impl std::borrow::Borrow<Indel>]) {
-    assert!(check_disjoint(indels));
-}
 fn check_disjoint(indels: &mut [impl std::borrow::Borrow<Indel>]) -> bool {
     indels.sort_by_key(|indel| (indel.borrow().delete.start(), indel.borrow().delete.end()));
     indels
diff --git a/xtask/src/codegen/gen_syntax.rs b/xtask/src/codegen/gen_syntax.rs
index 733493fef0..200e8aa504 100644
--- a/xtask/src/codegen/gen_syntax.rs
+++ b/xtask/src/codegen/gen_syntax.rs
@@ -247,14 +247,14 @@ fn generate_nodes(kinds: KindsSrc<'_>, grammar: &AstSrc) -> Result<String> {
         #(#display_impls)*
     };
 
-    let ast = ast.to_string().replace("T ! [", "T![");
+    let ast = ast.to_string().replace("T ! [ ", "T![").replace(" ] )", "])");
 
     let mut res = String::with_capacity(ast.len() * 2);
 
     let mut docs =
         grammar.nodes.iter().map(|it| &it.doc).chain(grammar.enums.iter().map(|it| &it.doc));
 
-    for chunk in ast.split("# [pretty_doc_comment_placeholder_workaround] ") {
+    for chunk in ast.split("# [ pretty_doc_comment_placeholder_workaround ]") {
         res.push_str(chunk);
         if let Some(doc) = docs.next() {
             write_doc_comment(&doc, &mut res);


Test patch:
diff --git a/crates/assists/src/tests/generated.rs b/crates/assists/src/tests/generated.rs
index 27d15adb08..04c8fd1f94 100644
--- a/crates/assists/src/tests/generated.rs
+++ b/crates/assists/src/tests/generated.rs
@@ -814,19 +814,6 @@ fn handle(action: Action) {
     )
 }
 
-#[test]
-fn doctest_replace_impl_trait_with_generic() {
-    check_doc_test(
-        "replace_impl_trait_with_generic",
-        r#####"
-fn foo(bar: <|>impl Bar) {}
-"#####,
-        r#####"
-fn foo<B: Bar>(bar: B) {}
-"#####,
-    )
-}
-
 #[test]
 fn doctest_replace_let_with_if_let() {
     check_doc_test(