Repository: rust-analyzer/rust-analyzer
Pull request #4178 (2020-04-27 22:26:56)

Validate the location of `crate` in paths
**This solution does not fully handle `use` statements. See below**

This pull requests implements simple validation of usages of the `crate` keyword in `Path`s. Specifically it validates that:

- If a `PathSegment` is starts with the `crate` keyword, it is also the first segment of the `Path`
- All other usages of `crate` in `Path`s are considered errors.

This aligns with `rustc`'s rules. Unlike rustc this implementation does not issue a special error message in the case of `::crate` but it does catch the error.

Furthermore, this change does not cover all error cases. Specifically the following is not caught:

```rust
use foo::{crate}
```

This is because this check is context sensitive. From an AST perspective, `crate` is the root of the `Path`. Only by inspecting the full `UseItem` do we see that it is not in fact the root. This problem becomes worse because `UseTree`s are allowed to be arbitrarily nested:

```rust
use {crate, {{crate, foo::{crate}}}
```

So this is a hard problem to solve without essentially a breadth-first search. In a traditional compiler, I'd say this error is most easily found during the AST -> HIR conversion pass but within rust-analyzer I'm not sure where it belongs.  

Under the implementation in this PR, such errors are ignored so we're *more correct* just not *entirely correct*. 

djrenren: There's an alternate implementation that checks that the `PathSegment` is *exactly* the `crate` keyword:

```rust
    let segment = path.segment();

    let first_element_as_token = segment.as_ref().and_then(|it| it.syntax().first_child_or_token()?.into_token());
    let last_element_as_token = segment.as_ref().and_then(|it| it.syntax().last_child_or_token()?.into_token());

    let start_of_path = path.qualifier().is_none();

    // `crate` is allowed to be the start of the path, so we can't error if this is true
    if start_of_path && first_element_as_token == last_element_as_token {
        return
    }

    let child_tokens = segment
        .iter()
        .flat_map(|seg| seg.syntax().children_with_tokens())
        .flat_map(|it| it.into_token());


    // Beyond the first position, `crate` is never allowed to appear
    for child in child_tokens {
        if child.kind() == CRATE_KW {
            errors.push(SyntaxError::new("`crate` keyword is only allowed as the first segment of a path", child.text_range()))
        }
    }
```

but I wasn't sure what was preferable.
djrenren: Was thinking about this more last night, and this code is mainly weird because there's no accessor on `PathSegment` that would return the `crate` keyword. Maybe it would make more sense to add one? I imagine there's a similar situation for the `super` keyword.
matklad: >Maybe it would make more sense to add one? I imagine there's a similar situation for the super keyword.

Definitelly, this should work by adding `T![crate]` here: https://github.com/rust-analyzer/rust-analyzer/blob/549ce9a9cf25efa3eba6549f96b2e43bc640faa9/xtask/src/ast_src.rs#L598

> but within rust-analyzer I'm not sure where it belongs.

Not that I know for sure :D 

My gut feeling would probably try to push all diagnostics as early as possible, and, in this case, this means syntax tree validation. 

djrenren: Okay looks like it should handle everything! And the code looks so much cleaner.

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 3fb4195718..fb788736d8 100644
--- a/crates/ra_hir/src/code_model.rs
+++ b/crates/ra_hir/src/code_model.rs
@@ -1157,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_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_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_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/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_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 2cb3ad0114..3b5e05af92 100644
--- a/crates/ra_syntax/src/ast/generated/nodes.rs
+++ b/crates/ra_syntax/src/ast/generated/nodes.rs
@@ -1249,6 +1249,7 @@ 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 5e93895ec5..a30bc97bb0 100644
--- a/crates/ra_syntax/src/validation.rs
+++ b/crates/ra_syntax/src/validation.rs
@@ -96,6 +96,7 @@ 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),
                 _ => (),
             }
         }
@@ -222,3 +223,41 @@ 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/readme.adoc b/docs/user/readme.adoc
index 8b80a7df7b..76d065d35b 100644
--- a/docs/user/readme.adoc
+++ b/docs/user/readme.adoc
@@ -195,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):
 +
@@ -212,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..b8aaa07d87 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"
diff --git a/xtask/src/ast_src.rs b/xtask/src/ast_src.rs
index 9c02f7c6f0..98c8644e45 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![<], NameRef, TypeArgList, ParamList, RetType, PathType, T![>]
+            T![::], T![crate], 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") {


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
new file mode 100644
index 0000000000..8306f73610
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/err/0040_illegal_crate_kw_location.rast
@@ -0,0 +1,76 @@
+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
new file mode 100644
index 0000000000..bead4c0b6e
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/err/0040_illegal_crate_kw_location.rs
@@ -0,0 +1,4 @@
+use ::crate;
+use {crate, foo::{crate}};
+use hello::crate;
+use hello::crate::there;
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```""#)