Repository: rust-analyzer/rust-analyzer
Pull request #6512 (2020-11-09 17:44:35)

Don't replace parent node when inserting as first child in algo::diff
This makes the diff a bit more detailed.

See https://github.com/rust-analyzer/rust-analyzer/pull/6287#issuecomment-723889267 for context
cc @SomeoneToIgnore 


Patch:
diff --git a/crates/assists/src/assist_context.rs b/crates/assists/src/assist_context.rs
index 69499ea326..fcfe2d6ee4 100644
--- a/crates/assists/src/assist_context.rs
+++ b/crates/assists/src/assist_context.rs
@@ -208,7 +208,7 @@ pub(crate) struct AssistBuilder {
     edit: TextEditBuilder,
     file_id: FileId,
     is_snippet: bool,
-    source_file_edits: Vec<SourceFileEdit>,
+    change: SourceChange,
 }
 
 impl AssistBuilder {
@@ -217,27 +217,20 @@ impl AssistBuilder {
             edit: TextEdit::builder(),
             file_id,
             is_snippet: false,
-            source_file_edits: Vec::default(),
+            change: SourceChange::default(),
         }
     }
 
     pub(crate) fn edit_file(&mut self, file_id: FileId) {
-        self.commit();
         self.file_id = file_id;
     }
 
     fn commit(&mut self) {
         let edit = mem::take(&mut self.edit).finish();
         if !edit.is_empty() {
-            match self.source_file_edits.binary_search_by_key(&self.file_id, |edit| edit.file_id) {
-                Ok(idx) => self.source_file_edits[idx]
-                    .edit
-                    .union(edit)
-                    .expect("overlapping edits for same file"),
-                Err(idx) => self
-                    .source_file_edits
-                    .insert(idx, SourceFileEdit { file_id: self.file_id, edit }),
-            }
+            let new_edit = SourceFileEdit { file_id: self.file_id, edit };
+            assert!(!self.change.source_file_edits.iter().any(|it| it.file_id == new_edit.file_id));
+            self.change.source_file_edits.push(new_edit);
         }
     }
 
@@ -277,18 +270,23 @@ impl AssistBuilder {
         algo::diff(old.syntax(), new.syntax()).into_text_edit(&mut self.edit)
     }
     pub(crate) fn rewrite(&mut self, rewriter: SyntaxRewriter) {
-        if let Some(node) = rewriter.rewrite_root() {
-            let new = rewriter.rewrite(&node);
-            algo::diff(&node, &new).into_text_edit(&mut self.edit);
-        }
+        let node = rewriter.rewrite_root().unwrap();
+        let new = rewriter.rewrite(&node);
+        algo::diff(&node, &new).into_text_edit(&mut self.edit);
+    }
+
+    // FIXME: kill this API
+    /// Get access to the raw `TextEditBuilder`.
+    pub(crate) fn text_edit_builder(&mut self) -> &mut TextEditBuilder {
+        &mut self.edit
     }
 
     fn finish(mut self) -> SourceChange {
         self.commit();
-        SourceChange {
-            source_file_edits: mem::take(&mut self.source_file_edits),
-            file_system_edits: Default::default(),
-            is_snippet: self.is_snippet,
+        let mut change = mem::take(&mut self.change);
+        if self.is_snippet {
+            change.is_snippet = true;
         }
+        change
     }
 }
diff --git a/crates/assists/src/handlers/add_custom_impl.rs b/crates/assists/src/handlers/add_custom_impl.rs
new file mode 100644
index 0000000000..669dd9b217
--- /dev/null
+++ b/crates/assists/src/handlers/add_custom_impl.rs
@@ -0,0 +1,284 @@
+use ide_db::imports_locator;
+use itertools::Itertools;
+use syntax::{
+    ast::{self, make, AstNode},
+    Direction, SmolStr,
+    SyntaxKind::{IDENT, WHITESPACE},
+    TextRange, TextSize,
+};
+
+use crate::{
+    assist_config::SnippetCap,
+    assist_context::{AssistBuilder, AssistContext, Assists},
+    utils::mod_path_to_ast,
+    AssistId, AssistKind,
+};
+
+// Assist: add_custom_impl
+//
+// Adds impl block for derived trait.
+//
+// ```
+// #[derive(Deb<|>ug, Display)]
+// struct S;
+// ```
+// ->
+// ```
+// #[derive(Display)]
+// struct S;
+//
+// impl Debug for S {
+//     $0
+// }
+// ```
+pub(crate) fn add_custom_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
+    let attr = ctx.find_node_at_offset::<ast::Attr>()?;
+
+    let attr_name = attr
+        .syntax()
+        .descendants_with_tokens()
+        .filter(|t| t.kind() == IDENT)
+        .find_map(syntax::NodeOrToken::into_token)
+        .filter(|t| t.text() == "derive")?
+        .text()
+        .clone();
+
+    let trait_token =
+        ctx.token_at_offset().find(|t| t.kind() == IDENT && *t.text() != attr_name)?;
+    let trait_path = make::path_unqualified(make::path_segment(make::name_ref(trait_token.text())));
+
+    let annotated = attr.syntax().siblings(Direction::Next).find_map(ast::Name::cast)?;
+    let annotated_name = annotated.syntax().text().to_string();
+    let insert_pos = annotated.syntax().parent()?.text_range().end();
+
+    let current_module = ctx.sema.scope(annotated.syntax()).module()?;
+    let current_crate = current_module.krate();
+
+    let found_traits = imports_locator::find_imports(&ctx.sema, current_crate, trait_token.text())
+        .into_iter()
+        .filter_map(|candidate: either::Either<hir::ModuleDef, hir::MacroDef>| match candidate {
+            either::Either::Left(hir::ModuleDef::Trait(trait_)) => Some(trait_),
+            _ => None,
+        })
+        .flat_map(|trait_| {
+            current_module
+                .find_use_path(ctx.sema.db, hir::ModuleDef::Trait(trait_))
+                .as_ref()
+                .map(mod_path_to_ast)
+                .zip(Some(trait_))
+        });
+
+    let mut no_traits_found = true;
+    for (trait_path, _trait) in found_traits.inspect(|_| no_traits_found = false) {
+        add_assist(acc, ctx.config.snippet_cap, &attr, &trait_path, &annotated_name, insert_pos)?;
+    }
+    if no_traits_found {
+        add_assist(acc, ctx.config.snippet_cap, &attr, &trait_path, &annotated_name, insert_pos)?;
+    }
+    Some(())
+}
+
+fn add_assist(
+    acc: &mut Assists,
+    snippet_cap: Option<SnippetCap>,
+    attr: &ast::Attr,
+    trait_path: &ast::Path,
+    annotated_name: &str,
+    insert_pos: TextSize,
+) -> Option<()> {
+    let target = attr.syntax().text_range();
+    let input = attr.token_tree()?;
+    let label = format!("Add custom impl `{}` for `{}`", trait_path, annotated_name);
+    let trait_name = trait_path.segment().and_then(|seg| seg.name_ref())?;
+
+    acc.add(AssistId("add_custom_impl", AssistKind::Refactor), label, target, |builder| {
+        update_attribute(builder, &input, &trait_name, &attr);
+        match snippet_cap {
+            Some(cap) => {
+                builder.insert_snippet(
+                    cap,
+                    insert_pos,
+                    format!("\n\nimpl {} for {} {{\n    $0\n}}", trait_path, annotated_name),
+                );
+            }
+            None => {
+                builder.insert(
+                    insert_pos,
+                    format!("\n\nimpl {} for {} {{\n\n}}", trait_path, annotated_name),
+                );
+            }
+        }
+    })
+}
+
+fn update_attribute(
+    builder: &mut AssistBuilder,
+    input: &ast::TokenTree,
+    trait_name: &ast::NameRef,
+    attr: &ast::Attr,
+) {
+    let new_attr_input = input
+        .syntax()
+        .descendants_with_tokens()
+        .filter(|t| t.kind() == IDENT)
+        .filter_map(|t| t.into_token().map(|t| t.text().clone()))
+        .filter(|t| t != trait_name.text())
+        .collect::<Vec<SmolStr>>();
+    let has_more_derives = !new_attr_input.is_empty();
+
+    if has_more_derives {
+        let new_attr_input = format!("({})", new_attr_input.iter().format(", "));
+        builder.replace(input.syntax().text_range(), new_attr_input);
+    } else {
+        let attr_range = attr.syntax().text_range();
+        builder.delete(attr_range);
+
+        let line_break_range = attr
+            .syntax()
+            .next_sibling_or_token()
+            .filter(|t| t.kind() == WHITESPACE)
+            .map(|t| t.text_range())
+            .unwrap_or_else(|| TextRange::new(TextSize::from(0), TextSize::from(0)));
+        builder.delete(line_break_range);
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::tests::{check_assist, check_assist_not_applicable};
+
+    use super::*;
+
+    #[test]
+    fn add_custom_impl_qualified() {
+        check_assist(
+            add_custom_impl,
+            "
+mod fmt {
+    pub trait Debug {}
+}
+
+#[derive(Debu<|>g)]
+struct Foo {
+    bar: String,
+}
+",
+            "
+mod fmt {
+    pub trait Debug {}
+}
+
+struct Foo {
+    bar: String,
+}
+
+impl fmt::Debug for Foo {
+    $0
+}
+",
+        )
+    }
+    #[test]
+    fn add_custom_impl_for_unique_input() {
+        check_assist(
+            add_custom_impl,
+            "
+#[derive(Debu<|>g)]
+struct Foo {
+    bar: String,
+}
+            ",
+            "
+struct Foo {
+    bar: String,
+}
+
+impl Debug for Foo {
+    $0
+}
+            ",
+        )
+    }
+
+    #[test]
+    fn add_custom_impl_for_with_visibility_modifier() {
+        check_assist(
+            add_custom_impl,
+            "
+#[derive(Debug<|>)]
+pub struct Foo {
+    bar: String,
+}
+            ",
+            "
+pub struct Foo {
+    bar: String,
+}
+
+impl Debug for Foo {
+    $0
+}
+            ",
+        )
+    }
+
+    #[test]
+    fn add_custom_impl_when_multiple_inputs() {
+        check_assist(
+            add_custom_impl,
+            "
+#[derive(Display, Debug<|>, Serialize)]
+struct Foo {}
+            ",
+            "
+#[derive(Display, Serialize)]
+struct Foo {}
+
+impl Debug for Foo {
+    $0
+}
+            ",
+        )
+    }
+
+    #[test]
+    fn test_ignore_derive_macro_without_input() {
+        check_assist_not_applicable(
+            add_custom_impl,
+            "
+#[derive(<|>)]
+struct Foo {}
+            ",
+        )
+    }
+
+    #[test]
+    fn test_ignore_if_cursor_on_param() {
+        check_assist_not_applicable(
+            add_custom_impl,
+            "
+#[derive<|>(Debug)]
+struct Foo {}
+            ",
+        );
+
+        check_assist_not_applicable(
+            add_custom_impl,
+            "
+#[derive(Debug)<|>]
+struct Foo {}
+            ",
+        )
+    }
+
+    #[test]
+    fn test_ignore_if_not_derive() {
+        check_assist_not_applicable(
+            add_custom_impl,
+            "
+#[allow(non_camel_<|>case_types)]
+struct Foo {}
+            ",
+        )
+    }
+}
diff --git a/crates/assists/src/handlers/add_missing_impl_members.rs b/crates/assists/src/handlers/add_missing_impl_members.rs
index bbb71e261f..b82fb30ad4 100644
--- a/crates/assists/src/handlers/add_missing_impl_members.rs
+++ b/crates/assists/src/handlers/add_missing_impl_members.rs
@@ -1,14 +1,27 @@
-use ide_db::traits::resolve_target_trait;
-use syntax::ast::{self, AstNode};
+use hir::HasSource;
+use ide_db::traits::{get_missing_assoc_items, resolve_target_trait};
+use syntax::{
+    ast::{
+        self,
+        edit::{self, AstNodeEdit, IndentLevel},
+        make, AstNode, NameOwner,
+    },
+    SmolStr,
+};
 
 use crate::{
     assist_context::{AssistContext, Assists},
-    utils::add_trait_assoc_items_to_impl,
-    utils::DefaultMethods,
-    utils::{filter_assoc_items, render_snippet, Cursor},
+    ast_transform::{self, AstTransform, QualifyPaths, SubstituteTypeParams},
+    utils::{render_snippet, Cursor},
     AssistId, AssistKind,
 };
 
+#[derive(PartialEq)]
+enum AddMissingImplMembersMode {
+    DefaultMethodsOnly,
+    NoDefaultMethods,
+}
+
 // Assist: add_impl_missing_members
 //
 // Adds scaffold for required impl members.
@@ -42,7 +55,7 @@ pub(crate) fn add_missing_impl_members(acc: &mut Assists, ctx: &AssistContext) -
     add_missing_impl_members_inner(
         acc,
         ctx,
-        DefaultMethods::No,
+        AddMissingImplMembersMode::NoDefaultMethods,
         "add_impl_missing_members",
         "Implement missing members",
     )
@@ -84,7 +97,7 @@ pub(crate) fn add_missing_default_members(acc: &mut Assists, ctx: &AssistContext
     add_missing_impl_members_inner(
         acc,
         ctx,
-        DefaultMethods::Only,
+        AddMissingImplMembersMode::DefaultMethodsOnly,
         "add_impl_default_members",
         "Implement default members",
     )
@@ -93,7 +106,7 @@ pub(crate) fn add_missing_default_members(acc: &mut Assists, ctx: &AssistContext
 fn add_missing_impl_members_inner(
     acc: &mut Assists,
     ctx: &AssistContext,
-    mode: DefaultMethods,
+    mode: AddMissingImplMembersMode,
     assist_id: &'static str,
     label: &'static str,
 ) -> Option<()> {
@@ -101,11 +114,32 @@ fn add_missing_impl_members_inner(
     let impl_def = ctx.find_node_at_offset::<ast::Impl>()?;
     let trait_ = resolve_target_trait(&ctx.sema, &impl_def)?;
 
-    let missing_items = filter_assoc_items(
-        ctx.db(),
-        &ide_db::traits::get_missing_assoc_items(&ctx.sema, &impl_def),
-        mode,
-    );
+    let def_name = |item: &ast::AssocItem| -> Option<SmolStr> {
+        match item {
+            ast::AssocItem::Fn(def) => def.name(),
+            ast::AssocItem::TypeAlias(def) => def.name(),
+            ast::AssocItem::Const(def) => def.name(),
+            ast::AssocItem::MacroCall(_) => None,
+        }
+        .map(|it| it.text().clone())
+    };
+
+    let missing_items = get_missing_assoc_items(&ctx.sema, &impl_def)
+        .iter()
+        .map(|i| match i {
+            hir::AssocItem::Function(i) => ast::AssocItem::Fn(i.source(ctx.db()).value),
+            hir::AssocItem::TypeAlias(i) => ast::AssocItem::TypeAlias(i.source(ctx.db()).value),
+            hir::AssocItem::Const(i) => ast::AssocItem::Const(i.source(ctx.db()).value),
+        })
+        .filter(|t| def_name(&t).is_some())
+        .filter(|t| match t {
+            ast::AssocItem::Fn(def) => match mode {
+                AddMissingImplMembersMode::DefaultMethodsOnly => def.body().is_some(),
+                AddMissingImplMembersMode::NoDefaultMethods => def.body().is_none(),
+            },
+            _ => mode == AddMissingImplMembersMode::NoDefaultMethods,
+        })
+        .collect::<Vec<_>>();
 
     if missing_items.is_empty() {
         return None;
@@ -113,9 +147,29 @@ fn add_missing_impl_members_inner(
 
     let target = impl_def.syntax().text_range();
     acc.add(AssistId(assist_id, AssistKind::QuickFix), label, target, |builder| {
+        let impl_item_list = impl_def.assoc_item_list().unwrap_or_else(make::assoc_item_list);
+
+        let n_existing_items = impl_item_list.assoc_items().count();
+        let source_scope = ctx.sema.scope_for_def(trait_);
         let target_scope = ctx.sema.scope(impl_def.syntax());
-        let (new_impl_def, first_new_item) =
-            add_trait_assoc_items_to_impl(&ctx.sema, missing_items, trait_, impl_def, target_scope);
+        let ast_transform = QualifyPaths::new(&target_scope, &source_scope)
+            .or(SubstituteTypeParams::for_trait_impl(&source_scope, trait_, impl_def.clone()));
+
+        let items = missing_items
+            .into_iter()
+            .map(|it| ast_transform::apply(&*ast_transform, it))
+            .map(|it| match it {
+                ast::AssocItem::Fn(def) => ast::AssocItem::Fn(add_body(def)),
+                ast::AssocItem::TypeAlias(def) => ast::AssocItem::TypeAlias(def.remove_bounds()),
+                _ => it,
+            })
+            .map(|it| edit::remove_attrs_and_docs(&it));
+
+        let new_impl_item_list = impl_item_list.append_items(items);
+        let new_impl_def = impl_def.with_assoc_item_list(new_impl_item_list);
+        let first_new_item =
+            new_impl_def.assoc_item_list().unwrap().assoc_items().nth(n_existing_items).unwrap();
+
         match ctx.config.snippet_cap {
             None => builder.replace(target, new_impl_def.to_string()),
             Some(cap) => {
@@ -139,6 +193,14 @@ fn add_missing_impl_members_inner(
     })
 }
 
+fn add_body(fn_def: ast::Fn) -> ast::Fn {
+    if fn_def.body().is_some() {
+        return fn_def;
+    }
+    let body = make::block_expr(None, Some(make::expr_todo())).indent(IndentLevel(1));
+    fn_def.with_body(body)
+}
+
 #[cfg(test)]
 mod tests {
     use crate::tests::{check_assist, check_assist_not_applicable};
diff --git a/crates/assists/src/handlers/change_return_type_to_result.rs b/crates/assists/src/handlers/change_return_type_to_result.rs
new file mode 100644
index 0000000000..76f33a5b6d
--- /dev/null
+++ b/crates/assists/src/handlers/change_return_type_to_result.rs
@@ -0,0 +1,1091 @@
+use std::iter;
+
+use syntax::{
+    ast::{self, make, BlockExpr, Expr, LoopBodyOwner},
+    match_ast, AstNode, SyntaxNode,
+};
+use test_utils::mark;
+
+use crate::{AssistContext, AssistId, AssistKind, Assists};
+
+// Assist: change_return_type_to_result
+//
+// Change the function's return type to Result.
+//
+// ```
+// fn foo() -> i32<|> { 42i32 }
+// ```
+// ->
+// ```
+// fn foo() -> Result<i32, ${0:_}> { Ok(42i32) }
+// ```
+pub(crate) fn change_return_type_to_result(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
+    let ret_type = ctx.find_node_at_offset::<ast::RetType>()?;
+    let parent = ret_type.syntax().parent()?;
+    let block_expr = match_ast! {
+        match parent {
+            ast::Fn(func) => func.body()?,
+            ast::ClosureExpr(closure) => match closure.body()? {
+                Expr::BlockExpr(block) => block,
+                // closures require a block when a return type is specified
+                _ => return None,
+            },
+            _ => return None,
+        }
+    };
+
+    let type_ref = &ret_type.ty()?;
+    let ret_type_str = type_ref.syntax().text().to_string();
+    let first_part_ret_type = ret_type_str.splitn(2, '<').next();
+    if let Some(ret_type_first_part) = first_part_ret_type {
+        if ret_type_first_part.ends_with("Result") {
+            mark::hit!(change_return_type_to_result_simple_return_type_already_result);
+            return None;
+        }
+    }
+
+    acc.add(
+        AssistId("change_return_type_to_result", AssistKind::RefactorRewrite),
+        "Wrap return type in Result",
+        type_ref.syntax().text_range(),
+        |builder| {
+            let mut tail_return_expr_collector = TailReturnCollector::new();
+            tail_return_expr_collector.collect_jump_exprs(&block_expr, false);
+            tail_return_expr_collector.collect_tail_exprs(&block_expr);
+
+            for ret_expr_arg in tail_return_expr_collector.exprs_to_wrap {
+                let ok_wrapped = make::expr_call(
+                    make::expr_path(make::path_unqualified(make::path_segment(make::name_ref(
+                        "Ok",
+                    )))),
+                    make::arg_list(iter::once(ret_expr_arg.clone())),
+                );
+                builder.replace_ast(ret_expr_arg, ok_wrapped);
+            }
+
+            match ctx.config.snippet_cap {
+                Some(cap) => {
+                    let snippet = format!("Result<{}, ${{0:_}}>", type_ref);
+                    builder.replace_snippet(cap, type_ref.syntax().text_range(), snippet)
+                }
+                None => builder
+                    .replace(type_ref.syntax().text_range(), format!("Result<{}, _>", type_ref)),
+            }
+        },
+    )
+}
+
+struct TailReturnCollector {
+    exprs_to_wrap: Vec<ast::Expr>,
+}
+
+impl TailReturnCollector {
+    fn new() -> Self {
+        Self { exprs_to_wrap: vec![] }
+    }
+    /// Collect all`return` expression
+    fn collect_jump_exprs(&mut self, block_expr: &BlockExpr, collect_break: bool) {
+        let statements = block_expr.statements();
+        for stmt in statements {
+            let expr = match &stmt {
+                ast::Stmt::ExprStmt(stmt) => stmt.expr(),
+                ast::Stmt::LetStmt(stmt) => stmt.initializer(),
+                ast::Stmt::Item(_) => continue,
+            };
+            if let Some(expr) = &expr {
+                self.handle_exprs(expr, collect_break);
+            }
+        }
+
+        // Browse tail expressions for each block
+        if let Some(expr) = block_expr.expr() {
+            if let Some(last_exprs) = get_tail_expr_from_block(&expr) {
+                for last_expr in last_exprs {
+                    let last_expr = match last_expr {
+                        NodeType::Node(expr) => expr,
+                        NodeType::Leaf(expr) => expr.syntax().clone(),
+                    };
+
+                    if let Some(last_expr) = Expr::cast(last_expr.clone()) {
+                        self.handle_exprs(&last_expr, collect_break);
+                    } else if let Some(expr_stmt) = ast::Stmt::cast(last_expr) {
+                        let expr_stmt = match &expr_stmt {
+                            ast::Stmt::ExprStmt(stmt) => stmt.expr(),
+                            ast::Stmt::LetStmt(stmt) => stmt.initializer(),
+                            ast::Stmt::Item(_) => None,
+                        };
+                        if let Some(expr) = &expr_stmt {
+                            self.handle_exprs(expr, collect_break);
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    fn handle_exprs(&mut self, expr: &Expr, collect_break: bool) {
+        match expr {
+            Expr::BlockExpr(block_expr) => {
+                self.collect_jump_exprs(&block_expr, collect_break);
+            }
+            Expr::ReturnExpr(ret_expr) => {
+                if let Some(ret_expr_arg) = &ret_expr.expr() {
+                    self.exprs_to_wrap.push(ret_expr_arg.clone());
+                }
+            }
+            Expr::BreakExpr(break_expr) if collect_break => {
+                if let Some(break_expr_arg) = &break_expr.expr() {
+                    self.exprs_to_wrap.push(break_expr_arg.clone());
+                }
+            }
+            Expr::IfExpr(if_expr) => {
+                for block in if_expr.blocks() {
+                    self.collect_jump_exprs(&block, collect_break);
+                }
+            }
+            Expr::LoopExpr(loop_expr) => {
+                if let Some(block_expr) = loop_expr.loop_body() {
+                    self.collect_jump_exprs(&block_expr, collect_break);
+                }
+            }
+            Expr::ForExpr(for_expr) => {
+                if let Some(block_expr) = for_expr.loop_body() {
+                    self.collect_jump_exprs(&block_expr, collect_break);
+                }
+            }
+            Expr::WhileExpr(while_expr) => {
+                if let Some(block_expr) = while_expr.loop_body() {
+                    self.collect_jump_exprs(&block_expr, collect_break);
+                }
+            }
+            Expr::MatchExpr(match_expr) => {
+                if let Some(arm_list) = match_expr.match_arm_list() {
+                    arm_list.arms().filter_map(|match_arm| match_arm.expr()).for_each(|expr| {
+                        self.handle_exprs(&expr, collect_break);
+                    });
+                }
+            }
+            _ => {}
+        }
+    }
+
+    fn collect_tail_exprs(&mut self, block: &BlockExpr) {
+        if let Some(expr) = block.expr() {
+            self.handle_exprs(&expr, true);
+            self.fetch_tail_exprs(&expr);
+        }
+    }
+
+    fn fetch_tail_exprs(&mut self, expr: &Expr) {
+        if let Some(exprs) = get_tail_expr_from_block(expr) {
+            for node_type in &exprs {
+                match node_type {
+                    NodeType::Leaf(expr) => {
+                        self.exprs_to_wrap.push(expr.clone());
+                    }
+                    NodeType::Node(expr) => {
+                        if let Some(last_expr) = Expr::cast(expr.clone()) {
+                            self.fetch_tail_exprs(&last_expr);
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
+
+#[derive(Debug)]
+enum NodeType {
+    Leaf(ast::Expr),
+    Node(SyntaxNode),
+}
+
+/// Get a tail expression inside a block
+fn get_tail_expr_from_block(expr: &Expr) -> Option<Vec<NodeType>> {
+    match expr {
+        Expr::IfExpr(if_expr) => {
+            let mut nodes = vec![];
+            for block in if_expr.blocks() {
+                if let Some(block_expr) = block.expr() {
+                    if let Some(tail_exprs) = get_tail_expr_from_block(&block_expr) {
+                        nodes.extend(tail_exprs);
+                    }
+                } else if let Some(last_expr) = block.syntax().last_child() {
+                    nodes.push(NodeType::Node(last_expr));
+                } else {
+                    nodes.push(NodeType::Node(block.syntax().clone()));
+                }
+            }
+            Some(nodes)
+        }
+        Expr::LoopExpr(loop_expr) => {
+            loop_expr.syntax().last_child().map(|lc| vec![NodeType::Node(lc)])
+        }
+        Expr::ForExpr(for_expr) => {
+            for_expr.syntax().last_child().map(|lc| vec![NodeType::Node(lc)])
+        }
+        Expr::WhileExpr(while_expr) => {
+            while_expr.syntax().last_child().map(|lc| vec![NodeType::Node(lc)])
+        }
+        Expr::BlockExpr(block_expr) => {
+            block_expr.expr().map(|lc| vec![NodeType::Node(lc.syntax().clone())])
+        }
+        Expr::MatchExpr(match_expr) => {
+            let arm_list = match_expr.match_arm_list()?;
+            let arms: Vec<NodeType> = arm_list
+                .arms()
+                .filter_map(|match_arm| match_arm.expr())
+                .map(|expr| match expr {
+                    Expr::ReturnExpr(ret_expr) => NodeType::Node(ret_expr.syntax().clone()),
+                    Expr::BreakExpr(break_expr) => NodeType::Node(break_expr.syntax().clone()),
+                    _ => match expr.syntax().last_child() {
+                        Some(last_expr) => NodeType::Node(last_expr),
+                        None => NodeType::Node(expr.syntax().clone()),
+                    },
+                })
+                .collect();
+
+            Some(arms)
+        }
+        Expr::BreakExpr(expr) => expr.expr().map(|e| vec![NodeType::Leaf(e)]),
+        Expr::ReturnExpr(ret_expr) => Some(vec![NodeType::Node(ret_expr.syntax().clone())]),
+
+        Expr::CallExpr(_)
+        | Expr::Literal(_)
+        | Expr::TupleExpr(_)
+        | Expr::ArrayExpr(_)
+        | Expr::ParenExpr(_)
+        | Expr::PathExpr(_)
+        | Expr::RecordExpr(_)
+        | Expr::IndexExpr(_)
+        | Expr::MethodCallExpr(_)
+        | Expr::AwaitExpr(_)
+        | Expr::CastExpr(_)
+        | Expr::RefExpr(_)
+        | Expr::PrefixExpr(_)
+        | Expr::RangeExpr(_)
+        | Expr::BinExpr(_)
+        | Expr::MacroCall(_)
+        | Expr::BoxExpr(_) => Some(vec![NodeType::Leaf(expr.clone())]),
+        _ => None,
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::tests::{check_assist, check_assist_not_applicable};
+
+    use super::*;
+
+    #[test]
+    fn change_return_type_to_result_simple() {
+        check_assist(
+            change_return_type_to_result,
+            r#"fn foo() -> i3<|>2 {
+                let test = "test";
+                return 42i32;
+            }"#,
+            r#"fn foo() -> Result<i32, ${0:_}> {
+                let test = "test";
+                return Ok(42i32);
+            }"#,
+        );
+    }
+
+    #[test]
+    fn change_return_type_to_result_simple_closure() {
+        check_assist(
+            change_return_type_to_result,
+            r#"fn foo() {
+                || -> i32<|> {
+                    let test = "test";
+                    return 42i32;
+                };
+            }"#,
+            r#"fn foo() {
+                || -> Result<i32, ${0:_}> {
+                    let test = "test";
+                    return Ok(42i32);
+                };
+            }"#,
+        );
+    }
+
+    #[test]
+    fn change_return_type_to_result_simple_return_type_bad_cursor() {
+        check_assist_not_applicable(
+            change_return_type_to_result,
+            r#"fn foo() -> i32 {
+                let test = "test";<|>
+                return 42i32;
+            }"#,
+        );
+    }
+
+    #[test]
+    fn change_return_type_to_result_simple_return_type_bad_cursor_closure() {
+        check_assist_not_applicable(
+            change_return_type_to_result,
+            r#"fn foo() {
+                || -> i32 {
+                    let test = "test";<|>
+                    return 42i32;
+                };
+            }"#,
+        );
+    }
+
+    #[test]
+    fn change_return_type_to_result_closure_non_block() {
+        check_assist_not_applicable(
+            change_return_type_to_result,
+            r#"fn foo() {
+                || -> i<|>32 3;
+            }"#,
+        );
+    }
+
+    #[test]
+    fn change_return_type_to_result_simple_return_type_already_result_std() {
+        check_assist_not_applicable(
+            change_return_type_to_result,
+            r#"fn foo() -> std::result::Result<i32<|>, String> {
+                let test = "test";
+                return 42i32;
+            }"#,
+        );
+    }
+
+    #[test]
+    fn change_return_type_to_result_simple_return_type_already_result() {
+        mark::check!(change_return_type_to_result_simple_return_type_already_result);
+        check_assist_not_applicable(
+            change_return_type_to_result,
+            r#"fn foo() -> Result<i32<|>, String> {
+                let test = "test";
+                return 42i32;
+            }"#,
+        );
+    }
+
+    #[test]
+    fn change_return_type_to_result_simple_return_type_already_result_closure() {
+        check_assist_not_applicable(
+            change_return_type_to_result,
+            r#"fn foo() {
+                || -> Result<i32<|>, String> {
+                    let test = "test";
+                    return 42i32;
+                };
+            }"#,
+        );
+    }
+
+    #[test]
+    fn change_return_type_to_result_simple_with_cursor() {
+        check_assist(
+            change_return_type_to_result,
+            r#"fn foo() -> <|>i32 {
+                let test = "test";
+                return 42i32;
+            }"#,
+            r#"fn foo() -> Result<i32, ${0:_}> {
+                let test = "test";
+                return Ok(42i32);
+            }"#,
+        );
+    }
+
+    #[test]
+    fn change_return_type_to_result_simple_with_tail() {
+        check_assist(
+            change_return_type_to_result,
+            r#"fn foo() -><|> i32 {
+                let test = "test";
+                42i32
+            }"#,
+            r#"fn foo() -> Result<i32, ${0:_}> {
+                let test = "test";
+                Ok(42i32)
+            }"#,
+        );
+    }
+
+    #[test]
+    fn change_return_type_to_result_simple_with_tail_closure() {
+        check_assist(
+            change_return_type_to_result,
+            r#"fn foo() {
+                || -><|> i32 {
+                    let test = "test";
+                    42i32
+                };
+            }"#,
+            r#"fn foo() {
+                || -> Result<i32, ${0:_}> {
+                    let test = "test";
+                    Ok(42i32)
+                };
+            }"#,
+        );
+    }
+
+    #[test]
+    fn change_return_type_to_result_simple_with_tail_only() {
+        check_assist(
+            change_return_type_to_result,
+            r#"fn foo() -> i32<|> {
+                42i32
+            }"#,
+            r#"fn foo() -> Result<i32, ${0:_}> {
+                Ok(42i32)
+            }"#,
+        );
+    }
+
+    #[test]
+    fn change_return_type_to_result_simple_with_tail_block_like() {
+        check_assist(
+            change_return_type_to_result,
+            r#"fn foo() -> i32<|> {
+                if true {
+                    42i32
+                } else {
+                    24i32
+                }
+            }"#,
+            r#"fn foo() -> Result<i32, ${0:_}> {
+                if true {
+                    Ok(42i32)
+                } else {
+                    Ok(24i32)
+                }
+            }"#,
+        );
+    }
+
+    #[test]
+    fn change_return_type_to_result_simple_without_block_closure() {
+        check_assist(
+            change_return_type_to_result,
+            r#"fn foo() {
+                || -> i32<|> {
+                    if true {
+                        42i32
+                    } else {
+                        24i32
+                    }
+                };
+            }"#,
+            r#"fn foo() {
+                || -> Result<i32, ${0:_}> {
+                    if true {
+                        Ok(42i32)
+                    } else {
+                        Ok(24i32)
+                    }
+                };
+            }"#,
+        );
+    }
+
+    #[test]
+    fn change_return_type_to_result_simple_with_nested_if() {
+        check_assist(
+            change_return_type_to_result,
+            r#"fn foo() -> i32<|> {
+                if true {
+                    if false {
+                        1
+                    } else {
+                        2
+                    }
+                } else {
+                    24i32
+                }
+            }"#,
+            r#"fn foo() -> Result<i32, ${0:_}> {
+                if true {
+                    if false {
+                        Ok(1)
+                    } else {
+                        Ok(2)
+                    }
+                } else {
+                    Ok(24i32)
+                }
+            }"#,
+        );
+    }
+
+    #[test]
+    fn change_return_type_to_result_simple_with_await() {
+        check_assist(
+            change_return_type_to_result,
+            r#"async fn foo() -> i<|>32 {
+                if true {
+                    if false {
+                        1.await
+                    } else {
+                        2.await
+                    }
+                } else {
+                    24i32.await
+                }
+            }"#,
+            r#"async fn foo() -> Result<i32, ${0:_}> {
+                if true {
+                    if false {
+                        Ok(1.await)
+                    } else {
+                        Ok(2.await)
+                    }
+                } else {
+                    Ok(24i32.await)
+                }
+            }"#,
+        );
+    }
+
+    #[test]
+    fn change_return_type_to_result_simple_with_array() {
+        check_assist(
+            change_return_type_to_result,
+            r#"fn foo() -> [i32;<|> 3] {
+                [1, 2, 3]
+            }"#,
+            r#"fn foo() -> Result<[i32; 3], ${0:_}> {
+                Ok([1, 2, 3])
+            }"#,
+        );
+    }
+
+    #[test]
+    fn change_return_type_to_result_simple_with_cast() {
+        check_assist(
+            change_return_type_to_result,
+            r#"fn foo() -<|>> i32 {
+                if true {
+                    if false {
+                        1 as i32
+                    } else {
+                        2 as i32
+                    }
+                } else {
+                    24 as i32
+                }
+            }"#,
+            r#"fn foo() -> Result<i32, ${0:_}> {
+                if true {
+                    if false {
+                        Ok(1 as i32)
+                    } else {
+                        Ok(2 as i32)
+                    }
+                } else {
+                    Ok(24 as i32)
+                }
+            }"#,
+        );
+    }
+
+    #[test]
+    fn change_return_type_to_result_simple_with_tail_block_like_match() {
+        check_assist(
+            change_return_type_to_result,
+            r#"fn foo() -> i32<|> {
+                let my_var = 5;
+                match my_var {
+                    5 => 42i32,
+                    _ => 24i32,
+                }
+            }"#,
+            r#"fn foo() -> Result<i32, ${0:_}> {
+                let my_var = 5;
+                match my_var {
+                    5 => Ok(42i32),
+                    _ => Ok(24i32),
+                }
+            }"#,
+        );
+    }
+
+    #[test]
+    fn change_return_type_to_result_simple_with_loop_with_tail() {
+        check_assist(
+            change_return_type_to_result,
+            r#"fn foo() -> i32<|> {
+                let my_var = 5;
+                loop {
+                    println!("test");
+                    5
+                }
+
+                my_var
+            }"#,
+            r#"fn foo() -> Result<i32, ${0:_}> {
+                let my_var = 5;
+                loop {
+                    println!("test");
+                    5
+                }
+
+                Ok(my_var)
+            }"#,
+        );
+    }
+
+    #[test]
+    fn change_return_type_to_result_simple_with_loop_in_let_stmt() {
+        check_assist(
+            change_return_type_to_result,
+            r#"fn foo() -> i32<|> {
+                let my_var = let x = loop {
+                    break 1;
+                };
+
+                my_var
+            }"#,
+            r#"fn foo() -> Result<i32, ${0:_}> {
+                let my_var = let x = loop {
+                    break 1;
+                };
+
+                Ok(my_var)
+            }"#,
+        );
+    }
+
+    #[test]
+    fn change_return_type_to_result_simple_with_tail_block_like_match_return_expr() {
+        check_assist(
+            change_return_type_to_result,
+            r#"fn foo() -> i32<|> {
+                let my_var = 5;
+                let res = match my_var {
+                    5 => 42i32,
+                    _ => return 24i32,
+                };
+
+                res
+            }"#,
+            r#"fn foo() -> Result<i32, ${0:_}> {
+                let my_var = 5;
+                let res = match my_var {
+                    5 => 42i32,
+                    _ => return Ok(24i32),
+                };
+
+                Ok(res)
+            }"#,
+        );
+
+        check_assist(
+            change_return_type_to_result,
+            r#"fn foo() -> i32<|> {
+                let my_var = 5;
+                let res = if my_var == 5 {
+                    42i32
+                } else {
+                    return 24i32;
+                };
+
+                res
+            }"#,
+            r#"fn foo() -> Result<i32, ${0:_}> {
+                let my_var = 5;
+                let res = if my_var == 5 {
+                    42i32
+                } else {
+                    return Ok(24i32);
+                };
+
+                Ok(res)
+            }"#,
+        );
+    }
+
+    #[test]
+    fn change_return_type_to_result_simple_with_tail_block_like_match_deeper() {
+        check_assist(
+            change_return_type_to_result,
+            r#"fn foo() -> i32<|> {
+                let my_var = 5;
+                match my_var {
+                    5 => {
+                        if true {
+                            42i32
+                        } else {
+                            25i32
+                        }
+                    },
+                    _ => {
+                        let test = "test";
+                        if test == "test" {
+                            return bar();
+                        }
+                        53i32
+                    },
+                }
+            }"#,
+            r#"fn foo() -> Result<i32, ${0:_}> {
+                let my_var = 5;
+                match my_var {
+                    5 => {
+                        if true {
+                            Ok(42i32)
+                        } else {
+                            Ok(25i32)
+                        }
+                    },
+                    _ => {
+                        let test = "test";
+                        if test == "test" {
+                            return Ok(bar());
+                        }
+                        Ok(53i32)
+                    },
+                }
+            }"#,
+        );
+    }
+
+    #[test]
+    fn change_return_type_to_result_simple_with_tail_block_like_early_return() {
+        check_assist(
+            change_return_type_to_result,
+            r#"fn foo() -> i<|>32 {
+                let test = "test";
+                if test == "test" {
+                    return 24i32;
+                }
+                53i32
+            }"#,
+            r#"fn foo() -> Result<i32, ${0:_}> {
+                let test = "test";
+                if test == "test" {
+                    return Ok(24i32);
+                }
+                Ok(53i32)
+            }"#,
+        );
+    }
+
+    #[test]
+    fn change_return_type_to_result_simple_with_closure() {
+        check_assist(
+            change_return_type_to_result,
+            r#"fn foo(the_field: u32) -><|> u32 {
+                let true_closure = || {
+                    return true;
+                };
+                if the_field < 5 {
+                    let mut i = 0;
+
+
+                    if true_closure() {
+                        return 99;
+                    } else {
+                        return 0;
+                    }
+                }
+
+                the_field
+            }"#,
+            r#"fn foo(the_field: u32) -> Result<u32, ${0:_}> {
+                let true_closure = || {
+                    return true;
+                };
+                if the_field < 5 {
+                    let mut i = 0;
+
+
+                    if true_closure() {
+                        return Ok(99);
+                    } else {
+                        return Ok(0);
+                    }
+                }
+
+                Ok(the_field)
+            }"#,
+        );
+
+        check_assist(
+            change_return_type_to_result,
+            r#"fn foo(the_field: u32) -> u32<|> {
+                let true_closure = || {
+                    return true;
+                };
+                if the_field < 5 {
+                    let mut i = 0;
+
+
+                    if true_closure() {
+                        return 99;
+                    } else {
+                        return 0;
+                    }
+                }
+                let t = None;
+
+                t.unwrap_or_else(|| the_field)
+            }"#,
+            r#"fn foo(the_field: u32) -> Result<u32, ${0:_}> {
+                let true_closure = || {
+                    return true;
+                };
+                if the_field < 5 {
+                    let mut i = 0;
+
+
+                    if true_closure() {
+                        return Ok(99);
+                    } else {
+                        return Ok(0);
+                    }
+                }
+                let t = None;
+
+                Ok(t.unwrap_or_else(|| the_field))
+            }"#,
+        );
+    }
+
+    #[test]
+    fn change_return_type_to_result_simple_with_weird_forms() {
+        check_assist(
+            change_return_type_to_result,
+            r#"fn foo() -> i32<|> {
+                let test = "test";
+                if test == "test" {
+                    return 24i32;
+                }
+                let mut i = 0;
+                loop {
+                    if i == 1 {
+                        break 55;
+                    }
+                    i += 1;
+                }
+            }"#,
+            r#"fn foo() -> Result<i32, ${0:_}> {
+                let test = "test";
+                if test == "test" {
+                    return Ok(24i32);
+                }
+                let mut i = 0;
+                loop {
+                    if i == 1 {
+                        break Ok(55);
+                    }
+                    i += 1;
+                }
+            }"#,
+        );
+
+        check_assist(
+            change_return_type_to_result,
+            r#"fn foo() -> i32<|> {
+                let test = "test";
+                if test == "test" {
+                    return 24i32;
+                }
+                let mut i = 0;
+                loop {
+                    loop {
+                        if i == 1 {
+                            break 55;
+                        }
+                        i += 1;
+                    }
+                }
+            }"#,
+            r#"fn foo() -> Result<i32, ${0:_}> {
+                let test = "test";
+                if test == "test" {
+                    return Ok(24i32);
+                }
+                let mut i = 0;
+                loop {
+                    loop {
+                        if i == 1 {
+                            break Ok(55);
+                        }
+                        i += 1;
+                    }
+                }
+            }"#,
+        );
+
+        check_assist(
+            change_return_type_to_result,
+            r#"fn foo() -> i3<|>2 {
+                let test = "test";
+                let other = 5;
+                if test == "test" {
+                    let res = match other {
+                        5 => 43,
+                        _ => return 56,
+                    };
+                }
+                let mut i = 0;
+                loop {
+                    loop {
+                        if i == 1 {
+                            break 55;
+                        }
+                        i += 1;
+                    }
+                }
+            }"#,
+            r#"fn foo() -> Result<i32, ${0:_}> {
+                let test = "test";
+                let other = 5;
+                if test == "test" {
+                    let res = match other {
+                        5 => 43,
+                        _ => return Ok(56),
+                    };
+                }
+                let mut i = 0;
+                loop {
+                    loop {
+                        if i == 1 {
+                            break Ok(55);
+                        }
+                        i += 1;
+                    }
+                }
+            }"#,
+        );
+
+        check_assist(
+            change_return_type_to_result,
+            r#"fn foo(the_field: u32) -> u32<|> {
+                if the_field < 5 {
+                    let mut i = 0;
+                    loop {
+                        if i > 5 {
+                            return 55u32;
+                        }
+                        i += 3;
+                    }
+
+                    match i {
+                        5 => return 99,
+                        _ => return 0,
+                    };
+                }
+
+                the_field
+            }"#,
+            r#"fn foo(the_field: u32) -> Result<u32, ${0:_}> {
+                if the_field < 5 {
+                    let mut i = 0;
+                    loop {
+                        if i > 5 {
+                            return Ok(55u32);
+                        }
+                        i += 3;
+                    }
+
+                    match i {
+                        5 => return Ok(99),
+                        _ => return Ok(0),
+                    };
+                }
+
+                Ok(the_field)
+            }"#,
+        );
+
+        check_assist(
+            change_return_type_to_result,
+            r#"fn foo(the_field: u32) -> u3<|>2 {
+                if the_field < 5 {
+                    let mut i = 0;
+
+                    match i {
+                        5 => return 99,
+                        _ => return 0,
+                    }
+                }
+
+                the_field
+            }"#,
+            r#"fn foo(the_field: u32) -> Result<u32, ${0:_}> {
+                if the_field < 5 {
+                    let mut i = 0;
+
+                    match i {
+                        5 => return Ok(99),
+                        _ => return Ok(0),
+                    }
+                }
+
+                Ok(the_field)
+            }"#,
+        );
+
+        check_assist(
+            change_return_type_to_result,
+            r#"fn foo(the_field: u32) -> u32<|> {
+                if the_field < 5 {
+                    let mut i = 0;
+
+                    if i == 5 {
+                        return 99
+                    } else {
+                        return 0
+                    }
+                }
+
+                the_field
+            }"#,
+            r#"fn foo(the_field: u32) -> Result<u32, ${0:_}> {
+                if the_field < 5 {
+                    let mut i = 0;
+
+                    if i == 5 {
+                        return Ok(99)
+                    } else {
+                        return Ok(0)
+                    }
+                }
+
+                Ok(the_field)
+            }"#,
+        );
+
+        check_assist(
+            change_return_type_to_result,
+            r#"fn foo(the_field: u32) -> <|>u32 {
+                if the_field < 5 {
+                    let mut i = 0;
+
+                    if i == 5 {
+                        return 99;
+                    } else {
+                        return 0;
+                    }
+                }
+
+                the_field
+            }"#,
+            r#"fn foo(the_field: u32) -> Result<u32, ${0:_}> {
+                if the_field < 5 {
+                    let mut i = 0;
+
+                    if i == 5 {
+                        return Ok(99);
+                    } else {
+                        return Ok(0);
+                    }
+                }
+
+                Ok(the_field)
+            }"#,
+        );
+    }
+}
diff --git a/crates/assists/src/handlers/convert_integer_literal.rs b/crates/assists/src/handlers/convert_integer_literal.rs
index 6671153825..1094ed3f3a 100644
--- a/crates/assists/src/handlers/convert_integer_literal.rs
+++ b/crates/assists/src/handlers/convert_integer_literal.rs
@@ -129,6 +129,34 @@ mod tests {
         );
     }
 
+    // Decimal numbers under 3 digits have a special case where they return early because we can't fit a
+    // other base's prefix, so we have a separate test for that.
+    #[test]
+    fn convert_small_decimal_integer() {
+        let before = "const _: i32 = 10<|>;";
+
+        check_assist_by_label(
+            convert_integer_literal,
+            before,
+            "const _: i32 = 0b1010;",
+            "Convert 10 to 0b1010",
+        );
+
+        check_assist_by_label(
+            convert_integer_literal,
+            before,
+            "const _: i32 = 0o12;",
+            "Convert 10 to 0o12",
+        );
+
+        check_assist_by_label(
+            convert_integer_literal,
+            before,
+            "const _: i32 = 0xA;",
+            "Convert 10 to 0xA",
+        );
+    }
+
     #[test]
     fn convert_hexadecimal_integer() {
         let before = "const _: i32 = 0xFF<|>;";
@@ -208,7 +236,7 @@ mod tests {
     }
 
     #[test]
-    fn convert_integer_with_underscores() {
+    fn convert_decimal_integer_with_underscores() {
         let before = "const _: i32 = 1_00_0<|>;";
 
         check_assist_by_label(
@@ -234,7 +262,111 @@ mod tests {
     }
 
     #[test]
-    fn convert_integer_with_suffix() {
+    fn convert_small_decimal_integer_with_underscores() {
+        let before = "const _: i32 = 1_0<|>;";
+
+        check_assist_by_label(
+            convert_integer_literal,
+            before,
+            "const _: i32 = 0b1010;",
+            "Convert 1_0 to 0b1010",
+        );
+
+        check_assist_by_label(
+            convert_integer_literal,
+            before,
+            "const _: i32 = 0o12;",
+            "Convert 1_0 to 0o12",
+        );
+
+        check_assist_by_label(
+            convert_integer_literal,
+            before,
+            "const _: i32 = 0xA;",
+            "Convert 1_0 to 0xA",
+        );
+    }
+
+    #[test]
+    fn convert_hexadecimal_integer_with_underscores() {
+        let before = "const _: i32 = 0x_F_F<|>;";
+
+        check_assist_by_label(
+            convert_integer_literal,
+            before,
+            "const _: i32 = 0b11111111;",
+            "Convert 0x_F_F to 0b11111111",
+        );
+
+        check_assist_by_label(
+            convert_integer_literal,
+            before,
+            "const _: i32 = 0o377;",
+            "Convert 0x_F_F to 0o377",
+        );
+
+        check_assist_by_label(
+            convert_integer_literal,
+            before,
+            "const _: i32 = 255;",
+            "Convert 0x_F_F to 255",
+        );
+    }
+
+    #[test]
+    fn convert_binary_integer_with_underscores() {
+        let before = "const _: i32 = 0b1111_1111<|>;";
+
+        check_assist_by_label(
+            convert_integer_literal,
+            before,
+            "const _: i32 = 0o377;",
+            "Convert 0b1111_1111 to 0o377",
+        );
+
+        check_assist_by_label(
+            convert_integer_literal,
+            before,
+            "const _: i32 = 255;",
+            "Convert 0b1111_1111 to 255",
+        );
+
+        check_assist_by_label(
+            convert_integer_literal,
+            before,
+            "const _: i32 = 0xFF;",
+            "Convert 0b1111_1111 to 0xFF",
+        );
+    }
+
+    #[test]
+    fn convert_octal_integer_with_underscores() {
+        let before = "const _: i32 = 0o3_77<|>;";
+
+        check_assist_by_label(
+            convert_integer_literal,
+            before,
+            "const _: i32 = 0b11111111;",
+            "Convert 0o3_77 to 0b11111111",
+        );
+
+        check_assist_by_label(
+            convert_integer_literal,
+            before,
+            "const _: i32 = 255;",
+            "Convert 0o3_77 to 255",
+        );
+
+        check_assist_by_label(
+            convert_integer_literal,
+            before,
+            "const _: i32 = 0xFF;",
+            "Convert 0o3_77 to 0xFF",
+        );
+    }
+
+    #[test]
+    fn convert_decimal_integer_with_suffix() {
         let before = "const _: i32 = 1000i32<|>;";
 
         check_assist_by_label(
@@ -259,6 +391,240 @@ mod tests {
         );
     }
 
+    #[test]
+    fn convert_small_decimal_integer_with_suffix() {
+        let before = "const _: i32 = 10i32<|>;";
+
+        check_assist_by_label(
+            convert_integer_literal,
+            before,
+            "const _: i32 = 0b1010i32;",
+            "Convert 10i32 to 0b1010i32",
+        );
+
+        check_assist_by_label(
+            convert_integer_literal,
+            before,
+            "const _: i32 = 0o12i32;",
+            "Convert 10i32 to 0o12i32",
+        );
+
+        check_assist_by_label(
+            convert_integer_literal,
+            before,
+            "const _: i32 = 0xAi32;",
+            "Convert 10i32 to 0xAi32",
+        );
+    }
+
+    #[test]
+    fn convert_hexadecimal_integer_with_suffix() {
+        let before = "const _: i32 = 0xFFi32<|>;";
+
+        check_assist_by_label(
+            convert_integer_literal,
+            before,
+            "const _: i32 = 0b11111111i32;",
+            "Convert 0xFFi32 to 0b11111111i32",
+        );
+
+        check_assist_by_label(
+            convert_integer_literal,
+            before,
+            "const _: i32 = 0o377i32;",
+            "Convert 0xFFi32 to 0o377i32",
+        );
+
+        check_assist_by_label(
+            convert_integer_literal,
+            before,
+            "const _: i32 = 255i32;",
+            "Convert 0xFFi32 to 255i32",
+        );
+    }
+
+    #[test]
+    fn convert_binary_integer_with_suffix() {
+        let before = "const _: i32 = 0b11111111i32<|>;";
+
+        check_assist_by_label(
+            convert_integer_literal,
+            before,
+            "const _: i32 = 0o377i32;",
+            "Convert 0b11111111i32 to 0o377i32",
+        );
+
+        check_assist_by_label(
+            convert_integer_literal,
+            before,
+            "const _: i32 = 255i32;",
+            "Convert 0b11111111i32 to 255i32",
+        );
+
+        check_assist_by_label(
+            convert_integer_literal,
+            before,
+            "const _: i32 = 0xFFi32;",
+            "Convert 0b11111111i32 to 0xFFi32",
+        );
+    }
+
+    #[test]
+    fn convert_octal_integer_with_suffix() {
+        let before = "const _: i32 = 0o377i32<|>;";
+
+        check_assist_by_label(
+            convert_integer_literal,
+            before,
+            "const _: i32 = 0b11111111i32;",
+            "Convert 0o377i32 to 0b11111111i32",
+        );
+
+        check_assist_by_label(
+            convert_integer_literal,
+            before,
+            "const _: i32 = 255i32;",
+            "Convert 0o377i32 to 255i32",
+        );
+
+        check_assist_by_label(
+            convert_integer_literal,
+            before,
+            "const _: i32 = 0xFFi32;",
+            "Convert 0o377i32 to 0xFFi32",
+        );
+    }
+
+    #[test]
+    fn convert_decimal_integer_with_underscores_and_suffix() {
+        let before = "const _: i32 = 1_00_0i32<|>;";
+
+        check_assist_by_label(
+            convert_integer_literal,
+            before,
+            "const _: i32 = 0b1111101000i32;",
+            "Convert 1_00_0i32 to 0b1111101000i32",
+        );
+
+        check_assist_by_label(
+            convert_integer_literal,
+            before,
+            "const _: i32 = 0o1750i32;",
+            "Convert 1_00_0i32 to 0o1750i32",
+        );
+
+        check_assist_by_label(
+            convert_integer_literal,
+            before,
+            "const _: i32 = 0x3E8i32;",
+            "Convert 1_00_0i32 to 0x3E8i32",
+        );
+    }
+
+    #[test]
+    fn convert_small_decimal_integer_with_underscores_and_suffix() {
+        let before = "const _: i32 = 1_0i32<|>;";
+
+        check_assist_by_label(
+            convert_integer_literal,
+            before,
+            "const _: i32 = 0b1010i32;",
+            "Convert 1_0i32 to 0b1010i32",
+        );
+
+        check_assist_by_label(
+            convert_integer_literal,
+            before,
+            "const _: i32 = 0o12i32;",
+            "Convert 1_0i32 to 0o12i32",
+        );
+
+        check_assist_by_label(
+            convert_integer_literal,
+            before,
+            "const _: i32 = 0xAi32;",
+            "Convert 1_0i32 to 0xAi32",
+        );
+    }
+
+    #[test]
+    fn convert_hexadecimal_integer_with_underscores_and_suffix() {
+        let before = "const _: i32 = 0x_F_Fi32<|>;";
+
+        check_assist_by_label(
+            convert_integer_literal,
+            before,
+            "const _: i32 = 0b11111111i32;",
+            "Convert 0x_F_Fi32 to 0b11111111i32",
+        );
+
+        check_assist_by_label(
+            convert_integer_literal,
+            before,
+            "const _: i32 = 0o377i32;",
+            "Convert 0x_F_Fi32 to 0o377i32",
+        );
+
+        check_assist_by_label(
+            convert_integer_literal,
+            before,
+            "const _: i32 = 255i32;",
+            "Convert 0x_F_Fi32 to 255i32",
+        );
+    }
+
+    #[test]
+    fn convert_binary_integer_with_underscores_and_suffix() {
+        let before = "const _: i32 = 0b1111_1111i32<|>;";
+
+        check_assist_by_label(
+            convert_integer_literal,
+            before,
+            "const _: i32 = 0o377i32;",
+            "Convert 0b1111_1111i32 to 0o377i32",
+        );
+
+        check_assist_by_label(
+            convert_integer_literal,
+            before,
+            "const _: i32 = 255i32;",
+            "Convert 0b1111_1111i32 to 255i32",
+        );
+
+        check_assist_by_label(
+            convert_integer_literal,
+            before,
+            "const _: i32 = 0xFFi32;",
+            "Convert 0b1111_1111i32 to 0xFFi32",
+        );
+    }
+
+    #[test]
+    fn convert_octal_integer_with_underscores_and_suffix() {
+        let before = "const _: i32 = 0o3_77i32<|>;";
+
+        check_assist_by_label(
+            convert_integer_literal,
+            before,
+            "const _: i32 = 0b11111111i32;",
+            "Convert 0o3_77i32 to 0b11111111i32",
+        );
+
+        check_assist_by_label(
+            convert_integer_literal,
+            before,
+            "const _: i32 = 255i32;",
+            "Convert 0o3_77i32 to 255i32",
+        );
+
+        check_assist_by_label(
+            convert_integer_literal,
+            before,
+            "const _: i32 = 0xFFi32;",
+            "Convert 0o3_77i32 to 0xFFi32",
+        );
+    }
+
     #[test]
     fn convert_overflowing_literal() {
         let before = "const _: i32 =
diff --git a/crates/assists/src/handlers/expand_glob_import.rs b/crates/assists/src/handlers/expand_glob_import.rs
index f51a9a4ad1..853266395d 100644
--- a/crates/assists/src/handlers/expand_glob_import.rs
+++ b/crates/assists/src/handlers/expand_glob_import.rs
@@ -5,13 +5,13 @@ use ide_db::{
     search::SearchScope,
 };
 use syntax::{
-    algo::SyntaxRewriter,
+    algo,
     ast::{self, make},
     AstNode, Direction, SyntaxNode, SyntaxToken, T,
 };
 
 use crate::{
-    assist_context::{AssistContext, Assists},
+    assist_context::{AssistBuilder, AssistContext, Assists},
     AssistId, AssistKind,
 };
 
@@ -61,9 +61,7 @@ pub(crate) fn expand_glob_import(acc: &mut Assists, ctx: &AssistContext) -> Opti
         "Expand glob import",
         target.text_range(),
         |builder| {
-            let mut rewriter = SyntaxRewriter::default();
-            replace_ast(&mut rewriter, parent, mod_path, names_to_import);
-            builder.rewrite(rewriter);
+            replace_ast(builder, parent, mod_path, names_to_import);
         },
     )
 }
@@ -238,7 +236,7 @@ fn find_names_to_import(
 }
 
 fn replace_ast(
-    rewriter: &mut SyntaxRewriter,
+    builder: &mut AssistBuilder,
     parent: Either<ast::UseTree, ast::UseTreeList>,
     path: ast::Path,
     names_to_import: Vec<Name>,
@@ -266,21 +264,32 @@ fn replace_ast(
     match use_trees.as_slice() {
         [name] => {
             if let Some(end_path) = name.path() {
-                rewriter.replace_ast(
-                    &parent.left_or_else(|tl| tl.parent_use_tree()),
-                    &make::use_tree(make::path_concat(path, end_path), None, None, false),
-                );
+                let replacement =
+                    make::use_tree(make::path_concat(path, end_path), None, None, false);
+
+                algo::diff(
+                    &parent.either(|n| n.syntax().clone(), |n| n.syntax().clone()),
+                    replacement.syntax(),
+                )
+                .into_text_edit(builder.text_edit_builder());
             }
         }
-        names => match &parent {
-            Either::Left(parent) => rewriter.replace_ast(
-                parent,
-                &make::use_tree(path, Some(make::use_tree_list(names.to_owned())), None, false),
-            ),
-            Either::Right(parent) => {
-                rewriter.replace_ast(parent, &make::use_tree_list(names.to_owned()))
-            }
-        },
+        names => {
+            let replacement = match parent {
+                Either::Left(_) => {
+                    make::use_tree(path, Some(make::use_tree_list(names.to_owned())), None, false)
+                        .syntax()
+                        .clone()
+                }
+                Either::Right(_) => make::use_tree_list(names.to_owned()).syntax().clone(),
+            };
+
+            algo::diff(
+                &parent.either(|n| n.syntax().clone(), |n| n.syntax().clone()),
+                &replacement,
+            )
+            .into_text_edit(builder.text_edit_builder());
+        }
     };
 }
 
@@ -875,33 +884,4 @@ fn qux(baz: Baz) {}
     ",
         )
     }
-
-    #[test]
-    fn expanding_glob_import_single_nested_glob_only() {
-        check_assist(
-            expand_glob_import,
-            r"
-mod foo {
-    pub struct Bar;
-}
-
-use foo::{*<|>};
-
-struct Baz {
-    bar: Bar
-}
-",
-            r"
-mod foo {
-    pub struct Bar;
-}
-
-use foo::Bar;
-
-struct Baz {
-    bar: Bar
-}
-",
-        );
-    }
 }
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 84662d832d..14209b7714 100644
--- a/crates/assists/src/handlers/extract_struct_from_enum_variant.rs
+++ b/crates/assists/src/handlers/extract_struct_from_enum_variant.rs
@@ -345,73 +345,6 @@ fn another_fn() {
         );
     }
 
-    #[test]
-    fn test_several_files() {
-        check_assist(
-            extract_struct_from_enum_variant,
-            r#"
-//- /main.rs
-enum E {
-    <|>V(i32, i32)
-}
-mod foo;
-
-//- /foo.rs
-use crate::E;
-fn f() {
-    let e = E::V(9, 2);
-}
-"#,
-            r#"
-//- /main.rs
-struct V(pub i32, pub i32);
-
-enum E {
-    V(V)
-}
-mod foo;
-
-//- /foo.rs
-use V;
-
-use crate::E;
-fn f() {
-    let e = E::V(V(9, 2));
-}
-"#,
-        )
-    }
-
-    #[test]
-    fn test_several_files_record() {
-        // FIXME: this should fix the usage as well!
-        check_assist(
-            extract_struct_from_enum_variant,
-            r#"
-//- /main.rs
-enum E {
-    <|>V { i: i32, j: i32 }
-}
-mod foo;
-
-//- /foo.rs
-use crate::E;
-fn f() {
-    let e = E::V { i: 9, j: 2 };
-}
-"#,
-            r#"
-struct V{ pub i: i32, pub j: i32 }
-
-enum E {
-    V(V)
-}
-mod foo;
-
-"#,
-        )
-    }
-
     fn check_not_applicable(ra_fixture: &str) {
         let fixture =
             format!("//- /main.rs crate:main deps:core\n{}\n{}", ra_fixture, FamousDefs::FIXTURE);
diff --git a/crates/assists/src/handlers/reorder_fields.rs b/crates/assists/src/handlers/reorder_fields.rs
index 7c0f0f44e1..527f457a79 100644
--- a/crates/assists/src/handlers/reorder_fields.rs
+++ b/crates/assists/src/handlers/reorder_fields.rs
@@ -47,11 +47,9 @@ fn reorder<R: AstNode>(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
         "Reorder record fields",
         target,
         |edit| {
-            let mut rewriter = algo::SyntaxRewriter::default();
             for (old, new) in fields.iter().zip(&sorted_fields) {
-                rewriter.replace(old, new);
+                algo::diff(old, new).into_text_edit(edit.text_edit_builder());
             }
-            edit.rewrite(rewriter);
         },
     )
 }
diff --git a/crates/assists/src/handlers/replace_derive_with_manual_impl.rs b/crates/assists/src/handlers/replace_derive_with_manual_impl.rs
deleted file mode 100644
index 82625516c2..0000000000
--- a/crates/assists/src/handlers/replace_derive_with_manual_impl.rs
+++ /dev/null
@@ -1,398 +0,0 @@
-use ide_db::imports_locator;
-use itertools::Itertools;
-use syntax::{
-    ast::{self, make, AstNode},
-    Direction, SmolStr,
-    SyntaxKind::{IDENT, WHITESPACE},
-    TextSize,
-};
-
-use crate::{
-    assist_context::{AssistBuilder, AssistContext, Assists},
-    utils::{
-        add_trait_assoc_items_to_impl, filter_assoc_items, mod_path_to_ast, render_snippet, Cursor,
-        DefaultMethods,
-    },
-    AssistId, AssistKind,
-};
-
-// Assist: replace_derive_with_manual_impl
-//
-// Converts a `derive` impl into a manual one.
-//
-// ```
-// # trait Debug { fn fmt(&self, f: &mut Formatter) -> Result<()>; }
-// #[derive(Deb<|>ug, Display)]
-// struct S;
-// ```
-// ->
-// ```
-// # trait Debug { fn fmt(&self, f: &mut Formatter) -> Result<()>; }
-// #[derive(Display)]
-// struct S;
-//
-// impl Debug for S {
-//     fn fmt(&self, f: &mut Formatter) -> Result<()> {
-//         ${0:todo!()}
-//     }
-// }
-// ```
-pub(crate) fn replace_derive_with_manual_impl(
-    acc: &mut Assists,
-    ctx: &AssistContext,
-) -> Option<()> {
-    let attr = ctx.find_node_at_offset::<ast::Attr>()?;
-
-    let attr_name = attr
-        .syntax()
-        .descendants_with_tokens()
-        .filter(|t| t.kind() == IDENT)
-        .find_map(syntax::NodeOrToken::into_token)
-        .filter(|t| t.text() == "derive")?
-        .text()
-        .clone();
-
-    let trait_token =
-        ctx.token_at_offset().find(|t| t.kind() == IDENT && *t.text() != attr_name)?;
-    let trait_path = make::path_unqualified(make::path_segment(make::name_ref(trait_token.text())));
-
-    let annotated_name = attr.syntax().siblings(Direction::Next).find_map(ast::Name::cast)?;
-    let insert_pos = annotated_name.syntax().parent()?.text_range().end();
-
-    let current_module = ctx.sema.scope(annotated_name.syntax()).module()?;
-    let current_crate = current_module.krate();
-
-    let found_traits = imports_locator::find_imports(&ctx.sema, current_crate, trait_token.text())
-        .into_iter()
-        .filter_map(|candidate: either::Either<hir::ModuleDef, hir::MacroDef>| match candidate {
-            either::Either::Left(hir::ModuleDef::Trait(trait_)) => Some(trait_),
-            _ => None,
-        })
-        .flat_map(|trait_| {
-            current_module
-                .find_use_path(ctx.sema.db, hir::ModuleDef::Trait(trait_))
-                .as_ref()
-                .map(mod_path_to_ast)
-                .zip(Some(trait_))
-        });
-
-    let mut no_traits_found = true;
-    for (trait_path, trait_) in found_traits.inspect(|_| no_traits_found = false) {
-        add_assist(acc, ctx, &attr, &trait_path, Some(trait_), &annotated_name, insert_pos)?;
-    }
-    if no_traits_found {
-        add_assist(acc, ctx, &attr, &trait_path, None, &annotated_name, insert_pos)?;
-    }
-    Some(())
-}
-
-fn add_assist(
-    acc: &mut Assists,
-    ctx: &AssistContext,
-    attr: &ast::Attr,
-    trait_path: &ast::Path,
-    trait_: Option<hir::Trait>,
-    annotated_name: &ast::Name,
-    insert_pos: TextSize,
-) -> Option<()> {
-    let target = attr.syntax().text_range();
-    let input = attr.token_tree()?;
-    let label = format!("Convert to manual  `impl {} for {}`", trait_path, annotated_name);
-    let trait_name = trait_path.segment().and_then(|seg| seg.name_ref())?;
-
-    acc.add(
-        AssistId("replace_derive_with_manual_impl", AssistKind::Refactor),
-        label,
-        target,
-        |builder| {
-            let impl_def_with_items =
-                impl_def_from_trait(&ctx.sema, annotated_name, trait_, trait_path);
-            update_attribute(builder, &input, &trait_name, &attr);
-            match (ctx.config.snippet_cap, impl_def_with_items) {
-                (None, _) => builder.insert(
-                    insert_pos,
-                    format!("\n\nimpl {} for {} {{\n\n}}", trait_path, annotated_name),
-                ),
-                (Some(cap), None) => builder.insert_snippet(
-                    cap,
-                    insert_pos,
-                    format!("\n\nimpl {} for {} {{\n    $0\n}}", trait_path, annotated_name),
-                ),
-                (Some(cap), Some((impl_def, first_assoc_item))) => {
-                    let mut cursor = Cursor::Before(first_assoc_item.syntax());
-                    let placeholder;
-                    if let ast::AssocItem::Fn(ref func) = first_assoc_item {
-                        if let Some(m) = func.syntax().descendants().find_map(ast::MacroCall::cast)
-                        {
-                            if m.syntax().text() == "todo!()" {
-                                placeholder = m;
-                                cursor = Cursor::Replace(placeholder.syntax());
-                            }
-                        }
-                    }
-
-                    builder.insert_snippet(
-                        cap,
-                        insert_pos,
-                        format!("\n\n{}", render_snippet(cap, impl_def.syntax(), cursor)),
-                    )
-                }
-            };
-        },
-    )
-}
-
-fn impl_def_from_trait(
-    sema: &hir::Semantics<ide_db::RootDatabase>,
-    annotated_name: &ast::Name,
-    trait_: Option<hir::Trait>,
-    trait_path: &ast::Path,
-) -> Option<(ast::Impl, ast::AssocItem)> {
-    let trait_ = trait_?;
-    let target_scope = sema.scope(annotated_name.syntax());
-    let trait_items = filter_assoc_items(sema.db, &trait_.items(sema.db), DefaultMethods::No);
-    if trait_items.is_empty() {
-        return None;
-    }
-    let impl_def = make::impl_trait(
-        trait_path.clone(),
-        make::path_unqualified(make::path_segment(make::name_ref(annotated_name.text()))),
-    );
-    let (impl_def, first_assoc_item) =
-        add_trait_assoc_items_to_impl(sema, trait_items, trait_, impl_def, target_scope);
-    Some((impl_def, first_assoc_item))
-}
-
-fn update_attribute(
-    builder: &mut AssistBuilder,
-    input: &ast::TokenTree,
-    trait_name: &ast::NameRef,
-    attr: &ast::Attr,
-) {
-    let new_attr_input = input
-        .syntax()
-        .descendants_with_tokens()
-        .filter(|t| t.kind() == IDENT)
-        .filter_map(|t| t.into_token().map(|t| t.text().clone()))
-        .filter(|t| t != trait_name.text())
-        .collect::<Vec<SmolStr>>();
-    let has_more_derives = !new_attr_input.is_empty();
-
-    if has_more_derives {
-        let new_attr_input = format!("({})", new_attr_input.iter().format(", "));
-        builder.replace(input.syntax().text_range(), new_attr_input);
-    } else {
-        let attr_range = attr.syntax().text_range();
-        builder.delete(attr_range);
-
-        if let Some(line_break_range) = attr
-            .syntax()
-            .next_sibling_or_token()
-            .filter(|t| t.kind() == WHITESPACE)
-            .map(|t| t.text_range())
-        {
-            builder.delete(line_break_range);
-        }
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use crate::tests::{check_assist, check_assist_not_applicable};
-
-    use super::*;
-
-    #[test]
-    fn add_custom_impl_debug() {
-        check_assist(
-            replace_derive_with_manual_impl,
-            "
-mod fmt {
-    pub struct Error;
-    pub type Result = Result<(), Error>;
-    pub struct Formatter<'a>;
-    pub trait Debug {
-        fn fmt(&self, f: &mut Formatter<'_>) -> Result;
-    }
-}
-
-#[derive(Debu<|>g)]
-struct Foo {
-    bar: String,
-}
-",
-            "
-mod fmt {
-    pub struct Error;
-    pub type Result = Result<(), Error>;
-    pub struct Formatter<'a>;
-    pub trait Debug {
-        fn fmt(&self, f: &mut Formatter<'_>) -> Result;
-    }
-}
-
-struct Foo {
-    bar: String,
-}
-
-impl fmt::Debug for Foo {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        ${0:todo!()}
-    }
-}
-",
-        )
-    }
-    #[test]
-    fn add_custom_impl_all() {
-        check_assist(
-            replace_derive_with_manual_impl,
-            "
-mod foo {
-    pub trait Bar {
-        type Qux;
-        const Baz: usize = 42;
-        const Fez: usize;
-        fn foo();
-        fn bar() {}
-    }
-}
-
-#[derive(<|>Bar)]
-struct Foo {
-    bar: String,
-}
-",
-            "
-mod foo {
-    pub trait Bar {
-        type Qux;
-        const Baz: usize = 42;
-        const Fez: usize;
-        fn foo();
-        fn bar() {}
-    }
-}
-
-struct Foo {
-    bar: String,
-}
-
-impl foo::Bar for Foo {
-    $0type Qux;
-
-    const Baz: usize = 42;
-
-    const Fez: usize;
-
-    fn foo() {
-        todo!()
-    }
-}
-",
-        )
-    }
-    #[test]
-    fn add_custom_impl_for_unique_input() {
-        check_assist(
-            replace_derive_with_manual_impl,
-            "
-#[derive(Debu<|>g)]
-struct Foo {
-    bar: String,
-}
-            ",
-            "
-struct Foo {
-    bar: String,
-}
-
-impl Debug for Foo {
-    $0
-}
-            ",
-        )
-    }
-
-    #[test]
-    fn add_custom_impl_for_with_visibility_modifier() {
-        check_assist(
-            replace_derive_with_manual_impl,
-            "
-#[derive(Debug<|>)]
-pub struct Foo {
-    bar: String,
-}
-            ",
-            "
-pub struct Foo {
-    bar: String,
-}
-
-impl Debug for Foo {
-    $0
-}
-            ",
-        )
-    }
-
-    #[test]
-    fn add_custom_impl_when_multiple_inputs() {
-        check_assist(
-            replace_derive_with_manual_impl,
-            "
-#[derive(Display, Debug<|>, Serialize)]
-struct Foo {}
-            ",
-            "
-#[derive(Display, Serialize)]
-struct Foo {}
-
-impl Debug for Foo {
-    $0
-}
-            ",
-        )
-    }
-
-    #[test]
-    fn test_ignore_derive_macro_without_input() {
-        check_assist_not_applicable(
-            replace_derive_with_manual_impl,
-            "
-#[derive(<|>)]
-struct Foo {}
-            ",
-        )
-    }
-
-    #[test]
-    fn test_ignore_if_cursor_on_param() {
-        check_assist_not_applicable(
-            replace_derive_with_manual_impl,
-            "
-#[derive<|>(Debug)]
-struct Foo {}
-            ",
-        );
-
-        check_assist_not_applicable(
-            replace_derive_with_manual_impl,
-            "
-#[derive(Debug)<|>]
-struct Foo {}
-            ",
-        )
-    }
-
-    #[test]
-    fn test_ignore_if_not_derive() {
-        check_assist_not_applicable(
-            replace_derive_with_manual_impl,
-            "
-#[allow(non_camel_<|>case_types)]
-struct Foo {}
-            ",
-        )
-    }
-}
diff --git a/crates/assists/src/handlers/wrap_return_type_in_result.rs b/crates/assists/src/handlers/wrap_return_type_in_result.rs
deleted file mode 100644
index 59e5debb13..0000000000
--- a/crates/assists/src/handlers/wrap_return_type_in_result.rs
+++ /dev/null
@@ -1,1158 +0,0 @@
-use std::iter;
-
-use syntax::{
-    ast::{self, make, BlockExpr, Expr, LoopBodyOwner},
-    match_ast, AstNode, SyntaxNode,
-};
-use test_utils::mark;
-
-use crate::{AssistContext, AssistId, AssistKind, Assists};
-
-// Assist: wrap_return_type_in_result
-//
-// Wrap the function's return type into Result.
-//
-// ```
-// fn foo() -> i32<|> { 42i32 }
-// ```
-// ->
-// ```
-// fn foo() -> Result<i32, ${0:_}> { Ok(42i32) }
-// ```
-pub(crate) fn wrap_return_type_in_result(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
-    let ret_type = ctx.find_node_at_offset::<ast::RetType>()?;
-    let parent = ret_type.syntax().parent()?;
-    let block_expr = match_ast! {
-        match parent {
-            ast::Fn(func) => func.body()?,
-            ast::ClosureExpr(closure) => match closure.body()? {
-                Expr::BlockExpr(block) => block,
-                // closures require a block when a return type is specified
-                _ => return None,
-            },
-            _ => return None,
-        }
-    };
-
-    let type_ref = &ret_type.ty()?;
-    let ret_type_str = type_ref.syntax().text().to_string();
-    let first_part_ret_type = ret_type_str.splitn(2, '<').next();
-    if let Some(ret_type_first_part) = first_part_ret_type {
-        if ret_type_first_part.ends_with("Result") {
-            mark::hit!(wrap_return_type_in_result_simple_return_type_already_result);
-            return None;
-        }
-    }
-
-    acc.add(
-        AssistId("wrap_return_type_in_result", AssistKind::RefactorRewrite),
-        "Wrap return type in Result",
-        type_ref.syntax().text_range(),
-        |builder| {
-            let mut tail_return_expr_collector = TailReturnCollector::new();
-            tail_return_expr_collector.collect_jump_exprs(&block_expr, false);
-            tail_return_expr_collector.collect_tail_exprs(&block_expr);
-
-            for ret_expr_arg in tail_return_expr_collector.exprs_to_wrap {
-                let ok_wrapped = make::expr_call(
-                    make::expr_path(make::path_unqualified(make::path_segment(make::name_ref(
-                        "Ok",
-                    )))),
-                    make::arg_list(iter::once(ret_expr_arg.clone())),
-                );
-                builder.replace_ast(ret_expr_arg, ok_wrapped);
-            }
-
-            match ctx.config.snippet_cap {
-                Some(cap) => {
-                    let snippet = format!("Result<{}, ${{0:_}}>", type_ref);
-                    builder.replace_snippet(cap, type_ref.syntax().text_range(), snippet)
-                }
-                None => builder
-                    .replace(type_ref.syntax().text_range(), format!("Result<{}, _>", type_ref)),
-            }
-        },
-    )
-}
-
-struct TailReturnCollector {
-    exprs_to_wrap: Vec<ast::Expr>,
-}
-
-impl TailReturnCollector {
-    fn new() -> Self {
-        Self { exprs_to_wrap: vec![] }
-    }
-    /// Collect all`return` expression
-    fn collect_jump_exprs(&mut self, block_expr: &BlockExpr, collect_break: bool) {
-        let statements = block_expr.statements();
-        for stmt in statements {
-            let expr = match &stmt {
-                ast::Stmt::ExprStmt(stmt) => stmt.expr(),
-                ast::Stmt::LetStmt(stmt) => stmt.initializer(),
-                ast::Stmt::Item(_) => continue,
-            };
-            if let Some(expr) = &expr {
-                self.handle_exprs(expr, collect_break);
-            }
-        }
-
-        // Browse tail expressions for each block
-        if let Some(expr) = block_expr.expr() {
-            if let Some(last_exprs) = get_tail_expr_from_block(&expr) {
-                for last_expr in last_exprs {
-                    let last_expr = match last_expr {
-                        NodeType::Node(expr) => expr,
-                        NodeType::Leaf(expr) => expr.syntax().clone(),
-                    };
-
-                    if let Some(last_expr) = Expr::cast(last_expr.clone()) {
-                        self.handle_exprs(&last_expr, collect_break);
-                    } else if let Some(expr_stmt) = ast::Stmt::cast(last_expr) {
-                        let expr_stmt = match &expr_stmt {
-                            ast::Stmt::ExprStmt(stmt) => stmt.expr(),
-                            ast::Stmt::LetStmt(stmt) => stmt.initializer(),
-                            ast::Stmt::Item(_) => None,
-                        };
-                        if let Some(expr) = &expr_stmt {
-                            self.handle_exprs(expr, collect_break);
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-    fn handle_exprs(&mut self, expr: &Expr, collect_break: bool) {
-        match expr {
-            Expr::BlockExpr(block_expr) => {
-                self.collect_jump_exprs(&block_expr, collect_break);
-            }
-            Expr::ReturnExpr(ret_expr) => {
-                if let Some(ret_expr_arg) = &ret_expr.expr() {
-                    self.exprs_to_wrap.push(ret_expr_arg.clone());
-                }
-            }
-            Expr::BreakExpr(break_expr) if collect_break => {
-                if let Some(break_expr_arg) = &break_expr.expr() {
-                    self.exprs_to_wrap.push(break_expr_arg.clone());
-                }
-            }
-            Expr::IfExpr(if_expr) => {
-                for block in if_expr.blocks() {
-                    self.collect_jump_exprs(&block, collect_break);
-                }
-            }
-            Expr::LoopExpr(loop_expr) => {
-                if let Some(block_expr) = loop_expr.loop_body() {
-                    self.collect_jump_exprs(&block_expr, collect_break);
-                }
-            }
-            Expr::ForExpr(for_expr) => {
-                if let Some(block_expr) = for_expr.loop_body() {
-                    self.collect_jump_exprs(&block_expr, collect_break);
-                }
-            }
-            Expr::WhileExpr(while_expr) => {
-                if let Some(block_expr) = while_expr.loop_body() {
-                    self.collect_jump_exprs(&block_expr, collect_break);
-                }
-            }
-            Expr::MatchExpr(match_expr) => {
-                if let Some(arm_list) = match_expr.match_arm_list() {
-                    arm_list.arms().filter_map(|match_arm| match_arm.expr()).for_each(|expr| {
-                        self.handle_exprs(&expr, collect_break);
-                    });
-                }
-            }
-            _ => {}
-        }
-    }
-
-    fn collect_tail_exprs(&mut self, block: &BlockExpr) {
-        if let Some(expr) = block.expr() {
-            self.handle_exprs(&expr, true);
-            self.fetch_tail_exprs(&expr);
-        }
-    }
-
-    fn fetch_tail_exprs(&mut self, expr: &Expr) {
-        if let Some(exprs) = get_tail_expr_from_block(expr) {
-            for node_type in &exprs {
-                match node_type {
-                    NodeType::Leaf(expr) => {
-                        self.exprs_to_wrap.push(expr.clone());
-                    }
-                    NodeType::Node(expr) => {
-                        if let Some(last_expr) = Expr::cast(expr.clone()) {
-                            self.fetch_tail_exprs(&last_expr);
-                        }
-                    }
-                }
-            }
-        }
-    }
-}
-
-#[derive(Debug)]
-enum NodeType {
-    Leaf(ast::Expr),
-    Node(SyntaxNode),
-}
-
-/// Get a tail expression inside a block
-fn get_tail_expr_from_block(expr: &Expr) -> Option<Vec<NodeType>> {
-    match expr {
-        Expr::IfExpr(if_expr) => {
-            let mut nodes = vec![];
-            for block in if_expr.blocks() {
-                if let Some(block_expr) = block.expr() {
-                    if let Some(tail_exprs) = get_tail_expr_from_block(&block_expr) {
-                        nodes.extend(tail_exprs);
-                    }
-                } else if let Some(last_expr) = block.syntax().last_child() {
-                    nodes.push(NodeType::Node(last_expr));
-                } else {
-                    nodes.push(NodeType::Node(block.syntax().clone()));
-                }
-            }
-            Some(nodes)
-        }
-        Expr::LoopExpr(loop_expr) => {
-            loop_expr.syntax().last_child().map(|lc| vec![NodeType::Node(lc)])
-        }
-        Expr::ForExpr(for_expr) => {
-            for_expr.syntax().last_child().map(|lc| vec![NodeType::Node(lc)])
-        }
-        Expr::WhileExpr(while_expr) => {
-            while_expr.syntax().last_child().map(|lc| vec![NodeType::Node(lc)])
-        }
-        Expr::BlockExpr(block_expr) => {
-            block_expr.expr().map(|lc| vec![NodeType::Node(lc.syntax().clone())])
-        }
-        Expr::MatchExpr(match_expr) => {
-            let arm_list = match_expr.match_arm_list()?;
-            let arms: Vec<NodeType> = arm_list
-                .arms()
-                .filter_map(|match_arm| match_arm.expr())
-                .map(|expr| match expr {
-                    Expr::ReturnExpr(ret_expr) => NodeType::Node(ret_expr.syntax().clone()),
-                    Expr::BreakExpr(break_expr) => NodeType::Node(break_expr.syntax().clone()),
-                    _ => match expr.syntax().last_child() {
-                        Some(last_expr) => NodeType::Node(last_expr),
-                        None => NodeType::Node(expr.syntax().clone()),
-                    },
-                })
-                .collect();
-
-            Some(arms)
-        }
-        Expr::BreakExpr(expr) => expr.expr().map(|e| vec![NodeType::Leaf(e)]),
-        Expr::ReturnExpr(ret_expr) => Some(vec![NodeType::Node(ret_expr.syntax().clone())]),
-
-        Expr::CallExpr(_)
-        | Expr::Literal(_)
-        | Expr::TupleExpr(_)
-        | Expr::ArrayExpr(_)
-        | Expr::ParenExpr(_)
-        | Expr::PathExpr(_)
-        | Expr::RecordExpr(_)
-        | Expr::IndexExpr(_)
-        | Expr::MethodCallExpr(_)
-        | Expr::AwaitExpr(_)
-        | Expr::CastExpr(_)
-        | Expr::RefExpr(_)
-        | Expr::PrefixExpr(_)
-        | Expr::RangeExpr(_)
-        | Expr::BinExpr(_)
-        | Expr::MacroCall(_)
-        | Expr::BoxExpr(_) => Some(vec![NodeType::Leaf(expr.clone())]),
-        _ => None,
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use crate::tests::{check_assist, check_assist_not_applicable};
-
-    use super::*;
-
-    #[test]
-    fn wrap_return_type_in_result_simple() {
-        check_assist(
-            wrap_return_type_in_result,
-            r#"
-fn foo() -> i3<|>2 {
-    let test = "test";
-    return 42i32;
-}
-"#,
-            r#"
-fn foo() -> Result<i32, ${0:_}> {
-    let test = "test";
-    return Ok(42i32);
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn wrap_return_type_in_result_simple_closure() {
-        check_assist(
-            wrap_return_type_in_result,
-            r#"
-fn foo() {
-    || -> i32<|> {
-        let test = "test";
-        return 42i32;
-    };
-}
-"#,
-            r#"
-fn foo() {
-    || -> Result<i32, ${0:_}> {
-        let test = "test";
-        return Ok(42i32);
-    };
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn wrap_return_type_in_result_simple_return_type_bad_cursor() {
-        check_assist_not_applicable(
-            wrap_return_type_in_result,
-            r#"
-fn foo() -> i32 {
-    let test = "test";<|>
-    return 42i32;
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn wrap_return_type_in_result_simple_return_type_bad_cursor_closure() {
-        check_assist_not_applicable(
-            wrap_return_type_in_result,
-            r#"
-fn foo() {
-    || -> i32 {
-        let test = "test";<|>
-        return 42i32;
-    };
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn wrap_return_type_in_result_closure_non_block() {
-        check_assist_not_applicable(wrap_return_type_in_result, r#"fn foo() { || -> i<|>32 3; }"#);
-    }
-
-    #[test]
-    fn wrap_return_type_in_result_simple_return_type_already_result_std() {
-        check_assist_not_applicable(
-            wrap_return_type_in_result,
-            r#"
-fn foo() -> std::result::Result<i32<|>, String> {
-    let test = "test";
-    return 42i32;
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn wrap_return_type_in_result_simple_return_type_already_result() {
-        mark::check!(wrap_return_type_in_result_simple_return_type_already_result);
-        check_assist_not_applicable(
-            wrap_return_type_in_result,
-            r#"
-fn foo() -> Result<i32<|>, String> {
-    let test = "test";
-    return 42i32;
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn wrap_return_type_in_result_simple_return_type_already_result_closure() {
-        check_assist_not_applicable(
-            wrap_return_type_in_result,
-            r#"
-fn foo() {
-    || -> Result<i32<|>, String> {
-        let test = "test";
-        return 42i32;
-    };
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn wrap_return_type_in_result_simple_with_cursor() {
-        check_assist(
-            wrap_return_type_in_result,
-            r#"
-fn foo() -> <|>i32 {
-    let test = "test";
-    return 42i32;
-}
-"#,
-            r#"
-fn foo() -> Result<i32, ${0:_}> {
-    let test = "test";
-    return Ok(42i32);
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn wrap_return_type_in_result_simple_with_tail() {
-        check_assist(
-            wrap_return_type_in_result,
-            r#"
-fn foo() -><|> i32 {
-    let test = "test";
-    42i32
-}
-"#,
-            r#"
-fn foo() -> Result<i32, ${0:_}> {
-    let test = "test";
-    Ok(42i32)
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn wrap_return_type_in_result_simple_with_tail_closure() {
-        check_assist(
-            wrap_return_type_in_result,
-            r#"
-fn foo() {
-    || -><|> i32 {
-        let test = "test";
-        42i32
-    };
-}
-"#,
-            r#"
-fn foo() {
-    || -> Result<i32, ${0:_}> {
-        let test = "test";
-        Ok(42i32)
-    };
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn wrap_return_type_in_result_simple_with_tail_only() {
-        check_assist(
-            wrap_return_type_in_result,
-            r#"fn foo() -> i32<|> { 42i32 }"#,
-            r#"fn foo() -> Result<i32, ${0:_}> { Ok(42i32) }"#,
-        );
-    }
-
-    #[test]
-    fn wrap_return_type_in_result_simple_with_tail_block_like() {
-        check_assist(
-            wrap_return_type_in_result,
-            r#"
-fn foo() -> i32<|> {
-    if true {
-        42i32
-    } else {
-        24i32
-    }
-}
-"#,
-            r#"
-fn foo() -> Result<i32, ${0:_}> {
-    if true {
-        Ok(42i32)
-    } else {
-        Ok(24i32)
-    }
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn wrap_return_type_in_result_simple_without_block_closure() {
-        check_assist(
-            wrap_return_type_in_result,
-            r#"
-fn foo() {
-    || -> i32<|> {
-        if true {
-            42i32
-        } else {
-            24i32
-        }
-    };
-}
-"#,
-            r#"
-fn foo() {
-    || -> Result<i32, ${0:_}> {
-        if true {
-            Ok(42i32)
-        } else {
-            Ok(24i32)
-        }
-    };
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn wrap_return_type_in_result_simple_with_nested_if() {
-        check_assist(
-            wrap_return_type_in_result,
-            r#"
-fn foo() -> i32<|> {
-    if true {
-        if false {
-            1
-        } else {
-            2
-        }
-    } else {
-        24i32
-    }
-}
-"#,
-            r#"
-fn foo() -> Result<i32, ${0:_}> {
-    if true {
-        if false {
-            Ok(1)
-        } else {
-            Ok(2)
-        }
-    } else {
-        Ok(24i32)
-    }
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn wrap_return_type_in_result_simple_with_await() {
-        check_assist(
-            wrap_return_type_in_result,
-            r#"
-async fn foo() -> i<|>32 {
-    if true {
-        if false {
-            1.await
-        } else {
-            2.await
-        }
-    } else {
-        24i32.await
-    }
-}
-"#,
-            r#"
-async fn foo() -> Result<i32, ${0:_}> {
-    if true {
-        if false {
-            Ok(1.await)
-        } else {
-            Ok(2.await)
-        }
-    } else {
-        Ok(24i32.await)
-    }
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn wrap_return_type_in_result_simple_with_array() {
-        check_assist(
-            wrap_return_type_in_result,
-            r#"fn foo() -> [i32;<|> 3] { [1, 2, 3] }"#,
-            r#"fn foo() -> Result<[i32; 3], ${0:_}> { Ok([1, 2, 3]) }"#,
-        );
-    }
-
-    #[test]
-    fn wrap_return_type_in_result_simple_with_cast() {
-        check_assist(
-            wrap_return_type_in_result,
-            r#"
-fn foo() -<|>> i32 {
-    if true {
-        if false {
-            1 as i32
-        } else {
-            2 as i32
-        }
-    } else {
-        24 as i32
-    }
-}
-"#,
-            r#"
-fn foo() -> Result<i32, ${0:_}> {
-    if true {
-        if false {
-            Ok(1 as i32)
-        } else {
-            Ok(2 as i32)
-        }
-    } else {
-        Ok(24 as i32)
-    }
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn wrap_return_type_in_result_simple_with_tail_block_like_match() {
-        check_assist(
-            wrap_return_type_in_result,
-            r#"
-fn foo() -> i32<|> {
-    let my_var = 5;
-    match my_var {
-        5 => 42i32,
-        _ => 24i32,
-    }
-}
-"#,
-            r#"
-fn foo() -> Result<i32, ${0:_}> {
-    let my_var = 5;
-    match my_var {
-        5 => Ok(42i32),
-        _ => Ok(24i32),
-    }
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn wrap_return_type_in_result_simple_with_loop_with_tail() {
-        check_assist(
-            wrap_return_type_in_result,
-            r#"
-fn foo() -> i32<|> {
-    let my_var = 5;
-    loop {
-        println!("test");
-        5
-    }
-    my_var
-}
-"#,
-            r#"
-fn foo() -> Result<i32, ${0:_}> {
-    let my_var = 5;
-    loop {
-        println!("test");
-        5
-    }
-    Ok(my_var)
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn wrap_return_type_in_result_simple_with_loop_in_let_stmt() {
-        check_assist(
-            wrap_return_type_in_result,
-            r#"
-fn foo() -> i32<|> {
-    let my_var = let x = loop {
-        break 1;
-    };
-    my_var
-}
-"#,
-            r#"
-fn foo() -> Result<i32, ${0:_}> {
-    let my_var = let x = loop {
-        break 1;
-    };
-    Ok(my_var)
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn wrap_return_type_in_result_simple_with_tail_block_like_match_return_expr() {
-        check_assist(
-            wrap_return_type_in_result,
-            r#"
-fn foo() -> i32<|> {
-    let my_var = 5;
-    let res = match my_var {
-        5 => 42i32,
-        _ => return 24i32,
-    };
-    res
-}
-"#,
-            r#"
-fn foo() -> Result<i32, ${0:_}> {
-    let my_var = 5;
-    let res = match my_var {
-        5 => 42i32,
-        _ => return Ok(24i32),
-    };
-    Ok(res)
-}
-"#,
-        );
-
-        check_assist(
-            wrap_return_type_in_result,
-            r#"
-fn foo() -> i32<|> {
-    let my_var = 5;
-    let res = if my_var == 5 {
-        42i32
-    } else {
-        return 24i32;
-    };
-    res
-}
-"#,
-            r#"
-fn foo() -> Result<i32, ${0:_}> {
-    let my_var = 5;
-    let res = if my_var == 5 {
-        42i32
-    } else {
-        return Ok(24i32);
-    };
-    Ok(res)
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn wrap_return_type_in_result_simple_with_tail_block_like_match_deeper() {
-        check_assist(
-            wrap_return_type_in_result,
-            r#"
-fn foo() -> i32<|> {
-    let my_var = 5;
-    match my_var {
-        5 => {
-            if true {
-                42i32
-            } else {
-                25i32
-            }
-        },
-        _ => {
-            let test = "test";
-            if test == "test" {
-                return bar();
-            }
-            53i32
-        },
-    }
-}
-"#,
-            r#"
-fn foo() -> Result<i32, ${0:_}> {
-    let my_var = 5;
-    match my_var {
-        5 => {
-            if true {
-                Ok(42i32)
-            } else {
-                Ok(25i32)
-            }
-        },
-        _ => {
-            let test = "test";
-            if test == "test" {
-                return Ok(bar());
-            }
-            Ok(53i32)
-        },
-    }
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn wrap_return_type_in_result_simple_with_tail_block_like_early_return() {
-        check_assist(
-            wrap_return_type_in_result,
-            r#"
-fn foo() -> i<|>32 {
-    let test = "test";
-    if test == "test" {
-        return 24i32;
-    }
-    53i32
-}
-"#,
-            r#"
-fn foo() -> Result<i32, ${0:_}> {
-    let test = "test";
-    if test == "test" {
-        return Ok(24i32);
-    }
-    Ok(53i32)
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn wrap_return_type_in_result_simple_with_closure() {
-        check_assist(
-            wrap_return_type_in_result,
-            r#"
-fn foo(the_field: u32) -><|> u32 {
-    let true_closure = || { return true; };
-    if the_field < 5 {
-        let mut i = 0;
-        if true_closure() {
-            return 99;
-        } else {
-            return 0;
-        }
-    }
-    the_field
-}
-"#,
-            r#"
-fn foo(the_field: u32) -> Result<u32, ${0:_}> {
-    let true_closure = || { return true; };
-    if the_field < 5 {
-        let mut i = 0;
-        if true_closure() {
-            return Ok(99);
-        } else {
-            return Ok(0);
-        }
-    }
-    Ok(the_field)
-}
-"#,
-        );
-
-        check_assist(
-            wrap_return_type_in_result,
-            r#"
-            fn foo(the_field: u32) -> u32<|> {
-                let true_closure = || {
-                    return true;
-                };
-                if the_field < 5 {
-                    let mut i = 0;
-
-
-                    if true_closure() {
-                        return 99;
-                    } else {
-                        return 0;
-                    }
-                }
-                let t = None;
-
-                t.unwrap_or_else(|| the_field)
-            }
-            "#,
-            r#"
-            fn foo(the_field: u32) -> Result<u32, ${0:_}> {
-                let true_closure = || {
-                    return true;
-                };
-                if the_field < 5 {
-                    let mut i = 0;
-
-
-                    if true_closure() {
-                        return Ok(99);
-                    } else {
-                        return Ok(0);
-                    }
-                }
-                let t = None;
-
-                Ok(t.unwrap_or_else(|| the_field))
-            }
-            "#,
-        );
-    }
-
-    #[test]
-    fn wrap_return_type_in_result_simple_with_weird_forms() {
-        check_assist(
-            wrap_return_type_in_result,
-            r#"
-fn foo() -> i32<|> {
-    let test = "test";
-    if test == "test" {
-        return 24i32;
-    }
-    let mut i = 0;
-    loop {
-        if i == 1 {
-            break 55;
-        }
-        i += 1;
-    }
-}
-"#,
-            r#"
-fn foo() -> Result<i32, ${0:_}> {
-    let test = "test";
-    if test == "test" {
-        return Ok(24i32);
-    }
-    let mut i = 0;
-    loop {
-        if i == 1 {
-            break Ok(55);
-        }
-        i += 1;
-    }
-}
-"#,
-        );
-
-        check_assist(
-            wrap_return_type_in_result,
-            r#"
-fn foo() -> i32<|> {
-    let test = "test";
-    if test == "test" {
-        return 24i32;
-    }
-    let mut i = 0;
-    loop {
-        loop {
-            if i == 1 {
-                break 55;
-            }
-            i += 1;
-        }
-    }
-}
-"#,
-            r#"
-fn foo() -> Result<i32, ${0:_}> {
-    let test = "test";
-    if test == "test" {
-        return Ok(24i32);
-    }
-    let mut i = 0;
-    loop {
-        loop {
-            if i == 1 {
-                break Ok(55);
-            }
-            i += 1;
-        }
-    }
-}
-"#,
-        );
-
-        check_assist(
-            wrap_return_type_in_result,
-            r#"
-fn foo() -> i3<|>2 {
-    let test = "test";
-    let other = 5;
-    if test == "test" {
-        let res = match other {
-            5 => 43,
-            _ => return 56,
-        };
-    }
-    let mut i = 0;
-    loop {
-        loop {
-            if i == 1 {
-                break 55;
-            }
-            i += 1;
-        }
-    }
-}
-"#,
-            r#"
-fn foo() -> Result<i32, ${0:_}> {
-    let test = "test";
-    let other = 5;
-    if test == "test" {
-        let res = match other {
-            5 => 43,
-            _ => return Ok(56),
-        };
-    }
-    let mut i = 0;
-    loop {
-        loop {
-            if i == 1 {
-                break Ok(55);
-            }
-            i += 1;
-        }
-    }
-}
-"#,
-        );
-
-        check_assist(
-            wrap_return_type_in_result,
-            r#"
-fn foo(the_field: u32) -> u32<|> {
-    if the_field < 5 {
-        let mut i = 0;
-        loop {
-            if i > 5 {
-                return 55u32;
-            }
-            i += 3;
-        }
-        match i {
-            5 => return 99,
-            _ => return 0,
-        };
-    }
-    the_field
-}
-"#,
-            r#"
-fn foo(the_field: u32) -> Result<u32, ${0:_}> {
-    if the_field < 5 {
-        let mut i = 0;
-        loop {
-            if i > 5 {
-                return Ok(55u32);
-            }
-            i += 3;
-        }
-        match i {
-            5 => return Ok(99),
-            _ => return Ok(0),
-        };
-    }
-    Ok(the_field)
-}
-"#,
-        );
-
-        check_assist(
-            wrap_return_type_in_result,
-            r#"
-fn foo(the_field: u32) -> u3<|>2 {
-    if the_field < 5 {
-        let mut i = 0;
-        match i {
-            5 => return 99,
-            _ => return 0,
-        }
-    }
-    the_field
-}
-"#,
-            r#"
-fn foo(the_field: u32) -> Result<u32, ${0:_}> {
-    if the_field < 5 {
-        let mut i = 0;
-        match i {
-            5 => return Ok(99),
-            _ => return Ok(0),
-        }
-    }
-    Ok(the_field)
-}
-"#,
-        );
-
-        check_assist(
-            wrap_return_type_in_result,
-            r#"
-fn foo(the_field: u32) -> u32<|> {
-    if the_field < 5 {
-        let mut i = 0;
-        if i == 5 {
-            return 99
-        } else {
-            return 0
-        }
-    }
-    the_field
-}
-"#,
-            r#"
-fn foo(the_field: u32) -> Result<u32, ${0:_}> {
-    if the_field < 5 {
-        let mut i = 0;
-        if i == 5 {
-            return Ok(99)
-        } else {
-            return Ok(0)
-        }
-    }
-    Ok(the_field)
-}
-"#,
-        );
-
-        check_assist(
-            wrap_return_type_in_result,
-            r#"
-fn foo(the_field: u32) -> <|>u32 {
-    if the_field < 5 {
-        let mut i = 0;
-        if i == 5 {
-            return 99;
-        } else {
-            return 0;
-        }
-    }
-    the_field
-}
-"#,
-            r#"
-fn foo(the_field: u32) -> Result<u32, ${0:_}> {
-    if the_field < 5 {
-        let mut i = 0;
-        if i == 5 {
-            return Ok(99);
-        } else {
-            return Ok(0);
-        }
-    }
-    Ok(the_field)
-}
-"#,
-        );
-    }
-}
diff --git a/crates/assists/src/lib.rs b/crates/assists/src/lib.rs
index e8d81b33d7..af88b34374 100644
--- a/crates/assists/src/lib.rs
+++ b/crates/assists/src/lib.rs
@@ -120,11 +120,13 @@ mod handlers {
 
     pub(crate) type Handler = fn(&mut Assists, &AssistContext) -> Option<()>;
 
+    mod add_custom_impl;
     mod add_explicit_type;
     mod add_missing_impl_members;
     mod add_turbo_fish;
     mod apply_demorgan;
     mod auto_import;
+    mod change_return_type_to_result;
     mod change_visibility;
     mod convert_integer_literal;
     mod early_return;
@@ -155,7 +157,6 @@ mod handlers {
     mod remove_mut;
     mod remove_unused_param;
     mod reorder_fields;
-    mod replace_derive_with_manual_impl;
     mod replace_if_let_with_match;
     mod replace_impl_trait_with_generic;
     mod replace_let_with_if_let;
@@ -164,15 +165,16 @@ mod handlers {
     mod replace_unwrap_with_match;
     mod split_import;
     mod unwrap_block;
-    mod wrap_return_type_in_result;
 
     pub(crate) fn all() -> &'static [Handler] {
         &[
             // These are alphabetic for the foolish consistency
+            add_custom_impl::add_custom_impl,
             add_explicit_type::add_explicit_type,
             add_turbo_fish::add_turbo_fish,
             apply_demorgan::apply_demorgan,
             auto_import::auto_import,
+            change_return_type_to_result::change_return_type_to_result,
             change_visibility::change_visibility,
             convert_integer_literal::convert_integer_literal,
             early_return::convert_to_guarded_return,
@@ -206,7 +208,6 @@ mod handlers {
             remove_mut::remove_mut,
             remove_unused_param::remove_unused_param,
             reorder_fields::reorder_fields,
-            replace_derive_with_manual_impl::replace_derive_with_manual_impl,
             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,
@@ -214,7 +215,6 @@ mod handlers {
             replace_unwrap_with_match::replace_unwrap_with_match,
             split_import::split_import,
             unwrap_block::unwrap_block,
-            wrap_return_type_in_result::wrap_return_type_in_result,
             // These are manually sorted for better priorities
             add_missing_impl_members::add_missing_impl_members,
             add_missing_impl_members::add_missing_default_members,
diff --git a/crates/assists/src/utils.rs b/crates/assists/src/utils.rs
index 7071fe96b1..56f925ee69 100644
--- a/crates/assists/src/utils.rs
+++ b/crates/assists/src/utils.rs
@@ -4,22 +4,17 @@ pub(crate) mod import_assets;
 
 use std::ops;
 
-use hir::{Crate, Enum, HasSource, Module, ScopeDef, Semantics, Trait};
+use hir::{Crate, Enum, Module, ScopeDef, Semantics, Trait};
 use ide_db::RootDatabase;
 use itertools::Itertools;
 use syntax::{
-    ast::edit::AstNodeEdit,
-    ast::NameOwner,
-    ast::{self, edit, make, ArgListOwner},
+    ast::{self, make, ArgListOwner},
     AstNode, Direction,
     SyntaxKind::*,
     SyntaxNode, TextSize, T,
 };
 
-use crate::{
-    assist_config::SnippetCap,
-    ast_transform::{self, AstTransform, QualifyPaths, SubstituteTypeParams},
-};
+use crate::assist_config::SnippetCap;
 
 pub use insert_use::MergeBehaviour;
 pub(crate) use insert_use::{insert_use, ImportScope};
@@ -82,87 +77,6 @@ pub fn extract_trivial_expression(block: &ast::BlockExpr) -> Option<ast::Expr> {
     None
 }
 
-#[derive(Copy, Clone, PartialEq)]
-pub enum DefaultMethods {
-    Only,
-    No,
-}
-
-pub fn filter_assoc_items(
-    db: &RootDatabase,
-    items: &[hir::AssocItem],
-    default_methods: DefaultMethods,
-) -> Vec<ast::AssocItem> {
-    fn has_def_name(item: &ast::AssocItem) -> bool {
-        match item {
-            ast::AssocItem::Fn(def) => def.name(),
-            ast::AssocItem::TypeAlias(def) => def.name(),
-            ast::AssocItem::Const(def) => def.name(),
-            ast::AssocItem::MacroCall(_) => None,
-        }
-        .is_some()
-    };
-
-    items
-        .iter()
-        .map(|i| match i {
-            hir::AssocItem::Function(i) => ast::AssocItem::Fn(i.source(db).value),
-            hir::AssocItem::TypeAlias(i) => ast::AssocItem::TypeAlias(i.source(db).value),
-            hir::AssocItem::Const(i) => ast::AssocItem::Const(i.source(db).value),
-        })
-        .filter(has_def_name)
-        .filter(|it| match it {
-            ast::AssocItem::Fn(def) => matches!(
-                (default_methods, def.body()),
-                (DefaultMethods::Only, Some(_)) | (DefaultMethods::No, None)
-            ),
-            _ => default_methods == DefaultMethods::No,
-        })
-        .collect::<Vec<_>>()
-}
-
-pub fn add_trait_assoc_items_to_impl(
-    sema: &hir::Semantics<ide_db::RootDatabase>,
-    items: Vec<ast::AssocItem>,
-    trait_: hir::Trait,
-    impl_def: ast::Impl,
-    target_scope: hir::SemanticsScope,
-) -> (ast::Impl, ast::AssocItem) {
-    let impl_item_list = impl_def.assoc_item_list().unwrap_or_else(make::assoc_item_list);
-
-    let n_existing_items = impl_item_list.assoc_items().count();
-    let source_scope = sema.scope_for_def(trait_);
-    let ast_transform = QualifyPaths::new(&target_scope, &source_scope)
-        .or(SubstituteTypeParams::for_trait_impl(&source_scope, trait_, impl_def.clone()));
-
-    let items = items
-        .into_iter()
-        .map(|it| ast_transform::apply(&*ast_transform, it))
-        .map(|it| match it {
-            ast::AssocItem::Fn(def) => ast::AssocItem::Fn(add_body(def)),
-            ast::AssocItem::TypeAlias(def) => ast::AssocItem::TypeAlias(def.remove_bounds()),
-            _ => it,
-        })
-        .map(|it| edit::remove_attrs_and_docs(&it));
-
-    let new_impl_item_list = impl_item_list.append_items(items);
-    let new_impl_def = impl_def.with_assoc_item_list(new_impl_item_list);
-    let first_new_item =
-        new_impl_def.assoc_item_list().unwrap().assoc_items().nth(n_existing_items).unwrap();
-    return (new_impl_def, first_new_item);
-
-    fn add_body(fn_def: ast::Fn) -> ast::Fn {
-        match fn_def.body() {
-            Some(_) => fn_def,
-            None => {
-                let body =
-                    make::block_expr(None, Some(make::expr_todo())).indent(edit::IndentLevel(1));
-                fn_def.with_body(body)
-            }
-        }
-    }
-}
-
 #[derive(Clone, Copy, Debug)]
 pub(crate) enum Cursor<'a> {
     Replace(&'a SyntaxNode),
diff --git a/crates/completion/src/completions/postfix.rs b/crates/completion/src/completions/postfix.rs
index 7fbda7a6bd..348f017bd7 100644
--- a/crates/completion/src/completions/postfix.rs
+++ b/crates/completion/src/completions/postfix.rs
@@ -180,16 +180,6 @@ pub(crate) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
     postfix_snippet(ctx, cap, &dot_receiver, "ok", "Ok(expr)", &format!("Ok({})", receiver_text))
         .add_to(acc);
 
-    postfix_snippet(
-        ctx,
-        cap,
-        &dot_receiver,
-        "some",
-        "Some(expr)",
-        &format!("Some({})", receiver_text),
-    )
-    .add_to(acc);
-
     postfix_snippet(
         ctx,
         cap,
@@ -301,7 +291,6 @@ fn main() {
                 sn ok    Ok(expr)
                 sn ref   &expr
                 sn refm  &mut expr
-                sn some  Some(expr)
                 sn while while expr {}
             "#]],
         );
@@ -325,7 +314,6 @@ fn main() {
                 sn ok    Ok(expr)
                 sn ref   &expr
                 sn refm  &mut expr
-                sn some  Some(expr)
             "#]],
         )
     }
diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs
index 37ab7a8f67..05bafe9c89 100644
--- a/crates/ide/src/syntax_highlighting.rs
+++ b/crates/ide/src/syntax_highlighting.rs
@@ -557,9 +557,7 @@ fn highlight_element(
                     h
                 }
             }
-            T![::] | T![->] | T![=>] | T![..] | T![=] | T![@] | T![.] => {
-                HighlightTag::Operator.into()
-            }
+            T![::] | T![->] | T![=>] | T![..] | T![=] | T![@] => HighlightTag::Operator.into(),
             T![!] if element.parent().and_then(ast::MacroCall::cast).is_some() => {
                 HighlightTag::Macro.into()
             }
diff --git a/crates/syntax/src/algo.rs b/crates/syntax/src/algo.rs
index 30adb72176..7ac6076a44 100644
--- a/crates/syntax/src/algo.rs
+++ b/crates/syntax/src/algo.rs
@@ -111,18 +111,28 @@ pub enum InsertPosition<T> {
 
 type FxIndexMap<K, V> = IndexMap<K, V, BuildHasherDefault<rustc_hash::FxHasher>>;
 
+#[derive(Debug, Hash, PartialEq, Eq)]
+enum TreeDiffInsertPos {
+    After(SyntaxElement),
+    AsFirstChild(SyntaxElement),
+}
+
 #[derive(Debug)]
 pub struct TreeDiff {
     replacements: FxHashMap<SyntaxElement, SyntaxElement>,
     deletions: Vec<SyntaxElement>,
     // the vec as well as the indexmap are both here to preserve order
-    insertions: FxIndexMap<SyntaxElement, Vec<SyntaxElement>>,
+    insertions: FxIndexMap<TreeDiffInsertPos, Vec<SyntaxElement>>,
 }
 
 impl TreeDiff {
     pub fn into_text_edit(&self, builder: &mut TextEditBuilder) {
         for (anchor, to) in self.insertions.iter() {
-            to.iter().for_each(|to| builder.insert(anchor.text_range().end(), to.to_string()));
+            let offset = match anchor {
+                TreeDiffInsertPos::After(it) => it.text_range().end(),
+                TreeDiffInsertPos::AsFirstChild(it) => it.text_range().start(),
+            };
+            to.iter().for_each(|to| builder.insert(offset, to.to_string()));
         }
         for (from, to) in self.replacements.iter() {
             builder.replace(from.text_range(), to.to_string())
@@ -188,19 +198,20 @@ pub fn diff(from: &SyntaxNode, to: &SyntaxNode) -> TreeDiff {
             let lhs_child = lhs_children.next();
             match (lhs_child.clone(), rhs_children.next()) {
                 (None, None) => break,
-                (None, Some(element)) => match last_lhs.clone() {
-                    Some(prev) => {
-                        mark::hit!(diff_insert);
-                        diff.insertions.entry(prev).or_insert_with(Vec::new).push(element);
-                    }
-                    // first iteration, this means we got no anchor element to insert after
-                    // therefor replace the parent node instead
-                    None => {
-                        mark::hit!(diff_replace_parent);
-                        diff.replacements.insert(lhs.clone().into(), rhs.clone().into());
-                        break;
-                    }
-                },
+                (None, Some(element)) => {
+                    let insert_pos = match last_lhs.clone() {
+                        Some(prev) => {
+                            mark::hit!(diff_insert);
+                            TreeDiffInsertPos::After(prev)
+                        }
+                        // first iteration, insert into out parent as the first child
+                        None => {
+                            mark::hit!(diff_insert_as_first_child);
+                            TreeDiffInsertPos::AsFirstChild(lhs.clone().into())
+                        }
+                    };
+                    diff.insertions.entry(insert_pos).or_insert_with(Vec::new).push(element);
+                }
                 (Some(element), None) => {
                     mark::hit!(diff_delete);
                     diff.deletions.push(element);
@@ -224,8 +235,15 @@ pub fn diff(from: &SyntaxNode, to: &SyntaxNode) -> TreeDiff {
                         }
                     }
                     let drain = look_ahead_scratch.drain(..);
-                    if let Some(prev) = last_lhs.clone().filter(|_| insert) {
-                        diff.insertions.entry(prev).or_insert_with(Vec::new).extend(drain);
+                    if insert {
+                        let insert_pos = if let Some(prev) = last_lhs.clone().filter(|_| insert) {
+                            TreeDiffInsertPos::After(prev)
+                        } else {
+                            mark::hit!(insert_first_child);
+                            TreeDiffInsertPos::AsFirstChild(lhs.clone().into())
+                        };
+
+                        diff.insertions.entry(insert_pos).or_insert_with(Vec::new).extend(drain);
                         rhs_children = rhs_children_clone;
                     } else {
                         go(diff, lhs_ele, rhs_ele)
@@ -331,10 +349,7 @@ pub struct SyntaxRewriter<'a> {
 
 impl fmt::Debug for SyntaxRewriter<'_> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.debug_struct("SyntaxRewriter")
-            .field("replacements", &self.replacements)
-            .field("insertions", &self.insertions)
-            .finish()
+        f.debug_struct("SyntaxRewriter").field("replacements", &self.replacements).finish()
     }
 }
 
@@ -632,18 +647,19 @@ mod tests {
 
     #[test]
     fn replace_parent() {
-        mark::check!(diff_replace_parent);
+        mark::check!(diff_insert_as_first_child);
         check_diff(
             r#""#,
             r#"use foo::bar;"#,
             expect![[r#"
                 insertions:
 
-
+                Line 0: AsFirstChild(Node(SOURCE_FILE@0..0))
+                -> use foo::bar;
 
                 replacements:
 
-                Line 0: Node(SOURCE_FILE@0..0) -> use foo::bar;
+
 
                 deletions:
 
@@ -666,7 +682,7 @@ use baz;"#,
             expect![[r#"
                 insertions:
 
-                Line 2: Node(USE@10..18)
+                Line 2: After(Node(USE@10..18))
                 -> "\n"
                 -> use baz;
 
@@ -694,7 +710,7 @@ use baz;"#,
             expect![[r#"
                 insertions:
 
-                Line 2: Token(WHITESPACE@9..10 "\n")
+                Line 2: After(Token(WHITESPACE@9..10 "\n"))
                 -> use bar;
                 -> "\n"
 
@@ -722,7 +738,7 @@ use baz;"#,
             expect![[r#"
                 insertions:
 
-                Line 0: Token(WHITESPACE@0..1 "\n")
+                Line 0: After(Token(WHITESPACE@0..1 "\n"))
                 -> use foo;
                 -> "\n"
 
@@ -737,6 +753,36 @@ use baz;"#,
         )
     }
 
+    #[test]
+    fn first_child_insertion() {
+        mark::check!(insert_first_child);
+        check_diff(
+            r#"fn main() {
+        stdi
+    }"#,
+            r#"use foo::bar;
+
+    fn main() {
+        stdi
+    }"#,
+            expect![[r#"
+                insertions:
+
+                Line 0: AsFirstChild(Node(SOURCE_FILE@0..30))
+                -> use foo::bar;
+                -> "\n\n    "
+
+                replacements:
+
+
+
+                deletions:
+
+
+            "#]],
+        );
+    }
+
     #[test]
     fn delete_last() {
         mark::check!(diff_delete);
@@ -779,7 +825,7 @@ use crate::AstNode;
             expect![[r#"
                 insertions:
 
-                Line 1: Node(USE@1..35)
+                Line 1: After(Node(USE@1..35))
                 -> "\n\n"
                 -> use crate::AstNode;
 
@@ -845,10 +891,10 @@ use std::ops::{self, RangeInclusive};
             expect![[r#"
                 insertions:
 
-                Line 2: Node(PATH_SEGMENT@5..8)
+                Line 2: After(Node(PATH_SEGMENT@5..8))
                 -> ::
                 -> fmt
-                Line 6: Token(WHITESPACE@86..87 "\n")
+                Line 6: After(Token(WHITESPACE@86..87 "\n"))
                 -> use std::hash::BuildHasherDefault;
                 -> "\n"
                 -> use std::ops::{self, RangeInclusive};
@@ -892,14 +938,14 @@ fn main() {
             expect![[r#"
                 insertions:
 
-                Line 3: Node(BLOCK_EXPR@40..63)
+                Line 3: After(Node(BLOCK_EXPR@40..63))
                 -> " "
                 -> match Err(92) {
                         Ok(it) => it,
                         _ => return,
                     }
                 -> ;
-                Line 3: Node(IF_EXPR@17..63)
+                Line 3: After(Node(IF_EXPR@17..63))
                 -> "\n    "
                 -> foo(x);
 
@@ -934,14 +980,18 @@ fn main() {
             _ => format!("{}", syn),
         };
 
-        let insertions = diff.insertions.iter().format_with("\n", |(k, v), f| {
-            f(&format!(
-                "Line {}: {:?}\n-> {}",
-                line_number(k),
-                k,
-                v.iter().format_with("\n-> ", |v, f| f(&fmt_syntax(v)))
-            ))
-        });
+        let insertions =
+            diff.insertions.iter().format_with("\n", |(k, v), f| -> Result<(), std::fmt::Error> {
+                f(&format!(
+                    "Line {}: {:?}\n-> {}",
+                    line_number(match k {
+                        super::TreeDiffInsertPos::After(syn) => syn,
+                        super::TreeDiffInsertPos::AsFirstChild(syn) => syn,
+                    }),
+                    k,
+                    v.iter().format_with("\n-> ", |v, f| f(&fmt_syntax(v)))
+                ))
+            });
 
         let replacements = diff
             .replacements
diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs
index 876659a2b7..b1578820fe 100644
--- a/crates/syntax/src/ast/make.rs
+++ b/crates/syntax/src/ast/make.rs
@@ -25,10 +25,6 @@ pub fn assoc_item_list() -> ast::AssocItemList {
     ast_from_text("impl C for D {};")
 }
 
-pub fn impl_trait(trait_: ast::Path, ty: ast::Path) -> ast::Impl {
-    ast_from_text(&format!("impl {} for {} {{}}", trait_, ty))
-}
-
 pub fn path_segment(name_ref: ast::NameRef) -> ast::PathSegment {
     ast_from_text(&format!("use {};", name_ref))
 }
diff --git a/editors/code/rust.tmGrammar.json b/editors/code/rust.tmGrammar.json
index b3a10795eb..77595aa006 100644
--- a/editors/code/rust.tmGrammar.json
+++ b/editors/code/rust.tmGrammar.json
@@ -50,7 +50,7 @@
         {
             "comment": "macro type metavariables",
             "name": "meta.macro.metavariable.type.rust",
-            "match": "(\\$)((crate)|([A-Z][A-Za-z0-9_]*))((:)(block|expr|ident|item|lifetime|literal|meta|path?|stmt|tt|ty|vis))?",
+            "match": "(\\$)((crate)|([A-Z][A-Za-z0-9_]*))((:)(block|expr|ident|item|lifetime|literal|meta|pat|path|stmt|tt|ty|vis))?",
             "captures": {
                 "1": {
                     "name": "keyword.operator.macro.dollar.rust"
@@ -77,7 +77,7 @@
         {
             "comment": "macro metavariables",
             "name": "meta.macro.metavariable.rust",
-            "match": "(\\$)([a-z][A-Za-z0-9_]*)((:)(block|expr|ident|item|lifetime|literal|meta|path?|stmt|tt|ty|vis))?",
+            "match": "(\\$)([a-z][A-Za-z0-9_]*)((:)(block|expr|ident|item|lifetime|literal|meta|pat|path|stmt|tt|ty|vis))?",
             "captures": {
                 "1": {
                     "name": "keyword.operator.macro.dollar.rust"
@@ -167,7 +167,7 @@
             "match": "(mod)\\s+((?:r#(?!crate|[Ss]elf|super))?[a-z][A-Za-z0-9_]*)",
             "captures": {
                 "1": {
-                    "name": "storage.type.rust"
+                    "name": "keyword.control.rust"
                 },
                 "2": {
                     "name": "entity.name.module.rust"
@@ -180,7 +180,7 @@
             "begin": "\\b(extern)\\s+(crate)",
             "beginCaptures": {
                 "1": {
-                    "name": "storage.type.rust"
+                    "name": "keyword.control.rust"
                 },
                 "2": {
                     "name": "keyword.other.crate.rust"
@@ -213,7 +213,7 @@
             "begin": "\\b(use)\\s",
             "beginCaptures": {
                 "1": {
-                    "name": "keyword.other.rust"
+                    "name": "keyword.control.rust"
                 }
             },
             "end": ";",
@@ -342,7 +342,7 @@
                     "match": "\\b(const)\\s+([A-Z][A-Za-z0-9_]*)\\b",
                     "captures": {
                         "1": {
-                            "name": "storage.type.rust"
+                            "name": "keyword.control.rust"
                         },
                         "2": {
                             "name": "constant.other.caps.rust"
@@ -450,7 +450,7 @@
                     "begin": "\\b(fn)\\s+((?:r#(?!crate|[Ss]elf|super))?[A-Za-z0-9_]+)((\\()|(<))",
                     "beginCaptures": {
                         "1": {
-                            "name": "keyword.other.fn.rust"
+                            "name": "keyword.control.fn.rust"
                         },
                         "2": {
                             "name": "entity.name.function.rust"
@@ -643,7 +643,7 @@
                 {
                     "comment": "control flow keywords",
                     "name": "keyword.control.rust",
-                    "match": "\\b(await|break|continue|do|else|for|if|loop|match|return|try|while|yield)\\b"
+                    "match": "\\b(async|await|break|continue|do|else|for|if|loop|match|move|return|try|where|while|yield)\\b"
                 },
                 {
                     "comment": "storage keywords",
@@ -658,7 +658,7 @@
                 {
                     "comment": "other keywords",
                     "name": "keyword.other.rust",
-                    "match": "\\b(as|async|become|box|dyn|move|final|impl|in|override|priv|pub|ref|typeof|union|unsafe|unsized|use|virtual|where)\\b"
+                    "match": "\\b(as|become|box|dyn|final|impl|in|override|priv|pub|ref|typeof|union|unsafe|unsized|use|virtual)\\b"
                 },
                 {
                     "comment": "fn",
@@ -675,6 +675,11 @@
                     "name": "storage.modifier.mut.rust",
                     "match": "\\bmut\\b"
                 },
+                {
+                    "comment": "math operators",
+                    "name": "keyword.operator.math.rust",
+                    "match": "(([+%]|(\\*(?!\\w)))(?!=))|(-(?!>))|(/(?!/))"
+                },
                 {
                     "comment": "logical operators",
                     "name": "keyword.operator.logical.rust",
@@ -688,7 +693,7 @@
                 {
                     "comment": "assignment operators",
                     "name": "keyword.operator.assignment.rust",
-                    "match": "(\\+=|-=|\\*=|/=|%=|\\^=|&=|\\|=|<<=|>>=)"
+                    "match": "(-=|\\*=|/=|%=|\\^=|&=|\\|=|<<=|>>=)"
                 },
                 {
                     "comment": "single equal",
@@ -700,11 +705,6 @@
                     "name": "keyword.operator.comparison.rust",
                     "match": "(=(=)?(?!>)|!=|<=|(?<!=)>=)"
                 },
-                {
-                    "comment": "math operators",
-                    "name": "keyword.operator.math.rust",
-                    "match": "(([+%]|(\\*(?!\\w)))(?!=))|(-(?!>))|(/(?!/))"
-                },
                 {
                     "comment": "less than, greater than (special case)",
                     "match": "(?:\\b|(?:(\\))|(\\])|(\\})))[ \\t]+([<>])[ \\t]+(?:\\b|(?:(\\()|(\\[)|(\\{)))",
@@ -1127,7 +1127,7 @@
                 {
                     "comment": "variables",
                     "name": "variable.other.rust",
-                    "match": "\\b(?<!(?<!\\.)\\.)(?:r#(?!(crate|[Ss]elf|super)))?[a-z0-9_]+\\b"
+                    "match": "\\b(?<!\\.)(?:r#(?!(crate|[Ss]elf|super)))?[a-z0-9_]+\\b"
                 }
             ]
         }
diff --git a/editors/code/src/snippets.ts b/editors/code/src/snippets.ts
index fee736e7d9..258b49982e 100644
--- a/editors/code/src/snippets.ts
+++ b/editors/code/src/snippets.ts
@@ -3,29 +3,16 @@ import * as vscode from 'vscode';
 import { assert } from './util';
 
 export async function applySnippetWorkspaceEdit(edit: vscode.WorkspaceEdit) {
-    if (edit.entries().length === 1) {
-        const [uri, edits] = edit.entries()[0];
-        const editor = await editorFromUri(uri);
-        if (editor) await applySnippetTextEdits(editor, edits);
-        return;
-    }
-    for (const [uri, edits] of edit.entries()) {
-        const editor = await editorFromUri(uri);
-        if (editor) await editor.edit((builder) => {
-            for (const indel of edits) {
-                assert(!parseSnippet(indel.newText), `bad ws edit: snippet received with multiple edits: ${JSON.stringify(edit)}`);
-                builder.replace(indel.range, indel.newText);
-            }
-        });
-    }
-}
+    assert(edit.entries().length === 1, `bad ws edit: ${JSON.stringify(edit)}`);
+    const [uri, edits] = edit.entries()[0];
 
-async function editorFromUri(uri: vscode.Uri): Promise<vscode.TextEditor | undefined> {
     if (vscode.window.activeTextEditor?.document.uri !== uri) {
         // `vscode.window.visibleTextEditors` only contains editors whose contents are being displayed
         await vscode.window.showTextDocument(uri, {});
     }
-    return vscode.window.visibleTextEditors.find((it) => it.document.uri.toString() === uri.toString());
+    const editor = vscode.window.visibleTextEditors.find((it) => it.document.uri.toString() === uri.toString());
+    if (!editor) return;
+    await applySnippetTextEdits(editor, edits);
 }
 
 export async function applySnippetTextEdits(editor: vscode.TextEditor, edits: vscode.TextEdit[]) {


Test patch:
diff --git a/crates/assists/src/tests.rs b/crates/assists/src/tests.rs
index 709a34d03a..849d85e763 100644
--- a/crates/assists/src/tests.rs
+++ b/crates/assists/src/tests.rs
@@ -7,7 +7,7 @@ use syntax::TextRange;
 use test_utils::{assert_eq_text, extract_offset, extract_range};
 
 use crate::{handlers::Handler, Assist, AssistConfig, AssistContext, AssistKind, Assists};
-use stdx::{format_to, trim_indent};
+use stdx::trim_indent;
 
 pub(crate) fn with_single_file(text: &str) -> (RootDatabase, FileId) {
     RootDatabase::with_single_file(text)
@@ -98,24 +98,11 @@ fn check(handler: Handler, before: &str, expected: ExpectedResult, assist_label:
     match (assist, expected) {
         (Some(assist), ExpectedResult::After(after)) => {
             let mut source_change = assist.source_change;
-            assert!(!source_change.source_file_edits.is_empty());
-            let skip_header = source_change.source_file_edits.len() == 1;
-            source_change.source_file_edits.sort_by_key(|it| it.file_id);
-
-            let mut buf = String::new();
-            for source_file_edit in source_change.source_file_edits {
-                let mut text = db.file_text(source_file_edit.file_id).as_ref().to_owned();
-                source_file_edit.edit.apply(&mut text);
-                if !skip_header {
-                    let sr = db.file_source_root(source_file_edit.file_id);
-                    let sr = db.source_root(sr);
-                    let path = sr.path_for_file(&source_file_edit.file_id).unwrap();
-                    format_to!(buf, "//- {}\n", path)
-                }
-                buf.push_str(&text);
-            }
-
-            assert_eq_text!(after, &buf);
+            let change = source_change.source_file_edits.pop().unwrap();
+
+            let mut actual = db.file_text(change.file_id).as_ref().to_owned();
+            change.edit.apply(&mut actual);
+            assert_eq_text!(after, &actual);
         }
         (Some(assist), ExpectedResult::Target(target)) => {
             let range = assist.assist.target;
diff --git a/crates/assists/src/tests/generated.rs b/crates/assists/src/tests/generated.rs
index dbf4f21aa5..168e1626ab 100644
--- a/crates/assists/src/tests/generated.rs
+++ b/crates/assists/src/tests/generated.rs
@@ -2,6 +2,25 @@
 
 use super::check_doc_test;
 
+#[test]
+fn doctest_add_custom_impl() {
+    check_doc_test(
+        "add_custom_impl",
+        r#####"
+#[derive(Deb<|>ug, Display)]
+struct S;
+"#####,
+        r#####"
+#[derive(Display)]
+struct S;
+
+impl Debug for S {
+    $0
+}
+"#####,
+    )
+}
+
 #[test]
 fn doctest_add_explicit_type() {
     check_doc_test(
@@ -158,6 +177,19 @@ pub mod std { pub mod collections { pub struct HashMap { } } }
     )
 }
 
+#[test]
+fn doctest_change_return_type_to_result() {
+    check_doc_test(
+        "change_return_type_to_result",
+        r#####"
+fn foo() -> i32<|> { 42i32 }
+"#####,
+        r#####"
+fn foo() -> Result<i32, ${0:_}> { Ok(42i32) }
+"#####,
+    )
+}
+
 #[test]
 fn doctest_change_visibility() {
     check_doc_test(
@@ -799,29 +831,6 @@ const test: Foo = Foo {foo: 1, bar: 0}
     )
 }
 
-#[test]
-fn doctest_replace_derive_with_manual_impl() {
-    check_doc_test(
-        "replace_derive_with_manual_impl",
-        r#####"
-trait Debug { fn fmt(&self, f: &mut Formatter) -> Result<()>; }
-#[derive(Deb<|>ug, Display)]
-struct S;
-"#####,
-        r#####"
-trait Debug { fn fmt(&self, f: &mut Formatter) -> Result<()>; }
-#[derive(Display)]
-struct S;
-
-impl Debug for S {
-    fn fmt(&self, f: &mut Formatter) -> Result<()> {
-        ${0:todo!()}
-    }
-}
-"#####,
-    )
-}
-
 #[test]
 fn doctest_replace_if_let_with_match() {
     check_doc_test(
@@ -976,16 +985,3 @@ fn foo() {
 "#####,
     )
 }
-
-#[test]
-fn doctest_wrap_return_type_in_result() {
-    check_doc_test(
-        "wrap_return_type_in_result",
-        r#####"
-fn foo() -> i32<|> { 42i32 }
-"#####,
-        r#####"
-fn foo() -> Result<i32, ${0:_}> { Ok(42i32) }
-"#####,
-    )
-}
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
index f44fe457d4..6322d404fb 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
@@ -67,9 +67,9 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
     <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="generic injected"> </span><span class="variable declaration injected">foo</span><span class="generic injected"> </span><span class="operator injected">=</span><span class="generic injected"> </span><span class="struct injected">Foo</span><span class="operator injected">::</span><span class="function injected">new</span><span class="punctuation injected">(</span><span class="punctuation injected">)</span><span class="punctuation injected">;</span>
     <span class="comment documentation">///</span>
     <span class="comment documentation">/// </span><span class="comment injected">// calls bar on foo</span>
-    <span class="comment documentation">/// </span><span class="macro injected">assert!</span><span class="punctuation injected">(</span><span class="generic injected">foo</span><span class="operator injected">.</span><span class="generic injected">bar</span><span class="punctuation injected">(</span><span class="punctuation injected">)</span><span class="punctuation injected">)</span><span class="punctuation injected">;</span>
+    <span class="comment documentation">/// </span><span class="macro injected">assert!</span><span class="punctuation injected">(</span><span class="generic injected">foo</span><span class="punctuation injected">.</span><span class="generic injected">bar</span><span class="punctuation injected">(</span><span class="punctuation injected">)</span><span class="punctuation injected">)</span><span class="punctuation injected">;</span>
     <span class="comment documentation">///</span>
-    <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="generic injected"> </span><span class="variable declaration injected">bar</span><span class="generic injected"> </span><span class="operator injected">=</span><span class="generic injected"> </span><span class="variable injected">foo</span><span class="operator injected">.</span><span class="field injected">bar</span><span class="generic injected"> </span><span class="operator injected">||</span><span class="generic injected"> </span><span class="struct injected">Foo</span><span class="operator injected">::</span><span class="constant injected">bar</span><span class="punctuation injected">;</span>
+    <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="generic injected"> </span><span class="variable declaration injected">bar</span><span class="generic injected"> </span><span class="operator injected">=</span><span class="generic injected"> </span><span class="variable injected">foo</span><span class="punctuation injected">.</span><span class="field injected">bar</span><span class="generic injected"> </span><span class="operator injected">||</span><span class="generic injected"> </span><span class="struct injected">Foo</span><span class="operator injected">::</span><span class="constant injected">bar</span><span class="punctuation injected">;</span>
     <span class="comment documentation">///</span>
     <span class="comment documentation">/// </span><span class="comment injected">/* multi-line
     </span><span class="comment documentation">/// </span><span class="comment injected">       comment */</span>
@@ -81,7 +81,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
     <span class="comment documentation">/// ```</span>
     <span class="comment documentation">///</span>
     <span class="comment documentation">/// ```rust,no_run</span>
-    <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="generic injected"> </span><span class="variable declaration injected">foobar</span><span class="generic injected"> </span><span class="operator injected">=</span><span class="generic injected"> </span><span class="struct injected">Foo</span><span class="operator injected">::</span><span class="function injected">new</span><span class="punctuation injected">(</span><span class="punctuation injected">)</span><span class="operator injected">.</span><span class="function injected">bar</span><span class="punctuation injected">(</span><span class="punctuation injected">)</span><span class="punctuation injected">;</span><span class="punctuation injected">
+    <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="generic injected"> </span><span class="variable declaration injected">foobar</span><span class="generic injected"> </span><span class="operator injected">=</span><span class="generic injected"> </span><span class="struct injected">Foo</span><span class="operator injected">::</span><span class="function injected">new</span><span class="punctuation injected">(</span><span class="punctuation injected">)</span><span class="punctuation injected">.</span><span class="function injected">bar</span><span class="punctuation injected">(</span><span class="punctuation injected">)</span><span class="punctuation injected">;</span><span class="punctuation injected">
 </span>    <span class="comment documentation">/// ```</span>
     <span class="comment documentation">///</span>
     <span class="comment documentation">/// ```sh</span>
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html b/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
index 4b6d6adc96..552fea6689 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
@@ -73,27 +73,27 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
     <span class="keyword unsafe">unsafe</span> <span class="punctuation">{</span>
         <span class="comment">// unsafe fn and method calls</span>
         <span class="function unsafe">unsafe_fn</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span>
-        <span class="keyword">let</span> <span class="variable declaration">b</span> <span class="operator">=</span> <span class="variable">u</span><span class="operator">.</span><span class="field unsafe">b</span><span class="punctuation">;</span>
+        <span class="keyword">let</span> <span class="variable declaration">b</span> <span class="operator">=</span> <span class="variable">u</span><span class="punctuation">.</span><span class="field unsafe">b</span><span class="punctuation">;</span>
         <span class="keyword control">match</span> <span class="variable">u</span> <span class="punctuation">{</span>
             <span class="union">Union</span> <span class="punctuation">{</span> <span class="field unsafe">b</span><span class="punctuation">:</span> <span class="numeric_literal">0</span> <span class="punctuation">}</span> <span class="operator">=&gt;</span> <span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">,</span>
             <span class="union">Union</span> <span class="punctuation">{</span> <span class="field unsafe">a</span> <span class="punctuation">}</span> <span class="operator">=&gt;</span> <span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">,</span>
         <span class="punctuation">}</span>
-        <span class="struct">HasUnsafeFn</span><span class="operator">.</span><span class="function unsafe">unsafe_method</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span>
+        <span class="struct">HasUnsafeFn</span><span class="punctuation">.</span><span class="function unsafe">unsafe_method</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span>
 
         <span class="comment">// unsafe deref</span>
         <span class="keyword">let</span> <span class="variable declaration">y</span> <span class="operator">=</span> <span class="operator unsafe">*</span><span class="variable">x</span><span class="punctuation">;</span>
 
         <span class="comment">// unsafe access to a static mut</span>
-        <span class="keyword">let</span> <span class="variable declaration">a</span> <span class="operator">=</span> <span class="static mutable unsafe">global_mut</span><span class="operator">.</span><span class="field">a</span><span class="punctuation">;</span>
+        <span class="keyword">let</span> <span class="variable declaration">a</span> <span class="operator">=</span> <span class="static mutable unsafe">global_mut</span><span class="punctuation">.</span><span class="field">a</span><span class="punctuation">;</span>
 
         <span class="comment">// unsafe ref of packed fields</span>
         <span class="keyword">let</span> <span class="variable declaration">packed</span> <span class="operator">=</span> <span class="struct">Packed</span> <span class="punctuation">{</span> <span class="field">a</span><span class="punctuation">:</span> <span class="numeric_literal">0</span> <span class="punctuation">}</span><span class="punctuation">;</span>
-        <span class="keyword">let</span> <span class="variable declaration">a</span> <span class="operator">=</span> <span class="operator unsafe">&</span><span class="variable">packed</span><span class="operator">.</span><span class="field">a</span><span class="punctuation">;</span>
-        <span class="keyword">let</span> <span class="keyword unsafe">ref</span> <span class="variable declaration">a</span> <span class="operator">=</span> <span class="variable">packed</span><span class="operator">.</span><span class="field">a</span><span class="punctuation">;</span>
+        <span class="keyword">let</span> <span class="variable declaration">a</span> <span class="operator">=</span> <span class="operator unsafe">&</span><span class="variable">packed</span><span class="punctuation">.</span><span class="field">a</span><span class="punctuation">;</span>
+        <span class="keyword">let</span> <span class="keyword unsafe">ref</span> <span class="variable declaration">a</span> <span class="operator">=</span> <span class="variable">packed</span><span class="punctuation">.</span><span class="field">a</span><span class="punctuation">;</span>
         <span class="keyword">let</span> <span class="struct">Packed</span> <span class="punctuation">{</span> <span class="keyword unsafe">ref</span> <span class="field">a</span> <span class="punctuation">}</span> <span class="operator">=</span> <span class="variable">packed</span><span class="punctuation">;</span>
         <span class="keyword">let</span> <span class="struct">Packed</span> <span class="punctuation">{</span> <span class="field">a</span><span class="punctuation">:</span> <span class="keyword unsafe">ref</span> <span class="variable declaration">_a</span> <span class="punctuation">}</span> <span class="operator">=</span> <span class="variable">packed</span><span class="punctuation">;</span>
 
         <span class="comment">// unsafe auto ref of packed field</span>
-        <span class="variable">packed</span><span class="operator">.</span><span class="field">a</span><span class="operator">.</span><span class="function unsafe">calls_autoref</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span>
+        <span class="variable">packed</span><span class="punctuation">.</span><span class="field">a</span><span class="punctuation">.</span><span class="function unsafe">calls_autoref</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span>
     <span class="punctuation">}</span>
 <span class="punctuation">}</span></code></pre>
\ No newline at end of file
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlighting.html b/crates/ide/src/syntax_highlighting/test_data/highlighting.html
index 6a10a9dcd0..5eb222ee2d 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlighting.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlighting.html
@@ -67,21 +67,21 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 
 <span class="keyword">impl</span> <span class="trait">Bar</span> <span class="keyword">for</span> <span class="struct">Foo</span> <span class="punctuation">{</span>
     <span class="keyword">fn</span> <span class="function declaration">bar</span><span class="punctuation">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="punctuation">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">i32</span> <span class="punctuation">{</span>
-        <span class="self_keyword">self</span><span class="operator">.</span><span class="field">x</span>
+        <span class="self_keyword">self</span><span class="punctuation">.</span><span class="field">x</span>
     <span class="punctuation">}</span>
 <span class="punctuation">}</span>
 
 <span class="keyword">impl</span> <span class="struct">Foo</span> <span class="punctuation">{</span>
     <span class="keyword">fn</span> <span class="function declaration">baz</span><span class="punctuation">(</span><span class="keyword">mut</span> <span class="self_keyword mutable">self</span><span class="punctuation">,</span> <span class="value_param declaration">f</span><span class="punctuation">:</span> <span class="struct">Foo</span><span class="punctuation">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">i32</span> <span class="punctuation">{</span>
-        <span class="value_param">f</span><span class="operator">.</span><span class="function consuming">baz</span><span class="punctuation">(</span><span class="self_keyword mutable consuming">self</span><span class="punctuation">)</span>
+        <span class="value_param">f</span><span class="punctuation">.</span><span class="function consuming">baz</span><span class="punctuation">(</span><span class="self_keyword mutable consuming">self</span><span class="punctuation">)</span>
     <span class="punctuation">}</span>
 
     <span class="keyword">fn</span> <span class="function declaration">qux</span><span class="punctuation">(</span><span class="operator">&</span><span class="keyword">mut</span> <span class="self_keyword mutable">self</span><span class="punctuation">)</span> <span class="punctuation">{</span>
-        <span class="self_keyword mutable">self</span><span class="operator">.</span><span class="field">x</span> <span class="operator">=</span> <span class="numeric_literal">0</span><span class="punctuation">;</span>
+        <span class="self_keyword mutable">self</span><span class="punctuation">.</span><span class="field">x</span> <span class="operator">=</span> <span class="numeric_literal">0</span><span class="punctuation">;</span>
     <span class="punctuation">}</span>
 
     <span class="keyword">fn</span> <span class="function declaration">quop</span><span class="punctuation">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="punctuation">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">i32</span> <span class="punctuation">{</span>
-        <span class="self_keyword">self</span><span class="operator">.</span><span class="field">x</span>
+        <span class="self_keyword">self</span><span class="punctuation">.</span><span class="field">x</span>
     <span class="punctuation">}</span>
 <span class="punctuation">}</span>
 
@@ -92,15 +92,15 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 
 <span class="keyword">impl</span> <span class="struct">FooCopy</span> <span class="punctuation">{</span>
     <span class="keyword">fn</span> <span class="function declaration">baz</span><span class="punctuation">(</span><span class="self_keyword">self</span><span class="punctuation">,</span> <span class="value_param declaration">f</span><span class="punctuation">:</span> <span class="struct">FooCopy</span><span class="punctuation">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">u32</span> <span class="punctuation">{</span>
-        <span class="value_param">f</span><span class="operator">.</span><span class="function">baz</span><span class="punctuation">(</span><span class="self_keyword">self</span><span class="punctuation">)</span>
+        <span class="value_param">f</span><span class="punctuation">.</span><span class="function">baz</span><span class="punctuation">(</span><span class="self_keyword">self</span><span class="punctuation">)</span>
     <span class="punctuation">}</span>
 
     <span class="keyword">fn</span> <span class="function declaration">qux</span><span class="punctuation">(</span><span class="operator">&</span><span class="keyword">mut</span> <span class="self_keyword mutable">self</span><span class="punctuation">)</span> <span class="punctuation">{</span>
-        <span class="self_keyword mutable">self</span><span class="operator">.</span><span class="field">x</span> <span class="operator">=</span> <span class="numeric_literal">0</span><span class="punctuation">;</span>
+        <span class="self_keyword mutable">self</span><span class="punctuation">.</span><span class="field">x</span> <span class="operator">=</span> <span class="numeric_literal">0</span><span class="punctuation">;</span>
     <span class="punctuation">}</span>
 
     <span class="keyword">fn</span> <span class="function declaration">quop</span><span class="punctuation">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="punctuation">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">u32</span> <span class="punctuation">{</span>
-        <span class="self_keyword">self</span><span class="operator">.</span><span class="field">x</span>
+        <span class="self_keyword">self</span><span class="punctuation">.</span><span class="field">x</span>
     <span class="punctuation">}</span>
 <span class="punctuation">}</span>
 
@@ -152,10 +152,10 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
     <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">vec</span> <span class="operator">=</span> <span class="unresolved_reference">Vec</span><span class="operator">::</span><span class="unresolved_reference">new</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span>
     <span class="keyword control">if</span> <span class="bool_literal">true</span> <span class="punctuation">{</span>
         <span class="keyword">let</span> <span class="variable declaration">x</span> <span class="operator">=</span> <span class="numeric_literal">92</span><span class="punctuation">;</span>
-        <span class="variable mutable">vec</span><span class="operator">.</span><span class="unresolved_reference">push</span><span class="punctuation">(</span><span class="struct">Foo</span> <span class="punctuation">{</span> <span class="field">x</span><span class="punctuation">,</span> <span class="field">y</span><span class="punctuation">:</span> <span class="numeric_literal">1</span> <span class="punctuation">}</span><span class="punctuation">)</span><span class="punctuation">;</span>
+        <span class="variable mutable">vec</span><span class="punctuation">.</span><span class="unresolved_reference">push</span><span class="punctuation">(</span><span class="struct">Foo</span> <span class="punctuation">{</span> <span class="field">x</span><span class="punctuation">,</span> <span class="field">y</span><span class="punctuation">:</span> <span class="numeric_literal">1</span> <span class="punctuation">}</span><span class="punctuation">)</span><span class="punctuation">;</span>
     <span class="punctuation">}</span>
     <span class="keyword unsafe">unsafe</span> <span class="punctuation">{</span>
-        <span class="variable mutable">vec</span><span class="operator">.</span><span class="unresolved_reference">set_len</span><span class="punctuation">(</span><span class="numeric_literal">0</span><span class="punctuation">)</span><span class="punctuation">;</span>
+        <span class="variable mutable">vec</span><span class="punctuation">.</span><span class="unresolved_reference">set_len</span><span class="punctuation">(</span><span class="numeric_literal">0</span><span class="punctuation">)</span><span class="punctuation">;</span>
         <span class="static mutable unsafe">STATIC_MUT</span> <span class="operator">=</span> <span class="numeric_literal">1</span><span class="punctuation">;</span>
     <span class="punctuation">}</span>
 
@@ -175,14 +175,14 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 
     <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">foo</span> <span class="operator">=</span> <span class="struct">Foo</span> <span class="punctuation">{</span> <span class="field">x</span><span class="punctuation">,</span> <span class="field">y</span><span class="punctuation">:</span> <span class="variable mutable">x</span> <span class="punctuation">}</span><span class="punctuation">;</span>
     <span class="keyword">let</span> <span class="variable declaration">foo2</span> <span class="operator">=</span> <span class="struct">Foo</span> <span class="punctuation">{</span> <span class="field">x</span><span class="punctuation">,</span> <span class="field">y</span><span class="punctuation">:</span> <span class="variable mutable">x</span> <span class="punctuation">}</span><span class="punctuation">;</span>
-    <span class="variable mutable">foo</span><span class="operator">.</span><span class="function">quop</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span>
-    <span class="variable mutable">foo</span><span class="operator">.</span><span class="function mutable">qux</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span>
-    <span class="variable mutable">foo</span><span class="operator">.</span><span class="function consuming">baz</span><span class="punctuation">(</span><span class="variable consuming">foo2</span><span class="punctuation">)</span><span class="punctuation">;</span>
+    <span class="variable mutable">foo</span><span class="punctuation">.</span><span class="function">quop</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span>
+    <span class="variable mutable">foo</span><span class="punctuation">.</span><span class="function mutable">qux</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span>
+    <span class="variable mutable">foo</span><span class="punctuation">.</span><span class="function consuming">baz</span><span class="punctuation">(</span><span class="variable consuming">foo2</span><span class="punctuation">)</span><span class="punctuation">;</span>
 
     <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">copy</span> <span class="operator">=</span> <span class="struct">FooCopy</span> <span class="punctuation">{</span> <span class="field">x</span> <span class="punctuation">}</span><span class="punctuation">;</span>
-    <span class="variable mutable">copy</span><span class="operator">.</span><span class="function">quop</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span>
-    <span class="variable mutable">copy</span><span class="operator">.</span><span class="function mutable">qux</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span>
-    <span class="variable mutable">copy</span><span class="operator">.</span><span class="function">baz</span><span class="punctuation">(</span><span class="variable mutable">copy</span><span class="punctuation">)</span><span class="punctuation">;</span>
+    <span class="variable mutable">copy</span><span class="punctuation">.</span><span class="function">quop</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span>
+    <span class="variable mutable">copy</span><span class="punctuation">.</span><span class="function mutable">qux</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span>
+    <span class="variable mutable">copy</span><span class="punctuation">.</span><span class="function">baz</span><span class="punctuation">(</span><span class="variable mutable">copy</span><span class="punctuation">)</span><span class="punctuation">;</span>
 
     <span class="keyword">let</span> <span class="variable declaration callable">a</span> <span class="operator">=</span> <span class="punctuation">|</span><span class="value_param declaration">x</span><span class="punctuation">|</span> <span class="value_param">x</span><span class="punctuation">;</span>
     <span class="keyword">let</span> <span class="variable declaration callable">bar</span> <span class="operator">=</span> <span class="struct">Foo</span><span class="operator">::</span><span class="function">baz</span><span class="punctuation">;</span>
diff --git a/crates/ide/src/syntax_highlighting/test_data/rainbow_highlighting.html b/crates/ide/src/syntax_highlighting/test_data/rainbow_highlighting.html
index c7589605fa..401e87a73d 100644
--- a/crates/ide/src/syntax_highlighting/test_data/rainbow_highlighting.html
+++ b/crates/ide/src/syntax_highlighting/test_data/rainbow_highlighting.html
@@ -37,11 +37,11 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 </style>
 <pre><code><span class="keyword">fn</span> <span class="function declaration">main</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span>
     <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="8121853618659664005" style="color: hsl(273,88%,88%);">hello</span> <span class="operator">=</span> <span class="string_literal">"hello"</span><span class="punctuation">;</span>
-    <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="2705725358298919760" style="color: hsl(76,47%,83%);">x</span> <span class="operator">=</span> <span class="variable" data-binding-hash="8121853618659664005" style="color: hsl(273,88%,88%);">hello</span><span class="operator">.</span><span class="unresolved_reference">to_string</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span>
-    <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="3365759661443752373" style="color: hsl(15,86%,51%);">y</span> <span class="operator">=</span> <span class="variable" data-binding-hash="8121853618659664005" style="color: hsl(273,88%,88%);">hello</span><span class="operator">.</span><span class="unresolved_reference">to_string</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span>
+    <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="2705725358298919760" style="color: hsl(76,47%,83%);">x</span> <span class="operator">=</span> <span class="variable" data-binding-hash="8121853618659664005" style="color: hsl(273,88%,88%);">hello</span><span class="punctuation">.</span><span class="unresolved_reference">to_string</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span>
+    <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="3365759661443752373" style="color: hsl(15,86%,51%);">y</span> <span class="operator">=</span> <span class="variable" data-binding-hash="8121853618659664005" style="color: hsl(273,88%,88%);">hello</span><span class="punctuation">.</span><span class="unresolved_reference">to_string</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span>
 
     <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="794745962933817518" style="color: hsl(127,71%,87%);">x</span> <span class="operator">=</span> <span class="string_literal">"other color please!"</span><span class="punctuation">;</span>
-    <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="6717528807933952652" style="color: hsl(90,74%,79%);">y</span> <span class="operator">=</span> <span class="variable" data-binding-hash="794745962933817518" style="color: hsl(127,71%,87%);">x</span><span class="operator">.</span><span class="unresolved_reference">to_string</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span>
+    <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="6717528807933952652" style="color: hsl(90,74%,79%);">y</span> <span class="operator">=</span> <span class="variable" data-binding-hash="794745962933817518" style="color: hsl(127,71%,87%);">x</span><span class="punctuation">.</span><span class="unresolved_reference">to_string</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span>
 <span class="punctuation">}</span>
 
 <span class="keyword">fn</span> <span class="function declaration">bar</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span>
diff --git a/xtask/tests/tidy.rs b/xtask/tests/tidy.rs
index 99652e76b6..9de60c76c9 100644
--- a/xtask/tests/tidy.rs
+++ b/xtask/tests/tidy.rs
@@ -214,6 +214,8 @@ fn check_todo(path: &Path, text: &str) {
         // This file itself obviously needs to use todo (<- like this!).
         "tests/cli.rs",
         // Some of our assists generate `todo!()`.
+        "tests/generated.rs",
+        "handlers/add_missing_impl_members.rs",
         "handlers/add_turbo_fish.rs",
         "handlers/generate_function.rs",
         // To support generating `todo!()` in assists, we have `expr_todo()` in
@@ -226,11 +228,6 @@ fn check_todo(path: &Path, text: &str) {
         return;
     }
     if text.contains("TODO") || text.contains("TOOD") || text.contains("todo!") {
-        // Generated by an assist
-        if text.contains("${0:todo!()}") {
-            return;
-        }
-
         panic!(
             "\nTODO markers or todo! macros should not be committed to the master branch,\n\
              use FIXME instead\n\