Repository: rust-analyzer/rust-analyzer
Pull request #6399 (2020-10-29 09:27:11)

Keep generic annotations when qualifying things
The `qualify_path` assists currently eats up already annotated generics in all but one cases which can be annoying if one already pre-fills generics of a type before it's been qualified.


Patch:
diff --git a/Cargo.lock b/Cargo.lock
index 0705aca4e3..fe211b9b30 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -723,11 +723,11 @@ dependencies = [
 
 [[package]]
 name = "instant"
-version = "0.1.8"
+version = "0.1.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cb1fc4429a33e1f80d41dc9fea4d108a88bec1de8053878898ae448a0b52f613"
+checksum = "63312a18f7ea8760cdd0a7c5aac1a619752a246b833545e3e36d1f81f7cd9e66"
 dependencies = [
- "cfg-if 1.0.0",
+ "cfg-if 0.1.10",
 ]
 
 [[package]]
@@ -784,9 +784,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
 
 [[package]]
 name = "libc"
-version = "0.2.80"
+version = "0.2.79"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4d58d1b70b004888f764dfbf6a26a3b0342a1632d33968e4a179d8011c760614"
+checksum = "2448f6066e80e3bfc792e9c98bf705b4b0fc6e8ef5b43e5889aff0eaa9c58743"
 
 [[package]]
 name = "libloading"
@@ -1614,9 +1614,9 @@ version = "0.0.0"
 
 [[package]]
 name = "syn"
-version = "1.0.48"
+version = "1.0.46"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cc371affeffc477f42a221a1e4297aedcea33d47d19b61455588bd9d8f6b19ac"
+checksum = "5ad5de3220ea04da322618ded2c42233d02baca219d6f160a3e9c87cda16c942"
 dependencies = [
  "proc-macro2",
  "quote",
diff --git a/crates/assists/src/handlers/extract_struct_from_enum_variant.rs b/crates/assists/src/handlers/extract_struct_from_enum_variant.rs
index 178718c5e1..48433feb9e 100644
--- a/crates/assists/src/handlers/extract_struct_from_enum_variant.rs
+++ b/crates/assists/src/handlers/extract_struct_from_enum_variant.rs
@@ -37,12 +37,6 @@ pub(crate) fn extract_struct_from_enum_variant(
         ast::StructKind::Tuple(field_list) => field_list,
         _ => return None,
     };
-
-    // skip 1-tuple variants
-    if field_list.fields().count() == 1 {
-        return None;
-    }
-
     let variant_name = variant.name()?.to_string();
     let variant_hir = ctx.sema.to_def(&variant)?;
     if existing_struct_def(ctx.db(), &variant_name, &variant_hir) {
@@ -234,6 +228,17 @@ mod tests {
             "enum A { <|>One(u32, u32) }",
             r#"struct One(pub u32, pub u32);
 
+enum A { One(One) }"#,
+        );
+    }
+
+    #[test]
+    fn test_extract_struct_one_field() {
+        check_assist(
+            extract_struct_from_enum_variant,
+            "enum A { <|>One(u32) }",
+            r#"struct One(pub u32);
+
 enum A { One(One) }"#,
         );
     }
@@ -319,9 +324,4 @@ fn another_fn() {
         enum A { <|>One(u8) }"#,
         );
     }
-
-    #[test]
-    fn test_extract_not_applicable_one_field() {
-        check_not_applicable(r"enum A { <|>One(u32) }");
-    }
 }
diff --git a/crates/assists/src/handlers/qualify_path.rs b/crates/assists/src/handlers/qualify_path.rs
index f436bdbbfa..d5bc4e574f 100644
--- a/crates/assists/src/handlers/qualify_path.rs
+++ b/crates/assists/src/handlers/qualify_path.rs
@@ -56,12 +56,14 @@ pub(crate) fn qualify_path(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
         ImportCandidate::QualifierStart(_) => {
             mark::hit!(qualify_path_qualifier_start);
             let path = ast::Path::cast(import_assets.syntax_under_caret().clone())?;
-            let segment = path.segment()?;
-            QualifyCandidate::QualifierStart(segment)
+            let (prev_segment, segment) = (path.qualifier()?.segment()?, path.segment()?);
+            QualifyCandidate::QualifierStart(segment, prev_segment.generic_arg_list())
         }
         ImportCandidate::UnqualifiedName(_) => {
             mark::hit!(qualify_path_unqualified_name);
-            QualifyCandidate::UnqualifiedName
+            let path = ast::Path::cast(import_assets.syntax_under_caret().clone())?;
+            let generics = path.segment()?.generic_arg_list();
+            QualifyCandidate::UnqualifiedName(generics)
         }
         ImportCandidate::TraitAssocItem(_) => {
             mark::hit!(qualify_path_trait_assoc_item);
@@ -96,22 +98,25 @@ pub(crate) fn qualify_path(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
 }
 
 enum QualifyCandidate<'db> {
-    QualifierStart(ast::PathSegment),
-    UnqualifiedName,
+    QualifierStart(ast::PathSegment, Option<ast::GenericArgList>),
+    UnqualifiedName(Option<ast::GenericArgList>),
     TraitAssocItem(ast::Path, ast::PathSegment),
     TraitMethod(&'db RootDatabase, ast::MethodCallExpr),
 }
 
 impl QualifyCandidate<'_> {
     fn qualify(&self, mut replacer: impl FnMut(String), import: hir::ModPath, item: hir::ItemInNs) {
+        let import = mod_path_to_ast(&import);
         match self {
-            QualifyCandidate::QualifierStart(segment) => {
-                let import = mod_path_to_ast(&import);
-                replacer(format!("{}::{}", import, segment));
+            QualifyCandidate::QualifierStart(segment, generics) => {
+                let generics = generics.as_ref().map_or_else(String::new, ToString::to_string);
+                replacer(format!("{}{}::{}", import, generics, segment));
+            }
+            QualifyCandidate::UnqualifiedName(generics) => {
+                let generics = generics.as_ref().map_or_else(String::new, ToString::to_string);
+                replacer(format!("{}{}", import.to_string(), generics));
             }
-            QualifyCandidate::UnqualifiedName => replacer(mod_path_to_ast(&import).to_string()),
             QualifyCandidate::TraitAssocItem(qualifier, segment) => {
-                let import = mod_path_to_ast(&import);
                 replacer(format!("<{} as {}>::{}", qualifier, import, segment));
             }
             &QualifyCandidate::TraitMethod(db, ref mcall_expr) => {
@@ -124,25 +129,27 @@ impl QualifyCandidate<'_> {
         db: &RootDatabase,
         mcall_expr: &ast::MethodCallExpr,
         mut replacer: impl FnMut(String),
-        import: hir::ModPath,
+        import: ast::Path,
         item: hir::ItemInNs,
     ) -> Option<()> {
         let receiver = mcall_expr.receiver()?;
         let trait_method_name = mcall_expr.name_ref()?;
+        let generics =
+            mcall_expr.generic_arg_list().as_ref().map_or_else(String::new, ToString::to_string);
         let arg_list = mcall_expr.arg_list().map(|arg_list| arg_list.args());
         let trait_ = item_as_trait(item)?;
         let method = find_trait_method(db, trait_, &trait_method_name)?;
         if let Some(self_access) = method.self_param(db).map(|sp| sp.access(db)) {
-            let import = mod_path_to_ast(&import);
             let receiver = match self_access {
                 hir::Access::Shared => make::expr_ref(receiver, false),
                 hir::Access::Exclusive => make::expr_ref(receiver, true),
                 hir::Access::Owned => receiver,
             };
             replacer(format!(
-                "{}::{}{}",
+                "{}::{}{}{}",
                 import,
                 trait_method_name,
+                generics,
                 match arg_list.clone() {
                     Some(args) => make::arg_list(iter::once(receiver).chain(args)),
                     None => make::arg_list(iter::once(receiver)),
@@ -1045,4 +1052,153 @@ fn main() {
 ",
         );
     }
+
+    #[test]
+    fn keep_generic_annotations() {
+        check_assist(
+            qualify_path,
+            r"
+//- /lib.rs crate:dep
+pub mod generic { pub struct Thing<'a, T>(&'a T); }
+
+//- /main.rs crate:main deps:dep
+fn foo() -> Thin<|>g<'static, ()> {}
+
+fn main() {}
+",
+            r"
+fn foo() -> dep::generic::Thing<'static, ()> {}
+
+fn main() {}
+",
+        );
+    }
+
+    #[test]
+    fn keep_generic_annotations_leading_colon() {
+        check_assist(
+            qualify_path,
+            r"
+//- /lib.rs crate:dep
+pub mod generic { pub struct Thing<'a, T>(&'a T); }
+
+//- /main.rs crate:main deps:dep
+fn foo() -> Thin<|>g::<'static, ()> {}
+
+fn main() {}
+",
+            r"
+fn foo() -> dep::generic::Thing::<'static, ()> {}
+
+fn main() {}
+",
+        );
+    }
+
+    #[test]
+    fn associated_struct_const_generic() {
+        check_assist(
+            qualify_path,
+            r"
+            mod test_mod {
+                pub struct TestStruct<T> {}
+                impl<T> TestStruct<T> {
+                    const TEST_CONST: u8 = 42;
+                }
+            }
+
+            fn main() {
+                TestStruct::<()>::TEST_CONST<|>
+            }
+            ",
+            r"
+            mod test_mod {
+                pub struct TestStruct<T> {}
+                impl<T> TestStruct<T> {
+                    const TEST_CONST: u8 = 42;
+                }
+            }
+
+            fn main() {
+                test_mod::TestStruct::<()>::TEST_CONST
+            }
+            ",
+        );
+    }
+
+    #[test]
+    fn associated_trait_const_generic() {
+        check_assist(
+            qualify_path,
+            r"
+            mod test_mod {
+                pub trait TestTrait {
+                    const TEST_CONST: u8;
+                }
+                pub struct TestStruct<T> {}
+                impl<T> TestTrait for TestStruct<T> {
+                    const TEST_CONST: u8 = 42;
+                }
+            }
+
+            fn main() {
+                test_mod::TestStruct::<()>::TEST_CONST<|>
+            }
+            ",
+            r"
+            mod test_mod {
+                pub trait TestTrait {
+                    const TEST_CONST: u8;
+                }
+                pub struct TestStruct<T> {}
+                impl<T> TestTrait for TestStruct<T> {
+                    const TEST_CONST: u8 = 42;
+                }
+            }
+
+            fn main() {
+                <test_mod::TestStruct::<()> as test_mod::TestTrait>::TEST_CONST
+            }
+            ",
+        );
+    }
+
+    #[test]
+    fn trait_method_generic() {
+        check_assist(
+            qualify_path,
+            r"
+            mod test_mod {
+                pub trait TestTrait {
+                    fn test_method<T>(&self);
+                }
+                pub struct TestStruct {}
+                impl TestTrait for TestStruct {
+                    fn test_method<T>(&self) {}
+                }
+            }
+
+            fn main() {
+                let test_struct = test_mod::TestStruct {};
+                test_struct.test_meth<|>od::<()>()
+            }
+            ",
+            r"
+            mod test_mod {
+                pub trait TestTrait {
+                    fn test_method<T>(&self);
+                }
+                pub struct TestStruct {}
+                impl TestTrait for TestStruct {
+                    fn test_method<T>(&self) {}
+                }
+            }
+
+            fn main() {
+                let test_struct = test_mod::TestStruct {};
+                test_mod::TestTrait::test_method::<()>(&test_struct)
+            }
+            ",
+        );
+    }
 }
diff --git a/crates/hir/src/code_model.rs b/crates/hir/src/code_model.rs
index 563145f921..63c1a8ebf0 100644
--- a/crates/hir/src/code_model.rs
+++ b/crates/hir/src/code_model.rs
@@ -1398,7 +1398,7 @@ impl Type {
         };
 
         let canonical_ty = Canonical { value: self.ty.value.clone(), kinds: Arc::new([]) };
-        method_resolution::implements_trait_unique(
+        method_resolution::implements_trait(
             &canonical_ty,
             db,
             self.ty.environment.clone(),
diff --git a/crates/hir_ty/src/display.rs b/crates/hir_ty/src/display.rs
index 14e8c06332..d2e151f252 100644
--- a/crates/hir_ty/src/display.rs
+++ b/crates/hir_ty/src/display.rs
@@ -25,20 +25,6 @@ pub struct HirFormatter<'a> {
 pub trait HirDisplay {
     fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError>;
 
-    /// Returns a `Display`able type that is human-readable.
-    fn into_displayable<'a>(
-        &'a self,
-        db: &'a dyn HirDatabase,
-        max_size: Option<usize>,
-        omit_verbose_types: bool,
-        display_target: DisplayTarget,
-    ) -> HirDisplayWrapper<'a, Self>
-    where
-        Self: Sized,
-    {
-        HirDisplayWrapper { db, t: self, max_size, omit_verbose_types, display_target }
-    }
-
     /// Returns a `Display`able type that is human-readable.
     /// Use this for showing types to the user (e.g. diagnostics)
     fn display<'a>(&'a self, db: &'a dyn HirDatabase) -> HirDisplayWrapper<'a, Self>
@@ -96,20 +82,6 @@ pub trait HirDisplay {
         };
         Ok(result)
     }
-
-    /// Returns a String representation of `self` for test purposes
-    fn display_test<'a>(&'a self, db: &'a dyn HirDatabase) -> HirDisplayWrapper<'a, Self>
-    where
-        Self: Sized,
-    {
-        HirDisplayWrapper {
-            db,
-            t: self,
-            max_size: None,
-            omit_verbose_types: false,
-            display_target: DisplayTarget::Test,
-        }
-    }
 }
 
 impl<'a> HirFormatter<'a> {
@@ -154,7 +126,7 @@ impl<'a> HirFormatter<'a> {
 }
 
 #[derive(Clone, Copy)]
-pub enum DisplayTarget {
+enum DisplayTarget {
     /// Display types for inlays, doc popups, autocompletion, etc...
     /// Showing `{unknown}` or not qualifying paths is fine here.
     /// There's no reason for this to fail.
@@ -162,17 +134,12 @@ pub enum DisplayTarget {
     /// Display types for inserting them in source files.
     /// The generated code should compile, so paths need to be qualified.
     SourceCode { module_id: ModuleId },
-    /// Only for test purpose to keep real types
-    Test,
 }
 
 impl DisplayTarget {
     fn is_source_code(&self) -> bool {
         matches!(self, Self::SourceCode {..})
     }
-    fn is_test(&self) -> bool {
-        matches!(self, Self::Test)
-    }
 }
 
 #[derive(Debug)]
@@ -246,32 +213,32 @@ impl HirDisplay for ApplicationTy {
             TypeCtor::Str => write!(f, "str")?,
             TypeCtor::Slice => {
                 let t = self.parameters.as_single();
-                write!(f, "[")?;
-                t.hir_fmt(f)?;
-                write!(f, "]")?;
+                write!(f, "[{}]", t.display(f.db))?;
             }
             TypeCtor::Array => {
                 let t = self.parameters.as_single();
-                write!(f, "[")?;
-                t.hir_fmt(f)?;
-                write!(f, "; _]")?;
+                write!(f, "[{}; _]", t.display(f.db))?;
             }
             TypeCtor::RawPtr(m) => {
                 let t = self.parameters.as_single();
+                let ty_display = t.display(f.db);
 
                 write!(f, "*{}", m.as_keyword_for_ptr())?;
                 if matches!(t, Ty::Dyn(predicates) if predicates.len() > 1) {
                     write!(f, "(")?;
-                    t.hir_fmt(f)?;
+                    write!(f, "{}", ty_display)?;
                     write!(f, ")")?;
                 } else {
-                    t.hir_fmt(f)?;
+                    write!(f, "{}", ty_display)?;
                 }
             }
             TypeCtor::Ref(m) => {
                 let t = self.parameters.as_single();
-                let ty_display =
-                    t.into_displayable(f.db, f.max_size, f.omit_verbose_types, f.display_target);
+                let ty_display = if f.omit_verbose_types() {
+                    t.display_truncated(f.db, f.max_size)
+                } else {
+                    t.display(f.db)
+                };
 
                 write!(f, "&{}", m.as_keyword_for_ref())?;
                 if matches!(t, Ty::Dyn(predicates) if predicates.len() > 1) {
@@ -286,9 +253,7 @@ impl HirDisplay for ApplicationTy {
             TypeCtor::Tuple { .. } => {
                 let ts = &self.parameters;
                 if ts.len() == 1 {
-                    write!(f, "(")?;
-                    ts[0].hir_fmt(f)?;
-                    write!(f, ",)")?;
+                    write!(f, "({},)", ts[0].display(f.db))?;
                 } else {
                     write!(f, "(")?;
                     f.write_joined(&*ts.0, ", ")?;
@@ -309,12 +274,11 @@ impl HirDisplay for ApplicationTy {
                 write!(f, ")")?;
                 let ret = sig.ret();
                 if *ret != Ty::unit() {
-                    let ret_display = ret.into_displayable(
-                        f.db,
-                        f.max_size,
-                        f.omit_verbose_types,
-                        f.display_target,
-                    );
+                    let ret_display = if f.omit_verbose_types() {
+                        ret.display_truncated(f.db, f.max_size)
+                    } else {
+                        ret.display(f.db)
+                    };
                     write!(f, " -> {}", ret_display)?;
                 }
             }
@@ -346,19 +310,17 @@ impl HirDisplay for ApplicationTy {
                 write!(f, ")")?;
                 let ret = sig.ret();
                 if *ret != Ty::unit() {
-                    let ret_display = ret.into_displayable(
-                        f.db,
-                        f.max_size,
-                        f.omit_verbose_types,
-                        f.display_target,
-                    );
-
+                    let ret_display = if f.omit_verbose_types() {
+                        ret.display_truncated(f.db, f.max_size)
+                    } else {
+                        ret.display(f.db)
+                    };
                     write!(f, " -> {}", ret_display)?;
                 }
             }
             TypeCtor::Adt(def_id) => {
                 match f.display_target {
-                    DisplayTarget::Diagnostics | DisplayTarget::Test => {
+                    DisplayTarget::Diagnostics => {
                         let name = match def_id {
                             AdtId::StructId(it) => f.db.struct_data(it).name.clone(),
                             AdtId::UnionId(it) => f.db.union_data(it).name.clone(),
@@ -427,23 +389,12 @@ impl HirDisplay for ApplicationTy {
                     _ => panic!("not an associated type"),
                 };
                 let trait_ = f.db.trait_data(trait_);
-                let type_alias_data = f.db.type_alias_data(type_alias);
-
-                // Use placeholder associated types when the target is test (https://rust-lang.github.io/chalk/book/clauses/type_equality.html#placeholder-associated-types)
-                if f.display_target.is_test() {
-                    write!(f, "{}::{}", trait_.name, type_alias_data.name)?;
-                    if self.parameters.len() > 0 {
-                        write!(f, "<")?;
-                        f.write_joined(&*self.parameters.0, ", ")?;
-                        write!(f, ">")?;
-                    }
-                } else {
-                    let projection_ty = ProjectionTy {
-                        associated_ty: type_alias,
-                        parameters: self.parameters.clone(),
-                    };
-
-                    projection_ty.hir_fmt(f)?;
+                let type_alias = f.db.type_alias_data(type_alias);
+                write!(f, "{}::{}", trait_.name, type_alias.name)?;
+                if self.parameters.len() > 0 {
+                    write!(f, "<")?;
+                    f.write_joined(&*self.parameters.0, ", ")?;
+                    write!(f, ">")?;
                 }
             }
             TypeCtor::ForeignType(type_alias) => {
@@ -488,12 +439,11 @@ impl HirDisplay for ApplicationTy {
                         write!(f, "|")?;
                     };
 
-                    let ret_display = sig.ret().into_displayable(
-                        f.db,
-                        f.max_size,
-                        f.omit_verbose_types,
-                        f.display_target,
-                    );
+                    let ret_display = if f.omit_verbose_types() {
+                        sig.ret().display_truncated(f.db, f.max_size)
+                    } else {
+                        sig.ret().display(f.db)
+                    };
                     write!(f, " -> {}", ret_display)?;
                 } else {
                     write!(f, "{{closure}}")?;
@@ -511,13 +461,7 @@ impl HirDisplay for ProjectionTy {
         }
 
         let trait_ = f.db.trait_data(self.trait_(f.db));
-        let first_parameter = self.parameters[0].into_displayable(
-            f.db,
-            f.max_size,
-            f.omit_verbose_types,
-            f.display_target,
-        );
-        write!(f, "<{} as {}", first_parameter, trait_.name)?;
+        write!(f, "<{} as {}", self.parameters[0].display(f.db), trait_.name)?;
         if self.parameters.len() > 1 {
             write!(f, "<")?;
             f.write_joined(&self.parameters[1..], ", ")?;
@@ -696,10 +640,10 @@ impl HirDisplay for GenericPredicate {
                 projection_pred.projection_ty.trait_ref(f.db).hir_fmt_ext(f, true)?;
                 write!(
                     f,
-                    ">::{} = ",
+                    ">::{} = {}",
                     f.db.type_alias_data(projection_pred.projection_ty.associated_ty).name,
+                    projection_pred.ty.display(f.db)
                 )?;
-                projection_pred.ty.hir_fmt(f)?;
             }
             GenericPredicate::Error => write!(f, "{{error}}")?,
         }
@@ -710,18 +654,13 @@ impl HirDisplay for GenericPredicate {
 impl HirDisplay for Obligation {
     fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
         match self {
-            Obligation::Trait(tr) => {
-                write!(f, "Implements(")?;
-                tr.hir_fmt(f)?;
-                write!(f, ")")
-            }
-            Obligation::Projection(proj) => {
-                write!(f, "Normalize(")?;
-                proj.projection_ty.hir_fmt(f)?;
-                write!(f, " => ")?;
-                proj.ty.hir_fmt(f)?;
-                write!(f, ")")
-            }
+            Obligation::Trait(tr) => write!(f, "Implements({})", tr.display(f.db)),
+            Obligation::Projection(proj) => write!(
+                f,
+                "Normalize({} => {})",
+                proj.projection_ty.display(f.db),
+                proj.ty.display(f.db)
+            ),
         }
     }
 }
diff --git a/crates/hir_ty/src/method_resolution.rs b/crates/hir_ty/src/method_resolution.rs
index 5a6f0c67fb..8961df4049 100644
--- a/crates/hir_ty/src/method_resolution.rs
+++ b/crates/hir_ty/src/method_resolution.rs
@@ -740,19 +740,6 @@ pub fn implements_trait(
     solution.is_some()
 }
 
-pub fn implements_trait_unique(
-    ty: &Canonical<Ty>,
-    db: &dyn HirDatabase,
-    env: Arc<TraitEnvironment>,
-    krate: CrateId,
-    trait_: TraitId,
-) -> bool {
-    let goal = generic_implements_goal(db, env, trait_, ty.clone());
-    let solution = db.trait_solve(krate, goal);
-
-    matches!(solution, Some(crate::traits::Solution::Unique(_)))
-}
-
 /// This creates Substs for a trait with the given Self type and type variables
 /// for all other parameters, to query Chalk with it.
 fn generic_implements_goal(
diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs
index adb93efd7e..49d8e4ae14 100644
--- a/crates/ide/src/inlay_hints.rs
+++ b/crates/ide/src/inlay_hints.rs
@@ -1235,25 +1235,4 @@ fn main() {
 "#,
         );
     }
-
-    #[test]
-    fn infer_call_method_return_associated_types_with_generic() {
-        check(
-            r#"
-            pub trait Default {
-                fn default() -> Self;
-            }
-            pub trait Foo {
-                type Bar: Default;
-            }
-
-            pub fn quux<T: Foo>() -> T::Bar {
-                let y = Default::default();
-                  //^ <T as Foo>::Bar
-
-                y
-            }
-            "#,
-        );
-    }
 }
diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs
index 2bd0e86e54..eb82456ad8 100644
--- a/crates/ide/src/runnables.rs
+++ b/crates/ide/src/runnables.rs
@@ -102,7 +102,6 @@ pub(crate) fn runnable(
 ) -> Option<Runnable> {
     match_ast! {
         match item {
-            ast::Struct(it) => runnable_struct(sema, it, file_id),
             ast::Fn(it) => runnable_fn(sema, it, file_id),
             ast::Module(it) => runnable_mod(sema, it, file_id),
             _ => None,
@@ -183,43 +182,6 @@ fn runnable_fn(
     Some(Runnable { nav, kind, cfg })
 }
 
-fn runnable_struct(
-    sema: &Semantics<RootDatabase>,
-    struct_def: ast::Struct,
-    file_id: FileId,
-) -> Option<Runnable> {
-    if !has_runnable_doc_test(&struct_def) {
-        return None;
-    }
-    let name_string = struct_def.name()?.text().to_string();
-
-    let attrs =
-        Attrs::from_attrs_owner(sema.db, InFile::new(HirFileId::from(file_id), &struct_def));
-    let cfg = attrs.cfg();
-
-    let test_id = match sema.to_def(&struct_def).map(|def| def.module(sema.db)) {
-        Some(module) => {
-            let path_iter = module
-                .path_to_root(sema.db)
-                .into_iter()
-                .rev()
-                .filter_map(|it| it.name(sema.db))
-                .map(|name| name.to_string());
-            let path = path_iter.chain(std::iter::once(name_string)).join("::");
-
-            TestId::Path(path)
-        }
-        None => TestId::Name(name_string),
-    };
-
-    let nav = NavigationTarget::from_doc_commented(
-        sema.db,
-        InFile::new(file_id.into(), &struct_def),
-        InFile::new(file_id.into(), &struct_def),
-    );
-    Some(Runnable { nav, kind: RunnableKind::DocTest { test_id }, cfg })
-}
-
 #[derive(Debug, Copy, Clone)]
 pub struct TestAttr {
     pub ignore: bool,
@@ -253,8 +215,8 @@ const RUSTDOC_FENCE: &str = "```";
 const RUSTDOC_CODE_BLOCK_ATTRIBUTES_RUNNABLE: &[&str] =
     &["", "rust", "should_panic", "edition2015", "edition2018"];
 
-fn has_runnable_doc_test(def: &dyn DocCommentsOwner) -> bool {
-    def.doc_comment_text().map_or(false, |comments_text| {
+fn has_runnable_doc_test(fn_def: &ast::Fn) -> bool {
+    fn_def.doc_comment_text().map_or(false, |comments_text| {
         let mut in_code_block = false;
 
         for line in comments_text.lines() {
@@ -525,14 +487,8 @@ fn should_have_no_runnable_5() {}
 /// let z = 55;
 /// ```
 fn should_have_no_runnable_6() {}
-
-/// ```
-/// let x = 5;
-/// ```
-struct StructWithRunnable(String);
-
 "#,
-            &[&BIN, &DOCTEST, &DOCTEST, &DOCTEST, &DOCTEST],
+            &[&BIN, &DOCTEST, &DOCTEST, &DOCTEST],
             expect![[r#"
                 [
                     Runnable {
@@ -613,26 +569,6 @@ struct StructWithRunnable(String);
                         },
                         cfg: None,
                     },
-                    Runnable {
-                        nav: NavigationTarget {
-                            file_id: FileId(
-                                0,
-                            ),
-                            full_range: 756..821,
-                            focus_range: None,
-                            name: "StructWithRunnable",
-                            kind: STRUCT,
-                            container_name: None,
-                            description: None,
-                            docs: None,
-                        },
-                        kind: DocTest {
-                            test_id: Path(
-                                "StructWithRunnable",
-                            ),
-                        },
-                        cfg: None,
-                    },
                 ]
             "#]],
         );
diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs
index 624a630756..9f864179e2 100644
--- a/crates/ide/src/syntax_highlighting.rs
+++ b/crates/ide/src/syntax_highlighting.rs
@@ -562,9 +562,6 @@ fn highlight_element(
             T![!] if element.parent().and_then(ast::MacroCall::cast).is_some() => {
                 HighlightTag::Macro.into()
             }
-            T![!] if element.parent().and_then(ast::NeverType::cast).is_some() => {
-                HighlightTag::BuiltinType.into()
-            }
             T![*] if element.parent().and_then(ast::PtrType::cast).is_some() => {
                 HighlightTag::Keyword.into()
             }
diff --git a/docs/dev/README.md b/docs/dev/README.md
index abb387e8ef..90e74f226a 100644
--- a/docs/dev/README.md
+++ b/docs/dev/README.md
@@ -80,7 +80,7 @@ For this, it is important to have the following in your `settings.json` file:
     "rust-analyzer.serverPath": "rust-analyzer"
 }
 ```
-After I am done with the fix, I use `cargo xtask install --client` to try the new extension for real.
+After I am done with the fix, I use `cargo xtask install --client-code` to try the new extension for real.
 
 If I need to fix something in the `rust-analyzer` crate, I feel sad because it's on the boundary between the two processes, and working there is slow.
 I usually just `cargo xtask install --server` and poke changes from my live environment.
diff --git a/editors/code/src/inlay_hints.ts b/editors/code/src/inlay_hints.ts
index 38eb1c15b9..ed0db29245 100644
--- a/editors/code/src/inlay_hints.ts
+++ b/editors/code/src/inlay_hints.ts
@@ -60,8 +60,6 @@ function createHintStyle(hintKind: "type" | "parameter" | "chaining") {
                 color: fg,
                 backgroundColor: bg,
                 fontStyle: "normal",
-                fontWeight: "normal",
-                textDecoration: ";font-size:smaller",
             },
         }),
         toDecoration(hint: ra.InlayHint, conv: lc.Protocol2CodeConverter): vscode.DecorationOptions {


Test patch:
diff --git a/crates/hir_ty/src/tests.rs b/crates/hir_ty/src/tests.rs
index 104ef334c6..0445efc9e0 100644
--- a/crates/hir_ty/src/tests.rs
+++ b/crates/hir_ty/src/tests.rs
@@ -74,7 +74,7 @@ fn check_types_impl(ra_fixture: &str, display_source: bool) {
                 let module = db.module_for_file(file_id);
                 ty.display_source_code(&db, module).unwrap()
             } else {
-                ty.display_test(&db).to_string()
+                ty.display(&db).to_string()
             };
             assert_eq!(expected, actual);
             checked_one = true;
@@ -163,7 +163,7 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String {
                 macro_prefix,
                 range,
                 ellipsize(text, 15),
-                ty.display_test(&db)
+                ty.display(&db)
             );
         }
         if include_mismatches {
@@ -179,8 +179,8 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String {
                     "{}{:?}: expected {}, got {}\n",
                     macro_prefix,
                     range,
-                    mismatch.expected.display_test(&db),
-                    mismatch.actual.display_test(&db),
+                    mismatch.expected.display(&db),
+                    mismatch.actual.display(&db),
                 );
             }
         }
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlighting.html b/crates/ide/src/syntax_highlighting/test_data/highlighting.html
index 5eb222ee2d..c6b4f5a002 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlighting.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlighting.html
@@ -110,21 +110,11 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
     <span class="function">foo</span><span class="operator">::</span><span class="punctuation">&lt;</span><span class="lifetime">'a</span><span class="punctuation">,</span> <span class="builtin_type">i32</span><span class="punctuation">&gt;</span><span class="punctuation">(</span><span class="punctuation">)</span>
 <span class="punctuation">}</span>
 
-<span class="keyword">fn</span> <span class="function declaration">never</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">!</span> <span class="punctuation">{</span>
-    <span class="keyword control">loop</span> <span class="punctuation">{</span><span class="punctuation">}</span>
-<span class="punctuation">}</span>
-
 <span class="keyword">use</span> <span class="module">ops</span><span class="operator">::</span><span class="trait">Fn</span><span class="punctuation">;</span>
 <span class="keyword">fn</span> <span class="function declaration">baz</span><span class="punctuation">&lt;</span><span class="type_param declaration">F</span><span class="punctuation">:</span> <span class="trait">Fn</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="operator">-&gt;</span> <span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">&gt;</span><span class="punctuation">(</span><span class="value_param declaration callable">f</span><span class="punctuation">:</span> <span class="type_param">F</span><span class="punctuation">)</span> <span class="punctuation">{</span>
     <span class="value_param callable">f</span><span class="punctuation">(</span><span class="punctuation">)</span>
 <span class="punctuation">}</span>
 
-<span class="keyword">fn</span> <span class="function declaration">foobar</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="operator">-&gt;</span> <span class="keyword">impl</span> <span class="unresolved_reference">Copy</span> <span class="punctuation">{</span><span class="punctuation">}</span>
-
-<span class="keyword">fn</span> <span class="function declaration">foo</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span>
-    <span class="keyword">let</span> <span class="variable declaration">bar</span> <span class="operator">=</span> <span class="function">foobar</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span>
-<span class="punctuation">}</span>
-
 <span class="macro">macro_rules!</span> <span class="macro declaration">def_fn</span> <span class="punctuation">{</span>
     <span class="punctuation">(</span><span class="punctuation">$</span><span class="punctuation">(</span><span class="punctuation">$</span>tt<span class="punctuation">:</span>tt<span class="punctuation">)</span><span class="punctuation">*</span><span class="punctuation">)</span> <span class="operator">=</span><span class="punctuation">&gt;</span> <span class="punctuation">{</span><span class="punctuation">$</span><span class="punctuation">(</span><span class="punctuation">$</span>tt<span class="punctuation">)</span><span class="punctuation">*</span><span class="punctuation">}</span>
 <span class="punctuation">}</span>
@@ -189,8 +179,6 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 
     <span class="keyword">let</span> <span class="variable declaration">baz</span> <span class="operator">=</span> <span class="numeric_literal">-</span><span class="numeric_literal">42</span><span class="punctuation">;</span>
     <span class="keyword">let</span> <span class="variable declaration">baz</span> <span class="operator">=</span> <span class="operator">-</span><span class="variable">baz</span><span class="punctuation">;</span>
-
-    <span class="keyword">let</span> <span class="punctuation">_</span> <span class="operator">=</span> <span class="operator">!</span><span class="bool_literal">true</span><span class="punctuation">;</span>
 <span class="punctuation">}</span>
 
 <span class="keyword">enum</span> <span class="enum declaration">Option</span><span class="punctuation">&lt;</span><span class="type_param declaration">T</span><span class="punctuation">&gt;</span> <span class="punctuation">{</span>
diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs
index 2b667b0d4a..dd43f9dd95 100644
--- a/crates/ide/src/syntax_highlighting/tests.rs
+++ b/crates/ide/src/syntax_highlighting/tests.rs
@@ -84,21 +84,11 @@ fn foo<'a, T>() -> T {
     foo::<'a, i32>()
 }
 
-fn never() -> ! {
-    loop {}
-}
-
 use ops::Fn;
 fn baz<F: Fn() -> ()>(f: F) {
     f()
 }
 
-fn foobar() -> impl Copy {}
-
-fn foo() {
-    let bar = foobar();
-}
-
 macro_rules! def_fn {
     ($($tt:tt)*) => {$($tt)*}
 }
@@ -163,8 +153,6 @@ fn main() {
 
     let baz = -42;
     let baz = -baz;
-
-    let _ = !true;
 }
 
 enum Option<T> {