Repository: rust-analyzer/rust-analyzer
Pull request #8674 (2021-04-27 01:18:06)

fix for #8664: Emit folding ranges for multi-line where clauses
#8664 

I added a test that assert folding multi-line where clauses while leaving single lined one. Please, let me know if the code needs further improvements.

matklad: There is_multiline check being done above already. For not hiding the where
clause itself, yeah, I guess we need a special case, but it seems that the
special case is better fit for the common code-path, not for the
continious_range one.

On Tue, 27 Apr 2021 at 17:39, m5tfi ***@***.***> wrote:

> ***@***.**** commented on this pull request.
> ------------------------------
>
> In crates/ide/src/folding_ranges.rs
> <https://github.com/rust-analyzer/rust-analyzer/pull/8674#discussion_r621274839>
> :
>
> > +                // Fold where clause
> +                if node.kind() == WHERE_CLAUSE && !visited_where_clauses.contains(&node) {
> +                    if let Some(range) =
> +                        contiguous_range_for_where(&node, &mut visited_where_clauses)
> +                    {
> +                        res.push(Fold { range, kind: FoldKind::WhereClause })
> +                    }
> +                }
>
> But when we fold the where clause as a whole, it hides the where keyword
>
> fn foo()where
>     A: Foo,
>     B: Foo,
>     C: Foo,
>     D: Foo, {}
>
> will be:
>
> fn foo()/* */ {}
>
> instead of
>
> fn foo()where /* */ {}
>
> and i won't be able to check if it's a multi-line where clause or not, am
> I missing something ?
>
> —
> You are receiving this because you commented.
> Reply to this email directly, view it on GitHub
> <https://github.com/rust-analyzer/rust-analyzer/pull/8674#discussion_r621274839>,
> or unsubscribe
> <https://github.com/notifications/unsubscribe-auth/AANB3M3M4NCADY3QSOMS33DTK3EDTANCNFSM43T7UENQ>
> .
>

m5tfi: > There is_multiline check being done above already

According to rustfmt ([here](https://rust-lang.github.io/rustfmt/?version=v1.4.36&search=#Where\%20predicates)). The `indent_style` of the where predicates defaults to "block". so, there will always be a newline `\n`.

```rust
fn foo()
where
    A: Foo,
{
}
```
Therefore, the `is_multiline` check above will always count where clauses as multi-line even if there was only one predicate.

So, I check if the first predicate matches the last one to make sure it's more than one, and start the fold at the end of the `where` keyword (second token) and end the fold at the end of the last comma (last token).

```rust
fn contiguous_range_for_where(
    node: &SyntaxNode,
    visited: &mut FxHashSet<SyntaxNode>,
) -> Option<TextRange> {
    let first_where_pred = node.first_child();
    let last_where_pred = node.last_child();

    if first_where_pred != last_where_pred {
        let mut it = node.descendants_with_tokens();
        if let (Some(_where_clause), Some(where_kw), Some(last_comma)) =
            (it.next(), it.next(), it.last())
        {
            let start = where_kw.text_range().end();
            let end = last_comma.text_range().end();

            visited.insert(node.clone());
            return Some(TextRange::new(start, end));
        }
    }
    None
}
```

I think my naming for the function might be misleading.
m5tfi: I am sorry, It's my third contribution ever on github and I made a couple of mistakes.

**TLDR** It is passing all the tests on my machine now.

I was working inside the `ide` crate and forgot to run tests for the whole project (outside the `ide` crate) before pushing. Then, I thought that I should merge with the recent commit in the original repo before trying to fix the failed test, but that failed another test (`tidy::check_merge_commits`) as I didn't know that I am not allowed to merge. So, I followed the stdout instructions, made a soft reset and fixed the issues.

Now, It's passing all of the tests on my machine. Can we try the github tests again please?

Patch:
diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index 63518e67ff..0f68b234c7 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -42,6 +42,14 @@ jobs:
       if: matrix.os == 'windows-latest'
       run: Rename-Item C:\Users\runneradmin\.rustup\toolchains\stable-x86_64-pc-windows-msvc C:\Users\runneradmin\.rustup\toolchains\stable-x86_64-pc-windows-msvc.old
 
+    # Work around https://github.com/actions/cache/issues/403 by using GNU tar
+    # instead of BSD tar.
+    - name: Install GNU tar
+      if: matrix.os == 'macos-latest'
+      run: |
+        brew install gnu-tar
+        echo PATH="/usr/local/opt/gnu-tar/libexec/gnubin:$PATH" >> $GITHUB_ENV
+
     - name: Install Rust toolchain
       uses: actions-rs/toolchain@v1
       with:
@@ -50,8 +58,19 @@ jobs:
         override: true
         components: rustfmt, rust-src
 
-    - name: Cache Dependencies
-      uses: Swatinem/rust-cache@ce325b60658c1b38465c06cc965b79baf32c1e72
+    - name: Cache cargo directories
+      uses: actions/cache@v2
+      with:
+        path: |
+          ~/.cargo/registry
+          ~/.cargo/git
+        key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
+
+    - name: Cache cargo target dir
+      uses: actions/cache@v2
+      with:
+        path: target
+        key: ${{ runner.os }}-cargo-build-target-${{ hashFiles('**/Cargo.lock') }}
 
     - name: Compile
       run: cargo test --no-run --locked
@@ -59,6 +78,9 @@ jobs:
     - name: Test
       run: cargo test -- --nocapture
 
+    - name: Prepare cache
+      run: cargo xtask pre-cache
+
   # Weird targets to catch non-portable code
   rust-cross:
     name: Rust Cross
@@ -81,8 +103,13 @@ jobs:
     - name: Install Rust targets
       run: rustup target add ${{ env.targets }}
 
-    - name: Cache Dependencies
-      uses: Swatinem/rust-cache@ce325b60658c1b38465c06cc965b79baf32c1e72
+    - name: Cache cargo directories
+      uses: actions/cache@v2
+      with:
+        path: |
+          ~/.cargo/registry
+          ~/.cargo/git
+        key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
 
     - name: Check
       run: |
diff --git a/Cargo.lock b/Cargo.lock
index 0ca0b7b63d..ba6862e984 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -72,12 +72,11 @@ checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
 
 [[package]]
 name = "backtrace"
-version = "0.3.58"
+version = "0.3.57"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "88fb5a785d6b44fd9d6700935608639af1b8356de1e55d5f7c2740f4faa15d82"
+checksum = "78ed203b9ba68b242c62b3fb7480f589dd49829be1edb3fe8fc8b4ffda2dcb8d"
 dependencies = [
  "addr2line",
- "cc",
  "cfg-if",
  "libc",
  "miniz_oxide",
@@ -281,9 +280,9 @@ dependencies = [
 
 [[package]]
 name = "crossbeam-epoch"
-version = "0.9.4"
+version = "0.9.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "52fb27eab85b17fbb9f6fd667089e07d6a2eb8743d02639ee7f6a7a7729c9c94"
+checksum = "2584f639eb95fea8c798496315b297cf81b9b58b6d30ab066a75455333cf4b12"
 dependencies = [
  "cfg-if",
  "crossbeam-utils",
@@ -294,9 +293,9 @@ dependencies = [
 
 [[package]]
 name = "crossbeam-utils"
-version = "0.8.4"
+version = "0.8.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4feb231f0d4d6af81aed15928e58ecf5816aa62a2393e2c82f46973e92a9a278"
+checksum = "e7e9d99fa91428effe99c5c6d4634cdeba32b8cf784fc428a2a687f61a952c49"
 dependencies = [
  "autocfg",
  "cfg-if",
@@ -799,9 +798,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
 
 [[package]]
 name = "libc"
-version = "0.2.94"
+version = "0.2.93"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "18794a8ad5b29321f790b55d93dfba91e125cb1a9edbd4f8e3150acc771c1a5e"
+checksum = "9385f66bf6105b241aa65a61cb923ef20efc665cb9f9bb50ac2f0c4b7f378d41"
 
 [[package]]
 name = "libloading"
@@ -824,9 +823,9 @@ dependencies = [
 
 [[package]]
 name = "lock_api"
-version = "0.4.4"
+version = "0.4.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0382880606dff6d15c9476c416d18690b72742aa7b605bb6dd6ec9030fbf07eb"
+checksum = "5a3c91c24eae6777794bb1997ad98bbb87daf92890acab859f7eaa4320333176"
 dependencies = [
  "scopeguard",
 ]
@@ -898,9 +897,19 @@ dependencies = [
 
 [[package]]
 name = "memchr"
-version = "2.4.0"
+version = "2.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525"
+
+[[package]]
+name = "memmap"
+version = "0.7.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc"
+checksum = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b"
+dependencies = [
+ "libc",
+ "winapi",
+]
 
 [[package]]
 name = "memmap2"
@@ -1099,9 +1108,9 @@ checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
 
 [[package]]
 name = "perf-event"
-version = "0.4.7"
+version = "0.4.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5396562cd2eaa828445d6d34258ae21ee1eb9d40fe626ca7f51c8dccb4af9d66"
+checksum = "b7a1c2678a77d65edf773bd900f5b87f0944ac3421949842a2c6a4efe42d6c66"
 dependencies = [
  "libc",
  "perf-event-open-sys",
@@ -1164,7 +1173,7 @@ dependencies = [
  "crossbeam-channel",
  "jod-thread",
  "log",
- "memmap2",
+ "memmap",
  "object",
  "profile",
  "serde",
@@ -1285,18 +1294,18 @@ dependencies = [
 
 [[package]]
 name = "redox_syscall"
-version = "0.2.7"
+version = "0.2.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "85dd92e586f7355c633911e11f77f3d12f04b1b1bd76a198bd34ae3af8341ef2"
+checksum = "8270314b5ccceb518e7e578952f0b72b88222d02e8f77f5ecf7abbb673539041"
 dependencies = [
  "bitflags",
 ]
 
 [[package]]
 name = "regex"
-version = "1.5.3"
+version = "1.4.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ce5f1ceb7f74abbce32601642fcf8e8508a8a8991e0621c7d750295b9095702b"
+checksum = "2a26af418b574bd56588335b3a3659a65725d4e636eb1016c2f9e3b38c7cc759"
 dependencies = [
  "regex-syntax",
 ]
@@ -1313,9 +1322,9 @@ dependencies = [
 
 [[package]]
 name = "regex-syntax"
-version = "0.6.25"
+version = "0.6.23"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
+checksum = "24d5f089152e60f62d28b835fbff2cd2e8dc0baf1ac13343bef92ab7eed84548"
 
 [[package]]
 name = "rowan"
@@ -1556,9 +1565,9 @@ dependencies = [
 
 [[package]]
 name = "snap"
-version = "1.0.5"
+version = "1.0.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "45456094d1983e2ee2a18fdfebce3189fa451699d0502cb8e3b49dba5ba41451"
+checksum = "dc725476a1398f0480d56cd0ad381f6f32acf2642704456f8f59a35df464b59a"
 
 [[package]]
 name = "stdx"
@@ -1573,9 +1582,9 @@ dependencies = [
 
 [[package]]
 name = "syn"
-version = "1.0.71"
+version = "1.0.70"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ad184cc9470f9117b2ac6817bfe297307418819ba40552f9b3846f05c33d5373"
+checksum = "b9505f307c872bab8eb46f77ae357c8eba1fdacead58ee5a850116b1d7f82883"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -1693,9 +1702,9 @@ dependencies = [
 
 [[package]]
 name = "tracing"
-version = "0.1.26"
+version = "0.1.25"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "09adeb8c97449311ccd28a427f96fb563e7fd31aabf994189879d9da2394b89d"
+checksum = "01ebdc2bb4498ab1ab5f5b73c5803825e60199229ccba0698170e3be0e7f959f"
 dependencies = [
  "cfg-if",
  "pin-project-lite",
@@ -1716,9 +1725,9 @@ dependencies = [
 
 [[package]]
 name = "tracing-core"
-version = "0.1.18"
+version = "0.1.17"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a9ff14f98b1a4b289c6248a023c1c2fa1491062964e9fed67ab29c4e4da4a052"
+checksum = "f50de3927f93d202783f4513cda820ab47ef17f624b03c096e86ef00c67e6b5f"
 dependencies = [
  "lazy_static",
 ]
@@ -1746,9 +1755,9 @@ dependencies = [
 
 [[package]]
 name = "tracing-subscriber"
-version = "0.2.18"
+version = "0.2.17"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aa5553bf0883ba7c9cbe493b085c29926bd41b66afc31ff72cf17ff4fb60dcd5"
+checksum = "705096c6f83bf68ea5d357a6aa01829ddbdac531b357b45abeca842938085baa"
 dependencies = [
  "ansi_term",
  "chrono",
@@ -1835,9 +1844,9 @@ checksum = "bb0d2e7be6ae3a5fa87eed5fb451aff96f2573d2694942e40543ae0bbe19c796"
 
 [[package]]
 name = "unicode-xid"
-version = "0.2.2"
+version = "0.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
+checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
 
 [[package]]
 name = "url"
@@ -1950,18 +1959,18 @@ dependencies = [
 
 [[package]]
 name = "xshell"
-version = "0.1.10"
+version = "0.1.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aa25217c682f9f991d7889238a99e65eb8431c266d36e0f4e850a73773415473"
+checksum = "6f18102278453c8f70ea5c514ac78cb4c73a0ef72a8273d17094b52f9584c0c1"
 dependencies = [
  "xshell-macros",
 ]
 
 [[package]]
 name = "xshell-macros"
-version = "0.1.10"
+version = "0.1.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4404d53d2113af4fa31c58326eb7b37d6d7bf11ba87520787cddeaff45385c72"
+checksum = "6093c460064572007f885facc70bb0ca5e40a83ea7ff8b16c1abbee56fd2e767"
 
 [[package]]
 name = "xtask"
diff --git a/crates/hir/src/display.rs b/crates/hir/src/display.rs
index 508ac37c22..01a4d205fd 100644
--- a/crates/hir/src/display.rs
+++ b/crates/hir/src/display.rs
@@ -170,7 +170,7 @@ impl HirDisplay for Field {
     fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
         write_visibility(self.parent.module(f.db).id, self.visibility(f.db), f)?;
         write!(f, "{}: ", self.name(f.db))?;
-        self.ty(f.db).hir_fmt(f)
+        self.signature_ty(f.db).hir_fmt(f)
     }
 }
 
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index d8ccfde0cf..0acfa582a0 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -509,7 +509,7 @@ impl Field {
     /// placeholder types for type parameters). This is good for showing
     /// signature help, but not so good to actually get the type of the field
     /// when you actually have a variable of the struct.
-    pub fn ty(&self, db: &dyn HirDatabase) -> Type {
+    pub fn signature_ty(&self, db: &dyn HirDatabase) -> Type {
         let var_id = self.parent.into();
         let generic_def_id: GenericDefId = match self.parent {
             VariantDef::Struct(it) => it.id.into(),
@@ -1984,7 +1984,7 @@ impl Type {
         None
     }
 
-    pub fn type_arguments(&self) -> impl Iterator<Item = Type> + '_ {
+    pub fn type_parameters(&self) -> impl Iterator<Item = Type> + '_ {
         self.ty
             .strip_references()
             .as_adt()
diff --git a/crates/hir_def/src/attr.rs b/crates/hir_def/src/attr.rs
index 0171d8a92a..d9294d93af 100644
--- a/crates/hir_def/src/attr.rs
+++ b/crates/hir_def/src/attr.rs
@@ -484,10 +484,10 @@ impl AttrsWithOwner {
         let mut buf = String::new();
         let mut mapping = Vec::new();
         for (doc, idx) in docs {
+            // str::lines doesn't yield anything for the empty string
             if !doc.is_empty() {
-                let mut base_offset = 0;
-                for raw_line in doc.split('\n') {
-                    let line = raw_line.trim_end();
+                for line in doc.split('\n') {
+                    let line = line.trim_end();
                     let line_len = line.len();
                     let (offset, line) = match line.char_indices().nth(indent) {
                         Some((offset, _)) => (offset, &line[offset..]),
@@ -498,13 +498,9 @@ impl AttrsWithOwner {
                     mapping.push((
                         TextRange::new(buf_offset.try_into().ok()?, buf.len().try_into().ok()?),
                         idx,
-                        TextRange::at(
-                            (base_offset + offset).try_into().ok()?,
-                            line_len.try_into().ok()?,
-                        ),
+                        TextRange::new(offset.try_into().ok()?, line_len.try_into().ok()?),
                     ));
                     buf.push('\n');
-                    base_offset += raw_line.len() + 1;
                 }
             } else {
                 buf.push('\n');
diff --git a/crates/hir_def/src/find_path.rs b/crates/hir_def/src/find_path.rs
index c06a372948..dc3f2908f8 100644
--- a/crates/hir_def/src/find_path.rs
+++ b/crates/hir_def/src/find_path.rs
@@ -130,8 +130,7 @@ fn find_path_inner(
     }
 
     // - if the item is the crate root of a dependency crate, return the name from the extern prelude
-    let root_def_map = root.def_map(db);
-    for (name, def_id) in root_def_map.extern_prelude() {
+    for (name, def_id) in root.def_map(db).extern_prelude() {
         if item == ItemInNs::Types(*def_id) {
             let name = scope_name.unwrap_or_else(|| name.clone());
             return Some(ModPath::from_segments(PathKind::Plain, vec![name]));
@@ -139,8 +138,7 @@ fn find_path_inner(
     }
 
     // - if the item is in the prelude, return the name from there
-    if let Some(prelude_module) = root_def_map.prelude() {
-        // Preludes in block DefMaps are ignored, only the crate DefMap is searched
+    if let Some(prelude_module) = def_map.prelude() {
         let prelude_def_map = prelude_module.def_map(db);
         let prelude_scope: &crate::item_scope::ItemScope =
             &prelude_def_map[prelude_module.local_id].scope;
@@ -1059,28 +1057,4 @@ fn f() {
             "dep",
         );
     }
-
-    #[test]
-    fn prelude_with_inner_items() {
-        check_found_path(
-            r#"
-//- /main.rs crate:main deps:std
-fn f() {
-    fn inner() {}
-    $0
-}
-//- /std.rs crate:std
-pub mod prelude {
-    pub enum Option { None }
-    pub use Option::*;
-}
-#[prelude_import]
-pub use prelude::*;
-        "#,
-            "None",
-            "None",
-            "None",
-            "None",
-        );
-    }
 }
diff --git a/crates/hir_expand/src/db.rs b/crates/hir_expand/src/db.rs
index 3e9abd8a19..1e4b0cc194 100644
--- a/crates/hir_expand/src/db.rs
+++ b/crates/hir_expand/src/db.rs
@@ -3,14 +3,14 @@
 use std::sync::Arc;
 
 use base_db::{salsa, SourceDatabase};
-use mbe::{ExpandError, ExpandResult};
+use mbe::{ExpandError, ExpandResult, MacroDef, MacroRules};
 use parser::FragmentKind;
 use syntax::{
     algo::diff,
-    ast::{self, NameOwner},
+    ast::{MacroStmts, NameOwner},
     AstNode, GreenNode, Parse,
     SyntaxKind::*,
-    SyntaxNode, SyntaxToken,
+    SyntaxNode,
 };
 
 use crate::{
@@ -27,28 +27,23 @@ const TOKEN_LIMIT: usize = 524288;
 
 #[derive(Debug, Clone, Eq, PartialEq)]
 pub enum TokenExpander {
-    /// Old-style `macro_rules`.
-    MacroRules { mac: mbe::MacroRules, def_site_token_map: mbe::TokenMap },
-    /// AKA macros 2.0.
-    MacroDef { mac: mbe::MacroDef, def_site_token_map: mbe::TokenMap },
-    /// Stuff like `line!` and `file!`.
+    MacroRules(mbe::MacroRules),
+    MacroDef(mbe::MacroDef),
     Builtin(BuiltinFnLikeExpander),
-    /// `derive(Copy)` and such.
     BuiltinDerive(BuiltinDeriveExpander),
-    /// The thing we love the most here in rust-analyzer -- procedural macros.
     ProcMacro(ProcMacroExpander),
 }
 
 impl TokenExpander {
-    fn expand(
+    pub fn expand(
         &self,
         db: &dyn AstDatabase,
         id: LazyMacroId,
         tt: &tt::Subtree,
     ) -> mbe::ExpandResult<tt::Subtree> {
         match self {
-            TokenExpander::MacroRules { mac, .. } => mac.expand(tt),
-            TokenExpander::MacroDef { mac, .. } => mac.expand(tt),
+            TokenExpander::MacroRules(it) => it.expand(tt),
+            TokenExpander::MacroDef(it) => it.expand(tt),
             TokenExpander::Builtin(it) => it.expand(db, id, tt),
             // FIXME switch these to ExpandResult as well
             TokenExpander::BuiltinDerive(it) => it.expand(db, id, tt).into(),
@@ -61,23 +56,23 @@ impl TokenExpander {
         }
     }
 
-    pub(crate) fn map_id_down(&self, id: tt::TokenId) -> tt::TokenId {
+    pub fn map_id_down(&self, id: tt::TokenId) -> tt::TokenId {
         match self {
-            TokenExpander::MacroRules { mac, .. } => mac.map_id_down(id),
-            TokenExpander::MacroDef { mac, .. } => mac.map_id_down(id),
-            TokenExpander::Builtin(..)
-            | TokenExpander::BuiltinDerive(..)
-            | TokenExpander::ProcMacro(..) => id,
+            TokenExpander::MacroRules(it) => it.map_id_down(id),
+            TokenExpander::MacroDef(it) => it.map_id_down(id),
+            TokenExpander::Builtin(..) => id,
+            TokenExpander::BuiltinDerive(..) => id,
+            TokenExpander::ProcMacro(..) => id,
         }
     }
 
-    pub(crate) fn map_id_up(&self, id: tt::TokenId) -> (tt::TokenId, mbe::Origin) {
+    pub fn map_id_up(&self, id: tt::TokenId) -> (tt::TokenId, mbe::Origin) {
         match self {
-            TokenExpander::MacroRules { mac, .. } => mac.map_id_up(id),
-            TokenExpander::MacroDef { mac, .. } => mac.map_id_up(id),
-            TokenExpander::Builtin(..)
-            | TokenExpander::BuiltinDerive(..)
-            | TokenExpander::ProcMacro(..) => (id, mbe::Origin::Call),
+            TokenExpander::MacroRules(it) => it.map_id_up(id),
+            TokenExpander::MacroDef(it) => it.map_id_up(id),
+            TokenExpander::Builtin(..) => (id, mbe::Origin::Call),
+            TokenExpander::BuiltinDerive(..) => (id, mbe::Origin::Call),
+            TokenExpander::ProcMacro(..) => (id, mbe::Origin::Call),
         }
     }
 }
@@ -87,48 +82,28 @@ impl TokenExpander {
 pub trait AstDatabase: SourceDatabase {
     fn ast_id_map(&self, file_id: HirFileId) -> Arc<AstIdMap>;
 
-    /// Main public API -- parsis a hir file, not caring whether it's a real
-    /// file or a macro expansion.
     #[salsa::transparent]
     fn parse_or_expand(&self, file_id: HirFileId) -> Option<SyntaxNode>;
-    /// Implementation for the macro case.
+
+    #[salsa::interned]
+    fn intern_macro(&self, macro_call: MacroCallLoc) -> LazyMacroId;
+    fn macro_arg_text(&self, id: MacroCallId) -> Option<GreenNode>;
+    #[salsa::transparent]
+    fn macro_arg(&self, id: MacroCallId) -> Option<Arc<(tt::Subtree, mbe::TokenMap)>>;
+    fn macro_def(&self, id: MacroDefId) -> Option<Arc<(TokenExpander, mbe::TokenMap)>>;
     fn parse_macro_expansion(
         &self,
         macro_file: MacroFile,
     ) -> ExpandResult<Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)>>;
+    fn macro_expand(&self, macro_call: MacroCallId) -> ExpandResult<Option<Arc<tt::Subtree>>>;
+
+    /// Firewall query that returns the error from the `macro_expand` query.
+    fn macro_expand_error(&self, macro_call: MacroCallId) -> Option<ExpandError>;
 
-    /// Macro ids. That's probably the tricksiest bit in rust-analyzer, and the
-    /// reason why we use salsa at all.
-    ///
-    /// We encode macro definitions into ids of macro calls, this what allows us
-    /// to be incremental.
-    #[salsa::interned]
-    fn intern_macro(&self, macro_call: MacroCallLoc) -> LazyMacroId;
-    /// Certain built-in macros are eager (`format!(concat!("file: ", file!(), "{}"")), 92`).
-    /// For them, we actually want to encode the whole token tree as an argument.
     #[salsa::interned]
     fn intern_eager_expansion(&self, eager: EagerCallLoc) -> EagerMacroId;
 
-    /// Lowers syntactic macro call to a token tree representation.
-    #[salsa::transparent]
-    fn macro_arg(&self, id: MacroCallId) -> Option<Arc<(tt::Subtree, mbe::TokenMap)>>;
-    /// Extracts syntax node, corresponding to a macro call. That's a firewall
-    /// query, only typing in the macro call itself changes the returned
-    /// subtree.
-    fn macro_arg_text(&self, id: MacroCallId) -> Option<GreenNode>;
-    /// Gets the expander for this macro. This compiles declarative macros, and
-    /// just fetches procedural ones.
-    fn macro_def(&self, id: MacroDefId) -> Option<Arc<TokenExpander>>;
-
-    /// Expand macro call to a token tree. This query is LRUed (we keep 128 or so results in memory)
-    fn macro_expand(&self, macro_call: MacroCallId) -> ExpandResult<Option<Arc<tt::Subtree>>>;
-    /// Special case of the previous query for procedural macros. We can't LRU
-    /// proc macros, since they are not deterministic in general, and
-    /// non-determinism breaks salsa in a very, very, very bad way. @edwin0cheng
-    /// heroically debugged this once!
     fn expand_proc_macro(&self, call: MacroCallId) -> Result<tt::Subtree, mbe::ExpandError>;
-    /// Firewall query that returns the error from the `macro_expand` query.
-    fn macro_expand_error(&self, macro_call: MacroCallId) -> Option<ExpandError>;
 
     fn hygiene_frame(&self, file_id: HirFileId) -> Arc<HygieneFrame>;
 }
@@ -140,159 +115,36 @@ pub trait AstDatabase: SourceDatabase {
 pub fn expand_hypothetical(
     db: &dyn AstDatabase,
     actual_macro_call: MacroCallId,
-    hypothetical_args: &ast::TokenTree,
-    token_to_map: SyntaxToken,
-) -> Option<(SyntaxNode, SyntaxToken)> {
+    hypothetical_args: &syntax::ast::TokenTree,
+    token_to_map: syntax::SyntaxToken,
+) -> Option<(SyntaxNode, syntax::SyntaxToken)> {
+    let macro_file = MacroFile { macro_call_id: actual_macro_call };
     let (tt, tmap_1) = mbe::syntax_node_to_token_tree(hypothetical_args.syntax());
     let range =
         token_to_map.text_range().checked_sub(hypothetical_args.syntax().text_range().start())?;
     let token_id = tmap_1.token_by_range(range)?;
-
-    let lazy_id = match actual_macro_call {
-        MacroCallId::LazyMacro(id) => id,
-        MacroCallId::EagerMacro(_) => return None,
-    };
-
-    let macro_def = {
-        let loc = db.lookup_intern_macro(lazy_id);
-        db.macro_def(loc.def)?
-    };
-
-    let hypothetical_expansion = macro_def.expand(db, lazy_id, &tt);
-
-    let fragment_kind = to_fragment_kind(db, actual_macro_call);
-
+    let macro_def = expander(db, actual_macro_call)?;
     let (node, tmap_2) =
-        mbe::token_tree_to_syntax_node(&hypothetical_expansion.value, fragment_kind).ok()?;
-
-    let token_id = macro_def.map_id_down(token_id);
+        parse_macro_with_arg(db, macro_file, Some(std::sync::Arc::new((tt, tmap_1)))).value?;
+    let token_id = macro_def.0.map_id_down(token_id);
     let range = tmap_2.range_by_token(token_id)?.by_kind(token_to_map.kind())?;
     let token = node.syntax_node().covering_element(range).into_token()?;
     Some((node.syntax_node(), token))
 }
 
 fn ast_id_map(db: &dyn AstDatabase, file_id: HirFileId) -> Arc<AstIdMap> {
-    let map = db.parse_or_expand(file_id).map(|it| AstIdMap::from_source(&it)).unwrap_or_default();
+    let map =
+        db.parse_or_expand(file_id).map_or_else(AstIdMap::default, |it| AstIdMap::from_source(&it));
     Arc::new(map)
 }
 
-fn parse_or_expand(db: &dyn AstDatabase, file_id: HirFileId) -> Option<SyntaxNode> {
-    match file_id.0 {
-        HirFileIdRepr::FileId(file_id) => Some(db.parse(file_id).tree().syntax().clone()),
-        HirFileIdRepr::MacroFile(macro_file) => {
-            db.parse_macro_expansion(macro_file).value.map(|(it, _)| it.syntax_node())
-        }
-    }
-}
-
-fn parse_macro_expansion(
-    db: &dyn AstDatabase,
-    macro_file: MacroFile,
-) -> ExpandResult<Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)>> {
-    let _p = profile::span("parse_macro_expansion");
-    let result = db.macro_expand(macro_file.macro_call_id);
-
-    if let Some(err) = &result.err {
-        // Note:
-        // The final goal we would like to make all parse_macro success,
-        // such that the following log will not call anyway.
-        match macro_file.macro_call_id {
-            MacroCallId::LazyMacro(id) => {
-                let loc: MacroCallLoc = db.lookup_intern_macro(id);
-                let node = loc.kind.node(db);
-
-                // collect parent information for warning log
-                let parents = std::iter::successors(loc.kind.file_id().call_node(db), |it| {
-                    it.file_id.call_node(db)
-                })
-                .map(|n| format!("{:#}", n.value))
-                .collect::<Vec<_>>()
-                .join("\n");
-
-                log::warn!(
-                    "fail on macro_parse: (reason: {:?} macro_call: {:#}) parents: {}",
-                    err,
-                    node.value,
-                    parents
-                );
-            }
-            _ => {
-                log::warn!("fail on macro_parse: (reason: {:?})", err);
-            }
-        }
-    }
-    let tt = match result.value {
-        Some(tt) => tt,
-        None => return ExpandResult { value: None, err: result.err },
-    };
-
-    let fragment_kind = to_fragment_kind(db, macro_file.macro_call_id);
-
-    log::debug!("expanded = {}", tt.as_debug_string());
-    log::debug!("kind = {:?}", fragment_kind);
-
-    let (parse, rev_token_map) = match mbe::token_tree_to_syntax_node(&tt, fragment_kind) {
-        Ok(it) => it,
-        Err(err) => {
-            log::debug!(
-                "failed to parse expanstion to {:?} = {}",
-                fragment_kind,
-                tt.as_debug_string()
-            );
-            return ExpandResult::only_err(err);
-        }
-    };
-
-    match result.err {
-        Some(err) => {
-            // Safety check for recursive identity macro.
-            let node = parse.syntax_node();
-            let file: HirFileId = macro_file.into();
-            let call_node = match file.call_node(db) {
-                Some(it) => it,
-                None => {
-                    return ExpandResult::only_err(err);
-                }
-            };
-            if is_self_replicating(&node, &call_node.value) {
-                return ExpandResult::only_err(err);
-            } else {
-                ExpandResult { value: Some((parse, Arc::new(rev_token_map))), err: Some(err) }
-            }
-        }
-        None => {
-            log::debug!("parse = {:?}", parse.syntax_node().kind());
-            ExpandResult { value: Some((parse, Arc::new(rev_token_map))), err: None }
-        }
-    }
-}
-
-fn macro_arg(db: &dyn AstDatabase, id: MacroCallId) -> Option<Arc<(tt::Subtree, mbe::TokenMap)>> {
-    let arg = db.macro_arg_text(id)?;
-    let (tt, tmap) = mbe::syntax_node_to_token_tree(&SyntaxNode::new_root(arg));
-    Some(Arc::new((tt, tmap)))
-}
-
-fn macro_arg_text(db: &dyn AstDatabase, id: MacroCallId) -> Option<GreenNode> {
-    let id = match id {
-        MacroCallId::LazyMacro(id) => id,
-        MacroCallId::EagerMacro(_id) => {
-            // FIXME: support macro_arg for eager macro
-            return None;
-        }
-    };
-    let loc = db.lookup_intern_macro(id);
-    let arg = loc.kind.arg(db)?;
-    Some(arg.green())
-}
-
-fn macro_def(db: &dyn AstDatabase, id: MacroDefId) -> Option<Arc<TokenExpander>> {
+fn macro_def(db: &dyn AstDatabase, id: MacroDefId) -> Option<Arc<(TokenExpander, mbe::TokenMap)>> {
     match id.kind {
         MacroDefKind::Declarative(ast_id) => match ast_id.to_node(db) {
-            ast::Macro::MacroRules(macro_rules) => {
+            syntax::ast::Macro::MacroRules(macro_rules) => {
                 let arg = macro_rules.token_tree()?;
-                let (tt, def_site_token_map) = mbe::ast_to_token_tree(&arg);
-                let mac = match mbe::MacroRules::parse(&tt) {
+                let (tt, tmap) = mbe::ast_to_token_tree(&arg);
+                let rules = match MacroRules::parse(&tt) {
                     Ok(it) => it,
                     Err(err) => {
                         let name = macro_rules.name().map(|n| n.to_string()).unwrap_or_default();
@@ -300,12 +152,12 @@ fn macro_def(db: &dyn AstDatabase, id: MacroDefId) -> Option<Arc<TokenExpander>>
                         return None;
                     }
                 };
-                Some(Arc::new(TokenExpander::MacroRules { mac, def_site_token_map }))
+                Some(Arc::new((TokenExpander::MacroRules(rules), tmap)))
             }
-            ast::Macro::MacroDef(macro_def) => {
+            syntax::ast::Macro::MacroDef(macro_def) => {
                 let arg = macro_def.body()?;
-                let (tt, def_site_token_map) = mbe::ast_to_token_tree(&arg);
-                let mac = match mbe::MacroDef::parse(&tt) {
+                let (tt, tmap) = mbe::ast_to_token_tree(&arg);
+                let rules = match MacroDef::parse(&tt) {
                     Ok(it) => it,
                     Err(err) => {
                         let name = macro_def.name().map(|n| n.to_string()).unwrap_or_default();
@@ -313,18 +165,41 @@ fn macro_def(db: &dyn AstDatabase, id: MacroDefId) -> Option<Arc<TokenExpander>>
                         return None;
                     }
                 };
-                Some(Arc::new(TokenExpander::MacroDef { mac, def_site_token_map }))
+                Some(Arc::new((TokenExpander::MacroDef(rules), tmap)))
             }
         },
-        MacroDefKind::BuiltIn(expander, _) => Some(Arc::new(TokenExpander::Builtin(expander))),
+        MacroDefKind::BuiltIn(expander, _) => {
+            Some(Arc::new((TokenExpander::Builtin(expander), mbe::TokenMap::default())))
+        }
         MacroDefKind::BuiltInDerive(expander, _) => {
-            Some(Arc::new(TokenExpander::BuiltinDerive(expander)))
+            Some(Arc::new((TokenExpander::BuiltinDerive(expander), mbe::TokenMap::default())))
         }
         MacroDefKind::BuiltInEager(..) => None,
-        MacroDefKind::ProcMacro(expander, ..) => Some(Arc::new(TokenExpander::ProcMacro(expander))),
+        MacroDefKind::ProcMacro(expander, ..) => {
+            Some(Arc::new((TokenExpander::ProcMacro(expander), mbe::TokenMap::default())))
+        }
     }
 }
 
+fn macro_arg_text(db: &dyn AstDatabase, id: MacroCallId) -> Option<GreenNode> {
+    let id = match id {
+        MacroCallId::LazyMacro(id) => id,
+        MacroCallId::EagerMacro(_id) => {
+            // FIXME: support macro_arg for eager macro
+            return None;
+        }
+    };
+    let loc = db.lookup_intern_macro(id);
+    let arg = loc.kind.arg(db)?;
+    Some(arg.green())
+}
+
+fn macro_arg(db: &dyn AstDatabase, id: MacroCallId) -> Option<Arc<(tt::Subtree, mbe::TokenMap)>> {
+    let arg = db.macro_arg_text(id)?;
+    let (tt, tmap) = mbe::syntax_node_to_token_tree(&SyntaxNode::new_root(arg));
+    Some(Arc::new((tt, tmap)))
+}
+
 fn macro_expand(db: &dyn AstDatabase, id: MacroCallId) -> ExpandResult<Option<Arc<tt::Subtree>>> {
     macro_expand_with_arg(db, id, None)
 }
@@ -333,6 +208,19 @@ fn macro_expand_error(db: &dyn AstDatabase, macro_call: MacroCallId) -> Option<E
     db.macro_expand(macro_call).err
 }
 
+fn expander(db: &dyn AstDatabase, id: MacroCallId) -> Option<Arc<(TokenExpander, mbe::TokenMap)>> {
+    let lazy_id = match id {
+        MacroCallId::LazyMacro(id) => id,
+        MacroCallId::EagerMacro(_id) => {
+            return None;
+        }
+    };
+
+    let loc = db.lookup_intern_macro(lazy_id);
+    let macro_rules = db.macro_def(loc.def)?;
+    Some(macro_rules)
+}
+
 fn macro_expand_with_arg(
     db: &dyn AstDatabase,
     id: MacroCallId,
@@ -366,7 +254,7 @@ fn macro_expand_with_arg(
         Some(it) => it,
         None => return ExpandResult::str_err("Fail to find macro definition".into()),
     };
-    let ExpandResult { value: tt, err } = macro_rules.expand(db, lazy_id, &macro_arg.0);
+    let ExpandResult { value: tt, err } = macro_rules.0.expand(db, lazy_id, &macro_arg.0);
     // Set a hard limit for the expanded tt
     let count = tt.count();
     if count > TOKEN_LIMIT {
@@ -406,11 +294,116 @@ fn expand_proc_macro(
     expander.expand(db, loc.krate, &macro_arg.0)
 }
 
+fn parse_or_expand(db: &dyn AstDatabase, file_id: HirFileId) -> Option<SyntaxNode> {
+    match file_id.0 {
+        HirFileIdRepr::FileId(file_id) => Some(db.parse(file_id).tree().syntax().clone()),
+        HirFileIdRepr::MacroFile(macro_file) => {
+            db.parse_macro_expansion(macro_file).value.map(|(it, _)| it.syntax_node())
+        }
+    }
+}
+
+fn parse_macro_expansion(
+    db: &dyn AstDatabase,
+    macro_file: MacroFile,
+) -> ExpandResult<Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)>> {
+    parse_macro_with_arg(db, macro_file, None)
+}
+
+fn parse_macro_with_arg(
+    db: &dyn AstDatabase,
+    macro_file: MacroFile,
+    arg: Option<Arc<(tt::Subtree, mbe::TokenMap)>>,
+) -> ExpandResult<Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)>> {
+    let macro_call_id = macro_file.macro_call_id;
+    let result = if let Some(arg) = arg {
+        macro_expand_with_arg(db, macro_call_id, Some(arg))
+    } else {
+        db.macro_expand(macro_call_id)
+    };
+
+    let _p = profile::span("parse_macro_expansion");
+
+    if let Some(err) = &result.err {
+        // Note:
+        // The final goal we would like to make all parse_macro success,
+        // such that the following log will not call anyway.
+        match macro_call_id {
+            MacroCallId::LazyMacro(id) => {
+                let loc: MacroCallLoc = db.lookup_intern_macro(id);
+                let node = loc.kind.node(db);
+
+                // collect parent information for warning log
+                let parents = std::iter::successors(loc.kind.file_id().call_node(db), |it| {
+                    it.file_id.call_node(db)
+                })
+                .map(|n| format!("{:#}", n.value))
+                .collect::<Vec<_>>()
+                .join("\n");
+
+                log::warn!(
+                    "fail on macro_parse: (reason: {:?} macro_call: {:#}) parents: {}",
+                    err,
+                    node.value,
+                    parents
+                );
+            }
+            _ => {
+                log::warn!("fail on macro_parse: (reason: {:?})", err);
+            }
+        }
+    }
+    let tt = match result.value {
+        Some(tt) => tt,
+        None => return ExpandResult { value: None, err: result.err },
+    };
+
+    let fragment_kind = to_fragment_kind(db, macro_call_id);
+
+    log::debug!("expanded = {}", tt.as_debug_string());
+    log::debug!("kind = {:?}", fragment_kind);
+
+    let (parse, rev_token_map) = match mbe::token_tree_to_syntax_node(&tt, fragment_kind) {
+        Ok(it) => it,
+        Err(err) => {
+            log::debug!(
+                "failed to parse expanstion to {:?} = {}",
+                fragment_kind,
+                tt.as_debug_string()
+            );
+            return ExpandResult::only_err(err);
+        }
+    };
+
+    match result.err {
+        Some(err) => {
+            // Safety check for recursive identity macro.
+            let node = parse.syntax_node();
+            let file: HirFileId = macro_file.into();
+            let call_node = match file.call_node(db) {
+                Some(it) => it,
+                None => {
+                    return ExpandResult::only_err(err);
+                }
+            };
+            if is_self_replicating(&node, &call_node.value) {
+                return ExpandResult::only_err(err);
+            } else {
+                ExpandResult { value: Some((parse, Arc::new(rev_token_map))), err: Some(err) }
+            }
+        }
+        None => {
+            log::debug!("parse = {:?}", parse.syntax_node().kind());
+            ExpandResult { value: Some((parse, Arc::new(rev_token_map))), err: None }
+        }
+    }
+}
+
 fn is_self_replicating(from: &SyntaxNode, to: &SyntaxNode) -> bool {
     if diff(from, to).is_empty() {
         return true;
     }
-    if let Some(stmts) = ast::MacroStmts::cast(from.clone()) {
+    if let Some(stmts) = MacroStmts::cast(from.clone()) {
         if stmts.statements().any(|stmt| diff(stmt.syntax(), to).is_empty()) {
             return true;
         }
diff --git a/crates/hir_expand/src/hygiene.rs b/crates/hir_expand/src/hygiene.rs
index ed61ebca3a..779725629a 100644
--- a/crates/hir_expand/src/hygiene.rs
+++ b/crates/hir_expand/src/hygiene.rs
@@ -5,7 +5,6 @@
 use std::sync::Arc;
 
 use base_db::CrateId;
-use db::TokenExpander;
 use either::Either;
 use mbe::Origin;
 use parser::SyntaxKind;
@@ -116,7 +115,7 @@ struct HygieneInfo {
     /// The `macro_rules!` arguments.
     def_start: Option<InFile<TextSize>>,
 
-    macro_def: Arc<TokenExpander>,
+    macro_def: Arc<(db::TokenExpander, mbe::TokenMap)>,
     macro_arg: Arc<(tt::Subtree, mbe::TokenMap)>,
     exp_map: Arc<mbe::TokenMap>,
 }
@@ -125,16 +124,13 @@ impl HygieneInfo {
     fn map_ident_up(&self, token: TextRange) -> Option<(InFile<TextRange>, Origin)> {
         let token_id = self.exp_map.token_by_range(token)?;
 
-        let (token_id, origin) = self.macro_def.map_id_up(token_id);
+        let (token_id, origin) = self.macro_def.0.map_id_up(token_id);
         let (token_map, tt) = match origin {
             mbe::Origin::Call => (&self.macro_arg.1, self.arg_start),
-            mbe::Origin::Def => match (&*self.macro_def, self.def_start) {
-                (TokenExpander::MacroDef { def_site_token_map, .. }, Some(tt))
-                | (TokenExpander::MacroRules { def_site_token_map, .. }, Some(tt)) => {
-                    (def_site_token_map, tt)
-                }
-                _ => panic!("`Origin::Def` used with non-`macro_rules!` macro"),
-            },
+            mbe::Origin::Def => (
+                &self.macro_def.1,
+                *self.def_start.as_ref().expect("`Origin::Def` used with non-`macro_rules!` macro"),
+            ),
         };
 
         let range = token_map.range_by_token(token_id)?.by_kind(SyntaxKind::IDENT)?;
diff --git a/crates/hir_expand/src/lib.rs b/crates/hir_expand/src/lib.rs
index 0402640de3..a0e6aec628 100644
--- a/crates/hir_expand/src/lib.rs
+++ b/crates/hir_expand/src/lib.rs
@@ -351,7 +351,7 @@ pub struct ExpansionInfo {
     /// The `macro_rules!` arguments.
     def: Option<InFile<ast::TokenTree>>,
 
-    macro_def: Arc<db::TokenExpander>,
+    macro_def: Arc<(db::TokenExpander, mbe::TokenMap)>,
     macro_arg: Arc<(tt::Subtree, mbe::TokenMap)>,
     exp_map: Arc<mbe::TokenMap>,
 }
@@ -368,7 +368,7 @@ impl ExpansionInfo {
         assert_eq!(token.file_id, self.arg.file_id);
         let range = token.value.text_range().checked_sub(self.arg.value.text_range().start())?;
         let token_id = self.macro_arg.1.token_by_range(range)?;
-        let token_id = self.macro_def.map_id_down(token_id);
+        let token_id = self.macro_def.0.map_id_down(token_id);
 
         let range = self.exp_map.range_by_token(token_id)?.by_kind(token.value.kind())?;
 
@@ -383,16 +383,17 @@ impl ExpansionInfo {
     ) -> Option<(InFile<SyntaxToken>, Origin)> {
         let token_id = self.exp_map.token_by_range(token.value.text_range())?;
 
-        let (token_id, origin) = self.macro_def.map_id_up(token_id);
+        let (token_id, origin) = self.macro_def.0.map_id_up(token_id);
         let (token_map, tt) = match origin {
             mbe::Origin::Call => (&self.macro_arg.1, self.arg.clone()),
-            mbe::Origin::Def => match (&*self.macro_def, self.def.as_ref()) {
-                (db::TokenExpander::MacroRules { def_site_token_map, .. }, Some(tt))
-                | (db::TokenExpander::MacroDef { def_site_token_map, .. }, Some(tt)) => {
-                    (def_site_token_map, tt.as_ref().map(|tt| tt.syntax().clone()))
-                }
-                _ => panic!("`Origin::Def` used with non-`macro_rules!` macro"),
-            },
+            mbe::Origin::Def => (
+                &self.macro_def.1,
+                self.def
+                    .as_ref()
+                    .expect("`Origin::Def` used with non-`macro_rules!` macro")
+                    .as_ref()
+                    .map(|tt| tt.syntax().clone()),
+            ),
         };
 
         let range = token_map.range_by_token(token_id)?.by_kind(token.value.kind())?;
diff --git a/crates/hir_ty/src/db.rs b/crates/hir_ty/src/db.rs
index 9da0a02e33..cf67d42662 100644
--- a/crates/hir_ty/src/db.rs
+++ b/crates/hir_ty/src/db.rs
@@ -70,7 +70,6 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
     fn trait_environment(&self, def: GenericDefId) -> Arc<crate::TraitEnvironment>;
 
     #[salsa::invoke(crate::lower::generic_defaults_query)]
-    #[salsa::cycle(crate::lower::generic_defaults_recover)]
     fn generic_defaults(&self, def: GenericDefId) -> Arc<[Binders<Ty>]>;
 
     #[salsa::invoke(InherentImpls::inherent_impls_in_crate_query)]
diff --git a/crates/hir_ty/src/lower.rs b/crates/hir_ty/src/lower.rs
index c99dd8d0a4..7fd46becdb 100644
--- a/crates/hir_ty/src/lower.rs
+++ b/crates/hir_ty/src/lower.rs
@@ -414,16 +414,17 @@ impl<'a> TyLoweringContext<'a> {
                     self.lower_trait_ref_from_resolved_path(trait_, resolved_segment, self_ty);
                 let ty = if remaining_segments.len() == 1 {
                     let segment = remaining_segments.first().unwrap();
-                    let found = self
-                        .db
-                        .trait_data(trait_ref.hir_trait_id())
-                        .associated_type_by_name(&segment.name);
+                    let found = associated_type_by_name_including_super_traits(
+                        self.db,
+                        trait_ref,
+                        &segment.name,
+                    );
                     match found {
-                        Some(associated_ty) => {
+                        Some((super_trait_ref, associated_ty)) => {
                             // FIXME handle type parameters on the segment
                             TyKind::Alias(AliasTy::Projection(ProjectionTy {
                                 associated_ty_id: to_assoc_type_id(associated_ty),
-                                substitution: trait_ref.substitution,
+                                substitution: super_trait_ref.substitution,
                             }))
                             .intern(&Interner)
                         }
@@ -1088,27 +1089,6 @@ pub(crate) fn generic_defaults_query(
     defaults
 }
 
-pub(crate) fn generic_defaults_recover(
-    db: &dyn HirDatabase,
-    _cycle: &[String],
-    def: &GenericDefId,
-) -> Arc<[Binders<Ty>]> {
-    let generic_params = generics(db.upcast(), *def);
-
-    // we still need one default per parameter
-    let defaults = generic_params
-        .iter()
-        .enumerate()
-        .map(|(idx, _)| {
-            let ty = TyKind::Error.intern(&Interner);
-
-            crate::make_only_type_binders(idx, ty)
-        })
-        .collect();
-
-    defaults
-}
-
 fn fn_sig_for_fn(db: &dyn HirDatabase, def: FunctionId) -> PolyFnSig {
     let data = db.function_data(def);
     let resolver = def.resolver(db.upcast());
diff --git a/crates/hir_ty/src/utils.rs b/crates/hir_ty/src/utils.rs
index 2f490fb920..2f04ee57ae 100644
--- a/crates/hir_ty/src/utils.rs
+++ b/crates/hir_ty/src/utils.rs
@@ -1,8 +1,6 @@
 //! Helper functions for working with def, which don't need to be a separate
 //! query, but can't be computed directly from `*Data` (ie, which need a `db`).
 
-use std::iter;
-
 use chalk_ir::{fold::Shift, BoundVar, DebruijnIndex};
 use hir_def::{
     db::DefDatabase,
@@ -16,12 +14,8 @@ use hir_def::{
     AssocContainerId, GenericDefId, Lookup, TraitId, TypeAliasId, TypeParamId,
 };
 use hir_expand::name::{name, Name};
-use rustc_hash::FxHashSet;
 
-use crate::{
-    db::HirDatabase, ChalkTraitId, Interner, Substitution, TraitRef, TraitRefExt, TyKind,
-    WhereClause,
-};
+use crate::{db::HirDatabase, Interner, Substitution, TraitRef, TraitRefExt, TyKind, WhereClause};
 
 fn direct_super_traits(db: &dyn DefDatabase, trait_: TraitId) -> Vec<TraitId> {
     let resolver = trait_.resolver(db);
@@ -108,35 +102,25 @@ pub fn all_super_traits(db: &dyn DefDatabase, trait_: TraitId) -> Vec<TraitId> {
 /// `all_super_traits` is that we keep track of type parameters; for example if
 /// we have `Self: Trait<u32, i32>` and `Trait<T, U>: OtherTrait<U>` we'll get
 /// `Self: OtherTrait<i32>`.
-pub(super) fn all_super_trait_refs(db: &dyn HirDatabase, trait_ref: TraitRef) -> SuperTraits {
-    SuperTraits { db, seen: iter::once(trait_ref.trait_id).collect(), stack: vec![trait_ref] }
-}
-
-pub(super) struct SuperTraits<'a> {
-    db: &'a dyn HirDatabase,
-    stack: Vec<TraitRef>,
-    seen: FxHashSet<ChalkTraitId>,
-}
+pub(super) fn all_super_trait_refs(db: &dyn HirDatabase, trait_ref: TraitRef) -> Vec<TraitRef> {
+    // FIXME: replace by Chalk's `super_traits`, maybe make this a query
 
-impl<'a> SuperTraits<'a> {
-    fn elaborate(&mut self, trait_ref: &TraitRef) {
-        let mut trait_refs = direct_super_trait_refs(self.db, trait_ref);
-        trait_refs.retain(|tr| !self.seen.contains(&tr.trait_id));
-        self.stack.extend(trait_refs);
-    }
-}
-
-impl<'a> Iterator for SuperTraits<'a> {
-    type Item = TraitRef;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        if let Some(next) = self.stack.pop() {
-            self.elaborate(&next);
-            Some(next)
-        } else {
-            None
+    // 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 = vec![trait_ref];
+    let mut i = 0;
+    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_trait_refs(db, t) {
+            if !result.iter().any(|tr| tr.trait_id == tt.trait_id) {
+                result.push(tt);
+            }
         }
+        i += 1;
     }
+    result
 }
 
 pub(super) fn associated_type_by_name_including_super_traits(
@@ -144,7 +128,7 @@ pub(super) fn associated_type_by_name_including_super_traits(
     trait_ref: TraitRef,
     name: &Name,
 ) -> Option<(TraitRef, TypeAliasId)> {
-    all_super_trait_refs(db, trait_ref).find_map(|t| {
+    all_super_trait_refs(db, trait_ref).into_iter().find_map(|t| {
         let assoc_type = db.trait_data(t.hir_trait_id()).associated_type_by_name(name)?;
         Some((t, assoc_type))
     })
diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs
index b14f908b75..1c911a8b2c 100644
--- a/crates/ide/src/diagnostics.rs
+++ b/crates/ide/src/diagnostics.rs
@@ -15,7 +15,6 @@ use hir::{
     diagnostics::{Diagnostic as _, DiagnosticCode, DiagnosticSinkBuilder},
     InFile, Semantics,
 };
-use ide_assists::AssistResolveStrategy;
 use ide_db::{base_db::SourceDatabase, RootDatabase};
 use itertools::Itertools;
 use rustc_hash::FxHashSet;
@@ -85,7 +84,7 @@ pub struct DiagnosticsConfig {
 pub(crate) fn diagnostics(
     db: &RootDatabase,
     config: &DiagnosticsConfig,
-    resolve: &AssistResolveStrategy,
+    resolve: bool,
     file_id: FileId,
 ) -> Vec<Diagnostic> {
     let _p = profile::span("diagnostics");
@@ -213,7 +212,7 @@ pub(crate) fn diagnostics(
 fn diagnostic_with_fix<D: DiagnosticWithFix>(
     d: &D,
     sema: &Semantics<RootDatabase>,
-    resolve: &AssistResolveStrategy,
+    resolve: bool,
 ) -> Diagnostic {
     Diagnostic::error(sema.diagnostics_display_range(d.display_source()).range, d.message())
         .with_fix(d.fix(&sema, resolve))
@@ -223,7 +222,7 @@ fn diagnostic_with_fix<D: DiagnosticWithFix>(
 fn warning_with_fix<D: DiagnosticWithFix>(
     d: &D,
     sema: &Semantics<RootDatabase>,
-    resolve: &AssistResolveStrategy,
+    resolve: bool,
 ) -> Diagnostic {
     Diagnostic::hint(sema.diagnostics_display_range(d.display_source()).range, d.message())
         .with_fix(d.fix(&sema, resolve))
@@ -300,7 +299,6 @@ fn unresolved_fix(id: &'static str, label: &str, target: TextRange) -> Assist {
 #[cfg(test)]
 mod tests {
     use expect_test::{expect, Expect};
-    use ide_assists::AssistResolveStrategy;
     use stdx::trim_indent;
     use test_utils::assert_eq_text;
 
@@ -316,11 +314,7 @@ mod tests {
 
         let (analysis, file_position) = fixture::position(ra_fixture_before);
         let diagnostic = analysis
-            .diagnostics(
-                &DiagnosticsConfig::default(),
-                AssistResolveStrategy::All,
-                file_position.file_id,
-            )
+            .diagnostics(&DiagnosticsConfig::default(), true, file_position.file_id)
             .unwrap()
             .pop()
             .unwrap();
@@ -349,11 +343,7 @@ mod tests {
     fn check_no_fix(ra_fixture: &str) {
         let (analysis, file_position) = fixture::position(ra_fixture);
         let diagnostic = analysis
-            .diagnostics(
-                &DiagnosticsConfig::default(),
-                AssistResolveStrategy::All,
-                file_position.file_id,
-            )
+            .diagnostics(&DiagnosticsConfig::default(), true, file_position.file_id)
             .unwrap()
             .pop()
             .unwrap();
@@ -367,9 +357,7 @@ mod tests {
         let diagnostics = files
             .into_iter()
             .flat_map(|file_id| {
-                analysis
-                    .diagnostics(&DiagnosticsConfig::default(), AssistResolveStrategy::All, file_id)
-                    .unwrap()
+                analysis.diagnostics(&DiagnosticsConfig::default(), true, file_id).unwrap()
             })
             .collect::<Vec<_>>();
         assert_eq!(diagnostics.len(), 0, "unexpected diagnostics:\n{:#?}", diagnostics);
@@ -377,9 +365,8 @@ mod tests {
 
     fn check_expect(ra_fixture: &str, expect: Expect) {
         let (analysis, file_id) = fixture::file(ra_fixture);
-        let diagnostics = analysis
-            .diagnostics(&DiagnosticsConfig::default(), AssistResolveStrategy::All, file_id)
-            .unwrap();
+        let diagnostics =
+            analysis.diagnostics(&DiagnosticsConfig::default(), true, file_id).unwrap();
         expect.assert_debug_eq(&diagnostics)
     }
 
@@ -924,13 +911,11 @@ struct Foo {
 
         let (analysis, file_id) = fixture::file(r#"mod foo;"#);
 
-        let diagnostics =
-            analysis.diagnostics(&config, AssistResolveStrategy::All, file_id).unwrap();
+        let diagnostics = analysis.diagnostics(&config, true, file_id).unwrap();
         assert!(diagnostics.is_empty());
 
-        let diagnostics = analysis
-            .diagnostics(&DiagnosticsConfig::default(), AssistResolveStrategy::All, file_id)
-            .unwrap();
+        let diagnostics =
+            analysis.diagnostics(&DiagnosticsConfig::default(), true, file_id).unwrap();
         assert!(!diagnostics.is_empty());
     }
 
@@ -1037,11 +1022,7 @@ impl TestStruct {
 
         let (analysis, file_position) = fixture::position(input);
         let diagnostics = analysis
-            .diagnostics(
-                &DiagnosticsConfig::default(),
-                AssistResolveStrategy::All,
-                file_position.file_id,
-            )
+            .diagnostics(&DiagnosticsConfig::default(), true, file_position.file_id)
             .unwrap();
         assert_eq!(diagnostics.len(), 1);
 
diff --git a/crates/ide/src/diagnostics/fixes.rs b/crates/ide/src/diagnostics/fixes.rs
index 15821500f1..7be8b34593 100644
--- a/crates/ide/src/diagnostics/fixes.rs
+++ b/crates/ide/src/diagnostics/fixes.rs
@@ -8,7 +8,6 @@ use hir::{
     },
     HasSource, HirDisplay, InFile, Semantics, VariantDef,
 };
-use ide_assists::AssistResolveStrategy;
 use ide_db::{
     base_db::{AnchoredPathBuf, FileId},
     source_change::{FileSystemEdit, SourceChange},
@@ -36,19 +35,11 @@ pub(crate) trait DiagnosticWithFix: Diagnostic {
     ///
     /// If `resolve` is false, the edit will be computed later, on demand, and
     /// can be omitted.
-    fn fix(
-        &self,
-        sema: &Semantics<RootDatabase>,
-        _resolve: &AssistResolveStrategy,
-    ) -> Option<Assist>;
+    fn fix(&self, sema: &Semantics<RootDatabase>, _resolve: bool) -> Option<Assist>;
 }
 
 impl DiagnosticWithFix for UnresolvedModule {
-    fn fix(
-        &self,
-        sema: &Semantics<RootDatabase>,
-        _resolve: &AssistResolveStrategy,
-    ) -> Option<Assist> {
+    fn fix(&self, sema: &Semantics<RootDatabase>, _resolve: bool) -> Option<Assist> {
         let root = sema.db.parse_or_expand(self.file)?;
         let unresolved_module = self.decl.to_node(&root);
         Some(fix(
@@ -68,11 +59,7 @@ impl DiagnosticWithFix for UnresolvedModule {
 }
 
 impl DiagnosticWithFix for NoSuchField {
-    fn fix(
-        &self,
-        sema: &Semantics<RootDatabase>,
-        _resolve: &AssistResolveStrategy,
-    ) -> Option<Assist> {
+    fn fix(&self, sema: &Semantics<RootDatabase>, _resolve: bool) -> Option<Assist> {
         let root = sema.db.parse_or_expand(self.file)?;
         missing_record_expr_field_fix(
             &sema,
@@ -83,11 +70,7 @@ impl DiagnosticWithFix for NoSuchField {
 }
 
 impl DiagnosticWithFix for MissingFields {
-    fn fix(
-        &self,
-        sema: &Semantics<RootDatabase>,
-        _resolve: &AssistResolveStrategy,
-    ) -> Option<Assist> {
+    fn fix(&self, sema: &Semantics<RootDatabase>, _resolve: bool) -> Option<Assist> {
         // Note that although we could add a diagnostics to
         // fill the missing tuple field, e.g :
         // `struct A(usize);`
@@ -123,11 +106,7 @@ impl DiagnosticWithFix for MissingFields {
 }
 
 impl DiagnosticWithFix for MissingOkOrSomeInTailExpr {
-    fn fix(
-        &self,
-        sema: &Semantics<RootDatabase>,
-        _resolve: &AssistResolveStrategy,
-    ) -> Option<Assist> {
+    fn fix(&self, sema: &Semantics<RootDatabase>, _resolve: bool) -> Option<Assist> {
         let root = sema.db.parse_or_expand(self.file)?;
         let tail_expr = self.expr.to_node(&root);
         let tail_expr_range = tail_expr.syntax().text_range();
@@ -140,11 +119,7 @@ impl DiagnosticWithFix for MissingOkOrSomeInTailExpr {
 }
 
 impl DiagnosticWithFix for RemoveThisSemicolon {
-    fn fix(
-        &self,
-        sema: &Semantics<RootDatabase>,
-        _resolve: &AssistResolveStrategy,
-    ) -> Option<Assist> {
+    fn fix(&self, sema: &Semantics<RootDatabase>, _resolve: bool) -> Option<Assist> {
         let root = sema.db.parse_or_expand(self.file)?;
 
         let semicolon = self
@@ -164,11 +139,7 @@ impl DiagnosticWithFix for RemoveThisSemicolon {
 }
 
 impl DiagnosticWithFix for IncorrectCase {
-    fn fix(
-        &self,
-        sema: &Semantics<RootDatabase>,
-        resolve: &AssistResolveStrategy,
-    ) -> Option<Assist> {
+    fn fix(&self, sema: &Semantics<RootDatabase>, resolve: bool) -> Option<Assist> {
         let root = sema.db.parse_or_expand(self.file)?;
         let name_node = self.ident.to_node(&root);
 
@@ -178,7 +149,7 @@ impl DiagnosticWithFix for IncorrectCase {
 
         let label = format!("Rename to {}", self.suggested_text);
         let mut res = unresolved_fix("change_case", &label, frange.range);
-        if resolve.should_resolve(&res.id) {
+        if resolve {
             let source_change = rename_with_semantics(sema, file_position, &self.suggested_text);
             res.source_change = Some(source_change.ok().unwrap_or_default());
         }
@@ -188,11 +159,7 @@ impl DiagnosticWithFix for IncorrectCase {
 }
 
 impl DiagnosticWithFix for ReplaceFilterMapNextWithFindMap {
-    fn fix(
-        &self,
-        sema: &Semantics<RootDatabase>,
-        _resolve: &AssistResolveStrategy,
-    ) -> Option<Assist> {
+    fn fix(&self, sema: &Semantics<RootDatabase>, _resolve: bool) -> Option<Assist> {
         let root = sema.db.parse_or_expand(self.file)?;
         let next_expr = self.next_expr.to_node(&root);
         let next_call = ast::MethodCallExpr::cast(next_expr.syntax().clone())?;
diff --git a/crates/ide/src/diagnostics/unlinked_file.rs b/crates/ide/src/diagnostics/unlinked_file.rs
index 93fd25dea4..7d39f4fbe4 100644
--- a/crates/ide/src/diagnostics/unlinked_file.rs
+++ b/crates/ide/src/diagnostics/unlinked_file.rs
@@ -5,7 +5,6 @@ use hir::{
     diagnostics::{Diagnostic, DiagnosticCode},
     InFile,
 };
-use ide_assists::AssistResolveStrategy;
 use ide_db::{
     base_db::{FileId, FileLoader, SourceDatabase, SourceDatabaseExt},
     source_change::SourceChange,
@@ -51,11 +50,7 @@ impl Diagnostic for UnlinkedFile {
 }
 
 impl DiagnosticWithFix for UnlinkedFile {
-    fn fix(
-        &self,
-        sema: &hir::Semantics<RootDatabase>,
-        _resolve: &AssistResolveStrategy,
-    ) -> Option<Assist> {
+    fn fix(&self, sema: &hir::Semantics<RootDatabase>, _resolve: bool) -> Option<Assist> {
         // If there's an existing module that could add a `mod` item to include the unlinked file,
         // suggest that as a fix.
 
diff --git a/crates/ide/src/folding_ranges.rs b/crates/ide/src/folding_ranges.rs
old mode 100644
new mode 100755
index 2b9ed123c1..a039887783
--- a/crates/ide/src/folding_ranges.rs
+++ b/crates/ide/src/folding_ranges.rs
@@ -20,6 +20,7 @@ pub enum FoldKind {
     Consts,
     Statics,
     Array,
+    WhereClause,
 }
 
 #[derive(Debug)]
@@ -109,6 +110,13 @@ pub(crate) fn folding_ranges(file: &SourceFile) -> Vec<Fold> {
                         res.push(Fold { range, kind: FoldKind::Statics })
                     }
                 }
+
+                // Fold where clause
+                if node.kind() == WHERE_CLAUSE {
+                    if let Some(range) = fold_range_for_where_clause(&node) {
+                        res.push(Fold { range, kind: FoldKind::WhereClause })
+                    }
+                }
             }
         }
     }
@@ -241,6 +249,23 @@ fn contiguous_range_for_comment(
     }
 }
 
+fn fold_range_for_where_clause(node: &SyntaxNode) -> Option<TextRange> {
+    let first_where_pred = node.first_child();
+    let last_where_pred = node.last_child();
+
+    if first_where_pred != last_where_pred {
+        let mut it = node.descendants_with_tokens();
+        if let (Some(_where_clause), Some(where_kw), Some(last_comma)) =
+            (it.next(), it.next(), it.last())
+        {
+            let start = where_kw.text_range().end();
+            let end = last_comma.text_range().end();
+            return Some(TextRange::new(start, end));
+        }
+    }
+    None
+}
+
 #[cfg(test)]
 mod tests {
     use test_utils::extract_tags;
@@ -272,6 +297,7 @@ mod tests {
                 FoldKind::Consts => "consts",
                 FoldKind::Statics => "statics",
                 FoldKind::Array => "array",
+                FoldKind::WhereClause => "whereclause",
             };
             assert_eq!(kind, &attr.unwrap());
         }
@@ -513,4 +539,23 @@ static SECOND_STATIC: &str = "second";</fold>
             "#,
         )
     }
+
+    #[test]
+    fn fold_where_clause() {
+        // fold multi-line and don't fold single line.
+        check(
+            r#"
+fn foo()
+where<fold whereclause>
+    A: Foo,
+    B: Foo,
+    C: Foo,
+    D: Foo,</fold> {}
+
+fn bar()
+where
+    A: Bar, {}
+"#,
+        )
+    }
 }
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs
index 8e5b720449..99e45633e4 100644
--- a/crates/ide/src/lib.rs
+++ b/crates/ide/src/lib.rs
@@ -87,9 +87,7 @@ pub use crate::{
     },
 };
 pub use hir::{Documentation, Semantics};
-pub use ide_assists::{
-    Assist, AssistConfig, AssistId, AssistKind, AssistResolveStrategy, SingleResolve,
-};
+pub use ide_assists::{Assist, AssistConfig, AssistId, AssistKind};
 pub use ide_completion::{
     CompletionConfig, CompletionItem, CompletionItemKind, CompletionRelevance, ImportEdit,
     InsertTextFormat,
@@ -520,13 +518,12 @@ impl Analysis {
     pub fn assists(
         &self,
         config: &AssistConfig,
-        resolve: AssistResolveStrategy,
+        resolve: bool,
         frange: FileRange,
     ) -> Cancelable<Vec<Assist>> {
         self.with_db(|db| {
-            let ssr_assists = ssr::ssr_assists(db, &resolve, frange);
             let mut acc = Assist::get(db, config, resolve, frange);
-            acc.extend(ssr_assists.into_iter());
+            ssr::add_ssr_assist(db, &mut acc, resolve, frange);
             acc
         })
     }
@@ -535,10 +532,10 @@ impl Analysis {
     pub fn diagnostics(
         &self,
         config: &DiagnosticsConfig,
-        resolve: AssistResolveStrategy,
+        resolve: bool,
         file_id: FileId,
     ) -> Cancelable<Vec<Diagnostic>> {
-        self.with_db(|db| diagnostics::diagnostics(db, config, &resolve, file_id))
+        self.with_db(|db| diagnostics::diagnostics(db, config, resolve, file_id))
     }
 
     /// Convenience function to return assists + quick fixes for diagnostics
@@ -546,7 +543,7 @@ impl Analysis {
         &self,
         assist_config: &AssistConfig,
         diagnostics_config: &DiagnosticsConfig,
-        resolve: AssistResolveStrategy,
+        resolve: bool,
         frange: FileRange,
     ) -> Cancelable<Vec<Assist>> {
         let include_fixes = match &assist_config.allowed {
@@ -555,21 +552,17 @@ impl Analysis {
         };
 
         self.with_db(|db| {
-            let ssr_assists = ssr::ssr_assists(db, &resolve, frange);
-            let diagnostic_assists = if include_fixes {
-                diagnostics::diagnostics(db, diagnostics_config, &resolve, frange.file_id)
-                    .into_iter()
-                    .filter_map(|it| it.fix)
-                    .filter(|it| it.target.intersect(frange.range).is_some())
-                    .collect()
-            } else {
-                Vec::new()
-            };
-
             let mut res = Assist::get(db, assist_config, resolve, frange);
-            res.extend(ssr_assists.into_iter());
-            res.extend(diagnostic_assists.into_iter());
-
+            ssr::add_ssr_assist(db, &mut res, resolve, frange);
+
+            if include_fixes {
+                res.extend(
+                    diagnostics::diagnostics(db, diagnostics_config, resolve, frange.file_id)
+                        .into_iter()
+                        .filter_map(|it| it.fix)
+                        .filter(|it| it.target.intersect(frange.range).is_some()),
+                );
+            }
             res
         })
     }
diff --git a/crates/ide/src/prime_caches.rs b/crates/ide/src/prime_caches.rs
index d912a01b84..03597f5078 100644
--- a/crates/ide/src/prime_caches.rs
+++ b/crates/ide/src/prime_caches.rs
@@ -27,7 +27,6 @@ pub(crate) fn prime_caches(db: &RootDatabase, cb: &(dyn Fn(PrimeCachesProgress)
     let topo = &graph.crates_in_topological_order();
 
     cb(PrimeCachesProgress::Started);
-    // Take care to emit the finish signal  even when the computation is canceled.
     let _d = stdx::defer(|| cb(PrimeCachesProgress::Finished));
 
     // FIXME: This would be easy to parallelize, since it's in the ideal ordering for that.
diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs
index f76715d84f..3eb9e27eef 100644
--- a/crates/ide/src/runnables.rs
+++ b/crates/ide/src/runnables.rs
@@ -304,11 +304,11 @@ fn module_def_doctest(sema: &Semantics<RootDatabase>, def: hir::ModuleDef) -> Op
                         let name = adt.name(sema.db);
                         let idx = path.rfind(':').map_or(0, |idx| idx + 1);
                         let (prefix, suffix) = path.split_at(idx);
-                        let mut ty_args = ty.type_arguments().peekable();
-                        let params = if ty_args.peek().is_some() {
+                        let mut ty_params = ty.type_parameters().peekable();
+                        let params = if ty_params.peek().is_some() {
                             format!(
                                 "<{}>",
-                                ty_args.format_with(", ", |ty, cb| cb(&ty.display(sema.db)))
+                                ty_params.format_with(", ", |ty, cb| cb(&ty.display(sema.db)))
                             )
                         } else {
                             String::new()
diff --git a/crates/ide/src/ssr.rs b/crates/ide/src/ssr.rs
index 57ec802614..f3638d928c 100644
--- a/crates/ide/src/ssr.rs
+++ b/crates/ide/src/ssr.rs
@@ -2,23 +2,18 @@
 //! assist in ide_assists because that would require the ide_assists crate
 //! depend on the ide_ssr crate.
 
-use ide_assists::{Assist, AssistId, AssistKind, AssistResolveStrategy, GroupLabel};
+use ide_assists::{Assist, AssistId, AssistKind, GroupLabel};
 use ide_db::{base_db::FileRange, label::Label, source_change::SourceChange, RootDatabase};
 
-pub(crate) fn ssr_assists(
+pub(crate) fn add_ssr_assist(
     db: &RootDatabase,
-    resolve: &AssistResolveStrategy,
+    base: &mut Vec<Assist>,
+    resolve: bool,
     frange: FileRange,
-) -> Vec<Assist> {
-    let mut ssr_assists = Vec::with_capacity(2);
+) -> Option<()> {
+    let (match_finder, comment_range) = ide_ssr::ssr_from_comment(db, frange)?;
 
-    let (match_finder, comment_range) = match ide_ssr::ssr_from_comment(db, frange) {
-        Some(ssr_data) => ssr_data,
-        None => return ssr_assists,
-    };
-    let id = AssistId("ssr", AssistKind::RefactorRewrite);
-
-    let (source_change_for_file, source_change_for_workspace) = if resolve.should_resolve(&id) {
+    let (source_change_for_file, source_change_for_workspace) = if resolve {
         let edits = match_finder.edits();
 
         let source_change_for_file = {
@@ -40,17 +35,16 @@ pub(crate) fn ssr_assists(
 
     for (label, source_change) in assists.into_iter() {
         let assist = Assist {
-            id,
+            id: AssistId("ssr", AssistKind::RefactorRewrite),
             label: Label::new(label),
             group: Some(GroupLabel("Apply SSR".into())),
             target: comment_range,
             source_change,
         };
 
-        ssr_assists.push(assist);
+        base.push(assist);
     }
-
-    ssr_assists
+    Some(())
 }
 
 #[cfg(test)]
@@ -58,7 +52,7 @@ mod tests {
     use std::sync::Arc;
 
     use expect_test::expect;
-    use ide_assists::{Assist, AssistResolveStrategy};
+    use ide_assists::Assist;
     use ide_db::{
         base_db::{fixture::WithFixture, salsa::Durability, FileRange},
         symbol_index::SymbolsDatabase,
@@ -66,14 +60,24 @@ mod tests {
     };
     use rustc_hash::FxHashSet;
 
-    use super::ssr_assists;
+    use super::add_ssr_assist;
 
-    fn get_assists(ra_fixture: &str, resolve: AssistResolveStrategy) -> Vec<Assist> {
+    fn get_assists(ra_fixture: &str, resolve: bool) -> Vec<Assist> {
         let (mut db, file_id, range_or_offset) = RootDatabase::with_range_or_offset(ra_fixture);
         let mut local_roots = FxHashSet::default();
         local_roots.insert(ide_db::base_db::fixture::WORKSPACE);
         db.set_local_roots_with_durability(Arc::new(local_roots), Durability::HIGH);
-        ssr_assists(&db, &resolve, FileRange { file_id, range: range_or_offset.into() })
+
+        let mut assists = vec![];
+
+        add_ssr_assist(
+            &db,
+            &mut assists,
+            resolve,
+            FileRange { file_id, range: range_or_offset.into() },
+        );
+
+        assists
     }
 
     #[test]
@@ -84,13 +88,16 @@ mod tests {
             // This is foo $0
             fn foo() {}
             "#;
-        let assists = get_assists(ra_fixture, AssistResolveStrategy::All);
+        let resolve = true;
+
+        let assists = get_assists(ra_fixture, resolve);
 
         assert_eq!(0, assists.len());
     }
 
     #[test]
     fn resolve_edits_true() {
+        let resolve = true;
         let assists = get_assists(
             r#"
             //- /lib.rs
@@ -102,7 +109,7 @@ mod tests {
             //- /bar.rs
             fn bar() { 2 }
             "#,
-            AssistResolveStrategy::All,
+            resolve,
         );
 
         assert_eq!(2, assists.len());
@@ -193,6 +200,7 @@ mod tests {
 
     #[test]
     fn resolve_edits_false() {
+        let resolve = false;
         let assists = get_assists(
             r#"
             //- /lib.rs
@@ -204,7 +212,7 @@ mod tests {
             //- /bar.rs
             fn bar() { 2 }
             "#,
-            AssistResolveStrategy::None,
+            resolve,
         );
 
         assert_eq!(2, assists.len());
diff --git a/crates/ide_assists/src/assist_context.rs b/crates/ide_assists/src/assist_context.rs
index 1129399484..8714e4978c 100644
--- a/crates/ide_assists/src/assist_context.rs
+++ b/crates/ide_assists/src/assist_context.rs
@@ -19,9 +19,7 @@ use syntax::{
 };
 use text_edit::{TextEdit, TextEditBuilder};
 
-use crate::{
-    assist_config::AssistConfig, Assist, AssistId, AssistKind, AssistResolveStrategy, GroupLabel,
-};
+use crate::{assist_config::AssistConfig, Assist, AssistId, AssistKind, GroupLabel};
 
 /// `AssistContext` allows to apply an assist or check if it could be applied.
 ///
@@ -107,14 +105,14 @@ impl<'a> AssistContext<'a> {
 }
 
 pub(crate) struct Assists {
+    resolve: bool,
     file: FileId,
-    resolve: AssistResolveStrategy,
     buf: Vec<Assist>,
     allowed: Option<Vec<AssistKind>>,
 }
 
 impl Assists {
-    pub(crate) fn new(ctx: &AssistContext, resolve: AssistResolveStrategy) -> Assists {
+    pub(crate) fn new(ctx: &AssistContext, resolve: bool) -> Assists {
         Assists {
             resolve,
             file: ctx.frange.file_id,
@@ -160,7 +158,7 @@ impl Assists {
     }
 
     fn add_impl(&mut self, mut assist: Assist, f: impl FnOnce(&mut AssistBuilder)) -> Option<()> {
-        let source_change = if self.resolve.should_resolve(&assist.id) {
+        let source_change = if self.resolve {
             let mut builder = AssistBuilder::new(self.file);
             f(&mut builder);
             Some(builder.finish())
@@ -187,29 +185,7 @@ pub(crate) struct AssistBuilder {
     source_change: SourceChange,
 
     /// Maps the original, immutable `SyntaxNode` to a `clone_for_update` twin.
-    mutated_tree: Option<TreeMutator>,
-}
-
-pub(crate) struct TreeMutator {
-    immutable: SyntaxNode,
-    mutable_clone: SyntaxNode,
-}
-
-impl TreeMutator {
-    pub(crate) fn new(immutable: &SyntaxNode) -> TreeMutator {
-        let immutable = immutable.ancestors().last().unwrap();
-        let mutable_clone = immutable.clone_for_update();
-        TreeMutator { immutable, mutable_clone }
-    }
-
-    pub(crate) fn make_mut<N: AstNode>(&self, node: &N) -> N {
-        N::cast(self.make_syntax_mut(node.syntax())).unwrap()
-    }
-
-    pub(crate) fn make_syntax_mut(&self, node: &SyntaxNode) -> SyntaxNode {
-        let ptr = SyntaxNodePtr::new(node);
-        ptr.to_node(&self.mutable_clone)
-    }
+    mutated_tree: Option<(SyntaxNode, SyntaxNode)>,
 }
 
 impl AssistBuilder {
@@ -228,8 +204,8 @@ impl AssistBuilder {
     }
 
     fn commit(&mut self) {
-        if let Some(tm) = self.mutated_tree.take() {
-            algo::diff(&tm.immutable, &tm.mutable_clone).into_text_edit(&mut self.edit)
+        if let Some((old, new)) = self.mutated_tree.take() {
+            algo::diff(&old, &new).into_text_edit(&mut self.edit)
         }
 
         let edit = mem::take(&mut self.edit).finish();
@@ -252,7 +228,16 @@ impl AssistBuilder {
     /// phase, and then get their mutable couterparts using `make_mut` in the
     /// mutable state.
     pub(crate) fn make_mut(&mut self, node: SyntaxNode) -> SyntaxNode {
-        self.mutated_tree.get_or_insert_with(|| TreeMutator::new(&node)).make_syntax_mut(&node)
+        let root = &self
+            .mutated_tree
+            .get_or_insert_with(|| {
+                let immutable = node.ancestors().last().unwrap();
+                let mutable = immutable.clone_for_update();
+                (immutable, mutable)
+            })
+            .1;
+        let ptr = SyntaxNodePtr::new(&&node);
+        ptr.to_node(root)
     }
 
     /// Remove specified `range` of text.
diff --git a/crates/ide_assists/src/handlers/extract_function.rs b/crates/ide_assists/src/handlers/extract_function.rs
index 93b28370ca..5f80a40c8a 100644
--- a/crates/ide_assists/src/handlers/extract_function.rs
+++ b/crates/ide_assists/src/handlers/extract_function.rs
@@ -16,13 +16,12 @@ use syntax::{
         edit::{AstNodeEdit, IndentLevel},
         AstNode,
     },
-    ted,
     SyntaxKind::{self, BLOCK_EXPR, BREAK_EXPR, COMMENT, PATH_EXPR, RETURN_EXPR},
     SyntaxNode, SyntaxToken, TextRange, TextSize, TokenAtOffset, WalkEvent, T,
 };
 
 use crate::{
-    assist_context::{AssistContext, Assists, TreeMutator},
+    assist_context::{AssistContext, Assists},
     AssistId,
 };
 
@@ -1184,7 +1183,7 @@ fn make_ret_ty(ctx: &AssistContext, module: hir::Module, fun: &Function) -> Opti
         }
         FlowHandler::Try { kind: TryKind::Result { ty: parent_ret_ty } } => {
             let handler_ty = parent_ret_ty
-                .type_arguments()
+                .type_parameters()
                 .nth(1)
                 .map(|ty| make_ty(&ty, ctx, module))
                 .unwrap_or_else(make::ty_unit);
@@ -1367,10 +1366,7 @@ fn rewrite_body_segment(
 
 /// change all usages to account for added `&`/`&mut` for some params
 fn fix_param_usages(ctx: &AssistContext, params: &[Param], syntax: &SyntaxNode) -> SyntaxNode {
-    let mut usages_for_param: Vec<(&Param, Vec<ast::Expr>)> = Vec::new();
-
-    let tm = TreeMutator::new(syntax);
-
+    let mut rewriter = SyntaxRewriter::default();
     for param in params {
         if !param.kind().is_ref() {
             continue;
@@ -1380,39 +1376,30 @@ fn fix_param_usages(ctx: &AssistContext, params: &[Param], syntax: &SyntaxNode)
         let usages = usages
             .iter()
             .filter(|reference| syntax.text_range().contains_range(reference.range))
-            .filter_map(|reference| path_element_of_reference(syntax, reference))
-            .map(|expr| tm.make_mut(&expr));
-
-        usages_for_param.push((param, usages.collect()));
-    }
-
-    let res = tm.make_syntax_mut(syntax);
-
-    for (param, usages) in usages_for_param {
-        for usage in usages {
-            match usage.syntax().ancestors().skip(1).find_map(ast::Expr::cast) {
+            .filter_map(|reference| path_element_of_reference(syntax, reference));
+        for path in usages {
+            match path.syntax().ancestors().skip(1).find_map(ast::Expr::cast) {
                 Some(ast::Expr::MethodCallExpr(_)) | Some(ast::Expr::FieldExpr(_)) => {
                     // do nothing
                 }
                 Some(ast::Expr::RefExpr(node))
                     if param.kind() == ParamKind::MutRef && node.mut_token().is_some() =>
                 {
-                    ted::replace(node.syntax(), node.expr().unwrap().syntax());
+                    rewriter.replace_ast(&node.clone().into(), &node.expr().unwrap());
                 }
                 Some(ast::Expr::RefExpr(node))
                     if param.kind() == ParamKind::SharedRef && node.mut_token().is_none() =>
                 {
-                    ted::replace(node.syntax(), node.expr().unwrap().syntax());
+                    rewriter.replace_ast(&node.clone().into(), &node.expr().unwrap());
                 }
                 Some(_) | None => {
-                    let p = &make::expr_prefix(T![*], usage.clone()).clone_for_update();
-                    ted::replace(usage.syntax(), p.syntax())
+                    rewriter.replace_ast(&path, &make::expr_prefix(T![*], path.clone()));
                 }
-            }
+            };
         }
     }
 
-    res
+    rewriter.rewrite(syntax)
 }
 
 fn update_external_control_flow(handler: &FlowHandler, syntax: &SyntaxNode) -> SyntaxNode {
diff --git a/crates/ide_assists/src/handlers/generate_from_impl_for_enum.rs b/crates/ide_assists/src/handlers/generate_from_impl_for_enum.rs
index ce6998d828..c13c6eebe8 100644
--- a/crates/ide_assists/src/handlers/generate_from_impl_for_enum.rs
+++ b/crates/ide_assists/src/handlers/generate_from_impl_for_enum.rs
@@ -91,7 +91,7 @@ fn existing_from_impl(
 
     let enum_type = enum_.ty(sema.db);
 
-    let wrapped_type = variant.fields(sema.db).get(0)?.ty(sema.db);
+    let wrapped_type = variant.fields(sema.db).get(0)?.signature_ty(sema.db);
 
     if enum_type.impls_trait(sema.db, from_trait, &[wrapped_type]) {
         Some(())
diff --git a/crates/ide_assists/src/handlers/reorder_impl.rs b/crates/ide_assists/src/handlers/reorder_impl.rs
index 54a9a468ec..72d8892481 100644
--- a/crates/ide_assists/src/handlers/reorder_impl.rs
+++ b/crates/ide_assists/src/handlers/reorder_impl.rs
@@ -79,12 +79,9 @@ pub(crate) fn reorder_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
         "Sort methods",
         target,
         |builder| {
-            let methods =
-                methods.into_iter().map(|fn_| builder.make_ast_mut(fn_)).collect::<Vec<_>>();
-            methods
-                .into_iter()
-                .zip(sorted)
-                .for_each(|(old, new)| ted::replace(old.syntax(), new.clone_for_update().syntax()));
+            methods.into_iter().zip(sorted).for_each(|(old, new)| {
+                ted::replace(builder.make_ast_mut(old).syntax(), new.clone_for_update().syntax())
+            });
         },
     )
 }
@@ -163,7 +160,7 @@ $0impl Bar for Foo {}
     }
 
     #[test]
-    fn reorder_impl_trait_functions() {
+    fn reorder_impl_trait_methods() {
         check_assist(
             reorder_impl,
             r#"
@@ -200,33 +197,4 @@ impl Bar for Foo {
         "#,
         )
     }
-
-    #[test]
-    fn reorder_impl_trait_methods_uneven_ident_lengths() {
-        check_assist(
-            reorder_impl,
-            r#"
-trait Bar {
-    fn foo(&mut self) {}
-    fn fooo(&mut self) {}
-}
-
-struct Foo;
-impl Bar for Foo {
-    fn fooo(&mut self) {}
-    fn foo(&mut self) {$0}
-}"#,
-            r#"
-trait Bar {
-    fn foo(&mut self) {}
-    fn fooo(&mut self) {}
-}
-
-struct Foo;
-impl Bar for Foo {
-    fn foo(&mut self) {}
-    fn fooo(&mut self) {}
-}"#,
-        )
-    }
 }
diff --git a/crates/ide_assists/src/lib.rs b/crates/ide_assists/src/lib.rs
index 2e0c58504c..88ae5c9a93 100644
--- a/crates/ide_assists/src/lib.rs
+++ b/crates/ide_assists/src/lib.rs
@@ -17,8 +17,6 @@ mod tests;
 pub mod utils;
 pub mod ast_transform;
 
-use std::str::FromStr;
-
 use hir::Semantics;
 use ide_db::base_db::FileRange;
 use ide_db::{label::Label, source_change::SourceChange, RootDatabase};
@@ -58,35 +56,6 @@ impl AssistKind {
             _ => return false,
         }
     }
-
-    pub fn name(&self) -> &str {
-        match self {
-            AssistKind::None => "None",
-            AssistKind::QuickFix => "QuickFix",
-            AssistKind::Generate => "Generate",
-            AssistKind::Refactor => "Refactor",
-            AssistKind::RefactorExtract => "RefactorExtract",
-            AssistKind::RefactorInline => "RefactorInline",
-            AssistKind::RefactorRewrite => "RefactorRewrite",
-        }
-    }
-}
-
-impl FromStr for AssistKind {
-    type Err = String;
-
-    fn from_str(s: &str) -> Result<Self, Self::Err> {
-        match s {
-            "None" => Ok(AssistKind::None),
-            "QuickFix" => Ok(AssistKind::QuickFix),
-            "Generate" => Ok(AssistKind::Generate),
-            "Refactor" => Ok(AssistKind::Refactor),
-            "RefactorExtract" => Ok(AssistKind::RefactorExtract),
-            "RefactorInline" => Ok(AssistKind::RefactorInline),
-            "RefactorRewrite" => Ok(AssistKind::RefactorRewrite),
-            unknown => Err(format!("Unknown AssistKind: '{}'", unknown)),
-        }
-    }
 }
 
 /// Unique identifier of the assist, should not be shown to the user
@@ -94,41 +63,6 @@ impl FromStr for AssistKind {
 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
 pub struct AssistId(pub &'static str, pub AssistKind);
 
-/// A way to control how many asssist to resolve during the assist resolution.
-/// When an assist is resolved, its edits are calculated that might be costly to always do by default.
-#[derive(Debug)]
-pub enum AssistResolveStrategy {
-    /// No assists should be resolved.
-    None,
-    /// All assists should be resolved.
-    All,
-    /// Only a certain assist should be resolved.
-    Single(SingleResolve),
-}
-
-/// Hold the [`AssistId`] data of a certain assist to resolve.
-/// The original id object cannot be used due to a `'static` lifetime
-/// and the requirement to construct this struct dynamically during the resolve handling.
-#[derive(Debug)]
-pub struct SingleResolve {
-    /// The id of the assist.
-    pub assist_id: String,
-    // The kind of the assist.
-    pub assist_kind: AssistKind,
-}
-
-impl AssistResolveStrategy {
-    pub fn should_resolve(&self, id: &AssistId) -> bool {
-        match self {
-            AssistResolveStrategy::None => false,
-            AssistResolveStrategy::All => true,
-            AssistResolveStrategy::Single(single_resolve) => {
-                single_resolve.assist_id == id.0 && single_resolve.assist_kind == id.1
-            }
-        }
-    }
-}
-
 #[derive(Clone, Debug)]
 pub struct GroupLabel(pub String);
 
@@ -157,7 +91,7 @@ impl Assist {
     pub fn get(
         db: &RootDatabase,
         config: &AssistConfig,
-        resolve: AssistResolveStrategy,
+        resolve: bool,
         range: FileRange,
     ) -> Vec<Assist> {
         let sema = Semantics::new(db);
diff --git a/crates/ide_assists/src/utils/suggest_name.rs b/crates/ide_assists/src/utils/suggest_name.rs
index deafcd630e..533624c1fd 100644
--- a/crates/ide_assists/src/utils/suggest_name.rs
+++ b/crates/ide_assists/src/utils/suggest_name.rs
@@ -227,7 +227,7 @@ fn name_of_type(ty: &hir::Type, db: &RootDatabase) -> Option<String> {
         let name = adt.name(db).to_string();
 
         if WRAPPER_TYPES.contains(&name.as_str()) {
-            let inner_ty = ty.type_arguments().next()?;
+            let inner_ty = ty.type_parameters().next()?;
             return name_of_type(&inner_ty, db);
         }
 
diff --git a/crates/ide_completion/src/completions/postfix/format_like.rs b/crates/ide_completion/src/completions/postfix/format_like.rs
index 0dcb3e8988..e86ffa8f81 100644
--- a/crates/ide_completion/src/completions/postfix/format_like.rs
+++ b/crates/ide_completion/src/completions/postfix/format_like.rs
@@ -1,4 +1,4 @@
-// Feature: Format String Completion
+// Feature: Format String Completion.
 //
 // `"Result {result} is {2 + 2}"` is expanded to the `"Result {} is {}", result, 2 + 2`.
 //
diff --git a/crates/ide_completion/src/completions/qualified_path.rs b/crates/ide_completion/src/completions/qualified_path.rs
index eedb44873f..969249df60 100644
--- a/crates/ide_completion/src/completions/qualified_path.rs
+++ b/crates/ide_completion/src/completions/qualified_path.rs
@@ -52,24 +52,18 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
         | PathResolution::Def(def @ hir::ModuleDef::TypeAlias(_))
         | PathResolution::Def(def @ hir::ModuleDef::BuiltinType(_)) => {
             if let hir::ModuleDef::Adt(Adt::Enum(e)) = def {
-                add_enum_variants(ctx, acc, e);
+                for variant in e.variants(ctx.db) {
+                    acc.add_enum_variant(ctx, variant, None);
+                }
             }
             let ty = match def {
                 hir::ModuleDef::Adt(adt) => adt.ty(ctx.db),
-                hir::ModuleDef::TypeAlias(a) => {
-                    let ty = a.ty(ctx.db);
-                    if let Some(Adt::Enum(e)) = ty.as_adt() {
-                        cov_mark::hit!(completes_variant_through_alias);
-                        add_enum_variants(ctx, acc, e);
-                    }
-                    ty
-                }
+                hir::ModuleDef::TypeAlias(a) => a.ty(ctx.db),
                 hir::ModuleDef::BuiltinType(builtin) => {
                     let module = match ctx.scope.module() {
                         Some(it) => it,
                         None => return,
                     };
-                    cov_mark::hit!(completes_primitive_assoc_const);
                     builtin.ty(ctx.db, module)
                 }
                 _ => unreachable!(),
@@ -98,8 +92,9 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
                     if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) {
                         return None;
                     }
-                    if let hir::AssocItem::TypeAlias(ty) = item {
-                        acc.add_type_alias(ctx, ty)
+                    match item {
+                        hir::AssocItem::Function(_) | hir::AssocItem::Const(_) => {}
+                        hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty),
                     }
                     None::<()>
                 });
@@ -127,7 +122,9 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
                 };
 
                 if let Some(Adt::Enum(e)) = ty.as_adt() {
-                    add_enum_variants(ctx, acc, e);
+                    for variant in e.variants(ctx.db) {
+                        acc.add_enum_variant(ctx, variant, None);
+                    }
                 }
 
                 let traits_in_scope = ctx.scope.traits_in_scope();
@@ -154,12 +151,6 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
     }
 }
 
-fn add_enum_variants(ctx: &CompletionContext, acc: &mut Completions, e: hir::Enum) {
-    for variant in e.variants(ctx.db) {
-        acc.add_enum_variant(ctx, variant, None);
-    }
-}
-
 #[cfg(test)]
 mod tests {
     use expect_test::{expect, Expect};
@@ -746,7 +737,29 @@ fn f() {}
     }
 
     #[test]
-    fn completes_variant_through_self() {
+    fn completes_function() {
+        check(
+            r#"
+fn foo(
+    a: i32,
+    b: i32
+) {
+
+}
+
+fn main() {
+    fo$0
+}
+"#,
+            expect![[r#"
+                fn main() fn()
+                fn foo(…) fn(i32, i32)
+            "#]],
+        );
+    }
+
+    #[test]
+    fn completes_self_enum() {
         check(
             r#"
 enum Foo {
@@ -770,7 +783,6 @@ impl Foo {
 
     #[test]
     fn completes_primitive_assoc_const() {
-        cov_mark::check!(completes_primitive_assoc_const);
         check(
             r#"
 //- /lib.rs crate:lib deps:core
@@ -792,23 +804,4 @@ impl u8 {
             "#]],
         );
     }
-
-    #[test]
-    fn completes_variant_through_alias() {
-        cov_mark::check!(completes_variant_through_alias);
-        check(
-            r#"
-enum Foo {
-    Bar
-}
-type Foo2 = Foo;
-fn main() {
-    Foo2::$0
-}
-"#,
-            expect![[r#"
-                ev Bar ()
-            "#]],
-        );
-    }
 }
diff --git a/crates/ide_completion/src/context.rs b/crates/ide_completion/src/context.rs
index f3fcb712c7..32f81aec14 100644
--- a/crates/ide_completion/src/context.rs
+++ b/crates/ide_completion/src/context.rs
@@ -301,108 +301,103 @@ impl<'a> CompletionContext<'a> {
             .find_map(ast::Impl::cast);
     }
 
-    fn expected_type_and_name(&self) -> (Option<Type>, Option<NameOrNameRef>) {
-        let mut node = match self.token.parent() {
-            Some(it) => it,
-            None => return (None, None),
-        };
-        loop {
-            break match_ast! {
-                match node {
-                    ast::LetStmt(it) => {
-                        cov_mark::hit!(expected_type_let_with_leading_char);
-                        cov_mark::hit!(expected_type_let_without_leading_char);
-                        let ty = it.pat()
-                            .and_then(|pat| self.sema.type_of_pat(&pat));
-                        let name = if let Some(ast::Pat::IdentPat(ident)) = it.pat() {
-                            ident.name().map(NameOrNameRef::Name)
-                        } else {
-                            None
-                        };
-
-                        (ty, name)
-                    },
-                    ast::ArgList(_it) => {
-                        cov_mark::hit!(expected_type_fn_param_with_leading_char);
-                        cov_mark::hit!(expected_type_fn_param_without_leading_char);
-                        ActiveParameter::at_token(
-                            &self.sema,
-                            self.token.clone(),
-                        ).map(|ap| {
-                            let name = ap.ident().map(NameOrNameRef::Name);
-                            (Some(ap.ty), name)
-                        })
-                        .unwrap_or((None, None))
-                    },
-                    ast::RecordExprFieldList(_it) => {
-                        cov_mark::hit!(expected_type_struct_field_without_leading_char);
-                        self.token.prev_sibling_or_token()
-                            .and_then(|se| se.into_node())
-                            .and_then(|node| ast::RecordExprField::cast(node))
-                            .and_then(|rf| self.sema.resolve_record_field(&rf).zip(Some(rf)))
-                            .map(|(f, rf)|(
-                                Some(f.0.ty(self.db)),
-                                rf.field_name().map(NameOrNameRef::NameRef),
-                            ))
-                            .unwrap_or((None, None))
-                    },
-                    ast::RecordExprField(it) => {
-                        cov_mark::hit!(expected_type_struct_field_with_leading_char);
-                        self.sema
-                            .resolve_record_field(&it)
-                            .map(|f|(
-                                Some(f.0.ty(self.db)),
-                                it.field_name().map(NameOrNameRef::NameRef),
-                            ))
-                            .unwrap_or((None, None))
-                    },
-                    ast::MatchExpr(it) => {
-                        cov_mark::hit!(expected_type_match_arm_without_leading_char);
-                        let ty = it.expr()
-                            .and_then(|e| self.sema.type_of_expr(&e));
-                        (ty, None)
-                    },
-                    ast::IfExpr(it) => {
-                        cov_mark::hit!(expected_type_if_let_without_leading_char);
-                        let ty = it.condition()
-                            .and_then(|cond| cond.expr())
-                            .and_then(|e| self.sema.type_of_expr(&e));
-                        (ty, None)
-                    },
-                    ast::IdentPat(it) => {
-                        cov_mark::hit!(expected_type_if_let_with_leading_char);
-                        cov_mark::hit!(expected_type_match_arm_with_leading_char);
-                        let ty = self.sema.type_of_pat(&ast::Pat::from(it));
-                        (ty, None)
-                    },
-                    ast::Fn(it) => {
-                        cov_mark::hit!(expected_type_fn_ret_with_leading_char);
-                        cov_mark::hit!(expected_type_fn_ret_without_leading_char);
-                        let def = self.sema.to_def(&it);
-                        (def.map(|def| def.ret_type(self.db)), None)
-                    },
-                    ast::Stmt(it) => (None, None),
-                    _ => {
-                        match node.parent() {
-                            Some(n) => {
-                                node = n;
-                                continue;
-                            },
-                            None => (None, None),
-                        }
-                    },
-                }
-            };
-        }
-    }
-
     fn fill(
         &mut self,
         original_file: &SyntaxNode,
         file_with_fake_ident: SyntaxNode,
         offset: TextSize,
     ) {
-        let (expected_type, expected_name) = self.expected_type_and_name();
+        let (expected_type, expected_name) = {
+            let mut node = match self.token.parent() {
+                Some(it) => it,
+                None => return,
+            };
+            loop {
+                break match_ast! {
+                    match node {
+                        ast::LetStmt(it) => {
+                            cov_mark::hit!(expected_type_let_with_leading_char);
+                            cov_mark::hit!(expected_type_let_without_leading_char);
+                            let ty = it.pat()
+                                .and_then(|pat| self.sema.type_of_pat(&pat));
+                            let name = if let Some(ast::Pat::IdentPat(ident)) = it.pat() {
+                                ident.name().map(NameOrNameRef::Name)
+                            } else {
+                                None
+                            };
+
+                            (ty, name)
+                        },
+                        ast::ArgList(_it) => {
+                            cov_mark::hit!(expected_type_fn_param_with_leading_char);
+                            cov_mark::hit!(expected_type_fn_param_without_leading_char);
+                            ActiveParameter::at_token(
+                                &self.sema,
+                                self.token.clone(),
+                            ).map(|ap| {
+                                let name = ap.ident().map(NameOrNameRef::Name);
+                                (Some(ap.ty), name)
+                            })
+                            .unwrap_or((None, None))
+                        },
+                        ast::RecordExprFieldList(_it) => {
+                            cov_mark::hit!(expected_type_struct_field_without_leading_char);
+                            self.token.prev_sibling_or_token()
+                                .and_then(|se| se.into_node())
+                                .and_then(|node| ast::RecordExprField::cast(node))
+                                .and_then(|rf| self.sema.resolve_record_field(&rf).zip(Some(rf)))
+                                .map(|(f, rf)|(
+                                    Some(f.0.signature_ty(self.db)),
+                                    rf.field_name().map(NameOrNameRef::NameRef),
+                                ))
+                                .unwrap_or((None, None))
+                        },
+                        ast::RecordExprField(it) => {
+                            cov_mark::hit!(expected_type_struct_field_with_leading_char);
+                            self.sema
+                                .resolve_record_field(&it)
+                                .map(|f|(
+                                    Some(f.0.signature_ty(self.db)),
+                                    it.field_name().map(NameOrNameRef::NameRef),
+                                ))
+                                .unwrap_or((None, None))
+                        },
+                        ast::MatchExpr(it) => {
+                            cov_mark::hit!(expected_type_match_arm_without_leading_char);
+                            let ty = it.expr()
+                                .and_then(|e| self.sema.type_of_expr(&e));
+
+                            (ty, None)
+                        },
+                        ast::IdentPat(it) => {
+                            cov_mark::hit!(expected_type_if_let_with_leading_char);
+                            cov_mark::hit!(expected_type_match_arm_with_leading_char);
+                            let ty = self.sema.type_of_pat(&ast::Pat::from(it));
+
+                            (ty, None)
+                        },
+                        ast::Fn(_it) => {
+                            cov_mark::hit!(expected_type_fn_ret_with_leading_char);
+                            cov_mark::hit!(expected_type_fn_ret_without_leading_char);
+                            let ty = self.token.ancestors()
+                                .find_map(|ancestor| ast::Expr::cast(ancestor))
+                                .and_then(|expr| self.sema.type_of_expr(&expr));
+
+                            (ty, None)
+                        },
+                        _ => {
+                            match node.parent() {
+                                Some(n) => {
+                                    node = n;
+                                    continue;
+                                },
+                                None => (None, None),
+                            }
+                        },
+                    }
+                };
+            }
+        };
         self.expected_type = expected_type;
         self.expected_name = expected_name;
         self.attribute_under_caret = find_node_at_offset(&file_with_fake_ident, offset);
@@ -807,7 +802,6 @@ fn foo() {
 
     #[test]
     fn expected_type_if_let_without_leading_char() {
-        cov_mark::check!(expected_type_if_let_without_leading_char);
         check_expected_type_and_name(
             r#"
 enum Foo { Bar, Baz, Quux }
@@ -817,8 +811,8 @@ fn foo() {
     if let $0 = f { }
 }
 "#,
-            expect![[r#"ty: Foo, name: ?"#]],
-        )
+            expect![[r#"ty: (), name: ?"#]],
+        ) // FIXME should be `ty: u32, name: ?`
     }
 
     #[test]
@@ -846,8 +840,8 @@ fn foo() -> u32 {
     $0
 }
 "#,
-            expect![[r#"ty: u32, name: ?"#]],
-        )
+            expect![[r#"ty: (), name: ?"#]],
+        ) // FIXME this should be `ty: u32, name: ?`
     }
 
     #[test]
@@ -858,18 +852,6 @@ fn foo() -> u32 {
 fn foo() -> u32 {
     c$0
 }
-"#,
-            expect![[r#"ty: u32, name: ?"#]],
-        )
-    }
-
-    #[test]
-    fn expected_type_fn_ret_fn_ref_fully_typed() {
-        check_expected_type_and_name(
-            r#"
-fn foo() -> u32 {
-    foo$0
-}
 "#,
             expect![[r#"ty: u32, name: ?"#]],
         )
diff --git a/crates/ide_completion/src/render/enum_variant.rs b/crates/ide_completion/src/render/enum_variant.rs
index 0c0c71134a..832f5ced13 100644
--- a/crates/ide_completion/src/render/enum_variant.rs
+++ b/crates/ide_completion/src/render/enum_variant.rs
@@ -93,7 +93,7 @@ impl<'a> EnumRender<'a> {
             .variant
             .fields(self.ctx.db())
             .into_iter()
-            .map(|field| (field.name(self.ctx.db()), field.ty(self.ctx.db())));
+            .map(|field| (field.name(self.ctx.db()), field.signature_ty(self.ctx.db())));
 
         match self.variant_kind {
             StructKind::Tuple | StructKind::Unit => format!(
diff --git a/crates/proc_macro_api/Cargo.toml b/crates/proc_macro_api/Cargo.toml
index 2ce5eeeddc..1ba1e4abd5 100644
--- a/crates/proc_macro_api/Cargo.toml
+++ b/crates/proc_macro_api/Cargo.toml
@@ -15,7 +15,7 @@ serde_json = { version = "1.0", features = ["unbounded_depth"] }
 log = "0.4.8"
 crossbeam-channel = "0.5.0"
 jod-thread = "0.1.1"
-memmap2 = "0.2.0"
+memmap = "0.7.0"
 object = { version = "0.23.0", default-features = false, features = ["std", "read_core", "elf", "macho", "pe", "unaligned"] }
 snap = "1.0"
 
diff --git a/crates/proc_macro_api/src/version.rs b/crates/proc_macro_api/src/version.rs
index 6dbac50b4a..dcf8fae8f5 100644
--- a/crates/proc_macro_api/src/version.rs
+++ b/crates/proc_macro_api/src/version.rs
@@ -6,7 +6,7 @@ use std::{
     path::Path,
 };
 
-use memmap2::Mmap;
+use memmap::Mmap;
 use object::read::{File as BinaryFile, Object, ObjectSection};
 use snap::read::FrameDecoder as SnapDecoder;
 
diff --git a/crates/rust-analyzer/src/benchmarks.rs b/crates/rust-analyzer/src/benchmarks.rs
new file mode 100644
index 0000000000..bdd94b1c4e
--- /dev/null
+++ b/crates/rust-analyzer/src/benchmarks.rs
@@ -0,0 +1,74 @@
+//! Fully integrated benchmarks for rust-analyzer, which load real cargo
+//! projects.
+//!
+//! The benchmark here is used to debug specific performance regressions. If you
+//! notice that, eg, completion is slow in some specific case, you can  modify
+//! code here exercise this specific completion, and thus have a fast
+//! edit/compile/test cycle.
+//!
+//! Note that "Rust Analyzer: Run" action does not allow running a single test
+//! in release mode in VS Code. There's however "Rust Analyzer: Copy Run Command Line"
+//! which you can use to paste the command in terminal and add `--release` manually.
+
+use std::sync::Arc;
+
+use ide::Change;
+use test_utils::project_root;
+use vfs::{AbsPathBuf, VfsPath};
+
+use crate::cli::load_cargo::{load_workspace_at, LoadCargoConfig};
+
+#[test]
+fn benchmark_integrated_highlighting() {
+    // Don't run slow benchmark by default
+    if true {
+        return;
+    }
+
+    // Load rust-analyzer itself.
+    let workspace_to_load = project_root();
+    let file = "./crates/ide_db/src/apply_change.rs";
+
+    let cargo_config = Default::default();
+    let load_cargo_config = LoadCargoConfig {
+        load_out_dirs_from_check: true,
+        wrap_rustc: false,
+        with_proc_macro: false,
+    };
+
+    let (mut host, vfs, _proc_macro) = {
+        let _it = stdx::timeit("workspace loading");
+        load_workspace_at(&workspace_to_load, &cargo_config, &load_cargo_config, &|_| {}).unwrap()
+    };
+
+    let file_id = {
+        let file = workspace_to_load.join(file);
+        let path = VfsPath::from(AbsPathBuf::assert(file));
+        vfs.file_id(&path).unwrap_or_else(|| panic!("can't find virtual file for {}", path))
+    };
+
+    {
+        let _it = stdx::timeit("initial");
+        let analysis = host.analysis();
+        analysis.highlight_as_html(file_id, false).unwrap();
+    }
+
+    profile::init_from("*>100");
+    // let _s = profile::heartbeat_span();
+
+    {
+        let _it = stdx::timeit("change");
+        let mut text = host.analysis().file_text(file_id).unwrap().to_string();
+        text.push_str("\npub fn _dummy() {}\n");
+        let mut change = Change::new();
+        change.change_file(file_id, Some(Arc::new(text)));
+        host.apply_change(change);
+    }
+
+    {
+        let _it = stdx::timeit("after change");
+        let _span = profile::cpu_span();
+        let analysis = host.analysis();
+        analysis.highlight_as_html(file_id, false).unwrap();
+    }
+}
diff --git a/crates/rust-analyzer/src/cli/diagnostics.rs b/crates/rust-analyzer/src/cli/diagnostics.rs
index c33c8179cb..74f784338f 100644
--- a/crates/rust-analyzer/src/cli/diagnostics.rs
+++ b/crates/rust-analyzer/src/cli/diagnostics.rs
@@ -7,7 +7,7 @@ use anyhow::anyhow;
 use rustc_hash::FxHashSet;
 
 use hir::{db::HirDatabase, Crate, Module};
-use ide::{AssistResolveStrategy, DiagnosticsConfig, Severity};
+use ide::{DiagnosticsConfig, Severity};
 use ide_db::base_db::SourceDatabaseExt;
 
 use crate::cli::{
@@ -57,9 +57,8 @@ pub fn diagnostics(
             let crate_name =
                 module.krate().display_name(db).as_deref().unwrap_or("unknown").to_string();
             println!("processing crate: {}, module: {}", crate_name, _vfs.file_path(file_id));
-            for diagnostic in analysis
-                .diagnostics(&DiagnosticsConfig::default(), AssistResolveStrategy::None, file_id)
-                .unwrap()
+            for diagnostic in
+                analysis.diagnostics(&DiagnosticsConfig::default(), false, file_id).unwrap()
             {
                 if matches!(diagnostic.severity, Severity::Error) {
                     found_error = true;
diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs
index 6f2f482c15..adeb7a97e8 100644
--- a/crates/rust-analyzer/src/global_state.rs
+++ b/crates/rust-analyzer/src/global_state.rs
@@ -84,7 +84,6 @@ pub(crate) struct GlobalState {
     pub(crate) workspace_build_data: Option<BuildDataResult>,
     pub(crate) fetch_build_data_queue:
         OpQueue<BuildDataCollector, Option<anyhow::Result<BuildDataResult>>>,
-    pub(crate) prime_caches_queue: OpQueue<(), ()>,
 
     latest_requests: Arc<RwLock<LatestRequests>>,
 }
@@ -147,7 +146,6 @@ impl GlobalState {
             workspaces: Arc::new(Vec::new()),
             fetch_workspaces_queue: OpQueue::default(),
             workspace_build_data: None,
-            prime_caches_queue: OpQueue::default(),
 
             fetch_build_data_queue: OpQueue::default(),
             latest_requests: Default::default(),
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs
index f6e40f8722..1f59402e5c 100644
--- a/crates/rust-analyzer/src/handlers.rs
+++ b/crates/rust-analyzer/src/handlers.rs
@@ -8,9 +8,8 @@ use std::{
 };
 
 use ide::{
-    AnnotationConfig, AssistKind, AssistResolveStrategy, FileId, FilePosition, FileRange,
-    HoverAction, HoverGotoTypeData, Query, RangeInfo, Runnable, RunnableKind, SearchScope,
-    SingleResolve, SourceChange, TextEdit,
+    AnnotationConfig, FileId, FilePosition, FileRange, HoverAction, HoverGotoTypeData, Query,
+    RangeInfo, Runnable, RunnableKind, SearchScope, SourceChange, TextEdit,
 };
 use ide_db::SymbolKind;
 use itertools::Itertools;
@@ -28,7 +27,7 @@ use lsp_types::{
 use project_model::TargetKind;
 use serde::{Deserialize, Serialize};
 use serde_json::to_value;
-use stdx::format_to;
+use stdx::{format_to, split_once};
 use syntax::{algo, ast, AstNode, TextRange, TextSize};
 
 use crate::{
@@ -1005,15 +1004,10 @@ pub(crate) fn handle_code_action(
     let mut res: Vec<lsp_ext::CodeAction> = Vec::new();
 
     let code_action_resolve_cap = snap.config.code_action_resolve();
-    let resolve = if code_action_resolve_cap {
-        AssistResolveStrategy::None
-    } else {
-        AssistResolveStrategy::All
-    };
     let assists = snap.analysis.assists_with_fixes(
         &assists_config,
         &snap.config.diagnostics(),
-        resolve,
+        !code_action_resolve_cap,
         frange,
     )?;
     for (index, assist) in assists.into_iter().enumerate() {
@@ -1058,68 +1052,22 @@ pub(crate) fn handle_code_action_resolve(
         .only
         .map(|it| it.into_iter().filter_map(from_proto::assist_kind).collect());
 
-    let (assist_index, assist_resolve) = match parse_action_id(&params.id) {
-        Ok(parsed_data) => parsed_data,
-        Err(e) => {
-            return Err(LspError::new(
-                ErrorCode::InvalidParams as i32,
-                format!("Failed to parse action id string '{}': {}", params.id, e),
-            )
-            .into())
-        }
-    };
-
-    let expected_assist_id = assist_resolve.assist_id.clone();
-    let expected_kind = assist_resolve.assist_kind;
-
     let assists = snap.analysis.assists_with_fixes(
         &assists_config,
         &snap.config.diagnostics(),
-        AssistResolveStrategy::Single(assist_resolve),
+        true,
         frange,
     )?;
 
-    let assist = match assists.get(assist_index) {
-        Some(assist) => assist,
-        None => return Err(LspError::new(
-            ErrorCode::InvalidParams as i32,
-            format!(
-                "Failed to find the assist for index {} provided by the resolve request. Resolve request assist id: {}",
-                assist_index, params.id,
-            ),
-        )
-        .into())
-    };
-    if assist.id.0 != expected_assist_id || assist.id.1 != expected_kind {
-        return Err(LspError::new(
-            ErrorCode::InvalidParams as i32,
-            format!(
-                "Mismatching assist at index {} for the resolve parameters given. Resolve request assist id: {}, actual id: {:?}.",
-                assist_index, params.id, assist.id
-            ),
-        )
-        .into());
-    }
+    let (id, index) = split_once(&params.id, ':').unwrap();
+    let index = index.parse::<usize>().unwrap();
+    let assist = &assists[index];
+    assert!(assist.id.0 == id);
     let edit = to_proto::code_action(&snap, assist.clone(), None)?.edit;
     code_action.edit = edit;
     Ok(code_action)
 }
 
-fn parse_action_id(action_id: &str) -> Result<(usize, SingleResolve), String> {
-    let id_parts = action_id.split(':').collect_vec();
-    match id_parts.as_slice() {
-        &[assist_id_string, assist_kind_string, index_string] => {
-            let assist_kind: AssistKind = assist_kind_string.parse()?;
-            let index: usize = match index_string.parse() {
-                Ok(index) => index,
-                Err(e) => return Err(format!("Incorrect index string: {}", e)),
-            };
-            Ok((index, SingleResolve { assist_id: assist_id_string.to_string(), assist_kind }))
-        }
-        _ => Err("Action id contains incorrect number of segments".to_string()),
-    }
-}
-
 pub(crate) fn handle_code_lens(
     snap: GlobalStateSnapshot,
     params: lsp_types::CodeLensParams,
@@ -1234,7 +1182,7 @@ pub(crate) fn publish_diagnostics(
 
     let diagnostics: Vec<Diagnostic> = snap
         .analysis
-        .diagnostics(&snap.config.diagnostics(), AssistResolveStrategy::None, file_id)?
+        .diagnostics(&snap.config.diagnostics(), false, file_id)?
         .into_iter()
         .map(|d| Diagnostic {
             range: to_proto::range(&line_index, d.range),
diff --git a/crates/rust-analyzer/src/integrated_benchmarks.rs b/crates/rust-analyzer/src/integrated_benchmarks.rs
deleted file mode 100644
index 3dcbe397ad..0000000000
--- a/crates/rust-analyzer/src/integrated_benchmarks.rs
+++ /dev/null
@@ -1,184 +0,0 @@
-//! Fully integrated benchmarks for rust-analyzer, which load real cargo
-//! projects.
-//!
-//! The benchmark here is used to debug specific performance regressions. If you
-//! notice that, eg, completion is slow in some specific case, you can  modify
-//! code here exercise this specific completion, and thus have a fast
-//! edit/compile/test cycle.
-//!
-//! Note that "Rust Analyzer: Run" action does not allow running a single test
-//! in release mode in VS Code. There's however "Rust Analyzer: Copy Run Command Line"
-//! which you can use to paste the command in terminal and add `--release` manually.
-
-use std::{convert::TryFrom, sync::Arc};
-
-use ide::{Change, CompletionConfig, FilePosition, TextSize};
-use ide_db::helpers::{insert_use::InsertUseConfig, merge_imports::MergeBehavior, SnippetCap};
-use test_utils::project_root;
-use vfs::{AbsPathBuf, VfsPath};
-
-use crate::cli::load_cargo::{load_workspace_at, LoadCargoConfig};
-
-#[test]
-fn integrated_highlighting_benchmark() {
-    if std::env::var("RUN_SLOW_BENCHES").is_err() {
-        return;
-    }
-
-    // Load rust-analyzer itself.
-    let workspace_to_load = project_root();
-    let file = "./crates/ide_db/src/apply_change.rs";
-
-    let cargo_config = Default::default();
-    let load_cargo_config = LoadCargoConfig {
-        load_out_dirs_from_check: true,
-        wrap_rustc: false,
-        with_proc_macro: false,
-    };
-
-    let (mut host, vfs, _proc_macro) = {
-        let _it = stdx::timeit("workspace loading");
-        load_workspace_at(&workspace_to_load, &cargo_config, &load_cargo_config, &|_| {}).unwrap()
-    };
-
-    let file_id = {
-        let file = workspace_to_load.join(file);
-        let path = VfsPath::from(AbsPathBuf::assert(file));
-        vfs.file_id(&path).unwrap_or_else(|| panic!("can't find virtual file for {}", path))
-    };
-
-    {
-        let _it = stdx::timeit("initial");
-        let analysis = host.analysis();
-        analysis.highlight_as_html(file_id, false).unwrap();
-    }
-
-    profile::init_from("*>100");
-    // let _s = profile::heartbeat_span();
-
-    {
-        let _it = stdx::timeit("change");
-        let mut text = host.analysis().file_text(file_id).unwrap().to_string();
-        text.push_str("\npub fn _dummy() {}\n");
-        let mut change = Change::new();
-        change.change_file(file_id, Some(Arc::new(text)));
-        host.apply_change(change);
-    }
-
-    {
-        let _it = stdx::timeit("after change");
-        let _span = profile::cpu_span();
-        let analysis = host.analysis();
-        analysis.highlight_as_html(file_id, false).unwrap();
-    }
-}
-
-#[test]
-fn integrated_completion_benchmark() {
-    if std::env::var("RUN_SLOW_BENCHES").is_err() {
-        return;
-    }
-
-    // Load rust-analyzer itself.
-    let workspace_to_load = project_root();
-    let file = "./crates/hir/src/lib.rs";
-
-    let cargo_config = Default::default();
-    let load_cargo_config = LoadCargoConfig {
-        load_out_dirs_from_check: true,
-        wrap_rustc: false,
-        with_proc_macro: false,
-    };
-
-    let (mut host, vfs, _proc_macro) = {
-        let _it = stdx::timeit("workspace loading");
-        load_workspace_at(&workspace_to_load, &cargo_config, &load_cargo_config, &|_| {}).unwrap()
-    };
-
-    let file_id = {
-        let file = workspace_to_load.join(file);
-        let path = VfsPath::from(AbsPathBuf::assert(file));
-        vfs.file_id(&path).unwrap_or_else(|| panic!("can't find virtual file for {}", path))
-    };
-
-    {
-        let _it = stdx::timeit("initial");
-        let analysis = host.analysis();
-        analysis.highlight_as_html(file_id, false).unwrap();
-    }
-
-    profile::init_from("*>5");
-    // let _s = profile::heartbeat_span();
-
-    let completion_offset = {
-        let _it = stdx::timeit("change");
-        let mut text = host.analysis().file_text(file_id).unwrap().to_string();
-        let completion_offset =
-            patch(&mut text, "db.struct_data(self.id)", "sel;\ndb.struct_data(self.id)")
-                + "sel".len();
-        let mut change = Change::new();
-        change.change_file(file_id, Some(Arc::new(text)));
-        host.apply_change(change);
-        completion_offset
-    };
-
-    {
-        let _it = stdx::timeit("unqualified path completion");
-        let _span = profile::cpu_span();
-        let analysis = host.analysis();
-        let config = CompletionConfig {
-            enable_postfix_completions: true,
-            enable_imports_on_the_fly: true,
-            add_call_parenthesis: true,
-            add_call_argument_snippets: true,
-            snippet_cap: SnippetCap::new(true),
-            insert_use: InsertUseConfig {
-                merge: Some(MergeBehavior::Full),
-                prefix_kind: hir::PrefixKind::ByCrate,
-                group: true,
-            },
-        };
-        let position =
-            FilePosition { file_id, offset: TextSize::try_from(completion_offset).unwrap() };
-        analysis.completions(&config, position).unwrap();
-    }
-
-    let completion_offset = {
-        let _it = stdx::timeit("change");
-        let mut text = host.analysis().file_text(file_id).unwrap().to_string();
-        let completion_offset =
-            patch(&mut text, "sel;\ndb.struct_data(self.id)", "self.;\ndb.struct_data(self.id)")
-                + "self.".len();
-        let mut change = Change::new();
-        change.change_file(file_id, Some(Arc::new(text)));
-        host.apply_change(change);
-        completion_offset
-    };
-
-    {
-        let _it = stdx::timeit("dot completion");
-        let _span = profile::cpu_span();
-        let analysis = host.analysis();
-        let config = CompletionConfig {
-            enable_postfix_completions: true,
-            enable_imports_on_the_fly: true,
-            add_call_parenthesis: true,
-            add_call_argument_snippets: true,
-            snippet_cap: SnippetCap::new(true),
-            insert_use: InsertUseConfig {
-                merge: Some(MergeBehavior::Full),
-                prefix_kind: hir::PrefixKind::ByCrate,
-                group: true,
-            },
-        };
-        let position =
-            FilePosition { file_id, offset: TextSize::try_from(completion_offset).unwrap() };
-        analysis.completions(&config, position).unwrap();
-    }
-}
-
-fn patch(what: &mut String, from: &str, to: &str) -> usize {
-    let idx = what.find(from).unwrap();
-    *what = what.replacen(from, to, 1);
-    idx
-}
diff --git a/crates/rust-analyzer/src/lib.rs b/crates/rust-analyzer/src/lib.rs
index da7e24becd..d9a5030a0f 100644
--- a/crates/rust-analyzer/src/lib.rs
+++ b/crates/rust-analyzer/src/lib.rs
@@ -40,7 +40,7 @@ pub mod lsp_ext;
 pub mod config;
 
 #[cfg(test)]
-mod integrated_benchmarks;
+mod benchmarks;
 
 use serde::de::DeserializeOwned;
 use std::fmt;
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index ce7ece559f..a766aacadd 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -8,7 +8,8 @@ use std::{
 
 use always_assert::always;
 use crossbeam_channel::{select, Receiver};
-use ide::{FileId, PrimeCachesProgress};
+use ide::PrimeCachesProgress;
+use ide::{Canceled, FileId};
 use ide_db::base_db::VfsPath;
 use lsp_server::{Connection, Notification, Request, Response};
 use lsp_types::notification::Notification as _;
@@ -294,8 +295,6 @@ impl GlobalState {
                             state = Progress::End;
                             message = None;
                             fraction = 1.0;
-
-                            self.prime_caches_queue.op_completed(());
                         }
                     };
 
@@ -712,23 +711,18 @@ impl GlobalState {
     }
     fn update_file_notifications_on_threadpool(&mut self) {
         self.maybe_update_diagnostics();
-
-        // Ensure that only one cache priming task can run at a time
-        self.prime_caches_queue.request_op(());
-        if self.prime_caches_queue.should_start_op().is_none() {
-            return;
-        }
-
         self.task_pool.handle.spawn_with_sender({
             let snap = self.snapshot();
             move |sender| {
-                let cb = |progress| {
-                    sender.send(Task::PrimeCaches(progress)).unwrap();
-                };
-                match snap.analysis.prime_caches(cb) {
-                    Ok(()) => (),
-                    Err(_canceled) => (),
-                }
+                snap.analysis
+                    .prime_caches(|progress| {
+                        sender.send(Task::PrimeCaches(progress)).unwrap();
+                    })
+                    .unwrap_or_else(|_: Canceled| {
+                        // Pretend that we're done, so that the progress bar is removed. Otherwise
+                        // the editor may complain about it already existing.
+                        sender.send(Task::PrimeCaches(PrimeCachesProgress::Finished)).unwrap()
+                    });
             }
         });
     }
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index 1d27aa7b37..5eff036ecf 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -524,6 +524,7 @@ pub(crate) fn folding_range(
         | FoldKind::ArgList
         | FoldKind::Consts
         | FoldKind::Statics
+        | FoldKind::WhereClause
         | FoldKind::Array => None,
     };
 
@@ -897,7 +898,7 @@ pub(crate) fn code_action(
         (Some(it), _) => res.edit = Some(snippet_workspace_edit(snap, it)?),
         (None, Some((index, code_action_params))) => {
             res.data = Some(lsp_ext::CodeActionData {
-                id: format!("{}:{}:{}", assist.id.0, assist.id.1.name(), index),
+                id: format!("{}:{}", assist.id.0, index.to_string()),
                 code_action_params,
             });
         }
diff --git a/docs/dev/style.md b/docs/dev/style.md
index 00de7a7117..6ab60b50ed 100644
--- a/docs/dev/style.md
+++ b/docs/dev/style.md
@@ -449,39 +449,6 @@ fn query_all(name: String, case_sensitive: bool) -> Vec<Item> { ... }
 fn query_first(name: String, case_sensitive: bool) -> Option<Item> { ... }
 ```
 
-## Prefer Separate Functions Over Parameters
-
-If a function has a `bool` or an `Option` parameter, and it is always called with `true`, `false`, `Some` and `None` literals, split the function in two.
-
-```rust
-// GOOD
-fn caller_a() {
-    foo()
-}
-
-fn caller_b() {
-    foo_with_bar(Bar::new())
-}
-
-fn foo() { ... }
-fn foo_with_bar(bar: Bar) { ... }
-
-// BAD
-fn caller_a() {
-    foo(None)
-}
-
-fn caller_b() {
-    foo(Some(Bar::new()))
-}
-
-fn foo(bar: Option<Bar>) { ... }
-```
-
-**Rationale:** more often than not, such functions display "`false sharing`" -- they have additional `if` branching inside for two different cases.
-Splitting the two different control flows into two functions simplifies each path, and remove cross-dependencies between the two paths.
-If there's common code between `foo` and `foo_with_bar`, extract *that* into a common helper.
-
 ## Avoid Monomorphization
 
 Avoid making a lot of code type parametric, *especially* on the boundaries between crates.
diff --git a/docs/user/manual.adoc b/docs/user/manual.adoc
index 58722aaa32..54195adb74 100644
--- a/docs/user/manual.adoc
+++ b/docs/user/manual.adoc
@@ -611,41 +611,6 @@ For example, mutable bindings are underlined by default and you can override thi
 }
 ----
 
-Most themes doesn't support styling unsafe operations differently yet. You can fix this by adding overrides for the rules `operator.unsafe`, `function.unsafe`, and `method.unsafe`:
-
-[source,jsonc]
-----
-{
-   "editor.semanticTokenColorCustomizations": {
-         "rules": {
-             "operator.unsafe": "#ff6600",
-             "function.unsafe": "#ff6600"
-             "method.unsafe": "#ff6600"
-         }
-    },
-}
-----
-
-In addition to the top-level rules you can specify overrides for specific themes. For example, if you wanted to use a darker text color on a specific light theme, you might write:
-
-[source,jsonc]
-----
-{
-   "editor.semanticTokenColorCustomizations": {
-         "rules": {
-             "operator.unsafe": "#ff6600"
-         },
-         "[Ayu Light]": {
-            "rules": {
-               "operator.unsafe": "#572300"
-            }
-         }
-    },
-}
-----
-
-Make sure you include the brackets around the theme name. For example, use `"[Ayu Light]"` to customize the theme Ayu Light.
-
 ==== Special `when` clause context for keybindings.
 You may use `inRustProject` context to configure keybindings for rust projects only.
 For example:
diff --git a/xtask/src/dist.rs b/xtask/src/dist.rs
index b6aa2f52af..12a7fea1e1 100644
--- a/xtask/src/dist.rs
+++ b/xtask/src/dist.rs
@@ -66,7 +66,6 @@ fn dist_client(version: &str, release_tag: &str) -> Result<()> {
 
 fn dist_server(release_channel: &str) -> Result<()> {
     let _e = pushenv("RUST_ANALYZER_CHANNEL", release_channel);
-    let _e = pushenv("CARGO_PROFILE_RELEASE_LTO", "true");
     let target = get_target();
     if target.contains("-linux-gnu") || target.contains("-linux-musl") {
         env::set_var("CC", "clang");
diff --git a/xtask/src/flags.rs b/xtask/src/flags.rs
index 69b3cb9c17..f80a5dd163 100644
--- a/xtask/src/flags.rs
+++ b/xtask/src/flags.rs
@@ -28,6 +28,7 @@ xflags::xflags! {
         }
 
         cmd fuzz-tests {}
+        cmd pre-cache {}
 
         cmd release {
             optional --dry-run
@@ -61,6 +62,7 @@ pub enum XtaskCmd {
     Help(Help),
     Install(Install),
     FuzzTests(FuzzTests),
+    PreCache(PreCache),
     Release(Release),
     Promote(Promote),
     Dist(Dist),
@@ -85,6 +87,9 @@ pub struct Install {
 #[derive(Debug)]
 pub struct FuzzTests;
 
+#[derive(Debug)]
+pub struct PreCache;
+
 #[derive(Debug)]
 pub struct Release {
     pub dry_run: bool,
diff --git a/xtask/src/main.rs b/xtask/src/main.rs
index d0bef7b7ae..ce33534108 100644
--- a/xtask/src/main.rs
+++ b/xtask/src/main.rs
@@ -18,6 +18,7 @@ mod install;
 mod release;
 mod dist;
 mod metrics;
+mod pre_cache;
 
 use anyhow::{bail, Result};
 use std::{
@@ -38,6 +39,7 @@ fn main() -> Result<()> {
         }
         flags::XtaskCmd::Install(cmd) => cmd.run(),
         flags::XtaskCmd::FuzzTests(_) => run_fuzzer(),
+        flags::XtaskCmd::PreCache(cmd) => cmd.run(),
         flags::XtaskCmd::Release(cmd) => cmd.run(),
         flags::XtaskCmd::Promote(cmd) => cmd.run(),
         flags::XtaskCmd::Dist(cmd) => cmd.run(),
diff --git a/xtask/src/pre_cache.rs b/xtask/src/pre_cache.rs
new file mode 100644
index 0000000000..b456224fd6
--- /dev/null
+++ b/xtask/src/pre_cache.rs
@@ -0,0 +1,79 @@
+use std::{
+    fs::FileType,
+    path::{Path, PathBuf},
+};
+
+use anyhow::Result;
+use xshell::rm_rf;
+
+use crate::flags;
+
+impl flags::PreCache {
+    /// Cleans the `./target` dir after the build such that only
+    /// dependencies are cached on CI.
+    pub(crate) fn run(self) -> Result<()> {
+        let slow_tests_cookie = Path::new("./target/.slow_tests_cookie");
+        if !slow_tests_cookie.exists() {
+            panic!("slow tests were skipped on CI!")
+        }
+        rm_rf(slow_tests_cookie)?;
+
+        for path in read_dir("./target/debug", FileType::is_file)? {
+            // Can't delete yourself on windows :-(
+            if !path.ends_with("xtask.exe") {
+                rm_rf(&path)?
+            }
+        }
+
+        rm_rf("./target/.rustc_info.json")?;
+
+        let to_delete = read_dir("./crates", FileType::is_dir)?
+            .into_iter()
+            .map(|path| path.file_name().unwrap().to_string_lossy().replace('-', "_"))
+            .collect::<Vec<_>>();
+
+        for &dir in ["./target/debug/deps", "target/debug/.fingerprint"].iter() {
+            for path in read_dir(dir, |_file_type| true)? {
+                if path.ends_with("xtask.exe") {
+                    continue;
+                }
+                let file_name = path.file_name().unwrap().to_string_lossy();
+                let (stem, _) = match rsplit_once(&file_name, '-') {
+                    Some(it) => it,
+                    None => {
+                        rm_rf(path)?;
+                        continue;
+                    }
+                };
+                let stem = stem.replace('-', "_");
+                if to_delete.contains(&stem) {
+                    rm_rf(path)?;
+                }
+            }
+        }
+
+        Ok(())
+    }
+}
+fn read_dir(path: impl AsRef<Path>, cond: impl Fn(&FileType) -> bool) -> Result<Vec<PathBuf>> {
+    read_dir_impl(path.as_ref(), &cond)
+}
+
+fn read_dir_impl(path: &Path, cond: &dyn Fn(&FileType) -> bool) -> Result<Vec<PathBuf>> {
+    let mut res = Vec::new();
+    for entry in path.read_dir()? {
+        let entry = entry?;
+        let file_type = entry.file_type()?;
+        if cond(&file_type) {
+            res.push(entry.path())
+        }
+    }
+    Ok(res)
+}
+
+fn rsplit_once(haystack: &str, delim: char) -> Option<(&str, &str)> {
+    let mut split = haystack.rsplitn(2, delim);
+    let suffix = split.next()?;
+    let prefix = split.next()?;
+    Some((prefix, suffix))
+}


Test patch:
diff --git a/crates/hir_ty/src/tests/regression.rs b/crates/hir_ty/src/tests/regression.rs
index d14f5c9bb1..9cd9f473d7 100644
--- a/crates/hir_ty/src/tests/regression.rs
+++ b/crates/hir_ty/src/tests/regression.rs
@@ -1012,41 +1012,3 @@ fn lifetime_from_chalk_during_deref() {
         "#,
     )
 }
-
-#[test]
-fn issue_8686() {
-    check_infer(
-        r#"
-pub trait Try: FromResidual {
-    type Output;
-    type Residual;
-}
-pub trait FromResidual<R = <Self as Try>::Residual> {
-     fn from_residual(residual: R) -> Self;
-}
-
-struct ControlFlow<B, C>;
-impl<B, C> Try for ControlFlow<B, C> {
-    type Output = C;
-    type Residual = ControlFlow<B, !>;
-}
-impl<B, C> FromResidual for ControlFlow<B, C> {
-    fn from_residual(r: ControlFlow<B, !>) -> Self { ControlFlow }
-}
-
-fn test() {
-    ControlFlow::from_residual(ControlFlow::<u32, !>);
-}
-        "#,
-        expect![[r#"
-            144..152 'residual': R
-            365..366 'r': ControlFlow<B, !>
-            395..410 '{ ControlFlow }': ControlFlow<B, C>
-            397..408 'ControlFlow': ControlFlow<B, C>
-            424..482 '{     ...!>); }': ()
-            430..456 'Contro...sidual': fn from_residual<ControlFlow<u32, {unknown}>, ControlFlow<u32, !>>(ControlFlow<u32, !>) -> ControlFlow<u32, {unknown}>
-            430..479 'Contro...2, !>)': ControlFlow<u32, {unknown}>
-            457..478 'Contro...32, !>': ControlFlow<u32, !>
-        "#]],
-    );
-}
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
index 8d83ba2065..638f42c2f0 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
@@ -142,7 +142,6 @@ It is beyond me why you'd use these when you got ///
 ```rust
 </span><span class="keyword injected">let</span><span class="none injected"> </span><span class="punctuation injected">_</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="function injected">example</span><span class="parenthesis injected">(</span><span class="operator injected">&</span><span class="bracket injected">[</span><span class="numeric_literal injected">1</span><span class="comma injected">,</span><span class="none injected"> </span><span class="numeric_literal injected">2</span><span class="comma injected">,</span><span class="none injected"> </span><span class="numeric_literal injected">3</span><span class="bracket injected">]</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span><span class="comment documentation">
 ```
-</span><span class="function documentation injected intra_doc_link">[`block_comments2`]</span><span class="comment documentation"> tests these with indentation
  */</span>
 <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration">block_comments</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
 
@@ -151,6 +150,5 @@ It is beyond me why you'd use these when you got ///
     ```rust
 </span><span class="comment documentation"> </span><span class="none injected">   </span><span class="keyword injected">let</span><span class="none injected"> </span><span class="punctuation injected">_</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="function injected">example</span><span class="parenthesis injected">(</span><span class="operator injected">&</span><span class="bracket injected">[</span><span class="numeric_literal injected">1</span><span class="comma injected">,</span><span class="none injected"> </span><span class="numeric_literal injected">2</span><span class="comma injected">,</span><span class="none injected"> </span><span class="numeric_literal injected">3</span><span class="bracket injected">]</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span><span class="comment documentation">
     ```
-    </span><span class="function documentation injected intra_doc_link">[`block_comments`]</span><span class="comment documentation"> tests these without indentation
 */</span>
 <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration">block_comments2</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span></code></pre>
\ No newline at end of file
diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs
index b6e952b088..17cc6334b9 100644
--- a/crates/ide/src/syntax_highlighting/tests.rs
+++ b/crates/ide/src/syntax_highlighting/tests.rs
@@ -618,7 +618,6 @@ It is beyond me why you'd use these when you got ///
 ```rust
 let _ = example(&[1, 2, 3]);
 ```
-[`block_comments2`] tests these with indentation
  */
 pub fn block_comments() {}
 
@@ -627,7 +626,6 @@ pub fn block_comments() {}
     ```rust
     let _ = example(&[1, 2, 3]);
     ```
-    [`block_comments`] tests these without indentation
 */
 pub fn block_comments2() {}
 "#
diff --git a/crates/ide_assists/src/tests.rs b/crates/ide_assists/src/tests.rs
index 9c28479984..6f4f97361d 100644
--- a/crates/ide_assists/src/tests.rs
+++ b/crates/ide_assists/src/tests.rs
@@ -12,10 +12,7 @@ use stdx::{format_to, trim_indent};
 use syntax::TextRange;
 use test_utils::{assert_eq_text, extract_offset};
 
-use crate::{
-    handlers::Handler, Assist, AssistConfig, AssistContext, AssistKind, AssistResolveStrategy,
-    Assists, SingleResolve,
-};
+use crate::{handlers::Handler, Assist, AssistConfig, AssistContext, AssistKind, Assists};
 
 pub(crate) const TEST_CONFIG: AssistConfig = AssistConfig {
     snippet_cap: SnippetCap::new(true),
@@ -68,14 +65,14 @@ fn check_doc_test(assist_id: &str, before: &str, after: &str) {
     let before = db.file_text(file_id).to_string();
     let frange = FileRange { file_id, range: selection.into() };
 
-    let assist = Assist::get(&db, &TEST_CONFIG, AssistResolveStrategy::All, frange)
+    let assist = Assist::get(&db, &TEST_CONFIG, true, frange)
         .into_iter()
         .find(|assist| assist.id.0 == assist_id)
         .unwrap_or_else(|| {
             panic!(
                 "\n\nAssist is not applicable: {}\nAvailable assists: {}",
                 assist_id,
-                Assist::get(&db, &TEST_CONFIG, AssistResolveStrategy::None, frange)
+                Assist::get(&db, &TEST_CONFIG, false, frange)
                     .into_iter()
                     .map(|assist| assist.id.0)
                     .collect::<Vec<_>>()
@@ -111,7 +108,7 @@ fn check(handler: Handler, before: &str, expected: ExpectedResult, assist_label:
     let sema = Semantics::new(&db);
     let config = TEST_CONFIG;
     let ctx = AssistContext::new(sema, &config, frange);
-    let mut acc = Assists::new(&ctx, AssistResolveStrategy::All);
+    let mut acc = Assists::new(&ctx, true);
     handler(&mut acc, &ctx);
     let mut res = acc.finish();
 
@@ -189,7 +186,7 @@ fn assist_order_field_struct() {
     let (before_cursor_pos, before) = extract_offset(before);
     let (db, file_id) = with_single_file(&before);
     let frange = FileRange { file_id, range: TextRange::empty(before_cursor_pos) };
-    let assists = Assist::get(&db, &TEST_CONFIG, AssistResolveStrategy::None, frange);
+    let assists = Assist::get(&db, &TEST_CONFIG, false, frange);
     let mut assists = assists.iter();
 
     assert_eq!(assists.next().expect("expected assist").label, "Change visibility to pub(crate)");
@@ -214,7 +211,7 @@ pub fn test_some_range(a: int) -> bool {
 "#,
     );
 
-    let assists = Assist::get(&db, &TEST_CONFIG, AssistResolveStrategy::None, frange);
+    let assists = Assist::get(&db, &TEST_CONFIG, false, frange);
     let expected = labels(&assists);
 
     expect![[r#"
@@ -243,7 +240,7 @@ pub fn test_some_range(a: int) -> bool {
         let mut cfg = TEST_CONFIG;
         cfg.allowed = Some(vec![AssistKind::Refactor]);
 
-        let assists = Assist::get(&db, &cfg, AssistResolveStrategy::None, frange);
+        let assists = Assist::get(&db, &cfg, false, frange);
         let expected = labels(&assists);
 
         expect![[r#"
@@ -258,7 +255,7 @@ pub fn test_some_range(a: int) -> bool {
     {
         let mut cfg = TEST_CONFIG;
         cfg.allowed = Some(vec![AssistKind::RefactorExtract]);
-        let assists = Assist::get(&db, &cfg, AssistResolveStrategy::None, frange);
+        let assists = Assist::get(&db, &cfg, false, frange);
         let expected = labels(&assists);
 
         expect![[r#"
@@ -271,250 +268,9 @@ pub fn test_some_range(a: int) -> bool {
     {
         let mut cfg = TEST_CONFIG;
         cfg.allowed = Some(vec![AssistKind::QuickFix]);
-        let assists = Assist::get(&db, &cfg, AssistResolveStrategy::None, frange);
+        let assists = Assist::get(&db, &cfg, false, frange);
         let expected = labels(&assists);
 
         expect![[r#""#]].assert_eq(&expected);
     }
 }
-
-#[test]
-fn various_resolve_strategies() {
-    let (db, frange) = RootDatabase::with_range(
-        r#"
-pub fn test_some_range(a: int) -> bool {
-    if let 2..6 = $05$0 {
-        true
-    } else {
-        false
-    }
-}
-"#,
-    );
-
-    let mut cfg = TEST_CONFIG;
-    cfg.allowed = Some(vec![AssistKind::RefactorExtract]);
-
-    {
-        let assists = Assist::get(&db, &cfg, AssistResolveStrategy::None, frange);
-        assert_eq!(2, assists.len());
-        let mut assists = assists.into_iter();
-
-        let extract_into_variable_assist = assists.next().unwrap();
-        expect![[r#"
-            Assist {
-                id: AssistId(
-                    "extract_variable",
-                    RefactorExtract,
-                ),
-                label: "Extract into variable",
-                group: None,
-                target: 59..60,
-                source_change: None,
-            }
-        "#]]
-        .assert_debug_eq(&extract_into_variable_assist);
-
-        let extract_into_function_assist = assists.next().unwrap();
-        expect![[r#"
-            Assist {
-                id: AssistId(
-                    "extract_function",
-                    RefactorExtract,
-                ),
-                label: "Extract into function",
-                group: None,
-                target: 59..60,
-                source_change: None,
-            }
-        "#]]
-        .assert_debug_eq(&extract_into_function_assist);
-    }
-
-    {
-        let assists = Assist::get(
-            &db,
-            &cfg,
-            AssistResolveStrategy::Single(SingleResolve {
-                assist_id: "SOMETHING_MISMATCHING".to_string(),
-                assist_kind: AssistKind::RefactorExtract,
-            }),
-            frange,
-        );
-        assert_eq!(2, assists.len());
-        let mut assists = assists.into_iter();
-
-        let extract_into_variable_assist = assists.next().unwrap();
-        expect![[r#"
-            Assist {
-                id: AssistId(
-                    "extract_variable",
-                    RefactorExtract,
-                ),
-                label: "Extract into variable",
-                group: None,
-                target: 59..60,
-                source_change: None,
-            }
-        "#]]
-        .assert_debug_eq(&extract_into_variable_assist);
-
-        let extract_into_function_assist = assists.next().unwrap();
-        expect![[r#"
-            Assist {
-                id: AssistId(
-                    "extract_function",
-                    RefactorExtract,
-                ),
-                label: "Extract into function",
-                group: None,
-                target: 59..60,
-                source_change: None,
-            }
-        "#]]
-        .assert_debug_eq(&extract_into_function_assist);
-    }
-
-    {
-        let assists = Assist::get(
-            &db,
-            &cfg,
-            AssistResolveStrategy::Single(SingleResolve {
-                assist_id: "extract_variable".to_string(),
-                assist_kind: AssistKind::RefactorExtract,
-            }),
-            frange,
-        );
-        assert_eq!(2, assists.len());
-        let mut assists = assists.into_iter();
-
-        let extract_into_variable_assist = assists.next().unwrap();
-        expect![[r#"
-            Assist {
-                id: AssistId(
-                    "extract_variable",
-                    RefactorExtract,
-                ),
-                label: "Extract into variable",
-                group: None,
-                target: 59..60,
-                source_change: Some(
-                    SourceChange {
-                        source_file_edits: {
-                            FileId(
-                                0,
-                            ): TextEdit {
-                                indels: [
-                                    Indel {
-                                        insert: "let $0var_name = 5;\n    ",
-                                        delete: 45..45,
-                                    },
-                                    Indel {
-                                        insert: "var_name",
-                                        delete: 59..60,
-                                    },
-                                ],
-                            },
-                        },
-                        file_system_edits: [],
-                        is_snippet: true,
-                    },
-                ),
-            }
-        "#]]
-        .assert_debug_eq(&extract_into_variable_assist);
-
-        let extract_into_function_assist = assists.next().unwrap();
-        expect![[r#"
-            Assist {
-                id: AssistId(
-                    "extract_function",
-                    RefactorExtract,
-                ),
-                label: "Extract into function",
-                group: None,
-                target: 59..60,
-                source_change: None,
-            }
-        "#]]
-        .assert_debug_eq(&extract_into_function_assist);
-    }
-
-    {
-        let assists = Assist::get(&db, &cfg, AssistResolveStrategy::All, frange);
-        assert_eq!(2, assists.len());
-        let mut assists = assists.into_iter();
-
-        let extract_into_variable_assist = assists.next().unwrap();
-        expect![[r#"
-            Assist {
-                id: AssistId(
-                    "extract_variable",
-                    RefactorExtract,
-                ),
-                label: "Extract into variable",
-                group: None,
-                target: 59..60,
-                source_change: Some(
-                    SourceChange {
-                        source_file_edits: {
-                            FileId(
-                                0,
-                            ): TextEdit {
-                                indels: [
-                                    Indel {
-                                        insert: "let $0var_name = 5;\n    ",
-                                        delete: 45..45,
-                                    },
-                                    Indel {
-                                        insert: "var_name",
-                                        delete: 59..60,
-                                    },
-                                ],
-                            },
-                        },
-                        file_system_edits: [],
-                        is_snippet: true,
-                    },
-                ),
-            }
-        "#]]
-        .assert_debug_eq(&extract_into_variable_assist);
-
-        let extract_into_function_assist = assists.next().unwrap();
-        expect![[r#"
-            Assist {
-                id: AssistId(
-                    "extract_function",
-                    RefactorExtract,
-                ),
-                label: "Extract into function",
-                group: None,
-                target: 59..60,
-                source_change: Some(
-                    SourceChange {
-                        source_file_edits: {
-                            FileId(
-                                0,
-                            ): TextEdit {
-                                indels: [
-                                    Indel {
-                                        insert: "fun_name()",
-                                        delete: 59..60,
-                                    },
-                                    Indel {
-                                        insert: "\n\nfn $0fun_name() -> i32 {\n    5\n}",
-                                        delete: 110..110,
-                                    },
-                                ],
-                            },
-                        },
-                        file_system_edits: [],
-                        is_snippet: true,
-                    },
-                ),
-            }
-        "#]]
-        .assert_debug_eq(&extract_into_function_assist);
-    }
-}