Repository: rust-analyzer/rust-analyzer
Pull request #11086 (2021-12-21 14:23:48)

internal: Simplify completion rendering
Removes all the helper render structs in favor of simple functions, making things a lot easier to oversee(imho)
bors r+


Patch:
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index b57f49690a..526e90bd0b 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -3024,7 +3024,7 @@ impl Callable {
 }
 
 /// For IDE only
-#[derive(Debug, PartialEq, Eq, Hash)]
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
 pub enum ScopeDef {
     ModuleDef(ModuleDef),
     MacroDef(MacroDef),
diff --git a/crates/ide_completion/src/completions.rs b/crates/ide_completion/src/completions.rs
index 8474ebec0d..974264c319 100644
--- a/crates/ide_completion/src/completions.rs
+++ b/crates/ide_completion/src/completions.rs
@@ -84,7 +84,7 @@ impl Completions {
         &mut self,
         ctx: &CompletionContext,
         local_name: hir::Name,
-        resolution: &hir::ScopeDef,
+        resolution: hir::ScopeDef,
     ) {
         if ctx.is_scope_def_hidden(resolution) {
             cov_mark::hit!(qualified_path_doc_hidden);
@@ -115,7 +115,7 @@ impl Completions {
         if !ctx.is_visible(&func) {
             return;
         }
-        self.add_opt(render_fn(RenderContext::new(ctx), None, local_name, func));
+        self.add(render_fn(RenderContext::new(ctx), None, local_name, func));
     }
 
     pub(crate) fn add_method(
@@ -128,7 +128,7 @@ impl Completions {
         if !ctx.is_visible(&func) {
             return;
         }
-        self.add_opt(render_method(RenderContext::new(ctx), None, receiver, local_name, func));
+        self.add(render_method(RenderContext::new(ctx), None, receiver, local_name, func));
     }
 
     pub(crate) fn add_const(&mut self, ctx: &CompletionContext, konst: hir::Const) {
diff --git a/crates/ide_completion/src/completions/lifetime.rs b/crates/ide_completion/src/completions/lifetime.rs
index f530825434..4082414f02 100644
--- a/crates/ide_completion/src/completions/lifetime.rs
+++ b/crates/ide_completion/src/completions/lifetime.rs
@@ -35,7 +35,7 @@ pub(crate) fn complete_lifetime(acc: &mut Completions, ctx: &CompletionContext)
     ctx.scope.process_all_names(&mut |name, res| {
         if let ScopeDef::GenericParam(hir::GenericParam::LifetimeParam(_)) = res {
             if param_lifetime != Some(&*name.to_smol_str()) {
-                acc.add_resolution(ctx, name, &res);
+                acc.add_resolution(ctx, name, res);
             }
         }
     });
@@ -51,7 +51,7 @@ pub(crate) fn complete_label(acc: &mut Completions, ctx: &CompletionContext) {
     }
     ctx.scope.process_all_names(&mut |name, res| {
         if let ScopeDef::Label(_) = res {
-            acc.add_resolution(ctx, name, &res);
+            acc.add_resolution(ctx, name, res);
         }
     });
 }
diff --git a/crates/ide_completion/src/completions/pattern.rs b/crates/ide_completion/src/completions/pattern.rs
index b3b27f251f..a140ca4239 100644
--- a/crates/ide_completion/src/completions/pattern.rs
+++ b/crates/ide_completion/src/completions/pattern.rs
@@ -59,7 +59,7 @@ pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) {
             _ => false,
         };
         if add_resolution {
-            acc.add_resolution(ctx, name, &res);
+            acc.add_resolution(ctx, name, res);
         }
     });
 }
diff --git a/crates/ide_completion/src/completions/qualified_path.rs b/crates/ide_completion/src/completions/qualified_path.rs
index a0d6d5cdc6..656d46b10b 100644
--- a/crates/ide_completion/src/completions/qualified_path.rs
+++ b/crates/ide_completion/src/completions/qualified_path.rs
@@ -44,7 +44,7 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
                         }
                     }
                     if let ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) = def {
-                        acc.add_resolution(ctx, name, &def);
+                        acc.add_resolution(ctx, name, def);
                     }
                 }
             }
@@ -64,7 +64,7 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
                         .next()
                     {
                         if let Some(name) = next.name(ctx.db) {
-                            acc.add_resolution(ctx, name, &ScopeDef::ModuleDef(next.into()));
+                            acc.add_resolution(ctx, name, ScopeDef::ModuleDef(next.into()));
                         }
                     }
                 }
@@ -80,7 +80,7 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
                         _ => false,
                     };
                     if add_resolution {
-                        acc.add_resolution(ctx, name, &def);
+                        acc.add_resolution(ctx, name, def);
                     }
                 }
             }
@@ -147,7 +147,7 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
                 };
 
                 if add_resolution {
-                    acc.add_resolution(ctx, name, &def);
+                    acc.add_resolution(ctx, name, def);
                 }
             }
         }
diff --git a/crates/ide_completion/src/completions/unqualified_path.rs b/crates/ide_completion/src/completions/unqualified_path.rs
index ac1ba2da7c..e7980c12d7 100644
--- a/crates/ide_completion/src/completions/unqualified_path.rs
+++ b/crates/ide_completion/src/completions/unqualified_path.rs
@@ -24,7 +24,7 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC
         cov_mark::hit!(unqualified_path_only_modules_in_import);
         ctx.process_all_names(&mut |name, res| {
             if let ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) = res {
-                acc.add_resolution(ctx, name, &res);
+                acc.add_resolution(ctx, name, res);
             }
         });
 
@@ -43,7 +43,7 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC
                     _ => false,
                 };
                 if add_resolution {
-                    acc.add_resolution(ctx, name, &res);
+                    acc.add_resolution(ctx, name, res);
                 }
             });
             return;
@@ -61,7 +61,7 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC
                     }
                 }
                 if let hir::ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) = res {
-                    acc.add_resolution(ctx, name, &res);
+                    acc.add_resolution(ctx, name, res);
                 }
             });
             return;
@@ -76,7 +76,7 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC
                     _ => false,
                 };
                 if add_resolution {
-                    acc.add_resolution(ctx, name, &res);
+                    acc.add_resolution(ctx, name, res);
                 }
             });
             return;
@@ -134,7 +134,7 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC
             _ => true,
         };
         if add_resolution {
-            acc.add_resolution(ctx, name, &res);
+            acc.add_resolution(ctx, name, res);
         }
     });
 }
diff --git a/crates/ide_completion/src/context.rs b/crates/ide_completion/src/context.rs
index c6af285e86..d0ed988989 100644
--- a/crates/ide_completion/src/context.rs
+++ b/crates/ide_completion/src/context.rs
@@ -278,7 +278,7 @@ impl<'a> CompletionContext<'a> {
         self.is_visible_impl(&item.visibility(self.db), &item.attrs(self.db), item.krate(self.db))
     }
 
-    pub(crate) fn is_scope_def_hidden(&self, scope_def: &ScopeDef) -> bool {
+    pub(crate) fn is_scope_def_hidden(&self, scope_def: ScopeDef) -> bool {
         if let (Some(attrs), Some(krate)) = (scope_def.attrs(self.db), scope_def.krate(self.db)) {
             return self.is_doc_hidden(&attrs, krate);
         }
@@ -303,7 +303,7 @@ impl<'a> CompletionContext<'a> {
     /// A version of [`SemanticsScope::process_all_names`] that filters out `#[doc(hidden)]` items.
     pub(crate) fn process_all_names(&self, f: &mut dyn FnMut(Name, ScopeDef)) {
         self.scope.process_all_names(&mut |name, def| {
-            if self.is_scope_def_hidden(&def) {
+            if self.is_scope_def_hidden(def) {
                 return;
             }
 
@@ -367,7 +367,7 @@ impl<'a> CompletionContext<'a> {
             parse.reparse(&edit).tree()
         };
         let fake_ident_token =
-            file_with_fake_ident.syntax().token_at_offset(offset).right_biased().unwrap();
+            file_with_fake_ident.syntax().token_at_offset(offset).right_biased()?;
 
         let original_token = original_file.syntax().token_at_offset(offset).left_biased()?;
         let token = sema.descend_into_macros_single(original_token.clone());
diff --git a/crates/ide_completion/src/render.rs b/crates/ide_completion/src/render.rs
index 4b4fbb3ed9..404a2fa9d3 100644
--- a/crates/ide_completion/src/render.rs
+++ b/crates/ide_completion/src/render.rs
@@ -11,7 +11,7 @@ pub(crate) mod struct_literal;
 
 mod builder_ext;
 
-use hir::{AsAssocItem, HasAttrs, HirDisplay};
+use hir::{AsAssocItem, HasAttrs, HirDisplay, ScopeDef};
 use ide_db::{
     helpers::{item_name, SnippetCap},
     RootDatabase, SymbolKind,
@@ -47,8 +47,8 @@ impl<'a> RenderContext<'a> {
         self.completion.source_range()
     }
 
-    fn is_deprecated(&self, node: impl HasAttrs) -> bool {
-        let attrs = node.attrs(self.db());
+    fn is_deprecated(&self, def: impl HasAttrs) -> bool {
+        let attrs = def.attrs(self.db());
         attrs.by_key("deprecated").exists() || attrs.by_key("rustc_deprecated").exists()
     }
 
@@ -71,8 +71,8 @@ impl<'a> RenderContext<'a> {
                 .unwrap_or(false)
     }
 
-    fn docs(&self, node: impl HasAttrs) -> Option<hir::Documentation> {
-        node.docs(self.db())
+    fn docs(&self, def: impl HasAttrs) -> Option<hir::Documentation> {
+        def.docs(self.db())
     }
 }
 
@@ -128,7 +128,7 @@ pub(crate) fn render_tuple_field(
 pub(crate) fn render_resolution(
     ctx: RenderContext<'_>,
     local_name: hir::Name,
-    resolution: &hir::ScopeDef,
+    resolution: ScopeDef,
 ) -> Option<CompletionItem> {
     render_resolution_(ctx, local_name, None, resolution)
 }
@@ -137,83 +137,77 @@ pub(crate) fn render_resolution_with_import(
     ctx: RenderContext<'_>,
     import_edit: ImportEdit,
 ) -> Option<CompletionItem> {
-    let resolution = hir::ScopeDef::from(import_edit.import.original_item);
+    let resolution = ScopeDef::from(import_edit.import.original_item);
     let local_name = match resolution {
-        hir::ScopeDef::ModuleDef(hir::ModuleDef::Function(f)) => f.name(ctx.completion.db),
-        hir::ScopeDef::ModuleDef(hir::ModuleDef::Const(c)) => c.name(ctx.completion.db)?,
-        hir::ScopeDef::ModuleDef(hir::ModuleDef::TypeAlias(t)) => t.name(ctx.completion.db),
+        ScopeDef::ModuleDef(hir::ModuleDef::Function(f)) => f.name(ctx.completion.db),
+        ScopeDef::ModuleDef(hir::ModuleDef::Const(c)) => c.name(ctx.completion.db)?,
+        ScopeDef::ModuleDef(hir::ModuleDef::TypeAlias(t)) => t.name(ctx.completion.db),
         _ => item_name(ctx.db(), import_edit.import.original_item)?,
     };
-    render_resolution_(ctx, local_name, Some(import_edit), &resolution)
+    render_resolution_(ctx, local_name, Some(import_edit), resolution)
 }
 
 fn render_resolution_(
     ctx: RenderContext<'_>,
     local_name: hir::Name,
     import_to_add: Option<ImportEdit>,
-    resolution: &hir::ScopeDef,
+    resolution: ScopeDef,
 ) -> Option<CompletionItem> {
     let _p = profile::span("render_resolution");
     use hir::ModuleDef::*;
 
+    let db = ctx.db();
+
     let kind = match resolution {
-        hir::ScopeDef::ModuleDef(Function(func)) => {
-            return render_fn(ctx, import_to_add, Some(local_name), *func);
-        }
-        hir::ScopeDef::ModuleDef(Variant(_)) if ctx.completion.pattern_ctx.is_some() => {
-            CompletionItemKind::SymbolKind(SymbolKind::Variant)
+        ScopeDef::ModuleDef(Function(func)) => {
+            return Some(render_fn(ctx, import_to_add, Some(local_name), func));
         }
-        hir::ScopeDef::ModuleDef(Variant(var)) => {
-            let item = render_variant(ctx, import_to_add, Some(local_name), *var, None);
-            return Some(item);
+        ScopeDef::ModuleDef(Variant(var)) if ctx.completion.pattern_ctx.is_none() => {
+            return Some(render_variant(ctx, import_to_add, Some(local_name), var, None));
         }
-        hir::ScopeDef::MacroDef(mac) => {
-            let item = render_macro(ctx, import_to_add, local_name, *mac);
-            return item;
+        ScopeDef::MacroDef(mac) => return render_macro(ctx, import_to_add, local_name, mac),
+        ScopeDef::Unknown => {
+            let mut item = CompletionItem::new(
+                CompletionItemKind::UnresolvedReference,
+                ctx.source_range(),
+                local_name.to_smol_str(),
+            );
+            if let Some(import_to_add) = import_to_add {
+                item.add_import(import_to_add);
+            }
+            return Some(item.build());
         }
 
-        hir::ScopeDef::ModuleDef(Module(..)) => CompletionItemKind::SymbolKind(SymbolKind::Module),
-        hir::ScopeDef::ModuleDef(Adt(adt)) => CompletionItemKind::SymbolKind(match adt {
+        ScopeDef::ModuleDef(Variant(_)) => CompletionItemKind::SymbolKind(SymbolKind::Variant),
+        ScopeDef::ModuleDef(Module(..)) => CompletionItemKind::SymbolKind(SymbolKind::Module),
+        ScopeDef::ModuleDef(Adt(adt)) => CompletionItemKind::SymbolKind(match adt {
             hir::Adt::Struct(_) => SymbolKind::Struct,
             hir::Adt::Union(_) => SymbolKind::Union,
             hir::Adt::Enum(_) => SymbolKind::Enum,
         }),
-        hir::ScopeDef::ModuleDef(Const(..)) => CompletionItemKind::SymbolKind(SymbolKind::Const),
-        hir::ScopeDef::ModuleDef(Static(..)) => CompletionItemKind::SymbolKind(SymbolKind::Static),
-        hir::ScopeDef::ModuleDef(Trait(..)) => CompletionItemKind::SymbolKind(SymbolKind::Trait),
-        hir::ScopeDef::ModuleDef(TypeAlias(..)) => {
-            CompletionItemKind::SymbolKind(SymbolKind::TypeAlias)
-        }
-        hir::ScopeDef::ModuleDef(BuiltinType(..)) => CompletionItemKind::BuiltinType,
-        hir::ScopeDef::GenericParam(param) => CompletionItemKind::SymbolKind(match param {
+        ScopeDef::ModuleDef(Const(..)) => CompletionItemKind::SymbolKind(SymbolKind::Const),
+        ScopeDef::ModuleDef(Static(..)) => CompletionItemKind::SymbolKind(SymbolKind::Static),
+        ScopeDef::ModuleDef(Trait(..)) => CompletionItemKind::SymbolKind(SymbolKind::Trait),
+        ScopeDef::ModuleDef(TypeAlias(..)) => CompletionItemKind::SymbolKind(SymbolKind::TypeAlias),
+        ScopeDef::ModuleDef(BuiltinType(..)) => CompletionItemKind::BuiltinType,
+        ScopeDef::GenericParam(param) => CompletionItemKind::SymbolKind(match param {
             hir::GenericParam::TypeParam(_) => SymbolKind::TypeParam,
             hir::GenericParam::LifetimeParam(_) => SymbolKind::LifetimeParam,
             hir::GenericParam::ConstParam(_) => SymbolKind::ConstParam,
         }),
-        hir::ScopeDef::Local(..) => CompletionItemKind::SymbolKind(SymbolKind::Local),
-        hir::ScopeDef::Label(..) => CompletionItemKind::SymbolKind(SymbolKind::Label),
-        hir::ScopeDef::AdtSelfType(..) | hir::ScopeDef::ImplSelfType(..) => {
+        ScopeDef::Local(..) => CompletionItemKind::SymbolKind(SymbolKind::Local),
+        ScopeDef::Label(..) => CompletionItemKind::SymbolKind(SymbolKind::Label),
+        ScopeDef::AdtSelfType(..) | ScopeDef::ImplSelfType(..) => {
             CompletionItemKind::SymbolKind(SymbolKind::SelfParam)
         }
-        hir::ScopeDef::Unknown => {
-            let mut item = CompletionItem::new(
-                CompletionItemKind::UnresolvedReference,
-                ctx.source_range(),
-                local_name.to_smol_str(),
-            );
-            if let Some(import_to_add) = import_to_add {
-                item.add_import(import_to_add);
-            }
-            return Some(item.build());
-        }
     };
 
     let local_name = local_name.to_smol_str();
     let mut item = CompletionItem::new(kind, ctx.source_range(), local_name.clone());
-    if let hir::ScopeDef::Local(local) = resolution {
-        let ty = local.ty(ctx.db());
+    if let ScopeDef::Local(local) = resolution {
+        let ty = local.ty(db);
         if !ty.is_unknown() {
-            item.detail(ty.display(ctx.db()).to_string());
+            item.detail(ty.display(db).to_string());
         }
 
         item.set_relevance(CompletionRelevance {
@@ -229,15 +223,15 @@ fn render_resolution_(
     };
 
     // Add `<>` for generic types
-    if matches!(
+    let type_path_no_ty_args = matches!(
         ctx.completion.path_context,
         Some(PathCompletionContext { kind: Some(PathKind::Type), has_type_args: false, .. })
-    ) && ctx.completion.config.add_call_parenthesis
-    {
+    ) && ctx.completion.config.add_call_parenthesis;
+    if type_path_no_ty_args {
         if let Some(cap) = ctx.snippet_cap() {
             let has_non_default_type_params = match resolution {
-                hir::ScopeDef::ModuleDef(Adt(it)) => it.has_non_default_type_params(ctx.db()),
-                hir::ScopeDef::ModuleDef(TypeAlias(it)) => it.has_non_default_type_params(ctx.db()),
+                ScopeDef::ModuleDef(Adt(it)) => it.has_non_default_type_params(db),
+                ScopeDef::ModuleDef(TypeAlias(it)) => it.has_non_default_type_params(db),
                 _ => false,
             };
             if has_non_default_type_params {
@@ -248,7 +242,7 @@ fn render_resolution_(
             }
         }
     }
-    item.set_documentation(scope_def_docs(ctx.db(), resolution))
+    item.set_documentation(scope_def_docs(db, resolution))
         .set_deprecated(scope_def_is_deprecated(&ctx, resolution));
 
     if let Some(import_to_add) = import_to_add {
@@ -257,26 +251,26 @@ fn render_resolution_(
     Some(item.build())
 }
 
-fn scope_def_docs(db: &RootDatabase, resolution: &hir::ScopeDef) -> Option<hir::Documentation> {
+fn scope_def_docs(db: &RootDatabase, resolution: ScopeDef) -> Option<hir::Documentation> {
     use hir::ModuleDef::*;
     match resolution {
-        hir::ScopeDef::ModuleDef(Module(it)) => it.docs(db),
-        hir::ScopeDef::ModuleDef(Adt(it)) => it.docs(db),
-        hir::ScopeDef::ModuleDef(Variant(it)) => it.docs(db),
-        hir::ScopeDef::ModuleDef(Const(it)) => it.docs(db),
-        hir::ScopeDef::ModuleDef(Static(it)) => it.docs(db),
-        hir::ScopeDef::ModuleDef(Trait(it)) => it.docs(db),
-        hir::ScopeDef::ModuleDef(TypeAlias(it)) => it.docs(db),
+        ScopeDef::ModuleDef(Module(it)) => it.docs(db),
+        ScopeDef::ModuleDef(Adt(it)) => it.docs(db),
+        ScopeDef::ModuleDef(Variant(it)) => it.docs(db),
+        ScopeDef::ModuleDef(Const(it)) => it.docs(db),
+        ScopeDef::ModuleDef(Static(it)) => it.docs(db),
+        ScopeDef::ModuleDef(Trait(it)) => it.docs(db),
+        ScopeDef::ModuleDef(TypeAlias(it)) => it.docs(db),
         _ => None,
     }
 }
 
-fn scope_def_is_deprecated(ctx: &RenderContext<'_>, resolution: &hir::ScopeDef) -> bool {
+fn scope_def_is_deprecated(ctx: &RenderContext<'_>, resolution: ScopeDef) -> bool {
     match resolution {
-        hir::ScopeDef::ModuleDef(it) => ctx.is_deprecated_assoc_item(*it),
-        hir::ScopeDef::MacroDef(it) => ctx.is_deprecated(*it),
-        hir::ScopeDef::GenericParam(it) => ctx.is_deprecated(*it),
-        hir::ScopeDef::AdtSelfType(it) => ctx.is_deprecated(*it),
+        ScopeDef::ModuleDef(it) => ctx.is_deprecated_assoc_item(it),
+        ScopeDef::MacroDef(it) => ctx.is_deprecated(it),
+        ScopeDef::GenericParam(it) => ctx.is_deprecated(it),
+        ScopeDef::AdtSelfType(it) => ctx.is_deprecated(it),
         _ => false,
     }
 }
diff --git a/crates/ide_completion/src/render/const_.rs b/crates/ide_completion/src/render/const_.rs
index c40490ac0b..65c9d1d63e 100644
--- a/crates/ide_completion/src/render/const_.rs
+++ b/crates/ide_completion/src/render/const_.rs
@@ -2,53 +2,32 @@
 
 use hir::{AsAssocItem, HasSource};
 use ide_db::SymbolKind;
-use syntax::{ast::Const, display::const_label};
+use syntax::display::const_label;
 
 use crate::{item::CompletionItem, render::RenderContext};
 
 pub(crate) fn render_const(ctx: RenderContext<'_>, const_: hir::Const) -> Option<CompletionItem> {
     let _p = profile::span("render_const");
-    ConstRender::new(ctx, const_)?.render()
+    render(ctx, const_)
 }
 
-#[derive(Debug)]
-struct ConstRender<'a> {
-    ctx: RenderContext<'a>,
-    const_: hir::Const,
-    ast_node: Const,
-}
-
-impl<'a> ConstRender<'a> {
-    fn new(ctx: RenderContext<'a>, const_: hir::Const) -> Option<ConstRender<'a>> {
-        let ast_node = const_.source(ctx.db())?.value;
-        Some(ConstRender { ctx, const_, ast_node })
-    }
-
-    fn render(self) -> Option<CompletionItem> {
-        let name = self.const_.name(self.ctx.db())?.to_smol_str();
-        let detail = self.detail();
-
-        let mut item =
-            CompletionItem::new(SymbolKind::Const, self.ctx.source_range(), name.clone());
-        item.set_documentation(self.ctx.docs(self.const_))
-            .set_deprecated(
-                self.ctx.is_deprecated(self.const_)
-                    || self.ctx.is_deprecated_assoc_item(self.const_),
-            )
-            .detail(detail);
-
-        let db = self.ctx.db();
-        if let Some(actm) = self.const_.as_assoc_item(db) {
-            if let Some(trt) = actm.containing_trait_or_trait_impl(db) {
-                item.trait_name(trt.name(db).to_smol_str());
-                item.insert_text(name);
-            }
+fn render(ctx: RenderContext<'_>, const_: hir::Const) -> Option<CompletionItem> {
+    let db = ctx.db();
+    let name = const_.name(db)?.to_smol_str();
+    // FIXME: This is parsing files!
+    let detail = const_label(&const_.source(db)?.value);
+
+    let mut item = CompletionItem::new(SymbolKind::Const, ctx.source_range(), name.clone());
+    item.set_documentation(ctx.docs(const_))
+        .set_deprecated(ctx.is_deprecated(const_) || ctx.is_deprecated_assoc_item(const_))
+        .detail(detail);
+
+    if let Some(actm) = const_.as_assoc_item(db) {
+        if let Some(trt) = actm.containing_trait_or_trait_impl(db) {
+            item.trait_name(trt.name(db).to_smol_str());
+            item.insert_text(name);
         }
-
-        Some(item.build())
     }
 
-    fn detail(&self) -> String {
-        const_label(&self.ast_node)
-    }
+    Some(item.build())
 }
diff --git a/crates/ide_completion/src/render/enum_variant.rs b/crates/ide_completion/src/render/enum_variant.rs
index 26cb73d09c..f613f0dfde 100644
--- a/crates/ide_completion/src/render/enum_variant.rs
+++ b/crates/ide_completion/src/render/enum_variant.rs
@@ -1,10 +1,10 @@
 //! Renderer for `enum` variants.
 
-use std::{iter, mem};
+use std::iter;
 
-use hir::{HasAttrs, HirDisplay};
+use hir::{db::HirDatabase, HasAttrs, HirDisplay, StructKind};
 use ide_db::SymbolKind;
-use stdx::format_to;
+use itertools::Itertools;
 
 use crate::{
     item::{CompletionItem, ImportEdit},
@@ -20,116 +20,86 @@ pub(crate) fn render_variant(
     path: Option<hir::ModPath>,
 ) -> CompletionItem {
     let _p = profile::span("render_enum_variant");
-    EnumRender::new(ctx, local_name, variant, path).render(import_to_add)
+    render(ctx, local_name, variant, path, import_to_add)
 }
 
-#[derive(Debug)]
-struct EnumRender<'a> {
-    ctx: RenderContext<'a>,
+fn render(
+    ctx @ RenderContext { completion }: RenderContext<'_>,
+    local_name: Option<hir::Name>,
     variant: hir::Variant,
     path: Option<hir::ModPath>,
-    qualified_name: hir::ModPath,
-    short_qualified_name: hir::ModPath,
-    variant_kind: hir::StructKind,
-}
-
-impl<'a> EnumRender<'a> {
-    fn new(
-        ctx: RenderContext<'a>,
-        local_name: Option<hir::Name>,
-        variant: hir::Variant,
-        path: Option<hir::ModPath>,
-    ) -> EnumRender<'a> {
-        let name = local_name.unwrap_or_else(|| variant.name(ctx.db()));
-        let variant_kind = variant.kind(ctx.db());
-
-        let (qualified_name, short_qualified_name) = match &path {
-            Some(path) => {
-                let short = hir::ModPath::from_segments(
-                    hir::PathKind::Plain,
-                    path.segments().iter().skip(path.segments().len().saturating_sub(2)).cloned(),
-                );
-                (path.clone(), short)
-            }
-            None => (
-                hir::ModPath::from_segments(hir::PathKind::Plain, iter::once(name.clone())),
-                hir::ModPath::from_segments(hir::PathKind::Plain, iter::once(name)),
-            ),
-        };
-
-        EnumRender { ctx, variant, path, qualified_name, short_qualified_name, variant_kind }
-    }
-    fn render(self, import_to_add: Option<ImportEdit>) -> CompletionItem {
-        let mut item = CompletionItem::new(
-            SymbolKind::Variant,
-            self.ctx.source_range(),
-            self.qualified_name.to_string(),
-        );
-        item.set_documentation(self.variant.docs(self.ctx.db()))
-            .set_deprecated(self.ctx.is_deprecated(self.variant))
-            .detail(self.detail());
-
-        if let Some(import_to_add) = import_to_add {
-            item.add_import(import_to_add);
-        }
-
-        if self.variant_kind == hir::StructKind::Tuple {
-            cov_mark::hit!(inserts_parens_for_tuple_enums);
-            let params = Params::Anonymous(self.variant.fields(self.ctx.db()).len());
-            item.add_call_parens(
-                self.ctx.completion,
-                self.short_qualified_name.to_string(),
-                params,
+    import_to_add: Option<ImportEdit>,
+) -> CompletionItem {
+    let db = completion.db;
+    let name = local_name.unwrap_or_else(|| variant.name(db));
+    let variant_kind = variant.kind(db);
+
+    let (qualified_name, short_qualified_name, qualified) = match path {
+        Some(path) => {
+            let short = hir::ModPath::from_segments(
+                hir::PathKind::Plain,
+                path.segments().iter().skip(path.segments().len().saturating_sub(2)).cloned(),
             );
-        } else if self.path.is_some() {
-            item.lookup_by(self.short_qualified_name.to_string());
+            (path, short, true)
         }
+        None => (
+            hir::ModPath::from_segments(hir::PathKind::Plain, iter::once(name.clone())),
+            hir::ModPath::from_segments(hir::PathKind::Plain, iter::once(name)),
+            false,
+        ),
+    };
+
+    // FIXME: ModPath::to_smol_str()?
+    let mut item =
+        CompletionItem::new(SymbolKind::Variant, ctx.source_range(), qualified_name.to_string());
+    item.set_documentation(variant.docs(db))
+        .set_deprecated(ctx.is_deprecated(variant))
+        .detail(detail(db, variant, variant_kind));
+
+    if let Some(import_to_add) = import_to_add {
+        item.add_import(import_to_add);
+    }
 
-        let ty = self.variant.parent_enum(self.ctx.completion.db).ty(self.ctx.completion.db);
-        item.set_relevance(CompletionRelevance {
-            type_match: compute_type_match(self.ctx.completion, &ty),
-            ..CompletionRelevance::default()
-        });
+    // FIXME: ModPath::to_smol_str()?
+    let short_qualified_name = short_qualified_name.to_string();
+    if variant_kind == hir::StructKind::Tuple {
+        cov_mark::hit!(inserts_parens_for_tuple_enums);
+        let params = Params::Anonymous(variant.fields(db).len());
+        item.add_call_parens(ctx.completion, short_qualified_name, params);
+    } else if qualified {
+        item.lookup_by(short_qualified_name);
+    }
 
-        if let Some(ref_match) = compute_ref_match(self.ctx.completion, &ty) {
-            item.ref_match(ref_match);
-        }
+    let ty = variant.parent_enum(ctx.completion.db).ty(ctx.completion.db);
+    item.set_relevance(CompletionRelevance {
+        type_match: compute_type_match(ctx.completion, &ty),
+        ..CompletionRelevance::default()
+    });
 
-        item.build()
+    if let Some(ref_match) = compute_ref_match(ctx.completion, &ty) {
+        item.ref_match(ref_match);
     }
 
-    fn detail(&self) -> String {
-        let detail_types = self
-            .variant
-            .fields(self.ctx.db())
-            .into_iter()
-            .map(|field| (field.name(self.ctx.db()), field.ty(self.ctx.db())));
-
-        let mut b = String::new();
-        let mut first_run = true;
-        match self.variant_kind {
-            hir::StructKind::Tuple | hir::StructKind::Unit => {
-                format_to!(b, "(");
-                for (_, t) in detail_types {
-                    if !mem::take(&mut first_run) {
-                        format_to!(b, ", ");
-                    }
-                    format_to!(b, "{}", t.display(self.ctx.db()));
-                }
-                format_to!(b, ")");
-            }
-            hir::StructKind::Record => {
-                format_to!(b, "{{");
-                for (n, t) in detail_types {
-                    if !mem::take(&mut first_run) {
-                        format_to!(b, ", ");
-                    }
-                    format_to!(b, "{}: {}", n, t.display(self.ctx.db()));
-                }
-                format_to!(b, "}}");
-            }
+    item.build()
+}
+
+fn detail(db: &dyn HirDatabase, variant: hir::Variant, variant_kind: StructKind) -> String {
+    let detail_types = variant.fields(db).into_iter().map(|field| (field.name(db), field.ty(db)));
+
+    match variant_kind {
+        hir::StructKind::Tuple | hir::StructKind::Unit => {
+            format!("({})", detail_types.format_with(", ", |(_, t), f| f(&t.display(db))))
+        }
+        hir::StructKind::Record => {
+            format!(
+                "{{{}}}",
+                detail_types.format_with(", ", |(n, t), f| {
+                    f(&n)?;
+                    f(&": ")?;
+                    f(&t.display(db))
+                }),
+            )
         }
-        b
     }
 }
 
diff --git a/crates/ide_completion/src/render/function.rs b/crates/ide_completion/src/render/function.rs
index 918210f2f3..f166b87ab6 100644
--- a/crates/ide_completion/src/render/function.rs
+++ b/crates/ide_completion/src/render/function.rs
@@ -1,11 +1,12 @@
 //! Renderer for function calls.
 
-use hir::{AsAssocItem, HirDisplay};
+use hir::{db::HirDatabase, AsAssocItem, HirDisplay};
 use ide_db::SymbolKind;
 use itertools::Itertools;
 use stdx::format_to;
 
 use crate::{
+    context::CompletionContext,
     item::{CompletionItem, CompletionItemKind, CompletionRelevance, ImportEdit},
     render::{
         builder_ext::Params, compute_exact_name_match, compute_ref_match, compute_type_match,
@@ -13,14 +14,19 @@ use crate::{
     },
 };
 
+enum FuncType {
+    Function,
+    Method(Option<hir::Name>),
+}
+
 pub(crate) fn render_fn(
     ctx: RenderContext<'_>,
     import_to_add: Option<ImportEdit>,
     local_name: Option<hir::Name>,
-    fn_: hir::Function,
-) -> Option<CompletionItem> {
+    func: hir::Function,
+) -> CompletionItem {
     let _p = profile::span("render_fn");
-    Some(FunctionRender::new(ctx, None, local_name, fn_, false)?.render(import_to_add))
+    render(ctx, local_name, func, FuncType::Function, import_to_add)
 }
 
 pub(crate) fn render_method(
@@ -28,135 +34,120 @@ pub(crate) fn render_method(
     import_to_add: Option<ImportEdit>,
     receiver: Option<hir::Name>,
     local_name: Option<hir::Name>,
-    fn_: hir::Function,
-) -> Option<CompletionItem> {
+    func: hir::Function,
+) -> CompletionItem {
     let _p = profile::span("render_method");
-    Some(FunctionRender::new(ctx, receiver, local_name, fn_, true)?.render(import_to_add))
+    render(ctx, local_name, func, FuncType::Method(receiver), import_to_add)
 }
 
-#[derive(Debug)]
-struct FunctionRender<'a> {
-    ctx: RenderContext<'a>,
-    name: hir::Name,
-    receiver: Option<hir::Name>,
+fn render(
+    ctx @ RenderContext { completion }: RenderContext<'_>,
+    local_name: Option<hir::Name>,
     func: hir::Function,
-    is_method: bool,
-}
-
-impl<'a> FunctionRender<'a> {
-    fn new(
-        ctx: RenderContext<'a>,
-        receiver: Option<hir::Name>,
-        local_name: Option<hir::Name>,
-        fn_: hir::Function,
-        is_method: bool,
-    ) -> Option<FunctionRender<'a>> {
-        let name = local_name.unwrap_or_else(|| fn_.name(ctx.db()));
-
-        Some(FunctionRender { ctx, name, receiver, func: fn_, is_method })
-    }
+    func_type: FuncType,
+    import_to_add: Option<ImportEdit>,
+) -> CompletionItem {
+    let db = completion.db;
 
-    fn render(self, import_to_add: Option<ImportEdit>) -> CompletionItem {
-        let params = self.params();
-        let call = match &self.receiver {
-            Some(receiver) => format!("{}.{}", receiver, &self.name),
-            None => self.name.to_string(),
-        };
-        let mut item = CompletionItem::new(self.kind(), self.ctx.source_range(), call.clone());
-        item.set_documentation(self.ctx.docs(self.func))
-            .set_deprecated(
-                self.ctx.is_deprecated(self.func) || self.ctx.is_deprecated_assoc_item(self.func),
-            )
-            .detail(self.detail())
-            .add_call_parens(self.ctx.completion, call.clone(), params);
-
-        if import_to_add.is_none() {
-            let db = self.ctx.db();
-            if let Some(actm) = self.func.as_assoc_item(db) {
-                if let Some(trt) = actm.containing_trait_or_trait_impl(db) {
-                    item.trait_name(trt.name(db).to_smol_str());
-                }
-            }
-        }
+    let name = local_name.unwrap_or_else(|| func.name(db));
+    let params = params(completion, func, &func_type);
 
-        if let Some(import_to_add) = import_to_add {
-            item.add_import(import_to_add);
-        }
-        item.lookup_by(self.name.to_smol_str());
-
-        let ret_type = self.func.ret_type(self.ctx.db());
-        item.set_relevance(CompletionRelevance {
-            type_match: compute_type_match(self.ctx.completion, &ret_type),
-            exact_name_match: compute_exact_name_match(self.ctx.completion, &call),
-            ..CompletionRelevance::default()
-        });
-
-        if let Some(ref_match) = compute_ref_match(self.ctx.completion, &ret_type) {
-            // FIXME
-            // For now we don't properly calculate the edits for ref match
-            // completions on methods, so we've disabled them. See #8058.
-            if !self.is_method {
-                item.ref_match(ref_match);
+    // FIXME: SmolStr?
+    let call = match &func_type {
+        FuncType::Method(Some(receiver)) => format!("{}.{}", receiver, &name),
+        _ => name.to_string(),
+    };
+    let mut item = CompletionItem::new(
+        if func.self_param(db).is_some() {
+            CompletionItemKind::Method
+        } else {
+            CompletionItemKind::SymbolKind(SymbolKind::Function)
+        },
+        ctx.source_range(),
+        call.clone(),
+    );
+    item.set_documentation(ctx.docs(func))
+        .set_deprecated(ctx.is_deprecated(func) || ctx.is_deprecated_assoc_item(func))
+        .detail(detail(db, func))
+        .add_call_parens(completion, call.clone(), params);
+
+    if import_to_add.is_none() {
+        if let Some(actm) = func.as_assoc_item(db) {
+            if let Some(trt) = actm.containing_trait_or_trait_impl(db) {
+                item.trait_name(trt.name(db).to_smol_str());
             }
         }
-
-        item.build()
     }
 
-    fn detail(&self) -> String {
-        let ret_ty = self.func.ret_type(self.ctx.db());
-        let mut detail = format!("fn({})", self.params_display());
-        if !ret_ty.is_unit() {
-            format_to!(detail, " -> {}", ret_ty.display(self.ctx.db()));
+    if let Some(import_to_add) = import_to_add {
+        item.add_import(import_to_add);
+    }
+    item.lookup_by(name.to_smol_str());
+
+    let ret_type = func.ret_type(db);
+    item.set_relevance(CompletionRelevance {
+        type_match: compute_type_match(completion, &ret_type),
+        exact_name_match: compute_exact_name_match(completion, &call),
+        ..CompletionRelevance::default()
+    });
+
+    if let Some(ref_match) = compute_ref_match(completion, &ret_type) {
+        // FIXME
+        // For now we don't properly calculate the edits for ref match
+        // completions on methods, so we've disabled them. See #8058.
+        if matches!(func_type, FuncType::Function) {
+            item.ref_match(ref_match);
         }
-        detail
     }
 
-    fn params_display(&self) -> String {
-        if let Some(self_param) = self.func.self_param(self.ctx.db()) {
-            let params = self
-                .func
-                .assoc_fn_params(self.ctx.db())
-                .into_iter()
-                .skip(1) // skip the self param because we are manually handling that
-                .map(|p| p.ty().display(self.ctx.db()).to_string());
+    item.build()
+}
 
-            std::iter::once(self_param.display(self.ctx.db()).to_owned()).chain(params).join(", ")
-        } else {
-            let params = self
-                .func
-                .assoc_fn_params(self.ctx.db())
-                .into_iter()
-                .map(|p| p.ty().display(self.ctx.db()).to_string())
-                .join(", ");
-            params
-        }
+fn detail(db: &dyn HirDatabase, func: hir::Function) -> String {
+    let ret_ty = func.ret_type(db);
+    let mut detail = format!("fn({})", params_display(db, func));
+    if !ret_ty.is_unit() {
+        format_to!(detail, " -> {}", ret_ty.display(db));
     }
+    detail
+}
 
-    fn params(&self) -> Params {
-        let (params, self_param) =
-            if self.ctx.completion.has_dot_receiver() || self.receiver.is_some() {
-                (self.func.method_params(self.ctx.db()).unwrap_or_default(), None)
-            } else {
-                let self_param = self.func.self_param(self.ctx.db());
-
-                let mut assoc_params = self.func.assoc_fn_params(self.ctx.db());
-                if self_param.is_some() {
-                    assoc_params.remove(0);
-                }
-                (assoc_params, self_param)
-            };
-
-        Params::Named(self_param, params)
+fn params_display(db: &dyn HirDatabase, func: hir::Function) -> String {
+    if let Some(self_param) = func.self_param(db) {
+        let assoc_fn_params = func.assoc_fn_params(db);
+        let params = assoc_fn_params
+            .iter()
+            .skip(1) // skip the self param because we are manually handling that
+            .map(|p| p.ty().display(db));
+        format!(
+            "{}{}",
+            self_param.display(db),
+            params.format_with("", |display, f| {
+                f(&", ")?;
+                f(&display)
+            })
+        )
+    } else {
+        let assoc_fn_params = func.assoc_fn_params(db);
+        assoc_fn_params.iter().map(|p| p.ty().display(db)).join(", ")
     }
+}
 
-    fn kind(&self) -> CompletionItemKind {
-        if self.func.self_param(self.ctx.db()).is_some() {
-            CompletionItemKind::Method
+fn params(ctx: &CompletionContext<'_>, func: hir::Function, func_type: &FuncType) -> Params {
+    let (params, self_param) =
+        if ctx.has_dot_receiver() || matches!(func_type, FuncType::Method(Some(_))) {
+            (func.method_params(ctx.db).unwrap_or_default(), None)
         } else {
-            SymbolKind::Function.into()
-        }
-    }
+            let self_param = func.self_param(ctx.db);
+
+            let mut assoc_params = func.assoc_fn_params(ctx.db);
+            if self_param.is_some() {
+                assoc_params.remove(0);
+            }
+            (assoc_params, self_param)
+        };
+
+    Params::Named(self_param, params)
 }
 
 #[cfg(test)]
diff --git a/crates/ide_completion/src/render/macro_.rs b/crates/ide_completion/src/render/macro_.rs
index ac1f152aea..9c7b9a6db3 100644
--- a/crates/ide_completion/src/render/macro_.rs
+++ b/crates/ide_completion/src/render/macro_.rs
@@ -1,7 +1,7 @@
 //! Renderer for macro invocations.
 
 use either::Either;
-use hir::HasSource;
+use hir::{db::HirDatabase, Documentation, HasSource};
 use ide_db::SymbolKind;
 use syntax::{
     display::{fn_as_proc_macro_label, macro_label},
@@ -21,94 +21,96 @@ pub(crate) fn render_macro(
     macro_: hir::MacroDef,
 ) -> Option<CompletionItem> {
     let _p = profile::span("render_macro");
-    MacroRender::new(ctx, name, macro_).render(import_to_add)
+    render(ctx, name, macro_, import_to_add)
 }
 
-#[derive(Debug)]
-struct MacroRender<'a> {
-    ctx: RenderContext<'a>,
-    name: SmolStr,
+fn render(
+    ctx @ RenderContext { completion }: RenderContext<'_>,
+    name: hir::Name,
     macro_: hir::MacroDef,
-    docs: Option<hir::Documentation>,
-    bra: &'static str,
-    ket: &'static str,
-}
+    import_to_add: Option<ImportEdit>,
+) -> Option<CompletionItem> {
+    let db = completion.db;
 
-impl<'a> MacroRender<'a> {
-    fn new(ctx: RenderContext<'a>, name: hir::Name, macro_: hir::MacroDef) -> MacroRender<'a> {
-        let name = name.to_smol_str();
-        let docs = ctx.docs(macro_);
-        let docs_str = docs.as_ref().map_or("", |s| s.as_str());
-        let (bra, ket) =
-            if macro_.is_fn_like() { guess_macro_braces(&name, docs_str) } else { ("", "") };
+    let source_range = if completion.is_immediately_after_macro_bang() {
+        cov_mark::hit!(completes_macro_call_if_cursor_at_bang_token);
+        completion.token.parent().map(|it| it.text_range())
+    } else {
+        Some(ctx.source_range())
+    }?;
 
-        MacroRender { ctx, name, macro_, docs, bra, ket }
-    }
+    let name = name.to_smol_str();
+    let docs = ctx.docs(macro_);
+    let docs_str = docs.as_ref().map(Documentation::as_str).unwrap_or_default();
+    let (bra, ket) =
+        if macro_.is_fn_like() { guess_macro_braces(&name, docs_str) } else { ("", "") };
 
-    fn render(self, import_to_add: Option<ImportEdit>) -> Option<CompletionItem> {
-        let source_range = if self.ctx.completion.is_immediately_after_macro_bang() {
-            cov_mark::hit!(completes_macro_call_if_cursor_at_bang_token);
-            self.ctx.completion.token.parent().map(|it| it.text_range())
-        } else {
-            Some(self.ctx.source_range())
-        }?;
-        let mut item =
-            CompletionItem::new(SymbolKind::from(self.macro_.kind()), source_range, self.label());
-        item.set_deprecated(self.ctx.is_deprecated(self.macro_)).set_detail(self.detail());
+    let needs_bang = macro_.is_fn_like()
+        && !matches!(completion.path_kind(), Some(PathKind::Mac | PathKind::Use));
 
-        if let Some(import_to_add) = import_to_add {
-            item.add_import(import_to_add);
-        }
+    let mut item = CompletionItem::new(
+        SymbolKind::from(macro_.kind()),
+        source_range,
+        label(&ctx, needs_bang, bra, ket, &name),
+    );
+    item.set_deprecated(ctx.is_deprecated(macro_))
+        .set_detail(detail(db, macro_))
+        .set_documentation(docs);
 
-        let needs_bang = self.macro_.is_fn_like()
-            && !matches!(self.ctx.completion.path_kind(), Some(PathKind::Mac | PathKind::Use));
-        let has_parens = self.ctx.completion.path_is_call();
+    if let Some(import_to_add) = import_to_add {
+        item.add_import(import_to_add);
+    }
 
-        match self.ctx.snippet_cap() {
-            Some(cap) if needs_bang && !has_parens => {
-                let snippet = format!("{}!{}$0{}", self.name, self.bra, self.ket);
-                let lookup = self.banged_name();
-                item.insert_snippet(cap, snippet).lookup_by(lookup);
-            }
-            _ if needs_bang => {
-                let lookup = self.banged_name();
-                item.insert_text(self.banged_name()).lookup_by(lookup);
-            }
-            _ => {
-                cov_mark::hit!(dont_insert_macro_call_parens_unncessary);
-                item.insert_text(&*self.name);
-            }
-        };
+    let name = &*name;
 
-        item.set_documentation(self.docs);
-        Some(item.build())
-    }
+    match ctx.snippet_cap() {
+        Some(cap) if needs_bang && !completion.path_is_call() => {
+            let snippet = format!("{}!{}$0{}", name, bra, ket);
+            let lookup = banged_name(name);
+            item.insert_snippet(cap, snippet).lookup_by(lookup);
+        }
+        _ if needs_bang => {
+            let banged_name = banged_name(name);
+            item.insert_text(banged_name.clone()).lookup_by(banged_name);
+        }
+        _ => {
+            cov_mark::hit!(dont_insert_macro_call_parens_unncessary);
+            item.insert_text(name);
+        }
+    };
 
-    fn needs_bang(&self) -> bool {
-        !matches!(self.ctx.completion.path_kind(), Some(PathKind::Mac | PathKind::Use))
-    }
+    Some(item.build())
+}
 
-    fn label(&self) -> SmolStr {
-        if !self.macro_.is_fn_like() {
-            self.name.clone()
-        } else if self.needs_bang() && self.ctx.snippet_cap().is_some() {
-            SmolStr::from_iter([&*self.name, "!", self.bra, "…", self.ket])
+fn label(
+    ctx: &RenderContext<'_>,
+    needs_bang: bool,
+    bra: &str,
+    ket: &str,
+    name: &SmolStr,
+) -> SmolStr {
+    if needs_bang {
+        if ctx.snippet_cap().is_some() {
+            SmolStr::from_iter([&*name, "!", bra, "…", ket])
         } else {
-            self.banged_name()
+            banged_name(name)
         }
+    } else {
+        name.clone()
     }
+}
 
-    fn banged_name(&self) -> SmolStr {
-        SmolStr::from_iter([&*self.name, "!"])
-    }
+fn banged_name(name: &str) -> SmolStr {
+    SmolStr::from_iter([name, "!"])
+}
 
-    fn detail(&self) -> Option<String> {
-        let detail = match self.macro_.source(self.ctx.db())?.value {
-            Either::Left(node) => macro_label(&node),
-            Either::Right(node) => fn_as_proc_macro_label(&node),
-        };
-        Some(detail)
-    }
+fn detail(db: &dyn HirDatabase, macro_: hir::MacroDef) -> Option<String> {
+    // FIXME: This is parsing the file!
+    let detail = match macro_.source(db)?.value {
+        Either::Left(node) => macro_label(&node),
+        Either::Right(node) => fn_as_proc_macro_label(&node),
+    };
+    Some(detail)
 }
 
 fn guess_macro_braces(macro_name: &str, docs: &str) -> (&'static str, &'static str) {
@@ -147,7 +149,7 @@ mod tests {
     fn dont_insert_macro_call_parens_unncessary() {
         cov_mark::check!(dont_insert_macro_call_parens_unncessary);
         check_edit(
-            "frobnicate!",
+            "frobnicate",
             r#"
 //- /main.rs crate:main deps:foo
 use foo::$0;
@@ -161,7 +163,7 @@ use foo::frobnicate;
         );
 
         check_edit(
-            "frobnicate!",
+            "frobnicate",
             r#"
 macro_rules! frobnicate { () => () }
 fn main() { frob$0!(); }
diff --git a/crates/ide_completion/src/render/type_alias.rs b/crates/ide_completion/src/render/type_alias.rs
index 259fe25a6c..5df21fb36c 100644
--- a/crates/ide_completion/src/render/type_alias.rs
+++ b/crates/ide_completion/src/render/type_alias.rs
@@ -2,10 +2,7 @@
 
 use hir::{AsAssocItem, HasSource};
 use ide_db::SymbolKind;
-use syntax::{
-    ast::{HasName, TypeAlias},
-    display::type_label,
-};
+use syntax::{ast::HasName, display::type_label};
 
 use crate::{item::CompletionItem, render::RenderContext};
 
@@ -14,7 +11,7 @@ pub(crate) fn render_type_alias(
     type_alias: hir::TypeAlias,
 ) -> Option<CompletionItem> {
     let _p = profile::span("render_type_alias");
-    TypeAliasRender::new(ctx, type_alias)?.render(false)
+    render(ctx, type_alias, false)
 }
 
 pub(crate) fn render_type_alias_with_eq(
@@ -22,53 +19,38 @@ pub(crate) fn render_type_alias_with_eq(
     type_alias: hir::TypeAlias,
 ) -> Option<CompletionItem> {
     let _p = profile::span("render_type_alias_with_eq");
-    TypeAliasRender::new(ctx, type_alias)?.render(true)
+    render(ctx, type_alias, true)
 }
 
-#[derive(Debug)]
-struct TypeAliasRender<'a> {
-    ctx: RenderContext<'a>,
+fn render(
+    ctx: RenderContext<'_>,
     type_alias: hir::TypeAlias,
-    ast_node: TypeAlias,
-}
-
-impl<'a> TypeAliasRender<'a> {
-    fn new(ctx: RenderContext<'a>, type_alias: hir::TypeAlias) -> Option<TypeAliasRender<'a>> {
-        let ast_node = type_alias.source(ctx.db())?.value;
-        Some(TypeAliasRender { ctx, type_alias, ast_node })
-    }
-
-    fn render(self, with_eq: bool) -> Option<CompletionItem> {
-        let name = self.ast_node.name().map(|name| {
-            if with_eq {
-                format!("{} = ", name.text())
-            } else {
-                name.text().to_string()
-            }
-        })?;
-        let detail = self.detail();
-
-        let mut item =
-            CompletionItem::new(SymbolKind::TypeAlias, self.ctx.source_range(), name.clone());
-        item.set_documentation(self.ctx.docs(self.type_alias))
-            .set_deprecated(
-                self.ctx.is_deprecated(self.type_alias)
-                    || self.ctx.is_deprecated_assoc_item(self.type_alias),
-            )
-            .detail(detail);
-
-        let db = self.ctx.db();
-        if let Some(actm) = self.type_alias.as_assoc_item(db) {
-            if let Some(trt) = actm.containing_trait_or_trait_impl(db) {
-                item.trait_name(trt.name(db).to_smol_str());
-                item.insert_text(name);
-            }
+    with_eq: bool,
+) -> Option<CompletionItem> {
+    let db = ctx.db();
+
+    // FIXME: This parses the file!
+    let ast_node = type_alias.source(db)?.value;
+    let name = ast_node.name().map(|name| {
+        if with_eq {
+            format!("{} = ", name.text())
+        } else {
+            name.text().to_string()
+        }
+    })?;
+    let detail = type_label(&ast_node);
+
+    let mut item = CompletionItem::new(SymbolKind::TypeAlias, ctx.source_range(), name.clone());
+    item.set_documentation(ctx.docs(type_alias))
+        .set_deprecated(ctx.is_deprecated(type_alias) || ctx.is_deprecated_assoc_item(type_alias))
+        .detail(detail);
+
+    if let Some(actm) = type_alias.as_assoc_item(db) {
+        if let Some(trt) = actm.containing_trait_or_trait_impl(db) {
+            item.trait_name(trt.name(db).to_smol_str());
+            item.insert_text(name);
         }
-
-        Some(item.build())
     }
 
-    fn detail(&self) -> String {
-        type_label(&self.ast_node)
-    }
+    Some(item.build())
 }


Test patch:
diff --git a/crates/ide_completion/src/tests/use_tree.rs b/crates/ide_completion/src/tests/use_tree.rs
index 4cde258122..5f20e342e0 100644
--- a/crates/ide_completion/src/tests/use_tree.rs
+++ b/crates/ide_completion/src/tests/use_tree.rs
@@ -129,7 +129,7 @@ struct Bar;
 "#,
         expect![[r#"
             st Foo
-            ma foo! macro_rules! foo_
+            ma foo macro_rules! foo_
         "#]],
     );
 }