Repository: rust-lang/rust-analyzer
Pull request #12599 (2022-06-20 18:14:25)

fix: Only apply `cfg(test)` for local crates
Don't analyze dependencies with `test`; this should fix various cases where crates use `cfg(not(test))` and so we didn't find things.

"Local" here currently means anything that's not from the registry, so anything inside the workspace, but also path dependencies. So this isn't perfect, and users might still need to use `rust-analyzer.cargo.unsetTest` for these in some cases.


Patch:
diff --git a/crates/ide-completion/src/completions.rs b/crates/ide-completion/src/completions.rs
index 4f522ee761..284372f7fc 100644
--- a/crates/ide-completion/src/completions.rs
+++ b/crates/ide-completion/src/completions.rs
@@ -22,15 +22,12 @@ pub(crate) mod vis;
 
 use std::iter;
 
-use hir::{known, ScopeDef};
-use ide_db::{imports::import_assets::LocatedImport, SymbolKind};
+use hir::{db::HirDatabase, known, ScopeDef};
+use ide_db::SymbolKind;
 use syntax::ast;
 
 use crate::{
-    context::{
-        DotAccess, ItemListKind, NameContext, NameKind, NameRefContext, NameRefKind,
-        PathCompletionCtx, PathKind, PatternContext, TypeLocation, Visible,
-    },
+    context::Visible,
     item::Builder,
     render::{
         const_::render_const,
@@ -38,7 +35,7 @@ use crate::{
         literal::{render_struct_literal, render_variant_lit},
         macro_::render_macro,
         pattern::{render_struct_pat, render_variant_pat},
-        render_field, render_path_resolution, render_resolution_simple, render_tuple_field,
+        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,
@@ -46,6 +43,22 @@ use crate::{
     CompletionContext, CompletionItem, CompletionItemKind,
 };
 
+fn module_or_attr(db: &dyn HirDatabase, def: ScopeDef) -> Option<ScopeDef> {
+    match def {
+        ScopeDef::ModuleDef(hir::ModuleDef::Macro(m)) if m.is_attr(db) => Some(def),
+        ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) => Some(def),
+        _ => None,
+    }
+}
+
+fn module_or_fn_macro(db: &dyn HirDatabase, def: ScopeDef) -> Option<ScopeDef> {
+    match def {
+        ScopeDef::ModuleDef(hir::ModuleDef::Macro(m)) if m.is_fn_like(db) => Some(def),
+        ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) => Some(def),
+        _ => None,
+    }
+}
+
 /// Represents an in-progress set of completions being built.
 #[derive(Debug, Default)]
 pub struct Completions {
@@ -137,16 +150,15 @@ impl Completions {
     pub(crate) fn add_crate_roots(&mut self, ctx: &CompletionContext) {
         ctx.process_all_names(&mut |name, res| match res {
             ScopeDef::ModuleDef(hir::ModuleDef::Module(m)) if m.is_crate_root(ctx.db) => {
-                self.add_module(ctx, m, name);
+                self.add_resolution(ctx, name, res);
             }
             _ => (),
         });
     }
 
-    pub(crate) fn add_path_resolution(
+    pub(crate) fn add_resolution(
         &mut self,
         ctx: &CompletionContext,
-        path_ctx: &PathCompletionCtx,
         local_name: hir::Name,
         resolution: hir::ScopeDef,
     ) {
@@ -154,10 +166,7 @@ impl Completions {
             cov_mark::hit!(qualified_path_doc_hidden);
             return;
         }
-        self.add(
-            render_path_resolution(RenderContext::new(ctx), path_ctx, local_name, resolution)
-                .build(),
-        );
+        self.add(render_resolution(RenderContext::new(ctx), local_name, resolution).build());
     }
 
     pub(crate) fn add_resolution_simple(
@@ -172,19 +181,9 @@ impl Completions {
         self.add(render_resolution_simple(RenderContext::new(ctx), local_name, resolution).build());
     }
 
-    pub(crate) fn add_module(
-        &mut self,
-        ctx: &CompletionContext,
-        module: hir::Module,
-        local_name: hir::Name,
-    ) {
-        self.add_resolution_simple(ctx, local_name, hir::ScopeDef::ModuleDef(module.into()));
-    }
-
     pub(crate) fn add_macro(
         &mut self,
         ctx: &CompletionContext,
-        path_ctx: &PathCompletionCtx,
         mac: hir::Macro,
         local_name: hir::Name,
     ) {
@@ -196,7 +195,6 @@ impl Completions {
         self.add(
             render_macro(
                 RenderContext::new(ctx).private_editable(is_private_editable),
-                path_ctx,
                 local_name,
                 mac,
             )
@@ -207,7 +205,6 @@ impl Completions {
     pub(crate) fn add_function(
         &mut self,
         ctx: &CompletionContext,
-        path_ctx: &PathCompletionCtx,
         func: hir::Function,
         local_name: Option<hir::Name>,
     ) {
@@ -219,7 +216,6 @@ impl Completions {
         self.add(
             render_fn(
                 RenderContext::new(ctx).private_editable(is_private_editable),
-                path_ctx,
                 local_name,
                 func,
             )
@@ -230,7 +226,6 @@ impl Completions {
     pub(crate) fn add_method(
         &mut self,
         ctx: &CompletionContext,
-        dot_access: &DotAccess,
         func: hir::Function,
         receiver: Option<hir::Name>,
         local_name: Option<hir::Name>,
@@ -243,7 +238,6 @@ impl Completions {
         self.add(
             render_method(
                 RenderContext::new(ctx).private_editable(is_private_editable),
-                dot_access,
                 receiver,
                 local_name,
                 func,
@@ -252,32 +246,6 @@ impl Completions {
         );
     }
 
-    pub(crate) fn add_method_with_import(
-        &mut self,
-        ctx: &CompletionContext,
-        dot_access: &DotAccess,
-        func: hir::Function,
-        import: LocatedImport,
-    ) {
-        let is_private_editable = match ctx.is_visible(&func) {
-            Visible::Yes => false,
-            Visible::Editable => true,
-            Visible::No => return,
-        };
-        self.add(
-            render_method(
-                RenderContext::new(ctx)
-                    .private_editable(is_private_editable)
-                    .import_to_add(Some(import)),
-                dot_access,
-                None,
-                None,
-                func,
-            )
-            .build(),
-        );
-    }
-
     pub(crate) fn add_const(&mut self, ctx: &CompletionContext, konst: hir::Const) {
         let is_private_editable = match ctx.is_visible(&konst) {
             Visible::Yes => false,
@@ -313,12 +281,11 @@ impl Completions {
     pub(crate) fn add_qualified_enum_variant(
         &mut self,
         ctx: &CompletionContext,
-        path_ctx: &PathCompletionCtx,
         variant: hir::Variant,
         path: hir::ModPath,
     ) {
         if let Some(builder) =
-            render_variant_lit(RenderContext::new(ctx), path_ctx, None, variant, Some(path))
+            render_variant_lit(RenderContext::new(ctx), None, variant, Some(path))
         {
             self.add(builder.build());
         }
@@ -327,12 +294,11 @@ impl Completions {
     pub(crate) fn add_enum_variant(
         &mut self,
         ctx: &CompletionContext,
-        path_ctx: &PathCompletionCtx,
         variant: hir::Variant,
         local_name: Option<hir::Name>,
     ) {
         if let Some(builder) =
-            render_variant_lit(RenderContext::new(ctx), path_ctx, local_name, variant, None)
+            render_variant_lit(RenderContext::new(ctx), local_name, variant, None)
         {
             self.add(builder.build());
         }
@@ -362,13 +328,12 @@ impl Completions {
     pub(crate) fn add_struct_literal(
         &mut self,
         ctx: &CompletionContext,
-        path_ctx: &PathCompletionCtx,
         strukt: hir::Struct,
         path: Option<hir::ModPath>,
         local_name: Option<hir::Name>,
     ) {
         if let Some(builder) =
-            render_struct_literal(RenderContext::new(ctx), path_ctx, strukt, path, local_name)
+            render_struct_literal(RenderContext::new(ctx), strukt, path, local_name)
         {
             self.add(builder.build());
         }
@@ -408,13 +373,11 @@ impl Completions {
     pub(crate) fn add_variant_pat(
         &mut self,
         ctx: &CompletionContext,
-        pattern_ctx: &PatternContext,
         variant: hir::Variant,
         local_name: Option<hir::Name>,
     ) {
         self.add_opt(render_variant_pat(
             RenderContext::new(ctx),
-            pattern_ctx,
             variant,
             local_name.clone(),
             None,
@@ -424,22 +387,20 @@ impl Completions {
     pub(crate) fn add_qualified_variant_pat(
         &mut self,
         ctx: &CompletionContext,
-        pattern_ctx: &PatternContext,
         variant: hir::Variant,
         path: hir::ModPath,
     ) {
         let path = Some(&path);
-        self.add_opt(render_variant_pat(RenderContext::new(ctx), pattern_ctx, variant, None, path));
+        self.add_opt(render_variant_pat(RenderContext::new(ctx), variant, None, path));
     }
 
     pub(crate) fn add_struct_pat(
         &mut self,
         ctx: &CompletionContext,
-        pattern_ctx: &PatternContext,
         strukt: hir::Struct,
         local_name: Option<hir::Name>,
     ) {
-        self.add_opt(render_struct_pat(RenderContext::new(ctx), pattern_ctx, strukt, local_name));
+        self.add_opt(render_struct_pat(RenderContext::new(ctx), strukt, local_name));
     }
 }
 
@@ -476,128 +437,3 @@ fn enum_variants_with_paths(
         }
     }
 }
-
-pub(super) fn complete_name(
-    acc: &mut Completions,
-    ctx: &CompletionContext,
-    NameContext { name, kind }: &NameContext,
-) {
-    match kind {
-        NameKind::Const => {
-            item_list::trait_impl::complete_trait_impl_const(acc, ctx, name);
-        }
-        NameKind::Function => {
-            item_list::trait_impl::complete_trait_impl_fn(acc, ctx, name);
-        }
-        NameKind::IdentPat(pattern_ctx) => {
-            if ctx.token.kind() != syntax::T![_] {
-                complete_patterns(acc, ctx, pattern_ctx)
-            }
-        }
-        NameKind::Module(mod_under_caret) => {
-            mod_::complete_mod(acc, ctx, mod_under_caret);
-        }
-        NameKind::TypeAlias => {
-            item_list::trait_impl::complete_trait_impl_type_alias(acc, ctx, name);
-        }
-        NameKind::RecordField => {
-            field::complete_field_list_record_variant(acc, ctx);
-        }
-        NameKind::ConstParam
-        | NameKind::Enum
-        | NameKind::MacroDef
-        | NameKind::MacroRules
-        | NameKind::Rename
-        | NameKind::SelfParam
-        | NameKind::Static
-        | NameKind::Struct
-        | NameKind::Trait
-        | NameKind::TypeParam
-        | NameKind::Union
-        | NameKind::Variant => (),
-    }
-}
-
-pub(super) fn complete_name_ref(
-    acc: &mut Completions,
-    ctx: &CompletionContext,
-    NameRefContext { nameref, kind }: &NameRefContext,
-) {
-    match kind {
-        NameRefKind::Path(path_ctx) => {
-            flyimport::import_on_the_fly_path(acc, ctx, path_ctx);
-
-            match &path_ctx.kind {
-                PathKind::Expr { expr_ctx } => {
-                    expr::complete_expr_path(acc, ctx, path_ctx, expr_ctx);
-
-                    dot::complete_undotted_self(acc, ctx, path_ctx, expr_ctx);
-                    item_list::complete_item_list_in_expr(acc, ctx, path_ctx, expr_ctx);
-                    record::complete_record_expr_func_update(acc, ctx, path_ctx, expr_ctx);
-                    snippet::complete_expr_snippet(acc, ctx, path_ctx, expr_ctx);
-                }
-                PathKind::Type { location } => {
-                    r#type::complete_type_path(acc, ctx, path_ctx, location);
-
-                    match location {
-                        TypeLocation::TupleField => {
-                            field::complete_field_list_tuple_variant(acc, ctx, path_ctx);
-                        }
-                        TypeLocation::TypeAscription(ascription) => {
-                            r#type::complete_ascribed_type(acc, ctx, path_ctx, ascription);
-                        }
-                        TypeLocation::GenericArgList(_)
-                        | TypeLocation::TypeBound
-                        | TypeLocation::ImplTarget
-                        | TypeLocation::ImplTrait
-                        | TypeLocation::Other => (),
-                    }
-                }
-                PathKind::Attr { attr_ctx } => {
-                    attribute::complete_attribute_path(acc, ctx, path_ctx, attr_ctx);
-                }
-                PathKind::Derive { existing_derives } => {
-                    attribute::complete_derive_path(acc, ctx, path_ctx, existing_derives);
-                }
-                PathKind::Item { kind } => {
-                    item_list::complete_item_list(acc, ctx, path_ctx, kind);
-
-                    snippet::complete_item_snippet(acc, ctx, path_ctx, kind);
-                    if let ItemListKind::TraitImpl(impl_) = kind {
-                        item_list::trait_impl::complete_trait_impl_item_by_name(
-                            acc, ctx, path_ctx, nameref, impl_,
-                        );
-                    }
-                }
-                PathKind::Pat { .. } => {
-                    pattern::complete_pattern_path(acc, ctx, path_ctx);
-                }
-                PathKind::Vis { has_in_token } => {
-                    vis::complete_vis_path(acc, ctx, path_ctx, has_in_token);
-                }
-                PathKind::Use => {
-                    use_::complete_use_path(acc, ctx, path_ctx, nameref);
-                }
-            }
-        }
-        NameRefKind::DotAccess(dot_access) => {
-            flyimport::import_on_the_fly_dot(acc, ctx, dot_access);
-            dot::complete_dot(acc, ctx, dot_access);
-            postfix::complete_postfix(acc, ctx, dot_access);
-        }
-        NameRefKind::Keyword(item) => {
-            keyword::complete_for_and_where(acc, ctx, item);
-        }
-        NameRefKind::RecordExpr { dot_prefix, expr } => {
-            record::complete_record_expr_fields(acc, ctx, expr, dot_prefix);
-        }
-        NameRefKind::Pattern(pattern_ctx) => complete_patterns(acc, ctx, pattern_ctx),
-    }
-}
-
-fn complete_patterns(acc: &mut Completions, ctx: &CompletionContext, pattern_ctx: &PatternContext) {
-    flyimport::import_on_the_fly_pat(acc, ctx, pattern_ctx);
-    fn_param::complete_fn_param(acc, ctx, pattern_ctx);
-    pattern::complete_pattern(acc, ctx, pattern_ctx);
-    record::complete_record_pattern_fields(acc, ctx, pattern_ctx);
-}
diff --git a/crates/ide-completion/src/completions/attribute.rs b/crates/ide-completion/src/completions/attribute.rs
index 1c4f9a3113..992d7eabd8 100644
--- a/crates/ide-completion/src/completions/attribute.rs
+++ b/crates/ide-completion/src/completions/attribute.rs
@@ -17,7 +17,8 @@ use syntax::{
 };
 
 use crate::{
-    context::{AttrCtx, CompletionContext, PathCompletionCtx, Qualified},
+    completions::module_or_attr,
+    context::{CompletionContext, PathCompletionCtx, PathKind, Qualified},
     item::CompletionItem,
     Completions,
 };
@@ -27,13 +28,12 @@ mod derive;
 mod lint;
 mod repr;
 
-pub(crate) use self::derive::complete_derive_path;
+pub(crate) use self::derive::complete_derive;
 
 /// Complete inputs to known builtin attributes as well as derive attributes
 pub(crate) fn complete_known_attribute_input(
     acc: &mut Completions,
     ctx: &CompletionContext,
-    &colon_prefix: &bool,
     fake_attribute_under_caret: &ast::Attr,
 ) -> Option<()> {
     let attribute = fake_attribute_under_caret;
@@ -48,9 +48,7 @@ pub(crate) fn complete_known_attribute_input(
 
     match path.text().as_str() {
         "repr" => repr::complete_repr(acc, ctx, tt),
-        "feature" => {
-            lint::complete_lint(acc, ctx, colon_prefix, &parse_tt_as_comma_sep_paths(tt)?, FEATURES)
-        }
+        "feature" => lint::complete_lint(acc, ctx, &parse_tt_as_comma_sep_paths(tt)?, FEATURES),
         "allow" | "warn" | "deny" | "forbid" => {
             let existing_lints = parse_tt_as_comma_sep_paths(tt)?;
 
@@ -63,7 +61,7 @@ pub(crate) fn complete_known_attribute_input(
                 .cloned()
                 .collect();
 
-            lint::complete_lint(acc, ctx, colon_prefix, &existing_lints, &lints);
+            lint::complete_lint(acc, ctx, &existing_lints, &lints);
         }
         "cfg" => cfg::complete_cfg(acc, ctx),
         _ => (),
@@ -71,13 +69,19 @@ pub(crate) fn complete_known_attribute_input(
     Some(())
 }
 
-pub(crate) fn complete_attribute_path(
+pub(crate) fn complete_attribute(
     acc: &mut Completions,
     ctx: &CompletionContext,
-    path_ctx @ PathCompletionCtx { qualified, .. }: &PathCompletionCtx,
-    &AttrCtx { kind, annotated_item_kind }: &AttrCtx,
+    path_ctx: &PathCompletionCtx,
 ) {
-    let is_inner = kind == AttrKind::Inner;
+    let (qualified, is_inner, annotated_item_kind) = match path_ctx {
+        &PathCompletionCtx {
+            kind: PathKind::Attr { kind, annotated_item_kind },
+            ref qualified,
+            ..
+        } => (qualified, kind == AttrKind::Inner, annotated_item_kind),
+        _ => return,
+    };
 
     match qualified {
         Qualified::With {
@@ -90,14 +94,8 @@ pub(crate) fn complete_attribute_path(
             }
 
             for (name, def) in module.scope(ctx.db, Some(ctx.module)) {
-                match def {
-                    hir::ScopeDef::ModuleDef(hir::ModuleDef::Macro(m)) if m.is_attr(ctx.db) => {
-                        acc.add_macro(ctx, path_ctx, m, name)
-                    }
-                    hir::ScopeDef::ModuleDef(hir::ModuleDef::Module(m)) => {
-                        acc.add_module(ctx, m, name)
-                    }
-                    _ => (),
+                if let Some(def) = module_or_attr(ctx.db, def) {
+                    acc.add_resolution(ctx, name, def);
                 }
             }
             return;
@@ -106,12 +104,10 @@ pub(crate) fn complete_attribute_path(
         Qualified::Absolute => acc.add_crate_roots(ctx),
         // only show modules in a fresh UseTree
         Qualified::No => {
-            ctx.process_all_names(&mut |name, def| match def {
-                hir::ScopeDef::ModuleDef(hir::ModuleDef::Macro(m)) if m.is_attr(ctx.db) => {
-                    acc.add_macro(ctx, path_ctx, m, name)
+            ctx.process_all_names(&mut |name, def| {
+                if let Some(def) = module_or_attr(ctx.db, def) {
+                    acc.add_resolution(ctx, name, def);
                 }
-                hir::ScopeDef::ModuleDef(hir::ModuleDef::Module(m)) => acc.add_module(ctx, m, name),
-                _ => (),
             });
             acc.add_nameref_keywords_with_colon(ctx);
         }
diff --git a/crates/ide-completion/src/completions/attribute/derive.rs b/crates/ide-completion/src/completions/attribute/derive.rs
index 21298b6ca5..0927d2f764 100644
--- a/crates/ide-completion/src/completions/attribute/derive.rs
+++ b/crates/ide-completion/src/completions/attribute/derive.rs
@@ -5,17 +5,23 @@ use itertools::Itertools;
 use syntax::SmolStr;
 
 use crate::{
-    context::{CompletionContext, ExistingDerives, PathCompletionCtx, Qualified},
+    context::{CompletionContext, PathCompletionCtx, PathKind, Qualified},
     item::CompletionItem,
     Completions,
 };
 
-pub(crate) fn complete_derive_path(
+pub(crate) fn complete_derive(
     acc: &mut Completions,
     ctx: &CompletionContext,
-    path_ctx @ PathCompletionCtx { qualified, .. }: &PathCompletionCtx,
-    existing_derives: &ExistingDerives,
+    path_ctx: &PathCompletionCtx,
 ) {
+    let (qualified, existing_derives) = match path_ctx {
+        PathCompletionCtx { kind: PathKind::Derive { existing_derives }, qualified, .. } => {
+            (qualified, existing_derives)
+        }
+        _ => return,
+    };
+
     let core = ctx.famous_defs().core();
 
     match qualified {
@@ -29,14 +35,15 @@ pub(crate) fn complete_derive_path(
             }
 
             for (name, def) in module.scope(ctx.db, Some(ctx.module)) {
-                match def {
-                    ScopeDef::ModuleDef(hir::ModuleDef::Macro(mac))
-                        if !existing_derives.contains(&mac) && mac.is_derive(ctx.db) =>
-                    {
-                        acc.add_macro(ctx, path_ctx, mac, name)
+                let add_def = match def {
+                    ScopeDef::ModuleDef(hir::ModuleDef::Macro(mac)) => {
+                        !existing_derives.contains(&mac) && mac.is_derive(ctx.db)
                     }
-                    ScopeDef::ModuleDef(hir::ModuleDef::Module(m)) => acc.add_module(ctx, m, name),
-                    _ => (),
+                    ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) => true,
+                    _ => false,
+                };
+                if add_def {
+                    acc.add_resolution(ctx, name, def);
                 }
             }
         }
@@ -50,8 +57,8 @@ pub(crate) fn complete_derive_path(
                     {
                         mac
                     }
-                    ScopeDef::ModuleDef(hir::ModuleDef::Module(m)) => {
-                        return acc.add_module(ctx, m, name);
+                    ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) => {
+                        return acc.add_resolution(ctx, name, def);
                     }
                     _ => return,
                 };
@@ -59,7 +66,7 @@ pub(crate) fn complete_derive_path(
                 match (core, mac.module(ctx.db).krate()) {
                     // show derive dependencies for `core`/`std` derives
                     (Some(core), mac_krate) if core == mac_krate => {}
-                    _ => return acc.add_macro(ctx, path_ctx, mac, name),
+                    _ => return acc.add_resolution(ctx, name, def),
                 };
 
                 let name_ = name.to_smol_str();
@@ -92,7 +99,7 @@ pub(crate) fn complete_derive_path(
                         item.lookup_by(lookup);
                         item.add_to(acc);
                     }
-                    None => acc.add_macro(ctx, path_ctx, mac, name),
+                    None => acc.add_resolution(ctx, name, def),
                 }
             });
             acc.add_nameref_keywords_with_colon(ctx);
diff --git a/crates/ide-completion/src/completions/attribute/lint.rs b/crates/ide-completion/src/completions/attribute/lint.rs
index 5c04810318..8991d657e8 100644
--- a/crates/ide-completion/src/completions/attribute/lint.rs
+++ b/crates/ide-completion/src/completions/attribute/lint.rs
@@ -1,16 +1,16 @@
 //! Completion for lints
 use ide_db::{generated::lints::Lint, SymbolKind};
-use syntax::ast;
+use syntax::{ast, T};
 
 use crate::{context::CompletionContext, item::CompletionItem, Completions};
 
 pub(super) fn complete_lint(
     acc: &mut Completions,
     ctx: &CompletionContext,
-    is_qualified: bool,
     existing_lints: &[ast::Path],
     lints_completions: &[Lint],
 ) {
+    let is_qualified = ctx.previous_token_is(T![:]);
     for &Lint { label, description } in lints_completions {
         let (qual, name) = {
             // FIXME: change `Lint`'s label to not store a path in it but split the prefix off instead?
diff --git a/crates/ide-completion/src/completions/dot.rs b/crates/ide-completion/src/completions/dot.rs
index b58a9f39f2..da26c2ad68 100644
--- a/crates/ide-completion/src/completions/dot.rs
+++ b/crates/ide-completion/src/completions/dot.rs
@@ -3,7 +3,9 @@
 use ide_db::FxHashSet;
 
 use crate::{
-    context::{CompletionContext, DotAccess, DotAccessKind, ExprCtx, PathCompletionCtx, Qualified},
+    context::{
+        CompletionContext, DotAccess, DotAccessKind, PathCompletionCtx, PathKind, Qualified,
+    },
     CompletionItem, CompletionItemKind, Completions,
 };
 
@@ -33,29 +35,23 @@ pub(crate) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext, dot_a
             |acc, field, ty| acc.add_tuple_field(ctx, None, field, &ty),
         );
     }
-    complete_methods(ctx, &receiver_ty, |func| acc.add_method(ctx, dot_access, func, None, None));
+    complete_methods(ctx, &receiver_ty, |func| acc.add_method(ctx, func, None, None));
 }
 
 pub(crate) fn complete_undotted_self(
     acc: &mut Completions,
     ctx: &CompletionContext,
     path_ctx: &PathCompletionCtx,
-    expr_ctx: &ExprCtx,
 ) {
     if !ctx.config.enable_self_on_the_fly {
         return;
     }
-    if !path_ctx.is_trivial_path() {
-        return;
-    }
-    if !ctx.qualifier_ctx.none() {
-        return;
-    }
-    if !matches!(path_ctx.qualified, Qualified::No) {
-        return;
-    }
-    let self_param = match expr_ctx {
-        ExprCtx { self_param: Some(self_param), .. } => self_param,
+    let self_param = match path_ctx {
+        PathCompletionCtx {
+            qualified: Qualified::No,
+            kind: PathKind::Expr { self_param: Some(self_param), .. },
+            ..
+        } if path_ctx.is_trivial_path() && ctx.qualifier_ctx.none() => self_param,
         _ => return,
     };
 
@@ -68,17 +64,7 @@ pub(crate) fn complete_undotted_self(
         |acc, field, ty| acc.add_tuple_field(ctx, Some(hir::known::SELF_PARAM), field, &ty),
     );
     complete_methods(ctx, &ty, |func| {
-        acc.add_method(
-            ctx,
-            &DotAccess {
-                receiver: None,
-                receiver_ty: None,
-                kind: DotAccessKind::Method { has_parens: false },
-            },
-            func,
-            Some(hir::known::SELF_PARAM),
-            None,
-        )
+        acc.add_method(ctx, func, Some(hir::known::SELF_PARAM), None)
     });
 }
 
diff --git a/crates/ide-completion/src/completions/expr.rs b/crates/ide-completion/src/completions/expr.rs
index 9c003be6af..83ecb51aaa 100644
--- a/crates/ide-completion/src/completions/expr.rs
+++ b/crates/ide-completion/src/completions/expr.rs
@@ -4,42 +4,68 @@ use hir::ScopeDef;
 use ide_db::FxHashSet;
 
 use crate::{
-    context::{ExprCtx, PathCompletionCtx, Qualified},
+    context::{PathCompletionCtx, PathKind, Qualified},
     CompletionContext, Completions,
 };
 
 pub(crate) fn complete_expr_path(
     acc: &mut Completions,
     ctx: &CompletionContext,
-    path_ctx @ PathCompletionCtx { qualified, .. }: &PathCompletionCtx,
-    &ExprCtx {
-        in_block_expr,
-        in_loop_body,
-        after_if_expr,
-        in_condition,
-        incomplete_let,
-        ref ref_expr_parent,
-        ref is_func_update,
-        ref innermost_ret_ty,
-        ref impl_,
-        ..
-    }: &ExprCtx,
+    path_ctx: &PathCompletionCtx,
 ) {
     let _p = profile::span("complete_expr_path");
     if !ctx.qualifier_ctx.none() {
         return;
     }
-
-    let wants_mut_token =
-        ref_expr_parent.as_ref().map(|it| it.mut_token().is_none()).unwrap_or(false);
+    let (
+        qualified,
+        in_block_expr,
+        in_loop_body,
+        is_func_update,
+        after_if_expr,
+        wants_mut_token,
+        in_condition,
+        ty,
+        incomplete_let,
+        impl_,
+    ) = match path_ctx {
+        &PathCompletionCtx {
+            kind:
+                PathKind::Expr {
+                    in_block_expr,
+                    in_loop_body,
+                    after_if_expr,
+                    in_condition,
+                    incomplete_let,
+                    ref ref_expr_parent,
+                    ref is_func_update,
+                    ref innermost_ret_ty,
+                    ref impl_,
+                    ..
+                },
+            ref qualified,
+            ..
+        } => (
+            qualified,
+            in_block_expr,
+            in_loop_body,
+            is_func_update.is_some(),
+            after_if_expr,
+            ref_expr_parent.as_ref().map(|it| it.mut_token().is_none()).unwrap_or(false),
+            in_condition,
+            innermost_ret_ty,
+            incomplete_let,
+            impl_,
+        ),
+        _ => return,
+    };
 
     let scope_def_applicable = |def| {
+        use hir::{GenericParam::*, ModuleDef::*};
         match def {
-            ScopeDef::GenericParam(hir::GenericParam::LifetimeParam(_)) | ScopeDef::Label(_) => {
-                false
-            }
+            ScopeDef::GenericParam(LifetimeParam(_)) | ScopeDef::Label(_) => false,
             // Don't suggest attribute macros and derives.
-            ScopeDef::ModuleDef(hir::ModuleDef::Macro(mac)) => mac.is_fn_like(ctx.db),
+            ScopeDef::ModuleDef(Macro(mac)) => mac.is_fn_like(ctx.db),
             _ => true,
         }
     };
@@ -50,7 +76,7 @@ pub(crate) fn complete_expr_path(
             .0
             .into_iter()
             .flat_map(|it| hir::Trait::from(it).items(ctx.sema.db))
-            .for_each(|item| add_assoc_item(acc, ctx, path_ctx, item)),
+            .for_each(|item| add_assoc_item(acc, ctx, item)),
         Qualified::With { resolution: None, .. } => {}
         Qualified::With { resolution: Some(resolution), .. } => {
             // Add associated types on type parameters and `Self`.
@@ -63,7 +89,7 @@ pub(crate) fn complete_expr_path(
                     let module_scope = module.scope(ctx.db, Some(ctx.module));
                     for (name, def) in module_scope {
                         if scope_def_applicable(def) {
-                            acc.add_path_resolution(ctx, path_ctx, name, def);
+                            acc.add_resolution(ctx, name, def);
                         }
                     }
                 }
@@ -74,7 +100,7 @@ pub(crate) fn complete_expr_path(
                     | hir::ModuleDef::BuiltinType(_)),
                 ) => {
                     if let &hir::ModuleDef::Adt(hir::Adt::Enum(e)) = def {
-                        add_enum_variants(acc, ctx, path_ctx, e);
+                        add_enum_variants(acc, ctx, e);
                     }
                     let ty = match def {
                         hir::ModuleDef::Adt(adt) => adt.ty(ctx.db),
@@ -82,7 +108,7 @@ pub(crate) fn complete_expr_path(
                             let ty = a.ty(ctx.db);
                             if let Some(hir::Adt::Enum(e)) = ty.as_adt() {
                                 cov_mark::hit!(completes_variant_through_alias);
-                                add_enum_variants(acc, ctx, path_ctx, e);
+                                add_enum_variants(acc, ctx, e);
                             }
                             ty
                         }
@@ -103,7 +129,7 @@ pub(crate) fn complete_expr_path(
                         Some(ctx.module),
                         None,
                         |item| {
-                            add_assoc_item(acc, ctx, path_ctx, item);
+                            add_assoc_item(acc, ctx, item);
                             None::<()>
                         },
                     );
@@ -119,7 +145,7 @@ pub(crate) fn complete_expr_path(
                 hir::PathResolution::Def(hir::ModuleDef::Trait(t)) => {
                     // Handles `Trait::assoc` as well as `<Ty as Trait>::assoc`.
                     for item in t.items(ctx.db) {
-                        add_assoc_item(acc, ctx, path_ctx, item);
+                        add_assoc_item(acc, ctx, item);
                     }
                 }
                 hir::PathResolution::TypeParam(_) | hir::PathResolution::SelfType(_) => {
@@ -130,7 +156,7 @@ pub(crate) fn complete_expr_path(
                     };
 
                     if let Some(hir::Adt::Enum(e)) = ty.as_adt() {
-                        add_enum_variants(acc, ctx, path_ctx, e);
+                        add_enum_variants(acc, ctx, e);
                     }
                     let mut seen = FxHashSet::default();
                     ty.iterate_path_candidates(
@@ -143,7 +169,7 @@ pub(crate) fn complete_expr_path(
                             // We might iterate candidates of a trait multiple times here, so deduplicate
                             // them.
                             if seen.insert(item) {
-                                add_assoc_item(acc, ctx, path_ctx, item);
+                                add_assoc_item(acc, ctx, item);
                             }
                             None::<()>
                         },
@@ -168,16 +194,10 @@ pub(crate) fn complete_expr_path(
                             .find_use_path(ctx.db, hir::ModuleDef::from(strukt))
                             .filter(|it| it.len() > 1);
 
-                        acc.add_struct_literal(ctx, path_ctx, strukt, path, None);
+                        acc.add_struct_literal(ctx, strukt, path, None);
 
                         if complete_self {
-                            acc.add_struct_literal(
-                                ctx,
-                                path_ctx,
-                                strukt,
-                                None,
-                                Some(hir::known::SELF_TYPE),
-                            );
+                            acc.add_struct_literal(ctx, strukt, None, Some(hir::known::SELF_TYPE));
                         }
                     }
                     hir::Adt::Union(un) => {
@@ -198,7 +218,7 @@ pub(crate) fn complete_expr_path(
                             e,
                             impl_,
                             |acc, ctx, variant, path| {
-                                acc.add_qualified_enum_variant(ctx, path_ctx, variant, path)
+                                acc.add_qualified_enum_variant(ctx, variant, path)
                             },
                         );
                     }
@@ -206,11 +226,11 @@ pub(crate) fn complete_expr_path(
             }
             ctx.process_all_names(&mut |name, def| {
                 if scope_def_applicable(def) {
-                    acc.add_path_resolution(ctx, path_ctx, name, def);
+                    acc.add_resolution(ctx, name, def);
                 }
             });
 
-            if is_func_update.is_none() {
+            if !is_func_update {
                 let mut add_keyword =
                     |kw, snippet| acc.add_keyword_snippet_expr(ctx, kw, snippet, incomplete_let);
 
@@ -250,7 +270,7 @@ pub(crate) fn complete_expr_path(
                     }
                 }
 
-                if let Some(ty) = innermost_ret_ty {
+                if let Some(ty) = ty {
                     add_keyword(
                         "return",
                         match (in_block_expr, ty.is_unit()) {
@@ -266,26 +286,14 @@ pub(crate) fn complete_expr_path(
     }
 }
 
-fn add_assoc_item(
-    acc: &mut Completions,
-    ctx: &CompletionContext,
-    path_ctx: &PathCompletionCtx,
-    item: hir::AssocItem,
-) {
+fn add_assoc_item(acc: &mut Completions, ctx: &CompletionContext, item: hir::AssocItem) {
     match item {
-        hir::AssocItem::Function(func) => acc.add_function(ctx, path_ctx, func, None),
+        hir::AssocItem::Function(func) => acc.add_function(ctx, func, None),
         hir::AssocItem::Const(ct) => acc.add_const(ctx, ct),
         hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty),
     }
 }
 
-fn add_enum_variants(
-    acc: &mut Completions,
-    ctx: &CompletionContext,
-    path_ctx: &PathCompletionCtx,
-    e: hir::Enum,
-) {
-    e.variants(ctx.db)
-        .into_iter()
-        .for_each(|variant| acc.add_enum_variant(ctx, path_ctx, variant, None));
+fn add_enum_variants(acc: &mut Completions, ctx: &CompletionContext, e: hir::Enum) {
+    e.variants(ctx.db).into_iter().for_each(|variant| acc.add_enum_variant(ctx, variant, None));
 }
diff --git a/crates/ide-completion/src/completions/field.rs b/crates/ide-completion/src/completions/field.rs
index 738c24574c..6d346c5fd4 100644
--- a/crates/ide-completion/src/completions/field.rs
+++ b/crates/ide-completion/src/completions/field.rs
@@ -1,7 +1,7 @@
 //! Completion of field list position.
 
 use crate::{
-    context::{PathCompletionCtx, Qualified},
+    context::{NameContext, NameKind, PathCompletionCtx, PathKind, Qualified, TypeLocation},
     CompletionContext, Completions,
 };
 
@@ -10,31 +10,37 @@ pub(crate) fn complete_field_list_tuple_variant(
     ctx: &CompletionContext,
     path_ctx: &PathCompletionCtx,
 ) {
-    if ctx.qualifier_ctx.vis_node.is_some() {
-        return;
-    }
     match path_ctx {
         PathCompletionCtx {
             has_macro_bang: false,
             qualified: Qualified::No,
             parent: None,
+            kind: PathKind::Type { location: TypeLocation::TupleField },
             has_type_args: false,
             ..
         } => {
-            let mut add_keyword = |kw, snippet| acc.add_keyword_snippet(ctx, kw, snippet);
-            add_keyword("pub(crate)", "pub(crate)");
-            add_keyword("pub(super)", "pub(super)");
-            add_keyword("pub", "pub");
+            if ctx.qualifier_ctx.vis_node.is_none() {
+                let mut add_keyword = |kw, snippet| acc.add_keyword_snippet(ctx, kw, snippet);
+                add_keyword("pub(crate)", "pub(crate)");
+                add_keyword("pub(super)", "pub(super)");
+                add_keyword("pub", "pub");
+            }
         }
         _ => (),
     }
 }
 
-pub(crate) fn complete_field_list_record_variant(acc: &mut Completions, ctx: &CompletionContext) {
-    if ctx.qualifier_ctx.vis_node.is_none() {
-        let mut add_keyword = |kw, snippet| acc.add_keyword_snippet(ctx, kw, snippet);
-        add_keyword("pub(crate)", "pub(crate)");
-        add_keyword("pub(super)", "pub(super)");
-        add_keyword("pub", "pub");
+pub(crate) fn complete_field_list_record_variant(
+    acc: &mut Completions,
+    ctx: &CompletionContext,
+    name_ctx: &NameContext,
+) {
+    if let NameContext { kind: NameKind::RecordField, .. } = name_ctx {
+        if ctx.qualifier_ctx.vis_node.is_none() {
+            let mut add_keyword = |kw, snippet| acc.add_keyword_snippet(ctx, kw, snippet);
+            add_keyword("pub(crate)", "pub(crate)");
+            add_keyword("pub(super)", "pub(super)");
+            add_keyword("pub", "pub");
+        }
     }
 }
diff --git a/crates/ide-completion/src/completions/flyimport.rs b/crates/ide-completion/src/completions/flyimport.rs
index 129910465c..608a74dc15 100644
--- a/crates/ide-completion/src/completions/flyimport.rs
+++ b/crates/ide-completion/src/completions/flyimport.rs
@@ -116,17 +116,17 @@ pub(crate) fn import_on_the_fly_path(
     if !ctx.config.enable_imports_on_the_fly {
         return None;
     }
-    let qualified = match path_ctx {
+    let (kind, qualified) = match path_ctx {
         PathCompletionCtx {
             kind:
-                PathKind::Expr { .. }
+                kind @ (PathKind::Expr { .. }
                 | PathKind::Type { .. }
                 | PathKind::Attr { .. }
                 | PathKind::Derive { .. }
-                | PathKind::Pat { .. },
+                | PathKind::Pat { .. }),
             qualified,
             ..
-        } => qualified,
+        } => (Some(kind), qualified),
         _ => return None,
     };
     let potential_import_name = import_name(ctx);
@@ -139,70 +139,63 @@ pub(crate) fn import_on_the_fly_path(
     import_on_the_fly(
         acc,
         ctx,
-        path_ctx,
+        kind,
         import_assets,
         qualifier.map(|it| it.syntax().clone()).or_else(|| ctx.original_token.parent())?,
         potential_import_name,
     )
 }
 
-pub(crate) fn import_on_the_fly_pat(
+pub(crate) fn import_on_the_fly_dot(
     acc: &mut Completions,
     ctx: &CompletionContext,
-    pat_ctx: &PatternContext,
+    dot_access: &DotAccess,
 ) -> Option<()> {
     if !ctx.config.enable_imports_on_the_fly {
         return None;
     }
-    if let PatternContext { record_pat: Some(_), .. } = pat_ctx {
-        return None;
-    }
-
+    let receiver = dot_access.receiver.as_ref()?;
+    let ty = dot_access.receiver_ty.as_ref()?;
     let potential_import_name = import_name(ctx);
-    let import_assets = import_assets_for_path(ctx, &potential_import_name, None)?;
+    let import_assets = ImportAssets::for_fuzzy_method_call(
+        ctx.module,
+        ty.original.clone(),
+        potential_import_name.clone(),
+        receiver.syntax().clone(),
+    )?;
 
     import_on_the_fly(
         acc,
         ctx,
-        &PathCompletionCtx {
-            has_call_parens: false,
-            has_macro_bang: false,
-            qualified: Qualified::No,
-            parent: None,
-            kind: crate::context::PathKind::Pat { pat_ctx: pat_ctx.clone() },
-            has_type_args: false,
-            use_tree_parent: false,
-        },
+        None,
         import_assets,
-        ctx.original_token.parent()?,
+        receiver.syntax().clone(),
         potential_import_name,
     )
 }
 
-pub(crate) fn import_on_the_fly_dot(
+pub(crate) fn import_on_the_fly_pat(
     acc: &mut Completions,
     ctx: &CompletionContext,
-    dot_access: &DotAccess,
+    pat_ctx: &PatternContext,
 ) -> Option<()> {
     if !ctx.config.enable_imports_on_the_fly {
         return None;
     }
-    let receiver = dot_access.receiver.as_ref()?;
-    let ty = dot_access.receiver_ty.as_ref()?;
+    let kind = match pat_ctx {
+        PatternContext { record_pat: None, .. } => PathKind::Pat { pat_ctx: pat_ctx.clone() },
+        _ => return None,
+    };
+
     let potential_import_name = import_name(ctx);
-    let import_assets = ImportAssets::for_fuzzy_method_call(
-        ctx.module,
-        ty.original.clone(),
-        potential_import_name.clone(),
-        receiver.syntax().clone(),
-    )?;
+    let import_assets = import_assets_for_path(ctx, &potential_import_name, None)?;
 
-    import_on_the_fly_method(
+    import_on_the_fly(
         acc,
         ctx,
-        dot_access,
+        Some(&kind),
         import_assets,
-        receiver.syntax().clone(),
+        ctx.original_token.parent()?,
         potential_import_name,
     )
 }
@@ -210,7 +203,7 @@ pub(crate) fn import_on_the_fly_dot(
 fn import_on_the_fly(
     acc: &mut Completions,
     ctx: &CompletionContext,
-    path_ctx @ PathCompletionCtx { kind, .. }: &PathCompletionCtx,
+    path_kind: Option<&PathKind>,
     import_assets: ImportAssets,
     position: SyntaxNode,
     potential_import_name: String,
@@ -222,7 +215,11 @@ fn import_on_the_fly(
     }
 
     let ns_filter = |import: &LocatedImport| {
-        match (kind, import.original_item) {
+        let path_kind = match path_kind {
+            Some(it) => it,
+            None => return true,
+        };
+        match (path_kind, import.original_item) {
             // Aren't handled in flyimport
             (PathKind::Vis { .. } | PathKind::Use, _) => false,
             // modules are always fair game
@@ -279,49 +276,12 @@ fn import_on_the_fly(
                     &user_input_lowercased,
                 )
             })
-            .filter_map(|import| {
-                render_resolution_with_import(RenderContext::new(ctx), path_ctx, import)
-            })
+            .filter_map(|import| render_resolution_with_import(RenderContext::new(ctx), import))
             .map(|builder| builder.build()),
     );
     Some(())
 }
 
-fn import_on_the_fly_method(
-    acc: &mut Completions,
-    ctx: &CompletionContext,
-    dot_access: &DotAccess,
-    import_assets: ImportAssets,
-    position: SyntaxNode,
-    potential_import_name: String,
-) -> Option<()> {
-    let _p = profile::span("import_on_the_fly").detail(|| potential_import_name.clone());
-
-    if ImportScope::find_insert_use_container(&position, &ctx.sema).is_none() {
-        return None;
-    }
-
-    let user_input_lowercased = potential_import_name.to_lowercase();
-
-    import_assets
-        .search_for_imports(&ctx.sema, ctx.config.insert_use.prefix_kind)
-        .into_iter()
-        .filter(|import| {
-            !ctx.is_item_hidden(&import.item_to_import)
-                && !ctx.is_item_hidden(&import.original_item)
-        })
-        .sorted_by_key(|located_import| {
-            compute_fuzzy_completion_order_key(&located_import.import_path, &user_input_lowercased)
-        })
-        .for_each(|import| match import.original_item {
-            ItemInNs::Values(hir::ModuleDef::Function(f)) => {
-                acc.add_method_with_import(ctx, dot_access, f, import);
-            }
-            _ => (),
-        });
-    Some(())
-}
-
 fn import_name(ctx: &CompletionContext) -> String {
     let token_kind = ctx.token.kind();
     if matches!(token_kind, T![.] | T![::]) {
diff --git a/crates/ide-completion/src/completions/item_list.rs b/crates/ide-completion/src/completions/item_list.rs
index 329d08a9e7..941273fa97 100644
--- a/crates/ide-completion/src/completions/item_list.rs
+++ b/crates/ide-completion/src/completions/item_list.rs
@@ -1,37 +1,34 @@
 //! Completion of paths and keywords at item list position.
 
 use crate::{
-    context::{ExprCtx, ItemListKind, PathCompletionCtx, Qualified},
+    completions::module_or_fn_macro,
+    context::{ItemListKind, PathCompletionCtx, PathKind, Qualified},
     CompletionContext, Completions,
 };
 
 pub(crate) mod trait_impl;
 
-pub(crate) fn complete_item_list_in_expr(
-    acc: &mut Completions,
-    ctx: &CompletionContext,
-    path_ctx: &PathCompletionCtx,
-    expr_ctx: &ExprCtx,
-) {
-    if !expr_ctx.in_block_expr {
-        return;
-    }
-    if !path_ctx.is_trivial_path() {
-        return;
-    }
-    add_keywords(acc, ctx, None);
-}
-
 pub(crate) fn complete_item_list(
     acc: &mut Completions,
     ctx: &CompletionContext,
-    path_ctx @ PathCompletionCtx { qualified, .. }: &PathCompletionCtx,
-    kind: &ItemListKind,
+    path_ctx: &PathCompletionCtx,
 ) {
     let _p = profile::span("complete_item_list");
-    if path_ctx.is_trivial_path() {
-        add_keywords(acc, ctx, Some(kind));
-    }
+    let qualified = match path_ctx {
+        PathCompletionCtx { kind: PathKind::Item { kind }, qualified, .. } => {
+            if path_ctx.is_trivial_path() {
+                add_keywords(acc, ctx, Some(kind));
+            }
+            qualified
+        }
+        PathCompletionCtx { kind: PathKind::Expr { in_block_expr: true, .. }, .. }
+            if path_ctx.is_trivial_path() =>
+        {
+            add_keywords(acc, ctx, None);
+            return;
+        }
+        _ => return,
+    };
 
     match qualified {
         Qualified::With {
@@ -40,14 +37,8 @@ pub(crate) fn complete_item_list(
             ..
         } => {
             for (name, def) in module.scope(ctx.db, Some(ctx.module)) {
-                match def {
-                    hir::ScopeDef::ModuleDef(hir::ModuleDef::Macro(m)) if m.is_fn_like(ctx.db) => {
-                        acc.add_macro(ctx, path_ctx, m, name)
-                    }
-                    hir::ScopeDef::ModuleDef(hir::ModuleDef::Module(m)) => {
-                        acc.add_module(ctx, m, name)
-                    }
-                    _ => (),
+                if let Some(def) = module_or_fn_macro(ctx.db, def) {
+                    acc.add_resolution(ctx, name, def);
                 }
             }
 
@@ -57,12 +48,10 @@ pub(crate) fn complete_item_list(
         }
         Qualified::Absolute => acc.add_crate_roots(ctx),
         Qualified::No if ctx.qualifier_ctx.none() => {
-            ctx.process_all_names(&mut |name, def| match def {
-                hir::ScopeDef::ModuleDef(hir::ModuleDef::Macro(m)) if m.is_fn_like(ctx.db) => {
-                    acc.add_macro(ctx, path_ctx, m, name)
+            ctx.process_all_names(&mut |name, def| {
+                if let Some(def) = module_or_fn_macro(ctx.db, def) {
+                    acc.add_resolution(ctx, name, def);
                 }
-                hir::ScopeDef::ModuleDef(hir::ModuleDef::Module(m)) => acc.add_module(ctx, m, name),
-                _ => (),
             });
             acc.add_nameref_keywords_with_colon(ctx);
         }
diff --git a/crates/ide-completion/src/completions/item_list/trait_impl.rs b/crates/ide-completion/src/completions/item_list/trait_impl.rs
index 972a7d2f21..58b894bdd4 100644
--- a/crates/ide-completion/src/completions/item_list/trait_impl.rs
+++ b/crates/ide-completion/src/completions/item_list/trait_impl.rs
@@ -43,8 +43,11 @@ use syntax::{
 use text_edit::TextEdit;
 
 use crate::{
-    context::PathCompletionCtx, CompletionContext, CompletionItem, CompletionItemKind,
-    CompletionRelevance, Completions,
+    context::{
+        ItemListKind, NameContext, NameKind, NameRefContext, NameRefKind, PathCompletionCtx,
+        PathKind,
+    },
+    CompletionContext, CompletionItem, CompletionItemKind, CompletionRelevance, Completions,
 };
 
 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
@@ -55,36 +58,17 @@ enum ImplCompletionKind {
     Const,
 }
 
-pub(crate) fn complete_trait_impl_const(
+pub(crate) fn complete_trait_impl_name(
     acc: &mut Completions,
     ctx: &CompletionContext,
-    name: &Option<ast::Name>,
-) -> Option<()> {
-    complete_trait_impl_name(acc, ctx, name, ImplCompletionKind::Const)
-}
-
-pub(crate) fn complete_trait_impl_type_alias(
-    acc: &mut Completions,
-    ctx: &CompletionContext,
-    name: &Option<ast::Name>,
-) -> Option<()> {
-    complete_trait_impl_name(acc, ctx, name, ImplCompletionKind::TypeAlias)
-}
-
-pub(crate) fn complete_trait_impl_fn(
-    acc: &mut Completions,
-    ctx: &CompletionContext,
-    name: &Option<ast::Name>,
-) -> Option<()> {
-    complete_trait_impl_name(acc, ctx, name, ImplCompletionKind::Fn)
-}
-
-fn complete_trait_impl_name(
-    acc: &mut Completions,
-    ctx: &CompletionContext,
-    name: &Option<ast::Name>,
-    kind: ImplCompletionKind,
+    NameContext { name, kind, .. }: &NameContext,
 ) -> Option<()> {
+    let kind = match kind {
+        NameKind::Const => ImplCompletionKind::Const,
+        NameKind::Function => ImplCompletionKind::Fn,
+        NameKind::TypeAlias => ImplCompletionKind::TypeAlias,
+        _ => return None,
+    };
     let token = ctx.token.clone();
     let item = match name {
         Some(name) => name.syntax().parent(),
@@ -102,28 +86,34 @@ fn complete_trait_impl_name(
     Some(())
 }
 
-pub(crate) fn complete_trait_impl_item_by_name(
+pub(crate) fn complete_trait_impl_name_ref(
     acc: &mut Completions,
     ctx: &CompletionContext,
-    path_ctx: &PathCompletionCtx,
-    name_ref: &Option<ast::NameRef>,
-    impl_: &Option<ast::Impl>,
-) {
-    if !path_ctx.is_trivial_path() {
-        return;
-    }
-    if let Some(impl_) = impl_ {
-        complete_trait_impl(
+    name_ref_ctx: &NameRefContext,
+) -> Option<()> {
+    match name_ref_ctx {
+        NameRefContext {
+            nameref,
+            kind:
+                NameRefKind::Path(
+                    path_ctx @ PathCompletionCtx {
+                        kind: PathKind::Item { kind: ItemListKind::TraitImpl(Some(impl_)) },
+                        ..
+                    },
+                ),
+        } if path_ctx.is_trivial_path() => complete_trait_impl(
             acc,
             ctx,
             ImplCompletionKind::All,
-            match name_ref {
+            match nameref {
                 Some(name) => name.syntax().text_range(),
                 None => ctx.source_range(),
             },
             impl_,
-        );
+        ),
+        _ => (),
     }
+    Some(())
 }
 
 fn complete_trait_impl(
diff --git a/crates/ide-completion/src/completions/keyword.rs b/crates/ide-completion/src/completions/keyword.rs
index 7d8d3a9636..57d545ab8f 100644
--- a/crates/ide-completion/src/completions/keyword.rs
+++ b/crates/ide-completion/src/completions/keyword.rs
@@ -4,7 +4,7 @@ use syntax::ast::{self, Item};
 
 use crate::{CompletionContext, Completions};
 
-pub(crate) fn complete_for_and_where(
+pub(crate) fn complete_special_keywords(
     acc: &mut Completions,
     ctx: &CompletionContext,
     keyword_item: &ast::Item,
@@ -60,6 +60,8 @@ mod tests {
                 kw fn
                 kw impl
                 kw trait
+                sn pd
+                sn ppd
             "#]],
         );
     }
diff --git a/crates/ide-completion/src/completions/mod_.rs b/crates/ide-completion/src/completions/mod_.rs
index 6f67c38dfd..8dd1d1d8ac 100644
--- a/crates/ide-completion/src/completions/mod_.rs
+++ b/crates/ide-completion/src/completions/mod_.rs
@@ -9,14 +9,21 @@ use ide_db::{
 };
 use syntax::{ast, AstNode, SyntaxKind};
 
-use crate::{context::CompletionContext, CompletionItem, Completions};
+use crate::{
+    context::{CompletionContext, NameContext, NameKind},
+    CompletionItem, Completions,
+};
 
 /// Complete mod declaration, i.e. `mod $0;`
 pub(crate) fn complete_mod(
     acc: &mut Completions,
     ctx: &CompletionContext,
-    mod_under_caret: &ast::Module,
+    name_ctx: &NameContext,
 ) -> Option<()> {
+    let mod_under_caret = match name_ctx {
+        NameContext { kind: NameKind::Module(mod_under_caret), .. } => mod_under_caret,
+        _ => return None,
+    };
     if mod_under_caret.item_list().is_some() {
         return None;
     }
diff --git a/crates/ide-completion/src/completions/pattern.rs b/crates/ide-completion/src/completions/pattern.rs
index 91d5356541..149acb3c1b 100644
--- a/crates/ide-completion/src/completions/pattern.rs
+++ b/crates/ide-completion/src/completions/pattern.rs
@@ -5,7 +5,7 @@ use ide_db::FxHashSet;
 use syntax::ast::Pat;
 
 use crate::{
-    context::{PathCompletionCtx, PatternContext, PatternRefutability, Qualified},
+    context::{PathCompletionCtx, PathKind, PatternContext, PatternRefutability, Qualified},
     CompletionContext, Completions,
 };
 
@@ -13,9 +13,9 @@ use crate::{
 pub(crate) fn complete_pattern(
     acc: &mut Completions,
     ctx: &CompletionContext,
-    pattern_ctx: &PatternContext,
+    patctx: &PatternContext,
 ) {
-    match pattern_ctx.parent_pat.as_ref() {
+    match patctx.parent_pat.as_ref() {
         Some(Pat::RangePat(_) | Pat::BoxPat(_)) => (),
         Some(Pat::RefPat(r)) => {
             if r.mut_token().is_none() {
@@ -24,7 +24,7 @@ pub(crate) fn complete_pattern(
         }
         _ => {
             let tok = ctx.token.text_range().start();
-            match (pattern_ctx.ref_token.as_ref(), pattern_ctx.mut_token.as_ref()) {
+            match (patctx.ref_token.as_ref(), patctx.mut_token.as_ref()) {
                 (None, None) => {
                     acc.add_keyword(ctx, "ref");
                     acc.add_keyword(ctx, "mut");
@@ -40,11 +40,11 @@ pub(crate) fn complete_pattern(
         }
     }
 
-    if pattern_ctx.record_pat.is_some() {
+    if patctx.record_pat.is_some() {
         return;
     }
 
-    let refutable = pattern_ctx.refutability == PatternRefutability::Refutable;
+    let refutable = patctx.refutability == PatternRefutability::Refutable;
     let single_variant_enum = |enum_: hir::Enum| ctx.db.enum_data(enum_.into()).variants.len() == 1;
 
     if let Some(hir::Adt::Enum(e)) =
@@ -55,9 +55,9 @@ pub(crate) fn complete_pattern(
                 acc,
                 ctx,
                 e,
-                &pattern_ctx.impl_,
+                &patctx.impl_,
                 |acc, ctx, variant, path| {
-                    acc.add_qualified_variant_pat(ctx, pattern_ctx, variant, path);
+                    acc.add_qualified_variant_pat(ctx, variant, path);
                 },
             );
         }
@@ -69,39 +69,26 @@ pub(crate) fn complete_pattern(
         let add_simple_path = match res {
             hir::ScopeDef::ModuleDef(def) => match def {
                 hir::ModuleDef::Adt(hir::Adt::Struct(strukt)) => {
-                    acc.add_struct_pat(ctx, pattern_ctx, strukt, Some(name.clone()));
+                    acc.add_struct_pat(ctx, strukt, Some(name.clone()));
                     true
                 }
                 hir::ModuleDef::Variant(variant)
                     if refutable || single_variant_enum(variant.parent_enum(ctx.db)) =>
                 {
-                    acc.add_variant_pat(ctx, pattern_ctx, variant, Some(name.clone()));
+                    acc.add_variant_pat(ctx, variant, Some(name.clone()));
                     true
                 }
                 hir::ModuleDef::Adt(hir::Adt::Enum(e)) => refutable || single_variant_enum(e),
                 hir::ModuleDef::Const(..) => refutable,
                 hir::ModuleDef::Module(..) => true,
                 hir::ModuleDef::Macro(mac) if mac.is_fn_like(ctx.db) => {
-                    return acc.add_macro(
-                        ctx,
-                        &PathCompletionCtx {
-                            has_call_parens: false,
-                            has_macro_bang: false,
-                            qualified: Qualified::No,
-                            parent: None,
-                            kind: crate::context::PathKind::Pat { pat_ctx: pattern_ctx.clone() },
-                            has_type_args: false,
-                            use_tree_parent: false,
-                        },
-                        mac,
-                        name,
-                    )
+                    return acc.add_macro(ctx, mac, name)
                 }
                 _ => false,
             },
             hir::ScopeDef::ImplSelfType(impl_) => match impl_.self_ty(ctx.db).as_adt() {
                 Some(hir::Adt::Struct(strukt)) => {
-                    acc.add_struct_pat(ctx, pattern_ctx, strukt, Some(name.clone()));
+                    acc.add_struct_pat(ctx, strukt, Some(name.clone()));
                     true
                 }
                 Some(hir::Adt::Enum(e)) => refutable || single_variant_enum(e),
@@ -121,11 +108,14 @@ pub(crate) fn complete_pattern(
     });
 }
 
-pub(crate) fn complete_pattern_path(
+pub(crate) fn pattern_path_completion(
     acc: &mut Completions,
     ctx: &CompletionContext,
-    path_ctx @ PathCompletionCtx { qualified, .. }: &PathCompletionCtx,
+    PathCompletionCtx { qualified, kind, .. }: &PathCompletionCtx,
 ) {
+    if !matches!(kind, PathKind::Pat { .. }) {
+        return;
+    }
     match qualified {
         Qualified::With { resolution: Some(resolution), is_super_chain, .. } => {
             if *is_super_chain {
@@ -145,7 +135,7 @@ pub(crate) fn complete_pattern_path(
                         };
 
                         if add_resolution {
-                            acc.add_path_resolution(ctx, path_ctx, name, def);
+                            acc.add_resolution(ctx, name, def);
                         }
                     }
                 }
@@ -163,9 +153,9 @@ pub(crate) fn complete_pattern_path(
                         }
                         hir::PathResolution::Def(hir::ModuleDef::Adt(hir::Adt::Enum(e))) => {
                             cov_mark::hit!(enum_plain_qualified_use_tree);
-                            e.variants(ctx.db).into_iter().for_each(|variant| {
-                                acc.add_enum_variant(ctx, path_ctx, variant, None)
-                            });
+                            e.variants(ctx.db)
+                                .into_iter()
+                                .for_each(|variant| acc.add_enum_variant(ctx, variant, None));
                             e.ty(ctx.db)
                         }
                         hir::PathResolution::Def(hir::ModuleDef::Adt(hir::Adt::Union(u))) => {
@@ -210,7 +200,7 @@ pub(crate) fn complete_pattern_path(
             ctx.process_all_names(&mut |name, res| {
                 // FIXME: properly filter here
                 if let ScopeDef::ModuleDef(_) = res {
-                    acc.add_path_resolution(ctx, path_ctx, name, res);
+                    acc.add_resolution(ctx, name, res);
                 }
             });
 
diff --git a/crates/ide-completion/src/completions/record.rs b/crates/ide-completion/src/completions/record.rs
index 9f834e1ca1..eaab4cb4ee 100644
--- a/crates/ide-completion/src/completions/record.rs
+++ b/crates/ide-completion/src/completions/record.rs
@@ -1,9 +1,12 @@
 //! Complete fields in record literals and patterns.
 use ide_db::SymbolKind;
-use syntax::ast::{self, Expr};
+use syntax::{
+    ast::{self, Expr},
+    T,
+};
 
 use crate::{
-    context::{ExprCtx, PathCompletionCtx, PatternContext, Qualified},
+    context::{PathCompletionCtx, PathKind, PatternContext, Qualified},
     CompletionContext, CompletionItem, CompletionItemKind, CompletionRelevance,
     CompletionRelevancePostfixMatch, Completions,
 };
@@ -17,11 +20,10 @@ pub(crate) fn complete_record_pattern_fields(
         complete_fields(acc, ctx, ctx.sema.record_pattern_missing_fields(record_pat));
     }
 }
-pub(crate) fn complete_record_expr_fields(
+pub(crate) fn complete_record_expr_fields_record_expr(
     acc: &mut Completions,
     ctx: &CompletionContext,
     record_expr: &ast::RecordExpr,
-    &dot_prefix: &bool,
 ) {
     let ty = ctx.sema.type_of_expr(&Expr::RecordExpr(record_expr.clone()));
 
@@ -43,7 +45,7 @@ pub(crate) fn complete_record_expr_fields(
             let missing_fields = ctx.sema.record_literal_missing_fields(record_expr);
 
             add_default_update(acc, ctx, ty, &missing_fields);
-            if dot_prefix {
+            if ctx.previous_token_is(T![.]) {
                 let mut item =
                     CompletionItem::new(CompletionItemKind::Snippet, ctx.source_range(), "..");
                 item.insert_text(".");
@@ -83,12 +85,13 @@ pub(crate) fn complete_record_expr_func_update(
     acc: &mut Completions,
     ctx: &CompletionContext,
     path_ctx: &PathCompletionCtx,
-    expr_ctx: &ExprCtx,
 ) {
-    if !matches!(path_ctx.qualified, Qualified::No) {
-        return;
-    }
-    if let ExprCtx { is_func_update: Some(record_expr), .. } = expr_ctx {
+    if let PathCompletionCtx {
+        kind: PathKind::Expr { is_func_update: Some(record_expr), .. },
+        qualified: Qualified::No,
+        ..
+    } = path_ctx
+    {
         let ty = ctx.sema.type_of_expr(&Expr::RecordExpr(record_expr.clone()));
 
         match ty.as_ref().and_then(|t| t.original.as_adt()) {
diff --git a/crates/ide-completion/src/completions/snippet.rs b/crates/ide-completion/src/completions/snippet.rs
index 9992a81fe0..48366987f4 100644
--- a/crates/ide-completion/src/completions/snippet.rs
+++ b/crates/ide-completion/src/completions/snippet.rs
@@ -4,7 +4,7 @@ use hir::Documentation;
 use ide_db::{imports::insert_use::ImportScope, SnippetCap};
 
 use crate::{
-    context::{ExprCtx, ItemListKind, PathCompletionCtx, Qualified},
+    context::{ItemListKind, PathCompletionCtx, PathKind, Qualified},
     item::Builder,
     CompletionContext, CompletionItem, CompletionItemKind, Completions, SnippetScope,
 };
@@ -19,14 +19,15 @@ pub(crate) fn complete_expr_snippet(
     acc: &mut Completions,
     ctx: &CompletionContext,
     path_ctx: &PathCompletionCtx,
-    &ExprCtx { in_block_expr, .. }: &ExprCtx,
 ) {
-    if !matches!(path_ctx.qualified, Qualified::No) {
-        return;
-    }
-    if !ctx.qualifier_ctx.none() {
-        return;
-    }
+    let &can_be_stmt = match path_ctx {
+        PathCompletionCtx {
+            qualified: Qualified::No,
+            kind: PathKind::Expr { in_block_expr, .. },
+            ..
+        } => in_block_expr,
+        _ => return,
+    };
 
     let cap = match ctx.config.snippet_cap {
         Some(it) => it,
@@ -37,21 +38,9 @@ pub(crate) fn complete_expr_snippet(
         add_custom_completions(acc, ctx, cap, SnippetScope::Expr);
     }
 
-    if in_block_expr {
+    if can_be_stmt {
         snippet(ctx, cap, "pd", "eprintln!(\"$0 = {:?}\", $0);").add_to(acc);
         snippet(ctx, cap, "ppd", "eprintln!(\"$0 = {:#?}\", $0);").add_to(acc);
-        let item = snippet(
-            ctx,
-            cap,
-            "macro_rules",
-            "\
-macro_rules! $1 {
-    ($2) => {
-        $0
-    };
-}",
-        );
-        item.add_to(acc);
     }
 }
 
@@ -59,14 +48,22 @@ pub(crate) fn complete_item_snippet(
     acc: &mut Completions,
     ctx: &CompletionContext,
     path_ctx: &PathCompletionCtx,
-    kind: &ItemListKind,
 ) {
-    if !matches!(path_ctx.qualified, Qualified::No) {
-        return;
-    }
+    let path_kind = match path_ctx {
+        PathCompletionCtx {
+            qualified: Qualified::No,
+            kind: kind @ (PathKind::Item { .. } | PathKind::Expr { in_block_expr: true, .. }),
+            ..
+        } => kind,
+        _ => return,
+    };
     if !ctx.qualifier_ctx.none() {
         return;
     }
+    if ctx.qualifier_ctx.vis_node.is_some() {
+        return; // technically we could do some of these snippet completions if we were to put the
+                // attributes before the vis node.
+    }
     let cap = match ctx.config.snippet_cap {
         Some(it) => it,
         None => return,
@@ -77,7 +74,8 @@ pub(crate) fn complete_item_snippet(
     }
 
     // Test-related snippets shouldn't be shown in blocks.
-    if let ItemListKind::SourceFile | ItemListKind::Module = kind {
+    if let PathKind::Item { kind: ItemListKind::SourceFile | ItemListKind::Module, .. } = path_kind
+    {
         let mut item = snippet(
             ctx,
             cap,
@@ -108,7 +106,10 @@ fn ${1:feature}() {
         );
         item.lookup_by("tfn");
         item.add_to(acc);
-
+    }
+    if let PathKind::Item { kind: ItemListKind::SourceFile | ItemListKind::Module, .. }
+    | PathKind::Expr { .. } = path_kind
+    {
         let item = snippet(
             ctx,
             cap,
diff --git a/crates/ide-completion/src/completions/type.rs b/crates/ide-completion/src/completions/type.rs
index dea0c701b8..b8d172696d 100644
--- a/crates/ide-completion/src/completions/type.rs
+++ b/crates/ide-completion/src/completions/type.rs
@@ -5,7 +5,7 @@ use ide_db::FxHashSet;
 use syntax::{ast, AstNode};
 
 use crate::{
-    context::{PathCompletionCtx, Qualified, TypeAscriptionTarget, TypeLocation},
+    context::{PathCompletionCtx, PathKind, Qualified, TypeAscriptionTarget, TypeLocation},
     render::render_type_inference,
     CompletionContext, Completions,
 };
@@ -13,11 +13,17 @@ use crate::{
 pub(crate) fn complete_type_path(
     acc: &mut Completions,
     ctx: &CompletionContext,
-    path_ctx @ PathCompletionCtx { qualified, .. }: &PathCompletionCtx,
-    location: &TypeLocation,
+    path_ctx: &PathCompletionCtx,
 ) {
     let _p = profile::span("complete_type_path");
 
+    let (location, qualified) = match path_ctx {
+        PathCompletionCtx { kind: PathKind::Type { location }, qualified, .. } => {
+            (location, qualified)
+        }
+        _ => return,
+    };
+
     let scope_def_applicable = |def| {
         use hir::{GenericParam::*, ModuleDef::*};
         match def {
@@ -69,7 +75,7 @@ pub(crate) fn complete_type_path(
                     let module_scope = module.scope(ctx.db, Some(ctx.module));
                     for (name, def) in module_scope {
                         if scope_def_applicable(def) {
-                            acc.add_path_resolution(ctx, path_ctx, name, def);
+                            acc.add_resolution(ctx, name, def);
                         }
                     }
                 }
@@ -154,7 +160,7 @@ pub(crate) fn complete_type_path(
                         _ => false,
                     };
                     if add_resolution {
-                        acc.add_path_resolution(ctx, path_ctx, name, res);
+                        acc.add_resolution(ctx, name, res);
                     }
                 });
                 return;
@@ -178,23 +184,26 @@ pub(crate) fn complete_type_path(
             }
             ctx.process_all_names(&mut |name, def| {
                 if scope_def_applicable(def) {
-                    acc.add_path_resolution(ctx, path_ctx, name, def);
+                    acc.add_resolution(ctx, name, def);
                 }
             });
         }
     }
 }
 
-pub(crate) fn complete_ascribed_type(
+pub(crate) fn complete_inferred_type(
     acc: &mut Completions,
     ctx: &CompletionContext,
     path_ctx: &PathCompletionCtx,
-    ascription: &TypeAscriptionTarget,
 ) -> Option<()> {
-    if !path_ctx.is_trivial_path() {
-        return None;
-    }
-    let x = match ascription {
+    let pat = match path_ctx {
+        PathCompletionCtx {
+            kind: PathKind::Type { location: TypeLocation::TypeAscription(ascription), .. },
+            ..
+        } if path_ctx.is_trivial_path() => ascription,
+        _ => return None,
+    };
+    let x = match pat {
         TypeAscriptionTarget::Let(pat) | TypeAscriptionTarget::FnParam(pat) => {
             ctx.sema.type_of_pat(pat.as_ref()?)
         }
diff --git a/crates/ide-completion/src/completions/use_.rs b/crates/ide-completion/src/completions/use_.rs
index 2c039d5018..2cdd93f95b 100644
--- a/crates/ide-completion/src/completions/use_.rs
+++ b/crates/ide-completion/src/completions/use_.rs
@@ -5,17 +5,33 @@ use ide_db::{FxHashSet, SymbolKind};
 use syntax::{ast, AstNode};
 
 use crate::{
-    context::{CompletionContext, PathCompletionCtx, Qualified},
+    context::{
+        CompletionContext, NameRefContext, NameRefKind, PathCompletionCtx, PathKind, Qualified,
+    },
     item::Builder,
     CompletionItem, CompletionItemKind, CompletionRelevance, Completions,
 };
 
-pub(crate) fn complete_use_path(
+pub(crate) fn complete_use_tree(
     acc: &mut Completions,
     ctx: &CompletionContext,
-    path_ctx @ PathCompletionCtx { qualified, use_tree_parent, .. }: &PathCompletionCtx,
-    name_ref: &Option<ast::NameRef>,
+    name_ref_ctx: &NameRefContext,
 ) {
+    let (qualified, name_ref, use_tree_parent) = match name_ref_ctx {
+        NameRefContext {
+            kind:
+                NameRefKind::Path(PathCompletionCtx {
+                    kind: PathKind::Use,
+                    qualified,
+                    use_tree_parent,
+                    ..
+                }),
+            nameref,
+            ..
+        } => (qualified, nameref, use_tree_parent),
+        _ => return,
+    };
+
     match qualified {
         Qualified::With { path, resolution: Some(resolution), is_super_chain } => {
             if *is_super_chain {
@@ -68,7 +84,7 @@ pub(crate) fn complete_use_path(
                         };
 
                         if add_resolution {
-                            let mut builder = Builder::from_resolution(ctx, path_ctx, name, def);
+                            let mut builder = Builder::from_resolution(ctx, name, def);
                             builder.set_relevance(CompletionRelevance {
                                 is_name_already_imported,
                                 ..Default::default()
@@ -81,7 +97,7 @@ pub(crate) fn complete_use_path(
                     cov_mark::hit!(enum_plain_qualified_use_tree);
                     e.variants(ctx.db)
                         .into_iter()
-                        .for_each(|variant| acc.add_enum_variant(ctx, path_ctx, variant, None));
+                        .for_each(|variant| acc.add_enum_variant(ctx, variant, None));
                 }
                 _ => {}
             }
@@ -96,8 +112,8 @@ pub(crate) fn complete_use_path(
             cov_mark::hit!(unqualified_path_selected_only);
             ctx.process_all_names(&mut |name, res| {
                 match res {
-                    ScopeDef::ModuleDef(hir::ModuleDef::Module(module)) => {
-                        acc.add_module(ctx, module, name);
+                    ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) => {
+                        acc.add_resolution(ctx, name, res);
                     }
                     ScopeDef::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Enum(e))) => {
                         // exclude prelude enum
diff --git a/crates/ide-completion/src/completions/vis.rs b/crates/ide-completion/src/completions/vis.rs
index 30de0e94f7..18513039e6 100644
--- a/crates/ide-completion/src/completions/vis.rs
+++ b/crates/ide-completion/src/completions/vis.rs
@@ -1,16 +1,24 @@
 //! Completion for visibility specifiers.
 
+use hir::ScopeDef;
+
 use crate::{
-    context::{CompletionContext, PathCompletionCtx, Qualified},
+    context::{CompletionContext, PathCompletionCtx, PathKind, Qualified},
     Completions,
 };
 
 pub(crate) fn complete_vis_path(
     acc: &mut Completions,
     ctx: &CompletionContext,
-    PathCompletionCtx { qualified, .. }: &PathCompletionCtx,
-    &has_in_token: &bool,
+    path_ctx: &PathCompletionCtx,
 ) {
+    let (qualified, &has_in_token) = match path_ctx {
+        PathCompletionCtx { kind: PathKind::Vis { has_in_token }, qualified, .. } => {
+            (qualified, has_in_token)
+        }
+        _ => return,
+    };
+
     match qualified {
         Qualified::With {
             resolution: Some(hir::PathResolution::Def(hir::ModuleDef::Module(module))),
@@ -23,7 +31,7 @@ pub(crate) fn complete_vis_path(
             if let Some(next) = next_towards_current {
                 if let Some(name) = next.name(ctx.db) {
                     cov_mark::hit!(visibility_qualified);
-                    acc.add_module(ctx, next, name);
+                    acc.add_resolution(ctx, name, ScopeDef::ModuleDef(next.into()));
                 }
             }
 
diff --git a/crates/ide-completion/src/context.rs b/crates/ide-completion/src/context.rs
index 441f7ad70b..0c7c6ab5af 100644
--- a/crates/ide-completion/src/context.rs
+++ b/crates/ide-completion/src/context.rs
@@ -88,16 +88,27 @@ impl PathCompletionCtx {
 #[derive(Debug, PartialEq, Eq)]
 pub(super) enum PathKind {
     Expr {
-        expr_ctx: ExprCtx,
+        in_block_expr: bool,
+        in_loop_body: bool,
+        after_if_expr: bool,
+        /// Whether this expression is the direct condition of an if or while expression
+        in_condition: bool,
+        incomplete_let: bool,
+        ref_expr_parent: Option<ast::RefExpr>,
+        is_func_update: Option<ast::RecordExpr>,
+        self_param: Option<hir::SelfParam>,
+        innermost_ret_ty: Option<hir::Type>,
+        impl_: Option<ast::Impl>,
     },
     Type {
         location: TypeLocation,
     },
     Attr {
-        attr_ctx: AttrCtx,
+        kind: AttrKind,
+        annotated_item_kind: Option<SyntaxKind>,
     },
     Derive {
-        existing_derives: ExistingDerives,
+        existing_derives: FxHashSet<hir::Macro>,
     },
     /// Path in item position, that is inside an (Assoc)ItemList
     Item {
@@ -112,29 +123,6 @@ pub(super) enum PathKind {
     Use,
 }
 
-pub(crate) type ExistingDerives = FxHashSet<hir::Macro>;
-
-#[derive(Debug, PartialEq, Eq)]
-pub(crate) struct AttrCtx {
-    pub(crate) kind: AttrKind,
-    pub(crate) annotated_item_kind: Option<SyntaxKind>,
-}
-
-#[derive(Debug, PartialEq, Eq)]
-pub(crate) struct ExprCtx {
-    pub(crate) in_block_expr: bool,
-    pub(crate) in_loop_body: bool,
-    pub(crate) after_if_expr: bool,
-    /// Whether this expression is the direct condition of an if or while expression
-    pub(crate) in_condition: bool,
-    pub(crate) incomplete_let: bool,
-    pub(crate) ref_expr_parent: Option<ast::RefExpr>,
-    pub(crate) is_func_update: Option<ast::RecordExpr>,
-    pub(crate) self_param: Option<hir::SelfParam>,
-    pub(crate) innermost_ret_ty: Option<hir::Type>,
-    pub(crate) impl_: Option<ast::Impl>,
-}
-
 /// Original file ast nodes
 #[derive(Clone, Debug, PartialEq, Eq)]
 pub(crate) enum TypeLocation {
@@ -259,17 +247,14 @@ pub(super) enum NameRefKind {
     DotAccess(DotAccess),
     /// Position where we are only interested in keyword completions
     Keyword(ast::Item),
-    /// The record expression this nameref is a field of and whether a dot precedes the completion identifier.
-    RecordExpr {
-        dot_prefix: bool,
-        expr: ast::RecordExpr,
-    },
+    /// The record expression this nameref is a field of
+    RecordExpr(ast::RecordExpr),
     Pattern(PatternContext),
 }
 
 /// The identifier we are currently completing.
 #[derive(Debug)]
-pub(super) enum CompletionAnalysis {
+pub(super) enum IdentContext {
     Name(NameContext),
     NameRef(NameRefContext),
     Lifetime(LifetimeContext),
@@ -282,7 +267,6 @@ pub(super) enum CompletionAnalysis {
     },
     /// Set if we are currently completing in an unexpanded attribute, this usually implies a builtin attribute like `allow($0)`
     UnexpandedAttrTT {
-        colon_prefix: bool,
         fake_attribute_under_caret: Option<ast::Attr>,
     },
 }
@@ -338,6 +322,11 @@ pub(crate) struct CompletionContext<'a> {
     /// The expected type of what we are completing.
     pub(super) expected_type: Option<Type>,
 
+    // FIXME: This shouldn't exist
+    pub(super) previous_token: Option<SyntaxToken>,
+
+    // We might wanna split these out of CompletionContext
+    pub(super) ident_ctx: IdentContext,
     pub(super) qualifier_ctx: QualifierCtx,
 
     pub(super) locals: FxHashMap<Name, Local>,
@@ -360,6 +349,11 @@ impl<'a> CompletionContext<'a> {
         }
     }
 
+    // FIXME: This shouldn't exist
+    pub(crate) fn previous_token_is(&self, kind: SyntaxKind) -> bool {
+        self.previous_token.as_ref().map_or(false, |tok| tok.kind() == kind)
+    }
+
     pub(crate) fn famous_defs(&self) -> FamousDefs {
         FamousDefs(&self.sema, self.krate)
     }
@@ -459,7 +453,7 @@ impl<'a> CompletionContext<'a> {
         db: &'a RootDatabase,
         position @ FilePosition { file_id, offset }: FilePosition,
         config: &'a CompletionConfig,
-    ) -> Option<(CompletionContext<'a>, CompletionAnalysis)> {
+    ) -> Option<CompletionContext<'a>> {
         let _p = profile::span("CompletionContext::new");
         let sema = Semantics::new(db);
 
@@ -501,16 +495,19 @@ impl<'a> CompletionContext<'a> {
             module,
             expected_name: None,
             expected_type: None,
+            previous_token: None,
+            // dummy value, will be overwritten
+            ident_ctx: IdentContext::UnexpandedAttrTT { fake_attribute_under_caret: None },
             qualifier_ctx: Default::default(),
             locals,
         };
-        let ident_ctx = ctx.expand_and_analyze(
+        ctx.expand_and_fill(
             original_file.syntax().clone(),
             file_with_fake_ident.syntax().clone(),
             offset,
             fake_ident_token,
         )?;
-        Some((ctx, ident_ctx))
+        Some(ctx)
     }
 }
 
diff --git a/crates/ide-completion/src/context/analysis.rs b/crates/ide-completion/src/context/analysis.rs
index 7e6c842b1e..9ceb60f90f 100644
--- a/crates/ide-completion/src/context/analysis.rs
+++ b/crates/ide-completion/src/context/analysis.rs
@@ -11,23 +11,23 @@ use syntax::{
 };
 
 use crate::context::{
-    AttrCtx, CompletionAnalysis, CompletionContext, DotAccess, DotAccessKind, ExprCtx,
-    ItemListKind, LifetimeContext, LifetimeKind, NameContext, NameKind, NameRefContext,
-    NameRefKind, ParamKind, PathCompletionCtx, PathKind, PatternContext, PatternRefutability,
-    Qualified, QualifierCtx, TypeAscriptionTarget, TypeLocation, COMPLETION_MARKER,
+    CompletionContext, DotAccess, DotAccessKind, IdentContext, ItemListKind, LifetimeContext,
+    LifetimeKind, NameContext, NameKind, NameRefContext, NameRefKind, ParamKind, PathCompletionCtx,
+    PathKind, PatternContext, PatternRefutability, Qualified, QualifierCtx, TypeAscriptionTarget,
+    TypeLocation, COMPLETION_MARKER,
 };
 
 impl<'a> CompletionContext<'a> {
     /// Expand attributes and macro calls at the current cursor position for both the original file
     /// and fake file repeatedly. As soon as one of the two expansions fail we stop so the original
     /// and speculative states stay in sync.
-    pub(super) fn expand_and_analyze(
+    pub(super) fn expand_and_fill(
         &mut self,
         mut original_file: SyntaxNode,
         mut speculative_file: SyntaxNode,
         mut offset: TextSize,
         mut fake_ident_token: SyntaxToken,
-    ) -> Option<CompletionAnalysis> {
+    ) -> Option<()> {
         let _p = profile::span("CompletionContext::expand_and_fill");
         let mut derive_ctx = None;
 
@@ -157,7 +157,7 @@ impl<'a> CompletionContext<'a> {
             break 'expansion;
         }
 
-        self.analyze(&original_file, speculative_file, offset, derive_ctx)
+        self.fill(&original_file, speculative_file, offset, derive_ctx)
     }
 
     /// Calculate the expected type and name of the cursor position.
@@ -254,9 +254,7 @@ impl<'a> CompletionContext<'a> {
                     // match foo { $0 }
                     // match foo { ..., pat => $0 }
                     ast::MatchExpr(it) => {
-                        let on_arrow = previous_non_trivia_token(self.token.clone()).map_or(false, |it| T![=>] == it.kind());
-
-                        let ty = if on_arrow {
+                        let ty = if self.previous_token_is(T![=>]) {
                             // match foo { ..., pat => $0 }
                             cov_mark::hit!(expected_type_match_arm_body_without_leading_char);
                             cov_mark::hit!(expected_type_match_arm_body_with_leading_char);
@@ -311,13 +309,13 @@ impl<'a> CompletionContext<'a> {
 
     /// Fill the completion context, this is what does semantic reasoning about the surrounding context
     /// of the completion location.
-    fn analyze(
+    fn fill(
         &mut self,
         original_file: &SyntaxNode,
         file_with_fake_ident: SyntaxNode,
         offset: TextSize,
         derive_ctx: Option<(SyntaxNode, SyntaxNode, TextSize, ast::Attr)>,
-    ) -> Option<CompletionAnalysis> {
+    ) -> Option<()> {
         let fake_ident_token = file_with_fake_ident.token_at_offset(offset).right_biased()?;
         let syntax_element = NodeOrToken::Token(fake_ident_token);
         if is_in_token_of_for_loop(syntax_element.clone()) {
@@ -329,6 +327,9 @@ impl<'a> CompletionContext<'a> {
             return None;
         }
 
+        self.previous_token =
+            syntax_element.clone().into_token().and_then(previous_non_trivia_token);
+
         (self.expected_type, self.expected_name) = self.expected_type_and_name();
 
         // Overwrite the path kind for derives
@@ -350,7 +351,8 @@ impl<'a> CompletionContext<'a> {
                             .collect(),
                     };
                 }
-                return Some(CompletionAnalysis::NameRef(nameref_ctx));
+                self.ident_ctx = IdentContext::NameRef(nameref_ctx);
+                return Some(());
             }
             return None;
         }
@@ -358,54 +360,58 @@ impl<'a> CompletionContext<'a> {
         let name_like = match find_node_at_offset(&file_with_fake_ident, offset) {
             Some(it) => it,
             None => {
-                let analysis =
-                    if let Some(original) = ast::String::cast(self.original_token.clone()) {
-                        CompletionAnalysis::String {
-                            original,
-                            expanded: ast::String::cast(self.token.clone()),
-                        }
+                if let Some(original) = ast::String::cast(self.original_token.clone()) {
+                    self.ident_ctx = IdentContext::String {
+                        original,
+                        expanded: ast::String::cast(self.token.clone()),
+                    };
+                } else {
+                    // Fix up trailing whitespace problem
+                    // #[attr(foo = $0
+                    let token = if self.token.kind() == SyntaxKind::WHITESPACE {
+                        self.previous_token.as_ref()?
                     } else {
-                        // Fix up trailing whitespace problem
-                        // #[attr(foo = $0
-                        let token =
-                            syntax::algo::skip_trivia_token(self.token.clone(), Direction::Prev)?;
-                        let p = token.parent()?;
-                        if p.kind() == SyntaxKind::TOKEN_TREE
-                            && p.ancestors().any(|it| it.kind() == SyntaxKind::META)
-                        {
-                            let colon_prefix = previous_non_trivia_token(self.token.clone())
-                                .map_or(false, |it| T![:] == it.kind());
-                            CompletionAnalysis::UnexpandedAttrTT {
-                                fake_attribute_under_caret: syntax_element
-                                    .ancestors()
-                                    .find_map(ast::Attr::cast),
-                                colon_prefix,
-                            }
-                        } else {
-                            return None;
-                        }
+                        &self.token
                     };
-                return Some(analysis);
+                    let p = token.parent()?;
+                    if p.kind() == SyntaxKind::TOKEN_TREE
+                        && p.ancestors().any(|it| it.kind() == SyntaxKind::META)
+                    {
+                        self.ident_ctx = IdentContext::UnexpandedAttrTT {
+                            fake_attribute_under_caret: syntax_element
+                                .ancestors()
+                                .find_map(ast::Attr::cast),
+                        };
+                    } else {
+                        return None;
+                    }
+                }
+                return Some(());
             }
         };
-        let analysis = match name_like {
-            ast::NameLike::Lifetime(lifetime) => CompletionAnalysis::Lifetime(
-                Self::classify_lifetime(&self.sema, original_file, lifetime)?,
-            ),
+
+        match name_like {
+            ast::NameLike::Lifetime(lifetime) => {
+                self.ident_ctx = IdentContext::Lifetime(Self::classify_lifetime(
+                    &self.sema,
+                    original_file,
+                    lifetime,
+                )?);
+            }
             ast::NameLike::NameRef(name_ref) => {
                 let parent = name_ref.syntax().parent()?;
                 let (nameref_ctx, qualifier_ctx) =
                     Self::classify_name_ref(&self.sema, &original_file, name_ref, parent.clone())?;
 
                 self.qualifier_ctx = qualifier_ctx;
-                CompletionAnalysis::NameRef(nameref_ctx)
+                self.ident_ctx = IdentContext::NameRef(nameref_ctx);
             }
             ast::NameLike::Name(name) => {
                 let name_ctx = Self::classify_name(&self.sema, original_file, name)?;
-                CompletionAnalysis::Name(name_ctx)
+                self.ident_ctx = IdentContext::Name(name_ctx);
             }
-        };
-        Some(analysis)
+        }
+        Some(())
     }
 
     fn classify_lifetime(
@@ -487,15 +493,12 @@ impl<'a> CompletionContext<'a> {
             |kind| (NameRefContext { nameref: nameref.clone(), kind }, Default::default());
 
         if let Some(record_field) = ast::RecordExprField::for_field_name(&name_ref) {
-            let dot_prefix = previous_non_trivia_token(name_ref.syntax().clone())
-                .map_or(false, |it| T![.] == it.kind());
-
             return find_node_in_file_compensated(
                 sema,
                 original_file,
                 &record_field.parent_record_lit(),
             )
-            .map(|expr| NameRefKind::RecordExpr { expr, dot_prefix })
+            .map(NameRefKind::RecordExpr)
             .map(make_res);
         }
         if let Some(record_field) = ast::RecordPatField::for_field_name_ref(&name_ref) {
@@ -762,18 +765,16 @@ impl<'a> CompletionContext<'a> {
             let impl_ = fetch_immediate_impl(sema, original_file, expr.syntax());
 
             PathKind::Expr {
-                expr_ctx: ExprCtx {
-                    in_block_expr,
-                    in_loop_body,
-                    after_if_expr,
-                    in_condition,
-                    ref_expr_parent,
-                    is_func_update,
-                    innermost_ret_ty,
-                    self_param,
-                    incomplete_let,
-                    impl_,
-                },
+                in_block_expr,
+                in_loop_body,
+                after_if_expr,
+                in_condition,
+                ref_expr_parent,
+                is_func_update,
+                innermost_ret_ty,
+                self_param,
+                incomplete_let,
+                impl_,
             }
         };
         let make_path_kind_type = |ty: ast::Type| {
@@ -857,10 +858,8 @@ impl<'a> CompletionContext<'a> {
                             Some(attached.kind())
                         };
                         PathKind::Attr {
-                            attr_ctx: AttrCtx {
-                                kind,
-                                annotated_item_kind,
-                            }
+                            kind,
+                            annotated_item_kind,
                         }
                     },
                     ast::Visibility(it) => PathKind::Vis { has_in_token: it.in_token().is_some() },
@@ -915,7 +914,7 @@ impl<'a> CompletionContext<'a> {
         if path_ctx.is_trivial_path() {
             // fetch the full expression that may have qualifiers attached to it
             let top_node = match path_ctx.kind {
-                PathKind::Expr { expr_ctx: ExprCtx { in_block_expr: true, .. } } => {
+                PathKind::Expr { in_block_expr: true, .. } => {
                     parent.ancestors().find(|it| ast::PathExpr::can_cast(it.kind())).and_then(|p| {
                         let parent = p.parent()?;
                         if ast::StmtList::can_cast(parent.kind()) {
@@ -1177,12 +1176,8 @@ pub(crate) fn is_in_loop_body(node: &SyntaxNode) -> bool {
         .is_some()
 }
 
-fn previous_non_trivia_token(e: impl Into<SyntaxElement>) -> Option<SyntaxToken> {
-    let mut token = match e.into() {
-        SyntaxElement::Node(n) => n.first_token()?,
-        SyntaxElement::Token(t) => t,
-    }
-    .prev_token();
+fn previous_non_trivia_token(token: SyntaxToken) -> Option<SyntaxToken> {
+    let mut token = token.prev_token();
     while let Some(inner) = token {
         if !inner.kind().is_trivia() {
             return Some(inner);
diff --git a/crates/ide-completion/src/item.rs b/crates/ide-completion/src/item.rs
index 4774fe9db7..6e5c2a9386 100644
--- a/crates/ide-completion/src/item.rs
+++ b/crates/ide-completion/src/item.rs
@@ -10,8 +10,8 @@ use syntax::{SmolStr, TextRange};
 use text_edit::TextEdit;
 
 use crate::{
-    context::{CompletionContext, PathCompletionCtx},
-    render::{render_path_resolution, RenderContext},
+    context::CompletionContext,
+    render::{render_resolution, RenderContext},
 };
 
 /// `CompletionItem` describes a single completion variant in the editor pop-up.
@@ -434,11 +434,10 @@ pub(crate) struct Builder {
 impl Builder {
     pub(crate) fn from_resolution(
         ctx: &CompletionContext,
-        path_ctx: &PathCompletionCtx,
         local_name: hir::Name,
         resolution: hir::ScopeDef,
     ) -> Self {
-        render_path_resolution(RenderContext::new(ctx), path_ctx, local_name, resolution)
+        render_resolution(RenderContext::new(ctx), local_name, resolution)
     }
 
     pub(crate) fn build(self) -> CompletionItem {
diff --git a/crates/ide-completion/src/lib.rs b/crates/ide-completion/src/lib.rs
index fe02f05fd1..27fe66e385 100644
--- a/crates/ide-completion/src/lib.rs
+++ b/crates/ide-completion/src/lib.rs
@@ -24,10 +24,7 @@ use text_edit::TextEdit;
 
 use crate::{
     completions::Completions,
-    context::{
-        CompletionAnalysis, CompletionContext, NameRefContext, NameRefKind, PathCompletionCtx,
-        PathKind,
-    },
+    context::{CompletionContext, IdentContext, NameKind, NameRefContext, NameRefKind},
 };
 
 pub use crate::{
@@ -149,18 +146,15 @@ pub fn completions(
     position: FilePosition,
     trigger_character: Option<char>,
 ) -> Option<Completions> {
-    let (ctx, analysis) = &CompletionContext::new(db, position, config)?;
+    let ctx = &CompletionContext::new(db, position, config)?;
     let mut completions = Completions::default();
 
     // prevent `(` from triggering unwanted completion noise
     if trigger_character == Some('(') {
-        if let CompletionAnalysis::NameRef(NameRefContext { kind, .. }) = &analysis {
-            if let NameRefKind::Path(
-                path_ctx @ PathCompletionCtx { kind: PathKind::Vis { has_in_token }, .. },
-            ) = kind
-            {
-                completions::vis::complete_vis_path(&mut completions, ctx, path_ctx, has_in_token);
-            }
+        if let IdentContext::NameRef(NameRefContext { kind: NameRefKind::Path(path_ctx), .. }) =
+            &ctx.ident_ctx
+        {
+            completions::vis::complete_vis_path(&mut completions, ctx, path_ctx);
         }
         // prevent `(` from triggering unwanted completion noise
         return Some(completions);
@@ -169,31 +163,76 @@ pub fn completions(
     {
         let acc = &mut completions;
 
-        match &analysis {
-            CompletionAnalysis::Name(name_ctx) => completions::complete_name(acc, ctx, name_ctx),
-            CompletionAnalysis::NameRef(name_ref_ctx) => {
-                completions::complete_name_ref(acc, ctx, name_ref_ctx)
+        match &ctx.ident_ctx {
+            IdentContext::Name(name_ctx) => {
+                completions::field::complete_field_list_record_variant(acc, ctx, name_ctx);
+                completions::item_list::trait_impl::complete_trait_impl_name(acc, ctx, name_ctx);
+                completions::mod_::complete_mod(acc, ctx, name_ctx);
+                if let NameKind::IdentPat(pattern_ctx) = &name_ctx.kind {
+                    completions::flyimport::import_on_the_fly_pat(acc, ctx, pattern_ctx);
+                    completions::fn_param::complete_fn_param(acc, ctx, pattern_ctx);
+                    completions::pattern::complete_pattern(acc, ctx, pattern_ctx);
+                    completions::record::complete_record_pattern_fields(acc, ctx, pattern_ctx);
+                }
+            }
+            IdentContext::NameRef(name_ctx @ NameRefContext { kind, .. }) => {
+                completions::item_list::trait_impl::complete_trait_impl_name_ref(
+                    acc, ctx, name_ctx,
+                );
+                completions::use_::complete_use_tree(acc, ctx, name_ctx);
+
+                match kind {
+                    NameRefKind::Path(path_ctx) => {
+                        completions::attribute::complete_attribute(acc, ctx, path_ctx);
+                        completions::attribute::complete_derive(acc, ctx, path_ctx);
+                        completions::dot::complete_undotted_self(acc, ctx, path_ctx);
+                        completions::expr::complete_expr_path(acc, ctx, path_ctx);
+                        completions::field::complete_field_list_tuple_variant(acc, ctx, path_ctx);
+                        completions::flyimport::import_on_the_fly_path(acc, ctx, path_ctx);
+                        completions::item_list::complete_item_list(acc, ctx, path_ctx);
+                        completions::pattern::pattern_path_completion(acc, ctx, path_ctx);
+                        completions::r#type::complete_inferred_type(acc, ctx, path_ctx);
+                        completions::r#type::complete_type_path(acc, ctx, path_ctx);
+                        completions::record::complete_record_expr_func_update(acc, ctx, path_ctx);
+                        completions::snippet::complete_expr_snippet(acc, ctx, path_ctx);
+                        completions::snippet::complete_item_snippet(acc, ctx, path_ctx);
+                        completions::vis::complete_vis_path(acc, ctx, path_ctx);
+                    }
+                    NameRefKind::DotAccess(dot_access) => {
+                        completions::flyimport::import_on_the_fly_dot(acc, ctx, dot_access);
+                        completions::dot::complete_dot(acc, ctx, dot_access);
+                        completions::postfix::complete_postfix(acc, ctx, dot_access);
+                    }
+                    NameRefKind::Keyword(item) => {
+                        completions::keyword::complete_special_keywords(acc, ctx, item);
+                    }
+                    NameRefKind::RecordExpr(record_expr) => {
+                        completions::record::complete_record_expr_fields_record_expr(
+                            acc,
+                            ctx,
+                            record_expr,
+                        );
+                    }
+                    NameRefKind::Pattern(pattern_ctx) => {
+                        completions::flyimport::import_on_the_fly_pat(acc, ctx, pattern_ctx);
+                        completions::fn_param::complete_fn_param(acc, ctx, pattern_ctx);
+                        completions::pattern::complete_pattern(acc, ctx, pattern_ctx);
+                        completions::record::complete_record_pattern_fields(acc, ctx, pattern_ctx);
+                    }
+                }
             }
-            CompletionAnalysis::Lifetime(lifetime_ctx) => {
+            IdentContext::Lifetime(lifetime_ctx) => {
                 completions::lifetime::complete_label(acc, ctx, lifetime_ctx);
                 completions::lifetime::complete_lifetime(acc, ctx, lifetime_ctx);
             }
-            CompletionAnalysis::String { original, expanded: Some(expanded) } => {
+            IdentContext::String { original, expanded: Some(expanded) } => {
                 completions::extern_abi::complete_extern_abi(acc, ctx, expanded);
                 completions::format_string::format_string(acc, ctx, original, expanded);
             }
-            CompletionAnalysis::UnexpandedAttrTT {
-                colon_prefix,
-                fake_attribute_under_caret: Some(attr),
-            } => {
-                completions::attribute::complete_known_attribute_input(
-                    acc,
-                    ctx,
-                    colon_prefix,
-                    attr,
-                );
+            IdentContext::UnexpandedAttrTT { fake_attribute_under_caret: Some(attr) } => {
+                completions::attribute::complete_known_attribute_input(acc, ctx, attr);
             }
-            CompletionAnalysis::UnexpandedAttrTT { .. } | CompletionAnalysis::String { .. } => (),
+            IdentContext::UnexpandedAttrTT { .. } | IdentContext::String { .. } => (),
         }
     }
 
@@ -205,26 +244,22 @@ pub fn completions(
 pub fn resolve_completion_edits(
     db: &RootDatabase,
     config: &CompletionConfig,
-    FilePosition { file_id, offset }: FilePosition,
+    position: FilePosition,
     imports: impl IntoIterator<Item = (String, String)>,
 ) -> Option<Vec<TextEdit>> {
     let _p = profile::span("resolve_completion_edits");
-    let sema = hir::Semantics::new(db);
-
-    let original_file = sema.parse(file_id);
-    let original_token =
-        syntax::AstNode::syntax(&original_file).token_at_offset(offset).left_biased()?;
-    let position_for_import = &original_token.parent()?;
-    let scope = ImportScope::find_insert_use_container(position_for_import, &sema)?;
+    let ctx = CompletionContext::new(db, position, config)?;
+    let position_for_import = &ctx.original_token.parent()?;
+    let scope = ImportScope::find_insert_use_container(position_for_import, &ctx.sema)?;
 
-    let current_module = sema.scope(position_for_import)?.module();
+    let current_module = ctx.sema.scope(position_for_import)?.module();
     let current_crate = current_module.krate();
     let new_ast = scope.clone_for_update();
     let mut import_insert = TextEdit::builder();
 
     imports.into_iter().for_each(|(full_import_path, imported_name)| {
         let items_with_name = items_locator::items_with_name(
-            &sema,
+            &ctx.sema,
             current_crate,
             NameToImport::exact_case_sensitive(imported_name),
             items_locator::AssocItemSearch::Include,
diff --git a/crates/ide-completion/src/render.rs b/crates/ide-completion/src/render.rs
index 9c339a13e7..c6091645ca 100644
--- a/crates/ide-completion/src/render.rs
+++ b/crates/ide-completion/src/render.rs
@@ -17,7 +17,7 @@ use ide_db::{
 use syntax::{SmolStr, SyntaxKind, TextRange};
 
 use crate::{
-    context::{PathCompletionCtx, PathKind},
+    context::{IdentContext, NameRefContext, NameRefKind, PathCompletionCtx, PathKind},
     item::{Builder, CompletionRelevanceTypeMatch},
     render::{function::render_fn, literal::render_variant_lit, macro_::render_macro},
     CompletionContext, CompletionItem, CompletionItemKind, CompletionRelevance,
@@ -74,6 +74,16 @@ impl<'a> RenderContext<'a> {
                 .map_or(false, |it| it.kind() == SyntaxKind::MACRO_CALL)
     }
 
+    pub(crate) fn path_is_call(&self) -> bool {
+        matches!(
+            self.completion.ident_ctx,
+            IdentContext::NameRef(NameRefContext {
+                kind: NameRefKind::Path(PathCompletionCtx { has_call_parens: true, .. }),
+                ..
+            })
+        )
+    }
+
     fn is_deprecated(&self, def: impl HasAttrs) -> bool {
         let attrs = def.attrs(self.db());
         attrs.by_key("deprecated").exists()
@@ -153,13 +163,12 @@ pub(crate) fn render_tuple_field(
     item.build()
 }
 
-pub(crate) fn render_path_resolution(
+pub(crate) fn render_resolution(
     ctx: RenderContext<'_>,
-    path_ctx: &PathCompletionCtx,
     local_name: hir::Name,
     resolution: ScopeDef,
 ) -> Builder {
-    render_resolution_(ctx, path_ctx, local_name, None, resolution)
+    render_resolution_(ctx, local_name, None, resolution)
 }
 
 pub(crate) fn render_resolution_simple(
@@ -172,7 +181,6 @@ pub(crate) fn render_resolution_simple(
 
 pub(crate) fn render_resolution_with_import(
     ctx: RenderContext<'_>,
-    path_ctx: &PathCompletionCtx,
     import_edit: LocatedImport,
 ) -> Option<Builder> {
     let resolution = ScopeDef::from(import_edit.original_item);
@@ -182,7 +190,7 @@ pub(crate) fn render_resolution_with_import(
         ScopeDef::ModuleDef(hir::ModuleDef::TypeAlias(t)) => t.name(ctx.completion.db),
         _ => item_name(ctx.db(), import_edit.original_item)?,
     };
-    Some(render_resolution_(ctx, path_ctx, local_name, Some(import_edit), resolution))
+    Some(render_resolution_(ctx, local_name, Some(import_edit), resolution))
 }
 
 pub(crate) fn render_type_inference(ty_string: String, ctx: &CompletionContext) -> CompletionItem {
@@ -194,7 +202,6 @@ pub(crate) fn render_type_inference(ty_string: String, ctx: &CompletionContext)
 
 fn render_resolution_(
     ctx: RenderContext<'_>,
-    path_ctx: &PathCompletionCtx,
     local_name: hir::Name,
     import_to_add: Option<LocatedImport>,
     resolution: ScopeDef,
@@ -205,61 +212,21 @@ fn render_resolution_(
     match resolution {
         ScopeDef::ModuleDef(Macro(mac)) => {
             let ctx = ctx.import_to_add(import_to_add);
-            return render_macro(ctx, path_ctx, local_name, mac);
+            return render_macro(ctx, local_name, mac);
         }
         ScopeDef::ModuleDef(Function(func)) => {
             let ctx = ctx.import_to_add(import_to_add);
-            return render_fn(ctx, path_ctx, Some(local_name), func);
+            return render_fn(ctx, Some(local_name), func);
         }
         ScopeDef::ModuleDef(Variant(var)) => {
             let ctx = ctx.clone().import_to_add(import_to_add.clone());
-            if let Some(item) =
-                render_variant_lit(ctx, path_ctx, Some(local_name.clone()), var, None)
-            {
+            if let Some(item) = render_variant_lit(ctx, Some(local_name.clone()), var, None) {
                 return item;
             }
         }
         _ => (),
     }
-    render_resolution_simple_type(ctx, path_ctx, local_name, import_to_add, resolution)
-}
-
-fn render_resolution_simple_type(
-    ctx: RenderContext<'_>,
-    path_ctx: &PathCompletionCtx,
-    local_name: hir::Name,
-    import_to_add: Option<LocatedImport>,
-    resolution: ScopeDef,
-) -> Builder {
-    let cap = ctx.snippet_cap();
-    let db = ctx.completion.db;
-    let config = ctx.completion.config;
-    let name = local_name.to_smol_str();
-    let mut item = render_resolution_simple_(ctx, local_name, import_to_add, resolution);
-    // Add `<>` for generic types
-    let type_path_no_ty_args = matches!(
-        path_ctx,
-        PathCompletionCtx { kind: PathKind::Type { .. }, has_type_args: false, .. }
-    ) && config.callable.is_some();
-    if type_path_no_ty_args {
-        if let Some(cap) = cap {
-            let has_non_default_type_params = match resolution {
-                ScopeDef::ModuleDef(hir::ModuleDef::Adt(it)) => it.has_non_default_type_params(db),
-                ScopeDef::ModuleDef(hir::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(name.clone())
-                    .label(SmolStr::from_iter([&name, "<…>"]))
-                    .trigger_call_info()
-                    .insert_snippet(cap, format!("{}<$0>", name));
-            }
-        }
-    }
-    item
+    render_resolution_simple_(ctx, local_name, import_to_add, resolution)
 }
 
 fn render_resolution_simple_(
@@ -322,6 +289,34 @@ fn render_resolution_simple_(
         }
     };
 
+    // Add `<>` for generic types
+    let type_path_no_ty_args = matches!(
+        ctx.completion.ident_ctx,
+        IdentContext::NameRef(NameRefContext {
+            kind: NameRefKind::Path(PathCompletionCtx {
+                kind: PathKind::Type { .. },
+                has_type_args: false,
+                ..
+            }),
+            ..
+        })
+    ) && ctx.completion.config.callable.is_some();
+    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, "<…>"]))
+                    .trigger_call_info()
+                    .insert_snippet(cap, format!("{}<$0>", local_name));
+            }
+        }
+    }
     item.set_documentation(scope_def_docs(db, resolution))
         .set_deprecated(scope_def_is_deprecated(&ctx, resolution));
 
diff --git a/crates/ide-completion/src/render/function.rs b/crates/ide-completion/src/render/function.rs
index 3666ee40e2..48539a03b1 100644
--- a/crates/ide-completion/src/render/function.rs
+++ b/crates/ide-completion/src/render/function.rs
@@ -8,7 +8,8 @@ use syntax::SmolStr;
 
 use crate::{
     context::{
-        CompletionContext, DotAccess, DotAccessKind, PathCompletionCtx, PathKind, Qualified,
+        CompletionContext, DotAccess, DotAccessKind, IdentContext, NameRefContext, NameRefKind,
+        PathCompletionCtx, PathKind, Qualified,
     },
     item::{Builder, CompletionItem, CompletionItemKind, CompletionRelevance},
     render::{compute_exact_name_match, compute_ref_match, compute_type_match, RenderContext},
@@ -22,54 +23,21 @@ enum FuncKind {
 
 pub(crate) fn render_fn(
     ctx: RenderContext<'_>,
-    path_ctx: &PathCompletionCtx,
     local_name: Option<hir::Name>,
     func: hir::Function,
 ) -> Builder {
     let _p = profile::span("render_fn");
-    let func_kind = FuncKind::Function;
-    let params = match ctx.completion.config.snippet_cap {
-        Some(_) => {
-            if !matches!(
-                path_ctx,
-                PathCompletionCtx { kind: PathKind::Expr { .. }, has_call_parens: true, .. }
-                    | PathCompletionCtx { kind: PathKind::Use | PathKind::Type { .. }, .. }
-            ) {
-                params(ctx.completion, func, &func_kind, false)
-            } else {
-                None
-            }
-        }
-        _ => None,
-    };
-    render(
-        ctx,
-        local_name,
-        func,
-        func_kind,
-        params,
-        matches!(path_ctx.qualified, Qualified::With { .. }),
-    )
+    render(ctx, local_name, func, FuncKind::Function)
 }
 
 pub(crate) fn render_method(
     ctx: RenderContext<'_>,
-    dot_access: &DotAccess,
     receiver: Option<hir::Name>,
     local_name: Option<hir::Name>,
     func: hir::Function,
 ) -> Builder {
     let _p = profile::span("render_method");
-    let func_kind = FuncKind::Method(receiver);
-    let params = match ctx.completion.config.snippet_cap {
-        Some(_) => match dot_access {
-            DotAccess { kind: DotAccessKind::Method { has_parens: true }, .. } => None,
-            _ => params(ctx.completion, func, &func_kind, true),
-        },
-        _ => None,
-    };
-
-    render(ctx, local_name, func, func_kind, params, false)
+    render(ctx, local_name, func, FuncKind::Method(receiver))
 }
 
 fn render(
@@ -77,8 +45,6 @@ fn render(
     local_name: Option<hir::Name>,
     func: hir::Function,
     func_kind: FuncKind,
-    params: Option<(Option<hir::SelfParam>, Vec<hir::Param>)>,
-    qualified_path: bool,
 ) -> Builder {
     let db = completion.db;
 
@@ -114,6 +80,16 @@ fn render(
         // FIXME For now we don't properly calculate the edits for ref match
         // completions on methods or qualified paths, so we've disabled them.
         // See #8058.
+        let qualified_path = matches!(
+            ctx.completion.ident_ctx,
+            IdentContext::NameRef(NameRefContext {
+                kind: NameRefKind::Path(PathCompletionCtx {
+                    qualified: Qualified::With { .. },
+                    ..
+                }),
+                ..
+            })
+        );
         if matches!(func_kind, FuncKind::Function) && !qualified_path {
             item.ref_match(ref_match);
         }
@@ -124,9 +100,11 @@ fn render(
         .detail(detail(db, func))
         .lookup_by(name.to_smol_str());
 
-    match completion.config.snippet_cap.zip(params) {
-        Some((cap, (self_param, params))) => {
-            add_call_parens(&mut item, completion, cap, call, self_param, params);
+    match completion.config.snippet_cap {
+        Some(cap) => {
+            if let Some((self_param, params)) = params(completion, func, &func_kind) {
+                add_call_parens(&mut item, completion, cap, call, self_param, params);
+            }
         }
         _ => (),
     }
@@ -276,12 +254,37 @@ fn params(
     ctx: &CompletionContext<'_>,
     func: hir::Function,
     func_kind: &FuncKind,
-    has_dot_receiver: bool,
 ) -> Option<(Option<hir::SelfParam>, Vec<hir::Param>)> {
     if ctx.config.callable.is_none() {
         return None;
     }
 
+    let has_dot_receiver = match ctx.ident_ctx {
+        IdentContext::NameRef(NameRefContext {
+            kind:
+                NameRefKind::DotAccess(DotAccess {
+                    kind: DotAccessKind::Method { has_parens: true },
+                    ..
+                }),
+            ..
+        }) => return None,
+        IdentContext::NameRef(NameRefContext {
+            kind: NameRefKind::DotAccess(DotAccess { .. }),
+            ..
+        }) => true,
+        IdentContext::NameRef(NameRefContext {
+            kind:
+                NameRefKind::Path(
+                    PathCompletionCtx {
+                        kind: PathKind::Expr { .. }, has_call_parens: true, ..
+                    }
+                    | PathCompletionCtx { kind: PathKind::Use | PathKind::Type { .. }, .. },
+                ),
+            ..
+        }) => return None,
+        _ => false,
+    };
+
     // Don't add parentheses if the expected type is some function reference.
     if let Some(ty) = &ctx.expected_type {
         // FIXME: check signature matches?
diff --git a/crates/ide-completion/src/render/literal.rs b/crates/ide-completion/src/render/literal.rs
index b89030990e..042c974257 100644
--- a/crates/ide-completion/src/render/literal.rs
+++ b/crates/ide-completion/src/render/literal.rs
@@ -4,7 +4,9 @@ use hir::{db::HirDatabase, Documentation, HasAttrs, StructKind};
 use ide_db::SymbolKind;
 
 use crate::{
-    context::{CompletionContext, PathCompletionCtx, PathKind},
+    context::{
+        CompletionContext, IdentContext, NameRefContext, NameRefKind, PathCompletionCtx, PathKind,
+    },
     item::{Builder, CompletionItem},
     render::{
         compute_ref_match, compute_type_match,
@@ -19,7 +21,6 @@ use crate::{
 
 pub(crate) fn render_variant_lit(
     ctx: RenderContext<'_>,
-    path_ctx: &PathCompletionCtx,
     local_name: Option<hir::Name>,
     variant: hir::Variant,
     path: Option<hir::ModPath>,
@@ -28,12 +29,11 @@ pub(crate) fn render_variant_lit(
     let db = ctx.db();
 
     let name = local_name.unwrap_or_else(|| variant.name(db));
-    render(ctx, path_ctx, Variant::EnumVariant(variant), name, path)
+    render(ctx, Variant::EnumVariant(variant), name, path)
 }
 
 pub(crate) fn render_struct_literal(
     ctx: RenderContext<'_>,
-    path_ctx: &PathCompletionCtx,
     strukt: hir::Struct,
     path: Option<hir::ModPath>,
     local_name: Option<hir::Name>,
@@ -42,21 +42,29 @@ pub(crate) fn render_struct_literal(
     let db = ctx.db();
 
     let name = local_name.unwrap_or_else(|| strukt.name(db));
-    render(ctx, path_ctx, Variant::Struct(strukt), name, path)
+    render(ctx, Variant::Struct(strukt), name, path)
 }
 
 fn render(
     ctx @ RenderContext { completion, .. }: RenderContext<'_>,
-    path_ctx: &PathCompletionCtx,
     thing: Variant,
     name: hir::Name,
     path: Option<hir::ModPath>,
 ) -> Option<Builder> {
     let db = completion.db;
     let mut kind = thing.kind(db);
-    let should_add_parens = match &path_ctx {
-        PathCompletionCtx { has_call_parens: true, .. } => false,
-        PathCompletionCtx { kind: PathKind::Use | PathKind::Type { .. }, .. } => false,
+    let should_add_parens = match &completion.ident_ctx {
+        IdentContext::NameRef(NameRefContext {
+            kind: NameRefKind::Path(PathCompletionCtx { has_call_parens: true, .. }),
+            ..
+        }) => false,
+        IdentContext::NameRef(NameRefContext {
+            kind:
+                NameRefKind::Path(PathCompletionCtx {
+                    kind: PathKind::Use | PathKind::Type { .. }, ..
+                }),
+            ..
+        }) => false,
         _ => true,
     };
 
diff --git a/crates/ide-completion/src/render/macro_.rs b/crates/ide-completion/src/render/macro_.rs
index 6da7bb3193..0c9c65f423 100644
--- a/crates/ide-completion/src/render/macro_.rs
+++ b/crates/ide-completion/src/render/macro_.rs
@@ -5,24 +5,18 @@ use ide_db::SymbolKind;
 use syntax::SmolStr;
 
 use crate::{
-    context::{PathCompletionCtx, PathKind},
+    context::{IdentContext, NameRefContext, NameRefKind, PathCompletionCtx, PathKind},
     item::{Builder, CompletionItem},
     render::RenderContext,
 };
 
-pub(crate) fn render_macro(
-    ctx: RenderContext<'_>,
-    path_ctx: &PathCompletionCtx,
-    name: hir::Name,
-    macro_: hir::Macro,
-) -> Builder {
+pub(crate) fn render_macro(ctx: RenderContext<'_>, name: hir::Name, macro_: hir::Macro) -> Builder {
     let _p = profile::span("render_macro");
-    render(ctx, path_ctx, name, macro_)
+    render(ctx, name, macro_)
 }
 
 fn render(
     ctx @ RenderContext { completion, .. }: RenderContext<'_>,
-    PathCompletionCtx { kind, has_macro_bang, has_call_parens, .. }: &PathCompletionCtx,
     name: hir::Name,
     macro_: hir::Macro,
 ) -> Builder {
@@ -39,7 +33,13 @@ fn render(
     let is_fn_like = macro_.is_fn_like(completion.db);
     let (bra, ket) = if is_fn_like { guess_macro_braces(&name, docs_str) } else { ("", "") };
 
-    let needs_bang = is_fn_like && *kind != PathKind::Use && !has_macro_bang;
+    let needs_bang = match &completion.ident_ctx {
+        IdentContext::NameRef(NameRefContext {
+            kind: NameRefKind::Path(PathCompletionCtx { kind, has_macro_bang, .. }),
+            ..
+        }) => is_fn_like && *kind != PathKind::Use && !has_macro_bang,
+        _ => is_fn_like,
+    };
 
     let mut item = CompletionItem::new(
         SymbolKind::from(macro_.kind(completion.db)),
@@ -53,7 +53,7 @@ fn render(
 
     let name = &*name;
     match ctx.snippet_cap() {
-        Some(cap) if needs_bang && !has_call_parens => {
+        Some(cap) if needs_bang && !ctx.path_is_call() => {
             let snippet = format!("{}!{}$0{}", name, bra, ket);
             let lookup = banged_name(name);
             item.insert_snippet(cap, snippet).lookup_by(lookup);
diff --git a/crates/ide-completion/src/render/pattern.rs b/crates/ide-completion/src/render/pattern.rs
index 463d292955..74564bb3aa 100644
--- a/crates/ide-completion/src/render/pattern.rs
+++ b/crates/ide-completion/src/render/pattern.rs
@@ -6,14 +6,16 @@ use itertools::Itertools;
 use syntax::SmolStr;
 
 use crate::{
-    context::{ParamKind, PatternContext},
+    context::{
+        IdentContext, NameContext, NameKind, NameRefContext, NameRefKind, ParamKind,
+        PathCompletionCtx, PathKind, PatternContext,
+    },
     render::{variant::visible_fields, RenderContext},
     CompletionItem, CompletionItemKind,
 };
 
 pub(crate) fn render_struct_pat(
     ctx: RenderContext<'_>,
-    pattern_ctx: &PatternContext,
     strukt: hir::Struct,
     local_name: Option<Name>,
 ) -> Option<CompletionItem> {
@@ -28,21 +30,13 @@ pub(crate) fn render_struct_pat(
     }
 
     let name = local_name.unwrap_or_else(|| strukt.name(ctx.db())).to_smol_str();
-    let pat = render_pat(
-        &ctx,
-        pattern_ctx,
-        &name,
-        strukt.kind(ctx.db()),
-        &visible_fields,
-        fields_omitted,
-    )?;
+    let pat = render_pat(&ctx, &name, strukt.kind(ctx.db()), &visible_fields, fields_omitted)?;
 
     Some(build_completion(ctx, name, pat, strukt))
 }
 
 pub(crate) fn render_variant_pat(
     ctx: RenderContext<'_>,
-    pattern_ctx: &PatternContext,
     variant: hir::Variant,
     local_name: Option<Name>,
     path: Option<&hir::ModPath>,
@@ -56,14 +50,7 @@ pub(crate) fn render_variant_pat(
         Some(path) => path.to_string().into(),
         None => local_name.unwrap_or_else(|| variant.name(ctx.db())).to_smol_str(),
     };
-    let pat = render_pat(
-        &ctx,
-        pattern_ctx,
-        &name,
-        variant.kind(ctx.db()),
-        &visible_fields,
-        fields_omitted,
-    )?;
+    let pat = render_pat(&ctx, &name, variant.kind(ctx.db()), &visible_fields, fields_omitted)?;
 
     Some(build_completion(ctx, name, pat, variant))
 }
@@ -88,28 +75,49 @@ fn build_completion(
 
 fn render_pat(
     ctx: &RenderContext<'_>,
-    pattern_ctx: &PatternContext,
     name: &str,
     kind: StructKind,
     fields: &[hir::Field],
     fields_omitted: bool,
 ) -> Option<String> {
+    let has_call_parens = matches!(
+        ctx.completion.ident_ctx,
+        IdentContext::NameRef(NameRefContext {
+            kind: NameRefKind::Path(PathCompletionCtx { has_call_parens: true, .. }),
+            ..
+        })
+    );
     let mut pat = match kind {
-        StructKind::Tuple => render_tuple_as_pat(ctx.snippet_cap(), fields, name, fields_omitted),
-        StructKind::Record => {
+        StructKind::Tuple if !has_call_parens => {
+            render_tuple_as_pat(ctx.snippet_cap(), fields, name, fields_omitted)
+        }
+        StructKind::Record if !has_call_parens => {
             render_record_as_pat(ctx.db(), ctx.snippet_cap(), fields, name, fields_omitted)
         }
         StructKind::Unit => return None,
+        _ => name.to_owned(),
     };
 
-    let needs_ascription = matches!(
-        pattern_ctx,
-        PatternContext {
-            param_ctx: Some((.., ParamKind::Function(_))),
-            has_type_ascription: false,
-            ..
-        }
-    );
+    let needs_ascription = !has_call_parens
+        && matches!(
+            &ctx.completion.ident_ctx,
+            IdentContext::NameRef(NameRefContext {
+                kind: NameRefKind::Path(PathCompletionCtx {
+                    kind: PathKind::Pat {
+                        pat_ctx
+                    },
+                    ..
+                }),
+                ..
+            }) | IdentContext::Name(NameContext {
+                kind: NameKind::IdentPat(pat_ctx), ..}
+            )
+            if matches!(pat_ctx, PatternContext {
+                param_ctx: Some((.., ParamKind::Function(_))),
+                has_type_ascription: false,
+                ..
+            })
+        );
     if needs_ascription {
         pat.push(':');
         pat.push(' ');
diff --git a/crates/ide-completion/src/snippet.rs b/crates/ide-completion/src/snippet.rs
index a6bb3d0648..82632f440d 100644
--- a/crates/ide-completion/src/snippet.rs
+++ b/crates/ide-completion/src/snippet.rs
@@ -2,6 +2,8 @@
 //!
 //! Actual logic is implemented in [`crate::completions::postfix`] and [`crate::completions::snippet`] respectively.
 
+use std::ops::Deref;
+
 // Feature: User Snippet Completions
 //
 // rust-analyzer allows the user to define custom (postfix)-snippets that may depend on items to be accessible for the current scope to be applicable.
@@ -144,8 +146,8 @@ impl Snippet {
         let (requires, snippet, description) = validate_snippet(snippet, description, requires)?;
         Some(Snippet {
             // Box::into doesn't work as that has a Copy bound 😒
-            postfix_triggers: postfix_triggers.iter().map(String::as_str).map(Into::into).collect(),
-            prefix_triggers: prefix_triggers.iter().map(String::as_str).map(Into::into).collect(),
+            postfix_triggers: postfix_triggers.iter().map(Deref::deref).map(Into::into).collect(),
+            prefix_triggers: prefix_triggers.iter().map(Deref::deref).map(Into::into).collect(),
             scope,
             snippet,
             description,
diff --git a/crates/project-model/src/workspace.rs b/crates/project-model/src/workspace.rs
index 8982a9904e..6aefb84db8 100644
--- a/crates/project-model/src/workspace.rs
+++ b/crates/project-model/src/workspace.rs
@@ -541,8 +541,6 @@ fn cargo_to_crate_graph(
 
     let mut pkg_to_lib_crate = FxHashMap::default();
 
-    // Add test cfg for non-sysroot crates
-    cfg_options.insert_atom("test".into());
     cfg_options.insert_atom("debug_assertions".into());
 
     let mut pkg_crates = FxHashMap::default();
@@ -550,14 +548,18 @@ fn cargo_to_crate_graph(
     let mut has_private = false;
     // Next, create crates for each package, target pair
     for pkg in cargo.packages() {
-        let mut cfg_options = &cfg_options;
-        let mut replaced_cfg_options;
+        let mut cfg_options = cfg_options.clone();
 
         let overrides = match override_cfg {
             CfgOverrides::Wildcard(cfg_diff) => Some(cfg_diff),
             CfgOverrides::Selective(cfg_overrides) => cfg_overrides.get(&cargo[pkg].name),
         };
 
+        // Add test cfg for local crates
+        if cargo[pkg].is_local {
+            cfg_options.insert_atom("test".into());
+        }
+
         if let Some(overrides) = overrides {
             // FIXME: this is sort of a hack to deal with #![cfg(not(test))] vanishing such as seen
             // in ed25519_dalek (#7243), and libcore (#9203) (although you only hit that one while
@@ -566,9 +568,7 @@ fn cargo_to_crate_graph(
             // A more ideal solution might be to reanalyze crates based on where the cursor is and
             // figure out the set of cfgs that would have to apply to make it active.
 
-            replaced_cfg_options = cfg_options.clone();
-            replaced_cfg_options.apply_diff(overrides.clone());
-            cfg_options = &replaced_cfg_options;
+            cfg_options.apply_diff(overrides.clone());
         };
 
         has_private |= cargo[pkg].metadata.rustc_private;
@@ -588,7 +588,7 @@ fn cargo_to_crate_graph(
                     &mut crate_graph,
                     &cargo[pkg],
                     build_scripts.get_output(pkg),
-                    cfg_options,
+                    cfg_options.clone(),
                     &mut |path| load_proc_macro(&cargo[tgt].name, path),
                     file_id,
                     &cargo[tgt].name,
@@ -753,8 +753,7 @@ fn handle_rustc_crates(
                 queue.push_back(dep.pkg);
             }
 
-            let mut cfg_options = cfg_options;
-            let mut replaced_cfg_options;
+            let mut cfg_options = cfg_options.clone();
 
             let overrides = match override_cfg {
                 CfgOverrides::Wildcard(cfg_diff) => Some(cfg_diff),
@@ -771,9 +770,7 @@ fn handle_rustc_crates(
                 // A more ideal solution might be to reanalyze crates based on where the cursor is and
                 // figure out the set of cfgs that would have to apply to make it active.
 
-                replaced_cfg_options = cfg_options.clone();
-                replaced_cfg_options.apply_diff(overrides.clone());
-                cfg_options = &replaced_cfg_options;
+                cfg_options.apply_diff(overrides.clone());
             };
 
             for &tgt in rustc_workspace[pkg].targets.iter() {
@@ -785,7 +782,7 @@ fn handle_rustc_crates(
                         crate_graph,
                         &rustc_workspace[pkg],
                         build_scripts.get_output(pkg),
-                        cfg_options,
+                        cfg_options.clone(),
                         &mut |path| load_proc_macro(&rustc_workspace[tgt].name, path),
                         file_id,
                         &rustc_workspace[tgt].name,
@@ -840,15 +837,21 @@ fn add_target_crate_root(
     crate_graph: &mut CrateGraph,
     pkg: &PackageData,
     build_data: Option<&BuildScriptOutput>,
-    cfg_options: &CfgOptions,
+    cfg_options: CfgOptions,
     load_proc_macro: &mut dyn FnMut(&AbsPath) -> ProcMacroLoadResult,
     file_id: FileId,
     cargo_name: &str,
     is_proc_macro: bool,
 ) -> CrateId {
     let edition = pkg.edition;
+    let mut potential_cfg_options = cfg_options.clone();
+    potential_cfg_options.extend(
+        pkg.features
+            .iter()
+            .map(|feat| CfgFlag::KeyValue { key: "feature".into(), value: feat.0.into() }),
+    );
     let cfg_options = {
-        let mut opts = cfg_options.clone();
+        let mut opts = cfg_options;
         for feature in pkg.active_features.iter() {
             opts.insert_key_value("feature".into(), feature.into());
         }
@@ -873,12 +876,6 @@ fn add_target_crate_root(
     };
 
     let display_name = CrateDisplayName::from_canonical_name(cargo_name.to_string());
-    let mut potential_cfg_options = cfg_options.clone();
-    potential_cfg_options.extend(
-        pkg.features
-            .iter()
-            .map(|feat| CfgFlag::KeyValue { key: "feature".into(), value: feat.0.into() }),
-    );
     crate_graph.add_crate_root(
         file_id,
         edition,


Test patch:
diff --git a/crates/ide-completion/src/context/tests.rs b/crates/ide-completion/src/context/tests.rs
index c5557bdafb..ce9357270b 100644
--- a/crates/ide-completion/src/context/tests.rs
+++ b/crates/ide-completion/src/context/tests.rs
@@ -9,7 +9,7 @@ use crate::{
 fn check_expected_type_and_name(ra_fixture: &str, expect: Expect) {
     let (db, pos) = position(ra_fixture);
     let config = TEST_CONFIG;
-    let (completion_context, _analysis) = CompletionContext::new(&db, pos, &config).unwrap();
+    let completion_context = CompletionContext::new(&db, pos, &config).unwrap();
 
     let ty = completion_context
         .expected_type
diff --git a/crates/ide-completion/src/tests/flyimport.rs b/crates/ide-completion/src/tests/flyimport.rs
index c393e16e7a..a3bc1025e9 100644
--- a/crates/ide-completion/src/tests/flyimport.rs
+++ b/crates/ide-completion/src/tests/flyimport.rs
@@ -1,22 +1,22 @@
 use expect_test::{expect, Expect};
 
 use crate::{
-    context::{CompletionAnalysis, NameContext, NameKind, NameRefKind},
+    context::{IdentContext, NameContext, NameKind, NameRefKind},
     tests::{check_edit, check_edit_with_config, TEST_CONFIG},
 };
 
 fn check(ra_fixture: &str, expect: Expect) {
     let config = TEST_CONFIG;
     let (db, position) = crate::tests::position(ra_fixture);
-    let (ctx, analysis) = crate::context::CompletionContext::new(&db, position, &config).unwrap();
+    let ctx = crate::context::CompletionContext::new(&db, position, &config).unwrap();
 
     let mut acc = crate::completions::Completions::default();
-    if let CompletionAnalysis::Name(NameContext { kind: NameKind::IdentPat(pat_ctx), .. }) =
-        &analysis
+    if let IdentContext::Name(NameContext { kind: NameKind::IdentPat(pat_ctx), .. }) =
+        &ctx.ident_ctx
     {
         crate::completions::flyimport::import_on_the_fly_pat(&mut acc, &ctx, pat_ctx);
     }
-    if let CompletionAnalysis::NameRef(name_ref_ctx) = &analysis {
+    if let IdentContext::NameRef(name_ref_ctx) = &ctx.ident_ctx {
         match &name_ref_ctx.kind {
             NameRefKind::Path(path) => {
                 crate::completions::flyimport::import_on_the_fly_path(&mut acc, &ctx, path);
diff --git a/crates/ide-completion/src/tests/pattern.rs b/crates/ide-completion/src/tests/pattern.rs
index da9e5e2028..d21f62f4ad 100644
--- a/crates/ide-completion/src/tests/pattern.rs
+++ b/crates/ide-completion/src/tests/pattern.rs
@@ -13,18 +13,6 @@ fn check(ra_fixture: &str, expect: Expect) {
     expect.assert_eq(&actual)
 }
 
-#[test]
-fn wildcard() {
-    check(
-        r#"
-fn quux() {
-    let _$0
-}
-"#,
-        expect![""],
-    );
-}
-
 #[test]
 fn ident_rebind_pat() {
     check_empty(
diff --git a/crates/project-model/src/tests.rs b/crates/project-model/src/tests.rs
index 1a43935470..6f883b9e84 100644
--- a/crates/project-model/src/tests.rs
+++ b/crates/project-model/src/tests.rs
@@ -1041,7 +1041,6 @@ fn cargo_hello_world_project_model() {
                                 "debug_assertions",
                                 "feature=default",
                                 "feature=std",
-                                "test",
                             ],
                         ),
                         potential_cfg_options: CfgOptions(
@@ -1054,7 +1053,6 @@ fn cargo_hello_world_project_model() {
                                 "feature=rustc-dep-of-std",
                                 "feature=std",
                                 "feature=use_std",
-                                "test",
                             ],
                         ),
                         env: Env {