Repository: rust-analyzer/rust-analyzer
Pull request #6586 (2020-11-17 21:38:15)

Don't call a closure a function in the infer_function_return_type assist label
`Add this function's return type` becomes `Add this closure's return type` for closures. This makes it more obvious that we are indeed planning on modifying the closure and not its containing function.


Patch:
diff --git a/Cargo.lock b/Cargo.lock
index 6345ce9934..715a809789 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -242,9 +242,9 @@ dependencies = [
 
 [[package]]
 name = "cmake"
-version = "0.1.45"
+version = "0.1.44"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eb6210b637171dfba4cda12e579ac6dc73f5165ad56133e5d72ef3131f320855"
+checksum = "0e56268c17a6248366d66d4a47a3381369d068cce8409bb1716ed77ea32163bb"
 dependencies = [
  "cc",
 ]
@@ -255,7 +255,6 @@ version = "0.0.0"
 dependencies = [
  "assists",
  "base_db",
- "either",
  "expect-test",
  "hir",
  "ide_db",
@@ -853,9 +852,9 @@ dependencies = [
 
 [[package]]
 name = "lsp-server"
-version = "0.5.0"
+version = "0.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "69b18dfe0e4a380b872aa79d8e0ee6c3d7a9682466e84b83ad807c88b3545f79"
+checksum = "9c85acaf36c53bf15da2b8b35afeea56747707261f59eb0b77229081dd72b04e"
 dependencies = [
  "crossbeam-channel 0.5.0",
  "log",
@@ -865,9 +864,9 @@ dependencies = [
 
 [[package]]
 name = "lsp-types"
-version = "0.83.1"
+version = "0.83.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c4e79f39834b97271f9f5ecec573e42c7d9c5bdbd2620b30a851054ece6aab6d"
+checksum = "25e0bd4b95038f2c23bda332ba0ca684e8dda765db1f9bdb63dc4c3e01f3b456"
 dependencies = [
  "base64",
  "bitflags",
@@ -1066,9 +1065,9 @@ checksum = "8d3b63360ec3cb337817c2dbd47ab4a0f170d285d8e5a2064600f3def1402397"
 
 [[package]]
 name = "once_cell"
-version = "1.5.2"
+version = "1.5.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "13bd41f508810a131401606d54ac32a467c97172d74ba7662562ebba5ad07fa0"
+checksum = "f53cef67919d7d247eb9a2f128ca9e522789967ef1eb4ccd8c71a95a8aedf596"
 
 [[package]]
 name = "oorandom"
@@ -1740,18 +1739,9 @@ dependencies = [
 
 [[package]]
 name = "tinyvec"
-version = "1.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b78a366903f506d2ad52ca8dc552102ffdd3e937ba8a227f024dc1d1eae28575"
-dependencies = [
- "tinyvec_macros",
-]
-
-[[package]]
-name = "tinyvec_macros"
-version = "0.1.0"
+version = "0.3.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
+checksum = "238ce071d267c5710f9d31451efec16c5ee22de34df17cc05e56cbc92e967117"
 
 [[package]]
 name = "toolchain"
@@ -1889,18 +1879,18 @@ dependencies = [
 
 [[package]]
 name = "unicode-normalization"
-version = "0.1.14"
+version = "0.1.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b7f98e67a4d84f730d343392f9bfff7d21e3fca562b9cb7a43b768350beeddc6"
+checksum = "6fb19cf769fa8c6a80a162df694621ebeb4dafb606470b2b2fce0be40a98a977"
 dependencies = [
  "tinyvec",
 ]
 
 [[package]]
 name = "unicode-segmentation"
-version = "1.7.0"
+version = "1.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "db8716a166f290ff49dabc18b44aa407cb7c6dbe1aa0971b44b8a24b0ca35aae"
+checksum = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0"
 
 [[package]]
 name = "unicode-xid"
diff --git a/crates/assists/src/handlers/auto_import.rs b/crates/assists/src/handlers/auto_import.rs
index d665837a2f..37dd612661 100644
--- a/crates/assists/src/handlers/auto_import.rs
+++ b/crates/assists/src/handlers/auto_import.rs
@@ -98,8 +98,7 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
 
     let range = ctx.sema.original_range(import_assets.syntax_under_caret()).range;
     let group = import_group_message(import_assets.import_candidate());
-    let scope =
-        ImportScope::find_insert_use_container(import_assets.syntax_under_caret(), &ctx.sema)?;
+    let scope = ImportScope::find_insert_use_container(import_assets.syntax_under_caret(), ctx)?;
     for (import, _) in proposed_imports {
         acc.add_group(
             &group,
diff --git a/crates/assists/src/handlers/extract_struct_from_enum_variant.rs b/crates/assists/src/handlers/extract_struct_from_enum_variant.rs
index cac77c49bb..067afabf2e 100644
--- a/crates/assists/src/handlers/extract_struct_from_enum_variant.rs
+++ b/crates/assists/src/handlers/extract_struct_from_enum_variant.rs
@@ -143,7 +143,8 @@ fn insert_import(
     if let Some(mut mod_path) = mod_path {
         mod_path.segments.pop();
         mod_path.segments.push(variant_hir_name.clone());
-        let scope = ImportScope::find_insert_use_container(scope_node, &ctx.sema)?;
+        let scope = ImportScope::find_insert_use_container(scope_node, ctx)?;
+
         *rewriter += insert_use(&scope, mod_path_to_ast(&mod_path), ctx.config.insert_use.merge);
     }
     Some(())
diff --git a/crates/assists/src/handlers/infer_function_return_type.rs b/crates/assists/src/handlers/infer_function_return_type.rs
index 520d07ae06..aa584eb034 100644
--- a/crates/assists/src/handlers/infer_function_return_type.rs
+++ b/crates/assists/src/handlers/infer_function_return_type.rs
@@ -17,7 +17,7 @@ use crate::{AssistContext, AssistId, AssistKind, Assists};
 // fn foo() -> i32 { 42i32 }
 // ```
 pub(crate) fn infer_function_return_type(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
-    let (tail_expr, builder_edit_pos, wrap_expr) = extract_tail(ctx)?;
+    let (fn_type, tail_expr, builder_edit_pos) = extract_tail(ctx)?;
     let module = ctx.sema.scope(tail_expr.syntax()).module()?;
     let ty = ctx.sema.type_of_expr(&tail_expr)?;
     if ty.is_unit() {
@@ -27,7 +27,10 @@ pub(crate) fn infer_function_return_type(acc: &mut Assists, ctx: &AssistContext)
 
     acc.add(
         AssistId("infer_function_return_type", AssistKind::RefactorRewrite),
-        "Add this function's return type",
+        match fn_type {
+            FnType::Function => "Add this function's return type",
+            FnType::Closure { .. } => "Add this closure's return type",
+        },
         tail_expr.syntax().text_range(),
         |builder| {
             match builder_edit_pos {
@@ -38,7 +41,7 @@ pub(crate) fn infer_function_return_type(acc: &mut Assists, ctx: &AssistContext)
                     builder.replace(text_range, &format!("-> {}", ty))
                 }
             }
-            if wrap_expr {
+            if let FnType::Closure { wrap_expr: true } = fn_type {
                 mark::hit!(wrap_closure_non_block_expr);
                 // `|x| x` becomes `|x| -> T x` which is invalid, so wrap it in a block
                 builder.replace(tail_expr.syntax().text_range(), &format!("{{{}}}", tail_expr));
@@ -72,8 +75,13 @@ fn ret_ty_to_action(ret_ty: Option<ast::RetType>, insert_pos: TextSize) -> Optio
     }
 }
 
-fn extract_tail(ctx: &AssistContext) -> Option<(ast::Expr, InsertOrReplace, bool)> {
-    let (tail_expr, return_type_range, action, wrap_expr) =
+enum FnType {
+    Function,
+    Closure { wrap_expr: bool },
+}
+
+fn extract_tail(ctx: &AssistContext) -> Option<(FnType, ast::Expr, InsertOrReplace)> {
+    let (fn_type, tail_expr, return_type_range, action) =
         if let Some(closure) = ctx.find_node_at_offset::<ast::ClosureExpr>() {
             let rpipe_pos = closure.param_list()?.syntax().last_token()?.text_range().end();
             let action = ret_ty_to_action(closure.ret_type(), rpipe_pos)?;
@@ -86,7 +94,7 @@ fn extract_tail(ctx: &AssistContext) -> Option<(ast::Expr, InsertOrReplace, bool
             };
 
             let ret_range = TextRange::new(rpipe_pos, body_start);
-            (tail_expr, ret_range, action, wrap_expr)
+            (FnType::Closure { wrap_expr }, tail_expr, ret_range, action)
         } else {
             let func = ctx.find_node_at_offset::<ast::Fn>()?;
             let rparen_pos = func.param_list()?.r_paren_token()?.text_range().end();
@@ -97,7 +105,7 @@ fn extract_tail(ctx: &AssistContext) -> Option<(ast::Expr, InsertOrReplace, bool
 
             let ret_range_end = body.l_curly_token()?.text_range().start();
             let ret_range = TextRange::new(rparen_pos, ret_range_end);
-            (tail_expr, ret_range, action, false)
+            (FnType::Function, tail_expr, ret_range, action)
         };
     let frange = ctx.frange.range;
     if return_type_range.contains_range(frange) {
@@ -109,7 +117,7 @@ fn extract_tail(ctx: &AssistContext) -> Option<(ast::Expr, InsertOrReplace, bool
     } else {
         return None;
     }
-    Some((tail_expr, action, wrap_expr))
+    Some((fn_type, tail_expr, action))
 }
 
 #[cfg(test)]
diff --git a/crates/assists/src/handlers/replace_derive_with_manual_impl.rs b/crates/assists/src/handlers/replace_derive_with_manual_impl.rs
index 453a6cebfb..82625516c2 100644
--- a/crates/assists/src/handlers/replace_derive_with_manual_impl.rs
+++ b/crates/assists/src/handlers/replace_derive_with_manual_impl.rs
@@ -62,21 +62,19 @@ pub(crate) fn replace_derive_with_manual_impl(
     let current_module = ctx.sema.scope(annotated_name.syntax()).module()?;
     let current_crate = current_module.krate();
 
-    let found_traits =
-        imports_locator::find_exact_imports(&ctx.sema, current_crate, trait_token.text())
-            .filter_map(
-                |candidate: either::Either<hir::ModuleDef, hir::MacroDef>| match candidate {
-                    either::Either::Left(hir::ModuleDef::Trait(trait_)) => Some(trait_),
-                    _ => None,
-                },
-            )
-            .flat_map(|trait_| {
-                current_module
-                    .find_use_path(ctx.sema.db, hir::ModuleDef::Trait(trait_))
-                    .as_ref()
-                    .map(mod_path_to_ast)
-                    .zip(Some(trait_))
-            });
+    let found_traits = imports_locator::find_imports(&ctx.sema, current_crate, trait_token.text())
+        .into_iter()
+        .filter_map(|candidate: either::Either<hir::ModuleDef, hir::MacroDef>| match candidate {
+            either::Either::Left(hir::ModuleDef::Trait(trait_)) => Some(trait_),
+            _ => None,
+        })
+        .flat_map(|trait_| {
+            current_module
+                .find_use_path(ctx.sema.db, hir::ModuleDef::Trait(trait_))
+                .as_ref()
+                .map(mod_path_to_ast)
+                .zip(Some(trait_))
+        });
 
     let mut no_traits_found = true;
     for (trait_path, trait_) in found_traits.inspect(|_| no_traits_found = false) {
diff --git a/crates/assists/src/handlers/replace_qualified_name_with_use.rs b/crates/assists/src/handlers/replace_qualified_name_with_use.rs
index a66db9ae3a..d7e1d95805 100644
--- a/crates/assists/src/handlers/replace_qualified_name_with_use.rs
+++ b/crates/assists/src/handlers/replace_qualified_name_with_use.rs
@@ -34,7 +34,7 @@ pub(crate) fn replace_qualified_name_with_use(
     }
 
     let target = path.syntax().text_range();
-    let scope = ImportScope::find_insert_use_container(path.syntax(), &ctx.sema)?;
+    let scope = ImportScope::find_insert_use_container(path.syntax(), ctx)?;
     let syntax = scope.as_syntax_node();
     acc.add(
         AssistId("replace_qualified_name_with_use", AssistKind::RefactorRewrite),
diff --git a/crates/assists/src/handlers/unwrap_block.rs b/crates/assists/src/handlers/unwrap_block.rs
index 676db71373..36ef871b93 100644
--- a/crates/assists/src/handlers/unwrap_block.rs
+++ b/crates/assists/src/handlers/unwrap_block.rs
@@ -3,7 +3,7 @@ use syntax::{
         self,
         edit::{AstNodeEdit, IndentLevel},
     },
-    AstNode, SyntaxKind, TextRange, T,
+    AstNode, TextRange, T,
 };
 
 use crate::{utils::unwrap_trivial_block, AssistContext, AssistId, AssistKind, Assists};
@@ -31,21 +31,11 @@ pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
 
     let l_curly_token = ctx.find_token_syntax_at_offset(T!['{'])?;
     let mut block = ast::BlockExpr::cast(l_curly_token.parent())?;
-    let target = block.syntax().text_range();
     let mut parent = block.syntax().parent()?;
     if ast::MatchArm::can_cast(parent.kind()) {
         parent = parent.ancestors().find(|it| ast::MatchExpr::can_cast(it.kind()))?
     }
 
-    if matches!(parent.kind(), SyntaxKind::BLOCK_EXPR | SyntaxKind::EXPR_STMT) {
-        return acc.add(assist_id, assist_label, target, |builder| {
-            builder.replace(
-                block.syntax().text_range(),
-                update_expr_string(block.to_string(), &[' ', '{', '\n']),
-            );
-        });
-    }
-
     let parent = ast::Expr::cast(parent)?;
 
     match parent.clone() {
@@ -58,6 +48,7 @@ pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
                     // For `else if` blocks
                     let ancestor_then_branch = ancestor.then_branch()?;
 
+                    let target = then_branch.syntax().text_range();
                     return acc.add(assist_id, assist_label, target, |edit| {
                         let range_to_del_else_if = TextRange::new(
                             ancestor_then_branch.syntax().text_range().end(),
@@ -77,6 +68,7 @@ pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
                     });
                 }
             } else {
+                let target = block.syntax().text_range();
                 return acc.add(assist_id, assist_label, target, |edit| {
                     let range_to_del = TextRange::new(
                         then_branch.syntax().text_range().end(),
@@ -92,6 +84,7 @@ pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
     };
 
     let unwrapped = unwrap_trivial_block(block);
+    let target = unwrapped.syntax().text_range();
     acc.add(assist_id, assist_label, target, |builder| {
         builder.replace(
             parent.syntax().text_range(),
@@ -118,90 +111,32 @@ mod tests {
 
     use super::*;
 
-    #[test]
-    fn unwrap_tail_expr_block() {
-        check_assist(
-            unwrap_block,
-            r#"
-fn main() {
-    <|>{
-        92
-    }
-}
-"#,
-            r#"
-fn main() {
-    92
-}
-"#,
-        )
-    }
-
-    #[test]
-    fn unwrap_stmt_expr_block() {
-        check_assist(
-            unwrap_block,
-            r#"
-fn main() {
-    <|>{
-        92;
-    }
-    ()
-}
-"#,
-            r#"
-fn main() {
-    92;
-    ()
-}
-"#,
-        );
-        // Pedantically, we should add an `;` here...
-        check_assist(
-            unwrap_block,
-            r#"
-fn main() {
-    <|>{
-        92
-    }
-    ()
-}
-"#,
-            r#"
-fn main() {
-    92
-    ()
-}
-"#,
-        );
-    }
-
     #[test]
     fn simple_if() {
         check_assist(
             unwrap_block,
             r#"
-fn main() {
-    bar();
-    if true {<|>
-        foo();
-
-        //comment
-        bar();
-    } else {
-        println!("bar");
-    }
-}
-"#,
+            fn main() {
+                bar();
+                if true {<|>
+                    foo();
+
+                    //comment
+                    bar();
+                } else {
+                    println!("bar");
+                }
+            }
+            "#,
             r#"
-fn main() {
-    bar();
-    foo();
+            fn main() {
+                bar();
+                foo();
 
-    //comment
-    bar();
-}
-"#,
+                //comment
+                bar();
+            }
+            "#,
         );
     }
 
@@ -210,30 +145,30 @@ fn main() {
         check_assist(
             unwrap_block,
             r#"
-fn main() {
-    bar();
-    if true {
-        foo();
-
-        //comment
-        bar();
-    } else {<|>
-        println!("bar");
-    }
-}
-"#,
+            fn main() {
+                bar();
+                if true {
+                    foo();
+
+                    //comment
+                    bar();
+                } else {<|>
+                    println!("bar");
+                }
+            }
+            "#,
             r#"
-fn main() {
-    bar();
-    if true {
-        foo();
+            fn main() {
+                bar();
+                if true {
+                    foo();
 
-        //comment
-        bar();
-    }
-    println!("bar");
-}
-"#,
+                    //comment
+                    bar();
+                }
+                println!("bar");
+            }
+            "#,
         );
     }
 
@@ -242,32 +177,32 @@ fn main() {
         check_assist(
             unwrap_block,
             r#"
-fn main() {
-    //bar();
-    if true {
-        println!("true");
-
-        //comment
-        //bar();
-    } else if false {<|>
-        println!("bar");
-    } else {
-        println!("foo");
-    }
-}
-"#,
+            fn main() {
+                //bar();
+                if true {
+                    println!("true");
+
+                    //comment
+                    //bar();
+                } else if false {<|>
+                    println!("bar");
+                } else {
+                    println!("foo");
+                }
+            }
+            "#,
             r#"
-fn main() {
-    //bar();
-    if true {
-        println!("true");
+            fn main() {
+                //bar();
+                if true {
+                    println!("true");
 
-        //comment
-        //bar();
-    }
-    println!("bar");
-}
-"#,
+                    //comment
+                    //bar();
+                }
+                println!("bar");
+            }
+            "#,
         );
     }
 
@@ -276,34 +211,34 @@ fn main() {
         check_assist(
             unwrap_block,
             r#"
-fn main() {
-    //bar();
-    if true {
-        println!("true");
-
-        //comment
-        //bar();
-    } else if false {
-        println!("bar");
-    } else if true {<|>
-        println!("foo");
-    }
-}
-"#,
+            fn main() {
+                //bar();
+                if true {
+                    println!("true");
+
+                    //comment
+                    //bar();
+                } else if false {
+                    println!("bar");
+                } else if true {<|>
+                    println!("foo");
+                }
+            }
+            "#,
             r#"
-fn main() {
-    //bar();
-    if true {
-        println!("true");
-
-        //comment
-        //bar();
-    } else if false {
-        println!("bar");
-    }
-    println!("foo");
-}
-"#,
+            fn main() {
+                //bar();
+                if true {
+                    println!("true");
+
+                    //comment
+                    //bar();
+                } else if false {
+                    println!("bar");
+                }
+                println!("foo");
+            }
+            "#,
         );
     }
 
@@ -312,38 +247,38 @@ fn main() {
         check_assist(
             unwrap_block,
             r#"
-fn main() {
-    //bar();
-    if true {
-        println!("true");
-
-        //comment
-        //bar();
-    } else if false {
-        println!("bar");
-    } else if true {
-        println!("foo");
-    } else {<|>
-        println!("else");
-    }
-}
-"#,
+            fn main() {
+                //bar();
+                if true {
+                    println!("true");
+
+                    //comment
+                    //bar();
+                } else if false {
+                    println!("bar");
+                } else if true {
+                    println!("foo");
+                } else {<|>
+                    println!("else");
+                }
+            }
+            "#,
             r#"
-fn main() {
-    //bar();
-    if true {
-        println!("true");
-
-        //comment
-        //bar();
-    } else if false {
-        println!("bar");
-    } else if true {
-        println!("foo");
-    }
-    println!("else");
-}
-"#,
+            fn main() {
+                //bar();
+                if true {
+                    println!("true");
+
+                    //comment
+                    //bar();
+                } else if false {
+                    println!("bar");
+                } else if true {
+                    println!("foo");
+                }
+                println!("else");
+            }
+            "#,
         );
     }
 
@@ -352,36 +287,36 @@ fn main() {
         check_assist(
             unwrap_block,
             r#"
-fn main() {
-    //bar();
-    if true {
-        println!("true");
-
-        //comment
-        //bar();
-    } else if false {
-        println!("bar");
-    } else if true {<|>
-        println!("foo");
-    } else {
-        println!("else");
-    }
-}
-"#,
+            fn main() {
+                //bar();
+                if true {
+                    println!("true");
+
+                    //comment
+                    //bar();
+                } else if false {
+                    println!("bar");
+                } else if true {<|>
+                    println!("foo");
+                } else {
+                    println!("else");
+                }
+            }
+            "#,
             r#"
-fn main() {
-    //bar();
-    if true {
-        println!("true");
-
-        //comment
-        //bar();
-    } else if false {
-        println!("bar");
-    }
-    println!("foo");
-}
-"#,
+            fn main() {
+                //bar();
+                if true {
+                    println!("true");
+
+                    //comment
+                    //bar();
+                } else if false {
+                    println!("bar");
+                }
+                println!("foo");
+            }
+            "#,
         );
     }
 
@@ -390,18 +325,18 @@ fn main() {
         check_assist_not_applicable(
             unwrap_block,
             r#"
-fn main() {
-    bar();<|>
-    if true {
-        foo();
-
-        //comment
-        bar();
-    } else {
-        println!("bar");
-    }
-}
-"#,
+            fn main() {
+                bar();<|>
+                if true {
+                    foo();
+
+                    //comment
+                    bar();
+                } else {
+                    println!("bar");
+                }
+            }
+            "#,
         );
     }
 
@@ -410,31 +345,31 @@ fn main() {
         check_assist(
             unwrap_block,
             r#"
-fn main() {
-    for i in 0..5 {<|>
-        if true {
-            foo();
-
-            //comment
-            bar();
-        } else {
-            println!("bar");
-        }
-    }
-}
-"#,
+            fn main() {
+                for i in 0..5 {<|>
+                    if true {
+                        foo();
+
+                        //comment
+                        bar();
+                    } else {
+                        println!("bar");
+                    }
+                }
+            }
+            "#,
             r#"
-fn main() {
-    if true {
-        foo();
-
-        //comment
-        bar();
-    } else {
-        println!("bar");
-    }
-}
-"#,
+            fn main() {
+                if true {
+                    foo();
+
+                    //comment
+                    bar();
+                } else {
+                    println!("bar");
+                }
+            }
+            "#,
         );
     }
 
@@ -443,29 +378,29 @@ fn main() {
         check_assist(
             unwrap_block,
             r#"
-fn main() {
-    for i in 0..5 {
-        if true {<|>
-            foo();
-
-            //comment
-            bar();
-        } else {
-            println!("bar");
-        }
-    }
-}
-"#,
+            fn main() {
+                for i in 0..5 {
+                    if true {<|>
+                        foo();
+
+                        //comment
+                        bar();
+                    } else {
+                        println!("bar");
+                    }
+                }
+            }
+            "#,
             r#"
-fn main() {
-    for i in 0..5 {
-        foo();
+            fn main() {
+                for i in 0..5 {
+                    foo();
 
-        //comment
-        bar();
-    }
-}
-"#,
+                    //comment
+                    bar();
+                }
+            }
+            "#,
         );
     }
 
@@ -474,31 +409,31 @@ fn main() {
         check_assist(
             unwrap_block,
             r#"
-fn main() {
-    loop {<|>
-        if true {
-            foo();
-
-            //comment
-            bar();
-        } else {
-            println!("bar");
-        }
-    }
-}
-"#,
+            fn main() {
+                loop {<|>
+                    if true {
+                        foo();
+
+                        //comment
+                        bar();
+                    } else {
+                        println!("bar");
+                    }
+                }
+            }
+            "#,
             r#"
-fn main() {
-    if true {
-        foo();
-
-        //comment
-        bar();
-    } else {
-        println!("bar");
-    }
-}
-"#,
+            fn main() {
+                if true {
+                    foo();
+
+                    //comment
+                    bar();
+                } else {
+                    println!("bar");
+                }
+            }
+            "#,
         );
     }
 
@@ -507,31 +442,31 @@ fn main() {
         check_assist(
             unwrap_block,
             r#"
-fn main() {
-    while true {<|>
-        if true {
-            foo();
-
-            //comment
-            bar();
-        } else {
-            println!("bar");
-        }
-    }
-}
-"#,
+            fn main() {
+                while true {<|>
+                    if true {
+                        foo();
+
+                        //comment
+                        bar();
+                    } else {
+                        println!("bar");
+                    }
+                }
+            }
+            "#,
             r#"
-fn main() {
-    if true {
-        foo();
-
-        //comment
-        bar();
-    } else {
-        println!("bar");
-    }
-}
-"#,
+            fn main() {
+                if true {
+                    foo();
+
+                    //comment
+                    bar();
+                } else {
+                    println!("bar");
+                }
+            }
+            "#,
         );
     }
 
@@ -564,19 +499,19 @@ fn main() {
         check_assist_not_applicable(
             unwrap_block,
             r#"
-fn main() {
-    while true {
-        if true {
-            foo();<|>
-
-            //comment
-            bar();
-        } else {
-            println!("bar");
-        }
-    }
-}
-"#,
+            fn main() {
+                while true {
+                    if true {
+                        foo();<|>
+
+                        //comment
+                        bar();
+                    } else {
+                        println!("bar");
+                    }
+                }
+            }
+            "#,
         );
     }
 }
diff --git a/crates/assists/src/lib.rs b/crates/assists/src/lib.rs
index 17e9312db2..e8d81b33d7 100644
--- a/crates/assists/src/lib.rs
+++ b/crates/assists/src/lib.rs
@@ -141,7 +141,6 @@ mod handlers {
     mod generate_function;
     mod generate_impl;
     mod generate_new;
-    mod ignore_test;
     mod infer_function_return_type;
     mod inline_local_variable;
     mod introduce_named_lifetime;
@@ -190,7 +189,6 @@ mod handlers {
             generate_function::generate_function,
             generate_impl::generate_impl,
             generate_new::generate_new,
-            ignore_test::ignore_test,
             infer_function_return_type::infer_function_return_type,
             inline_local_variable::inline_local_variable,
             introduce_named_lifetime::introduce_named_lifetime,
diff --git a/crates/assists/src/utils.rs b/crates/assists/src/utils.rs
index 66c0cdd5fa..7bd338e99c 100644
--- a/crates/assists/src/utils.rs
+++ b/crates/assists/src/utils.rs
@@ -9,7 +9,6 @@ use ide_db::RootDatabase;
 use itertools::Itertools;
 use syntax::{
     ast::edit::AstNodeEdit,
-    ast::AttrsOwner,
     ast::NameOwner,
     ast::{self, edit, make, ArgListOwner},
     AstNode, Direction,
@@ -22,7 +21,8 @@ use crate::{
     ast_transform::{self, AstTransform, QualifyPaths, SubstituteTypeParams},
 };
 
-pub use insert_use::{insert_use, ImportScope, MergeBehaviour};
+pub use insert_use::MergeBehaviour;
+pub(crate) use insert_use::{insert_use, ImportScope};
 
 pub fn mod_path_to_ast(path: &hir::ModPath) -> ast::Path {
     let mut segments = Vec::new();
@@ -82,23 +82,6 @@ pub fn extract_trivial_expression(block: &ast::BlockExpr) -> Option<ast::Expr> {
     None
 }
 
-/// This is a method with a heuristics to support test methods annotated with custom test annotations, such as
-/// `#[test_case(...)]`, `#[tokio::test]` and similar.
-/// Also a regular `#[test]` annotation is supported.
-///
-/// It may produce false positives, for example, `#[wasm_bindgen_test]` requires a different command to run the test,
-/// but it's better than not to have the runnables for the tests at all.
-pub fn test_related_attribute(fn_def: &ast::Fn) -> Option<ast::Attr> {
-    fn_def.attrs().find_map(|attr| {
-        let path = attr.path()?;
-        if path.syntax().text().to_string().contains("test") {
-            Some(attr)
-        } else {
-            None
-        }
-    })
-}
-
 #[derive(Copy, Clone, PartialEq)]
 pub enum DefaultMethods {
     Only,
diff --git a/crates/assists/src/utils/import_assets.rs b/crates/assists/src/utils/import_assets.rs
index ff5c0e78ee..f47edbb765 100644
--- a/crates/assists/src/utils/import_assets.rs
+++ b/crates/assists/src/utils/import_assets.rs
@@ -179,25 +179,21 @@ impl ImportAssets {
             }
         };
 
-        let mut res =
-            imports_locator::find_exact_imports(sema, current_crate, &self.get_search_query())
-                .filter_map(filter)
-                .filter_map(|candidate| {
-                    let item: hir::ItemInNs = candidate.either(Into::into, Into::into);
-                    if let Some(prefix_kind) = prefixed {
-                        self.module_with_name_to_import.find_use_path_prefixed(
-                            db,
-                            item,
-                            prefix_kind,
-                        )
-                    } else {
-                        self.module_with_name_to_import.find_use_path(db, item)
-                    }
-                    .map(|path| (path, item))
-                })
-                .filter(|(use_path, _)| use_path.len() > 1)
-                .take(20)
-                .collect::<Vec<_>>();
+        let mut res = imports_locator::find_imports(sema, current_crate, &self.get_search_query())
+            .into_iter()
+            .filter_map(filter)
+            .filter_map(|candidate| {
+                let item: hir::ItemInNs = candidate.either(Into::into, Into::into);
+                if let Some(prefix_kind) = prefixed {
+                    self.module_with_name_to_import.find_use_path_prefixed(db, item, prefix_kind)
+                } else {
+                    self.module_with_name_to_import.find_use_path(db, item)
+                }
+                .map(|path| (path, item))
+            })
+            .filter(|(use_path, _)| !use_path.segments.is_empty())
+            .take(20)
+            .collect::<Vec<_>>();
         res.sort_by_key(|(path, _)| path.clone());
         res
     }
diff --git a/crates/assists/src/utils/insert_use.rs b/crates/assists/src/utils/insert_use.rs
index 423782a0e0..af3fc96b6c 100644
--- a/crates/assists/src/utils/insert_use.rs
+++ b/crates/assists/src/utils/insert_use.rs
@@ -1,8 +1,6 @@
 //! Handle syntactic aspects of inserting a new `use`.
 use std::{cmp::Ordering, iter::successors};
 
-use hir::Semantics;
-use ide_db::RootDatabase;
 use itertools::{EitherOrBoth, Itertools};
 use syntax::{
     algo::SyntaxRewriter,
@@ -15,8 +13,8 @@ use syntax::{
 };
 use test_utils::mark;
 
-#[derive(Debug, Clone)]
-pub enum ImportScope {
+#[derive(Debug)]
+pub(crate) enum ImportScope {
     File(ast::SourceFile),
     Module(ast::ItemList),
 }
@@ -33,14 +31,14 @@ impl ImportScope {
     }
 
     /// Determines the containing syntax node in which to insert a `use` statement affecting `position`.
-    pub fn find_insert_use_container(
+    pub(crate) fn find_insert_use_container(
         position: &SyntaxNode,
-        sema: &Semantics<'_, RootDatabase>,
+        ctx: &crate::assist_context::AssistContext,
     ) -> Option<Self> {
-        sema.ancestors_with_macros(position.clone()).find_map(Self::from)
+        ctx.sema.ancestors_with_macros(position.clone()).find_map(Self::from)
     }
 
-    pub fn as_syntax_node(&self) -> &SyntaxNode {
+    pub(crate) fn as_syntax_node(&self) -> &SyntaxNode {
         match self {
             ImportScope::File(file) => file.syntax(),
             ImportScope::Module(item_list) => item_list.syntax(),
@@ -90,7 +88,7 @@ fn is_inner_comment(token: SyntaxToken) -> bool {
 }
 
 /// Insert an import path into the given file/node. A `merge` value of none indicates that no import merging is allowed to occur.
-pub fn insert_use<'a>(
+pub(crate) fn insert_use<'a>(
     scope: &ImportScope,
     path: ast::Path,
     merge: Option<MergeBehaviour>,
diff --git a/crates/base_db/src/input.rs b/crates/base_db/src/input.rs
index 98ba372ad8..31907ed980 100644
--- a/crates/base_db/src/input.rs
+++ b/crates/base_db/src/input.rs
@@ -225,10 +225,7 @@ impl CrateGraph {
         to: CrateId,
     ) -> Result<(), CyclicDependenciesError> {
         if self.dfs_find(from, to, &mut FxHashSet::default()) {
-            return Err(CyclicDependenciesError {
-                from: (from, self[from].display_name.clone()),
-                to: (to, self[to].display_name.clone()),
-            });
+            return Err(CyclicDependenciesError);
         }
         self.arena.get_mut(&from).unwrap().add_dep(name, to);
         Ok(())
@@ -424,20 +421,7 @@ impl fmt::Display for ParseEditionError {
 impl std::error::Error for ParseEditionError {}
 
 #[derive(Debug)]
-pub struct CyclicDependenciesError {
-    from: (CrateId, Option<CrateDisplayName>),
-    to: (CrateId, Option<CrateDisplayName>),
-}
-
-impl fmt::Display for CyclicDependenciesError {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        let render = |(id, name): &(CrateId, Option<CrateDisplayName>)| match name {
-            Some(it) => format!("{}({:?})", it, id),
-            None => format!("{:?}", id),
-        };
-        write!(f, "cyclic deps: {} -> {}", render(&self.from), render(&self.to))
-    }
-}
+pub struct CyclicDependenciesError;
 
 #[cfg(test)]
 mod tests {
diff --git a/crates/cfg/src/lib.rs b/crates/cfg/src/lib.rs
index d88ecf8b09..d0e08cf5f7 100644
--- a/crates/cfg/src/lib.rs
+++ b/crates/cfg/src/lib.rs
@@ -41,6 +41,12 @@ impl CfgOptions {
         self.enabled.insert(CfgAtom::KeyValue { key, value });
     }
 
+    pub fn append(&mut self, other: &CfgOptions) {
+        for atom in &other.enabled {
+            self.enabled.insert(atom.clone());
+        }
+    }
+
     pub fn apply_diff(&mut self, diff: CfgDiff) {
         for atom in diff.enable {
             self.enabled.insert(atom);
diff --git a/crates/completion/Cargo.toml b/crates/completion/Cargo.toml
index e7df9d9556..3015ec9e0e 100644
--- a/crates/completion/Cargo.toml
+++ b/crates/completion/Cargo.toml
@@ -13,7 +13,6 @@ doctest = false
 itertools = "0.9.0"
 log = "0.4.8"
 rustc-hash = "1.1.0"
-either = "1.6.1"
 
 assists = { path = "../assists", version = "0.0.0" }
 stdx = { path = "../stdx", version = "0.0.0" }
diff --git a/crates/completion/src/completions.rs b/crates/completion/src/completions.rs
index 9b7d6c5809..75dbb1a23b 100644
--- a/crates/completion/src/completions.rs
+++ b/crates/completion/src/completions.rs
@@ -90,7 +90,7 @@ impl Completions {
             Some(it) => it,
             None => return,
         };
-        if let Some(item) = render_macro(RenderContext::new(ctx), None, name, macro_) {
+        if let Some(item) = render_macro(RenderContext::new(ctx), name, macro_) {
             self.add(item);
         }
     }
@@ -101,7 +101,7 @@ impl Completions {
         func: hir::Function,
         local_name: Option<String>,
     ) {
-        let item = render_fn(RenderContext::new(ctx), None, local_name, func);
+        let item = render_fn(RenderContext::new(ctx), local_name, func);
         self.add(item)
     }
 
@@ -123,7 +123,7 @@ impl Completions {
         variant: hir::EnumVariant,
         path: ModPath,
     ) {
-        let item = render_enum_variant(RenderContext::new(ctx), None, None, variant, Some(path));
+        let item = render_enum_variant(RenderContext::new(ctx), None, variant, Some(path));
         self.add(item);
     }
 
@@ -133,7 +133,7 @@ impl Completions {
         variant: hir::EnumVariant,
         local_name: Option<String>,
     ) {
-        let item = render_enum_variant(RenderContext::new(ctx), None, local_name, variant, None);
+        let item = render_enum_variant(RenderContext::new(ctx), local_name, variant, None);
         self.add(item);
     }
 }
diff --git a/crates/completion/src/completions/unqualified_path.rs b/crates/completion/src/completions/unqualified_path.rs
index 86c143b637..7df58e1da8 100644
--- a/crates/completion/src/completions/unqualified_path.rs
+++ b/crates/completion/src/completions/unqualified_path.rs
@@ -1,16 +1,10 @@
 //! Completion of names from the current scope, e.g. locals and imported items.
 
-use assists::utils::ImportScope;
-use either::Either;
 use hir::{Adt, ModuleDef, ScopeDef, Type};
-use ide_db::imports_locator;
 use syntax::AstNode;
 use test_utils::mark;
 
-use crate::{
-    render::{render_resolution_with_import, RenderContext},
-    CompletionContext, Completions,
-};
+use crate::{CompletionContext, Completions};
 
 pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) {
     if !(ctx.is_trivial_path || ctx.is_pat_binding_or_const) {
@@ -43,8 +37,6 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC
         }
         acc.add_resolution(ctx, name.to_string(), &res)
     });
-
-    fuzzy_completion(acc, ctx).unwrap_or_default()
 }
 
 fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext, ty: &Type) {
@@ -71,45 +63,6 @@ fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext, ty: &T
     }
 }
 
-fn fuzzy_completion(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
-    let _p = profile::span("fuzzy_completion");
-    let current_module = ctx.scope.module()?;
-    let anchor = ctx.name_ref_syntax.as_ref()?;
-    let import_scope = ImportScope::find_insert_use_container(anchor.syntax(), &ctx.sema)?;
-
-    let potential_import_name = ctx.token.to_string();
-
-    let possible_imports =
-        imports_locator::find_similar_imports(&ctx.sema, ctx.krate?, &potential_import_name, 400)
-            .filter_map(|import_candidate| match import_candidate {
-                // when completing outside the use declaration, modules are pretty useless
-                // and tend to bloat the completion suggestions a lot
-                Either::Left(ModuleDef::Module(_)) => None,
-                Either::Left(module_def) => Some((
-                    current_module.find_use_path(ctx.db, module_def)?,
-                    ScopeDef::ModuleDef(module_def),
-                )),
-                Either::Right(macro_def) => Some((
-                    current_module.find_use_path(ctx.db, macro_def)?,
-                    ScopeDef::MacroDef(macro_def),
-                )),
-            })
-            .filter(|(mod_path, _)| mod_path.len() > 1)
-            .filter_map(|(import_path, definition)| {
-                render_resolution_with_import(
-                    RenderContext::new(ctx),
-                    import_path.clone(),
-                    import_scope.clone(),
-                    ctx.config.merge,
-                    &definition,
-                )
-            })
-            .take(20);
-
-    acc.add_all(possible_imports);
-    Some(())
-}
-
 #[cfg(test)]
 mod tests {
     use expect_test::{expect, Expect};
@@ -723,85 +676,4 @@ impl My<|>
             "#]],
         )
     }
-
-    #[test]
-    fn function_fuzzy_completion() {
-        check_edit(
-            "stdin",
-            r#"
-//- /lib.rs crate:dep
-pub mod io {
-    pub fn stdin() {}
-};
-
-//- /main.rs crate:main deps:dep
-fn main() {
-    stdi<|>
-}
-"#,
-            r#"
-use dep::io::stdin;
-
-fn main() {
-    stdin()$0
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn macro_fuzzy_completion() {
-        check_edit(
-            "macro_with_curlies!",
-            r#"
-//- /lib.rs crate:dep
-/// Please call me as macro_with_curlies! {}
-#[macro_export]
-macro_rules! macro_with_curlies {
-    () => {}
-}
-
-//- /main.rs crate:main deps:dep
-fn main() {
-    curli<|>
-}
-"#,
-            r#"
-use dep::macro_with_curlies;
-
-fn main() {
-    macro_with_curlies! {$0}
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn struct_fuzzy_completion() {
-        check_edit(
-            "ThirdStruct",
-            r#"
-//- /lib.rs crate:dep
-pub struct FirstStruct;
-pub mod some_module {
-    pub struct SecondStruct;
-    pub struct ThirdStruct;
-}
-
-//- /main.rs crate:main deps:dep
-use dep::{FirstStruct, some_module::SecondStruct};
-
-fn main() {
-    this<|>
-}
-"#,
-            r#"
-use dep::{FirstStruct, some_module::{SecondStruct, ThirdStruct}};
-
-fn main() {
-    ThirdStruct
-}
-"#,
-        );
-    }
 }
diff --git a/crates/completion/src/config.rs b/crates/completion/src/config.rs
index 82874ff256..71b49ace8b 100644
--- a/crates/completion/src/config.rs
+++ b/crates/completion/src/config.rs
@@ -4,15 +4,12 @@
 //! module, and we use to statically check that we only produce snippet
 //! completions if we are allowed to.
 
-use assists::utils::MergeBehaviour;
-
 #[derive(Clone, Debug, PartialEq, Eq)]
 pub struct CompletionConfig {
     pub enable_postfix_completions: bool,
     pub add_call_parenthesis: bool,
     pub add_call_argument_snippets: bool,
     pub snippet_cap: Option<SnippetCap>,
-    pub merge: Option<MergeBehaviour>,
 }
 
 impl CompletionConfig {
@@ -33,7 +30,6 @@ impl Default for CompletionConfig {
             add_call_parenthesis: true,
             add_call_argument_snippets: true,
             snippet_cap: Some(SnippetCap { _private: () }),
-            merge: Some(MergeBehaviour::Full),
         }
     }
 }
diff --git a/crates/completion/src/item.rs b/crates/completion/src/item.rs
index b13c3f3762..6d1d085f4b 100644
--- a/crates/completion/src/item.rs
+++ b/crates/completion/src/item.rs
@@ -2,9 +2,8 @@
 
 use std::fmt;
 
-use assists::utils::{insert_use, mod_path_to_ast, ImportScope, MergeBehaviour};
-use hir::{Documentation, ModPath, Mutability};
-use syntax::{algo, TextRange};
+use hir::{Documentation, Mutability};
+use syntax::TextRange;
 use text_edit::TextEdit;
 
 use crate::config::SnippetCap;
@@ -32,7 +31,6 @@ pub struct CompletionItem {
     ///
     /// Typically, replaces `source_range` with new identifier.
     text_edit: TextEdit,
-
     insert_text_format: InsertTextFormat,
 
     /// What item (struct, function, etc) are we completing.
@@ -201,10 +199,8 @@ impl CompletionItem {
             trigger_call_info: None,
             score: None,
             ref_match: None,
-            import_data: None,
         }
     }
-
     /// What user sees in pop-up in the UI.
     pub fn label(&self) -> &str {
         &self.label
@@ -261,7 +257,6 @@ impl CompletionItem {
 pub(crate) struct Builder {
     source_range: TextRange,
     completion_kind: CompletionKind,
-    import_data: Option<(ModPath, ImportScope, Option<MergeBehaviour>)>,
     label: String,
     insert_text: Option<String>,
     insert_text_format: InsertTextFormat,
@@ -278,50 +273,23 @@ pub(crate) struct Builder {
 
 impl Builder {
     pub(crate) fn build(self) -> CompletionItem {
-        let mut label = self.label;
-        let mut lookup = self.lookup;
-        let mut insert_text = self.insert_text;
-        let mut text_edits = TextEdit::builder();
-
-        if let Some((import_path, import_scope, merge_behaviour)) = self.import_data {
-            let import = mod_path_to_ast(&import_path);
-            let mut import_path_without_last_segment = import_path;
-            let _ = import_path_without_last_segment.segments.pop();
-
-            if !import_path_without_last_segment.segments.is_empty() {
-                if lookup.is_none() {
-                    lookup = Some(label.clone());
-                }
-                if insert_text.is_none() {
-                    insert_text = Some(label.clone());
-                }
-                label = format!("{}::{}", import_path_without_last_segment, label);
-            }
-
-            let rewriter = insert_use(&import_scope, import, merge_behaviour);
-            if let Some(old_ast) = rewriter.rewrite_root() {
-                algo::diff(&old_ast, &rewriter.rewrite(&old_ast)).into_text_edit(&mut text_edits);
-            }
-        }
-
-        let original_edit = match self.text_edit {
+        let label = self.label;
+        let text_edit = match self.text_edit {
             Some(it) => it,
-            None => {
-                TextEdit::replace(self.source_range, insert_text.unwrap_or_else(|| label.clone()))
-            }
+            None => TextEdit::replace(
+                self.source_range,
+                self.insert_text.unwrap_or_else(|| label.clone()),
+            ),
         };
 
-        let mut resulting_edit = text_edits.finish();
-        resulting_edit.union(original_edit).expect("Failed to unite text edits");
-
         CompletionItem {
             source_range: self.source_range,
             label,
             insert_text_format: self.insert_text_format,
-            text_edit: resulting_edit,
+            text_edit,
             detail: self.detail,
             documentation: self.documentation,
-            lookup,
+            lookup: self.lookup,
             kind: self.kind,
             completion_kind: self.completion_kind,
             deprecated: self.deprecated.unwrap_or(false),
@@ -390,13 +358,6 @@ impl Builder {
         self.trigger_call_info = Some(true);
         self
     }
-    pub(crate) fn import_data(
-        mut self,
-        import_data: Option<(ModPath, ImportScope, Option<MergeBehaviour>)>,
-    ) -> Builder {
-        self.import_data = import_data;
-        self
-    }
     pub(crate) fn set_ref_match(
         mut self,
         ref_match: Option<(Mutability, CompletionScore)>,
diff --git a/crates/completion/src/render.rs b/crates/completion/src/render.rs
index e892d4de85..1fa02c3752 100644
--- a/crates/completion/src/render.rs
+++ b/crates/completion/src/render.rs
@@ -9,8 +9,7 @@ pub(crate) mod type_alias;
 
 mod builder_ext;
 
-use assists::utils::{ImportScope, MergeBehaviour};
-use hir::{Documentation, HasAttrs, HirDisplay, ModPath, Mutability, ScopeDef, Type};
+use hir::{Documentation, HasAttrs, HirDisplay, Mutability, ScopeDef, Type};
 use ide_db::RootDatabase;
 use syntax::TextRange;
 use test_utils::mark;
@@ -43,22 +42,7 @@ pub(crate) fn render_resolution<'a>(
     local_name: String,
     resolution: &ScopeDef,
 ) -> Option<CompletionItem> {
-    Render::new(ctx).render_resolution(local_name, None, resolution)
-}
-
-pub(crate) fn render_resolution_with_import<'a>(
-    ctx: RenderContext<'a>,
-    import: ModPath,
-    import_scope: ImportScope,
-    merge_behaviour: Option<MergeBehaviour>,
-    resolution: &ScopeDef,
-) -> Option<CompletionItem> {
-    let local_name = import.segments.last()?.to_string();
-    Render::new(ctx).render_resolution(
-        local_name,
-        Some((import, import_scope, merge_behaviour)),
-        resolution,
-    )
+    Render::new(ctx).render_resolution(local_name, resolution)
 }
 
 /// Interface for data and methods required for items rendering.
@@ -147,7 +131,6 @@ impl<'a> Render<'a> {
     fn render_resolution(
         self,
         local_name: String,
-        import_data: Option<(ModPath, ImportScope, Option<MergeBehaviour>)>,
         resolution: &ScopeDef,
     ) -> Option<CompletionItem> {
         use hir::ModuleDef::*;
@@ -159,15 +142,15 @@ impl<'a> Render<'a> {
 
         let kind = match resolution {
             ScopeDef::ModuleDef(Function(func)) => {
-                let item = render_fn(self.ctx, import_data, Some(local_name), *func);
+                let item = render_fn(self.ctx, Some(local_name), *func);
                 return Some(item);
             }
             ScopeDef::ModuleDef(EnumVariant(var)) => {
-                let item = render_enum_variant(self.ctx, import_data, Some(local_name), *var, None);
+                let item = render_enum_variant(self.ctx, Some(local_name), *var, None);
                 return Some(item);
             }
             ScopeDef::MacroDef(mac) => {
-                let item = render_macro(self.ctx, import_data, local_name, *mac);
+                let item = render_macro(self.ctx, local_name, *mac);
                 return item;
             }
 
@@ -192,7 +175,6 @@ impl<'a> Render<'a> {
                     local_name,
                 )
                 .kind(CompletionItemKind::UnresolvedReference)
-                .import_data(import_data)
                 .build();
                 return Some(item);
             }
@@ -245,12 +227,7 @@ impl<'a> Render<'a> {
             }
         }
 
-        let item = item
-            .kind(kind)
-            .import_data(import_data)
-            .set_documentation(docs)
-            .set_ref_match(ref_match)
-            .build();
+        let item = item.kind(kind).set_documentation(docs).set_ref_match(ref_match).build();
         Some(item)
     }
 
@@ -448,28 +425,6 @@ fn main() { let _: m::Spam = S<|> }
                         insert: "m",
                         kind: Module,
                     },
-                    CompletionItem {
-                        label: "m::Spam",
-                        source_range: 75..76,
-                        text_edit: TextEdit {
-                            indels: [
-                                Indel {
-                                    insert: "use m::Spam;",
-                                    delete: 0..0,
-                                },
-                                Indel {
-                                    insert: "\n\n",
-                                    delete: 0..0,
-                                },
-                                Indel {
-                                    insert: "Spam",
-                                    delete: 75..76,
-                                },
-                            ],
-                        },
-                        kind: Enum,
-                        lookup: "Spam",
-                    },
                     CompletionItem {
                         label: "m::Spam::Foo",
                         source_range: 75..76,
diff --git a/crates/completion/src/render/enum_variant.rs b/crates/completion/src/render/enum_variant.rs
index 6070e9b1d7..fd412ed0ee 100644
--- a/crates/completion/src/render/enum_variant.rs
+++ b/crates/completion/src/render/enum_variant.rs
@@ -1,6 +1,5 @@
 //! Renderer for `enum` variants.
 
-use assists::utils::{ImportScope, MergeBehaviour};
 use hir::{HasAttrs, HirDisplay, ModPath, StructKind};
 use itertools::Itertools;
 use test_utils::mark;
@@ -12,12 +11,11 @@ use crate::{
 
 pub(crate) fn render_enum_variant<'a>(
     ctx: RenderContext<'a>,
-    import_data: Option<(ModPath, ImportScope, Option<MergeBehaviour>)>,
     local_name: Option<String>,
     variant: hir::EnumVariant,
     path: Option<ModPath>,
 ) -> CompletionItem {
-    EnumVariantRender::new(ctx, local_name, variant, path).render(import_data)
+    EnumVariantRender::new(ctx, local_name, variant, path).render()
 }
 
 #[derive(Debug)]
@@ -62,10 +60,7 @@ impl<'a> EnumVariantRender<'a> {
         }
     }
 
-    fn render(
-        self,
-        import_data: Option<(ModPath, ImportScope, Option<MergeBehaviour>)>,
-    ) -> CompletionItem {
+    fn render(self) -> CompletionItem {
         let mut builder = CompletionItem::new(
             CompletionKind::Reference,
             self.ctx.source_range(),
@@ -74,7 +69,6 @@ impl<'a> EnumVariantRender<'a> {
         .kind(CompletionItemKind::EnumVariant)
         .set_documentation(self.variant.docs(self.ctx.db()))
         .set_deprecated(self.ctx.is_deprecated(self.variant))
-        .import_data(import_data)
         .detail(self.detail());
 
         if self.variant_kind == StructKind::Tuple {
diff --git a/crates/completion/src/render/function.rs b/crates/completion/src/render/function.rs
index 9dd5cd18c5..4fa6eafd72 100644
--- a/crates/completion/src/render/function.rs
+++ b/crates/completion/src/render/function.rs
@@ -1,7 +1,6 @@
 //! Renderer for function calls.
 
-use assists::utils::{ImportScope, MergeBehaviour};
-use hir::{HasSource, ModPath, Type};
+use hir::{HasSource, Type};
 use syntax::{ast::Fn, display::function_declaration};
 
 use crate::{
@@ -11,11 +10,10 @@ use crate::{
 
 pub(crate) fn render_fn<'a>(
     ctx: RenderContext<'a>,
-    import_data: Option<(ModPath, ImportScope, Option<MergeBehaviour>)>,
     local_name: Option<String>,
     fn_: hir::Function,
 ) -> CompletionItem {
-    FunctionRender::new(ctx, local_name, fn_).render(import_data)
+    FunctionRender::new(ctx, local_name, fn_).render()
 }
 
 #[derive(Debug)]
@@ -38,10 +36,7 @@ impl<'a> FunctionRender<'a> {
         FunctionRender { ctx, name, fn_, ast_node }
     }
 
-    fn render(
-        self,
-        import_data: Option<(ModPath, ImportScope, Option<MergeBehaviour>)>,
-    ) -> CompletionItem {
+    fn render(self) -> CompletionItem {
         let params = self.params();
         CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), self.name.clone())
             .kind(self.kind())
@@ -49,7 +44,6 @@ impl<'a> FunctionRender<'a> {
             .set_deprecated(self.ctx.is_deprecated(self.fn_))
             .detail(self.detail())
             .add_call_parens(self.ctx.completion, self.name, params)
-            .import_data(import_data)
             .build()
     }
 
diff --git a/crates/completion/src/render/macro_.rs b/crates/completion/src/render/macro_.rs
index fead59e41c..96be59cc33 100644
--- a/crates/completion/src/render/macro_.rs
+++ b/crates/completion/src/render/macro_.rs
@@ -1,7 +1,6 @@
 //! Renderer for macro invocations.
 
-use assists::utils::{ImportScope, MergeBehaviour};
-use hir::{Documentation, HasSource, ModPath};
+use hir::{Documentation, HasSource};
 use syntax::display::macro_label;
 use test_utils::mark;
 
@@ -12,11 +11,10 @@ use crate::{
 
 pub(crate) fn render_macro<'a>(
     ctx: RenderContext<'a>,
-    import_data: Option<(ModPath, ImportScope, Option<MergeBehaviour>)>,
     name: String,
     macro_: hir::MacroDef,
 ) -> Option<CompletionItem> {
-    MacroRender::new(ctx, name, macro_).render(import_data)
+    MacroRender::new(ctx, name, macro_).render()
 }
 
 #[derive(Debug)]
@@ -38,10 +36,7 @@ impl<'a> MacroRender<'a> {
         MacroRender { ctx, name, macro_, docs, bra, ket }
     }
 
-    fn render(
-        &self,
-        import_data: Option<(ModPath, ImportScope, Option<MergeBehaviour>)>,
-    ) -> Option<CompletionItem> {
+    fn render(&self) -> Option<CompletionItem> {
         // FIXME: Currently proc-macro do not have ast-node,
         // such that it does not have source
         if self.macro_.is_proc_macro() {
@@ -53,7 +48,6 @@ impl<'a> MacroRender<'a> {
                 .kind(CompletionItemKind::Macro)
                 .set_documentation(self.docs.clone())
                 .set_deprecated(self.ctx.is_deprecated(self.macro_))
-                .import_data(import_data)
                 .detail(self.detail());
 
         let needs_bang = self.needs_bang();
diff --git a/crates/hir/src/code_model.rs b/crates/hir/src/code_model.rs
index 37ed092ad0..30a5e45809 100644
--- a/crates/hir/src/code_model.rs
+++ b/crates/hir/src/code_model.rs
@@ -110,9 +110,15 @@ impl Crate {
     pub fn query_external_importables(
         self,
         db: &dyn DefDatabase,
-        query: import_map::Query,
+        query: &str,
     ) -> impl Iterator<Item = Either<ModuleDef, MacroDef>> {
-        import_map::search_dependencies(db, self.into(), query).into_iter().map(|item| match item {
+        import_map::search_dependencies(
+            db,
+            self.into(),
+            import_map::Query::new(query).anchor_end().case_sensitive().limit(40),
+        )
+        .into_iter()
+        .map(|item| match item {
             ItemInNs::Types(mod_id) | ItemInNs::Values(mod_id) => Either::Left(mod_id.into()),
             ItemInNs::Macros(mac_id) => Either::Right(mac_id.into()),
         })
diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs
index d9ad8db6f7..c18c1c5873 100644
--- a/crates/hir/src/diagnostics.rs
+++ b/crates/hir/src/diagnostics.rs
@@ -1,8 +1,6 @@
 //! FIXME: write short doc here
 pub use hir_def::diagnostics::{InactiveCode, UnresolvedModule};
-pub use hir_expand::diagnostics::{
-    Diagnostic, DiagnosticCode, DiagnosticSink, DiagnosticSinkBuilder,
-};
+pub use hir_expand::diagnostics::{Diagnostic, DiagnosticSink, DiagnosticSinkBuilder};
 pub use hir_ty::diagnostics::{
     IncorrectCase, MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkInTailExpr,
     NoSuchField,
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index 5fea25ef1b..0d184379f1 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -49,7 +49,6 @@ pub use hir_def::{
     builtin_type::BuiltinType,
     docs::Documentation,
     find_path::PrefixKind,
-    import_map,
     item_scope::ItemInNs,
     nameres::ModuleSource,
     path::{ModPath, PathKind},
diff --git a/crates/hir_expand/src/diagnostics.rs b/crates/hir_expand/src/diagnostics.rs
index 1043c6aeb5..78ccc212c8 100644
--- a/crates/hir_expand/src/diagnostics.rs
+++ b/crates/hir_expand/src/diagnostics.rs
@@ -20,7 +20,7 @@ use syntax::SyntaxNodePtr;
 
 use crate::InFile;
 
-#[derive(Copy, Clone, Debug, PartialEq)]
+#[derive(Copy, Clone, PartialEq)]
 pub struct DiagnosticCode(pub &'static str);
 
 impl DiagnosticCode {
diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs
index 3df73ed4fa..1c7f027632 100644
--- a/crates/ide/src/diagnostics.rs
+++ b/crates/ide/src/diagnostics.rs
@@ -10,7 +10,7 @@ mod field_shorthand;
 use std::cell::RefCell;
 
 use hir::{
-    diagnostics::{Diagnostic as _, DiagnosticCode, DiagnosticSinkBuilder},
+    diagnostics::{Diagnostic as _, DiagnosticSinkBuilder},
     Semantics,
 };
 use ide_db::base_db::SourceDatabase;
@@ -35,23 +35,15 @@ pub struct Diagnostic {
     pub severity: Severity,
     pub fix: Option<Fix>,
     pub unused: bool,
-    pub code: Option<DiagnosticCode>,
 }
 
 impl Diagnostic {
     fn error(range: TextRange, message: String) -> Self {
-        Self { message, range, severity: Severity::Error, fix: None, unused: false, code: None }
+        Self { message, range, severity: Severity::Error, fix: None, unused: false }
     }
 
     fn hint(range: TextRange, message: String) -> Self {
-        Self {
-            message,
-            range,
-            severity: Severity::WeakWarning,
-            fix: None,
-            unused: false,
-            code: None,
-        }
+        Self { message, range, severity: Severity::WeakWarning, fix: None, unused: false }
     }
 
     fn with_fix(self, fix: Option<Fix>) -> Self {
@@ -61,10 +53,6 @@ impl Diagnostic {
     fn with_unused(self, unused: bool) -> Self {
         Self { unused, ..self }
     }
-
-    fn with_code(self, code: Option<DiagnosticCode>) -> Self {
-        Self { code, ..self }
-    }
 }
 
 #[derive(Debug)]
@@ -138,8 +126,7 @@ pub(crate) fn diagnostics(
             // Override severity and mark as unused.
             res.borrow_mut().push(
                 Diagnostic::hint(sema.diagnostics_display_range(d).range, d.message())
-                    .with_unused(true)
-                    .with_code(Some(d.code())),
+                    .with_unused(true),
             );
         })
         // Only collect experimental diagnostics when they're enabled.
@@ -150,10 +137,8 @@ pub(crate) fn diagnostics(
     let mut sink = sink_builder
         // Diagnostics not handled above get no fix and default treatment.
         .build(|d| {
-            res.borrow_mut().push(
-                Diagnostic::error(sema.diagnostics_display_range(d).range, d.message())
-                    .with_code(Some(d.code())),
-            );
+            res.borrow_mut()
+                .push(Diagnostic::error(sema.diagnostics_display_range(d).range, d.message()));
         });
 
     if let Some(m) = sema.to_module_def(file_id) {
@@ -164,15 +149,11 @@ pub(crate) fn diagnostics(
 }
 
 fn diagnostic_with_fix<D: DiagnosticWithFix>(d: &D, sema: &Semantics<RootDatabase>) -> Diagnostic {
-    Diagnostic::error(sema.diagnostics_display_range(d).range, d.message())
-        .with_fix(d.fix(&sema))
-        .with_code(Some(d.code()))
+    Diagnostic::error(sema.diagnostics_display_range(d).range, d.message()).with_fix(d.fix(&sema))
 }
 
 fn warning_with_fix<D: DiagnosticWithFix>(d: &D, sema: &Semantics<RootDatabase>) -> Diagnostic {
-    Diagnostic::hint(sema.diagnostics_display_range(d).range, d.message())
-        .with_fix(d.fix(&sema))
-        .with_code(Some(d.code()))
+    Diagnostic::hint(sema.diagnostics_display_range(d).range, d.message()).with_fix(d.fix(&sema))
 }
 
 fn check_unnecessary_braces_in_use_statement(
@@ -608,11 +589,6 @@ fn test_fn() {
                             },
                         ),
                         unused: false,
-                        code: Some(
-                            DiagnosticCode(
-                                "unresolved-module",
-                            ),
-                        ),
                     },
                 ]
             "#]],
diff --git a/crates/ide/src/fn_references.rs b/crates/ide/src/fn_references.rs
index 5cbbe306e7..459f201ed8 100644
--- a/crates/ide/src/fn_references.rs
+++ b/crates/ide/src/fn_references.rs
@@ -1,12 +1,11 @@
 //! This module implements a methods and free functions search in the specified file.
 //! We have to skip tests, so cannot reuse file_structure module.
 
-use assists::utils::test_related_attribute;
 use hir::Semantics;
 use ide_db::RootDatabase;
 use syntax::{ast, ast::NameOwner, AstNode, SyntaxNode};
 
-use crate::{FileId, FileRange};
+use crate::{runnables::has_test_related_attribute, FileId, FileRange};
 
 pub(crate) fn find_all_methods(db: &RootDatabase, file_id: FileId) -> Vec<FileRange> {
     let sema = Semantics::new(db);
@@ -16,7 +15,7 @@ pub(crate) fn find_all_methods(db: &RootDatabase, file_id: FileId) -> Vec<FileRa
 
 fn method_range(item: SyntaxNode, file_id: FileId) -> Option<FileRange> {
     ast::Fn::cast(item).and_then(|fn_def| {
-        if test_related_attribute(&fn_def).is_some() {
+        if has_test_related_attribute(&fn_def) {
             None
         } else {
             fn_def.name().map(|name| FileRange { file_id, range: name.syntax().text_range() })
diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs
index e154117775..2bd0e86e54 100644
--- a/crates/ide/src/runnables.rs
+++ b/crates/ide/src/runnables.rs
@@ -1,6 +1,5 @@
 use std::fmt;
 
-use assists::utils::test_related_attribute;
 use cfg::CfgExpr;
 use hir::{AsAssocItem, Attrs, HirFileId, InFile, Semantics};
 use ide_db::RootDatabase;
@@ -157,7 +156,7 @@ fn runnable_fn(
             None => TestId::Name(name_string),
         };
 
-        if test_related_attribute(&fn_def).is_some() {
+        if has_test_related_attribute(&fn_def) {
             let attr = TestAttr::from_fn(&fn_def);
             RunnableKind::Test { test_id, attr }
         } else if fn_def.has_atom_attr("bench") {
@@ -236,6 +235,20 @@ impl TestAttr {
     }
 }
 
+/// This is a method with a heuristics to support test methods annotated with custom test annotations, such as
+/// `#[test_case(...)]`, `#[tokio::test]` and similar.
+/// Also a regular `#[test]` annotation is supported.
+///
+/// It may produce false positives, for example, `#[wasm_bindgen_test]` requires a different command to run the test,
+/// but it's better than not to have the runnables for the tests at all.
+pub(crate) fn has_test_related_attribute(fn_def: &ast::Fn) -> bool {
+    fn_def
+        .attrs()
+        .filter_map(|attr| attr.path())
+        .map(|path| path.syntax().to_string().to_lowercase())
+        .any(|attribute_text| attribute_text.contains("test"))
+}
+
 const RUSTDOC_FENCE: &str = "```";
 const RUSTDOC_CODE_BLOCK_ATTRIBUTES_RUNNABLE: &[&str] =
     &["", "rust", "should_panic", "edition2015", "edition2018"];
@@ -294,7 +307,7 @@ fn has_test_function_or_multiple_test_submodules(module: &ast::Module) -> bool {
         for item in item_list.items() {
             match item {
                 ast::Item::Fn(f) => {
-                    if test_related_attribute(&f).is_some() {
+                    if has_test_related_attribute(&f) {
                         return true;
                     }
                 }
diff --git a/crates/ide_db/src/imports_locator.rs b/crates/ide_db/src/imports_locator.rs
index 9d8ea7368d..df74be00bb 100644
--- a/crates/ide_db/src/imports_locator.rs
+++ b/crates/ide_db/src/imports_locator.rs
@@ -1,70 +1,36 @@
 //! This module contains an import search funcionality that is provided to the assists module.
 //! Later, this should be moved away to a separate crate that is accessible from the assists module.
 
-use hir::{import_map, Crate, MacroDef, ModuleDef, Semantics};
+use hir::{Crate, MacroDef, ModuleDef, Semantics};
 use syntax::{ast, AstNode, SyntaxKind::NAME};
 
 use crate::{
     defs::{Definition, NameClass},
-    symbol_index::{self, FileSymbol},
+    symbol_index::{self, FileSymbol, Query},
     RootDatabase,
 };
 use either::Either;
 use rustc_hash::FxHashSet;
 
-pub fn find_exact_imports<'a>(
+pub fn find_imports<'a>(
     sema: &Semantics<'a, RootDatabase>,
     krate: Crate,
     name_to_import: &str,
-) -> impl Iterator<Item = Either<ModuleDef, MacroDef>> {
-    let _p = profile::span("find_exact_imports");
-    find_imports(
-        sema,
-        krate,
-        {
-            let mut local_query = symbol_index::Query::new(name_to_import.to_string());
-            local_query.exact();
-            local_query.limit(40);
-            local_query
-        },
-        import_map::Query::new(name_to_import).anchor_end().case_sensitive().limit(40),
-    )
-}
-
-pub fn find_similar_imports<'a>(
-    sema: &Semantics<'a, RootDatabase>,
-    krate: Crate,
-    name_to_import: &str,
-    limit: usize,
-) -> impl Iterator<Item = Either<ModuleDef, MacroDef>> {
-    let _p = profile::span("find_similar_imports");
-    find_imports(
-        sema,
-        krate,
-        {
-            let mut local_query = symbol_index::Query::new(name_to_import.to_string());
-            local_query.limit(limit);
-            local_query
-        },
-        import_map::Query::new(name_to_import).limit(limit),
-    )
-}
-
-fn find_imports<'a>(
-    sema: &Semantics<'a, RootDatabase>,
-    krate: Crate,
-    local_query: symbol_index::Query,
-    external_query: import_map::Query,
-) -> impl Iterator<Item = Either<ModuleDef, MacroDef>> {
-    let _p = profile::span("find_similar_imports");
+) -> Vec<Either<ModuleDef, MacroDef>> {
+    let _p = profile::span("search_for_imports");
     let db = sema.db;
 
     // Query dependencies first.
     let mut candidates: FxHashSet<_> =
-        krate.query_external_importables(db, external_query).collect();
+        krate.query_external_importables(db, name_to_import).collect();
 
     // Query the local crate using the symbol index.
-    let local_results = symbol_index::crate_symbols(db, krate.into(), local_query);
+    let local_results = {
+        let mut query = Query::new(name_to_import.to_string());
+        query.exact();
+        query.limit(40);
+        symbol_index::crate_symbols(db, krate.into(), query)
+    };
 
     candidates.extend(
         local_results
@@ -77,7 +43,7 @@ fn find_imports<'a>(
             }),
     );
 
-    candidates.into_iter()
+    candidates.into_iter().collect()
 }
 
 fn get_name_definition<'a>(
diff --git a/crates/project_model/src/sysroot.rs b/crates/project_model/src/sysroot.rs
index f0a43eaf6b..b0e8863f6a 100644
--- a/crates/project_model/src/sysroot.rs
+++ b/crates/project_model/src/sysroot.rs
@@ -37,7 +37,7 @@ impl Sysroot {
     pub fn public_deps(&self) -> impl Iterator<Item = (&'static str, SysrootCrate)> + '_ {
         // core is added as a dependency before std in order to
         // mimic rustcs dependency order
-        ["core", "alloc", "std"].iter().filter_map(move |&it| Some((it, self.by_name(it)?)))
+        vec!["core", "alloc", "std"].into_iter().filter_map(move |it| Some((it, self.by_name(it)?)))
     }
 
     pub fn proc_macro(&self) -> Option<SysrootCrate> {
diff --git a/crates/project_model/src/workspace.rs b/crates/project_model/src/workspace.rs
index a71f96164d..9ebb0a811b 100644
--- a/crates/project_model/src/workspace.rs
+++ b/crates/project_model/src/workspace.rs
@@ -12,8 +12,8 @@ use proc_macro_api::ProcMacroClient;
 use rustc_hash::{FxHashMap, FxHashSet};
 
 use crate::{
-    cargo_workspace, cfg_flag::CfgFlag, sysroot::SysrootCrate, utf8_stdout, CargoConfig,
-    CargoWorkspace, ProjectJson, ProjectManifest, Sysroot, TargetKind,
+    cargo_workspace, cfg_flag::CfgFlag, utf8_stdout, CargoConfig, CargoWorkspace, ProjectJson,
+    ProjectManifest, Sysroot, TargetKind,
 };
 
 /// `PackageRoot` describes a package root folder.
@@ -70,8 +70,12 @@ impl ProjectWorkspace {
                     format!("Failed to deserialize json file {}", project_json.display())
                 })?;
                 let project_location = project_json.parent().unwrap().to_path_buf();
-                let project_json = ProjectJson::new(&project_location, data);
-                ProjectWorkspace::load_inline(project_json)?
+                let project = ProjectJson::new(&project_location, data);
+                let sysroot = match &project.sysroot_src {
+                    Some(path) => Some(Sysroot::load(path)?),
+                    None => None,
+                };
+                ProjectWorkspace::Json { project, sysroot }
             }
             ProjectManifest::CargoToml(cargo_toml) => {
                 let cargo_version = utf8_stdout({
@@ -146,38 +150,43 @@ impl ProjectWorkspace {
                     })
                 }))
                 .collect::<Vec<_>>(),
-            ProjectWorkspace::Cargo { cargo, sysroot, rustc } => cargo
-                .packages()
-                .map(|pkg| {
-                    let is_member = cargo[pkg].is_member;
-                    let pkg_root = cargo[pkg].root().to_path_buf();
-
-                    let mut include = vec![pkg_root.clone()];
-                    include.extend(cargo[pkg].out_dir.clone());
-
-                    let mut exclude = vec![pkg_root.join(".git")];
-                    if is_member {
-                        exclude.push(pkg_root.join("target"));
-                    } else {
-                        exclude.push(pkg_root.join("tests"));
-                        exclude.push(pkg_root.join("examples"));
-                        exclude.push(pkg_root.join("benches"));
-                    }
-                    PackageRoot { is_member, include, exclude }
-                })
-                .chain(sysroot.crates().map(|krate| PackageRoot {
-                    is_member: false,
-                    include: vec![sysroot[krate].root_dir().to_path_buf()],
-                    exclude: Vec::new(),
-                }))
-                .chain(rustc.into_iter().flat_map(|rustc| {
-                    rustc.packages().map(move |krate| PackageRoot {
+            ProjectWorkspace::Cargo { cargo, sysroot, rustc } => {
+                let roots = cargo
+                    .packages()
+                    .map(|pkg| {
+                        let is_member = cargo[pkg].is_member;
+                        let pkg_root = cargo[pkg].root().to_path_buf();
+
+                        let mut include = vec![pkg_root.clone()];
+                        include.extend(cargo[pkg].out_dir.clone());
+
+                        let mut exclude = vec![pkg_root.join(".git")];
+                        if is_member {
+                            exclude.push(pkg_root.join("target"));
+                        } else {
+                            exclude.push(pkg_root.join("tests"));
+                            exclude.push(pkg_root.join("examples"));
+                            exclude.push(pkg_root.join("benches"));
+                        }
+                        PackageRoot { is_member, include, exclude }
+                    })
+                    .chain(sysroot.crates().map(|krate| PackageRoot {
                         is_member: false,
-                        include: vec![rustc[krate].root().to_path_buf()],
+                        include: vec![sysroot[krate].root_dir().to_path_buf()],
                         exclude: Vec::new(),
-                    })
-                }))
-                .collect(),
+                    }));
+                if let Some(rustc_packages) = rustc {
+                    roots
+                        .chain(rustc_packages.packages().map(|krate| PackageRoot {
+                            is_member: false,
+                            include: vec![rustc_packages[krate].root().to_path_buf()],
+                            exclude: Vec::new(),
+                        }))
+                        .collect()
+                } else {
+                    roots.collect()
+                }
+            }
         }
     }
 
@@ -197,280 +206,312 @@ impl ProjectWorkspace {
         proc_macro_client: &ProcMacroClient,
         load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
     ) -> CrateGraph {
-        let mut crate_graph = match self {
+        let mut crate_graph = CrateGraph::default();
+        match self {
             ProjectWorkspace::Json { project, sysroot } => {
-                project_json_to_crate_graph(target, proc_macro_client, load, project, sysroot)
+                let sysroot_dps = sysroot
+                    .as_ref()
+                    .map(|sysroot| sysroot_to_crate_graph(&mut crate_graph, sysroot, target, load));
+
+                let mut cfg_cache: FxHashMap<Option<&str>, Vec<CfgFlag>> = FxHashMap::default();
+                let crates: FxHashMap<_, _> = project
+                    .crates()
+                    .filter_map(|(crate_id, krate)| {
+                        let file_path = &krate.root_module;
+                        let file_id = match load(&file_path) {
+                            Some(id) => id,
+                            None => {
+                                log::error!("failed to load crate root {}", file_path.display());
+                                return None;
+                            }
+                        };
+
+                        let env = krate.env.clone().into_iter().collect();
+                        let proc_macro = krate
+                            .proc_macro_dylib_path
+                            .clone()
+                            .map(|it| proc_macro_client.by_dylib_path(&it));
+
+                        let target = krate.target.as_deref().or(target);
+                        let target_cfgs = cfg_cache
+                            .entry(target)
+                            .or_insert_with(|| get_rustc_cfg_options(target));
+
+                        let mut cfg_options = CfgOptions::default();
+                        cfg_options.extend(target_cfgs.iter().chain(krate.cfg.iter()).cloned());
+
+                        Some((
+                            crate_id,
+                            crate_graph.add_crate_root(
+                                file_id,
+                                krate.edition,
+                                krate.display_name.clone(),
+                                cfg_options,
+                                env,
+                                proc_macro.unwrap_or_default(),
+                            ),
+                        ))
+                    })
+                    .collect();
+
+                for (from, krate) in project.crates() {
+                    if let Some(&from) = crates.get(&from) {
+                        if let Some((public_deps, _proc_macro)) = &sysroot_dps {
+                            for (name, to) in public_deps.iter() {
+                                if let Err(_) = crate_graph.add_dep(from, name.clone(), *to) {
+                                    log::error!("cyclic dependency on {} for {:?}", name, from)
+                                }
+                            }
+                        }
+
+                        for dep in &krate.deps {
+                            let to_crate_id = dep.crate_id;
+                            if let Some(&to) = crates.get(&to_crate_id) {
+                                if let Err(_) = crate_graph.add_dep(from, dep.name.clone(), to) {
+                                    log::error!("cyclic dependency {:?} -> {:?}", from, to);
+                                }
+                            }
+                        }
+                    }
+                }
             }
             ProjectWorkspace::Cargo { cargo, sysroot, rustc } => {
-                cargo_to_crate_graph(target, proc_macro_client, load, cargo, sysroot, rustc)
-            }
-        };
-        if crate_graph.patch_cfg_if() {
-            log::debug!("Patched std to depend on cfg-if")
-        } else {
-            log::debug!("Did not patch std to depend on cfg-if")
-        }
-        crate_graph
-    }
-}
-
-fn project_json_to_crate_graph(
-    target: Option<&str>,
-    proc_macro_client: &ProcMacroClient,
-    load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
-    project: &ProjectJson,
-    sysroot: &Option<Sysroot>,
-) -> CrateGraph {
-    let mut crate_graph = CrateGraph::default();
-    let sysroot_deps = sysroot
-        .as_ref()
-        .map(|sysroot| sysroot_to_crate_graph(&mut crate_graph, sysroot, target, load));
-
-    let mut cfg_cache: FxHashMap<Option<&str>, Vec<CfgFlag>> = FxHashMap::default();
-    let crates: FxHashMap<CrateId, CrateId> = project
-        .crates()
-        .filter_map(|(crate_id, krate)| {
-            let file_path = &krate.root_module;
-            let file_id = load(&file_path)?;
-            Some((crate_id, krate, file_id))
-        })
-        .map(|(crate_id, krate, file_id)| {
-            let env = krate.env.clone().into_iter().collect();
-            let proc_macro =
-                krate.proc_macro_dylib_path.clone().map(|it| proc_macro_client.by_dylib_path(&it));
-
-            let target = krate.target.as_deref().or(target);
-            let target_cfgs =
-                cfg_cache.entry(target).or_insert_with(|| get_rustc_cfg_options(target));
-
-            let mut cfg_options = CfgOptions::default();
-            cfg_options.extend(target_cfgs.iter().chain(krate.cfg.iter()).cloned());
-            (
-                crate_id,
-                crate_graph.add_crate_root(
-                    file_id,
-                    krate.edition,
-                    krate.display_name.clone(),
-                    cfg_options,
-                    env,
-                    proc_macro.unwrap_or_default(),
-                ),
-            )
-        })
-        .collect();
+                let (public_deps, libproc_macro) =
+                    sysroot_to_crate_graph(&mut crate_graph, sysroot, target, load);
 
-    for (from, krate) in project.crates() {
-        if let Some(&from) = crates.get(&from) {
-            if let Some((public_deps, _proc_macro)) = &sysroot_deps {
-                for (name, to) in public_deps.iter() {
-                    add_dep(&mut crate_graph, from, name.clone(), *to)
-                }
-            }
+                let mut cfg_options = CfgOptions::default();
+                cfg_options.extend(get_rustc_cfg_options(target));
 
-            for dep in &krate.deps {
-                if let Some(&to) = crates.get(&dep.crate_id) {
-                    add_dep(&mut crate_graph, from, dep.name.clone(), to)
-                }
-            }
-        }
-    }
-    crate_graph
-}
+                let mut pkg_to_lib_crate = FxHashMap::default();
 
-fn cargo_to_crate_graph(
-    target: Option<&str>,
-    proc_macro_client: &ProcMacroClient,
-    load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
-    cargo: &CargoWorkspace,
-    sysroot: &Sysroot,
-    rustc: &Option<CargoWorkspace>,
-) -> CrateGraph {
-    let mut crate_graph = CrateGraph::default();
-    let (public_deps, libproc_macro) =
-        sysroot_to_crate_graph(&mut crate_graph, sysroot, target, load);
+                // Add test cfg for non-sysroot crates
+                cfg_options.insert_atom("test".into());
+                cfg_options.insert_atom("debug_assertions".into());
 
-    let mut cfg_options = CfgOptions::default();
-    cfg_options.extend(get_rustc_cfg_options(target));
+                let mut pkg_crates = FxHashMap::default();
 
-    let mut pkg_to_lib_crate = FxHashMap::default();
-
-    // Add test cfg for non-sysroot crates
-    cfg_options.insert_atom("test".into());
-    cfg_options.insert_atom("debug_assertions".into());
-
-    let mut pkg_crates = FxHashMap::default();
-
-    // Next, create crates for each package, target pair
-    for pkg in cargo.packages() {
-        let mut lib_tgt = None;
-        for &tgt in cargo[pkg].targets.iter() {
-            if let Some(file_id) = load(&cargo[tgt].root) {
-                let crate_id = add_target_crate_root(
-                    &mut crate_graph,
-                    &cargo[pkg],
-                    &cfg_options,
-                    proc_macro_client,
-                    file_id,
-                );
-                if cargo[tgt].kind == TargetKind::Lib {
-                    lib_tgt = Some((crate_id, cargo[tgt].name.clone()));
-                    pkg_to_lib_crate.insert(pkg, crate_id);
-                }
-                if cargo[tgt].is_proc_macro {
-                    if let Some(proc_macro) = libproc_macro {
-                        add_dep(
+                // Next, create crates for each package, target pair
+                for pkg in cargo.packages() {
+                    let mut lib_tgt = None;
+                    for &tgt in cargo[pkg].targets.iter() {
+                        if let Some(crate_id) = add_target_crate_root(
                             &mut crate_graph,
-                            crate_id,
-                            CrateName::new("proc_macro").unwrap(),
-                            proc_macro,
-                        );
+                            &cargo[pkg],
+                            &cargo[tgt],
+                            &cfg_options,
+                            proc_macro_client,
+                            load,
+                        ) {
+                            if cargo[tgt].kind == TargetKind::Lib {
+                                lib_tgt = Some((crate_id, cargo[tgt].name.clone()));
+                                pkg_to_lib_crate.insert(pkg, crate_id);
+                            }
+                            if cargo[tgt].is_proc_macro {
+                                if let Some(proc_macro) = libproc_macro {
+                                    if let Err(_) = crate_graph.add_dep(
+                                        crate_id,
+                                        CrateName::new("proc_macro").unwrap(),
+                                        proc_macro,
+                                    ) {
+                                        log::error!(
+                                            "cyclic dependency on proc_macro for {}",
+                                            &cargo[pkg].name
+                                        )
+                                    }
+                                }
+                            }
+
+                            pkg_crates.entry(pkg).or_insert_with(Vec::new).push(crate_id);
+                        }
                     }
-                }
-
-                pkg_crates.entry(pkg).or_insert_with(Vec::new).push(crate_id);
-            }
-        }
-
-        // Set deps to the core, std and to the lib target of the current package
-        for &from in pkg_crates.get(&pkg).into_iter().flatten() {
-            if let Some((to, name)) = lib_tgt.clone() {
-                if to != from {
-                    // For root projects with dashes in their name,
-                    // cargo metadata does not do any normalization,
-                    // so we do it ourselves currently
-                    let name = CrateName::normalize_dashes(&name);
-                    add_dep(&mut crate_graph, from, name, to);
-                }
-            }
-            for (name, krate) in public_deps.iter() {
-                add_dep(&mut crate_graph, from, name.clone(), *krate);
-            }
-        }
-    }
 
-    // Now add a dep edge from all targets of upstream to the lib
-    // target of downstream.
-    for pkg in cargo.packages() {
-        for dep in cargo[pkg].dependencies.iter() {
-            let name = CrateName::new(&dep.name).unwrap();
-            if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) {
-                for &from in pkg_crates.get(&pkg).into_iter().flatten() {
-                    add_dep(&mut crate_graph, from, name.clone(), to)
+                    // Set deps to the core, std and to the lib target of the current package
+                    for &from in pkg_crates.get(&pkg).into_iter().flatten() {
+                        if let Some((to, name)) = lib_tgt.clone() {
+                            // For root projects with dashes in their name,
+                            // cargo metadata does not do any normalization,
+                            // so we do it ourselves currently
+                            let name = CrateName::normalize_dashes(&name);
+                            if to != from && crate_graph.add_dep(from, name, to).is_err() {
+                                log::error!(
+                                    "cyclic dependency between targets of {}",
+                                    &cargo[pkg].name
+                                )
+                            }
+                        }
+                        for (name, krate) in public_deps.iter() {
+                            if let Err(_) = crate_graph.add_dep(from, name.clone(), *krate) {
+                                log::error!(
+                                    "cyclic dependency on {} for {}",
+                                    name,
+                                    &cargo[pkg].name
+                                )
+                            }
+                        }
+                    }
                 }
-            }
-        }
-    }
-
-    let mut rustc_pkg_crates = FxHashMap::default();
 
-    // If the user provided a path to rustc sources, we add all the rustc_private crates
-    // and create dependencies on them for the crates in the current workspace
-    if let Some(rustc_workspace) = rustc {
-        for pkg in rustc_workspace.packages() {
-            for &tgt in rustc_workspace[pkg].targets.iter() {
-                if rustc_workspace[tgt].kind != TargetKind::Lib {
-                    continue;
-                }
-                // Exclude alloc / core / std
-                if rustc_workspace[tgt]
-                    .root
-                    .components()
-                    .any(|c| c == Component::Normal("library".as_ref()))
-                {
-                    continue;
+                // Now add a dep edge from all targets of upstream to the lib
+                // target of downstream.
+                for pkg in cargo.packages() {
+                    for dep in cargo[pkg].dependencies.iter() {
+                        let name = CrateName::new(&dep.name).unwrap();
+                        if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) {
+                            for &from in pkg_crates.get(&pkg).into_iter().flatten() {
+                                if let Err(_) = crate_graph.add_dep(from, name.clone(), to) {
+                                    log::error!(
+                                        "cyclic dependency {} -> {}",
+                                        &cargo[pkg].name,
+                                        &cargo[dep.pkg].name
+                                    )
+                                }
+                            }
+                        }
+                    }
                 }
 
-                if let Some(file_id) = load(&rustc_workspace[tgt].root) {
-                    let crate_id = add_target_crate_root(
-                        &mut crate_graph,
-                        &rustc_workspace[pkg],
-                        &cfg_options,
-                        proc_macro_client,
-                        file_id,
-                    );
-                    pkg_to_lib_crate.insert(pkg, crate_id);
-                    // Add dependencies on the core / std / alloc for rustc
-                    for (name, krate) in public_deps.iter() {
-                        add_dep(&mut crate_graph, crate_id, name.clone(), *krate);
+                let mut rustc_pkg_crates = FxHashMap::default();
+
+                // If the user provided a path to rustc sources, we add all the rustc_private crates
+                // and create dependencies on them for the crates in the current workspace
+                if let Some(rustc_workspace) = rustc {
+                    for pkg in rustc_workspace.packages() {
+                        for &tgt in rustc_workspace[pkg].targets.iter() {
+                            if rustc_workspace[tgt].kind != TargetKind::Lib {
+                                continue;
+                            }
+                            // Exclude alloc / core / std
+                            if rustc_workspace[tgt]
+                                .root
+                                .components()
+                                .any(|c| c == Component::Normal("library".as_ref()))
+                            {
+                                continue;
+                            }
+
+                            if let Some(crate_id) = add_target_crate_root(
+                                &mut crate_graph,
+                                &rustc_workspace[pkg],
+                                &rustc_workspace[tgt],
+                                &cfg_options,
+                                proc_macro_client,
+                                load,
+                            ) {
+                                pkg_to_lib_crate.insert(pkg, crate_id);
+                                // Add dependencies on the core / std / alloc for rustc
+                                for (name, krate) in public_deps.iter() {
+                                    if let Err(_) =
+                                        crate_graph.add_dep(crate_id, name.clone(), *krate)
+                                    {
+                                        log::error!(
+                                            "cyclic dependency on {} for {}",
+                                            name,
+                                            &cargo[pkg].name
+                                        )
+                                    }
+                                }
+                                rustc_pkg_crates.entry(pkg).or_insert_with(Vec::new).push(crate_id);
+                            }
+                        }
                     }
-                    rustc_pkg_crates.entry(pkg).or_insert_with(Vec::new).push(crate_id);
-                }
-            }
-        }
-        // Now add a dep edge from all targets of upstream to the lib
-        // target of downstream.
-        for pkg in rustc_workspace.packages() {
-            for dep in rustc_workspace[pkg].dependencies.iter() {
-                let name = CrateName::new(&dep.name).unwrap();
-                if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) {
-                    for &from in rustc_pkg_crates.get(&pkg).into_iter().flatten() {
-                        add_dep(&mut crate_graph, from, name.clone(), to);
+                    // Now add a dep edge from all targets of upstream to the lib
+                    // target of downstream.
+                    for pkg in rustc_workspace.packages() {
+                        for dep in rustc_workspace[pkg].dependencies.iter() {
+                            let name = CrateName::new(&dep.name).unwrap();
+                            if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) {
+                                for &from in rustc_pkg_crates.get(&pkg).into_iter().flatten() {
+                                    if let Err(_) = crate_graph.add_dep(from, name.clone(), to) {
+                                        log::error!(
+                                            "cyclic dependency {} -> {}",
+                                            &rustc_workspace[pkg].name,
+                                            &rustc_workspace[dep.pkg].name
+                                        )
+                                    }
+                                }
+                            }
+                        }
                     }
-                }
-            }
-        }
 
-        // Add dependencies for all the crates of the current workspace to rustc_private libraries
-        for dep in rustc_workspace.packages() {
-            let name = CrateName::normalize_dashes(&rustc_workspace[dep].name);
-
-            if let Some(&to) = pkg_to_lib_crate.get(&dep) {
-                for pkg in cargo.packages() {
-                    if !cargo[pkg].is_member {
-                        continue;
-                    }
-                    for &from in pkg_crates.get(&pkg).into_iter().flatten() {
-                        add_dep(&mut crate_graph, from, name.clone(), to);
+                    // Add dependencies for all the crates of the current workspace to rustc_private libraries
+                    for dep in rustc_workspace.packages() {
+                        let name = CrateName::normalize_dashes(&rustc_workspace[dep].name);
+
+                        if let Some(&to) = pkg_to_lib_crate.get(&dep) {
+                            for pkg in cargo.packages() {
+                                if !cargo[pkg].is_member {
+                                    continue;
+                                }
+                                for &from in pkg_crates.get(&pkg).into_iter().flatten() {
+                                    if let Err(_) = crate_graph.add_dep(from, name.clone(), to) {
+                                        log::error!(
+                                            "cyclic dependency {} -> {}",
+                                            &cargo[pkg].name,
+                                            &rustc_workspace[dep].name
+                                        )
+                                    }
+                                }
+                            }
+                        }
                     }
                 }
             }
         }
+        if crate_graph.patch_cfg_if() {
+            log::debug!("Patched std to depend on cfg-if")
+        } else {
+            log::debug!("Did not patch std to depend on cfg-if")
+        }
+        crate_graph
     }
-    crate_graph
 }
 
 fn add_target_crate_root(
     crate_graph: &mut CrateGraph,
     pkg: &cargo_workspace::PackageData,
+    tgt: &cargo_workspace::TargetData,
     cfg_options: &CfgOptions,
     proc_macro_client: &ProcMacroClient,
-    file_id: FileId,
-) -> CrateId {
-    let edition = pkg.edition;
-    let cfg_options = {
-        let mut opts = cfg_options.clone();
-        for feature in pkg.features.iter() {
-            opts.insert_key_value("feature".into(), feature.into());
-        }
-        opts.extend(pkg.cfgs.iter().cloned());
-        opts
-    };
-    let mut env = Env::default();
-    if let Some(out_dir) = &pkg.out_dir {
-        // NOTE: cargo and rustc seem to hide non-UTF-8 strings from env! and option_env!()
-        if let Some(out_dir) = out_dir.to_str().map(|s| s.to_owned()) {
-            env.set("OUT_DIR", out_dir);
+    load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
+) -> Option<CrateId> {
+    let root = tgt.root.as_path();
+    if let Some(file_id) = load(root) {
+        let edition = pkg.edition;
+        let cfg_options = {
+            let mut opts = cfg_options.clone();
+            for feature in pkg.features.iter() {
+                opts.insert_key_value("feature".into(), feature.into());
+            }
+            opts.extend(pkg.cfgs.iter().cloned());
+            opts
+        };
+        let mut env = Env::default();
+        if let Some(out_dir) = &pkg.out_dir {
+            // NOTE: cargo and rustc seem to hide non-UTF-8 strings from env! and option_env!()
+            if let Some(out_dir) = out_dir.to_str().map(|s| s.to_owned()) {
+                env.set("OUT_DIR", out_dir);
+            }
         }
+        let proc_macro = pkg
+            .proc_macro_dylib_path
+            .as_ref()
+            .map(|it| proc_macro_client.by_dylib_path(&it))
+            .unwrap_or_default();
+
+        let display_name = CrateDisplayName::from_canonical_name(pkg.name.clone());
+        let crate_id = crate_graph.add_crate_root(
+            file_id,
+            edition,
+            Some(display_name),
+            cfg_options,
+            env,
+            proc_macro.clone(),
+        );
+
+        return Some(crate_id);
     }
-    let proc_macro = pkg
-        .proc_macro_dylib_path
-        .as_ref()
-        .map(|it| proc_macro_client.by_dylib_path(&it))
-        .unwrap_or_default();
-
-    let display_name = CrateDisplayName::from_canonical_name(pkg.name.clone());
-    let crate_id = crate_graph.add_crate_root(
-        file_id,
-        edition,
-        Some(display_name),
-        cfg_options,
-        env,
-        proc_macro.clone(),
-    );
-
-    crate_id
+    None
 }
-
 fn sysroot_to_crate_graph(
     crate_graph: &mut CrateGraph,
     sysroot: &Sysroot,
@@ -479,18 +520,19 @@ fn sysroot_to_crate_graph(
 ) -> (Vec<(CrateName, CrateId)>, Option<CrateId>) {
     let mut cfg_options = CfgOptions::default();
     cfg_options.extend(get_rustc_cfg_options(target));
-    let sysroot_crates: FxHashMap<SysrootCrate, CrateId> = sysroot
+    let sysroot_crates: FxHashMap<_, _> = sysroot
         .crates()
         .filter_map(|krate| {
             let file_id = load(&sysroot[krate].root)?;
 
             let env = Env::default();
             let proc_macro = vec![];
-            let display_name = CrateDisplayName::from_canonical_name(sysroot[krate].name.clone());
+            let name = CrateName::new(&sysroot[krate].name)
+                .expect("Sysroot crates' names do not contain dashes");
             let crate_id = crate_graph.add_crate_root(
                 file_id,
                 Edition::Edition2018,
-                Some(display_name),
+                Some(name.into()),
                 cfg_options.clone(),
                 env,
                 proc_macro,
@@ -503,7 +545,9 @@ fn sysroot_to_crate_graph(
         for &to in sysroot[from].deps.iter() {
             let name = CrateName::new(&sysroot[to].name).unwrap();
             if let (Some(&from), Some(&to)) = (sysroot_crates.get(&from), sysroot_crates.get(&to)) {
-                add_dep(crate_graph, from, name, to);
+                if let Err(_) = crate_graph.add_dep(from, name, to) {
+                    log::error!("cyclic dependency between sysroot crates")
+                }
             }
         }
     }
@@ -544,9 +588,3 @@ fn get_rustc_cfg_options(target: Option<&str>) -> Vec<CfgFlag> {
 
     res
 }
-
-fn add_dep(graph: &mut CrateGraph, from: CrateId, name: CrateName, to: CrateId) {
-    if let Err(err) = graph.add_dep(from, name, to) {
-        log::error!("{}", err)
-    }
-}
diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml
index 56c51486f2..d25c4bf837 100644
--- a/crates/rust-analyzer/Cargo.toml
+++ b/crates/rust-analyzer/Cargo.toml
@@ -21,7 +21,7 @@ env_logger = { version = "0.8.1", default-features = false }
 itertools = "0.9.0"
 jod-thread = "0.1.0"
 log = "0.4.8"
-lsp-types = { version = "0.83.1", features = ["proposed"] }
+lsp-types = { version = "0.83.0", features = ["proposed"] }
 parking_lot = "0.11.0"
 pico-args = "0.3.1"
 oorandom = "11.1.2"
@@ -31,7 +31,7 @@ serde_json = "1.0.48"
 threadpool = "1.7.1"
 rayon = "1.5"
 mimalloc = { version = "0.1.19", default-features = false, optional = true }
-lsp-server = "0.5.0"
+lsp-server = "0.4.0"
 tracing = "0.1"
 tracing-subscriber = { version = "0.2", default-features = false, features = ["env-filter", "registry"] }
 tracing-tree = { version = "0.1.4" }
diff --git a/crates/rust-analyzer/src/caps.rs b/crates/rust-analyzer/src/caps.rs
index c7203451ca..9a88700534 100644
--- a/crates/rust-analyzer/src/caps.rs
+++ b/crates/rust-analyzer/src/caps.rs
@@ -62,7 +62,6 @@ pub fn server_capabilities(client_caps: &ClientCapabilities) -> ServerCapabiliti
             prepare_provider: Some(true),
             work_done_progress_options: WorkDoneProgressOptions { work_done_progress: None },
         })),
-        on_type_rename_provider: None,
         document_link_provider: None,
         color_provider: None,
         execute_command_provider: None,
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index 5fc6800cf2..d167965900 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -294,6 +294,10 @@ impl Config {
             max_length: data.inlayHints_maxLength,
         };
 
+        self.completion.enable_postfix_completions = data.completion_postfix_enable;
+        self.completion.add_call_parenthesis = data.completion_addCallParenthesis;
+        self.completion.add_call_argument_snippets = data.completion_addCallArgumentSnippets;
+
         self.assist.insert_use.merge = match data.assist_importMergeBehaviour {
             MergeBehaviourDef::None => None,
             MergeBehaviourDef::Full => Some(MergeBehaviour::Full),
@@ -305,11 +309,6 @@ impl Config {
             ImportPrefixDef::BySelf => PrefixKind::BySelf,
         };
 
-        self.completion.enable_postfix_completions = data.completion_postfix_enable;
-        self.completion.add_call_parenthesis = data.completion_addCallParenthesis;
-        self.completion.add_call_argument_snippets = data.completion_addCallArgumentSnippets;
-        self.completion.merge = self.assist.insert_use.merge;
-
         self.call_info_full = data.callInfo_full;
 
         self.lens = LensConfig {
diff --git a/crates/rust-analyzer/src/diagnostics/to_proto.rs b/crates/rust-analyzer/src/diagnostics/to_proto.rs
index 93bef5c8b4..15145415b3 100644
--- a/crates/rust-analyzer/src/diagnostics/to_proto.rs
+++ b/crates/rust-analyzer/src/diagnostics/to_proto.rs
@@ -55,8 +55,8 @@ fn location_naive(workspace_root: &Path, span: &DiagnosticSpan) -> lsp_types::Lo
 
     // FIXME: this doesn't handle UTF16 offsets correctly
     let range = lsp_types::Range::new(
-        lsp_types::Position::new(span.line_start as u32 - 1, span.column_start as u32 - 1),
-        lsp_types::Position::new(span.line_end as u32 - 1, span.column_end as u32 - 1),
+        lsp_types::Position::new(span.line_start as u64 - 1, span.column_start as u64 - 1),
+        lsp_types::Position::new(span.line_end as u64 - 1, span.column_end as u64 - 1),
     );
 
     lsp_types::Location { uri, range }
diff --git a/crates/rust-analyzer/src/document.rs b/crates/rust-analyzer/src/document.rs
index cf091510ff..04c7ee150a 100644
--- a/crates/rust-analyzer/src/document.rs
+++ b/crates/rust-analyzer/src/document.rs
@@ -6,11 +6,11 @@
 /// client notifications.
 #[derive(Debug, Clone)]
 pub(crate) struct DocumentData {
-    pub(crate) version: i32,
+    pub(crate) version: Option<i64>,
 }
 
 impl DocumentData {
-    pub(crate) fn new(version: i32) -> Self {
-        DocumentData { version }
+    pub(crate) fn new(version: i64) -> Self {
+        DocumentData { version: Some(version) }
     }
 }
diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs
index defe11c553..63c70a09d8 100644
--- a/crates/rust-analyzer/src/global_state.rs
+++ b/crates/rust-analyzer/src/global_state.rs
@@ -263,9 +263,9 @@ impl GlobalStateSnapshot {
         self.vfs.read().1[&id]
     }
 
-    pub(crate) fn url_file_version(&self, url: &Url) -> Option<i32> {
+    pub(crate) fn url_file_version(&self, url: &Url) -> Option<i64> {
         let path = from_proto::vfs_path(&url).ok()?;
-        Some(self.mem_docs.get(&path)?.version)
+        self.mem_docs.get(&path)?.version
     }
 
     pub(crate) fn anchored_path(&self, file_id: FileId, path: &str) -> Url {
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs
index 118e7276f2..782797e85f 100644
--- a/crates/rust-analyzer/src/handlers.rs
+++ b/crates/rust-analyzer/src/handlers.rs
@@ -18,7 +18,7 @@ use lsp_types::{
     CallHierarchyOutgoingCall, CallHierarchyOutgoingCallsParams, CallHierarchyPrepareParams,
     CodeActionKind, CodeLens, Command, CompletionItem, Diagnostic, DiagnosticTag,
     DocumentFormattingParams, DocumentHighlight, DocumentSymbol, FoldingRange, FoldingRangeParams,
-    HoverContents, Location, NumberOrString, Position, PrepareRenameResponse, Range, RenameParams,
+    HoverContents, Location, Position, PrepareRenameResponse, Range, RenameParams,
     SemanticTokensDeltaParams, SemanticTokensFullDeltaResult, SemanticTokensParams,
     SemanticTokensRangeParams, SemanticTokensRangeResult, SemanticTokensResult, SymbolInformation,
     SymbolTag, TextDocumentIdentifier, Url, WorkspaceEdit,
@@ -573,8 +573,7 @@ pub(crate) fn handle_completion(
         .flat_map(|item| to_proto::completion_item(&line_index, line_endings, item))
         .collect();
 
-    let completion_list = lsp_types::CompletionList { is_incomplete: true, items };
-    Ok(Some(completion_list.into()))
+    Ok(Some(items.into()))
 }
 
 pub(crate) fn handle_folding_range(
@@ -1128,7 +1127,7 @@ pub(crate) fn publish_diagnostics(
         .map(|d| Diagnostic {
             range: to_proto::range(&line_index, d.range),
             severity: Some(to_proto::diagnostic_severity(d.severity)),
-            code: d.code.map(|d| d.as_str().to_owned()).map(NumberOrString::String),
+            code: None,
             code_description: None,
             source: Some("rust-analyzer".to_string()),
             message: d.message,
diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs
index 93ac45415f..a5c65fa3e8 100644
--- a/crates/rust-analyzer/src/lsp_ext.rs
+++ b/crates/rust-analyzer/src/lsp_ext.rs
@@ -302,7 +302,7 @@ pub enum SnippetDocumentChangeOperation {
 #[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
 #[serde(rename_all = "camelCase")]
 pub struct SnippetTextDocumentEdit {
-    pub text_document: lsp_types::OptionalVersionedTextDocumentIdentifier,
+    pub text_document: lsp_types::VersionedTextDocumentIdentifier,
     pub edits: Vec<SnippetTextEdit>,
 }
 
diff --git a/crates/rust-analyzer/src/lsp_utils.rs b/crates/rust-analyzer/src/lsp_utils.rs
index 6427c73672..1d271a9d8b 100644
--- a/crates/rust-analyzer/src/lsp_utils.rs
+++ b/crates/rust-analyzer/src/lsp_utils.rs
@@ -51,7 +51,7 @@ impl GlobalState {
         }
         let percentage = fraction.map(|f| {
             assert!(0.0 <= f && f <= 1.0);
-            (f * 100.0) as u32
+            f * 100.0
         });
         let token = lsp_types::ProgressToken::String(format!("rustAnalyzer/{}", title));
         let work_done_progress = match state {
@@ -98,11 +98,11 @@ pub(crate) fn apply_document_changes(
     // The VFS will normalize the end of lines to `\n`.
     enum IndexValid {
         All,
-        UpToLineExclusive(u32),
+        UpToLineExclusive(u64),
     }
 
     impl IndexValid {
-        fn covers(&self, line: u32) -> bool {
+        fn covers(&self, line: u64) -> bool {
             match *self {
                 IndexValid::UpToLineExclusive(to) => to > line,
                 _ => true,
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index 6ea08adcea..68a53bbcb9 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -368,7 +368,7 @@ impl GlobalState {
                 let url = file_id_to_url(&self.vfs.read().0, file_id);
                 let diagnostics = self.diagnostics.diagnostics_for(file_id).cloned().collect();
                 let version = from_proto::vfs_path(&url)
-                    .map(|path| self.mem_docs.get(&path).map(|it| it.version))
+                    .map(|path| self.mem_docs.get(&path)?.version)
                     .unwrap_or_default();
 
                 self.send_notification::<lsp_types::notification::PublishDiagnostics>(
@@ -521,7 +521,7 @@ impl GlobalState {
                 let mut version = None;
                 if let Ok(path) = from_proto::vfs_path(&params.text_document.uri) {
                     match this.mem_docs.remove(&path) {
-                        Some(doc) => version = Some(doc.version),
+                        Some(doc) => version = doc.version,
                         None => log::error!("orphan DidCloseTextDocument: {}", path),
                     }
 
diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs
index 001bf59498..fa6e09f42b 100644
--- a/crates/rust-analyzer/src/reload.rs
+++ b/crates/rust-analyzer/src/reload.rs
@@ -203,11 +203,7 @@ impl GlobalState {
                     let contents = loader.handle.load_sync(path);
                     vfs.set_file_contents(vfs_path.clone(), contents);
                 }
-                let res = vfs.file_id(&vfs_path);
-                if res.is_none() {
-                    log::error!("failed to load {}", path.display())
-                }
-                res
+                vfs.file_id(&vfs_path)
             };
             for ws in workspaces.iter() {
                 crate_graph.extend(ws.to_crate_graph(
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index 2f35425bbc..4bdf4bf0fa 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -21,7 +21,9 @@ use crate::{
 
 pub(crate) fn position(line_index: &LineIndex, offset: TextSize) -> lsp_types::Position {
     let line_col = line_index.line_col(offset);
-    lsp_types::Position::new(line_col.line, line_col.col_utf16)
+    let line = u64::from(line_col.line);
+    let character = u64::from(line_col.col_utf16);
+    lsp_types::Position::new(line, character)
 }
 
 pub(crate) fn range(line_index: &LineIndex, range: TextRange) -> lsp_types::Range {
@@ -276,9 +278,9 @@ pub(crate) fn signature_help(
                     label.push_str(", ");
                 }
                 first = false;
-                let start = label.len() as u32;
+                let start = label.len() as u64;
                 label.push_str(param);
-                let end = label.len() as u32;
+                let end = label.len() as u64;
                 params.push(lsp_types::ParameterInformation {
                     label: lsp_types::ParameterLabel::LabelOffsets([start, end]),
                     documentation: None,
@@ -300,7 +302,7 @@ pub(crate) fn signature_help(
         })
     };
 
-    let active_parameter = call_info.active_parameter.map(|it| it as u32);
+    let active_parameter = call_info.active_parameter.map(|it| it as i64);
 
     let signature = lsp_types::SignatureInformation {
         label,
@@ -516,13 +518,13 @@ pub(crate) fn url_from_abs_path(path: &Path) -> lsp_types::Url {
     lsp_types::Url::parse(&url).unwrap()
 }
 
-pub(crate) fn optional_versioned_text_document_identifier(
+pub(crate) fn versioned_text_document_identifier(
     snap: &GlobalStateSnapshot,
     file_id: FileId,
-) -> lsp_types::OptionalVersionedTextDocumentIdentifier {
+) -> lsp_types::VersionedTextDocumentIdentifier {
     let url = url(snap, file_id);
     let version = snap.url_file_version(&url);
-    lsp_types::OptionalVersionedTextDocumentIdentifier { uri: url, version }
+    lsp_types::VersionedTextDocumentIdentifier { uri: url, version }
 }
 
 pub(crate) fn location(
@@ -611,7 +613,7 @@ pub(crate) fn snippet_text_document_edit(
     is_snippet: bool,
     source_file_edit: SourceFileEdit,
 ) -> Result<lsp_ext::SnippetTextDocumentEdit> {
-    let text_document = optional_versioned_text_document_identifier(snap, source_file_edit.file_id);
+    let text_document = versioned_text_document_identifier(snap, source_file_edit.file_id);
     let line_index = snap.analysis.file_line_index(source_file_edit.file_id)?;
     let line_endings = snap.file_line_endings(source_file_edit.file_id);
     let edits = source_file_edit
diff --git a/docs/dev/lsp-extensions.md b/docs/dev/lsp-extensions.md
index 8c01db07c8..db9727bee4 100644
--- a/docs/dev/lsp-extensions.md
+++ b/docs/dev/lsp-extensions.md
@@ -1,5 +1,5 @@
 <!---
-lsp_ext.rs hash: 203fdf79b21b5987
+lsp_ext.rs hash: 9d5daed5b25dc4f6
 
 If you need to change the above hash to make the test pass, please check if you
 need to adjust this doc as well and ping this  issue:
@@ -45,7 +45,7 @@ interface SnippetTextEdit extends TextEdit {
 
 ```typescript
 export interface TextDocumentEdit {
-	textDocument: OptionalVersionedTextDocumentIdentifier;
+	textDocument: VersionedTextDocumentIdentifier;
 	edits: (TextEdit | SnippetTextEdit)[];
 }
 ```
diff --git a/docs/dev/syntax.md b/docs/dev/syntax.md
index 1edafab680..2eb08b7cab 100644
--- a/docs/dev/syntax.md
+++ b/docs/dev/syntax.md
@@ -195,7 +195,7 @@ Modeling this with immutable trees is possible, but annoying.
 A function green tree is not super-convenient to use.
 The biggest problem is accessing parents (there are no parent pointers!).
 But there are also "identify" issues.
-Let's say you want to write a code which builds a list of expressions in a file: `fn collect_expressions(file: GreenNode) -> HashSet<GreenNode>`.
+Let's say you want to write a code which builds a list of expressions in a file: `fn collect_exrepssions(file: GreenNode) -> HashSet<GreenNode>`.
 For the input like
 
 ```rust
@@ -236,7 +236,7 @@ impl SyntaxNode {
         self.parent.clone()
     }
     fn children(&self) -> impl Iterator<Item = SyntaxNode> {
-        let mut offset = self.offset;
+        let mut offset = self.offset
         self.green.children().map(|green_child| {
             let child_offset = offset;
             offset += green_child.text_len;
diff --git a/editors/code/package-lock.json b/editors/code/package-lock.json
index a60d3668be..83ef00058e 100644
--- a/editors/code/package-lock.json
+++ b/editors/code/package-lock.json
@@ -2414,27 +2414,27 @@
             "integrity": "sha512-1nG+6cuTtpzmXe7yYfO9GCkYlyV6Ai+jDnwidHiT2T7zhc+bJM+VTtc0T/CdTlDyTNTqIcCj0V1nD4TcVjJ7Ug=="
         },
         "vscode-languageclient": {
-            "version": "7.0.0-next.14",
-            "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-7.0.0-next.14.tgz",
-            "integrity": "sha512-QUccfXK2F6AXXRFR8QJCaIz7N2BhJK6ok8E1aO8LHq2IBU33+5hTSJBXs7nEqrqZ/cY2VlDDbMWtMvCxz+/y1w==",
+            "version": "7.0.0-next.12",
+            "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-7.0.0-next.12.tgz",
+            "integrity": "sha512-OrzvOvhS5o26C0KctTJC7hkwh3avCwkVhllzy42AqwpIUZ3p2aVqkSG2uVxaeodq8ThBb3TLgtg50vxyWs6FEg==",
             "requires": {
                 "semver": "^6.3.0",
-                "vscode-languageserver-protocol": "3.16.0-next.11"
+                "vscode-languageserver-protocol": "3.16.0-next.10"
             }
         },
         "vscode-languageserver-protocol": {
-            "version": "3.16.0-next.11",
-            "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.16.0-next.11.tgz",
-            "integrity": "sha512-31FmupmSmfznuMuGp7qN6h3d/hKUbexbvcwTvrUE/igqRlzFU542s8MtGICx1ERbVuDOLGp96W2Z92qbUbmBPA==",
+            "version": "3.16.0-next.10",
+            "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.16.0-next.10.tgz",
+            "integrity": "sha512-YRTctHUZvts0Z1xXKNYU0ha0o+Tlgtwr+6O8OmDquM086N8exiSKBMwMC+Ra1QtIE+1mfW43Wxsme2FnMkAS9A==",
             "requires": {
                 "vscode-jsonrpc": "6.0.0-next.7",
-                "vscode-languageserver-types": "3.16.0-next.5"
+                "vscode-languageserver-types": "3.16.0-next.4"
             }
         },
         "vscode-languageserver-types": {
-            "version": "3.16.0-next.5",
-            "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.16.0-next.5.tgz",
-            "integrity": "sha512-lf8Y1XXMtF1r2oDDAmJe+drizNXkybSRXAQQk5dPy2rYJsY9SPXYNO074L3THu9zNYepzV5fRJZUPo/V/TLBRQ=="
+            "version": "3.16.0-next.4",
+            "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.16.0-next.4.tgz",
+            "integrity": "sha512-NlKJyGcET/ZBCCLBYIPaGo2c37R03bPYeWXozUtnjyye7+9dhlbMSODyoG2INcQf8zFmB4qhm2UOJjgYEgPCNA=="
         },
         "vscode-test": {
             "version": "1.4.0",
diff --git a/editors/code/package.json b/editors/code/package.json
index a2d6b1148f..220d44abc8 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -21,7 +21,7 @@
         "Programming Languages"
     ],
     "engines": {
-        "vscode": "^1.51.0"
+        "vscode": "^1.47.1"
     },
     "enableProposedApi": true,
     "scripts": {
@@ -36,7 +36,7 @@
     },
     "dependencies": {
         "node-fetch": "^2.6.1",
-        "vscode-languageclient": "7.0.0-next.14"
+        "vscode-languageclient": "7.0.0-next.12"
     },
     "devDependencies": {
         "@rollup/plugin-commonjs": "^13.0.2",
diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts
index 63ab82dde7..c9d032ead3 100644
--- a/editors/code/src/client.ts
+++ b/editors/code/src/client.ts
@@ -174,8 +174,6 @@ class ExperimentalFeatures implements lc.StaticFeature {
     }
     initialize(_capabilities: lc.ServerCapabilities<any>, _documentSelector: lc.DocumentSelector | undefined): void {
     }
-    dispose(): void {
-    }
 }
 
 function isCodeActionWithoutEditsAndCommands(value: any): boolean {


Test patch:
diff --git a/crates/assists/src/handlers/ignore_test.rs b/crates/assists/src/handlers/ignore_test.rs
deleted file mode 100644
index d2339184f1..0000000000
--- a/crates/assists/src/handlers/ignore_test.rs
+++ /dev/null
@@ -1,34 +0,0 @@
-use syntax::{ast, AstNode};
-
-use crate::{utils::test_related_attribute, AssistContext, AssistId, AssistKind, Assists};
-
-// Assist: ignore_test
-//
-// Adds `#[ignore]` attribute to the test.
-//
-// ```
-// <|>#[test]
-// fn arithmetics {
-//     assert_eq!(2 + 2, 5);
-// }
-// ```
-// ->
-// ```
-// #[test]
-// #[ignore]
-// fn arithmetics {
-//     assert_eq!(2 + 2, 5);
-// }
-// ```
-pub(crate) fn ignore_test(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
-    let attr: ast::Attr = ctx.find_node_at_offset()?;
-    let func = attr.syntax().parent().and_then(ast::Fn::cast)?;
-    let attr = test_related_attribute(&func)?;
-
-    acc.add(
-        AssistId("ignore_test", AssistKind::None),
-        "Ignore this test",
-        attr.syntax().text_range(),
-        |builder| builder.insert(attr.syntax().text_range().end(), &format!("\n#[ignore]")),
-    )
-}
diff --git a/crates/assists/src/tests/generated.rs b/crates/assists/src/tests/generated.rs
index 5a9d1a01b7..dbf4f21aa5 100644
--- a/crates/assists/src/tests/generated.rs
+++ b/crates/assists/src/tests/generated.rs
@@ -473,26 +473,6 @@ impl<T: Clone> Ctx<T> {
     )
 }
 
-#[test]
-fn doctest_ignore_test() {
-    check_doc_test(
-        "ignore_test",
-        r#####"
-<|>#[test]
-fn arithmetics {
-    assert_eq!(2 + 2, 5);
-}
-"#####,
-        r#####"
-#[test]
-#[ignore]
-fn arithmetics {
-    assert_eq!(2 + 2, 5);
-}
-"#####,
-    )
-}
-
 #[test]
 fn doctest_infer_function_return_type() {
     check_doc_test(
diff --git a/crates/rust-analyzer/tests/rust-analyzer/support.rs b/crates/rust-analyzer/tests/rust-analyzer/support.rs
index 4561257893..b210b98f0d 100644
--- a/crates/rust-analyzer/tests/rust-analyzer/support.rs
+++ b/crates/rust-analyzer/tests/rust-analyzer/support.rs
@@ -108,7 +108,7 @@ pub(crate) fn project(fixture: &str) -> Server {
 }
 
 pub(crate) struct Server {
-    req_id: Cell<i32>,
+    req_id: Cell<u64>,
     messages: RefCell<Vec<Message>>,
     _thread: jod_thread::JoinHandle<()>,
     client: Connection,
@@ -165,7 +165,7 @@ impl Server {
         R::Params: Serialize,
     {
         let id = self.req_id.get();
-        self.req_id.set(id.wrapping_add(1));
+        self.req_id.set(id + 1);
 
         let r = Request::new(id.into(), R::METHOD.to_string(), params);
         self.send_request_(r)
diff --git a/xtask/tests/tidy.rs b/xtask/tests/tidy.rs
index 4c58aed599..99652e76b6 100644
--- a/xtask/tests/tidy.rs
+++ b/xtask/tests/tidy.rs
@@ -168,7 +168,6 @@ ISC
 MIT
 MIT / Apache-2.0
 MIT OR Apache-2.0
-MIT OR Apache-2.0 OR Zlib
 MIT OR Zlib OR Apache-2.0
 MIT/Apache-2.0
 Unlicense OR MIT