Repository: rust-lang/rust-analyzer
Pull request #12461 (2022-06-03 17:56:00)

Move trait_impl completion analysis into CompletionContext


Patch:
diff --git a/crates/ide-assists/src/handlers/auto_import.rs b/crates/ide-assists/src/handlers/auto_import.rs
index 1ec24d8fcc..0a0dafb35e 100644
--- a/crates/ide-assists/src/handlers/auto_import.rs
+++ b/crates/ide-assists/src/handlers/auto_import.rs
@@ -1,10 +1,7 @@
-use std::cmp::Reverse;
-
-use hir::{db::HirDatabase, Module};
 use ide_db::{
     helpers::mod_path_to_ast,
     imports::{
-        import_assets::{ImportAssets, ImportCandidate, LocatedImport},
+        import_assets::{ImportAssets, ImportCandidate},
         insert_use::{insert_use, ImportScope},
     },
 };
@@ -110,19 +107,6 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
 
     // we aren't interested in different namespaces
     proposed_imports.dedup_by(|a, b| a.import_path == b.import_path);
-
-    let current_node = match ctx.covering_element() {
-        NodeOrToken::Node(node) => Some(node),
-        NodeOrToken::Token(token) => token.parent(),
-    };
-
-    let current_module =
-        current_node.as_ref().and_then(|node| ctx.sema.scope(node)).map(|scope| scope.module());
-
-    // prioritize more relevant imports
-    proposed_imports
-        .sort_by_key(|import| Reverse(relevance_score(ctx, import, current_module.as_ref())));
-
     for import in proposed_imports {
         acc.add_group(
             &group_label,
@@ -174,135 +158,11 @@ fn group_label(import_candidate: &ImportCandidate) -> GroupLabel {
     GroupLabel(name)
 }
 
-/// Determine how relevant a given import is in the current context. Higher scores are more
-/// relevant.
-fn relevance_score(
-    ctx: &AssistContext,
-    import: &LocatedImport,
-    current_module: Option<&Module>,
-) -> i32 {
-    let mut score = 0;
-
-    let db = ctx.db();
-
-    let item_module = match import.item_to_import {
-        hir::ItemInNs::Types(item) | hir::ItemInNs::Values(item) => item.module(db),
-        hir::ItemInNs::Macros(makro) => Some(makro.module(db)),
-    };
-
-    match item_module.zip(current_module) {
-        // get the distance between the imported path and the current module
-        // (prefer items that are more local)
-        Some((item_module, current_module)) => {
-            score -= module_distance_hueristic(db, &current_module, &item_module) as i32;
-        }
-
-        // could not find relevant modules, so just use the length of the path as an estimate
-        None => return -(2 * import.import_path.len() as i32),
-    }
-
-    score
-}
-
-/// A heuristic that gives a higher score to modules that are more separated.
-fn module_distance_hueristic(db: &dyn HirDatabase, current: &Module, item: &Module) -> usize {
-    // get the path starting from the item to the respective crate roots
-    let mut current_path = current.path_to_root(db);
-    let mut item_path = item.path_to_root(db);
-
-    // we want paths going from the root to the item
-    current_path.reverse();
-    item_path.reverse();
-
-    // length of the common prefix of the two paths
-    let prefix_length = current_path.iter().zip(&item_path).take_while(|(a, b)| a == b).count();
-
-    // how many modules differ between the two paths (all modules, removing any duplicates)
-    let distinct_length = current_path.len() + item_path.len() - 2 * prefix_length;
-
-    // cost of importing from another crate
-    let crate_boundary_cost = if current.krate() == item.krate() {
-        0
-    } else if item.krate().is_builtin(db) {
-        2
-    } else {
-        4
-    };
-
-    distinct_length + crate_boundary_cost
-}
-
 #[cfg(test)]
 mod tests {
     use super::*;
 
-    use hir::Semantics;
-    use ide_db::{
-        assists::AssistResolveStrategy,
-        base_db::{fixture::WithFixture, FileRange},
-        RootDatabase,
-    };
-
-    use crate::tests::{
-        check_assist, check_assist_not_applicable, check_assist_target, TEST_CONFIG,
-    };
-
-    fn check_auto_import_order(before: &str, order: &[&str]) {
-        let (db, file_id, range_or_offset) = RootDatabase::with_range_or_offset(before);
-        let frange = FileRange { file_id, range: range_or_offset.into() };
-
-        let sema = Semantics::new(&db);
-        let config = TEST_CONFIG;
-        let ctx = AssistContext::new(sema, &config, frange);
-        let mut acc = Assists::new(&ctx, AssistResolveStrategy::All);
-        auto_import(&mut acc, &ctx);
-        let assists = acc.finish();
-
-        let labels = assists.iter().map(|assist| assist.label.to_string()).collect::<Vec<_>>();
-
-        assert_eq!(labels, order);
-    }
-
-    #[test]
-    fn prefer_shorter_paths() {
-        let before = r"
-//- /main.rs crate:main deps:foo,bar
-HashMap$0::new();
-
-//- /lib.rs crate:foo
-pub mod collections { pub struct HashMap; }
-
-//- /lib.rs crate:bar
-pub mod collections { pub mod hash_map { pub struct HashMap; } }
-        ";
-
-        check_auto_import_order(
-            before,
-            &["Import `foo::collections::HashMap`", "Import `bar::collections::hash_map::HashMap`"],
-        )
-    }
-
-    #[test]
-    fn prefer_same_crate() {
-        let before = r"
-//- /main.rs crate:main deps:foo
-HashMap$0::new();
-
-mod collections {
-    pub mod hash_map {
-        pub struct HashMap;
-    }
-}
-
-//- /lib.rs crate:foo
-pub struct HashMap;
-        ";
-
-        check_auto_import_order(
-            before,
-            &["Import `collections::hash_map::HashMap`", "Import `foo::HashMap`"],
-        )
-    }
+    use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
 
     #[test]
     fn not_applicable_if_scope_inside_macro() {
diff --git a/crates/ide-assists/src/handlers/extract_module.rs b/crates/ide-assists/src/handlers/extract_module.rs
index 11349b45d3..b5ed5699c9 100644
--- a/crates/ide-assists/src/handlers/extract_module.rs
+++ b/crates/ide-assists/src/handlers/extract_module.rs
@@ -413,10 +413,7 @@ impl Module {
                                         ctx,
                                     )
                                 {
-                                    check_intersection_and_push(
-                                        &mut import_paths_to_be_removed,
-                                        import_path,
-                                    );
+                                    import_paths_to_be_removed.push(import_path);
                                 }
                             }
                         }
@@ -442,10 +439,7 @@ impl Module {
                                         ctx,
                                     )
                                 {
-                                    check_intersection_and_push(
-                                        &mut import_paths_to_be_removed,
-                                        import_path,
-                                    );
+                                    import_paths_to_be_removed.push(import_path);
                                 }
                             }
                         }
@@ -549,25 +543,12 @@ impl Module {
         } else if exists_inside_sel && !exists_outside_sel {
             //Changes to be made inside new module, and remove import from outside
 
-            if let Some((mut use_tree_str, text_range_opt)) =
+            if let Some((use_tree_str, text_range_opt)) =
                 self.process_use_stmt_for_import_resolve(use_stmt_opt, node_syntax)
             {
                 if let Some(text_range) = text_range_opt {
                     import_path_to_be_removed = Some(text_range);
                 }
-
-                if source_exists_outside_sel_in_same_mod {
-                    if let Some(first_path_in_use_tree) = use_tree_str.last() {
-                        let first_path_in_use_tree_str = first_path_in_use_tree.to_string();
-                        if !first_path_in_use_tree_str.contains("super")
-                            && !first_path_in_use_tree_str.contains("crate")
-                        {
-                            let super_path = make::ext::ident_path("super");
-                            use_tree_str.push(super_path);
-                        }
-                    }
-                }
-
                 use_tree_str_opt = Some(use_tree_str);
             } else if source_exists_outside_sel_in_same_mod {
                 self.make_use_stmt_of_node_with_super(node_syntax);
@@ -577,16 +558,9 @@ impl Module {
         if let Some(use_tree_str) = use_tree_str_opt {
             let mut use_tree_str = use_tree_str;
             use_tree_str.reverse();
-
-            if !(!exists_outside_sel && exists_inside_sel && source_exists_outside_sel_in_same_mod)
-            {
-                if let Some(first_path_in_use_tree) = use_tree_str.first() {
-                    let first_path_in_use_tree_str = first_path_in_use_tree.to_string();
-                    if first_path_in_use_tree_str.contains("super") {
-                        let super_path = make::ext::ident_path("super");
-                        use_tree_str.insert(0, super_path)
-                    }
-                }
+            if use_tree_str[0].to_string().contains("super") {
+                let super_path = make::ext::ident_path("super");
+                use_tree_str.insert(0, super_path)
             }
 
             let use_ =
@@ -647,32 +621,6 @@ impl Module {
     }
 }
 
-fn check_intersection_and_push(
-    import_paths_to_be_removed: &mut Vec<TextRange>,
-    import_path: TextRange,
-) {
-    if import_paths_to_be_removed.len() > 0 {
-        // Text ranges recieved here for imports are extended to the
-        // next/previous comma which can cause intersections among them
-        // and later deletion of these can cause panics similar
-        // to reported in #11766. So to mitigate it, we
-        // check for intersection between all current members
-        // and if it exists we combine both text ranges into
-        // one
-        let r = import_paths_to_be_removed
-            .into_iter()
-            .position(|it| it.intersect(import_path).is_some());
-        match r {
-            Some(it) => {
-                import_paths_to_be_removed[it] = import_paths_to_be_removed[it].cover(import_path)
-            }
-            None => import_paths_to_be_removed.push(import_path),
-        }
-    } else {
-        import_paths_to_be_removed.push(import_path);
-    }
-}
-
 fn does_source_exists_outside_sel_in_same_mod(
     def: Definition,
     ctx: &AssistContext,
@@ -1547,38 +1495,4 @@ mod modname {
         ",
         )
     }
-
-    #[test]
-    fn test_issue_11766() {
-        //https://github.com/rust-lang/rust-analyzer/issues/11766
-        check_assist(
-            extract_module,
-            r"
-            mod x {
-                pub struct Foo;
-                pub struct Bar;
-            }
-
-            use x::{Bar, Foo};
-
-            $0type A = (Foo, Bar);$0
-        ",
-            r"
-            mod x {
-                pub struct Foo;
-                pub struct Bar;
-            }
-
-            use x::{};
-
-            mod modname {
-                use super::x::Bar;
-
-                use super::x::Foo;
-
-                pub(crate) type A = (Foo, Bar);
-            }
-        ",
-        )
-    }
 }
diff --git a/crates/ide-assists/src/handlers/merge_imports.rs b/crates/ide-assists/src/handlers/merge_imports.rs
index 946e66f1f5..705ae66617 100644
--- a/crates/ide-assists/src/handlers/merge_imports.rs
+++ b/crates/ide-assists/src/handlers/merge_imports.rs
@@ -1,6 +1,5 @@
-use either::Either;
 use ide_db::imports::merge_imports::{try_merge_imports, try_merge_trees, MergeBehavior};
-use syntax::{algo::neighbor, ast, match_ast, ted, AstNode, SyntaxElement, SyntaxNode};
+use syntax::{algo::neighbor, ast, ted, AstNode};
 
 use crate::{
     assist_context::{AssistContext, Assists},
@@ -8,8 +7,6 @@ use crate::{
     AssistId, AssistKind,
 };
 
-use Edit::*;
-
 // Assist: merge_imports
 //
 // Merges two imports with a common prefix.
@@ -23,113 +20,49 @@ use Edit::*;
 // use std::{fmt::Formatter, io};
 // ```
 pub(crate) fn merge_imports(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
-    let (target, edits) = if ctx.has_empty_selection() {
-        // Merge a neighbor
-        let tree: ast::UseTree = ctx.find_node_at_offset()?;
-        let target = tree.syntax().text_range();
-
-        let edits = if let Some(use_item) = tree.syntax().parent().and_then(ast::Use::cast) {
-            let mut neighbor = next_prev().find_map(|dir| neighbor(&use_item, dir)).into_iter();
-            use_item.try_merge_from(&mut neighbor)
-        } else {
-            let mut neighbor = next_prev().find_map(|dir| neighbor(&tree, dir)).into_iter();
-            tree.try_merge_from(&mut neighbor)
-        };
-        (target, edits?)
+    let tree: ast::UseTree = ctx.find_node_at_offset()?;
+
+    let mut imports = None;
+    let mut uses = None;
+    if let Some(use_item) = tree.syntax().parent().and_then(ast::Use::cast) {
+        let (merged, to_remove) =
+            next_prev().filter_map(|dir| neighbor(&use_item, dir)).find_map(|use_item2| {
+                try_merge_imports(&use_item, &use_item2, MergeBehavior::Crate).zip(Some(use_item2))
+            })?;
+
+        imports = Some((use_item, merged, to_remove));
     } else {
-        // Merge selected
-        let selection_range = ctx.selection_trimmed();
-        let parent_node = match ctx.covering_element() {
-            SyntaxElement::Node(n) => n,
-            SyntaxElement::Token(t) => t.parent()?,
-        };
-        let mut selected_nodes =
-            parent_node.children().filter(|it| selection_range.contains_range(it.text_range()));
-
-        let first_selected = selected_nodes.next()?;
-        let edits = match_ast! {
-            match first_selected {
-                ast::Use(use_item) => {
-                    use_item.try_merge_from(&mut selected_nodes.filter_map(ast::Use::cast))
-                },
-                ast::UseTree(use_tree) => {
-                    use_tree.try_merge_from(&mut selected_nodes.filter_map(ast::UseTree::cast))
-                },
-                _ => return None,
-            }
-        };
-        (selection_range, edits?)
+        let (merged, to_remove) =
+            next_prev().filter_map(|dir| neighbor(&tree, dir)).find_map(|use_tree| {
+                try_merge_trees(&tree, &use_tree, MergeBehavior::Crate).zip(Some(use_tree))
+            })?;
+
+        uses = Some((tree.clone(), merged, to_remove))
     };
 
+    let target = tree.syntax().text_range();
     acc.add(
         AssistId("merge_imports", AssistKind::RefactorRewrite),
         "Merge imports",
         target,
         |builder| {
-            let edits_mut: Vec<Edit> = edits
-                .into_iter()
-                .map(|it| match it {
-                    Remove(Either::Left(it)) => Remove(Either::Left(builder.make_mut(it))),
-                    Remove(Either::Right(it)) => Remove(Either::Right(builder.make_mut(it))),
-                    Replace(old, new) => Replace(builder.make_syntax_mut(old), new),
-                })
-                .collect();
-            for edit in edits_mut {
-                match edit {
-                    Remove(it) => it.as_ref().either(ast::Use::remove, ast::UseTree::remove),
-                    Replace(old, new) => ted::replace(old, new),
-                }
-            }
-        },
-    )
-}
-
-trait Merge: AstNode + Clone {
-    fn try_merge_from(self, items: &mut dyn Iterator<Item = Self>) -> Option<Vec<Edit>> {
-        let mut edits = Vec::new();
-        let mut merged = self.clone();
-        while let Some(item) = items.next() {
-            merged = merged.try_merge(&item)?;
-            edits.push(Edit::Remove(item.into_either()));
-        }
-        if !edits.is_empty() {
-            edits.push(Edit::replace(self, merged));
-            Some(edits)
-        } else {
-            None
-        }
-    }
-    fn try_merge(&self, other: &Self) -> Option<Self>;
-    fn into_either(self) -> Either<ast::Use, ast::UseTree>;
-}
+            if let Some((to_replace, replacement, to_remove)) = imports {
+                let to_replace = builder.make_mut(to_replace);
+                let to_remove = builder.make_mut(to_remove);
 
-impl Merge for ast::Use {
-    fn try_merge(&self, other: &Self) -> Option<Self> {
-        try_merge_imports(self, other, MergeBehavior::Crate)
-    }
-    fn into_either(self) -> Either<ast::Use, ast::UseTree> {
-        Either::Left(self)
-    }
-}
-
-impl Merge for ast::UseTree {
-    fn try_merge(&self, other: &Self) -> Option<Self> {
-        try_merge_trees(self, other, MergeBehavior::Crate)
-    }
-    fn into_either(self) -> Either<ast::Use, ast::UseTree> {
-        Either::Right(self)
-    }
-}
+                ted::replace(to_replace.syntax(), replacement.syntax());
+                to_remove.remove();
+            }
 
-enum Edit {
-    Remove(Either<ast::Use, ast::UseTree>),
-    Replace(SyntaxNode, SyntaxNode),
-}
+            if let Some((to_replace, replacement, to_remove)) = uses {
+                let to_replace = builder.make_mut(to_replace);
+                let to_remove = builder.make_mut(to_remove);
 
-impl Edit {
-    fn replace(old: impl AstNode, new: impl AstNode) -> Self {
-        Edit::Replace(old.syntax().clone(), new.syntax().clone())
-    }
+                ted::replace(to_replace.syntax(), replacement.syntax());
+                to_remove.remove()
+            }
+        },
+    )
 }
 
 #[cfg(test)]
@@ -521,50 +454,4 @@ use foo::{*, bar::Baz};
 ",
         );
     }
-
-    #[test]
-    fn merge_selection_uses() {
-        check_assist(
-            merge_imports,
-            r"
-use std::fmt::Error;
-$0use std::fmt::Display;
-use std::fmt::Debug;
-use std::fmt::Write;
-$0use std::fmt::Result;
-",
-            r"
-use std::fmt::Error;
-use std::fmt::{Display, Debug, Write};
-use std::fmt::Result;
-",
-        );
-    }
-
-    #[test]
-    fn merge_selection_use_trees() {
-        check_assist(
-            merge_imports,
-            r"
-use std::{
-    fmt::Error,
-    $0fmt::Display,
-    fmt::Debug,
-    fmt::Write,$0
-    fmt::Result,
-};",
-            r"
-use std::{
-    fmt::Error,
-    fmt::{Display, Debug, Write},
-    fmt::Result,
-};",
-        );
-        // FIXME: Remove redundant braces. See also unnecessary-braces diagnostic.
-        check_assist(
-            merge_imports,
-            r"use std::$0{fmt::Display, fmt::Debug}$0;",
-            r"use std::{fmt::{Display, Debug}};",
-        );
-    }
 }
diff --git a/crates/ide-assists/src/handlers/move_guard.rs b/crates/ide-assists/src/handlers/move_guard.rs
index f5e8b60ad6..5c05cb921d 100644
--- a/crates/ide-assists/src/handlers/move_guard.rs
+++ b/crates/ide-assists/src/handlers/move_guard.rs
@@ -106,10 +106,6 @@ pub(crate) fn move_arm_cond_to_match_guard(acc: &mut Assists, ctx: &AssistContex
             None
         }
     })?;
-    if ctx.offset() > if_expr.then_branch()?.syntax().text_range().start() {
-        return None;
-    }
-
     let replace_node = replace_node.unwrap_or_else(|| if_expr.syntax().clone());
     let needs_dedent = replace_node != *if_expr.syntax();
     let (conds_blocks, tail) = parse_if_chain(if_expr)?;
@@ -327,7 +323,7 @@ fn main() {
             r#"
 fn main() {
     match 92 {
-        x => if x > 10$0 { false },
+        x => if x > 10 { $0false },
         _ => true
     }
 }
@@ -461,7 +457,7 @@ fn main() {
             r#"
 fn main() {
     match 92 {
-        x => if let 62 = x $0&& true { false },
+        x => if let 62 = x && true { $0false },
         _ => true
     }
 }
@@ -484,7 +480,7 @@ fn main() {
             r#"
 fn main() {
     match 92 {
-        x => if x $0> 10 {  },
+        x => if x > 10 { $0 },
         _ => true
     }
 }
@@ -507,8 +503,8 @@ fn main() {
             r#"
 fn main() {
     match 92 {
-        x => if$0 x > 10 {
-            92;
+        x => if x > 10 {
+            92;$0
             false
         },
         _ => true
@@ -537,8 +533,8 @@ fn main() {
 fn main() {
     match 92 {
         x => {
-            if x > $010 {
-                92;
+            if x > 10 {
+                92;$0
                 false
             }
         }
@@ -567,7 +563,7 @@ fn main() {
             r#"
 fn main() {
     match 92 {
-        x => if x > $010 {
+        x => if x > 10 {$0
             false
         } else {
             true
@@ -597,7 +593,7 @@ fn main() {
 fn main() {
     match 92 {
         x => {
-            if x $0> 10 {
+            if x > 10 {$0
                 false
             } else {
                 true
@@ -626,7 +622,7 @@ fn main() {
             r#"
 fn main() {
     match 92 {
-        x => if x > $010 {  } else { },
+        x => if x > 10 { $0 } else { },
         _ => true
     }
 }
@@ -650,8 +646,8 @@ fn main() {
             r#"
 fn main() {
     match 92 {
-        x => if$0 x > 10 {
-            92;
+        x => if x > 10 {
+            92;$0
             false
         } else {
             true
@@ -683,7 +679,7 @@ fn main() {
             r#"
 fn main() {
     match 92 {
-        x => if x $0> 10 {
+        x => if x > 10 {$0
             false
         } else {
             42;
@@ -717,7 +713,7 @@ fn main() {
 fn main() {
     match 92 {
         x => {
-            if x > $010 {
+            if x > 10 {$0
                 false
             } else {
                 42;
@@ -752,7 +748,7 @@ fn main() {
     match 92 {
         3 => true,
         x => {
-            if x > $010 {
+            if x > 10 {$0
                 false
             } else {
                 92;
@@ -785,7 +781,7 @@ fn main() {
 fn main() {
     match 92 {
         3 => true,
-        x => if x > $010 {
+        x => if x > 10 {$0
             false
         } else {
             92;
@@ -817,7 +813,7 @@ fn main() {
 fn main() {
     match 92 {
         3 => true,
-        x => if x $0> 10 {
+        x => if x > 10 {$0
             false
         } else if x > 5 {
             true
@@ -853,7 +849,7 @@ fn main() {
     match 92 {
         3 => true,
         x => {
-            if x > $010 {
+            if x > 10 {$0
                 false
             } else if x > 5 {
                 true
@@ -889,7 +885,7 @@ fn main() {
 fn main() {
     match 92 {
         3 => 0,
-        x => if x $0> 10 {
+        x => if x > 10 {$0
             1
         } else if x > 5 {
             2
@@ -927,7 +923,7 @@ fn main() {
 fn main() {
     match 92 {
         3 => 0,
-        x => if x $0> 10 {
+        x => if x > 10 {$0
             1
         } else if x > 5 {
             2
@@ -964,7 +960,7 @@ fn main() {
 fn main() {
     match 92 {
         3 => 0,
-        x => if x > $010 {
+        x => if x > 10 {$0
             1
         } else if x > 5 {
             2
diff --git a/crates/ide-completion/src/completions.rs b/crates/ide-completion/src/completions.rs
index b6358d4f40..b076358443 100644
--- a/crates/ide-completion/src/completions.rs
+++ b/crates/ide-completion/src/completions.rs
@@ -16,7 +16,6 @@ pub(crate) mod pattern;
 pub(crate) mod postfix;
 pub(crate) mod record;
 pub(crate) mod snippet;
-pub(crate) mod trait_impl;
 pub(crate) mod r#type;
 pub(crate) mod use_;
 pub(crate) mod vis;
diff --git a/crates/ide-completion/src/completions/dot.rs b/crates/ide-completion/src/completions/dot.rs
index 1e0b771166..a11652ca30 100644
--- a/crates/ide-completion/src/completions/dot.rs
+++ b/crates/ide-completion/src/completions/dot.rs
@@ -46,12 +46,14 @@ fn complete_undotted_self(acc: &mut Completions, ctx: &CompletionContext) {
         return;
     }
     match ctx.path_context() {
-        Some(PathCompletionCtx {
-            is_absolute_path: false,
-            qualifier: None,
-            kind: PathKind::Expr { .. },
-            ..
-        }) if !ctx.is_path_disallowed() => {}
+        Some(
+            path_ctx @ PathCompletionCtx {
+                is_absolute_path: false,
+                qualifier: None,
+                kind: PathKind::Expr { .. },
+                ..
+            },
+        ) if path_ctx.is_trivial_path() && ctx.qualifier_ctx.none() => {}
         _ => return,
     }
 
diff --git a/crates/ide-completion/src/completions/expr.rs b/crates/ide-completion/src/completions/expr.rs
index 23f47523d6..7c3296a0b3 100644
--- a/crates/ide-completion/src/completions/expr.rs
+++ b/crates/ide-completion/src/completions/expr.rs
@@ -11,32 +11,38 @@ use crate::{
 
 pub(crate) fn complete_expr_path(acc: &mut Completions, ctx: &CompletionContext) {
     let _p = profile::span("complete_expr_path");
-    if ctx.is_path_disallowed() {
-        return;
-    }
 
-    let (is_absolute_path, qualifier, in_block_expr, in_loop_body, is_func_update, after_if_expr) =
-        match ctx.nameref_ctx() {
-            Some(NameRefContext {
-                path_ctx:
-                    Some(PathCompletionCtx {
-                        kind: PathKind::Expr { in_block_expr, in_loop_body, after_if_expr },
-                        is_absolute_path,
-                        qualifier,
-                        ..
-                    }),
-                record_expr,
-                ..
-            }) => (
-                *is_absolute_path,
-                qualifier,
-                *in_block_expr,
-                *in_loop_body,
-                record_expr.as_ref().map_or(false, |&(_, it)| it),
-                *after_if_expr,
-            ),
-            _ => return,
-        };
+    let (
+        is_absolute_path,
+        qualifier,
+        in_block_expr,
+        in_loop_body,
+        is_func_update,
+        after_if_expr,
+        wants_mut_token,
+    ) = match ctx.nameref_ctx() {
+        Some(NameRefContext {
+            path_ctx:
+                Some(PathCompletionCtx {
+                    kind:
+                        PathKind::Expr { in_block_expr, in_loop_body, after_if_expr, ref_expr_parent },
+                    is_absolute_path,
+                    qualifier,
+                    ..
+                }),
+            record_expr,
+            ..
+        }) if ctx.qualifier_ctx.none() => (
+            *is_absolute_path,
+            qualifier,
+            *in_block_expr,
+            *in_loop_body,
+            record_expr.as_ref().map_or(false, |&(_, it)| it),
+            *after_if_expr,
+            ref_expr_parent.as_ref().map(|it| it.mut_token().is_none()).unwrap_or(false),
+        ),
+        _ => return,
+    };
 
     let scope_def_applicable = |def| {
         use hir::{GenericParam::*, ModuleDef::*};
@@ -164,12 +170,43 @@ pub(crate) fn complete_expr_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(hir::Adt::Enum(e)) =
+            if let Some(adt) =
                 ctx.expected_type.as_ref().and_then(|ty| ty.strip_references().as_adt())
             {
-                super::enum_variants_with_paths(acc, ctx, e, |acc, ctx, variant, path| {
-                    acc.add_qualified_enum_variant(ctx, variant, path)
-                });
+                let self_ty =
+                    (|| ctx.sema.to_def(ctx.impl_def.as_ref()?)?.self_ty(ctx.db).as_adt())();
+                let complete_self = self_ty == Some(adt);
+
+                match adt {
+                    hir::Adt::Struct(strukt) => {
+                        let path = ctx
+                            .module
+                            .find_use_path(ctx.db, hir::ModuleDef::from(strukt))
+                            .filter(|it| it.len() > 1);
+
+                        acc.add_struct_literal(ctx, strukt, path, None);
+
+                        if complete_self {
+                            acc.add_struct_literal(ctx, strukt, None, Some(hir::known::SELF_TYPE));
+                        }
+                    }
+                    hir::Adt::Union(un) => {
+                        let path = ctx
+                            .module
+                            .find_use_path(ctx.db, hir::ModuleDef::from(un))
+                            .filter(|it| it.len() > 1);
+
+                        acc.add_union_literal(ctx, un, path, None);
+                        if complete_self {
+                            acc.add_union_literal(ctx, un, None, Some(hir::known::SELF_TYPE));
+                        }
+                    }
+                    hir::Adt::Enum(e) => {
+                        super::enum_variants_with_paths(acc, ctx, e, |acc, ctx, variant, path| {
+                            acc.add_qualified_enum_variant(ctx, variant, path)
+                        });
+                    }
+                }
             }
             ctx.process_all_names(&mut |name, def| {
                 if scope_def_applicable(def) {
@@ -180,20 +217,18 @@ pub(crate) fn complete_expr_path(acc: &mut Completions, ctx: &CompletionContext)
             if !is_func_update {
                 let mut add_keyword = |kw, snippet| acc.add_keyword_snippet(ctx, kw, snippet);
 
-                if ctx.expects_expression() {
-                    if !in_block_expr {
-                        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}");
-                    add_keyword("loop", "loop {\n    $0\n}");
-                    add_keyword("if", "if $1 {\n    $0\n}");
-                    add_keyword("if let", "if let $1 = $2 {\n    $0\n}");
-                    add_keyword("for", "for $1 in $2 {\n    $0\n}");
-                    add_keyword("true", "true");
-                    add_keyword("false", "false");
+                if !in_block_expr {
+                    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}");
+                add_keyword("loop", "loop {\n    $0\n}");
+                add_keyword("if", "if $1 {\n    $0\n}");
+                add_keyword("if let", "if let $1 = $2 {\n    $0\n}");
+                add_keyword("for", "for $1 in $2 {\n    $0\n}");
+                add_keyword("true", "true");
+                add_keyword("false", "false");
 
                 if ctx.previous_token_is(T![if])
                     || ctx.previous_token_is(T![while])
@@ -207,7 +242,7 @@ pub(crate) fn complete_expr_path(acc: &mut Completions, ctx: &CompletionContext)
                     add_keyword("else if", "else if $1 {\n    $0\n}");
                 }
 
-                if ctx.expects_ident_ref_expr() {
+                if wants_mut_token {
                     add_keyword("mut", "mut ");
                 }
 
diff --git a/crates/ide-completion/src/completions/flyimport.rs b/crates/ide-completion/src/completions/flyimport.rs
index 22068096ba..901f7519d2 100644
--- a/crates/ide-completion/src/completions/flyimport.rs
+++ b/crates/ide-completion/src/completions/flyimport.rs
@@ -8,7 +8,7 @@ use itertools::Itertools;
 use syntax::{AstNode, SyntaxNode, T};
 
 use crate::{
-    context::{CompletionContext, PathKind},
+    context::{CompletionContext, NameRefContext, PathCompletionCtx, PathKind, PatternContext},
     patterns::ImmediateLocation,
     render::{render_resolution_with_import, RenderContext},
 };
@@ -110,16 +110,26 @@ pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext)
     if !ctx.config.enable_imports_on_the_fly {
         return None;
     }
-    if matches!(ctx.path_kind(), Some(PathKind::Vis { .. } | PathKind::Use | PathKind::Item { .. }))
-        || ctx.is_path_disallowed()
-    {
-        return None;
-    }
-    // FIXME: This should be encoded in a different way
-    if ctx.pattern_ctx.is_none() && ctx.path_context().is_none() && !ctx.has_dot_receiver() {
-        // completion inside `ast::Name` of a item declaration
-        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,
+        None if matches!(ctx.pattern_ctx, Some(PatternContext { record_pat: None, .. })) => {
+            Some(&PathKind::Pat)
+        }
+        _ => return None,
+    };
+
     let potential_import_name = {
         let token_kind = ctx.token.kind();
         if matches!(token_kind, T![.] | T![::]) {
@@ -138,18 +148,10 @@ pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext)
         return None;
     }
 
-    let path_kind = match ctx.path_kind() {
-        Some(kind) => Some(kind),
-        None if ctx.pattern_ctx.is_some() => Some(PathKind::Pat),
-        None => None,
-    };
     let ns_filter = |import: &LocatedImport| {
         let path_kind = match path_kind {
-            Some(path_kind) => path_kind,
-            None => match import.original_item {
-                ItemInNs::Macros(mac) => return mac.is_fn_like(ctx.db),
-                _ => return true,
-            },
+            Some(it) => it,
+            None => return true,
         };
         match (path_kind, import.original_item) {
             // Aren't handled in flyimport
diff --git a/crates/ide-completion/src/completions/item_list.rs b/crates/ide-completion/src/completions/item_list.rs
index 287cf46f2e..d44bf0a6ab 100644
--- a/crates/ide-completion/src/completions/item_list.rs
+++ b/crates/ide-completion/src/completions/item_list.rs
@@ -6,88 +6,42 @@ use crate::{
     CompletionContext, Completions,
 };
 
+mod trait_impl;
+
 pub(crate) fn complete_item_list(acc: &mut Completions, ctx: &CompletionContext) {
     let _p = profile::span("complete_item_list");
 
-    let (&is_absolute_path, path_qualifier, kind) = match ctx.path_context() {
-        Some(PathCompletionCtx {
-            kind: PathKind::Item { kind },
-            is_absolute_path,
-            qualifier,
-            ..
-        }) => (is_absolute_path, qualifier, Some(kind)),
-        Some(PathCompletionCtx {
-            kind: PathKind::Expr { in_block_expr: true, .. },
-            is_absolute_path,
-            qualifier,
-            ..
-        }) => (is_absolute_path, qualifier, None),
+    if let Some(_) = ctx.name_ctx() {
+        trait_impl::complete_trait_impl(acc, ctx);
+        return;
+    }
+
+    let (&is_absolute_path, path_qualifier, kind, is_trivial_path) = match ctx.path_context() {
+        Some(
+            ctx @ PathCompletionCtx {
+                kind: PathKind::Item { kind },
+                is_absolute_path,
+                qualifier,
+                ..
+            },
+        ) => (is_absolute_path, qualifier, Some(kind), ctx.is_trivial_path()),
+        Some(
+            ctx @ PathCompletionCtx {
+                kind: PathKind::Expr { in_block_expr: true, .. },
+                is_absolute_path,
+                qualifier,
+                ..
+            },
+        ) => (is_absolute_path, qualifier, None, ctx.is_trivial_path()),
         _ => return,
     };
-    let mut add_keyword = |kw, snippet| acc.add_keyword_snippet(ctx, kw, snippet);
-
-    let in_item_list = matches!(kind, Some(ItemListKind::SourceFile | ItemListKind::Module) | None);
-    let in_assoc_non_trait_impl = matches!(kind, Some(ItemListKind::Impl | ItemListKind::Trait));
-    let in_extern_block = matches!(kind, Some(ItemListKind::ExternBlock));
-    let in_trait = matches!(kind, Some(ItemListKind::Trait));
-    let in_trait_impl = matches!(kind, Some(ItemListKind::TraitImpl));
-    let in_inherent_impl = matches!(kind, Some(ItemListKind::Impl));
-    let no_qualifiers = ctx.qualifier_ctx.vis_node.is_none();
-    let in_block = matches!(kind, None);
-
-    'block: loop {
-        if ctx.is_non_trivial_path() {
-            break 'block;
-        }
-        if !in_trait_impl {
-            if ctx.qualifier_ctx.unsafe_tok.is_some() {
-                if in_item_list || in_assoc_non_trait_impl {
-                    add_keyword("fn", "fn $1($2) {\n    $0\n}");
-                }
-                if in_item_list {
-                    add_keyword("trait", "trait $1 {\n    $0\n}");
-                    if no_qualifiers {
-                        add_keyword("impl", "impl $1 {\n    $0\n}");
-                    }
-                }
-                break 'block;
-            }
 
-            if in_item_list {
-                add_keyword("enum", "enum $1 {\n    $0\n}");
-                add_keyword("mod", "mod $0");
-                add_keyword("static", "static $0");
-                add_keyword("struct", "struct $0");
-                add_keyword("trait", "trait $1 {\n    $0\n}");
-                add_keyword("union", "union $1 {\n    $0\n}");
-                add_keyword("use", "use $0");
-                if no_qualifiers {
-                    add_keyword("impl", "impl $1 {\n    $0\n}");
-                }
-            }
-
-            if !in_trait && !in_block && no_qualifiers {
-                add_keyword("pub(crate)", "pub(crate)");
-                add_keyword("pub(super)", "pub(super)");
-                add_keyword("pub", "pub");
-            }
-
-            if in_extern_block {
-                add_keyword("fn", "fn $1($2);");
-            } else {
-                if !in_inherent_impl {
-                    if !in_trait {
-                        add_keyword("extern", "extern $0");
-                    }
-                    add_keyword("type", "type $0");
-                }
+    if matches!(kind, Some(ItemListKind::TraitImpl)) {
+        trait_impl::complete_trait_impl(acc, ctx);
+    }
 
-                add_keyword("fn", "fn $1($2) {\n    $0\n}");
-                add_keyword("unsafe", "unsafe");
-                add_keyword("const", "const $0");
-            }
-        }
-        break 'block;
+    if is_trivial_path {
+        add_keywords(acc, ctx, kind);
     }
 
     if kind.is_none() {
@@ -121,3 +75,65 @@ pub(crate) fn complete_item_list(acc: &mut Completions, ctx: &CompletionContext)
         None => {}
     }
 }
+
+fn add_keywords(acc: &mut Completions, ctx: &CompletionContext, kind: Option<&ItemListKind>) {
+    let mut add_keyword = |kw, snippet| acc.add_keyword_snippet(ctx, kw, snippet);
+
+    let in_item_list = matches!(kind, Some(ItemListKind::SourceFile | ItemListKind::Module) | None);
+    let in_assoc_non_trait_impl = matches!(kind, Some(ItemListKind::Impl | ItemListKind::Trait));
+    let in_extern_block = matches!(kind, Some(ItemListKind::ExternBlock));
+    let in_trait = matches!(kind, Some(ItemListKind::Trait));
+    let in_trait_impl = matches!(kind, Some(ItemListKind::TraitImpl));
+    let in_inherent_impl = matches!(kind, Some(ItemListKind::Impl));
+    let no_qualifiers = ctx.qualifier_ctx.vis_node.is_none();
+    let in_block = matches!(kind, None);
+
+    if !in_trait_impl {
+        if ctx.qualifier_ctx.unsafe_tok.is_some() {
+            if in_item_list || in_assoc_non_trait_impl {
+                add_keyword("fn", "fn $1($2) {\n    $0\n}");
+            }
+            if in_item_list {
+                add_keyword("trait", "trait $1 {\n    $0\n}");
+                if no_qualifiers {
+                    add_keyword("impl", "impl $1 {\n    $0\n}");
+                }
+            }
+            return;
+        }
+
+        if in_item_list {
+            add_keyword("enum", "enum $1 {\n    $0\n}");
+            add_keyword("mod", "mod $0");
+            add_keyword("static", "static $0");
+            add_keyword("struct", "struct $0");
+            add_keyword("trait", "trait $1 {\n    $0\n}");
+            add_keyword("union", "union $1 {\n    $0\n}");
+            add_keyword("use", "use $0");
+            if no_qualifiers {
+                add_keyword("impl", "impl $1 {\n    $0\n}");
+            }
+        }
+
+        if !in_trait && !in_block && no_qualifiers {
+            add_keyword("pub(crate)", "pub(crate)");
+            add_keyword("pub(super)", "pub(super)");
+            add_keyword("pub", "pub");
+        }
+
+        if in_extern_block {
+            add_keyword("fn", "fn $1($2);");
+        } else {
+            if !in_inherent_impl {
+                if !in_trait {
+                    add_keyword("extern", "extern $0");
+                }
+                add_keyword("type", "type $0");
+            }
+
+            add_keyword("fn", "fn $1($2) {\n    $0\n}");
+            add_keyword("unsafe", "unsafe");
+            add_keyword("const", "const $0");
+        }
+    }
+}
diff --git a/crates/ide-completion/src/completions/trait_impl.rs b/crates/ide-completion/src/completions/item_list/trait_impl.rs
similarity index 88%
rename from crates/ide-completion/src/completions/trait_impl.rs
rename to crates/ide-completion/src/completions/item_list/trait_impl.rs
index 54bd77526e..8a2bbae73f 100644
--- a/crates/ide-completion/src/completions/trait_impl.rs
+++ b/crates/ide-completion/src/completions/item_list/trait_impl.rs
@@ -43,6 +43,10 @@ use syntax::{
 use text_edit::TextEdit;
 
 use crate::{
+    context::{
+        IdentContext, ItemListKind, NameContext, NameKind, NameRefContext, PathCompletionCtx,
+        PathKind,
+    },
     CompletionContext, CompletionItem, CompletionItemKind, CompletionRelevance, Completions,
 };
 
@@ -54,7 +58,6 @@ enum ImplCompletionKind {
     Const,
 }
 
-// FIXME: Make this a submodule of [`item_list`]
 pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext) {
     if let Some((kind, replacement_range, impl_def)) = completion_match(ctx) {
         if let Some(hir_impl) = ctx.sema.to_def(&impl_def) {
@@ -77,73 +80,49 @@ pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext
     }
 }
 
-// FIXME: This should be lifted out so that we can do proper smart item keyword completions
 fn completion_match(ctx: &CompletionContext) -> Option<(ImplCompletionKind, TextRange, ast::Impl)> {
-    let token = ctx.token.clone();
-
-    // For keyword without name like `impl .. { fn $0 }`, the current position is inside
-    // the whitespace token, which is outside `FN` syntax node.
-    // We need to follow the previous token in this case.
-    let mut token_before_ws = token.clone();
-    if token.kind() == SyntaxKind::WHITESPACE {
-        token_before_ws = token.prev_token()?;
-    }
-
-    let parent_kind = token_before_ws.parent().map_or(SyntaxKind::EOF, |it| it.kind());
-    if token.parent().map(|n| n.kind()) == Some(SyntaxKind::ASSOC_ITEM_LIST)
-        && matches!(
-            token_before_ws.kind(),
-            SyntaxKind::SEMICOLON | SyntaxKind::R_CURLY | SyntaxKind::L_CURLY
-        )
-    {
-        let impl_def = ast::Impl::cast(token.parent()?.parent()?)?;
-        let kind = ImplCompletionKind::All;
-        let replacement_range = TextRange::empty(ctx.position.offset);
-        Some((kind, replacement_range, impl_def))
-    } else {
-        let impl_item_offset = match token_before_ws.kind() {
-            // `impl .. { const $0 }`
-            // ERROR      0
-            //   CONST_KW <- *
-            T![const] => 0,
-            // `impl .. { fn/type $0 }`
-            // FN/TYPE_ALIAS  0
-            //   FN_KW        <- *
-            T![fn] | T![type] => 0,
-            // `impl .. { fn/type/const foo$0 }`
-            // FN/TYPE_ALIAS/CONST  1
-            //  NAME                0
-            //    IDENT             <- *
-            SyntaxKind::IDENT if parent_kind == SyntaxKind::NAME => 1,
-            // `impl .. { foo$0 }`
-            // MACRO_CALL       3
-            //  PATH            2
-            //    PATH_SEGMENT  1
-            //      NAME_REF    0
-            //        IDENT     <- *
-            SyntaxKind::IDENT if parent_kind == SyntaxKind::NAME_REF => 3,
-            _ => return None,
-        };
-
-        let impl_item = token_before_ws.ancestors().nth(impl_item_offset)?;
-        // Must directly belong to an impl block.
-        // IMPL
-        //   ASSOC_ITEM_LIST
-        //     <item>
-        let impl_def = ast::Impl::cast(impl_item.parent()?.parent()?)?;
-        let kind = match impl_item.kind() {
-            // `impl ... { const $0 fn/type/const }`
-            _ if token_before_ws.kind() == T![const] => ImplCompletionKind::Const,
-            SyntaxKind::CONST | SyntaxKind::ERROR => ImplCompletionKind::Const,
-            SyntaxKind::TYPE_ALIAS => ImplCompletionKind::TypeAlias,
-            SyntaxKind::FN => ImplCompletionKind::Fn,
-            SyntaxKind::MACRO_CALL => ImplCompletionKind::All,
-            _ => return None,
-        };
-
-        let replacement_range = replacement_range(ctx, &impl_item);
-
-        Some((kind, replacement_range, impl_def))
+    match &ctx.ident_ctx {
+        IdentContext::Name(NameContext { name, kind, .. }) => {
+            let kind = match kind {
+                NameKind::Const => ImplCompletionKind::Const,
+                NameKind::Function => ImplCompletionKind::Fn,
+                NameKind::TypeAlias => ImplCompletionKind::TypeAlias,
+                _ => return None,
+            };
+            let token = ctx.token.clone();
+            let item = match name {
+                Some(name) => name.syntax().parent(),
+                None => {
+                    if token.kind() == SyntaxKind::WHITESPACE { token.prev_token()? } else { token }
+                        .parent()
+                }
+            }?;
+            Some((
+                kind,
+                replacement_range(ctx, &item),
+                // item -> ASSOC_ITEM_LIST -> IMPL
+                ast::Impl::cast(item.parent()?.parent()?)?,
+            ))
+        }
+        IdentContext::NameRef(NameRefContext {
+            nameref,
+            path_ctx:
+                Some(
+                    path_ctx @ PathCompletionCtx {
+                        kind: PathKind::Item { kind: ItemListKind::TraitImpl },
+                        ..
+                    },
+                ),
+            ..
+        }) if path_ctx.is_trivial_path() => Some((
+            ImplCompletionKind::All,
+            match nameref {
+                Some(name) => name.syntax().text_range(),
+                None => TextRange::empty(ctx.position.offset),
+            },
+            ctx.impl_def.clone()?,
+        )),
+        _ => None,
     }
 }
 
diff --git a/crates/ide-completion/src/completions/keyword.rs b/crates/ide-completion/src/completions/keyword.rs
index e870ecc229..65fa119178 100644
--- a/crates/ide-completion/src/completions/keyword.rs
+++ b/crates/ide-completion/src/completions/keyword.rs
@@ -8,11 +8,7 @@ use crate::{context::NameRefContext, 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, .. })
-            if !ctx.is_non_trivial_path() =>
-        {
-            item
-        }
+        Some(NameRefContext { keyword: Some(item), record_expr: None, .. }) => item,
         _ => return,
     };
 
diff --git a/crates/ide-completion/src/completions/record.rs b/crates/ide-completion/src/completions/record.rs
index 6717ca0a0e..65805dba1c 100644
--- a/crates/ide-completion/src/completions/record.rs
+++ b/crates/ide-completion/src/completions/record.rs
@@ -71,43 +71,6 @@ pub(crate) fn complete_record(acc: &mut Completions, ctx: &CompletionContext) ->
     Some(())
 }
 
-pub(crate) fn complete_record_literal(
-    acc: &mut Completions,
-    ctx: &CompletionContext,
-) -> Option<()> {
-    if !ctx.expects_expression() {
-        return None;
-    }
-
-    match ctx.expected_type.as_ref()?.as_adt()? {
-        hir::Adt::Struct(strukt) if ctx.path_qual().is_none() => {
-            let path = ctx
-                .module
-                .find_use_path(ctx.db, hir::ModuleDef::from(strukt))
-                .filter(|it| it.len() > 1);
-
-            acc.add_struct_literal(ctx, strukt, path, None);
-
-            let impl_ = ctx.impl_def.as_ref()?;
-            let impl_adt = ctx.sema.to_def(impl_)?.self_ty(ctx.db).as_adt()?;
-            if hir::Adt::Struct(strukt) == impl_adt {
-                acc.add_struct_literal(ctx, strukt, None, Some(hir::known::SELF_TYPE));
-            }
-        }
-        hir::Adt::Union(un) if ctx.path_qual().is_none() => {
-            let path = ctx
-                .module
-                .find_use_path(ctx.db, hir::ModuleDef::from(un))
-                .filter(|it| it.len() > 1);
-
-            acc.add_union_literal(ctx, un, path, None);
-        }
-        _ => {}
-    };
-
-    Some(())
-}
-
 #[cfg(test)]
 mod tests {
     use crate::tests::check_edit;
diff --git a/crates/ide-completion/src/completions/type.rs b/crates/ide-completion/src/completions/type.rs
index bc8c070c14..9cf0b87ad6 100644
--- a/crates/ide-completion/src/completions/type.rs
+++ b/crates/ide-completion/src/completions/type.rs
@@ -13,9 +13,6 @@ use crate::{
 
 pub(crate) fn complete_type_path(acc: &mut Completions, ctx: &CompletionContext) {
     let _p = profile::span("complete_type_path");
-    if ctx.is_path_disallowed() {
-        return;
-    }
 
     let (&is_absolute_path, qualifier) = match ctx.path_context() {
         Some(PathCompletionCtx {
diff --git a/crates/ide-completion/src/context.rs b/crates/ide-completion/src/context.rs
index 6068a9eb32..02307def9e 100644
--- a/crates/ide-completion/src/context.rs
+++ b/crates/ide-completion/src/context.rs
@@ -43,12 +43,13 @@ pub(crate) enum Visible {
     No,
 }
 
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+#[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,
@@ -101,8 +102,6 @@ pub(crate) struct PathCompletionCtx {
     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,
@@ -110,6 +109,23 @@ pub(crate) struct PathCompletionCtx {
     pub(super) has_type_args: bool,
 }
 
+impl PathCompletionCtx {
+    pub(super) fn is_trivial_path(&self) -> bool {
+        matches!(
+            self,
+            PathCompletionCtx {
+                has_call_parens: false,
+                has_macro_bang: false,
+                is_absolute_path: false,
+                qualifier: None,
+                parent: None,
+                has_type_args: false,
+                ..
+            }
+        )
+    }
+}
+
 #[derive(Debug)]
 pub(crate) struct PathQualifierCtx {
     pub(crate) path: ast::Path,
@@ -341,47 +357,14 @@ impl<'a> CompletionContext<'a> {
         matches!(self.completion_location, Some(ImmediateLocation::GenericArgList(_)))
     }
 
-    pub(crate) fn expects_ident_ref_expr(&self) -> bool {
-        matches!(self.completion_location, Some(ImmediateLocation::RefExpr))
-    }
-
-    // FIXME: This shouldn't exist
-    pub(crate) fn is_path_disallowed(&self) -> bool {
-        !self.qualifier_ctx.none()
-            || (matches!(self.name_ctx(), Some(NameContext { .. })) && self.pattern_ctx.is_none())
-            || matches!(self.pattern_ctx, Some(PatternContext { record_pat: Some(_), .. }))
-            || matches!(
-                self.nameref_ctx(),
-                Some(NameRefContext { record_expr: Some((_, false)), .. })
-            )
-    }
-
     pub(crate) fn path_context(&self) -> Option<&PathCompletionCtx> {
         self.nameref_ctx().and_then(|ctx| ctx.path_ctx.as_ref())
     }
 
-    pub(crate) fn expects_expression(&self) -> bool {
-        matches!(self.path_context(), Some(PathCompletionCtx { kind: PathKind::Expr { .. }, .. }))
-    }
-
-    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().and_then(|it| it.qualifier.as_ref().map(|it| &it.path))
     }
 
-    pub(crate) fn path_kind(&self) -> Option<PathKind> {
-        self.path_context().map(|it| it.kind)
-    }
-
     /// 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
@@ -872,7 +855,7 @@ impl<'a> CompletionContext<'a> {
                 find_node_at_offset(&file_with_fake_ident, offset)
             {
                 let parent = name_ref.syntax().parent()?;
-                let (mut nameref_ctx, _) =
+                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 {
                     path_ctx.kind = PathKind::Derive;
@@ -920,13 +903,23 @@ impl<'a> CompletionContext<'a> {
         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);
+            .take_while(|it| it.kind() != SOURCE_FILE)
+            .filter_map(ast::Item::cast)
+            .take(2)
+            .find_map(|it| match it {
+                ast::Item::Impl(impl_) => Some(impl_),
+                _ => None,
+            });
         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);
+            .filter_map(ast::Item::cast)
+            .take(2)
+            .find_map(|it| match it {
+                ast::Item::Fn(fn_) => Some(fn_),
+                _ => None,
+            });
 
         match name_like {
             ast::NameLike::Lifetime(lifetime) => {
@@ -938,50 +931,10 @@ impl<'a> CompletionContext<'a> {
             }
             ast::NameLike::NameRef(name_ref) => {
                 let parent = name_ref.syntax().parent()?;
-                let (nameref_ctx, pat_ctx) =
+                let (nameref_ctx, pat_ctx, qualifier_ctx) =
                     Self::classify_name_ref(&self.sema, &original_file, name_ref, parent.clone());
 
-                // Extract qualifiers
-                if let Some(path_ctx) = &nameref_ctx.path_ctx {
-                    if path_ctx.qualifier.is_none() {
-                        let top = match path_ctx.kind {
-                            PathKind::Expr { in_block_expr: true, .. } => parent
-                                .ancestors()
-                                .find(|it| ast::PathExpr::can_cast(it.kind()))
-                                .and_then(|p| {
-                                    let parent = p.parent()?;
-                                    if ast::StmtList::can_cast(parent.kind()) {
-                                        Some(p)
-                                    } else if ast::ExprStmt::can_cast(parent.kind()) {
-                                        Some(parent)
-                                    } else {
-                                        None
-                                    }
-                                }),
-                            PathKind::Item { .. } => {
-                                parent.ancestors().find(|it| ast::MacroCall::can_cast(it.kind()))
-                            }
-                            _ => None,
-                        };
-                        if let Some(top) = top {
-                            if let Some(NodeOrToken::Node(error_node)) =
-                                syntax::algo::non_trivia_sibling(
-                                    top.into(),
-                                    syntax::Direction::Prev,
-                                )
-                            {
-                                if error_node.kind() == SyntaxKind::ERROR {
-                                    self.qualifier_ctx.unsafe_tok = error_node
-                                        .children_with_tokens()
-                                        .filter_map(NodeOrToken::into_token)
-                                        .find(|it| it.kind() == T![unsafe]);
-                                    self.qualifier_ctx.vis_node =
-                                        error_node.children().find_map(ast::Visibility::cast);
-                                }
-                            }
-                        }
-                    }
-                }
+                self.qualifier_ctx = qualifier_ctx;
                 self.ident_ctx = IdentContext::NameRef(nameref_ctx);
                 self.pattern_ctx = pat_ctx;
             }
@@ -1070,40 +1023,44 @@ impl<'a> CompletionContext<'a> {
         original_file: &SyntaxNode,
         name_ref: ast::NameRef,
         parent: SyntaxNode,
-    ) -> (NameRefContext, Option<PatternContext>) {
+    ) -> (NameRefContext, Option<PatternContext>, QualifierCtx) {
         let nameref = find_node_at_offset(&original_file, name_ref.syntax().text_range().start());
 
-        let mut nameref_ctx = NameRefContext {
-            dot_access: None,
-            path_ctx: None,
-            nameref,
-            record_expr: None,
-            keyword: None,
-        };
+        let mut res = (
+            NameRefContext {
+                dot_access: None,
+                path_ctx: None,
+                nameref,
+                record_expr: None,
+                keyword: 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 =
                 find_node_in_file_compensated(original_file, &record_field.parent_record_lit())
                     .zip(Some(false));
-            return (nameref_ctx, None);
+            return res;
         }
         if let Some(record_field) = ast::RecordPatField::for_field_name_ref(&name_ref) {
-            let pat_ctx =
-                pattern_context_for(original_file, record_field.parent_record_pat().clone().into());
-            return (
-                nameref_ctx,
-                Some(PatternContext {
-                    param_ctx: None,
-                    has_type_ascription: false,
-                    ref_token: None,
-                    mut_token: None,
-                    record_pat: find_node_in_file_compensated(
-                        original_file,
-                        &record_field.parent_record_pat(),
-                    ),
-                    ..pat_ctx
-                }),
-            );
+            *pattern_ctx = Some(PatternContext {
+                param_ctx: None,
+                has_type_ascription: false,
+                ref_token: None,
+                mut_token: None,
+                record_pat: find_node_in_file_compensated(
+                    original_file,
+                    &record_field.parent_record_pat(),
+                ),
+                ..pattern_context_for(
+                    original_file,
+                    record_field.parent_record_pat().clone().into(),
+                )
+            });
+            return res;
         }
 
         let segment = match_ast! {
@@ -1123,7 +1080,7 @@ impl<'a> CompletionContext<'a> {
                         kind: DotAccessKind::Field { receiver_is_ambiguous_float_literal },
                         receiver
                     });
-                    return (nameref_ctx, None);
+                    return res;
                 },
                 ast::MethodCallExpr(method) => {
                     let receiver = find_in_original_file(method.receiver(), original_file);
@@ -1132,9 +1089,9 @@ impl<'a> CompletionContext<'a> {
                         kind: DotAccessKind::Method { has_parens: method.arg_list().map_or(false, |it| it.l_paren_token().is_some()) },
                         receiver
                     });
-                    return (nameref_ctx, None);
+                    return res;
                 },
-                _ => return (nameref_ctx, None),
+                _ => return res,
             }
         };
 
@@ -1148,7 +1105,6 @@ impl<'a> CompletionContext<'a> {
             kind: PathKind::Item { kind: ItemListKind::SourceFile },
             has_type_args: false,
         };
-        let mut pat_ctx = None;
 
         let is_in_block = |it: &SyntaxNode| {
             it.parent()
@@ -1205,9 +1161,9 @@ impl<'a> CompletionContext<'a> {
             None
         };
 
-        let kind = path.syntax().ancestors().find_map(|it| {
-            // using Option<Option<PathKind>> as extra controlflow
-            let kind = match_ast! {
+        // 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()))
@@ -1217,7 +1173,7 @@ impl<'a> CompletionContext<'a> {
                             if ast::ExprStmt::can_cast(p.kind()) {
                                 if let Some(kind) = inbetween_body_and_decl_check(p) {
                                     nameref_ctx.keyword = Some(kind);
-                                    return Some(None);
+                                    return None;
                                 }
                             }
                         }
@@ -1228,27 +1184,29 @@ impl<'a> CompletionContext<'a> {
                         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);
 
-                        Some(PathKind::Expr { in_block_expr, in_loop_body, after_if_expr })
+                        Some(PathKind::Expr { in_block_expr, in_loop_body, after_if_expr, ref_expr_parent })
                     },
                     ast::TupleStructPat(it) => {
                         path_ctx.has_call_parens = true;
-                        pat_ctx = Some(pattern_context_for(original_file, it.into()));
+                        *pattern_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()));
+                        *pattern_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()));
+                        *pattern_ctx = Some(pattern_context_for(original_file, it.into()));
                         Some(PathKind::Pat)
                     },
                     ast::MacroCall(it) => {
                         if let Some(kind) = inbetween_body_and_decl_check(it.syntax().clone()) {
                             nameref_ctx.keyword = Some(kind);
-                            return Some(None);
+                            return None;
                         }
 
                         path_ctx.has_macro_bang = it.excl_token().is_some();
@@ -1266,21 +1224,23 @@ impl<'a> CompletionContext<'a> {
                                         } else {
                                             ItemListKind::Impl
                                         },
-                                        _ => return Some(None)
+                                        _ => return None
                                     }
                                 },
-                                None => return Some(None),
+                                None => return 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| {
+                               return 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());
                                     let after_if_expr = after_if_expr(it.syntax().clone());
                                     fill_record_expr(it.syntax());
-                                    PathKind::Expr { in_block_expr, in_loop_body, after_if_expr }
-                                }));
+                                    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 }
+                                });
                             },
                         }
                     },
@@ -1302,30 +1262,14 @@ impl<'a> CompletionContext<'a> {
                     })(),
                     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_ast! {
-                            match (it.syntax().parent()?) {
-                                ast::Trait(_) => ItemListKind::Trait,
-                                ast::Impl(it) => if it.trait_().is_some() {
-                                    ItemListKind::TraitImpl
-                                } else {
-                                    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();
+            }
+        });
+
         match kind {
             Some(kind) => path_ctx.kind = kind,
-            None => return (nameref_ctx, pat_ctx),
+            None => return res,
         }
         path_ctx.has_type_args = segment.generic_arg_list().is_some();
 
@@ -1367,8 +1311,62 @@ impl<'a> CompletionContext<'a> {
                 path_ctx.is_absolute_path = true;
             }
         }
+
+        if path_ctx.is_trivial_path() {
+            // fetch the full expression that may have qualifiers attached to it
+            let top_node = match path_ctx.kind {
+                PathKind::Expr { in_block_expr: true, .. } => {
+                    parent.ancestors().find(|it| ast::PathExpr::can_cast(it.kind())).and_then(|p| {
+                        let parent = p.parent()?;
+                        if ast::StmtList::can_cast(parent.kind()) {
+                            Some(p)
+                        } else if ast::ExprStmt::can_cast(parent.kind()) {
+                            Some(parent)
+                        } else {
+                            None
+                        }
+                    })
+                }
+                PathKind::Item { .. } => {
+                    parent.ancestors().find(|it| ast::MacroCall::can_cast(it.kind()))
+                }
+                _ => None,
+            };
+            if let Some(top) = top_node {
+                if let Some(NodeOrToken::Node(error_node)) =
+                    syntax::algo::non_trivia_sibling(top.clone().into(), syntax::Direction::Prev)
+                {
+                    if error_node.kind() == SyntaxKind::ERROR {
+                        qualifier_ctx.unsafe_tok = error_node
+                            .children_with_tokens()
+                            .filter_map(NodeOrToken::into_token)
+                            .find(|it| it.kind() == T![unsafe]);
+                        qualifier_ctx.vis_node =
+                            error_node.children().find_map(ast::Visibility::cast);
+                    }
+                }
+
+                if let PathKind::Item { .. } = path_ctx.kind {
+                    if qualifier_ctx.none() {
+                        if let Some(t) = top.first_token() {
+                            if let Some(prev) = t
+                                .prev_token()
+                                .and_then(|t| syntax::algo::skip_trivia_token(t, Direction::Prev))
+                            {
+                                if ![T![;], T!['}'], T!['{']].contains(&prev.kind()) {
+                                    // This was inferred to be an item position path, but it seems
+                                    // to be part of some other broken node which leaked into an item
+                                    // list, so return without setting the path context
+                                    return res;
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
         nameref_ctx.path_ctx = Some(path_ctx);
-        (nameref_ctx, pat_ctx)
+        res
     }
 }
 
diff --git a/crates/ide-completion/src/lib.rs b/crates/ide-completion/src/lib.rs
index c100dd63ea..9dc367b0bc 100644
--- a/crates/ide-completion/src/lib.rs
+++ b/crates/ide-completion/src/lib.rs
@@ -169,11 +169,9 @@ pub fn completions(
             completions::mod_::complete_mod(acc, ctx);
             completions::pattern::complete_pattern(acc, ctx);
             completions::postfix::complete_postfix(acc, ctx);
-            completions::record::complete_record_literal(acc, ctx);
             completions::record::complete_record(acc, ctx);
             completions::snippet::complete_expr_snippet(acc, ctx);
             completions::snippet::complete_item_snippet(acc, ctx);
-            completions::trait_impl::complete_trait_impl(acc, ctx);
             completions::r#type::complete_type_path(acc, ctx);
             completions::r#type::complete_inferred_type(acc, ctx);
             completions::use_::complete_use_tree(acc, ctx);
diff --git a/crates/ide-completion/src/patterns.rs b/crates/ide-completion/src/patterns.rs
index 9abbfaa407..761c97b9a9 100644
--- a/crates/ide-completion/src/patterns.rs
+++ b/crates/ide-completion/src/patterns.rs
@@ -30,7 +30,6 @@ pub(crate) enum TypeAnnotation {
 /// from which file the nodes are.
 #[derive(Clone, Debug, PartialEq, Eq)]
 pub(crate) enum ImmediateLocation {
-    RefExpr,
     TypeBound,
     /// Original file ast node
     TypeAnnotation(TypeAnnotation),
@@ -80,7 +79,6 @@ pub(crate) fn determine_location(
 
     let res = match_ast! {
         match parent {
-            ast::RefExpr(_) => ImmediateLocation::RefExpr,
             ast::TypeBound(_) => ImmediateLocation::TypeBound,
             ast::TypeBoundList(_) => ImmediateLocation::TypeBound,
             ast::GenericArgList(_) => sema
@@ -248,30 +246,3 @@ fn next_non_trivia_sibling(ele: SyntaxElement) -> Option<SyntaxElement> {
     }
     None
 }
-
-#[cfg(test)]
-mod tests {
-    use syntax::algo::find_node_at_offset;
-
-    use crate::tests::position;
-
-    use super::*;
-
-    fn check_location(code: &str, loc: impl Into<Option<ImmediateLocation>>) {
-        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()
-        );
-    }
-
-    #[test]
-    fn test_ref_expr_loc() {
-        check_location(r"fn my_fn() { let x = &m$0 foo; }", ImmediateLocation::RefExpr);
-    }
-}
diff --git a/crates/ide-completion/src/render.rs b/crates/ide-completion/src/render.rs
index ca2b3ad343..942dc03368 100644
--- a/crates/ide-completion/src/render.rs
+++ b/crates/ide-completion/src/render.rs
@@ -1099,6 +1099,8 @@ fn go(world: &WorldSnapshot) { go(w$0) }
 "#,
             expect![[r#"
                 lc world [type+name+local]
+                st WorldSnapshot {…} []
+                st &WorldSnapshot {…} [type]
                 st WorldSnapshot []
                 fn go(…) []
             "#]],
@@ -1197,6 +1199,8 @@ fn main() {
                 lc s [name+local]
                 lc &mut s [type+name+local]
                 st S []
+                st &mut S [type]
+                st S []
                 fn main() []
                 fn foo(…) []
             "#]],
@@ -1266,6 +1270,8 @@ fn main() {
                 lc m [local]
                 lc t [local]
                 lc &t [type+local]
+                st S []
+                st &S [type]
                 st T []
                 st S []
                 fn main() []
@@ -1311,6 +1317,8 @@ fn main() {
                 lc m [local]
                 lc t [local]
                 lc &mut t [type+local]
+                st S []
+                st &mut S [type]
                 st T []
                 st S []
                 fn main() []
@@ -1405,6 +1413,8 @@ fn main() {
 }
 "#,
             expect![[r#"
+                st S []
+                st &S [type]
                 st T []
                 st S []
                 fn main() []
diff --git a/crates/ide-completion/src/render/macro_.rs b/crates/ide-completion/src/render/macro_.rs
index 5c862f013a..de527860d8 100644
--- a/crates/ide-completion/src/render/macro_.rs
+++ b/crates/ide-completion/src/render/macro_.rs
@@ -34,8 +34,8 @@ fn render(
     let (bra, ket) = if is_fn_like { guess_macro_braces(&name, docs_str) } else { ("", "") };
 
     let needs_bang = match completion.path_context() {
-        Some(&PathCompletionCtx { kind, has_macro_bang, .. }) => {
-            is_fn_like && kind != PathKind::Use && !has_macro_bang
+        Some(PathCompletionCtx { kind, has_macro_bang, .. }) => {
+            is_fn_like && *kind != PathKind::Use && !has_macro_bang
         }
         _ => is_fn_like,
     };
diff --git a/crates/ide-db/src/imports/merge_imports.rs b/crates/ide-db/src/imports/merge_imports.rs
index 7fb4b90e6d..c7d9034f74 100644
--- a/crates/ide-db/src/imports/merge_imports.rs
+++ b/crates/ide-db/src/imports/merge_imports.rs
@@ -30,8 +30,6 @@ impl MergeBehavior {
     }
 }
 
-/// Merge `rhs` into `lhs` keeping both intact.
-/// Returned AST is mutable.
 pub fn try_merge_imports(
     lhs: &ast::Use,
     rhs: &ast::Use,
@@ -53,8 +51,6 @@ pub fn try_merge_imports(
     Some(lhs)
 }
 
-/// Merge `rhs` into `lhs` keeping both intact.
-/// Returned AST is mutable.
 pub fn try_merge_trees(
     lhs: &ast::UseTree,
     rhs: &ast::UseTree,
diff --git a/crates/mbe/src/parser.rs b/crates/mbe/src/parser.rs
index 6c7be59841..9be8d7085d 100644
--- a/crates/mbe/src/parser.rs
+++ b/crates/mbe/src/parser.rs
@@ -135,14 +135,6 @@ fn next_op<'a>(first: &tt::TokenTree, src: &mut TtIter<'a>, mode: Mode) -> Resul
                         let id = lit.id;
                         Op::Var { name, kind, id }
                     }
-                    tt::Leaf::Punct(punct @ tt::Punct { char: '$', .. }) => match mode {
-                        Mode::Pattern => {
-                            return Err(ParseError::unexpected(
-                                "`$$` is not allowed on the pattern side",
-                            ))
-                        }
-                        Mode::Template => Op::Leaf(tt::Leaf::Punct(*punct)),
-                    },
                     tt::Leaf::Punct(_) | tt::Leaf::Literal(_) => {
                         return Err(ParseError::expected("expected ident"))
                     }
diff --git a/crates/parser/src/grammar/generic_params.rs b/crates/parser/src/grammar/generic_params.rs
index 78a75fad1c..fbd514078d 100644
--- a/crates/parser/src/grammar/generic_params.rs
+++ b/crates/parser/src/grammar/generic_params.rs
@@ -133,12 +133,6 @@ fn type_bound(p: &mut Parser) -> bool {
     match p.current() {
         LIFETIME_IDENT => lifetime(p),
         T![for] => types::for_type(p, false),
-        T![?] if p.nth_at(1, T![for]) => {
-            // test question_for_type_trait_bound
-            // fn f<T>() where T: ?for<> Sized {}
-            p.bump_any();
-            types::for_type(p, false)
-        }
         current => {
             match current {
                 T![?] => p.bump_any(),
diff --git a/crates/project-model/src/build_scripts.rs b/crates/project-model/src/build_scripts.rs
index 73091306a8..e0e29552ac 100644
--- a/crates/project-model/src/build_scripts.rs
+++ b/crates/project-model/src/build_scripts.rs
@@ -18,7 +18,7 @@ use crate::{cfg_flag::CfgFlag, CargoConfig, CargoWorkspace, Package};
 
 #[derive(Debug, Default, Clone, PartialEq, Eq)]
 pub struct WorkspaceBuildScripts {
-    outputs: ArenaMap<Package, Option<BuildScriptOutput>>,
+    pub(crate) outputs: ArenaMap<Package, BuildScriptOutput>,
     error: Option<String>,
 }
 
@@ -72,7 +72,6 @@ impl WorkspaceBuildScripts {
 
         cmd
     }
-
     pub(crate) fn run(
         config: &CargoConfig,
         workspace: &CargoWorkspace,
@@ -92,13 +91,13 @@ impl WorkspaceBuildScripts {
         cmd.current_dir(workspace.workspace_root());
 
         let mut res = WorkspaceBuildScripts::default();
-        let outputs = &mut res.outputs;
         // NB: Cargo.toml could have been modified between `cargo metadata` and
         // `cargo check`. We shouldn't assume that package ids we see here are
         // exactly those from `config`.
         let mut by_id: FxHashMap<String, Package> = FxHashMap::default();
+
         for package in workspace.packages() {
-            outputs.insert(package, None);
+            res.outputs.insert(package, BuildScriptOutput::default());
             by_id.insert(workspace[package].id.clone(), package);
         }
 
@@ -142,8 +141,7 @@ impl WorkspaceBuildScripts {
                             }
                             acc
                         };
-                        let package_build_data =
-                            outputs[package].get_or_insert_with(Default::default);
+                        let package_build_data = &mut res.outputs[package];
                         // cargo_metadata crate returns default (empty) path for
                         // older cargos, which is not absolute, so work around that.
                         if !message.out_dir.as_str().is_empty() {
@@ -169,9 +167,7 @@ impl WorkspaceBuildScripts {
                                 message.filenames.iter().find(|name| is_dylib(name))
                             {
                                 let filename = AbsPathBuf::assert(PathBuf::from(&filename));
-                                outputs[package]
-                                    .get_or_insert_with(Default::default)
-                                    .proc_macro_dylib_path = Some(filename);
+                                res.outputs[package].proc_macro_dylib_path = Some(filename);
                             }
                         }
                     }
@@ -193,18 +189,17 @@ impl WorkspaceBuildScripts {
         )?;
 
         for package in workspace.packages() {
-            if let Some(package_build_data) = &mut outputs[package] {
-                tracing::info!(
-                    "{} BuildScriptOutput: {:?}",
-                    workspace[package].manifest.parent().display(),
-                    package_build_data,
-                );
-                // inject_cargo_env(package, package_build_data);
-                if let Some(out_dir) = &package_build_data.out_dir {
-                    // NOTE: cargo and rustc seem to hide non-UTF-8 strings from env! and option_env!()
-                    if let Some(out_dir) = out_dir.as_os_str().to_str().map(|s| s.to_owned()) {
-                        package_build_data.envs.push(("OUT_DIR".to_string(), out_dir));
-                    }
+            let package_build_data = &mut res.outputs[package];
+            tracing::info!(
+                "{} BuildScriptOutput: {:?}",
+                workspace[package].manifest.parent().display(),
+                package_build_data,
+            );
+            // inject_cargo_env(package, package_build_data);
+            if let Some(out_dir) = &package_build_data.out_dir {
+                // NOTE: cargo and rustc seem to hide non-UTF-8 strings from env! and option_env!()
+                if let Some(out_dir) = out_dir.as_os_str().to_str().map(|s| s.to_owned()) {
+                    package_build_data.envs.push(("OUT_DIR".to_string(), out_dir));
                 }
             }
         }
@@ -223,10 +218,6 @@ impl WorkspaceBuildScripts {
     pub fn error(&self) -> Option<&str> {
         self.error.as_deref()
     }
-
-    pub(crate) fn get_output(&self, idx: Package) -> Option<&BuildScriptOutput> {
-        self.outputs.get(idx)?.as_ref()
-    }
 }
 
 // FIXME: File a better way to know if it is a dylib.
diff --git a/crates/project-model/src/workspace.rs b/crates/project-model/src/workspace.rs
index 80fd01a4c6..ace1d455c4 100644
--- a/crates/project-model/src/workspace.rs
+++ b/crates/project-model/src/workspace.rs
@@ -312,9 +312,9 @@ impl ProjectWorkspace {
                         let pkg_root = cargo[pkg].manifest.parent().to_path_buf();
 
                         let mut include = vec![pkg_root.clone()];
-                        let out_dir =
-                            build_scripts.get_output(pkg).and_then(|it| it.out_dir.clone());
-                        include.extend(out_dir);
+                        include.extend(
+                            build_scripts.outputs.get(pkg).and_then(|it| it.out_dir.clone()),
+                        );
 
                         // In case target's path is manually set in Cargo.toml to be
                         // outside the package root, add its parent as an extra include.
@@ -586,7 +586,7 @@ fn cargo_to_crate_graph(
                 let crate_id = add_target_crate_root(
                     &mut crate_graph,
                     &cargo[pkg],
-                    build_scripts.get_output(pkg),
+                    build_scripts.outputs.get(pkg),
                     cfg_options,
                     &mut |path| load_proc_macro(&cargo[tgt].name, path),
                     file_id,
diff --git a/crates/rust-analyzer/src/bin/main.rs b/crates/rust-analyzer/src/bin/main.rs
index 4787786cac..a9e79cc7c3 100644
--- a/crates/rust-analyzer/src/bin/main.rs
+++ b/crates/rust-analyzer/src/bin/main.rs
@@ -77,10 +77,12 @@ fn try_main() -> Result<()> {
                 println!("{}", flags::RustAnalyzer::HELP);
                 return Ok(());
             }
-            with_extra_thread("LspServer", run_server)?;
+            with_extra_thread("rust-analyzer server thread", run_server)?;
         }
         flags::RustAnalyzerCmd::ProcMacro(flags::ProcMacro) => {
-            with_extra_thread("MacroExpander", || proc_macro_srv::cli::run().map_err(Into::into))?;
+            with_extra_thread("rust-analyzer proc-macro expander", || {
+                proc_macro_srv::cli::run().map_err(Into::into)
+            })?;
         }
         flags::RustAnalyzerCmd::Parse(cmd) => cmd.run()?,
         flags::RustAnalyzerCmd::Symbols(cmd) => cmd.run()?,
diff --git a/crates/rust-analyzer/src/diagnostics/to_proto.rs b/crates/rust-analyzer/src/diagnostics/to_proto.rs
index 8f779df18d..aac8e9222d 100644
--- a/crates/rust-analyzer/src/diagnostics/to_proto.rs
+++ b/crates/rust-analyzer/src/diagnostics/to_proto.rs
@@ -462,10 +462,9 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
                 message: "original diagnostic".to_string(),
             };
             for sub in &subdiagnostics {
-                let mut message = sub.related.message.clone();
-                // Change empty message to " ", as they greatly confuse VS Code.
-                if message.is_empty() {
-                    message = String::from(" ");
+                // Filter out empty/non-existent messages, as they greatly confuse VS Code.
+                if sub.related.message.is_empty() {
+                    continue;
                 }
                 diagnostics.push(MappedRustDiagnostic {
                     url: sub.related.location.uri.clone(),
@@ -476,7 +475,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
                         code: code.clone().map(lsp_types::NumberOrString::String),
                         code_description: code_description.clone(),
                         source: Some(source.clone()),
-                        message,
+                        message: sub.related.message.clone(),
                         related_information: Some(vec![back_ref.clone()]),
                         tags: None, // don't apply modifiers again
                         data: None,
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index babcbb4782..0fc5fd9dfa 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -665,11 +665,11 @@ impl GlobalState {
             })?
             .on::<lsp_types::notification::DidOpenTextDocument>(|this, params| {
                 if let Ok(path) = from_proto::vfs_path(&params.text_document.uri) {
-                    let already_exists = this
+                    if this
                         .mem_docs
                         .insert(path.clone(), DocumentData::new(params.text_document.version))
-                        .is_err();
-                    if already_exists {
+                        .is_err()
+                    {
                         tracing::error!("duplicate DidOpenTextDocument: {}", path)
                     }
                     this.vfs
@@ -688,7 +688,7 @@ impl GlobalState {
                             doc.version = params.text_document.version;
                         }
                         None => {
-                            tracing::error!("unexpected DidChangeTextDocument: {}", path);
+                            tracing::error!("unexpected DidChangeTextDocument: {}; send DidOpenTextDocument first", path);
                             return Ok(());
                         }
                     };
@@ -722,8 +722,7 @@ impl GlobalState {
                 }
                 if let Ok(abs_path) = from_proto::abs_path(&params.text_document.uri) {
                     if reload::should_refresh_for_change(&abs_path, ChangeKind::Modify) {
-                        this.fetch_workspaces_queue
-                            .request_op(format!("DidSaveTextDocument {}", abs_path.display()));
+                        this.fetch_workspaces_queue.request_op(format!("DidSaveTextDocument {}", abs_path.display()));
                     }
                 }
                 Ok(())
@@ -752,10 +751,7 @@ impl GlobalState {
                                     // provide a configuration. This is handled in Config::update below.
                                     let mut config = Config::clone(&*this.config);
                                     if let Err(error) = config.update(json.take()) {
-                                        this.show_message(
-                                            lsp_types::MessageType::WARNING,
-                                            error.to_string(),
-                                        );
+                                        this.show_message(lsp_types::MessageType::WARNING, error.to_string());
                                     }
                                     this.update_configuration(config);
                                 }


Test patch:
diff --git a/crates/hir-def/src/macro_expansion_tests/mbe.rs b/crates/hir-def/src/macro_expansion_tests/mbe.rs
index 93d2f648ae..befef6547c 100644
--- a/crates/hir-def/src/macro_expansion_tests/mbe.rs
+++ b/crates/hir-def/src/macro_expansion_tests/mbe.rs
@@ -1544,71 +1544,3 @@ struct Foo;
 "##]],
     )
 }
-
-#[test]
-fn test_dollar_dollar() {
-    check(
-        r#"
-macro_rules! register_struct { ($Struct:ident) => {
-    macro_rules! register_methods { ($$($method:ident),*) => {
-        macro_rules! implement_methods { ($$$$($$val:expr),*) => {
-            struct $Struct;
-            impl $Struct { $$(fn $method() -> &'static [u32] { &[$$$$($$$$val),*] })*}
-        }}
-    }}
-}}
-
-register_struct!(Foo);
-register_methods!(alpha, beta);
-implement_methods!(1, 2, 3);
-"#,
-        expect![[r#"
-macro_rules! register_struct { ($Struct:ident) => {
-    macro_rules! register_methods { ($$($method:ident),*) => {
-        macro_rules! implement_methods { ($$$$($$val:expr),*) => {
-            struct $Struct;
-            impl $Struct { $$(fn $method() -> &'static [u32] { &[$$$$($$$$val),*] })*}
-        }}
-    }}
-}}
-
-macro_rules !register_methods {
-    ($($method: ident), *) = > {
-        macro_rules!implement_methods {
-            ($$($val: expr), *) = > {
-                struct Foo;
-                impl Foo {
-                    $(fn $method()-> & 'static[u32] {
-                        &[$$($$val), *]
-                    }
-                    )*
-                }
-            }
-        }
-    }
-}
-macro_rules !implement_methods {
-    ($($val: expr), *) = > {
-        struct Foo;
-        impl Foo {
-            fn alpha()-> & 'static[u32] {
-                &[$($val), *]
-            }
-            fn beta()-> & 'static[u32] {
-                &[$($val), *]
-            }
-        }
-    }
-}
-struct Foo;
-impl Foo {
-    fn alpha() -> & 'static[u32] {
-        &[1, 2, 3]
-    }
-    fn beta() -> & 'static[u32] {
-        &[1, 2, 3]
-    }
-}
-"#]],
-    )
-}
diff --git a/crates/hir-def/src/macro_expansion_tests/mbe/meta_syntax.rs b/crates/hir-def/src/macro_expansion_tests/mbe/meta_syntax.rs
index 636a66ad53..2de10ddbdf 100644
--- a/crates/hir-def/src/macro_expansion_tests/mbe/meta_syntax.rs
+++ b/crates/hir-def/src/macro_expansion_tests/mbe/meta_syntax.rs
@@ -56,9 +56,6 @@ macro_rules! f2 { ($i:) => ($i) }
 f2!();
 macro_rules! f3 { ($i:_) => () }
 f3!();
-
-macro_rules! m1 { ($$i) => () }
-m1!();
 "#,
         expect![[r#"
 macro_rules! i1 { invalid }
@@ -77,9 +74,6 @@ macro_rules! f2 { ($i:) => ($i) }
 /* error: invalid macro definition: missing fragment specifier */
 macro_rules! f3 { ($i:_) => () }
 /* error: invalid macro definition: missing fragment specifier */
-
-macro_rules! m1 { ($$i) => () }
-/* error: invalid macro definition: `$$` is not allowed on the pattern side */
 "#]],
     )
 }
diff --git a/crates/parser/test_data/parser/inline/ok/0201_question_for_type_trait_bound.rast b/crates/parser/test_data/parser/inline/ok/0201_question_for_type_trait_bound.rast
deleted file mode 100644
index 56e2d1095d..0000000000
--- a/crates/parser/test_data/parser/inline/ok/0201_question_for_type_trait_bound.rast
+++ /dev/null
@@ -1,47 +0,0 @@
-SOURCE_FILE
-  FN
-    FN_KW "fn"
-    WHITESPACE " "
-    NAME
-      IDENT "f"
-    GENERIC_PARAM_LIST
-      L_ANGLE "<"
-      TYPE_PARAM
-        NAME
-          IDENT "T"
-      R_ANGLE ">"
-    PARAM_LIST
-      L_PAREN "("
-      R_PAREN ")"
-    WHITESPACE " "
-    WHERE_CLAUSE
-      WHERE_KW "where"
-      WHITESPACE " "
-      WHERE_PRED
-        PATH_TYPE
-          PATH
-            PATH_SEGMENT
-              NAME_REF
-                IDENT "T"
-        COLON ":"
-        WHITESPACE " "
-        TYPE_BOUND_LIST
-          TYPE_BOUND
-            QUESTION "?"
-            FOR_TYPE
-              FOR_KW "for"
-              GENERIC_PARAM_LIST
-                L_ANGLE "<"
-                R_ANGLE ">"
-              WHITESPACE " "
-              PATH_TYPE
-                PATH
-                  PATH_SEGMENT
-                    NAME_REF
-                      IDENT "Sized"
-    WHITESPACE " "
-    BLOCK_EXPR
-      STMT_LIST
-        L_CURLY "{"
-        R_CURLY "}"
-  WHITESPACE "\n"
diff --git a/crates/parser/test_data/parser/inline/ok/0201_question_for_type_trait_bound.rs b/crates/parser/test_data/parser/inline/ok/0201_question_for_type_trait_bound.rs
deleted file mode 100644
index f80dd90d44..0000000000
--- a/crates/parser/test_data/parser/inline/ok/0201_question_for_type_trait_bound.rs
+++ /dev/null
@@ -1,1 +0,0 @@
-fn f<T>() where T: ?for<> Sized {}