Repository: rust-lang/rust-analyzer
Pull request #16757 (2024-03-05 10:42:16)

fix: Put style lints behind disabled-by-default config
Fixes https://github.com/rust-lang/rust-analyzer/issues/16542
Fixes https://github.com/rust-lang/rust-analyzer/issues/16725
cc https://github.com/rust-lang/rust-analyzer/issues/16628

Our diagnostic infra is not yet setup for those kinds of diagnostics


Patch:
diff --git a/crates/hir-ty/src/diagnostics/expr.rs b/crates/hir-ty/src/diagnostics/expr.rs
index 1a134e6d78..dfc0a25bf4 100644
--- a/crates/hir-ty/src/diagnostics/expr.rs
+++ b/crates/hir-ty/src/diagnostics/expr.rs
@@ -60,12 +60,17 @@ pub enum BodyValidationDiagnostic {
 }
 
 impl BodyValidationDiagnostic {
-    pub fn collect(db: &dyn HirDatabase, owner: DefWithBodyId) -> Vec<BodyValidationDiagnostic> {
+    pub fn collect(
+        db: &dyn HirDatabase,
+        owner: DefWithBodyId,
+        validate_lints: bool,
+    ) -> Vec<BodyValidationDiagnostic> {
         let _p =
             tracing::span!(tracing::Level::INFO, "BodyValidationDiagnostic::collect").entered();
         let infer = db.infer(owner);
         let body = db.body(owner);
-        let mut validator = ExprValidator { owner, body, infer, diagnostics: Vec::new() };
+        let mut validator =
+            ExprValidator { owner, body, infer, diagnostics: Vec::new(), validate_lints };
         validator.validate_body(db);
         validator.diagnostics
     }
@@ -76,6 +81,7 @@ struct ExprValidator {
     body: Arc<Body>,
     infer: Arc<InferenceResult>,
     diagnostics: Vec<BodyValidationDiagnostic>,
+    validate_lints: bool,
 }
 
 impl ExprValidator {
@@ -139,6 +145,9 @@ impl ExprValidator {
         expr: &Expr,
         filter_map_next_checker: &mut Option<FilterMapNextChecker>,
     ) {
+        if !self.validate_lints {
+            return;
+        }
         // Check that the number of arguments matches the number of parameters.
 
         if self.infer.expr_type_mismatches().next().is_some() {
@@ -308,6 +317,9 @@ impl ExprValidator {
     }
 
     fn check_for_trailing_return(&mut self, body_expr: ExprId, body: &Body) {
+        if !self.validate_lints {
+            return;
+        }
         match &body.exprs[body_expr] {
             Expr::Block { statements, tail, .. } => {
                 let last_stmt = tail.or_else(|| match statements.last()? {
@@ -340,6 +352,9 @@ impl ExprValidator {
     }
 
     fn check_for_unnecessary_else(&mut self, id: ExprId, expr: &Expr, db: &dyn HirDatabase) {
+        if !self.validate_lints {
+            return;
+        }
         if let Expr::If { condition: _, then_branch, else_branch } = expr {
             if else_branch.is_none() {
                 return;
diff --git a/crates/hir-ty/src/display.rs b/crates/hir-ty/src/display.rs
index fe191a456a..328dea87ac 100644
--- a/crates/hir-ty/src/display.rs
+++ b/crates/hir-ty/src/display.rs
@@ -63,7 +63,7 @@ pub struct HirFormatter<'a> {
     buf: String,
     curr_size: usize,
     pub(crate) max_size: Option<usize>,
-    pub entity_limit: Option<usize>,
+    pub limited_size: Option<usize>,
     omit_verbose_types: bool,
     closure_style: ClosureStyle,
     display_target: DisplayTarget,
@@ -148,8 +148,8 @@ pub trait HirDisplay {
         }
     }
 
-    /// Returns a `Display`able type that is human-readable and tries to limit the number of items inside.
-    /// Use this for showing definitions which may contain too many items, like `trait`, `struct`, `enum`
+    ///  Returns a `Display`able type that is human-readable and tries to limit the item inside this type.
+    /// Use this for showing types which may contain two many item when user hover on, like `trait`, `struct`, `enum`
     fn display_limited<'a>(
         &'a self,
         db: &'a dyn HirDatabase,
@@ -184,7 +184,7 @@ pub trait HirDisplay {
             buf: String::with_capacity(20),
             curr_size: 0,
             max_size: None,
-            entity_limit: None,
+            limited_size: None,
             omit_verbose_types: false,
             closure_style: ClosureStyle::ImplFn,
             display_target: DisplayTarget::SourceCode { module_id, allow_opaque },
@@ -352,7 +352,7 @@ impl<T: HirDisplay> HirDisplayWrapper<'_, T> {
             buf: String::with_capacity(20),
             curr_size: 0,
             max_size: self.max_size,
-            entity_limit: self.limited_size,
+            limited_size: self.limited_size,
             omit_verbose_types: self.omit_verbose_types,
             display_target: self.display_target,
             closure_style: self.closure_style,
diff --git a/crates/hir/src/display.rs b/crates/hir/src/display.rs
index cdc0db8653..20ba72d998 100644
--- a/crates/hir/src/display.rs
+++ b/crates/hir/src/display.rs
@@ -596,32 +596,33 @@ impl HirDisplay for Trait {
         write_generic_params(def_id, f)?;
         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);
-            if count == 0 {
-                if assoc_items.is_empty() {
-                    f.write_str(" {}")?;
-                } else {
-                    f.write_str(" { /* … */ }")?;
-                }
-            } else {
-                f.write_str(" {\n")?;
-                for item in &assoc_items[..count] {
-                    f.write_str("    ")?;
-                    match item {
-                        AssocItem::Function(func) => func.hir_fmt(f),
-                        AssocItem::Const(cst) => cst.hir_fmt(f),
-                        AssocItem::TypeAlias(type_alias) => type_alias.hir_fmt(f),
-                    }?;
-                    f.write_str(";\n")?;
-                }
-
-                if assoc_items.len() > count {
-                    f.write_str("    /* … */\n")?;
+        let assoc_items = self.items(f.db);
+        let assoc_items_size = assoc_items.len();
+        let limited_size = f.limited_size.unwrap_or(assoc_items_size);
+        if assoc_items.is_empty() {
+            f.write_str(" {}")?;
+        } else {
+            f.write_str(" {\n")?;
+            for (index, item) in assoc_items.iter().enumerate() {
+                f.write_str("    ")?;
+                match item {
+                    AssocItem::Function(func) => {
+                        func.hir_fmt(f)?;
+                    }
+                    AssocItem::Const(cst) => {
+                        cst.hir_fmt(f)?;
+                    }
+                    AssocItem::TypeAlias(type_alias) => {
+                        type_alias.hir_fmt(f)?;
+                    }
+                };
+                f.write_str(",\n")?;
+                if index + 1 == limited_size && index + 1 != assoc_items_size {
+                    f.write_str("    ...\n")?;
+                    break;
                 }
-                f.write_str("}")?;
             }
+            f.write_str("}")?;
         }
 
         Ok(())
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index 1e16f78ca2..4872c47c31 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -365,7 +365,7 @@ impl ModuleDef {
         Some(name)
     }
 
-    pub fn diagnostics(self, db: &dyn HirDatabase) -> Vec<AnyDiagnostic> {
+    pub fn diagnostics(self, db: &dyn HirDatabase, style_lints: bool) -> Vec<AnyDiagnostic> {
         let id = match self {
             ModuleDef::Adt(it) => match it {
                 Adt::Struct(it) => it.id.into(),
@@ -387,7 +387,7 @@ impl ModuleDef {
 
         match self.as_def_with_body() {
             Some(def) => {
-                def.diagnostics(db, &mut acc);
+                def.diagnostics(db, &mut acc, style_lints);
             }
             None => {
                 for diag in hir_ty::diagnostics::incorrect_case(db, id) {
@@ -541,7 +541,12 @@ impl Module {
     }
 
     /// Fills `acc` with the module's diagnostics.
-    pub fn diagnostics(self, db: &dyn HirDatabase, acc: &mut Vec<AnyDiagnostic>) {
+    pub fn diagnostics(
+        self,
+        db: &dyn HirDatabase,
+        acc: &mut Vec<AnyDiagnostic>,
+        style_lints: bool,
+    ) {
         let name = self.name(db);
         let _p = tracing::span!(tracing::Level::INFO, "Module::diagnostics", ?name);
         let def_map = self.id.def_map(db.upcast());
@@ -558,9 +563,9 @@ impl Module {
                 ModuleDef::Module(m) => {
                     // Only add diagnostics from inline modules
                     if def_map[m.id.local_id].origin.is_inline() {
-                        m.diagnostics(db, acc)
+                        m.diagnostics(db, acc, style_lints)
                     }
-                    acc.extend(def.diagnostics(db))
+                    acc.extend(def.diagnostics(db, style_lints))
                 }
                 ModuleDef::Trait(t) => {
                     for diag in db.trait_data_with_diagnostics(t.id).1.iter() {
@@ -568,10 +573,10 @@ impl Module {
                     }
 
                     for item in t.items(db) {
-                        item.diagnostics(db, acc);
+                        item.diagnostics(db, acc, style_lints);
                     }
 
-                    acc.extend(def.diagnostics(db))
+                    acc.extend(def.diagnostics(db, style_lints))
                 }
                 ModuleDef::Adt(adt) => {
                     match adt {
@@ -587,17 +592,17 @@ impl Module {
                         }
                         Adt::Enum(e) => {
                             for v in e.variants(db) {
-                                acc.extend(ModuleDef::Variant(v).diagnostics(db));
+                                acc.extend(ModuleDef::Variant(v).diagnostics(db, style_lints));
                                 for diag in db.enum_variant_data_with_diagnostics(v.id).1.iter() {
                                     emit_def_diagnostic(db, acc, diag);
                                 }
                             }
                         }
                     }
-                    acc.extend(def.diagnostics(db))
+                    acc.extend(def.diagnostics(db, style_lints))
                 }
                 ModuleDef::Macro(m) => emit_macro_def_diagnostics(db, acc, m),
-                _ => acc.extend(def.diagnostics(db)),
+                _ => acc.extend(def.diagnostics(db, style_lints)),
             }
         }
         self.legacy_macros(db).into_iter().for_each(|m| emit_macro_def_diagnostics(db, acc, m));
@@ -738,7 +743,7 @@ impl Module {
             }
 
             for &item in &db.impl_data(impl_def.id).items {
-                AssocItem::from(item).diagnostics(db, acc);
+                AssocItem::from(item).diagnostics(db, acc, style_lints);
             }
         }
     }
@@ -1616,14 +1621,19 @@ impl DefWithBody {
         }
     }
 
-    pub fn diagnostics(self, db: &dyn HirDatabase, acc: &mut Vec<AnyDiagnostic>) {
+    pub fn diagnostics(
+        self,
+        db: &dyn HirDatabase,
+        acc: &mut Vec<AnyDiagnostic>,
+        style_lints: bool,
+    ) {
         db.unwind_if_cancelled();
         let krate = self.module(db).id.krate();
 
         let (body, source_map) = db.body_with_source_map(self.into());
 
         for (_, def_map) in body.blocks(db.upcast()) {
-            Module { id: def_map.module_id(DefMap::ROOT) }.diagnostics(db, acc);
+            Module { id: def_map.module_id(DefMap::ROOT) }.diagnostics(db, acc, style_lints);
         }
 
         for diag in source_map.diagnostics() {
@@ -1784,7 +1794,7 @@ impl DefWithBody {
             }
         }
 
-        for diagnostic in BodyValidationDiagnostic::collect(db, self.into()) {
+        for diagnostic in BodyValidationDiagnostic::collect(db, self.into(), style_lints) {
             acc.extend(AnyDiagnostic::body_validation_diagnostic(db, diagnostic, &source_map));
         }
 
@@ -2897,13 +2907,18 @@ impl AssocItem {
         }
     }
 
-    pub fn diagnostics(self, db: &dyn HirDatabase, acc: &mut Vec<AnyDiagnostic>) {
+    pub fn diagnostics(
+        self,
+        db: &dyn HirDatabase,
+        acc: &mut Vec<AnyDiagnostic>,
+        style_lints: bool,
+    ) {
         match self {
             AssocItem::Function(func) => {
-                DefWithBody::from(func).diagnostics(db, acc);
+                DefWithBody::from(func).diagnostics(db, acc, style_lints);
             }
             AssocItem::Const(const_) => {
-                DefWithBody::from(const_).diagnostics(db, acc);
+                DefWithBody::from(const_).diagnostics(db, acc, style_lints);
             }
             AssocItem::TypeAlias(type_alias) => {
                 for diag in hir_ty::diagnostics::incorrect_case(db, type_alias.id.into()) {
diff --git a/crates/ide-diagnostics/src/lib.rs b/crates/ide-diagnostics/src/lib.rs
index 9a7e1a2ae6..0df6f0e037 100644
--- a/crates/ide-diagnostics/src/lib.rs
+++ b/crates/ide-diagnostics/src/lib.rs
@@ -227,6 +227,7 @@ pub struct DiagnosticsConfig {
     pub disable_experimental: bool,
     pub disabled: FxHashSet<String>,
     pub expr_fill_default: ExprFillDefaultMode,
+    pub style_lints: bool,
     // FIXME: We may want to include a whole `AssistConfig` here
     pub insert_use: InsertUseConfig,
     pub prefer_no_std: bool,
@@ -245,6 +246,7 @@ impl DiagnosticsConfig {
             disable_experimental: Default::default(),
             disabled: Default::default(),
             expr_fill_default: Default::default(),
+            style_lints: true,
             insert_use: InsertUseConfig {
                 granularity: ImportGranularity::Preserve,
                 enforce_granularity: false,
@@ -324,7 +326,7 @@ pub fn diagnostics(
 
     let mut diags = Vec::new();
     if let Some(m) = module {
-        m.diagnostics(db, &mut diags);
+        m.diagnostics(db, &mut diags, config.style_lints);
     }
 
     for diag in diags {
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs
index 8f4c629b58..b6877c5e8c 100644
--- a/crates/ide/src/hover.rs
+++ b/crates/ide/src/hover.rs
@@ -32,7 +32,7 @@ pub struct HoverConfig {
     pub documentation: bool,
     pub keywords: bool,
     pub format: HoverDocFormat,
-    pub max_trait_assoc_items_count: Option<usize>,
+    pub trait_assoc_items_size: Option<usize>,
 }
 
 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
diff --git a/crates/ide/src/hover/render.rs b/crates/ide/src/hover/render.rs
index d1d039534d..f528bd512c 100644
--- a/crates/ide/src/hover/render.rs
+++ b/crates/ide/src/hover/render.rs
@@ -408,7 +408,7 @@ pub(super) fn definition(
     let mod_path = definition_mod_path(db, &def);
     let label = match def {
         Definition::Trait(trait_) => {
-            trait_.display_limited(db, config.max_trait_assoc_items_count).to_string()
+            trait_.display_limited(db, config.trait_assoc_items_size).to_string()
         }
         _ => def.label(db),
     };
diff --git a/crates/ide/src/static_index.rs b/crates/ide/src/static_index.rs
index fe063081f7..0f19da0ac5 100644
--- a/crates/ide/src/static_index.rs
+++ b/crates/ide/src/static_index.rs
@@ -166,7 +166,7 @@ impl StaticIndex<'_> {
             documentation: true,
             keywords: true,
             format: crate::HoverDocFormat::Markdown,
-            max_trait_assoc_items_count: None,
+            trait_assoc_items_size: None,
         };
         let tokens = tokens.filter(|token| {
             matches!(
diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs
index 7e027c9f92..ef184032bf 100644
--- a/crates/rust-analyzer/src/cli/analysis_stats.rs
+++ b/crates/rust-analyzer/src/cli/analysis_stats.rs
@@ -982,6 +982,7 @@ impl flags::AnalysisStats {
                     },
                     prefer_no_std: false,
                     prefer_prelude: true,
+                    style_lints: false,
                 },
                 ide::AssistResolveStrategy::All,
                 file_id,
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index ad3721a995..906b000829 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -311,6 +311,8 @@ config_data! {
         /// Map of prefixes to be substituted when parsing diagnostic file paths.
         /// This should be the reverse mapping of what is passed to `rustc` as `--remap-path-prefix`.
         diagnostics_remapPrefix: FxHashMap<String, String> = "{}",
+        /// Whether to run additional style lints.
+        diagnostics_styleLints_enable: bool =    "false",
         /// List of warnings that should be displayed with hint severity.
         ///
         /// The warnings will be indicated by faded text or three dots in code
@@ -1162,6 +1164,7 @@ impl Config {
             insert_use: self.insert_use_config(),
             prefer_no_std: self.data.imports_preferNoStd,
             prefer_prelude: self.data.imports_preferPrelude,
+            style_lints: self.data.diagnostics_styleLints_enable,
         }
     }
 
@@ -1682,7 +1685,7 @@ impl Config {
                 }
             },
             keywords: self.data.hover_documentation_keywords_enable,
-            max_trait_assoc_items_count: self.data.hover_show_traitAssocItems,
+            trait_assoc_items_size: self.data.hover_show_traitAssocItems,
         }
     }
 
diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc
index 0fb275961b..5e782b7831 100644
--- a/docs/user/generated_config.adoc
+++ b/docs/user/generated_config.adoc
@@ -386,6 +386,11 @@ have more false positives than usual.
 Map of prefixes to be substituted when parsing diagnostic file paths.
 This should be the reverse mapping of what is passed to `rustc` as `--remap-path-prefix`.
 --
+[[rust-analyzer.diagnostics.styleLints.enable]]rust-analyzer.diagnostics.styleLints.enable (default: `false`)::
++
+--
+Whether to run additional style lints.
+--
 [[rust-analyzer.diagnostics.warningsAsHint]]rust-analyzer.diagnostics.warningsAsHint (default: `[]`)::
 +
 --
diff --git a/editors/code/package.json b/editors/code/package.json
index 3a276409fb..16d3dcbab6 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -948,6 +948,11 @@
                     "default": {},
                     "type": "object"
                 },
+                "rust-analyzer.diagnostics.styleLints.enable": {
+                    "markdownDescription": "Whether to run additional style lints.",
+                    "default": false,
+                    "type": "boolean"
+                },
                 "rust-analyzer.diagnostics.warningsAsHint": {
                     "markdownDescription": "List of warnings that should be displayed with hint severity.\n\nThe warnings will be indicated by faded text or three dots in code\nand will not show up in the `Problems Panel`.",
                     "default": [],


Test patch:
diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs
index c3cd6513dc..cec1375271 100644
--- a/crates/ide/src/hover/tests.rs
+++ b/crates/ide/src/hover/tests.rs
@@ -17,7 +17,7 @@ const HOVER_BASE_CONFIG: HoverConfig = HoverConfig {
     documentation: true,
     format: HoverDocFormat::Markdown,
     keywords: true,
-    max_trait_assoc_items_count: None,
+    trait_assoc_items_size: None,
 };
 
 fn check_hover_no_result(ra_fixture: &str) {
@@ -49,28 +49,6 @@ fn check(ra_fixture: &str, expect: Expect) {
     expect.assert_eq(&actual)
 }
 
-#[track_caller]
-fn check_assoc_count(count: usize, ra_fixture: &str, expect: Expect) {
-    let (analysis, position) = fixture::position(ra_fixture);
-    let hover = analysis
-        .hover(
-            &HoverConfig {
-                links_in_hover: true,
-                max_trait_assoc_items_count: Some(count),
-                ..HOVER_BASE_CONFIG
-            },
-            FileRange { file_id: position.file_id, range: TextRange::empty(position.offset) },
-        )
-        .unwrap()
-        .unwrap();
-
-    let content = analysis.db.file_text(position.file_id);
-    let hovered_element = &content[hover.range];
-
-    let actual = format!("*{hovered_element}*\n{}\n", hover.info.markup);
-    expect.assert_eq(&actual)
-}
-
 fn check_hover_no_links(ra_fixture: &str, expect: Expect) {
     let (analysis, position) = fixture::position(ra_fixture);
     let hover = analysis
@@ -434,7 +412,7 @@ fn main() {
                                 name: "FnOnce",
                                 kind: Trait,
                                 container_name: "function",
-                                description: "pub trait FnOnce<Args>\nwhere\n    Args: Tuple,",
+                                description: "pub trait FnOnce<Args>\nwhere\n    Args: Tuple, {\n    pub type Output,\n    pub extern \"rust-call\" fn call_once(self, args: Args) -> Self::Output,\n}",
                             },
                         },
                     ],
@@ -2695,26 +2673,26 @@ fn foo() -> impl Foo {}
 fn main() { let s$0t = foo(); }
 "#,
         expect![[r#"
-            [
-                GoToType(
-                    [
-                        HoverGotoTypeData {
-                            mod_path: "test::Foo",
-                            nav: NavigationTarget {
-                                file_id: FileId(
-                                    0,
-                                ),
-                                full_range: 0..12,
-                                focus_range: 6..9,
-                                name: "Foo",
-                                kind: Trait,
-                                description: "trait Foo",
+                [
+                    GoToType(
+                        [
+                            HoverGotoTypeData {
+                                mod_path: "test::Foo",
+                                nav: NavigationTarget {
+                                    file_id: FileId(
+                                        0,
+                                    ),
+                                    full_range: 0..12,
+                                    focus_range: 6..9,
+                                    name: "Foo",
+                                    kind: Trait,
+                                    description: "trait Foo {}",
+                                },
                             },
-                        },
-                    ],
-                ),
-            ]
-        "#]],
+                        ],
+                    ),
+                ]
+            "#]],
     );
 }
 
@@ -2729,39 +2707,39 @@ fn foo() -> impl Foo<S> {}
 fn main() { let s$0t = foo(); }
 "#,
         expect![[r#"
-            [
-                GoToType(
-                    [
-                        HoverGotoTypeData {
-                            mod_path: "test::Foo",
-                            nav: NavigationTarget {
-                                file_id: FileId(
-                                    0,
-                                ),
-                                full_range: 0..15,
-                                focus_range: 6..9,
-                                name: "Foo",
-                                kind: Trait,
-                                description: "trait Foo<T>",
+                [
+                    GoToType(
+                        [
+                            HoverGotoTypeData {
+                                mod_path: "test::Foo",
+                                nav: NavigationTarget {
+                                    file_id: FileId(
+                                        0,
+                                    ),
+                                    full_range: 0..15,
+                                    focus_range: 6..9,
+                                    name: "Foo",
+                                    kind: Trait,
+                                    description: "trait Foo<T> {}",
+                                },
                             },
-                        },
-                        HoverGotoTypeData {
-                            mod_path: "test::S",
-                            nav: NavigationTarget {
-                                file_id: FileId(
-                                    0,
-                                ),
-                                full_range: 16..25,
-                                focus_range: 23..24,
-                                name: "S",
-                                kind: Struct,
-                                description: "struct S",
+                            HoverGotoTypeData {
+                                mod_path: "test::S",
+                                nav: NavigationTarget {
+                                    file_id: FileId(
+                                        0,
+                                    ),
+                                    full_range: 16..25,
+                                    focus_range: 23..24,
+                                    name: "S",
+                                    kind: Struct,
+                                    description: "struct S",
+                                },
                             },
-                        },
-                    ],
-                ),
-            ]
-        "#]],
+                        ],
+                    ),
+                ]
+            "#]],
     );
 }
 
@@ -2789,7 +2767,7 @@ fn main() { let s$0t = foo(); }
                                 focus_range: 19..22,
                                 name: "Bar",
                                 kind: Trait,
-                                description: "trait Bar",
+                                description: "trait Bar {}",
                             },
                         },
                         HoverGotoTypeData {
@@ -2802,7 +2780,7 @@ fn main() { let s$0t = foo(); }
                                 focus_range: 6..9,
                                 name: "Foo",
                                 kind: Trait,
-                                description: "trait Foo",
+                                description: "trait Foo {}",
                             },
                         },
                     ],
@@ -2839,7 +2817,7 @@ fn main() { let s$0t = foo(); }
                                 focus_range: 22..25,
                                 name: "Bar",
                                 kind: Trait,
-                                description: "trait Bar<T>",
+                                description: "trait Bar<T> {}",
                             },
                         },
                         HoverGotoTypeData {
@@ -2852,7 +2830,7 @@ fn main() { let s$0t = foo(); }
                                 focus_range: 6..9,
                                 name: "Foo",
                                 kind: Trait,
-                                description: "trait Foo<T>",
+                                description: "trait Foo<T> {}",
                             },
                         },
                         HoverGotoTypeData {
@@ -2896,26 +2874,26 @@ trait Foo {}
 fn foo(ar$0g: &impl Foo) {}
 "#,
         expect![[r#"
-            [
-                GoToType(
-                    [
-                        HoverGotoTypeData {
-                            mod_path: "test::Foo",
-                            nav: NavigationTarget {
-                                file_id: FileId(
-                                    0,
-                                ),
-                                full_range: 0..12,
-                                focus_range: 6..9,
-                                name: "Foo",
-                                kind: Trait,
-                                description: "trait Foo",
+                [
+                    GoToType(
+                        [
+                            HoverGotoTypeData {
+                                mod_path: "test::Foo",
+                                nav: NavigationTarget {
+                                    file_id: FileId(
+                                        0,
+                                    ),
+                                    full_range: 0..12,
+                                    focus_range: 6..9,
+                                    name: "Foo",
+                                    kind: Trait,
+                                    description: "trait Foo {}",
+                                },
                             },
-                        },
-                    ],
-                ),
-            ]
-        "#]],
+                        ],
+                    ),
+                ]
+            "#]],
     );
 }
 
@@ -2943,7 +2921,7 @@ fn foo(ar$0g: &impl Foo + Bar<S>) {}
                                 focus_range: 19..22,
                                 name: "Bar",
                                 kind: Trait,
-                                description: "trait Bar<T>",
+                                description: "trait Bar<T> {}",
                             },
                         },
                         HoverGotoTypeData {
@@ -2956,7 +2934,7 @@ fn foo(ar$0g: &impl Foo + Bar<S>) {}
                                 focus_range: 6..9,
                                 name: "Foo",
                                 kind: Trait,
-                                description: "trait Foo",
+                                description: "trait Foo {}",
                             },
                         },
                         HoverGotoTypeData {
@@ -3011,7 +2989,7 @@ pub mod future {
                                 name: "Future",
                                 kind: Trait,
                                 container_name: "future",
-                                description: "pub trait Future",
+                                description: "pub trait Future {}",
                             },
                         },
                         HoverGotoTypeData {
@@ -3043,39 +3021,39 @@ struct S {}
 fn foo(ar$0g: &impl Foo<S>) {}
 "#,
         expect![[r#"
-            [
-                GoToType(
-                    [
-                        HoverGotoTypeData {
-                            mod_path: "test::Foo",
-                            nav: NavigationTarget {
-                                file_id: FileId(
-                                    0,
-                                ),
-                                full_range: 0..15,
-                                focus_range: 6..9,
-                                name: "Foo",
-                                kind: Trait,
-                                description: "trait Foo<T>",
+                [
+                    GoToType(
+                        [
+                            HoverGotoTypeData {
+                                mod_path: "test::Foo",
+                                nav: NavigationTarget {
+                                    file_id: FileId(
+                                        0,
+                                    ),
+                                    full_range: 0..15,
+                                    focus_range: 6..9,
+                                    name: "Foo",
+                                    kind: Trait,
+                                    description: "trait Foo<T> {}",
+                                },
                             },
-                        },
-                        HoverGotoTypeData {
-                            mod_path: "test::S",
-                            nav: NavigationTarget {
-                                file_id: FileId(
-                                    0,
-                                ),
-                                full_range: 16..27,
-                                focus_range: 23..24,
-                                name: "S",
-                                kind: Struct,
-                                description: "struct S {}",
+                            HoverGotoTypeData {
+                                mod_path: "test::S",
+                                nav: NavigationTarget {
+                                    file_id: FileId(
+                                        0,
+                                    ),
+                                    full_range: 16..27,
+                                    focus_range: 23..24,
+                                    name: "S",
+                                    kind: Struct,
+                                    description: "struct S {}",
+                                },
                             },
-                        },
-                    ],
-                ),
-            ]
-        "#]],
+                        ],
+                    ),
+                ]
+            "#]],
     );
 }
 
@@ -3093,39 +3071,39 @@ fn foo() -> B<dyn Foo> {}
 fn main() { let s$0t = foo(); }
 "#,
         expect![[r#"
-            [
-                GoToType(
-                    [
-                        HoverGotoTypeData {
-                            mod_path: "test::B",
-                            nav: NavigationTarget {
-                                file_id: FileId(
-                                    0,
-                                ),
-                                full_range: 42..55,
-                                focus_range: 49..50,
-                                name: "B",
-                                kind: Struct,
-                                description: "struct B<T> {}",
+                [
+                    GoToType(
+                        [
+                            HoverGotoTypeData {
+                                mod_path: "test::B",
+                                nav: NavigationTarget {
+                                    file_id: FileId(
+                                        0,
+                                    ),
+                                    full_range: 42..55,
+                                    focus_range: 49..50,
+                                    name: "B",
+                                    kind: Struct,
+                                    description: "struct B<T> {}",
+                                },
                             },
-                        },
-                        HoverGotoTypeData {
-                            mod_path: "test::Foo",
-                            nav: NavigationTarget {
-                                file_id: FileId(
-                                    0,
-                                ),
-                                full_range: 0..12,
-                                focus_range: 6..9,
-                                name: "Foo",
-                                kind: Trait,
-                                description: "trait Foo",
+                            HoverGotoTypeData {
+                                mod_path: "test::Foo",
+                                nav: NavigationTarget {
+                                    file_id: FileId(
+                                        0,
+                                    ),
+                                    full_range: 0..12,
+                                    focus_range: 6..9,
+                                    name: "Foo",
+                                    kind: Trait,
+                                    description: "trait Foo {}",
+                                },
                             },
-                        },
-                    ],
-                ),
-            ]
-        "#]],
+                        ],
+                    ),
+                ]
+            "#]],
     );
 }
 
@@ -3137,26 +3115,26 @@ trait Foo {}
 fn foo(ar$0g: &dyn Foo) {}
 "#,
         expect![[r#"
-            [
-                GoToType(
-                    [
-                        HoverGotoTypeData {
-                            mod_path: "test::Foo",
-                            nav: NavigationTarget {
-                                file_id: FileId(
-                                    0,
-                                ),
-                                full_range: 0..12,
-                                focus_range: 6..9,
-                                name: "Foo",
-                                kind: Trait,
-                                description: "trait Foo",
+                [
+                    GoToType(
+                        [
+                            HoverGotoTypeData {
+                                mod_path: "test::Foo",
+                                nav: NavigationTarget {
+                                    file_id: FileId(
+                                        0,
+                                    ),
+                                    full_range: 0..12,
+                                    focus_range: 6..9,
+                                    name: "Foo",
+                                    kind: Trait,
+                                    description: "trait Foo {}",
+                                },
                             },
-                        },
-                    ],
-                ),
-            ]
-        "#]],
+                        ],
+                    ),
+                ]
+            "#]],
     );
 }
 
@@ -3169,39 +3147,39 @@ struct S {}
 fn foo(ar$0g: &dyn Foo<S>) {}
 "#,
         expect![[r#"
-            [
-                GoToType(
-                    [
-                        HoverGotoTypeData {
-                            mod_path: "test::Foo",
-                            nav: NavigationTarget {
-                                file_id: FileId(
-                                    0,
-                                ),
-                                full_range: 0..15,
-                                focus_range: 6..9,
-                                name: "Foo",
-                                kind: Trait,
-                                description: "trait Foo<T>",
+                [
+                    GoToType(
+                        [
+                            HoverGotoTypeData {
+                                mod_path: "test::Foo",
+                                nav: NavigationTarget {
+                                    file_id: FileId(
+                                        0,
+                                    ),
+                                    full_range: 0..15,
+                                    focus_range: 6..9,
+                                    name: "Foo",
+                                    kind: Trait,
+                                    description: "trait Foo<T> {}",
+                                },
                             },
-                        },
-                        HoverGotoTypeData {
-                            mod_path: "test::S",
-                            nav: NavigationTarget {
-                                file_id: FileId(
-                                    0,
-                                ),
-                                full_range: 16..27,
-                                focus_range: 23..24,
-                                name: "S",
-                                kind: Struct,
-                                description: "struct S {}",
+                            HoverGotoTypeData {
+                                mod_path: "test::S",
+                                nav: NavigationTarget {
+                                    file_id: FileId(
+                                        0,
+                                    ),
+                                    full_range: 16..27,
+                                    focus_range: 23..24,
+                                    name: "S",
+                                    kind: Struct,
+                                    description: "struct S {}",
+                                },
                             },
-                        },
-                    ],
-                ),
-            ]
-        "#]],
+                        ],
+                    ),
+                ]
+            "#]],
     );
 }
 
@@ -3243,7 +3221,7 @@ fn foo(a$0rg: &impl ImplTrait<B<dyn DynTrait<B<S>>>>) {}
                                 focus_range: 28..36,
                                 name: "DynTrait",
                                 kind: Trait,
-                                description: "trait DynTrait<T>",
+                                description: "trait DynTrait<T> {}",
                             },
                         },
                         HoverGotoTypeData {
@@ -3256,7 +3234,7 @@ fn foo(a$0rg: &impl ImplTrait<B<dyn DynTrait<B<S>>>>) {}
                                 focus_range: 6..15,
                                 name: "ImplTrait",
                                 kind: Trait,
-                                description: "trait ImplTrait<T>",
+                                description: "trait ImplTrait<T> {}",
                             },
                         },
                         HoverGotoTypeData {
@@ -3298,26 +3276,26 @@ fn test() -> impl Foo { S {} }
 fn main() { let s$0t = test().get(); }
 "#,
         expect![[r#"
-            [
-                GoToType(
-                    [
-                        HoverGotoTypeData {
-                            mod_path: "test::Foo",
-                            nav: NavigationTarget {
-                                file_id: FileId(
-                                    0,
-                                ),
-                                full_range: 0..62,
-                                focus_range: 6..9,
-                                name: "Foo",
-                                kind: Trait,
-                                description: "trait Foo",
+                [
+                    GoToType(
+                        [
+                            HoverGotoTypeData {
+                                mod_path: "test::Foo",
+                                nav: NavigationTarget {
+                                    file_id: FileId(
+                                        0,
+                                    ),
+                                    full_range: 0..62,
+                                    focus_range: 6..9,
+                                    name: "Foo",
+                                    kind: Trait,
+                                    description: "trait Foo {\n    type Item,\n    fn get(self) -> Self::Item,\n}",
+                                },
                             },
-                        },
-                    ],
-                ),
-            ]
-        "#]],
+                        ],
+                    ),
+                ]
+            "#]],
     );
 }
 
@@ -3363,26 +3341,26 @@ trait Foo {}
 fn foo<T: Foo>(t: T$0){}
 "#,
         expect![[r#"
-            [
-                GoToType(
-                    [
-                        HoverGotoTypeData {
-                            mod_path: "test::Foo",
-                            nav: NavigationTarget {
-                                file_id: FileId(
-                                    0,
-                                ),
-                                full_range: 0..12,
-                                focus_range: 6..9,
-                                name: "Foo",
-                                kind: Trait,
-                                description: "trait Foo",
+                [
+                    GoToType(
+                        [
+                            HoverGotoTypeData {
+                                mod_path: "test::Foo",
+                                nav: NavigationTarget {
+                                    file_id: FileId(
+                                        0,
+                                    ),
+                                    full_range: 0..12,
+                                    focus_range: 6..9,
+                                    name: "Foo",
+                                    kind: Trait,
+                                    description: "trait Foo {}",
+                                },
                             },
-                        },
-                    ],
-                ),
-            ]
-        "#]],
+                        ],
+                    ),
+                ]
+            "#]],
     );
 }
 
@@ -6352,123 +6330,71 @@ impl T for () {
 
 #[test]
 fn hover_trait_show_assoc_items() {
-    check_assoc_count(
-        0,
-        r#"
-trait T {}
-impl T$0 for () {}
-"#,
-        expect![[r#"
-            *T*
-
-            ```rust
-            test
-            ```
-
-            ```rust
-            trait T {}
-            ```
-        "#]],
-    );
-
-    check_assoc_count(
-        1,
+    check(
         r#"
 trait T {}
 impl T$0 for () {}
 "#,
         expect![[r#"
-            *T*
+        *T*
 
-            ```rust
-            test
-            ```
+        ```rust
+        test
+        ```
 
-            ```rust
-            trait T {}
-            ```
+        ```rust
+        trait T {}
+        ```
         "#]],
     );
 
-    check_assoc_count(
-        0,
+    check(
         r#"
 trait T {
     fn func() {}
-    const FLAG: i32 = 34;
-    type Bar;
 }
 impl T$0 for () {}
 "#,
         expect![[r#"
-            *T*
+        *T*
 
-            ```rust
-            test
-            ```
+        ```rust
+        test
+        ```
 
-            ```rust
-            trait T { /* … */ }
-            ```
-        "#]],
-    );
-
-    check_assoc_count(
-        2,
-        r#"
-trait T {
-    fn func() {}
-    const FLAG: i32 = 34;
-    type Bar;
-}
-impl T$0 for () {}
-"#,
-        expect![[r#"
-            *T*
-
-            ```rust
-            test
-            ```
-
-            ```rust
-            trait T {
-                fn func();
-                const FLAG: i32;
-                /* … */
-            }
-            ```
+        ```rust
+        trait T {
+            fn func(),
+        }
+        ```
         "#]],
     );
 
-    check_assoc_count(
-        3,
+    check(
         r#"
 trait T {
     fn func() {}
     const FLAG: i32 = 34;
-    type Bar;
 }
 impl T$0 for () {}
 "#,
         expect![[r#"
-            *T*
+        *T*
 
-            ```rust
-            test
-            ```
+        ```rust
+        test
+        ```
 
-            ```rust
-            trait T {
-                fn func();
-                const FLAG: i32;
-                type Bar;
-            }
-            ```
+        ```rust
+        trait T {
+            fn func(),
+            const FLAG: i32,
+        }
+        ```
         "#]],
     );
 
-    check_assoc_count(
-        4,
+    check(
         r#"
 trait T {
     fn func() {}
@@ -6478,19 +6404,19 @@ trait T {
 impl T$0 for () {}
 "#,
         expect![[r#"
-            *T*
+        *T*
 
-            ```rust
-            test
-            ```
+        ```rust
+        test
+        ```
 
-            ```rust
-            trait T {
-                fn func();
-                const FLAG: i32;
-                type Bar;
-            }
-            ```
+        ```rust
+        trait T {
+            fn func(),
+            const FLAG: i32,
+            type Bar,
+        }
+        ```
         "#]],
     );
 }
@@ -7606,7 +7532,7 @@ impl Iterator for S {
                                 name: "Future",
                                 kind: Trait,
                                 container_name: "future",
-                                description: "pub trait Future",
+                                description: "pub trait Future {\n    pub type Output,\n    pub fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>,\n}",
                             },
                         },
                         HoverGotoTypeData {
@@ -7620,7 +7546,7 @@ impl Iterator for S {
                                 name: "Iterator",
                                 kind: Trait,
                                 container_name: "iterator",
-                                description: "pub trait Iterator",
+                                description: "pub trait Iterator {\n    pub type Item,\n    pub fn next(&mut self) -> Option<Self::Item>,\n    pub fn nth(&mut self, n: usize) -> Option<Self::Item>,\n    pub fn by_ref(&mut self) -> &mut Self\nwhere\n    Self: Sized,,\n}",
                             },
                         },
                         HoverGotoTypeData {
@@ -7633,7 +7559,7 @@ impl Iterator for S {
                                 focus_range: 49..56,
                                 name: "Notable",
                                 kind: Trait,
-                                description: "trait Notable",
+                                description: "trait Notable {}",
                             },
                         },
                         HoverGotoTypeData {