about summary refs log tree commit diff
diff options
context:
space:
mode:
authorLukas Wirth <lukastw97@gmail.com>2022-03-16 16:27:55 +0100
committerLukas Wirth <lukastw97@gmail.com>2022-03-16 16:27:55 +0100
commit02b401b130fc048a9f863fe0a58655f540947ff6 (patch)
tree2ec624ec5631668556be9d534b36417949e57662
parent4fe5f03c7fee75eccd29623347fa31bf5fa7c482 (diff)
downloadrust-02b401b130fc048a9f863fe0a58655f540947ff6.tar.gz
rust-02b401b130fc048a9f863fe0a58655f540947ff6.zip
Simplify completion render functionality
-rw-r--r--crates/ide_completion/src/completions.rs93
-rw-r--r--crates/ide_completion/src/completions/flyimport.rs2
-rw-r--r--crates/ide_completion/src/completions/pattern.rs28
-rw-r--r--crates/ide_completion/src/completions/record.rs9
-rw-r--r--crates/ide_completion/src/render.rs143
-rw-r--r--crates/ide_completion/src/render/enum_variant.rs102
-rw-r--r--crates/ide_completion/src/render/function.rs26
-rw-r--r--crates/ide_completion/src/render/literal.rs174
-rw-r--r--crates/ide_completion/src/render/macro_.rs18
-rw-r--r--crates/ide_completion/src/render/pattern.rs107
-rw-r--r--crates/ide_completion/src/render/struct_literal.rs92
-rw-r--r--crates/ide_completion/src/render/union_literal.rs2
-rw-r--r--crates/ide_completion/src/render/variant.rs26
-rw-r--r--crates/ide_completion/src/tests/pattern.rs13
14 files changed, 490 insertions, 345 deletions
diff --git a/crates/ide_completion/src/completions.rs b/crates/ide_completion/src/completions.rs
index 91e6b84294e..d187d56cb55 100644
--- a/crates/ide_completion/src/completions.rs
+++ b/crates/ide_completion/src/completions.rs
@@ -29,11 +29,11 @@ use crate::{
     item::Builder,
     render::{
         const_::render_const,
-        enum_variant::render_variant,
         function::{render_fn, render_method},
+        literal::{render_struct_literal, render_variant_lit},
+        macro_::render_macro,
         pattern::{render_struct_pat, render_variant_pat},
-        render_field, render_resolution, render_tuple_field,
-        struct_literal::render_struct_literal,
+        render_field, render_resolution, render_resolution_simple, render_tuple_field,
         type_alias::{render_type_alias, render_type_alias_with_eq},
         union_literal::render_union_literal,
         RenderContext,
@@ -124,7 +124,37 @@ impl Completions {
             cov_mark::hit!(qualified_path_doc_hidden);
             return;
         }
-        self.add(render_resolution(RenderContext::new(ctx, false), local_name, resolution));
+        self.add(render_resolution(RenderContext::new(ctx), local_name, resolution));
+    }
+
+    pub(crate) fn add_resolution_simple(
+        &mut self,
+        ctx: &CompletionContext,
+        local_name: hir::Name,
+        resolution: hir::ScopeDef,
+    ) {
+        if ctx.is_scope_def_hidden(resolution) {
+            return;
+        }
+        self.add(render_resolution_simple(RenderContext::new(ctx), local_name, resolution));
+    }
+
+    pub(crate) fn add_macro(
+        &mut self,
+        ctx: &CompletionContext,
+        mac: hir::Macro,
+        local_name: hir::Name,
+    ) {
+        let is_private_editable = match ctx.is_visible(&mac) {
+            Visible::Yes => false,
+            Visible::Editable => true,
+            Visible::No => return,
+        };
+        self.add(render_macro(
+            RenderContext::new(ctx).private_editable(is_private_editable),
+            local_name,
+            mac,
+        ));
     }
 
     pub(crate) fn add_function(
@@ -138,7 +168,11 @@ impl Completions {
             Visible::Editable => true,
             Visible::No => return,
         };
-        self.add(render_fn(RenderContext::new(ctx, is_private_editable), None, local_name, func));
+        self.add(render_fn(
+            RenderContext::new(ctx).private_editable(is_private_editable),
+            local_name,
+            func,
+        ));
     }
 
     pub(crate) fn add_method(
@@ -154,8 +188,7 @@ impl Completions {
             Visible::No => return,
         };
         self.add(render_method(
-            RenderContext::new(ctx, is_private_editable),
-            None,
+            RenderContext::new(ctx).private_editable(is_private_editable),
             receiver,
             local_name,
             func,
@@ -168,7 +201,10 @@ impl Completions {
             Visible::Editable => true,
             Visible::No => return,
         };
-        self.add_opt(render_const(RenderContext::new(ctx, is_private_editable), konst));
+        self.add_opt(render_const(
+            RenderContext::new(ctx).private_editable(is_private_editable),
+            konst,
+        ));
     }
 
     pub(crate) fn add_type_alias(&mut self, ctx: &CompletionContext, type_alias: hir::TypeAlias) {
@@ -177,7 +213,10 @@ impl Completions {
             Visible::Editable => true,
             Visible::No => return,
         };
-        self.add_opt(render_type_alias(RenderContext::new(ctx, is_private_editable), type_alias));
+        self.add_opt(render_type_alias(
+            RenderContext::new(ctx).private_editable(is_private_editable),
+            type_alias,
+        ));
     }
 
     pub(crate) fn add_type_alias_with_eq(
@@ -185,7 +224,7 @@ impl Completions {
         ctx: &CompletionContext,
         type_alias: hir::TypeAlias,
     ) {
-        self.add_opt(render_type_alias_with_eq(RenderContext::new(ctx, false), type_alias));
+        self.add_opt(render_type_alias_with_eq(RenderContext::new(ctx), type_alias));
     }
 
     pub(crate) fn add_qualified_enum_variant(
@@ -194,8 +233,7 @@ impl Completions {
         variant: hir::Variant,
         path: hir::ModPath,
     ) {
-        let item = render_variant(RenderContext::new(ctx, false), None, None, variant, Some(path));
-        self.add(item);
+        self.add_opt(render_variant_lit(RenderContext::new(ctx), None, variant, Some(path)));
     }
 
     pub(crate) fn add_enum_variant(
@@ -204,8 +242,7 @@ impl Completions {
         variant: hir::Variant,
         local_name: Option<hir::Name>,
     ) {
-        let item = render_variant(RenderContext::new(ctx, false), None, local_name, variant, None);
-        self.add(item);
+        self.add_opt(render_variant_lit(RenderContext::new(ctx), local_name, variant, None));
     }
 
     pub(crate) fn add_field(
@@ -220,7 +257,12 @@ impl Completions {
             Visible::Editable => true,
             Visible::No => return,
         };
-        let item = render_field(RenderContext::new(ctx, is_private_editable), receiver, field, ty);
+        let item = render_field(
+            RenderContext::new(ctx).private_editable(is_private_editable),
+            receiver,
+            field,
+            ty,
+        );
         self.add(item);
     }
 
@@ -231,7 +273,7 @@ impl Completions {
         path: Option<hir::ModPath>,
         local_name: Option<hir::Name>,
     ) {
-        let item = render_struct_literal(RenderContext::new(ctx, false), strukt, path, local_name);
+        let item = render_struct_literal(RenderContext::new(ctx), strukt, path, local_name);
         self.add_opt(item);
     }
 
@@ -242,7 +284,7 @@ impl Completions {
         path: Option<hir::ModPath>,
         local_name: Option<hir::Name>,
     ) {
-        let item = render_union_literal(RenderContext::new(ctx, false), un, path, local_name);
+        let item = render_union_literal(RenderContext::new(ctx), un, path, local_name);
         self.add_opt(item);
     }
 
@@ -253,7 +295,7 @@ impl Completions {
         field: usize,
         ty: &hir::Type,
     ) {
-        let item = render_tuple_field(RenderContext::new(ctx, false), receiver, field, ty);
+        let item = render_tuple_field(RenderContext::new(ctx), receiver, field, ty);
         self.add(item);
     }
 
@@ -272,7 +314,14 @@ impl Completions {
         variant: hir::Variant,
         local_name: Option<hir::Name>,
     ) {
-        self.add_opt(render_variant_pat(RenderContext::new(ctx, false), variant, local_name, None));
+        self.add_opt(render_variant_pat(
+            RenderContext::new(ctx),
+            variant,
+            local_name.clone(),
+            None,
+            false,
+        ));
+        self.add_opt(render_variant_pat(RenderContext::new(ctx), variant, local_name, None, true));
     }
 
     pub(crate) fn add_qualified_variant_pat(
@@ -281,7 +330,9 @@ impl Completions {
         variant: hir::Variant,
         path: hir::ModPath,
     ) {
-        self.add_opt(render_variant_pat(RenderContext::new(ctx, false), variant, None, Some(path)));
+        let path = Some(&path);
+        self.add_opt(render_variant_pat(RenderContext::new(ctx), variant, None, path, false));
+        self.add_opt(render_variant_pat(RenderContext::new(ctx), variant, None, path, true));
     }
 
     pub(crate) fn add_struct_pat(
@@ -290,7 +341,7 @@ impl Completions {
         strukt: hir::Struct,
         local_name: Option<hir::Name>,
     ) {
-        self.add_opt(render_struct_pat(RenderContext::new(ctx, false), strukt, local_name));
+        self.add_opt(render_struct_pat(RenderContext::new(ctx), strukt, local_name));
     }
 }
 
diff --git a/crates/ide_completion/src/completions/flyimport.rs b/crates/ide_completion/src/completions/flyimport.rs
index aee2bbb53c3..6c8878a7bb0 100644
--- a/crates/ide_completion/src/completions/flyimport.rs
+++ b/crates/ide_completion/src/completions/flyimport.rs
@@ -198,7 +198,7 @@ pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext)
             })
             .filter_map(|import| {
                 render_resolution_with_import(
-                    RenderContext::new(ctx, false),
+                    RenderContext::new(ctx),
                     ImportEdit { import, scope: import_scope.clone() },
                 )
             }),
diff --git a/crates/ide_completion/src/completions/pattern.rs b/crates/ide_completion/src/completions/pattern.rs
index 958c892b8d4..6c17da07d6f 100644
--- a/crates/ide_completion/src/completions/pattern.rs
+++ b/crates/ide_completion/src/completions/pattern.rs
@@ -54,8 +54,7 @@ pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) {
     {
         if refutable || single_variant_enum(e) {
             super::enum_variants_with_paths(acc, ctx, e, |acc, ctx, variant, path| {
-                acc.add_qualified_variant_pat(ctx, variant, path.clone());
-                acc.add_qualified_enum_variant(ctx, variant, path);
+                acc.add_qualified_variant_pat(ctx, variant, path);
             });
         }
     }
@@ -63,7 +62,7 @@ pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) {
     // FIXME: ideally, we should look at the type we are matching against and
     // suggest variants + auto-imports
     ctx.process_all_names(&mut |name, res| {
-        let add_resolution = match res {
+        let add_simple_path = match res {
             hir::ScopeDef::ModuleDef(def) => match def {
                 hir::ModuleDef::Adt(hir::Adt::Struct(strukt)) => {
                     acc.add_struct_pat(ctx, strukt, Some(name.clone()));
@@ -76,8 +75,11 @@ pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) {
                     true
                 }
                 hir::ModuleDef::Adt(hir::Adt::Enum(e)) => refutable || single_variant_enum(e),
-                hir::ModuleDef::Const(..) | hir::ModuleDef::Module(..) => refutable,
-                hir::ModuleDef::Macro(mac) => mac.is_fn_like(ctx.db),
+                hir::ModuleDef::Const(..) => refutable,
+                hir::ModuleDef::Module(..) => true,
+                hir::ModuleDef::Macro(mac) if mac.is_fn_like(ctx.db) => {
+                    return acc.add_macro(ctx, mac, name)
+                }
                 _ => false,
             },
             hir::ScopeDef::ImplSelfType(impl_) => match impl_.self_ty(ctx.db).as_adt() {
@@ -85,13 +87,19 @@ pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) {
                     acc.add_struct_pat(ctx, strukt, Some(name.clone()));
                     true
                 }
-                Some(hir::Adt::Enum(_)) => refutable,
-                _ => true,
+                Some(hir::Adt::Enum(e)) => refutable || single_variant_enum(e),
+                Some(hir::Adt::Union(_)) => true,
+                _ => false,
             },
-            _ => false,
+            ScopeDef::GenericParam(hir::GenericParam::ConstParam(_)) => true,
+            ScopeDef::GenericParam(_)
+            | ScopeDef::AdtSelfType(_)
+            | ScopeDef::Local(_)
+            | ScopeDef::Label(_)
+            | ScopeDef::Unknown => false,
         };
-        if add_resolution {
-            acc.add_resolution(ctx, name, res);
+        if add_simple_path {
+            acc.add_resolution_simple(ctx, name, res);
         }
     });
 }
diff --git a/crates/ide_completion/src/completions/record.rs b/crates/ide_completion/src/completions/record.rs
index 264b3784bf1..5509ec922f3 100644
--- a/crates/ide_completion/src/completions/record.rs
+++ b/crates/ide_completion/src/completions/record.rs
@@ -84,13 +84,16 @@ pub(crate) fn complete_record_literal(
     match ctx.expected_type.as_ref()?.as_adt()? {
         hir::Adt::Struct(strukt) if ctx.path_qual().is_none() => {
             let module = if let Some(module) = ctx.module { module } else { strukt.module(ctx.db) };
-            let path = module.find_use_path(ctx.db, hir::ModuleDef::from(strukt));
+            let path = module
+                .find_use_path(ctx.db, hir::ModuleDef::from(strukt))
+                .filter(|it| it.len() > 1);
 
             acc.add_struct_literal(ctx, strukt, path, None);
         }
         hir::Adt::Union(un) if ctx.path_qual().is_none() => {
             let module = if let Some(module) = ctx.module { module } else { un.module(ctx.db) };
-            let path = module.find_use_path(ctx.db, hir::ModuleDef::from(un));
+            let path =
+                module.find_use_path(ctx.db, hir::ModuleDef::from(un)).filter(|it| it.len() > 1);
 
             acc.add_union_literal(ctx, un, path, None);
         }
@@ -132,7 +135,7 @@ fn baz() {
     #[test]
     fn literal_struct_completion_from_sub_modules() {
         check_edit(
-            "Struct {…}",
+            "submod::Struct {…}",
             r#"
 mod submod {
     pub struct Struct {
diff --git a/crates/ide_completion/src/render.rs b/crates/ide_completion/src/render.rs
index d6fa86b55da..13de818da15 100644
--- a/crates/ide_completion/src/render.rs
+++ b/crates/ide_completion/src/render.rs
@@ -3,13 +3,12 @@
 
 pub(crate) mod macro_;
 pub(crate) mod function;
-pub(crate) mod enum_variant;
 pub(crate) mod const_;
 pub(crate) mod pattern;
 pub(crate) mod type_alias;
-pub(crate) mod struct_literal;
 pub(crate) mod variant;
 pub(crate) mod union_literal;
+pub(crate) mod literal;
 
 use hir::{AsAssocItem, HasAttrs, HirDisplay, ScopeDef};
 use ide_db::{helpers::item_name, RootDatabase, SnippetCap, SymbolKind};
@@ -18,22 +17,30 @@ use syntax::{SmolStr, SyntaxKind, TextRange};
 use crate::{
     context::{PathCompletionCtx, PathKind},
     item::{CompletionRelevanceTypeMatch, ImportEdit},
-    render::{enum_variant::render_variant, function::render_fn, macro_::render_macro},
+    render::{function::render_fn, literal::render_variant_lit, macro_::render_macro},
     CompletionContext, CompletionItem, CompletionItemKind, CompletionRelevance,
 };
 /// Interface for data and methods required for items rendering.
-#[derive(Debug)]
+#[derive(Debug, Clone)]
 pub(crate) struct RenderContext<'a> {
     completion: &'a CompletionContext<'a>,
     is_private_editable: bool,
+    import_to_add: Option<ImportEdit>,
 }
 
 impl<'a> RenderContext<'a> {
-    pub(crate) fn new(
-        completion: &'a CompletionContext<'a>,
-        is_private_editable: bool,
-    ) -> RenderContext<'a> {
-        RenderContext { completion, is_private_editable }
+    pub(crate) fn new(completion: &'a CompletionContext<'a>) -> RenderContext<'a> {
+        RenderContext { completion, is_private_editable: false, import_to_add: None }
+    }
+
+    pub(crate) fn private_editable(mut self, private_editable: bool) -> Self {
+        self.is_private_editable = private_editable;
+        self
+    }
+
+    pub(crate) fn import_to_add(mut self, import_to_add: Option<ImportEdit>) -> Self {
+        self.import_to_add = import_to_add;
+        self
     }
 
     fn snippet_cap(&self) -> Option<SnippetCap> {
@@ -139,6 +146,14 @@ pub(crate) fn render_resolution(
     render_resolution_(ctx, local_name, None, resolution)
 }
 
+pub(crate) fn render_resolution_simple(
+    ctx: RenderContext<'_>,
+    local_name: hir::Name,
+    resolution: ScopeDef,
+) -> CompletionItem {
+    render_resolution_simple_(ctx, local_name, None, resolution)
+}
+
 pub(crate) fn render_resolution_with_import(
     ctx: RenderContext<'_>,
     import_edit: ImportEdit,
@@ -163,24 +178,26 @@ fn render_resolution_(
     use hir::ModuleDef::*;
 
     let db = ctx.db();
-
+    let ctx = ctx.import_to_add(import_to_add);
     let kind = match resolution {
         ScopeDef::ModuleDef(Function(func)) => {
-            return render_fn(ctx, import_to_add, Some(local_name), func);
+            return render_fn(ctx, Some(local_name), func);
         }
         ScopeDef::ModuleDef(Variant(var)) if ctx.completion.pattern_ctx.is_none() => {
-            return render_variant(ctx, import_to_add, Some(local_name), var, None);
-        }
-        ScopeDef::ModuleDef(Macro(mac)) => {
-            return render_macro(ctx, import_to_add, local_name, mac)
+            if let Some(item) = render_variant_lit(ctx.clone(), Some(local_name.clone()), var, None)
+            {
+                return item;
+            }
+            CompletionItemKind::SymbolKind(SymbolKind::Variant)
         }
+        ScopeDef::ModuleDef(Macro(mac)) => return render_macro(ctx, 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 {
+            if let Some(import_to_add) = ctx.import_to_add {
                 item.add_import(import_to_add);
             }
             return item.build();
@@ -253,7 +270,95 @@ fn render_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 {
+    if let Some(import_to_add) = ctx.import_to_add {
+        item.add_import(import_to_add);
+    }
+    item.build()
+}
+
+fn render_resolution_simple_(
+    ctx: RenderContext<'_>,
+    local_name: hir::Name,
+    import_to_add: Option<ImportEdit>,
+    resolution: ScopeDef,
+) -> CompletionItem {
+    let _p = profile::span("render_resolution");
+    use hir::ModuleDef::*;
+
+    let db = ctx.db();
+    let ctx = ctx.import_to_add(import_to_add);
+    let kind = match resolution {
+        ScopeDef::Unknown => CompletionItemKind::UnresolvedReference,
+        ScopeDef::ModuleDef(Function(_)) => CompletionItemKind::SymbolKind(SymbolKind::Function),
+        ScopeDef::ModuleDef(Variant(_)) => CompletionItemKind::SymbolKind(SymbolKind::Variant),
+        ScopeDef::ModuleDef(Macro(_)) => CompletionItemKind::SymbolKind(SymbolKind::Macro),
+        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,
+        }),
+        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::ConstParam(_) => SymbolKind::ConstParam,
+            hir::GenericParam::LifetimeParam(_) => SymbolKind::LifetimeParam,
+        }),
+        ScopeDef::Local(..) => CompletionItemKind::SymbolKind(SymbolKind::Local),
+        ScopeDef::Label(..) => CompletionItemKind::SymbolKind(SymbolKind::Label),
+        ScopeDef::AdtSelfType(..) | ScopeDef::ImplSelfType(..) => {
+            CompletionItemKind::SymbolKind(SymbolKind::SelfParam)
+        }
+    };
+
+    let local_name = local_name.to_smol_str();
+    let mut item = CompletionItem::new(kind, ctx.source_range(), local_name.clone());
+    if let ScopeDef::Local(local) = resolution {
+        let ty = local.ty(db);
+        if !ty.is_unknown() {
+            item.detail(ty.display(db).to_string());
+        }
+
+        item.set_relevance(CompletionRelevance {
+            type_match: compute_type_match(ctx.completion, &ty),
+            exact_name_match: compute_exact_name_match(ctx.completion, &local_name),
+            is_local: true,
+            ..CompletionRelevance::default()
+        });
+
+        if let Some(ref_match) = compute_ref_match(ctx.completion, &ty) {
+            item.ref_match(ref_match);
+        }
+    };
+
+    // Add `<>` for generic types
+    let type_path_no_ty_args = matches!(
+        ctx.completion.path_context,
+        Some(PathCompletionCtx { kind: Some(PathKind::Type), has_type_args: false, .. })
+    ) && 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 {
+                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 {
+                cov_mark::hit!(inserts_angle_brackets_for_generics);
+                item.lookup_by(local_name.clone())
+                    .label(SmolStr::from_iter([&local_name, "<…>"]))
+                    .insert_snippet(cap, format!("{}<$0>", local_name));
+            }
+        }
+    }
+    item.set_documentation(scope_def_docs(db, resolution))
+        .set_deprecated(scope_def_is_deprecated(&ctx, resolution));
+
+    if let Some(import_to_add) = ctx.import_to_add {
         item.add_import(import_to_add);
     }
     item.build()
@@ -577,7 +682,7 @@ fn main() { let _: m::Spam = S$0 }
                         kind: SymbolKind(
                             Variant,
                         ),
-                        lookup: "Spam::Bar",
+                        lookup: "Spam::Bar(…)",
                         detail: "m::Spam::Bar(i32)",
                         relevance: CompletionRelevance {
                             exact_name_match: false,
@@ -1156,6 +1261,7 @@ fn main() {
             "#,
             expect![[r#"
                 lc s [type+name+local]
+                st S [type]
                 st S []
                 fn main() []
                 fn foo(…) []
@@ -1172,6 +1278,7 @@ fn main() {
             "#,
             expect![[r#"
                 lc ssss [type+local]
+                st S [type]
                 st S []
                 fn main() []
                 fn foo(…) []
diff --git a/crates/ide_completion/src/render/enum_variant.rs b/crates/ide_completion/src/render/enum_variant.rs
deleted file mode 100644
index 9e27a84e2f7..00000000000
--- a/crates/ide_completion/src/render/enum_variant.rs
+++ /dev/null
@@ -1,102 +0,0 @@
-//! Renderer for `enum` variants.
-
-use hir::{HasAttrs, StructKind};
-use ide_db::SymbolKind;
-use syntax::SmolStr;
-
-use crate::{
-    item::{CompletionItem, ImportEdit},
-    render::{
-        compute_ref_match, compute_type_match,
-        variant::{format_literal_label, render_record, render_tuple, RenderedLiteral},
-        RenderContext,
-    },
-    CompletionRelevance,
-};
-
-pub(crate) fn render_variant(
-    ctx: RenderContext<'_>,
-    import_to_add: Option<ImportEdit>,
-    local_name: Option<hir::Name>,
-    variant: hir::Variant,
-    path: Option<hir::ModPath>,
-) -> CompletionItem {
-    let _p = profile::span("render_enum_variant");
-    render(ctx, local_name, variant, path, import_to_add)
-}
-
-fn render(
-    ctx @ RenderContext { completion, .. }: RenderContext<'_>,
-    local_name: Option<hir::Name>,
-    variant: hir::Variant,
-    path: Option<hir::ModPath>,
-    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(),
-            );
-            (path, short, true)
-        }
-        None => (name.clone().into(), name.into(), false),
-    };
-    let qualified_name = qualified_name.to_string();
-    let short_qualified_name: SmolStr = short_qualified_name.to_string().into();
-
-    let mut rendered = match variant_kind {
-        StructKind::Tuple => {
-            render_tuple(db, ctx.snippet_cap(), &variant.fields(db), Some(&qualified_name))
-        }
-        StructKind::Record => {
-            render_record(db, ctx.snippet_cap(), &variant.fields(db), Some(&qualified_name))
-        }
-        StructKind::Unit => {
-            RenderedLiteral { literal: qualified_name.clone(), detail: qualified_name.clone() }
-        }
-    };
-
-    if ctx.snippet_cap().is_some() {
-        rendered.literal.push_str("$0");
-    }
-
-    let mut item = CompletionItem::new(
-        SymbolKind::Variant,
-        ctx.source_range(),
-        format_literal_label(&qualified_name, variant_kind),
-    );
-
-    item.set_documentation(variant.docs(db))
-        .set_deprecated(ctx.is_deprecated(variant))
-        .detail(rendered.detail);
-
-    match ctx.snippet_cap() {
-        Some(snippet_cap) => item.insert_snippet(snippet_cap, rendered.literal),
-        None => item.insert_text(rendered.literal),
-    };
-
-    if let Some(import_to_add) = import_to_add {
-        item.add_import(import_to_add);
-    }
-
-    if qualified {
-        item.lookup_by(short_qualified_name);
-    }
-
-    let ty = variant.parent_enum(completion.db).ty(completion.db);
-    item.set_relevance(CompletionRelevance {
-        type_match: compute_type_match(completion, &ty),
-        ..ctx.completion_relevance()
-    });
-
-    if let Some(ref_match) = compute_ref_match(completion, &ty) {
-        item.ref_match(ref_match);
-    }
-
-    item.build()
-}
diff --git a/crates/ide_completion/src/render/function.rs b/crates/ide_completion/src/render/function.rs
index 6a46de4229a..7df13988ad6 100644
--- a/crates/ide_completion/src/render/function.rs
+++ b/crates/ide_completion/src/render/function.rs
@@ -8,7 +8,7 @@ use syntax::SmolStr;
 
 use crate::{
     context::{CompletionContext, PathCompletionCtx, PathKind},
-    item::{Builder, CompletionItem, CompletionItemKind, CompletionRelevance, ImportEdit},
+    item::{Builder, CompletionItem, CompletionItemKind, CompletionRelevance},
     patterns::ImmediateLocation,
     render::{compute_exact_name_match, compute_ref_match, compute_type_match, RenderContext},
 };
@@ -20,23 +20,21 @@ enum FuncKind {
 
 pub(crate) fn render_fn(
     ctx: RenderContext<'_>,
-    import_to_add: Option<ImportEdit>,
     local_name: Option<hir::Name>,
     func: hir::Function,
 ) -> CompletionItem {
     let _p = profile::span("render_fn");
-    render(ctx, local_name, func, FuncKind::Function, import_to_add)
+    render(ctx, local_name, func, FuncKind::Function)
 }
 
 pub(crate) fn render_method(
     ctx: RenderContext<'_>,
-    import_to_add: Option<ImportEdit>,
     receiver: Option<hir::Name>,
     local_name: Option<hir::Name>,
     func: hir::Function,
 ) -> CompletionItem {
     let _p = profile::span("render_method");
-    render(ctx, local_name, func, FuncKind::Method(receiver), import_to_add)
+    render(ctx, local_name, func, FuncKind::Method(receiver))
 }
 
 fn render(
@@ -44,7 +42,6 @@ fn render(
     local_name: Option<hir::Name>,
     func: hir::Function,
     func_kind: FuncKind,
-    import_to_add: Option<ImportEdit>,
 ) -> CompletionItem {
     let db = completion.db;
 
@@ -98,17 +95,18 @@ fn render(
         _ => (),
     }
 
-    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());
+    match ctx.import_to_add {
+        Some(import_to_add) => {
+            item.add_import(import_to_add);
+        }
+        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());
+                }
             }
         }
     }
-
-    if let Some(import_to_add) = import_to_add {
-        item.add_import(import_to_add);
-    }
     item.build()
 }
 
diff --git a/crates/ide_completion/src/render/literal.rs b/crates/ide_completion/src/render/literal.rs
new file mode 100644
index 00000000000..d91e80f90c0
--- /dev/null
+++ b/crates/ide_completion/src/render/literal.rs
@@ -0,0 +1,174 @@
+//! Renderer for `enum` variants.
+
+use hir::{db::HirDatabase, Documentation, HasAttrs, StructKind};
+use ide_db::SymbolKind;
+
+use crate::{
+    context::{CompletionContext, PathCompletionCtx},
+    item::CompletionItem,
+    render::{
+        compute_ref_match, compute_type_match,
+        variant::{
+            format_literal_label, render_record_lit, render_tuple_lit, visible_fields,
+            RenderedLiteral,
+        },
+        RenderContext,
+    },
+    CompletionItemKind, CompletionRelevance,
+};
+
+pub(crate) fn render_variant_lit(
+    ctx: RenderContext<'_>,
+    local_name: Option<hir::Name>,
+    variant: hir::Variant,
+    path: Option<hir::ModPath>,
+) -> Option<CompletionItem> {
+    let _p = profile::span("render_enum_variant");
+    let db = ctx.db();
+
+    let name = local_name.unwrap_or_else(|| variant.name(db));
+    render(ctx, Variant::EnumVariant(variant), name, path)
+}
+
+pub(crate) fn render_struct_literal(
+    ctx: RenderContext<'_>,
+    strukt: hir::Struct,
+    path: Option<hir::ModPath>,
+    local_name: Option<hir::Name>,
+) -> Option<CompletionItem> {
+    let _p = profile::span("render_struct_literal");
+    let db = ctx.db();
+
+    let name = local_name.unwrap_or_else(|| strukt.name(db));
+    render(ctx, Variant::Struct(strukt), name, path)
+}
+
+fn render(
+    ctx @ RenderContext { completion, .. }: RenderContext<'_>,
+    thing: Variant,
+    name: hir::Name,
+    path: Option<hir::ModPath>,
+) -> Option<CompletionItem> {
+    if let Some(PathCompletionCtx { has_call_parens: true, .. }) = completion.path_context {
+        return None;
+    }
+    let db = completion.db;
+    let fields = thing.fields(completion)?;
+
+    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(),
+            );
+            (path, short, true)
+        }
+        None => (name.clone().into(), name.into(), false),
+    };
+    let qualified_name = qualified_name.to_string();
+    let snippet_cap = ctx.snippet_cap();
+
+    let kind = thing.kind(db);
+    let mut rendered = match kind {
+        StructKind::Tuple => render_tuple_lit(db, snippet_cap, &fields, &qualified_name),
+        StructKind::Record => render_record_lit(db, snippet_cap, &fields, &qualified_name),
+        StructKind::Unit => {
+            RenderedLiteral { literal: qualified_name.clone(), detail: qualified_name.clone() }
+        }
+    };
+
+    if snippet_cap.is_some() {
+        rendered.literal.push_str("$0");
+    }
+
+    let mut item = CompletionItem::new(
+        CompletionItemKind::SymbolKind(thing.symbol_kind()),
+        ctx.source_range(),
+        format_literal_label(&qualified_name, kind),
+    );
+
+    item.detail(rendered.detail);
+
+    match snippet_cap {
+        Some(snippet_cap) => item.insert_snippet(snippet_cap, rendered.literal),
+        None => item.insert_text(rendered.literal),
+    };
+
+    if qualified {
+        item.lookup_by(format_literal_label(&short_qualified_name.to_string(), kind));
+    }
+    item.set_documentation(thing.docs(db)).set_deprecated(thing.is_deprecated(&ctx));
+
+    let ty = thing.ty(db);
+    item.set_relevance(CompletionRelevance {
+        type_match: compute_type_match(ctx.completion, &ty),
+        ..ctx.completion_relevance()
+    });
+    if let Some(ref_match) = compute_ref_match(completion, &ty) {
+        item.ref_match(ref_match);
+    }
+
+    if let Some(import_to_add) = ctx.import_to_add {
+        item.add_import(import_to_add);
+    }
+    Some(item.build())
+}
+
+#[derive(Clone, Copy)]
+enum Variant {
+    Struct(hir::Struct),
+    EnumVariant(hir::Variant),
+}
+
+impl Variant {
+    fn fields(self, ctx: &CompletionContext) -> Option<Vec<hir::Field>> {
+        let fields = match self {
+            Variant::Struct(it) => it.fields(ctx.db),
+            Variant::EnumVariant(it) => it.fields(ctx.db),
+        };
+        let (visible_fields, fields_omitted) = match self {
+            Variant::Struct(it) => visible_fields(ctx, &fields, it)?,
+            Variant::EnumVariant(it) => visible_fields(ctx, &fields, it)?,
+        };
+        if !fields_omitted {
+            Some(visible_fields)
+        } else {
+            None
+        }
+    }
+
+    fn kind(self, db: &dyn HirDatabase) -> StructKind {
+        match self {
+            Variant::Struct(it) => it.kind(db),
+            Variant::EnumVariant(it) => it.kind(db),
+        }
+    }
+
+    fn symbol_kind(self) -> SymbolKind {
+        match self {
+            Variant::Struct(_) => SymbolKind::Struct,
+            Variant::EnumVariant(_) => SymbolKind::Variant,
+        }
+    }
+
+    fn docs(self, db: &dyn HirDatabase) -> Option<Documentation> {
+        match self {
+            Variant::Struct(it) => it.docs(db),
+            Variant::EnumVariant(it) => it.docs(db),
+        }
+    }
+
+    fn is_deprecated(self, ctx: &RenderContext<'_>) -> bool {
+        match self {
+            Variant::Struct(it) => ctx.is_deprecated(it),
+            Variant::EnumVariant(it) => ctx.is_deprecated(it),
+        }
+    }
+
+    fn ty(self, db: &dyn HirDatabase) -> hir::Type {
+        match self {
+            Variant::Struct(it) => it.ty(db),
+            Variant::EnumVariant(it) => it.parent_enum(db).ty(db),
+        }
+    }
+}
diff --git a/crates/ide_completion/src/render/macro_.rs b/crates/ide_completion/src/render/macro_.rs
index d3b0de429ca..9f848febeb3 100644
--- a/crates/ide_completion/src/render/macro_.rs
+++ b/crates/ide_completion/src/render/macro_.rs
@@ -4,27 +4,21 @@ use hir::{Documentation, HirDisplay};
 use ide_db::SymbolKind;
 use syntax::SmolStr;
 
-use crate::{
-    context::PathKind,
-    item::{CompletionItem, ImportEdit},
-    render::RenderContext,
-};
+use crate::{context::PathKind, item::CompletionItem, render::RenderContext};
 
 pub(crate) fn render_macro(
     ctx: RenderContext<'_>,
-    import_to_add: Option<ImportEdit>,
     name: hir::Name,
     macro_: hir::Macro,
 ) -> CompletionItem {
     let _p = profile::span("render_macro");
-    render(ctx, name, macro_, import_to_add)
+    render(ctx, name, macro_)
 }
 
 fn render(
     ctx @ RenderContext { completion, .. }: RenderContext<'_>,
     name: hir::Name,
     macro_: hir::Macro,
-    import_to_add: Option<ImportEdit>,
 ) -> CompletionItem {
     let source_range = if completion.is_immediately_after_macro_bang() {
         cov_mark::hit!(completes_macro_call_if_cursor_at_bang_token);
@@ -52,12 +46,7 @@ fn render(
         .set_documentation(docs)
         .set_relevance(ctx.completion_relevance());
 
-    if let Some(import_to_add) = import_to_add {
-        item.add_import(import_to_add);
-    }
-
     let name = &*name;
-
     match ctx.snippet_cap() {
         Some(cap) if needs_bang && !completion.path_is_call() => {
             let snippet = format!("{}!{}$0{}", name, bra, ket);
@@ -73,6 +62,9 @@ fn render(
             item.insert_text(name);
         }
     };
+    if let Some(import_to_add) = ctx.import_to_add {
+        item.add_import(import_to_add);
+    }
 
     item.build()
 }
diff --git a/crates/ide_completion/src/render/pattern.rs b/crates/ide_completion/src/render/pattern.rs
index c2d51b1252c..b47be68e074 100644
--- a/crates/ide_completion/src/render/pattern.rs
+++ b/crates/ide_completion/src/render/pattern.rs
@@ -1,13 +1,13 @@
 //! Renderer for patterns.
 
-use hir::{db::HirDatabase, HasAttrs, HasVisibility, Name, StructKind};
+use hir::{db::HirDatabase, HasAttrs, Name, StructKind};
 use ide_db::SnippetCap;
 use itertools::Itertools;
 use syntax::SmolStr;
 
 use crate::{
     context::{ParamKind, PatternContext},
-    render::RenderContext,
+    render::{variant::visible_fields, RenderContext},
     CompletionItem, CompletionItemKind,
 };
 
@@ -19,7 +19,7 @@ pub(crate) fn render_struct_pat(
     let _p = profile::span("render_struct_pat");
 
     let fields = strukt.fields(ctx.db());
-    let (visible_fields, fields_omitted) = visible_fields(&ctx, &fields, strukt)?;
+    let (visible_fields, fields_omitted) = visible_fields(ctx.completion, &fields, strukt)?;
 
     if visible_fields.is_empty() {
         // Matching a struct without matching its fields is pointless, unlike matching a Variant without its fields
@@ -36,14 +36,19 @@ pub(crate) fn render_variant_pat(
     ctx: RenderContext<'_>,
     variant: hir::Variant,
     local_name: Option<Name>,
-    path: Option<hir::ModPath>,
+    path: Option<&hir::ModPath>,
+    omit_fields: bool,
 ) -> Option<CompletionItem> {
     let _p = profile::span("render_variant_pat");
 
     let fields = variant.fields(ctx.db());
-    let (visible_fields, fields_omitted) = visible_fields(&ctx, &fields, variant)?;
+    let (visible_fields, fields_omitted) = if omit_fields {
+        (Vec::new(), false)
+    } else {
+        visible_fields(ctx.completion, &fields, variant)?
+    };
 
-    let name = match &path {
+    let name = match path {
         Some(path) => path.to_string().into(),
         None => local_name.unwrap_or_else(|| variant.name(ctx.db())).to_smol_str(),
     };
@@ -78,9 +83,7 @@ fn render_pat(
     fields_omitted: bool,
 ) -> Option<String> {
     let mut pat = match kind {
-        StructKind::Tuple if ctx.snippet_cap().is_some() => {
-            render_tuple_as_pat(fields, name, fields_omitted)
-        }
+        StructKind::Tuple => render_tuple_as_pat(ctx.snippet_cap(), fields, name, fields_omitted),
         StructKind::Record => {
             render_record_as_pat(ctx.db(), ctx.snippet_cap(), fields, name, fields_omitted)
         }
@@ -113,49 +116,53 @@ fn render_record_as_pat(
     fields_omitted: bool,
 ) -> String {
     let fields = fields.iter();
-    if snippet_cap.is_some() {
-        format!(
-            "{name} {{ {}{} }}",
-            fields
-                .enumerate()
-                .map(|(idx, field)| format!("{}${}", field.name(db), idx + 1))
-                .format(", "),
-            if fields_omitted { ", .." } else { "" },
-            name = name
-        )
-    } else {
-        format!(
-            "{name} {{ {}{} }}",
-            fields.map(|field| field.name(db)).format(", "),
-            if fields_omitted { ", .." } else { "" },
-            name = name
-        )
+    match snippet_cap {
+        Some(_) => {
+            format!(
+                "{name} {{ {}{} }}",
+                fields.enumerate().format_with(", ", |(idx, field), f| {
+                    f(&format_args!("{}${}", field.name(db), idx + 1))
+                }),
+                if fields_omitted { ", .." } else { "" },
+                name = name
+            )
+        }
+        None => {
+            format!(
+                "{name} {{ {}{} }}",
+                fields.map(|field| field.name(db)).format(", "),
+                if fields_omitted { ", .." } else { "" },
+                name = name
+            )
+        }
     }
 }
 
-fn render_tuple_as_pat(fields: &[hir::Field], name: &str, fields_omitted: bool) -> String {
-    format!(
-        "{name}({}{})",
-        fields.iter().enumerate().map(|(idx, _)| format!("${}", idx + 1)).format(", "),
-        if fields_omitted { ", .." } else { "" },
-        name = name
-    )
-}
-
-fn visible_fields(
-    ctx: &RenderContext<'_>,
+fn render_tuple_as_pat(
+    snippet_cap: Option<SnippetCap>,
     fields: &[hir::Field],
-    item: impl HasAttrs,
-) -> Option<(Vec<hir::Field>, bool)> {
-    let module = ctx.completion.module?;
-    let n_fields = fields.len();
-    let fields = fields
-        .iter()
-        .filter(|field| field.is_visible_from(ctx.db(), module))
-        .copied()
-        .collect::<Vec<_>>();
-
-    let fields_omitted =
-        n_fields - fields.len() > 0 || item.attrs(ctx.db()).by_key("non_exhaustive").exists();
-    Some((fields, fields_omitted))
+    name: &str,
+    fields_omitted: bool,
+) -> String {
+    let fields = fields.iter();
+    match snippet_cap {
+        Some(_) => {
+            format!(
+                "{name}({}{})",
+                fields
+                    .enumerate()
+                    .format_with(", ", |(idx, _), f| { f(&format_args!("${}", idx + 1)) }),
+                if fields_omitted { ", .." } else { "" },
+                name = name
+            )
+        }
+        None => {
+            format!(
+                "{name}({}{})",
+                fields.enumerate().map(|(idx, _)| idx).format(", "),
+                if fields_omitted { ", .." } else { "" },
+                name = name
+            )
+        }
+    }
 }
diff --git a/crates/ide_completion/src/render/struct_literal.rs b/crates/ide_completion/src/render/struct_literal.rs
deleted file mode 100644
index b4fa26add1f..00000000000
--- a/crates/ide_completion/src/render/struct_literal.rs
+++ /dev/null
@@ -1,92 +0,0 @@
-//! Renderer for `struct` literal.
-
-use hir::{HasAttrs, Name, StructKind};
-use ide_db::SymbolKind;
-use syntax::SmolStr;
-
-use crate::{
-    render::variant::{
-        format_literal_label, render_record, render_tuple, visible_fields, RenderedLiteral,
-    },
-    render::RenderContext,
-    CompletionItem, CompletionItemKind,
-};
-
-pub(crate) fn render_struct_literal(
-    ctx: RenderContext<'_>,
-    strukt: hir::Struct,
-    path: Option<hir::ModPath>,
-    local_name: Option<Name>,
-) -> Option<CompletionItem> {
-    let _p = profile::span("render_struct_literal");
-
-    let fields = strukt.fields(ctx.db());
-    let (visible_fields, fields_omitted) = visible_fields(&ctx, &fields, strukt)?;
-
-    if fields_omitted {
-        // If some fields are private you can't make `struct` literal.
-        return None;
-    }
-
-    let name = local_name.unwrap_or_else(|| strukt.name(ctx.db())).to_smol_str();
-
-    let rendered = render_literal(&ctx, path, &name, strukt.kind(ctx.db()), &visible_fields)?;
-
-    Some(build_completion(&ctx, name, rendered, strukt.kind(ctx.db()), strukt))
-}
-
-fn build_completion(
-    ctx: &RenderContext<'_>,
-    name: SmolStr,
-    rendered: RenderedLiteral,
-    kind: StructKind,
-    def: impl HasAttrs + Copy,
-) -> CompletionItem {
-    let mut item = CompletionItem::new(
-        CompletionItemKind::SymbolKind(SymbolKind::Struct),
-        ctx.source_range(),
-        format_literal_label(&name, kind),
-    );
-
-    item.set_documentation(ctx.docs(def))
-        .set_deprecated(ctx.is_deprecated(def))
-        .detail(&rendered.detail)
-        .set_relevance(ctx.completion_relevance());
-    match ctx.snippet_cap() {
-        Some(snippet_cap) => item.insert_snippet(snippet_cap, rendered.literal),
-        None => item.insert_text(rendered.literal),
-    };
-    item.build()
-}
-
-fn render_literal(
-    ctx: &RenderContext<'_>,
-    path: Option<hir::ModPath>,
-    name: &str,
-    kind: StructKind,
-    fields: &[hir::Field],
-) -> Option<RenderedLiteral> {
-    let path_string;
-
-    let qualified_name = if let Some(path) = path {
-        path_string = path.to_string();
-        &path_string
-    } else {
-        name
-    };
-
-    let mut rendered = match kind {
-        StructKind::Tuple if ctx.snippet_cap().is_some() => {
-            render_tuple(ctx.db(), ctx.snippet_cap(), fields, Some(qualified_name))
-        }
-        StructKind::Record => {
-            render_record(ctx.db(), ctx.snippet_cap(), fields, Some(qualified_name))
-        }
-        _ => return None,
-    };
-
-    if ctx.snippet_cap().is_some() {
-        rendered.literal.push_str("$0");
-    }
-    Some(rendered)
-}
diff --git a/crates/ide_completion/src/render/union_literal.rs b/crates/ide_completion/src/render/union_literal.rs
index 209f0069821..aafedaf5aa7 100644
--- a/crates/ide_completion/src/render/union_literal.rs
+++ b/crates/ide_completion/src/render/union_literal.rs
@@ -32,7 +32,7 @@ pub(crate) fn render_union_literal(
     );
 
     let fields = un.fields(ctx.db());
-    let (fields, fields_omitted) = visible_fields(&ctx, &fields, un)?;
+    let (fields, fields_omitted) = visible_fields(ctx.completion, &fields, un)?;
 
     if fields.is_empty() {
         return None;
diff --git a/crates/ide_completion/src/render/variant.rs b/crates/ide_completion/src/render/variant.rs
index e6a92f387ea..a37b4237c45 100644
--- a/crates/ide_completion/src/render/variant.rs
+++ b/crates/ide_completion/src/render/variant.rs
@@ -1,6 +1,6 @@
 //! Code common to structs, unions, and enum variants.
 
-use crate::render::RenderContext;
+use crate::context::CompletionContext;
 use hir::{db::HirDatabase, HasAttrs, HasVisibility, HirDisplay, StructKind};
 use ide_db::SnippetCap;
 use itertools::Itertools;
@@ -16,11 +16,11 @@ pub(crate) struct RenderedLiteral {
 
 /// Render a record type (or sub-type) to a `RenderedCompound`. Use `None` for
 /// the `name` argument for an anonymous type.
-pub(crate) fn render_record(
+pub(crate) fn render_record_lit(
     db: &dyn HirDatabase,
     snippet_cap: Option<SnippetCap>,
     fields: &[hir::Field],
-    name: Option<&str>,
+    path: &str,
 ) -> RenderedLiteral {
     let completions = fields.iter().enumerate().format_with(", ", |(idx, field), f| {
         if snippet_cap.is_some() {
@@ -35,18 +35,18 @@ pub(crate) fn render_record(
     });
 
     RenderedLiteral {
-        literal: format!("{} {{ {} }}", name.unwrap_or(""), completions),
-        detail: format!("{} {{ {} }}", name.unwrap_or(""), types),
+        literal: format!("{} {{ {} }}", path, completions),
+        detail: format!("{} {{ {} }}", path, types),
     }
 }
 
 /// Render a tuple type (or sub-type) to a `RenderedCompound`. Use `None` for
 /// the `name` argument for an anonymous type.
-pub(crate) fn render_tuple(
+pub(crate) fn render_tuple_lit(
     db: &dyn HirDatabase,
     snippet_cap: Option<SnippetCap>,
     fields: &[hir::Field],
-    name: Option<&str>,
+    path: &str,
 ) -> RenderedLiteral {
     let completions = fields.iter().enumerate().format_with(", ", |(idx, _), f| {
         if snippet_cap.is_some() {
@@ -59,8 +59,8 @@ pub(crate) fn render_tuple(
     let types = fields.iter().format_with(", ", |field, f| f(&field.ty(db).display(db)));
 
     RenderedLiteral {
-        literal: format!("{}({})", name.unwrap_or(""), completions),
-        detail: format!("{}({})", name.unwrap_or(""), types),
+        literal: format!("{}({})", path, completions),
+        detail: format!("{}({})", path, types),
     }
 }
 
@@ -68,20 +68,20 @@ pub(crate) fn render_tuple(
 /// fields, plus a boolean for whether the list is comprehensive (contains no
 /// private fields and its item is not marked `#[non_exhaustive]`).
 pub(crate) fn visible_fields(
-    ctx: &RenderContext<'_>,
+    ctx: &CompletionContext,
     fields: &[hir::Field],
     item: impl HasAttrs,
 ) -> Option<(Vec<hir::Field>, bool)> {
-    let module = ctx.completion.module?;
+    let module = ctx.module?;
     let n_fields = fields.len();
     let fields = fields
         .iter()
-        .filter(|field| field.is_visible_from(ctx.db(), module))
+        .filter(|field| field.is_visible_from(ctx.db, module))
         .copied()
         .collect::<Vec<_>>();
 
     let fields_omitted =
-        n_fields - fields.len() > 0 || item.attrs(ctx.db()).by_key("non_exhaustive").exists();
+        n_fields - fields.len() > 0 || item.attrs(ctx.db).by_key("non_exhaustive").exists();
     Some((fields, fields_omitted))
 }
 
diff --git a/crates/ide_completion/src/tests/pattern.rs b/crates/ide_completion/src/tests/pattern.rs
index 28b6ba6a374..aa8cb36f040 100644
--- a/crates/ide_completion/src/tests/pattern.rs
+++ b/crates/ide_completion/src/tests/pattern.rs
@@ -124,6 +124,7 @@ fn foo() {
             st Unit
             ma makro!(…) macro_rules! makro
             bn TupleV    TupleV($1)$0
+            bn TupleV    TupleV()$0
             ev TupleV
             ct CONST
         "#]],
@@ -150,6 +151,7 @@ fn foo() {
             bn Tuple             Tuple($1)$0
             st Tuple
             ev Variant
+            md module
             en SingleVariantEnum
             st Unit
             ma makro!(…)         macro_rules! makro
@@ -171,6 +173,7 @@ fn foo(a$0) {
             st Record
             bn Tuple     Tuple($1): Tuple$0
             st Tuple
+            md module
             st Unit
             ma makro!(…) macro_rules! makro
         "#]],
@@ -187,6 +190,7 @@ fn foo(a$0: Tuple) {
             st Record
             bn Tuple     Tuple($1)$0
             st Tuple
+            md module
             st Unit
             ma makro!(…) macro_rules! makro
         "#]],
@@ -228,7 +232,6 @@ fn foo() {
         expect![[r#"
             kw ref
             kw mut
-            ev E::X  E::X
             en E
             ma m!(…) macro_rules! m
         "#]],
@@ -426,9 +429,7 @@ fn foo() {
     }
 }
 "#,
-        expect![[r#"
-            ev TupleVariant(…) TupleVariant(u32)
-        "#]],
+        expect![[r#""#]],
     );
     check_empty(
         r#"
@@ -441,8 +442,6 @@ fn foo() {
     }
 }
 "#,
-        expect![[r#"
-            ev RecordVariant {…} RecordVariant { field: u32 }
-        "#]],
+        expect![[r#""#]],
     );
 }