Repository: rust-lang/rust-analyzer
Pull request #12560 (2022-06-17 12:55:35)

internal: More completions refactoring
This gets rid of the remaining `ImmediateLocation` bits


Patch:
diff --git a/crates/ide-completion/src/completions/dot.rs b/crates/ide-completion/src/completions/dot.rs
index 4eb1fccd7d..ade57bed95 100644
--- a/crates/ide-completion/src/completions/dot.rs
+++ b/crates/ide-completion/src/completions/dot.rs
@@ -4,7 +4,8 @@ use ide_db::FxHashSet;
 
 use crate::{
     context::{
-        CompletionContext, DotAccess, DotAccessKind, NameRefContext, PathCompletionCtx, PathKind,
+        CompletionContext, DotAccess, DotAccessKind, NameRefContext, NameRefKind,
+        PathCompletionCtx, PathKind,
     },
     CompletionItem, CompletionItemKind, Completions,
 };
@@ -13,7 +14,8 @@ use crate::{
 pub(crate) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) {
     let (dot_access, receiver_ty) = match ctx.nameref_ctx() {
         Some(NameRefContext {
-            dot_access: Some(access @ DotAccess { receiver_ty: Some(receiver_ty), .. }),
+            kind:
+                Some(NameRefKind::DotAccess(access @ DotAccess { receiver_ty: Some(receiver_ty), .. })),
             ..
         }) => (access, &receiver_ty.original),
         _ => return complete_undotted_self(acc, ctx),
diff --git a/crates/ide-completion/src/completions/expr.rs b/crates/ide-completion/src/completions/expr.rs
index 7c3296a0b3..3d92a0ccef 100644
--- a/crates/ide-completion/src/completions/expr.rs
+++ b/crates/ide-completion/src/completions/expr.rs
@@ -5,7 +5,7 @@ use ide_db::FxHashSet;
 use syntax::T;
 
 use crate::{
-    context::{NameRefContext, PathCompletionCtx, PathKind, PathQualifierCtx},
+    context::{NameRefContext, NameRefKind, PathCompletionCtx, PathKind, PathQualifierCtx},
     CompletionContext, Completions,
 };
 
@@ -21,24 +21,29 @@ pub(crate) fn complete_expr_path(acc: &mut Completions, ctx: &CompletionContext)
         after_if_expr,
         wants_mut_token,
     ) = match ctx.nameref_ctx() {
-        Some(NameRefContext {
-            path_ctx:
-                Some(PathCompletionCtx {
+        Some(&NameRefContext {
+            kind:
+                Some(NameRefKind::Path(PathCompletionCtx {
                     kind:
-                        PathKind::Expr { in_block_expr, in_loop_body, after_if_expr, ref_expr_parent },
+                        PathKind::Expr {
+                            in_block_expr,
+                            in_loop_body,
+                            after_if_expr,
+                            ref ref_expr_parent,
+                            ref is_func_update,
+                        },
                     is_absolute_path,
-                    qualifier,
+                    ref qualifier,
                     ..
-                }),
-            record_expr,
+                })),
             ..
         }) if ctx.qualifier_ctx.none() => (
-            *is_absolute_path,
+            is_absolute_path,
             qualifier,
-            *in_block_expr,
-            *in_loop_body,
-            record_expr.as_ref().map_or(false, |&(_, it)| it),
-            *after_if_expr,
+            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),
         ),
         _ => return,
diff --git a/crates/ide-completion/src/completions/field.rs b/crates/ide-completion/src/completions/field.rs
index 1739527917..c540b87a46 100644
--- a/crates/ide-completion/src/completions/field.rs
+++ b/crates/ide-completion/src/completions/field.rs
@@ -1,7 +1,10 @@
 //! Completion of field list position.
 
 use crate::{
-    context::{IdentContext, NameContext, NameKind, NameRefContext, PathCompletionCtx, PathKind},
+    context::{
+        IdentContext, NameContext, NameKind, NameRefContext, NameRefKind, PathCompletionCtx,
+        PathKind, TypeLocation,
+    },
     CompletionContext, Completions,
 };
 
@@ -9,16 +12,16 @@ pub(crate) fn complete_field_list(acc: &mut Completions, ctx: &CompletionContext
     match &ctx.ident_ctx {
         IdentContext::Name(NameContext { kind: NameKind::RecordField, .. })
         | IdentContext::NameRef(NameRefContext {
-            path_ctx:
-                Some(PathCompletionCtx {
+            kind:
+                Some(NameRefKind::Path(PathCompletionCtx {
                     has_macro_bang: false,
                     is_absolute_path: false,
                     qualifier: None,
                     parent: None,
-                    kind: PathKind::Type { in_tuple_struct: true },
+                    kind: PathKind::Type { location: TypeLocation::TupleField },
                     has_type_args: false,
                     ..
-                }),
+                })),
             ..
         }) => {
             if ctx.qualifier_ctx.vis_node.is_none() {
diff --git a/crates/ide-completion/src/completions/flyimport.rs b/crates/ide-completion/src/completions/flyimport.rs
index 901f7519d2..6266bcef34 100644
--- a/crates/ide-completion/src/completions/flyimport.rs
+++ b/crates/ide-completion/src/completions/flyimport.rs
@@ -8,8 +8,10 @@ use itertools::Itertools;
 use syntax::{AstNode, SyntaxNode, T};
 
 use crate::{
-    context::{CompletionContext, NameRefContext, PathCompletionCtx, PathKind, PatternContext},
-    patterns::ImmediateLocation,
+    context::{
+        CompletionContext, NameRefContext, NameRefKind, PathCompletionCtx, PathKind,
+        PatternContext, TypeLocation,
+    },
     render::{render_resolution_with_import, RenderContext},
 };
 
@@ -111,19 +113,20 @@ pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext)
         return None;
     }
     let path_kind = match ctx.nameref_ctx() {
-        Some(NameRefContext { path_ctx: Some(PathCompletionCtx { kind, .. }), .. })
-            if matches!(
-                kind,
-                PathKind::Expr { .. }
-                    | PathKind::Type { .. }
-                    | PathKind::Attr { .. }
-                    | PathKind::Derive
-                    | PathKind::Pat
-            ) =>
-        {
-            Some(kind)
-        }
-        Some(NameRefContext { dot_access: Some(_), .. }) => None,
+        Some(NameRefContext {
+            kind:
+                Some(NameRefKind::Path(PathCompletionCtx {
+                    kind:
+                        kind @ (PathKind::Expr { .. }
+                        | PathKind::Type { .. }
+                        | PathKind::Attr { .. }
+                        | PathKind::Derive
+                        | PathKind::Pat),
+                    ..
+                })),
+            ..
+        }) => Some(kind),
+        Some(NameRefContext { kind: Some(NameRefKind::DotAccess(_)), .. }) => None,
         None if matches!(ctx.pattern_ctx, Some(PatternContext { record_pat: None, .. })) => {
             Some(&PathKind::Pat)
         }
@@ -173,8 +176,8 @@ pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext)
             (PathKind::Pat, ItemInNs::Types(_)) => true,
             (PathKind::Pat, ItemInNs::Values(def)) => matches!(def, hir::ModuleDef::Const(_)),
 
-            (PathKind::Type { .. }, ItemInNs::Types(ty)) => {
-                if matches!(ctx.completion_location, Some(ImmediateLocation::TypeBound)) {
+            (PathKind::Type { location }, ItemInNs::Types(ty)) => {
+                if matches!(location, TypeLocation::TypeBound) {
                     matches!(ty, ModuleDef::Trait(_))
                 } else {
                     true
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 56f656f479..846d5f0902 100644
--- a/crates/ide-completion/src/completions/item_list/trait_impl.rs
+++ b/crates/ide-completion/src/completions/item_list/trait_impl.rs
@@ -44,8 +44,8 @@ use text_edit::TextEdit;
 
 use crate::{
     context::{
-        IdentContext, ItemListKind, NameContext, NameKind, NameRefContext, PathCompletionCtx,
-        PathKind,
+        IdentContext, ItemListKind, NameContext, NameKind, NameRefContext, NameRefKind,
+        PathCompletionCtx, PathKind,
     },
     CompletionContext, CompletionItem, CompletionItemKind, CompletionRelevance, Completions,
 };
@@ -106,14 +106,13 @@ fn completion_match(ctx: &CompletionContext) -> Option<(ImplCompletionKind, Text
         }
         IdentContext::NameRef(NameRefContext {
             nameref,
-            path_ctx:
-                Some(
+            kind:
+                Some(NameRefKind::Path(
                     path_ctx @ PathCompletionCtx {
                         kind: PathKind::Item { kind: ItemListKind::TraitImpl },
                         ..
                     },
-                ),
-            ..
+                )),
         }) if path_ctx.is_trivial_path() => Some((
             ImplCompletionKind::All,
             match nameref {
diff --git a/crates/ide-completion/src/completions/keyword.rs b/crates/ide-completion/src/completions/keyword.rs
index 65fa119178..2e266b7714 100644
--- a/crates/ide-completion/src/completions/keyword.rs
+++ b/crates/ide-completion/src/completions/keyword.rs
@@ -4,11 +4,14 @@
 
 use syntax::ast::Item;
 
-use crate::{context::NameRefContext, CompletionContext, Completions};
+use crate::{
+    context::{NameRefContext, NameRefKind},
+    CompletionContext, Completions,
+};
 
 pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) {
     let item = match ctx.nameref_ctx() {
-        Some(NameRefContext { keyword: Some(item), record_expr: None, .. }) => item,
+        Some(NameRefContext { kind: Some(NameRefKind::Keyword(item)), .. }) => item,
         _ => return,
     };
 
diff --git a/crates/ide-completion/src/completions/postfix.rs b/crates/ide-completion/src/completions/postfix.rs
index 4868225ce3..888b8f3488 100644
--- a/crates/ide-completion/src/completions/postfix.rs
+++ b/crates/ide-completion/src/completions/postfix.rs
@@ -13,7 +13,7 @@ use text_edit::TextEdit;
 
 use crate::{
     completions::postfix::format_like::add_format_like_completions,
-    context::{CompletionContext, DotAccess, DotAccessKind, NameRefContext},
+    context::{CompletionContext, DotAccess, DotAccessKind, NameRefContext, NameRefKind},
     item::{Builder, CompletionRelevancePostfixMatch},
     CompletionItem, CompletionItemKind, CompletionRelevance, Completions, SnippetScope,
 };
@@ -25,7 +25,13 @@ pub(crate) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
 
     let (dot_receiver, receiver_ty, receiver_is_ambiguous_float_literal) = match ctx.nameref_ctx() {
         Some(NameRefContext {
-            dot_access: Some(DotAccess { receiver_ty: Some(ty), receiver: Some(it), kind, .. }),
+            kind:
+                Some(NameRefKind::DotAccess(DotAccess {
+                    receiver_ty: Some(ty),
+                    receiver: Some(it),
+                    kind,
+                    ..
+                })),
             ..
         }) => (
             it,
diff --git a/crates/ide-completion/src/completions/record.rs b/crates/ide-completion/src/completions/record.rs
index 65805dba1c..d74ae260b5 100644
--- a/crates/ide-completion/src/completions/record.rs
+++ b/crates/ide-completion/src/completions/record.rs
@@ -3,7 +3,7 @@ use ide_db::SymbolKind;
 use syntax::{ast::Expr, T};
 
 use crate::{
-    context::{NameRefContext, PatternContext},
+    context::{NameRefContext, NameRefKind, PathCompletionCtx, PathKind, PatternContext},
     CompletionContext, CompletionItem, CompletionItemKind, CompletionRelevance,
     CompletionRelevancePostfixMatch, Completions,
 };
@@ -13,8 +13,18 @@ pub(crate) fn complete_record(acc: &mut Completions, ctx: &CompletionContext) ->
         &ctx.pattern_ctx
     {
         ctx.sema.record_pattern_missing_fields(record_pat)
-    } else if let Some(NameRefContext { record_expr: Some((record_expr, _)), .. }) =
-        ctx.nameref_ctx()
+    } else if let Some(NameRefContext {
+        kind:
+            Some(
+                NameRefKind::RecordExpr(record_expr)
+                | NameRefKind::Path(PathCompletionCtx {
+                    kind: PathKind::Expr { is_func_update: Some(record_expr), .. },
+                    qualifier: None,
+                    ..
+                }),
+            ),
+        ..
+    }) = ctx.nameref_ctx()
     {
         let ty = ctx.sema.type_of_expr(&Expr::RecordExpr(record_expr.clone()));
 
@@ -39,7 +49,7 @@ pub(crate) fn complete_record(acc: &mut Completions, ctx: &CompletionContext) ->
                     ty.original.impls_trait(ctx.db, default_trait, &[])
                 });
 
-            if impl_default_trait && !missing_fields.is_empty() && ctx.path_qual().is_none() {
+            if impl_default_trait && !missing_fields.is_empty() {
                 let completion_text = "..Default::default()";
                 let mut item =
                     CompletionItem::new(SymbolKind::Field, ctx.source_range(), completion_text);
diff --git a/crates/ide-completion/src/completions/type.rs b/crates/ide-completion/src/completions/type.rs
index 9cf0b87ad6..9d3a1c2429 100644
--- a/crates/ide-completion/src/completions/type.rs
+++ b/crates/ide-completion/src/completions/type.rs
@@ -5,8 +5,7 @@ use ide_db::FxHashSet;
 use syntax::{ast, AstNode};
 
 use crate::{
-    context::{PathCompletionCtx, PathKind, PathQualifierCtx},
-    patterns::{ImmediateLocation, TypeAnnotation},
+    context::{PathCompletionCtx, PathKind, PathQualifierCtx, TypeAscriptionTarget, TypeLocation},
     render::render_type_inference,
     CompletionContext, Completions,
 };
@@ -14,13 +13,13 @@ use crate::{
 pub(crate) fn complete_type_path(acc: &mut Completions, ctx: &CompletionContext) {
     let _p = profile::span("complete_type_path");
 
-    let (&is_absolute_path, qualifier) = match ctx.path_context() {
+    let (&is_absolute_path, location, qualifier) = match ctx.path_context() {
         Some(PathCompletionCtx {
-            kind: PathKind::Type { .. },
+            kind: PathKind::Type { location },
             is_absolute_path,
             qualifier,
             ..
-        }) => (is_absolute_path, qualifier),
+        }) => (is_absolute_path, location, qualifier),
         _ => return,
     };
 
@@ -32,7 +31,7 @@ pub(crate) fn complete_type_path(acc: &mut Completions, ctx: &CompletionContext)
             ScopeDef::ModuleDef(Function(_) | Variant(_) | Static(_)) | ScopeDef::Local(_) => false,
             // unless its a constant in a generic arg list position
             ScopeDef::ModuleDef(Const(_)) | ScopeDef::GenericParam(ConstParam(_)) => {
-                ctx.expects_generic_arg()
+                matches!(location, TypeLocation::GenericArgList(_))
             }
             ScopeDef::ImplSelfType(_) => {
                 !ctx.previous_token_is(syntax::T![impl]) && !ctx.previous_token_is(syntax::T![for])
@@ -47,6 +46,14 @@ pub(crate) fn complete_type_path(acc: &mut Completions, ctx: &CompletionContext)
         }
     };
 
+    let add_assoc_item = |acc: &mut Completions, item| match item {
+        hir::AssocItem::Const(ct) if matches!(location, TypeLocation::GenericArgList(_)) => {
+            acc.add_const(ctx, ct)
+        }
+        hir::AssocItem::Function(_) | hir::AssocItem::Const(_) => (),
+        hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty),
+    };
+
     match qualifier {
         Some(PathQualifierCtx { is_infer_qualifier, resolution, .. }) => {
             if *is_infer_qualifier {
@@ -54,7 +61,7 @@ pub(crate) fn complete_type_path(acc: &mut Completions, ctx: &CompletionContext)
                     .0
                     .into_iter()
                     .flat_map(|it| hir::Trait::from(it).items(ctx.sema.db))
-                    .for_each(|item| add_assoc_item(acc, ctx, item));
+                    .for_each(|item| add_assoc_item(acc, item));
                 return;
             }
             let resolution = match resolution {
@@ -98,7 +105,7 @@ pub(crate) fn complete_type_path(acc: &mut Completions, ctx: &CompletionContext)
                         Some(ctx.module),
                         None,
                         |item| {
-                            add_assoc_item(acc, ctx, item);
+                            add_assoc_item(acc, item);
                             None::<()>
                         },
                     );
@@ -114,7 +121,7 @@ pub(crate) fn complete_type_path(acc: &mut Completions, ctx: &CompletionContext)
                 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, item);
+                        add_assoc_item(acc, item);
                     }
                 }
                 hir::PathResolution::TypeParam(_) | hir::PathResolution::SelfType(_) => {
@@ -135,7 +142,7 @@ pub(crate) fn complete_type_path(acc: &mut Completions, ctx: &CompletionContext)
                             // We might iterate candidates of a trait multiple times here, so deduplicate
                             // them.
                             if seen.insert(item) {
-                                add_assoc_item(acc, ctx, item);
+                                add_assoc_item(acc, item);
                             }
                             None::<()>
                         },
@@ -147,7 +154,7 @@ pub(crate) fn complete_type_path(acc: &mut Completions, ctx: &CompletionContext)
         None if is_absolute_path => acc.add_crate_roots(ctx),
         None => {
             acc.add_nameref_keywords_with_colon(ctx);
-            if let Some(ImmediateLocation::TypeBound) = &ctx.completion_location {
+            if let TypeLocation::TypeBound = location {
                 ctx.process_all_names(&mut |name, res| {
                     let add_resolution = match res {
                         ScopeDef::ModuleDef(hir::ModuleDef::Macro(mac)) => mac.is_fn_like(ctx.db),
@@ -162,7 +169,7 @@ pub(crate) fn complete_type_path(acc: &mut Completions, ctx: &CompletionContext)
                 });
                 return;
             }
-            if let Some(ImmediateLocation::GenericArgList(arg_list)) = &ctx.completion_location {
+            if let TypeLocation::GenericArgList(Some(arg_list)) = location {
                 if let Some(path_seg) = arg_list.syntax().parent().and_then(ast::PathSegment::cast)
                 {
                     if path_seg.syntax().ancestors().find_map(ast::TypeBound::cast).is_some() {
@@ -189,25 +196,25 @@ pub(crate) fn complete_type_path(acc: &mut Completions, ctx: &CompletionContext)
 }
 
 pub(crate) fn complete_inferred_type(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
-    use TypeAnnotation::*;
-    let pat = match &ctx.completion_location {
-        Some(ImmediateLocation::TypeAnnotation(t)) => t,
+    let pat = match ctx.path_context() {
+        Some(
+            ctx @ PathCompletionCtx {
+                kind: PathKind::Type { location: TypeLocation::TypeAscription(ascription), .. },
+                ..
+            },
+        ) if ctx.is_trivial_path() => ascription,
         _ => return None,
     };
     let x = match pat {
-        Let(pat) | FnParam(pat) => ctx.sema.type_of_pat(pat.as_ref()?),
-        Const(exp) | RetType(exp) => ctx.sema.type_of_expr(exp.as_ref()?),
+        TypeAscriptionTarget::Let(pat) | TypeAscriptionTarget::FnParam(pat) => {
+            ctx.sema.type_of_pat(pat.as_ref()?)
+        }
+        TypeAscriptionTarget::Const(exp) | TypeAscriptionTarget::RetType(exp) => {
+            ctx.sema.type_of_expr(exp.as_ref()?)
+        }
     }?
     .adjusted();
     let ty_string = x.display_source_code(ctx.db, ctx.module.into()).ok()?;
     acc.add(render_type_inference(ty_string, ctx));
     None
 }
-
-fn add_assoc_item(acc: &mut Completions, ctx: &CompletionContext, item: hir::AssocItem) {
-    match item {
-        hir::AssocItem::Const(ct) if ctx.expects_generic_arg() => acc.add_const(ctx, ct),
-        hir::AssocItem::Function(_) | hir::AssocItem::Const(_) => (),
-        hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty),
-    }
-}
diff --git a/crates/ide-completion/src/completions/use_.rs b/crates/ide-completion/src/completions/use_.rs
index dc7a342dce..5d062098d7 100644
--- a/crates/ide-completion/src/completions/use_.rs
+++ b/crates/ide-completion/src/completions/use_.rs
@@ -5,7 +5,10 @@ use ide_db::{FxHashSet, SymbolKind};
 use syntax::{ast, AstNode};
 
 use crate::{
-    context::{CompletionContext, NameRefContext, PathCompletionCtx, PathKind, PathQualifierCtx},
+    context::{
+        CompletionContext, NameRefContext, NameRefKind, PathCompletionCtx, PathKind,
+        PathQualifierCtx,
+    },
     item::Builder,
     CompletionItem, CompletionItemKind, CompletionRelevance, Completions,
 };
@@ -13,8 +16,13 @@ use crate::{
 pub(crate) fn complete_use_tree(acc: &mut Completions, ctx: &CompletionContext) {
     let (&is_absolute_path, qualifier, name_ref) = match ctx.nameref_ctx() {
         Some(NameRefContext {
-            path_ctx:
-                Some(PathCompletionCtx { kind: PathKind::Use, is_absolute_path, qualifier, .. }),
+            kind:
+                Some(NameRefKind::Path(PathCompletionCtx {
+                    kind: PathKind::Use,
+                    is_absolute_path,
+                    qualifier,
+                    ..
+                })),
             nameref,
             ..
         }) => (is_absolute_path, qualifier, nameref),
diff --git a/crates/ide-completion/src/context.rs b/crates/ide-completion/src/context.rs
index 02307def9e..7421a3e98b 100644
--- a/crates/ide-completion/src/context.rs
+++ b/crates/ide-completion/src/context.rs
@@ -22,10 +22,7 @@ use syntax::{
 use text_edit::Indel;
 
 use crate::{
-    patterns::{
-        determine_location, is_in_loop_body, is_in_token_of_for_loop, previous_token,
-        ImmediateLocation,
-    },
+    patterns::{is_in_loop_body, is_in_token_of_for_loop, previous_token},
     CompletionConfig,
 };
 
@@ -43,43 +40,7 @@ pub(crate) enum Visible {
     No,
 }
 
-#[derive(Clone, Debug, PartialEq, Eq)]
-pub(super) enum PathKind {
-    Expr {
-        in_block_expr: bool,
-        in_loop_body: bool,
-        after_if_expr: bool,
-        ref_expr_parent: Option<ast::RefExpr>,
-    },
-    Type {
-        in_tuple_struct: bool,
-    },
-    Attr {
-        kind: AttrKind,
-        annotated_item_kind: Option<SyntaxKind>,
-    },
-    Derive,
-    /// Path in item position, that is inside an (Assoc)ItemList
-    Item {
-        kind: ItemListKind,
-    },
-    Pat,
-    Vis {
-        has_in_token: bool,
-    },
-    Use,
-}
-
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
-pub(super) enum ItemListKind {
-    SourceFile,
-    Module,
-    Impl,
-    TraitImpl,
-    Trait,
-    ExternBlock,
-}
-
+/// Existing qualifiers for the thing we are currently completing.
 #[derive(Debug, Default)]
 pub(super) struct QualifierCtx {
     pub(super) unsafe_tok: Option<SyntaxToken>,
@@ -92,6 +53,7 @@ impl QualifierCtx {
     }
 }
 
+/// The state of the path we are currently completing.
 #[derive(Debug)]
 pub(crate) struct PathCompletionCtx {
     /// If this is a call with () already there (or {} in case of record patterns)
@@ -126,6 +88,65 @@ impl PathCompletionCtx {
     }
 }
 
+/// The kind of path we are completing right now.
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub(super) enum PathKind {
+    Expr {
+        in_block_expr: bool,
+        in_loop_body: bool,
+        after_if_expr: bool,
+        ref_expr_parent: Option<ast::RefExpr>,
+        is_func_update: Option<ast::RecordExpr>,
+    },
+    Type {
+        location: TypeLocation,
+    },
+    Attr {
+        kind: AttrKind,
+        annotated_item_kind: Option<SyntaxKind>,
+    },
+    Derive,
+    /// Path in item position, that is inside an (Assoc)ItemList
+    Item {
+        kind: ItemListKind,
+    },
+    Pat,
+    Vis {
+        has_in_token: bool,
+    },
+    Use,
+}
+
+/// Original file ast nodes
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub(crate) enum TypeLocation {
+    TupleField,
+    TypeAscription(TypeAscriptionTarget),
+    GenericArgList(Option<ast::GenericArgList>),
+    TypeBound,
+    Other,
+}
+
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub(crate) enum TypeAscriptionTarget {
+    Let(Option<ast::Pat>),
+    FnParam(Option<ast::Pat>),
+    RetType(Option<ast::Expr>),
+    Const(Option<ast::Expr>),
+}
+
+/// The kind of item list a [`PathKind::Item`] belongs to.
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub(super) enum ItemListKind {
+    SourceFile,
+    Module,
+    Impl,
+    TraitImpl,
+    Trait,
+    ExternBlock,
+}
+
+/// The path qualifier state of the path we are completing.
 #[derive(Debug)]
 pub(crate) struct PathQualifierCtx {
     pub(crate) path: ast::Path,
@@ -138,6 +159,7 @@ pub(crate) struct PathQualifierCtx {
     pub(crate) is_infer_qualifier: bool,
 }
 
+/// The state of the pattern we are completing.
 #[derive(Debug)]
 pub(super) struct PatternContext {
     pub(super) refutability: PatternRefutability,
@@ -150,12 +172,14 @@ pub(super) struct PatternContext {
     pub(super) record_pat: Option<ast::RecordPat>,
 }
 
+/// The state of the lifetime we are completing.
 #[derive(Debug)]
 pub(super) struct LifetimeContext {
     pub(super) lifetime: Option<ast::Lifetime>,
     pub(super) kind: LifetimeKind,
 }
 
+/// The kind of lifetime we are completing.
 #[derive(Debug)]
 pub(super) enum LifetimeKind {
     LifetimeParam { is_decl: bool, param: ast::LifetimeParam },
@@ -164,6 +188,7 @@ pub(super) enum LifetimeKind {
     LabelDef,
 }
 
+/// The state of the name we are completing.
 #[derive(Debug)]
 pub(super) struct NameContext {
     #[allow(dead_code)]
@@ -171,6 +196,7 @@ pub(super) struct NameContext {
     pub(super) kind: NameKind,
 }
 
+/// The kind of the name we are completing.
 #[derive(Debug)]
 #[allow(dead_code)]
 pub(super) enum NameKind {
@@ -195,34 +221,46 @@ pub(super) enum NameKind {
     Variant,
 }
 
+/// The state of the NameRef we are completing.
 #[derive(Debug)]
 pub(super) struct NameRefContext {
     /// NameRef syntax in the original file
     pub(super) nameref: Option<ast::NameRef>,
-    // FIXME: these fields are actually disjoint -> enum
-    pub(super) dot_access: Option<DotAccess>,
-    pub(super) path_ctx: Option<PathCompletionCtx>,
+    // FIXME: This shouldn't be an Option
+    pub(super) kind: Option<NameRefKind>,
+}
+
+/// The kind of the NameRef we are completing.
+#[derive(Debug)]
+pub(super) enum NameRefKind {
+    Path(PathCompletionCtx),
+    DotAccess(DotAccess),
     /// Position where we are only interested in keyword completions
-    pub(super) keyword: Option<ast::Item>,
+    Keyword(ast::Item),
     /// The record expression this nameref is a field of
-    pub(super) record_expr: Option<(ast::RecordExpr, bool)>,
+    RecordExpr(ast::RecordExpr),
 }
 
+/// The identifier we are currently completing.
 #[derive(Debug)]
 pub(super) enum IdentContext {
     Name(NameContext),
     NameRef(NameRefContext),
     Lifetime(LifetimeContext),
-    /// Original token, fake token
+    /// The string the cursor is currently inside
     String {
+        /// original token
         original: ast::String,
+        /// fake token
         expanded: Option<ast::String>,
     },
+    /// Set if we are currently completing in an unexpanded attribute, this usually implies a builtin attribute like `allow($0)`
     UnexpandedAttrTT {
         fake_attribute_under_caret: Option<ast::Attr>,
     },
 }
 
+/// Information about the field or method access we are completing.
 #[derive(Debug)]
 pub(super) struct DotAccess {
     pub(super) receiver: Option<ast::Expr>,
@@ -278,9 +316,9 @@ pub(crate) struct CompletionContext<'a> {
     /// The parent impl of the cursor position if it exists.
     pub(super) impl_def: Option<ast::Impl>,
     /// Are we completing inside a let statement with a missing semicolon?
+    // FIXME: This should be part of PathKind::Expr
     pub(super) incomplete_let: bool,
 
-    pub(super) completion_location: Option<ImmediateLocation>,
     pub(super) previous_token: Option<SyntaxToken>,
 
     pub(super) ident_ctx: IdentContext,
@@ -341,9 +379,10 @@ impl<'a> CompletionContext<'a> {
 
     pub(crate) fn dot_receiver(&self) -> Option<&ast::Expr> {
         match self.nameref_ctx() {
-            Some(NameRefContext { dot_access: Some(DotAccess { receiver, .. }), .. }) => {
-                receiver.as_ref()
-            }
+            Some(NameRefContext {
+                kind: Some(NameRefKind::DotAccess(DotAccess { receiver, .. })),
+                ..
+            }) => receiver.as_ref(),
             _ => None,
         }
     }
@@ -352,13 +391,11 @@ impl<'a> CompletionContext<'a> {
         self.dot_receiver().is_some()
     }
 
-    // FIXME: This shouldn't exist
-    pub(crate) fn expects_generic_arg(&self) -> bool {
-        matches!(self.completion_location, Some(ImmediateLocation::GenericArgList(_)))
-    }
-
     pub(crate) fn path_context(&self) -> Option<&PathCompletionCtx> {
-        self.nameref_ctx().and_then(|ctx| ctx.path_ctx.as_ref())
+        self.nameref_ctx().and_then(|ctx| match &ctx.kind {
+            Some(NameRefKind::Path(path)) => Some(path),
+            _ => None,
+        })
     }
 
     pub(crate) fn path_qual(&self) -> Option<&ast::Path> {
@@ -505,7 +542,6 @@ impl<'a> CompletionContext<'a> {
             function_def: None,
             impl_def: None,
             incomplete_let: false,
-            completion_location: None,
             previous_token: None,
             // dummy value, will be overwritten
             ident_ctx: IdentContext::UnexpandedAttrTT { fake_attribute_under_caret: None },
@@ -857,7 +893,7 @@ impl<'a> CompletionContext<'a> {
                 let parent = name_ref.syntax().parent()?;
                 let (mut nameref_ctx, _, _) =
                     Self::classify_name_ref(&self.sema, &original_file, name_ref, parent);
-                if let Some(path_ctx) = &mut nameref_ctx.path_ctx {
+                if let Some(NameRefKind::Path(path_ctx)) = &mut nameref_ctx.kind {
                     path_ctx.kind = PathKind::Derive;
                 }
                 self.ident_ctx = IdentContext::NameRef(nameref_ctx);
@@ -898,8 +934,6 @@ impl<'a> CompletionContext<'a> {
                 return Some(());
             }
         };
-        self.completion_location =
-            determine_location(&self.sema, original_file, offset, &name_like);
         self.impl_def = self
             .sema
             .token_ancestors_with_macros(self.token.clone())
@@ -1026,23 +1060,13 @@ impl<'a> CompletionContext<'a> {
     ) -> (NameRefContext, Option<PatternContext>, QualifierCtx) {
         let nameref = find_node_at_offset(&original_file, name_ref.syntax().text_range().start());
 
-        let mut res = (
-            NameRefContext {
-                dot_access: None,
-                path_ctx: None,
-                nameref,
-                record_expr: None,
-                keyword: None,
-            },
-            None,
-            QualifierCtx::default(),
-        );
+        let mut res = (NameRefContext { nameref, kind: None }, None, QualifierCtx::default());
         let (nameref_ctx, pattern_ctx, qualifier_ctx) = &mut res;
 
         if let Some(record_field) = ast::RecordExprField::for_field_name(&name_ref) {
-            nameref_ctx.record_expr =
+            nameref_ctx.kind =
                 find_node_in_file_compensated(original_file, &record_field.parent_record_lit())
-                    .zip(Some(false));
+                    .map(NameRefKind::RecordExpr);
             return res;
         }
         if let Some(record_field) = ast::RecordPatField::for_field_name_ref(&name_ref) {
@@ -1067,7 +1091,7 @@ impl<'a> CompletionContext<'a> {
             match parent {
                 ast::PathSegment(segment) => segment,
                 ast::FieldExpr(field) => {
-                    let receiver = find_in_original_file(field.expr(), original_file);
+                    let receiver = find_opt_node_in_file(original_file, field.expr());
                     let receiver_is_ambiguous_float_literal = match &receiver {
                         Some(ast::Expr::Literal(l)) => matches! {
                             l.kind(),
@@ -1075,20 +1099,20 @@ impl<'a> CompletionContext<'a> {
                         },
                         _ => false,
                     };
-                    nameref_ctx.dot_access = Some(DotAccess {
+                    nameref_ctx.kind = Some(NameRefKind::DotAccess(DotAccess {
                         receiver_ty: receiver.as_ref().and_then(|it| sema.type_of_expr(it)),
                         kind: DotAccessKind::Field { receiver_is_ambiguous_float_literal },
                         receiver
-                    });
+                    }));
                     return res;
                 },
                 ast::MethodCallExpr(method) => {
-                    let receiver = find_in_original_file(method.receiver(), original_file);
-                    nameref_ctx.dot_access = Some(DotAccess {
+                    let receiver = find_opt_node_in_file(original_file, method.receiver());
+                    nameref_ctx.kind = Some(NameRefKind::DotAccess(DotAccess {
                         receiver_ty: receiver.as_ref().and_then(|it| sema.type_of_expr(it)),
                         kind: DotAccessKind::Method { has_parens: method.arg_list().map_or(false, |it| it.l_paren_token().is_some()) },
                         receiver
-                    });
+                    }));
                     return res;
                 },
                 _ => return res,
@@ -1113,10 +1137,11 @@ impl<'a> CompletionContext<'a> {
                 })
                 .unwrap_or(false)
         };
-        let mut fill_record_expr = |syn: &SyntaxNode| {
+        let func_update_record = |syn: &SyntaxNode| {
             if let Some(record_expr) = syn.ancestors().nth(2).and_then(ast::RecordExpr::cast) {
-                nameref_ctx.record_expr =
-                    find_node_in_file_compensated(original_file, &record_expr).zip(Some(true));
+                find_node_in_file_compensated(original_file, &record_expr)
+            } else {
+                None
             }
         };
         let after_if_expr = |node: SyntaxNode| {
@@ -1161,33 +1186,91 @@ impl<'a> CompletionContext<'a> {
             None
         };
 
+        let type_location = |it: Option<SyntaxNode>| {
+            let parent = it?;
+            let res = match_ast! {
+                match parent {
+                    ast::Const(it) => {
+                        let name = find_opt_node_in_file(original_file, it.name())?;
+                        let original = ast::Const::cast(name.syntax().parent()?)?;
+                        TypeLocation::TypeAscription(TypeAscriptionTarget::Const(original.body()))
+                    },
+                    ast::RetType(it) => {
+                        if it.thin_arrow_token().is_none() {
+                            return None;
+                        }
+                        let parent = match ast::Fn::cast(parent.parent()?) {
+                            Some(x) => x.param_list(),
+                            None => ast::ClosureExpr::cast(parent.parent()?)?.param_list(),
+                        };
+
+                        let parent = find_opt_node_in_file(original_file, parent)?.syntax().parent()?;
+                        TypeLocation::TypeAscription(TypeAscriptionTarget::RetType(match_ast! {
+                            match parent {
+                                ast::ClosureExpr(it) => {
+                                    it.body()
+                                },
+                                ast::Fn(it) => {
+                                    it.body().map(ast::Expr::BlockExpr)
+                                },
+                                _ => return None,
+                            }
+                        }))
+                    },
+                    ast::Param(it) => {
+                        if it.colon_token().is_none() {
+                            return None;
+                        }
+                        TypeLocation::TypeAscription(TypeAscriptionTarget::FnParam(find_opt_node_in_file(original_file, it.pat())))
+                    },
+                    ast::LetStmt(it) => {
+                        if it.colon_token().is_none() {
+                            return None;
+                        }
+                        TypeLocation::TypeAscription(TypeAscriptionTarget::Let(find_opt_node_in_file(original_file, it.pat())))
+                    },
+                    ast::TypeBound(_) => TypeLocation::TypeBound,
+                    // is this case needed?
+                    ast::TypeBoundList(_) => TypeLocation::TypeBound,
+                    ast::GenericArg(it) => TypeLocation::GenericArgList(find_opt_node_in_file_compensated(original_file, it.syntax().parent().and_then(ast::GenericArgList::cast))),
+                    // is this case needed?
+                    ast::GenericArgList(it) => TypeLocation::GenericArgList(find_opt_node_in_file_compensated(original_file, Some(it))),
+                    ast::TupleField(_) => TypeLocation::TupleField,
+                    _ => return None,
+                }
+            };
+            Some(res)
+        };
+
         // Infer the path kind
         let kind = path.syntax().parent().and_then(|it| {
             match_ast! {
                 match it {
-                    ast::PathType(it) => Some(PathKind::Type {
-                        in_tuple_struct: it.syntax().parent().map_or(false, |it| ast::TupleField::can_cast(it.kind()))
-                    }),
+                    ast::PathType(it) => {
+                        let location = type_location(it.syntax().parent());
+                        Some(PathKind::Type {
+                            location: location.unwrap_or(TypeLocation::Other),
+                        })
+                    },
                     ast::PathExpr(it) => {
                         if let Some(p) = it.syntax().parent() {
                             if ast::ExprStmt::can_cast(p.kind()) {
                                 if let Some(kind) = inbetween_body_and_decl_check(p) {
-                                    nameref_ctx.keyword = Some(kind);
+                                    nameref_ctx.kind = Some(NameRefKind::Keyword(kind));
                                     return None;
                                 }
                             }
                         }
 
-                        fill_record_expr(it.syntax());
-
                         path_ctx.has_call_parens = it.syntax().parent().map_or(false, |it| ast::CallExpr::can_cast(it.kind()));
                         let in_block_expr = is_in_block(it.syntax());
                         let in_loop_body = is_in_loop_body(it.syntax());
                         let after_if_expr = after_if_expr(it.syntax().clone());
                         let ref_expr_parent = path.as_single_name_ref()
                             .and_then(|_| it.syntax().parent()).and_then(ast::RefExpr::cast);
+                        let is_func_update = func_update_record(it.syntax());
 
-                        Some(PathKind::Expr { in_block_expr, in_loop_body, after_if_expr, ref_expr_parent })
+                        Some(PathKind::Expr { in_block_expr, in_loop_body, after_if_expr, ref_expr_parent, is_func_update })
                     },
                     ast::TupleStructPat(it) => {
                         path_ctx.has_call_parens = true;
@@ -1205,7 +1288,7 @@ impl<'a> CompletionContext<'a> {
                     },
                     ast::MacroCall(it) => {
                         if let Some(kind) = inbetween_body_and_decl_check(it.syntax().clone()) {
-                            nameref_ctx.keyword = Some(kind);
+                            nameref_ctx.kind = Some(NameRefKind::Keyword(kind));
                             return None;
                         }
 
@@ -1213,7 +1296,12 @@ impl<'a> CompletionContext<'a> {
                         let parent = it.syntax().parent();
                         match parent.as_ref().map(|it| it.kind()) {
                             Some(SyntaxKind::MACRO_PAT) => Some(PathKind::Pat),
-                            Some(SyntaxKind::MACRO_TYPE) => Some(PathKind::Type { in_tuple_struct: false }),
+                            Some(SyntaxKind::MACRO_TYPE) => {
+                                let location = type_location(parent.unwrap().parent());
+                                Some(PathKind::Type {
+                                    location: location.unwrap_or(TypeLocation::Other),
+                                })
+                            },
                             Some(SyntaxKind::ITEM_LIST) => Some(PathKind::Item { kind: ItemListKind::Module }),
                             Some(SyntaxKind::ASSOC_ITEM_LIST) => Some(PathKind::Item { kind: match parent.and_then(|it| it.parent()) {
                                 Some(it) => match_ast! {
@@ -1236,10 +1324,10 @@ impl<'a> CompletionContext<'a> {
                                     let in_loop_body = is_in_loop_body(it.syntax());
                                     let in_block_expr = is_in_block(it.syntax());
                                     let after_if_expr = after_if_expr(it.syntax().clone());
-                                    fill_record_expr(it.syntax());
                                     let ref_expr_parent = path.as_single_name_ref()
                                         .and_then(|_| it.syntax().parent()).and_then(ast::RefExpr::cast);
-                                    PathKind::Expr { in_block_expr, in_loop_body, after_if_expr, ref_expr_parent }
+                                    let is_func_update = func_update_record(it.syntax());
+                                    PathKind::Expr { in_block_expr, in_loop_body, after_if_expr, ref_expr_parent, is_func_update }
                                 });
                             },
                         }
@@ -1365,7 +1453,7 @@ impl<'a> CompletionContext<'a> {
                 }
             }
         }
-        nameref_ctx.path_ctx = Some(path_ctx);
+        nameref_ctx.kind = Some(NameRefKind::Path(path_ctx));
         res
     }
 }
@@ -1422,15 +1510,14 @@ fn pattern_context_for(original_file: &SyntaxNode, pat: ast::Pat) -> PatternCont
     }
 }
 
-fn find_in_original_file<N: AstNode>(x: Option<N>, original_file: &SyntaxNode) -> Option<N> {
-    fn find_node_with_range<N: AstNode>(syntax: &SyntaxNode, range: TextRange) -> Option<N> {
-        let range = syntax.text_range().intersect(range)?;
-        syntax.covering_element(range).ancestors().find_map(N::cast)
-    }
-    x.map(|e| e.syntax().text_range()).and_then(|r| find_node_with_range(original_file, r))
+/// Attempts to find `node` inside `syntax` via `node`'s text range.
+/// If the fake identifier has been inserted after this node or inside of this node use the `_compensated` version instead.
+fn find_opt_node_in_file<N: AstNode>(syntax: &SyntaxNode, node: Option<N>) -> Option<N> {
+    find_node_in_file(syntax, &node?)
 }
 
 /// Attempts to find `node` inside `syntax` via `node`'s text range.
+/// If the fake identifier has been inserted after this node or inside of this node use the `_compensated` version instead.
 fn find_node_in_file<N: AstNode>(syntax: &SyntaxNode, node: &N) -> Option<N> {
     let syntax_range = syntax.text_range();
     let range = node.syntax().text_range();
@@ -1449,11 +1536,21 @@ fn find_node_in_file_compensated<N: AstNode>(syntax: &SyntaxNode, node: &N) -> O
         return None;
     }
     let range = TextRange::new(range.start(), end);
-    // our inserted ident could cause `range` to be go outside of the original syntax, so cap it
+    // our inserted ident could cause `range` to go outside of the original syntax, so cap it
     let intersection = range.intersect(syntax_range)?;
     syntax.covering_element(intersection).ancestors().find_map(N::cast)
 }
 
+/// Attempts to find `node` inside `syntax` via `node`'s text range while compensating
+/// for the offset introduced by the fake ident..
+/// This is wrong if `node` comes before the insertion point! Use `find_node_in_file` instead.
+fn find_opt_node_in_file_compensated<N: AstNode>(
+    syntax: &SyntaxNode,
+    node: Option<N>,
+) -> Option<N> {
+    find_node_in_file_compensated(syntax, &node?)
+}
+
 fn path_or_use_tree_qualifier(path: &ast::Path) -> Option<(ast::Path, bool)> {
     if let Some(qual) = path.qualifier() {
         return Some((qual, false));
diff --git a/crates/ide-completion/src/patterns.rs b/crates/ide-completion/src/patterns.rs
index bc46ff7f2d..9efb42c4de 100644
--- a/crates/ide-completion/src/patterns.rs
+++ b/crates/ide-completion/src/patterns.rs
@@ -4,171 +4,16 @@
 //! This means we for example expand a NameRef token to its outermost Path node, as semantically these act in the same location
 //! and the completions usually query for path specific things on the Path context instead. This simplifies some location handling.
 
-use hir::Semantics;
-use ide_db::RootDatabase;
 use syntax::{
-    ast::{self, HasLoopBody, HasName},
+    ast::{self, HasLoopBody},
     match_ast, AstNode, SyntaxElement,
     SyntaxKind::*,
-    SyntaxNode, SyntaxToken, TextRange, TextSize,
+    SyntaxNode, SyntaxToken,
 };
 
 #[cfg(test)]
 use crate::tests::check_pattern_is_applicable;
 
-#[derive(Clone, Debug, PartialEq, Eq)]
-pub(crate) enum TypeAnnotation {
-    Let(Option<ast::Pat>),
-    FnParam(Option<ast::Pat>),
-    RetType(Option<ast::Expr>),
-    Const(Option<ast::Expr>),
-}
-
-/// Direct parent "thing" of what we are currently completing.
-///
-/// This may contain nodes of the fake file as well as the original, comments on the variants specify
-/// from which file the nodes are.
-#[derive(Clone, Debug, PartialEq, Eq)]
-pub(crate) enum ImmediateLocation {
-    TypeBound,
-    /// Original file ast node
-    TypeAnnotation(TypeAnnotation),
-    // Only set from a type arg
-    /// Original file ast node
-    GenericArgList(ast::GenericArgList),
-}
-
-pub(crate) fn determine_location(
-    sema: &Semantics<RootDatabase>,
-    original_file: &SyntaxNode,
-    offset: TextSize,
-    name_like: &ast::NameLike,
-) -> Option<ImmediateLocation> {
-    let node = match name_like {
-        ast::NameLike::NameRef(name_ref) => maximize_name_ref(name_ref),
-        ast::NameLike::Name(name) => name.syntax().clone(),
-        ast::NameLike::Lifetime(lt) => lt.syntax().clone(),
-    };
-
-    match_ast! {
-        match node {
-            ast::TypeBoundList(_it) => return Some(ImmediateLocation::TypeBound),
-            _ => (),
-        }
-    };
-
-    let parent = match node.parent() {
-        Some(parent) => match ast::MacroCall::cast(parent.clone()) {
-            // When a path is being typed in an (Assoc)ItemList the parser will always emit a macro_call.
-            // This is usually fine as the node expansion code above already accounts for that with
-            // the ancestors call, but there is one exception to this which is that when an attribute
-            // precedes it the code above will not walk the Path to the parent MacroCall as their ranges differ.
-            // FIXME path expr and statement have a similar problem
-            Some(call)
-                if call.excl_token().is_none()
-                    && call.token_tree().is_none()
-                    && call.semicolon_token().is_none() =>
-            {
-                call.syntax().parent()?
-            }
-            _ => parent,
-        },
-        // SourceFile
-        None => return None,
-    };
-
-    let res = match_ast! {
-        match parent {
-            ast::TypeBound(_) => ImmediateLocation::TypeBound,
-            ast::TypeBoundList(_) => ImmediateLocation::TypeBound,
-            ast::GenericArgList(_) => sema
-                .find_node_at_offset_with_macros(original_file, offset)
-                .map(ImmediateLocation::GenericArgList)?,
-            ast::Const(it) => {
-                if !it.ty().map_or(false, |x| x.syntax().text_range().contains(offset)) {
-                    return None;
-                }
-                let name = find_in_original_file(it.name(), original_file)?;
-                let original = ast::Const::cast(name.syntax().parent()?)?;
-                ImmediateLocation::TypeAnnotation(TypeAnnotation::Const(original.body()))
-            },
-            ast::RetType(it) => {
-                if it.thin_arrow_token().is_none() {
-                    return None;
-                }
-                if !it.ty().map_or(false, |x| x.syntax().text_range().contains(offset)) {
-                    return None;
-                }
-                let parent = match ast::Fn::cast(parent.parent()?) {
-                    Some(x) => x.param_list(),
-                    None => ast::ClosureExpr::cast(parent.parent()?)?.param_list(),
-                };
-                let parent = find_in_original_file(parent, original_file)?.syntax().parent()?;
-                ImmediateLocation::TypeAnnotation(TypeAnnotation::RetType(match_ast! {
-                    match parent {
-                        ast::ClosureExpr(it) => {
-                            it.body()
-                        },
-                        ast::Fn(it) => {
-                            it.body().map(ast::Expr::BlockExpr)
-                        },
-                        _ => return None,
-                    }
-                }))
-            },
-            ast::Param(it) => {
-                if it.colon_token().is_none() {
-                    return None;
-                }
-                if !it.ty().map_or(false, |x| x.syntax().text_range().contains(offset)) {
-                    return None;
-                }
-                ImmediateLocation::TypeAnnotation(TypeAnnotation::FnParam(find_in_original_file(it.pat(), original_file)))
-            },
-            ast::LetStmt(it) => {
-                if it.colon_token().is_none() {
-                    return None;
-                }
-                if !it.ty().map_or(false, |x| x.syntax().text_range().contains(offset)) {
-                    return None;
-                }
-                ImmediateLocation::TypeAnnotation(TypeAnnotation::Let(find_in_original_file(it.pat(), original_file)))
-            },
-            _ => return None,
-        }
-    };
-    fn find_in_original_file<N: AstNode>(x: Option<N>, original_file: &SyntaxNode) -> Option<N> {
-        x.map(|e| e.syntax().text_range()).and_then(|r| find_node_with_range(original_file, r))
-    }
-    Some(res)
-}
-
-/// Maximize a nameref to its enclosing path if its the last segment of said path.
-/// That is, when completing a [`NameRef`] we actually handle it as the path it is part of when determining
-/// its location.
-fn maximize_name_ref(name_ref: &ast::NameRef) -> SyntaxNode {
-    if let Some(segment) = name_ref.syntax().parent().and_then(ast::PathSegment::cast) {
-        let p = segment.parent_path();
-        if p.parent_path().is_none() {
-            // Get rid of PathExpr, PathType, etc...
-            let path = p
-                .syntax()
-                .ancestors()
-                .take_while(|it| it.text_range() == p.syntax().text_range())
-                .last();
-            if let Some(it) = path {
-                return it;
-            }
-        }
-    }
-    name_ref.syntax().clone()
-}
-
-fn find_node_with_range<N: AstNode>(syntax: &SyntaxNode, range: TextRange) -> Option<N> {
-    let range = syntax.text_range().intersect(range)?;
-    syntax.covering_element(range).ancestors().find_map(N::cast)
-}
-
 pub(crate) fn previous_token(element: SyntaxElement) -> Option<SyntaxToken> {
     element.into_token().and_then(previous_non_trivia_token)
 }
diff --git a/crates/ide-completion/src/render/function.rs b/crates/ide-completion/src/render/function.rs
index 0be51b0e3f..566eaa575d 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, NameRefContext, PathCompletionCtx, PathKind,
+        CompletionContext, DotAccess, DotAccessKind, NameRefContext, NameRefKind,
+        PathCompletionCtx, PathKind,
     },
     item::{Builder, CompletionItem, CompletionItemKind, CompletionRelevance},
     render::{compute_exact_name_match, compute_ref_match, compute_type_match, RenderContext},
@@ -212,7 +213,10 @@ fn should_add_parens(ctx: &CompletionContext) -> bool {
     if matches!(
         ctx.nameref_ctx(),
         Some(NameRefContext {
-            dot_access: Some(DotAccess { kind: DotAccessKind::Method { has_parens: true }, .. }),
+            kind: Some(NameRefKind::DotAccess(DotAccess {
+                kind: DotAccessKind::Method { has_parens: true },
+                ..
+            })),
             ..
         })
     ) {


Test patch:
diff --git a/crates/ide-completion/src/tests/record.rs b/crates/ide-completion/src/tests/record.rs
index 9369034cc6..c7514e1b57 100644
--- a/crates/ide-completion/src/tests/record.rs
+++ b/crates/ide-completion/src/tests/record.rs
@@ -105,7 +105,6 @@ fn foo(f: Struct) {
 #[test]
 fn functional_update() {
     // FIXME: This should filter out all completions that do not have the type `Foo`
-    // FIXME: Fields should not show up after `.`
     check(
         r#"
 //- minicore:default
@@ -192,8 +191,6 @@ fn main() {
 }
 "#,
         expect![[r#"
-            fd foo1                   u32
-            fd foo2                   u32
             fn default() (as Default) fn() -> Self
         "#]],
     );