Repository: rust-analyzer/rust-analyzer
Pull request #10998 (2021-12-12 19:36:29)

Add number representation assists
Reimplemented assists from this PR https://github.com/rust-analyzer/rust-analyzer/pull/3683 with current APIs.
![image](https://user-images.githubusercontent.com/462486/145726792-47700215-26f2-4fdc-9520-63d1487901e5.png)
![image](https://user-images.githubusercontent.com/462486/145726802-f528a2f7-9159-41d3-b459-fc3fae033e60.png)


I've decided not to add options about size of the groups so behaviour is similar to clippy's. 
Minimal number length is also taken from clippy.



Patch:
diff --git a/Cargo.lock b/Cargo.lock
index dca35a212a..92f98b536c 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -220,6 +220,12 @@ dependencies = [
  "tracing",
 ]
 
+[[package]]
+name = "countme"
+version = "2.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "328b822bdcba4d4e402be8d9adb6eebf269f969f8eadef977a553ff3c4fbcb58"
+
 [[package]]
 name = "countme"
 version = "3.0.0"
@@ -519,7 +525,6 @@ dependencies = [
  "cfg",
  "cov-mark",
  "either",
- "hashbrown",
  "itertools",
  "la-arena",
  "limit",
@@ -753,9 +758,9 @@ dependencies = [
 
 [[package]]
 name = "itertools"
-version = "0.10.3"
+version = "0.10.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3"
+checksum = "69ddb889f9d0d08a67338271fa9b62996bc788c7796a5c18cf057420aaed5eaf"
 dependencies = [
  "either",
 ]
@@ -804,9 +809,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
 
 [[package]]
 name = "libc"
-version = "0.2.111"
+version = "0.2.108"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8e167738f1866a7ec625567bae89ca0d44477232a4f7c52b1c7f2adc2c98804f"
+checksum = "8521a1b57e76b1ec69af7599e75e38e7b7fad6610f037db8c79b127201b5d119"
 
 [[package]]
 name = "libloading"
@@ -1122,9 +1127,9 @@ checksum = "8d31d11c69a6b52a174b42bdc0c30e5e11670f90788b2c471c31c1d17d449443"
 
 [[package]]
 name = "proc-macro2"
-version = "1.0.33"
+version = "1.0.32"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fb37d2df5df740e582f28f8560cf425f52bb267d872fe58358eadb554909f07a"
+checksum = "ba508cc11742c0dc5c1659771673afbab7a0efab23aa17e854cbab0837ed0b43"
 dependencies = [
  "unicode-xid",
 ]
@@ -1178,7 +1183,7 @@ name = "profile"
 version = "0.0.0"
 dependencies = [
  "cfg-if",
- "countme",
+ "countme 3.0.0",
  "la-arena",
  "libc",
  "once_cell",
@@ -1221,9 +1226,9 @@ dependencies = [
 
 [[package]]
 name = "pulldown-cmark-to-cmark"
-version = "7.1.1"
+version = "7.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8bc14bc6b9f5881f240f9766414707ab24f972bcc3388589fe15b22fb15dc60d"
+checksum = "2d85e607de0249c2b5041e38de8684b00b62a64edee60bfcd85c153031a9d658"
 dependencies = [
  "pulldown-cmark",
 ]
@@ -1297,11 +1302,11 @@ checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
 
 [[package]]
 name = "rowan"
-version = "0.15.1"
+version = "0.14.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "104e4d36da6a2a0c9485ebf1b2dfc628036c7cc9b38bd1b14fa785f1f1bdf5f0"
+checksum = "4f77412a3d1f26af0c0783c23b3555a301b1a49805cba7bf9a7827a9e9e285f0"
 dependencies = [
- "countme",
+ "countme 2.0.4",
  "hashbrown",
  "memoffset",
  "rustc-hash",
@@ -1383,9 +1388,9 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
 
 [[package]]
 name = "ryu"
-version = "1.0.8"
+version = "1.0.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b30e4c09749c107e83dd61baf9604198efc4542863c88af39dafcaca89c7c9f9"
+checksum = "3c9613b5a66ab9ba26415184cfc41156594925a9cf3a2057e57f31ff145f6568"
 
 [[package]]
 name = "salsa"
@@ -1448,18 +1453,18 @@ dependencies = [
 
 [[package]]
 name = "serde"
-version = "1.0.131"
+version = "1.0.130"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b4ad69dfbd3e45369132cc64e6748c2d65cdfb001a2b1c232d128b4ad60561c1"
+checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913"
 dependencies = [
  "serde_derive",
 ]
 
 [[package]]
 name = "serde_derive"
-version = "1.0.131"
+version = "1.0.130"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b710a83c4e0dff6a3d511946b95274ad9ca9e5d3ae497b63fda866ac955358d2"
+checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b"
 dependencies = [
  "proc-macro2",
  "quote",
diff --git a/crates/hir/src/display.rs b/crates/hir/src/display.rs
index 17465dc2b0..38bbf41797 100644
--- a/crates/hir/src/display.rs
+++ b/crates/hir/src/display.rs
@@ -12,10 +12,7 @@ use hir_ty::{
     },
     Interner, TraitRefExt, WhereClause,
 };
-use syntax::{
-    ast::{self, HasName},
-    SmolStr,
-};
+use syntax::ast::{self, HasName};
 
 use crate::{
     Adt, Const, ConstParam, Enum, Field, Function, GenericParam, HasCrate, HasVisibility,
@@ -250,8 +247,7 @@ impl HirDisplay for TypeParam {
             bounds.iter().cloned().map(|b| b.substitute(&Interner, &substs)).collect();
         let krate = self.id.parent.krate(f.db).id;
         let sized_trait =
-            f.db.lang_item(krate, SmolStr::new_inline("sized"))
-                .and_then(|lang_item| lang_item.as_trait());
+            f.db.lang_item(krate, "sized".into()).and_then(|lang_item| lang_item.as_trait());
         let has_only_sized_bound = predicates.iter().all(move |pred| match pred.skip_binders() {
             WhereClause::Implemented(it) => Some(it.hir_trait_id()) == sized_trait,
             _ => false,
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index 7add0f4a43..3e04ea8da9 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -2427,7 +2427,7 @@ impl Type {
         let krate = self.krate;
 
         let std_future_trait =
-            db.lang_item(krate, SmolStr::new_inline("future_trait")).and_then(|it| it.as_trait());
+            db.lang_item(krate, "future_trait".into()).and_then(|it| it.as_trait());
         let std_future_trait = match std_future_trait {
             Some(it) => it,
             None => return false,
@@ -2516,7 +2516,7 @@ impl Type {
     }
 
     pub fn is_copy(&self, db: &dyn HirDatabase) -> bool {
-        let lang_item = db.lang_item(self.krate, SmolStr::new_inline("copy"));
+        let lang_item = db.lang_item(self.krate, SmolStr::new("copy"));
         let copy_trait = match lang_item {
             Some(LangItemTarget::TraitId(it)) => it,
             _ => return false,
@@ -2623,15 +2623,13 @@ impl Type {
     }
 
     pub fn autoderef<'a>(&'a self, db: &'a dyn HirDatabase) -> impl Iterator<Item = Type> + 'a {
-        self.autoderef_(db).map(move |ty| self.derived(ty))
-    }
-
-    pub fn autoderef_<'a>(&'a self, db: &'a dyn HirDatabase) -> impl Iterator<Item = Ty> + 'a {
         // There should be no inference vars in types passed here
         let canonical = hir_ty::replace_errors_with_variables(&self.ty);
         let environment = self.env.env.clone();
         let ty = InEnvironment { goal: canonical, environment };
-        autoderef(db, Some(self.krate), ty).map(|canonical| canonical.value)
+        autoderef(db, Some(self.krate), ty)
+            .map(|canonical| canonical.value)
+            .map(move |ty| self.derived(ty))
     }
 
     // This would be nicer if it just returned an iterator, but that runs into
@@ -2810,32 +2808,22 @@ impl Type {
         db: &'a dyn HirDatabase,
     ) -> impl Iterator<Item = Trait> + 'a {
         let _p = profile::span("applicable_inherent_traits");
-        self.autoderef_(db)
-            .filter_map(|ty| ty.dyn_trait())
+        self.autoderef(db)
+            .filter_map(|derefed_type| derefed_type.ty.dyn_trait())
             .flat_map(move |dyn_trait_id| hir_ty::all_super_traits(db.upcast(), dyn_trait_id))
             .map(Trait::from)
     }
 
-    pub fn env_traits<'a>(&'a self, db: &'a dyn HirDatabase) -> impl Iterator<Item = Trait> + 'a {
-        let _p = profile::span("env_traits");
-        self.autoderef_(db)
-            .filter(|ty| matches!(ty.kind(&Interner), TyKind::Placeholder(_)))
-            .flat_map(|ty| {
-                self.env
-                    .traits_in_scope_from_clauses(ty)
-                    .flat_map(|t| hir_ty::all_super_traits(db.upcast(), t))
-            })
-            .map(Trait::from)
-    }
-
-    pub fn as_impl_traits(&self, db: &dyn HirDatabase) -> Option<impl Iterator<Item = Trait>> {
+    pub fn as_impl_traits(&self, db: &dyn HirDatabase) -> Option<Vec<Trait>> {
         self.ty.impl_trait_bounds(db).map(|it| {
-            it.into_iter().filter_map(|pred| match pred.skip_binders() {
-                hir_ty::WhereClause::Implemented(trait_ref) => {
-                    Some(Trait::from(trait_ref.hir_trait_id()))
-                }
-                _ => None,
-            })
+            it.into_iter()
+                .filter_map(|pred| match pred.skip_binders() {
+                    hir_ty::WhereClause::Implemented(trait_ref) => {
+                        Some(Trait::from(trait_ref.hir_trait_id()))
+                    }
+                    _ => None,
+                })
+                .collect()
         })
     }
 
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index 75f6b02577..ed1b2f64fd 100644
--- a/crates/hir/src/semantics.rs
+++ b/crates/hir/src/semantics.rs
@@ -1164,7 +1164,8 @@ impl<'a> SemanticsScope<'a> {
     }
 
     /// Note: `FxHashSet<TraitId>` should be treated as an opaque type, passed into `Type
-    pub fn visible_traits(&self) -> FxHashSet<TraitId> {
+    // FIXME: rename to visible_traits to not repeat scope?
+    pub fn traits_in_scope(&self) -> FxHashSet<TraitId> {
         let resolver = &self.resolver;
         resolver.traits_in_scope(self.db.upcast())
     }
diff --git a/crates/hir_def/src/data.rs b/crates/hir_def/src/data.rs
index b9f8c2f297..ad1c1f2ce5 100644
--- a/crates/hir_def/src/data.rs
+++ b/crates/hir_def/src/data.rs
@@ -255,7 +255,7 @@ impl ImplData {
 
 #[derive(Debug, Clone, PartialEq, Eq)]
 pub struct ConstData {
-    /// `None` for `const _: () = ();`
+    /// const _: () = ();
     pub name: Option<Name>,
     pub type_ref: Interned<TypeRef>,
     pub visibility: RawVisibility,
diff --git a/crates/hir_def/src/item_tree.rs b/crates/hir_def/src/item_tree.rs
index 68d77084a3..405cc2d00f 100644
--- a/crates/hir_def/src/item_tree.rs
+++ b/crates/hir_def/src/item_tree.rs
@@ -648,7 +648,7 @@ pub struct Enum {
 
 #[derive(Debug, Clone, Eq, PartialEq)]
 pub struct Const {
-    /// `None` for `const _: () = ();`
+    /// const _: () = ();
     pub name: Option<Name>,
     pub visibility: RawVisibilityId,
     pub type_ref: Interned<TypeRef>,
diff --git a/crates/hir_def/src/item_tree/lower.rs b/crates/hir_def/src/item_tree/lower.rs
index 4396801116..e9af991785 100644
--- a/crates/hir_def/src/item_tree/lower.rs
+++ b/crates/hir_def/src/item_tree/lower.rs
@@ -375,7 +375,12 @@ impl<'a> Ctx<'a> {
     }
 
     fn lower_const(&mut self, konst: &ast::Const) -> FileItemTreeId<Const> {
-        let name = konst.name().map(|it| it.as_name());
+        let mut name = konst.name().map(|it| it.as_name());
+        if name.as_ref().map_or(false, |n| n.to_smol_str().starts_with("_DERIVE_")) {
+            // FIXME: this is a hack to treat consts generated by synstructure as unnamed
+            // remove this some time in the future
+            name = None;
+        }
         let type_ref = self.lower_type_ref_opt(konst.ty());
         let visibility = self.lower_visibility(konst);
         let ast_id = self.source_ast_id_map.ast_id(konst);
diff --git a/crates/hir_def/src/resolver.rs b/crates/hir_def/src/resolver.rs
index 9f68b800a5..8b2eea9d58 100644
--- a/crates/hir_def/src/resolver.rs
+++ b/crates/hir_def/src/resolver.rs
@@ -8,7 +8,7 @@ use hir_expand::{
 };
 use indexmap::IndexMap;
 use rustc_hash::FxHashSet;
-use smallvec::{smallvec, SmallVec};
+use smallvec::SmallVec;
 
 use crate::{
     body::scope::{ExprScopes, ScopeId},
@@ -567,8 +567,6 @@ pub fn resolver_for_scope(
     let mut r = owner.resolver(db);
     let scopes = db.expr_scopes(owner);
     let scope_chain = scopes.scope_chain(scope_id).collect::<Vec<_>>();
-    r.scopes.reserve(scope_chain.len());
-
     for scope in scope_chain.into_iter().rev() {
         if let Some(block) = scopes.block(scope) {
             if let Some(def_map) = db.block_def_map(block) {
@@ -741,13 +739,12 @@ pub trait HasResolver: Copy {
 impl HasResolver for ModuleId {
     fn resolver(self, db: &dyn DefDatabase) -> Resolver {
         let mut def_map = self.def_map(db);
-        let mut modules: SmallVec<[_; 2]> = smallvec![(def_map.clone(), self.local_id)];
+        let mut modules = vec![(def_map.clone(), self.local_id)];
         while let Some(parent) = def_map.parent() {
             def_map = parent.def_map(db);
             modules.push((def_map.clone(), parent.local_id));
         }
         let mut resolver = Resolver::default();
-        resolver.scopes.reserve(modules.len());
         for (def_map, module) in modules.into_iter().rev() {
             resolver = resolver.push_module_scope(def_map, module);
         }
diff --git a/crates/hir_expand/Cargo.toml b/crates/hir_expand/Cargo.toml
index 039861f7f7..380b7ef877 100644
--- a/crates/hir_expand/Cargo.toml
+++ b/crates/hir_expand/Cargo.toml
@@ -16,7 +16,6 @@ either = "1.5.3"
 rustc-hash = "1.0.0"
 la-arena = { version = "0.3.0", path = "../../lib/arena" }
 itertools = "0.10.0"
-hashbrown = { version = "0.11", features = ["inline-more"], default-features = false }
 
 base_db = { path = "../base_db", version = "0.0.0" }
 cfg = { path = "../cfg", version = "0.0.0" }
diff --git a/crates/hir_expand/src/ast_id_map.rs b/crates/hir_expand/src/ast_id_map.rs
index 43c8a3db5c..16cf299076 100644
--- a/crates/hir_expand/src/ast_id_map.rs
+++ b/crates/hir_expand/src/ast_id_map.rs
@@ -8,13 +8,12 @@
 use std::{
     any::type_name,
     fmt,
-    hash::{BuildHasher, BuildHasherDefault, Hash, Hasher},
+    hash::{Hash, Hasher},
     marker::PhantomData,
 };
 
 use la_arena::{Arena, Idx};
 use profile::Count;
-use rustc_hash::FxHasher;
 use syntax::{ast, match_ast, AstNode, AstPtr, SyntaxNode, SyntaxNodePtr};
 
 /// `AstId` points to an AST node in a specific file.
@@ -61,28 +60,12 @@ impl<N: AstNode> FileAstId<N> {
 type ErasedFileAstId = Idx<SyntaxNodePtr>;
 
 /// Maps items' `SyntaxNode`s to `ErasedFileAstId`s and back.
-#[derive(Default)]
+#[derive(Debug, PartialEq, Eq, Default)]
 pub struct AstIdMap {
-    /// Maps stable id to unstable ptr.
     arena: Arena<SyntaxNodePtr>,
-    /// Reverse: map ptr to id.
-    map: hashbrown::HashMap<Idx<SyntaxNodePtr>, (), ()>,
     _c: Count<Self>,
 }
 
-impl fmt::Debug for AstIdMap {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.debug_struct("AstIdMap").field("arena", &self.arena).finish()
-    }
-}
-
-impl PartialEq for AstIdMap {
-    fn eq(&self, other: &Self) -> bool {
-        self.arena == other.arena
-    }
-}
-impl Eq for AstIdMap {}
-
 impl AstIdMap {
     pub(crate) fn from_source(node: &SyntaxNode) -> AstIdMap {
         assert!(node.parent().is_none());
@@ -106,16 +89,6 @@ impl AstIdMap {
                 }
             }
         });
-        res.map = hashbrown::HashMap::with_capacity_and_hasher(res.arena.len(), ());
-        for (idx, ptr) in res.arena.iter() {
-            let hash = hash_ptr(ptr);
-            match res.map.raw_entry_mut().from_hash(hash, |idx2| *idx2 == idx) {
-                hashbrown::hash_map::RawEntryMut::Occupied(_) => unreachable!(),
-                hashbrown::hash_map::RawEntryMut::Vacant(entry) => {
-                    entry.insert_with_hasher(hash, idx, (), |&idx| hash_ptr(&res.arena[idx]));
-                }
-            }
-        }
         res
     }
 
@@ -125,9 +98,8 @@ impl AstIdMap {
     }
     fn erased_ast_id(&self, item: &SyntaxNode) -> ErasedFileAstId {
         let ptr = SyntaxNodePtr::new(item);
-        let hash = hash_ptr(&ptr);
-        match self.map.raw_entry().from_hash(hash, |&idx| self.arena[idx] == ptr) {
-            Some((&idx, &())) => idx,
+        match self.arena.iter().find(|(_id, i)| **i == ptr) {
+            Some((it, _)) => it,
             None => panic!(
                 "Can't find {:?} in AstIdMap:\n{:?}",
                 item,
@@ -145,12 +117,6 @@ impl AstIdMap {
     }
 }
 
-fn hash_ptr(ptr: &SyntaxNodePtr) -> u64 {
-    let mut hasher = BuildHasherDefault::<FxHasher>::default().build_hasher();
-    ptr.hash(&mut hasher);
-    hasher.finish()
-}
-
 /// Walks the subtree in bdfs order, calling `f` for each node. What is bdfs
 /// order? It is a mix of breadth-first and depth first orders. Nodes for which
 /// `f` returns true are visited breadth-first, all the other nodes are explored
diff --git a/crates/hir_expand/src/builtin_fn_macro.rs b/crates/hir_expand/src/builtin_fn_macro.rs
index 57e66e5cc4..3add4c1103 100644
--- a/crates/hir_expand/src/builtin_fn_macro.rs
+++ b/crates/hir_expand/src/builtin_fn_macro.rs
@@ -386,18 +386,7 @@ fn concat_expand(
 ) -> ExpandResult<Option<ExpandedEager>> {
     let mut err = None;
     let mut text = String::new();
-    for (i, mut t) in tt.token_trees.iter().enumerate() {
-        // FIXME: hack on top of a hack: `$e:expr` captures get surrounded in parentheses
-        // to ensure the right parsing order, so skip the parentheses here. Ideally we'd
-        // implement rustc's model. cc https://github.com/rust-analyzer/rust-analyzer/pull/10623
-        if let tt::TokenTree::Subtree(tt::Subtree { delimiter: Some(delim), token_trees }) = t {
-            if let [tt] = &**token_trees {
-                if delim.kind == tt::DelimiterKind::Parenthesis {
-                    t = tt;
-                }
-            }
-        }
-
+    for (i, t) in tt.token_trees.iter().enumerate() {
         match t {
             tt::TokenTree::Leaf(tt::Leaf::Literal(it)) if i % 2 == 0 => {
                 // concat works with string and char literals, so remove any quotes.
diff --git a/crates/hir_expand/src/name.rs b/crates/hir_expand/src/name.rs
index 23f81d3c7f..ab730b54bc 100644
--- a/crates/hir_expand/src/name.rs
+++ b/crates/hir_expand/src/name.rs
@@ -334,13 +334,6 @@ pub mod known {
         gt,
         le,
         lt,
-        // lang items
-        not,
-        neg,
-        future_trait,
-        owned_box,
-        index,
-        partial_ord
     );
 
     // self/Self cannot be used as an identifier
diff --git a/crates/hir_ty/src/autoderef.rs b/crates/hir_ty/src/autoderef.rs
index 2d3d16cda0..1eb5490507 100644
--- a/crates/hir_ty/src/autoderef.rs
+++ b/crates/hir_ty/src/autoderef.rs
@@ -10,7 +10,6 @@ use chalk_ir::{cast::Cast, fold::Fold, interner::HasInterner, VariableKind};
 use hir_def::lang_item::LangItemTarget;
 use hir_expand::name::name;
 use limit::Limit;
-use syntax::SmolStr;
 use tracing::{info, warn};
 
 use crate::{
@@ -72,10 +71,7 @@ impl Iterator for Autoderef<'_> {
         }
 
         let (kind, new_ty) = if let Some(derefed) = builtin_deref(&self.ty.value) {
-            (
-                AutoderefKind::Builtin,
-                Canonical { value: derefed.clone(), binders: self.ty.binders.clone() },
-            )
+            (AutoderefKind::Builtin, Canonical { value: derefed, binders: self.ty.binders.clone() })
         } else {
             (
                 AutoderefKind::Overloaded,
@@ -114,17 +110,15 @@ pub(crate) fn deref(
 ) -> Option<Canonical<Ty>> {
     let _p = profile::span("deref");
     match builtin_deref(&ty.goal.value) {
-        Some(derefed) => {
-            Some(Canonical { value: derefed.clone(), binders: ty.goal.binders.clone() })
-        }
+        Some(derefed) => Some(Canonical { value: derefed, binders: ty.goal.binders.clone() }),
         None => deref_by_trait(db, krate, ty),
     }
 }
 
-fn builtin_deref(ty: &Ty) -> Option<&Ty> {
+fn builtin_deref(ty: &Ty) -> Option<Ty> {
     match ty.kind(&Interner) {
-        TyKind::Ref(.., ty) => Some(ty),
-        TyKind::Raw(.., ty) => Some(ty),
+        TyKind::Ref(.., ty) => Some(ty.clone()),
+        TyKind::Raw(.., ty) => Some(ty.clone()),
         _ => None,
     }
 }
@@ -135,7 +129,7 @@ fn deref_by_trait(
     ty: InEnvironment<&Canonical<Ty>>,
 ) -> Option<Canonical<Ty>> {
     let _p = profile::span("deref_by_trait");
-    let deref_trait = match db.lang_item(krate, SmolStr::new_inline("deref"))? {
+    let deref_trait = match db.lang_item(krate, "deref".into())? {
         LangItemTarget::TraitId(it) => it,
         _ => return None,
     };
diff --git a/crates/hir_ty/src/chalk_db.rs b/crates/hir_ty/src/chalk_db.rs
index 4a7b2d7363..dc5dcd163c 100644
--- a/crates/hir_ty/src/chalk_db.rs
+++ b/crates/hir_ty/src/chalk_db.rs
@@ -3,7 +3,6 @@
 use std::sync::Arc;
 
 use cov_mark::hit;
-use syntax::SmolStr;
 use tracing::debug;
 
 use chalk_ir::{cast::Cast, fold::shift::Shift, CanonicalVarKinds};
@@ -214,7 +213,7 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
             crate::ImplTraitId::AsyncBlockTypeImplTrait(..) => {
                 if let Some((future_trait, future_output)) = self
                     .db
-                    .lang_item(self.krate, SmolStr::new_inline("future_trait"))
+                    .lang_item(self.krate, "future_trait".into())
                     .and_then(|item| item.as_trait())
                     .and_then(|trait_| {
                         let alias =
@@ -420,7 +419,7 @@ pub(crate) fn associated_ty_data_query(
     if !ctx.unsized_types.borrow().contains(&self_ty) {
         let sized_trait = resolver
             .krate()
-            .and_then(|krate| db.lang_item(krate, SmolStr::new_inline("sized")))
+            .and_then(|krate| db.lang_item(krate, "sized".into()))
             .and_then(|lang_item| lang_item.as_trait().map(to_chalk_trait_id));
         let sized_bound = sized_trait.into_iter().map(|sized_trait| {
             let trait_bound =
diff --git a/crates/hir_ty/src/chalk_ext.rs b/crates/hir_ty/src/chalk_ext.rs
index 97d1ad0f9c..653c4d5ec8 100644
--- a/crates/hir_ty/src/chalk_ext.rs
+++ b/crates/hir_ty/src/chalk_ext.rs
@@ -6,7 +6,6 @@ use hir_def::{
     type_ref::Rawness,
     FunctionId, GenericDefId, HasModule, ItemContainerId, Lookup, TraitId,
 };
-use syntax::SmolStr;
 
 use crate::{
     db::HirDatabase, from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id,
@@ -188,7 +187,7 @@ impl TyExt for Ty {
                     ImplTraitId::AsyncBlockTypeImplTrait(def, _expr) => {
                         let krate = def.module(db.upcast()).krate();
                         if let Some(future_trait) = db
-                            .lang_item(krate, SmolStr::new_inline("future_trait"))
+                            .lang_item(krate, "future_trait".into())
                             .and_then(|item| item.as_trait())
                         {
                             // This is only used by type walking.
diff --git a/crates/hir_ty/src/diagnostics/decl_check.rs b/crates/hir_ty/src/diagnostics/decl_check.rs
index 9a0f457905..a1e7198bc2 100644
--- a/crates/hir_ty/src/diagnostics/decl_check.rs
+++ b/crates/hir_ty/src/diagnostics/decl_check.rs
@@ -181,12 +181,8 @@ impl<'a> DeclValidator<'a> {
                 AttrDefId::ExternBlockId(id) => Some(id.lookup(self.db.upcast()).container.into()),
                 // These warnings should not explore macro definitions at all
                 AttrDefId::MacroDefId(_) => None,
-                AttrDefId::AdtId(aid) => match aid {
-                    AdtId::StructId(sid) => Some(sid.lookup(self.db.upcast()).container.into()),
-                    AdtId::EnumId(eid) => Some(eid.lookup(self.db.upcast()).container.into()),
-                    // Unions aren't yet supported
-                    AdtId::UnionId(_) => None,
-                },
+                // Will never occur under an enum/struct/union/type alias
+                AttrDefId::AdtId(_) => None,
                 AttrDefId::FieldId(_) => None,
                 AttrDefId::EnumVariantId(_) => None,
                 AttrDefId::TypeAliasId(_) => None,
diff --git a/crates/hir_ty/src/diagnostics/match_check/deconstruct_pat.rs b/crates/hir_ty/src/diagnostics/match_check/deconstruct_pat.rs
index f268746809..f9439c706e 100644
--- a/crates/hir_ty/src/diagnostics/match_check/deconstruct_pat.rs
+++ b/crates/hir_ty/src/diagnostics/match_check/deconstruct_pat.rs
@@ -50,7 +50,6 @@ use std::{
 use hir_def::{EnumVariantId, HasModule, LocalFieldId, VariantId};
 use smallvec::{smallvec, SmallVec};
 use stdx::never;
-use syntax::SmolStr;
 
 use crate::{AdtId, Interner, Scalar, Ty, TyExt, TyKind};
 
@@ -906,7 +905,7 @@ fn is_field_list_non_exhaustive(variant_id: VariantId, cx: &MatchCheckCtx<'_>) -
 
 fn adt_is_box(adt: hir_def::AdtId, cx: &MatchCheckCtx<'_>) -> bool {
     use hir_def::lang_item::LangItemTarget;
-    match cx.db.lang_item(cx.module.krate(), SmolStr::new_inline("owned_box")) {
+    match cx.db.lang_item(cx.module.krate(), "owned_box".into()) {
         Some(LangItemTarget::StructId(box_id)) => adt == box_id.into(),
         _ => false,
     }
diff --git a/crates/hir_ty/src/display.rs b/crates/hir_ty/src/display.rs
index c09f8db86e..307323690a 100644
--- a/crates/hir_ty/src/display.rs
+++ b/crates/hir_ty/src/display.rs
@@ -20,7 +20,6 @@ use hir_def::{
 };
 use hir_expand::{hygiene::Hygiene, name::Name};
 use itertools::Itertools;
-use syntax::SmolStr;
 
 use crate::{
     const_from_placeholder_idx,
@@ -775,9 +774,8 @@ impl SizedByDefault {
         match self {
             Self::NotSized => false,
             Self::Sized { anchor } => {
-                let sized_trait = db
-                    .lang_item(anchor, SmolStr::new_inline("sized"))
-                    .and_then(|lang_item| lang_item.as_trait());
+                let sized_trait =
+                    db.lang_item(anchor, "sized".into()).and_then(|lang_item| lang_item.as_trait());
                 Some(trait_) == sized_trait
             }
         }
diff --git a/crates/hir_ty/src/infer.rs b/crates/hir_ty/src/infer.rs
index ce6cb81540..56e554736e 100644
--- a/crates/hir_ty/src/infer.rs
+++ b/crates/hir_ty/src/infer.rs
@@ -28,10 +28,11 @@ use hir_def::{
     AdtId, AssocItemId, DefWithBodyId, EnumVariantId, FieldId, FunctionId, HasModule, Lookup,
     TraitId, TypeAliasId, VariantId,
 };
-use hir_expand::name::{name, Name};
+use hir_expand::name::name;
 use la_arena::ArenaMap;
 use rustc_hash::FxHashMap;
 use stdx::impl_from;
+use syntax::SmolStr;
 
 use crate::{
     db::HirDatabase, fold_tys, infer::coerce::CoerceMany, lower::ImplTraitLoweringMode,
@@ -718,9 +719,10 @@ impl<'a> InferenceContext<'a> {
         self.infer_expr_coerce(self.body.body_expr, &Expectation::has_type(self.return_ty.clone()));
     }
 
-    fn resolve_lang_item(&self, name: Name) -> Option<LangItemTarget> {
+    fn resolve_lang_item(&self, name: &str) -> Option<LangItemTarget> {
         let krate = self.resolver.krate()?;
-        self.db.lang_item(krate, name.to_smol_str())
+        let name = SmolStr::new_inline(name);
+        self.db.lang_item(krate, name)
     }
 
     fn resolve_into_iter_item(&self) -> Option<TypeAliasId> {
@@ -741,22 +743,22 @@ impl<'a> InferenceContext<'a> {
     }
 
     fn resolve_ops_neg_output(&self) -> Option<TypeAliasId> {
-        let trait_ = self.resolve_lang_item(name![neg])?.as_trait()?;
+        let trait_ = self.resolve_lang_item("neg")?.as_trait()?;
         self.db.trait_data(trait_).associated_type_by_name(&name![Output])
     }
 
     fn resolve_ops_not_output(&self) -> Option<TypeAliasId> {
-        let trait_ = self.resolve_lang_item(name![not])?.as_trait()?;
+        let trait_ = self.resolve_lang_item("not")?.as_trait()?;
         self.db.trait_data(trait_).associated_type_by_name(&name![Output])
     }
 
     fn resolve_future_future_output(&self) -> Option<TypeAliasId> {
-        let trait_ = self.resolve_lang_item(name![future_trait])?.as_trait()?;
+        let trait_ = self.resolve_lang_item("future_trait")?.as_trait()?;
         self.db.trait_data(trait_).associated_type_by_name(&name![Output])
     }
 
     fn resolve_boxed_box(&self) -> Option<AdtId> {
-        let struct_ = self.resolve_lang_item(name![owned_box])?.as_struct()?;
+        let struct_ = self.resolve_lang_item("owned_box")?.as_struct()?;
         Some(struct_.into())
     }
 
@@ -797,7 +799,7 @@ impl<'a> InferenceContext<'a> {
     }
 
     fn resolve_ops_index(&self) -> Option<TraitId> {
-        self.resolve_lang_item(name![index])?.as_trait()
+        self.resolve_lang_item("index")?.as_trait()
     }
 
     fn resolve_ops_index_output(&self) -> Option<TypeAliasId> {
diff --git a/crates/hir_ty/src/infer/coerce.rs b/crates/hir_ty/src/infer/coerce.rs
index aebff59113..4d7ac6fd85 100644
--- a/crates/hir_ty/src/infer/coerce.rs
+++ b/crates/hir_ty/src/infer/coerce.rs
@@ -10,7 +10,6 @@ use std::iter;
 use chalk_ir::{cast::Cast, Goal, Mutability, TyVariableKind};
 use hir_def::{expr::ExprId, lang_item::LangItemTarget};
 use stdx::always;
-use syntax::SmolStr;
 
 use crate::{
     autoderef::{Autoderef, AutoderefKind},
@@ -537,11 +536,10 @@ impl<'a> InferenceContext<'a> {
             reborrow.as_ref().map_or_else(|| from_ty.clone(), |(_, adj)| adj.target.clone());
 
         let krate = self.resolver.krate().unwrap();
-        let coerce_unsized_trait =
-            match self.db.lang_item(krate, SmolStr::new_inline("coerce_unsized")) {
-                Some(LangItemTarget::TraitId(trait_)) => trait_,
-                _ => return Err(TypeError),
-            };
+        let coerce_unsized_trait = match self.db.lang_item(krate, "coerce_unsized".into()) {
+            Some(LangItemTarget::TraitId(trait_)) => trait_,
+            _ => return Err(TypeError),
+        };
 
         let coerce_unsized_tref = {
             let b = TyBuilder::trait_ref(self.db, coerce_unsized_trait);
diff --git a/crates/hir_ty/src/infer/expr.rs b/crates/hir_ty/src/infer/expr.rs
index 2b8dc793cc..90c68c9d3f 100644
--- a/crates/hir_ty/src/infer/expr.rs
+++ b/crates/hir_ty/src/infer/expr.rs
@@ -1276,43 +1276,41 @@ impl<'a> InferenceContext<'a> {
         let (name, lang_item) = match op {
             BinaryOp::LogicOp(_) => return None,
             BinaryOp::ArithOp(aop) => match aop {
-                ArithOp::Add => (name!(add), name!(add)),
-                ArithOp::Mul => (name!(mul), name!(mul)),
-                ArithOp::Sub => (name!(sub), name!(sub)),
-                ArithOp::Div => (name!(div), name!(div)),
-                ArithOp::Rem => (name!(rem), name!(rem)),
-                ArithOp::Shl => (name!(shl), name!(shl)),
-                ArithOp::Shr => (name!(shr), name!(shr)),
-                ArithOp::BitXor => (name!(bitxor), name!(bitxor)),
-                ArithOp::BitOr => (name!(bitor), name!(bitor)),
-                ArithOp::BitAnd => (name!(bitand), name!(bitand)),
+                ArithOp::Add => (name!(add), "add"),
+                ArithOp::Mul => (name!(mul), "mul"),
+                ArithOp::Sub => (name!(sub), "sub"),
+                ArithOp::Div => (name!(div), "div"),
+                ArithOp::Rem => (name!(rem), "rem"),
+                ArithOp::Shl => (name!(shl), "shl"),
+                ArithOp::Shr => (name!(shr), "shr"),
+                ArithOp::BitXor => (name!(bitxor), "bitxor"),
+                ArithOp::BitOr => (name!(bitor), "bitor"),
+                ArithOp::BitAnd => (name!(bitand), "bitand"),
             },
             BinaryOp::Assignment { op: Some(aop) } => match aop {
-                ArithOp::Add => (name!(add_assign), name!(add_assign)),
-                ArithOp::Mul => (name!(mul_assign), name!(mul_assign)),
-                ArithOp::Sub => (name!(sub_assign), name!(sub_assign)),
-                ArithOp::Div => (name!(div_assign), name!(div_assign)),
-                ArithOp::Rem => (name!(rem_assign), name!(rem_assign)),
-                ArithOp::Shl => (name!(shl_assign), name!(shl_assign)),
-                ArithOp::Shr => (name!(shr_assign), name!(shr_assign)),
-                ArithOp::BitXor => (name!(bitxor_assign), name!(bitxor_assign)),
-                ArithOp::BitOr => (name!(bitor_assign), name!(bitor_assign)),
-                ArithOp::BitAnd => (name!(bitand_assign), name!(bitand_assign)),
+                ArithOp::Add => (name!(add_assign), "add_assign"),
+                ArithOp::Mul => (name!(mul_assign), "mul_assign"),
+                ArithOp::Sub => (name!(sub_assign), "sub_assign"),
+                ArithOp::Div => (name!(div_assign), "div_assign"),
+                ArithOp::Rem => (name!(rem_assign), "rem_assign"),
+                ArithOp::Shl => (name!(shl_assign), "shl_assign"),
+                ArithOp::Shr => (name!(shr_assign), "shr_assign"),
+                ArithOp::BitXor => (name!(bitxor_assign), "bitxor_assign"),
+                ArithOp::BitOr => (name!(bitor_assign), "bitor_assign"),
+                ArithOp::BitAnd => (name!(bitand_assign), "bitand_assign"),
             },
             BinaryOp::CmpOp(cop) => match cop {
-                CmpOp::Eq { negated: false } => (name!(eq), name!(eq)),
-                CmpOp::Eq { negated: true } => (name!(ne), name!(eq)),
+                CmpOp::Eq { negated: false } => (name!(eq), "eq"),
+                CmpOp::Eq { negated: true } => (name!(ne), "eq"),
                 CmpOp::Ord { ordering: Ordering::Less, strict: false } => {
-                    (name!(le), name!(partial_ord))
-                }
-                CmpOp::Ord { ordering: Ordering::Less, strict: true } => {
-                    (name!(lt), name!(partial_ord))
+                    (name!(le), "partial_ord")
                 }
+                CmpOp::Ord { ordering: Ordering::Less, strict: true } => (name!(lt), "partial_ord"),
                 CmpOp::Ord { ordering: Ordering::Greater, strict: false } => {
-                    (name!(ge), name!(partial_ord))
+                    (name!(ge), "partial_ord")
                 }
                 CmpOp::Ord { ordering: Ordering::Greater, strict: true } => {
-                    (name!(gt), name!(partial_ord))
+                    (name!(gt), "partial_ord")
                 }
             },
             BinaryOp::Assignment { op: None } => return None,
diff --git a/crates/hir_ty/src/lower.rs b/crates/hir_ty/src/lower.rs
index 7373c9eb8b..99267e085f 100644
--- a/crates/hir_ty/src/lower.rs
+++ b/crates/hir_ty/src/lower.rs
@@ -28,7 +28,7 @@ use la_arena::ArenaMap;
 use rustc_hash::FxHashSet;
 use smallvec::SmallVec;
 use stdx::impl_from;
-use syntax::{ast, SmolStr};
+use syntax::ast;
 
 use crate::all_super_traits;
 use crate::{
@@ -96,9 +96,9 @@ impl<'a> TyLoweringContext<'a> {
         debruijn: DebruijnIndex,
         f: impl FnOnce(&TyLoweringContext) -> T,
     ) -> T {
-        let opaque_ty_data_vec = self.opaque_type_data.take();
-        let expander = self.expander.take();
-        let unsized_types = self.unsized_types.take();
+        let opaque_ty_data_vec = self.opaque_type_data.replace(Vec::new());
+        let expander = self.expander.replace(None);
+        let unsized_types = self.unsized_types.replace(Default::default());
         let new_ctx = Self {
             in_binders: debruijn,
             impl_trait_counter: Cell::new(self.impl_trait_counter.get()),
@@ -615,7 +615,7 @@ impl<'a> TyLoweringContext<'a> {
                 // `Option::None::<T>` are both allowed (though the former is
                 // preferred). See also `def_ids_for_path_segments` in rustc.
                 let len = path.segments().len();
-                let penultimate = len.checked_sub(2).and_then(|idx| path.segments().get(idx));
+                let penultimate = if len >= 2 { path.segments().get(len - 2) } else { None };
                 let segment = match penultimate {
                     Some(segment) if segment.args_and_bindings.is_some() => segment,
                     _ => last,
@@ -797,7 +797,7 @@ impl<'a> TyLoweringContext<'a> {
                 let sized_trait = self
                     .resolver
                     .krate()
-                    .and_then(|krate| self.db.lang_item(krate, SmolStr::new_inline("sized")))
+                    .and_then(|krate| self.db.lang_item(krate, "sized".into()))
                     .and_then(|lang_item| lang_item.as_trait());
                 // Don't lower associated type bindings as the only possible relaxed trait bound
                 // `?Sized` has no of them.
@@ -841,8 +841,8 @@ impl<'a> TyLoweringContext<'a> {
         };
         last_segment
             .into_iter()
-            .filter_map(|segment| segment.args_and_bindings)
-            .flat_map(|args_and_bindings| &args_and_bindings.bindings)
+            .flat_map(|segment| segment.args_and_bindings.into_iter())
+            .flat_map(|args_and_bindings| args_and_bindings.bindings.iter())
             .flat_map(move |binding| {
                 let found = associated_type_by_name_including_super_traits(
                     self.db,
@@ -850,14 +850,14 @@ impl<'a> TyLoweringContext<'a> {
                     &binding.name,
                 );
                 let (super_trait_ref, associated_ty) = match found {
-                    None => return SmallVec::new(),
+                    None => return SmallVec::<[QuantifiedWhereClause; 1]>::new(),
                     Some(t) => t,
                 };
                 let projection_ty = ProjectionTy {
                     associated_ty_id: to_assoc_type_id(associated_ty),
                     substitution: super_trait_ref.substitution,
                 };
-                let mut preds: SmallVec<[_; 1]> = SmallVec::with_capacity(
+                let mut preds = SmallVec::with_capacity(
                     binding.type_ref.as_ref().map_or(0, |_| 1) + binding.bounds.len(),
                 );
                 if let Some(type_ref) = &binding.type_ref {
@@ -895,7 +895,7 @@ impl<'a> TyLoweringContext<'a> {
                 let krate = func.lookup(ctx.db.upcast()).module(ctx.db.upcast()).krate();
                 let sized_trait = ctx
                     .db
-                    .lang_item(krate, SmolStr::new_inline("sized"))
+                    .lang_item(krate, "sized".into())
                     .and_then(|lang_item| lang_item.as_trait().map(to_chalk_trait_id));
                 let sized_clause = sized_trait.map(|trait_id| {
                     let clause = WhereClause::Implemented(TraitRef {
@@ -1200,7 +1200,7 @@ fn implicitly_sized_clauses<'a>(
     let generic_args = &substitution.as_slice(&Interner)[is_trait_def as usize..];
     let sized_trait = resolver
         .krate()
-        .and_then(|krate| db.lang_item(krate, SmolStr::new_inline("sized")))
+        .and_then(|krate| db.lang_item(krate, "sized".into()))
         .and_then(|lang_item| lang_item.as_trait().map(to_chalk_trait_id));
 
     sized_trait.into_iter().flat_map(move |sized_trait| {
diff --git a/crates/hir_ty/src/method_resolution.rs b/crates/hir_ty/src/method_resolution.rs
index 1549a40c91..6b3f48b194 100644
--- a/crates/hir_ty/src/method_resolution.rs
+++ b/crates/hir_ty/src/method_resolution.rs
@@ -8,9 +8,8 @@ use arrayvec::ArrayVec;
 use base_db::{CrateId, Edition};
 use chalk_ir::{cast::Cast, Mutability, UniverseIndex};
 use hir_def::{
-    item_scope::ItemScope, lang_item::LangItemTarget, nameres::DefMap, AssocItemId, BlockId,
-    ConstId, FunctionId, GenericDefId, HasModule, ImplId, ItemContainerId, Lookup, ModuleDefId,
-    ModuleId, TraitId,
+    lang_item::LangItemTarget, nameres::DefMap, AssocItemId, BlockId, FunctionId, GenericDefId,
+    HasModule, ImplId, ItemContainerId, Lookup, ModuleId, TraitId,
 };
 use hir_expand::name::Name;
 use rustc_hash::{FxHashMap, FxHashSet};
@@ -142,7 +141,6 @@ impl TraitImpls {
 
         let crate_def_map = db.crate_def_map(krate);
         impls.collect_def_map(db, &crate_def_map);
-        impls.shrink_to_fit();
 
         Arc::new(impls)
     }
@@ -156,32 +154,10 @@ impl TraitImpls {
 
         let block_def_map = db.block_def_map(block)?;
         impls.collect_def_map(db, &block_def_map);
-        impls.shrink_to_fit();
 
         Some(Arc::new(impls))
     }
 
-    pub(crate) fn trait_impls_in_deps_query(db: &dyn HirDatabase, krate: CrateId) -> Arc<Self> {
-        let _p = profile::span("trait_impls_in_deps_query");
-        let crate_graph = db.crate_graph();
-        let mut res = Self { map: FxHashMap::default() };
-
-        for krate in crate_graph.transitive_deps(krate) {
-            res.merge(&db.trait_impls_in_crate(krate));
-        }
-        res.shrink_to_fit();
-
-        Arc::new(res)
-    }
-
-    fn shrink_to_fit(&mut self) {
-        self.map.shrink_to_fit();
-        self.map.values_mut().for_each(|map| {
-            map.shrink_to_fit();
-            map.values_mut().for_each(Vec::shrink_to_fit);
-        });
-    }
-
     fn collect_def_map(&mut self, db: &dyn HirDatabase, def_map: &DefMap) {
         for (_module_id, module_data) in def_map.modules() {
             for impl_id in module_data.scope.impls() {
@@ -201,7 +177,7 @@ impl TraitImpls {
 
             // To better support custom derives, collect impls in all unnamed const items.
             // const _: () = { ... };
-            for konst in collect_unnamed_consts(db, &module_data.scope) {
+            for konst in module_data.scope.unnamed_consts() {
                 let body = db.body(konst.into());
                 for (_, block_def_map) in body.blocks(db.upcast()) {
                     self.collect_def_map(db, &block_def_map);
@@ -210,6 +186,18 @@ impl TraitImpls {
         }
     }
 
+    pub(crate) fn trait_impls_in_deps_query(db: &dyn HirDatabase, krate: CrateId) -> Arc<Self> {
+        let _p = profile::span("trait_impls_in_deps_query");
+        let crate_graph = db.crate_graph();
+        let mut res = Self { map: FxHashMap::default() };
+
+        for krate in crate_graph.transitive_deps(krate) {
+            res.merge(&db.trait_impls_in_crate(krate));
+        }
+
+        Arc::new(res)
+    }
+
     fn merge(&mut self, other: &Self) {
         for (trait_, other_map) in &other.map {
             let map = self.map.entry(*trait_).or_default();
@@ -275,7 +263,6 @@ impl InherentImpls {
 
         let crate_def_map = db.crate_def_map(krate);
         impls.collect_def_map(db, &crate_def_map);
-        impls.shrink_to_fit();
 
         return Arc::new(impls);
     }
@@ -287,17 +274,11 @@ impl InherentImpls {
         let mut impls = Self { map: FxHashMap::default() };
         if let Some(block_def_map) = db.block_def_map(block) {
             impls.collect_def_map(db, &block_def_map);
-            impls.shrink_to_fit();
             return Some(Arc::new(impls));
         }
         return None;
     }
 
-    fn shrink_to_fit(&mut self) {
-        self.map.values_mut().for_each(Vec::shrink_to_fit);
-        self.map.shrink_to_fit();
-    }
-
     fn collect_def_map(&mut self, db: &dyn HirDatabase, def_map: &DefMap) {
         for (_module_id, module_data) in def_map.modules() {
             for impl_id in module_data.scope.impls() {
@@ -316,7 +297,7 @@ impl InherentImpls {
 
             // To better support custom derives, collect impls in all unnamed const items.
             // const _: () = { ... };
-            for konst in collect_unnamed_consts(db, &module_data.scope) {
+            for konst in module_data.scope.unnamed_consts() {
                 let body = db.body(konst.into());
                 for (_, block_def_map) in body.blocks(db.upcast()) {
                     self.collect_def_map(db, &block_def_map);
@@ -337,34 +318,6 @@ impl InherentImpls {
     }
 }
 
-fn collect_unnamed_consts<'a>(
-    db: &'a dyn HirDatabase,
-    scope: &'a ItemScope,
-) -> impl Iterator<Item = ConstId> + 'a {
-    let unnamed_consts = scope.unnamed_consts();
-
-    // FIXME: Also treat consts named `_DERIVE_*` as unnamed, since synstructure generates those.
-    // Should be removed once synstructure stops doing that.
-    let synstructure_hack_consts = scope.values().filter_map(|(item, _)| match item {
-        ModuleDefId::ConstId(id) => {
-            let loc = id.lookup(db.upcast());
-            let item_tree = loc.id.item_tree(db.upcast());
-            if item_tree[loc.id.value]
-                .name
-                .as_ref()
-                .map_or(false, |n| n.to_smol_str().starts_with("_DERIVE_"))
-            {
-                Some(id)
-            } else {
-                None
-            }
-        }
-        _ => None,
-    });
-
-    unnamed_consts.chain(synstructure_hack_consts)
-}
-
 pub fn def_crates(
     db: &dyn HirDatabase,
     ty: &Ty,
@@ -541,10 +494,9 @@ pub fn iterate_method_candidates_dyn(
             // types*.
 
             let deref_chain = autoderef_method_receiver(db, krate, ty);
-            let mut deref_chains = stdx::slice_tails(&deref_chain);
-            deref_chains.try_for_each(|deref_chain| {
+            for i in 0..deref_chain.len() {
                 iterate_method_candidates_with_autoref(
-                    deref_chain,
+                    &deref_chain[i..],
                     db,
                     env.clone(),
                     krate,
@@ -552,8 +504,9 @@ pub fn iterate_method_candidates_dyn(
                     visible_from_module,
                     name,
                     callback,
-                )
-            })
+                )?;
+            }
+            ControlFlow::Continue(())
         }
         LookupMode::Path => {
             // No autoderef for path lookups
@@ -716,14 +669,15 @@ fn iterate_trait_method_candidates(
     // if ty is `dyn Trait`, the trait doesn't need to be in scope
     let inherent_trait =
         self_ty.value.dyn_trait().into_iter().flat_map(|t| all_super_traits(db.upcast(), t));
-    let env_traits = matches!(self_ty.value.kind(&Interner), TyKind::Placeholder(_))
-        // if we have `T: Trait` in the param env, the trait doesn't need to be in scope
-        .then(|| {
-            env.traits_in_scope_from_clauses(self_ty.value.clone())
+    let env_traits = match self_ty.value.kind(&Interner) {
+        TyKind::Placeholder(_) => {
+            // if we have `T: Trait` in the param env, the trait doesn't need to be in scope
+            env.traits_in_scope_from_clauses(&self_ty.value)
                 .flat_map(|t| all_super_traits(db.upcast(), t))
-        })
-        .into_iter()
-        .flatten();
+                .collect()
+        }
+        _ => Vec::new(),
+    };
     let traits = inherent_trait.chain(env_traits).chain(traits_in_scope.iter().copied());
 
     'traits: for t in traits {
@@ -746,10 +700,10 @@ fn iterate_trait_method_candidates(
         // trait, but if we find out it doesn't, we'll skip the rest of the
         // iteration
         let mut known_implemented = false;
-        for &(_, item) in data.items.iter() {
+        for (_name, item) in data.items.iter() {
             // Don't pass a `visible_from_module` down to `is_valid_candidate`,
             // since only inherent methods should be included into visibility checking.
-            if !is_valid_candidate(db, env.clone(), name, receiver_ty, item, self_ty, None) {
+            if !is_valid_candidate(db, env.clone(), name, receiver_ty, *item, self_ty, None) {
                 continue;
             }
             if !known_implemented {
@@ -760,7 +714,7 @@ fn iterate_trait_method_candidates(
             }
             known_implemented = true;
             // FIXME: we shouldn't be ignoring the binders here
-            callback(self_ty, item)?
+            callback(self_ty, *item)?
         }
     }
     ControlFlow::Continue(())
@@ -773,14 +727,18 @@ fn filter_inherent_impls_for_self_ty<'i>(
     // inherent methods on arrays are fingerprinted as [T; {unknown}], so we must also consider them when
     // resolving a method call on an array with a known len
     let array_impls = {
-        match self_ty.kind(&Interner) {
-            TyKind::Array(parameters, array_len) if !array_len.is_unknown() => {
+        if let TyKind::Array(parameters, array_len) = self_ty.kind(&Interner) {
+            if !array_len.is_unknown() {
                 let unknown_array_len_ty =
-                    TyKind::Array(parameters.clone(), consteval::usize_const(None));
+                    TyKind::Array(parameters.clone(), consteval::usize_const(None))
+                        .intern(&Interner);
 
-                Some(impls.for_self_ty(&unknown_array_len_ty.intern(&Interner)))
+                Some(impls.for_self_ty(&unknown_array_len_ty))
+            } else {
+                None
             }
-            _ => None,
+        } else {
+            None
         }
     }
     .into_iter()
diff --git a/crates/hir_ty/src/traits.rs b/crates/hir_ty/src/traits.rs
index 0d135651c7..4b89fd5a85 100644
--- a/crates/hir_ty/src/traits.rs
+++ b/crates/hir_ty/src/traits.rs
@@ -9,7 +9,6 @@ use chalk_solve::{logging_db::LoggingRustIrDatabase, Solver};
 use base_db::CrateId;
 use hir_def::{lang_item::LangItemTarget, TraitId};
 use stdx::panic_context;
-use syntax::SmolStr;
 
 use crate::{
     db::HirDatabase, AliasEq, AliasTy, Canonical, DomainGoal, Goal, Guidance, InEnvironment,
@@ -55,13 +54,17 @@ impl TraitEnvironment {
         }
     }
 
-    pub fn traits_in_scope_from_clauses<'a>(
+    pub(crate) fn traits_in_scope_from_clauses<'a>(
         &'a self,
-        ty: Ty,
+        ty: &'a Ty,
     ) -> impl Iterator<Item = TraitId> + 'a {
-        self.traits_from_clauses
-            .iter()
-            .filter_map(move |(self_ty, trait_id)| (*self_ty == ty).then(|| *trait_id))
+        self.traits_from_clauses.iter().filter_map(move |(self_ty, trait_id)| {
+            if self_ty == ty {
+                Some(*trait_id)
+            } else {
+                None
+            }
+        })
     }
 }
 
@@ -170,7 +173,7 @@ pub enum FnTrait {
 }
 
 impl FnTrait {
-    const fn lang_item_name(self) -> &'static str {
+    fn lang_item_name(self) -> &'static str {
         match self {
             FnTrait::FnOnce => "fn_once",
             FnTrait::FnMut => "fn_mut",
@@ -179,7 +182,7 @@ impl FnTrait {
     }
 
     pub fn get_id(&self, db: &dyn HirDatabase, krate: CrateId) -> Option<TraitId> {
-        let target = db.lang_item(krate, SmolStr::new_inline(self.lang_item_name()))?;
+        let target = db.lang_item(krate, self.lang_item_name().into())?;
         match target {
             LangItemTarget::TraitId(t) => Some(t),
             _ => None,
diff --git a/crates/hir_ty/src/utils.rs b/crates/hir_ty/src/utils.rs
index f36a5f8987..a0837377c6 100644
--- a/crates/hir_ty/src/utils.rs
+++ b/crates/hir_ty/src/utils.rs
@@ -18,8 +18,6 @@ use hir_def::{
 };
 use hir_expand::name::{name, Name};
 use rustc_hash::FxHashSet;
-use smallvec::{smallvec, SmallVec};
-use syntax::SmolStr;
 
 use crate::{
     db::HirDatabase, ChalkTraitId, Interner, Substitution, TraitRef, TraitRefExt, TyKind,
@@ -28,16 +26,16 @@ use crate::{
 
 pub(crate) fn fn_traits(db: &dyn DefDatabase, krate: CrateId) -> impl Iterator<Item = TraitId> {
     [
-        db.lang_item(krate, SmolStr::new_inline("fn")),
-        db.lang_item(krate, SmolStr::new_inline("fn_mut")),
-        db.lang_item(krate, SmolStr::new_inline("fn_once")),
+        db.lang_item(krate, "fn".into()),
+        db.lang_item(krate, "fn_mut".into()),
+        db.lang_item(krate, "fn_once".into()),
     ]
     .into_iter()
     .flatten()
     .flat_map(|it| it.as_trait())
 }
 
-fn direct_super_traits(db: &dyn DefDatabase, trait_: TraitId) -> SmallVec<[TraitId; 4]> {
+fn direct_super_traits(db: &dyn DefDatabase, trait_: TraitId) -> Vec<TraitId> {
     let resolver = trait_.resolver(db);
     // returning the iterator directly doesn't easily work because of
     // lifetime problems, but since there usually shouldn't be more than a
@@ -102,13 +100,13 @@ fn direct_super_trait_refs(db: &dyn HirDatabase, trait_ref: &TraitRef) -> Vec<Tr
 
 /// Returns an iterator over the whole super trait hierarchy (including the
 /// trait itself).
-pub fn all_super_traits(db: &dyn DefDatabase, trait_: TraitId) -> SmallVec<[TraitId; 4]> {
+pub fn all_super_traits(db: &dyn DefDatabase, trait_: TraitId) -> Vec<TraitId> {
     // we need to take care a bit here to avoid infinite loops in case of cycles
     // (i.e. if we have `trait A: B; trait B: A;`)
-
-    let mut result = smallvec![trait_];
+    let mut result = vec![trait_];
     let mut i = 0;
-    while let Some(&t) = result.get(i) {
+    while i < result.len() {
+        let t = result[i];
         // yeah this is quadratic, but trait hierarchies should be flat
         // enough that this doesn't matter
         for tt in direct_super_traits(db, t) {
diff --git a/crates/ide/src/expand_macro.rs b/crates/ide/src/expand_macro.rs
index 949744c01b..ee49732240 100644
--- a/crates/ide/src/expand_macro.rs
+++ b/crates/ide/src/expand_macro.rs
@@ -1,10 +1,9 @@
+use std::iter;
+
 use hir::Semantics;
-use ide_db::{
-    helpers::{insert_whitespace_into_node::insert_ws_into, pick_best_token},
-    RootDatabase,
-};
+use ide_db::{helpers::pick_best_token, RootDatabase};
 use itertools::Itertools;
-use syntax::{ast, ted, AstNode, SyntaxKind, SyntaxNode};
+use syntax::{ast, ted, AstNode, NodeOrToken, SyntaxKind, SyntaxNode, WalkEvent, T};
 
 use crate::FilePosition;
 
@@ -50,7 +49,7 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option<
             let expansions = sema.expand_derive_macro(&attr)?;
             Some(ExpandedMacro {
                 name: tt,
-                expansion: expansions.into_iter().map(insert_ws_into).join(""),
+                expansion: expansions.into_iter().map(insert_whitespaces).join(""),
             })
         } else {
             None
@@ -83,7 +82,7 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option<
     // FIXME:
     // macro expansion may lose all white space information
     // But we hope someday we can use ra_fmt for that
-    let expansion = insert_ws_into(expanded?).to_string();
+    let expansion = insert_whitespaces(expanded?);
     Some(ExpandedMacro { name: name.unwrap_or_else(|| "???".to_owned()), expansion })
 }
 
@@ -123,6 +122,84 @@ fn expand<T: AstNode>(
     Some(expanded)
 }
 
+// FIXME: It would also be cool to share logic here and in the mbe tests,
+// which are pretty unreadable at the moment.
+fn insert_whitespaces(syn: SyntaxNode) -> String {
+    use SyntaxKind::*;
+    let mut res = String::new();
+
+    let mut indent = 0;
+    let mut last: Option<SyntaxKind> = None;
+
+    for event in syn.preorder_with_tokens() {
+        let token = match event {
+            WalkEvent::Enter(NodeOrToken::Token(token)) => token,
+            WalkEvent::Leave(NodeOrToken::Node(node))
+                if matches!(node.kind(), ATTR | MATCH_ARM | STRUCT | ENUM | UNION | FN | IMPL) =>
+            {
+                res.push('\n');
+                res.extend(iter::repeat(" ").take(2 * indent));
+                continue;
+            }
+            _ => continue,
+        };
+        let is_next = |f: fn(SyntaxKind) -> bool, default| -> bool {
+            token.next_token().map(|it| f(it.kind())).unwrap_or(default)
+        };
+        let is_last =
+            |f: fn(SyntaxKind) -> bool, default| -> bool { last.map(f).unwrap_or(default) };
+
+        match token.kind() {
+            k if is_text(k) && is_next(|it| !it.is_punct(), true) => {
+                res.push_str(token.text());
+                res.push(' ');
+            }
+            L_CURLY if is_next(|it| it != R_CURLY, true) => {
+                indent += 1;
+                if is_last(is_text, false) {
+                    res.push(' ');
+                }
+                res.push_str("{\n");
+                res.extend(iter::repeat(" ").take(2 * indent));
+            }
+            R_CURLY if is_last(|it| it != L_CURLY, true) => {
+                indent = indent.saturating_sub(1);
+                res.push('\n');
+                res.extend(iter::repeat(" ").take(2 * indent));
+                res.push_str("}");
+            }
+            R_CURLY => {
+                res.push_str("}\n");
+                res.extend(iter::repeat(" ").take(2 * indent));
+            }
+            LIFETIME_IDENT if is_next(|it| it == IDENT || it == MUT_KW, true) => {
+                res.push_str(token.text());
+                res.push(' ');
+            }
+            AS_KW => {
+                res.push_str(token.text());
+                res.push(' ');
+            }
+            T![;] => {
+                res.push_str(";\n");
+                res.extend(iter::repeat(" ").take(2 * indent));
+            }
+            T![->] => res.push_str(" -> "),
+            T![=] => res.push_str(" = "),
+            T![=>] => res.push_str(" => "),
+            _ => res.push_str(token.text()),
+        }
+
+        last = Some(token.kind());
+    }
+
+    return res;
+
+    fn is_text(k: SyntaxKind) -> bool {
+        k.is_keyword() || k.is_literal() || k == IDENT
+    }
+}
+
 #[cfg(test)]
 mod tests {
     use expect_test::{expect, Expect};
diff --git a/crates/ide/src/goto_type_definition.rs b/crates/ide/src/goto_type_definition.rs
index 55cdb3200e..031c4af0de 100644
--- a/crates/ide/src/goto_type_definition.rs
+++ b/crates/ide/src/goto_type_definition.rs
@@ -75,7 +75,7 @@ pub(crate) fn goto_type_definition(
                 } else if let Some(trait_) = t.as_dyn_trait() {
                     push(trait_.into());
                 } else if let Some(traits) = t.as_impl_traits(db) {
-                    traits.for_each(|it| push(it.into()));
+                    traits.into_iter().for_each(|it| push(it.into()));
                 } else if let Some(trait_) = t.as_associated_type_parent_trait(db) {
                     push(trait_.into());
                 }
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs
index 7d5cfaa937..dd77dd3e4b 100644
--- a/crates/ide/src/hover.rs
+++ b/crates/ide/src/hover.rs
@@ -337,7 +337,7 @@ fn walk_and_push_ty(
         } else if let Some(trait_) = t.as_dyn_trait() {
             push_new_def(trait_.into());
         } else if let Some(traits) = t.as_impl_traits(db) {
-            traits.for_each(|it| push_new_def(it.into()));
+            traits.into_iter().for_each(|it| push_new_def(it.into()));
         } else if let Some(trait_) = t.as_associated_type_parent_trait(db) {
             push_new_def(trait_.into());
         }
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs
index 16a2b4a113..5bd9f054dd 100644
--- a/crates/ide/src/lib.rs
+++ b/crates/ide/src/lib.rs
@@ -555,6 +555,24 @@ impl Analysis {
             .unwrap_or_default())
     }
 
+    /// Computes assists (aka code actions aka intentions) for the given
+    /// position. If `resolve == false`, computes enough info to show the
+    /// lightbulb list in the editor, but doesn't compute actual edits, to
+    /// improve performance.
+    pub fn assists(
+        &self,
+        config: &AssistConfig,
+        resolve: AssistResolveStrategy,
+        frange: FileRange,
+    ) -> Cancellable<Vec<Assist>> {
+        self.with_db(|db| {
+            let ssr_assists = ssr::ssr_assists(db, &resolve, frange);
+            let mut acc = ide_assists::assists(db, config, resolve, frange);
+            acc.extend(ssr_assists.into_iter());
+            acc
+        })
+    }
+
     /// Computes the set of diagnostics for the given file.
     pub fn diagnostics(
         &self,
diff --git a/crates/ide/src/prime_caches.rs b/crates/ide/src/prime_caches.rs
index 5eba1d1e27..41e48c4ba5 100644
--- a/crates/ide/src/prime_caches.rs
+++ b/crates/ide/src/prime_caches.rs
@@ -4,7 +4,7 @@
 //! various caches, it's not really advanced at the moment.
 
 use hir::db::DefDatabase;
-use ide_db::base_db::{SourceDatabase, SourceDatabaseExt};
+use ide_db::base_db::{CrateGraph, CrateId, SourceDatabase, SourceDatabaseExt};
 use rustc_hash::FxHashSet;
 
 use crate::RootDatabase;
@@ -20,10 +20,7 @@ pub struct PrimeCachesProgress {
 pub(crate) fn prime_caches(db: &RootDatabase, cb: &(dyn Fn(PrimeCachesProgress) + Sync)) {
     let _p = profile::span("prime_caches");
     let graph = db.crate_graph();
-    // We're only interested in the workspace crates and the `ImportMap`s of their direct
-    // dependencies, though in practice the latter also compute the `DefMap`s.
-    // We don't prime transitive dependencies because they're generally not visible in
-    // the current workspace.
+    // We're only interested in the transitive dependencies of all workspace crates.
     let to_prime: FxHashSet<_> = graph
         .iter()
         .filter(|&id| {
@@ -31,18 +28,32 @@ pub(crate) fn prime_caches(db: &RootDatabase, cb: &(dyn Fn(PrimeCachesProgress)
             let root_id = db.file_source_root(file_id);
             !db.source_root(root_id).is_library
         })
-        .flat_map(|id| graph[id].dependencies.iter().map(|krate| krate.crate_id))
+        .flat_map(|id| graph.transitive_deps(id))
         .collect();
 
+    let topo = toposort(&graph, &to_prime);
+
     // FIXME: This would be easy to parallelize, since it's in the ideal ordering for that.
     // Unfortunately rayon prevents panics from propagation out of a `scope`, which breaks
     // cancellation, so we cannot use rayon.
-    let n_total = to_prime.len();
-    for (n_done, &crate_id) in to_prime.iter().enumerate() {
+    for (i, &crate_id) in topo.iter().enumerate() {
         let crate_name = graph[crate_id].display_name.as_deref().unwrap_or_default().to_string();
 
-        cb(PrimeCachesProgress { on_crate: crate_name, n_done, n_total });
-        // This also computes the DefMap
+        cb(PrimeCachesProgress { on_crate: crate_name, n_done: i, n_total: topo.len() });
+        db.crate_def_map(crate_id);
         db.import_map(crate_id);
     }
 }
+
+fn toposort(graph: &CrateGraph, crates: &FxHashSet<CrateId>) -> Vec<CrateId> {
+    // Just subset the full topologically sorted set for simplicity.
+
+    let all = graph.crates_in_topological_order();
+    let mut result = Vec::with_capacity(crates.len());
+    for krate in all {
+        if crates.contains(&krate) {
+            result.push(krate);
+        }
+    }
+    result
+}
diff --git a/crates/ide_assists/src/handlers/add_missing_impl_members.rs b/crates/ide_assists/src/handlers/add_missing_impl_members.rs
index a10eca10d1..a145598c79 100644
--- a/crates/ide_assists/src/handlers/add_missing_impl_members.rs
+++ b/crates/ide_assists/src/handlers/add_missing_impl_members.rs
@@ -1,5 +1,5 @@
 use hir::HasSource;
-use ide_db::{helpers::insert_whitespace_into_node::insert_ws_into, traits::resolve_target_trait};
+use ide_db::traits::resolve_target_trait;
 use syntax::ast::{self, make, AstNode};
 
 use crate::{
@@ -105,7 +105,7 @@ fn add_missing_impl_members_inner(
     let trait_ = resolve_target_trait(&ctx.sema, &impl_def)?;
 
     let missing_items = filter_assoc_items(
-        &ctx.sema,
+        ctx.db(),
         &ide_db::traits::get_missing_assoc_items(&ctx.sema, &impl_def),
         mode,
     );
@@ -117,17 +117,6 @@ fn add_missing_impl_members_inner(
     let target = impl_def.syntax().text_range();
     acc.add(AssistId(assist_id, AssistKind::QuickFix), label, target, |builder| {
         let target_scope = ctx.sema.scope(impl_def.syntax());
-        let missing_items = missing_items
-            .into_iter()
-            .map(|it| {
-                if ctx.sema.hir_file_for(it.syntax()).is_macro() {
-                    if let Some(it) = ast::AssocItem::cast(insert_ws_into(it.syntax().clone())) {
-                        return it;
-                    }
-                }
-                it.clone_for_update()
-            })
-            .collect();
         let (new_impl_def, first_new_item) = add_trait_assoc_items_to_impl(
             &ctx.sema,
             missing_items,
@@ -901,44 +890,6 @@ impl Default for Foo {
         Self(Default::default())
     }
 }
-"#,
-        )
-    }
-
-    #[test]
-    fn test_from_macro() {
-        check_assist(
-            add_missing_default_members,
-            r#"
-macro_rules! foo {
-    () => {
-        trait FooB {
-            fn foo<'lt>(&'lt self) {}
-        }
-    }
-}
-foo!();
-struct Foo(usize);
-
-impl FooB for Foo {
-    $0
-}
-"#,
-            r#"
-macro_rules! foo {
-    () => {
-        trait FooB {
-            fn foo<'lt>(&'lt self) {}
-        }
-    }
-}
-foo!();
-struct Foo(usize);
-
-impl FooB for Foo {
-    $0fn foo< 'lt>(& 'lt self){}
-
-}
 "#,
         )
     }
diff --git a/crates/ide_assists/src/handlers/convert_iter_for_each_to_for.rs b/crates/ide_assists/src/handlers/convert_iter_for_each_to_for.rs
index 7fbbdb4f5e..eca6d047a1 100644
--- a/crates/ide_assists/src/handlers/convert_iter_for_each_to_for.rs
+++ b/crates/ide_assists/src/handlers/convert_iter_for_each_to_for.rs
@@ -148,7 +148,7 @@ fn is_ref_and_impls_iter_method(
     let ty = sema.type_of_expr(&expr_behind_ref)?.adjusted();
     let scope = sema.scope(iterable.syntax());
     let krate = scope.module()?.krate();
-    let traits_in_scope = scope.visible_traits();
+    let traits_in_scope = scope.traits_in_scope();
     let iter_trait = FamousDefs(sema, Some(krate)).core_iter_Iterator()?;
 
     let has_wanted_method = ty
diff --git a/crates/ide_assists/src/handlers/extract_function.rs b/crates/ide_assists/src/handlers/extract_function.rs
index 63b0a91ce5..e2845bc58b 100644
--- a/crates/ide_assists/src/handlers/extract_function.rs
+++ b/crates/ide_assists/src/handlers/extract_function.rs
@@ -480,7 +480,7 @@ impl FunctionBody {
             .statements()
             .map(|stmt| stmt.syntax().text_range())
             .filter(|&stmt| selected.intersect(stmt).filter(|it| !it.is_empty()).is_some())
-            .reduce(|acc, stmt| acc.cover(stmt));
+            .fold1(|acc, stmt| acc.cover(stmt));
         if let Some(tail_range) = parent
             .tail_expr()
             .map(|it| it.syntax().text_range())
diff --git a/crates/ide_assists/src/handlers/generate_delegate_methods.rs b/crates/ide_assists/src/handlers/generate_delegate_methods.rs
index 31b438b15e..090dab946a 100644
--- a/crates/ide_assists/src/handlers/generate_delegate_methods.rs
+++ b/crates/ide_assists/src/handlers/generate_delegate_methods.rs
@@ -47,18 +47,18 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext)
     let strukt_name = strukt.name()?;
     let current_module = ctx.sema.scope(strukt.syntax()).module()?;
 
-    let (field_name, field_ty, target) = match ctx.find_node_at_offset::<ast::RecordField>() {
+    let (field_name, field_ty) = match ctx.find_node_at_offset::<ast::RecordField>() {
         Some(field) => {
             let field_name = field.name()?;
             let field_ty = field.ty()?;
-            (format!("{}", field_name), field_ty, field.syntax().text_range())
+            (format!("{}", field_name), field_ty)
         }
         None => {
             let field = ctx.find_node_at_offset::<ast::TupleField>()?;
             let field_list = ctx.find_node_at_offset::<ast::TupleFieldList>()?;
             let field_list_index = field_list.fields().position(|it| it == field)?;
             let field_ty = field.ty()?;
-            (format!("{}", field_list_index), field_ty, field.syntax().text_range())
+            (format!("{}", field_list_index), field_ty)
         }
     };
 
@@ -74,6 +74,7 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext)
         Option::<()>::None
     });
 
+    let target = field_ty.syntax().text_range();
     for method in methods {
         let adt = ast::Adt::Struct(strukt.clone());
         let name = method.name(ctx.db()).to_string();
diff --git a/crates/ide_assists/src/handlers/generate_documentation_template.rs b/crates/ide_assists/src/handlers/generate_documentation_template.rs
deleted file mode 100644
index cfc7e9d043..0000000000
--- a/crates/ide_assists/src/handlers/generate_documentation_template.rs
+++ /dev/null
@@ -1,1091 +0,0 @@
-use hir::{AsAssocItem, HasVisibility, ModuleDef, Visibility};
-use ide_db::assists::{AssistId, AssistKind};
-use itertools::Itertools;
-use stdx::to_lower_snake_case;
-use syntax::{
-    ast::{self, edit::IndentLevel, HasDocComments, HasName},
-    AstNode,
-};
-
-use crate::assist_context::{AssistContext, Assists};
-
-// Assist: generate_documentation_template
-//
-// Adds a documentation template above a function definition / declaration.
-//
-// ```
-// pub fn my_$0func(a: i32, b: i32) -> Result<(), std::io::Error> {
-//     unimplemented!()
-// }
-// ```
-// ->
-// ```
-// /// .
-// ///
-// /// # Examples
-// ///
-// /// ```
-// /// use test::my_func;
-// ///
-// /// assert_eq!(my_func(a, b), );
-// /// ```
-// ///
-// /// # Errors
-// ///
-// /// This function will return an error if .
-// pub fn my_func(a: i32, b: i32) -> Result<(), std::io::Error> {
-//     unimplemented!()
-// }
-// ```
-pub(crate) fn generate_documentation_template(
-    acc: &mut Assists,
-    ctx: &AssistContext,
-) -> Option<()> {
-    let name = ctx.find_node_at_offset::<ast::Name>()?;
-    let ast_func = name.syntax().parent().and_then(ast::Fn::cast)?;
-    if is_in_trait_impl(&ast_func, ctx)
-        || !is_public(&ast_func, ctx)?
-        || ast_func.doc_comments().next().is_some()
-    {
-        return None;
-    }
-
-    let parent_syntax = ast_func.syntax();
-    let text_range = parent_syntax.text_range();
-    let indent_level = IndentLevel::from_node(&parent_syntax);
-
-    acc.add(
-        AssistId("generate_documentation_template", AssistKind::Generate),
-        "Generate a documentation template",
-        text_range,
-        |builder| {
-            let mut doc_lines = Vec::new();
-            // Introduction / short function description before the sections
-            doc_lines.push(introduction_builder(&ast_func, ctx));
-            // Then come the sections
-            if let Some(mut lines) = examples_builder(&ast_func, ctx) {
-                doc_lines.push("".into());
-                doc_lines.append(&mut lines);
-            }
-            for section_builder in [panics_builder, errors_builder, safety_builder] {
-                if let Some(mut lines) = section_builder(&ast_func) {
-                    doc_lines.push("".into());
-                    doc_lines.append(&mut lines);
-                }
-            }
-            builder.insert(text_range.start(), documentation_from_lines(doc_lines, indent_level));
-        },
-    )
-}
-
-/// Builds an introduction, trying to be smart if the function is `::new()`
-fn introduction_builder(ast_func: &ast::Fn, ctx: &AssistContext) -> String {
-    || -> Option<String> {
-        let hir_func = ctx.sema.to_def(ast_func)?;
-        let container = hir_func.as_assoc_item(ctx.db())?.container(ctx.db());
-        if let hir::AssocItemContainer::Impl(implementation) = container {
-            let ret_ty = hir_func.ret_type(ctx.db());
-            let self_ty = implementation.self_ty(ctx.db());
-
-            let is_new = ast_func.name()?.to_string() == "new";
-            match is_new && ret_ty == self_ty {
-                true => Some(format!("Creates a new [`{}`].", self_type(ast_func)?)),
-                false => None,
-            }
-        } else {
-            None
-        }
-    }()
-    .unwrap_or_else(|| ".".into())
-}
-
-/// Builds an `# Examples` section. An option is returned to be able to manage an error in the AST.
-fn examples_builder(ast_func: &ast::Fn, ctx: &AssistContext) -> Option<Vec<String>> {
-    let mut lines = string_vec_from(&["# Examples", "", "```"]);
-    if is_in_trait_def(ast_func, ctx) {
-        lines.push("// Example template not implemented for trait functions".into());
-    } else {
-        lines.append(&mut gen_ex_template(ast_func, ctx)?)
-    };
-
-    lines.push("```".into());
-    Some(lines)
-}
-
-/// Builds an optional `# Panics` section
-fn panics_builder(ast_func: &ast::Fn) -> Option<Vec<String>> {
-    match can_panic(ast_func) {
-        Some(true) => Some(string_vec_from(&["# Panics", "", "Panics if ."])),
-        _ => None,
-    }
-}
-
-/// Builds an optional `# Errors` section
-fn errors_builder(ast_func: &ast::Fn) -> Option<Vec<String>> {
-    match return_type(ast_func)?.to_string().contains("Result") {
-        true => Some(string_vec_from(&["# Errors", "", "This function will return an error if ."])),
-        false => None,
-    }
-}
-
-/// Builds an optional `# Safety` section
-fn safety_builder(ast_func: &ast::Fn) -> Option<Vec<String>> {
-    let is_unsafe = ast_func.unsafe_token().is_some();
-    match is_unsafe {
-        true => Some(string_vec_from(&["# Safety", "", "."])),
-        false => None,
-    }
-}
-
-/// Generates an example template
-fn gen_ex_template(ast_func: &ast::Fn, ctx: &AssistContext) -> Option<Vec<String>> {
-    let mut lines = Vec::new();
-    let is_unsafe = ast_func.unsafe_token().is_some();
-    let param_list = ast_func.param_list()?;
-    let ref_mut_params = ref_mut_params(&param_list);
-    let self_name: Option<String> = self_name(ast_func);
-
-    lines.push(format!("use {};", build_path(ast_func, ctx)?));
-    lines.push("".into());
-    if let Some(self_definition) = self_definition(ast_func, self_name.as_deref()) {
-        lines.push(self_definition);
-    }
-    for param_name in &ref_mut_params {
-        lines.push(format!("let mut {} = ;", param_name))
-    }
-    // Call the function, check result
-    let function_call = function_call(ast_func, &param_list, self_name.as_deref(), is_unsafe)?;
-    if returns_a_value(ast_func, ctx) {
-        if count_parameters(&param_list) < 3 {
-            lines.push(format!("assert_eq!({}, );", function_call));
-        } else {
-            lines.push(format!("let result = {};", function_call));
-            lines.push("assert_eq!(result, );".into());
-        }
-    } else {
-        lines.push(format!("{};", function_call));
-    }
-    // Check the mutated values
-    if is_ref_mut_self(ast_func) == Some(true) {
-        lines.push(format!("assert_eq!({}, );", self_name?));
-    }
-    for param_name in &ref_mut_params {
-        lines.push(format!("assert_eq!({}, );", param_name));
-    }
-    Some(lines)
-}
-
-/// Checks if the function is public / exported
-fn is_public(ast_func: &ast::Fn, ctx: &AssistContext) -> Option<bool> {
-    let hir_func = ctx.sema.to_def(ast_func)?;
-    Some(
-        hir_func.visibility(ctx.db()) == Visibility::Public
-            && all_parent_mods_public(&hir_func, ctx),
-    )
-}
-
-/// Checks that all parent modules of the function are public / exported
-fn all_parent_mods_public(hir_func: &hir::Function, ctx: &AssistContext) -> bool {
-    let mut module = hir_func.module(ctx.db());
-    loop {
-        if let Some(parent) = module.parent(ctx.db()) {
-            match ModuleDef::from(module).visibility(ctx.db()) {
-                Visibility::Public => module = parent,
-                _ => break false,
-            }
-        } else {
-            break true;
-        }
-    }
-}
-
-/// Returns the name of the current crate
-fn crate_name(ast_func: &ast::Fn, ctx: &AssistContext) -> Option<String> {
-    let krate = ctx.sema.scope(&ast_func.syntax()).module()?.krate();
-    Some(krate.display_name(ctx.db())?.to_string())
-}
-
-/// `None` if function without a body; some bool to guess if function can panic
-fn can_panic(ast_func: &ast::Fn) -> Option<bool> {
-    let body = ast_func.body()?.to_string();
-    let can_panic = body.contains("panic!(")
-        // FIXME it would be better to not match `debug_assert*!` macro invocations
-        || body.contains("assert!(")
-        || body.contains(".unwrap()")
-        || body.contains(".expect(");
-    Some(can_panic)
-}
-
-/// Helper function to get the name that should be given to `self` arguments
-fn self_name(ast_func: &ast::Fn) -> Option<String> {
-    self_partial_type(ast_func).map(|name| to_lower_snake_case(&name))
-}
-
-/// Heper function to get the name of the type of `self`
-fn self_type(ast_func: &ast::Fn) -> Option<String> {
-    ast_func
-        .syntax()
-        .ancestors()
-        .find_map(ast::Impl::cast)
-        .and_then(|i| i.self_ty())
-        .map(|t| (t.to_string()))
-}
-
-/// Heper function to get the name of the type of `self` without generic arguments
-fn self_partial_type(ast_func: &ast::Fn) -> Option<String> {
-    let mut self_type = self_type(ast_func)?;
-    if let Some(idx) = self_type.find(|c| ['<', ' '].contains(&c)) {
-        self_type.truncate(idx);
-    }
-    Some(self_type)
-}
-
-/// Helper function to determine if the function is in a trait implementation
-fn is_in_trait_impl(ast_func: &ast::Fn, ctx: &AssistContext) -> bool {
-    ctx.sema
-        .to_def(ast_func)
-        .and_then(|hir_func| hir_func.as_assoc_item(ctx.db()))
-        .and_then(|assoc_item| assoc_item.containing_trait_impl(ctx.db()))
-        .is_some()
-}
-
-/// Helper function to determine if the function definition is in a trait definition
-fn is_in_trait_def(ast_func: &ast::Fn, ctx: &AssistContext) -> bool {
-    ctx.sema
-        .to_def(ast_func)
-        .and_then(|hir_func| hir_func.as_assoc_item(ctx.db()))
-        .and_then(|assoc_item| assoc_item.containing_trait(ctx.db()))
-        .is_some()
-}
-
-/// Returns `None` if no `self` at all, `Some(true)` if there is `&mut self` else `Some(false)`
-fn is_ref_mut_self(ast_func: &ast::Fn) -> Option<bool> {
-    let self_param = ast_func.param_list()?.self_param()?;
-    Some(self_param.mut_token().is_some() && self_param.amp_token().is_some())
-}
-
-/// Helper function to define an variable to be the `self` argument
-fn self_definition(ast_func: &ast::Fn, self_name: Option<&str>) -> Option<String> {
-    let definition = match is_ref_mut_self(ast_func)? {
-        true => format!("let mut {} = ;", self_name?),
-        false => format!("let {} = ;", self_name?),
-    };
-    Some(definition)
-}
-
-/// Helper function to determine if a parameter is `&mut`
-fn is_a_ref_mut_param(param: &ast::Param) -> bool {
-    match param.ty() {
-        Some(ast::Type::RefType(param_ref)) => param_ref.mut_token().is_some(),
-        _ => false,
-    }
-}
-
-/// Helper function to build the list of `&mut` parameters
-fn ref_mut_params(param_list: &ast::ParamList) -> Vec<String> {
-    param_list
-        .params()
-        .filter_map(|param| match is_a_ref_mut_param(&param) {
-            // Maybe better filter the param name (to do this maybe extract a function from
-            // `arguments_from_params`?) in case of a `mut a: &mut T`. Anyway managing most (not
-            // all) cases might be enough, the goal is just to produce a template.
-            true => Some(param.pat()?.to_string()),
-            false => None,
-        })
-        .collect()
-}
-
-/// Helper function to build the comma-separated list of arguments of the function
-fn arguments_from_params(param_list: &ast::ParamList) -> String {
-    let args_iter = param_list.params().map(|param| match param.pat() {
-        // To avoid `mut` in the function call (which would be a nonsense), `Pat` should not be
-        // written as is so its variants must be managed independently. Other variants (for
-        // instance `TuplePat`) could be managed later.
-        Some(ast::Pat::IdentPat(ident_pat)) => match ident_pat.name() {
-            Some(name) => match is_a_ref_mut_param(&param) {
-                true => format!("&mut {}", name.to_string()),
-                false => name.to_string(),
-            },
-            None => "_".to_string(),
-        },
-        _ => "_".to_string(),
-    });
-    Itertools::intersperse(args_iter, ", ".to_string()).collect()
-}
-
-/// Helper function to build a function call. `None` if expected `self_name` was not provided
-fn function_call(
-    ast_func: &ast::Fn,
-    param_list: &ast::ParamList,
-    self_name: Option<&str>,
-    is_unsafe: bool,
-) -> Option<String> {
-    let name = ast_func.name()?;
-    let arguments = arguments_from_params(&param_list);
-    let function_call = if param_list.self_param().is_some() {
-        format!("{}.{}({})", self_name?, name, arguments)
-    } else if let Some(implementation) = self_partial_type(ast_func) {
-        format!("{}::{}({})", implementation, name, arguments)
-    } else {
-        format!("{}({})", name, arguments)
-    };
-    match is_unsafe {
-        true => Some(format!("unsafe {{ {} }}", function_call)),
-        false => Some(function_call),
-    }
-}
-
-/// Helper function to count the parameters including `self`
-fn count_parameters(param_list: &ast::ParamList) -> usize {
-    param_list.params().count() + if param_list.self_param().is_some() { 1 } else { 0 }
-}
-
-/// Helper function to transform lines of documentation into a Rust code documentation
-fn documentation_from_lines(doc_lines: Vec<String>, indent_level: IndentLevel) -> String {
-    let mut result = String::new();
-    for doc_line in doc_lines {
-        result.push_str("///");
-        if !doc_line.is_empty() {
-            result.push(' ');
-            result.push_str(&doc_line);
-        }
-        result.push('\n');
-        result.push_str(&indent_level.to_string());
-    }
-    result
-}
-
-/// Helper function to transform an array of borrowed strings to an owned `Vec<String>`
-fn string_vec_from(string_array: &[&str]) -> Vec<String> {
-    string_array.iter().map(|&s| s.to_owned()).collect()
-}
-
-/// Helper function to build the path of the module in the which is the node
-fn build_path(ast_func: &ast::Fn, ctx: &AssistContext) -> Option<String> {
-    let crate_name = crate_name(ast_func, ctx)?;
-    let leaf = self_partial_type(ast_func)
-        .or_else(|| ast_func.name().map(|n| n.to_string()))
-        .unwrap_or_else(|| "*".into());
-    let module_def: ModuleDef = ctx.sema.to_def(ast_func)?.module(ctx.db()).into();
-    match module_def.canonical_path(ctx.db()) {
-        Some(path) => Some(format!("{}::{}::{}", crate_name, path, leaf)),
-        None => Some(format!("{}::{}", crate_name, leaf)),
-    }
-}
-
-/// Helper function to get the return type of a function
-fn return_type(ast_func: &ast::Fn) -> Option<ast::Type> {
-    ast_func.ret_type()?.ty()
-}
-
-/// Helper function to determine if the function returns some data
-fn returns_a_value(ast_func: &ast::Fn, ctx: &AssistContext) -> bool {
-    ctx.sema
-        .to_def(ast_func)
-        .map(|hir_func| hir_func.ret_type(ctx.db()))
-        .map(|ret_ty| !ret_ty.is_unit() && !ret_ty.is_never())
-        .unwrap_or(false)
-}
-
-#[cfg(test)]
-mod tests {
-    use crate::tests::{check_assist, check_assist_not_applicable};
-
-    use super::*;
-
-    #[test]
-    fn not_applicable_on_function_calls() {
-        check_assist_not_applicable(
-            generate_documentation_template,
-            r#"
-fn hello_world() {}
-fn calls_hello_world() {
-    hello_world$0();
-}
-"#,
-        )
-    }
-
-    #[test]
-    fn not_applicable_in_trait_impl() {
-        check_assist_not_applicable(
-            generate_documentation_template,
-            r#"
-trait MyTrait {}
-struct MyStruct;
-impl MyTrait for MyStruct {
-    fn hello_world$0();
-}
-"#,
-        )
-    }
-
-    #[test]
-    fn not_applicable_if_function_is_private() {
-        check_assist_not_applicable(generate_documentation_template, r#"fn priv$0ate() {}"#);
-    }
-
-    #[test]
-    fn not_applicable_if_function_is_pub_crate() {
-        check_assist_not_applicable(
-            generate_documentation_template,
-            r#"pub(crate) fn pri$0vate() {}"#,
-        );
-    }
-
-    #[test]
-    fn not_applicable_if_function_is_in_private_mod() {
-        check_assist_not_applicable(
-            generate_documentation_template,
-            r#"
-mod PrivateModule {
-    pub fn pri$0vate() {}
-}"#,
-        );
-    }
-
-    #[test]
-    fn not_applicable_if_function_is_in_pub_crate_mod() {
-        check_assist_not_applicable(
-            generate_documentation_template,
-            r#"
-pub(crate) mod PrivateModule {
-    pub fn pr$0ivate() {}
-}"#,
-        );
-    }
-
-    #[test]
-    fn not_applicable_if_function_is_in_non_public_mod_is_recursive() {
-        check_assist_not_applicable(
-            generate_documentation_template,
-            r#"
-mod ParentPrivateModule {
-    pub mod PrivateModule {
-        pub fn pr$0ivate() {}
-    }
-}"#,
-        );
-    }
-
-    #[test]
-    fn not_applicable_if_function_already_documented() {
-        check_assist_not_applicable(
-            generate_documentation_template,
-            r#"
-/// Some documentation here
-pub fn $0documented_function() {}
-"#,
-        );
-    }
-
-    #[test]
-    fn supports_noop_function() {
-        check_assist(
-            generate_documentation_template,
-            r#"
-pub fn no$0op() {}
-"#,
-            r#"
-/// .
-///
-/// # Examples
-///
-/// ```
-/// use test::noop;
-///
-/// noop();
-/// ```
-pub fn noop() {}
-"#,
-        );
-    }
-
-    #[test]
-    fn supports_a_parameter() {
-        check_assist(
-            generate_documentation_template,
-            r#"
-pub fn no$0op_with_param(_a: i32) {}
-"#,
-            r#"
-/// .
-///
-/// # Examples
-///
-/// ```
-/// use test::noop_with_param;
-///
-/// noop_with_param(_a);
-/// ```
-pub fn noop_with_param(_a: i32) {}
-"#,
-        );
-    }
-
-    #[test]
-    fn detects_unsafe_function() {
-        check_assist(
-            generate_documentation_template,
-            r#"
-pub unsafe fn no$0op_unsafe() {}
-"#,
-            r#"
-/// .
-///
-/// # Examples
-///
-/// ```
-/// use test::noop_unsafe;
-///
-/// unsafe { noop_unsafe() };
-/// ```
-///
-/// # Safety
-///
-/// .
-pub unsafe fn noop_unsafe() {}
-"#,
-        );
-    }
-
-    #[test]
-    fn guesses_panic_macro_can_panic() {
-        check_assist(
-            generate_documentation_template,
-            r#"
-pub fn panic$0s_if(a: bool) {
-    if a {
-        panic!();
-    }
-}
-"#,
-            r#"
-/// .
-///
-/// # Examples
-///
-/// ```
-/// use test::panics_if;
-///
-/// panics_if(a);
-/// ```
-///
-/// # Panics
-///
-/// Panics if .
-pub fn panics_if(a: bool) {
-    if a {
-        panic!();
-    }
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn guesses_assert_macro_can_panic() {
-        check_assist(
-            generate_documentation_template,
-            r#"
-pub fn $0panics_if_not(a: bool) {
-    assert!(a == true);
-}
-"#,
-            r#"
-/// .
-///
-/// # Examples
-///
-/// ```
-/// use test::panics_if_not;
-///
-/// panics_if_not(a);
-/// ```
-///
-/// # Panics
-///
-/// Panics if .
-pub fn panics_if_not(a: bool) {
-    assert!(a == true);
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn guesses_unwrap_can_panic() {
-        check_assist(
-            generate_documentation_template,
-            r#"
-pub fn $0panics_if_none(a: Option<()>) {
-    a.unwrap();
-}
-"#,
-            r#"
-/// .
-///
-/// # Examples
-///
-/// ```
-/// use test::panics_if_none;
-///
-/// panics_if_none(a);
-/// ```
-///
-/// # Panics
-///
-/// Panics if .
-pub fn panics_if_none(a: Option<()>) {
-    a.unwrap();
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn guesses_expect_can_panic() {
-        check_assist(
-            generate_documentation_template,
-            r#"
-pub fn $0panics_if_none2(a: Option<()>) {
-    a.expect("Bouh!");
-}
-"#,
-            r#"
-/// .
-///
-/// # Examples
-///
-/// ```
-/// use test::panics_if_none2;
-///
-/// panics_if_none2(a);
-/// ```
-///
-/// # Panics
-///
-/// Panics if .
-pub fn panics_if_none2(a: Option<()>) {
-    a.expect("Bouh!");
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn checks_output_in_example() {
-        check_assist(
-            generate_documentation_template,
-            r#"
-pub fn returns_a_value$0() -> i32 {
-    0
-}
-"#,
-            r#"
-/// .
-///
-/// # Examples
-///
-/// ```
-/// use test::returns_a_value;
-///
-/// assert_eq!(returns_a_value(), );
-/// ```
-pub fn returns_a_value() -> i32 {
-    0
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn detects_result_output() {
-        check_assist(
-            generate_documentation_template,
-            r#"
-pub fn returns_a_result$0() -> Result<i32, std::io::Error> {
-    Ok(0)
-}
-"#,
-            r#"
-/// .
-///
-/// # Examples
-///
-/// ```
-/// use test::returns_a_result;
-///
-/// assert_eq!(returns_a_result(), );
-/// ```
-///
-/// # Errors
-///
-/// This function will return an error if .
-pub fn returns_a_result() -> Result<i32, std::io::Error> {
-    Ok(0)
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn checks_ref_mut_in_example() {
-        check_assist(
-            generate_documentation_template,
-            r#"
-pub fn modifies_a_value$0(a: &mut i32) {
-    *a = 0;
-}
-"#,
-            r#"
-/// .
-///
-/// # Examples
-///
-/// ```
-/// use test::modifies_a_value;
-///
-/// let mut a = ;
-/// modifies_a_value(&mut a);
-/// assert_eq!(a, );
-/// ```
-pub fn modifies_a_value(a: &mut i32) {
-    *a = 0;
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn stores_result_if_at_least_3_params() {
-        check_assist(
-            generate_documentation_template,
-            r#"
-pub fn sum3$0(a: i32, b: i32, c: i32) -> i32 {
-    a + b + c
-}
-"#,
-            r#"
-/// .
-///
-/// # Examples
-///
-/// ```
-/// use test::sum3;
-///
-/// let result = sum3(a, b, c);
-/// assert_eq!(result, );
-/// ```
-pub fn sum3(a: i32, b: i32, c: i32) -> i32 {
-    a + b + c
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn supports_fn_in_mods() {
-        check_assist(
-            generate_documentation_template,
-            r#"
-pub mod a {
-    pub mod b {
-        pub fn no$0op() {}
-    }
-}
-"#,
-            r#"
-pub mod a {
-    pub mod b {
-        /// .
-        ///
-        /// # Examples
-        ///
-        /// ```
-        /// use test::a::b::noop;
-        ///
-        /// noop();
-        /// ```
-        pub fn noop() {}
-    }
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn supports_fn_in_impl() {
-        check_assist(
-            generate_documentation_template,
-            r#"
-pub struct MyStruct;
-impl MyStruct {
-    pub fn no$0op() {}
-}
-"#,
-            r#"
-pub struct MyStruct;
-impl MyStruct {
-    /// .
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// use test::MyStruct;
-    ///
-    /// MyStruct::noop();
-    /// ```
-    pub fn noop() {}
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn supports_fn_in_trait() {
-        check_assist(
-            generate_documentation_template,
-            r#"
-pub trait MyTrait {
-    fn fun$0ction_trait();
-}
-"#,
-            r#"
-pub trait MyTrait {
-    /// .
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// // Example template not implemented for trait functions
-    /// ```
-    fn function_trait();
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn supports_unsafe_fn_in_trait() {
-        check_assist(
-            generate_documentation_template,
-            r#"
-pub trait MyTrait {
-    unsafe fn unsafe_funct$0ion_trait();
-}
-"#,
-            r#"
-pub trait MyTrait {
-    /// .
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// // Example template not implemented for trait functions
-    /// ```
-    ///
-    /// # Safety
-    ///
-    /// .
-    unsafe fn unsafe_function_trait();
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn supports_fn_in_trait_with_default_panicking() {
-        check_assist(
-            generate_documentation_template,
-            r#"
-pub trait MyTrait {
-    fn function_trait_with_$0default_panicking() {
-        panic!()
-    }
-}
-"#,
-            r#"
-pub trait MyTrait {
-    /// .
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// // Example template not implemented for trait functions
-    /// ```
-    ///
-    /// # Panics
-    ///
-    /// Panics if .
-    fn function_trait_with_default_panicking() {
-        panic!()
-    }
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn supports_fn_in_trait_returning_result() {
-        check_assist(
-            generate_documentation_template,
-            r#"
-pub trait MyTrait {
-    fn function_tr$0ait_returning_result() -> Result<(), std::io::Error>;
-}
-"#,
-            r#"
-pub trait MyTrait {
-    /// .
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// // Example template not implemented for trait functions
-    /// ```
-    ///
-    /// # Errors
-    ///
-    /// This function will return an error if .
-    fn function_trait_returning_result() -> Result<(), std::io::Error>;
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn detects_new() {
-        check_assist(
-            generate_documentation_template,
-            r#"
-#[derive(Debug, PartialEq)]
-pub struct MyGenericStruct<T> {
-    pub x: T,
-}
-impl<T> MyGenericStruct<T> {
-    pub fn new$0(x: T) -> MyGenericStruct<T> {
-        MyGenericStruct { x }
-    }
-}
-"#,
-            r#"
-#[derive(Debug, PartialEq)]
-pub struct MyGenericStruct<T> {
-    pub x: T,
-}
-impl<T> MyGenericStruct<T> {
-    /// Creates a new [`MyGenericStruct<T>`].
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// use test::MyGenericStruct;
-    ///
-    /// assert_eq!(MyGenericStruct::new(x), );
-    /// ```
-    pub fn new(x: T) -> MyGenericStruct<T> {
-        MyGenericStruct { x }
-    }
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn detects_new_with_self() {
-        check_assist(
-            generate_documentation_template,
-            r#"
-#[derive(Debug, PartialEq)]
-pub struct MyGenericStruct2<T> {
-    pub x: T,
-}
-impl<T> MyGenericStruct2<T> {
-    pub fn new$0(x: T) -> Self {
-        MyGenericStruct2 { x }
-    }
-}
-"#,
-            r#"
-#[derive(Debug, PartialEq)]
-pub struct MyGenericStruct2<T> {
-    pub x: T,
-}
-impl<T> MyGenericStruct2<T> {
-    /// Creates a new [`MyGenericStruct2<T>`].
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// use test::MyGenericStruct2;
-    ///
-    /// assert_eq!(MyGenericStruct2::new(x), );
-    /// ```
-    pub fn new(x: T) -> Self {
-        MyGenericStruct2 { x }
-    }
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn supports_method_call() {
-        check_assist(
-            generate_documentation_template,
-            r#"
-impl<T> MyGenericStruct<T> {
-    pub fn co$0nsume(self) {}
-}
-"#,
-            r#"
-impl<T> MyGenericStruct<T> {
-    /// .
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// use test::MyGenericStruct;
-    ///
-    /// let my_generic_struct = ;
-    /// my_generic_struct.consume();
-    /// ```
-    pub fn consume(self) {}
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn checks_modified_self_param() {
-        check_assist(
-            generate_documentation_template,
-            r#"
-impl<T> MyGenericStruct<T> {
-    pub fn modi$0fy(&mut self, new_value: T) {
-        self.x = new_value;
-    }
-}
-"#,
-            r#"
-impl<T> MyGenericStruct<T> {
-    /// .
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// use test::MyGenericStruct;
-    ///
-    /// let mut my_generic_struct = ;
-    /// my_generic_struct.modify(new_value);
-    /// assert_eq!(my_generic_struct, );
-    /// ```
-    pub fn modify(&mut self, new_value: T) {
-        self.x = new_value;
-    }
-}
-"#,
-        );
-    }
-}
diff --git a/crates/ide_assists/src/handlers/generate_is_empty_from_len.rs b/crates/ide_assists/src/handlers/generate_is_empty_from_len.rs
index 15025cf0d0..d831289775 100644
--- a/crates/ide_assists/src/handlers/generate_is_empty_from_len.rs
+++ b/crates/ide_assists/src/handlers/generate_is_empty_from_len.rs
@@ -92,7 +92,7 @@ fn get_impl_method(
     let scope = ctx.sema.scope(impl_.syntax());
     let krate = impl_def.module(db).krate();
     let ty = impl_def.self_ty(db);
-    let traits_in_scope = scope.visible_traits();
+    let traits_in_scope = scope.traits_in_scope();
     ty.iterate_method_candidates(db, krate, &traits_in_scope, Some(fn_name), |_, func| Some(func))
 }
 
diff --git a/crates/ide_assists/src/handlers/number_representation.rs b/crates/ide_assists/src/handlers/number_representation.rs
new file mode 100644
index 0000000000..df01763663
--- /dev/null
+++ b/crates/ide_assists/src/handlers/number_representation.rs
@@ -0,0 +1,183 @@
+use syntax::{ast, ast::Radix, AstToken};
+
+use crate::{AssistContext, AssistId, AssistKind, Assists, GroupLabel};
+
+const MIN_NUMBER_OF_DIGITS_TO_FORMAT: usize = 5;
+
+// Assist: reformat_number_literal
+//
+// Adds or removes seprators from integer literal.
+//
+// ```
+// const _: i32 = 1012345$0;
+// ```
+// ->
+// ```
+// const _: i32 = 1_012_345;
+// ```
+pub(crate) fn reformat_number_literal(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
+    let literal = ctx.find_node_at_offset::<ast::Literal>()?;
+    let literal = match literal.kind() {
+        ast::LiteralKind::IntNumber(it) => it,
+        _ => return None,
+    };
+
+    let text = literal.text();
+    if text.contains('_') {
+        return remove_separators(acc, literal);
+    }
+
+    let (prefix, value, suffix) = literal.split_into_parts();
+    if value.len() < MIN_NUMBER_OF_DIGITS_TO_FORMAT {
+        return None;
+    }
+
+    let radix = literal.radix();
+    let mut converted = prefix.to_string();
+    converted.push_str(&add_group_separators(value, group_size(radix)));
+    converted.push_str(suffix);
+
+    let group_id = GroupLabel("Reformat number literal".into());
+    let label = format!("Convert {} to {}", literal, converted);
+    let range = literal.syntax().text_range();
+    acc.add_group(
+        &group_id,
+        AssistId("reformat_number_literal", AssistKind::RefactorInline),
+        label,
+        range,
+        |builder| builder.replace(range, converted),
+    )
+}
+
+fn remove_separators(acc: &mut Assists, literal: ast::IntNumber) -> Option<()> {
+    let group_id = GroupLabel("Reformat number literal".into());
+    let range = literal.syntax().text_range();
+    acc.add_group(
+        &group_id,
+        AssistId("reformat_number_literal", AssistKind::RefactorInline),
+        "Remove digit seprators",
+        range,
+        |builder| builder.replace(range, literal.text().replace("_", "")),
+    )
+}
+
+const fn group_size(r: Radix) -> usize {
+    match r {
+        Radix::Binary => 4,
+        Radix::Octal => 3,
+        Radix::Decimal => 3,
+        Radix::Hexadecimal => 4,
+    }
+}
+
+fn add_group_separators(s: &str, group_size: usize) -> String {
+    let mut chars = Vec::new();
+    for (i, ch) in s.chars().filter(|&ch| ch != '_').rev().enumerate() {
+        if i > 0 && i % group_size == 0 {
+            chars.push('_');
+        }
+        chars.push(ch);
+    }
+
+    chars.into_iter().rev().collect()
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::tests::{check_assist_by_label, check_assist_not_applicable, check_assist_target};
+
+    use super::*;
+
+    #[test]
+    fn group_separators() {
+        let cases = vec![
+            ("", 4, ""),
+            ("1", 4, "1"),
+            ("12", 4, "12"),
+            ("123", 4, "123"),
+            ("1234", 4, "1234"),
+            ("12345", 4, "1_2345"),
+            ("123456", 4, "12_3456"),
+            ("1234567", 4, "123_4567"),
+            ("12345678", 4, "1234_5678"),
+            ("123456789", 4, "1_2345_6789"),
+            ("1234567890", 4, "12_3456_7890"),
+            ("1_2_3_4_5_6_7_8_9_0_", 4, "12_3456_7890"),
+            ("1234567890", 3, "1_234_567_890"),
+            ("1234567890", 2, "12_34_56_78_90"),
+            ("1234567890", 1, "1_2_3_4_5_6_7_8_9_0"),
+        ];
+
+        for case in cases {
+            let (input, group_size, expected) = case;
+            assert_eq!(add_group_separators(input, group_size), expected)
+        }
+    }
+
+    #[test]
+    fn good_targets() {
+        let cases = vec![
+            ("const _: i32 = 0b11111$0", "0b11111"),
+            ("const _: i32 = 0o77777$0;", "0o77777"),
+            ("const _: i32 = 10000$0;", "10000"),
+            ("const _: i32 = 0xFFFFF$0;", "0xFFFFF"),
+            ("const _: i32 = 10000i32$0;", "10000i32"),
+            ("const _: i32 = 0b_10_0i32$0;", "0b_10_0i32"),
+        ];
+
+        for case in cases {
+            check_assist_target(reformat_number_literal, case.0, case.1);
+        }
+    }
+
+    #[test]
+    fn bad_targets() {
+        let cases = vec![
+            "const _: i32 = 0b111$0",
+            "const _: i32 = 0b1111$0",
+            "const _: i32 = 0o77$0;",
+            "const _: i32 = 0o777$0;",
+            "const _: i32 = 10$0;",
+            "const _: i32 = 999$0;",
+            "const _: i32 = 0xFF$0;",
+            "const _: i32 = 0xFFFF$0;",
+        ];
+
+        for case in cases {
+            check_assist_not_applicable(reformat_number_literal, case);
+        }
+    }
+
+    #[test]
+    fn labels() {
+        let cases = vec![
+            ("const _: i32 = 10000$0", "const _: i32 = 10_000", "Convert 10000 to 10_000"),
+            (
+                "const _: i32 = 0xFF0000$0;",
+                "const _: i32 = 0xFF_0000;",
+                "Convert 0xFF0000 to 0xFF_0000",
+            ),
+            (
+                "const _: i32 = 0b11111111$0;",
+                "const _: i32 = 0b1111_1111;",
+                "Convert 0b11111111 to 0b1111_1111",
+            ),
+            (
+                "const _: i32 = 0o377211$0;",
+                "const _: i32 = 0o377_211;",
+                "Convert 0o377211 to 0o377_211",
+            ),
+            (
+                "const _: i32 = 10000i32$0;",
+                "const _: i32 = 10_000i32;",
+                "Convert 10000i32 to 10_000i32",
+            ),
+            ("const _: i32 = 1_0_0_0_i32$0;", "const _: i32 = 1000i32;", "Remove digit seprators"),
+        ];
+
+        for case in cases {
+            let (before, after, label) = case;
+            check_assist_by_label(reformat_number_literal, before, after, label);
+        }
+    }
+}
diff --git a/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs b/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs
index b3723710a8..3e33c62144 100644
--- a/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs
+++ b/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs
@@ -1,5 +1,4 @@
 use hir::ModuleDef;
-use ide_db::helpers::insert_whitespace_into_node::insert_ws_into;
 use ide_db::helpers::{
     get_path_at_cursor_in_tt, import_assets::NameToImport, mod_path_to_ast,
     parse_tt_as_comma_sep_paths,
@@ -66,7 +65,7 @@ pub(crate) fn replace_derive_with_manual_impl(
     let found_traits = items_locator::items_with_name(
         &ctx.sema,
         current_crate,
-        NameToImport::exact_case_sensitive(trait_path.segments().last()?.to_string()),
+        NameToImport::Exact(trait_path.segments().last()?.to_string()),
         items_locator::AssocItemSearch::Exclude,
         Some(items_locator::DEFAULT_QUERY_SEARCH_LIMIT.inner()),
     )
@@ -171,7 +170,7 @@ fn impl_def_from_trait(
 ) -> Option<(ast::Impl, ast::AssocItem)> {
     let trait_ = trait_?;
     let target_scope = sema.scope(annotated_name.syntax());
-    let trait_items = filter_assoc_items(sema, &trait_.items(sema.db), DefaultMethods::No);
+    let trait_items = filter_assoc_items(sema.db, &trait_.items(sema.db), DefaultMethods::No);
     if trait_items.is_empty() {
         return None;
     }
@@ -194,17 +193,6 @@ fn impl_def_from_trait(
         node
     };
 
-    let trait_items = trait_items
-        .into_iter()
-        .map(|it| {
-            if sema.hir_file_for(it.syntax()).is_macro() {
-                if let Some(it) = ast::AssocItem::cast(insert_ws_into(it.syntax().clone())) {
-                    return it;
-                }
-            }
-            it.clone_for_update()
-        })
-        .collect();
     let (impl_def, first_assoc_item) =
         add_trait_assoc_items_to_impl(sema, trait_items, trait_, impl_def, target_scope);
 
diff --git a/crates/ide_assists/src/handlers/unmerge_use.rs b/crates/ide_assists/src/handlers/unmerge_use.rs
index 7d73632c79..71a816e19f 100644
--- a/crates/ide_assists/src/handlers/unmerge_use.rs
+++ b/crates/ide_assists/src/handlers/unmerge_use.rs
@@ -1,3 +1,4 @@
+use itertools::Itertools;
 use syntax::{
     ast::{self, make, HasVisibility},
     ted::{self, Position},
@@ -69,7 +70,7 @@ fn resolve_full_path(tree: &ast::UseTree) -> Option<ast::Path> {
         .filter_map(ast::UseTree::cast)
         .filter_map(|t| t.path());
 
-    let final_path = paths.reduce(|prev, next| make::path_concat(next, prev))?;
+    let final_path = paths.fold1(|prev, next| make::path_concat(next, prev))?;
     if final_path.segment().map_or(false, |it| it.self_token().is_some()) {
         final_path.qualifier()
     } else {
diff --git a/crates/ide_assists/src/lib.rs b/crates/ide_assists/src/lib.rs
index a1dfa065ff..8966b512be 100644
--- a/crates/ide_assists/src/lib.rs
+++ b/crates/ide_assists/src/lib.rs
@@ -136,7 +136,6 @@ mod handlers {
     mod generate_default_from_new;
     mod generate_deref;
     mod generate_derive;
-    mod generate_documentation_template;
     mod generate_enum_is_method;
     mod generate_enum_projection_method;
     mod generate_from_impl_for_enum;
@@ -159,6 +158,7 @@ mod handlers {
     mod move_module_to_file;
     mod move_to_mod_rs;
     mod move_from_mod_rs;
+    mod number_representation;
     mod promote_local_to_const;
     mod pull_assignment_up;
     mod qualify_path;
@@ -217,8 +217,9 @@ mod handlers {
             generate_constant::generate_constant,
             generate_default_from_enum_variant::generate_default_from_enum_variant,
             generate_default_from_new::generate_default_from_new,
+            generate_delegate_methods::generate_delegate_methods,
+            generate_deref::generate_deref,
             generate_derive::generate_derive,
-            generate_documentation_template::generate_documentation_template,
             generate_enum_is_method::generate_enum_is_method,
             generate_enum_projection_method::generate_enum_as_method,
             generate_enum_projection_method::generate_enum_try_into_method,
@@ -241,6 +242,7 @@ mod handlers {
             move_module_to_file::move_module_to_file,
             move_to_mod_rs::move_to_mod_rs,
             move_from_mod_rs::move_from_mod_rs,
+            number_representation::reformat_number_literal,
             pull_assignment_up::pull_assignment_up,
             promote_local_to_const::promote_local_to_const,
             qualify_path::qualify_path,
@@ -285,8 +287,6 @@ mod handlers {
             generate_getter::generate_getter,
             generate_getter::generate_getter_mut,
             generate_setter::generate_setter,
-            generate_delegate_methods::generate_delegate_methods,
-            generate_deref::generate_deref,
             // Are you sure you want to add new assist here, and not to the
             // sorted list above?
         ]
diff --git a/crates/ide_assists/src/utils.rs b/crates/ide_assists/src/utils.rs
index 90ec710c8e..03433fc42a 100644
--- a/crates/ide_assists/src/utils.rs
+++ b/crates/ide_assists/src/utils.rs
@@ -5,7 +5,7 @@ use std::ops;
 use itertools::Itertools;
 
 pub(crate) use gen_trait_fn_body::gen_trait_fn_body;
-use hir::{db::HirDatabase, HirDisplay, Semantics};
+use hir::{db::HirDatabase, HasSource, HirDisplay};
 use ide_db::{
     helpers::FamousDefs, helpers::SnippetCap, path_transform::PathTransform, RootDatabase,
 };
@@ -92,7 +92,7 @@ pub enum DefaultMethods {
 }
 
 pub fn filter_assoc_items(
-    sema: &Semantics<RootDatabase>,
+    db: &RootDatabase,
     items: &[hir::AssocItem],
     default_methods: DefaultMethods,
 ) -> Vec<ast::AssocItem> {
@@ -109,11 +109,11 @@ pub fn filter_assoc_items(
     items
         .iter()
         // Note: This throws away items with no source.
-        .filter_map(|&i| {
+        .filter_map(|i| {
             let item = match i {
-                hir::AssocItem::Function(i) => ast::AssocItem::Fn(sema.source(i)?.value),
-                hir::AssocItem::TypeAlias(i) => ast::AssocItem::TypeAlias(sema.source(i)?.value),
-                hir::AssocItem::Const(i) => ast::AssocItem::Const(sema.source(i)?.value),
+                hir::AssocItem::Function(i) => ast::AssocItem::Fn(i.source(db)?.value),
+                hir::AssocItem::TypeAlias(i) => ast::AssocItem::TypeAlias(i.source(db)?.value),
+                hir::AssocItem::Const(i) => ast::AssocItem::Const(i.source(db)?.value),
             };
             Some(item)
         })
@@ -129,7 +129,7 @@ pub fn filter_assoc_items(
 }
 
 pub fn add_trait_assoc_items_to_impl(
-    sema: &Semantics<RootDatabase>,
+    sema: &hir::Semantics<ide_db::RootDatabase>,
     items: Vec<ast::AssocItem>,
     trait_: hir::Trait,
     impl_: ast::Impl,
@@ -140,6 +140,7 @@ pub fn add_trait_assoc_items_to_impl(
     let transform = PathTransform::trait_impl(&target_scope, &source_scope, trait_, impl_.clone());
 
     let items = items.into_iter().map(|assoc_item| {
+        let assoc_item = assoc_item.clone_for_update();
         transform.apply(assoc_item.syntax());
         assoc_item.remove_attrs_and_docs();
         assoc_item
diff --git a/crates/ide_assists/src/utils/suggest_name.rs b/crates/ide_assists/src/utils/suggest_name.rs
index 2021db3aba..74523234b0 100644
--- a/crates/ide_assists/src/utils/suggest_name.rs
+++ b/crates/ide_assists/src/utils/suggest_name.rs
@@ -243,7 +243,7 @@ fn name_of_type(ty: &hir::Type, db: &RootDatabase) -> Option<String> {
     } else if let Some(trait_) = ty.as_dyn_trait() {
         trait_name(&trait_, db)?
     } else if let Some(traits) = ty.as_impl_traits(db) {
-        let mut iter = traits.filter_map(|t| trait_name(&t, db));
+        let mut iter = traits.into_iter().filter_map(|t| trait_name(&t, db));
         let name = iter.next()?;
         if iter.next().is_some() {
             return None;
diff --git a/crates/ide_completion/src/completions/attribute.rs b/crates/ide_completion/src/completions/attribute.rs
index d92e311915..f09015efb6 100644
--- a/crates/ide_completion/src/completions/attribute.rs
+++ b/crates/ide_completion/src/completions/attribute.rs
@@ -30,12 +30,16 @@ pub(crate) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext)
         None => None,
     };
     match (name_ref, attribute.token_tree()) {
-        (Some(path), Some(tt)) if tt.l_paren_token().is_some() => match path.text().as_str() {
-            "repr" => repr::complete_repr(acc, ctx, tt),
-            "derive" => derive::complete_derive(acc, ctx, &parse_tt_as_comma_sep_paths(tt)?),
-            "feature" => lint::complete_lint(acc, ctx, &parse_tt_as_comma_sep_paths(tt)?, FEATURES),
+        (Some(path), Some(token_tree)) => match path.text().as_str() {
+            "repr" => repr::complete_repr(acc, ctx, token_tree),
+            "derive" => {
+                derive::complete_derive(acc, ctx, &parse_tt_as_comma_sep_paths(token_tree)?)
+            }
+            "feature" => {
+                lint::complete_lint(acc, ctx, &parse_tt_as_comma_sep_paths(token_tree)?, FEATURES)
+            }
             "allow" | "warn" | "deny" | "forbid" => {
-                let existing_lints = parse_tt_as_comma_sep_paths(tt)?;
+                let existing_lints = parse_tt_as_comma_sep_paths(token_tree)?;
                 lint::complete_lint(acc, ctx, &existing_lints, DEFAULT_LINTS);
                 lint::complete_lint(acc, ctx, &existing_lints, CLIPPY_LINTS);
                 lint::complete_lint(acc, ctx, &existing_lints, RUSTDOC_LINTS);
@@ -45,8 +49,8 @@ pub(crate) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext)
             }
             _ => (),
         },
-        (_, Some(_)) => (),
-        (_, None) => complete_new_attribute(acc, ctx, attribute),
+        (None, Some(_)) => (),
+        _ => complete_new_attribute(acc, ctx, attribute),
     }
     Some(())
 }
diff --git a/crates/ide_completion/src/completions/dot.rs b/crates/ide_completion/src/completions/dot.rs
index e7371270fb..e01e9c9fa7 100644
--- a/crates/ide_completion/src/completions/dot.rs
+++ b/crates/ide_completion/src/completions/dot.rs
@@ -79,7 +79,7 @@ fn complete_methods(
 ) {
     if let Some(krate) = ctx.krate {
         let mut seen_methods = FxHashSet::default();
-        let traits_in_scope = ctx.scope.visible_traits();
+        let traits_in_scope = ctx.scope.traits_in_scope();
         receiver.iterate_method_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, func| {
             if func.self_param(ctx.db).is_some() && seen_methods.insert(func.name(ctx.db)) {
                 f(func);
diff --git a/crates/ide_completion/src/completions/flyimport.rs b/crates/ide_completion/src/completions/flyimport.rs
index 8994c25475..446a808de8 100644
--- a/crates/ide_completion/src/completions/flyimport.rs
+++ b/crates/ide_completion/src/completions/flyimport.rs
@@ -110,7 +110,7 @@ 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))
+    if ctx.in_use_tree()
         || ctx.is_path_disallowed()
         || ctx.expects_item()
         || ctx.expects_assoc_item()
@@ -118,11 +118,6 @@ pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext)
     {
         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 potential_import_name = {
         let token_kind = ctx.token.kind();
         if matches!(token_kind, T![.] | T![::]) {
@@ -152,25 +147,14 @@ pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext)
             }
         };
         match (kind, import.original_item) {
-            // Aren't handled in flyimport
-            (PathKind::Vis { .. } | PathKind::Use, _) => false,
-            // modules are always fair game
-            (_, ItemInNs::Types(hir::ModuleDef::Module(_))) => true,
-            // and so are macros(except for attributes)
-            (
-                PathKind::Expr | PathKind::Type | PathKind::Mac | PathKind::Pat,
-                ItemInNs::Macros(mac),
-            ) => mac.is_fn_like(),
-            (PathKind::Mac, _) => true,
-
             (PathKind::Expr, ItemInNs::Types(_) | ItemInNs::Values(_)) => true,
 
-            (PathKind::Pat, ItemInNs::Types(_)) => true,
-            (PathKind::Pat, ItemInNs::Values(def)) => matches!(def, hir::ModuleDef::Const(_)),
-
             (PathKind::Type, ItemInNs::Types(_)) => true,
             (PathKind::Type, ItemInNs::Values(_)) => false,
 
+            (PathKind::Expr | PathKind::Type, ItemInNs::Macros(mac)) => mac.is_fn_like(),
+
+            (PathKind::Attr, ItemInNs::Types(hir::ModuleDef::Module(_))) => true,
             (PathKind::Attr, ItemInNs::Macros(mac)) => mac.is_attr(),
             (PathKind::Attr, _) => false,
         }
@@ -227,18 +211,22 @@ fn import_assets(ctx: &CompletionContext, fuzzy_name: String) -> Option<ImportAs
         )
     } else {
         let fuzzy_name_length = fuzzy_name.len();
-        let mut assets_for_path = ImportAssets::for_fuzzy_path(
+        let assets_for_path = ImportAssets::for_fuzzy_path(
             current_module,
             ctx.path_qual().cloned(),
             fuzzy_name,
             &ctx.sema,
             ctx.token.parent()?,
         )?;
-        if fuzzy_name_length < 3 {
-            cov_mark::hit!(flyimport_exact_on_short_path);
-            assets_for_path.path_fuzzy_name_to_exact(false);
+
+        if matches!(assets_for_path.import_candidate(), ImportCandidate::Path(_))
+            && fuzzy_name_length < 2
+        {
+            cov_mark::hit!(ignore_short_input_for_path);
+            None
+        } else {
+            Some(assets_for_path)
         }
-        Some(assets_for_path)
     }
 }
 
diff --git a/crates/ide_completion/src/completions/keyword.rs b/crates/ide_completion/src/completions/keyword.rs
index 1c686dc0a6..36d25d5d02 100644
--- a/crates/ide_completion/src/completions/keyword.rs
+++ b/crates/ide_completion/src/completions/keyword.rs
@@ -5,9 +5,8 @@
 use syntax::{SyntaxKind, T};
 
 use crate::{
-    context::{PathCompletionContext, PathKind},
-    patterns::ImmediateLocation,
-    CompletionContext, CompletionItem, CompletionItemKind, Completions,
+    context::PathCompletionContext, patterns::ImmediateLocation, CompletionContext, CompletionItem,
+    CompletionItemKind, Completions,
 };
 
 pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) {
@@ -34,8 +33,8 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte
     let has_block_expr_parent = ctx.has_block_expr_parent();
     let expects_item = ctx.expects_item();
 
-    if let Some(PathKind::Vis { has_in_token }) = ctx.path_kind() {
-        if !has_in_token {
+    if let Some(ImmediateLocation::Visibility(vis)) = &ctx.completion_location {
+        if vis.in_token().is_none() {
             cov_mark::hit!(kw_completion_in);
             add_keyword("in", "in");
         }
diff --git a/crates/ide_completion/src/completions/pattern.rs b/crates/ide_completion/src/completions/pattern.rs
index b3b27f251f..c63c274151 100644
--- a/crates/ide_completion/src/completions/pattern.rs
+++ b/crates/ide_completion/src/completions/pattern.rs
@@ -1,26 +1,21 @@
-//! Completes constants and paths in unqualified patterns.
-
-use hir::db::DefDatabase;
+//! Completes constants and paths in patterns.
 
 use crate::{
     context::{PatternContext, PatternRefutability},
     CompletionContext, Completions,
 };
 
-/// Completes constants and paths in unqualified patterns.
+/// Completes constants and paths in patterns.
 pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) {
     let refutable = match ctx.pattern_ctx {
-        Some(PatternContext { refutability, .. }) if ctx.path_context.is_none() => {
-            refutability == PatternRefutability::Refutable
-        }
-        _ => return,
+        Some(PatternContext { refutability, .. }) => refutability == PatternRefutability::Refutable,
+        None => return,
     };
-    let single_variant_enum = |enum_: hir::Enum| ctx.db.enum_data(enum_.into()).variants.len() == 1;
 
-    if let Some(hir::Adt::Enum(e)) =
-        ctx.expected_type.as_ref().and_then(|ty| ty.strip_references().as_adt())
-    {
-        if refutable || single_variant_enum(e) {
+    if refutable {
+        if let Some(hir::Adt::Enum(e)) =
+            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_variant_pat(ctx, variant, path.clone());
                 acc.add_qualified_enum_variant(ctx, variant, path);
@@ -31,20 +26,20 @@ pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) {
     // FIXME: ideally, we should look at the type we are matching against and
     // suggest variants + auto-imports
     ctx.process_all_names(&mut |name, res| {
-        let add_resolution = match res {
+        let add_resolution = match &res {
             hir::ScopeDef::ModuleDef(def) => match def {
                 hir::ModuleDef::Adt(hir::Adt::Struct(strukt)) => {
-                    acc.add_struct_pat(ctx, strukt, Some(name.clone()));
+                    acc.add_struct_pat(ctx, *strukt, Some(name.clone()));
                     true
                 }
-                hir::ModuleDef::Variant(variant)
-                    if refutable || single_variant_enum(variant.parent_enum(ctx.db)) =>
-                {
-                    acc.add_variant_pat(ctx, variant, Some(name.clone()));
+                hir::ModuleDef::Variant(variant) if refutable => {
+                    acc.add_variant_pat(ctx, *variant, Some(name.clone()));
                     true
                 }
-                hir::ModuleDef::Adt(hir::Adt::Enum(e)) => refutable || single_variant_enum(e),
-                hir::ModuleDef::Const(..) | hir::ModuleDef::Module(..) => refutable,
+                hir::ModuleDef::Adt(hir::Adt::Enum(..))
+                | hir::ModuleDef::Variant(..)
+                | hir::ModuleDef::Const(..)
+                | hir::ModuleDef::Module(..) => refutable,
                 _ => false,
             },
             hir::ScopeDef::MacroDef(mac) => mac.is_fn_like(),
diff --git a/crates/ide_completion/src/completions/qualified_path.rs b/crates/ide_completion/src/completions/qualified_path.rs
index a0d6d5cdc6..4abf7374b3 100644
--- a/crates/ide_completion/src/completions/qualified_path.rs
+++ b/crates/ide_completion/src/completions/qualified_path.rs
@@ -7,23 +7,17 @@ use rustc_hash::FxHashSet;
 use syntax::{ast, AstNode};
 
 use crate::{
-    context::{PathCompletionContext, PathKind},
-    patterns::ImmediateLocation,
-    CompletionContext, Completions,
+    context::PathCompletionContext, patterns::ImmediateLocation, CompletionContext, Completions,
 };
 
 pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionContext) {
     if ctx.is_path_disallowed() || ctx.has_impl_or_trait_prev_sibling() {
         return;
     }
-    let (path, use_tree_parent, kind) = match ctx.path_context {
-        // let ... else, syntax would come in really handy here right now
-        Some(PathCompletionContext {
-            qualifier: Some(ref qualifier),
-            use_tree_parent,
-            kind,
-            ..
-        }) => (qualifier, use_tree_parent, kind),
+    let (path, use_tree_parent) = match &ctx.path_context {
+        Some(PathCompletionContext { qualifier: Some(qualifier), use_tree_parent, .. }) => {
+            (qualifier, *use_tree_parent)
+        }
         _ => return,
     };
 
@@ -50,11 +44,7 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
             }
             return;
         }
-        _ => (),
-    }
-
-    match kind {
-        Some(PathKind::Vis { .. }) => {
+        Some(ImmediateLocation::Visibility(_)) => {
             if let hir::PathResolution::Def(hir::ModuleDef::Module(module)) = resolution {
                 if let Some(current_module) = ctx.scope.module() {
                     if let Some(next) = current_module
@@ -71,7 +61,7 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
             }
             return;
         }
-        Some(PathKind::Attr) => {
+        Some(ImmediateLocation::Attribute(_)) => {
             if let hir::PathResolution::Def(hir::ModuleDef::Module(module)) = resolution {
                 for (name, def) in module.scope(ctx.db, context_module) {
                     let add_resolution = match def {
@@ -86,38 +76,37 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
             }
             return;
         }
-        Some(PathKind::Use) => {
-            if iter::successors(Some(path.clone()), |p| p.qualifier())
-                .all(|p| p.segment().and_then(|s| s.super_token()).is_some())
-            {
-                acc.add_keyword(ctx, "super::");
-            }
-            // only show `self` in a new use-tree when the qualifier doesn't end in self
-            if use_tree_parent
-                && !matches!(
-                    path.segment().and_then(|it| it.kind()),
-                    Some(ast::PathSegmentKind::SelfKw)
-                )
-            {
-                acc.add_keyword(ctx, "self");
-            }
-        }
         _ => (),
     }
 
-    if !matches!(kind, Some(PathKind::Pat)) {
-        // Add associated types on type parameters and `Self`.
-        resolution.assoc_type_shorthand_candidates(ctx.db, |_, alias| {
-            acc.add_type_alias(ctx, alias);
-            None::<()>
-        });
+    if ctx.in_use_tree() {
+        if iter::successors(Some(path.clone()), |p| p.qualifier())
+            .all(|p| p.segment().and_then(|s| s.super_token()).is_some())
+        {
+            acc.add_keyword(ctx, "super::");
+        }
+        // only show `self` in a new use-tree when the qualifier doesn't end in self
+        if use_tree_parent
+            && !matches!(
+                path.segment().and_then(|it| it.kind()),
+                Some(ast::PathSegmentKind::SelfKw)
+            )
+        {
+            acc.add_keyword(ctx, "self");
+        }
     }
 
+    // Add associated types on type parameters and `Self`.
+    resolution.assoc_type_shorthand_candidates(ctx.db, |_, alias| {
+        acc.add_type_alias(ctx, alias);
+        None::<()>
+    });
+
     match resolution {
         hir::PathResolution::Def(hir::ModuleDef::Module(module)) => {
             let module_scope = module.scope(ctx.db, context_module);
             for (name, def) in module_scope {
-                if let Some(PathKind::Use) = kind {
+                if ctx.in_use_tree() {
                     if let ScopeDef::Unknown = def {
                         if let Some(ast::NameLike::NameRef(name_ref)) = ctx.name_syntax.as_ref() {
                             if name_ref.syntax().text() == name.to_smol_str().as_str() {
@@ -187,7 +176,7 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
 
             let krate = ctx.krate;
             if let Some(krate) = krate {
-                let traits_in_scope = ctx.scope.visible_traits();
+                let traits_in_scope = ctx.scope.traits_in_scope();
                 ty.iterate_path_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, item| {
                     add_assoc_item(acc, ctx, item);
                     None::<()>
@@ -220,7 +209,7 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
                     add_enum_variants(acc, ctx, e);
                 }
 
-                let traits_in_scope = ctx.scope.visible_traits();
+                let traits_in_scope = ctx.scope.traits_in_scope();
                 let mut seen = FxHashSet::default();
                 ty.iterate_path_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, item| {
                     // We might iterate candidates of a trait multiple times here, so deduplicate
diff --git a/crates/ide_completion/src/completions/unqualified_path.rs b/crates/ide_completion/src/completions/unqualified_path.rs
index ac1ba2da7c..414c1d961b 100644
--- a/crates/ide_completion/src/completions/unqualified_path.rs
+++ b/crates/ide_completion/src/completions/unqualified_path.rs
@@ -3,23 +3,15 @@
 use hir::ScopeDef;
 use syntax::{ast, AstNode};
 
-use crate::{
-    context::{PathCompletionContext, PathKind},
-    patterns::ImmediateLocation,
-    CompletionContext, Completions,
-};
+use crate::{patterns::ImmediateLocation, CompletionContext, Completions};
 
 pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) {
     let _p = profile::span("complete_unqualified_path");
-    if ctx.is_path_disallowed() || ctx.has_impl_or_trait_prev_sibling() {
+    if ctx.is_path_disallowed() || !ctx.is_trivial_path() || ctx.has_impl_or_trait_prev_sibling() {
         return;
     }
-    let kind = match ctx.path_context {
-        Some(PathCompletionContext { is_trivial_path: true, kind, .. }) => kind,
-        _ => return,
-    };
 
-    if let Some(PathKind::Use) = kind {
+    if ctx.in_use_tree() {
         // only show modules in a fresh UseTree
         cov_mark::hit!(unqualified_path_only_modules_in_import);
         ctx.process_all_names(&mut |name, res| {
@@ -33,25 +25,8 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC
     }
     ["self", "super", "crate"].into_iter().for_each(|kw| acc.add_keyword(ctx, kw));
 
-    match kind {
-        Some(PathKind::Vis { .. }) => return,
-        Some(PathKind::Attr) => {
-            ctx.process_all_names(&mut |name, res| {
-                let add_resolution = match res {
-                    ScopeDef::MacroDef(mac) => mac.is_attr(),
-                    ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) => true,
-                    _ => false,
-                };
-                if add_resolution {
-                    acc.add_resolution(ctx, name, &res);
-                }
-            });
-            return;
-        }
-        _ => (),
-    }
-
     match &ctx.completion_location {
+        Some(ImmediateLocation::Visibility(_)) => return,
         Some(ImmediateLocation::ItemList | ImmediateLocation::Trait | ImmediateLocation::Impl) => {
             // only show macros in {Assoc}ItemList
             ctx.process_all_names(&mut |name, res| {
@@ -81,6 +56,19 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC
             });
             return;
         }
+        Some(ImmediateLocation::Attribute(_)) => {
+            ctx.process_all_names(&mut |name, res| {
+                let add_resolution = match res {
+                    ScopeDef::MacroDef(mac) => mac.is_attr(),
+                    ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) => true,
+                    _ => false,
+                };
+                if add_resolution {
+                    acc.add_resolution(ctx, name, &res);
+                }
+            });
+            return;
+        }
         _ => (),
     }
 
diff --git a/crates/ide_completion/src/context.rs b/crates/ide_completion/src/context.rs
index d459e51161..b3ce1f8e9c 100644
--- a/crates/ide_completion/src/context.rs
+++ b/crates/ide_completion/src/context.rs
@@ -35,23 +35,16 @@ pub(super) enum PathKind {
     Expr,
     Type,
     Attr,
-    Mac,
-    Pat,
-    Vis { has_in_token: bool },
-    Use,
 }
 
 #[derive(Debug)]
 pub(crate) struct PathCompletionContext {
     /// If this is a call with () already there
-    has_call_parens: bool,
+    call_kind: Option<CallKind>,
     /// A single-indent path, like `foo`. `::foo` should not be considered a trivial path.
     pub(super) is_trivial_path: bool,
     /// If not a trivial path, the prefix (qualifier).
     pub(super) qualifier: Option<ast::Path>,
-    #[allow(dead_code)]
-    /// If not a trivial path, the suffix (parent).
-    pub(super) parent: Option<ast::Path>,
     /// Whether the qualifier comes from a use tree parent or not
     pub(super) use_tree_parent: bool,
     pub(super) kind: Option<PathKind>,
@@ -77,6 +70,13 @@ pub(super) enum LifetimeContext {
     LabelDef,
 }
 
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub(crate) enum CallKind {
+    Pat,
+    Mac,
+    Expr,
+}
+
 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
 pub(crate) enum ParamKind {
     Function,
@@ -206,6 +206,13 @@ impl<'a> CompletionContext<'a> {
         )
     }
 
+    pub(crate) fn in_use_tree(&self) -> bool {
+        matches!(
+            self.completion_location,
+            Some(ImmediateLocation::Use | ImmediateLocation::UseTree)
+        )
+    }
+
     pub(crate) fn has_impl_or_trait_prev_sibling(&self) -> bool {
         matches!(
             self.prev_sibling,
@@ -250,8 +257,8 @@ impl<'a> CompletionContext<'a> {
         matches!(self.path_context, Some(PathCompletionContext { kind: Some(PathKind::Type), .. }))
     }
 
-    pub(crate) fn path_is_call(&self) -> bool {
-        self.path_context.as_ref().map_or(false, |it| it.has_call_parens)
+    pub(crate) fn path_call_kind(&self) -> Option<CallKind> {
+        self.path_context.as_ref().and_then(|it| it.call_kind)
     }
 
     pub(crate) fn is_trivial_path(&self) -> bool {
@@ -561,21 +568,11 @@ impl<'a> CompletionContext<'a> {
                         })().unwrap_or((None, None))
                     },
                     ast::RecordExprField(it) => {
-                        if let Some(expr) = it.expr() {
-                            cov_mark::hit!(expected_type_struct_field_with_leading_char);
-                            (
-                                self.sema.type_of_expr(&expr).map(TypeInfo::original),
-                                it.field_name().map(NameOrNameRef::NameRef),
-                            )
-                        } else {
-                            cov_mark::hit!(expected_type_struct_field_followed_by_comma);
-                            let ty = self.sema.resolve_record_field(&it)
-                                .map(|(_, _, ty)| ty);
-                            (
-                                ty,
-                                it.field_name().map(NameOrNameRef::NameRef),
-                            )
-                        }
+                        cov_mark::hit!(expected_type_struct_field_with_leading_char);
+                        (
+                            it.expr().as_ref().and_then(|e| self.sema.type_of_expr(e)).map(TypeInfo::original),
+                            it.field_name().map(NameOrNameRef::NameRef),
+                        )
                     },
                     ast::MatchExpr(it) => {
                         cov_mark::hit!(expected_type_match_arm_without_leading_char);
@@ -676,12 +673,7 @@ impl<'a> CompletionContext<'a> {
                     Self::classify_lifetime(&self.sema, original_file, lifetime, offset);
             }
             ast::NameLike::NameRef(name_ref) => {
-                if let Some((path_ctx, pat_ctx)) =
-                    Self::classify_name_ref(&self.sema, original_file, name_ref)
-                {
-                    self.path_context = Some(path_ctx);
-                    self.pattern_ctx = pat_ctx;
-                }
+                self.path_context = Self::classify_name_ref(&self.sema, original_file, name_ref);
             }
             ast::NameLike::Name(name) => {
                 self.pattern_ctx = Self::classify_name(&self.sema, name);
@@ -724,61 +716,83 @@ impl<'a> CompletionContext<'a> {
         if !bind_pat.is_simple_ident() {
             return None;
         }
-        Some(pattern_context_for(bind_pat.into()))
+        let mut is_param = None;
+        let (refutability, has_type_ascription) = bind_pat
+            .syntax()
+            .ancestors()
+            .skip_while(|it| ast::Pat::can_cast(it.kind()))
+            .next()
+            .map_or((PatternRefutability::Irrefutable, false), |node| {
+                let refutability = match_ast! {
+                    match node {
+                        ast::LetStmt(let_) => return (PatternRefutability::Irrefutable, let_.ty().is_some()),
+                        ast::Param(param) => {
+                            let is_closure_param = param
+                                .syntax()
+                                .ancestors()
+                                .nth(2)
+                                .and_then(ast::ClosureExpr::cast)
+                                .is_some();
+                            is_param = Some(if is_closure_param {
+                                ParamKind::Closure
+                            } else {
+                                ParamKind::Function
+                            });
+                            return (PatternRefutability::Irrefutable, param.ty().is_some())
+                        },
+                        ast::MatchArm(__) => PatternRefutability::Refutable,
+                        ast::Condition(__) => PatternRefutability::Refutable,
+                        ast::ForExpr(__) => PatternRefutability::Irrefutable,
+                        _ => PatternRefutability::Irrefutable,
+                    }
+                };
+                (refutability, false)
+            });
+        Some(PatternContext { refutability, is_param, has_type_ascription })
     }
 
     fn classify_name_ref(
         _sema: &Semantics<RootDatabase>,
         original_file: &SyntaxNode,
         name_ref: ast::NameRef,
-    ) -> Option<(PathCompletionContext, Option<PatternContext>)> {
+    ) -> Option<PathCompletionContext> {
         let parent = name_ref.syntax().parent()?;
         let segment = ast::PathSegment::cast(parent)?;
-        let path = segment.parent_path();
 
         let mut path_ctx = PathCompletionContext {
-            has_call_parens: false,
+            call_kind: None,
             is_trivial_path: false,
             qualifier: None,
-            parent: None,
             has_type_args: false,
             can_be_stmt: false,
             in_loop_body: false,
             use_tree_parent: false,
             kind: None,
         };
-        let mut pat_ctx = None;
         path_ctx.in_loop_body = is_in_loop_body(name_ref.syntax());
+        let path = segment.parent_path();
+
+        if let Some(p) = path.syntax().parent() {
+            path_ctx.call_kind = match_ast! {
+                match p {
+                    ast::PathExpr(it) => it.syntax().parent().and_then(ast::CallExpr::cast).map(|_| CallKind::Expr),
+                    ast::MacroCall(it) => it.excl_token().and(Some(CallKind::Mac)),
+                    ast::TupleStructPat(_it) => Some(CallKind::Pat),
+                    _ => None
+                }
+            };
+        }
 
-        path_ctx.kind  = path.syntax().ancestors().find_map(|it| {
-            match_ast! {
-                match it {
+        if let Some(parent) = path.syntax().parent() {
+            path_ctx.kind = match_ast! {
+                match parent {
                     ast::PathType(_it) => Some(PathKind::Type),
-                    ast::PathExpr(it) => {
-                        path_ctx.has_call_parens = it.syntax().parent().map_or(false, |it| ast::CallExpr::can_cast(it.kind()));
-                        Some(PathKind::Expr)
-                    },
-                    ast::TupleStructPat(it) => {
-                        path_ctx.has_call_parens = true;
-                        pat_ctx = Some(pattern_context_for(it.into()));
-                        Some(PathKind::Pat)
-                    },
-                    ast::RecordPat(it) => {
-                        pat_ctx = Some(pattern_context_for(it.into()));
-                        Some(PathKind::Pat)
-                    },
-                    ast::PathPat(it) => {
-                        pat_ctx = Some(pattern_context_for(it.into()));
-                        Some(PathKind::Pat)
-                    },
-                    ast::MacroCall(it) => it.excl_token().and(Some(PathKind::Mac)),
+                    ast::PathExpr(_it) => Some(PathKind::Expr),
                     ast::Meta(_it) => Some(PathKind::Attr),
-                    ast::Visibility(it) => Some(PathKind::Vis { has_in_token: it.in_token().is_some() }),
-                    ast::UseTree(_it) => Some(PathKind::Use),
                     _ => None,
                 }
-            }
-        });
+            };
+        }
         path_ctx.has_type_args = segment.generic_arg_list().is_some();
 
         if let Some((path, use_tree_parent)) = path_or_use_tree_qualifier(&path) {
@@ -792,12 +806,12 @@ impl<'a> CompletionContext<'a> {
                     )
                 })
                 .map(|it| it.parent_path());
-            return Some((path_ctx, pat_ctx));
+            return Some(path_ctx);
         }
 
         if let Some(segment) = path.segment() {
             if segment.coloncolon_token().is_some() {
-                return Some((path_ctx, pat_ctx));
+                return Some(path_ctx);
             }
         }
 
@@ -821,46 +835,10 @@ impl<'a> CompletionContext<'a> {
                 None
             })
             .unwrap_or(false);
-        Some((path_ctx, pat_ctx))
+        Some(path_ctx)
     }
 }
 
-fn pattern_context_for(pat: ast::Pat) -> PatternContext {
-    let mut is_param = None;
-    let (refutability, has_type_ascription) =
-    pat
-        .syntax()
-        .ancestors()
-        .skip_while(|it| ast::Pat::can_cast(it.kind()))
-        .next()
-        .map_or((PatternRefutability::Irrefutable, false), |node| {
-            let refutability = match_ast! {
-                match node {
-                    ast::LetStmt(let_) => return (PatternRefutability::Irrefutable, let_.ty().is_some()),
-                    ast::Param(param) => {
-                        let is_closure_param = param
-                            .syntax()
-                            .ancestors()
-                            .nth(2)
-                            .and_then(ast::ClosureExpr::cast)
-                            .is_some();
-                        is_param = Some(if is_closure_param {
-                            ParamKind::Closure
-                        } else {
-                            ParamKind::Function
-                        });
-                        return (PatternRefutability::Irrefutable, param.ty().is_some())
-                    },
-                    ast::MatchArm(__) => PatternRefutability::Refutable,
-                    ast::Condition(__) => PatternRefutability::Refutable,
-                    ast::ForExpr(__) => PatternRefutability::Irrefutable,
-                    _ => PatternRefutability::Irrefutable,
-                }
-            };
-            (refutability, false)
-        });
-    PatternContext { refutability, is_param, has_type_ascription }
-}
 fn find_node_with_range<N: AstNode>(syntax: &SyntaxNode, range: TextRange) -> Option<N> {
     syntax.covering_element(range).ancestors().find_map(N::cast)
 }
@@ -1018,20 +996,6 @@ fn foo() {
         )
     }
 
-    #[test]
-    fn expected_type_struct_field_followed_by_comma() {
-        cov_mark::check!(expected_type_struct_field_followed_by_comma);
-        check_expected_type_and_name(
-            r#"
-struct Foo { a: u32 }
-fn foo() {
-    Foo { a: $0, };
-}
-"#,
-            expect![[r#"ty: u32, name: a"#]],
-        )
-    }
-
     #[test]
     fn expected_type_generic_struct_field() {
         check_expected_type_and_name(
diff --git a/crates/ide_completion/src/lib.rs b/crates/ide_completion/src/lib.rs
index 6a087edc4f..d9618642c4 100644
--- a/crates/ide_completion/src/lib.rs
+++ b/crates/ide_completion/src/lib.rs
@@ -195,7 +195,7 @@ pub fn resolve_completion_edits(
         let items_with_name = items_locator::items_with_name(
             &ctx.sema,
             current_crate,
-            NameToImport::exact_case_sensitive(imported_name),
+            NameToImport::Exact(imported_name),
             items_locator::AssocItemSearch::Include,
             Some(items_locator::DEFAULT_QUERY_SEARCH_LIMIT.inner()),
         );
diff --git a/crates/ide_completion/src/patterns.rs b/crates/ide_completion/src/patterns.rs
index b50f76b911..13c739325c 100644
--- a/crates/ide_completion/src/patterns.rs
+++ b/crates/ide_completion/src/patterns.rs
@@ -33,6 +33,8 @@ pub(crate) enum ImmediatePrevSibling {
 /// from which file the nodes are.
 #[derive(Clone, Debug, PartialEq, Eq)]
 pub(crate) enum ImmediateLocation {
+    Use,
+    UseTree,
     Rename,
     Impl,
     Trait,
@@ -45,7 +47,10 @@ pub(crate) enum ImmediateLocation {
     TypeBound,
     Variant,
     /// Fake file ast node
+    Attribute(ast::Attr),
+    /// Fake file ast node
     ModDeclaration(ast::Module),
+    Visibility(ast::Visibility),
     /// Original file ast node
     MethodCall {
         receiver: Option<ast::Expr>,
@@ -201,6 +206,9 @@ pub(crate) fn determine_location(
     let res = match_ast! {
         match parent {
             ast::IdentPat(_it) => ImmediateLocation::IdentPat,
+            ast::Use(_it) => ImmediateLocation::Use,
+            ast::UseTree(_it) => ImmediateLocation::UseTree,
+            ast::UseTreeList(_it) => ImmediateLocation::UseTree,
             ast::Rename(_it) => ImmediateLocation::Rename,
             ast::StmtList(_it) => ImmediateLocation::StmtList,
             ast::SourceFile(_it) => ImmediateLocation::ItemList,
@@ -234,6 +242,7 @@ pub(crate) fn determine_location(
                     return None;
                 }
             },
+            ast::Attr(it) => ImmediateLocation::Attribute(it),
             ast::FieldExpr(it) => {
                 let receiver = it
                     .expr()
@@ -259,6 +268,8 @@ pub(crate) fn determine_location(
                     .and_then(|r| find_node_with_range(original_file, r)),
                 has_parens: it.arg_list().map_or(false, |it| it.l_paren_token().is_some())
             },
+            ast::Visibility(it) => it.pub_token()
+                .and_then(|t| (t.text_range().end() < offset).then(|| ImmediateLocation::Visibility(it)))?,
             _ => return None,
         }
     };
@@ -406,6 +417,14 @@ mod tests {
         check_location(r"impl A { fn f$0 }", None);
     }
 
+    #[test]
+    fn test_use_loc() {
+        check_location(r"use f$0", ImmediateLocation::Use);
+        check_location(r"use f$0;", ImmediateLocation::Use);
+        check_location(r"use f::{f$0}", ImmediateLocation::UseTree);
+        check_location(r"use {f$0}", ImmediateLocation::UseTree);
+    }
+
     #[test]
     fn test_record_field_loc() {
         check_location(r"struct Foo { f$0 }", ImmediateLocation::RecordField);
diff --git a/crates/ide_completion/src/render/builder_ext.rs b/crates/ide_completion/src/render/builder_ext.rs
index 4d04054445..ea8222f76a 100644
--- a/crates/ide_completion/src/render/builder_ext.rs
+++ b/crates/ide_completion/src/render/builder_ext.rs
@@ -4,7 +4,7 @@ use either::Either;
 use itertools::Itertools;
 use syntax::ast::{self, HasName};
 
-use crate::{context::PathKind, item::Builder, patterns::ImmediateLocation, CompletionContext};
+use crate::{context::CallKind, item::Builder, patterns::ImmediateLocation, CompletionContext};
 
 #[derive(Debug)]
 pub(super) enum Params {
@@ -30,11 +30,11 @@ impl Builder {
         if !ctx.config.add_call_parenthesis {
             return false;
         }
-        if let Some(PathKind::Use) = ctx.path_kind() {
+        if ctx.in_use_tree() {
             cov_mark::hit!(no_parens_in_use_item);
             return false;
         }
-        if matches!(ctx.path_kind(), Some(PathKind::Expr | PathKind::Pat) if ctx.path_is_call())
+        if matches!(ctx.path_call_kind(), Some(CallKind::Expr | CallKind::Pat))
             | matches!(
                 ctx.completion_location,
                 Some(ImmediateLocation::MethodCall { has_parens: true, .. })
diff --git a/crates/ide_completion/src/render/macro_.rs b/crates/ide_completion/src/render/macro_.rs
index ac1f152aea..4b505b3f5a 100644
--- a/crates/ide_completion/src/render/macro_.rs
+++ b/crates/ide_completion/src/render/macro_.rs
@@ -9,7 +9,7 @@ use syntax::{
 };
 
 use crate::{
-    context::PathKind,
+    context::CallKind,
     item::{CompletionItem, ImportEdit},
     render::RenderContext,
 };
@@ -61,8 +61,9 @@ impl<'a> MacroRender<'a> {
         }
 
         let needs_bang = self.macro_.is_fn_like()
-            && !matches!(self.ctx.completion.path_kind(), Some(PathKind::Mac | PathKind::Use));
-        let has_parens = self.ctx.completion.path_is_call();
+            && !(self.ctx.completion.in_use_tree()
+                || matches!(self.ctx.completion.path_call_kind(), Some(CallKind::Mac)));
+        let has_parens = self.ctx.completion.path_call_kind().is_some();
 
         match self.ctx.snippet_cap() {
             Some(cap) if needs_bang && !has_parens => {
@@ -85,7 +86,8 @@ impl<'a> MacroRender<'a> {
     }
 
     fn needs_bang(&self) -> bool {
-        !matches!(self.ctx.completion.path_kind(), Some(PathKind::Mac | PathKind::Use))
+        !self.ctx.completion.in_use_tree()
+            && !matches!(self.ctx.completion.path_call_kind(), Some(CallKind::Mac))
     }
 
     fn label(&self) -> SmolStr {
diff --git a/crates/ide_db/src/helpers.rs b/crates/ide_db/src/helpers.rs
index 18a5f64891..1b9cb7ff51 100644
--- a/crates/ide_db/src/helpers.rs
+++ b/crates/ide_db/src/helpers.rs
@@ -4,7 +4,6 @@ pub mod generated_lints;
 pub mod import_assets;
 pub mod insert_use;
 pub mod merge_imports;
-pub mod insert_whitespace_into_node;
 pub mod node_ext;
 pub mod rust_doc;
 
@@ -306,24 +305,20 @@ pub fn lint_eq_or_in_group(lint: &str, lint_is: &str) -> bool {
     }
 }
 
-/// Parses the input token tree as comma separated plain paths.
+/// Parses the input token tree as comma separated paths.
 pub fn parse_tt_as_comma_sep_paths(input: ast::TokenTree) -> Option<Vec<ast::Path>> {
-    let r_paren = input.r_paren_token();
-    let tokens =
-        input.syntax().children_with_tokens().skip(1).map_while(|it| match it.into_token() {
-            // seeing a keyword means the attribute is unclosed so stop parsing here
-            Some(tok) if tok.kind().is_keyword() => None,
-            // don't include the right token tree parenthesis if it exists
-            tok @ Some(_) if tok == r_paren => None,
-            // only nodes that we can find are other TokenTrees, those are unexpected in this parse though
-            None => None,
-            Some(tok) => Some(tok),
-        });
+    let r_paren = input.r_paren_token()?;
+    let tokens = input
+        .syntax()
+        .children_with_tokens()
+        .skip(1)
+        .take_while(|it| it.as_token() != Some(&r_paren));
     let input_expressions = tokens.into_iter().group_by(|tok| tok.kind() == T![,]);
-    let paths = input_expressions
-        .into_iter()
-        .filter_map(|(is_sep, group)| (!is_sep).then(|| group))
-        .filter_map(|mut tokens| ast::Path::parse(&tokens.join("")).ok())
-        .collect();
-    Some(paths)
+    Some(
+        input_expressions
+            .into_iter()
+            .filter_map(|(is_sep, group)| (!is_sep).then(|| group))
+            .filter_map(|mut tokens| ast::Path::parse(&tokens.join("")).ok())
+            .collect::<Vec<ast::Path>>(),
+    )
 }
diff --git a/crates/ide_db/src/helpers/import_assets.rs b/crates/ide_db/src/helpers/import_assets.rs
index d4bc7c7cf8..9a8adf167c 100644
--- a/crates/ide_db/src/helpers/import_assets.rs
+++ b/crates/ide_db/src/helpers/import_assets.rs
@@ -68,23 +68,17 @@ pub struct FirstSegmentUnresolved {
 /// A name that will be used during item lookups.
 #[derive(Debug, Clone)]
 pub enum NameToImport {
-    /// Requires items with names that exactly match the given string, bool indicatse case-sensitivity.
-    Exact(String, bool),
+    /// Requires items with names that exactly match the given string, case-sensitive.
+    Exact(String),
     /// Requires items with names that case-insensitively contain all letters from the string,
     /// in the same order, but not necessary adjacent.
     Fuzzy(String),
 }
 
-impl NameToImport {
-    pub fn exact_case_sensitive(s: String) -> NameToImport {
-        NameToImport::Exact(s, true)
-    }
-}
-
 impl NameToImport {
     pub fn text(&self) -> &str {
         match self {
-            NameToImport::Exact(text, _) => text.as_str(),
+            NameToImport::Exact(text) => text.as_str(),
             NameToImport::Fuzzy(text) => text.as_str(),
         }
     }
@@ -146,7 +140,7 @@ impl ImportAssets {
         if let Some(_) = path.qualifier() {
             return None;
         }
-        let name = NameToImport::exact_case_sensitive(path.segment()?.name_ref()?.to_string());
+        let name = NameToImport::Exact(path.segment()?.name_ref()?.to_string());
         let candidate_node = attr.syntax().clone();
         Some(Self {
             import_candidate: ImportCandidate::Path(PathImportCandidate { qualifier: None, name }),
@@ -236,18 +230,6 @@ impl ImportAssets {
         self.search_for(sema, None)
     }
 
-    pub fn path_fuzzy_name_to_exact(&mut self, case_sensitive: bool) {
-        if let ImportCandidate::Path(PathImportCandidate { name: to_import, .. }) =
-            &mut self.import_candidate
-        {
-            let name = match to_import {
-                NameToImport::Fuzzy(name) => std::mem::take(name),
-                _ => return,
-            };
-            *to_import = NameToImport::Exact(name, case_sensitive);
-        }
-    }
-
     fn search_for(
         &self,
         sema: &Semantics<RootDatabase>,
@@ -280,7 +262,7 @@ impl ImportAssets {
         .into_iter()
         .filter(|import| import.import_path.len() > 1)
         .filter(|import| !scope_definitions.contains(&ScopeDef::from(import.item_to_import)))
-        .sorted_by(|a, b| a.import_path.cmp(&b.import_path))
+        .sorted_by_key(|import| import.import_path.clone())
         .collect()
     }
 
@@ -309,9 +291,9 @@ fn path_applicable_imports(
                 current_crate,
                 path_candidate.name.clone(),
                 // FIXME: we could look up assoc items by the input and propose those in completion,
-                // but that requires more preparation first:
+                // but that requries more preparation first:
                 // * store non-trait assoc items in import_map to fully enable this lookup
-                // * ensure that does not degrade the performance (benchmark it)
+                // * ensure that does not degrade the performance (bencmark it)
                 // * write more logic to check for corresponding trait presence requirement (we're unable to flyimport multiple item right now)
                 // * improve the associated completion item matching and/or scoring to ensure no noisy completions appear
                 //
@@ -472,10 +454,8 @@ fn trait_applicable_items(
 
     let db = sema.db;
 
-    let inherent_traits = trait_candidate.receiver_ty.applicable_inherent_traits(db);
-    let env_traits = trait_candidate.receiver_ty.env_traits(db);
-    let related_traits = inherent_traits.chain(env_traits).collect::<FxHashSet<_>>();
-
+    let related_dyn_traits =
+        trait_candidate.receiver_ty.applicable_inherent_traits(db).collect::<FxHashSet<_>>();
     let mut required_assoc_items = FxHashSet::default();
     let trait_candidates = items_locator::items_with_name(
         sema,
@@ -487,7 +467,7 @@ fn trait_applicable_items(
     .filter_map(|input| item_as_assoc(db, input))
     .filter_map(|assoc| {
         let assoc_item_trait = assoc.containing_trait(db)?;
-        if related_traits.contains(&assoc_item_trait) {
+        if related_dyn_traits.contains(&assoc_item_trait) {
             None
         } else {
             required_assoc_items.insert(assoc);
@@ -581,9 +561,7 @@ impl ImportCandidate {
             Some(_) => None,
             None => Some(Self::TraitMethod(TraitImportCandidate {
                 receiver_ty: sema.type_of_expr(&method_call.receiver()?)?.adjusted(),
-                assoc_item_name: NameToImport::exact_case_sensitive(
-                    method_call.name_ref()?.to_string(),
-                ),
+                assoc_item_name: NameToImport::Exact(method_call.name_ref()?.to_string()),
             })),
         }
     }
@@ -595,7 +573,7 @@ impl ImportCandidate {
         path_import_candidate(
             sema,
             path.qualifier(),
-            NameToImport::exact_case_sensitive(path.segment()?.name_ref()?.to_string()),
+            NameToImport::Exact(path.segment()?.name_ref()?.to_string()),
         )
     }
 
@@ -609,7 +587,7 @@ impl ImportCandidate {
         }
         Some(ImportCandidate::Path(PathImportCandidate {
             qualifier: None,
-            name: NameToImport::exact_case_sensitive(name.to_string()),
+            name: NameToImport::Exact(name.to_string()),
         }))
     }
 
diff --git a/crates/ide_db/src/helpers/insert_whitespace_into_node.rs b/crates/ide_db/src/helpers/insert_whitespace_into_node.rs
deleted file mode 100644
index 251a4caa13..0000000000
--- a/crates/ide_db/src/helpers/insert_whitespace_into_node.rs
+++ /dev/null
@@ -1,119 +0,0 @@
-//! Utilities for formatting macro expanded nodes until we get a proper formatter.
-use syntax::{
-    ast::make,
-    ted::{self, Position},
-    NodeOrToken,
-    SyntaxKind::{self, *},
-    SyntaxNode, SyntaxToken, WalkEvent, T,
-};
-
-// FIXME: It would also be cool to share logic here and in the mbe tests,
-// which are pretty unreadable at the moment.
-/// Renders a [`SyntaxNode`] with whitespace inserted between tokens that require them.
-pub fn insert_ws_into(syn: SyntaxNode) -> SyntaxNode {
-    let mut indent = 0;
-    let mut last: Option<SyntaxKind> = None;
-    let mut mods = Vec::new();
-    let syn = syn.clone_subtree().clone_for_update();
-
-    let before = Position::before;
-    let after = Position::after;
-
-    let do_indent = |pos: fn(_) -> Position, token: &SyntaxToken, indent| {
-        (pos(token.clone()), make::tokens::whitespace(&" ".repeat(2 * indent)))
-    };
-    let do_ws = |pos: fn(_) -> Position, token: &SyntaxToken| {
-        (pos(token.clone()), make::tokens::single_space())
-    };
-    let do_nl = |pos: fn(_) -> Position, token: &SyntaxToken| {
-        (pos(token.clone()), make::tokens::single_newline())
-    };
-
-    for event in syn.preorder_with_tokens() {
-        let token = match event {
-            WalkEvent::Enter(NodeOrToken::Token(token)) => token,
-            WalkEvent::Leave(NodeOrToken::Node(node))
-                if matches!(node.kind(), ATTR | MATCH_ARM | STRUCT | ENUM | UNION | FN | IMPL) =>
-            {
-                if indent > 0 {
-                    mods.push((
-                        Position::after(node.clone()),
-                        make::tokens::whitespace(&" ".repeat(2 * indent)),
-                    ));
-                }
-                if node.parent().is_some() {
-                    mods.push((Position::after(node), make::tokens::single_newline()));
-                }
-                continue;
-            }
-            _ => continue,
-        };
-        let tok = &token;
-
-        let is_next = |f: fn(SyntaxKind) -> bool, default| -> bool {
-            tok.next_token().map(|it| f(it.kind())).unwrap_or(default)
-        };
-        let is_last =
-            |f: fn(SyntaxKind) -> bool, default| -> bool { last.map(f).unwrap_or(default) };
-
-        match tok.kind() {
-            k if is_text(k) && is_next(|it| !it.is_punct(), true) => {
-                mods.push(do_ws(after, tok));
-            }
-            L_CURLY if is_next(|it| it != R_CURLY, true) => {
-                indent += 1;
-                if is_last(is_text, false) {
-                    mods.push(do_ws(before, tok));
-                }
-
-                if indent > 0 {
-                    mods.push(do_indent(after, tok, indent));
-                }
-                mods.push(do_nl(after, &tok));
-            }
-            R_CURLY if is_last(|it| it != L_CURLY, true) => {
-                indent = indent.saturating_sub(1);
-
-                if indent > 0 {
-                    mods.push(do_indent(before, tok, indent));
-                }
-                mods.push(do_nl(before, tok));
-            }
-            R_CURLY => {
-                if indent > 0 {
-                    mods.push(do_indent(after, tok, indent));
-                }
-                mods.push(do_nl(after, tok));
-            }
-            LIFETIME_IDENT if is_next(|it| is_text(it), true) => {
-                mods.push(do_ws(after, tok));
-            }
-            AS_KW => {
-                mods.push(do_ws(after, tok));
-            }
-            T![;] => {
-                if indent > 0 {
-                    mods.push(do_indent(after, tok, indent));
-                }
-                mods.push(do_nl(after, tok));
-            }
-            T![->] | T![=] | T![=>] => {
-                mods.push(do_ws(before, tok));
-                mods.push(do_ws(after, tok));
-            }
-            _ => (),
-        }
-
-        last = Some(tok.kind());
-    }
-
-    for (pos, insert) in mods {
-        ted::insert(pos, insert);
-    }
-
-    syn
-}
-
-fn is_text(k: SyntaxKind) -> bool {
-    k.is_keyword() || k.is_literal() || k == IDENT
-}
diff --git a/crates/ide_db/src/items_locator.rs b/crates/ide_db/src/items_locator.rs
index ca8266b6d2..e0dbe6caf0 100644
--- a/crates/ide_db/src/items_locator.rs
+++ b/crates/ide_db/src/items_locator.rs
@@ -50,18 +50,16 @@ pub fn items_with_name<'a>(
     });
 
     let (mut local_query, mut external_query) = match name {
-        NameToImport::Exact(exact_name, case_sensitive) => {
+        NameToImport::Exact(exact_name) => {
             let mut local_query = symbol_index::Query::new(exact_name.clone());
             local_query.exact();
 
             let external_query = import_map::Query::new(exact_name)
                 .name_only()
-                .search_mode(import_map::SearchMode::Equals);
+                .search_mode(import_map::SearchMode::Equals)
+                .case_sensitive();
 
-            (
-                local_query,
-                if case_sensitive { external_query.case_sensitive() } else { external_query },
-            )
+            (local_query, external_query)
         }
         NameToImport::Fuzzy(fuzzy_search_string) => {
             let mut local_query = symbol_index::Query::new(fuzzy_search_string.clone());
diff --git a/crates/ide_diagnostics/src/handlers/incorrect_case.rs b/crates/ide_diagnostics/src/handlers/incorrect_case.rs
index 6a78c08d44..3206a63a90 100644
--- a/crates/ide_diagnostics/src/handlers/incorrect_case.rs
+++ b/crates/ide_diagnostics/src/handlers/incorrect_case.rs
@@ -332,15 +332,6 @@ fn main() {
         check_diagnostics(
             r#"
 #![allow(non_snake_case)]
-#![allow(non_camel_case_types)]
-
-struct S {
-    fooBar: bool,
-}
-
-enum E {
-    fooBar,
-}
 
 mod F {
     fn CheckItWorksWithCrateAttr(BAD_NAME_HI: u8) {}
diff --git a/crates/ide_ssr/src/resolving.rs b/crates/ide_ssr/src/resolving.rs
index 7902295d29..84e5f82604 100644
--- a/crates/ide_ssr/src/resolving.rs
+++ b/crates/ide_ssr/src/resolving.rs
@@ -222,7 +222,7 @@ impl<'db> ResolutionScope<'db> {
             adt.ty(self.scope.db).iterate_path_candidates(
                 self.scope.db,
                 self.scope.module()?.krate(),
-                &self.scope.visible_traits(),
+                &self.scope.traits_in_scope(),
                 None,
                 |_ty, assoc_item| {
                     let item_name = assoc_item.name(self.scope.db)?;
diff --git a/crates/mbe/src/lib.rs b/crates/mbe/src/lib.rs
index 1a56878fdb..b58b86b38d 100644
--- a/crates/mbe/src/lib.rs
+++ b/crates/mbe/src/lib.rs
@@ -10,7 +10,7 @@ mod parser;
 mod expander;
 mod syntax_bridge;
 mod tt_iter;
-mod to_parser_tokens;
+mod subtree_source;
 
 #[cfg(test)]
 mod benchmark;
diff --git a/crates/mbe/src/subtree_source.rs b/crates/mbe/src/subtree_source.rs
new file mode 100644
index 0000000000..6bdd787e30
--- /dev/null
+++ b/crates/mbe/src/subtree_source.rs
@@ -0,0 +1,174 @@
+//! Our parser is generic over the source of tokens it parses.
+//!
+//! This module defines tokens sourced from declarative macros.
+
+use parser::{Token, TokenSource};
+use syntax::{lex_single_syntax_kind, SmolStr, SyntaxKind, SyntaxKind::*, T};
+use tt::buffer::TokenBuffer;
+
+#[derive(Debug, Clone, Eq, PartialEq)]
+struct TtToken {
+    tt: Token,
+    text: SmolStr,
+}
+
+pub(crate) struct SubtreeTokenSource {
+    cached: Vec<TtToken>,
+    curr: (Token, usize),
+}
+
+impl<'a> SubtreeTokenSource {
+    pub(crate) fn new(buffer: &TokenBuffer) -> SubtreeTokenSource {
+        let mut current = buffer.begin();
+        let mut cached = Vec::with_capacity(100);
+
+        while !current.eof() {
+            let cursor = current;
+            let tt = cursor.token_tree();
+
+            // Check if it is lifetime
+            if let Some(tt::buffer::TokenTreeRef::Leaf(tt::Leaf::Punct(punct), _)) = tt {
+                if punct.char == '\'' {
+                    let next = cursor.bump();
+                    if let Some(tt::buffer::TokenTreeRef::Leaf(tt::Leaf::Ident(ident), _)) =
+                        next.token_tree()
+                    {
+                        let text = SmolStr::new("'".to_string() + &ident.text);
+                        cached.push(TtToken {
+                            tt: Token { kind: LIFETIME_IDENT, is_jointed_to_next: false },
+                            text,
+                        });
+                        current = next.bump();
+                        continue;
+                    } else {
+                        panic!("Next token must be ident : {:#?}", next.token_tree());
+                    }
+                }
+            }
+
+            current = match tt {
+                Some(tt::buffer::TokenTreeRef::Leaf(leaf, _)) => {
+                    cached.push(convert_leaf(leaf));
+                    cursor.bump()
+                }
+                Some(tt::buffer::TokenTreeRef::Subtree(subtree, _)) => {
+                    if let Some(d) = subtree.delimiter_kind() {
+                        cached.push(convert_delim(d, false));
+                    }
+                    cursor.subtree().unwrap()
+                }
+                None => match cursor.end() {
+                    Some(subtree) => {
+                        if let Some(d) = subtree.delimiter_kind() {
+                            cached.push(convert_delim(d, true));
+                        }
+                        cursor.bump()
+                    }
+                    None => continue,
+                },
+            };
+        }
+
+        let mut res = SubtreeTokenSource {
+            curr: (Token { kind: EOF, is_jointed_to_next: false }, 0),
+            cached,
+        };
+        res.curr = (res.token(0), 0);
+        res
+    }
+
+    fn token(&self, pos: usize) -> Token {
+        match self.cached.get(pos) {
+            Some(it) => it.tt,
+            None => Token { kind: EOF, is_jointed_to_next: false },
+        }
+    }
+}
+
+impl<'a> TokenSource for SubtreeTokenSource {
+    fn current(&self) -> Token {
+        self.curr.0
+    }
+
+    /// Lookahead n token
+    fn lookahead_nth(&self, n: usize) -> Token {
+        self.token(self.curr.1 + n)
+    }
+
+    /// bump cursor to next token
+    fn bump(&mut self) {
+        if self.current().kind == EOF {
+            return;
+        }
+        self.curr = (self.token(self.curr.1 + 1), self.curr.1 + 1);
+    }
+
+    /// Is the current token a specified keyword?
+    fn is_keyword(&self, kw: &str) -> bool {
+        match self.cached.get(self.curr.1) {
+            Some(t) => t.text == *kw,
+            None => false,
+        }
+    }
+}
+
+fn convert_delim(d: tt::DelimiterKind, closing: bool) -> TtToken {
+    let (kinds, texts) = match d {
+        tt::DelimiterKind::Parenthesis => ([T!['('], T![')']], "()"),
+        tt::DelimiterKind::Brace => ([T!['{'], T!['}']], "{}"),
+        tt::DelimiterKind::Bracket => ([T!['['], T![']']], "[]"),
+    };
+
+    let idx = closing as usize;
+    let kind = kinds[idx];
+    let text = &texts[idx..texts.len() - (1 - idx)];
+    TtToken { tt: Token { kind, is_jointed_to_next: false }, text: SmolStr::new(text) }
+}
+
+fn convert_literal(l: &tt::Literal) -> TtToken {
+    let is_negated = l.text.starts_with('-');
+    let inner_text = &l.text[if is_negated { 1 } else { 0 }..];
+
+    let kind = lex_single_syntax_kind(inner_text)
+        .map(|(kind, _error)| kind)
+        .filter(|kind| {
+            kind.is_literal() && (!is_negated || matches!(kind, FLOAT_NUMBER | INT_NUMBER))
+        })
+        .unwrap_or_else(|| panic!("Fail to convert given literal {:#?}", &l));
+
+    TtToken { tt: Token { kind, is_jointed_to_next: false }, text: l.text.clone() }
+}
+
+fn convert_ident(ident: &tt::Ident) -> TtToken {
+    let kind = match ident.text.as_ref() {
+        "true" => T![true],
+        "false" => T![false],
+        "_" => UNDERSCORE,
+        i if i.starts_with('\'') => LIFETIME_IDENT,
+        _ => SyntaxKind::from_keyword(ident.text.as_str()).unwrap_or(IDENT),
+    };
+
+    TtToken { tt: Token { kind, is_jointed_to_next: false }, text: ident.text.clone() }
+}
+
+fn convert_punct(p: tt::Punct) -> TtToken {
+    let kind = match SyntaxKind::from_char(p.char) {
+        None => panic!("{:#?} is not a valid punct", p),
+        Some(kind) => kind,
+    };
+
+    let text = {
+        let mut buf = [0u8; 4];
+        let s: &str = p.char.encode_utf8(&mut buf);
+        SmolStr::new(s)
+    };
+    TtToken { tt: Token { kind, is_jointed_to_next: p.spacing == tt::Spacing::Joint }, text }
+}
+
+fn convert_leaf(leaf: &tt::Leaf) -> TtToken {
+    match leaf {
+        tt::Leaf::Literal(l) => convert_literal(l),
+        tt::Leaf::Ident(ident) => convert_ident(ident),
+        tt::Leaf::Punct(punct) => convert_punct(*punct),
+    }
+}
diff --git a/crates/mbe/src/syntax_bridge.rs b/crates/mbe/src/syntax_bridge.rs
index 28a23f6be2..0b65fa171f 100644
--- a/crates/mbe/src/syntax_bridge.rs
+++ b/crates/mbe/src/syntax_bridge.rs
@@ -12,7 +12,7 @@ use syntax::{
 use tt::buffer::{Cursor, TokenBuffer};
 
 use crate::{
-    to_parser_tokens::to_parser_tokens, tt_iter::TtIter, ExpandError, ParserEntryPoint, TokenMap,
+    subtree_source::SubtreeTokenSource, tt_iter::TtIter, ExpandError, ParserEntryPoint, TokenMap,
 };
 
 /// Convert the syntax node to a `TokenTree` (what macro
@@ -56,9 +56,9 @@ pub fn token_tree_to_syntax_node(
         }
         _ => TokenBuffer::from_subtree(tt),
     };
-    let parser_tokens = to_parser_tokens(&buffer);
+    let mut token_source = SubtreeTokenSource::new(&buffer);
     let mut tree_sink = TtTreeSink::new(buffer.begin());
-    parser::parse(&parser_tokens, &mut tree_sink, entry_point);
+    parser::parse(&mut token_source, &mut tree_sink, entry_point);
     if tree_sink.roots.len() != 1 {
         return Err(ExpandError::ConversionError);
     }
diff --git a/crates/mbe/src/to_parser_tokens.rs b/crates/mbe/src/to_parser_tokens.rs
deleted file mode 100644
index 644689f432..0000000000
--- a/crates/mbe/src/to_parser_tokens.rs
+++ /dev/null
@@ -1,99 +0,0 @@
-//! Convert macro-by-example tokens which are specific to macro expansion into a
-//! format that works for our parser.
-
-use syntax::{lex_single_syntax_kind, SyntaxKind, SyntaxKind::*, T};
-use tt::buffer::TokenBuffer;
-
-pub(crate) fn to_parser_tokens(buffer: &TokenBuffer) -> parser::Tokens {
-    let mut res = parser::Tokens::default();
-
-    let mut current = buffer.begin();
-
-    while !current.eof() {
-        let cursor = current;
-        let tt = cursor.token_tree();
-
-        // Check if it is lifetime
-        if let Some(tt::buffer::TokenTreeRef::Leaf(tt::Leaf::Punct(punct), _)) = tt {
-            if punct.char == '\'' {
-                let next = cursor.bump();
-                match next.token_tree() {
-                    Some(tt::buffer::TokenTreeRef::Leaf(tt::Leaf::Ident(_ident), _)) => {
-                        res.push(LIFETIME_IDENT);
-                        current = next.bump();
-                        continue;
-                    }
-                    _ => panic!("Next token must be ident : {:#?}", next.token_tree()),
-                }
-            }
-        }
-
-        current = match tt {
-            Some(tt::buffer::TokenTreeRef::Leaf(leaf, _)) => {
-                match leaf {
-                    tt::Leaf::Literal(lit) => {
-                        let is_negated = lit.text.starts_with('-');
-                        let inner_text = &lit.text[if is_negated { 1 } else { 0 }..];
-
-                        let kind = lex_single_syntax_kind(inner_text)
-                            .map(|(kind, _error)| kind)
-                            .filter(|kind| {
-                                kind.is_literal()
-                                    && (!is_negated || matches!(kind, FLOAT_NUMBER | INT_NUMBER))
-                            })
-                            .unwrap_or_else(|| panic!("Fail to convert given literal {:#?}", &lit));
-
-                        res.push(kind);
-                    }
-                    tt::Leaf::Ident(ident) => match ident.text.as_ref() {
-                        "_" => res.push(T![_]),
-                        i if i.starts_with('\'') => res.push(LIFETIME_IDENT),
-                        _ => match SyntaxKind::from_keyword(&ident.text) {
-                            Some(kind) => res.push(kind),
-                            None => {
-                                let contextual_keyword =
-                                    SyntaxKind::from_contextual_keyword(&ident.text)
-                                        .unwrap_or(SyntaxKind::IDENT);
-                                res.push_ident(contextual_keyword);
-                            }
-                        },
-                    },
-                    tt::Leaf::Punct(punct) => {
-                        let kind = SyntaxKind::from_char(punct.char)
-                            .unwrap_or_else(|| panic!("{:#?} is not a valid punct", punct));
-                        res.push(kind);
-                        if punct.spacing == tt::Spacing::Joint {
-                            res.was_joint();
-                        }
-                    }
-                }
-                cursor.bump()
-            }
-            Some(tt::buffer::TokenTreeRef::Subtree(subtree, _)) => {
-                if let Some(d) = subtree.delimiter_kind() {
-                    res.push(match d {
-                        tt::DelimiterKind::Parenthesis => T!['('],
-                        tt::DelimiterKind::Brace => T!['{'],
-                        tt::DelimiterKind::Bracket => T!['['],
-                    });
-                }
-                cursor.subtree().unwrap()
-            }
-            None => match cursor.end() {
-                Some(subtree) => {
-                    if let Some(d) = subtree.delimiter_kind() {
-                        res.push(match d {
-                            tt::DelimiterKind::Parenthesis => T![')'],
-                            tt::DelimiterKind::Brace => T!['}'],
-                            tt::DelimiterKind::Bracket => T![']'],
-                        })
-                    }
-                    cursor.bump()
-                }
-                None => continue,
-            },
-        };
-    }
-
-    res
-}
diff --git a/crates/mbe/src/tt_iter.rs b/crates/mbe/src/tt_iter.rs
index d05e84b0f0..ff0272808b 100644
--- a/crates/mbe/src/tt_iter.rs
+++ b/crates/mbe/src/tt_iter.rs
@@ -1,7 +1,7 @@
 //! A "Parser" structure for token trees. We use this when parsing a declarative
 //! macro definition into a list of patterns and templates.
 
-use crate::{to_parser_tokens::to_parser_tokens, ExpandError, ExpandResult, ParserEntryPoint};
+use crate::{subtree_source::SubtreeTokenSource, ExpandError, ExpandResult, ParserEntryPoint};
 
 use parser::TreeSink;
 use syntax::SyntaxKind;
@@ -116,10 +116,10 @@ impl<'a> TtIter<'a> {
         }
 
         let buffer = TokenBuffer::from_tokens(self.inner.as_slice());
-        let parser_tokens = to_parser_tokens(&buffer);
+        let mut src = SubtreeTokenSource::new(&buffer);
         let mut sink = OffsetTokenSink { cursor: buffer.begin(), error: false };
 
-        parser::parse(&parser_tokens, &mut sink, entry_point);
+        parser::parse(&mut src, &mut sink, entry_point);
 
         let mut err = if !sink.cursor.is_root() || sink.error {
             Some(err!("expected {:?}", entry_point))
diff --git a/crates/parser/src/grammar/expressions.rs b/crates/parser/src/grammar/expressions.rs
index 4b9c579a05..54eb96d84e 100644
--- a/crates/parser/src/grammar/expressions.rs
+++ b/crates/parser/src/grammar/expressions.rs
@@ -296,7 +296,10 @@ fn lhs(p: &mut Parser, r: Restrictions) -> Option<(CompletedMarker, BlockLike)>
         T![&] => {
             m = p.start();
             p.bump(T![&]);
-            if p.at_contextual_kw(T![raw]) && (p.nth_at(1, T![mut]) || p.nth_at(1, T![const])) {
+            if p.at(IDENT)
+                && p.at_contextual_kw("raw")
+                && (p.nth_at(1, T![mut]) || p.nth_at(1, T![const]))
+            {
                 p.bump_remap(T![raw]);
                 p.bump_any();
             } else {
diff --git a/crates/parser/src/grammar/items.rs b/crates/parser/src/grammar/items.rs
index 896efaf375..39be0e1a19 100644
--- a/crates/parser/src/grammar/items.rs
+++ b/crates/parser/src/grammar/items.rs
@@ -122,14 +122,14 @@ pub(super) fn opt_item(p: &mut Parser, m: Marker) -> Result<(), Marker> {
         has_mods = true;
         abi(p);
     }
-    if p.at_contextual_kw(T![auto]) && p.nth(1) == T![trait] {
+    if p.at(IDENT) && p.at_contextual_kw("auto") && p.nth(1) == T![trait] {
         p.bump_remap(T![auto]);
         has_mods = true;
     }
 
     // test default_item
     // default impl T for Foo {}
-    if p.at_contextual_kw(T![default]) {
+    if p.at(IDENT) && p.at_contextual_kw("default") {
         match p.nth(1) {
             T![fn] | T![type] | T![const] | T![impl] => {
                 p.bump_remap(T![default]);
@@ -176,7 +176,7 @@ pub(super) fn opt_item(p: &mut Parser, m: Marker) -> Result<(), Marker> {
 
     // test existential_type
     // existential type Foo: Fn() -> usize;
-    if p.at_contextual_kw(T![existential]) && p.nth(1) == T![type] {
+    if p.at(IDENT) && p.at_contextual_kw("existential") && p.nth(1) == T![type] {
         p.bump_remap(T![existential]);
         has_mods = true;
     }
@@ -224,10 +224,10 @@ fn opt_item_without_modifiers(p: &mut Parser, m: Marker) -> Result<(), Marker> {
         T![type] => type_alias(p, m),
         T![struct] => adt::strukt(p, m),
         T![enum] => adt::enum_(p, m),
-        IDENT if p.at_contextual_kw(T![union]) && p.nth(1) == IDENT => adt::union(p, m),
+        IDENT if p.at_contextual_kw("union") && p.nth(1) == IDENT => adt::union(p, m),
 
         T![macro] => macro_def(p, m),
-        IDENT if p.at_contextual_kw(T![macro_rules]) && p.nth(1) == BANG => macro_rules(p, m),
+        IDENT if p.at_contextual_kw("macro_rules") && p.nth(1) == BANG => macro_rules(p, m),
 
         T![const] if (la == IDENT || la == T![_] || la == T![mut]) => consts::konst(p, m),
         T![static] => consts::static_(p, m),
@@ -319,7 +319,7 @@ pub(crate) fn extern_item_list(p: &mut Parser) {
 }
 
 fn macro_rules(p: &mut Parser, m: Marker) {
-    assert!(p.at_contextual_kw(T![macro_rules]));
+    assert!(p.at_contextual_kw("macro_rules"));
     p.bump_remap(T![macro_rules]);
     p.expect(T![!]);
 
diff --git a/crates/parser/src/grammar/items/adt.rs b/crates/parser/src/grammar/items/adt.rs
index 83b7ff0578..c5bd5b14ba 100644
--- a/crates/parser/src/grammar/items/adt.rs
+++ b/crates/parser/src/grammar/items/adt.rs
@@ -10,7 +10,7 @@ pub(super) fn strukt(p: &mut Parser, m: Marker) {
 // test union_item
 // struct U { i: i32, f: f32 }
 pub(super) fn union(p: &mut Parser, m: Marker) {
-    assert!(p.at_contextual_kw(T![union]));
+    assert!(p.at_contextual_kw("union"));
     p.bump_remap(T![union]);
     struct_or_union(p, m, false);
 }
diff --git a/crates/parser/src/lib.rs b/crates/parser/src/lib.rs
index 2e2d96d027..51ee969204 100644
--- a/crates/parser/src/lib.rs
+++ b/crates/parser/src/lib.rs
@@ -1,11 +1,8 @@
 //! The Rust parser.
 //!
-//! NOTE: The crate is undergoing refactors, don't believe everything the docs
-//! say :-)
-//!
 //! The parser doesn't know about concrete representation of tokens and syntax
-//! trees. Abstract [`TokenSource`] and [`TreeSink`] traits are used instead. As
-//! a consequence, this crate does not contain a lexer.
+//! trees. Abstract [`TokenSource`] and [`TreeSink`] traits are used instead.
+//! As a consequence, this crate does not contain a lexer.
 //!
 //! The [`Parser`] struct from the [`parser`] module is a cursor into the
 //! sequence of tokens.  Parsing routines use [`Parser`] to inspect current
@@ -23,15 +20,40 @@ mod syntax_kind;
 mod event;
 mod parser;
 mod grammar;
-mod tokens;
 
 pub(crate) use token_set::TokenSet;
 
-pub use crate::{syntax_kind::SyntaxKind, tokens::Tokens};
+pub use syntax_kind::SyntaxKind;
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub struct ParseError(pub Box<String>);
 
+/// `TokenSource` abstracts the source of the tokens parser operates on.
+///
+/// Hopefully this will allow us to treat text and token trees in the same way!
+pub trait TokenSource {
+    fn current(&self) -> Token;
+
+    /// Lookahead n token
+    fn lookahead_nth(&self, n: usize) -> Token;
+
+    /// bump cursor to next token
+    fn bump(&mut self);
+
+    /// Is the current token a specified keyword?
+    fn is_keyword(&self, kw: &str) -> bool;
+}
+
+/// `Token` abstracts the cursor of `TokenSource` operates on.
+#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+pub struct Token {
+    /// What is the current token?
+    pub kind: SyntaxKind,
+
+    /// Is the current token joined to the next one (`> >` vs `>>`).
+    pub is_jointed_to_next: bool,
+}
+
 /// `TreeSink` abstracts details of a particular syntax tree implementation.
 pub trait TreeSink {
     /// Adds new token to the current branch.
@@ -70,11 +92,15 @@ pub enum ParserEntryPoint {
 }
 
 /// Parse given tokens into the given sink as a rust file.
-pub fn parse_source_file(tokens: &Tokens, tree_sink: &mut dyn TreeSink) {
-    parse(tokens, tree_sink, ParserEntryPoint::SourceFile);
+pub fn parse_source_file(token_source: &mut dyn TokenSource, tree_sink: &mut dyn TreeSink) {
+    parse(token_source, tree_sink, ParserEntryPoint::SourceFile);
 }
 
-pub fn parse(tokens: &Tokens, tree_sink: &mut dyn TreeSink, entry_point: ParserEntryPoint) {
+pub fn parse(
+    token_source: &mut dyn TokenSource,
+    tree_sink: &mut dyn TreeSink,
+    entry_point: ParserEntryPoint,
+) {
     let entry_point: fn(&'_ mut parser::Parser) = match entry_point {
         ParserEntryPoint::SourceFile => grammar::entry_points::source_file,
         ParserEntryPoint::Path => grammar::entry_points::path,
@@ -92,7 +118,7 @@ pub fn parse(tokens: &Tokens, tree_sink: &mut dyn TreeSink, entry_point: ParserE
         ParserEntryPoint::Attr => grammar::entry_points::attr,
     };
 
-    let mut p = parser::Parser::new(tokens);
+    let mut p = parser::Parser::new(token_source);
     entry_point(&mut p);
     let events = p.finish();
     event::process(tree_sink, events);
@@ -115,9 +141,9 @@ impl Reparser {
     ///
     /// Tokens must start with `{`, end with `}` and form a valid brace
     /// sequence.
-    pub fn parse(self, tokens: &Tokens, tree_sink: &mut dyn TreeSink) {
+    pub fn parse(self, token_source: &mut dyn TokenSource, tree_sink: &mut dyn TreeSink) {
         let Reparser(r) = self;
-        let mut p = parser::Parser::new(tokens);
+        let mut p = parser::Parser::new(token_source);
         r(&mut p);
         let events = p.finish();
         event::process(tree_sink, events);
diff --git a/crates/parser/src/parser.rs b/crates/parser/src/parser.rs
index 4c891108a6..44c5f8e12f 100644
--- a/crates/parser/src/parser.rs
+++ b/crates/parser/src/parser.rs
@@ -7,10 +7,9 @@ use limit::Limit;
 
 use crate::{
     event::Event,
-    tokens::Tokens,
     ParseError,
     SyntaxKind::{self, EOF, ERROR, TOMBSTONE},
-    TokenSet, T,
+    TokenSet, TokenSource, T,
 };
 
 /// `Parser` struct provides the low-level API for
@@ -23,8 +22,7 @@ use crate::{
 /// "start expression, consume number literal,
 /// finish expression". See `Event` docs for more.
 pub(crate) struct Parser<'t> {
-    tokens: &'t Tokens,
-    pos: usize,
+    token_source: &'t mut dyn TokenSource,
     events: Vec<Event>,
     steps: Cell<u32>,
 }
@@ -32,8 +30,8 @@ pub(crate) struct Parser<'t> {
 static PARSER_STEP_LIMIT: Limit = Limit::new(15_000_000);
 
 impl<'t> Parser<'t> {
-    pub(super) fn new(tokens: &'t Tokens) -> Parser<'t> {
-        Parser { tokens, pos: 0, events: Vec::new(), steps: Cell::new(0) }
+    pub(super) fn new(token_source: &'t mut dyn TokenSource) -> Parser<'t> {
+        Parser { token_source, events: Vec::new(), steps: Cell::new(0) }
     }
 
     pub(crate) fn finish(self) -> Vec<Event> {
@@ -56,7 +54,7 @@ impl<'t> Parser<'t> {
         assert!(PARSER_STEP_LIMIT.check(steps as usize).is_ok(), "the parser seems stuck");
         self.steps.set(steps + 1);
 
-        self.tokens.kind(self.pos + n)
+        self.token_source.lookahead_nth(n).kind
     }
 
     /// Checks if the current token is `kind`.
@@ -92,7 +90,7 @@ impl<'t> Parser<'t> {
             T![<<=] => self.at_composite3(n, T![<], T![<], T![=]),
             T![>>=] => self.at_composite3(n, T![>], T![>], T![=]),
 
-            _ => self.tokens.kind(self.pos + n) == kind,
+            _ => self.token_source.lookahead_nth(n).kind == kind,
         }
     }
 
@@ -131,17 +129,25 @@ impl<'t> Parser<'t> {
     }
 
     fn at_composite2(&self, n: usize, k1: SyntaxKind, k2: SyntaxKind) -> bool {
-        self.tokens.kind(self.pos + n) == k1
-            && self.tokens.kind(self.pos + n + 1) == k2
-            && self.tokens.is_joint(self.pos + n)
+        let t1 = self.token_source.lookahead_nth(n);
+        if t1.kind != k1 || !t1.is_jointed_to_next {
+            return false;
+        }
+        let t2 = self.token_source.lookahead_nth(n + 1);
+        t2.kind == k2
     }
 
     fn at_composite3(&self, n: usize, k1: SyntaxKind, k2: SyntaxKind, k3: SyntaxKind) -> bool {
-        self.tokens.kind(self.pos + n) == k1
-            && self.tokens.kind(self.pos + n + 1) == k2
-            && self.tokens.kind(self.pos + n + 2) == k3
-            && self.tokens.is_joint(self.pos + n)
-            && self.tokens.is_joint(self.pos + n + 1)
+        let t1 = self.token_source.lookahead_nth(n);
+        if t1.kind != k1 || !t1.is_jointed_to_next {
+            return false;
+        }
+        let t2 = self.token_source.lookahead_nth(n + 1);
+        if t2.kind != k2 || !t2.is_jointed_to_next {
+            return false;
+        }
+        let t3 = self.token_source.lookahead_nth(n + 2);
+        t3.kind == k3
     }
 
     /// Checks if the current token is in `kinds`.
@@ -150,8 +156,8 @@ impl<'t> Parser<'t> {
     }
 
     /// Checks if the current token is contextual keyword with text `t`.
-    pub(crate) fn at_contextual_kw(&self, kw: SyntaxKind) -> bool {
-        self.tokens.contextual_kind(self.pos) == kw
+    pub(crate) fn at_contextual_kw(&self, kw: &str) -> bool {
+        self.token_source.is_keyword(kw)
     }
 
     /// Starts a new node in the syntax tree. All nodes and tokens
@@ -237,7 +243,10 @@ impl<'t> Parser<'t> {
     }
 
     fn do_bump(&mut self, kind: SyntaxKind, n_raw_tokens: u8) {
-        self.pos += n_raw_tokens as usize;
+        for _ in 0..n_raw_tokens {
+            self.token_source.bump();
+        }
+
         self.push_event(Event::Token { kind, n_raw_tokens });
     }
 
diff --git a/crates/parser/src/syntax_kind/generated.rs b/crates/parser/src/syntax_kind/generated.rs
index 601a5792af..99e7651906 100644
--- a/crates/parser/src/syntax_kind/generated.rs
+++ b/crates/parser/src/syntax_kind/generated.rs
@@ -334,18 +334,6 @@ impl SyntaxKind {
         };
         Some(kw)
     }
-    pub fn from_contextual_keyword(ident: &str) -> Option<SyntaxKind> {
-        let kw = match ident {
-            "auto" => AUTO_KW,
-            "default" => DEFAULT_KW,
-            "existential" => EXISTENTIAL_KW,
-            "union" => UNION_KW,
-            "raw" => RAW_KW,
-            "macro_rules" => MACRO_RULES_KW,
-            _ => return None,
-        };
-        Some(kw)
-    }
     pub fn from_char(c: char) -> Option<SyntaxKind> {
         let tok = match c {
             ';' => SEMICOLON,
diff --git a/crates/parser/src/tokens.rs b/crates/parser/src/tokens.rs
deleted file mode 100644
index 1c0672492d..0000000000
--- a/crates/parser/src/tokens.rs
+++ /dev/null
@@ -1,87 +0,0 @@
-//! Input for the parser -- a sequence of tokens.
-//!
-//! As of now, parser doesn't have access to the *text* of the tokens, and makes
-//! decisions based solely on their classification.
-
-use crate::SyntaxKind;
-
-#[allow(non_camel_case_types)]
-type bits = u64;
-
-/// Main input to the parser.
-///
-/// A sequence of tokens represented internally as a struct of arrays.
-#[derive(Default)]
-pub struct Tokens {
-    kind: Vec<SyntaxKind>,
-    joint: Vec<bits>,
-    contextual_kind: Vec<SyntaxKind>,
-}
-
-/// `pub` impl used by callers to create `Tokens`.
-impl Tokens {
-    #[inline]
-    pub fn push(&mut self, kind: SyntaxKind) {
-        self.push_impl(kind, SyntaxKind::EOF)
-    }
-    #[inline]
-    pub fn push_ident(&mut self, contextual_kind: SyntaxKind) {
-        self.push_impl(SyntaxKind::IDENT, contextual_kind)
-    }
-    /// Sets jointness for the last token we've pushed.
-    ///
-    /// This is a separate API rather than an argument to the `push` to make it
-    /// convenient both for textual and mbe tokens. With text, you know whether
-    /// the *previous* token was joint, with mbe, you know whether the *current*
-    /// one is joint. This API allows for styles of usage:
-    ///
-    /// ```
-    /// // In text:
-    /// tokens.was_joint(prev_joint);
-    /// tokens.push(curr);
-    ///
-    /// // In MBE:
-    /// token.push(curr);
-    /// tokens.push(curr_joint)
-    /// ```
-    #[inline]
-    pub fn was_joint(&mut self) {
-        let n = self.len() - 1;
-        let (idx, b_idx) = self.bit_index(n);
-        self.joint[idx] |= 1 << b_idx;
-    }
-    #[inline]
-    fn push_impl(&mut self, kind: SyntaxKind, contextual_kind: SyntaxKind) {
-        let idx = self.len();
-        if idx % (bits::BITS as usize) == 0 {
-            self.joint.push(0);
-        }
-        self.kind.push(kind);
-        self.contextual_kind.push(contextual_kind);
-    }
-}
-
-/// pub(crate) impl used by the parser to consume `Tokens`.
-impl Tokens {
-    pub(crate) fn kind(&self, idx: usize) -> SyntaxKind {
-        self.kind.get(idx).copied().unwrap_or(SyntaxKind::EOF)
-    }
-    pub(crate) fn contextual_kind(&self, idx: usize) -> SyntaxKind {
-        self.contextual_kind.get(idx).copied().unwrap_or(SyntaxKind::EOF)
-    }
-    pub(crate) fn is_joint(&self, n: usize) -> bool {
-        let (idx, b_idx) = self.bit_index(n);
-        self.joint[idx] & 1 << b_idx != 0
-    }
-}
-
-impl Tokens {
-    fn bit_index(&self, n: usize) -> (usize, usize) {
-        let idx = n / (bits::BITS as usize);
-        let b_idx = n % (bits::BITS as usize);
-        (idx, b_idx)
-    }
-    fn len(&self) -> usize {
-        self.kind.len()
-    }
-}
diff --git a/crates/proc_macro_api/src/version.rs b/crates/proc_macro_api/src/version.rs
index f29970d477..4c065c93c4 100644
--- a/crates/proc_macro_api/src/version.rs
+++ b/crates/proc_macro_api/src/version.rs
@@ -99,7 +99,7 @@ fn read_section<'a>(dylib_binary: &'a [u8], section_name: &str) -> io::Result<&'
 /// * [length byte] next 1 byte tells us how many bytes we should read next
 ///   for the version string's utf8 bytes
 /// * [version string bytes encoded in utf8] <- GET THIS BOI
-/// * [some more bytes that we don't really care but about still there] :-)
+/// * [some more bytes that we don really care but still there] :-)
 /// Check this issue for more about the bytes layout:
 /// <https://github.com/rust-analyzer/rust-analyzer/issues/6174>
 fn read_version(dylib_path: &AbsPath) -> io::Result<String> {
@@ -108,25 +108,15 @@ fn read_version(dylib_path: &AbsPath) -> io::Result<String> {
 
     let dot_rustc = read_section(&dylib_mmaped, ".rustc")?;
 
-    // check if magic is valid
-    if &dot_rustc[0..4] != b"rust" {
+    let header = &dot_rustc[..8];
+    const EXPECTED_HEADER: [u8; 8] = [b'r', b'u', b's', b't', 0, 0, 0, 5];
+    // check if header is valid
+    if header != EXPECTED_HEADER {
         return Err(io::Error::new(
             io::ErrorKind::InvalidData,
-            format!("unknown metadata magic, expected `rust`, found `{:?}`", &dot_rustc[0..4]),
+            format!("only metadata version 5 is supported, section header was: {:?}", header),
         ));
     }
-    let version = u32::from_be_bytes([dot_rustc[4], dot_rustc[5], dot_rustc[6], dot_rustc[7]]);
-    // Last supported version is:
-    // https://github.com/rust-lang/rust/commit/0696e79f2740ad89309269b460579e548a5cd632
-    match version {
-        5 | 6 => {}
-        _ => {
-            return Err(io::Error::new(
-                io::ErrorKind::InvalidData,
-                format!("unsupported metadata version {}", version),
-            ));
-        }
-    }
 
     let snappy_portion = &dot_rustc[8..];
 
@@ -140,7 +130,7 @@ fn read_version(dylib_path: &AbsPath) -> io::Result<String> {
     // to know the length
     let mut bytes_before_version = [0u8; 13];
     snappy_decoder.read_exact(&mut bytes_before_version)?;
-    let length = bytes_before_version[12];
+    let length = bytes_before_version[12]; // what? can't use -1 indexing?
 
     let mut version_string_utf8 = vec![0u8; length as usize];
     snappy_decoder.read_exact(&mut version_string_utf8)?;
diff --git a/crates/project_model/src/workspace.rs b/crates/project_model/src/workspace.rs
index 60f99f368b..0335f8b174 100644
--- a/crates/project_model/src/workspace.rs
+++ b/crates/project_model/src/workspace.rs
@@ -595,9 +595,6 @@ fn cargo_to_crate_graph(
 
         // Set deps to the core, std and to the lib target of the current package
         for (from, kind) in pkg_crates.get(&pkg).into_iter().flatten() {
-            // Add sysroot deps first so that a lib target named `core` etc. can overwrite them.
-            public_deps.add(*from, &mut crate_graph);
-
             if let Some((to, name)) = lib_tgt.clone() {
                 if to != *from && *kind != TargetKind::BuildScript {
                     // (build script can not depend on its library target)
@@ -609,6 +606,7 @@ fn cargo_to_crate_graph(
                     add_dep(&mut crate_graph, *from, name, to);
                 }
             }
+            public_deps.add(*from, &mut crate_graph);
         }
     }
 
diff --git a/crates/stdx/src/lib.rs b/crates/stdx/src/lib.rs
index e33b45cc8e..ee23f57934 100644
--- a/crates/stdx/src/lib.rs
+++ b/crates/stdx/src/lib.rs
@@ -193,11 +193,6 @@ where
     }
 }
 
-/// Returns all final segments of the argument, longest first.
-pub fn slice_tails<T>(this: &[T]) -> impl Iterator<Item = &[T]> {
-    (0..this.len()).map(|i| &this[i..])
-}
-
 #[cfg(test)]
 mod tests {
     use super::*;
diff --git a/crates/syntax/Cargo.toml b/crates/syntax/Cargo.toml
index 526024a836..8705fb2115 100644
--- a/crates/syntax/Cargo.toml
+++ b/crates/syntax/Cargo.toml
@@ -13,7 +13,7 @@ doctest = false
 [dependencies]
 cov-mark = "2.0.0-pre.1"
 itertools = "0.10.0"
-rowan = "0.15.0"
+rowan = "0.14.0"
 rustc_lexer = { version = "725.0.0", package = "rustc-ap-rustc_lexer" }
 rustc-hash = "1.1.0"
 once_cell = "1.3.1"
diff --git a/crates/syntax/src/ast/token_ext.rs b/crates/syntax/src/ast/token_ext.rs
index 1fb7f158f2..c32ab686cc 100644
--- a/crates/syntax/src/ast/token_ext.rs
+++ b/crates/syntax/src/ast/token_ext.rs
@@ -623,41 +623,38 @@ impl ast::IntNumber {
         }
     }
 
-    pub fn value(&self) -> Option<u128> {
-        let token = self.syntax();
-
-        let mut text = token.text();
-        if let Some(suffix) = self.suffix() {
-            text = &text[..text.len() - suffix.len()];
-        }
-
+    pub fn split_into_parts(&self) -> (&str, &str, &str) {
         let radix = self.radix();
-        text = &text[radix.prefix_len()..];
+        let (prefix, mut text) = self.text().split_at(radix.prefix_len());
+
+        let is_suffix_start: fn(&(usize, char)) -> bool = match radix {
+            Radix::Hexadecimal => |(_, c)| matches!(c, 'g'..='z' | 'G'..='Z'),
+            _ => |(_, c)| c.is_ascii_alphabetic(),
+        };
 
-        let buf;
-        if text.contains('_') {
-            buf = text.replace('_', "");
-            text = buf.as_str();
+        let mut suffix = "";
+        if let Some((suffix_start, _)) = text.char_indices().find(is_suffix_start) {
+            let (text2, suffix2) = text.split_at(suffix_start);
+            text = text2;
+            suffix = suffix2;
         };
 
-        let value = u128::from_str_radix(text, radix as u32).ok()?;
+        (prefix, text, suffix)
+    }
+
+    pub fn value(&self) -> Option<u128> {
+        let (_, text, _) = self.split_into_parts();
+        let value = u128::from_str_radix(&text.replace("_", ""), self.radix() as u32).ok()?;
         Some(value)
     }
 
     pub fn suffix(&self) -> Option<&str> {
-        let text = self.text();
-        let radix = self.radix();
-        let mut indices = text.char_indices();
-        if radix != Radix::Decimal {
-            indices.next()?;
-            indices.next()?;
+        let (_, _, suffix) = self.split_into_parts();
+        if suffix.is_empty() {
+            None
+        } else {
+            Some(suffix)
         }
-        let is_suffix_start: fn(&(usize, char)) -> bool = match radix {
-            Radix::Hexadecimal => |(_, c)| matches!(c, 'g'..='z' | 'G'..='Z'),
-            _ => |(_, c)| c.is_ascii_alphabetic(),
-        };
-        let (suffix_start, _) = indices.find(is_suffix_start)?;
-        Some(&text[suffix_start..])
     }
 }
 
diff --git a/crates/syntax/src/parsing.rs b/crates/syntax/src/parsing.rs
index 865e146482..a45f262877 100644
--- a/crates/syntax/src/parsing.rs
+++ b/crates/syntax/src/parsing.rs
@@ -2,10 +2,12 @@
 //! incremental reparsing.
 
 pub(crate) mod lexer;
+mod text_token_source;
 mod text_tree_sink;
 mod reparsing;
 
 use parser::SyntaxKind;
+use text_token_source::TextTokenSource;
 use text_tree_sink::TextTreeSink;
 
 use crate::{syntax_node::GreenNode, AstNode, SyntaxError, SyntaxNode};
@@ -13,12 +15,12 @@ use crate::{syntax_node::GreenNode, AstNode, SyntaxError, SyntaxNode};
 pub(crate) use crate::parsing::{lexer::*, reparsing::incremental_reparse};
 
 pub(crate) fn parse_text(text: &str) -> (GreenNode, Vec<SyntaxError>) {
-    let (lexer_tokens, lexer_errors) = tokenize(text);
-    let parser_tokens = to_parser_tokens(text, &lexer_tokens);
+    let (tokens, lexer_errors) = tokenize(text);
 
-    let mut tree_sink = TextTreeSink::new(text, &lexer_tokens);
+    let mut token_source = TextTokenSource::new(text, &tokens);
+    let mut tree_sink = TextTreeSink::new(text, &tokens);
 
-    parser::parse_source_file(&parser_tokens, &mut tree_sink);
+    parser::parse_source_file(&mut token_source, &mut tree_sink);
 
     let (tree, mut parser_errors) = tree_sink.finish();
     parser_errors.extend(lexer_errors);
@@ -31,52 +33,26 @@ pub(crate) fn parse_text_as<T: AstNode>(
     text: &str,
     entry_point: parser::ParserEntryPoint,
 ) -> Result<T, ()> {
-    let (lexer_tokens, lexer_errors) = tokenize(text);
+    let (tokens, lexer_errors) = tokenize(text);
     if !lexer_errors.is_empty() {
         return Err(());
     }
 
-    let parser_tokens = to_parser_tokens(text, &lexer_tokens);
-
-    let mut tree_sink = TextTreeSink::new(text, &lexer_tokens);
+    let mut token_source = TextTokenSource::new(text, &tokens);
+    let mut tree_sink = TextTreeSink::new(text, &tokens);
 
     // TextTreeSink assumes that there's at least some root node to which it can attach errors and
     // tokens. We arbitrarily give it a SourceFile.
     use parser::TreeSink;
     tree_sink.start_node(SyntaxKind::SOURCE_FILE);
-    parser::parse(&parser_tokens, &mut tree_sink, entry_point);
+    parser::parse(&mut token_source, &mut tree_sink, entry_point);
     tree_sink.finish_node();
 
-    let (tree, parser_errors, eof) = tree_sink.finish_eof();
-    if !parser_errors.is_empty() || !eof {
+    let (tree, parser_errors) = tree_sink.finish();
+    use parser::TokenSource;
+    if !parser_errors.is_empty() || token_source.current().kind != SyntaxKind::EOF {
         return Err(());
     }
 
     SyntaxNode::new_root(tree).first_child().and_then(T::cast).ok_or(())
 }
-
-pub(crate) fn to_parser_tokens(text: &str, lexer_tokens: &[lexer::Token]) -> ::parser::Tokens {
-    let mut off = 0;
-    let mut res = parser::Tokens::default();
-    let mut was_joint = false;
-    for t in lexer_tokens {
-        if t.kind.is_trivia() {
-            was_joint = false;
-        } else {
-            if t.kind == SyntaxKind::IDENT {
-                let token_text = &text[off..][..usize::from(t.len)];
-                let contextual_kw =
-                    SyntaxKind::from_contextual_keyword(token_text).unwrap_or(SyntaxKind::IDENT);
-                res.push_ident(contextual_kw);
-            } else {
-                if was_joint {
-                    res.was_joint();
-                }
-                res.push(t.kind);
-            }
-            was_joint = true;
-        }
-        off += usize::from(t.len);
-    }
-    res
-}
diff --git a/crates/syntax/src/parsing/reparsing.rs b/crates/syntax/src/parsing/reparsing.rs
index 62f39a9347..186cc9e74c 100644
--- a/crates/syntax/src/parsing/reparsing.rs
+++ b/crates/syntax/src/parsing/reparsing.rs
@@ -12,8 +12,8 @@ use text_edit::Indel;
 use crate::{
     parsing::{
         lexer::{lex_single_syntax_kind, tokenize, Token},
+        text_token_source::TextTokenSource,
         text_tree_sink::TextTreeSink,
-        to_parser_tokens,
     },
     syntax_node::{GreenNode, GreenToken, NodeOrToken, SyntaxElement, SyntaxNode},
     SyntaxError,
@@ -91,14 +91,14 @@ fn reparse_block(
     let (node, reparser) = find_reparsable_node(root, edit.delete)?;
     let text = get_text_after_edit(node.clone().into(), edit);
 
-    let (lexer_tokens, new_lexer_errors) = tokenize(&text);
-    if !is_balanced(&lexer_tokens) {
+    let (tokens, new_lexer_errors) = tokenize(&text);
+    if !is_balanced(&tokens) {
         return None;
     }
-    let parser_tokens = to_parser_tokens(&text, &lexer_tokens);
 
-    let mut tree_sink = TextTreeSink::new(&text, &lexer_tokens);
-    reparser.parse(&parser_tokens, &mut tree_sink);
+    let mut token_source = TextTokenSource::new(&text, &tokens);
+    let mut tree_sink = TextTreeSink::new(&text, &tokens);
+    reparser.parse(&mut token_source, &mut tree_sink);
 
     let (green, mut new_parser_errors) = tree_sink.finish();
     new_parser_errors.extend(new_lexer_errors);
diff --git a/crates/syntax/src/parsing/text_token_source.rs b/crates/syntax/src/parsing/text_token_source.rs
new file mode 100644
index 0000000000..11dfc63a65
--- /dev/null
+++ b/crates/syntax/src/parsing/text_token_source.rs
@@ -0,0 +1,82 @@
+//! See `TextTokenSource` docs.
+
+use parser::TokenSource;
+
+use crate::{parsing::lexer::Token, SyntaxKind::EOF, TextRange, TextSize};
+
+/// Implementation of `parser::TokenSource` that takes tokens from source code text.
+pub(crate) struct TextTokenSource<'t> {
+    text: &'t str,
+    /// token and its start position (non-whitespace/comment tokens)
+    /// ```non-rust
+    ///  struct Foo;
+    ///  ^------^--^-
+    ///  |      |    \________
+    ///  |      \____         \
+    ///  |           \         |
+    ///  (struct, 0) (Foo, 7) (;, 10)
+    /// ```
+    /// `[(struct, 0), (Foo, 7), (;, 10)]`
+    token_offset_pairs: Vec<(Token, TextSize)>,
+
+    /// Current token and position
+    curr: (parser::Token, usize),
+}
+
+impl<'t> TokenSource for TextTokenSource<'t> {
+    fn current(&self) -> parser::Token {
+        self.curr.0
+    }
+
+    fn lookahead_nth(&self, n: usize) -> parser::Token {
+        mk_token(self.curr.1 + n, &self.token_offset_pairs)
+    }
+
+    fn bump(&mut self) {
+        if self.curr.0.kind == EOF {
+            return;
+        }
+
+        let pos = self.curr.1 + 1;
+        self.curr = (mk_token(pos, &self.token_offset_pairs), pos);
+    }
+
+    fn is_keyword(&self, kw: &str) -> bool {
+        self.token_offset_pairs
+            .get(self.curr.1)
+            .map_or(false, |(token, offset)| &self.text[TextRange::at(*offset, token.len)] == kw)
+    }
+}
+
+fn mk_token(pos: usize, token_offset_pairs: &[(Token, TextSize)]) -> parser::Token {
+    let (kind, is_jointed_to_next) = match token_offset_pairs.get(pos) {
+        Some((token, offset)) => (
+            token.kind,
+            token_offset_pairs
+                .get(pos + 1)
+                .map_or(false, |(_, next_offset)| offset + token.len == *next_offset),
+        ),
+        None => (EOF, false),
+    };
+    parser::Token { kind, is_jointed_to_next }
+}
+
+impl<'t> TextTokenSource<'t> {
+    /// Generate input from tokens(expect comment and whitespace).
+    pub(crate) fn new(text: &'t str, raw_tokens: &'t [Token]) -> TextTokenSource<'t> {
+        let token_offset_pairs: Vec<_> = raw_tokens
+            .iter()
+            .filter_map({
+                let mut len = 0.into();
+                move |token| {
+                    let pair = if token.kind.is_trivia() { None } else { Some((*token, len)) };
+                    len += token.len;
+                    pair
+                }
+            })
+            .collect();
+
+        let first = mk_token(0, &token_offset_pairs);
+        TextTokenSource { text, token_offset_pairs, curr: (first, 0) }
+    }
+}
diff --git a/crates/syntax/src/parsing/text_tree_sink.rs b/crates/syntax/src/parsing/text_tree_sink.rs
index c1792199fd..8c1de92048 100644
--- a/crates/syntax/src/parsing/text_tree_sink.rs
+++ b/crates/syntax/src/parsing/text_tree_sink.rs
@@ -104,7 +104,7 @@ impl<'a> TextTreeSink<'a> {
         }
     }
 
-    pub(super) fn finish_eof(mut self) -> (GreenNode, Vec<SyntaxError>, bool) {
+    pub(super) fn finish(mut self) -> (GreenNode, Vec<SyntaxError>) {
         match mem::replace(&mut self.state, State::Normal) {
             State::PendingFinish => {
                 self.eat_trivias();
@@ -113,15 +113,7 @@ impl<'a> TextTreeSink<'a> {
             State::PendingStart | State::Normal => unreachable!(),
         }
 
-        let (node, errors) = self.inner.finish_raw();
-        let is_eof = self.token_pos == self.tokens.len();
-
-        (node, errors, is_eof)
-    }
-
-    pub(super) fn finish(self) -> (GreenNode, Vec<SyntaxError>) {
-        let (node, errors, _eof) = self.finish_eof();
-        (node, errors)
+        self.inner.finish_raw()
     }
 
     fn eat_trivias(&mut self) {
diff --git a/editors/code/package-lock.json b/editors/code/package-lock.json
index 038a07c75a..686844132c 100644
--- a/editors/code/package-lock.json
+++ b/editors/code/package-lock.json
@@ -17,7 +17,7 @@
             },
             "devDependencies": {
                 "@types/node": "~14.17.5",
-                "@types/vscode": "~1.62.0",
+                "@types/vscode": "^1.61.0",
                 "@typescript-eslint/eslint-plugin": "^5.5.0",
                 "@typescript-eslint/parser": "^5.5.0",
                 "@vscode/test-electron": "^1.6.2",
@@ -29,7 +29,7 @@
                 "vsce": "^2.5.1"
             },
             "engines": {
-                "vscode": "^1.62.0"
+                "vscode": "^1.61.0"
             }
         },
         "node_modules/@eslint/eslintrc": {
@@ -149,13 +149,13 @@
             "dev": true
         },
         "node_modules/@typescript-eslint/eslint-plugin": {
-            "version": "5.6.0",
-            "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.6.0.tgz",
-            "integrity": "sha512-MIbeMy5qfLqtgs1hWd088k1hOuRsN9JrHUPwVVKCD99EOUqScd7SrwoZl4Gso05EAP9w1kvLWUVGJOVpRPkDPA==",
+            "version": "5.5.0",
+            "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.5.0.tgz",
+            "integrity": "sha512-4bV6fulqbuaO9UMXU0Ia0o6z6if+kmMRW8rMRyfqXj/eGrZZRGedS4n0adeGNnjr8LKAM495hrQ7Tea52UWmQA==",
             "dev": true,
             "dependencies": {
-                "@typescript-eslint/experimental-utils": "5.6.0",
-                "@typescript-eslint/scope-manager": "5.6.0",
+                "@typescript-eslint/experimental-utils": "5.5.0",
+                "@typescript-eslint/scope-manager": "5.5.0",
                 "debug": "^4.3.2",
                 "functional-red-black-tree": "^1.0.1",
                 "ignore": "^5.1.8",
@@ -181,15 +181,15 @@
             }
         },
         "node_modules/@typescript-eslint/experimental-utils": {
-            "version": "5.6.0",
-            "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.6.0.tgz",
-            "integrity": "sha512-VDoRf3Qj7+W3sS/ZBXZh3LBzp0snDLEgvp6qj0vOAIiAPM07bd5ojQ3CTzF/QFl5AKh7Bh1ycgj6lFBJHUt/DA==",
+            "version": "5.5.0",
+            "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.5.0.tgz",
+            "integrity": "sha512-kjWeeVU+4lQ1SLYErRKV5yDXbWDPkpbzTUUlfAUifPYvpX0qZlrcCZ96/6oWxt3QxtK5WVhXz+KsnwW9cIW+3A==",
             "dev": true,
             "dependencies": {
                 "@types/json-schema": "^7.0.9",
-                "@typescript-eslint/scope-manager": "5.6.0",
-                "@typescript-eslint/types": "5.6.0",
-                "@typescript-eslint/typescript-estree": "5.6.0",
+                "@typescript-eslint/scope-manager": "5.5.0",
+                "@typescript-eslint/types": "5.5.0",
+                "@typescript-eslint/typescript-estree": "5.5.0",
                 "eslint-scope": "^5.1.1",
                 "eslint-utils": "^3.0.0"
             },
@@ -205,14 +205,14 @@
             }
         },
         "node_modules/@typescript-eslint/parser": {
-            "version": "5.6.0",
-            "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.6.0.tgz",
-            "integrity": "sha512-YVK49NgdUPQ8SpCZaOpiq1kLkYRPMv9U5gcMrywzI8brtwZjr/tG3sZpuHyODt76W/A0SufNjYt9ZOgrC4tLIQ==",
+            "version": "5.5.0",
+            "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.5.0.tgz",
+            "integrity": "sha512-JsXBU+kgQOAgzUn2jPrLA+Rd0Y1dswOlX3hp8MuRO1hQDs6xgHtbCXEiAu7bz5hyVURxbXcA2draasMbNqrhmg==",
             "dev": true,
             "dependencies": {
-                "@typescript-eslint/scope-manager": "5.6.0",
-                "@typescript-eslint/types": "5.6.0",
-                "@typescript-eslint/typescript-estree": "5.6.0",
+                "@typescript-eslint/scope-manager": "5.5.0",
+                "@typescript-eslint/types": "5.5.0",
+                "@typescript-eslint/typescript-estree": "5.5.0",
                 "debug": "^4.3.2"
             },
             "engines": {
@@ -232,13 +232,13 @@
             }
         },
         "node_modules/@typescript-eslint/scope-manager": {
-            "version": "5.6.0",
-            "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.6.0.tgz",
-            "integrity": "sha512-1U1G77Hw2jsGWVsO2w6eVCbOg0HZ5WxL/cozVSTfqnL/eB9muhb8THsP0G3w+BB5xAHv9KptwdfYFAUfzcIh4A==",
+            "version": "5.5.0",
+            "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.5.0.tgz",
+            "integrity": "sha512-0/r656RmRLo7CbN4Mdd+xZyPJ/fPCKhYdU6mnZx+8msAD8nJSP8EyCFkzbd6vNVZzZvWlMYrSNekqGrCBqFQhg==",
             "dev": true,
             "dependencies": {
-                "@typescript-eslint/types": "5.6.0",
-                "@typescript-eslint/visitor-keys": "5.6.0"
+                "@typescript-eslint/types": "5.5.0",
+                "@typescript-eslint/visitor-keys": "5.5.0"
             },
             "engines": {
                 "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
@@ -249,9 +249,9 @@
             }
         },
         "node_modules/@typescript-eslint/types": {
-            "version": "5.6.0",
-            "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.6.0.tgz",
-            "integrity": "sha512-OIZffked7mXv4mXzWU5MgAEbCf9ecNJBKi+Si6/I9PpTaj+cf2x58h2oHW5/P/yTnPkKaayfjhLvx+crnl5ubA==",
+            "version": "5.5.0",
+            "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.5.0.tgz",
+            "integrity": "sha512-OaYTqkW3GnuHxqsxxJ6KypIKd5Uw7bFiQJZRyNi1jbMJnK3Hc/DR4KwB6KJj6PBRkJJoaNwzMNv9vtTk87JhOg==",
             "dev": true,
             "engines": {
                 "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
@@ -262,13 +262,13 @@
             }
         },
         "node_modules/@typescript-eslint/typescript-estree": {
-            "version": "5.6.0",
-            "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.6.0.tgz",
-            "integrity": "sha512-92vK5tQaE81rK7fOmuWMrSQtK1IMonESR+RJR2Tlc7w4o0MeEdjgidY/uO2Gobh7z4Q1hhS94Cr7r021fMVEeA==",
+            "version": "5.5.0",
+            "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.5.0.tgz",
+            "integrity": "sha512-pVn8btYUiYrjonhMAO0yG8lm7RApzy2L4RC7Td/mC/qFkyf6vRbGyZozoA94+w6D2Y2GRqpMoCWcwx/EUOzyoQ==",
             "dev": true,
             "dependencies": {
-                "@typescript-eslint/types": "5.6.0",
-                "@typescript-eslint/visitor-keys": "5.6.0",
+                "@typescript-eslint/types": "5.5.0",
+                "@typescript-eslint/visitor-keys": "5.5.0",
                 "debug": "^4.3.2",
                 "globby": "^11.0.4",
                 "is-glob": "^4.0.3",
@@ -289,12 +289,12 @@
             }
         },
         "node_modules/@typescript-eslint/visitor-keys": {
-            "version": "5.6.0",
-            "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.6.0.tgz",
-            "integrity": "sha512-1p7hDp5cpRFUyE3+lvA74egs+RWSgumrBpzBCDzfTFv0aQ7lIeay80yU0hIxgAhwQ6PcasW35kaOCyDOv6O/Ng==",
+            "version": "5.5.0",
+            "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.5.0.tgz",
+            "integrity": "sha512-4GzJ1kRtsWzHhdM40tv0ZKHNSbkDhF0Woi/TDwVJX6UICwJItvP7ZTXbjTkCdrors7ww0sYe0t+cIKDAJwZ7Kw==",
             "dev": true,
             "dependencies": {
-                "@typescript-eslint/types": "5.6.0",
+                "@typescript-eslint/types": "5.5.0",
                 "eslint-visitor-keys": "^3.0.0"
             },
             "engines": {
@@ -999,9 +999,9 @@
             }
         },
         "node_modules/d3-format": {
-            "version": "3.1.0",
-            "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz",
-            "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==",
+            "version": "3.0.1",
+            "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.0.1.tgz",
+            "integrity": "sha512-hdL7+HBIohpgfolhBxr1KX47VMD6+vVD/oEFrxk5yhmzV2prk99EkFKYpXuhVkFpTgHdJ6/4bYcjdLPPXV4tIA==",
             "engines": {
                 "node": ">=12"
             }
@@ -1110,9 +1110,9 @@
             }
         },
         "node_modules/d3-hierarchy": {
-            "version": "3.1.0",
-            "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.0.tgz",
-            "integrity": "sha512-RZWnhsvcrf81F+khYxu2IOfoSwfCGla9je0JQaRIEuPOgJd+3cKdvAYyMXvTt0MDbluiHThYBQzIb0xxXfnqmA==",
+            "version": "3.0.1",
+            "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.0.1.tgz",
+            "integrity": "sha512-RlLTaofEoOrMK1JoXYIGhKTkJFI/6rFrYPgxy6QlZo2BcVc4HGTqEU0rPpzuMq5T/5XcMtAzv1XiLA3zRTfygw==",
             "engines": {
                 "node": ">=12"
             }
@@ -1215,9 +1215,9 @@
             }
         },
         "node_modules/d3-time-format": {
-            "version": "4.1.0",
-            "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz",
-            "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==",
+            "version": "4.0.0",
+            "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.0.0.tgz",
+            "integrity": "sha512-nzaCwlj+ZVBIlFuVOT1RmU+6xb/7D5IcnhHzHQcBgS/aTa5K9fWZNN5LCXA27LgF5WxoSNJqKBbLcGMtM6Ca6A==",
             "dependencies": {
                 "d3-time": "1 - 3"
             },
@@ -1778,9 +1778,9 @@
             }
         },
         "node_modules/eslint": {
-            "version": "8.4.1",
-            "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.4.1.tgz",
-            "integrity": "sha512-TxU/p7LB1KxQ6+7aztTnO7K0i+h0tDi81YRY9VzB6Id71kNz+fFYnf5HD5UOQmxkzcoa0TlVZf9dpMtUv0GpWg==",
+            "version": "8.4.0",
+            "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.4.0.tgz",
+            "integrity": "sha512-kv0XQcAQJL/VD9THQKhTQZVqkJKA+tIj/v2ZKNaIHRAADcJWFb+B/BAewUYuF6UVg1s2xC5qXVoDk0G8sKGeTA==",
             "dev": true,
             "dependencies": {
                 "@eslint/eslintrc": "^1.0.5",
@@ -2874,9 +2874,9 @@
             }
         },
         "node_modules/object-inspect": {
-            "version": "1.11.1",
-            "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.11.1.tgz",
-            "integrity": "sha512-If7BjFlpkzzBeV1cqgT3OSWT3azyoxDGajR+iGnFBfVV2EWyDyWaZZW2ERDjUaY2QM8i5jI3Sj7mhsM4DDAqWA==",
+            "version": "1.11.0",
+            "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.11.0.tgz",
+            "integrity": "sha512-jp7ikS6Sd3GxQfZJPyH3cjcbJF6GZPClgdV+EFygjFLQ5FmW/dRUnTd9PQ9k0JhoNDabWFbpF1yCdSWCC6gexg==",
             "dev": true,
             "funding": {
                 "url": "https://github.com/sponsors/ljharb"
@@ -3075,9 +3075,9 @@
             }
         },
         "node_modules/qs": {
-            "version": "6.10.2",
-            "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.2.tgz",
-            "integrity": "sha512-mSIdjzqznWgfd4pMii7sHtaYF8rx8861hBO80SraY5GT0XQibWZWJSid0avzHGkDIZLImux2S5mXO0Hfct2QCw==",
+            "version": "6.10.1",
+            "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.1.tgz",
+            "integrity": "sha512-M528Hph6wsSVOBiYUnGf+K/7w0hNshs/duGsNXPUCLH5XAqjEtiPGwNONLV0tBH8NoGb0mvD5JubnUTrujKDTg==",
             "dev": true,
             "dependencies": {
                 "side-channel": "^1.0.4"
@@ -4059,13 +4059,13 @@
             "dev": true
         },
         "@typescript-eslint/eslint-plugin": {
-            "version": "5.6.0",
-            "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.6.0.tgz",
-            "integrity": "sha512-MIbeMy5qfLqtgs1hWd088k1hOuRsN9JrHUPwVVKCD99EOUqScd7SrwoZl4Gso05EAP9w1kvLWUVGJOVpRPkDPA==",
+            "version": "5.5.0",
+            "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.5.0.tgz",
+            "integrity": "sha512-4bV6fulqbuaO9UMXU0Ia0o6z6if+kmMRW8rMRyfqXj/eGrZZRGedS4n0adeGNnjr8LKAM495hrQ7Tea52UWmQA==",
             "dev": true,
             "requires": {
-                "@typescript-eslint/experimental-utils": "5.6.0",
-                "@typescript-eslint/scope-manager": "5.6.0",
+                "@typescript-eslint/experimental-utils": "5.5.0",
+                "@typescript-eslint/scope-manager": "5.5.0",
                 "debug": "^4.3.2",
                 "functional-red-black-tree": "^1.0.1",
                 "ignore": "^5.1.8",
@@ -4075,55 +4075,55 @@
             }
         },
         "@typescript-eslint/experimental-utils": {
-            "version": "5.6.0",
-            "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.6.0.tgz",
-            "integrity": "sha512-VDoRf3Qj7+W3sS/ZBXZh3LBzp0snDLEgvp6qj0vOAIiAPM07bd5ojQ3CTzF/QFl5AKh7Bh1ycgj6lFBJHUt/DA==",
+            "version": "5.5.0",
+            "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.5.0.tgz",
+            "integrity": "sha512-kjWeeVU+4lQ1SLYErRKV5yDXbWDPkpbzTUUlfAUifPYvpX0qZlrcCZ96/6oWxt3QxtK5WVhXz+KsnwW9cIW+3A==",
             "dev": true,
             "requires": {
                 "@types/json-schema": "^7.0.9",
-                "@typescript-eslint/scope-manager": "5.6.0",
-                "@typescript-eslint/types": "5.6.0",
-                "@typescript-eslint/typescript-estree": "5.6.0",
+                "@typescript-eslint/scope-manager": "5.5.0",
+                "@typescript-eslint/types": "5.5.0",
+                "@typescript-eslint/typescript-estree": "5.5.0",
                 "eslint-scope": "^5.1.1",
                 "eslint-utils": "^3.0.0"
             }
         },
         "@typescript-eslint/parser": {
-            "version": "5.6.0",
-            "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.6.0.tgz",
-            "integrity": "sha512-YVK49NgdUPQ8SpCZaOpiq1kLkYRPMv9U5gcMrywzI8brtwZjr/tG3sZpuHyODt76W/A0SufNjYt9ZOgrC4tLIQ==",
+            "version": "5.5.0",
+            "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.5.0.tgz",
+            "integrity": "sha512-JsXBU+kgQOAgzUn2jPrLA+Rd0Y1dswOlX3hp8MuRO1hQDs6xgHtbCXEiAu7bz5hyVURxbXcA2draasMbNqrhmg==",
             "dev": true,
             "requires": {
-                "@typescript-eslint/scope-manager": "5.6.0",
-                "@typescript-eslint/types": "5.6.0",
-                "@typescript-eslint/typescript-estree": "5.6.0",
+                "@typescript-eslint/scope-manager": "5.5.0",
+                "@typescript-eslint/types": "5.5.0",
+                "@typescript-eslint/typescript-estree": "5.5.0",
                 "debug": "^4.3.2"
             }
         },
         "@typescript-eslint/scope-manager": {
-            "version": "5.6.0",
-            "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.6.0.tgz",
-            "integrity": "sha512-1U1G77Hw2jsGWVsO2w6eVCbOg0HZ5WxL/cozVSTfqnL/eB9muhb8THsP0G3w+BB5xAHv9KptwdfYFAUfzcIh4A==",
+            "version": "5.5.0",
+            "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.5.0.tgz",
+            "integrity": "sha512-0/r656RmRLo7CbN4Mdd+xZyPJ/fPCKhYdU6mnZx+8msAD8nJSP8EyCFkzbd6vNVZzZvWlMYrSNekqGrCBqFQhg==",
             "dev": true,
             "requires": {
-                "@typescript-eslint/types": "5.6.0",
-                "@typescript-eslint/visitor-keys": "5.6.0"
+                "@typescript-eslint/types": "5.5.0",
+                "@typescript-eslint/visitor-keys": "5.5.0"
             }
         },
         "@typescript-eslint/types": {
-            "version": "5.6.0",
-            "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.6.0.tgz",
-            "integrity": "sha512-OIZffked7mXv4mXzWU5MgAEbCf9ecNJBKi+Si6/I9PpTaj+cf2x58h2oHW5/P/yTnPkKaayfjhLvx+crnl5ubA==",
+            "version": "5.5.0",
+            "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.5.0.tgz",
+            "integrity": "sha512-OaYTqkW3GnuHxqsxxJ6KypIKd5Uw7bFiQJZRyNi1jbMJnK3Hc/DR4KwB6KJj6PBRkJJoaNwzMNv9vtTk87JhOg==",
             "dev": true
         },
         "@typescript-eslint/typescript-estree": {
-            "version": "5.6.0",
-            "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.6.0.tgz",
-            "integrity": "sha512-92vK5tQaE81rK7fOmuWMrSQtK1IMonESR+RJR2Tlc7w4o0MeEdjgidY/uO2Gobh7z4Q1hhS94Cr7r021fMVEeA==",
+            "version": "5.5.0",
+            "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.5.0.tgz",
+            "integrity": "sha512-pVn8btYUiYrjonhMAO0yG8lm7RApzy2L4RC7Td/mC/qFkyf6vRbGyZozoA94+w6D2Y2GRqpMoCWcwx/EUOzyoQ==",
             "dev": true,
             "requires": {
-                "@typescript-eslint/types": "5.6.0",
-                "@typescript-eslint/visitor-keys": "5.6.0",
+                "@typescript-eslint/types": "5.5.0",
+                "@typescript-eslint/visitor-keys": "5.5.0",
                 "debug": "^4.3.2",
                 "globby": "^11.0.4",
                 "is-glob": "^4.0.3",
@@ -4132,12 +4132,12 @@
             }
         },
         "@typescript-eslint/visitor-keys": {
-            "version": "5.6.0",
-            "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.6.0.tgz",
-            "integrity": "sha512-1p7hDp5cpRFUyE3+lvA74egs+RWSgumrBpzBCDzfTFv0aQ7lIeay80yU0hIxgAhwQ6PcasW35kaOCyDOv6O/Ng==",
+            "version": "5.5.0",
+            "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.5.0.tgz",
+            "integrity": "sha512-4GzJ1kRtsWzHhdM40tv0ZKHNSbkDhF0Woi/TDwVJX6UICwJItvP7ZTXbjTkCdrors7ww0sYe0t+cIKDAJwZ7Kw==",
             "dev": true,
             "requires": {
-                "@typescript-eslint/types": "5.6.0",
+                "@typescript-eslint/types": "5.5.0",
                 "eslint-visitor-keys": "^3.0.0"
             }
         },
@@ -4662,9 +4662,9 @@
             }
         },
         "d3-format": {
-            "version": "3.1.0",
-            "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz",
-            "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA=="
+            "version": "3.0.1",
+            "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.0.1.tgz",
+            "integrity": "sha512-hdL7+HBIohpgfolhBxr1KX47VMD6+vVD/oEFrxk5yhmzV2prk99EkFKYpXuhVkFpTgHdJ6/4bYcjdLPPXV4tIA=="
         },
         "d3-geo": {
             "version": "3.0.1",
@@ -4763,9 +4763,9 @@
             }
         },
         "d3-hierarchy": {
-            "version": "3.1.0",
-            "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.0.tgz",
-            "integrity": "sha512-RZWnhsvcrf81F+khYxu2IOfoSwfCGla9je0JQaRIEuPOgJd+3cKdvAYyMXvTt0MDbluiHThYBQzIb0xxXfnqmA=="
+            "version": "3.0.1",
+            "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.0.1.tgz",
+            "integrity": "sha512-RlLTaofEoOrMK1JoXYIGhKTkJFI/6rFrYPgxy6QlZo2BcVc4HGTqEU0rPpzuMq5T/5XcMtAzv1XiLA3zRTfygw=="
         },
         "d3-interpolate": {
             "version": "3.0.1",
@@ -4838,9 +4838,9 @@
             }
         },
         "d3-time-format": {
-            "version": "4.1.0",
-            "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz",
-            "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==",
+            "version": "4.0.0",
+            "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.0.0.tgz",
+            "integrity": "sha512-nzaCwlj+ZVBIlFuVOT1RmU+6xb/7D5IcnhHzHQcBgS/aTa5K9fWZNN5LCXA27LgF5WxoSNJqKBbLcGMtM6Ca6A==",
             "requires": {
                 "d3-time": "1 - 3"
             }
@@ -5209,9 +5209,9 @@
             "dev": true
         },
         "eslint": {
-            "version": "8.4.1",
-            "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.4.1.tgz",
-            "integrity": "sha512-TxU/p7LB1KxQ6+7aztTnO7K0i+h0tDi81YRY9VzB6Id71kNz+fFYnf5HD5UOQmxkzcoa0TlVZf9dpMtUv0GpWg==",
+            "version": "8.4.0",
+            "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.4.0.tgz",
+            "integrity": "sha512-kv0XQcAQJL/VD9THQKhTQZVqkJKA+tIj/v2ZKNaIHRAADcJWFb+B/BAewUYuF6UVg1s2xC5qXVoDk0G8sKGeTA==",
             "dev": true,
             "requires": {
                 "@eslint/eslintrc": "^1.0.5",
@@ -6065,9 +6065,9 @@
             "dev": true
         },
         "object-inspect": {
-            "version": "1.11.1",
-            "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.11.1.tgz",
-            "integrity": "sha512-If7BjFlpkzzBeV1cqgT3OSWT3azyoxDGajR+iGnFBfVV2EWyDyWaZZW2ERDjUaY2QM8i5jI3Sj7mhsM4DDAqWA==",
+            "version": "1.11.0",
+            "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.11.0.tgz",
+            "integrity": "sha512-jp7ikS6Sd3GxQfZJPyH3cjcbJF6GZPClgdV+EFygjFLQ5FmW/dRUnTd9PQ9k0JhoNDabWFbpF1yCdSWCC6gexg==",
             "dev": true
         },
         "once": {
@@ -6226,9 +6226,9 @@
             "dev": true
         },
         "qs": {
-            "version": "6.10.2",
-            "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.2.tgz",
-            "integrity": "sha512-mSIdjzqznWgfd4pMii7sHtaYF8rx8861hBO80SraY5GT0XQibWZWJSid0avzHGkDIZLImux2S5mXO0Hfct2QCw==",
+            "version": "6.10.1",
+            "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.1.tgz",
+            "integrity": "sha512-M528Hph6wsSVOBiYUnGf+K/7w0hNshs/duGsNXPUCLH5XAqjEtiPGwNONLV0tBH8NoGb0mvD5JubnUTrujKDTg==",
             "dev": true,
             "requires": {
                 "side-channel": "^1.0.4"
diff --git a/editors/code/package.json b/editors/code/package.json
index 2460308820..4b6e5ef333 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -21,7 +21,7 @@
         "Programming Languages"
     ],
     "engines": {
-        "vscode": "^1.62.0"
+        "vscode": "^1.61.0"
     },
     "enableProposedApi": true,
     "scripts": {
@@ -44,7 +44,7 @@
     },
     "devDependencies": {
         "@types/node": "~14.17.5",
-        "@types/vscode": "~1.62.0",
+        "@types/vscode": "^1.61.0",
         "@typescript-eslint/eslint-plugin": "^5.5.0",
         "@typescript-eslint/parser": "^5.5.0",
         "@vscode/test-electron": "^1.6.2",


Test patch:
diff --git a/crates/hir_def/src/macro_expansion_tests/builtin_fn_macro.rs b/crates/hir_def/src/macro_expansion_tests/builtin_fn_macro.rs
index 6e91301ecd..06039e95c5 100644
--- a/crates/hir_def/src/macro_expansion_tests/builtin_fn_macro.rs
+++ b/crates/hir_def/src/macro_expansion_tests/builtin_fn_macro.rs
@@ -313,40 +313,6 @@ fn main() { "foor0bar\nfalse"; }
     );
 }
 
-#[test]
-fn test_concat_with_captured_expr() {
-    check(
-        r##"
-#[rustc_builtin_macro]
-macro_rules! concat {}
-
-macro_rules! surprise {
-    () => { "s" };
-}
-
-macro_rules! stuff {
-    ($string:expr) => { concat!($string) };
-}
-
-fn main() { concat!(surprise!()); }
-"##,
-        expect![[r##"
-#[rustc_builtin_macro]
-macro_rules! concat {}
-
-macro_rules! surprise {
-    () => { "s" };
-}
-
-macro_rules! stuff {
-    ($string:expr) => { concat!($string) };
-}
-
-fn main() { "s"; }
-"##]],
-    );
-}
-
 #[test]
 fn test_concat_idents_expand() {
     check(
diff --git a/crates/hir_ty/src/tests/method_resolution.rs b/crates/hir_ty/src/tests/method_resolution.rs
index 731605ced1..9b1f682b61 100644
--- a/crates/hir_ty/src/tests/method_resolution.rs
+++ b/crates/hir_ty/src/tests/method_resolution.rs
@@ -1248,28 +1248,6 @@ fn f() {
     );
 }
 
-#[test]
-fn trait_impl_in_synstructure_const() {
-    check_types(
-        r#"
-struct S;
-
-trait Tr {
-    fn method(&self) -> u16;
-}
-
-const _DERIVE_Tr_: () = {
-    impl Tr for S {}
-};
-
-fn f() {
-    S.method();
-  //^^^^^^^^^^ u16
-}
-    "#,
-    );
-}
-
 #[test]
 fn inherent_impl_in_unnamed_const() {
     check_types(
diff --git a/crates/ide_assists/src/tests.rs b/crates/ide_assists/src/tests.rs
index 08947e4ecd..5a79979533 100644
--- a/crates/ide_assists/src/tests.rs
+++ b/crates/ide_assists/src/tests.rs
@@ -222,10 +222,10 @@ fn assist_order_field_struct() {
     let mut assists = assists.iter();
 
     assert_eq!(assists.next().expect("expected assist").label, "Change visibility to pub(crate)");
+    assert_eq!(assists.next().expect("expected assist").label, "Generate `Deref` impl using `bar`");
     assert_eq!(assists.next().expect("expected assist").label, "Generate a getter method");
     assert_eq!(assists.next().expect("expected assist").label, "Generate a mut getter method");
     assert_eq!(assists.next().expect("expected assist").label, "Generate a setter method");
-    assert_eq!(assists.next().expect("expected assist").label, "Generate `Deref` impl using `bar`");
     assert_eq!(assists.next().expect("expected assist").label, "Add `#[derive]`");
 }
 
diff --git a/crates/ide_assists/src/tests/generated.rs b/crates/ide_assists/src/tests/generated.rs
index c67e15b2ce..4e0d53ad80 100644
--- a/crates/ide_assists/src/tests/generated.rs
+++ b/crates/ide_assists/src/tests/generated.rs
@@ -839,36 +839,6 @@ struct Point {
     )
 }
 
-#[test]
-fn doctest_generate_documentation_template() {
-    check_doc_test(
-        "generate_documentation_template",
-        r#####"
-pub fn my_$0func(a: i32, b: i32) -> Result<(), std::io::Error> {
-    unimplemented!()
-}
-"#####,
-        r#####"
-/// .
-///
-/// # Examples
-///
-/// ```
-/// use test::my_func;
-///
-/// assert_eq!(my_func(a, b), );
-/// ```
-///
-/// # Errors
-///
-/// This function will return an error if .
-pub fn my_func(a: i32, b: i32) -> Result<(), std::io::Error> {
-    unimplemented!()
-}
-"#####,
-    )
-}
-
 #[test]
 fn doctest_generate_enum_as_method() {
     check_doc_test(
@@ -1607,6 +1577,19 @@ pub mod std { pub mod collections { pub struct HashMap { } } }
     )
 }
 
+#[test]
+fn doctest_reformat_number_literal() {
+    check_doc_test(
+        "reformat_number_literal",
+        r#####"
+const _: i32 = 1012345$0;
+"#####,
+        r#####"
+const _: i32 = 1_012_345;
+"#####,
+    )
+}
+
 #[test]
 fn doctest_remove_dbg() {
     check_doc_test(
diff --git a/crates/ide_completion/src/tests/attribute.rs b/crates/ide_completion/src/tests/attribute.rs
index c90d4966f3..c3dce61e7d 100644
--- a/crates/ide_completion/src/tests/attribute.rs
+++ b/crates/ide_completion/src/tests/attribute.rs
@@ -831,20 +831,6 @@ mod lint {
             r#"#[allow(rustdoc::bare_urls)] struct Test;"#,
         );
     }
-
-    #[test]
-    fn lint_unclosed() {
-        check_edit(
-            "deprecated",
-            r#"#[allow(dep$0 struct Test;"#,
-            r#"#[allow(deprecated struct Test;"#,
-        );
-        check_edit(
-            "bare_urls",
-            r#"#[allow(rustdoc::$0 struct Test;"#,
-            r#"#[allow(rustdoc::bare_urls struct Test;"#,
-        );
-    }
 }
 
 mod repr {
diff --git a/crates/ide_completion/src/tests/flyimport.rs b/crates/ide_completion/src/tests/flyimport.rs
index fbef6d9937..ff46dda5e2 100644
--- a/crates/ide_completion/src/tests/flyimport.rs
+++ b/crates/ide_completion/src/tests/flyimport.rs
@@ -96,29 +96,25 @@ fn main() {
 
 #[test]
 fn short_paths_are_ignored() {
-    cov_mark::check!(flyimport_exact_on_short_path);
+    cov_mark::check!(ignore_short_input_for_path);
 
     check(
         r#"
 //- /lib.rs crate:dep
-pub struct Bar;
-pub struct Rcar;
-pub struct Rc;
+pub struct FirstStruct;
 pub mod some_module {
-    pub struct Bar;
-    pub struct Rcar;
-    pub struct Rc;
+    pub struct SecondStruct;
+    pub struct ThirdStruct;
 }
 
 //- /main.rs crate:main deps:dep
+use dep::{FirstStruct, some_module::SecondStruct};
+
 fn main() {
-    rc$0
+    t$0
 }
 "#,
-        expect![[r#"
-            st Rc (use dep::Rc)
-            st Rc (use dep::some_module::Rc)
-        "#]],
+        expect![[r#""#]],
     );
 }
 
@@ -776,7 +772,7 @@ mod foo {
 }
 
 fn main() {
-    TES$0
+    TE$0
 }"#,
         expect![[r#"
         ct TEST_CONST (use foo::TEST_CONST)
@@ -793,7 +789,7 @@ mod foo {
 }
 
 fn main() {
-    tes$0
+    te$0
 }"#,
         expect![[r#"
         ct TEST_CONST (use foo::TEST_CONST)
@@ -850,7 +846,7 @@ struct Foo {
     some_field: i32,
 }
 fn main() {
-    let _ = Foo { some_field: som$0 };
+    let _ = Foo { some_field: so$0 };
 }
 "#,
         expect![[r#"
@@ -928,44 +924,6 @@ mod bar {
             "#,
         expect![[r#""#]],
     );
-    check(
-        r#"
-mod baz {
-    pub trait DefDatabase {
-        fn method1(&self);
-    }
-    pub trait HirDatabase: DefDatabase {
-        fn method2(&self);
-    }
-}
-
-mod bar {
-    fn test(db: &impl crate::baz::HirDatabase) {
-        db.metho$0
-    }
-}
-"#,
-        expect![[r#""#]],
-    );
-    check(
-        r#"
-mod baz {
-    pub trait DefDatabase {
-        fn method1(&self);
-    }
-    pub trait HirDatabase: DefDatabase {
-        fn method2(&self);
-    }
-}
-
-mod bar {
-    fn test<T: crate::baz::HirDatabase>(db: T) {
-        db.metho$0
-    }
-}
-"#,
-        expect![[r#""#]],
-    );
 }
 
 #[test]
@@ -1042,19 +1000,6 @@ fn function() {
     );
 }
 
-#[test]
-fn flyimport_item_name() {
-    check(
-        r#"
-mod module {
-    pub struct Struct;
-}
-struct Str$0
-    "#,
-        expect![[r#""#]],
-    );
-}
-
 #[test]
 fn flyimport_rename() {
     check(
diff --git a/crates/ide_completion/src/tests/pattern.rs b/crates/ide_completion/src/tests/pattern.rs
index 99e20b6ed0..81c45c64cc 100644
--- a/crates/ide_completion/src/tests/pattern.rs
+++ b/crates/ide_completion/src/tests/pattern.rs
@@ -130,24 +130,18 @@ fn foo() {
 fn irrefutable() {
     check(
         r#"
-enum SingleVariantEnum {
-    Variant
-}
-use SingleVariantEnum::Variant;
 fn foo() {
    let a$0
 }
 "#,
         expect![[r##"
             kw mut
-            bn Record            Record { field$1 }$0
+            bn Record    Record { field$1 }$0
             st Record
-            bn Tuple             Tuple($1)$0
+            bn Tuple     Tuple($1)$0
             st Tuple
-            ev Variant
-            en SingleVariantEnum
             st Unit
-            ma makro!(…)         #[macro_export] macro_rules! makro
+            ma makro!(…) #[macro_export] macro_rules! makro
         "##]],
     );
 }
diff --git a/crates/syntax/src/tests/sourcegen_ast.rs b/crates/syntax/src/tests/sourcegen_ast.rs
index c66edadc3c..dcd813bbe0 100644
--- a/crates/syntax/src/tests/sourcegen_ast.rs
+++ b/crates/syntax/src/tests/sourcegen_ast.rs
@@ -359,10 +359,6 @@ fn generate_syntax_kinds(grammar: KindsSrc<'_>) -> String {
     let full_keywords =
         full_keywords_values.iter().map(|kw| format_ident!("{}_KW", to_upper_snake_case(kw)));
 
-    let contextual_keywords_values = &grammar.contextual_keywords;
-    let contextual_keywords =
-        contextual_keywords_values.iter().map(|kw| format_ident!("{}_KW", to_upper_snake_case(kw)));
-
     let all_keywords_values =
         grammar.keywords.iter().chain(grammar.contextual_keywords.iter()).collect::<Vec<_>>();
     let all_keywords_idents = all_keywords_values.iter().map(|kw| format_ident!("{}", kw));
@@ -432,14 +428,6 @@ fn generate_syntax_kinds(grammar: KindsSrc<'_>) -> String {
                 Some(kw)
             }
 
-            pub fn from_contextual_keyword(ident: &str) -> Option<SyntaxKind> {
-                let kw = match ident {
-                    #(#contextual_keywords_values => #contextual_keywords,)*
-                    _ => return None,
-                };
-                Some(kw)
-            }
-
             pub fn from_char(c: char) -> Option<SyntaxKind> {
                 let tok = match c {
                     #(#single_byte_tokens_values => #single_byte_tokens,)*