Repository: rust-analyzer/rust-analyzer
Pull request #6731 (2020-12-05 14:49:33)

Add replace_match_with_if_let assist
Basically the counterpart to `replace_if_let_with_match`, I personally sometimes want to replace matches like
```rust
match foo {
    pat => expr,
    _ => (),
}
``` 
into the corresponding
```rust
if let pat = foo {
    expr
}
```
which is the main reasoning behind this.
I put this into the same file as `replace_if_let_with_match` because the are complementing each other and I would probably rename the file to something like `replace_if_let_match` but I didn't do that for now because I was unsure whether git would still view this as a rename or not due to the amount of changes in the file so that the diff is still properly visible for now.


Patch:
diff --git a/Cargo.lock b/Cargo.lock
index 8dcea6ea44..d8838cf2b0 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -802,9 +802,9 @@ checksum = "4d58d1b70b004888f764dfbf6a26a3b0342a1632d33968e4a179d8011c760614"
 
 [[package]]
 name = "libloading"
-version = "0.6.6"
+version = "0.6.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e9367bdfa836b7e3cf895867f7a570283444da90562980ec2263d6e1569b16bc"
+checksum = "1090080fe06ec2648d0da3881d9453d97e71a45f00eb179af7fdd7e3f686fdb0"
 dependencies = [
  "cfg-if 1.0.0",
  "winapi 0.3.9",
@@ -956,9 +956,9 @@ dependencies = [
 
 [[package]]
 name = "mio"
-version = "0.6.23"
+version = "0.6.22"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4afd66f5b91bf2a3bc13fad0e21caedac168ca4c707504e75585648ae80e4cc4"
+checksum = "fce347092656428bc8eaf6201042cb551b8d67855af7374542a92a0fbfcac430"
 dependencies = [
  "cfg-if 0.1.10",
  "fuchsia-zircon",
@@ -1555,9 +1555,9 @@ dependencies = [
 
 [[package]]
 name = "serde_json"
-version = "1.0.60"
+version = "1.0.59"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1500e84d27fe482ed1dc791a56eddc2f230046a040fa908c08bda1d9fb615779"
+checksum = "dcac07dbffa1c65e7f816ab9eba78eb142c6d44410f4eeba1e26e4f5dfa56b95"
 dependencies = [
  "itoa",
  "ryu",
diff --git a/crates/assists/src/handlers/early_return.rs b/crates/assists/src/handlers/early_return.rs
index 7fd78e9d47..7bcc318a99 100644
--- a/crates/assists/src/handlers/early_return.rs
+++ b/crates/assists/src/handlers/early_return.rs
@@ -112,7 +112,7 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext)
                         let then_branch =
                             make::block_expr(once(make::expr_stmt(early_expression).into()), None);
                         let cond = invert_boolean_expression(cond_expr);
-                        make::expr_if(make::condition(cond, None), then_branch)
+                        make::expr_if(make::condition(cond, None), then_branch, None)
                             .indent(if_indent_level)
                     };
                     replace(new_expr.syntax(), &then_block, &parent_block, &if_expr)
diff --git a/crates/assists/src/handlers/move_guard.rs b/crates/assists/src/handlers/move_guard.rs
index e1855b63d4..eaffd80ce3 100644
--- a/crates/assists/src/handlers/move_guard.rs
+++ b/crates/assists/src/handlers/move_guard.rs
@@ -42,6 +42,7 @@ pub(crate) fn move_guard_to_arm_body(acc: &mut Assists, ctx: &AssistContext) ->
     let if_expr = make::expr_if(
         make::condition(guard_condition, None),
         make::block_expr(None, Some(arm_expr.clone())),
+        None,
     )
     .indent(arm_expr.indent_level());
 
diff --git a/crates/assists/src/handlers/replace_if_let_with_match.rs b/crates/assists/src/handlers/replace_if_let_with_match.rs
index 9a49c48c1f..4a355c66f0 100644
--- a/crates/assists/src/handlers/replace_if_let_with_match.rs
+++ b/crates/assists/src/handlers/replace_if_let_with_match.rs
@@ -1,3 +1,6 @@
+use std::iter;
+
+use ide_db::{ty_filter::TryEnum, RootDatabase};
 use syntax::{
     ast::{
         self,
@@ -8,7 +11,6 @@ use syntax::{
 };
 
 use crate::{utils::unwrap_trivial_block, AssistContext, AssistId, AssistKind, Assists};
-use ide_db::ty_filter::TryEnum;
 
 // Assist: replace_if_let_with_match
 //
@@ -79,6 +81,91 @@ pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext)
     )
 }
 
+// Assist: replace_match_with_if_let
+//
+// Replaces a binary `match` with a wildcard pattern and no guards with an `if let` expression.
+//
+// ```
+// enum Action { Move { distance: u32 }, Stop }
+//
+// fn handle(action: Action) {
+//     <|>match action {
+//         Action::Move { distance } => foo(distance),
+//         _ => bar(),
+//     }
+// }
+// ```
+// ->
+// ```
+// enum Action { Move { distance: u32 }, Stop }
+//
+// fn handle(action: Action) {
+//     if let Action::Move { distance } = action {
+//         foo(distance)
+//     } else {
+//         bar()
+//     }
+// }
+// ```
+pub(crate) fn replace_match_with_if_let(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
+    let match_expr: ast::MatchExpr = ctx.find_node_at_offset()?;
+    let mut arms = match_expr.match_arm_list()?.arms();
+    let first_arm = arms.next()?;
+    let second_arm = arms.next()?;
+    if arms.next().is_some() || first_arm.guard().is_some() || second_arm.guard().is_some() {
+        return None;
+    }
+    let condition_expr = match_expr.expr()?;
+    let (if_let_pat, then_expr, else_expr) = if is_pat_wildcard_or_sad(&ctx.sema, &first_arm.pat()?)
+    {
+        (second_arm.pat()?, second_arm.expr()?, first_arm.expr()?)
+    } else if is_pat_wildcard_or_sad(&ctx.sema, &second_arm.pat()?) {
+        (first_arm.pat()?, first_arm.expr()?, second_arm.expr()?)
+    } else {
+        return None;
+    };
+
+    let target = match_expr.syntax().text_range();
+    acc.add(
+        AssistId("replace_match_with_if_let", AssistKind::RefactorRewrite),
+        "Replace with if let",
+        target,
+        move |edit| {
+            let condition = make::condition(condition_expr, Some(if_let_pat));
+            let then_block = match then_expr.reset_indent() {
+                ast::Expr::BlockExpr(block) => block,
+                expr => make::block_expr(iter::empty(), Some(expr)),
+            };
+            let else_expr = match else_expr {
+                ast::Expr::BlockExpr(block)
+                    if block.statements().count() == 0 && block.expr().is_none() =>
+                {
+                    None
+                }
+                ast::Expr::TupleExpr(tuple) if tuple.fields().count() == 0 => None,
+                expr => Some(expr),
+            };
+            let if_let_expr = make::expr_if(
+                condition,
+                then_block,
+                else_expr.map(|else_expr| {
+                    ast::ElseBranch::Block(make::block_expr(iter::empty(), Some(else_expr)))
+                }),
+            )
+            .indent(IndentLevel::from_node(match_expr.syntax()));
+
+            edit.replace_ast::<ast::Expr>(match_expr.into(), if_let_expr);
+        },
+    )
+}
+
+fn is_pat_wildcard_or_sad(sema: &hir::Semantics<RootDatabase>, pat: &ast::Pat) -> bool {
+    sema.type_of_pat(&pat)
+        .and_then(|ty| TryEnum::from_ty(sema, &ty))
+        .map(|it| it.sad_pattern().syntax().text() == pat.syntax().text())
+        .unwrap_or_else(|| matches!(pat, ast::Pat::WildcardPat(_)))
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;
@@ -249,6 +336,194 @@ fn main() {
         }
     }
 }
+"#,
+        )
+    }
+
+    #[test]
+    fn test_replace_match_with_if_let_unwraps_simple_expressions() {
+        check_assist(
+            replace_match_with_if_let,
+            r#"
+impl VariantData {
+    pub fn is_struct(&self) -> bool {
+        <|>match *self {
+            VariantData::Struct(..) => true,
+            _ => false,
+        }
+    }
+}           "#,
+            r#"
+impl VariantData {
+    pub fn is_struct(&self) -> bool {
+        if let VariantData::Struct(..) = *self {
+            true
+        } else {
+            false
+        }
+    }
+}           "#,
+        )
+    }
+
+    #[test]
+    fn test_replace_match_with_if_let_doesnt_unwrap_multiline_expressions() {
+        check_assist(
+            replace_match_with_if_let,
+            r#"
+fn foo() {
+    <|>match a {
+        VariantData::Struct(..) => {
+            bar(
+                123
+            )
+        }
+        _ => false,
+    }
+}           "#,
+            r#"
+fn foo() {
+    if let VariantData::Struct(..) = a {
+        bar(
+            123
+        )
+    } else {
+        false
+    }
+}           "#,
+        )
+    }
+
+    #[test]
+    fn replace_match_with_if_let_target() {
+        check_assist_target(
+            replace_match_with_if_let,
+            r#"
+impl VariantData {
+    pub fn is_struct(&self) -> bool {
+        <|>match *self {
+            VariantData::Struct(..) => true,
+            _ => false,
+        }
+    }
+}           "#,
+            r#"match *self {
+            VariantData::Struct(..) => true,
+            _ => false,
+        }"#,
+        );
+    }
+
+    #[test]
+    fn special_case_option_match_to_if_let() {
+        check_assist(
+            replace_match_with_if_let,
+            r#"
+enum Option<T> { Some(T), None }
+use Option::*;
+
+fn foo(x: Option<i32>) {
+    <|>match x {
+        Some(x) => println!("{}", x),
+        None => println!("none"),
+    }
+}
+           "#,
+            r#"
+enum Option<T> { Some(T), None }
+use Option::*;
+
+fn foo(x: Option<i32>) {
+    if let Some(x) = x {
+        println!("{}", x)
+    } else {
+        println!("none")
+    }
+}
+           "#,
+        );
+    }
+
+    #[test]
+    fn special_case_result_match_to_if_let() {
+        check_assist(
+            replace_match_with_if_let,
+            r#"
+enum Result<T, E> { Ok(T), Err(E) }
+use Result::*;
+
+fn foo(x: Result<i32, ()>) {
+    <|>match x {
+        Ok(x) => println!("{}", x),
+        Err(_) => println!("none"),
+    }
+}
+           "#,
+            r#"
+enum Result<T, E> { Ok(T), Err(E) }
+use Result::*;
+
+fn foo(x: Result<i32, ()>) {
+    if let Ok(x) = x {
+        println!("{}", x)
+    } else {
+        println!("none")
+    }
+}
+           "#,
+        );
+    }
+
+    #[test]
+    fn nested_indent_match_to_if_let() {
+        check_assist(
+            replace_match_with_if_let,
+            r#"
+fn main() {
+    if true {
+        <|>match path.strip_prefix(root_path) {
+            Ok(rel_path) => {
+                let rel_path = RelativePathBuf::from_path(rel_path).ok()?;
+                Some((*id, rel_path))
+            }
+            _ => None,
+        }
+    }
+}
+"#,
+            r#"
+fn main() {
+    if true {
+        if let Ok(rel_path) = path.strip_prefix(root_path) {
+            let rel_path = RelativePathBuf::from_path(rel_path).ok()?;
+            Some((*id, rel_path))
+        } else {
+            None
+        }
+    }
+}
+"#,
+        )
+    }
+
+    #[test]
+    fn replace_match_with_if_let_empty_wildcard_expr() {
+        check_assist(
+            replace_match_with_if_let,
+            r#"
+fn main() {
+    <|>match path.strip_prefix(root_path) {
+        Ok(rel_path) => println!("{}", rel_path),
+        _ => (),
+    }
+}
+"#,
+            r#"
+fn main() {
+    if let Ok(rel_path) = path.strip_prefix(root_path) {
+        println!("{}", rel_path)
+    }
+}
 "#,
         )
     }
diff --git a/crates/assists/src/handlers/replace_let_with_if_let.rs b/crates/assists/src/handlers/replace_let_with_if_let.rs
index 69d3b08d36..5970e283cf 100644
--- a/crates/assists/src/handlers/replace_let_with_if_let.rs
+++ b/crates/assists/src/handlers/replace_let_with_if_let.rs
@@ -60,7 +60,7 @@ pub(crate) fn replace_let_with_if_let(acc: &mut Assists, ctx: &AssistContext) ->
             };
             let block =
                 make::block_expr(None, None).indent(IndentLevel::from_node(let_stmt.syntax()));
-            let if_ = make::expr_if(make::condition(init, Some(with_placeholder)), block);
+            let if_ = make::expr_if(make::condition(init, Some(with_placeholder)), block, None);
             let stmt = make::expr_stmt(if_);
 
             let placeholder = stmt.syntax().descendants().find_map(ast::WildcardPat::cast).unwrap();
diff --git a/crates/assists/src/lib.rs b/crates/assists/src/lib.rs
index dfe6c27295..b8ce7418d6 100644
--- a/crates/assists/src/lib.rs
+++ b/crates/assists/src/lib.rs
@@ -209,6 +209,7 @@ mod handlers {
             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_if_let_with_match::replace_match_with_if_let,
             replace_impl_trait_with_generic::replace_impl_trait_with_generic,
             replace_let_with_if_let::replace_let_with_if_let,
             replace_qualified_name_with_use::replace_qualified_name_with_use,
diff --git a/crates/hir/src/code_model.rs b/crates/hir/src/code_model.rs
index 9a1e9ba496..4500050f1b 100644
--- a/crates/hir/src/code_model.rs
+++ b/crates/hir/src/code_model.rs
@@ -362,9 +362,7 @@ impl Module {
     }
 
     pub fn diagnostics(self, db: &dyn HirDatabase, sink: &mut DiagnosticSink) {
-        let _p = profile::span("Module::diagnostics").detail(|| {
-            format!("{:?}", self.name(db).map_or("<unknown>".into(), |name| name.to_string()))
-        });
+        let _p = profile::span("Module::diagnostics");
         let crate_def_map = db.crate_def_map(self.id.krate);
         crate_def_map.add_diagnostics(db.upcast(), self.id.local_id, sink);
         for decl in self.declarations(db) {
diff --git a/crates/hir_def/src/body.rs b/crates/hir_def/src/body.rs
index 92bcc17053..33eb5e78c4 100644
--- a/crates/hir_def/src/body.rs
+++ b/crates/hir_def/src/body.rs
@@ -120,24 +120,18 @@ impl Expander {
             self.resolve_path_as_macro(db, &path)
         };
 
-        let mut err = None;
-        let call_id =
-            macro_call.as_call_id_with_errors(db, self.crate_def_map.krate, resolver, &mut |e| {
-                err.get_or_insert(e);
-            });
-        let call_id = match call_id {
+        let call_id = match macro_call.as_call_id(db, self.crate_def_map.krate, resolver) {
             Some(it) => it,
             None => {
-                if err.is_none() {
-                    eprintln!("no error despite `as_call_id_with_errors` returning `None`");
-                }
-                return ExpandResult { value: None, err };
+                // FIXME: this can mean other things too, but `as_call_id` doesn't provide enough
+                // info.
+                return ExpandResult::only_err(mbe::ExpandError::Other(
+                    "failed to parse or resolve macro invocation".into(),
+                ));
             }
         };
 
-        if err.is_none() {
-            err = db.macro_expand_error(call_id);
-        }
+        let err = db.macro_expand_error(call_id);
 
         let file_id = call_id.as_file();
 
diff --git a/crates/hir_def/src/lib.rs b/crates/hir_def/src/lib.rs
index b41c5acb2a..1b22d1eec9 100644
--- a/crates/hir_def/src/lib.rs
+++ b/crates/hir_def/src/lib.rs
@@ -465,37 +465,21 @@ pub trait AsMacroCall {
         db: &dyn db::DefDatabase,
         krate: CrateId,
         resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
-    ) -> Option<MacroCallId> {
-        self.as_call_id_with_errors(db, krate, resolver, &mut |_| ())
-    }
-
-    fn as_call_id_with_errors(
-        &self,
-        db: &dyn db::DefDatabase,
-        krate: CrateId,
-        resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
-        error_sink: &mut dyn FnMut(mbe::ExpandError),
     ) -> Option<MacroCallId>;
 }
 
 impl AsMacroCall for InFile<&ast::MacroCall> {
-    fn as_call_id_with_errors(
+    fn as_call_id(
         &self,
         db: &dyn db::DefDatabase,
         krate: CrateId,
         resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
-        error_sink: &mut dyn FnMut(mbe::ExpandError),
     ) -> Option<MacroCallId> {
         let ast_id = AstId::new(self.file_id, db.ast_id_map(self.file_id).ast_id(self.value));
         let h = Hygiene::new(db.upcast(), self.file_id);
-        let path = self.value.path().and_then(|path| path::ModPath::from_src(path, &h));
-
-        if path.is_none() {
-            error_sink(mbe::ExpandError::Other("malformed macro invocation".into()));
-        }
+        let path = path::ModPath::from_src(self.value.path()?, &h)?;
 
-        AstIdWithPath::new(ast_id.file_id, ast_id.value, path?)
-            .as_call_id_with_errors(db, krate, resolver, error_sink)
+        AstIdWithPath::new(ast_id.file_id, ast_id.value, path).as_call_id(db, krate, resolver)
     }
 }
 
@@ -513,32 +497,22 @@ impl<T: ast::AstNode> AstIdWithPath<T> {
 }
 
 impl AsMacroCall for AstIdWithPath<ast::MacroCall> {
-    fn as_call_id_with_errors(
+    fn as_call_id(
         &self,
         db: &dyn db::DefDatabase,
         krate: CrateId,
         resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
-        error_sink: &mut dyn FnMut(mbe::ExpandError),
     ) -> Option<MacroCallId> {
-        let def: MacroDefId = resolver(self.path.clone()).or_else(|| {
-            error_sink(mbe::ExpandError::Other(format!("could not resolve macro `{}`", self.path)));
-            None
-        })?;
+        let def: MacroDefId = resolver(self.path.clone())?;
 
         if let MacroDefKind::BuiltInEager(_) = def.kind {
             let macro_call = InFile::new(self.ast_id.file_id, self.ast_id.to_node(db.upcast()));
             let hygiene = Hygiene::new(db.upcast(), self.ast_id.file_id);
 
             Some(
-                expand_eager_macro(
-                    db.upcast(),
-                    krate,
-                    macro_call,
-                    def,
-                    &|path: ast::Path| resolver(path::ModPath::from_src(path, &hygiene)?),
-                    error_sink,
-                )
-                .ok()?
+                expand_eager_macro(db.upcast(), krate, macro_call, def, &|path: ast::Path| {
+                    resolver(path::ModPath::from_src(path, &hygiene)?)
+                })?
                 .into(),
             )
         } else {
@@ -548,18 +522,13 @@ impl AsMacroCall for AstIdWithPath<ast::MacroCall> {
 }
 
 impl AsMacroCall for AstIdWithPath<ast::Item> {
-    fn as_call_id_with_errors(
+    fn as_call_id(
         &self,
         db: &dyn db::DefDatabase,
         krate: CrateId,
         resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
-        error_sink: &mut dyn FnMut(mbe::ExpandError),
     ) -> Option<MacroCallId> {
-        let def: MacroDefId = resolver(self.path.clone()).or_else(|| {
-            error_sink(mbe::ExpandError::Other(format!("could not resolve macro `{}`", self.path)));
-            None
-        })?;
-
+        let def = resolver(self.path.clone())?;
         Some(
             def.as_lazy_macro(
                 db.upcast(),
diff --git a/crates/hir_expand/src/builtin_macro.rs b/crates/hir_expand/src/builtin_macro.rs
index 44a5556b67..7f4db106db 100644
--- a/crates/hir_expand/src/builtin_macro.rs
+++ b/crates/hir_expand/src/builtin_macro.rs
@@ -86,6 +86,7 @@ pub fn find_builtin_macro(
 register_builtin! {
     LAZY:
     (column, Column) => column_expand,
+    (compile_error, CompileError) => compile_error_expand,
     (file, File) => file_expand,
     (line, Line) => line_expand,
     (assert, Assert) => assert_expand,
@@ -96,7 +97,6 @@ register_builtin! {
     (format_args_nl, FormatArgsNl) => format_args_expand,
 
     EAGER:
-    (compile_error, CompileError) => compile_error_expand,
     (concat, Concat) => concat_expand,
     (include, Include) => include_expand,
     (include_bytes, IncludeBytes) => include_bytes_expand,
@@ -213,6 +213,25 @@ fn file_expand(
     ExpandResult::ok(expanded)
 }
 
+fn compile_error_expand(
+    _db: &dyn AstDatabase,
+    _id: LazyMacroId,
+    tt: &tt::Subtree,
+) -> ExpandResult<tt::Subtree> {
+    if tt.count() == 1 {
+        if let tt::TokenTree::Leaf(tt::Leaf::Literal(it)) = &tt.token_trees[0] {
+            let s = it.text.as_str();
+            if s.contains('"') {
+                return ExpandResult::ok(quote! { loop { #it }});
+            }
+        };
+    }
+
+    ExpandResult::only_err(mbe::ExpandError::BindingError(
+        "`compile_error!` argument be a string".into(),
+    ))
+}
+
 fn format_args_expand(
     _db: &dyn AstDatabase,
     _id: LazyMacroId,
@@ -261,27 +280,6 @@ fn unquote_str(lit: &tt::Literal) -> Option<String> {
     token.value().map(|it| it.into_owned())
 }
 
-fn compile_error_expand(
-    _db: &dyn AstDatabase,
-    _id: EagerMacroId,
-    tt: &tt::Subtree,
-) -> ExpandResult<Option<(tt::Subtree, FragmentKind)>> {
-    let err = match &*tt.token_trees {
-        [tt::TokenTree::Leaf(tt::Leaf::Literal(it))] => {
-            let text = it.text.as_str();
-            if text.starts_with('"') && text.ends_with('"') {
-                // FIXME: does not handle raw strings
-                mbe::ExpandError::Other(text[1..text.len() - 1].to_string())
-            } else {
-                mbe::ExpandError::BindingError("`compile_error!` argument must be a string".into())
-            }
-        }
-        _ => mbe::ExpandError::BindingError("`compile_error!` argument must be a string".into()),
-    };
-
-    ExpandResult { value: Some((quote! {}, FragmentKind::Items)), err: Some(err) }
-}
-
 fn concat_expand(
     _db: &dyn AstDatabase,
     _arg_id: EagerMacroId,
@@ -419,25 +417,17 @@ fn env_expand(
         Err(e) => return ExpandResult::only_err(e),
     };
 
-    let mut err = None;
-    let s = get_env_inner(db, arg_id, &key).unwrap_or_else(|| {
-        // The only variable rust-analyzer ever sets is `OUT_DIR`, so only diagnose that to avoid
-        // unnecessary diagnostics for eg. `CARGO_PKG_NAME`.
-        if key == "OUT_DIR" {
-            err = Some(mbe::ExpandError::Other(
-                r#"`OUT_DIR` not set, enable "load out dirs from check" to fix"#.into(),
-            ));
-        }
-
-        // If the variable is unset, still return a dummy string to help type inference along.
-        // We cannot use an empty string here, because for
-        // `include!(concat!(env!("OUT_DIR"), "/foo.rs"))` will become
-        // `include!("foo.rs"), which might go to infinite loop
-        "__RA_UNIMPLEMENTED__".to_string()
-    });
+    // FIXME:
+    // If the environment variable is not defined int rustc, then a compilation error will be emitted.
+    // We might do the same if we fully support all other stuffs.
+    // But for now on, we should return some dummy string for better type infer purpose.
+    // However, we cannot use an empty string here, because for
+    // `include!(concat!(env!("OUT_DIR"), "/foo.rs"))` will become
+    // `include!("foo.rs"), which might go to infinite loop
+    let s = get_env_inner(db, arg_id, &key).unwrap_or_else(|| "__RA_UNIMPLEMENTED__".to_string());
     let expanded = quote! { #s };
 
-    ExpandResult { value: Some((expanded, FragmentKind::Expr)), err }
+    ExpandResult::ok(Some((expanded, FragmentKind::Expr)))
 }
 
 fn option_env_expand(
@@ -648,8 +638,7 @@ mod tests {
             "#,
         );
 
-        // This expands to nothing (since it's in item position), but emits an error.
-        assert_eq!(expanded, "");
+        assert_eq!(expanded, r#"loop{"error!"}"#);
     }
 
     #[test]
diff --git a/crates/hir_expand/src/db.rs b/crates/hir_expand/src/db.rs
index 842a177db3..4fd0ba290f 100644
--- a/crates/hir_expand/src/db.rs
+++ b/crates/hir_expand/src/db.rs
@@ -207,7 +207,6 @@ fn macro_expand_with_arg(
             } else {
                 return ExpandResult {
                     value: Some(db.lookup_intern_eager_expansion(id).subtree),
-                    // FIXME: There could be errors here!
                     err: None,
                 };
             }
diff --git a/crates/hir_expand/src/eager.rs b/crates/hir_expand/src/eager.rs
index 0229a836ec..ab6b4477c6 100644
--- a/crates/hir_expand/src/eager.rs
+++ b/crates/hir_expand/src/eager.rs
@@ -26,89 +26,19 @@ use crate::{
 };
 
 use base_db::CrateId;
-use mbe::ExpandResult;
 use parser::FragmentKind;
 use std::sync::Arc;
 use syntax::{algo::SyntaxRewriter, SyntaxNode};
 
-pub struct ErrorEmitted {
-    _private: (),
-}
-
-trait ErrorSink {
-    fn emit(&mut self, err: mbe::ExpandError);
-
-    fn option<T>(
-        &mut self,
-        opt: Option<T>,
-        error: impl FnOnce() -> mbe::ExpandError,
-    ) -> Result<T, ErrorEmitted> {
-        match opt {
-            Some(it) => Ok(it),
-            None => {
-                self.emit(error());
-                Err(ErrorEmitted { _private: () })
-            }
-        }
-    }
-
-    fn option_with<T>(
-        &mut self,
-        opt: impl FnOnce() -> Option<T>,
-        error: impl FnOnce() -> mbe::ExpandError,
-    ) -> Result<T, ErrorEmitted> {
-        self.option(opt(), error)
-    }
-
-    fn result<T>(&mut self, res: Result<T, mbe::ExpandError>) -> Result<T, ErrorEmitted> {
-        match res {
-            Ok(it) => Ok(it),
-            Err(e) => {
-                self.emit(e);
-                Err(ErrorEmitted { _private: () })
-            }
-        }
-    }
-
-    fn expand_result_option<T>(&mut self, res: ExpandResult<Option<T>>) -> Result<T, ErrorEmitted> {
-        match (res.value, res.err) {
-            (None, Some(err)) => {
-                self.emit(err);
-                Err(ErrorEmitted { _private: () })
-            }
-            (Some(value), opt_err) => {
-                if let Some(err) = opt_err {
-                    self.emit(err);
-                }
-                Ok(value)
-            }
-            (None, None) => unreachable!("`ExpandResult` without value or error"),
-        }
-    }
-}
-
-impl ErrorSink for &'_ mut dyn FnMut(mbe::ExpandError) {
-    fn emit(&mut self, err: mbe::ExpandError) {
-        self(err);
-    }
-}
-
-fn err(msg: impl Into<String>) -> mbe::ExpandError {
-    mbe::ExpandError::Other(msg.into())
-}
-
 pub fn expand_eager_macro(
     db: &dyn AstDatabase,
     krate: CrateId,
     macro_call: InFile<ast::MacroCall>,
     def: MacroDefId,
     resolver: &dyn Fn(ast::Path) -> Option<MacroDefId>,
-    mut diagnostic_sink: &mut dyn FnMut(mbe::ExpandError),
-) -> Result<EagerMacroId, ErrorEmitted> {
-    let parsed_args = diagnostic_sink.option_with(
-        || Some(mbe::ast_to_token_tree(&macro_call.value.token_tree()?)?.0),
-        || err("malformed macro invocation"),
-    )?;
+) -> Option<EagerMacroId> {
+    let args = macro_call.value.token_tree()?;
+    let parsed_args = mbe::ast_to_token_tree(&args)?.0;
 
     // Note:
     // When `lazy_expand` is called, its *parent* file must be already exists.
@@ -125,22 +55,17 @@ pub fn expand_eager_macro(
     });
     let arg_file_id: MacroCallId = arg_id.into();
 
-    let parsed_args =
-        diagnostic_sink.result(mbe::token_tree_to_syntax_node(&parsed_args, FragmentKind::Expr))?.0;
+    let parsed_args = mbe::token_tree_to_syntax_node(&parsed_args, FragmentKind::Expr).ok()?.0;
     let result = eager_macro_recur(
         db,
         InFile::new(arg_file_id.as_file(), parsed_args.syntax_node()),
         krate,
         resolver,
-        diagnostic_sink,
     )?;
-    let subtree =
-        diagnostic_sink.option(to_subtree(&result), || err("failed to parse macro result"))?;
+    let subtree = to_subtree(&result)?;
 
     if let MacroDefKind::BuiltInEager(eager) = def.kind {
-        let res = eager.expand(db, arg_id, &subtree);
-
-        let (subtree, fragment) = diagnostic_sink.expand_result_option(res)?;
+        let (subtree, fragment) = eager.expand(db, arg_id, &subtree).value?;
         let eager = EagerCallLoc {
             def,
             fragment,
@@ -149,9 +74,9 @@ pub fn expand_eager_macro(
             file_id: macro_call.file_id,
         };
 
-        Ok(db.intern_eager_expansion(eager))
+        Some(db.intern_eager_expansion(eager))
     } else {
-        panic!("called `expand_eager_macro` on non-eager macro def {:?}", def);
+        None
     }
 }
 
@@ -166,16 +91,13 @@ fn lazy_expand(
     def: &MacroDefId,
     macro_call: InFile<ast::MacroCall>,
     krate: CrateId,
-) -> ExpandResult<Option<InFile<SyntaxNode>>> {
+) -> Option<InFile<SyntaxNode>> {
     let ast_id = db.ast_id_map(macro_call.file_id).ast_id(&macro_call.value);
 
     let id: MacroCallId =
         def.as_lazy_macro(db, krate, MacroCallKind::FnLike(macro_call.with_value(ast_id))).into();
 
-    let err = db.macro_expand_error(id);
-    let value = db.parse_or_expand(id.as_file()).map(|node| InFile::new(id.as_file(), node));
-
-    ExpandResult { value, err }
+    db.parse_or_expand(id.as_file()).map(|node| InFile::new(id.as_file(), node))
 }
 
 fn eager_macro_recur(
@@ -183,8 +105,7 @@ fn eager_macro_recur(
     curr: InFile<SyntaxNode>,
     krate: CrateId,
     macro_resolver: &dyn Fn(ast::Path) -> Option<MacroDefId>,
-    mut diagnostic_sink: &mut dyn FnMut(mbe::ExpandError),
-) -> Result<SyntaxNode, ErrorEmitted> {
+) -> Option<SyntaxNode> {
     let original = curr.value.clone();
 
     let children = curr.value.descendants().filter_map(ast::MacroCall::cast);
@@ -192,8 +113,7 @@ fn eager_macro_recur(
 
     // Collect replacement
     for child in children {
-        let def = diagnostic_sink
-            .option_with(|| macro_resolver(child.path()?), || err("failed to resolve macro"))?;
+        let def: MacroDefId = macro_resolver(child.path()?)?;
         let insert = match def.kind {
             MacroDefKind::BuiltInEager(_) => {
                 let id: MacroCallId = expand_eager_macro(
@@ -202,21 +122,17 @@ fn eager_macro_recur(
                     curr.with_value(child.clone()),
                     def,
                     macro_resolver,
-                    diagnostic_sink,
                 )?
                 .into();
-                db.parse_or_expand(id.as_file())
-                    .expect("successful macro expansion should be parseable")
+                db.parse_or_expand(id.as_file())?
             }
             MacroDefKind::Declarative
             | MacroDefKind::BuiltIn(_)
             | MacroDefKind::BuiltInDerive(_)
             | MacroDefKind::ProcMacro(_) => {
-                let res = lazy_expand(db, &def, curr.with_value(child.clone()), krate);
-                let val = diagnostic_sink.expand_result_option(res)?;
-
+                let expanded = lazy_expand(db, &def, curr.with_value(child.clone()), krate)?;
                 // replace macro inside
-                eager_macro_recur(db, val, krate, macro_resolver, diagnostic_sink)?
+                eager_macro_recur(db, expanded, krate, macro_resolver)?
             }
         };
 
@@ -224,5 +140,5 @@ fn eager_macro_recur(
     }
 
     let res = rewriter.rewrite(&original);
-    Ok(res)
+    Some(res)
 }
diff --git a/crates/hir_expand/src/lib.rs b/crates/hir_expand/src/lib.rs
index 2633fd8f73..6dad2507bf 100644
--- a/crates/hir_expand/src/lib.rs
+++ b/crates/hir_expand/src/lib.rs
@@ -143,11 +143,7 @@ impl HirFileId {
                 let loc: MacroCallLoc = db.lookup_intern_macro(lazy_id);
 
                 let arg_tt = loc.kind.arg(db)?;
-
-                let def = loc.def.ast_id.and_then(|id| {
-                    let def_tt = id.to_node(db).token_tree()?;
-                    Some(InFile::new(id.file_id, def_tt))
-                });
+                let def_tt = loc.def.ast_id?.to_node(db).token_tree()?;
 
                 let macro_def = db.macro_def(loc.def)?;
                 let (parse, exp_map) = db.parse_macro_expansion(macro_file).value?;
@@ -156,7 +152,7 @@ impl HirFileId {
                 Some(ExpansionInfo {
                     expanded: InFile::new(self, parse.syntax_node()),
                     arg: InFile::new(loc.kind.file_id(), arg_tt),
-                    def,
+                    def: InFile::new(loc.def.ast_id?.file_id, def_tt),
                     macro_arg,
                     macro_def,
                     exp_map,
@@ -315,8 +311,7 @@ pub struct EagerCallLoc {
 pub struct ExpansionInfo {
     expanded: InFile<SyntaxNode>,
     arg: InFile<SyntaxNode>,
-    /// The `macro_rules!` arguments.
-    def: Option<InFile<ast::TokenTree>>,
+    def: InFile<ast::TokenTree>,
 
     macro_def: Arc<(db::TokenExpander, mbe::TokenMap)>,
     macro_arg: Arc<(tt::Subtree, mbe::TokenMap)>,
@@ -353,14 +348,9 @@ impl ExpansionInfo {
         let (token_id, origin) = self.macro_def.0.map_id_up(token_id);
         let (token_map, tt) = match origin {
             mbe::Origin::Call => (&self.macro_arg.1, self.arg.clone()),
-            mbe::Origin::Def => (
-                &self.macro_def.1,
-                self.def
-                    .as_ref()
-                    .expect("`Origin::Def` used with non-`macro_rules!` macro")
-                    .as_ref()
-                    .map(|tt| tt.syntax().clone()),
-            ),
+            mbe::Origin::Def => {
+                (&self.macro_def.1, self.def.as_ref().map(|tt| tt.syntax().clone()))
+            }
         };
 
         let range = token_map.range_by_token(token_id)?.by_kind(token.value.kind())?;
diff --git a/crates/hir_ty/src/method_resolution.rs b/crates/hir_ty/src/method_resolution.rs
index 8a289f52a6..5a6f0c67fb 100644
--- a/crates/hir_ty/src/method_resolution.rs
+++ b/crates/hir_ty/src/method_resolution.rs
@@ -720,13 +720,7 @@ fn transform_receiver_ty(
             .push(self_ty.value.clone())
             .fill_with_unknown()
             .build(),
-        AssocContainerId::ImplId(impl_id) => {
-            let impl_substs = inherent_impl_substs(db, impl_id, &self_ty)?;
-            Substs::build_for_def(db, function_id)
-                .use_parent_substs(&impl_substs)
-                .fill_with_unknown()
-                .build()
-        }
+        AssocContainerId::ImplId(impl_id) => inherent_impl_substs(db, impl_id, &self_ty)?,
         AssocContainerId::ContainerId(_) => unreachable!(),
     };
     let sig = db.callable_item_signature(function_id.into());
diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs
index c8453edb30..9d3d882895 100644
--- a/crates/ide/src/diagnostics.rs
+++ b/crates/ide/src/diagnostics.rs
@@ -135,11 +135,6 @@ pub(crate) fn diagnostics(
             res.borrow_mut().push(warning_with_fix(d, &sema));
         })
         .on::<hir::diagnostics::InactiveCode, _>(|d| {
-            // If there's inactive code somewhere in a macro, don't propagate to the call-site.
-            if d.display_source().file_id.expansion_info(db).is_some() {
-                return;
-            }
-
             // Override severity and mark as unused.
             res.borrow_mut().push(
                 Diagnostic::hint(sema.diagnostics_display_range(d).range, d.message())
diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs
index f5c6eabeff..5150a970c1 100644
--- a/crates/ide/src/syntax_highlighting.rs
+++ b/crates/ide/src/syntax_highlighting.rs
@@ -726,7 +726,7 @@ fn highlight_method_call(
     method_call: &ast::MethodCallExpr,
 ) -> Option<Highlight> {
     let func = sema.resolve_method_call(&method_call)?;
-    let mut h = HighlightTag::Method.into();
+    let mut h = HighlightTag::Function.into();
     if func.is_unsafe(sema.db) || sema.is_unsafe_method_call(&method_call) {
         h |= HighlightModifier::Unsafe;
     }
@@ -755,18 +755,13 @@ fn highlight_def(db: &RootDatabase, def: Definition) -> Highlight {
         Definition::ModuleDef(def) => match def {
             hir::ModuleDef::Module(_) => HighlightTag::Module,
             hir::ModuleDef::Function(func) => {
-                let mut h = if func.as_assoc_item(db).is_some() {
-                    if func.self_param(db).is_none() {
-                        Highlight::from(HighlightTag::Method) | HighlightModifier::Static
-                    } else {
-                        HighlightTag::Method.into()
-                    }
-                } else {
-                    HighlightTag::Function.into()
-                };
+                let mut h = HighlightTag::Function.into();
                 if func.is_unsafe(db) {
                     h |= HighlightModifier::Unsafe;
                 }
+                if func.as_assoc_item(db).is_some() && func.self_param(db).is_none() {
+                    h |= HighlightModifier::Static;
+                }
                 return h;
             }
             hir::ModuleDef::Adt(hir::Adt::Struct(_)) => HighlightTag::Struct,
diff --git a/crates/ide/src/syntax_highlighting/tags.rs b/crates/ide/src/syntax_highlighting/tags.rs
index ffd9588b84..65e0671a5a 100644
--- a/crates/ide/src/syntax_highlighting/tags.rs
+++ b/crates/ide/src/syntax_highlighting/tags.rs
@@ -30,7 +30,6 @@ pub enum HighlightTag {
     Keyword,
     Lifetime,
     Macro,
-    Method,
     Module,
     NumericLiteral,
     Punctuation,
@@ -91,7 +90,6 @@ impl HighlightTag {
             HighlightTag::Lifetime => "lifetime",
             HighlightTag::Punctuation => "punctuation",
             HighlightTag::Macro => "macro",
-            HighlightTag::Method => "method",
             HighlightTag::Module => "module",
             HighlightTag::NumericLiteral => "numeric_literal",
             HighlightTag::Operator => "operator",
diff --git a/crates/ide_db/src/call_info.rs b/crates/ide_db/src/call_info.rs
index 615fa7b0ee..83a602b9ad 100644
--- a/crates/ide_db/src/call_info.rs
+++ b/crates/ide_db/src/call_info.rs
@@ -228,4 +228,529 @@ impl FnCallNode {
 }
 
 #[cfg(test)]
-mod tests;
+mod tests {
+    use crate::RootDatabase;
+    use base_db::{fixture::ChangeFixture, FilePosition};
+    use expect_test::{expect, Expect};
+    use test_utils::{mark, RangeOrOffset};
+
+    /// Creates analysis from a multi-file fixture, returns positions marked with <|>.
+    pub(crate) fn position(ra_fixture: &str) -> (RootDatabase, FilePosition) {
+        let change_fixture = ChangeFixture::parse(ra_fixture);
+        let mut database = RootDatabase::default();
+        database.apply_change(change_fixture.change);
+        let (file_id, range_or_offset) =
+            change_fixture.file_position.expect("expected a marker (<|>)");
+        let offset = match range_or_offset {
+            RangeOrOffset::Range(_) => panic!(),
+            RangeOrOffset::Offset(it) => it,
+        };
+        (database, FilePosition { file_id, offset })
+    }
+
+    fn check(ra_fixture: &str, expect: Expect) {
+        let (db, position) = position(ra_fixture);
+        let call_info = crate::call_info::call_info(&db, position);
+        let actual = match call_info {
+            Some(call_info) => {
+                let docs = match &call_info.doc {
+                    None => "".to_string(),
+                    Some(docs) => format!("{}\n------\n", docs.as_str()),
+                };
+                let params = call_info
+                    .parameter_labels()
+                    .enumerate()
+                    .map(|(i, param)| {
+                        if Some(i) == call_info.active_parameter {
+                            format!("<{}>", param)
+                        } else {
+                            param.to_string()
+                        }
+                    })
+                    .collect::<Vec<_>>()
+                    .join(", ");
+                format!("{}{}\n({})\n", docs, call_info.signature, params)
+            }
+            None => String::new(),
+        };
+        expect.assert_eq(&actual);
+    }
+
+    #[test]
+    fn test_fn_signature_two_args() {
+        check(
+            r#"
+fn foo(x: u32, y: u32) -> u32 {x + y}
+fn bar() { foo(<|>3, ); }
+"#,
+            expect![[r#"
+                fn foo(x: u32, y: u32) -> u32
+                (<x: u32>, y: u32)
+            "#]],
+        );
+        check(
+            r#"
+fn foo(x: u32, y: u32) -> u32 {x + y}
+fn bar() { foo(3<|>, ); }
+"#,
+            expect![[r#"
+                fn foo(x: u32, y: u32) -> u32
+                (<x: u32>, y: u32)
+            "#]],
+        );
+        check(
+            r#"
+fn foo(x: u32, y: u32) -> u32 {x + y}
+fn bar() { foo(3,<|> ); }
+"#,
+            expect![[r#"
+                fn foo(x: u32, y: u32) -> u32
+                (x: u32, <y: u32>)
+            "#]],
+        );
+        check(
+            r#"
+fn foo(x: u32, y: u32) -> u32 {x + y}
+fn bar() { foo(3, <|>); }
+"#,
+            expect![[r#"
+                fn foo(x: u32, y: u32) -> u32
+                (x: u32, <y: u32>)
+            "#]],
+        );
+    }
+
+    #[test]
+    fn test_fn_signature_two_args_empty() {
+        check(
+            r#"
+fn foo(x: u32, y: u32) -> u32 {x + y}
+fn bar() { foo(<|>); }
+"#,
+            expect![[r#"
+                fn foo(x: u32, y: u32) -> u32
+                (<x: u32>, y: u32)
+            "#]],
+        );
+    }
+
+    #[test]
+    fn test_fn_signature_two_args_first_generics() {
+        check(
+            r#"
+fn foo<T, U: Copy + Display>(x: T, y: U) -> u32
+    where T: Copy + Display, U: Debug
+{ x + y }
+
+fn bar() { foo(<|>3, ); }
+"#,
+            expect![[r#"
+                fn foo(x: i32, y: {unknown}) -> u32
+                (<x: i32>, y: {unknown})
+            "#]],
+        );
+    }
+
+    #[test]
+    fn test_fn_signature_no_params() {
+        check(
+            r#"
+fn foo<T>() -> T where T: Copy + Display {}
+fn bar() { foo(<|>); }
+"#,
+            expect![[r#"
+                fn foo() -> {unknown}
+                ()
+            "#]],
+        );
+    }
+
+    #[test]
+    fn test_fn_signature_for_impl() {
+        check(
+            r#"
+struct F;
+impl F { pub fn new() { } }
+fn bar() {
+    let _ : F = F::new(<|>);
+}
+"#,
+            expect![[r#"
+                fn new()
+                ()
+            "#]],
+        );
+    }
+
+    #[test]
+    fn test_fn_signature_for_method_self() {
+        check(
+            r#"
+struct S;
+impl S { pub fn do_it(&self) {} }
+
+fn bar() {
+    let s: S = S;
+    s.do_it(<|>);
+}
+"#,
+            expect![[r#"
+                fn do_it(&self)
+                ()
+            "#]],
+        );
+    }
+
+    #[test]
+    fn test_fn_signature_for_method_with_arg() {
+        check(
+            r#"
+struct S;
+impl S {
+    fn foo(&self, x: i32) {}
+}
+
+fn main() { S.foo(<|>); }
+"#,
+            expect![[r#"
+                fn foo(&self, x: i32)
+                (<x: i32>)
+            "#]],
+        );
+    }
+
+    #[test]
+    fn test_fn_signature_for_method_with_arg_as_assoc_fn() {
+        check(
+            r#"
+struct S;
+impl S {
+    fn foo(&self, x: i32) {}
+}
+
+fn main() { S::foo(<|>); }
+"#,
+            expect![[r#"
+                fn foo(self: &S, x: i32)
+                (<self: &S>, x: i32)
+            "#]],
+        );
+    }
+
+    #[test]
+    fn test_fn_signature_with_docs_simple() {
+        check(
+            r#"
+/// test
+// non-doc-comment
+fn foo(j: u32) -> u32 {
+    j
+}
+
+fn bar() {
+    let _ = foo(<|>);
+}
+"#,
+            expect![[r#"
+                test
+                ------
+                fn foo(j: u32) -> u32
+                (<j: u32>)
+            "#]],
+        );
+    }
+
+    #[test]
+    fn test_fn_signature_with_docs() {
+        check(
+            r#"
+/// Adds one to the number given.
+///
+/// # Examples
+///
+/// ```
+/// let five = 5;
+///
+/// assert_eq!(6, my_crate::add_one(5));
+/// ```
+pub fn add_one(x: i32) -> i32 {
+    x + 1
+}
+
+pub fn do() {
+    add_one(<|>
+}"#,
+            expect![[r##"
+                Adds one to the number given.
+
+                # Examples
+
+                ```
+                let five = 5;
+
+                assert_eq!(6, my_crate::add_one(5));
+                ```
+                ------
+                fn add_one(x: i32) -> i32
+                (<x: i32>)
+            "##]],
+        );
+    }
+
+    #[test]
+    fn test_fn_signature_with_docs_impl() {
+        check(
+            r#"
+struct addr;
+impl addr {
+    /// Adds one to the number given.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// let five = 5;
+    ///
+    /// assert_eq!(6, my_crate::add_one(5));
+    /// ```
+    pub fn add_one(x: i32) -> i32 {
+        x + 1
+    }
+}
+
+pub fn do_it() {
+    addr {};
+    addr::add_one(<|>);
+}
+"#,
+            expect![[r##"
+                Adds one to the number given.
+
+                # Examples
+
+                ```
+                let five = 5;
+
+                assert_eq!(6, my_crate::add_one(5));
+                ```
+                ------
+                fn add_one(x: i32) -> i32
+                (<x: i32>)
+            "##]],
+        );
+    }
+
+    #[test]
+    fn test_fn_signature_with_docs_from_actix() {
+        check(
+            r#"
+struct WriteHandler<E>;
+
+impl<E> WriteHandler<E> {
+    /// Method is called when writer emits error.
+    ///
+    /// If this method returns `ErrorAction::Continue` writer processing
+    /// continues otherwise stream processing stops.
+    fn error(&mut self, err: E, ctx: &mut Self::Context) -> Running {
+        Running::Stop
+    }
+
+    /// Method is called when writer finishes.
+    ///
+    /// By default this method stops actor's `Context`.
+    fn finished(&mut self, ctx: &mut Self::Context) {
+        ctx.stop()
+    }
+}
+
+pub fn foo(mut r: WriteHandler<()>) {
+    r.finished(<|>);
+}
+"#,
+            expect![[r#"
+                Method is called when writer finishes.
+
+                By default this method stops actor's `Context`.
+                ------
+                fn finished(&mut self, ctx: &mut {unknown})
+                (<ctx: &mut {unknown}>)
+            "#]],
+        );
+    }
+
+    #[test]
+    fn call_info_bad_offset() {
+        mark::check!(call_info_bad_offset);
+        check(
+            r#"
+fn foo(x: u32, y: u32) -> u32 {x + y}
+fn bar() { foo <|> (3, ); }
+"#,
+            expect![[""]],
+        );
+    }
+
+    #[test]
+    fn test_nested_method_in_lambda() {
+        check(
+            r#"
+struct Foo;
+impl Foo { fn bar(&self, _: u32) { } }
+
+fn bar(_: u32) { }
+
+fn main() {
+    let foo = Foo;
+    std::thread::spawn(move || foo.bar(<|>));
+}
+"#,
+            expect![[r#"
+                fn bar(&self, _: u32)
+                (<_: u32>)
+            "#]],
+        );
+    }
+
+    #[test]
+    fn works_for_tuple_structs() {
+        check(
+            r#"
+/// A cool tuple struct
+struct S(u32, i32);
+fn main() {
+    let s = S(0, <|>);
+}
+"#,
+            expect![[r#"
+                A cool tuple struct
+                ------
+                struct S(u32, i32)
+                (u32, <i32>)
+            "#]],
+        );
+    }
+
+    #[test]
+    fn generic_struct() {
+        check(
+            r#"
+struct S<T>(T);
+fn main() {
+    let s = S(<|>);
+}
+"#,
+            expect![[r#"
+                struct S({unknown})
+                (<{unknown}>)
+            "#]],
+        );
+    }
+
+    #[test]
+    fn works_for_enum_variants() {
+        check(
+            r#"
+enum E {
+    /// A Variant
+    A(i32),
+    /// Another
+    B,
+    /// And C
+    C { a: i32, b: i32 }
+}
+
+fn main() {
+    let a = E::A(<|>);
+}
+"#,
+            expect![[r#"
+                A Variant
+                ------
+                enum E::A(i32)
+                (<i32>)
+            "#]],
+        );
+    }
+
+    #[test]
+    fn cant_call_struct_record() {
+        check(
+            r#"
+struct S { x: u32, y: i32 }
+fn main() {
+    let s = S(<|>);
+}
+"#,
+            expect![[""]],
+        );
+    }
+
+    #[test]
+    fn cant_call_enum_record() {
+        check(
+            r#"
+enum E {
+    /// A Variant
+    A(i32),
+    /// Another
+    B,
+    /// And C
+    C { a: i32, b: i32 }
+}
+
+fn main() {
+    let a = E::C(<|>);
+}
+"#,
+            expect![[""]],
+        );
+    }
+
+    #[test]
+    fn fn_signature_for_call_in_macro() {
+        check(
+            r#"
+macro_rules! id { ($($tt:tt)*) => { $($tt)* } }
+fn foo() { }
+id! {
+    fn bar() { foo(<|>); }
+}
+"#,
+            expect![[r#"
+                fn foo()
+                ()
+            "#]],
+        );
+    }
+
+    #[test]
+    fn call_info_for_lambdas() {
+        check(
+            r#"
+struct S;
+fn foo(s: S) -> i32 { 92 }
+fn main() {
+    (|s| foo(s))(<|>)
+}
+        "#,
+            expect![[r#"
+                (S) -> i32
+                (<S>)
+            "#]],
+        )
+    }
+
+    #[test]
+    fn call_info_for_fn_ptr() {
+        check(
+            r#"
+fn main(f: fn(i32, f64) -> char) {
+    f(0, <|>)
+}
+        "#,
+            expect![[r#"
+                (i32, f64) -> char
+                (i32, <f64>)
+            "#]],
+        )
+    }
+}
diff --git a/crates/ide_db/src/defs.rs b/crates/ide_db/src/defs.rs
index 5d2cd30d1e..201a3d6fa0 100644
--- a/crates/ide_db/src/defs.rs
+++ b/crates/ide_db/src/defs.rs
@@ -258,7 +258,7 @@ impl NameRefClass {
         sema: &Semantics<RootDatabase>,
         name_ref: &ast::NameRef,
     ) -> Option<NameRefClass> {
-        let _p = profile::span("classify_name_ref").detail(|| name_ref.to_string());
+        let _p = profile::span("classify_name_ref");
 
         let parent = name_ref.syntax().parent()?;
 
diff --git a/crates/ide_db/src/helpers/insert_use.rs b/crates/ide_db/src/helpers/insert_use.rs
index 040843990d..08d246c167 100644
--- a/crates/ide_db/src/helpers/insert_use.rs
+++ b/crates/ide_db/src/helpers/insert_use.rs
@@ -573,4 +573,641 @@ fn find_insert_position(
 }
 
 #[cfg(test)]
-mod tests;
+mod tests {
+    use super::*;
+
+    use test_utils::assert_eq_text;
+
+    #[test]
+    fn insert_existing() {
+        check_full("std::fs", "use std::fs;", "use std::fs;")
+    }
+
+    #[test]
+    fn insert_start() {
+        check_none(
+            "std::bar::AA",
+            r"
+use std::bar::B;
+use std::bar::D;
+use std::bar::F;
+use std::bar::G;",
+            r"
+use std::bar::AA;
+use std::bar::B;
+use std::bar::D;
+use std::bar::F;
+use std::bar::G;",
+        )
+    }
+
+    #[test]
+    fn insert_start_indent() {
+        mark::check!(insert_use_indent_after);
+        check_none(
+            "std::bar::AA",
+            r"
+    use std::bar::B;
+    use std::bar::D;",
+            r"
+    use std::bar::AA;
+    use std::bar::B;
+    use std::bar::D;",
+        )
+    }
+
+    #[test]
+    fn insert_middle() {
+        check_none(
+            "std::bar::EE",
+            r"
+use std::bar::A;
+use std::bar::D;
+use std::bar::F;
+use std::bar::G;",
+            r"
+use std::bar::A;
+use std::bar::D;
+use std::bar::EE;
+use std::bar::F;
+use std::bar::G;",
+        )
+    }
+
+    #[test]
+    fn insert_middle_indent() {
+        check_none(
+            "std::bar::EE",
+            r"
+    use std::bar::A;
+    use std::bar::D;
+    use std::bar::F;
+    use std::bar::G;",
+            r"
+    use std::bar::A;
+    use std::bar::D;
+    use std::bar::EE;
+    use std::bar::F;
+    use std::bar::G;",
+        )
+    }
+
+    #[test]
+    fn insert_end() {
+        check_none(
+            "std::bar::ZZ",
+            r"
+use std::bar::A;
+use std::bar::D;
+use std::bar::F;
+use std::bar::G;",
+            r"
+use std::bar::A;
+use std::bar::D;
+use std::bar::F;
+use std::bar::G;
+use std::bar::ZZ;",
+        )
+    }
+
+    #[test]
+    fn insert_end_indent() {
+        mark::check!(insert_use_indent_before);
+        check_none(
+            "std::bar::ZZ",
+            r"
+    use std::bar::A;
+    use std::bar::D;
+    use std::bar::F;
+    use std::bar::G;",
+            r"
+    use std::bar::A;
+    use std::bar::D;
+    use std::bar::F;
+    use std::bar::G;
+    use std::bar::ZZ;",
+        )
+    }
+
+    #[test]
+    fn insert_middle_nested() {
+        check_none(
+            "std::bar::EE",
+            r"
+use std::bar::A;
+use std::bar::{D, Z}; // example of weird imports due to user
+use std::bar::F;
+use std::bar::G;",
+            r"
+use std::bar::A;
+use std::bar::EE;
+use std::bar::{D, Z}; // example of weird imports due to user
+use std::bar::F;
+use std::bar::G;",
+        )
+    }
+
+    #[test]
+    fn insert_middle_groups() {
+        check_none(
+            "foo::bar::GG",
+            r"
+    use std::bar::A;
+    use std::bar::D;
+
+    use foo::bar::F;
+    use foo::bar::H;",
+            r"
+    use std::bar::A;
+    use std::bar::D;
+
+    use foo::bar::F;
+    use foo::bar::GG;
+    use foo::bar::H;",
+        )
+    }
+
+    #[test]
+    fn insert_first_matching_group() {
+        check_none(
+            "foo::bar::GG",
+            r"
+    use foo::bar::A;
+    use foo::bar::D;
+
+    use std;
+
+    use foo::bar::F;
+    use foo::bar::H;",
+            r"
+    use foo::bar::A;
+    use foo::bar::D;
+    use foo::bar::GG;
+
+    use std;
+
+    use foo::bar::F;
+    use foo::bar::H;",
+        )
+    }
+
+    #[test]
+    fn insert_missing_group_std() {
+        check_none(
+            "std::fmt",
+            r"
+    use foo::bar::A;
+    use foo::bar::D;",
+            r"
+    use std::fmt;
+
+    use foo::bar::A;
+    use foo::bar::D;",
+        )
+    }
+
+    #[test]
+    fn insert_missing_group_self() {
+        check_none(
+            "self::fmt",
+            r"
+use foo::bar::A;
+use foo::bar::D;",
+            r"
+use foo::bar::A;
+use foo::bar::D;
+
+use self::fmt;",
+        )
+    }
+
+    #[test]
+    fn insert_no_imports() {
+        check_full(
+            "foo::bar",
+            "fn main() {}",
+            r"use foo::bar;
+
+fn main() {}",
+        )
+    }
+
+    #[test]
+    fn insert_empty_file() {
+        // empty files will get two trailing newlines
+        // this is due to the test case insert_no_imports above
+        check_full(
+            "foo::bar",
+            "",
+            r"use foo::bar;
+
+",
+        )
+    }
+
+    #[test]
+    fn insert_empty_module() {
+        mark::check!(insert_use_no_indent_after);
+        check(
+            "foo::bar",
+            "mod x {}",
+            r"{
+    use foo::bar;
+}",
+            None,
+            true,
+        )
+    }
+
+    #[test]
+    fn insert_after_inner_attr() {
+        check_full(
+            "foo::bar",
+            r"#![allow(unused_imports)]",
+            r"#![allow(unused_imports)]
+
+use foo::bar;",
+        )
+    }
+
+    #[test]
+    fn insert_after_inner_attr2() {
+        check_full(
+            "foo::bar",
+            r"#![allow(unused_imports)]
+
+#![no_std]
+fn main() {}",
+            r"#![allow(unused_imports)]
+
+#![no_std]
+
+use foo::bar;
+fn main() {}",
+        );
+    }
+
+    #[test]
+    fn inserts_after_single_line_inner_comments() {
+        check_none(
+            "foo::bar::Baz",
+            "//! Single line inner comments do not allow any code before them.",
+            r#"//! Single line inner comments do not allow any code before them.
+
+use foo::bar::Baz;"#,
+        );
+    }
+
+    #[test]
+    fn inserts_after_multiline_inner_comments() {
+        check_none(
+            "foo::bar::Baz",
+            r#"/*! Multiline inner comments do not allow any code before them. */
+
+/*! Still an inner comment, cannot place any code before. */
+fn main() {}"#,
+            r#"/*! Multiline inner comments do not allow any code before them. */
+
+/*! Still an inner comment, cannot place any code before. */
+
+use foo::bar::Baz;
+fn main() {}"#,
+        )
+    }
+
+    #[test]
+    fn inserts_after_all_inner_items() {
+        check_none(
+            "foo::bar::Baz",
+            r#"#![allow(unused_imports)]
+/*! Multiline line comment 2 */
+
+
+//! Single line comment 1
+#![no_std]
+//! Single line comment 2
+fn main() {}"#,
+            r#"#![allow(unused_imports)]
+/*! Multiline line comment 2 */
+
+
+//! Single line comment 1
+#![no_std]
+//! Single line comment 2
+
+use foo::bar::Baz;
+fn main() {}"#,
+        )
+    }
+
+    #[test]
+    fn merge_groups() {
+        check_last("std::io", r"use std::fmt;", r"use std::{fmt, io};")
+    }
+
+    #[test]
+    fn merge_groups_last() {
+        check_last(
+            "std::io",
+            r"use std::fmt::{Result, Display};",
+            r"use std::fmt::{Result, Display};
+use std::io;",
+        )
+    }
+
+    #[test]
+    fn merge_last_into_self() {
+        check_last("foo::bar::baz", r"use foo::bar;", r"use foo::bar::{self, baz};");
+    }
+
+    #[test]
+    fn merge_groups_full() {
+        check_full(
+            "std::io",
+            r"use std::fmt::{Result, Display};",
+            r"use std::{fmt::{Result, Display}, io};",
+        )
+    }
+
+    #[test]
+    fn merge_groups_long_full() {
+        check_full(
+            "std::foo::bar::Baz",
+            r"use std::foo::bar::Qux;",
+            r"use std::foo::bar::{Baz, Qux};",
+        )
+    }
+
+    #[test]
+    fn merge_groups_long_last() {
+        check_last(
+            "std::foo::bar::Baz",
+            r"use std::foo::bar::Qux;",
+            r"use std::foo::bar::{Baz, Qux};",
+        )
+    }
+
+    #[test]
+    fn merge_groups_long_full_list() {
+        check_full(
+            "std::foo::bar::Baz",
+            r"use std::foo::bar::{Qux, Quux};",
+            r"use std::foo::bar::{Baz, Quux, Qux};",
+        )
+    }
+
+    #[test]
+    fn merge_groups_long_last_list() {
+        check_last(
+            "std::foo::bar::Baz",
+            r"use std::foo::bar::{Qux, Quux};",
+            r"use std::foo::bar::{Baz, Quux, Qux};",
+        )
+    }
+
+    #[test]
+    fn merge_groups_long_full_nested() {
+        check_full(
+            "std::foo::bar::Baz",
+            r"use std::foo::bar::{Qux, quux::{Fez, Fizz}};",
+            r"use std::foo::bar::{Baz, Qux, quux::{Fez, Fizz}};",
+        )
+    }
+
+    #[test]
+    fn merge_groups_long_last_nested() {
+        check_last(
+            "std::foo::bar::Baz",
+            r"use std::foo::bar::{Qux, quux::{Fez, Fizz}};",
+            r"use std::foo::bar::Baz;
+use std::foo::bar::{Qux, quux::{Fez, Fizz}};",
+        )
+    }
+
+    #[test]
+    fn merge_groups_full_nested_deep() {
+        check_full(
+            "std::foo::bar::quux::Baz",
+            r"use std::foo::bar::{Qux, quux::{Fez, Fizz}};",
+            r"use std::foo::bar::{Qux, quux::{Baz, Fez, Fizz}};",
+        )
+    }
+
+    #[test]
+    fn merge_groups_full_nested_long() {
+        check_full(
+            "std::foo::bar::Baz",
+            r"use std::{foo::bar::Qux};",
+            r"use std::{foo::bar::{Baz, Qux}};",
+        );
+    }
+
+    #[test]
+    fn merge_groups_last_nested_long() {
+        check_full(
+            "std::foo::bar::Baz",
+            r"use std::{foo::bar::Qux};",
+            r"use std::{foo::bar::{Baz, Qux}};",
+        );
+    }
+
+    #[test]
+    fn merge_groups_skip_pub() {
+        check_full(
+            "std::io",
+            r"pub use std::fmt::{Result, Display};",
+            r"pub use std::fmt::{Result, Display};
+use std::io;",
+        )
+    }
+
+    #[test]
+    fn merge_groups_skip_pub_crate() {
+        check_full(
+            "std::io",
+            r"pub(crate) use std::fmt::{Result, Display};",
+            r"pub(crate) use std::fmt::{Result, Display};
+use std::io;",
+        )
+    }
+
+    #[test]
+    #[ignore] // FIXME: Support this
+    fn split_out_merge() {
+        check_last(
+            "std::fmt::Result",
+            r"use std::{fmt, io};",
+            r"use std::fmt::{self, Result};
+use std::io;",
+        )
+    }
+
+    #[test]
+    fn merge_into_module_import() {
+        check_full(
+            "std::fmt::Result",
+            r"use std::{fmt, io};",
+            r"use std::{fmt::{self, Result}, io};",
+        )
+    }
+
+    #[test]
+    fn merge_groups_self() {
+        check_full("std::fmt::Debug", r"use std::fmt;", r"use std::fmt::{self, Debug};")
+    }
+
+    #[test]
+    fn merge_mod_into_glob() {
+        check_full(
+            "token::TokenKind",
+            r"use token::TokenKind::*;",
+            r"use token::TokenKind::{*, self};",
+        )
+        // FIXME: have it emit `use token::TokenKind::{self, *}`?
+    }
+
+    #[test]
+    fn merge_self_glob() {
+        check_full("self", r"use self::*;", r"use self::{*, self};")
+        // FIXME: have it emit `use {self, *}`?
+    }
+
+    #[test]
+    fn merge_glob_nested() {
+        check_full(
+            "foo::bar::quux::Fez",
+            r"use foo::bar::{Baz, quux::*};",
+            r"use foo::bar::{Baz, quux::{self::*, Fez}};",
+        )
+    }
+
+    #[test]
+    fn merge_nested_considers_first_segments() {
+        check_full(
+            "hir_ty::display::write_bounds_like_dyn_trait",
+            r"use hir_ty::{autoderef, display::{HirDisplayError, HirFormatter}, method_resolution};",
+            r"use hir_ty::{autoderef, display::{HirDisplayError, HirFormatter, write_bounds_like_dyn_trait}, method_resolution};",
+        );
+    }
+
+    #[test]
+    fn skip_merge_last_too_long() {
+        check_last(
+            "foo::bar",
+            r"use foo::bar::baz::Qux;",
+            r"use foo::bar;
+use foo::bar::baz::Qux;",
+        );
+    }
+
+    #[test]
+    fn skip_merge_last_too_long2() {
+        check_last(
+            "foo::bar::baz::Qux",
+            r"use foo::bar;",
+            r"use foo::bar;
+use foo::bar::baz::Qux;",
+        );
+    }
+
+    #[test]
+    fn insert_short_before_long() {
+        check_none(
+            "foo::bar",
+            r"use foo::bar::baz::Qux;",
+            r"use foo::bar;
+use foo::bar::baz::Qux;",
+        );
+    }
+
+    #[test]
+    fn merge_last_fail() {
+        check_merge_only_fail(
+            r"use foo::bar::{baz::{Qux, Fez}};",
+            r"use foo::bar::{baaz::{Quux, Feez}};",
+            MergeBehaviour::Last,
+        );
+    }
+
+    #[test]
+    fn merge_last_fail1() {
+        check_merge_only_fail(
+            r"use foo::bar::{baz::{Qux, Fez}};",
+            r"use foo::bar::baaz::{Quux, Feez};",
+            MergeBehaviour::Last,
+        );
+    }
+
+    #[test]
+    fn merge_last_fail2() {
+        check_merge_only_fail(
+            r"use foo::bar::baz::{Qux, Fez};",
+            r"use foo::bar::{baaz::{Quux, Feez}};",
+            MergeBehaviour::Last,
+        );
+    }
+
+    #[test]
+    fn merge_last_fail3() {
+        check_merge_only_fail(
+            r"use foo::bar::baz::{Qux, Fez};",
+            r"use foo::bar::baaz::{Quux, Feez};",
+            MergeBehaviour::Last,
+        );
+    }
+
+    fn check(
+        path: &str,
+        ra_fixture_before: &str,
+        ra_fixture_after: &str,
+        mb: Option<MergeBehaviour>,
+        module: bool,
+    ) {
+        let mut syntax = ast::SourceFile::parse(ra_fixture_before).tree().syntax().clone();
+        if module {
+            syntax = syntax.descendants().find_map(ast::Module::cast).unwrap().syntax().clone();
+        }
+        let file = super::ImportScope::from(syntax).unwrap();
+        let path = ast::SourceFile::parse(&format!("use {};", path))
+            .tree()
+            .syntax()
+            .descendants()
+            .find_map(ast::Path::cast)
+            .unwrap();
+
+        let rewriter = insert_use(&file, path, mb);
+        let result = rewriter.rewrite(file.as_syntax_node()).to_string();
+        assert_eq_text!(&result, ra_fixture_after);
+    }
+
+    fn check_full(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) {
+        check(path, ra_fixture_before, ra_fixture_after, Some(MergeBehaviour::Full), false)
+    }
+
+    fn check_last(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) {
+        check(path, ra_fixture_before, ra_fixture_after, Some(MergeBehaviour::Last), false)
+    }
+
+    fn check_none(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) {
+        check(path, ra_fixture_before, ra_fixture_after, None, false)
+    }
+
+    fn check_merge_only_fail(ra_fixture0: &str, ra_fixture1: &str, mb: MergeBehaviour) {
+        let use0 = ast::SourceFile::parse(ra_fixture0)
+            .tree()
+            .syntax()
+            .descendants()
+            .find_map(ast::Use::cast)
+            .unwrap();
+
+        let use1 = ast::SourceFile::parse(ra_fixture1)
+            .tree()
+            .syntax()
+            .descendants()
+            .find_map(ast::Use::cast)
+            .unwrap();
+
+        let result = try_merge_imports(&use0, &use1, mb);
+        assert_eq!(result.map(|u| u.to_string()), None);
+    }
+}
diff --git a/crates/ide_db/src/line_index.rs b/crates/ide_db/src/line_index.rs
index 41226305ea..a381f7fb8d 100644
--- a/crates/ide_db/src/line_index.rs
+++ b/crates/ide_db/src/line_index.rs
@@ -149,4 +149,133 @@ impl LineIndex {
 }
 
 #[cfg(test)]
-mod tests;
+mod tests {
+    use super::*;
+
+    #[test]
+    fn test_line_index() {
+        let text = "hello\nworld";
+        let index = LineIndex::new(text);
+        assert_eq!(index.line_col(0.into()), LineCol { line: 0, col_utf16: 0 });
+        assert_eq!(index.line_col(1.into()), LineCol { line: 0, col_utf16: 1 });
+        assert_eq!(index.line_col(5.into()), LineCol { line: 0, col_utf16: 5 });
+        assert_eq!(index.line_col(6.into()), LineCol { line: 1, col_utf16: 0 });
+        assert_eq!(index.line_col(7.into()), LineCol { line: 1, col_utf16: 1 });
+        assert_eq!(index.line_col(8.into()), LineCol { line: 1, col_utf16: 2 });
+        assert_eq!(index.line_col(10.into()), LineCol { line: 1, col_utf16: 4 });
+        assert_eq!(index.line_col(11.into()), LineCol { line: 1, col_utf16: 5 });
+        assert_eq!(index.line_col(12.into()), LineCol { line: 1, col_utf16: 6 });
+
+        let text = "\nhello\nworld";
+        let index = LineIndex::new(text);
+        assert_eq!(index.line_col(0.into()), LineCol { line: 0, col_utf16: 0 });
+        assert_eq!(index.line_col(1.into()), LineCol { line: 1, col_utf16: 0 });
+        assert_eq!(index.line_col(2.into()), LineCol { line: 1, col_utf16: 1 });
+        assert_eq!(index.line_col(6.into()), LineCol { line: 1, col_utf16: 5 });
+        assert_eq!(index.line_col(7.into()), LineCol { line: 2, col_utf16: 0 });
+    }
+
+    #[test]
+    fn test_char_len() {
+        assert_eq!('メ'.len_utf8(), 3);
+        assert_eq!('メ'.len_utf16(), 1);
+    }
+
+    #[test]
+    fn test_empty_index() {
+        let col_index = LineIndex::new(
+            "
+const C: char = 'x';
+",
+        );
+        assert_eq!(col_index.utf16_lines.len(), 0);
+    }
+
+    #[test]
+    fn test_single_char() {
+        let col_index = LineIndex::new(
+            "
+const C: char = 'メ';
+",
+        );
+
+        assert_eq!(col_index.utf16_lines.len(), 1);
+        assert_eq!(col_index.utf16_lines[&1].len(), 1);
+        assert_eq!(col_index.utf16_lines[&1][0], Utf16Char { start: 17.into(), end: 20.into() });
+
+        // UTF-8 to UTF-16, no changes
+        assert_eq!(col_index.utf8_to_utf16_col(1, 15.into()), 15);
+
+        // UTF-8 to UTF-16
+        assert_eq!(col_index.utf8_to_utf16_col(1, 22.into()), 20);
+
+        // UTF-16 to UTF-8, no changes
+        assert_eq!(col_index.utf16_to_utf8_col(1, 15), TextSize::from(15));
+
+        // UTF-16 to UTF-8
+        assert_eq!(col_index.utf16_to_utf8_col(1, 19), TextSize::from(21));
+
+        let col_index = LineIndex::new("a𐐏b");
+        assert_eq!(col_index.utf16_to_utf8_col(0, 3), TextSize::from(5));
+    }
+
+    #[test]
+    fn test_string() {
+        let col_index = LineIndex::new(
+            "
+const C: char = \"メ メ\";
+",
+        );
+
+        assert_eq!(col_index.utf16_lines.len(), 1);
+        assert_eq!(col_index.utf16_lines[&1].len(), 2);
+        assert_eq!(col_index.utf16_lines[&1][0], Utf16Char { start: 17.into(), end: 20.into() });
+        assert_eq!(col_index.utf16_lines[&1][1], Utf16Char { start: 21.into(), end: 24.into() });
+
+        // UTF-8 to UTF-16
+        assert_eq!(col_index.utf8_to_utf16_col(1, 15.into()), 15);
+
+        assert_eq!(col_index.utf8_to_utf16_col(1, 21.into()), 19);
+        assert_eq!(col_index.utf8_to_utf16_col(1, 25.into()), 21);
+
+        assert!(col_index.utf8_to_utf16_col(2, 15.into()) == 15);
+
+        // UTF-16 to UTF-8
+        assert_eq!(col_index.utf16_to_utf8_col(1, 15), TextSize::from(15));
+
+        // メ UTF-8: 0xE3 0x83 0xA1, UTF-16: 0x30E1
+        assert_eq!(col_index.utf16_to_utf8_col(1, 17), TextSize::from(17)); // first メ at 17..20
+        assert_eq!(col_index.utf16_to_utf8_col(1, 18), TextSize::from(20)); // space
+        assert_eq!(col_index.utf16_to_utf8_col(1, 19), TextSize::from(21)); // second メ at 21..24
+
+        assert_eq!(col_index.utf16_to_utf8_col(2, 15), TextSize::from(15));
+    }
+
+    #[test]
+    fn test_splitlines() {
+        fn r(lo: u32, hi: u32) -> TextRange {
+            TextRange::new(lo.into(), hi.into())
+        }
+
+        let text = "a\nbb\nccc\n";
+        let line_index = LineIndex::new(text);
+
+        let actual = line_index.lines(r(0, 9)).collect::<Vec<_>>();
+        let expected = vec![r(0, 2), r(2, 5), r(5, 9)];
+        assert_eq!(actual, expected);
+
+        let text = "";
+        let line_index = LineIndex::new(text);
+
+        let actual = line_index.lines(r(0, 0)).collect::<Vec<_>>();
+        let expected = vec![];
+        assert_eq!(actual, expected);
+
+        let text = "\n";
+        let line_index = LineIndex::new(text);
+
+        let actual = line_index.lines(r(0, 1)).collect::<Vec<_>>();
+        let expected = vec![r(0, 1)];
+        assert_eq!(actual, expected)
+    }
+}
diff --git a/crates/ide_db/src/traits.rs b/crates/ide_db/src/traits.rs
index 78a43f587a..f57b6dd91e 100644
--- a/crates/ide_db/src/traits.rs
+++ b/crates/ide_db/src/traits.rs
@@ -78,4 +78,150 @@ pub fn get_missing_assoc_items(
 }
 
 #[cfg(test)]
-mod tests;
+mod tests {
+    use crate::RootDatabase;
+    use base_db::{fixture::ChangeFixture, FilePosition};
+    use expect_test::{expect, Expect};
+    use hir::Semantics;
+    use syntax::ast::{self, AstNode};
+    use test_utils::RangeOrOffset;
+
+    /// Creates analysis from a multi-file fixture, returns positions marked with <|>.
+    pub(crate) fn position(ra_fixture: &str) -> (RootDatabase, FilePosition) {
+        let change_fixture = ChangeFixture::parse(ra_fixture);
+        let mut database = RootDatabase::default();
+        database.apply_change(change_fixture.change);
+        let (file_id, range_or_offset) =
+            change_fixture.file_position.expect("expected a marker (<|>)");
+        let offset = match range_or_offset {
+            RangeOrOffset::Range(_) => panic!(),
+            RangeOrOffset::Offset(it) => it,
+        };
+        (database, FilePosition { file_id, offset })
+    }
+
+    fn check_trait(ra_fixture: &str, expect: Expect) {
+        let (db, position) = position(ra_fixture);
+        let sema = Semantics::new(&db);
+        let file = sema.parse(position.file_id);
+        let impl_block: ast::Impl =
+            sema.find_node_at_offset_with_descend(file.syntax(), position.offset).unwrap();
+        let trait_ = crate::traits::resolve_target_trait(&sema, &impl_block);
+        let actual = match trait_ {
+            Some(trait_) => trait_.name(&db).to_string(),
+            None => String::new(),
+        };
+        expect.assert_eq(&actual);
+    }
+
+    fn check_missing_assoc(ra_fixture: &str, expect: Expect) {
+        let (db, position) = position(ra_fixture);
+        let sema = Semantics::new(&db);
+        let file = sema.parse(position.file_id);
+        let impl_block: ast::Impl =
+            sema.find_node_at_offset_with_descend(file.syntax(), position.offset).unwrap();
+        let items = crate::traits::get_missing_assoc_items(&sema, &impl_block);
+        let actual = items
+            .into_iter()
+            .map(|item| item.name(&db).unwrap().to_string())
+            .collect::<Vec<_>>()
+            .join("\n");
+        expect.assert_eq(&actual);
+    }
+
+    #[test]
+    fn resolve_trait() {
+        check_trait(
+            r#"
+pub trait Foo {
+    fn bar();
+}
+impl Foo for u8 {
+    <|>
+}
+            "#,
+            expect![["Foo"]],
+        );
+        check_trait(
+            r#"
+pub trait Foo {
+    fn bar();
+}
+impl Foo for u8 {
+    fn bar() {
+        fn baz() {
+            <|>
+        }
+        baz();
+    }
+}
+            "#,
+            expect![["Foo"]],
+        );
+        check_trait(
+            r#"
+pub trait Foo {
+    fn bar();
+}
+pub struct Bar;
+impl Bar {
+    <|>
+}
+            "#,
+            expect![[""]],
+        );
+    }
+
+    #[test]
+    fn missing_assoc_items() {
+        check_missing_assoc(
+            r#"
+pub trait Foo {
+    const FOO: u8;
+    fn bar();
+}
+impl Foo for u8 {
+    <|>
+}"#,
+            expect![[r#"
+                FOO
+                bar"#]],
+        );
+
+        check_missing_assoc(
+            r#"
+pub trait Foo {
+    const FOO: u8;
+    fn bar();
+}
+impl Foo for u8 {
+    const FOO: u8 = 10;
+    <|>
+}"#,
+            expect![[r#"
+                bar"#]],
+        );
+
+        check_missing_assoc(
+            r#"
+pub trait Foo {
+    const FOO: u8;
+    fn bar();
+}
+impl Foo for u8 {
+    const FOO: u8 = 10;
+    fn bar() {<|>}
+}"#,
+            expect![[r#""#]],
+        );
+
+        check_missing_assoc(
+            r#"
+pub struct Foo;
+impl Foo {
+    fn bar() {<|>}
+}"#,
+            expect![[r#""#]],
+        );
+    }
+}
diff --git a/crates/proc_macro_api/src/process.rs b/crates/proc_macro_api/src/process.rs
index 301888a0e0..907cb3db71 100644
--- a/crates/proc_macro_api/src/process.rs
+++ b/crates/proc_macro_api/src/process.rs
@@ -19,7 +19,7 @@ use crate::{
 
 #[derive(Debug, Default)]
 pub(crate) struct ProcMacroProcessSrv {
-    inner: Weak<Sender<Task>>,
+    inner: Option<Weak<Sender<Task>>>,
 }
 
 #[derive(Debug)]
@@ -42,7 +42,7 @@ impl ProcMacroProcessSrv {
         });
 
         let task_tx = Arc::new(task_tx);
-        let srv = ProcMacroProcessSrv { inner: Arc::downgrade(&task_tx) };
+        let srv = ProcMacroProcessSrv { inner: Some(Arc::downgrade(&task_tx)) };
         let thread = ProcMacroProcessThread { handle, sender: task_tx };
 
         Ok((thread, srv))
@@ -79,18 +79,22 @@ impl ProcMacroProcessSrv {
     where
         R: TryFrom<Response, Error = &'static str>,
     {
-        let (result_tx, result_rx) = bounded(0);
-        let sender = match self.inner.upgrade() {
-            None => return Err(tt::ExpansionError::Unknown("proc macro process is closed".into())),
+        let sender = match &self.inner {
+            None => return Err(tt::ExpansionError::Unknown("No sender is found.".to_string())),
             Some(it) => it,
         };
-        sender
-            .send(Task { req, result_tx })
-            .map_err(|_| tt::ExpansionError::Unknown("proc macro server crashed".into()))?;
 
+        let (result_tx, result_rx) = bounded(0);
+        let sender = match sender.upgrade() {
+            None => {
+                return Err(tt::ExpansionError::Unknown("Proc macro process is closed.".into()))
+            }
+            Some(it) => it,
+        };
+        sender.send(Task { req, result_tx }).unwrap();
         let res = result_rx
             .recv()
-            .map_err(|_| tt::ExpansionError::Unknown("proc macro server crashed".into()))?;
+            .map_err(|_| tt::ExpansionError::Unknown("Proc macro thread is closed.".into()))?;
 
         match res {
             Some(Response::Error(err)) => {
@@ -105,23 +109,32 @@ impl ProcMacroProcessSrv {
 }
 
 fn client_loop(task_rx: Receiver<Task>, mut process: Process) {
-    let (mut stdin, mut stdout) = process.stdio().expect("couldn't access child stdio");
+    let (mut stdin, mut stdout) = match process.stdio() {
+        None => return,
+        Some(it) => it,
+    };
+
+    for task in task_rx {
+        let Task { req, result_tx } = task;
 
-    for Task { req, result_tx } in task_rx {
         match send_request(&mut stdin, &mut stdout, req) {
             Ok(res) => result_tx.send(res).unwrap(),
             Err(_err) => {
-                log::error!(
-                    "proc macro server crashed, server process state: {:?}",
-                    process.child.try_wait()
-                );
                 let res = Response::Error(ResponseError {
                     code: ErrorCode::ServerErrorEnd,
-                    message: "proc macro server crashed".into(),
+                    message: "Server closed".into(),
                 });
                 result_tx.send(res.into()).unwrap();
-                // Exit the thread.
-                break;
+                // Restart the process
+                if process.restart().is_err() {
+                    break;
+                }
+                let stdio = match process.stdio() {
+                    None => break,
+                    Some(it) => it,
+                };
+                stdin = stdio.0;
+                stdout = stdio.1;
             }
         }
     }
@@ -133,6 +146,8 @@ struct Task {
 }
 
 struct Process {
+    path: PathBuf,
+    args: Vec<OsString>,
     child: Child,
 }
 
@@ -147,9 +162,15 @@ impl Process {
         path: PathBuf,
         args: impl IntoIterator<Item = impl AsRef<OsStr>>,
     ) -> io::Result<Process> {
-        let args: Vec<OsString> = args.into_iter().map(|s| s.as_ref().into()).collect();
+        let args = args.into_iter().map(|s| s.as_ref().into()).collect();
         let child = mk_child(&path, &args)?;
-        Ok(Process { child })
+        Ok(Process { path, args, child })
+    }
+
+    fn restart(&mut self) -> io::Result<()> {
+        let _ = self.child.kill();
+        self.child = mk_child(&self.path, &self.args)?;
+        Ok(())
     }
 
     fn stdio(&mut self) -> Option<(impl Write, impl BufRead)> {
diff --git a/crates/rust-analyzer/src/cli/diagnostics.rs b/crates/rust-analyzer/src/cli/diagnostics.rs
index 0090fd2c27..368f627ac4 100644
--- a/crates/rust-analyzer/src/cli/diagnostics.rs
+++ b/crates/rust-analyzer/src/cli/diagnostics.rs
@@ -6,25 +6,12 @@ use std::path::Path;
 use anyhow::anyhow;
 use rustc_hash::FxHashSet;
 
-use hir::{db::HirDatabase, Crate, Module};
+use hir::Crate;
 use ide::{DiagnosticsConfig, Severity};
 use ide_db::base_db::SourceDatabaseExt;
 
 use crate::cli::{load_cargo::load_cargo, Result};
 
-fn all_modules(db: &dyn HirDatabase) -> Vec<Module> {
-    let mut worklist: Vec<_> =
-        Crate::all(db).into_iter().map(|krate| krate.root_module(db)).collect();
-    let mut modules = Vec::new();
-
-    while let Some(module) = worklist.pop() {
-        modules.push(module);
-        worklist.extend(module.children(db));
-    }
-
-    modules
-}
-
 pub fn diagnostics(path: &Path, load_output_dirs: bool, with_proc_macro: bool) -> Result<()> {
     let (host, _vfs) = load_cargo(path, load_output_dirs, with_proc_macro)?;
     let db = host.raw_database();
@@ -33,12 +20,18 @@ pub fn diagnostics(path: &Path, load_output_dirs: bool, with_proc_macro: bool) -
     let mut found_error = false;
     let mut visited_files = FxHashSet::default();
 
-    let work = all_modules(db).into_iter().filter(|module| {
-        let file_id = module.definition_source(db).file_id.original_file(db);
+    let mut work = Vec::new();
+    let krates = Crate::all(db);
+    for krate in krates {
+        let module = krate.root_module(db);
+        let file_id = module.definition_source(db).file_id;
+        let file_id = file_id.original_file(db);
         let source_root = db.file_source_root(file_id);
         let source_root = db.source_root(source_root);
-        !source_root.is_library
-    });
+        if !source_root.is_library {
+            work.push(module);
+        }
+    }
 
     for module in work {
         let file_id = module.definition_source(db).file_id.original_file(db);
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index 55d46b09e7..f349b08103 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -506,7 +506,7 @@ impl GlobalState {
                         .write()
                         .0
                         .set_file_contents(path, Some(params.text_document.text.into_bytes()));
-                    this.maybe_update_diagnostics();
+                    this.update_file_notifications_on_threadpool();
                 }
                 Ok(())
             })?
@@ -616,23 +616,6 @@ impl GlobalState {
         Ok(())
     }
     fn update_file_notifications_on_threadpool(&mut self) {
-        self.maybe_update_diagnostics();
-        self.task_pool.handle.spawn_with_sender({
-            let snap = self.snapshot();
-            move |sender| {
-                snap.analysis
-                    .prime_caches(|progress| {
-                        sender.send(Task::PrimeCaches(progress)).unwrap();
-                    })
-                    .unwrap_or_else(|_: Canceled| {
-                        // Pretend that we're done, so that the progress bar is removed. Otherwise
-                        // the editor may complain about it already existing.
-                        sender.send(Task::PrimeCaches(PrimeCachesProgress::Finished)).unwrap()
-                    });
-            }
-        });
-    }
-    fn maybe_update_diagnostics(&mut self) {
         let subscriptions = self
             .mem_docs
             .keys()
@@ -661,5 +644,19 @@ impl GlobalState {
                 Task::Diagnostics(diagnostics)
             })
         }
+        self.task_pool.handle.spawn_with_sender({
+            let snap = self.snapshot();
+            move |sender| {
+                snap.analysis
+                    .prime_caches(|progress| {
+                        sender.send(Task::PrimeCaches(progress)).unwrap();
+                    })
+                    .unwrap_or_else(|_: Canceled| {
+                        // Pretend that we're done, so that the progress bar is removed. Otherwise
+                        // the editor may complain about it already existing.
+                        sender.send(Task::PrimeCaches(PrimeCachesProgress::Finished)).unwrap()
+                    });
+            }
+        });
     }
 }
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index 01eabe8526..2052b800cc 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -381,7 +381,6 @@ fn semantic_token_type_and_modifiers(
         HighlightTag::Function => lsp_types::SemanticTokenType::FUNCTION,
         HighlightTag::Generic => semantic_tokens::GENERIC,
         HighlightTag::Module => lsp_types::SemanticTokenType::NAMESPACE,
-        HighlightTag::Method => lsp_types::SemanticTokenType::METHOD,
         HighlightTag::Constant => {
             mods |= semantic_tokens::CONSTANT;
             mods |= lsp_types::SemanticTokenModifier::STATIC;
diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs
index 876659a2b7..cc09b77a59 100644
--- a/crates/syntax/src/ast/make.rs
+++ b/crates/syntax/src/ast/make.rs
@@ -171,8 +171,17 @@ pub fn expr_return() -> ast::Expr {
 pub fn expr_match(expr: ast::Expr, match_arm_list: ast::MatchArmList) -> ast::Expr {
     expr_from_text(&format!("match {} {}", expr, match_arm_list))
 }
-pub fn expr_if(condition: ast::Condition, then_branch: ast::BlockExpr) -> ast::Expr {
-    expr_from_text(&format!("if {} {}", condition, then_branch))
+pub fn expr_if(
+    condition: ast::Condition,
+    then_branch: ast::BlockExpr,
+    else_branch: Option<ast::ElseBranch>,
+) -> ast::Expr {
+    let else_branch = match else_branch {
+        Some(ast::ElseBranch::Block(block)) => format!("else {}", block),
+        Some(ast::ElseBranch::IfExpr(if_expr)) => format!("else {}", if_expr),
+        None => String::new(),
+    };
+    expr_from_text(&format!("if {} {} {}", condition, then_branch, else_branch))
 }
 pub fn expr_prefix(op: SyntaxKind, expr: ast::Expr) -> ast::Expr {
     let token = token(op);
diff --git a/docs/dev/README.md b/docs/dev/README.md
index ca324493f5..abb387e8ef 100644
--- a/docs/dev/README.md
+++ b/docs/dev/README.md
@@ -170,22 +170,6 @@ In general, API is centered around UI concerns -- the result of the call is what
 The results are 100% Rust specific though.
 Shout outs to LSP developers for popularizing the idea that "UI" is a good place to draw a boundary at.
 
-## LSP is sateless
-
-The protocol is implemented in the mostly stateless way.
-A good mental model is HTTP, which doesn't store per-client state, and instead relies on devices like cookies to maintain an illusion of state.
-If some action requires multi-step protocol, each step should be self-contained.
-
-A good example here is code action resolving process.
-TO display the lightbulb, we compute the list of code actions without computing edits.
-Figuring out the edit is done in a separate `codeAction/resolve` call.
-Rather than storing some `lazy_edit: Box<dyn FnOnce() -> Edit>` somewhere, we use a string ID of action to re-compute the list of actions during the resolve process.
-(See [this post](https://rust-analyzer.github.io/blog/2020/09/28/how-to-make-a-light-bulb.html) for more details.)
-The benefit here is that, generally speaking, the state of the world might change between `codeAction` and `codeAction` resolve requests, so any closure we store might become invalid.
-
-While we don't currently implement any complicated refactors with complex GUI, I imagine we'd use the same techniques for refactors.
-After clicking each "Next" button during refactor, the client would send all the info which server needs to re-recreate the context from scratch.
-
 ## CI
 
 CI does not test rust-analyzer, CI is a core part of rust-analyzer, and is maintained with above average standard of quality.


Test patch:
diff --git a/crates/assists/src/tests/generated.rs b/crates/assists/src/tests/generated.rs
index 8d50c8791a..853bde09ca 100644
--- a/crates/assists/src/tests/generated.rs
+++ b/crates/assists/src/tests/generated.rs
@@ -889,6 +889,34 @@ fn compute() -> Option<i32> { None }
     )
 }
 
+#[test]
+fn doctest_replace_match_with_if_let() {
+    check_doc_test(
+        "replace_match_with_if_let",
+        r#####"
+enum Action { Move { distance: u32 }, Stop }
+
+fn handle(action: Action) {
+    <|>match action {
+        Action::Move { distance } => foo(distance),
+        _ => bar(),
+    }
+}
+"#####,
+        r#####"
+enum Action { Move { distance: u32 }, Stop }
+
+fn handle(action: Action) {
+    if let Action::Move { distance } = action {
+        foo(distance)
+    } else {
+        bar()
+    }
+}
+"#####,
+    )
+}
+
 #[test]
 fn doctest_replace_qualified_name_with_use() {
     check_doc_test(
diff --git a/crates/hir_def/src/body/tests.rs b/crates/hir_def/src/body/tests.rs
index 6dba9817d4..baf1179f1f 100644
--- a/crates/hir_def/src/body/tests.rs
+++ b/crates/hir_def/src/body/tests.rs
@@ -78,41 +78,21 @@ fn f() {
 fn macro_diag_builtin() {
     check_diagnostics(
         r#"
-#[rustc_builtin_macro]
-macro_rules! env {}
-
-#[rustc_builtin_macro]
-macro_rules! include {}
-
-#[rustc_builtin_macro]
-macro_rules! compile_error {}
-
-#[rustc_builtin_macro]
-macro_rules! format_args {
-    () => {}
-}
-
 fn f() {
     // Test a handful of built-in (eager) macros:
 
     include!(invalid);
-  //^^^^^^^^^^^^^^^^^ could not convert tokens
+  //^^^^^^^^^^^^^^^^^ failed to parse or resolve macro invocation
     include!("does not exist");
-  //^^^^^^^^^^^^^^^^^^^^^^^^^^ could not convert tokens
+  //^^^^^^^^^^^^^^^^^^^^^^^^^^ failed to parse or resolve macro invocation
 
     env!(invalid);
-  //^^^^^^^^^^^^^ could not convert tokens
-
-    env!("OUT_DIR");
-  //^^^^^^^^^^^^^^^ `OUT_DIR` not set, enable "load out dirs from check" to fix
-
-    compile_error!("compile_error works");
-  //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ compile_error works
+  //^^^^^^^^^^^^^ failed to parse or resolve macro invocation
 
     // Lazy:
 
     format_args!();
-  //^^^^^^^^^^^^^^ no rule matches input tokens
+  //^^^^^^^^^^^^^^ failed to parse or resolve macro invocation
 }
         "#,
     );
diff --git a/crates/hir_ty/src/tests/method_resolution.rs b/crates/hir_ty/src/tests/method_resolution.rs
index a6a54e542a..0f17ff1517 100644
--- a/crates/hir_ty/src/tests/method_resolution.rs
+++ b/crates/hir_ty/src/tests/method_resolution.rs
@@ -1087,22 +1087,3 @@ fn method_resolution_foreign_opaque_type() {
         "#]],
     );
 }
-
-#[test]
-fn method_with_allocator_box_self_type() {
-    check_types(
-        r#"
-struct Slice<T> {}
-struct Box<T, A> {}
-
-impl<T> Slice<T> {
-    pub fn into_vec<A>(self: Box<Self, A>) { }
-}
-
-fn main() {
-    let foo: Slice<u32>;
-    (foo.into_vec()); // we don't actually support arbitrary self types, but we shouldn't crash at least
-} //^ {unknown}
-"#,
-    );
-}
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html b/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html
index 6fb606a472..cd80d72b79 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html
@@ -40,17 +40,17 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 <span class="keyword">struct</span> <span class="struct declaration">foo</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">pub</span> <span class="keyword">fn</span> <span class="method declaration static">is_static</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span>
-    <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="method declaration">is_not_static</span><span class="punctuation">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span>
+    <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration static">is_static</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span>
+    <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration">is_not_static</span><span class="punctuation">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span>
 <span class="punctuation">}</span>
 
 <span class="keyword">trait</span> <span class="trait declaration">t</span> <span class="punctuation">{</span>
-    <span class="keyword">fn</span> <span class="method declaration static">t_is_static</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span>
-    <span class="keyword">fn</span> <span class="method declaration">t_is_not_static</span><span class="punctuation">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span>
+    <span class="keyword">fn</span> <span class="function declaration static">t_is_static</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">t_is_not_static</span><span class="punctuation">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span>
 <span class="punctuation">}</span>
 
 <span class="keyword">impl</span> <span class="trait">t</span> <span class="keyword">for</span> <span class="struct">foo</span> <span class="punctuation">{</span>
-    <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="method declaration static">is_static</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span>
-    <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="method declaration">is_not_static</span><span class="punctuation">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span>
+    <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration static">is_static</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span>
+    <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration">is_not_static</span><span class="punctuation">(</span><span class="operator">&</span><span class="self_keyword">self</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/highlight_doctest.html b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
index 396fd46fbd..d79fa6dca8 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
@@ -53,7 +53,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
     <span class="comment documentation">/// #</span><span class="generic injected"> </span><span class="attribute attribute injected">#</span><span class="attribute attribute injected">!</span><span class="attribute attribute injected">[</span><span class="function attribute injected">allow</span><span class="punctuation attribute injected">(</span><span class="attribute attribute injected">unused_mut</span><span class="punctuation attribute injected">)</span><span class="attribute attribute injected">]</span>
     <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="generic injected"> </span><span class="keyword injected">mut</span><span class="generic injected"> </span><span class="variable declaration injected mutable">foo</span><span class="punctuation injected">:</span><span class="generic injected"> </span><span class="struct 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="punctuation injected">
 </span>    <span class="comment documentation">/// ```</span>
-    <span class="keyword">pub</span> <span class="keyword">const</span> <span class="keyword">fn</span> <span class="method declaration static">new</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="operator">-&gt;</span> <span class="struct">Foo</span> <span class="punctuation">{</span>
+    <span class="keyword">pub</span> <span class="keyword">const</span> <span class="keyword">fn</span> <span class="function declaration static">new</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="operator">-&gt;</span> <span class="struct">Foo</span> <span class="punctuation">{</span>
         <span class="struct">Foo</span> <span class="punctuation">{</span> <span class="field">bar</span><span class="punctuation">:</span> <span class="bool_literal">true</span> <span class="punctuation">}</span>
     <span class="punctuation">}</span>
 
@@ -87,7 +87,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
     <span class="comment documentation">/// ```sh</span>
     <span class="comment documentation">/// echo 1</span>
     <span class="comment documentation">/// ```</span>
-    <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="method declaration">foo</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">bool</span> <span class="punctuation">{</span>
+    <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration">foo</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">bool</span> <span class="punctuation">{</span>
         <span class="bool_literal">true</span>
     <span class="punctuation">}</span>
 <span class="punctuation">}</span>
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html b/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html
index 31daf2bd0b..57c178916f 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html
@@ -40,7 +40,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 <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="function">fixture</span><span class="punctuation">(</span><span class="string_literal">r#"</span>
         <span class="keyword">trait</span> <span class="trait declaration">Foo</span> <span class="punctuation">{</span>
-            <span class="keyword">fn</span> <span class="method declaration static">foo</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span>
+            <span class="keyword">fn</span> <span class="function declaration static">foo</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span>
                 <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"2 + 2 = {}"</span><span class="punctuation">,</span> <span class="numeric_literal">4</span><span class="punctuation">)</span><span class="punctuation">;</span>
             <span class="punctuation">}</span>
         <span class="punctuation">}</span><span class="string_literal">"#</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 67ec73f159..1d05b77131 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
@@ -45,7 +45,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 <span class="keyword">struct</span> <span class="struct declaration">HasUnsafeFn</span><span class="punctuation">;</span>
 
 <span class="keyword">impl</span> <span class="struct">HasUnsafeFn</span> <span class="punctuation">{</span>
-    <span class="keyword unsafe">unsafe</span> <span class="keyword">fn</span> <span class="method declaration unsafe">unsafe_method</span><span class="punctuation">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span>
+    <span class="keyword unsafe">unsafe</span> <span class="keyword">fn</span> <span class="function declaration unsafe">unsafe_method</span><span class="punctuation">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span>
 <span class="punctuation">}</span>
 
 <span class="keyword">struct</span> <span class="struct declaration">TypeForStaticMut</span> <span class="punctuation">{</span>
@@ -60,11 +60,11 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 <span class="punctuation">}</span>
 
 <span class="keyword">trait</span> <span class="trait declaration">DoTheAutoref</span> <span class="punctuation">{</span>
-    <span class="keyword">fn</span> <span class="method declaration">calls_autoref</span><span class="punctuation">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="punctuation">)</span><span class="punctuation">;</span>
+    <span class="keyword">fn</span> <span class="function declaration">calls_autoref</span><span class="punctuation">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="punctuation">)</span><span class="punctuation">;</span>
 <span class="punctuation">}</span>
 
 <span class="keyword">impl</span> <span class="trait">DoTheAutoref</span> <span class="keyword">for</span> <span class="builtin_type">u16</span> <span class="punctuation">{</span>
-    <span class="keyword">fn</span> <span class="method declaration">calls_autoref</span><span class="punctuation">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span>
+    <span class="keyword">fn</span> <span class="function declaration">calls_autoref</span><span class="punctuation">(</span><span class="operator">&</span><span class="self_keyword">self</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">main</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span>
@@ -78,7 +78,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
             <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="method unsafe">unsafe_method</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="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>
@@ -94,6 +94,6 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
         <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="method unsafe">calls_autoref</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</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="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 a3b4f20e87..15fbf2ce39 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlighting.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlighting.html
@@ -62,25 +62,25 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 <span class="punctuation">}</span>
 
 <span class="keyword">trait</span> <span class="trait declaration">Bar</span> <span class="punctuation">{</span>
-    <span class="keyword">fn</span> <span class="method 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="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="punctuation">}</span>
 
 <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="method 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="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="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="method 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="method consuming">baz</span><span class="punctuation">(</span><span class="self_keyword mutable consuming">self</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="punctuation">}</span>
 
-    <span class="keyword">fn</span> <span class="method 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="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="punctuation">}</span>
 
-    <span class="keyword">fn</span> <span class="method 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="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="punctuation">}</span>
 <span class="punctuation">}</span>
@@ -91,15 +91,15 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 <span class="punctuation">}</span>
 
 <span class="keyword">impl</span> <span class="struct">FooCopy</span> <span class="punctuation">{</span>
-    <span class="keyword">fn</span> <span class="method 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="method">baz</span><span class="punctuation">(</span><span class="self_keyword">self</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="punctuation">}</span>
 
-    <span class="keyword">fn</span> <span class="method 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="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="punctuation">}</span>
 
-    <span class="keyword">fn</span> <span class="method 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="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="punctuation">}</span>
 <span class="punctuation">}</span>
@@ -175,17 +175,17 @@ 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="method">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="method 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="method 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="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="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="method">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="method 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="method">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="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="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="method">baz</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>
 
     <span class="keyword">let</span> <span class="variable declaration">baz</span> <span class="operator">=</span> <span class="numeric_literal">-</span><span class="numeric_literal">42</span><span class="punctuation">;</span>
     <span class="keyword">let</span> <span class="variable declaration">baz</span> <span class="operator">=</span> <span class="operator">-</span><span class="variable">baz</span><span class="punctuation">;</span>
@@ -200,7 +200,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 <span class="keyword">use</span> <span class="enum">Option</span><span class="operator">::</span><span class="punctuation">*</span><span class="punctuation">;</span>
 
 <span class="keyword">impl</span><span class="punctuation">&lt;</span><span class="type_param declaration">T</span><span class="punctuation">&gt;</span> <span class="enum">Option</span><span class="punctuation">&lt;</span><span class="type_param">T</span><span class="punctuation">&gt;</span> <span class="punctuation">{</span>
-    <span class="keyword">fn</span> <span class="method declaration">and</span><span class="punctuation">&lt;</span><span class="type_param declaration">U</span><span class="punctuation">&gt;</span><span class="punctuation">(</span><span class="self_keyword">self</span><span class="punctuation">,</span> <span class="value_param declaration">other</span><span class="punctuation">:</span> <span class="enum">Option</span><span class="punctuation">&lt;</span><span class="type_param">U</span><span class="punctuation">&gt;</span><span class="punctuation">)</span> <span class="operator">-&gt;</span> <span class="enum">Option</span><span class="punctuation">&lt;</span><span class="punctuation">(</span><span class="type_param">T</span><span class="punctuation">,</span> <span class="type_param">U</span><span class="punctuation">)</span><span class="punctuation">&gt;</span> <span class="punctuation">{</span>
+    <span class="keyword">fn</span> <span class="function declaration">and</span><span class="punctuation">&lt;</span><span class="type_param declaration">U</span><span class="punctuation">&gt;</span><span class="punctuation">(</span><span class="self_keyword">self</span><span class="punctuation">,</span> <span class="value_param declaration">other</span><span class="punctuation">:</span> <span class="enum">Option</span><span class="punctuation">&lt;</span><span class="type_param">U</span><span class="punctuation">&gt;</span><span class="punctuation">)</span> <span class="operator">-&gt;</span> <span class="enum">Option</span><span class="punctuation">&lt;</span><span class="punctuation">(</span><span class="type_param">T</span><span class="punctuation">,</span> <span class="type_param">U</span><span class="punctuation">)</span><span class="punctuation">&gt;</span> <span class="punctuation">{</span>
         <span class="keyword control">match</span> <span class="value_param">other</span> <span class="punctuation">{</span>
             <span class="enum_variant">None</span> <span class="operator">=&gt;</span> <span class="macro">unimplemented!</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">,</span>
             <span class="variable declaration">Nope</span> <span class="operator">=&gt;</span> <span class="variable">Nope</span><span class="punctuation">,</span>
diff --git a/crates/ide_db/src/call_info/tests.rs b/crates/ide_db/src/call_info/tests.rs
deleted file mode 100644
index 9335aeaa5f..0000000000
--- a/crates/ide_db/src/call_info/tests.rs
+++ /dev/null
@@ -1,523 +0,0 @@
-use crate::RootDatabase;
-use base_db::{fixture::ChangeFixture, FilePosition};
-use expect_test::{expect, Expect};
-use test_utils::{mark, RangeOrOffset};
-
-/// Creates analysis from a multi-file fixture, returns positions marked with <|>.
-pub(crate) fn position(ra_fixture: &str) -> (RootDatabase, FilePosition) {
-    let change_fixture = ChangeFixture::parse(ra_fixture);
-    let mut database = RootDatabase::default();
-    database.apply_change(change_fixture.change);
-    let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker (<|>)");
-    let offset = match range_or_offset {
-        RangeOrOffset::Range(_) => panic!(),
-        RangeOrOffset::Offset(it) => it,
-    };
-    (database, FilePosition { file_id, offset })
-}
-
-fn check(ra_fixture: &str, expect: Expect) {
-    let (db, position) = position(ra_fixture);
-    let call_info = crate::call_info::call_info(&db, position);
-    let actual = match call_info {
-        Some(call_info) => {
-            let docs = match &call_info.doc {
-                None => "".to_string(),
-                Some(docs) => format!("{}\n------\n", docs.as_str()),
-            };
-            let params = call_info
-                .parameter_labels()
-                .enumerate()
-                .map(|(i, param)| {
-                    if Some(i) == call_info.active_parameter {
-                        format!("<{}>", param)
-                    } else {
-                        param.to_string()
-                    }
-                })
-                .collect::<Vec<_>>()
-                .join(", ");
-            format!("{}{}\n({})\n", docs, call_info.signature, params)
-        }
-        None => String::new(),
-    };
-    expect.assert_eq(&actual);
-}
-
-#[test]
-fn test_fn_signature_two_args() {
-    check(
-        r#"
-fn foo(x: u32, y: u32) -> u32 {x + y}
-fn bar() { foo(<|>3, ); }
-"#,
-        expect![[r#"
-                fn foo(x: u32, y: u32) -> u32
-                (<x: u32>, y: u32)
-            "#]],
-    );
-    check(
-        r#"
-fn foo(x: u32, y: u32) -> u32 {x + y}
-fn bar() { foo(3<|>, ); }
-"#,
-        expect![[r#"
-                fn foo(x: u32, y: u32) -> u32
-                (<x: u32>, y: u32)
-            "#]],
-    );
-    check(
-        r#"
-fn foo(x: u32, y: u32) -> u32 {x + y}
-fn bar() { foo(3,<|> ); }
-"#,
-        expect![[r#"
-                fn foo(x: u32, y: u32) -> u32
-                (x: u32, <y: u32>)
-            "#]],
-    );
-    check(
-        r#"
-fn foo(x: u32, y: u32) -> u32 {x + y}
-fn bar() { foo(3, <|>); }
-"#,
-        expect![[r#"
-                fn foo(x: u32, y: u32) -> u32
-                (x: u32, <y: u32>)
-            "#]],
-    );
-}
-
-#[test]
-fn test_fn_signature_two_args_empty() {
-    check(
-        r#"
-fn foo(x: u32, y: u32) -> u32 {x + y}
-fn bar() { foo(<|>); }
-"#,
-        expect![[r#"
-                fn foo(x: u32, y: u32) -> u32
-                (<x: u32>, y: u32)
-            "#]],
-    );
-}
-
-#[test]
-fn test_fn_signature_two_args_first_generics() {
-    check(
-        r#"
-fn foo<T, U: Copy + Display>(x: T, y: U) -> u32
-    where T: Copy + Display, U: Debug
-{ x + y }
-
-fn bar() { foo(<|>3, ); }
-"#,
-        expect![[r#"
-                fn foo(x: i32, y: {unknown}) -> u32
-                (<x: i32>, y: {unknown})
-            "#]],
-    );
-}
-
-#[test]
-fn test_fn_signature_no_params() {
-    check(
-        r#"
-fn foo<T>() -> T where T: Copy + Display {}
-fn bar() { foo(<|>); }
-"#,
-        expect![[r#"
-                fn foo() -> {unknown}
-                ()
-            "#]],
-    );
-}
-
-#[test]
-fn test_fn_signature_for_impl() {
-    check(
-        r#"
-struct F;
-impl F { pub fn new() { } }
-fn bar() {
-    let _ : F = F::new(<|>);
-}
-"#,
-        expect![[r#"
-                fn new()
-                ()
-            "#]],
-    );
-}
-
-#[test]
-fn test_fn_signature_for_method_self() {
-    check(
-        r#"
-struct S;
-impl S { pub fn do_it(&self) {} }
-
-fn bar() {
-    let s: S = S;
-    s.do_it(<|>);
-}
-"#,
-        expect![[r#"
-                fn do_it(&self)
-                ()
-            "#]],
-    );
-}
-
-#[test]
-fn test_fn_signature_for_method_with_arg() {
-    check(
-        r#"
-struct S;
-impl S {
-    fn foo(&self, x: i32) {}
-}
-
-fn main() { S.foo(<|>); }
-"#,
-        expect![[r#"
-                fn foo(&self, x: i32)
-                (<x: i32>)
-            "#]],
-    );
-}
-
-#[test]
-fn test_fn_signature_for_method_with_arg_as_assoc_fn() {
-    check(
-        r#"
-struct S;
-impl S {
-    fn foo(&self, x: i32) {}
-}
-
-fn main() { S::foo(<|>); }
-"#,
-        expect![[r#"
-                fn foo(self: &S, x: i32)
-                (<self: &S>, x: i32)
-            "#]],
-    );
-}
-
-#[test]
-fn test_fn_signature_with_docs_simple() {
-    check(
-        r#"
-/// test
-// non-doc-comment
-fn foo(j: u32) -> u32 {
-    j
-}
-
-fn bar() {
-    let _ = foo(<|>);
-}
-"#,
-        expect![[r#"
-                test
-                ------
-                fn foo(j: u32) -> u32
-                (<j: u32>)
-            "#]],
-    );
-}
-
-#[test]
-fn test_fn_signature_with_docs() {
-    check(
-        r#"
-/// Adds one to the number given.
-///
-/// # Examples
-///
-/// ```
-/// let five = 5;
-///
-/// assert_eq!(6, my_crate::add_one(5));
-/// ```
-pub fn add_one(x: i32) -> i32 {
-    x + 1
-}
-
-pub fn do() {
-    add_one(<|>
-}"#,
-        expect![[r##"
-                Adds one to the number given.
-
-                # Examples
-
-                ```
-                let five = 5;
-
-                assert_eq!(6, my_crate::add_one(5));
-                ```
-                ------
-                fn add_one(x: i32) -> i32
-                (<x: i32>)
-            "##]],
-    );
-}
-
-#[test]
-fn test_fn_signature_with_docs_impl() {
-    check(
-        r#"
-struct addr;
-impl addr {
-    /// Adds one to the number given.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// let five = 5;
-    ///
-    /// assert_eq!(6, my_crate::add_one(5));
-    /// ```
-    pub fn add_one(x: i32) -> i32 {
-        x + 1
-    }
-}
-
-pub fn do_it() {
-    addr {};
-    addr::add_one(<|>);
-}
-"#,
-        expect![[r##"
-                Adds one to the number given.
-
-                # Examples
-
-                ```
-                let five = 5;
-
-                assert_eq!(6, my_crate::add_one(5));
-                ```
-                ------
-                fn add_one(x: i32) -> i32
-                (<x: i32>)
-            "##]],
-    );
-}
-
-#[test]
-fn test_fn_signature_with_docs_from_actix() {
-    check(
-        r#"
-struct WriteHandler<E>;
-
-impl<E> WriteHandler<E> {
-    /// Method is called when writer emits error.
-    ///
-    /// If this method returns `ErrorAction::Continue` writer processing
-    /// continues otherwise stream processing stops.
-    fn error(&mut self, err: E, ctx: &mut Self::Context) -> Running {
-        Running::Stop
-    }
-
-    /// Method is called when writer finishes.
-    ///
-    /// By default this method stops actor's `Context`.
-    fn finished(&mut self, ctx: &mut Self::Context) {
-        ctx.stop()
-    }
-}
-
-pub fn foo(mut r: WriteHandler<()>) {
-    r.finished(<|>);
-}
-"#,
-        expect![[r#"
-                Method is called when writer finishes.
-
-                By default this method stops actor's `Context`.
-                ------
-                fn finished(&mut self, ctx: &mut {unknown})
-                (<ctx: &mut {unknown}>)
-            "#]],
-    );
-}
-
-#[test]
-fn call_info_bad_offset() {
-    mark::check!(call_info_bad_offset);
-    check(
-        r#"
-fn foo(x: u32, y: u32) -> u32 {x + y}
-fn bar() { foo <|> (3, ); }
-"#,
-        expect![[""]],
-    );
-}
-
-#[test]
-fn test_nested_method_in_lambda() {
-    check(
-        r#"
-struct Foo;
-impl Foo { fn bar(&self, _: u32) { } }
-
-fn bar(_: u32) { }
-
-fn main() {
-    let foo = Foo;
-    std::thread::spawn(move || foo.bar(<|>));
-}
-"#,
-        expect![[r#"
-                fn bar(&self, _: u32)
-                (<_: u32>)
-            "#]],
-    );
-}
-
-#[test]
-fn works_for_tuple_structs() {
-    check(
-        r#"
-/// A cool tuple struct
-struct S(u32, i32);
-fn main() {
-    let s = S(0, <|>);
-}
-"#,
-        expect![[r#"
-                A cool tuple struct
-                ------
-                struct S(u32, i32)
-                (u32, <i32>)
-            "#]],
-    );
-}
-
-#[test]
-fn generic_struct() {
-    check(
-        r#"
-struct S<T>(T);
-fn main() {
-    let s = S(<|>);
-}
-"#,
-        expect![[r#"
-                struct S({unknown})
-                (<{unknown}>)
-            "#]],
-    );
-}
-
-#[test]
-fn works_for_enum_variants() {
-    check(
-        r#"
-enum E {
-    /// A Variant
-    A(i32),
-    /// Another
-    B,
-    /// And C
-    C { a: i32, b: i32 }
-}
-
-fn main() {
-    let a = E::A(<|>);
-}
-"#,
-        expect![[r#"
-                A Variant
-                ------
-                enum E::A(i32)
-                (<i32>)
-            "#]],
-    );
-}
-
-#[test]
-fn cant_call_struct_record() {
-    check(
-        r#"
-struct S { x: u32, y: i32 }
-fn main() {
-    let s = S(<|>);
-}
-"#,
-        expect![[""]],
-    );
-}
-
-#[test]
-fn cant_call_enum_record() {
-    check(
-        r#"
-enum E {
-    /// A Variant
-    A(i32),
-    /// Another
-    B,
-    /// And C
-    C { a: i32, b: i32 }
-}
-
-fn main() {
-    let a = E::C(<|>);
-}
-"#,
-        expect![[""]],
-    );
-}
-
-#[test]
-fn fn_signature_for_call_in_macro() {
-    check(
-        r#"
-macro_rules! id { ($($tt:tt)*) => { $($tt)* } }
-fn foo() { }
-id! {
-    fn bar() { foo(<|>); }
-}
-"#,
-        expect![[r#"
-                fn foo()
-                ()
-            "#]],
-    );
-}
-
-#[test]
-fn call_info_for_lambdas() {
-    check(
-        r#"
-struct S;
-fn foo(s: S) -> i32 { 92 }
-fn main() {
-    (|s| foo(s))(<|>)
-}
-        "#,
-        expect![[r#"
-                (S) -> i32
-                (<S>)
-            "#]],
-    )
-}
-
-#[test]
-fn call_info_for_fn_ptr() {
-    check(
-        r#"
-fn main(f: fn(i32, f64) -> char) {
-    f(0, <|>)
-}
-        "#,
-        expect![[r#"
-                (i32, f64) -> char
-                (i32, <f64>)
-            "#]],
-    )
-}
diff --git a/crates/ide_db/src/helpers/insert_use/tests.rs b/crates/ide_db/src/helpers/insert_use/tests.rs
deleted file mode 100644
index 86bfa5b41f..0000000000
--- a/crates/ide_db/src/helpers/insert_use/tests.rs
+++ /dev/null
@@ -1,620 +0,0 @@
-use super::*;
-
-use test_utils::assert_eq_text;
-
-#[test]
-fn insert_existing() {
-    check_full("std::fs", "use std::fs;", "use std::fs;")
-}
-
-#[test]
-fn insert_start() {
-    check_none(
-        "std::bar::AA",
-        r"
-use std::bar::B;
-use std::bar::D;
-use std::bar::F;
-use std::bar::G;",
-        r"
-use std::bar::AA;
-use std::bar::B;
-use std::bar::D;
-use std::bar::F;
-use std::bar::G;",
-    )
-}
-
-#[test]
-fn insert_start_indent() {
-    mark::check!(insert_use_indent_after);
-    check_none(
-        "std::bar::AA",
-        r"
-    use std::bar::B;
-    use std::bar::D;",
-        r"
-    use std::bar::AA;
-    use std::bar::B;
-    use std::bar::D;",
-    )
-}
-
-#[test]
-fn insert_middle() {
-    check_none(
-        "std::bar::EE",
-        r"
-use std::bar::A;
-use std::bar::D;
-use std::bar::F;
-use std::bar::G;",
-        r"
-use std::bar::A;
-use std::bar::D;
-use std::bar::EE;
-use std::bar::F;
-use std::bar::G;",
-    )
-}
-
-#[test]
-fn insert_middle_indent() {
-    check_none(
-        "std::bar::EE",
-        r"
-    use std::bar::A;
-    use std::bar::D;
-    use std::bar::F;
-    use std::bar::G;",
-        r"
-    use std::bar::A;
-    use std::bar::D;
-    use std::bar::EE;
-    use std::bar::F;
-    use std::bar::G;",
-    )
-}
-
-#[test]
-fn insert_end() {
-    check_none(
-        "std::bar::ZZ",
-        r"
-use std::bar::A;
-use std::bar::D;
-use std::bar::F;
-use std::bar::G;",
-        r"
-use std::bar::A;
-use std::bar::D;
-use std::bar::F;
-use std::bar::G;
-use std::bar::ZZ;",
-    )
-}
-
-#[test]
-fn insert_end_indent() {
-    mark::check!(insert_use_indent_before);
-    check_none(
-        "std::bar::ZZ",
-        r"
-    use std::bar::A;
-    use std::bar::D;
-    use std::bar::F;
-    use std::bar::G;",
-        r"
-    use std::bar::A;
-    use std::bar::D;
-    use std::bar::F;
-    use std::bar::G;
-    use std::bar::ZZ;",
-    )
-}
-
-#[test]
-fn insert_middle_nested() {
-    check_none(
-        "std::bar::EE",
-        r"
-use std::bar::A;
-use std::bar::{D, Z}; // example of weird imports due to user
-use std::bar::F;
-use std::bar::G;",
-        r"
-use std::bar::A;
-use std::bar::EE;
-use std::bar::{D, Z}; // example of weird imports due to user
-use std::bar::F;
-use std::bar::G;",
-    )
-}
-
-#[test]
-fn insert_middle_groups() {
-    check_none(
-        "foo::bar::GG",
-        r"
-    use std::bar::A;
-    use std::bar::D;
-
-    use foo::bar::F;
-    use foo::bar::H;",
-        r"
-    use std::bar::A;
-    use std::bar::D;
-
-    use foo::bar::F;
-    use foo::bar::GG;
-    use foo::bar::H;",
-    )
-}
-
-#[test]
-fn insert_first_matching_group() {
-    check_none(
-        "foo::bar::GG",
-        r"
-    use foo::bar::A;
-    use foo::bar::D;
-
-    use std;
-
-    use foo::bar::F;
-    use foo::bar::H;",
-        r"
-    use foo::bar::A;
-    use foo::bar::D;
-    use foo::bar::GG;
-
-    use std;
-
-    use foo::bar::F;
-    use foo::bar::H;",
-    )
-}
-
-#[test]
-fn insert_missing_group_std() {
-    check_none(
-        "std::fmt",
-        r"
-    use foo::bar::A;
-    use foo::bar::D;",
-        r"
-    use std::fmt;
-
-    use foo::bar::A;
-    use foo::bar::D;",
-    )
-}
-
-#[test]
-fn insert_missing_group_self() {
-    check_none(
-        "self::fmt",
-        r"
-use foo::bar::A;
-use foo::bar::D;",
-        r"
-use foo::bar::A;
-use foo::bar::D;
-
-use self::fmt;",
-    )
-}
-
-#[test]
-fn insert_no_imports() {
-    check_full(
-        "foo::bar",
-        "fn main() {}",
-        r"use foo::bar;
-
-fn main() {}",
-    )
-}
-
-#[test]
-fn insert_empty_file() {
-    // empty files will get two trailing newlines
-    // this is due to the test case insert_no_imports above
-    check_full(
-        "foo::bar",
-        "",
-        r"use foo::bar;
-
-",
-    )
-}
-
-#[test]
-fn insert_empty_module() {
-    mark::check!(insert_use_no_indent_after);
-    check(
-        "foo::bar",
-        "mod x {}",
-        r"{
-    use foo::bar;
-}",
-        None,
-        true,
-    )
-}
-
-#[test]
-fn insert_after_inner_attr() {
-    check_full(
-        "foo::bar",
-        r"#![allow(unused_imports)]",
-        r"#![allow(unused_imports)]
-
-use foo::bar;",
-    )
-}
-
-#[test]
-fn insert_after_inner_attr2() {
-    check_full(
-        "foo::bar",
-        r"#![allow(unused_imports)]
-
-#![no_std]
-fn main() {}",
-        r"#![allow(unused_imports)]
-
-#![no_std]
-
-use foo::bar;
-fn main() {}",
-    );
-}
-
-#[test]
-fn inserts_after_single_line_inner_comments() {
-    check_none(
-        "foo::bar::Baz",
-        "//! Single line inner comments do not allow any code before them.",
-        r#"//! Single line inner comments do not allow any code before them.
-
-use foo::bar::Baz;"#,
-    );
-}
-
-#[test]
-fn inserts_after_multiline_inner_comments() {
-    check_none(
-        "foo::bar::Baz",
-        r#"/*! Multiline inner comments do not allow any code before them. */
-
-/*! Still an inner comment, cannot place any code before. */
-fn main() {}"#,
-        r#"/*! Multiline inner comments do not allow any code before them. */
-
-/*! Still an inner comment, cannot place any code before. */
-
-use foo::bar::Baz;
-fn main() {}"#,
-    )
-}
-
-#[test]
-fn inserts_after_all_inner_items() {
-    check_none(
-        "foo::bar::Baz",
-        r#"#![allow(unused_imports)]
-/*! Multiline line comment 2 */
-
-
-//! Single line comment 1
-#![no_std]
-//! Single line comment 2
-fn main() {}"#,
-        r#"#![allow(unused_imports)]
-/*! Multiline line comment 2 */
-
-
-//! Single line comment 1
-#![no_std]
-//! Single line comment 2
-
-use foo::bar::Baz;
-fn main() {}"#,
-    )
-}
-
-#[test]
-fn merge_groups() {
-    check_last("std::io", r"use std::fmt;", r"use std::{fmt, io};")
-}
-
-#[test]
-fn merge_groups_last() {
-    check_last(
-        "std::io",
-        r"use std::fmt::{Result, Display};",
-        r"use std::fmt::{Result, Display};
-use std::io;",
-    )
-}
-
-#[test]
-fn merge_last_into_self() {
-    check_last("foo::bar::baz", r"use foo::bar;", r"use foo::bar::{self, baz};");
-}
-
-#[test]
-fn merge_groups_full() {
-    check_full(
-        "std::io",
-        r"use std::fmt::{Result, Display};",
-        r"use std::{fmt::{Result, Display}, io};",
-    )
-}
-
-#[test]
-fn merge_groups_long_full() {
-    check_full("std::foo::bar::Baz", r"use std::foo::bar::Qux;", r"use std::foo::bar::{Baz, Qux};")
-}
-
-#[test]
-fn merge_groups_long_last() {
-    check_last("std::foo::bar::Baz", r"use std::foo::bar::Qux;", r"use std::foo::bar::{Baz, Qux};")
-}
-
-#[test]
-fn merge_groups_long_full_list() {
-    check_full(
-        "std::foo::bar::Baz",
-        r"use std::foo::bar::{Qux, Quux};",
-        r"use std::foo::bar::{Baz, Quux, Qux};",
-    )
-}
-
-#[test]
-fn merge_groups_long_last_list() {
-    check_last(
-        "std::foo::bar::Baz",
-        r"use std::foo::bar::{Qux, Quux};",
-        r"use std::foo::bar::{Baz, Quux, Qux};",
-    )
-}
-
-#[test]
-fn merge_groups_long_full_nested() {
-    check_full(
-        "std::foo::bar::Baz",
-        r"use std::foo::bar::{Qux, quux::{Fez, Fizz}};",
-        r"use std::foo::bar::{Baz, Qux, quux::{Fez, Fizz}};",
-    )
-}
-
-#[test]
-fn merge_groups_long_last_nested() {
-    check_last(
-        "std::foo::bar::Baz",
-        r"use std::foo::bar::{Qux, quux::{Fez, Fizz}};",
-        r"use std::foo::bar::Baz;
-use std::foo::bar::{Qux, quux::{Fez, Fizz}};",
-    )
-}
-
-#[test]
-fn merge_groups_full_nested_deep() {
-    check_full(
-        "std::foo::bar::quux::Baz",
-        r"use std::foo::bar::{Qux, quux::{Fez, Fizz}};",
-        r"use std::foo::bar::{Qux, quux::{Baz, Fez, Fizz}};",
-    )
-}
-
-#[test]
-fn merge_groups_full_nested_long() {
-    check_full(
-        "std::foo::bar::Baz",
-        r"use std::{foo::bar::Qux};",
-        r"use std::{foo::bar::{Baz, Qux}};",
-    );
-}
-
-#[test]
-fn merge_groups_last_nested_long() {
-    check_full(
-        "std::foo::bar::Baz",
-        r"use std::{foo::bar::Qux};",
-        r"use std::{foo::bar::{Baz, Qux}};",
-    );
-}
-
-#[test]
-fn merge_groups_skip_pub() {
-    check_full(
-        "std::io",
-        r"pub use std::fmt::{Result, Display};",
-        r"pub use std::fmt::{Result, Display};
-use std::io;",
-    )
-}
-
-#[test]
-fn merge_groups_skip_pub_crate() {
-    check_full(
-        "std::io",
-        r"pub(crate) use std::fmt::{Result, Display};",
-        r"pub(crate) use std::fmt::{Result, Display};
-use std::io;",
-    )
-}
-
-#[test]
-#[ignore] // FIXME: Support this
-fn split_out_merge() {
-    check_last(
-        "std::fmt::Result",
-        r"use std::{fmt, io};",
-        r"use std::fmt::{self, Result};
-use std::io;",
-    )
-}
-
-#[test]
-fn merge_into_module_import() {
-    check_full("std::fmt::Result", r"use std::{fmt, io};", r"use std::{fmt::{self, Result}, io};")
-}
-
-#[test]
-fn merge_groups_self() {
-    check_full("std::fmt::Debug", r"use std::fmt;", r"use std::fmt::{self, Debug};")
-}
-
-#[test]
-fn merge_mod_into_glob() {
-    check_full("token::TokenKind", r"use token::TokenKind::*;", r"use token::TokenKind::{*, self};")
-    // FIXME: have it emit `use token::TokenKind::{self, *}`?
-}
-
-#[test]
-fn merge_self_glob() {
-    check_full("self", r"use self::*;", r"use self::{*, self};")
-    // FIXME: have it emit `use {self, *}`?
-}
-
-#[test]
-fn merge_glob_nested() {
-    check_full(
-        "foo::bar::quux::Fez",
-        r"use foo::bar::{Baz, quux::*};",
-        r"use foo::bar::{Baz, quux::{self::*, Fez}};",
-    )
-}
-
-#[test]
-fn merge_nested_considers_first_segments() {
-    check_full(
-        "hir_ty::display::write_bounds_like_dyn_trait",
-        r"use hir_ty::{autoderef, display::{HirDisplayError, HirFormatter}, method_resolution};",
-        r"use hir_ty::{autoderef, display::{HirDisplayError, HirFormatter, write_bounds_like_dyn_trait}, method_resolution};",
-    );
-}
-
-#[test]
-fn skip_merge_last_too_long() {
-    check_last(
-        "foo::bar",
-        r"use foo::bar::baz::Qux;",
-        r"use foo::bar;
-use foo::bar::baz::Qux;",
-    );
-}
-
-#[test]
-fn skip_merge_last_too_long2() {
-    check_last(
-        "foo::bar::baz::Qux",
-        r"use foo::bar;",
-        r"use foo::bar;
-use foo::bar::baz::Qux;",
-    );
-}
-
-#[test]
-fn insert_short_before_long() {
-    check_none(
-        "foo::bar",
-        r"use foo::bar::baz::Qux;",
-        r"use foo::bar;
-use foo::bar::baz::Qux;",
-    );
-}
-
-#[test]
-fn merge_last_fail() {
-    check_merge_only_fail(
-        r"use foo::bar::{baz::{Qux, Fez}};",
-        r"use foo::bar::{baaz::{Quux, Feez}};",
-        MergeBehaviour::Last,
-    );
-}
-
-#[test]
-fn merge_last_fail1() {
-    check_merge_only_fail(
-        r"use foo::bar::{baz::{Qux, Fez}};",
-        r"use foo::bar::baaz::{Quux, Feez};",
-        MergeBehaviour::Last,
-    );
-}
-
-#[test]
-fn merge_last_fail2() {
-    check_merge_only_fail(
-        r"use foo::bar::baz::{Qux, Fez};",
-        r"use foo::bar::{baaz::{Quux, Feez}};",
-        MergeBehaviour::Last,
-    );
-}
-
-#[test]
-fn merge_last_fail3() {
-    check_merge_only_fail(
-        r"use foo::bar::baz::{Qux, Fez};",
-        r"use foo::bar::baaz::{Quux, Feez};",
-        MergeBehaviour::Last,
-    );
-}
-
-fn check(
-    path: &str,
-    ra_fixture_before: &str,
-    ra_fixture_after: &str,
-    mb: Option<MergeBehaviour>,
-    module: bool,
-) {
-    let mut syntax = ast::SourceFile::parse(ra_fixture_before).tree().syntax().clone();
-    if module {
-        syntax = syntax.descendants().find_map(ast::Module::cast).unwrap().syntax().clone();
-    }
-    let file = super::ImportScope::from(syntax).unwrap();
-    let path = ast::SourceFile::parse(&format!("use {};", path))
-        .tree()
-        .syntax()
-        .descendants()
-        .find_map(ast::Path::cast)
-        .unwrap();
-
-    let rewriter = insert_use(&file, path, mb);
-    let result = rewriter.rewrite(file.as_syntax_node()).to_string();
-    assert_eq_text!(&result, ra_fixture_after);
-}
-
-fn check_full(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) {
-    check(path, ra_fixture_before, ra_fixture_after, Some(MergeBehaviour::Full), false)
-}
-
-fn check_last(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) {
-    check(path, ra_fixture_before, ra_fixture_after, Some(MergeBehaviour::Last), false)
-}
-
-fn check_none(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) {
-    check(path, ra_fixture_before, ra_fixture_after, None, false)
-}
-
-fn check_merge_only_fail(ra_fixture0: &str, ra_fixture1: &str, mb: MergeBehaviour) {
-    let use0 = ast::SourceFile::parse(ra_fixture0)
-        .tree()
-        .syntax()
-        .descendants()
-        .find_map(ast::Use::cast)
-        .unwrap();
-
-    let use1 = ast::SourceFile::parse(ra_fixture1)
-        .tree()
-        .syntax()
-        .descendants()
-        .find_map(ast::Use::cast)
-        .unwrap();
-
-    let result = try_merge_imports(&use0, &use1, mb);
-    assert_eq!(result.map(|u| u.to_string()), None);
-}
diff --git a/crates/ide_db/src/line_index/tests.rs b/crates/ide_db/src/line_index/tests.rs
deleted file mode 100644
index 05f7484e8f..0000000000
--- a/crates/ide_db/src/line_index/tests.rs
+++ /dev/null
@@ -1,128 +0,0 @@
-use super::*;
-
-#[test]
-fn test_line_index() {
-    let text = "hello\nworld";
-    let index = LineIndex::new(text);
-    assert_eq!(index.line_col(0.into()), LineCol { line: 0, col_utf16: 0 });
-    assert_eq!(index.line_col(1.into()), LineCol { line: 0, col_utf16: 1 });
-    assert_eq!(index.line_col(5.into()), LineCol { line: 0, col_utf16: 5 });
-    assert_eq!(index.line_col(6.into()), LineCol { line: 1, col_utf16: 0 });
-    assert_eq!(index.line_col(7.into()), LineCol { line: 1, col_utf16: 1 });
-    assert_eq!(index.line_col(8.into()), LineCol { line: 1, col_utf16: 2 });
-    assert_eq!(index.line_col(10.into()), LineCol { line: 1, col_utf16: 4 });
-    assert_eq!(index.line_col(11.into()), LineCol { line: 1, col_utf16: 5 });
-    assert_eq!(index.line_col(12.into()), LineCol { line: 1, col_utf16: 6 });
-
-    let text = "\nhello\nworld";
-    let index = LineIndex::new(text);
-    assert_eq!(index.line_col(0.into()), LineCol { line: 0, col_utf16: 0 });
-    assert_eq!(index.line_col(1.into()), LineCol { line: 1, col_utf16: 0 });
-    assert_eq!(index.line_col(2.into()), LineCol { line: 1, col_utf16: 1 });
-    assert_eq!(index.line_col(6.into()), LineCol { line: 1, col_utf16: 5 });
-    assert_eq!(index.line_col(7.into()), LineCol { line: 2, col_utf16: 0 });
-}
-
-#[test]
-fn test_char_len() {
-    assert_eq!('メ'.len_utf8(), 3);
-    assert_eq!('メ'.len_utf16(), 1);
-}
-
-#[test]
-fn test_empty_index() {
-    let col_index = LineIndex::new(
-        "
-const C: char = 'x';
-",
-    );
-    assert_eq!(col_index.utf16_lines.len(), 0);
-}
-
-#[test]
-fn test_single_char() {
-    let col_index = LineIndex::new(
-        "
-const C: char = 'メ';
-",
-    );
-
-    assert_eq!(col_index.utf16_lines.len(), 1);
-    assert_eq!(col_index.utf16_lines[&1].len(), 1);
-    assert_eq!(col_index.utf16_lines[&1][0], Utf16Char { start: 17.into(), end: 20.into() });
-
-    // UTF-8 to UTF-16, no changes
-    assert_eq!(col_index.utf8_to_utf16_col(1, 15.into()), 15);
-
-    // UTF-8 to UTF-16
-    assert_eq!(col_index.utf8_to_utf16_col(1, 22.into()), 20);
-
-    // UTF-16 to UTF-8, no changes
-    assert_eq!(col_index.utf16_to_utf8_col(1, 15), TextSize::from(15));
-
-    // UTF-16 to UTF-8
-    assert_eq!(col_index.utf16_to_utf8_col(1, 19), TextSize::from(21));
-
-    let col_index = LineIndex::new("a𐐏b");
-    assert_eq!(col_index.utf16_to_utf8_col(0, 3), TextSize::from(5));
-}
-
-#[test]
-fn test_string() {
-    let col_index = LineIndex::new(
-        "
-const C: char = \"メ メ\";
-",
-    );
-
-    assert_eq!(col_index.utf16_lines.len(), 1);
-    assert_eq!(col_index.utf16_lines[&1].len(), 2);
-    assert_eq!(col_index.utf16_lines[&1][0], Utf16Char { start: 17.into(), end: 20.into() });
-    assert_eq!(col_index.utf16_lines[&1][1], Utf16Char { start: 21.into(), end: 24.into() });
-
-    // UTF-8 to UTF-16
-    assert_eq!(col_index.utf8_to_utf16_col(1, 15.into()), 15);
-
-    assert_eq!(col_index.utf8_to_utf16_col(1, 21.into()), 19);
-    assert_eq!(col_index.utf8_to_utf16_col(1, 25.into()), 21);
-
-    assert!(col_index.utf8_to_utf16_col(2, 15.into()) == 15);
-
-    // UTF-16 to UTF-8
-    assert_eq!(col_index.utf16_to_utf8_col(1, 15), TextSize::from(15));
-
-    // メ UTF-8: 0xE3 0x83 0xA1, UTF-16: 0x30E1
-    assert_eq!(col_index.utf16_to_utf8_col(1, 17), TextSize::from(17)); // first メ at 17..20
-    assert_eq!(col_index.utf16_to_utf8_col(1, 18), TextSize::from(20)); // space
-    assert_eq!(col_index.utf16_to_utf8_col(1, 19), TextSize::from(21)); // second メ at 21..24
-
-    assert_eq!(col_index.utf16_to_utf8_col(2, 15), TextSize::from(15));
-}
-
-#[test]
-fn test_splitlines() {
-    fn r(lo: u32, hi: u32) -> TextRange {
-        TextRange::new(lo.into(), hi.into())
-    }
-
-    let text = "a\nbb\nccc\n";
-    let line_index = LineIndex::new(text);
-
-    let actual = line_index.lines(r(0, 9)).collect::<Vec<_>>();
-    let expected = vec![r(0, 2), r(2, 5), r(5, 9)];
-    assert_eq!(actual, expected);
-
-    let text = "";
-    let line_index = LineIndex::new(text);
-
-    let actual = line_index.lines(r(0, 0)).collect::<Vec<_>>();
-    let expected = vec![];
-    assert_eq!(actual, expected);
-
-    let text = "\n";
-    let line_index = LineIndex::new(text);
-
-    let actual = line_index.lines(r(0, 1)).collect::<Vec<_>>();
-    let expected = vec![r(0, 1)];
-    assert_eq!(actual, expected)
-}
diff --git a/crates/ide_db/src/traits/tests.rs b/crates/ide_db/src/traits/tests.rs
deleted file mode 100644
index 09c7ac3ec4..0000000000
--- a/crates/ide_db/src/traits/tests.rs
+++ /dev/null
@@ -1,144 +0,0 @@
-use crate::RootDatabase;
-use base_db::{fixture::ChangeFixture, FilePosition};
-use expect_test::{expect, Expect};
-use hir::Semantics;
-use syntax::ast::{self, AstNode};
-use test_utils::RangeOrOffset;
-
-/// Creates analysis from a multi-file fixture, returns positions marked with <|>.
-pub(crate) fn position(ra_fixture: &str) -> (RootDatabase, FilePosition) {
-    let change_fixture = ChangeFixture::parse(ra_fixture);
-    let mut database = RootDatabase::default();
-    database.apply_change(change_fixture.change);
-    let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker (<|>)");
-    let offset = match range_or_offset {
-        RangeOrOffset::Range(_) => panic!(),
-        RangeOrOffset::Offset(it) => it,
-    };
-    (database, FilePosition { file_id, offset })
-}
-
-fn check_trait(ra_fixture: &str, expect: Expect) {
-    let (db, position) = position(ra_fixture);
-    let sema = Semantics::new(&db);
-    let file = sema.parse(position.file_id);
-    let impl_block: ast::Impl =
-        sema.find_node_at_offset_with_descend(file.syntax(), position.offset).unwrap();
-    let trait_ = crate::traits::resolve_target_trait(&sema, &impl_block);
-    let actual = match trait_ {
-        Some(trait_) => trait_.name(&db).to_string(),
-        None => String::new(),
-    };
-    expect.assert_eq(&actual);
-}
-
-fn check_missing_assoc(ra_fixture: &str, expect: Expect) {
-    let (db, position) = position(ra_fixture);
-    let sema = Semantics::new(&db);
-    let file = sema.parse(position.file_id);
-    let impl_block: ast::Impl =
-        sema.find_node_at_offset_with_descend(file.syntax(), position.offset).unwrap();
-    let items = crate::traits::get_missing_assoc_items(&sema, &impl_block);
-    let actual = items
-        .into_iter()
-        .map(|item| item.name(&db).unwrap().to_string())
-        .collect::<Vec<_>>()
-        .join("\n");
-    expect.assert_eq(&actual);
-}
-
-#[test]
-fn resolve_trait() {
-    check_trait(
-        r#"
-pub trait Foo {
-    fn bar();
-}
-impl Foo for u8 {
-    <|>
-}
-            "#,
-        expect![["Foo"]],
-    );
-    check_trait(
-        r#"
-pub trait Foo {
-    fn bar();
-}
-impl Foo for u8 {
-    fn bar() {
-        fn baz() {
-            <|>
-        }
-        baz();
-    }
-}
-            "#,
-        expect![["Foo"]],
-    );
-    check_trait(
-        r#"
-pub trait Foo {
-    fn bar();
-}
-pub struct Bar;
-impl Bar {
-    <|>
-}
-            "#,
-        expect![[""]],
-    );
-}
-
-#[test]
-fn missing_assoc_items() {
-    check_missing_assoc(
-        r#"
-pub trait Foo {
-    const FOO: u8;
-    fn bar();
-}
-impl Foo for u8 {
-    <|>
-}"#,
-        expect![[r#"
-                FOO
-                bar"#]],
-    );
-
-    check_missing_assoc(
-        r#"
-pub trait Foo {
-    const FOO: u8;
-    fn bar();
-}
-impl Foo for u8 {
-    const FOO: u8 = 10;
-    <|>
-}"#,
-        expect![[r#"
-                bar"#]],
-    );
-
-    check_missing_assoc(
-        r#"
-pub trait Foo {
-    const FOO: u8;
-    fn bar();
-}
-impl Foo for u8 {
-    const FOO: u8 = 10;
-    fn bar() {<|>}
-}"#,
-        expect![[r#""#]],
-    );
-
-    check_missing_assoc(
-        r#"
-pub struct Foo;
-impl Foo {
-    fn bar() {<|>}
-}"#,
-        expect![[r#""#]],
-    );
-}