Repository: rust-analyzer/rust-analyzer
Pull request #4222 (2020-04-30 13:18:32)

Introduce C/C++ for Visual Studio Code extension as an alternative debug engine for Debug Code lens.
At the moment Debug Code Lens can use only one debug engine: lldb via [CodeLLDB](https://marketplace.visualstudio.com/items?itemName=vadimcn.vscode-lldb) extension.

This PR adds support of the debug engine from the [MS C++ tools](https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools) extension, as well as the configuration option. If both extensions are installed, `CodeLLDB` will be used by default.

Another new option `rust-analyzer.debug.sourceFileMap` allows, for example, to step into Rust std library during debugging. Works only with `MS C++ tools`.

On Windows: 
```json
"rust-analyzer.debug.sourceFileMap": {
    "/rustc/4fb7144ed159f94491249e86d5bbd033b5d60550": "${env:USERPROFILE}/.rustup/toolchains/stable-x86_64-pc-windows-msvc/lib/rustlib/src/rust"
}
```
On Linux:
```json
"rust-analyzer.debug.sourceFileMap": {
    "/rustc/4fb7144ed159f94491249e86d5bbd033b5d60550": "~/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust"
}
```


Patch:
diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml
index 3f52f31f80..2c1192f072 100644
--- a/.github/workflows/release.yaml
+++ b/.github/workflows/release.yaml
@@ -39,6 +39,7 @@ jobs:
       with:
         toolchain: stable
         profile: minimal
+        target: x86_64-unknown-linux-musl
         override: true
 
     - name: Install Nodejs
diff --git a/.vscode/launch.json b/.vscode/launch.json
index 6a2fff9065..3f74d75666 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -41,7 +41,7 @@
       "outFiles": [
         "${workspaceFolder}/editors/code/out/**/*.js"
       ],
-      "preLaunchTask": "Build Server and Extension",
+      "preLaunchTask": "Build Extension",
       "skipFiles": [
         "<node_internals>/**/*.js"
       ],
@@ -62,7 +62,7 @@
       "outFiles": [
         "${workspaceFolder}/editors/code/out/**/*.js"
       ],
-      "preLaunchTask": "Build Server (Release) and Extension",
+      "preLaunchTask": "Build Extension",
       "skipFiles": [
         "<node_internals>/**/*.js"
       ],
diff --git a/.vscode/tasks.json b/.vscode/tasks.json
index 0969ce89a1..4037e7cce1 100644
--- a/.vscode/tasks.json
+++ b/.vscode/tasks.json
@@ -4,7 +4,7 @@
   "version": "2.0.0",
   "tasks": [
     {
-      "label": "Build Extension in Background",
+      "label": "Build Extension",
       "group": "build",
       "type": "npm",
       "script": "watch",
@@ -15,17 +15,6 @@
       },
       "isBackground": true,
     },
-    {
-      "label": "Build Extension",
-      "group": "build",
-      "type": "npm",
-      "script": "build",
-      "path": "editors/code/",
-      "problemMatcher": {
-        "base": "$tsc",
-        "fileLocation": ["relative", "${workspaceFolder}/editors/code/"]
-      },
-    },
     {
       "label": "Build Server",
       "group": "build",
@@ -33,23 +22,5 @@
       "command": "cargo build --package rust-analyzer",
       "problemMatcher": "$rustc"
     },
-    {
-      "label": "Build Server (Release)",
-      "group": "build",
-      "type": "shell",
-      "command": "cargo build --release --package rust-analyzer",
-      "problemMatcher": "$rustc"
-    },
-
-    {
-      "label": "Build Server and Extension",
-      "dependsOn": ["Build Server", "Build Extension"],
-      "problemMatcher": "$rustc"
-    },
-    {
-      "label": "Build Server (Release) and Extension",
-      "dependsOn": ["Build Server (Release)", "Build Extension"],
-      "problemMatcher": "$rustc"
-    }
   ]
 }
diff --git a/Cargo.lock b/Cargo.lock
index e933598fb9..367ff3f828 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -68,9 +68,9 @@ dependencies = [
 
 [[package]]
 name = "base64"
-version = "0.12.0"
+version = "0.11.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7d5ca2cd0adc3f48f9e9ea5a6bbdf9ccc0bfade884847e484d452414c7ccffb3"
+checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7"
 
 [[package]]
 name = "bitflags"
@@ -645,9 +645,9 @@ dependencies = [
 
 [[package]]
 name = "lsp-types"
-version = "0.74.0"
+version = "0.73.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "820f746e5716ab9a2d664794636188bd003023b72e55404ee27105dc22869922"
+checksum = "93d0cf64ea141b43d9e055f6b9df13f0bce32b103d84237509ce0a571ab9b159"
 dependencies = [
  "base64",
  "bitflags",
diff --git a/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs b/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs
index 49deb67017..03806724a3 100644
--- a/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs
+++ b/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs
@@ -1,12 +1,11 @@
-use ra_ide_db::RootDatabase;
 use ra_syntax::{
     ast::{self, AstNode, NameOwner},
     TextSize,
 };
 use stdx::format_to;
 
-use crate::{utils::FamousDefs, Assist, AssistCtx, AssistId};
-use test_utils::tested_by;
+use crate::{Assist, AssistCtx, AssistId};
+use ra_ide_db::RootDatabase;
 
 // Assist add_from_impl_for_enum
 //
@@ -42,8 +41,7 @@ pub(crate) fn add_from_impl_for_enum(ctx: AssistCtx) -> Option<Assist> {
         _ => return None,
     };
 
-    if existing_from_impl(ctx.sema, &variant).is_some() {
-        tested_by!(test_add_from_impl_already_exists);
+    if already_has_from_impl(ctx.sema, &variant) {
         return None;
     }
 
@@ -72,33 +70,41 @@ impl From<{0}> for {1} {{
     )
 }
 
-fn existing_from_impl(
+fn already_has_from_impl(
     sema: &'_ hir::Semantics<'_, RootDatabase>,
     variant: &ast::EnumVariant,
-) -> Option<()> {
-    let variant = sema.to_def(variant)?;
-    let enum_ = variant.parent_enum(sema.db);
-    let krate = enum_.module(sema.db).krate();
+) -> bool {
+    let scope = sema.scope(&variant.syntax());
 
-    let from_trait = FamousDefs(sema, krate).core_convert_From()?;
+    let from_path = ast::make::path_from_text("From");
+    let from_hir_path = match hir::Path::from_ast(from_path) {
+        Some(p) => p,
+        None => return false,
+    };
+    let from_trait = match scope.resolve_hir_path(&from_hir_path) {
+        Some(hir::PathResolution::Def(hir::ModuleDef::Trait(t))) => t,
+        _ => return false,
+    };
 
-    let enum_type = enum_.ty(sema.db);
+    let e: hir::Enum = match sema.to_def(&variant.parent_enum()) {
+        Some(e) => e,
+        None => return false,
+    };
+    let e_ty = e.ty(sema.db);
 
-    let wrapped_type = variant.fields(sema.db).get(0)?.signature_ty(sema.db);
+    let hir_enum_var: hir::EnumVariant = match sema.to_def(variant) {
+        Some(ev) => ev,
+        None => return false,
+    };
+    let var_ty = hir_enum_var.fields(sema.db)[0].signature_ty(sema.db);
 
-    if enum_type.impls_trait(sema.db, from_trait, &[wrapped_type]) {
-        Some(())
-    } else {
-        None
-    }
+    e_ty.impls_trait(sema.db, from_trait, &[var_ty])
 }
 
 #[cfg(test)]
 mod tests {
     use super::*;
-
     use crate::helpers::{check_assist, check_assist_not_applicable};
-    use test_utils::covers;
 
     #[test]
     fn test_add_from_impl_for_enum() {
@@ -130,40 +136,36 @@ mod tests {
         );
     }
 
-    fn check_not_applicable(ra_fixture: &str) {
-        let fixture =
-            format!("//- main.rs crate:main deps:core\n{}\n{}", ra_fixture, FamousDefs::FIXTURE);
-        check_assist_not_applicable(add_from_impl_for_enum, &fixture)
-    }
-
     #[test]
     fn test_add_from_impl_no_element() {
-        check_not_applicable("enum A { <|>One }");
+        check_assist_not_applicable(add_from_impl_for_enum, "enum A { <|>One }");
     }
 
     #[test]
     fn test_add_from_impl_more_than_one_element_in_tuple() {
-        check_not_applicable("enum A { <|>One(u32, String) }");
+        check_assist_not_applicable(add_from_impl_for_enum, "enum A { <|>One(u32, String) }");
     }
 
     #[test]
     fn test_add_from_impl_struct_variant() {
-        check_not_applicable("enum A { <|>One { x: u32 } }");
+        check_assist_not_applicable(add_from_impl_for_enum, "enum A { <|>One { x: u32 } }");
     }
 
     #[test]
     fn test_add_from_impl_already_exists() {
-        covers!(test_add_from_impl_already_exists);
-        check_not_applicable(
-            r#"
-enum A { <|>One(u32), }
+        check_assist_not_applicable(
+            add_from_impl_for_enum,
+            r#"enum A { <|>One(u32), }
 
 impl From<u32> for A {
     fn from(v: u32) -> Self {
         A::One(v)
     }
 }
-"#,
+
+pub trait From<T> {
+    fn from(T) -> Self;
+}"#,
         );
     }
 
diff --git a/crates/ra_assists/src/handlers/replace_if_let_with_match.rs b/crates/ra_assists/src/handlers/replace_if_let_with_match.rs
index 9841f6980b..0a0a88f3dc 100644
--- a/crates/ra_assists/src/handlers/replace_if_let_with_match.rs
+++ b/crates/ra_assists/src/handlers/replace_if_let_with_match.rs
@@ -1,10 +1,11 @@
 use ra_fmt::unwrap_trivial_block;
 use ra_syntax::{
-    ast::{self, edit::IndentLevel, make},
+    ast::{self, make},
     AstNode,
 };
 
-use crate::{utils::TryEnum, Assist, AssistCtx, AssistId};
+use crate::{Assist, AssistCtx, AssistId};
+use ast::edit::IndentLevel;
 
 // Assist: replace_if_let_with_match
 //
@@ -43,21 +44,15 @@ pub(crate) fn replace_if_let_with_match(ctx: AssistCtx) -> Option<Assist> {
         ast::ElseBranch::IfExpr(_) => return None,
     };
 
-    let sema = ctx.sema;
-    ctx.add_assist(AssistId("replace_if_let_with_match"), "Replace with match", move |edit| {
+    ctx.add_assist(AssistId("replace_if_let_with_match"), "Replace with match", |edit| {
         let match_expr = {
             let then_arm = {
                 let then_expr = unwrap_trivial_block(then_block);
-                make::match_arm(vec![pat.clone()], then_expr)
+                make::match_arm(vec![pat], then_expr)
             };
             let else_arm = {
-                let pattern = sema
-                    .type_of_pat(&pat)
-                    .and_then(|ty| TryEnum::from_ty(sema, &ty))
-                    .map(|it| it.sad_pattern())
-                    .unwrap_or_else(|| make::placeholder_pat().into());
                 let else_expr = unwrap_trivial_block(else_block);
-                make::match_arm(vec![pattern], else_expr)
+                make::match_arm(vec![make::placeholder_pat().into()], else_expr)
             };
             make::expr_match(expr, make::match_arm_list(vec![then_arm, else_arm]))
         };
@@ -73,7 +68,6 @@ pub(crate) fn replace_if_let_with_match(ctx: AssistCtx) -> Option<Assist> {
 #[cfg(test)]
 mod tests {
     use super::*;
-
     use crate::helpers::{check_assist, check_assist_target};
 
     #[test]
@@ -151,64 +145,4 @@ impl VariantData {
         }",
         );
     }
-
-    #[test]
-    fn special_case_option() {
-        check_assist(
-            replace_if_let_with_match,
-            r#"
-enum Option<T> { Some(T), None }
-use Option::*;
-
-fn foo(x: Option<i32>) {
-    <|>if let Some(x) = x {
-        println!("{}", x)
-    } else {
-        println!("none")
-    }
-}
-           "#,
-            r#"
-enum Option<T> { Some(T), None }
-use Option::*;
-
-fn foo(x: Option<i32>) {
-    <|>match x {
-        Some(x) => println!("{}", x),
-        None => println!("none"),
-    }
-}
-           "#,
-        );
-    }
-
-    #[test]
-    fn special_case_result() {
-        check_assist(
-            replace_if_let_with_match,
-            r#"
-enum Result<T, E> { Ok(T), Err(E) }
-use Result::*;
-
-fn foo(x: Result<i32, ()>) {
-    <|>if let Ok(x) = x {
-        println!("{}", x)
-    } else {
-        println!("none")
-    }
-}
-           "#,
-            r#"
-enum Result<T, E> { Ok(T), Err(E) }
-use Result::*;
-
-fn foo(x: Result<i32, ()>) {
-    <|>match x {
-        Ok(x) => println!("{}", x),
-        Err(_) => println!("none"),
-    }
-}
-           "#,
-        );
-    }
 }
diff --git a/crates/ra_assists/src/handlers/replace_let_with_if_let.rs b/crates/ra_assists/src/handlers/replace_let_with_if_let.rs
index 0cf23b754e..bdbaae3898 100644
--- a/crates/ra_assists/src/handlers/replace_let_with_if_let.rs
+++ b/crates/ra_assists/src/handlers/replace_let_with_if_let.rs
@@ -1,5 +1,6 @@
 use std::iter::once;
 
+use hir::Adt;
 use ra_syntax::{
     ast::{
         self,
@@ -11,7 +12,6 @@ use ra_syntax::{
 
 use crate::{
     assist_ctx::{Assist, AssistCtx},
-    utils::TryEnum,
     AssistId,
 };
 
@@ -45,10 +45,20 @@ pub(crate) fn replace_let_with_if_let(ctx: AssistCtx) -> Option<Assist> {
     let init = let_stmt.initializer()?;
     let original_pat = let_stmt.pat()?;
     let ty = ctx.sema.type_of_expr(&init)?;
-    let happy_variant = TryEnum::from_ty(ctx.sema, &ty).map(|it| it.happy_case());
+    let enum_ = match ty.as_adt() {
+        Some(Adt::Enum(it)) => it,
+        _ => return None,
+    };
+    let happy_case =
+        [("Result", "Ok"), ("Option", "Some")].iter().find_map(|(known_type, happy_case)| {
+            if &enum_.name(ctx.db).to_string() == known_type {
+                return Some(happy_case);
+            }
+            None
+        });
 
     ctx.add_assist(AssistId("replace_let_with_if_let"), "Replace with if-let", |edit| {
-        let with_placeholder: ast::Pat = match happy_variant {
+        let with_placeholder: ast::Pat = match happy_case {
             None => make::placeholder_pat().into(),
             Some(var_name) => make::tuple_struct_pat(
                 make::path_unqualified(make::path_segment(make::name_ref(var_name))),
diff --git a/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs b/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs
index 62d4ea5220..62cb7a7631 100644
--- a/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs
+++ b/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs
@@ -1,11 +1,12 @@
 use std::iter;
 
 use ra_syntax::{
-    ast::{self, edit::IndentLevel, make},
+    ast::{self, make},
     AstNode,
 };
 
-use crate::{utils::TryEnum, Assist, AssistCtx, AssistId};
+use crate::{Assist, AssistCtx, AssistId};
+use ast::edit::IndentLevel;
 
 // Assist: replace_unwrap_with_match
 //
@@ -37,27 +38,42 @@ pub(crate) fn replace_unwrap_with_match(ctx: AssistCtx) -> Option<Assist> {
     }
     let caller = method_call.expr()?;
     let ty = ctx.sema.type_of_expr(&caller)?;
-    let happy_variant = TryEnum::from_ty(ctx.sema, &ty)?.happy_case();
 
-    ctx.add_assist(AssistId("replace_unwrap_with_match"), "Replace unwrap with match", |edit| {
-        let ok_path = make::path_unqualified(make::path_segment(make::name_ref(happy_variant)));
-        let it = make::bind_pat(make::name("a")).into();
-        let ok_tuple = make::tuple_struct_pat(ok_path, iter::once(it)).into();
+    let type_name = ty.as_adt()?.name(ctx.sema.db).to_string();
 
-        let bind_path = make::path_unqualified(make::path_segment(make::name_ref("a")));
-        let ok_arm = make::match_arm(iter::once(ok_tuple), make::expr_path(bind_path));
+    for (unwrap_type, variant_name) in [("Result", "Ok"), ("Option", "Some")].iter() {
+        if &type_name == unwrap_type {
+            return ctx.add_assist(
+                AssistId("replace_unwrap_with_match"),
+                "Replace unwrap with match",
+                |edit| {
+                    let ok_path =
+                        make::path_unqualified(make::path_segment(make::name_ref(variant_name)));
+                    let it = make::bind_pat(make::name("a")).into();
+                    let ok_tuple = make::tuple_struct_pat(ok_path, iter::once(it)).into();
 
-        let unreachable_call = make::unreachable_macro_call().into();
-        let err_arm = make::match_arm(iter::once(make::placeholder_pat().into()), unreachable_call);
+                    let bind_path = make::path_unqualified(make::path_segment(make::name_ref("a")));
+                    let ok_arm = make::match_arm(iter::once(ok_tuple), make::expr_path(bind_path));
 
-        let match_arm_list = make::match_arm_list(vec![ok_arm, err_arm]);
-        let match_expr = make::expr_match(caller.clone(), match_arm_list);
-        let match_expr = IndentLevel::from_node(method_call.syntax()).increase_indent(match_expr);
+                    let unreachable_call = make::unreachable_macro_call().into();
+                    let err_arm = make::match_arm(
+                        iter::once(make::placeholder_pat().into()),
+                        unreachable_call,
+                    );
 
-        edit.target(method_call.syntax().text_range());
-        edit.set_cursor(caller.syntax().text_range().start());
-        edit.replace_ast::<ast::Expr>(method_call.into(), match_expr);
-    })
+                    let match_arm_list = make::match_arm_list(vec![ok_arm, err_arm]);
+                    let match_expr = make::expr_match(caller.clone(), match_arm_list);
+                    let match_expr =
+                        IndentLevel::from_node(method_call.syntax()).increase_indent(match_expr);
+
+                    edit.target(method_call.syntax().text_range());
+                    edit.set_cursor(caller.syntax().text_range().start());
+                    edit.replace_ast::<ast::Expr>(method_call.into(), match_expr);
+                },
+            );
+        }
+    }
+    None
 }
 
 #[cfg(test)]
diff --git a/crates/ra_assists/src/marks.rs b/crates/ra_assists/src/marks.rs
index 8d910205f0..6c2a2b8b6e 100644
--- a/crates/ra_assists/src/marks.rs
+++ b/crates/ra_assists/src/marks.rs
@@ -8,5 +8,4 @@ test_utils::marks![
     test_not_inline_mut_variable
     test_not_applicable_if_variable_unused
     change_visibility_field_false_positive
-    test_add_from_impl_already_exists
 ];
diff --git a/crates/ra_assists/src/utils.rs b/crates/ra_assists/src/utils.rs
index efd9886978..3d6c59bda9 100644
--- a/crates/ra_assists/src/utils.rs
+++ b/crates/ra_assists/src/utils.rs
@@ -1,9 +1,7 @@
 //! Assorted functions shared by several assists.
 pub(crate) mod insert_use;
 
-use std::iter;
-
-use hir::{Adt, Crate, Semantics, Trait, Type};
+use hir::Semantics;
 use ra_ide_db::RootDatabase;
 use ra_syntax::{
     ast::{self, make, NameOwner},
@@ -101,109 +99,3 @@ fn invert_special_case(expr: &ast::Expr) -> Option<ast::Expr> {
         _ => None,
     }
 }
-
-#[derive(Clone, Copy)]
-pub(crate) enum TryEnum {
-    Result,
-    Option,
-}
-
-impl TryEnum {
-    const ALL: [TryEnum; 2] = [TryEnum::Option, TryEnum::Result];
-
-    pub(crate) fn from_ty(sema: &Semantics<RootDatabase>, ty: &Type) -> Option<TryEnum> {
-        let enum_ = match ty.as_adt() {
-            Some(Adt::Enum(it)) => it,
-            _ => return None,
-        };
-        TryEnum::ALL.iter().find_map(|&var| {
-            if &enum_.name(sema.db).to_string() == var.type_name() {
-                return Some(var);
-            }
-            None
-        })
-    }
-
-    pub(crate) fn happy_case(self) -> &'static str {
-        match self {
-            TryEnum::Result => "Ok",
-            TryEnum::Option => "Some",
-        }
-    }
-
-    pub(crate) fn sad_pattern(self) -> ast::Pat {
-        match self {
-            TryEnum::Result => make::tuple_struct_pat(
-                make::path_unqualified(make::path_segment(make::name_ref("Err"))),
-                iter::once(make::placeholder_pat().into()),
-            )
-            .into(),
-            TryEnum::Option => make::bind_pat(make::name("None")).into(),
-        }
-    }
-
-    fn type_name(self) -> &'static str {
-        match self {
-            TryEnum::Result => "Result",
-            TryEnum::Option => "Option",
-        }
-    }
-}
-
-/// Helps with finding well-know things inside the standard library. This is
-/// somewhat similar to the known paths infra inside hir, but it different; We
-/// want to make sure that IDE specific paths don't become interesting inside
-/// the compiler itself as well.
-pub(crate) struct FamousDefs<'a, 'b>(pub(crate) &'a Semantics<'b, RootDatabase>, pub(crate) Crate);
-
-#[allow(non_snake_case)]
-impl FamousDefs<'_, '_> {
-    #[cfg(test)]
-    pub(crate) const FIXTURE: &'static str = r#"
-//- /libcore.rs crate:core
-pub mod convert{
-    pub trait From<T> {
-        fn from(T) -> Self;
-    }
-}
-
-pub mod prelude { pub use crate::convert::From }
-#[prelude_import]
-pub use prelude::*;
-"#;
-
-    pub(crate) fn core_convert_From(&self) -> Option<Trait> {
-        self.find_trait("core:convert:From")
-    }
-
-    fn find_trait(&self, path: &str) -> Option<Trait> {
-        let db = self.0.db;
-        let mut path = path.split(':');
-        let trait_ = path.next_back()?;
-        let std_crate = path.next()?;
-        let std_crate = self
-            .1
-            .dependencies(db)
-            .into_iter()
-            .find(|dep| &dep.name.to_string() == std_crate)?
-            .krate;
-
-        let mut module = std_crate.root_module(db)?;
-        for segment in path {
-            module = module.children(db).find_map(|child| {
-                let name = child.name(db)?;
-                if &name.to_string() == segment {
-                    Some(child)
-                } else {
-                    None
-                }
-            })?;
-        }
-        let def =
-            module.scope(db, None).into_iter().find(|(name, _def)| &name.to_string() == trait_)?.1;
-        match def {
-            hir::ScopeDef::ModuleDef(hir::ModuleDef::Trait(it)) => Some(it),
-            _ => None,
-        }
-    }
-}
diff --git a/crates/ra_flycheck/Cargo.toml b/crates/ra_flycheck/Cargo.toml
index 324c33d9dd..76e5cada4f 100644
--- a/crates/ra_flycheck/Cargo.toml
+++ b/crates/ra_flycheck/Cargo.toml
@@ -6,7 +6,7 @@ authors = ["rust-analyzer developers"]
 
 [dependencies]
 crossbeam-channel = "0.4.0"
-lsp-types = { version = "0.74.0", features = ["proposed"] }
+lsp-types = { version = "0.73.0", features = ["proposed"] }
 log = "0.4.8"
 cargo_metadata = "0.9.1"
 serde_json = "1.0.48"
diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs
index af59aa1b6c..fb788736d8 100644
--- a/crates/ra_hir/src/code_model.rs
+++ b/crates/ra_hir/src/code_model.rs
@@ -953,16 +953,6 @@ impl TypeParam {
     pub fn module(self, db: &dyn HirDatabase) -> Module {
         self.id.parent.module(db.upcast()).into()
     }
-
-    pub fn ty(self, db: &dyn HirDatabase) -> Type {
-        let resolver = self.id.parent.resolver(db.upcast());
-        let environment = TraitEnvironment::lower(db, &resolver);
-        let ty = Ty::Placeholder(self.id);
-        Type {
-            krate: self.id.parent.module(db.upcast()).krate,
-            ty: InEnvironment { value: ty, environment },
-        }
-    }
 }
 
 // FIXME: rename from `ImplDef` to `Impl`
@@ -1167,21 +1157,18 @@ impl Type {
 
     pub fn fields(&self, db: &dyn HirDatabase) -> Vec<(Field, Type)> {
         if let Ty::Apply(a_ty) = &self.ty.value {
-            let variant_id = match a_ty.ctor {
-                TypeCtor::Adt(AdtId::StructId(s)) => s.into(),
-                TypeCtor::Adt(AdtId::UnionId(u)) => u.into(),
-                _ => return Vec::new(),
-            };
-
-            return db
-                .field_types(variant_id)
-                .iter()
-                .map(|(local_id, ty)| {
-                    let def = Field { parent: variant_id.into(), id: local_id };
-                    let ty = ty.clone().subst(&a_ty.parameters);
-                    (def, self.derived(ty))
-                })
-                .collect();
+            if let TypeCtor::Adt(AdtId::StructId(s)) = a_ty.ctor {
+                let var_def = s.into();
+                return db
+                    .field_types(var_def)
+                    .iter()
+                    .map(|(local_id, ty)| {
+                        let def = Field { parent: var_def.into(), id: local_id };
+                        let ty = ty.clone().subst(&a_ty.parameters);
+                        (def, self.derived(ty))
+                    })
+                    .collect();
+            }
         };
         Vec::new()
     }
diff --git a/crates/ra_hir/src/semantics.rs b/crates/ra_hir/src/semantics.rs
index a0a0f234bf..86bfb416c8 100644
--- a/crates/ra_hir/src/semantics.rs
+++ b/crates/ra_hir/src/semantics.rs
@@ -9,7 +9,6 @@ use hir_def::{
     AsMacroCall, TraitId,
 };
 use hir_expand::ExpansionInfo;
-use hir_ty::associated_type_shorthand_candidates;
 use itertools::Itertools;
 use ra_db::{FileId, FileRange};
 use ra_prof::profile;
@@ -25,9 +24,8 @@ use crate::{
     semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx},
     source_analyzer::{resolve_hir_path, SourceAnalyzer},
     AssocItem, Field, Function, HirFileId, ImplDef, InFile, Local, MacroDef, Module, ModuleDef,
-    Name, Origin, Path, ScopeDef, Trait, Type, TypeAlias, TypeParam,
+    Name, Origin, Path, ScopeDef, Trait, Type, TypeParam,
 };
-use resolver::TypeNs;
 
 #[derive(Debug, Clone, PartialEq, Eq)]
 pub enum PathResolution {
@@ -42,44 +40,6 @@ pub enum PathResolution {
     AssocItem(AssocItem),
 }
 
-impl PathResolution {
-    fn in_type_ns(&self) -> Option<TypeNs> {
-        match self {
-            PathResolution::Def(ModuleDef::Adt(adt)) => Some(TypeNs::AdtId((*adt).into())),
-            PathResolution::Def(ModuleDef::BuiltinType(builtin)) => {
-                Some(TypeNs::BuiltinType(*builtin))
-            }
-            PathResolution::Def(ModuleDef::Const(_))
-            | PathResolution::Def(ModuleDef::EnumVariant(_))
-            | PathResolution::Def(ModuleDef::Function(_))
-            | PathResolution::Def(ModuleDef::Module(_))
-            | PathResolution::Def(ModuleDef::Static(_))
-            | PathResolution::Def(ModuleDef::Trait(_)) => None,
-            PathResolution::Def(ModuleDef::TypeAlias(alias)) => {
-                Some(TypeNs::TypeAliasId((*alias).into()))
-            }
-            PathResolution::Local(_) | PathResolution::Macro(_) => None,
-            PathResolution::TypeParam(param) => Some(TypeNs::GenericParam((*param).into())),
-            PathResolution::SelfType(impl_def) => Some(TypeNs::SelfType((*impl_def).into())),
-            PathResolution::AssocItem(AssocItem::Const(_))
-            | PathResolution::AssocItem(AssocItem::Function(_)) => None,
-            PathResolution::AssocItem(AssocItem::TypeAlias(alias)) => {
-                Some(TypeNs::TypeAliasId((*alias).into()))
-            }
-        }
-    }
-
-    /// Returns an iterator over associated types that may be specified after this path (using
-    /// `Ty::Assoc` syntax).
-    pub fn assoc_type_shorthand_candidates<R>(
-        &self,
-        db: &dyn HirDatabase,
-        mut cb: impl FnMut(TypeAlias) -> Option<R>,
-    ) -> Option<R> {
-        associated_type_shorthand_candidates(db, self.in_type_ns()?, |_, _, id| cb(id.into()))
-    }
-}
-
 /// Primary API to get semantic information, like types, from syntax trees.
 pub struct Semantics<'db, DB> {
     pub db: &'db DB,
diff --git a/crates/ra_hir_expand/src/builtin_derive.rs b/crates/ra_hir_expand/src/builtin_derive.rs
index e60f879a39..bb45b0f1da 100644
--- a/crates/ra_hir_expand/src/builtin_derive.rs
+++ b/crates/ra_hir_expand/src/builtin_derive.rs
@@ -9,7 +9,7 @@ use ra_syntax::{
 };
 
 use crate::db::AstDatabase;
-use crate::{name, quote, LazyMacroId, MacroCallId, MacroDefId, MacroDefKind};
+use crate::{name, quote, LazyMacroId, MacroDefId, MacroDefKind};
 
 macro_rules! register_builtin {
     ( $($trait:ident => $expand:ident),* ) => {
@@ -153,113 +153,76 @@ fn expand_simple_derive(
     Ok(expanded)
 }
 
-fn find_builtin_crate(db: &dyn AstDatabase, id: LazyMacroId) -> tt::TokenTree {
-    // FIXME: make hygiene works for builtin derive macro
-    // such that $crate can be used here.
-
-    let m: MacroCallId = id.into();
-    let file_id = m.as_file().original_file(db);
-    let cg = db.crate_graph();
-    let krates = db.relevant_crates(file_id);
-    let krate = match krates.get(0) {
-        Some(krate) => krate,
-        None => {
-            let tt = quote! { core };
-            return tt.token_trees[0].clone();
-        }
-    };
-
-    // XXX
-    //  All crates except core itself should have a dependency on core,
-    //  We detect `core` by seeing whether it doesn't have such a dependency.
-    let tt = if cg[*krate].dependencies.iter().any(|dep| dep.name == "core") {
-        quote! { core }
-    } else {
-        quote! { crate }
-    };
-
-    tt.token_trees[0].clone()
-}
-
 fn copy_expand(
-    db: &dyn AstDatabase,
-    id: LazyMacroId,
+    _db: &dyn AstDatabase,
+    _id: LazyMacroId,
     tt: &tt::Subtree,
 ) -> Result<tt::Subtree, mbe::ExpandError> {
-    let krate = find_builtin_crate(db, id);
-    expand_simple_derive(tt, quote! { #krate::marker::Copy })
+    expand_simple_derive(tt, quote! { std::marker::Copy })
 }
 
 fn clone_expand(
-    db: &dyn AstDatabase,
-    id: LazyMacroId,
+    _db: &dyn AstDatabase,
+    _id: LazyMacroId,
     tt: &tt::Subtree,
 ) -> Result<tt::Subtree, mbe::ExpandError> {
-    let krate = find_builtin_crate(db, id);
-    expand_simple_derive(tt, quote! { #krate::clone::Clone })
+    expand_simple_derive(tt, quote! { std::clone::Clone })
 }
 
 fn default_expand(
-    db: &dyn AstDatabase,
-    id: LazyMacroId,
+    _db: &dyn AstDatabase,
+    _id: LazyMacroId,
     tt: &tt::Subtree,
 ) -> Result<tt::Subtree, mbe::ExpandError> {
-    let krate = find_builtin_crate(db, id);
-    expand_simple_derive(tt, quote! { #krate::default::Default })
+    expand_simple_derive(tt, quote! { std::default::Default })
 }
 
 fn debug_expand(
-    db: &dyn AstDatabase,
-    id: LazyMacroId,
+    _db: &dyn AstDatabase,
+    _id: LazyMacroId,
     tt: &tt::Subtree,
 ) -> Result<tt::Subtree, mbe::ExpandError> {
-    let krate = find_builtin_crate(db, id);
-    expand_simple_derive(tt, quote! { #krate::fmt::Debug })
+    expand_simple_derive(tt, quote! { std::fmt::Debug })
 }
 
 fn hash_expand(
-    db: &dyn AstDatabase,
-    id: LazyMacroId,
+    _db: &dyn AstDatabase,
+    _id: LazyMacroId,
     tt: &tt::Subtree,
 ) -> Result<tt::Subtree, mbe::ExpandError> {
-    let krate = find_builtin_crate(db, id);
-    expand_simple_derive(tt, quote! { #krate::hash::Hash })
+    expand_simple_derive(tt, quote! { std::hash::Hash })
 }
 
 fn eq_expand(
-    db: &dyn AstDatabase,
-    id: LazyMacroId,
+    _db: &dyn AstDatabase,
+    _id: LazyMacroId,
     tt: &tt::Subtree,
 ) -> Result<tt::Subtree, mbe::ExpandError> {
-    let krate = find_builtin_crate(db, id);
-    expand_simple_derive(tt, quote! { #krate::cmp::Eq })
+    expand_simple_derive(tt, quote! { std::cmp::Eq })
 }
 
 fn partial_eq_expand(
-    db: &dyn AstDatabase,
-    id: LazyMacroId,
+    _db: &dyn AstDatabase,
+    _id: LazyMacroId,
     tt: &tt::Subtree,
 ) -> Result<tt::Subtree, mbe::ExpandError> {
-    let krate = find_builtin_crate(db, id);
-    expand_simple_derive(tt, quote! { #krate::cmp::PartialEq })
+    expand_simple_derive(tt, quote! { std::cmp::PartialEq })
 }
 
 fn ord_expand(
-    db: &dyn AstDatabase,
-    id: LazyMacroId,
+    _db: &dyn AstDatabase,
+    _id: LazyMacroId,
     tt: &tt::Subtree,
 ) -> Result<tt::Subtree, mbe::ExpandError> {
-    let krate = find_builtin_crate(db, id);
-    expand_simple_derive(tt, quote! { #krate::cmp::Ord })
+    expand_simple_derive(tt, quote! { std::cmp::Ord })
 }
 
 fn partial_ord_expand(
-    db: &dyn AstDatabase,
-    id: LazyMacroId,
+    _db: &dyn AstDatabase,
+    _id: LazyMacroId,
     tt: &tt::Subtree,
 ) -> Result<tt::Subtree, mbe::ExpandError> {
-    let krate = find_builtin_crate(db, id);
-    expand_simple_derive(tt, quote! { #krate::cmp::PartialOrd })
+    expand_simple_derive(tt, quote! { std::cmp::PartialOrd })
 }
 
 #[cfg(test)]
@@ -271,18 +234,8 @@ mod tests {
 
     fn expand_builtin_derive(s: &str, name: Name) -> String {
         let def = find_builtin_derive(&name).unwrap();
-        let fixture = format!(
-            r#"//- /main.rs crate:main deps:core
-<|>
-{}
-//- /lib.rs crate:core
-// empty
-"#,
-            s
-        );
 
-        let (db, file_pos) = TestDB::with_position(&fixture);
-        let file_id = file_pos.file_id;
+        let (db, file_id) = TestDB::with_single_file(&s);
         let parsed = db.parse(file_id);
         let items: Vec<_> =
             parsed.syntax_node().descendants().filter_map(ast::ModuleItem::cast).collect();
@@ -311,7 +264,7 @@ mod tests {
             known::Copy,
         );
 
-        assert_eq!(expanded, "impl< >core::marker::CopyforFoo< >{}");
+        assert_eq!(expanded, "impl< >std::marker::CopyforFoo< >{}");
     }
 
     #[test]
@@ -326,7 +279,7 @@ mod tests {
 
         assert_eq!(
             expanded,
-            "impl<T0:core::marker::Copy,T1:core::marker::Copy>core::marker::CopyforFoo<T0,T1>{}"
+            "impl<T0:std::marker::Copy,T1:std::marker::Copy>std::marker::CopyforFoo<T0,T1>{}"
         );
     }
 
@@ -344,7 +297,7 @@ mod tests {
 
         assert_eq!(
             expanded,
-            "impl<T0:core::marker::Copy,T1:core::marker::Copy>core::marker::CopyforFoo<T0,T1>{}"
+            "impl<T0:std::marker::Copy,T1:std::marker::Copy>std::marker::CopyforFoo<T0,T1>{}"
         );
     }
 
@@ -360,7 +313,7 @@ mod tests {
 
         assert_eq!(
             expanded,
-            "impl<T0:core::clone::Clone,T1:core::clone::Clone>core::clone::CloneforFoo<T0,T1>{}"
+            "impl<T0:std::clone::Clone,T1:std::clone::Clone>std::clone::CloneforFoo<T0,T1>{}"
         );
     }
 }
diff --git a/crates/ra_hir_ty/src/lib.rs b/crates/ra_hir_ty/src/lib.rs
index a6f56c661e..a8ef32ec59 100644
--- a/crates/ra_hir_ty/src/lib.rs
+++ b/crates/ra_hir_ty/src/lib.rs
@@ -66,8 +66,7 @@ pub use autoderef::autoderef;
 pub use infer::{InferTy, InferenceResult};
 pub use lower::CallableDef;
 pub use lower::{
-    associated_type_shorthand_candidates, callable_item_sig, ImplTraitLoweringMode, TyDefId,
-    TyLoweringContext, ValueTyDefId,
+    callable_item_sig, ImplTraitLoweringMode, TyDefId, TyLoweringContext, ValueTyDefId,
 };
 pub use traits::{InEnvironment, Obligation, ProjectionPredicate, TraitEnvironment};
 
diff --git a/crates/ra_hir_ty/src/lower.rs b/crates/ra_hir_ty/src/lower.rs
index 9ad6dbe075..a6f893037f 100644
--- a/crates/ra_hir_ty/src/lower.rs
+++ b/crates/ra_hir_ty/src/lower.rs
@@ -17,9 +17,9 @@ use hir_def::{
     path::{GenericArg, Path, PathSegment, PathSegments},
     resolver::{HasResolver, Resolver, TypeNs},
     type_ref::{TypeBound, TypeRef},
-    AdtId, AssocContainerId, AssocItemId, ConstId, EnumId, EnumVariantId, FunctionId, GenericDefId,
-    HasModule, ImplId, LocalFieldId, Lookup, StaticId, StructId, TraitId, TypeAliasId, TypeParamId,
-    UnionId, VariantId,
+    AdtId, AssocContainerId, ConstId, EnumId, EnumVariantId, FunctionId, GenericDefId, HasModule,
+    ImplId, LocalFieldId, Lookup, StaticId, StructId, TraitId, TypeAliasId, TypeParamId, UnionId,
+    VariantId,
 };
 use ra_arena::map::ArenaMap;
 use ra_db::CrateId;
@@ -34,7 +34,6 @@ use crate::{
     Binders, BoundVar, DebruijnIndex, FnSig, GenericPredicate, PolyFnSig, ProjectionPredicate,
     ProjectionTy, Substs, TraitEnvironment, TraitRef, Ty, TypeCtor, TypeWalk,
 };
-use hir_expand::name::Name;
 
 #[derive(Debug)]
 pub struct TyLoweringContext<'a> {
@@ -384,38 +383,61 @@ impl Ty {
         res: Option<TypeNs>,
         segment: PathSegment<'_>,
     ) -> Ty {
-        if let Some(res) = res {
-            let ty =
-                associated_type_shorthand_candidates(ctx.db, res, move |name, t, associated_ty| {
-                    if name == segment.name {
-                        let substs = match ctx.type_param_mode {
-                            TypeParamLoweringMode::Placeholder => {
-                                // if we're lowering to placeholders, we have to put
-                                // them in now
-                                let s = Substs::type_params(
-                                    ctx.db,
-                                    ctx.resolver.generic_def().expect(
-                                        "there should be generics if there's a generic param",
-                                    ),
-                                );
-                                t.substs.clone().subst_bound_vars(&s)
-                            }
-                            TypeParamLoweringMode::Variable => t.substs.clone(),
+        let traits_from_env: Vec<_> = match res {
+            Some(TypeNs::SelfType(impl_id)) => match ctx.db.impl_trait(impl_id) {
+                None => return Ty::Unknown,
+                Some(trait_ref) => vec![trait_ref.value],
+            },
+            Some(TypeNs::GenericParam(param_id)) => {
+                let predicates = ctx.db.generic_predicates_for_param(param_id);
+                let mut traits_: Vec<_> = predicates
+                    .iter()
+                    .filter_map(|pred| match &pred.value {
+                        GenericPredicate::Implemented(tr) => Some(tr.clone()),
+                        _ => None,
+                    })
+                    .collect();
+                // Handle `Self::Type` referring to own associated type in trait definitions
+                if let GenericDefId::TraitId(trait_id) = param_id.parent {
+                    let generics = generics(ctx.db.upcast(), trait_id.into());
+                    if generics.params.types[param_id.local_id].provenance
+                        == TypeParamProvenance::TraitSelf
+                    {
+                        let trait_ref = TraitRef {
+                            trait_: trait_id,
+                            substs: Substs::bound_vars(&generics, DebruijnIndex::INNERMOST),
                         };
-                        // FIXME handle type parameters on the segment
-                        return Some(Ty::Projection(ProjectionTy {
-                            associated_ty,
-                            parameters: substs,
-                        }));
+                        traits_.push(trait_ref);
                     }
-
-                    None
-                });
-
-            ty.unwrap_or(Ty::Unknown)
-        } else {
-            Ty::Unknown
+                }
+                traits_
+            }
+            _ => return Ty::Unknown,
+        };
+        let traits = traits_from_env.into_iter().flat_map(|t| all_super_trait_refs(ctx.db, t));
+        for t in traits {
+            if let Some(associated_ty) =
+                ctx.db.trait_data(t.trait_).associated_type_by_name(&segment.name)
+            {
+                let substs = match ctx.type_param_mode {
+                    TypeParamLoweringMode::Placeholder => {
+                        // if we're lowering to placeholders, we have to put
+                        // them in now
+                        let s = Substs::type_params(
+                            ctx.db,
+                            ctx.resolver
+                                .generic_def()
+                                .expect("there should be generics if there's a generic param"),
+                        );
+                        t.substs.subst_bound_vars(&s)
+                    }
+                    TypeParamLoweringMode::Variable => t.substs,
+                };
+                // FIXME handle (forbid) type parameters on the segment
+                return Ty::Projection(ProjectionTy { associated_ty, parameters: substs });
+            }
         }
+        Ty::Unknown
     }
 
     fn from_hir_path_inner(
@@ -672,61 +694,6 @@ pub fn callable_item_sig(db: &dyn HirDatabase, def: CallableDef) -> PolyFnSig {
     }
 }
 
-pub fn associated_type_shorthand_candidates<R>(
-    db: &dyn HirDatabase,
-    res: TypeNs,
-    mut cb: impl FnMut(&Name, &TraitRef, TypeAliasId) -> Option<R>,
-) -> Option<R> {
-    let traits_from_env: Vec<_> = match res {
-        TypeNs::SelfType(impl_id) => match db.impl_trait(impl_id) {
-            None => vec![],
-            Some(trait_ref) => vec![trait_ref.value],
-        },
-        TypeNs::GenericParam(param_id) => {
-            let predicates = db.generic_predicates_for_param(param_id);
-            let mut traits_: Vec<_> = predicates
-                .iter()
-                .filter_map(|pred| match &pred.value {
-                    GenericPredicate::Implemented(tr) => Some(tr.clone()),
-                    _ => None,
-                })
-                .collect();
-            // Handle `Self::Type` referring to own associated type in trait definitions
-            if let GenericDefId::TraitId(trait_id) = param_id.parent {
-                let generics = generics(db.upcast(), trait_id.into());
-                if generics.params.types[param_id.local_id].provenance
-                    == TypeParamProvenance::TraitSelf
-                {
-                    let trait_ref = TraitRef {
-                        trait_: trait_id,
-                        substs: Substs::bound_vars(&generics, DebruijnIndex::INNERMOST),
-                    };
-                    traits_.push(trait_ref);
-                }
-            }
-            traits_
-        }
-        _ => vec![],
-    };
-
-    for t in traits_from_env.into_iter().flat_map(move |t| all_super_trait_refs(db, t)) {
-        let data = db.trait_data(t.trait_);
-
-        for (name, assoc_id) in &data.items {
-            match assoc_id {
-                AssocItemId::TypeAliasId(alias) => {
-                    if let Some(result) = cb(name, &t, *alias) {
-                        return Some(result);
-                    }
-                }
-                AssocItemId::FunctionId(_) | AssocItemId::ConstId(_) => {}
-            }
-        }
-    }
-
-    None
-}
-
 /// Build the type of all specific fields of a struct or enum variant.
 pub(crate) fn field_types_query(
     db: &dyn HirDatabase,
diff --git a/crates/ra_ide/src/completion/complete_dot.rs b/crates/ra_ide/src/completion/complete_dot.rs
index 05f825c6fe..814354ffa6 100644
--- a/crates/ra_ide/src/completion/complete_dot.rs
+++ b/crates/ra_ide/src/completion/complete_dot.rs
@@ -249,44 +249,6 @@ mod tests {
         );
     }
 
-    #[test]
-    fn test_union_field_completion() {
-        assert_debug_snapshot!(
-            do_ref_completion(
-                r"
-            union Un {
-                field: u8,
-                other: u16,
-            }
-
-            fn foo(u: Un) {
-                u.<|>
-            }
-            ",
-            ),
-            @r###"
-        [
-            CompletionItem {
-                label: "field",
-                source_range: 140..140,
-                delete: 140..140,
-                insert: "field",
-                kind: Field,
-                detail: "u8",
-            },
-            CompletionItem {
-                label: "other",
-                source_range: 140..140,
-                delete: 140..140,
-                insert: "other",
-                kind: Field,
-                detail: "u16",
-            },
-        ]
-        "###
-        );
-    }
-
     #[test]
     fn test_method_completion() {
         assert_debug_snapshot!(
diff --git a/crates/ra_ide/src/completion/complete_qualified_path.rs b/crates/ra_ide/src/completion/complete_qualified_path.rs
index aa56a5cd87..dd10f74e6d 100644
--- a/crates/ra_ide/src/completion/complete_qualified_path.rs
+++ b/crates/ra_ide/src/completion/complete_qualified_path.rs
@@ -5,29 +5,19 @@ use ra_syntax::AstNode;
 use test_utils::tested_by;
 
 use crate::completion::{CompletionContext, Completions};
-use rustc_hash::FxHashSet;
 
 pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionContext) {
     let path = match &ctx.path_prefix {
         Some(path) => path.clone(),
         _ => return,
     };
-    let scope = ctx.scope();
-    let context_module = scope.module();
-
-    let res = match scope.resolve_hir_path(&path) {
-        Some(res) => res,
-        None => return,
+    let def = match ctx.scope().resolve_hir_path(&path) {
+        Some(PathResolution::Def(def)) => def,
+        _ => return,
     };
-
-    // Add associated types on type parameters and `Self`.
-    res.assoc_type_shorthand_candidates(ctx.db, |alias| {
-        acc.add_type_alias(ctx, alias);
-        None::<()>
-    });
-
-    match res {
-        PathResolution::Def(hir::ModuleDef::Module(module)) => {
+    let context_module = ctx.scope().module();
+    match def {
+        hir::ModuleDef::Module(module) => {
             let module_scope = module.scope(ctx.db, context_module);
             for (name, def) in module_scope {
                 if ctx.use_item_syntax.is_some() {
@@ -45,8 +35,7 @@ pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
                 acc.add_resolution(ctx, name.to_string(), &def);
             }
         }
-        PathResolution::Def(def @ hir::ModuleDef::Adt(_))
-        | PathResolution::Def(def @ hir::ModuleDef::TypeAlias(_)) => {
+        hir::ModuleDef::Adt(_) | hir::ModuleDef::TypeAlias(_) => {
             if let hir::ModuleDef::Adt(Adt::Enum(e)) = def {
                 for variant in e.variants(ctx.db) {
                     acc.add_enum_variant(ctx, variant, None);
@@ -57,10 +46,8 @@ pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
                 hir::ModuleDef::TypeAlias(a) => a.ty(ctx.db),
                 _ => unreachable!(),
             };
-
-            // XXX: For parity with Rust bug #22519, this does not complete Ty::AssocType.
-            // (where AssocType is defined on a trait, not an inherent impl)
-
+            // Iterate assoc types separately
+            // FIXME: complete T::AssocType
             let krate = ctx.krate;
             if let Some(krate) = krate {
                 let traits_in_scope = ctx.scope().traits_in_scope();
@@ -78,7 +65,6 @@ pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
                     None::<()>
                 });
 
-                // Iterate assoc types separately
                 ty.iterate_impl_items(ctx.db, krate, |item| {
                     if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) {
                         return None;
@@ -91,8 +77,7 @@ pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
                 });
             }
         }
-        PathResolution::Def(hir::ModuleDef::Trait(t)) => {
-            // Handles `Trait::assoc` as well as `<Ty as Trait>::assoc`.
+        hir::ModuleDef::Trait(t) => {
             for item in t.items(ctx.db) {
                 if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) {
                     continue;
@@ -106,38 +91,8 @@ pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
                 }
             }
         }
-        PathResolution::TypeParam(_) | PathResolution::SelfType(_) => {
-            if let Some(krate) = ctx.krate {
-                let ty = match res {
-                    PathResolution::TypeParam(param) => param.ty(ctx.db),
-                    PathResolution::SelfType(impl_def) => impl_def.target_ty(ctx.db),
-                    _ => return,
-                };
-
-                let traits_in_scope = ctx.scope().traits_in_scope();
-                let mut seen = FxHashSet::default();
-                ty.iterate_path_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, item| {
-                    if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) {
-                        return None;
-                    }
-
-                    // We might iterate candidates of a trait multiple times here, so deduplicate
-                    // them.
-                    if seen.insert(item) {
-                        match item {
-                            hir::AssocItem::Function(func) => {
-                                acc.add_function(ctx, func, None);
-                            }
-                            hir::AssocItem::Const(ct) => acc.add_const(ctx, ct),
-                            hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty),
-                        }
-                    }
-                    None::<()>
-                });
-            }
-        }
         _ => {}
-    }
+    };
 }
 
 #[cfg(test)]
@@ -888,211 +843,6 @@ mod tests {
         );
     }
 
-    #[test]
-    fn completes_ty_param_assoc_ty() {
-        assert_debug_snapshot!(
-            do_reference_completion(
-                "
-                //- /lib.rs
-                trait Super {
-                    type Ty;
-                    const CONST: u8;
-                    fn func() {}
-                    fn method(&self) {}
-                }
-
-                trait Sub: Super {
-                    type SubTy;
-                    const C2: ();
-                    fn subfunc() {}
-                    fn submethod(&self) {}
-                }
-
-                fn foo<T: Sub>() {
-                    T::<|>
-                }
-                "
-            ),
-            @r###"
-        [
-            CompletionItem {
-                label: "C2",
-                source_range: 219..219,
-                delete: 219..219,
-                insert: "C2",
-                kind: Const,
-                detail: "const C2: ();",
-            },
-            CompletionItem {
-                label: "CONST",
-                source_range: 219..219,
-                delete: 219..219,
-                insert: "CONST",
-                kind: Const,
-                detail: "const CONST: u8;",
-            },
-            CompletionItem {
-                label: "SubTy",
-                source_range: 219..219,
-                delete: 219..219,
-                insert: "SubTy",
-                kind: TypeAlias,
-                detail: "type SubTy;",
-            },
-            CompletionItem {
-                label: "Ty",
-                source_range: 219..219,
-                delete: 219..219,
-                insert: "Ty",
-                kind: TypeAlias,
-                detail: "type Ty;",
-            },
-            CompletionItem {
-                label: "func()",
-                source_range: 219..219,
-                delete: 219..219,
-                insert: "func()$0",
-                kind: Function,
-                lookup: "func",
-                detail: "fn func()",
-            },
-            CompletionItem {
-                label: "method()",
-                source_range: 219..219,
-                delete: 219..219,
-                insert: "method()$0",
-                kind: Method,
-                lookup: "method",
-                detail: "fn method(&self)",
-            },
-            CompletionItem {
-                label: "subfunc()",
-                source_range: 219..219,
-                delete: 219..219,
-                insert: "subfunc()$0",
-                kind: Function,
-                lookup: "subfunc",
-                detail: "fn subfunc()",
-            },
-            CompletionItem {
-                label: "submethod()",
-                source_range: 219..219,
-                delete: 219..219,
-                insert: "submethod()$0",
-                kind: Method,
-                lookup: "submethod",
-                detail: "fn submethod(&self)",
-            },
-        ]
-        "###
-        );
-    }
-
-    #[test]
-    fn completes_self_param_assoc_ty() {
-        assert_debug_snapshot!(
-            do_reference_completion(
-                "
-                //- /lib.rs
-                trait Super {
-                    type Ty;
-                    const CONST: u8 = 0;
-                    fn func() {}
-                    fn method(&self) {}
-                }
-
-                trait Sub: Super {
-                    type SubTy;
-                    const C2: () = ();
-                    fn subfunc() {}
-                    fn submethod(&self) {}
-                }
-
-                struct Wrap<T>(T);
-                impl<T> Super for Wrap<T> {}
-                impl<T> Sub for Wrap<T> {
-                    fn subfunc() {
-                        // Should be able to assume `Self: Sub + Super`
-                        Self::<|>
-                    }
-                }
-                "
-            ),
-            @r###"
-        [
-            CompletionItem {
-                label: "C2",
-                source_range: 365..365,
-                delete: 365..365,
-                insert: "C2",
-                kind: Const,
-                detail: "const C2: () = ();",
-            },
-            CompletionItem {
-                label: "CONST",
-                source_range: 365..365,
-                delete: 365..365,
-                insert: "CONST",
-                kind: Const,
-                detail: "const CONST: u8 = 0;",
-            },
-            CompletionItem {
-                label: "SubTy",
-                source_range: 365..365,
-                delete: 365..365,
-                insert: "SubTy",
-                kind: TypeAlias,
-                detail: "type SubTy;",
-            },
-            CompletionItem {
-                label: "Ty",
-                source_range: 365..365,
-                delete: 365..365,
-                insert: "Ty",
-                kind: TypeAlias,
-                detail: "type Ty;",
-            },
-            CompletionItem {
-                label: "func()",
-                source_range: 365..365,
-                delete: 365..365,
-                insert: "func()$0",
-                kind: Function,
-                lookup: "func",
-                detail: "fn func()",
-            },
-            CompletionItem {
-                label: "method()",
-                source_range: 365..365,
-                delete: 365..365,
-                insert: "method()$0",
-                kind: Method,
-                lookup: "method",
-                detail: "fn method(&self)",
-            },
-            CompletionItem {
-                label: "subfunc()",
-                source_range: 365..365,
-                delete: 365..365,
-                insert: "subfunc()$0",
-                kind: Function,
-                lookup: "subfunc",
-                detail: "fn subfunc()",
-            },
-            CompletionItem {
-                label: "submethod()",
-                source_range: 365..365,
-                delete: 365..365,
-                insert: "submethod()$0",
-                kind: Method,
-                lookup: "submethod",
-                detail: "fn submethod(&self)",
-            },
-        ]
-        "###
-        );
-    }
-
     #[test]
     fn completes_type_alias() {
         assert_debug_snapshot!(
diff --git a/crates/ra_ide/src/completion/complete_unqualified_path.rs b/crates/ra_ide/src/completion/complete_unqualified_path.rs
index a6a5568de0..f559f2b970 100644
--- a/crates/ra_ide/src/completion/complete_unqualified_path.rs
+++ b/crates/ra_ide/src/completion/complete_unqualified_path.rs
@@ -53,7 +53,7 @@ fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext, ty: &T
                 // Variants with trivial paths are already added by the existing completion logic,
                 // so we should avoid adding these twice
                 if path.segments.len() > 1 {
-                    acc.add_qualified_enum_variant(ctx, variant, path);
+                    acc.add_enum_variant(ctx, variant, Some(path.to_string()));
                 }
             }
         }
@@ -1173,7 +1173,6 @@ mod tests {
                 delete: 248..250,
                 insert: "Foo::Bar",
                 kind: EnumVariant,
-                lookup: "Bar",
                 detail: "()",
             },
             CompletionItem {
@@ -1182,7 +1181,6 @@ mod tests {
                 delete: 248..250,
                 insert: "Foo::Baz",
                 kind: EnumVariant,
-                lookup: "Baz",
                 detail: "()",
             },
             CompletionItem {
@@ -1191,7 +1189,6 @@ mod tests {
                 delete: 248..250,
                 insert: "Foo::Quux",
                 kind: EnumVariant,
-                lookup: "Quux",
                 detail: "()",
             },
         ]
@@ -1234,7 +1231,6 @@ mod tests {
                 delete: 219..221,
                 insert: "Foo::Bar",
                 kind: EnumVariant,
-                lookup: "Bar",
                 detail: "()",
             },
             CompletionItem {
@@ -1243,7 +1239,6 @@ mod tests {
                 delete: 219..221,
                 insert: "Foo::Baz",
                 kind: EnumVariant,
-                lookup: "Baz",
                 detail: "()",
             },
             CompletionItem {
@@ -1252,7 +1247,6 @@ mod tests {
                 delete: 219..221,
                 insert: "Foo::Quux",
                 kind: EnumVariant,
-                lookup: "Quux",
                 detail: "()",
             },
         ]
@@ -1291,7 +1285,6 @@ mod tests {
                 delete: 185..186,
                 insert: "Foo::Bar",
                 kind: EnumVariant,
-                lookup: "Bar",
                 detail: "()",
             },
             CompletionItem {
@@ -1300,7 +1293,6 @@ mod tests {
                 delete: 185..186,
                 insert: "Foo::Baz",
                 kind: EnumVariant,
-                lookup: "Baz",
                 detail: "()",
             },
             CompletionItem {
@@ -1309,7 +1301,6 @@ mod tests {
                 delete: 185..186,
                 insert: "Foo::Quux",
                 kind: EnumVariant,
-                lookup: "Quux",
                 detail: "()",
             },
             CompletionItem {
@@ -1362,7 +1353,6 @@ mod tests {
                 delete: 98..99,
                 insert: "m::E::V",
                 kind: EnumVariant,
-                lookup: "V",
                 detail: "()",
             },
         ]
diff --git a/crates/ra_ide/src/completion/presentation.rs b/crates/ra_ide/src/completion/presentation.rs
index 2edb130cf7..77d3543760 100644
--- a/crates/ra_ide/src/completion/presentation.rs
+++ b/crates/ra_ide/src/completion/presentation.rs
@@ -1,6 +1,6 @@
 //! This modules takes care of rendering various definitions as completion items.
 
-use hir::{Docs, HasAttrs, HasSource, HirDisplay, ModPath, ScopeDef, StructKind, Type};
+use hir::{Docs, HasAttrs, HasSource, HirDisplay, ScopeDef, StructKind, Type};
 use ra_syntax::ast::NameOwner;
 use stdx::SepBy;
 use test_utils::tested_by;
@@ -246,37 +246,14 @@ impl Completions {
             .add_to(self);
     }
 
-    pub(crate) fn add_qualified_enum_variant(
-        &mut self,
-        ctx: &CompletionContext,
-        variant: hir::EnumVariant,
-        path: ModPath,
-    ) {
-        self.add_enum_variant_impl(ctx, variant, None, Some(path))
-    }
-
     pub(crate) fn add_enum_variant(
         &mut self,
         ctx: &CompletionContext,
         variant: hir::EnumVariant,
         local_name: Option<String>,
-    ) {
-        self.add_enum_variant_impl(ctx, variant, local_name, None)
-    }
-
-    fn add_enum_variant_impl(
-        &mut self,
-        ctx: &CompletionContext,
-        variant: hir::EnumVariant,
-        local_name: Option<String>,
-        path: Option<ModPath>,
     ) {
         let is_deprecated = is_deprecated(variant, ctx.db);
         let name = local_name.unwrap_or_else(|| variant.name(ctx.db).to_string());
-        let qualified_name = match &path {
-            Some(it) => it.to_string(),
-            None => name.to_string(),
-        };
         let detail_types = variant
             .fields(ctx.db)
             .into_iter()
@@ -294,23 +271,16 @@ impl Completions {
                 .surround_with("{ ", " }")
                 .to_string(),
         };
-        let mut res = CompletionItem::new(
-            CompletionKind::Reference,
-            ctx.source_range(),
-            qualified_name.clone(),
-        )
-        .kind(CompletionItemKind::EnumVariant)
-        .set_documentation(variant.docs(ctx.db))
-        .set_deprecated(is_deprecated)
-        .detail(detail);
-
-        if path.is_some() {
-            res = res.lookup_by(name);
-        }
+        let mut res =
+            CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.clone())
+                .kind(CompletionItemKind::EnumVariant)
+                .set_documentation(variant.docs(ctx.db))
+                .set_deprecated(is_deprecated)
+                .detail(detail);
 
         if variant_kind == StructKind::Tuple {
             let params = Params::Anonymous(variant.fields(ctx.db).len());
-            res = res.add_call_parens(ctx, qualified_name, params)
+            res = res.add_call_parens(ctx, name, params)
         }
 
         res.add_to(self);
diff --git a/crates/ra_ide/src/display/function_signature.rs b/crates/ra_ide/src/display/function_signature.rs
index db3907fe64..b5e2785fe0 100644
--- a/crates/ra_ide/src/display/function_signature.rs
+++ b/crates/ra_ide/src/display/function_signature.rs
@@ -26,8 +26,6 @@ pub struct FunctionSignature {
     pub kind: CallableKind,
     /// Optional visibility
     pub visibility: Option<String>,
-    /// Qualifiers like `async`, `unsafe`, ...
-    pub qualifier: FunctionQualifier,
     /// Name of the function
     pub name: Option<String>,
     /// Documentation for the function
@@ -48,16 +46,6 @@ pub struct FunctionSignature {
     pub has_self_param: bool,
 }
 
-#[derive(Debug, Default)]
-pub struct FunctionQualifier {
-    // `async` and `const` are mutually exclusive. Do we need to enforcing it here?
-    pub is_async: bool,
-    pub is_const: bool,
-    pub is_unsafe: bool,
-    /// The string `extern ".."`
-    pub extern_abi: Option<String>,
-}
-
 impl FunctionSignature {
     pub(crate) fn with_doc_opt(mut self, doc: Option<Documentation>) -> Self {
         self.doc = doc;
@@ -95,8 +83,6 @@ impl FunctionSignature {
             FunctionSignature {
                 kind: CallableKind::StructConstructor,
                 visibility: node.visibility().map(|n| n.syntax().text().to_string()),
-                // Do we need `const`?
-                qualifier: Default::default(),
                 name: node.name().map(|n| n.text().to_string()),
                 ret_type: node.name().map(|n| n.text().to_string()),
                 parameters: params,
@@ -142,8 +128,6 @@ impl FunctionSignature {
             FunctionSignature {
                 kind: CallableKind::VariantConstructor,
                 visibility: None,
-                // Do we need `const`?
-                qualifier: Default::default(),
                 name: Some(name),
                 ret_type: None,
                 parameters: params,
@@ -167,7 +151,6 @@ impl FunctionSignature {
             FunctionSignature {
                 kind: CallableKind::Macro,
                 visibility: None,
-                qualifier: Default::default(),
                 name: node.name().map(|n| n.text().to_string()),
                 ret_type: None,
                 parameters: params,
@@ -240,12 +223,6 @@ impl From<&'_ ast::FnDef> for FunctionSignature {
         FunctionSignature {
             kind: CallableKind::Function,
             visibility: node.visibility().map(|n| n.syntax().text().to_string()),
-            qualifier: FunctionQualifier {
-                is_async: node.async_token().is_some(),
-                is_const: node.const_token().is_some(),
-                is_unsafe: node.unsafe_token().is_some(),
-                extern_abi: node.abi().map(|n| n.to_string()),
-            },
             name: node.name().map(|n| n.text().to_string()),
             ret_type: node
                 .ret_type()
@@ -269,23 +246,6 @@ impl Display for FunctionSignature {
             write!(f, "{} ", t)?;
         }
 
-        if self.qualifier.is_async {
-            write!(f, "async ")?;
-        }
-
-        if self.qualifier.is_const {
-            write!(f, "const ")?;
-        }
-
-        if self.qualifier.is_unsafe {
-            write!(f, "unsafe ")?;
-        }
-
-        if let Some(extern_abi) = &self.qualifier.extern_abi {
-            // Keyword `extern` is included in the string.
-            write!(f, "{} ", extern_abi)?;
-        }
-
         if let Some(name) = &self.name {
             match self.kind {
                 CallableKind::Function => write!(f, "fn {}", name)?,
diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs
index a62f598f0d..58c799eca7 100644
--- a/crates/ra_ide/src/hover.rs
+++ b/crates/ra_ide/src/hover.rs
@@ -844,29 +844,4 @@ fn func(foo: i32) { if true { <|>foo; }; }
             &["fn foo()\n```\n\n<- `\u{3000}` here"],
         );
     }
-
-    #[test]
-    fn test_hover_function_show_qualifiers() {
-        check_hover_result(
-            "
-            //- /lib.rs
-            async fn foo<|>() {}
-            ",
-            &["async fn foo()"],
-        );
-        check_hover_result(
-            "
-            //- /lib.rs
-            pub const unsafe fn foo<|>() {}
-            ",
-            &["pub const unsafe fn foo()"],
-        );
-        check_hover_result(
-            r#"
-            //- /lib.rs
-            pub(crate) async unsafe extern "C" fn foo<|>() {}
-            "#,
-            &[r#"pub(crate) async unsafe extern "C" fn foo()"#],
-        );
-    }
 }
diff --git a/crates/ra_ide/src/snapshots/highlight_injection.html b/crates/ra_ide/src/snapshots/highlight_injection.html
index ea026d7a04..6ec13bd80f 100644
--- a/crates/ra_ide/src/snapshots/highlight_injection.html
+++ b/crates/ra_ide/src/snapshots/highlight_injection.html
@@ -20,7 +20,6 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .macro              { color: #94BFF3; }
 .module             { color: #AFD8AF; }
 .variable           { color: #DCDCCC; }
-.format_specifier   { color: #CC696B; }
 .mutable            { text-decoration: underline; }
 
 .keyword            { color: #F0DFAF; font-weight: bold; }
diff --git a/crates/ra_ide/src/snapshots/highlight_strings.html b/crates/ra_ide/src/snapshots/highlight_strings.html
index de06daf72b..433f2e0c5e 100644
--- a/crates/ra_ide/src/snapshots/highlight_strings.html
+++ b/crates/ra_ide/src/snapshots/highlight_strings.html
@@ -20,7 +20,6 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .macro              { color: #94BFF3; }
 .module             { color: #AFD8AF; }
 .variable           { color: #DCDCCC; }
-.format_specifier   { color: #CC696B; }
 .mutable            { text-decoration: underline; }
 
 .keyword            { color: #F0DFAF; font-weight: bold; }
@@ -41,43 +40,43 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 <span class="keyword">fn</span> <span class="function declaration">main</span>() {
     <span class="comment">// from https://doc.rust-lang.org/std/fmt/index.html</span>
     <span class="macro">println!</span>(<span class="string_literal">"Hello"</span>);                 <span class="comment">// =&gt; "Hello"</span>
-    <span class="macro">println!</span>(<span class="string_literal">"Hello, </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="string_literal">"world"</span>);   <span class="comment">// =&gt; "Hello, world!"</span>
-    <span class="macro">println!</span>(<span class="string_literal">"The number is </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">"</span>, <span class="numeric_literal">1</span>);   <span class="comment">// =&gt; "The number is 1"</span>
-    <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">?</span><span class="format_specifier">}</span><span class="string_literal">"</span>, (<span class="numeric_literal">3</span>, <span class="numeric_literal">4</span>));          <span class="comment">// =&gt; "(3, 4)"</span>
-    <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">value</span><span class="format_specifier">}</span><span class="string_literal">"</span>, value=<span class="numeric_literal">4</span>);      <span class="comment">// =&gt; "4"</span>
-    <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">"</span>, <span class="numeric_literal">1</span>, <span class="numeric_literal">2</span>);           <span class="comment">// =&gt; "1 2"</span>
-    <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="numeric_literal">4</span><span class="format_specifier">}</span><span class="string_literal">"</span>, <span class="numeric_literal">42</span>);             <span class="comment">// =&gt; "0042" with leading zerosV</span>
-    <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">"</span>, <span class="numeric_literal">1</span>, <span class="numeric_literal">2</span>);   <span class="comment">// =&gt; "2 1 1 2"</span>
-    <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">argument</span><span class="format_specifier">}</span><span class="string_literal">"</span>, argument = <span class="string_literal">"test"</span>);   <span class="comment">// =&gt; "test"</span>
-    <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">"</span>, <span class="numeric_literal">1</span>, name = <span class="numeric_literal">2</span>);          <span class="comment">// =&gt; "2 1"</span>
-    <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">a</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="variable">c</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="variable">b</span><span class="format_specifier">}</span><span class="string_literal">"</span>, a=<span class="string_literal">"a"</span>, b=<span class="char_literal">'b'</span>, c=<span class="numeric_literal">3</span>);  <span class="comment">// =&gt; "a 3 b"</span>
-    <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>);
-    <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">1</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>, <span class="numeric_literal">5</span>);
-    <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="numeric_literal">5</span>, <span class="string_literal">"x"</span>);
-    <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="variable">width</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>, width = <span class="numeric_literal">5</span>);
-    <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">&lt;</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>);
-    <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">-</span><span class="format_specifier">&lt;</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>);
-    <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">^</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>);
-    <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">&gt;</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>);
-    <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">+</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="numeric_literal">5</span>);
-    <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">#</span><span class="variable">x</span><span class="string_literal">}!"</span>, <span class="numeric_literal">27</span>);
-    <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="numeric_literal">5</span>);
-    <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, -<span class="numeric_literal">5</span>);
-    <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">#</span><span class="numeric_literal">0</span><span class="numeric_literal">10</span><span class="variable">x</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="numeric_literal">27</span>);
-    <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal"> is </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">"</span>, <span class="string_literal">"x"</span>, <span class="numeric_literal">0.01</span>);
-    <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">}</span><span class="string_literal"> is </span><span class="format_specifier">{</span><span class="numeric_literal">2</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="numeric_literal">0</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">"</span>, <span class="numeric_literal">5</span>, <span class="string_literal">"x"</span>, <span class="numeric_literal">0.01</span>);
-    <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal"> is </span><span class="format_specifier">{</span><span class="numeric_literal">2</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="numeric_literal">1</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">"</span>, <span class="string_literal">"x"</span>, <span class="numeric_literal">5</span>, <span class="numeric_literal">0.01</span>);
-    <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> is </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal">"</span>,    <span class="string_literal">"x"</span>, <span class="numeric_literal">5</span>, <span class="numeric_literal">0.01</span>);
-    <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> is </span><span class="format_specifier">{</span><span class="numeric_literal">2</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal">"</span>,   <span class="string_literal">"x"</span>, <span class="numeric_literal">5</span>, <span class="numeric_literal">0.01</span>);
-    <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> is </span><span class="format_specifier">{</span><span class="variable">number</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="variable">prec</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">"</span>, <span class="string_literal">"x"</span>, prec = <span class="numeric_literal">5</span>, number = <span class="numeric_literal">0.01</span>);
-    <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal">` has 3 fractional digits"</span>, <span class="string_literal">"Hello"</span>, <span class="numeric_literal">3</span>, name=<span class="numeric_literal">1234.56</span>);
-    <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal">` has 3 characters"</span>, <span class="string_literal">"Hello"</span>, <span class="numeric_literal">3</span>, name=<span class="string_literal">"1234.56"</span>);
-    <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">&gt;</span><span class="numeric_literal">8</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal">` has 3 right-aligned characters"</span>, <span class="string_literal">"Hello"</span>, <span class="numeric_literal">3</span>, name=<span class="string_literal">"1234.56"</span>);
+    <span class="macro">println!</span>(<span class="string_literal">"Hello, </span><span class="attribute">{</span><span class="attribute">}</span><span class="string_literal">!"</span>, <span class="string_literal">"world"</span>);   <span class="comment">// =&gt; "Hello, world!"</span>
+    <span class="macro">println!</span>(<span class="string_literal">"The number is </span><span class="attribute">{</span><span class="attribute">}</span><span class="string_literal">"</span>, <span class="numeric_literal">1</span>);   <span class="comment">// =&gt; "The number is 1"</span>
+    <span class="macro">println!</span>(<span class="string_literal">"</span><span class="attribute">{</span><span class="attribute">:</span><span class="attribute">?</span><span class="attribute">}</span><span class="string_literal">"</span>, (<span class="numeric_literal">3</span>, <span class="numeric_literal">4</span>));          <span class="comment">// =&gt; "(3, 4)"</span>
+    <span class="macro">println!</span>(<span class="string_literal">"</span><span class="attribute">{</span><span class="variable">value</span><span class="attribute">}</span><span class="string_literal">"</span>, value=<span class="numeric_literal">4</span>);      <span class="comment">// =&gt; "4"</span>
+    <span class="macro">println!</span>(<span class="string_literal">"</span><span class="attribute">{</span><span class="attribute">}</span><span class="string_literal"> </span><span class="attribute">{</span><span class="attribute">}</span><span class="string_literal">"</span>, <span class="numeric_literal">1</span>, <span class="numeric_literal">2</span>);           <span class="comment">// =&gt; "1 2"</span>
+    <span class="macro">println!</span>(<span class="string_literal">"</span><span class="attribute">{</span><span class="attribute">:</span><span class="numeric_literal">0</span><span class="numeric_literal">4</span><span class="attribute">}</span><span class="string_literal">"</span>, <span class="numeric_literal">42</span>);             <span class="comment">// =&gt; "0042" with leading zerosV</span>
+    <span class="macro">println!</span>(<span class="string_literal">"</span><span class="attribute">{</span><span class="numeric_literal">1</span><span class="attribute">}</span><span class="string_literal"> </span><span class="attribute">{</span><span class="attribute">}</span><span class="string_literal"> </span><span class="attribute">{</span><span class="numeric_literal">0</span><span class="attribute">}</span><span class="string_literal"> </span><span class="attribute">{</span><span class="attribute">}</span><span class="string_literal">"</span>, <span class="numeric_literal">1</span>, <span class="numeric_literal">2</span>);   <span class="comment">// =&gt; "2 1 1 2"</span>
+    <span class="macro">println!</span>(<span class="string_literal">"</span><span class="attribute">{</span><span class="variable">argument</span><span class="attribute">}</span><span class="string_literal">"</span>, argument = <span class="string_literal">"test"</span>);   <span class="comment">// =&gt; "test"</span>
+    <span class="macro">println!</span>(<span class="string_literal">"</span><span class="attribute">{</span><span class="variable">name</span><span class="attribute">}</span><span class="string_literal"> </span><span class="attribute">{</span><span class="attribute">}</span><span class="string_literal">"</span>, <span class="numeric_literal">1</span>, name = <span class="numeric_literal">2</span>);          <span class="comment">// =&gt; "2 1"</span>
+    <span class="macro">println!</span>(<span class="string_literal">"</span><span class="attribute">{</span><span class="variable">a</span><span class="attribute">}</span><span class="string_literal"> </span><span class="attribute">{</span><span class="variable">c</span><span class="attribute">}</span><span class="string_literal"> </span><span class="attribute">{</span><span class="variable">b</span><span class="attribute">}</span><span class="string_literal">"</span>, a=<span class="string_literal">"a"</span>, b=<span class="char_literal">'b'</span>, c=<span class="numeric_literal">3</span>);  <span class="comment">// =&gt; "a 3 b"</span>
+    <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="attribute">{</span><span class="attribute">:</span><span class="numeric_literal">5</span><span class="attribute">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>);
+    <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="attribute">{</span><span class="attribute">:</span><span class="numeric_literal">1</span><span class="attribute">$</span><span class="attribute">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>, <span class="numeric_literal">5</span>);
+    <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="attribute">{</span><span class="numeric_literal">1</span><span class="attribute">:</span><span class="numeric_literal">0</span><span class="attribute">$</span><span class="attribute">}</span><span class="string_literal">!"</span>, <span class="numeric_literal">5</span>, <span class="string_literal">"x"</span>);
+    <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="attribute">{</span><span class="attribute">:</span><span class="variable">width</span><span class="attribute">$</span><span class="attribute">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>, width = <span class="numeric_literal">5</span>);
+    <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="attribute">{</span><span class="attribute">:</span><span class="attribute">&lt;</span><span class="numeric_literal">5</span><span class="attribute">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>);
+    <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="attribute">{</span><span class="attribute">:</span><span class="attribute">-</span><span class="attribute">&lt;</span><span class="numeric_literal">5</span><span class="attribute">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>);
+    <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="attribute">{</span><span class="attribute">:</span><span class="attribute">^</span><span class="numeric_literal">5</span><span class="attribute">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>);
+    <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="attribute">{</span><span class="attribute">:</span><span class="attribute">&gt;</span><span class="numeric_literal">5</span><span class="attribute">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>);
+    <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="attribute">{</span><span class="attribute">:</span><span class="attribute">+</span><span class="attribute">}</span><span class="string_literal">!"</span>, <span class="numeric_literal">5</span>);
+    <span class="macro">println!</span>(<span class="string_literal">"</span><span class="attribute">{</span><span class="attribute">:</span><span class="attribute">#</span><span class="variable">x</span><span class="string_literal">}!"</span>, <span class="numeric_literal">27</span>);
+    <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="attribute">{</span><span class="attribute">:</span><span class="numeric_literal">0</span><span class="numeric_literal">5</span><span class="attribute">}</span><span class="string_literal">!"</span>, <span class="numeric_literal">5</span>);
+    <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="attribute">{</span><span class="attribute">:</span><span class="numeric_literal">0</span><span class="numeric_literal">5</span><span class="attribute">}</span><span class="string_literal">!"</span>, -<span class="numeric_literal">5</span>);
+    <span class="macro">println!</span>(<span class="string_literal">"</span><span class="attribute">{</span><span class="attribute">:</span><span class="attribute">#</span><span class="numeric_literal">0</span><span class="numeric_literal">10</span><span class="variable">x</span><span class="attribute">}</span><span class="string_literal">!"</span>, <span class="numeric_literal">27</span>);
+    <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="attribute">{</span><span class="numeric_literal">0</span><span class="attribute">}</span><span class="string_literal"> is </span><span class="attribute">{</span><span class="numeric_literal">1</span><span class="attribute">:</span><span class="attribute">.</span><span class="numeric_literal">5</span><span class="attribute">}</span><span class="string_literal">"</span>, <span class="string_literal">"x"</span>, <span class="numeric_literal">0.01</span>);
+    <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="attribute">{</span><span class="numeric_literal">1</span><span class="attribute">}</span><span class="string_literal"> is </span><span class="attribute">{</span><span class="numeric_literal">2</span><span class="attribute">:</span><span class="attribute">.</span><span class="numeric_literal">0</span><span class="attribute">$</span><span class="attribute">}</span><span class="string_literal">"</span>, <span class="numeric_literal">5</span>, <span class="string_literal">"x"</span>, <span class="numeric_literal">0.01</span>);
+    <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="attribute">{</span><span class="numeric_literal">0</span><span class="attribute">}</span><span class="string_literal"> is </span><span class="attribute">{</span><span class="numeric_literal">2</span><span class="attribute">:</span><span class="attribute">.</span><span class="numeric_literal">1</span><span class="attribute">$</span><span class="attribute">}</span><span class="string_literal">"</span>, <span class="string_literal">"x"</span>, <span class="numeric_literal">5</span>, <span class="numeric_literal">0.01</span>);
+    <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="attribute">{</span><span class="attribute">}</span><span class="string_literal"> is </span><span class="attribute">{</span><span class="attribute">:</span><span class="attribute">.</span><span class="attribute">*</span><span class="attribute">}</span><span class="string_literal">"</span>,    <span class="string_literal">"x"</span>, <span class="numeric_literal">5</span>, <span class="numeric_literal">0.01</span>);
+    <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="attribute">{</span><span class="attribute">}</span><span class="string_literal"> is </span><span class="attribute">{</span><span class="numeric_literal">2</span><span class="attribute">:</span><span class="attribute">.</span><span class="attribute">*</span><span class="attribute">}</span><span class="string_literal">"</span>,   <span class="string_literal">"x"</span>, <span class="numeric_literal">5</span>, <span class="numeric_literal">0.01</span>);
+    <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="attribute">{</span><span class="attribute">}</span><span class="string_literal"> is </span><span class="attribute">{</span><span class="variable">number</span><span class="attribute">:</span><span class="attribute">.</span><span class="variable">prec</span><span class="attribute">$</span><span class="attribute">}</span><span class="string_literal">"</span>, <span class="string_literal">"x"</span>, prec = <span class="numeric_literal">5</span>, number = <span class="numeric_literal">0.01</span>);
+    <span class="macro">println!</span>(<span class="string_literal">"</span><span class="attribute">{</span><span class="attribute">}</span><span class="string_literal">, `</span><span class="attribute">{</span><span class="variable">name</span><span class="attribute">:</span><span class="attribute">.</span><span class="attribute">*</span><span class="attribute">}</span><span class="string_literal">` has 3 fractional digits"</span>, <span class="string_literal">"Hello"</span>, <span class="numeric_literal">3</span>, name=<span class="numeric_literal">1234.56</span>);
+    <span class="macro">println!</span>(<span class="string_literal">"</span><span class="attribute">{</span><span class="attribute">}</span><span class="string_literal">, `</span><span class="attribute">{</span><span class="variable">name</span><span class="attribute">:</span><span class="attribute">.</span><span class="attribute">*</span><span class="attribute">}</span><span class="string_literal">` has 3 characters"</span>, <span class="string_literal">"Hello"</span>, <span class="numeric_literal">3</span>, name=<span class="string_literal">"1234.56"</span>);
+    <span class="macro">println!</span>(<span class="string_literal">"</span><span class="attribute">{</span><span class="attribute">}</span><span class="string_literal">, `</span><span class="attribute">{</span><span class="variable">name</span><span class="attribute">:</span><span class="attribute">&gt;</span><span class="numeric_literal">8</span><span class="attribute">.</span><span class="attribute">*</span><span class="attribute">}</span><span class="string_literal">` has 3 right-aligned characters"</span>, <span class="string_literal">"Hello"</span>, <span class="numeric_literal">3</span>, name=<span class="string_literal">"1234.56"</span>);
     <span class="macro">println!</span>(<span class="string_literal">"Hello {{}}"</span>);
     <span class="macro">println!</span>(<span class="string_literal">"{{ Hello"</span>);
 
-    <span class="macro">println!</span>(<span class="string_literal">r"Hello, </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="string_literal">"world"</span>);
+    <span class="macro">println!</span>(<span class="string_literal">r"Hello, </span><span class="attribute">{</span><span class="attribute">}</span><span class="string_literal">!"</span>, <span class="string_literal">"world"</span>);
 
-    <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">\x41</span><span class="format_specifier">}</span><span class="string_literal">"</span>, A = <span class="numeric_literal">92</span>);
-    <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">ничоси</span><span class="format_specifier">}</span><span class="string_literal">"</span>, ничоси = <span class="numeric_literal">92</span>);
+    <span class="macro">println!</span>(<span class="string_literal">"</span><span class="attribute">{</span><span class="variable">\x41</span><span class="attribute">}</span><span class="string_literal">"</span>, A = <span class="numeric_literal">92</span>);
+    <span class="macro">println!</span>(<span class="string_literal">"</span><span class="attribute">{</span><span class="variable">ничоси</span><span class="attribute">}</span><span class="string_literal">"</span>, ничоси = <span class="numeric_literal">92</span>);
 }</code></pre>
\ No newline at end of file
diff --git a/crates/ra_ide/src/snapshots/highlighting.html b/crates/ra_ide/src/snapshots/highlighting.html
index 4b12fe8238..ccb1fc7516 100644
--- a/crates/ra_ide/src/snapshots/highlighting.html
+++ b/crates/ra_ide/src/snapshots/highlighting.html
@@ -20,7 +20,6 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .macro              { color: #94BFF3; }
 .module             { color: #AFD8AF; }
 .variable           { color: #DCDCCC; }
-.format_specifier   { color: #CC696B; }
 .mutable            { text-decoration: underline; }
 
 .keyword            { color: #F0DFAF; font-weight: bold; }
diff --git a/crates/ra_ide/src/snapshots/rainbow_highlighting.html b/crates/ra_ide/src/snapshots/rainbow_highlighting.html
index 11e1f3e44e..3df82c45fb 100644
--- a/crates/ra_ide/src/snapshots/rainbow_highlighting.html
+++ b/crates/ra_ide/src/snapshots/rainbow_highlighting.html
@@ -20,7 +20,6 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .macro              { color: #94BFF3; }
 .module             { color: #AFD8AF; }
 .variable           { color: #DCDCCC; }
-.format_specifier   { color: #CC696B; }
 .mutable            { text-decoration: underline; }
 
 .keyword            { color: #F0DFAF; font-weight: bold; }
diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs
index 6658c7bb27..be0f8c827a 100644
--- a/crates/ra_ide/src/syntax_highlighting.rs
+++ b/crates/ra_ide/src/syntax_highlighting.rs
@@ -290,7 +290,7 @@ fn highlight_format_specifier(kind: FormatSpecifier) -> Option<HighlightTag> {
         | FormatSpecifier::DollarSign
         | FormatSpecifier::Dot
         | FormatSpecifier::Asterisk
-        | FormatSpecifier::QuestionMark => HighlightTag::FormatSpecifier,
+        | FormatSpecifier::QuestionMark => HighlightTag::Attribute,
         FormatSpecifier::Integer | FormatSpecifier::Zero => HighlightTag::NumericLiteral,
         FormatSpecifier::Identifier => HighlightTag::Local,
     })
diff --git a/crates/ra_ide/src/syntax_highlighting/html.rs b/crates/ra_ide/src/syntax_highlighting/html.rs
index ff0eeeb52c..010db40175 100644
--- a/crates/ra_ide/src/syntax_highlighting/html.rs
+++ b/crates/ra_ide/src/syntax_highlighting/html.rs
@@ -79,7 +79,6 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .macro              { color: #94BFF3; }
 .module             { color: #AFD8AF; }
 .variable           { color: #DCDCCC; }
-.format_specifier   { color: #CC696B; }
 .mutable            { text-decoration: underline; }
 
 .keyword            { color: #F0DFAF; font-weight: bold; }
diff --git a/crates/ra_ide/src/syntax_highlighting/tags.rs b/crates/ra_ide/src/syntax_highlighting/tags.rs
index be1a0f12b2..f2c4216545 100644
--- a/crates/ra_ide/src/syntax_highlighting/tags.rs
+++ b/crates/ra_ide/src/syntax_highlighting/tags.rs
@@ -39,7 +39,6 @@ pub enum HighlightTag {
     Union,
     Local,
     UnresolvedReference,
-    FormatSpecifier,
 }
 
 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
@@ -82,7 +81,6 @@ impl HighlightTag {
             HighlightTag::Union => "union",
             HighlightTag::Local => "variable",
             HighlightTag::UnresolvedReference => "unresolved_reference",
-            HighlightTag::FormatSpecifier => "format_specifier",
         }
     }
 }
diff --git a/crates/ra_parser/src/grammar/expressions/atom.rs b/crates/ra_parser/src/grammar/expressions/atom.rs
index 166dfc472b..0d277a5866 100644
--- a/crates/ra_parser/src/grammar/expressions/atom.rs
+++ b/crates/ra_parser/src/grammar/expressions/atom.rs
@@ -535,22 +535,6 @@ fn break_expr(p: &mut Parser, r: Restrictions) -> CompletedMarker {
 fn try_block_expr(p: &mut Parser, m: Option<Marker>) -> CompletedMarker {
     assert!(p.at(T![try]));
     let m = m.unwrap_or_else(|| p.start());
-    // Special-case `try!` as macro.
-    // This is a hack until we do proper edition support
-    if p.nth_at(1, T![!]) {
-        // test try_macro_fallback
-        // fn foo() { try!(Ok(())); }
-        let path = p.start();
-        let path_segment = p.start();
-        let name_ref = p.start();
-        p.bump_remap(IDENT);
-        name_ref.complete(p, NAME_REF);
-        path_segment.complete(p, PATH_SEGMENT);
-        path.complete(p, PATH);
-        let _block_like = items::macro_call_after_excl(p);
-        return m.complete(p, MACRO_CALL);
-    }
-
     p.bump(T![try]);
     block(p);
     m.complete(p, TRY_EXPR)
diff --git a/crates/ra_proc_macro_srv/src/cli.rs b/crates/ra_proc_macro_srv/src/cli.rs
index 1437794c9e..7282e5b9b7 100644
--- a/crates/ra_proc_macro_srv/src/cli.rs
+++ b/crates/ra_proc_macro_srv/src/cli.rs
@@ -1,17 +1,15 @@
 //! Driver for proc macro server
 
-use crate::ProcMacroSrv;
+use crate::{expand_task, list_macros};
 use ra_proc_macro::msg::{self, Message};
 use std::io;
 
 pub fn run() -> io::Result<()> {
-    let mut srv = ProcMacroSrv::default();
-
     while let Some(req) = read_request()? {
         let res = match req {
-            msg::Request::ListMacro(task) => srv.list_macros(&task).map(msg::Response::ListMacro),
+            msg::Request::ListMacro(task) => Ok(msg::Response::ListMacro(list_macros(&task))),
             msg::Request::ExpansionMacro(task) => {
-                srv.expand(&task).map(msg::Response::ExpansionMacro)
+                expand_task(&task).map(msg::Response::ExpansionMacro)
             }
         };
 
diff --git a/crates/ra_proc_macro_srv/src/dylib.rs b/crates/ra_proc_macro_srv/src/dylib.rs
index aa84e951cd..d202eb0fde 100644
--- a/crates/ra_proc_macro_srv/src/dylib.rs
+++ b/crates/ra_proc_macro_srv/src/dylib.rs
@@ -2,12 +2,13 @@
 
 use crate::{proc_macro::bridge, rustc_server::TokenStream};
 use std::fs::File;
-use std::path::{Path, PathBuf};
+use std::path::Path;
 
 use goblin::{mach::Mach, Object};
 use libloading::Library;
 use memmap::Mmap;
 use ra_proc_macro::ProcMacroKind;
+
 use std::io;
 
 const NEW_REGISTRAR_SYMBOL: &str = "_rustc_proc_macro_decls_";
@@ -108,21 +109,23 @@ impl ProcMacroLibraryLibloading {
     }
 }
 
+type ProcMacroLibraryImpl = ProcMacroLibraryLibloading;
+
 pub struct Expander {
-    inner: ProcMacroLibraryLibloading,
+    libs: Vec<ProcMacroLibraryImpl>,
 }
 
 impl Expander {
-    pub fn new(lib: &Path) -> io::Result<Expander> {
+    pub fn new(lib: &Path) -> Result<Expander, String> {
         // Some libraries for dynamic loading require canonicalized path even when it is
         // already absolute
-        let lib = lib.canonicalize()?;
-
-        let lib = ensure_file_with_lock_free_access(&lib)?;
+        let lib = lib
+            .canonicalize()
+            .unwrap_or_else(|err| panic!("Cannot canonicalize {}: {:?}", lib.display(), err));
 
-        let library = ProcMacroLibraryLibloading::open(&lib)?;
+        let library = ProcMacroLibraryImpl::open(&lib).map_err(|e| e.to_string())?;
 
-        Ok(Expander { inner: library })
+        Ok(Expander { libs: vec![library] })
     }
 
     pub fn expand(
@@ -138,36 +141,38 @@ impl Expander {
                 TokenStream::with_subtree(attr.clone())
             });
 
-        for proc_macro in &self.inner.exported_macros {
-            match proc_macro {
-                bridge::client::ProcMacro::CustomDerive { trait_name, client, .. }
-                    if *trait_name == macro_name =>
-                {
-                    let res = client.run(
-                        &crate::proc_macro::bridge::server::SameThread,
-                        crate::rustc_server::Rustc::default(),
-                        parsed_body,
-                    );
-                    return res.map(|it| it.subtree);
-                }
-                bridge::client::ProcMacro::Bang { name, client } if *name == macro_name => {
-                    let res = client.run(
-                        &crate::proc_macro::bridge::server::SameThread,
-                        crate::rustc_server::Rustc::default(),
-                        parsed_body,
-                    );
-                    return res.map(|it| it.subtree);
-                }
-                bridge::client::ProcMacro::Attr { name, client } if *name == macro_name => {
-                    let res = client.run(
-                        &crate::proc_macro::bridge::server::SameThread,
-                        crate::rustc_server::Rustc::default(),
-                        parsed_attributes,
-                        parsed_body,
-                    );
-                    return res.map(|it| it.subtree);
+        for lib in &self.libs {
+            for proc_macro in &lib.exported_macros {
+                match proc_macro {
+                    bridge::client::ProcMacro::CustomDerive { trait_name, client, .. }
+                        if *trait_name == macro_name =>
+                    {
+                        let res = client.run(
+                            &crate::proc_macro::bridge::server::SameThread,
+                            crate::rustc_server::Rustc::default(),
+                            parsed_body,
+                        );
+                        return res.map(|it| it.subtree);
+                    }
+                    bridge::client::ProcMacro::Bang { name, client } if *name == macro_name => {
+                        let res = client.run(
+                            &crate::proc_macro::bridge::server::SameThread,
+                            crate::rustc_server::Rustc::default(),
+                            parsed_body,
+                        );
+                        return res.map(|it| it.subtree);
+                    }
+                    bridge::client::ProcMacro::Attr { name, client } if *name == macro_name => {
+                        let res = client.run(
+                            &crate::proc_macro::bridge::server::SameThread,
+                            crate::rustc_server::Rustc::default(),
+                            parsed_attributes,
+                            parsed_body,
+                        );
+                        return res.map(|it| it.subtree);
+                    }
+                    _ => continue,
                 }
-                _ => continue,
             }
         }
 
@@ -175,9 +180,9 @@ impl Expander {
     }
 
     pub fn list_macros(&self) -> Vec<(String, ProcMacroKind)> {
-        self.inner
-            .exported_macros
+        self.libs
             .iter()
+            .flat_map(|it| &it.exported_macros)
             .map(|proc_macro| match proc_macro {
                 bridge::client::ProcMacro::CustomDerive { trait_name, .. } => {
                     (trait_name.to_string(), ProcMacroKind::CustomDerive)
@@ -192,33 +197,3 @@ impl Expander {
             .collect()
     }
 }
-
-/// Copy the dylib to temp directory to prevent locking in Windows
-#[cfg(windows)]
-fn ensure_file_with_lock_free_access(path: &Path) -> io::Result<PathBuf> {
-    use std::{ffi::OsString, time::SystemTime};
-
-    let mut to = std::env::temp_dir();
-
-    let file_name = path.file_name().ok_or_else(|| {
-        io::Error::new(
-            io::ErrorKind::InvalidInput,
-            format!("File path is invalid: {}", path.display()),
-        )
-    })?;
-
-    // generate a time deps unique number
-    let t = SystemTime::now().duration_since(std::time::UNIX_EPOCH).expect("Time went backwards");
-
-    let mut unique_name = OsString::from(t.as_millis().to_string());
-    unique_name.push(file_name);
-
-    to.push(unique_name);
-    std::fs::copy(path, &to).unwrap();
-    Ok(to)
-}
-
-#[cfg(unix)]
-fn ensure_file_with_lock_free_access(path: &Path) -> io::Result<PathBuf> {
-    Ok(path.to_path_buf())
-}
diff --git a/crates/ra_proc_macro_srv/src/lib.rs b/crates/ra_proc_macro_srv/src/lib.rs
index 922bb84bbf..3aca859db3 100644
--- a/crates/ra_proc_macro_srv/src/lib.rs
+++ b/crates/ra_proc_macro_srv/src/lib.rs
@@ -21,46 +21,28 @@ mod dylib;
 
 use proc_macro::bridge::client::TokenStream;
 use ra_proc_macro::{ExpansionResult, ExpansionTask, ListMacrosResult, ListMacrosTask};
-use std::{
-    collections::{hash_map::Entry, HashMap},
-    fs,
-    path::{Path, PathBuf},
-    time::SystemTime,
-};
+use std::path::Path;
 
-#[derive(Default)]
-pub(crate) struct ProcMacroSrv {
-    expanders: HashMap<(PathBuf, SystemTime), dylib::Expander>,
-}
+pub(crate) fn expand_task(task: &ExpansionTask) -> Result<ExpansionResult, String> {
+    let expander = create_expander(&task.lib);
 
-impl ProcMacroSrv {
-    pub fn expand(&mut self, task: &ExpansionTask) -> Result<ExpansionResult, String> {
-        let expander = self.expander(&task.lib)?;
-        match expander.expand(&task.macro_name, &task.macro_body, task.attributes.as_ref()) {
-            Ok(expansion) => Ok(ExpansionResult { expansion }),
-            Err(msg) => {
-                Err(format!("Cannot perform expansion for {}: error {:?}", &task.macro_name, msg))
-            }
+    match expander.expand(&task.macro_name, &task.macro_body, task.attributes.as_ref()) {
+        Ok(expansion) => Ok(ExpansionResult { expansion }),
+        Err(msg) => {
+            Err(format!("Cannot perform expansion for {}: error {:?}", &task.macro_name, msg))
         }
     }
+}
 
-    pub fn list_macros(&mut self, task: &ListMacrosTask) -> Result<ListMacrosResult, String> {
-        let expander = self.expander(&task.lib)?;
-        Ok(ListMacrosResult { macros: expander.list_macros() })
-    }
+pub(crate) fn list_macros(task: &ListMacrosTask) -> ListMacrosResult {
+    let expander = create_expander(&task.lib);
 
-    fn expander(&mut self, path: &Path) -> Result<&dylib::Expander, String> {
-        let time = fs::metadata(path).and_then(|it| it.modified()).map_err(|err| {
-            format!("Failed to get file metadata for {}: {:?}", path.display(), err)
-        })?;
+    ListMacrosResult { macros: expander.list_macros() }
+}
 
-        Ok(match self.expanders.entry((path.to_path_buf(), time)) {
-            Entry::Vacant(v) => v.insert(dylib::Expander::new(path).map_err(|err| {
-                format!("Cannot create expander for {}: {:?}", path.display(), err)
-            })?),
-            Entry::Occupied(e) => e.into_mut(),
-        })
-    }
+fn create_expander(lib: &Path) -> dylib::Expander {
+    dylib::Expander::new(lib)
+        .unwrap_or_else(|err| panic!("Cannot create expander for {}: {:?}", lib.display(), err))
 }
 
 pub mod cli;
diff --git a/crates/ra_prof/src/hprof.rs b/crates/ra_prof/src/hprof.rs
index a3f5321fb3..2b8a903636 100644
--- a/crates/ra_prof/src/hprof.rs
+++ b/crates/ra_prof/src/hprof.rs
@@ -30,9 +30,8 @@ pub fn init_from(spec: &str) {
 pub type Label = &'static str;
 
 /// This function starts a profiling scope in the current execution stack with a given description.
-/// It returns a `Profile` struct that measures elapsed time between this method invocation and `Profile` struct drop.
-/// It supports nested profiling scopes in case when this function is invoked multiple times at the execution stack.
-/// In this case the profiling information will be nested at the output.
+/// It returns a Profile structure and measure elapsed time between this method invocation and Profile structure drop.
+/// It supports nested profiling scopes in case when this function invoked multiple times at the execution stack. In this case the profiling information will be nested at the output.
 /// Profiling information is being printed in the stderr.
 ///
 /// # Example
@@ -59,35 +58,36 @@ pub type Label = &'static str;
 /// ```
 pub fn profile(label: Label) -> Profiler {
     assert!(!label.is_empty());
-
-    if PROFILING_ENABLED.load(Ordering::Relaxed)
-        && PROFILE_STACK.with(|stack| stack.borrow_mut().push(label))
-    {
-        Profiler(Some(ProfilerImpl { label, detail: None }))
-    } else {
-        Profiler(None)
-    }
+    let enabled = PROFILING_ENABLED.load(Ordering::Relaxed)
+        && PROFILE_STACK.with(|stack| stack.borrow_mut().push(label));
+    let label = if enabled { Some(label) } else { None };
+    Profiler { label, detail: None }
 }
 
-pub struct Profiler(Option<ProfilerImpl>);
-
-struct ProfilerImpl {
-    label: Label,
+pub struct Profiler {
+    label: Option<Label>,
     detail: Option<String>,
 }
 
 impl Profiler {
     pub fn detail(mut self, detail: impl FnOnce() -> String) -> Profiler {
-        if let Some(profiler) = &mut self.0 {
-            profiler.detail = Some(detail())
+        if self.label.is_some() {
+            self.detail = Some(detail())
         }
         self
     }
 }
 
-impl Drop for ProfilerImpl {
+impl Drop for Profiler {
     fn drop(&mut self) {
-        PROFILE_STACK.with(|it| it.borrow_mut().pop(self.label, self.detail.take()));
+        match self {
+            Profiler { label: Some(label), detail } => {
+                PROFILE_STACK.with(|stack| {
+                    stack.borrow_mut().pop(label, detail.take());
+                });
+            }
+            Profiler { label: None, .. } => (),
+        }
     }
 }
 
@@ -179,18 +179,21 @@ impl ProfileStack {
     pub fn pop(&mut self, label: Label, detail: Option<String>) {
         let start = self.starts.pop().unwrap();
         let duration = start.elapsed();
+        let level = self.starts.len();
         self.messages.finish(Message { duration, label, detail });
-        if self.starts.is_empty() {
+        if level == 0 {
             let longer_than = self.filter.longer_than;
             // Convert to millis for comparison to avoid problems with rounding
             // (otherwise we could print `0ms` despite user's `>0` filter when
             // `duration` is just a few nanos).
             if duration.as_millis() > longer_than.as_millis() {
+                let stderr = stderr();
                 if let Some(root) = self.messages.root() {
-                    print(&self.messages, root, 0, longer_than, &mut stderr().lock());
+                    print(&self.messages, root, 0, longer_than, &mut stderr.lock());
                 }
             }
             self.messages.clear();
+            assert!(self.starts.is_empty())
         }
     }
 }
diff --git a/crates/ra_syntax/src/ast.rs b/crates/ra_syntax/src/ast.rs
index a716e525b9..7fca5661ef 100644
--- a/crates/ra_syntax/src/ast.rs
+++ b/crates/ra_syntax/src/ast.rs
@@ -242,21 +242,6 @@ fn test_comments_preserve_trailing_whitespace() {
     );
 }
 
-#[test]
-fn test_four_slash_line_comment() {
-    let file = SourceFile::parse(
-        r#"
-        //// too many slashes to be a doc comment
-        /// doc comment
-        mod foo {}
-        "#,
-    )
-    .ok()
-    .unwrap();
-    let module = file.syntax().descendants().find_map(Module::cast).unwrap();
-    assert_eq!("doc comment", module.doc_comment_text().unwrap());
-}
-
 #[test]
 fn test_where_predicates() {
     fn assert_bound(text: &str, bound: Option<TypeBound>) {
diff --git a/crates/ra_syntax/src/ast/generated/nodes.rs b/crates/ra_syntax/src/ast/generated/nodes.rs
index 3b5e05af92..2cb3ad0114 100644
--- a/crates/ra_syntax/src/ast/generated/nodes.rs
+++ b/crates/ra_syntax/src/ast/generated/nodes.rs
@@ -1249,7 +1249,6 @@ pub struct PathSegment {
 }
 impl PathSegment {
     pub fn coloncolon_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![::]) }
-    pub fn crate_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![crate]) }
     pub fn l_angle_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![<]) }
     pub fn name_ref(&self) -> Option<NameRef> { support::child(&self.syntax) }
     pub fn type_arg_list(&self) -> Option<TypeArgList> { support::child(&self.syntax) }
diff --git a/crates/ra_syntax/src/ast/make.rs b/crates/ra_syntax/src/ast/make.rs
index 4920883532..ee0f5cc406 100644
--- a/crates/ra_syntax/src/ast/make.rs
+++ b/crates/ra_syntax/src/ast/make.rs
@@ -22,7 +22,8 @@ pub fn path_unqualified(segment: ast::PathSegment) -> ast::Path {
 pub fn path_qualified(qual: ast::Path, segment: ast::PathSegment) -> ast::Path {
     path_from_text(&format!("{}::{}", qual, segment))
 }
-fn path_from_text(text: &str) -> ast::Path {
+
+pub fn path_from_text(text: &str) -> ast::Path {
     ast_from_text(text)
 }
 
diff --git a/crates/ra_syntax/src/ast/tokens.rs b/crates/ra_syntax/src/ast/tokens.rs
index 74906d8a62..3865729b8c 100644
--- a/crates/ra_syntax/src/ast/tokens.rs
+++ b/crates/ra_syntax/src/ast/tokens.rs
@@ -13,12 +13,7 @@ impl Comment {
     }
 
     pub fn prefix(&self) -> &'static str {
-        for (prefix, k) in COMMENT_PREFIX_TO_KIND.iter() {
-            if *k == self.kind() && self.text().starts_with(prefix) {
-                return prefix;
-            }
-        }
-        unreachable!()
+        prefix_by_kind(self.kind())
     }
 }
 
@@ -53,7 +48,6 @@ pub enum CommentPlacement {
 const COMMENT_PREFIX_TO_KIND: &[(&str, CommentKind)] = {
     use {CommentPlacement::*, CommentShape::*};
     &[
-        ("////", CommentKind { shape: Line, doc: None }),
         ("///", CommentKind { shape: Line, doc: Some(Outer) }),
         ("//!", CommentKind { shape: Line, doc: Some(Inner) }),
         ("/**", CommentKind { shape: Block, doc: Some(Outer) }),
@@ -75,6 +69,15 @@ fn kind_by_prefix(text: &str) -> CommentKind {
     panic!("bad comment text: {:?}", text)
 }
 
+fn prefix_by_kind(kind: CommentKind) -> &'static str {
+    for (prefix, k) in COMMENT_PREFIX_TO_KIND.iter() {
+        if *k == kind {
+            return prefix;
+        }
+    }
+    unreachable!()
+}
+
 impl Whitespace {
     pub fn spans_multiple_lines(&self) -> bool {
         let text = self.text();
diff --git a/crates/ra_syntax/src/validation.rs b/crates/ra_syntax/src/validation.rs
index a30bc97bb0..5e93895ec5 100644
--- a/crates/ra_syntax/src/validation.rs
+++ b/crates/ra_syntax/src/validation.rs
@@ -96,7 +96,6 @@ pub(crate) fn validate(root: &SyntaxNode) -> Vec<SyntaxError> {
                 ast::RecordField(it) => validate_numeric_name(it.name_ref(), &mut errors),
                 ast::Visibility(it) => validate_visibility(it, &mut errors),
                 ast::RangeExpr(it) => validate_range_expr(it, &mut errors),
-                ast::PathSegment(it) => validate_crate_keyword_in_path_segment(it, &mut errors),
                 _ => (),
             }
         }
@@ -223,41 +222,3 @@ fn validate_range_expr(expr: ast::RangeExpr, errors: &mut Vec<SyntaxError>) {
         ));
     }
 }
-
-fn validate_crate_keyword_in_path_segment(
-    segment: ast::PathSegment,
-    errors: &mut Vec<SyntaxError>,
-) {
-    const ERR_MSG: &str = "The `crate` keyword is only allowed as the first segment of a path";
-
-    let crate_token = match segment.crate_token() {
-        None => return,
-        Some(it) => it,
-    };
-
-    // Disallow both ::crate and foo::crate
-    let path = segment.parent_path();
-    if segment.coloncolon_token().is_some() || path.qualifier().is_some() {
-        errors.push(SyntaxError::new(ERR_MSG, crate_token.text_range()));
-        return;
-    }
-
-    // We now know that the path variable describes a complete path.
-    // For expressions and types, validation is complete, but we still have
-    // to handle UseItems like this:
-    //      use foo:{crate};
-    // so we crawl upwards looking for any preceding paths on `UseTree`s
-    for node in path.syntax().ancestors().skip(1) {
-        match_ast! {
-            match node {
-                ast::UseTree(it) => if let Some(tree_path) = it.path() {
-                    if tree_path != path {
-                        errors.push(SyntaxError::new(ERR_MSG, crate_token.text_range()));
-                    }
-                },
-                ast::UseTreeList(_it) => continue,
-                _ => return,
-            }
-        };
-    }
-}
diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml
index 514d6d1a94..cee0248b62 100644
--- a/crates/rust-analyzer/Cargo.toml
+++ b/crates/rust-analyzer/Cargo.toml
@@ -20,7 +20,7 @@ globset = "0.4.4"
 itertools = "0.9.0"
 jod-thread = "0.1.0"
 log = "0.4.8"
-lsp-types = { version = "0.74.0", features = ["proposed"] }
+lsp-types = { version = "0.73.0", features = ["proposed"] }
 parking_lot = "0.10.0"
 pico-args = "0.3.1"
 rand = { version = "0.7.3", features = ["small_rng"] }
diff --git a/crates/rust-analyzer/src/conv.rs b/crates/rust-analyzer/src/conv.rs
index 7be5ebcdb5..b0f911f713 100644
--- a/crates/rust-analyzer/src/conv.rs
+++ b/crates/rust-analyzer/src/conv.rs
@@ -25,8 +25,7 @@ use crate::{
     Result,
 };
 use semantic_tokens::{
-    ATTRIBUTE, BUILTIN_TYPE, ENUM_MEMBER, FORMAT_SPECIFIER, LIFETIME, TYPE_ALIAS, UNION,
-    UNRESOLVED_REFERENCE,
+    ATTRIBUTE, BUILTIN_TYPE, ENUM_MEMBER, LIFETIME, TYPE_ALIAS, UNION, UNRESOLVED_REFERENCE,
 };
 
 pub trait Conv {
@@ -150,7 +149,7 @@ impl ConvWith<(&LineIndex, LineEndings)> for CompletionItem {
             detail: self.detail().map(|it| it.to_string()),
             filter_text: Some(self.lookup().to_string()),
             kind: self.kind().map(|it| it.conv()),
-            text_edit: Some(text_edit.into()),
+            text_edit: Some(text_edit),
             additional_text_edits: Some(additional_text_edits),
             documentation: self.documentation().map(|it| it.conv()),
             deprecated: Some(self.deprecated()),
@@ -382,7 +381,6 @@ impl Conv for Highlight {
             HighlightTag::Attribute => ATTRIBUTE,
             HighlightTag::Keyword => SemanticTokenType::KEYWORD,
             HighlightTag::UnresolvedReference => UNRESOLVED_REFERENCE,
-            HighlightTag::FormatSpecifier => FORMAT_SPECIFIER,
         };
 
         for modifier in self.modifiers.iter() {
diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs
index 8db2dfa0c8..6caaf5f88d 100644
--- a/crates/rust-analyzer/src/main_loop/handlers.rs
+++ b/crates/rust-analyzer/src/main_loop/handlers.rs
@@ -326,10 +326,10 @@ pub fn handle_workspace_symbol(
 
 pub fn handle_goto_definition(
     world: WorldSnapshot,
-    params: req::GotoDefinitionParams,
+    params: req::TextDocumentPositionParams,
 ) -> Result<Option<req::GotoDefinitionResponse>> {
     let _p = profile("handle_goto_definition");
-    let position = params.text_document_position_params.try_conv_with(&world)?;
+    let position = params.try_conv_with(&world)?;
     let nav_info = match world.analysis().goto_definition(position)? {
         None => return Ok(None),
         Some(it) => it,
@@ -340,10 +340,10 @@ pub fn handle_goto_definition(
 
 pub fn handle_goto_implementation(
     world: WorldSnapshot,
-    params: req::GotoImplementationParams,
+    params: req::TextDocumentPositionParams,
 ) -> Result<Option<req::GotoImplementationResponse>> {
     let _p = profile("handle_goto_implementation");
-    let position = params.text_document_position_params.try_conv_with(&world)?;
+    let position = params.try_conv_with(&world)?;
     let nav_info = match world.analysis().goto_implementation(position)? {
         None => return Ok(None),
         Some(it) => it,
@@ -354,10 +354,10 @@ pub fn handle_goto_implementation(
 
 pub fn handle_goto_type_definition(
     world: WorldSnapshot,
-    params: req::GotoTypeDefinitionParams,
+    params: req::TextDocumentPositionParams,
 ) -> Result<Option<req::GotoTypeDefinitionResponse>> {
     let _p = profile("handle_goto_type_definition");
-    let position = params.text_document_position_params.try_conv_with(&world)?;
+    let position = params.try_conv_with(&world)?;
     let nav_info = match world.analysis().goto_type_definition(position)? {
         None => return Ok(None),
         Some(it) => it,
@@ -487,10 +487,10 @@ pub fn handle_folding_range(
 
 pub fn handle_signature_help(
     world: WorldSnapshot,
-    params: req::SignatureHelpParams,
+    params: req::TextDocumentPositionParams,
 ) -> Result<Option<req::SignatureHelp>> {
     let _p = profile("handle_signature_help");
-    let position = params.text_document_position_params.try_conv_with(&world)?;
+    let position = params.try_conv_with(&world)?;
     if let Some(call_info) = world.analysis().call_info(position)? {
         let concise = !world.config.call_info_full;
         let mut active_parameter = call_info.active_parameter.map(|it| it as i64);
@@ -509,9 +509,12 @@ pub fn handle_signature_help(
     }
 }
 
-pub fn handle_hover(world: WorldSnapshot, params: req::HoverParams) -> Result<Option<Hover>> {
+pub fn handle_hover(
+    world: WorldSnapshot,
+    params: req::TextDocumentPositionParams,
+) -> Result<Option<Hover>> {
     let _p = profile("handle_hover");
-    let position = params.text_document_position_params.try_conv_with(&world)?;
+    let position = params.try_conv_with(&world)?;
     let info = match world.analysis().hover(position)? {
         None => return Ok(None),
         Some(info) => info,
@@ -875,14 +878,8 @@ pub fn handle_code_lens(
             .map(|it| {
                 let range = it.node_range.conv_with(&line_index);
                 let pos = range.start;
-                let lens_params = req::GotoImplementationParams {
-                    text_document_position_params: req::TextDocumentPositionParams::new(
-                        params.text_document.clone(),
-                        pos,
-                    ),
-                    work_done_progress_params: Default::default(),
-                    partial_result_params: Default::default(),
-                };
+                let lens_params =
+                    req::TextDocumentPositionParams::new(params.text_document.clone(), pos);
                 CodeLens {
                     range,
                     command: None,
@@ -897,7 +894,7 @@ pub fn handle_code_lens(
 #[derive(Debug, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 enum CodeLensResolveData {
-    Impls(req::GotoImplementationParams),
+    Impls(req::TextDocumentPositionParams),
 }
 
 pub fn handle_code_lens_resolve(world: WorldSnapshot, code_lens: CodeLens) -> Result<CodeLens> {
@@ -930,7 +927,7 @@ pub fn handle_code_lens_resolve(world: WorldSnapshot, code_lens: CodeLens) -> Re
                 title,
                 command: "rust-analyzer.showReferences".into(),
                 arguments: Some(vec![
-                    to_value(&lens_params.text_document_position_params.text_document.uri).unwrap(),
+                    to_value(&lens_params.text_document.uri).unwrap(),
                     to_value(code_lens.range.start).unwrap(),
                     to_value(locations).unwrap(),
                 ]),
@@ -947,16 +944,16 @@ pub fn handle_code_lens_resolve(world: WorldSnapshot, code_lens: CodeLens) -> Re
 
 pub fn handle_document_highlight(
     world: WorldSnapshot,
-    params: req::DocumentHighlightParams,
+    params: req::TextDocumentPositionParams,
 ) -> Result<Option<Vec<DocumentHighlight>>> {
     let _p = profile("handle_document_highlight");
-    let file_id = params.text_document_position_params.text_document.try_conv_with(&world)?;
+    let file_id = params.text_document.try_conv_with(&world)?;
     let line_index = world.analysis().file_line_index(file_id)?;
 
-    let refs = match world.analysis().find_all_refs(
-        params.text_document_position_params.try_conv_with(&world)?,
-        Some(SearchScope::single_file(file_id)),
-    )? {
+    let refs = match world
+        .analysis()
+        .find_all_refs(params.try_conv_with(&world)?, Some(SearchScope::single_file(file_id)))?
+    {
         None => return Ok(None),
         Some(refs) => refs,
     };
diff --git a/crates/rust-analyzer/src/req.rs b/crates/rust-analyzer/src/req.rs
index 0dae6bad4f..ae34488929 100644
--- a/crates/rust-analyzer/src/req.rs
+++ b/crates/rust-analyzer/src/req.rs
@@ -8,15 +8,14 @@ pub use lsp_types::{
     notification::*, request::*, ApplyWorkspaceEditParams, CodeActionParams, CodeLens,
     CodeLensParams, CompletionParams, CompletionResponse, ConfigurationItem, ConfigurationParams,
     DiagnosticTag, DidChangeConfigurationParams, DidChangeWatchedFilesParams,
-    DidChangeWatchedFilesRegistrationOptions, DocumentHighlightParams,
-    DocumentOnTypeFormattingParams, DocumentSymbolParams, DocumentSymbolResponse,
-    FileSystemWatcher, GotoDefinitionParams, GotoDefinitionResponse, Hover, HoverParams,
-    InitializeResult, MessageType, PartialResultParams, ProgressParams, ProgressParamsValue,
-    ProgressToken, PublishDiagnosticsParams, ReferenceParams, Registration, RegistrationParams,
-    SelectionRange, SelectionRangeParams, SemanticTokensParams, SemanticTokensRangeParams,
+    DidChangeWatchedFilesRegistrationOptions, DocumentOnTypeFormattingParams, DocumentSymbolParams,
+    DocumentSymbolResponse, FileSystemWatcher, Hover, InitializeResult, MessageType,
+    PartialResultParams, ProgressParams, ProgressParamsValue, ProgressToken,
+    PublishDiagnosticsParams, ReferenceParams, Registration, RegistrationParams, SelectionRange,
+    SelectionRangeParams, SemanticTokensParams, SemanticTokensRangeParams,
     SemanticTokensRangeResult, SemanticTokensResult, ServerCapabilities, ShowMessageParams,
-    SignatureHelp, SignatureHelpParams, SymbolKind, TextDocumentEdit, TextDocumentPositionParams,
-    TextEdit, WorkDoneProgressParams, WorkspaceEdit, WorkspaceSymbolParams,
+    SignatureHelp, SymbolKind, TextDocumentEdit, TextDocumentPositionParams, TextEdit,
+    WorkDoneProgressParams, WorkspaceEdit, WorkspaceSymbolParams,
 };
 use std::path::PathBuf;
 
diff --git a/crates/rust-analyzer/src/semantic_tokens.rs b/crates/rust-analyzer/src/semantic_tokens.rs
index 2dc5cb1196..10fe696f6f 100644
--- a/crates/rust-analyzer/src/semantic_tokens.rs
+++ b/crates/rust-analyzer/src/semantic_tokens.rs
@@ -4,69 +4,62 @@ use std::ops;
 
 use lsp_types::{Range, SemanticToken, SemanticTokenModifier, SemanticTokenType, SemanticTokens};
 
-macro_rules! define_semantic_token_types {
-    ($(($ident:ident, $string:literal)),*$(,)?) => {
-        $(pub(crate) const $ident: SemanticTokenType = SemanticTokenType::new($string);)*
-
-        pub(crate) const SUPPORTED_TYPES: &[SemanticTokenType] = &[
-            SemanticTokenType::COMMENT,
-            SemanticTokenType::KEYWORD,
-            SemanticTokenType::STRING,
-            SemanticTokenType::NUMBER,
-            SemanticTokenType::REGEXP,
-            SemanticTokenType::OPERATOR,
-            SemanticTokenType::NAMESPACE,
-            SemanticTokenType::TYPE,
-            SemanticTokenType::STRUCT,
-            SemanticTokenType::CLASS,
-            SemanticTokenType::INTERFACE,
-            SemanticTokenType::ENUM,
-            SemanticTokenType::TYPE_PARAMETER,
-            SemanticTokenType::FUNCTION,
-            SemanticTokenType::MEMBER,
-            SemanticTokenType::PROPERTY,
-            SemanticTokenType::MACRO,
-            SemanticTokenType::VARIABLE,
-            SemanticTokenType::PARAMETER,
-            SemanticTokenType::LABEL,
-            $($ident),*
-        ];
-    };
-}
-
-define_semantic_token_types![
-    (ATTRIBUTE, "attribute"),
-    (BUILTIN_TYPE, "builtinType"),
-    (ENUM_MEMBER, "enumMember"),
-    (LIFETIME, "lifetime"),
-    (TYPE_ALIAS, "typeAlias"),
-    (UNION, "union"),
-    (UNRESOLVED_REFERENCE, "unresolvedReference"),
-    (FORMAT_SPECIFIER, "formatSpecifier"),
+pub(crate) const ATTRIBUTE: SemanticTokenType = SemanticTokenType::new("attribute");
+pub(crate) const BUILTIN_TYPE: SemanticTokenType = SemanticTokenType::new("builtinType");
+pub(crate) const ENUM_MEMBER: SemanticTokenType = SemanticTokenType::new("enumMember");
+pub(crate) const LIFETIME: SemanticTokenType = SemanticTokenType::new("lifetime");
+pub(crate) const TYPE_ALIAS: SemanticTokenType = SemanticTokenType::new("typeAlias");
+pub(crate) const UNION: SemanticTokenType = SemanticTokenType::new("union");
+pub(crate) const UNRESOLVED_REFERENCE: SemanticTokenType =
+    SemanticTokenType::new("unresolvedReference");
+
+pub(crate) const CONSTANT: SemanticTokenModifier = SemanticTokenModifier::new("constant");
+pub(crate) const CONTROL_FLOW: SemanticTokenModifier = SemanticTokenModifier::new("controlFlow");
+pub(crate) const MUTABLE: SemanticTokenModifier = SemanticTokenModifier::new("mutable");
+pub(crate) const UNSAFE: SemanticTokenModifier = SemanticTokenModifier::new("unsafe");
+
+pub(crate) const SUPPORTED_TYPES: &[SemanticTokenType] = &[
+    SemanticTokenType::COMMENT,
+    SemanticTokenType::KEYWORD,
+    SemanticTokenType::STRING,
+    SemanticTokenType::NUMBER,
+    SemanticTokenType::REGEXP,
+    SemanticTokenType::OPERATOR,
+    SemanticTokenType::NAMESPACE,
+    SemanticTokenType::TYPE,
+    SemanticTokenType::STRUCT,
+    SemanticTokenType::CLASS,
+    SemanticTokenType::INTERFACE,
+    SemanticTokenType::ENUM,
+    SemanticTokenType::TYPE_PARAMETER,
+    SemanticTokenType::FUNCTION,
+    SemanticTokenType::MEMBER,
+    SemanticTokenType::PROPERTY,
+    SemanticTokenType::MACRO,
+    SemanticTokenType::VARIABLE,
+    SemanticTokenType::PARAMETER,
+    SemanticTokenType::LABEL,
+    ATTRIBUTE,
+    BUILTIN_TYPE,
+    ENUM_MEMBER,
+    LIFETIME,
+    TYPE_ALIAS,
+    UNION,
+    UNRESOLVED_REFERENCE,
 ];
 
-macro_rules! define_semantic_token_modifiers {
-    ($(($ident:ident, $string:literal)),*$(,)?) => {
-        $(pub(crate) const $ident: SemanticTokenModifier = SemanticTokenModifier::new($string);)*
-
-        pub(crate) const SUPPORTED_MODIFIERS: &[SemanticTokenModifier] = &[
-            SemanticTokenModifier::DOCUMENTATION,
-            SemanticTokenModifier::DECLARATION,
-            SemanticTokenModifier::DEFINITION,
-            SemanticTokenModifier::STATIC,
-            SemanticTokenModifier::ABSTRACT,
-            SemanticTokenModifier::DEPRECATED,
-            SemanticTokenModifier::READONLY,
-            $($ident),*
-        ];
-    };
-}
-
-define_semantic_token_modifiers![
-    (CONSTANT, "constant"),
-    (CONTROL_FLOW, "controlFlow"),
-    (MUTABLE, "mutable"),
-    (UNSAFE, "unsafe"),
+pub(crate) const SUPPORTED_MODIFIERS: &[SemanticTokenModifier] = &[
+    SemanticTokenModifier::DOCUMENTATION,
+    SemanticTokenModifier::DECLARATION,
+    SemanticTokenModifier::DEFINITION,
+    SemanticTokenModifier::STATIC,
+    SemanticTokenModifier::ABSTRACT,
+    SemanticTokenModifier::DEPRECATED,
+    SemanticTokenModifier::READONLY,
+    CONSTANT,
+    MUTABLE,
+    UNSAFE,
+    CONTROL_FLOW,
 ];
 
 #[derive(Default)]
diff --git a/docs/dev/architecture.md b/docs/dev/architecture.md
index 3a337c5746..0343b6c81f 100644
--- a/docs/dev/architecture.md
+++ b/docs/dev/architecture.md
@@ -56,7 +56,7 @@ In particular, `cargo xtask codegen` generates:
 2. [`ast/generated`](https://github.com/rust-analyzer/rust-analyzer/blob/a0be39296d2925972cacd9fbf8b5fb258fad6947/crates/ra_syntax/src/ast/generated.rs)
   -- AST data structure.
 
-3. [`doc_tests/generated`](https://github.com/rust-analyzer/rust-analyzer/blob/a0be39296d2925972cacd9fbf8b5fb258fad6947/crates/ra_assists/src/doc_tests/generated.rs),
+.3 [`doc_tests/generated`](https://github.com/rust-analyzer/rust-analyzer/blob/a0be39296d2925972cacd9fbf8b5fb258fad6947/crates/ra_assists/src/doc_tests/generated.rs),
   [`test_data/parser/inline`](https://github.com/rust-analyzer/rust-analyzer/tree/a0be39296d2925972cacd9fbf8b5fb258fad6947/crates/ra_syntax/test_data/parser/inline)
   -- tests for assists and the parser.
 
diff --git a/docs/user/features.md b/docs/user/features.md
index b9a365fc19..56d2969fd4 100644
--- a/docs/user/features.md
+++ b/docs/user/features.md
@@ -140,8 +140,8 @@ space or `;` depending on the return type of the function.
 When completing a function call, `()` are automatically inserted. If a function
 takes arguments, the cursor is positioned inside the parenthesis.
 
-There are postfix completions, which can be triggered by typing something like
-`foo().if`. The word after `.` determines postfix completion. Possible variants are:
+There are postifx completions, which can be triggerd by typing something like
+`foo().if`. The word after `.` determines postifx completion. Possible variants are:
 
 - `expr.if` -> `if expr {}`
 - `expr.match` -> `match expr {}`
diff --git a/docs/user/readme.adoc b/docs/user/readme.adoc
index 54342026bd..76d065d35b 100644
--- a/docs/user/readme.adoc
+++ b/docs/user/readme.adoc
@@ -169,15 +169,13 @@ The are several LSP client implementations for vim:
 
 1. Install coc.nvim by following the instructions at
    https://github.com/neoclide/coc.nvim[coc.nvim]
-   (Node.js required)
+   (nodejs required)
 2. Run `:CocInstall coc-rust-analyzer` to install
    https://github.com/fannheyward/coc-rust-analyzer[coc-rust-analyzer],
    this extension implements _most_ of the features supported in the VSCode extension:
-   * automatically install and upgrade stable/nightly releases
    * same configurations as VSCode extension, `rust-analyzer.serverPath`, `rust-analyzer.cargo.features` etc.
    * same commands too, `rust-analyzer.analyzerStatus`, `rust-analyzer.ssr` etc.
-   * inlay hints for method chaining support, _Neovim Only_
-   * semantic highlighting is not implemented yet
+   * highlighting and inlay_hints are not implemented yet
 
 ==== LanguageClient-neovim
 
@@ -197,7 +195,7 @@ let g:LanguageClient_serverCommands = {
 ==== YouCompleteMe
 
 1. Install YouCompleteMe by following the instructions
-  https://github.com/ycm-core/lsp-examples#rust-rust-analyzer[here]
+  https://ycm-core.github.io/YouCompleteMe/#rust-semantic-completion[here]
 
 2. Configure by adding this to your vim/neovim config file (replacing the existing Rust-specific line if it exists):
 +
@@ -214,21 +212,6 @@ let g:ycm_language_server =
 \ ]
 ----
 
-==== ALE
-
-To add the LSP server to https://github.com/dense-analysis/ale[ale]:
-
-[source,vim]
-----
-call ale#linter#Define('rust', {
-\   'name': 'rust-analyzer',
-\   'lsp': 'stdio',
-\   'executable': 'rust-analyzer',
-\   'command': '%e',
-\   'project_root': '.',
-\})
-----
-
 ==== nvim-lsp
 
 NeoVim 0.5 (not yet released) has built-in language server support.
diff --git a/editors/code/package.json b/editors/code/package.json
index c4dfa7e13d..70a5c66198 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -27,7 +27,6 @@
     "scripts": {
         "vscode:prepublish": "tsc && rollup -c",
         "package": "vsce package -o rust-analyzer.vsix",
-        "build": "tsc",
         "watch": "tsc --watch",
         "lint": "tsfmt --verify && eslint -c .eslintrc.js --ext ts ./src",
         "fix": " tsfmt -r       && eslint -c .eslintrc.js --ext ts ./src --fix"
@@ -389,6 +388,28 @@
                     "description": "Enable Proc macro support, cargo.loadOutDirsFromCheck must be enabled.",
                     "type": "boolean",
                     "default": false
+                },
+                "rust-analyzer.debug.engine": {
+                    "type": "string",
+                    "enum": [
+                        "auto",
+                        "vadimcn.vscode-lldb",
+                        "ms-vscode.cpptools"
+                    ],
+                    "default": "auto",
+                    "description": "Preffered debug engine.",
+                    "markdownEnumDescriptions": [
+                        "First try to use [CodeLLDB](https://marketplace.visualstudio.com/items?itemName=vadimcn.vscode-lldb), if it's not installed try to use [MS C++ tools](https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools).",
+                        "Use [CodeLLDB](https://marketplace.visualstudio.com/items?itemName=vadimcn.vscode-lldb)",
+                        "Use [MS C++ tools](https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools)"
+                    ]
+                },
+                "rust-analyzer.debug.sourceFileMap": {
+                    "type": "object",
+                    "description": "Optional source file mappings passed to the debug engine.",
+                    "default": {
+                        "/rustc/<id>": "${env:USERPROFILE}/.rustup/toolchains/<toolchain-id>/lib/rustlib/src/rust"
+                    }
                 }
             }
         },
diff --git a/editors/code/src/cargo.ts b/editors/code/src/cargo.ts
new file mode 100644
index 0000000000..a328ba9bd0
--- /dev/null
+++ b/editors/code/src/cargo.ts
@@ -0,0 +1,106 @@
+import * as cp from 'child_process';
+import * as readline from 'readline';
+import { OutputChannel } from 'vscode';
+
+interface CompilationArtifact {
+    fileName: string;
+    name: string;
+    kind: string;
+    isTest: boolean;
+}
+
+export class Cargo {
+    rootFolder: string;
+    env?: Record<string, string>;
+    output: OutputChannel;
+
+    public constructor(cargoTomlFolder: string, output: OutputChannel, env: Record<string, string> | undefined = undefined) {
+        this.rootFolder = cargoTomlFolder;
+        this.output = output;
+        this.env = env;
+    }
+
+    public async artifactsFromArgs(cargoArgs: string[]): Promise<CompilationArtifact[]> {
+        const artifacts: CompilationArtifact[] = [];
+
+        try {
+            await this.runCargo(cargoArgs,
+                message => {
+                    if (message.reason === 'compiler-artifact' && message.executable) {
+                        const isBinary = message.target.crate_types.includes('bin');
+                        const isBuildScript = message.target.kind.includes('custom-build');
+                        if ((isBinary && !isBuildScript) || message.profile.test) {
+                            artifacts.push({
+                                fileName: message.executable,
+                                name: message.target.name,
+                                kind: message.target.kind[0],
+                                isTest: message.profile.test
+                            });
+                        }
+                    }
+                    else if (message.reason === 'compiler-message') {
+                        this.output.append(message.message.rendered);
+                    }
+                },
+                stderr => {
+                    this.output.append(stderr);
+                }
+            );
+        }
+        catch (err) {
+            this.output.show(true);
+            throw new Error(`Cargo invocation has failed: ${err}`);
+        }
+
+        return artifacts;
+    }
+
+    public async executableFromArgs(args: string[]): Promise<string> {
+        const cargoArgs = [...args]; // to remain  args unchanged
+        cargoArgs.push("--message-format=json");
+
+        const artifacts = await this.artifactsFromArgs(cargoArgs);
+
+        if (artifacts.length === 0) {
+            throw new Error('No compilation artifacts');
+        } else if (artifacts.length > 1) {
+            throw new Error('Multiple compilation artifacts are not supported.');
+        }
+
+        return artifacts[0].fileName;
+    }
+
+    runCargo(
+        cargoArgs: string[],
+        onStdoutJson: (obj: any) => void,
+        onStderrString: (data: string) => void
+    ): Promise<number> {
+        return new Promise<number>((resolve, reject) => {
+            const cargo = cp.spawn('cargo', cargoArgs, {
+                stdio: ['ignore', 'pipe', 'pipe'],
+                cwd: this.rootFolder,
+                env: this.env,
+            });
+
+            cargo.on('error', err => {
+                reject(new Error(`could not launch cargo: ${err}`));
+            });
+            cargo.stderr.on('data', chunk => {
+                onStderrString(chunk.toString());
+            });
+
+            const rl = readline.createInterface({ input: cargo.stdout });
+            rl.on('line', line => {
+                const message = JSON.parse(line);
+                onStdoutJson(message);
+            });
+
+            cargo.on('exit', (exitCode, _) => {
+                if (exitCode === 0)
+                    resolve(exitCode);
+                else
+                    reject(new Error(`exit code: ${exitCode}.`));
+            });
+        });
+    }
+}
\ No newline at end of file
diff --git a/editors/code/src/commands/runnables.ts b/editors/code/src/commands/runnables.ts
index 2635a14408..d77e8188c7 100644
--- a/editors/code/src/commands/runnables.ts
+++ b/editors/code/src/commands/runnables.ts
@@ -1,8 +1,10 @@
 import * as vscode from 'vscode';
 import * as lc from 'vscode-languageclient';
 import * as ra from '../rust-analyzer-api';
+import * as os from "os";
 
 import { Ctx, Cmd } from '../ctx';
+import { Cargo } from '../cargo';
 
 export function run(ctx: Ctx): Cmd {
     let prevRunnable: RunnableQuickPick | undefined;
@@ -62,25 +64,69 @@ export function runSingle(ctx: Ctx): Cmd {
     };
 }
 
+function getLldbDebugConfig(config: ra.Runnable, sourceFileMap: Record<string, string>): vscode.DebugConfiguration {
+    return {
+        type: "lldb",
+        request: "launch",
+        name: config.label,
+        cargo: {
+            args: config.args,
+        },
+        args: config.extraArgs,
+        cwd: config.cwd,
+        sourceMap: sourceFileMap
+    };
+}
+
+const debugOutput = vscode.window.createOutputChannel("Debug");
+
+async function getCppvsDebugConfig(config: ra.Runnable, sourceFileMap: Record<string, string>): Promise<vscode.DebugConfiguration> {
+    debugOutput.clear();
+
+    const cargo = new Cargo(config.cwd || '.', debugOutput);
+    const executable = await cargo.executableFromArgs(config.args);
+
+    // if we are here, there were no compilation errors.
+    return {
+        type: (os.platform() === "win32") ? "cppvsdbg" : 'cppdbg',
+        request: "launch",
+        name: config.label,
+        program: executable,
+        args: config.extraArgs,
+        cwd: config.cwd,
+        sourceFileMap: sourceFileMap,
+    };
+}
+
 export function debugSingle(ctx: Ctx): Cmd {
     return async (config: ra.Runnable) => {
         const editor = ctx.activeRustEditor;
         if (!editor) return;
-        if (!vscode.extensions.getExtension("vadimcn.vscode-lldb")) {
-            vscode.window.showErrorMessage("Install `vadimcn.vscode-lldb` extension for debugging");
+
+        const lldbId = "vadimcn.vscode-lldb";
+        const cpptoolsId = "ms-vscode.cpptools";
+
+        const debugEngineId = ctx.config.debug.engine;
+        let debugEngine = null;
+        if (debugEngineId === "auto") {
+            debugEngine = vscode.extensions.getExtension(lldbId);
+            if (!debugEngine) {
+                debugEngine = vscode.extensions.getExtension(cpptoolsId);
+            }
+        }
+        else {
+            debugEngine = vscode.extensions.getExtension(debugEngineId);
+        }
+
+        if (!debugEngine) {
+            vscode.window.showErrorMessage(`Install [CodeLLDB](https://marketplace.visualstudio.com/items?itemName=${lldbId})`
+                + ` or [MS C++ tools](https://marketplace.visualstudio.com/items?itemName=${cpptoolsId}) extension for debugging.`);
             return;
         }
 
-        const debugConfig = {
-            type: "lldb",
-            request: "launch",
-            name: config.label,
-            cargo: {
-                args: config.args,
-            },
-            args: config.extraArgs,
-            cwd: config.cwd
-        };
+        const debugConfig = lldbId === debugEngine.id
+            ? getLldbDebugConfig(config, ctx.config.debug.sourceFileMap)
+            : await getCppvsDebugConfig(config, ctx.config.debug.sourceFileMap);
 
         return vscode.debug.startDebugging(undefined, debugConfig);
     };
diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts
index 3b2eec8baa..110e541800 100644
--- a/editors/code/src/config.ts
+++ b/editors/code/src/config.ts
@@ -92,7 +92,6 @@ export class Config {
     get askBeforeDownload() { return this.get<boolean>("updates.askBeforeDownload"); }
     get traceExtension() { return this.get<boolean>("trace.extension"); }
 
-
     get inlayHints() {
         return {
             typeHints: this.get<boolean>("inlayHints.typeHints"),
@@ -107,4 +106,12 @@ export class Config {
             command: this.get<string>("checkOnSave.command"),
         };
     }
+
+    get debug() {
+        return {
+            engine: this.get<string>("debug.engine"),
+            sourceFileMap: this.get<Record<string, string>>("debug.sourceFileMap"),
+        };
+    }
+
 }
diff --git a/xtask/src/ast_src.rs b/xtask/src/ast_src.rs
index 98c8644e45..9c02f7c6f0 100644
--- a/xtask/src/ast_src.rs
+++ b/xtask/src/ast_src.rs
@@ -595,7 +595,7 @@ pub(crate) const AST_SRC: AstSrc = AstSrc {
             qualifier: Path,
         }
         struct PathSegment {
-            T![::], T![crate], T![<], NameRef, TypeArgList, ParamList, RetType, PathType, T![>]
+            T![::], T![<], NameRef, TypeArgList, ParamList, RetType, PathType, T![>]
         }
         struct TypeArgList {
             T![::],
diff --git a/xtask/src/dist.rs b/xtask/src/dist.rs
index aef68089e1..a56eeef8d2 100644
--- a/xtask/src/dist.rs
+++ b/xtask/src/dist.rs
@@ -50,19 +50,21 @@ fn dist_server(nightly: bool) -> Result<()> {
     if cfg!(target_os = "linux") {
         std::env::set_var("CC", "clang");
         run!(
-            "cargo build --manifest-path ./crates/rust-analyzer/Cargo.toml --bin rust-analyzer --release"
+            "cargo build --manifest-path ./crates/rust-analyzer/Cargo.toml --bin rust-analyzer --release
+             --target x86_64-unknown-linux-musl
+            "
             // We'd want to add, but that requires setting the right linker somehow
             // --features=jemalloc
         )?;
         if !nightly {
-            run!("strip ./target/release/rust-analyzer")?;
+            run!("strip ./target/x86_64-unknown-linux-musl/release/rust-analyzer")?;
         }
     } else {
         run!("cargo build --manifest-path ./crates/rust-analyzer/Cargo.toml --bin rust-analyzer --release")?;
     }
 
     let (src, dst) = if cfg!(target_os = "linux") {
-        ("./target/release/rust-analyzer", "./dist/rust-analyzer-linux")
+        ("./target/x86_64-unknown-linux-musl/release/rust-analyzer", "./dist/rust-analyzer-linux")
     } else if cfg!(target_os = "windows") {
         ("./target/release/rust-analyzer.exe", "./dist/rust-analyzer-windows.exe")
     } else if cfg!(target_os = "macos") {
diff --git a/xtask/src/lib.rs b/xtask/src/lib.rs
index bf4d85dcb0..ec824a5183 100644
--- a/xtask/src/lib.rs
+++ b/xtask/src/lib.rs
@@ -10,6 +10,7 @@ pub mod pre_commit;
 pub mod codegen;
 mod ast_src;
 
+use anyhow::Context;
 use std::{
     env,
     io::Write,
@@ -23,9 +24,9 @@ use crate::{
     not_bash::{date_iso, fs2, pushd, rm_rf, run},
 };
 
-pub use anyhow::{bail, Context as _, Result};
+pub use anyhow::Result;
 
-const RUSTFMT_TOOLCHAIN: &str = "stable";
+const TOOLCHAIN: &str = "stable";
 
 pub fn project_root() -> PathBuf {
     Path::new(
@@ -56,25 +57,15 @@ pub fn run_rustfmt(mode: Mode) -> Result<()> {
     let _dir = pushd(project_root());
     ensure_rustfmt()?;
 
-    if Command::new("cargo")
-        .env("RUSTUP_TOOLCHAIN", RUSTFMT_TOOLCHAIN)
-        .args(&["fmt", "--"])
-        .args(if mode == Mode::Verify { &["--check"][..] } else { &[] })
-        .stderr(Stdio::inherit())
-        .status()?
-        .success()
-    {
-        Ok(())
-    } else {
-        bail!("Rustfmt failed");
-    }
+    let check = if mode == Mode::Verify { "--check" } else { "" };
+    run!("rustup run {} -- cargo fmt -- {}", TOOLCHAIN, check)?;
+    Ok(())
 }
 
 fn reformat(text: impl std::fmt::Display) -> Result<String> {
     ensure_rustfmt()?;
-    let mut rustfmt = Command::new("rustfmt")
-        .env("RUSTUP_TOOLCHAIN", RUSTFMT_TOOLCHAIN)
-        .args(&["--config-path"])
+    let mut rustfmt = Command::new("rustup")
+        .args(&["run", TOOLCHAIN, "--", "rustfmt", "--config-path"])
         .arg(project_root().join("rustfmt.toml"))
         .args(&["--config", "fn_single_line=true"])
         .stdin(Stdio::piped())
@@ -88,42 +79,29 @@ fn reformat(text: impl std::fmt::Display) -> Result<String> {
 }
 
 fn ensure_rustfmt() -> Result<()> {
-    match Command::new("rustfmt")
-        .args(&["--version"])
-        .env("RUSTUP_TOOLCHAIN", RUSTFMT_TOOLCHAIN)
-        .stdout(Stdio::piped())
+    match Command::new("rustup")
+        .args(&["run", TOOLCHAIN, "--", "cargo", "fmt", "--version"])
         .stderr(Stdio::null())
-        .spawn()
-        .and_then(|child| child.wait_with_output())
+        .stdout(Stdio::null())
+        .status()
     {
-        Ok(output)
-            if output.status.success()
-                && std::str::from_utf8(&output.stdout)?.contains(RUSTFMT_TOOLCHAIN) =>
-        {
-            Ok(())
-        }
-        _ => {
-            bail!(
-                "Failed to run rustfmt from toolchain '{0}'. \
-                Please run `rustup component add rustfmt --toolchain {0}` to install it.",
-                RUSTFMT_TOOLCHAIN,
-            );
-        }
-    }
+        Ok(status) if status.success() => return Ok(()),
+        _ => (),
+    };
+    run!("rustup toolchain install {}", TOOLCHAIN)?;
+    run!("rustup component add rustfmt --toolchain {}", TOOLCHAIN)?;
+    Ok(())
 }
 
 pub fn run_clippy() -> Result<()> {
-    match Command::new("cargo")
-        .args(&["clippy", "--version"])
+    match Command::new("rustup")
+        .args(&["run", TOOLCHAIN, "--", "cargo", "clippy", "--version"])
         .stderr(Stdio::null())
         .stdout(Stdio::null())
         .status()
     {
         Ok(status) if status.success() => (),
-        _ => bail!(
-            "Failed run cargo clippy. \
-            Please run `rustup component add clippy` to install it.",
-        ),
+        _ => install_clippy().context("install clippy")?,
     };
 
     let allowed_lints = [
@@ -132,7 +110,17 @@ pub fn run_clippy() -> Result<()> {
         "clippy::nonminimal_bool",
         "clippy::redundant_pattern_matching",
     ];
-    run!("cargo clippy --all-features --all-targets -- -A {}", allowed_lints.join(" -A "))?;
+    run!(
+        "rustup run {} -- cargo clippy --all-features --all-targets -- -A {}",
+        TOOLCHAIN,
+        allowed_lints.join(" -A ")
+    )?;
+    Ok(())
+}
+
+fn install_clippy() -> Result<()> {
+    run!("rustup toolchain install {}", TOOLCHAIN)?;
+    run!("rustup component add clippy --toolchain {}", TOOLCHAIN)?;
     Ok(())
 }
 
@@ -142,29 +130,7 @@ pub fn run_fuzzer() -> Result<()> {
         run!("cargo install cargo-fuzz")?;
     };
 
-    // Expecting nightly rustc
-    match Command::new("rustc")
-        .args(&["--version"])
-        .env("RUSTUP_TOOLCHAIN", "nightly")
-        .stdout(Stdio::piped())
-        .stderr(Stdio::null())
-        .spawn()
-        .and_then(|child| child.wait_with_output())
-    {
-        Ok(output)
-            if output.status.success()
-                && std::str::from_utf8(&output.stdout)?.contains("nightly") => {}
-        _ => bail!("fuzz tests require nightly rustc"),
-    }
-
-    let status = Command::new("cargo")
-        .env("RUSTUP_TOOLCHAIN", "nightly")
-        .args(&["fuzz", "run", "parser"])
-        .stderr(Stdio::inherit())
-        .status()?;
-    if !status.success() {
-        bail!("{}", status);
-    }
+    run!("rustup run nightly -- cargo fuzz run parser")?;
     Ok(())
 }
 


Test patch:
diff --git a/crates/ra_hir_ty/src/tests/macros.rs b/crates/ra_hir_ty/src/tests/macros.rs
index 5ddecbdc68..6b52672325 100644
--- a/crates/ra_hir_ty/src/tests/macros.rs
+++ b/crates/ra_hir_ty/src/tests/macros.rs
@@ -622,14 +622,14 @@ fn main() {
 fn infer_derive_clone_simple() {
     let (db, pos) = TestDB::with_position(
         r#"
-//- /main.rs crate:main deps:core
+//- /main.rs crate:main deps:std
 #[derive(Clone)]
 struct S;
 fn test() {
     S.clone()<|>;
 }
 
-//- /lib.rs crate:core
+//- /lib.rs crate:std
 #[prelude_import]
 use clone::*;
 mod clone {
@@ -642,36 +642,11 @@ mod clone {
     assert_eq!("S", type_at_pos(&db, pos));
 }
 
-#[test]
-fn infer_derive_clone_in_core() {
-    let (db, pos) = TestDB::with_position(
-        r#"
-//- /lib.rs crate:core
-#[prelude_import]
-use clone::*;
-mod clone {
-    trait Clone {
-        fn clone(&self) -> Self;
-    }
-}
-#[derive(Clone)]
-pub struct S;
-
-//- /main.rs crate:main deps:core
-use core::S;
-fn test() {
-    S.clone()<|>;
-}
-"#,
-    );
-    assert_eq!("S", type_at_pos(&db, pos));
-}
-
 #[test]
 fn infer_derive_clone_with_params() {
     let (db, pos) = TestDB::with_position(
         r#"
-//- /main.rs crate:main deps:core
+//- /main.rs crate:main deps:std
 #[derive(Clone)]
 struct S;
 #[derive(Clone)]
@@ -681,7 +656,7 @@ fn test() {
     (Wrapper(S).clone(), Wrapper(NonClone).clone())<|>;
 }
 
-//- /lib.rs crate:core
+//- /lib.rs crate:std
 #[prelude_import]
 use clone::*;
 mod clone {
diff --git a/crates/ra_proc_macro_srv/src/tests/utils.rs b/crates/ra_proc_macro_srv/src/tests/utils.rs
index 646a427c56..2139ec7a4d 100644
--- a/crates/ra_proc_macro_srv/src/tests/utils.rs
+++ b/crates/ra_proc_macro_srv/src/tests/utils.rs
@@ -1,7 +1,7 @@
 //! utils used in proc-macro tests
 
 use crate::dylib;
-use crate::ProcMacroSrv;
+use crate::list_macros;
 pub use difference::Changeset as __Changeset;
 use ra_proc_macro::ListMacrosTask;
 use std::str::FromStr;
@@ -59,7 +59,7 @@ pub fn assert_expand(
 pub fn list(crate_name: &str, version: &str) -> Vec<String> {
     let path = fixtures::dylib_path(crate_name, version);
     let task = ListMacrosTask { lib: path };
-    let mut srv = ProcMacroSrv::default();
-    let res = srv.list_macros(&task).unwrap();
+
+    let res = list_macros(&task);
     res.macros.into_iter().map(|(name, kind)| format!("{} [{:?}]", name, kind)).collect()
 }
diff --git a/crates/ra_syntax/test_data/parser/err/0040_illegal_crate_kw_location.rast b/crates/ra_syntax/test_data/parser/err/0040_illegal_crate_kw_location.rast
deleted file mode 100644
index 8306f73610..0000000000
--- a/crates/ra_syntax/test_data/parser/err/0040_illegal_crate_kw_location.rast
+++ /dev/null
@@ -1,76 +0,0 @@
-SOURCE_FILE@0..83
-  USE_ITEM@0..12
-    USE_KW@0..3 "use"
-    WHITESPACE@3..4 " "
-    USE_TREE@4..11
-      PATH@4..11
-        PATH_SEGMENT@4..11
-          COLON2@4..6 "::"
-          CRATE_KW@6..11 "crate"
-    SEMICOLON@11..12 ";"
-  WHITESPACE@12..13 "\n"
-  USE_ITEM@13..39
-    USE_KW@13..16 "use"
-    WHITESPACE@16..17 " "
-    USE_TREE@17..38
-      USE_TREE_LIST@17..38
-        L_CURLY@17..18 "{"
-        USE_TREE@18..23
-          PATH@18..23
-            PATH_SEGMENT@18..23
-              CRATE_KW@18..23 "crate"
-        COMMA@23..24 ","
-        WHITESPACE@24..25 " "
-        USE_TREE@25..37
-          PATH@25..28
-            PATH_SEGMENT@25..28
-              NAME_REF@25..28
-                IDENT@25..28 "foo"
-          COLON2@28..30 "::"
-          USE_TREE_LIST@30..37
-            L_CURLY@30..31 "{"
-            USE_TREE@31..36
-              PATH@31..36
-                PATH_SEGMENT@31..36
-                  CRATE_KW@31..36 "crate"
-            R_CURLY@36..37 "}"
-        R_CURLY@37..38 "}"
-    SEMICOLON@38..39 ";"
-  WHITESPACE@39..40 "\n"
-  USE_ITEM@40..57
-    USE_KW@40..43 "use"
-    WHITESPACE@43..44 " "
-    USE_TREE@44..56
-      PATH@44..56
-        PATH@44..49
-          PATH_SEGMENT@44..49
-            NAME_REF@44..49
-              IDENT@44..49 "hello"
-        COLON2@49..51 "::"
-        PATH_SEGMENT@51..56
-          CRATE_KW@51..56 "crate"
-    SEMICOLON@56..57 ";"
-  WHITESPACE@57..58 "\n"
-  USE_ITEM@58..82
-    USE_KW@58..61 "use"
-    WHITESPACE@61..62 " "
-    USE_TREE@62..81
-      PATH@62..81
-        PATH@62..74
-          PATH@62..67
-            PATH_SEGMENT@62..67
-              NAME_REF@62..67
-                IDENT@62..67 "hello"
-          COLON2@67..69 "::"
-          PATH_SEGMENT@69..74
-            CRATE_KW@69..74 "crate"
-        COLON2@74..76 "::"
-        PATH_SEGMENT@76..81
-          NAME_REF@76..81
-            IDENT@76..81 "there"
-    SEMICOLON@81..82 ";"
-  WHITESPACE@82..83 "\n"
-error 6..11: The `crate` keyword is only allowed as the first segment of a path
-error 31..36: The `crate` keyword is only allowed as the first segment of a path
-error 51..56: The `crate` keyword is only allowed as the first segment of a path
-error 69..74: The `crate` keyword is only allowed as the first segment of a path
diff --git a/crates/ra_syntax/test_data/parser/err/0040_illegal_crate_kw_location.rs b/crates/ra_syntax/test_data/parser/err/0040_illegal_crate_kw_location.rs
deleted file mode 100644
index bead4c0b6e..0000000000
--- a/crates/ra_syntax/test_data/parser/err/0040_illegal_crate_kw_location.rs
+++ /dev/null
@@ -1,4 +0,0 @@
-use ::crate;
-use {crate, foo::{crate}};
-use hello::crate;
-use hello::crate::there;
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0159_try_macro_fallback.rast b/crates/ra_syntax/test_data/parser/inline/ok/0159_try_macro_fallback.rast
deleted file mode 100644
index beb6d80104..0000000000
--- a/crates/ra_syntax/test_data/parser/inline/ok/0159_try_macro_fallback.rast
+++ /dev/null
@@ -1,35 +0,0 @@
-SOURCE_FILE@0..27
-  FN_DEF@0..26
-    FN_KW@0..2 "fn"
-    WHITESPACE@2..3 " "
-    NAME@3..6
-      IDENT@3..6 "foo"
-    PARAM_LIST@6..8
-      L_PAREN@6..7 "("
-      R_PAREN@7..8 ")"
-    WHITESPACE@8..9 " "
-    BLOCK_EXPR@9..26
-      BLOCK@9..26
-        L_CURLY@9..10 "{"
-        WHITESPACE@10..11 " "
-        EXPR_STMT@11..24
-          MACRO_CALL@11..23
-            PATH@11..14
-              PATH_SEGMENT@11..14
-                NAME_REF@11..14
-                  IDENT@11..14 "try"
-            BANG@14..15 "!"
-            TOKEN_TREE@15..23
-              L_PAREN@15..16 "("
-              IDENT@16..18 "Ok"
-              TOKEN_TREE@18..22
-                L_PAREN@18..19 "("
-                TOKEN_TREE@19..21
-                  L_PAREN@19..20 "("
-                  R_PAREN@20..21 ")"
-                R_PAREN@21..22 ")"
-              R_PAREN@22..23 ")"
-          SEMICOLON@23..24 ";"
-        WHITESPACE@24..25 " "
-        R_CURLY@25..26 "}"
-  WHITESPACE@26..27 "\n"
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0159_try_macro_fallback.rs b/crates/ra_syntax/test_data/parser/inline/ok/0159_try_macro_fallback.rs
deleted file mode 100644
index 61a6b46a0b..0000000000
--- a/crates/ra_syntax/test_data/parser/inline/ok/0159_try_macro_fallback.rs
+++ /dev/null
@@ -1,1 +0,0 @@
-fn foo() { try!(Ok(())); }
diff --git a/crates/rust-analyzer/tests/heavy_tests/main.rs b/crates/rust-analyzer/tests/heavy_tests/main.rs
index 07b8114c61..f6245ddd47 100644
--- a/crates/rust-analyzer/tests/heavy_tests/main.rs
+++ b/crates/rust-analyzer/tests/heavy_tests/main.rs
@@ -4,8 +4,8 @@ use std::{collections::HashMap, path::PathBuf, time::Instant};
 
 use lsp_types::{
     CodeActionContext, DidOpenTextDocumentParams, DocumentFormattingParams, FormattingOptions,
-    GotoDefinitionParams, HoverParams, PartialResultParams, Position, Range, TextDocumentItem,
-    TextDocumentPositionParams, WorkDoneProgressParams,
+    PartialResultParams, Position, Range, TextDocumentItem, TextDocumentPositionParams,
+    WorkDoneProgressParams,
 };
 use rust_analyzer::req::{
     CodeActionParams, CodeActionRequest, Completion, CompletionParams, DidOpenTextDocument,
@@ -610,14 +610,10 @@ fn main() { message(); }
     })
     .server();
     server.wait_until_workspace_is_loaded();
-    let res = server.send_request::<GotoDefinition>(GotoDefinitionParams {
-        text_document_position_params: TextDocumentPositionParams::new(
-            server.doc_id("src/main.rs"),
-            Position::new(2, 15),
-        ),
-        work_done_progress_params: Default::default(),
-        partial_result_params: Default::default(),
-    });
+    let res = server.send_request::<GotoDefinition>(TextDocumentPositionParams::new(
+        server.doc_id("src/main.rs"),
+        Position::new(2, 15),
+    ));
     assert!(format!("{}", res).contains("hello.rs"));
 }
 
@@ -696,13 +692,10 @@ pub fn foo(_input: TokenStream) -> TokenStream {
     .root("bar")
     .server();
     server.wait_until_workspace_is_loaded();
-    let res = server.send_request::<HoverRequest>(HoverParams {
-        text_document_position_params: TextDocumentPositionParams::new(
-            server.doc_id("foo/src/main.rs"),
-            Position::new(7, 9),
-        ),
-        work_done_progress_params: Default::default(),
-    });
+    let res = server.send_request::<HoverRequest>(TextDocumentPositionParams::new(
+        server.doc_id("foo/src/main.rs"),
+        Position::new(7, 9),
+    ));
 
     let value = res.get("contents").unwrap().get("value").unwrap().to_string();
     assert_eq!(value, r#""```rust\nfoo::Bar\nfn bar()\n```""#)