Repository: rust-analyzer/rust-analyzer
Pull request #9068 (2021-05-30 19:37:15)

internal: Move more `CompletionContext` fields to `ImmediateLocation`
bors r+


Patch:
diff --git a/crates/hir_ty/src/chalk_db.rs b/crates/hir_ty/src/chalk_db.rs
index 4e042bf422..b108fd559c 100644
--- a/crates/hir_ty/src/chalk_db.rs
+++ b/crates/hir_ty/src/chalk_db.rs
@@ -383,7 +383,7 @@ pub(crate) fn associated_ty_data_query(
     // Lower bounds -- we could/should maybe move this to a separate query in `lower`
     let type_alias_data = db.type_alias_data(type_alias);
     let generic_params = generics(db.upcast(), type_alias.into());
-    // let bound_vars = generic_params.bound_vars_subst(DebruijnIndex::INNERMOST);
+    let bound_vars = generic_params.bound_vars_subst(DebruijnIndex::INNERMOST);
     let resolver = hir_def::resolver::HasResolver::resolver(type_alias, db.upcast());
     let ctx = crate::TyLoweringContext::new(db, &resolver)
         .with_type_param_mode(crate::lower::TypeParamLoweringMode::Variable);
@@ -396,10 +396,8 @@ pub(crate) fn associated_ty_data_query(
         .filter_map(|pred| generic_predicate_to_inline_bound(db, &pred, &self_ty))
         .collect();
 
-    // FIXME: Re-enable where clauses on associated types when an upstream chalk bug is fixed.
-    //        (rust-analyzer#9052)
-    // let where_clauses = convert_where_clauses(db, type_alias.into(), &bound_vars);
-    let bound_data = rust_ir::AssociatedTyDatumBound { bounds, where_clauses: vec![] };
+    let where_clauses = convert_where_clauses(db, type_alias.into(), &bound_vars);
+    let bound_data = rust_ir::AssociatedTyDatumBound { bounds, where_clauses };
     let datum = AssociatedTyDatum {
         trait_id: to_chalk_trait_id(trait_),
         id,
diff --git a/crates/ide_completion/src/completions/attribute.rs b/crates/ide_completion/src/completions/attribute.rs
index 76d926157c..13d5b90c99 100644
--- a/crates/ide_completion/src/completions/attribute.rs
+++ b/crates/ide_completion/src/completions/attribute.rs
@@ -219,7 +219,8 @@ const ATTRIBUTES: &[AttrCompletion] = &[
     ),
     attr("feature(…)", Some("feature"), Some("feature(${0:flag})")).prefer_inner(),
     attr("forbid(…)", Some("forbid"), Some("forbid(${0:lint})")),
-    attr("global_allocator", None, None),
+    // FIXME: resolve through macro resolution?
+    attr("global_allocator", None, None).prefer_inner(),
     attr(r#"ignore = "…""#, Some("ignore"), Some(r#"ignore = "${0:reason}""#)),
     attr("inline", Some("inline"), Some("inline")),
     attr("link", None, None),
@@ -238,7 +239,7 @@ const ATTRIBUTES: &[AttrCompletion] = &[
     attr("no_mangle", None, None),
     attr("no_std", None, None).prefer_inner(),
     attr("non_exhaustive", None, None),
-    attr("panic_handler", None, None),
+    attr("panic_handler", None, None).prefer_inner(),
     attr(r#"path = "…""#, Some("path"), Some(r#"path ="${0:path}""#)),
     attr("proc_macro", None, None),
     attr("proc_macro_attribute", None, None),
@@ -608,7 +609,6 @@ mod tests {
                 at export_name = "…"
                 at link_name = "…"
                 at link_section = "…"
-                at global_allocator
                 at used
             "#]],
         );
@@ -732,9 +732,9 @@ mod tests {
     }
 
     #[test]
-    fn complete_attribute_on_fn() {
+    fn complete_attribute_on_expr() {
         check(
-            r#"#[$0] fn main() {}"#,
+            r#"fn main() { #[$0] foo() }"#,
             expect![[r#"
                 at allow(…)
                 at cfg(…)
@@ -742,35 +742,10 @@ mod tests {
                 at deny(…)
                 at forbid(…)
                 at warn(…)
-                at deprecated
-                at doc = "…"
-                at doc(hidden)
-                at doc(alias = "…")
-                at must_use
-                at no_mangle
-                at export_name = "…"
-                at link_name = "…"
-                at link_section = "…"
-                at cold
-                at ignore = "…"
-                at inline
-                at must_use
-                at panic_handler
-                at proc_macro
-                at proc_macro_derive(…)
-                at proc_macro_attribute
-                at should_panic
-                at target_feature = "…"
-                at test
-                at track_caller
             "#]],
         );
-    }
-
-    #[test]
-    fn complete_attribute_on_expr() {
         check(
-            r#"fn main() { #[$0] foo() }"#,
+            r#"fn main() { #[$0] foo(); }"#,
             expect![[r#"
                 at allow(…)
                 at cfg(…)
diff --git a/crates/ide_completion/src/completions/fn_param.rs b/crates/ide_completion/src/completions/fn_param.rs
index 0ea5584893..cb90e8a3e1 100644
--- a/crates/ide_completion/src/completions/fn_param.rs
+++ b/crates/ide_completion/src/completions/fn_param.rs
@@ -128,4 +128,19 @@ fn outer(text: String) {
             "#]],
         )
     }
+
+    #[test]
+    fn completes_non_ident_pat_param() {
+        check(
+            r#"
+struct Bar { bar: u32 }
+
+fn foo(Bar { bar }: Bar) {}
+fn foo2($0) {}
+"#,
+            expect![[r#"
+                bn Bar { bar }: Bar
+            "#]],
+        )
+    }
 }
diff --git a/crates/ide_completion/src/completions/keyword.rs b/crates/ide_completion/src/completions/keyword.rs
index e71a04b6ec..0d035c6115 100644
--- a/crates/ide_completion/src/completions/keyword.rs
+++ b/crates/ide_completion/src/completions/keyword.rs
@@ -4,7 +4,10 @@ use std::iter;
 
 use syntax::{SyntaxKind, T};
 
-use crate::{CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions};
+use crate::{
+    patterns::ImmediateLocation, CompletionContext, CompletionItem, CompletionItemKind,
+    CompletionKind, Completions,
+};
 
 pub(crate) fn complete_use_tree_keyword(acc: &mut Completions, ctx: &CompletionContext) {
     // complete keyword "crate" in use stmt
@@ -44,7 +47,7 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte
         cov_mark::hit!(no_keyword_completion_in_comments);
         return;
     }
-    if ctx.record_lit_syntax.is_some() {
+    if matches!(ctx.completion_location, Some(ImmediateLocation::RecordExpr(_))) {
         cov_mark::hit!(no_keyword_completion_in_record_lit);
         return;
     }
@@ -55,7 +58,6 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte
     let expects_item = ctx.expects_item();
 
     if ctx.has_impl_or_trait_prev_sibling() {
-        // FIXME this also incorrectly shows up after a complete trait/impl
         add_keyword("where", "where ");
         return;
     }
@@ -77,11 +79,8 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte
         add_keyword("pub", "pub ");
     }
 
-    if expects_item || expects_assoc_item || has_block_expr_parent || ctx.is_match_arm {
-        add_keyword("unsafe", "unsafe ");
-    }
-
     if expects_item || expects_assoc_item || has_block_expr_parent {
+        add_keyword("unsafe", "unsafe ");
         add_keyword("fn", "fn $1($2) {\n    $0\n}");
         add_keyword("const", "const $0");
         add_keyword("type", "type $0");
@@ -103,6 +102,9 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte
     }
 
     if ctx.expects_expression() {
+        if !has_block_expr_parent {
+            add_keyword("unsafe", "unsafe {\n    $0\n}");
+        }
         add_keyword("match", "match $1 {\n    $0\n}");
         add_keyword("while", "while $1 {\n    $0\n}");
         add_keyword("while let", "while let $1 = $2 {\n    $0\n}");
@@ -574,6 +576,7 @@ pub mod future {
         check(
             r#"fn main() { let _ = $0 }"#,
             expect![[r#"
+                kw unsafe
                 kw match
                 kw while
                 kw while let
@@ -634,6 +637,7 @@ fn foo() {
 }
 "#,
             expect![[r#"
+                kw unsafe
                 kw match
                 kw while
                 kw while let
diff --git a/crates/ide_completion/src/completions/mod_.rs b/crates/ide_completion/src/completions/mod_.rs
index 4f94157366..6a5746fb94 100644
--- a/crates/ide_completion/src/completions/mod_.rs
+++ b/crates/ide_completion/src/completions/mod_.rs
@@ -9,14 +9,14 @@ use ide_db::{
 };
 use rustc_hash::FxHashSet;
 
-use crate::CompletionItem;
+use crate::{patterns::ImmediateLocation, CompletionItem};
 
 use crate::{context::CompletionContext, item::CompletionKind, Completions};
 
 /// Complete mod declaration, i.e. `mod $0 ;`
 pub(crate) fn complete_mod(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
-    let mod_under_caret = match &ctx.mod_declaration_under_caret {
-        Some(mod_under_caret) if mod_under_caret.item_list().is_none() => mod_under_caret,
+    let mod_under_caret = match &ctx.completion_location {
+        Some(ImmediateLocation::ModDeclaration(mod_under_caret)) => mod_under_caret,
         _ => return None,
     };
 
diff --git a/crates/ide_completion/src/completions/record.rs b/crates/ide_completion/src/completions/record.rs
index e1526b70b8..227c08d010 100644
--- a/crates/ide_completion/src/completions/record.rs
+++ b/crates/ide_completion/src/completions/record.rs
@@ -2,21 +2,21 @@
 use ide_db::{helpers::FamousDefs, SymbolKind};
 use syntax::ast::Expr;
 
-use crate::{item::CompletionKind, CompletionContext, CompletionItem, Completions};
+use crate::{
+    item::CompletionKind, patterns::ImmediateLocation, CompletionContext, CompletionItem,
+    Completions,
+};
 
 pub(crate) fn complete_record(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
-    let missing_fields = match (ctx.record_pat_syntax.as_ref(), ctx.record_lit_syntax.as_ref()) {
-        (None, None) => return None,
-        (Some(_), Some(_)) => unreachable!("A record cannot be both a literal and a pattern"),
-        (Some(record_pat), _) => ctx.sema.record_pattern_missing_fields(record_pat),
-        (_, Some(record_lit)) => {
-            let ty = ctx.sema.type_of_expr(&Expr::RecordExpr(record_lit.clone()));
+    let missing_fields = match &ctx.completion_location {
+        Some(ImmediateLocation::RecordExpr(record_expr)) => {
+            let ty = ctx.sema.type_of_expr(&Expr::RecordExpr(record_expr.clone()));
             let default_trait = FamousDefs(&ctx.sema, ctx.krate).core_default_Default();
             let impl_default_trait = default_trait
                 .zip(ty)
                 .map_or(false, |(default_trait, ty)| ty.impls_trait(ctx.db, default_trait, &[]));
 
-            let missing_fields = ctx.sema.record_literal_missing_fields(record_lit);
+            let missing_fields = ctx.sema.record_literal_missing_fields(record_expr);
             if impl_default_trait && !missing_fields.is_empty() {
                 let completion_text = "..Default::default()";
                 let mut item = CompletionItem::new(
@@ -32,6 +32,10 @@ pub(crate) fn complete_record(acc: &mut Completions, ctx: &CompletionContext) ->
 
             missing_fields
         }
+        Some(ImmediateLocation::RecordPat(record_pat)) => {
+            ctx.sema.record_pattern_missing_fields(record_pat)
+        }
+        _ => return None,
     };
 
     for (field, ty) in missing_fields {
diff --git a/crates/ide_completion/src/context.rs b/crates/ide_completion/src/context.rs
index 8d6440cb23..7c46c815d2 100644
--- a/crates/ide_completion/src/context.rs
+++ b/crates/ide_completion/src/context.rs
@@ -18,7 +18,7 @@ use text_edit::Indel;
 use crate::{
     patterns::{
         determine_location, determine_prev_sibling, for_is_prev2, inside_impl_trait_block,
-        is_in_loop_body, is_match_arm, previous_token, ImmediateLocation, ImmediatePrevSibling,
+        is_in_loop_body, previous_token, ImmediateLocation, ImmediatePrevSibling,
     },
     CompletionConfig,
 };
@@ -54,11 +54,6 @@ pub(crate) struct CompletionContext<'a> {
     /// The parent impl of the cursor position if it exists.
     pub(super) impl_def: Option<ast::Impl>,
 
-    /// RecordExpr the token is a field of
-    pub(super) record_lit_syntax: Option<ast::RecordExpr>,
-    /// RecordPat the token is a field of
-    pub(super) record_pat_syntax: Option<ast::RecordPat>,
-
     // potentially set if we are completing a lifetime
     pub(super) lifetime_syntax: Option<ast::Lifetime>,
     pub(super) lifetime_param_syntax: Option<ast::LifetimeParam>,
@@ -71,6 +66,7 @@ pub(crate) struct CompletionContext<'a> {
 
     pub(super) completion_location: Option<ImmediateLocation>,
     pub(super) prev_sibling: Option<ImmediatePrevSibling>,
+    pub(super) attribute_under_caret: Option<ast::Attr>,
 
     /// FIXME: `ActiveParameter` is string-based, which is very very wrong
     pub(super) active_parameter: Option<ActiveParameter>,
@@ -95,14 +91,10 @@ pub(crate) struct CompletionContext<'a> {
     pub(super) is_macro_call: bool,
     pub(super) is_path_type: bool,
     pub(super) has_type_args: bool,
-    pub(super) attribute_under_caret: Option<ast::Attr>,
-    pub(super) mod_declaration_under_caret: Option<ast::Module>,
     pub(super) locals: Vec<(String, Local)>,
 
-    // keyword patterns
     pub(super) previous_token: Option<SyntaxToken>,
     pub(super) in_loop_body: bool,
-    pub(super) is_match_arm: bool,
     pub(super) incomplete_let: bool,
 
     no_completion_required: bool,
@@ -157,8 +149,6 @@ impl<'a> CompletionContext<'a> {
             lifetime_param_syntax: None,
             function_def: None,
             use_item_syntax: None,
-            record_lit_syntax: None,
-            record_pat_syntax: None,
             impl_def: None,
             active_parameter: ActiveParameter::at(db, position),
             is_label_ref: false,
@@ -176,15 +166,13 @@ impl<'a> CompletionContext<'a> {
             is_macro_call: false,
             is_path_type: false,
             has_type_args: false,
-            attribute_under_caret: None,
-            mod_declaration_under_caret: None,
             previous_token: None,
             in_loop_body: false,
             completion_location: None,
             prev_sibling: None,
-            is_match_arm: false,
             no_completion_required: false,
             incomplete_let: false,
+            attribute_under_caret: None,
             locals,
         };
 
@@ -227,7 +215,6 @@ impl<'a> CompletionContext<'a> {
                 break;
             }
         }
-        ctx.fill_keyword_patterns(&speculative_file, offset);
         ctx.fill(&original_file, speculative_file, offset);
         Some(ctx)
     }
@@ -311,31 +298,13 @@ impl<'a> CompletionContext<'a> {
     }
 
     pub(crate) fn is_path_disallowed(&self) -> bool {
-        self.record_lit_syntax.is_some()
-            || self.record_pat_syntax.is_some()
-            || self.attribute_under_caret.is_some()
-            || self.mod_declaration_under_caret.is_some()
-    }
-
-    fn fill_keyword_patterns(&mut self, file_with_fake_ident: &SyntaxNode, offset: TextSize) {
-        let fake_ident_token = file_with_fake_ident.token_at_offset(offset).right_biased().unwrap();
-        let syntax_element = NodeOrToken::Token(fake_ident_token);
-        self.previous_token = previous_token(syntax_element.clone());
-        self.in_loop_body = is_in_loop_body(syntax_element.clone());
-        self.is_match_arm = is_match_arm(syntax_element.clone());
-
-        self.mod_declaration_under_caret =
-            find_node_at_offset::<ast::Module>(&file_with_fake_ident, offset)
-                .filter(|module| module.item_list().is_none());
-        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()
-            });
-
-        let inside_impl_trait_block = inside_impl_trait_block(syntax_element.clone());
-        let fn_is_prev = self.previous_token_is(T![fn]);
-        let for_is_prev2 = for_is_prev2(syntax_element.clone());
-        self.no_completion_required = (fn_is_prev && !inside_impl_trait_block) || for_is_prev2;
+        matches!(
+            self.completion_location,
+            Some(ImmediateLocation::Attribute(_))
+                | Some(ImmediateLocation::ModDeclaration(_))
+                | Some(ImmediateLocation::RecordPat(_))
+                | Some(ImmediateLocation::RecordExpr(_))
+        ) || self.attribute_under_caret.is_some()
     }
 
     fn fill_impl_def(&mut self) {
@@ -453,25 +422,43 @@ impl<'a> CompletionContext<'a> {
         file_with_fake_ident: SyntaxNode,
         offset: TextSize,
     ) {
+        let fake_ident_token = file_with_fake_ident.token_at_offset(offset).right_biased().unwrap();
+        let syntax_element = NodeOrToken::Token(fake_ident_token);
+        self.previous_token = previous_token(syntax_element.clone());
+        self.attribute_under_caret = syntax_element.ancestors().find_map(ast::Attr::cast);
+        self.no_completion_required = {
+            let inside_impl_trait_block = inside_impl_trait_block(syntax_element.clone());
+            let fn_is_prev = self.previous_token_is(T![fn]);
+            let for_is_prev2 = for_is_prev2(syntax_element.clone());
+            (fn_is_prev && !inside_impl_trait_block) || for_is_prev2
+        };
+        self.in_loop_body = is_in_loop_body(syntax_element.clone());
+
+        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()
+            });
+
         let (expected_type, expected_name) = self.expected_type_and_name();
         self.expected_type = expected_type;
         self.expected_name = expected_name;
-        self.attribute_under_caret = find_node_at_offset(&file_with_fake_ident, offset);
+
         let name_like = match find_node_at_offset(&&file_with_fake_ident, offset) {
             Some(it) => it,
             None => return,
         };
-        self.completion_location = determine_location(&name_like);
+        self.completion_location =
+            determine_location(&self.sema, original_file, offset, &name_like);
         self.prev_sibling = determine_prev_sibling(&name_like);
         match name_like {
             ast::NameLike::Lifetime(lifetime) => {
                 self.classify_lifetime(original_file, lifetime, offset);
             }
             ast::NameLike::NameRef(name_ref) => {
-                self.classify_name_ref(original_file, name_ref, offset);
+                self.classify_name_ref(original_file, name_ref);
             }
             ast::NameLike::Name(name) => {
-                self.classify_name(original_file, name, offset);
+                self.classify_name(name);
             }
         }
     }
@@ -505,7 +492,7 @@ impl<'a> CompletionContext<'a> {
         }
     }
 
-    fn classify_name(&mut self, original_file: &SyntaxNode, name: ast::Name, offset: TextSize) {
+    fn classify_name(&mut self, name: ast::Name) {
         if let Some(bind_pat) = name.syntax().parent().and_then(ast::IdentPat::cast) {
             self.is_pat_or_const = Some(PatternRefutability::Refutable);
             // if any of these is here our bind pat can't be a const pat anymore
@@ -543,28 +530,12 @@ impl<'a> CompletionContext<'a> {
 
             self.fill_impl_def();
         }
+
         self.is_param |= is_node::<ast::Param>(name.syntax());
-        if ast::RecordPatField::for_field_name(&name).is_some() {
-            self.record_pat_syntax =
-                self.sema.find_node_at_offset_with_macros(&original_file, offset);
-        }
     }
 
-    fn classify_name_ref(
-        &mut self,
-        original_file: &SyntaxNode,
-        name_ref: ast::NameRef,
-        offset: TextSize,
-    ) {
+    fn classify_name_ref(&mut self, original_file: &SyntaxNode, name_ref: ast::NameRef) {
         self.fill_impl_def();
-        if ast::RecordExprField::for_field_name(&name_ref).is_some() {
-            self.record_lit_syntax =
-                self.sema.find_node_at_offset_with_macros(original_file, offset);
-        }
-        if ast::RecordPatField::for_field_name_ref(&name_ref).is_some() {
-            self.record_pat_syntax =
-                self.sema.find_node_at_offset_with_macros(&original_file, offset);
-        }
 
         self.name_ref_syntax =
             find_node_at_offset(original_file, name_ref.syntax().text_range().start());
diff --git a/crates/ide_completion/src/patterns.rs b/crates/ide_completion/src/patterns.rs
index caf0ef39fc..26516046bc 100644
--- a/crates/ide_completion/src/patterns.rs
+++ b/crates/ide_completion/src/patterns.rs
@@ -1,15 +1,18 @@
 //! Patterns telling us certain facts about current syntax element, they are used in completion context
 
+use hir::Semantics;
+use ide_db::RootDatabase;
 use syntax::{
     algo::non_trivia_sibling,
     ast::{self, LoopBodyOwner},
-    match_ast, AstNode, Direction, NodeOrToken, SyntaxElement,
+    match_ast, AstNode, Direction, SyntaxElement,
     SyntaxKind::*,
-    SyntaxNode, SyntaxToken, T,
+    SyntaxNode, SyntaxToken, TextSize, T,
 };
 
 #[cfg(test)]
 use crate::test_utils::{check_pattern_is_applicable, check_pattern_is_not_applicable};
+
 /// Direct parent container of the cursor position
 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
 pub(crate) enum ImmediatePrevSibling {
@@ -19,7 +22,7 @@ pub(crate) enum ImmediatePrevSibling {
 }
 
 /// Direct parent container of the cursor position
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+#[derive(Clone, Debug, PartialEq, Eq)]
 pub(crate) enum ImmediateLocation {
     Use,
     Impl,
@@ -29,10 +32,24 @@ pub(crate) enum ImmediateLocation {
     IdentPat,
     BlockExpr,
     ItemList,
+    // Fake file ast node
+    Attribute(ast::Attr),
+    // Fake file ast node
+    ModDeclaration(ast::Module),
+    // Original file ast node
+    /// The record expr of the field name we are completing
+    RecordExpr(ast::RecordExpr),
+    // Original file ast node
+    /// The record pat of the field name we are completing
+    RecordPat(ast::RecordPat),
 }
 
 pub(crate) fn determine_prev_sibling(name_like: &ast::NameLike) -> Option<ImmediatePrevSibling> {
-    let node = maximize_name_ref(name_like)?;
+    let node = match name_like {
+        ast::NameLike::NameRef(name_ref) => maximize_name_ref(name_ref),
+        ast::NameLike::Name(n) => n.syntax().clone(),
+        ast::NameLike::Lifetime(lt) => lt.syntax().clone(),
+    };
     let node = match node.parent().and_then(ast::MacroCall::cast) {
         // When a path is being typed after the name of a trait/type of an impl it is being
         // parsed as a macro, so when the trait/impl has a block following it an we are between the
@@ -77,8 +94,37 @@ pub(crate) fn determine_prev_sibling(name_like: &ast::NameLike) -> Option<Immedi
     Some(res)
 }
 
-pub(crate) fn determine_location(name_like: &ast::NameLike) -> Option<ImmediateLocation> {
-    let node = maximize_name_ref(name_like)?;
+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) => {
+            if ast::RecordExprField::for_field_name(&name_ref).is_some() {
+                return sema
+                    .find_node_at_offset_with_macros(original_file, offset)
+                    .map(ImmediateLocation::RecordExpr);
+            }
+            if ast::RecordPatField::for_field_name_ref(&name_ref).is_some() {
+                return sema
+                    .find_node_at_offset_with_macros(original_file, offset)
+                    .map(ImmediateLocation::RecordPat);
+            }
+            maximize_name_ref(name_ref)
+        }
+        ast::NameLike::Name(name) => {
+            if ast::RecordPatField::for_field_name(&name).is_some() {
+                return sema
+                    .find_node_at_offset_with_macros(original_file, offset)
+                    .map(ImmediateLocation::RecordPat);
+            }
+            name.syntax().clone()
+        }
+        ast::NameLike::Lifetime(lt) => lt.syntax().clone(),
+    };
+
     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.
@@ -103,6 +149,7 @@ pub(crate) fn determine_location(name_like: &ast::NameLike) -> Option<ImmediateL
             }
         }
     };
+
     let res = match_ast! {
         match parent {
             ast::IdentPat(_it) => ImmediateLocation::IdentPat,
@@ -117,36 +164,34 @@ pub(crate) fn determine_location(name_like: &ast::NameLike) -> Option<ImmediateL
                 Some(TRAIT) => ImmediateLocation::Trait,
                 _ => return None,
             },
+            ast::Module(it) => if it.item_list().is_none() {
+                    ImmediateLocation::ModDeclaration(it)
+                } else {
+                    return None
+            },
+            ast::Attr(it) => ImmediateLocation::Attribute(it),
             _ => return None,
         }
     };
     Some(res)
 }
 
-fn maximize_name_ref(name_like: &ast::NameLike) -> Option<SyntaxNode> {
-    // First walk the element we are completing up to its highest node that has the same text range
-    // as the element so that we can check in what context it immediately lies. We only do this for
-    // NameRef -> Path as that's the only thing that makes sense to being "expanded" semantically.
-    // We only wanna do this if the NameRef is the last segment of the path.
-    let node = match name_like {
-        ast::NameLike::NameRef(name_ref) => {
-            if let Some(segment) = name_ref.syntax().parent().and_then(ast::PathSegment::cast) {
-                let p = segment.parent_path();
-                if p.parent_path().is_none() {
-                    p.syntax()
-                        .ancestors()
-                        .take_while(|it| it.text_range() == p.syntax().text_range())
-                        .last()?
-                } else {
-                    return None;
-                }
-            } else {
-                return None;
+fn maximize_name_ref(name_ref: &ast::NameRef) -> SyntaxNode {
+    // Maximize a nameref to its enclosing path if its the last segment of said path
+    if let Some(segment) = name_ref.syntax().parent().and_then(ast::PathSegment::cast) {
+        let p = segment.parent_path();
+        if p.parent_path().is_none() {
+            if let Some(it) = p
+                .syntax()
+                .ancestors()
+                .take_while(|it| it.text_range() == p.syntax().text_range())
+                .last()
+            {
+                return it;
             }
         }
-        it @ ast::NameLike::Name(_) | it @ ast::NameLike::Lifetime(_) => it.syntax().clone(),
-    };
-    Some(node)
+    }
+    name_ref.syntax().clone()
 }
 
 pub(crate) fn inside_impl_trait_block(element: SyntaxElement) -> bool {
@@ -167,18 +212,6 @@ fn test_inside_impl_trait_block() {
     check_pattern_is_not_applicable(r"impl A { fn f$0 }", inside_impl_trait_block);
 }
 
-pub(crate) fn is_match_arm(element: SyntaxElement) -> bool {
-    not_same_range_ancestor(element.clone()).filter(|it| it.kind() == MATCH_ARM).is_some()
-        && previous_sibling_or_ancestor_sibling(element)
-            .and_then(|it| it.into_token())
-            .filter(|it| it.kind() == FAT_ARROW)
-            .is_some()
-}
-#[test]
-fn test_is_match_arm() {
-    check_pattern_is_applicable(r"fn my_fn() { match () { () => m$0 } }", is_match_arm);
-}
-
 pub(crate) fn previous_token(element: SyntaxElement) -> Option<SyntaxToken> {
     element.into_token().and_then(|it| previous_non_trivia_token(it))
 }
@@ -216,10 +249,6 @@ pub(crate) fn is_in_loop_body(element: SyntaxElement) -> bool {
         .is_some()
 }
 
-pub(crate) fn not_same_range_ancestor(element: SyntaxElement) -> Option<SyntaxNode> {
-    element.ancestors().skip_while(|it| it.text_range() == element.text_range()).next()
-}
-
 fn previous_non_trivia_token(token: SyntaxToken) -> Option<SyntaxToken> {
     let mut token = token.prev_token();
     while let Some(inner) = token.clone() {
@@ -232,31 +261,25 @@ fn previous_non_trivia_token(token: SyntaxToken) -> Option<SyntaxToken> {
     None
 }
 
-fn previous_sibling_or_ancestor_sibling(element: SyntaxElement) -> Option<SyntaxElement> {
-    let token_sibling = non_trivia_sibling(element.clone(), Direction::Prev);
-    if let Some(sibling) = token_sibling {
-        Some(sibling)
-    } else {
-        // if not trying to find first ancestor which has such a sibling
-        let range = element.text_range();
-        let top_node = element.ancestors().take_while(|it| it.text_range() == range).last()?;
-        let prev_sibling_node = top_node.ancestors().find(|it| {
-            non_trivia_sibling(NodeOrToken::Node(it.to_owned()), Direction::Prev).is_some()
-        })?;
-        non_trivia_sibling(NodeOrToken::Node(prev_sibling_node), Direction::Prev)
-    }
-}
-
 #[cfg(test)]
 mod tests {
+    use syntax::algo::find_node_at_offset;
+
+    use crate::test_utils::position;
+
     use super::*;
 
     fn check_location(code: &str, loc: impl Into<Option<ImmediateLocation>>) {
-        check_pattern_is_applicable(code, |e| {
-            let name = &e.parent().and_then(ast::NameLike::cast).expect("Expected a namelike");
-            assert_eq!(determine_location(name), loc.into());
-            true
-        });
+        let (db, pos) = position(code);
+
+        let sema = Semantics::new(&db);
+        let original_file = sema.parse(pos.file_id);
+
+        let name_like = find_node_at_offset(original_file.syntax(), pos.offset).unwrap();
+        assert_eq!(
+            determine_location(&sema, original_file.syntax(), pos.offset, &name_like),
+            loc.into()
+        );
     }
 
     fn check_prev_sibling(code: &str, sibling: impl Into<Option<ImmediatePrevSibling>>) {


Test patch:
diff --git a/crates/hir_ty/src/tests/traits.rs b/crates/hir_ty/src/tests/traits.rs
index 49add4ab99..7c0ff2170f 100644
--- a/crates/hir_ty/src/tests/traits.rs
+++ b/crates/hir_ty/src/tests/traits.rs
@@ -161,7 +161,7 @@ mod result {
 }
 
 #[test]
-fn infer_try_trait_v2() {
+fn infer_tryv2() {
     check_types(
         r#"
 //- /main.rs crate:main deps:core
@@ -172,41 +172,26 @@ fn test() {
 } //^ i32
 
 //- /core.rs crate:core
+#[prelude_import] use ops::*;
 mod ops {
-    mod try_trait {
-        pub trait Try: FromResidual {
-            type Output;
-            type Residual;
-        }
-        pub trait FromResidual<R = <Self as Try>::Residual> {}
+    trait Try {
+        type Output;
+        type Residual;
     }
-
-    pub use self::try_trait::FromResidual;
-    pub use self::try_trait::Try;
-}
-
-mov convert {
-    pub trait From<T> {}
-    impl<T> From<T> for T {}
 }
 
 #[prelude_import] use result::*;
 mod result {
-    use crate::convert::From;
-    use crate::ops::{Try, FromResidual};
-
-    pub enum Infallible {}
-    pub enum Result<O, E> {
+    enum Infallible {}
+    enum Result<O, E> {
         Ok(O),
         Err(E)
     }
 
-    impl<O, E> Try for Result<O, E> {
+    impl<O, E> crate::ops::Try for Result<O, E> {
         type Output = O;
         type Error = Result<Infallible, E>;
     }
-
-    impl<T, E, F: From<E>> FromResidual<Result<Infallible, E>> for Result<T, F> {}
 }
 "#,
     );