Repository: rust-lang/rust-analyzer
Pull request #16856 (2024-03-16 16:50:53)

fix: Fix wrong where clause rendering on hover
We were not accounting for proper newline indentation in some places making the hover look weird (or just straight up wrong for type aliases)


Patch:
diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml
index dc0a6c2d91..ac536d0fdd 100644
--- a/.github/workflows/release.yaml
+++ b/.github/workflows/release.yaml
@@ -36,6 +36,7 @@ jobs:
           - os: ubuntu-20.04
             target: x86_64-unknown-linux-gnu
             code-target: linux-x64
+            container: ubuntu:18.04
           - os: ubuntu-20.04
             target: aarch64-unknown-linux-gnu
             code-target: linux-arm64
@@ -62,6 +63,14 @@ jobs:
         with:
           fetch-depth: ${{ env.FETCH_DEPTH }}
 
+      - name: Install toolchain dependencies
+        if: matrix.container == 'ubuntu:18.04'
+        shell: bash
+        run: |
+          apt-get update && apt-get install -y build-essential curl
+          curl --proto '=https' --tlsv1.2 --retry 10 --retry-connrefused -fsSL "https://sh.rustup.rs" | sh -s -- --profile minimal --default-toolchain none -y
+          echo "${CARGO_HOME:-$HOME/.cargo}/bin" >> $GITHUB_PATH
+
       - name: Install Rust toolchain
         run: |
           rustup update --no-self-update stable
diff --git a/crates/hir/src/display.rs b/crates/hir/src/display.rs
index cdc0db8653..c5d44c11f2 100644
--- a/crates/hir/src/display.rs
+++ b/crates/hir/src/display.rs
@@ -159,6 +159,7 @@ impl HirDisplay for Adt {
 impl HirDisplay for Struct {
     fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
         let module_id = self.module(f.db).id;
+        // FIXME: Render repr if its set explicitly?
         write_visibility(module_id, self.visibility(f.db), f)?;
         f.write_str("struct ")?;
         write!(f, "{}", self.name(f.db).display(f.db.upcast()))?;
@@ -166,37 +167,40 @@ impl HirDisplay for Struct {
         write_generic_params(def_id, f)?;
 
         let variant_data = self.variant_data(f.db);
-        if let StructKind::Tuple = variant_data.kind() {
-            f.write_char('(')?;
-            let mut it = variant_data.fields().iter().peekable();
-
-            while let Some((id, _)) = it.next() {
-                let field = Field { parent: (*self).into(), id };
-                write_visibility(module_id, field.visibility(f.db), f)?;
-                field.ty(f.db).hir_fmt(f)?;
-                if it.peek().is_some() {
-                    f.write_str(", ")?;
-                }
-            }
-
-            f.write_str(");")?;
-        }
+        match variant_data.kind() {
+            StructKind::Tuple => {
+                f.write_char('(')?;
+                let mut it = variant_data.fields().iter().peekable();
 
-        write_where_clause(def_id, f)?;
+                while let Some((id, _)) = it.next() {
+                    let field = Field { parent: (*self).into(), id };
+                    write_visibility(module_id, field.visibility(f.db), f)?;
+                    field.ty(f.db).hir_fmt(f)?;
+                    if it.peek().is_some() {
+                        f.write_str(", ")?;
+                    }
+                }
 
-        if let StructKind::Record = variant_data.kind() {
-            let fields = self.fields(f.db);
-            if fields.is_empty() {
-                f.write_str(" {}")?;
-            } else {
-                f.write_str(" {\n")?;
-                for field in self.fields(f.db) {
-                    f.write_str("    ")?;
-                    field.hir_fmt(f)?;
-                    f.write_str(",\n")?;
+                f.write_char(')')?;
+                write_where_clause(def_id, f)?;
+            }
+            StructKind::Record => {
+                let has_where_clause = write_where_clause(def_id, f)?;
+                let fields = self.fields(f.db);
+                f.write_char(if !has_where_clause { ' ' } else { '\n' })?;
+                if fields.is_empty() {
+                    f.write_str("{}")?;
+                } else {
+                    f.write_str("{\n")?;
+                    for field in self.fields(f.db) {
+                        f.write_str("    ")?;
+                        field.hir_fmt(f)?;
+                        f.write_str(",\n")?;
+                    }
+                    f.write_str("}")?;
                 }
-                f.write_str("}")?;
             }
+            StructKind::Unit => _ = write_where_clause(def_id, f)?,
         }
 
         Ok(())
@@ -210,11 +214,12 @@ impl HirDisplay for Enum {
         write!(f, "{}", self.name(f.db).display(f.db.upcast()))?;
         let def_id = GenericDefId::AdtId(AdtId::EnumId(self.id));
         write_generic_params(def_id, f)?;
-        write_where_clause(def_id, f)?;
+        let has_where_clause = write_where_clause(def_id, f)?;
 
         let variants = self.variants(f.db);
         if !variants.is_empty() {
-            f.write_str(" {\n")?;
+            f.write_char(if !has_where_clause { ' ' } else { '\n' })?;
+            f.write_str("{\n")?;
             for variant in variants {
                 f.write_str("    ")?;
                 variant.hir_fmt(f)?;
@@ -234,11 +239,12 @@ impl HirDisplay for Union {
         write!(f, "{}", self.name(f.db).display(f.db.upcast()))?;
         let def_id = GenericDefId::AdtId(AdtId::UnionId(self.id));
         write_generic_params(def_id, f)?;
-        write_where_clause(def_id, f)?;
+        let has_where_clause = write_where_clause(def_id, f)?;
 
         let fields = self.fields(f.db);
         if !fields.is_empty() {
-            f.write_str(" {\n")?;
+            f.write_char(if !has_where_clause { ' ' } else { '\n' })?;
+            f.write_str("{\n")?;
             for field in self.fields(f.db) {
                 f.write_str("    ")?;
                 field.hir_fmt(f)?;
@@ -446,7 +452,10 @@ fn write_generic_params(
     Ok(())
 }
 
-fn write_where_clause(def: GenericDefId, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
+fn write_where_clause(
+    def: GenericDefId,
+    f: &mut HirFormatter<'_>,
+) -> Result<bool, HirDisplayError> {
     let params = f.db.generic_params(def);
 
     // unnamed type targets are displayed inline with the argument itself, e.g. `f: impl Y`.
@@ -465,7 +474,7 @@ fn write_where_clause(def: GenericDefId, f: &mut HirFormatter<'_>) -> Result<(),
         });
 
     if !has_displayable_predicate {
-        return Ok(());
+        return Ok(false);
     }
 
     let write_target = |target: &WherePredicateTypeTarget, f: &mut HirFormatter<'_>| match target {
@@ -543,7 +552,7 @@ fn write_where_clause(def: GenericDefId, f: &mut HirFormatter<'_>) -> Result<(),
     // End of final predicate. There must be at least one predicate here.
     f.write_char(',')?;
 
-    Ok(())
+    Ok(true)
 }
 
 impl HirDisplay for Const {
@@ -594,19 +603,20 @@ impl HirDisplay for Trait {
         write!(f, "trait {}", data.name.display(f.db.upcast()))?;
         let def_id = GenericDefId::TraitId(self.id);
         write_generic_params(def_id, f)?;
-        write_where_clause(def_id, f)?;
+        let has_where_clause = write_where_clause(def_id, f)?;
 
         if let Some(limit) = f.entity_limit {
             let assoc_items = self.items(f.db);
             let count = assoc_items.len().min(limit);
+            f.write_char(if !has_where_clause { ' ' } else { '\n' })?;
             if count == 0 {
                 if assoc_items.is_empty() {
-                    f.write_str(" {}")?;
+                    f.write_str("{}")?;
                 } else {
-                    f.write_str(" { /* … */ }")?;
+                    f.write_str("{ /* … */ }")?;
                 }
             } else {
-                f.write_str(" {\n")?;
+                f.write_str("{\n")?;
                 for item in &assoc_items[..count] {
                     f.write_str("    ")?;
                     match item {
@@ -651,7 +661,6 @@ impl HirDisplay for TypeAlias {
         write!(f, "type {}", data.name.display(f.db.upcast()))?;
         let def_id = GenericDefId::TypeAliasId(self.id);
         write_generic_params(def_id, f)?;
-        write_where_clause(def_id, f)?;
         if !data.bounds.is_empty() {
             f.write_str(": ")?;
             f.write_joined(data.bounds.iter(), " + ")?;
@@ -660,6 +669,7 @@ impl HirDisplay for TypeAlias {
             f.write_str(" = ")?;
             ty.hir_fmt(f)?;
         }
+        write_where_clause(def_id, f)?;
         Ok(())
     }
 }
diff --git a/crates/rust-analyzer/src/lsp/ext.rs b/crates/rust-analyzer/src/lsp/ext.rs
index 710ce7f8ac..86ab652f8e 100644
--- a/crates/rust-analyzer/src/lsp/ext.rs
+++ b/crates/rust-analyzer/src/lsp/ext.rs
@@ -234,13 +234,6 @@ impl Notification for EndRunTest {
     const METHOD: &'static str = "experimental/endRunTest";
 }
 
-pub enum AppendOutputToRunTest {}
-
-impl Notification for AppendOutputToRunTest {
-    type Params = String;
-    const METHOD: &'static str = "experimental/appendOutputToRunTest";
-}
-
 pub enum AbortRunTest {}
 
 impl Notification for AbortRunTest {
diff --git a/crates/rust-analyzer/src/lsp/to_proto.rs b/crates/rust-analyzer/src/lsp/to_proto.rs
index e77d0c13bf..0423b2f4da 100644
--- a/crates/rust-analyzer/src/lsp/to_proto.rs
+++ b/crates/rust-analyzer/src/lsp/to_proto.rs
@@ -1519,28 +1519,13 @@ pub(crate) fn test_item(
         id: test_item.id,
         label: test_item.label,
         kind: match test_item.kind {
-            ide::TestItemKind::Crate(id) => 'b: {
-                let Some((cargo_ws, target)) = snap.cargo_target_for_crate_root(id) else {
-                    break 'b lsp_ext::TestItemKind::Package;
-                };
-                let target = &cargo_ws[target];
-                match target.kind {
-                    project_model::TargetKind::Bin
-                    | project_model::TargetKind::Lib { .. }
-                    | project_model::TargetKind::Example
-                    | project_model::TargetKind::BuildScript
-                    | project_model::TargetKind::Other => lsp_ext::TestItemKind::Package,
-                    project_model::TargetKind::Test | project_model::TargetKind::Bench => {
-                        lsp_ext::TestItemKind::Test
-                    }
-                }
-            }
+            ide::TestItemKind::Crate => lsp_ext::TestItemKind::Package,
             ide::TestItemKind::Module => lsp_ext::TestItemKind::Module,
             ide::TestItemKind::Function => lsp_ext::TestItemKind::Test,
         },
         can_resolve_children: matches!(
             test_item.kind,
-            ide::TestItemKind::Crate(_) | ide::TestItemKind::Module
+            ide::TestItemKind::Crate | ide::TestItemKind::Module
         ),
         parent: test_item.parent,
         text_document: test_item
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index ffe56e4143..e106a85c6e 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -799,9 +799,6 @@ impl GlobalState {
                 self.send_notification::<lsp_ext::EndRunTest>(());
                 self.test_run_session = None;
             }
-            flycheck::CargoTestMessage::Custom { text } => {
-                self.send_notification::<lsp_ext::AppendOutputToRunTest>(text);
-            }
         }
     }
 
diff --git a/docs/dev/lsp-extensions.md b/docs/dev/lsp-extensions.md
index cf9ad5fe04..af5b4e51ef 100644
--- a/docs/dev/lsp-extensions.md
+++ b/docs/dev/lsp-extensions.md
@@ -1,5 +1,5 @@
 <!---
-lsp/ext.rs hash: 61f485497d6e8e88
+lsp/ext.rs hash: 6bc140531b403717
 
 If you need to change the above hash to make the test pass, please check if you
 need to adjust this doc as well and ping this issue:
@@ -509,13 +509,6 @@ interface ChangeTestStateParams {
 }
 ```
 
-**Method:** `experimental/appendOutputToRunTest`
-
-**Notification:** `string`
-
-This notification is used for reporting messages independent of any single test and related to the run session
-in general, e.g. cargo compiling progress messages or warnings.
-
 ## Open External Documentation
 
 This request is sent from the client to the server to obtain web and local URL(s) for documentation related to the symbol under the cursor, if available.
diff --git a/editors/code/package-lock.json b/editors/code/package-lock.json
index bd8b0e9c4e..291cef926f 100644
--- a/editors/code/package-lock.json
+++ b/editors/code/package-lock.json
@@ -2290,9 +2290,9 @@
             "dev": true
         },
         "node_modules/follow-redirects": {
-            "version": "1.15.6",
-            "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz",
-            "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==",
+            "version": "1.15.4",
+            "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.4.tgz",
+            "integrity": "sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw==",
             "dev": true,
             "funding": [
                 {
diff --git a/editors/code/src/lsp_ext.ts b/editors/code/src/lsp_ext.ts
index ca8106371b..31ac3d9413 100644
--- a/editors/code/src/lsp_ext.ts
+++ b/editors/code/src/lsp_ext.ts
@@ -100,9 +100,6 @@ export const discoveredTests = new lc.NotificationType<DiscoverTestResults>(
 export const runTest = new lc.RequestType<RunTestParams, void, void>("experimental/runTest");
 export const abortRunTest = new lc.NotificationType0("experimental/abortRunTest");
 export const endRunTest = new lc.NotificationType0("experimental/endRunTest");
-export const appendOutputToRunTest = new lc.NotificationType<string>(
-    "experimental/appendOutputToRunTest",
-);
 export const changeTestState = new lc.NotificationType<ChangeTestStateParams>(
     "experimental/changeTestState",
 );


Test patch:
diff --git a/crates/flycheck/src/test_runner.rs b/crates/flycheck/src/test_runner.rs
index 31378716b3..6dac5899ee 100644
--- a/crates/flycheck/src/test_runner.rs
+++ b/crates/flycheck/src/test_runner.rs
@@ -28,20 +28,19 @@ pub enum CargoTestMessage {
     },
     Suite,
     Finished,
-    Custom {
-        text: String,
-    },
 }
 
 impl ParseFromLine for CargoTestMessage {
-    fn from_line(line: &str, _: &mut String) -> Option<Self> {
+    fn from_line(line: &str, error: &mut String) -> Option<Self> {
         let mut deserializer = serde_json::Deserializer::from_str(line);
         deserializer.disable_recursion_limit();
         if let Ok(message) = CargoTestMessage::deserialize(&mut deserializer) {
             return Some(message);
         }
 
-        Some(CargoTestMessage::Custom { text: line.to_owned() })
+        error.push_str(line);
+        error.push('\n');
+        None
     }
 
     fn from_eof() -> Option<Self> {
diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs
index 051a96233a..4451e31870 100644
--- a/crates/ide/src/hover/tests.rs
+++ b/crates/ide/src/hover/tests.rs
@@ -819,7 +819,7 @@ fn foo(foo: Foo) {
 fn hover_tuple_struct() {
     check(
         r#"
-struct Foo$0(pub u32)
+struct Foo$0(pub u32) where u32: Copy;
 "#,
         expect![[r#"
             *Foo*
@@ -830,7 +830,99 @@ struct Foo$0(pub u32)
 
             ```rust
             // size = 4, align = 4
-            struct Foo(pub u32);
+            struct Foo(pub u32)
+            where
+                u32: Copy,
+            ```
+        "#]],
+    );
+}
+
+#[test]
+fn hover_record_struct() {
+    check(
+        r#"
+struct Foo$0 { field: u32 }
+"#,
+        expect![[r#"
+            *Foo*
+
+            ```rust
+            test
+            ```
+
+            ```rust
+            // size = 4, align = 4
+            struct Foo {
+                field: u32,
+            }
+            ```
+        "#]],
+    );
+    check(
+        r#"
+struct Foo$0 where u32: Copy { field: u32 }
+"#,
+        expect![[r#"
+            *Foo*
+
+            ```rust
+            test
+            ```
+
+            ```rust
+            // size = 4, align = 4
+            struct Foo
+            where
+                u32: Copy,
+            {
+                field: u32,
+            }
+            ```
+        "#]],
+    );
+}
+
+#[test]
+fn hover_unit_struct() {
+    check(
+        r#"
+struct Foo$0 where u32: Copy;
+"#,
+        expect![[r#"
+            *Foo*
+
+            ```rust
+            test
+            ```
+
+            ```rust
+            // size = 0, align = 1
+            struct Foo
+            where
+                u32: Copy,
+            ```
+        "#]],
+    );
+}
+
+#[test]
+fn hover_type_alias() {
+    check(
+        r#"
+type Fo$0o: Trait = S where T: Trait;
+"#,
+        expect![[r#"
+            *Foo*
+
+            ```rust
+            test
+            ```
+
+            ```rust
+            type Foo: Trait = S
+            where
+                T: Trait,
             ```
         "#]],
     );
@@ -2540,7 +2632,7 @@ fn main() { let s$0t = S{ f1:Arg(0) }; }
                                 focus_range: 7..10,
                                 name: "Arg",
                                 kind: Struct,
-                                description: "struct Arg(u32);",
+                                description: "struct Arg(u32)",
                             },
                         },
                         HoverGotoTypeData {
@@ -2599,7 +2691,7 @@ fn main() { let s$0t = S{ f1: S{ f1: Arg(0) } }; }
                                 focus_range: 7..10,
                                 name: "Arg",
                                 kind: Struct,
-                                description: "struct Arg(u32);",
+                                description: "struct Arg(u32)",
                             },
                         },
                         HoverGotoTypeData {
@@ -2648,7 +2740,7 @@ fn main() { let s$0t = (A(1), B(2), M::C(3) ); }
                                 focus_range: 7..8,
                                 name: "A",
                                 kind: Struct,
-                                description: "struct A(u32);",
+                                description: "struct A(u32)",
                             },
                         },
                         HoverGotoTypeData {
@@ -2661,7 +2753,7 @@ fn main() { let s$0t = (A(1), B(2), M::C(3) ); }
                                 focus_range: 22..23,
                                 name: "B",
                                 kind: Struct,
-                                description: "struct B(u32);",
+                                description: "struct B(u32)",
                             },
                         },
                         HoverGotoTypeData {
@@ -2675,7 +2767,7 @@ fn main() { let s$0t = (A(1), B(2), M::C(3) ); }
                                 name: "C",
                                 kind: Struct,
                                 container_name: "M",
-                                description: "pub struct C(u32);",
+                                description: "pub struct C(u32)",
                             },
                         },
                     ],
@@ -3331,26 +3423,26 @@ struct Foo<const BAR: Bar>;
 impl<const BAR: Bar> Foo<BAR$0> {}
 "#,
         expect![[r#"
-                [
-                    GoToType(
-                        [
-                            HoverGotoTypeData {
-                                mod_path: "test::Bar",
-                                nav: NavigationTarget {
-                                    file_id: FileId(
-                                        0,
-                                    ),
-                                    full_range: 0..11,
-                                    focus_range: 7..10,
-                                    name: "Bar",
-                                    kind: Struct,
-                                    description: "struct Bar",
-                                },
+            [
+                GoToType(
+                    [
+                        HoverGotoTypeData {
+                            mod_path: "test::Bar",
+                            nav: NavigationTarget {
+                                file_id: FileId(
+                                    0,
+                                ),
+                                full_range: 0..11,
+                                focus_range: 7..10,
+                                name: "Bar",
+                                kind: Struct,
+                                description: "struct Bar",
                             },
-                        ],
-                    ),
-                ]
-            "#]],
+                        },
+                    ],
+                ),
+            ]
+        "#]],
     );
 }
 
@@ -3396,26 +3488,26 @@ impl Foo {
 }
 "#,
         expect![[r#"
-                [
-                    GoToType(
-                        [
-                            HoverGotoTypeData {
-                                mod_path: "test::Foo",
-                                nav: NavigationTarget {
-                                    file_id: FileId(
-                                        0,
-                                    ),
-                                    full_range: 0..11,
-                                    focus_range: 7..10,
-                                    name: "Foo",
-                                    kind: Struct,
-                                    description: "struct Foo",
-                                },
+            [
+                GoToType(
+                    [
+                        HoverGotoTypeData {
+                            mod_path: "test::Foo",
+                            nav: NavigationTarget {
+                                file_id: FileId(
+                                    0,
+                                ),
+                                full_range: 0..11,
+                                focus_range: 7..10,
+                                name: "Foo",
+                                kind: Struct,
+                                description: "struct Foo",
                             },
-                        ],
-                    ),
-                ]
-            "#]],
+                        },
+                    ],
+                ),
+            ]
+        "#]],
     );
 }
 
@@ -3498,7 +3590,7 @@ struct S$0T<const C: usize = 1, T = Foo>(T);
             ```
 
             ```rust
-            struct ST<const C: usize = 1, T = Foo>(T);
+            struct ST<const C: usize = 1, T = Foo>(T)
             ```
         "#]],
     );
@@ -3519,7 +3611,7 @@ struct S$0T<const C: usize = {40 + 2}, T = Foo>(T);
             ```
 
             ```rust
-            struct ST<const C: usize = {const}, T = Foo>(T);
+            struct ST<const C: usize = {const}, T = Foo>(T)
             ```
         "#]],
     );
@@ -3541,7 +3633,7 @@ struct S$0T<const C: usize = VAL, T = Foo>(T);
             ```
 
             ```rust
-            struct ST<const C: usize = VAL, T = Foo>(T);
+            struct ST<const C: usize = VAL, T = Foo>(T)
             ```
         "#]],
     );
@@ -5931,26 +6023,26 @@ fn foo() {
 }
 "#,
         expect![[r#"
-                [
-                    GoToType(
-                        [
-                            HoverGotoTypeData {
-                                mod_path: "test::Foo",
-                                nav: NavigationTarget {
-                                    file_id: FileId(
-                                        0,
-                                    ),
-                                    full_range: 0..11,
-                                    focus_range: 7..10,
-                                    name: "Foo",
-                                    kind: Struct,
-                                    description: "struct Foo",
-                                },
+            [
+                GoToType(
+                    [
+                        HoverGotoTypeData {
+                            mod_path: "test::Foo",
+                            nav: NavigationTarget {
+                                file_id: FileId(
+                                    0,
+                                ),
+                                full_range: 0..11,
+                                focus_range: 7..10,
+                                name: "Foo",
+                                kind: Struct,
+                                description: "struct Foo",
                             },
-                        ],
-                    ),
-                ]
-            "#]],
+                        },
+                    ],
+                ),
+            ]
+        "#]],
     );
 }
 
@@ -6166,7 +6258,7 @@ pub struct Foo(i32);
 
             ```rust
             // size = 4, align = 4
-            pub struct Foo(i32);
+            pub struct Foo(i32)
             ```
 
             ---
@@ -6191,7 +6283,7 @@ pub struct Foo<T>(T);
             ```
 
             ```rust
-            pub struct Foo<T>(T);
+            pub struct Foo<T>(T)
             ```
 
             ---
diff --git a/crates/ide/src/test_explorer.rs b/crates/ide/src/test_explorer.rs
index ca47139970..2e741021ea 100644
--- a/crates/ide/src/test_explorer.rs
+++ b/crates/ide/src/test_explorer.rs
@@ -11,7 +11,7 @@ use crate::{navigation_target::ToNav, runnables::runnable_fn, Runnable, TryToNav
 
 #[derive(Debug)]
 pub enum TestItemKind {
-    Crate(CrateId),
+    Crate,
     Module,
     Function,
 }
@@ -32,17 +32,15 @@ pub(crate) fn discover_test_roots(db: &RootDatabase) -> Vec<TestItem> {
     crate_graph
         .iter()
         .filter(|&id| crate_graph[id].origin.is_local())
-        .filter_map(|id| {
-            let test_id = crate_graph[id].display_name.as_ref()?.to_string();
-            Some(TestItem {
-                kind: TestItemKind::Crate(id),
-                label: test_id.clone(),
-                id: test_id,
-                parent: None,
-                file: None,
-                text_range: None,
-                runnable: None,
-            })
+        .filter_map(|id| Some(crate_graph[id].display_name.as_ref()?.to_string()))
+        .map(|id| TestItem {
+            kind: TestItemKind::Crate,
+            label: id.clone(),
+            id,
+            parent: None,
+            file: None,
+            text_range: None,
+            runnable: None,
         })
         .collect()
 }
@@ -120,13 +118,12 @@ pub(crate) fn discover_tests_in_crate(db: &RootDatabase, crate_id: CrateId) -> V
     let Some(crate_test_id) = &crate_graph[crate_id].display_name else {
         return vec![];
     };
-    let kind = TestItemKind::Crate(crate_id);
     let crate_test_id = crate_test_id.to_string();
     let crate_id: Crate = crate_id.into();
     let module = crate_id.root_module();
     let mut r = vec![TestItem {
         id: crate_test_id.clone(),
-        kind,
+        kind: TestItemKind::Crate,
         label: crate_test_id.clone(),
         parent: None,
         file: None,
diff --git a/editors/code/src/test_explorer.ts b/editors/code/src/test_explorer.ts
index ac4ffb1926..2f0b4d5b5c 100644
--- a/editors/code/src/test_explorer.ts
+++ b/editors/code/src/test_explorer.ts
@@ -141,12 +141,6 @@ export const prepareTestExplorer = (
         }),
     );
 
-    ctx.pushClientCleanup(
-        client.onNotification(ra.appendOutputToRunTest, (output) => {
-            currentTestRun!.appendOutput(`${output}\r\n`);
-        }),
-    );
-
     ctx.pushClientCleanup(
         client.onNotification(ra.changeTestState, (results) => {
             const test = idToTestMap.get(results.testId)!;