]> git.lizzy.rs Git - rust.git/blobdiff - crates/ide-completion/src/context.rs
internal: Split flyimport into its 3 applicable contexts
[rust.git] / crates / ide-completion / src / context.rs
index 802e48f23a61f34ce5cf8e9abc1023270d11bbcb..5c408c0cc0594a95dc0db2b872948471d2c20c2e 100644 (file)
@@ -1,33 +1,27 @@
 //! See `CompletionContext` structure.
 
-use std::iter;
+mod analysis;
+#[cfg(test)]
+mod tests;
 
 use base_db::SourceDatabaseExt;
 use hir::{
     HasAttrs, Local, Name, PathResolution, ScopeDef, Semantics, SemanticsScope, Type, TypeInfo,
 };
 use ide_db::{
-    active_parameter::ActiveParameter,
     base_db::{FilePosition, SourceDatabase},
     famous_defs::FamousDefs,
     FxHashMap, FxHashSet, RootDatabase,
 };
 use syntax::{
-    algo::{find_node_at_offset, non_trivia_sibling},
-    ast::{self, AttrKind, HasName, NameOrNameRef},
-    match_ast, AstNode, NodeOrToken,
+    ast::{self, AttrKind, NameOrNameRef},
+    AstNode,
     SyntaxKind::{self, *},
-    SyntaxNode, SyntaxToken, TextRange, TextSize, T,
+    SyntaxToken, TextRange, TextSize,
 };
 use text_edit::Indel;
 
-use crate::{
-    patterns::{
-        determine_location, determine_prev_sibling, is_in_loop_body, is_in_token_of_for_loop,
-        previous_token, ImmediateLocation, ImmediatePrevSibling,
-    },
-    CompletionConfig,
-};
+use crate::CompletionConfig;
 
 const COMPLETION_MARKER: &str = "intellijRulezz";
 
@@ -43,18 +37,75 @@ pub(crate) enum Visible {
     No,
 }
 
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+/// Existing qualifiers for the thing we are currently completing.
+#[derive(Debug, Default)]
+pub(super) struct QualifierCtx {
+    pub(super) unsafe_tok: Option<SyntaxToken>,
+    pub(super) vis_node: Option<ast::Visibility>,
+}
+
+impl QualifierCtx {
+    pub(super) fn none(&self) -> bool {
+        self.unsafe_tok.is_none() && self.vis_node.is_none()
+    }
+}
+
+/// 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)
+    pub(super) has_call_parens: bool,
+    /// If this has a macro call bang !
+    pub(super) has_macro_bang: bool,
+    /// The qualifier of the current path.
+    pub(super) qualified: Qualified,
+    /// The parent of the path we are completing.
+    pub(super) parent: Option<ast::Path>,
+    pub(super) kind: PathKind,
+    /// Whether the path segment has type args or not.
+    pub(super) has_type_args: bool,
+    /// Whether the qualifier comes from a use tree parent or not
+    pub(crate) use_tree_parent: bool,
+}
+
+impl PathCompletionCtx {
+    pub(super) fn is_trivial_path(&self) -> bool {
+        matches!(
+            self,
+            PathCompletionCtx {
+                has_call_parens: false,
+                has_macro_bang: false,
+                qualified: Qualified::No,
+                parent: None,
+                has_type_args: false,
+                ..
+            }
+        )
+    }
+}
+
+/// 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,
+        /// Whether this expression is the direct condition of an if or while expression
+        in_condition: bool,
+        ref_expr_parent: Option<ast::RefExpr>,
+        is_func_update: Option<ast::RecordExpr>,
+    },
+    Type {
+        location: TypeLocation,
     },
-    Type,
     Attr {
         kind: AttrKind,
         annotated_item_kind: Option<SyntaxKind>,
     },
-    Derive,
+    Derive {
+        existing_derives: FxHashSet<hir::Macro>,
+    },
     /// Path in item position, that is inside an (Assoc)ItemList
     Item {
         kind: ItemListKind,
@@ -66,46 +117,53 @@ pub(super) enum PathKind {
     Use,
 }
 
+/// Original file ast nodes
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub(crate) enum TypeLocation {
+    TupleField,
+    TypeAscription(TypeAscriptionTarget),
+    GenericArgList(Option<ast::GenericArgList>),
+    TypeBound,
+    ImplTarget,
+    ImplTrait,
+    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,
 }
 
 #[derive(Debug)]
-pub(crate) struct PathCompletionCtx {
-    /// If this is a call with () already there (or {} in case of record patterns)
-    pub(super) has_call_parens: bool,
-    /// If this has a macro call bang !
-    pub(super) has_macro_bang: bool,
-    /// Whether this path stars with a `::`.
-    pub(super) is_absolute_path: bool,
-    /// The qualifier of the current path if it exists.
-    pub(super) qualifier: Option<PathQualifierCtx>,
-    #[allow(dead_code)]
-    // FIXME: use this
-    /// The parent of the path we are completing.
-    pub(super) parent: Option<ast::Path>,
-    pub(super) kind: PathKind,
-    /// Whether the path segment has type args or not.
-    pub(super) has_type_args: bool,
-}
-
-#[derive(Debug)]
-pub(crate) struct PathQualifierCtx {
-    pub(crate) path: ast::Path,
-    pub(crate) resolution: Option<PathResolution>,
-    /// Whether this path consists solely of `super` segments
-    pub(crate) is_super_chain: bool,
-    /// Whether the qualifier comes from a use tree parent or not
-    pub(crate) use_tree_parent: bool,
-    /// <_>
-    pub(crate) is_infer_qualifier: bool,
+pub(super) enum Qualified {
+    No,
+    With {
+        path: ast::Path,
+        resolution: Option<PathResolution>,
+        /// Whether this path consists solely of `super` segments
+        is_super_chain: bool,
+    },
+    /// <_>::
+    Infer,
+    /// Whether the path is an absolute path
+    Absolute,
 }
 
+/// The state of the pattern we are completing.
 #[derive(Debug)]
 pub(super) struct PatternContext {
     pub(super) refutability: PatternRefutability,
@@ -114,19 +172,38 @@ pub(super) struct PatternContext {
     pub(super) parent_pat: Option<ast::Pat>,
     pub(super) ref_token: Option<SyntaxToken>,
     pub(super) mut_token: Option<SyntaxToken>,
+    /// The record pattern this name or ref is a field of
+    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 LifetimeContext {
+pub(super) enum LifetimeKind {
     LifetimeParam { is_decl: bool, param: ast::LifetimeParam },
     Lifetime,
     LabelRef,
     LabelDef,
 }
 
+/// The state of the name we are completing.
+#[derive(Debug)]
+pub(super) struct NameContext {
+    #[allow(dead_code)]
+    pub(super) name: Option<ast::Name>,
+    pub(super) kind: NameKind,
+}
+
+/// The kind of the name we are completing.
 #[derive(Debug)]
 #[allow(dead_code)]
-pub(super) enum NameContext {
+pub(super) enum NameKind {
     Const,
     ConstParam,
     Enum,
@@ -148,6 +225,65 @@ pub(super) enum NameContext {
     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: 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
+    Keyword(ast::Item),
+    /// The record expression this nameref is a field of
+    RecordExpr(ast::RecordExpr),
+}
+
+/// The identifier we are currently completing.
+#[derive(Debug)]
+pub(super) enum IdentContext {
+    Name(NameContext),
+    NameRef(NameRefContext),
+    Lifetime(LifetimeContext),
+    /// 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>,
+    pub(super) receiver_ty: Option<TypeInfo>,
+    pub(super) kind: DotAccessKind,
+}
+
+#[derive(Debug)]
+pub(super) enum DotAccessKind {
+    Field {
+        /// True if the receiver is an integer and there is no ident in the original file after it yet
+        /// like `0.$0`
+        receiver_is_ambiguous_float_literal: bool,
+    },
+    Method {
+        has_parens: bool,
+    },
+}
+
 #[derive(Clone, Debug, PartialEq, Eq)]
 pub(crate) enum ParamKind {
     Function(ast::Fn),
@@ -180,25 +316,22 @@ pub(crate) struct CompletionContext<'a> {
     pub(super) expected_type: Option<Type>,
 
     /// The parent function of the cursor position if it exists.
+    // FIXME: This probably doesn't belong here
     pub(super) function_def: Option<ast::Fn>,
     /// The parent impl of the cursor position if it exists.
+    // FIXME: This probably doesn't belong here
     pub(super) impl_def: Option<ast::Impl>,
-    /// The NameLike under the cursor in the original file if it exists.
-    pub(super) name_syntax: Option<ast::NameLike>,
     /// 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) prev_sibling: Option<ImmediatePrevSibling>,
-    pub(super) fake_attribute_under_caret: Option<ast::Attr>,
+    // FIXME: This shouldn't exist
     pub(super) previous_token: Option<SyntaxToken>,
 
-    pub(super) name_ctx: Option<NameContext>,
-    pub(super) lifetime_ctx: Option<LifetimeContext>,
+    // We might wanna split these out of CompletionContext
+    pub(super) ident_ctx: IdentContext,
     pub(super) pattern_ctx: Option<PatternContext>,
-    pub(super) path_context: Option<PathCompletionCtx>,
-
-    pub(super) existing_derives: FxHashSet<hir::Macro>,
+    pub(super) qualifier_ctx: QualifierCtx,
 
     pub(super) locals: FxHashMap<Name, Local>,
 }
@@ -220,14 +353,7 @@ pub(crate) fn source_range(&self) -> TextRange {
         }
     }
 
-    pub(crate) fn name_ref(&self) -> Option<&ast::NameRef> {
-        self.name_syntax.as_ref().and_then(ast::NameLike::as_name_ref)
-    }
-
-    pub(crate) fn lifetime(&self) -> Option<&ast::Lifetime> {
-        self.name_syntax.as_ref().and_then(ast::NameLike::as_lifetime)
-    }
-
+    // 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)
     }
@@ -236,128 +362,6 @@ pub(crate) fn famous_defs(&self) -> FamousDefs {
         FamousDefs(&self.sema, self.krate)
     }
 
-    pub(crate) fn dot_receiver(&self) -> Option<&ast::Expr> {
-        match &self.completion_location {
-            Some(
-                ImmediateLocation::MethodCall { receiver, .. }
-                | ImmediateLocation::FieldAccess { receiver, .. },
-            ) => receiver.as_ref(),
-            _ => None,
-        }
-    }
-
-    pub(crate) fn has_dot_receiver(&self) -> bool {
-        matches!(
-            &self.completion_location,
-            Some(ImmediateLocation::FieldAccess { receiver, .. } | ImmediateLocation::MethodCall { receiver,.. })
-                if receiver.is_some()
-        )
-    }
-
-    pub(crate) fn expects_assoc_item(&self) -> bool {
-        matches!(self.completion_location, Some(ImmediateLocation::Trait | ImmediateLocation::Impl))
-    }
-
-    pub(crate) fn expects_variant(&self) -> bool {
-        matches!(self.name_ctx, Some(NameContext::Variant))
-    }
-
-    pub(crate) fn expects_non_trait_assoc_item(&self) -> bool {
-        matches!(self.completion_location, Some(ImmediateLocation::Impl))
-    }
-
-    pub(crate) fn expects_item(&self) -> bool {
-        matches!(self.completion_location, Some(ImmediateLocation::ItemList))
-    }
-
-    pub(crate) fn expects_generic_arg(&self) -> bool {
-        matches!(self.completion_location, Some(ImmediateLocation::GenericArgList(_)))
-    }
-
-    pub(crate) fn has_block_expr_parent(&self) -> bool {
-        matches!(self.completion_location, Some(ImmediateLocation::StmtList))
-    }
-
-    pub(crate) fn expects_ident_ref_expr(&self) -> bool {
-        matches!(self.completion_location, Some(ImmediateLocation::RefExpr))
-    }
-
-    pub(crate) fn expect_field(&self) -> bool {
-        matches!(self.completion_location, Some(ImmediateLocation::TupleField))
-            || matches!(self.name_ctx, Some(NameContext::RecordField))
-    }
-
-    /// Whether the cursor is right after a trait or impl header.
-    /// trait Foo ident$0
-    // FIXME: This probably shouldn't exist
-    pub(crate) fn has_unfinished_impl_or_trait_prev_sibling(&self) -> bool {
-        matches!(
-            self.prev_sibling,
-            Some(ImmediatePrevSibling::ImplDefType | ImmediatePrevSibling::TraitDefName)
-        )
-    }
-
-    // FIXME: This probably shouldn't exist
-    pub(crate) fn has_impl_prev_sibling(&self) -> bool {
-        matches!(self.prev_sibling, Some(ImmediatePrevSibling::ImplDefType))
-    }
-
-    pub(crate) fn has_visibility_prev_sibling(&self) -> bool {
-        matches!(self.prev_sibling, Some(ImmediatePrevSibling::Visibility))
-    }
-
-    pub(crate) fn after_if(&self) -> bool {
-        matches!(self.prev_sibling, Some(ImmediatePrevSibling::IfExpr))
-    }
-
-    // FIXME: This shouldn't exist
-    pub(crate) fn is_path_disallowed(&self) -> bool {
-        self.previous_token_is(T![unsafe])
-            || matches!(
-                self.prev_sibling,
-                Some(ImmediatePrevSibling::Attribute | ImmediatePrevSibling::Visibility)
-            )
-            || matches!(
-                self.completion_location,
-                Some(ImmediateLocation::RecordPat(_) | ImmediateLocation::RecordExpr(_))
-            )
-            || matches!(self.name_ctx, Some(NameContext::Module(_) | NameContext::Rename))
-    }
-
-    pub(crate) fn expects_expression(&self) -> bool {
-        matches!(self.path_context, Some(PathCompletionCtx { kind: PathKind::Expr { .. }, .. }))
-    }
-
-    pub(crate) fn expects_type(&self) -> bool {
-        matches!(self.path_context, Some(PathCompletionCtx { kind: PathKind::Type, .. }))
-    }
-
-    pub(crate) fn path_is_call(&self) -> bool {
-        self.path_context.as_ref().map_or(false, |it| it.has_call_parens)
-    }
-
-    pub(crate) fn is_non_trivial_path(&self) -> bool {
-        matches!(
-            self.path_context,
-            Some(
-                PathCompletionCtx { is_absolute_path: true, .. }
-                    | PathCompletionCtx { qualifier: Some(_), .. }
-            )
-        )
-    }
-
-    pub(crate) fn path_qual(&self) -> Option<&ast::Path> {
-        self.path_context.as_ref().and_then(|it| it.qualifier.as_ref().map(|it| &it.path))
-    }
-
-    pub(crate) fn path_kind(&self) -> Option<PathKind> {
-        self.path_context.as_ref().map(|it| it.kind)
-    }
-
-    pub(crate) fn is_immediately_after_macro_bang(&self) -> bool {
-        self.token.kind() == BANG && self.token.parent().map_or(false, |it| it.kind() == MACRO_CALL)
-    }
-
     /// Checks if an item is visible and not `doc(hidden)` at the completion site.
     pub(crate) fn is_visible<I>(&self, item: &I) -> Visible
     where
@@ -497,727 +501,22 @@ pub(super) fn new(
             expected_type: None,
             function_def: None,
             impl_def: None,
-            name_syntax: None,
-            lifetime_ctx: None,
-            pattern_ctx: None,
-            name_ctx: None,
-            completion_location: None,
-            prev_sibling: None,
-            fake_attribute_under_caret: None,
+            incomplete_let: false,
             previous_token: None,
-            path_context: None,
+            // dummy value, will be overwritten
+            ident_ctx: IdentContext::UnexpandedAttrTT { fake_attribute_under_caret: None },
+            pattern_ctx: None,
+            qualifier_ctx: Default::default(),
             locals,
-            incomplete_let: false,
-            existing_derives: Default::default(),
         };
         ctx.expand_and_fill(
             original_file.syntax().clone(),
             file_with_fake_ident.syntax().clone(),
             offset,
             fake_ident_token,
-        );
+        )?;
         Some(ctx)
     }
-
-    /// 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.
-    fn expand_and_fill(
-        &mut self,
-        mut original_file: SyntaxNode,
-        mut speculative_file: SyntaxNode,
-        mut offset: TextSize,
-        mut fake_ident_token: SyntaxToken,
-    ) {
-        let _p = profile::span("CompletionContext::expand_and_fill");
-        let mut derive_ctx = None;
-
-        'expansion: loop {
-            let parent_item =
-                |item: &ast::Item| item.syntax().ancestors().skip(1).find_map(ast::Item::cast);
-            let ancestor_items = iter::successors(
-                Option::zip(
-                    find_node_at_offset::<ast::Item>(&original_file, offset),
-                    find_node_at_offset::<ast::Item>(&speculative_file, offset),
-                ),
-                |(a, b)| parent_item(a).zip(parent_item(b)),
-            );
-
-            // first try to expand attributes as these are always the outermost macro calls
-            'ancestors: for (actual_item, item_with_fake_ident) in ancestor_items {
-                match (
-                    self.sema.expand_attr_macro(&actual_item),
-                    self.sema.speculative_expand_attr_macro(
-                        &actual_item,
-                        &item_with_fake_ident,
-                        fake_ident_token.clone(),
-                    ),
-                ) {
-                    // maybe parent items have attributes, so continue walking the ancestors
-                    (None, None) => continue 'ancestors,
-                    // successful expansions
-                    (Some(actual_expansion), Some((fake_expansion, fake_mapped_token))) => {
-                        let new_offset = fake_mapped_token.text_range().start();
-                        if new_offset > actual_expansion.text_range().end() {
-                            // offset outside of bounds from the original expansion,
-                            // stop here to prevent problems from happening
-                            break 'expansion;
-                        }
-                        original_file = actual_expansion;
-                        speculative_file = fake_expansion;
-                        fake_ident_token = fake_mapped_token;
-                        offset = new_offset;
-                        continue 'expansion;
-                    }
-                    // exactly one expansion failed, inconsistent state so stop expanding completely
-                    _ => break 'expansion,
-                }
-            }
-
-            // No attributes have been expanded, so look for macro_call! token trees or derive token trees
-            let orig_tt = match find_node_at_offset::<ast::TokenTree>(&original_file, offset) {
-                Some(it) => it,
-                None => break 'expansion,
-            };
-            let spec_tt = match find_node_at_offset::<ast::TokenTree>(&speculative_file, offset) {
-                Some(it) => it,
-                None => break 'expansion,
-            };
-
-            // Expand pseudo-derive expansion
-            if let (Some(orig_attr), Some(spec_attr)) = (
-                orig_tt.syntax().parent().and_then(ast::Meta::cast).and_then(|it| it.parent_attr()),
-                spec_tt.syntax().parent().and_then(ast::Meta::cast).and_then(|it| it.parent_attr()),
-            ) {
-                if let (Some(actual_expansion), Some((fake_expansion, fake_mapped_token))) = (
-                    self.sema.expand_derive_as_pseudo_attr_macro(&orig_attr),
-                    self.sema.speculative_expand_derive_as_pseudo_attr_macro(
-                        &orig_attr,
-                        &spec_attr,
-                        fake_ident_token.clone(),
-                    ),
-                ) {
-                    derive_ctx = Some((
-                        actual_expansion,
-                        fake_expansion,
-                        fake_mapped_token.text_range().start(),
-                        orig_attr,
-                    ));
-                }
-                // at this point we won't have any more successful expansions, so stop
-                break 'expansion;
-            }
-
-            // Expand fn-like macro calls
-            if let (Some(actual_macro_call), Some(macro_call_with_fake_ident)) = (
-                orig_tt.syntax().ancestors().find_map(ast::MacroCall::cast),
-                spec_tt.syntax().ancestors().find_map(ast::MacroCall::cast),
-            ) {
-                let mac_call_path0 = actual_macro_call.path().as_ref().map(|s| s.syntax().text());
-                let mac_call_path1 =
-                    macro_call_with_fake_ident.path().as_ref().map(|s| s.syntax().text());
-
-                // inconsistent state, stop expanding
-                if mac_call_path0 != mac_call_path1 {
-                    break 'expansion;
-                }
-                let speculative_args = match macro_call_with_fake_ident.token_tree() {
-                    Some(tt) => tt,
-                    None => break 'expansion,
-                };
-
-                match (
-                    self.sema.expand(&actual_macro_call),
-                    self.sema.speculative_expand(
-                        &actual_macro_call,
-                        &speculative_args,
-                        fake_ident_token.clone(),
-                    ),
-                ) {
-                    // successful expansions
-                    (Some(actual_expansion), Some((fake_expansion, fake_mapped_token))) => {
-                        let new_offset = fake_mapped_token.text_range().start();
-                        if new_offset > actual_expansion.text_range().end() {
-                            // offset outside of bounds from the original expansion,
-                            // stop here to prevent problems from happening
-                            break 'expansion;
-                        }
-                        original_file = actual_expansion;
-                        speculative_file = fake_expansion;
-                        fake_ident_token = fake_mapped_token;
-                        offset = new_offset;
-                        continue 'expansion;
-                    }
-                    // at least on expansion failed, we won't have anything to expand from this point
-                    // onwards so break out
-                    _ => break 'expansion,
-                }
-            }
-
-            // none of our states have changed so stop the loop
-            break 'expansion;
-        }
-
-        self.fill(&original_file, speculative_file, offset, derive_ctx);
-    }
-
-    /// Calculate the expected type and name of the cursor position.
-    fn expected_type_and_name(&self) -> (Option<Type>, Option<NameOrNameRef>) {
-        let mut node = match self.token.parent() {
-            Some(it) => it,
-            None => return (None, None),
-        };
-        loop {
-            break match_ast! {
-                match node {
-                    ast::LetStmt(it) => {
-                        cov_mark::hit!(expected_type_let_with_leading_char);
-                        cov_mark::hit!(expected_type_let_without_leading_char);
-                        let ty = it.pat()
-                            .and_then(|pat| self.sema.type_of_pat(&pat))
-                            .or_else(|| it.initializer().and_then(|it| self.sema.type_of_expr(&it)))
-                            .map(TypeInfo::original);
-                        let name = match it.pat() {
-                            Some(ast::Pat::IdentPat(ident)) => ident.name().map(NameOrNameRef::Name),
-                            Some(_) | None => None,
-                        };
-
-                        (ty, name)
-                    },
-                    ast::LetExpr(it) => {
-                        cov_mark::hit!(expected_type_if_let_without_leading_char);
-                        let ty = it.pat()
-                            .and_then(|pat| self.sema.type_of_pat(&pat))
-                            .or_else(|| it.expr().and_then(|it| self.sema.type_of_expr(&it)))
-                            .map(TypeInfo::original);
-                        (ty, None)
-                    },
-                    ast::ArgList(_) => {
-                        cov_mark::hit!(expected_type_fn_param);
-                        ActiveParameter::at_token(
-                            &self.sema,
-                            self.token.clone(),
-                        ).map(|ap| {
-                            let name = ap.ident().map(NameOrNameRef::Name);
-                            let ty = if has_ref(&self.token) {
-                                cov_mark::hit!(expected_type_fn_param_ref);
-                                ap.ty.remove_ref()
-                            } else {
-                                Some(ap.ty)
-                            };
-                            (ty, name)
-                        })
-                        .unwrap_or((None, None))
-                    },
-                    ast::RecordExprFieldList(it) => {
-                        // wouldn't try {} be nice...
-                        (|| {
-                            if self.token.kind() == T![..]
-                                || self.token.prev_token().map(|t| t.kind()) == Some(T![..])
-                            {
-                                cov_mark::hit!(expected_type_struct_func_update);
-                                let record_expr = it.syntax().parent().and_then(ast::RecordExpr::cast)?;
-                                let ty = self.sema.type_of_expr(&record_expr.into())?;
-                                Some((
-                                    Some(ty.original),
-                                    None
-                                ))
-                            } else {
-                                cov_mark::hit!(expected_type_struct_field_without_leading_char);
-                                let expr_field = self.token.prev_sibling_or_token()?
-                                    .into_node()
-                                    .and_then(ast::RecordExprField::cast)?;
-                                let (_, _, ty) = self.sema.resolve_record_field(&expr_field)?;
-                                Some((
-                                    Some(ty),
-                                    expr_field.field_name().map(NameOrNameRef::NameRef),
-                                ))
-                            }
-                        })().unwrap_or((None, None))
-                    },
-                    ast::RecordExprField(it) => {
-                        if let Some(expr) = it.expr() {
-                            cov_mark::hit!(expected_type_struct_field_with_leading_char);
-                            (
-                                self.sema.type_of_expr(&expr).map(TypeInfo::original),
-                                it.field_name().map(NameOrNameRef::NameRef),
-                            )
-                        } else {
-                            cov_mark::hit!(expected_type_struct_field_followed_by_comma);
-                            let ty = self.sema.resolve_record_field(&it)
-                                .map(|(_, _, ty)| ty);
-                            (
-                                ty,
-                                it.field_name().map(NameOrNameRef::NameRef),
-                            )
-                        }
-                    },
-                    ast::MatchExpr(it) => {
-                        cov_mark::hit!(expected_type_match_arm_without_leading_char);
-                        let ty = it.expr().and_then(|e| self.sema.type_of_expr(&e)).map(TypeInfo::original);
-                        (ty, None)
-                    },
-                    ast::IfExpr(it) => {
-                        let ty = it.condition()
-                            .and_then(|e| self.sema.type_of_expr(&e))
-                            .map(TypeInfo::original);
-                        (ty, None)
-                    },
-                    ast::IdentPat(it) => {
-                        cov_mark::hit!(expected_type_if_let_with_leading_char);
-                        cov_mark::hit!(expected_type_match_arm_with_leading_char);
-                        let ty = self.sema.type_of_pat(&ast::Pat::from(it)).map(TypeInfo::original);
-                        (ty, None)
-                    },
-                    ast::Fn(it) => {
-                        cov_mark::hit!(expected_type_fn_ret_with_leading_char);
-                        cov_mark::hit!(expected_type_fn_ret_without_leading_char);
-                        let def = self.sema.to_def(&it);
-                        (def.map(|def| def.ret_type(self.db)), None)
-                    },
-                    ast::ClosureExpr(it) => {
-                        let ty = self.sema.type_of_expr(&it.into());
-                        ty.and_then(|ty| ty.original.as_callable(self.db))
-                            .map(|c| (Some(c.return_type()), None))
-                            .unwrap_or((None, None))
-                    },
-                    ast::ParamList(_) => (None, None),
-                    ast::Stmt(_) => (None, None),
-                    ast::Item(_) => (None, None),
-                    _ => {
-                        match node.parent() {
-                            Some(n) => {
-                                node = n;
-                                continue;
-                            },
-                            None => (None, None),
-                        }
-                    },
-                }
-            };
-        }
-    }
-
-    /// Fill the completion context, this is what does semantic reasoning about the surrounding context
-    /// of the completion location.
-    fn fill(
-        &mut self,
-        original_file: &SyntaxNode,
-        file_with_fake_ident: SyntaxNode,
-        offset: TextSize,
-        derive_ctx: Option<(SyntaxNode, SyntaxNode, TextSize, ast::Attr)>,
-    ) {
-        let fake_ident_token = file_with_fake_ident.token_at_offset(offset).right_biased().unwrap();
-        let syntax_element = NodeOrToken::Token(fake_ident_token);
-        if is_in_token_of_for_loop(syntax_element.clone()) {
-            // for pat $0
-            // there is nothing to complete here except `in` keyword
-            // don't bother populating the context
-            // FIXME: the completion calculations should end up good enough
-            // such that this special case becomes unnecessary
-            return;
-        }
-
-        self.previous_token = previous_token(syntax_element.clone());
-        self.fake_attribute_under_caret = syntax_element.ancestors().find_map(ast::Attr::cast);
-
-        self.incomplete_let =
-            syntax_element.ancestors().take(6).find_map(ast::LetStmt::cast).map_or(false, |it| {
-                it.syntax().text_range().end() == syntax_element.text_range().end()
-            });
-
-        (self.expected_type, self.expected_name) = self.expected_type_and_name();
-
-        // Overwrite the path kind for derives
-        if let Some((original_file, file_with_fake_ident, offset, origin_attr)) = derive_ctx {
-            self.existing_derives = self
-                .sema
-                .resolve_derive_macro(&origin_attr)
-                .into_iter()
-                .flatten()
-                .flatten()
-                .collect();
-
-            if let Some(ast::NameLike::NameRef(name_ref)) =
-                find_node_at_offset(&file_with_fake_ident, offset)
-            {
-                self.name_syntax =
-                    find_node_at_offset(&original_file, name_ref.syntax().text_range().start());
-                if let Some((path_ctx, _)) =
-                    Self::classify_name_ref(&self.sema, &original_file, name_ref)
-                {
-                    self.path_context =
-                        Some(PathCompletionCtx { kind: PathKind::Derive, ..path_ctx });
-                }
-            }
-            return;
-        }
-
-        let name_like = match find_node_at_offset(&file_with_fake_ident, offset) {
-            Some(it) => it,
-            None => return,
-        };
-        self.completion_location =
-            determine_location(&self.sema, original_file, offset, &name_like);
-        self.prev_sibling = determine_prev_sibling(&name_like);
-        self.name_syntax =
-            find_node_at_offset(original_file, name_like.syntax().text_range().start());
-        self.impl_def = self
-            .sema
-            .token_ancestors_with_macros(self.token.clone())
-            .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE)
-            .find_map(ast::Impl::cast);
-        self.function_def = self
-            .sema
-            .token_ancestors_with_macros(self.token.clone())
-            .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE)
-            .find_map(ast::Fn::cast);
-
-        match name_like {
-            ast::NameLike::Lifetime(lifetime) => {
-                self.lifetime_ctx = Self::classify_lifetime(&self.sema, original_file, lifetime);
-            }
-            ast::NameLike::NameRef(name_ref) => {
-                if let Some((path_ctx, pat_ctx)) =
-                    Self::classify_name_ref(&self.sema, original_file, name_ref)
-                {
-                    self.path_context = Some(path_ctx);
-                    self.pattern_ctx = pat_ctx;
-                }
-            }
-            ast::NameLike::Name(name) => {
-                if let Some((name_ctx, pat_ctx)) =
-                    Self::classify_name(&self.sema, original_file, name)
-                {
-                    self.pattern_ctx = pat_ctx;
-                    self.name_ctx = Some(name_ctx);
-                }
-            }
-        }
-    }
-
-    fn classify_lifetime(
-        _sema: &Semantics<RootDatabase>,
-        _original_file: &SyntaxNode,
-        lifetime: ast::Lifetime,
-    ) -> Option<LifetimeContext> {
-        let parent = lifetime.syntax().parent()?;
-        if parent.kind() == ERROR {
-            return None;
-        }
-
-        Some(match_ast! {
-            match parent {
-                ast::LifetimeParam(param) => LifetimeContext::LifetimeParam {
-                    is_decl: param.lifetime().as_ref() == Some(&lifetime),
-                    param
-                },
-                ast::BreakExpr(_) => LifetimeContext::LabelRef,
-                ast::ContinueExpr(_) => LifetimeContext::LabelRef,
-                ast::Label(_) => LifetimeContext::LabelDef,
-                _ => LifetimeContext::Lifetime,
-            }
-        })
-    }
-
-    fn classify_name(
-        _sema: &Semantics<RootDatabase>,
-        original_file: &SyntaxNode,
-        name: ast::Name,
-    ) -> Option<(NameContext, Option<PatternContext>)> {
-        let parent = name.syntax().parent()?;
-        let mut pat_ctx = None;
-        let name_ctx = match_ast! {
-            match parent {
-                ast::Const(_) => NameContext::Const,
-                ast::ConstParam(_) => NameContext::ConstParam,
-                ast::Enum(_) => NameContext::Enum,
-                ast::Fn(_) => NameContext::Function,
-                ast::IdentPat(bind_pat) => {
-                    let is_name_in_field_pat = bind_pat
-                        .syntax()
-                        .parent()
-                        .and_then(ast::RecordPatField::cast)
-                        .map_or(false, |pat_field| pat_field.name_ref().is_none());
-                    if !is_name_in_field_pat {
-                        pat_ctx = Some(pattern_context_for(original_file, bind_pat.into()));
-                    }
-
-                    NameContext::IdentPat
-                },
-                ast::MacroDef(_) => NameContext::MacroDef,
-                ast::MacroRules(_) => NameContext::MacroRules,
-                ast::Module(module) => NameContext::Module(module),
-                ast::RecordField(_) => NameContext::RecordField,
-                ast::Rename(_) => NameContext::Rename,
-                ast::SelfParam(_) => NameContext::SelfParam,
-                ast::Static(_) => NameContext::Static,
-                ast::Struct(_) => NameContext::Struct,
-                ast::Trait(_) => NameContext::Trait,
-                ast::TypeAlias(_) => NameContext::TypeAlias,
-                ast::TypeParam(_) => NameContext::TypeParam,
-                ast::Union(_) => NameContext::Union,
-                ast::Variant(_) => NameContext::Variant,
-                _ => return None,
-            }
-        };
-        Some((name_ctx, pat_ctx))
-    }
-
-    fn classify_name_ref(
-        sema: &Semantics<RootDatabase>,
-        original_file: &SyntaxNode,
-        name_ref: ast::NameRef,
-    ) -> Option<(PathCompletionCtx, Option<PatternContext>)> {
-        let parent = name_ref.syntax().parent()?;
-        let segment = ast::PathSegment::cast(parent)?;
-        let path = segment.parent_path();
-
-        let mut path_ctx = PathCompletionCtx {
-            has_call_parens: false,
-            has_macro_bang: false,
-            is_absolute_path: false,
-            qualifier: None,
-            parent: path.parent_path(),
-            kind: PathKind::Item { kind: ItemListKind::SourceFile },
-            has_type_args: false,
-        };
-        let mut pat_ctx = None;
-
-        let is_in_block = |it: &SyntaxNode| {
-            it.parent()
-                .map(|node| {
-                    ast::ExprStmt::can_cast(node.kind()) || ast::StmtList::can_cast(node.kind())
-                })
-                .unwrap_or(false)
-        };
-
-        path_ctx.kind = path.syntax().ancestors().find_map(|it| {
-            // using Option<Option<PathKind>> as extra controlflow
-            let kind = match_ast! {
-                match it {
-                    ast::PathType(_) => Some(PathKind::Type),
-                    ast::PathExpr(it) => {
-                        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());
-
-                        Some(PathKind::Expr { in_block_expr, in_loop_body })
-                    },
-                    ast::TupleStructPat(it) => {
-                        path_ctx.has_call_parens = true;
-                        pat_ctx = Some(pattern_context_for(original_file, it.into()));
-                        Some(PathKind::Pat)
-                    },
-                    ast::RecordPat(it) => {
-                        path_ctx.has_call_parens = true;
-                        pat_ctx = Some(pattern_context_for(original_file, it.into()));
-                        Some(PathKind::Pat)
-                    },
-                    ast::PathPat(it) => {
-                        pat_ctx = Some(pattern_context_for(original_file, it.into()));
-                        Some(PathKind::Pat)
-                    },
-                    ast::MacroCall(it) => {
-                        path_ctx.has_macro_bang = it.excl_token().is_some();
-                        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),
-                            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()).map(|it| it.kind()) {
-                                Some(SyntaxKind::TRAIT) => ItemListKind::Trait,
-                                Some(SyntaxKind::IMPL) => ItemListKind::Impl,
-                                _ => return Some(None),
-                            } }),
-                            Some(SyntaxKind::EXTERN_ITEM_LIST) => Some(PathKind::Item { kind: ItemListKind::ExternBlock }),
-                            Some(SyntaxKind::SOURCE_FILE) => Some(PathKind::Item { kind: ItemListKind::SourceFile }),
-                            _ => {
-                               return Some(parent.and_then(ast::MacroExpr::cast).map(|it| {
-                                    let in_loop_body = is_in_loop_body(it.syntax());
-                                    let in_block_expr = is_in_block(it.syntax());
-                                    PathKind::Expr { in_block_expr, in_loop_body }
-                                }));
-                            },
-                        }
-                    },
-                    ast::Meta(meta) => (|| {
-                        let attr = meta.parent_attr()?;
-                        let kind = attr.kind();
-                        let attached = attr.syntax().parent()?;
-                        let is_trailing_outer_attr = kind != AttrKind::Inner
-                            && non_trivia_sibling(attr.syntax().clone().into(), syntax::Direction::Next).is_none();
-                        let annotated_item_kind = if is_trailing_outer_attr {
-                            None
-                        } else {
-                            Some(attached.kind())
-                        };
-                        Some(PathKind::Attr {
-                            kind,
-                            annotated_item_kind,
-                        })
-                    })(),
-                    ast::Visibility(it) => Some(PathKind::Vis { has_in_token: it.in_token().is_some() }),
-                    ast::UseTree(_) => Some(PathKind::Use),
-                    ast::ItemList(_) => Some(PathKind::Item { kind: ItemListKind::Module }),
-                    ast::AssocItemList(it) => Some(PathKind::Item { kind: {
-                            match it.syntax().parent()?.kind() {
-                                SyntaxKind::TRAIT => ItemListKind::Trait,
-                                SyntaxKind::IMPL => ItemListKind::Impl,
-                                _ => return None,
-                            }
-                        }}),
-                    ast::ExternItemList(_) => Some(PathKind::Item { kind: ItemListKind::ExternBlock }),
-                    ast::SourceFile(_) => Some(PathKind::Item { kind: ItemListKind::SourceFile }),
-                    _ => return None,
-                }
-            };
-            Some(kind)
-        }).flatten()?;
-        path_ctx.has_type_args = segment.generic_arg_list().is_some();
-
-        if let Some((path, use_tree_parent)) = path_or_use_tree_qualifier(&path) {
-            if !use_tree_parent {
-                path_ctx.is_absolute_path =
-                    path.top_path().segment().map_or(false, |it| it.coloncolon_token().is_some());
-            }
-
-            let path = path
-                .segment()
-                .and_then(|it| find_node_in_file(original_file, &it))
-                .map(|it| it.parent_path());
-            path_ctx.qualifier = path.map(|path| {
-                let res = sema.resolve_path(&path);
-                let is_super_chain = iter::successors(Some(path.clone()), |p| p.qualifier())
-                    .all(|p| p.segment().and_then(|s| s.super_token()).is_some());
-
-                // `<_>::$0`
-                let is_infer_qualifier = path.qualifier().is_none()
-                    && matches!(
-                        path.segment().and_then(|it| it.kind()),
-                        Some(ast::PathSegmentKind::Type {
-                            type_ref: Some(ast::Type::InferType(_)),
-                            trait_ref: None,
-                        })
-                    );
-
-                PathQualifierCtx {
-                    path,
-                    resolution: res,
-                    is_super_chain,
-                    use_tree_parent,
-                    is_infer_qualifier,
-                }
-            });
-            return Some((path_ctx, pat_ctx));
-        }
-
-        if let Some(segment) = path.segment() {
-            if segment.coloncolon_token().is_some() {
-                path_ctx.is_absolute_path = true;
-                return Some((path_ctx, pat_ctx));
-            }
-        }
-
-        Some((path_ctx, pat_ctx))
-    }
-}
-
-fn pattern_context_for(original_file: &SyntaxNode, pat: ast::Pat) -> PatternContext {
-    let mut is_param = None;
-    let (refutability, has_type_ascription) =
-    pat
-        .syntax()
-        .ancestors()
-        .skip_while(|it| ast::Pat::can_cast(it.kind()))
-        .next()
-        .map_or((PatternRefutability::Irrefutable, false), |node| {
-            let refutability = match_ast! {
-                match node {
-                    ast::LetStmt(let_) => return (PatternRefutability::Irrefutable, let_.ty().is_some()),
-                    ast::Param(param) => {
-                        let has_type_ascription = param.ty().is_some();
-                        is_param = (|| {
-                            let fake_param_list = param.syntax().parent().and_then(ast::ParamList::cast)?;
-                            let param_list = find_node_in_file_compensated(original_file, &fake_param_list)?;
-                            let param_list_owner = param_list.syntax().parent()?;
-                            let kind = match_ast! {
-                                match param_list_owner {
-                                    ast::ClosureExpr(closure) => ParamKind::Closure(closure),
-                                    ast::Fn(fn_) => ParamKind::Function(fn_),
-                                    _ => return None,
-                                }
-                            };
-                            Some((param_list, param, kind))
-                        })();
-                        return (PatternRefutability::Irrefutable, has_type_ascription)
-                    },
-                    ast::MatchArm(_) => PatternRefutability::Refutable,
-                    ast::LetExpr(_) => PatternRefutability::Refutable,
-                    ast::ForExpr(_) => PatternRefutability::Irrefutable,
-                    _ => PatternRefutability::Irrefutable,
-                }
-            };
-            (refutability, false)
-        });
-    let (ref_token, mut_token) = match &pat {
-        ast::Pat::IdentPat(it) => (it.ref_token(), it.mut_token()),
-        _ => (None, None),
-    };
-    PatternContext {
-        refutability,
-        param_ctx: is_param,
-        has_type_ascription,
-        parent_pat: pat.syntax().parent().and_then(ast::Pat::cast),
-        mut_token,
-        ref_token,
-    }
-}
-
-/// Attempts to find `node` inside `syntax` via `node`'s text range.
-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();
-    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_node_in_file_compensated<N: AstNode>(syntax: &SyntaxNode, node: &N) -> Option<N> {
-    let syntax_range = syntax.text_range();
-    let range = node.syntax().text_range();
-    let end = range.end().checked_sub(TextSize::try_from(COMPLETION_MARKER.len()).ok()?)?;
-    if end < range.start() {
-        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
-    let intersection = range.intersect(syntax_range)?;
-    syntax.covering_element(intersection).ancestors().find_map(N::cast)
-}
-
-fn path_or_use_tree_qualifier(path: &ast::Path) -> Option<(ast::Path, bool)> {
-    if let Some(qual) = path.qualifier() {
-        return Some((qual, false));
-    }
-    let use_tree_list = path.syntax().ancestors().find_map(ast::UseTreeList::cast)?;
-    let use_tree = use_tree_list.syntax().parent().and_then(ast::UseTree::cast)?;
-    Some((use_tree.path()?, true))
-}
-
-fn has_ref(token: &SyntaxToken) -> bool {
-    let mut token = token.clone();
-    for skip in [IDENT, WHITESPACE, T![mut]] {
-        if token.kind() == skip {
-            token = match token.prev_token() {
-                Some(it) => it,
-                None => return false,
-            }
-        }
-    }
-    token.kind() == T![&]
 }
 
 const OP_TRAIT_LANG_NAMES: &[&str] = &[
@@ -1252,370 +551,3 @@ fn has_ref(token: &SyntaxToken) -> bool {
     "shr",
     "sub",
 ];
-
-#[cfg(test)]
-mod tests {
-    use expect_test::{expect, Expect};
-    use hir::HirDisplay;
-
-    use crate::tests::{position, TEST_CONFIG};
-
-    use super::CompletionContext;
-
-    fn check_expected_type_and_name(ra_fixture: &str, expect: Expect) {
-        let (db, pos) = position(ra_fixture);
-        let config = TEST_CONFIG;
-        let completion_context = CompletionContext::new(&db, pos, &config).unwrap();
-
-        let ty = completion_context
-            .expected_type
-            .map(|t| t.display_test(&db).to_string())
-            .unwrap_or("?".to_owned());
-
-        let name = completion_context
-            .expected_name
-            .map_or_else(|| "?".to_owned(), |name| name.to_string());
-
-        expect.assert_eq(&format!("ty: {}, name: {}", ty, name));
-    }
-
-    #[test]
-    fn expected_type_let_without_leading_char() {
-        cov_mark::check!(expected_type_let_without_leading_char);
-        check_expected_type_and_name(
-            r#"
-fn foo() {
-    let x: u32 = $0;
-}
-"#,
-            expect![[r#"ty: u32, name: x"#]],
-        );
-    }
-
-    #[test]
-    fn expected_type_let_with_leading_char() {
-        cov_mark::check!(expected_type_let_with_leading_char);
-        check_expected_type_and_name(
-            r#"
-fn foo() {
-    let x: u32 = c$0;
-}
-"#,
-            expect![[r#"ty: u32, name: x"#]],
-        );
-    }
-
-    #[test]
-    fn expected_type_let_pat() {
-        check_expected_type_and_name(
-            r#"
-fn foo() {
-    let x$0 = 0u32;
-}
-"#,
-            expect![[r#"ty: u32, name: ?"#]],
-        );
-        check_expected_type_and_name(
-            r#"
-fn foo() {
-    let $0 = 0u32;
-}
-"#,
-            expect![[r#"ty: u32, name: ?"#]],
-        );
-    }
-
-    #[test]
-    fn expected_type_fn_param() {
-        cov_mark::check!(expected_type_fn_param);
-        check_expected_type_and_name(
-            r#"
-fn foo() { bar($0); }
-fn bar(x: u32) {}
-"#,
-            expect![[r#"ty: u32, name: x"#]],
-        );
-        check_expected_type_and_name(
-            r#"
-fn foo() { bar(c$0); }
-fn bar(x: u32) {}
-"#,
-            expect![[r#"ty: u32, name: x"#]],
-        );
-    }
-
-    #[test]
-    fn expected_type_fn_param_ref() {
-        cov_mark::check!(expected_type_fn_param_ref);
-        check_expected_type_and_name(
-            r#"
-fn foo() { bar(&$0); }
-fn bar(x: &u32) {}
-"#,
-            expect![[r#"ty: u32, name: x"#]],
-        );
-        check_expected_type_and_name(
-            r#"
-fn foo() { bar(&mut $0); }
-fn bar(x: &mut u32) {}
-"#,
-            expect![[r#"ty: u32, name: x"#]],
-        );
-        check_expected_type_and_name(
-            r#"
-fn foo() { bar(& c$0); }
-fn bar(x: &u32) {}
-        "#,
-            expect![[r#"ty: u32, name: x"#]],
-        );
-        check_expected_type_and_name(
-            r#"
-fn foo() { bar(&mut c$0); }
-fn bar(x: &mut u32) {}
-"#,
-            expect![[r#"ty: u32, name: x"#]],
-        );
-        check_expected_type_and_name(
-            r#"
-fn foo() { bar(&c$0); }
-fn bar(x: &u32) {}
-        "#,
-            expect![[r#"ty: u32, name: x"#]],
-        );
-    }
-
-    #[test]
-    fn expected_type_struct_field_without_leading_char() {
-        cov_mark::check!(expected_type_struct_field_without_leading_char);
-        check_expected_type_and_name(
-            r#"
-struct Foo { a: u32 }
-fn foo() {
-    Foo { a: $0 };
-}
-"#,
-            expect![[r#"ty: u32, name: a"#]],
-        )
-    }
-
-    #[test]
-    fn expected_type_struct_field_followed_by_comma() {
-        cov_mark::check!(expected_type_struct_field_followed_by_comma);
-        check_expected_type_and_name(
-            r#"
-struct Foo { a: u32 }
-fn foo() {
-    Foo { a: $0, };
-}
-"#,
-            expect![[r#"ty: u32, name: a"#]],
-        )
-    }
-
-    #[test]
-    fn expected_type_generic_struct_field() {
-        check_expected_type_and_name(
-            r#"
-struct Foo<T> { a: T }
-fn foo() -> Foo<u32> {
-    Foo { a: $0 }
-}
-"#,
-            expect![[r#"ty: u32, name: a"#]],
-        )
-    }
-
-    #[test]
-    fn expected_type_struct_field_with_leading_char() {
-        cov_mark::check!(expected_type_struct_field_with_leading_char);
-        check_expected_type_and_name(
-            r#"
-struct Foo { a: u32 }
-fn foo() {
-    Foo { a: c$0 };
-}
-"#,
-            expect![[r#"ty: u32, name: a"#]],
-        );
-    }
-
-    #[test]
-    fn expected_type_match_arm_without_leading_char() {
-        cov_mark::check!(expected_type_match_arm_without_leading_char);
-        check_expected_type_and_name(
-            r#"
-enum E { X }
-fn foo() {
-   match E::X { $0 }
-}
-"#,
-            expect![[r#"ty: E, name: ?"#]],
-        );
-    }
-
-    #[test]
-    fn expected_type_match_arm_with_leading_char() {
-        cov_mark::check!(expected_type_match_arm_with_leading_char);
-        check_expected_type_and_name(
-            r#"
-enum E { X }
-fn foo() {
-   match E::X { c$0 }
-}
-"#,
-            expect![[r#"ty: E, name: ?"#]],
-        );
-    }
-
-    #[test]
-    fn expected_type_if_let_without_leading_char() {
-        cov_mark::check!(expected_type_if_let_without_leading_char);
-        check_expected_type_and_name(
-            r#"
-enum Foo { Bar, Baz, Quux }
-
-fn foo() {
-    let f = Foo::Quux;
-    if let $0 = f { }
-}
-"#,
-            expect![[r#"ty: Foo, name: ?"#]],
-        )
-    }
-
-    #[test]
-    fn expected_type_if_let_with_leading_char() {
-        cov_mark::check!(expected_type_if_let_with_leading_char);
-        check_expected_type_and_name(
-            r#"
-enum Foo { Bar, Baz, Quux }
-
-fn foo() {
-    let f = Foo::Quux;
-    if let c$0 = f { }
-}
-"#,
-            expect![[r#"ty: Foo, name: ?"#]],
-        )
-    }
-
-    #[test]
-    fn expected_type_fn_ret_without_leading_char() {
-        cov_mark::check!(expected_type_fn_ret_without_leading_char);
-        check_expected_type_and_name(
-            r#"
-fn foo() -> u32 {
-    $0
-}
-"#,
-            expect![[r#"ty: u32, name: ?"#]],
-        )
-    }
-
-    #[test]
-    fn expected_type_fn_ret_with_leading_char() {
-        cov_mark::check!(expected_type_fn_ret_with_leading_char);
-        check_expected_type_and_name(
-            r#"
-fn foo() -> u32 {
-    c$0
-}
-"#,
-            expect![[r#"ty: u32, name: ?"#]],
-        )
-    }
-
-    #[test]
-    fn expected_type_fn_ret_fn_ref_fully_typed() {
-        check_expected_type_and_name(
-            r#"
-fn foo() -> u32 {
-    foo$0
-}
-"#,
-            expect![[r#"ty: u32, name: ?"#]],
-        )
-    }
-
-    #[test]
-    fn expected_type_closure_param_return() {
-        // FIXME: make this work with `|| $0`
-        check_expected_type_and_name(
-            r#"
-//- minicore: fn
-fn foo() {
-    bar(|| a$0);
-}
-
-fn bar(f: impl FnOnce() -> u32) {}
-"#,
-            expect![[r#"ty: u32, name: ?"#]],
-        );
-    }
-
-    #[test]
-    fn expected_type_generic_function() {
-        check_expected_type_and_name(
-            r#"
-fn foo() {
-    bar::<u32>($0);
-}
-
-fn bar<T>(t: T) {}
-"#,
-            expect![[r#"ty: u32, name: t"#]],
-        );
-    }
-
-    #[test]
-    fn expected_type_generic_method() {
-        check_expected_type_and_name(
-            r#"
-fn foo() {
-    S(1u32).bar($0);
-}
-
-struct S<T>(T);
-impl<T> S<T> {
-    fn bar(self, t: T) {}
-}
-"#,
-            expect![[r#"ty: u32, name: t"#]],
-        );
-    }
-
-    #[test]
-    fn expected_type_functional_update() {
-        cov_mark::check!(expected_type_struct_func_update);
-        check_expected_type_and_name(
-            r#"
-struct Foo { field: u32 }
-fn foo() {
-    Foo {
-        ..$0
-    }
-}
-"#,
-            expect![[r#"ty: Foo, name: ?"#]],
-        );
-    }
-
-    #[test]
-    fn expected_type_param_pat() {
-        check_expected_type_and_name(
-            r#"
-struct Foo { field: u32 }
-fn foo(a$0: Foo) {}
-"#,
-            expect![[r#"ty: Foo, name: ?"#]],
-        );
-        check_expected_type_and_name(
-            r#"
-struct Foo { field: u32 }
-fn foo($0: Foo) {}
-"#,
-            // FIXME make this work, currently fails due to pattern recovery eating the `:`
-            expect![[r#"ty: ?, name: ?"#]],
-        );
-    }
-}