Repository: rust-analyzer/rust-analyzer
Pull request #5354 (2020-07-14 01:50:03)

Add opt-in mimalloc feature
This adds a `mimalloc` feature to use [mimalloc](https://github.com/microsoft/mimalloc) via [mimalloc_rust](https://github.com/purpleprotocol/mimalloc_rust), and a corresponding `cargo xtask install --server --mimalloc`.

In my tests on Linux, mimalloc seems to run consistently faster than jemalloc and uses only slightly more memory in `analysis-stats` on chalk. Also, builds with mimalloc produce a binary 3MB smaller than builds with jemalloc.

A summary of `env time -v cargo run --release -p rust-analyzer -- analysis-stats ../chalk/` runs on NixOS master on an Intel 4790K in VMware Workstation:

<table>
<tr>
<td></td><td>self-reported time</td><td>elapsed time</td><td>max RSS</td>
</tr>
<tr><td>glibc 2.30 run 1</td><td>225.1 sec</td><td>3:46.91</td><td>1075208</td></tr>
<tr><td>glibc 2.30 run 2</td><td>228.4 sec</td><td>3:50.13</td><td>1074996</td></tr>
<tr><td>jemalloc run 1</td><td>201.8 sec</td><td>3:23.03</td><td>1055960</td></tr>
<tr><td>jemalloc run 2</td><td>199.2 sec</td><td>3:20.41</td><td>1065040</td></tr>
<tr><td>mimalloc run 1</td><td>188.6 sec</td><td>3:09.77</td><td>1105584</td></tr>
<tr><td>mimalloc run 2</td><td>185.0 sec</td><td>3:06.23</td><td>1108132</td></tr>
<tr><td>mimalloc + lto run 1</td><td>160.7 sec</td><td>2:41.80</td><td>1106076</td></tr>
<tr><td>mimalloc + lto run 2</td><td>162.2 sec</td><td>2:43.31</td><td>1104268</td></tr>
</tr>
</table>

I included an `lto = true; codegen-units = 1` run out of curiosity, this PR doesn't enable it.

<details>
<summary>analysis-stats benchmark runs</summary>

## default

```
# env time -v cargo run --release -p rust-analyzer -- analysis-stats ../chalk/
    Finished release [optimized] target(s) in 0.10s
     Running `target/release/rust-analyzer analysis-stats ../chalk/`
[ERROR ra_project_model] cyclic dependency chalk-integration -> chalk-engine
[ERROR ra_project_model] cyclic dependency chalk-recursive -> chalk-integration
[ERROR ra_project_model] cyclic dependency chalk-solve -> chalk-integration
Database loaded 333.880345ms
Crates in this dir: 11
Total modules found: 159
Total declarations: 2631
Total functions: 1947
Item Collection: 10.176299461s, 0b allocated 0b resident
Total expressions: 57094
Expressions of unknown type: 2938 (5%)
Expressions of partially unknown type: 2427 (4%)
Type mismatches: 232
Inference: 214.968806927s, 0b allocated 0b resident
Total: 225.145114417s, 0b allocated 0b resident
        Command being timed: "cargo run --release -p rust-analyzer -- analysis-stats ../chalk/"
        User time (seconds): 225.34
        System time (seconds): 1.49
        Percent of CPU this job got: 99%
        Elapsed (wall clock) time (h:mm:ss or m:ss): 3:46.91
        Average shared text size (kbytes): 0
        Average unshared data size (kbytes): 0
        Average stack size (kbytes): 0
        Average total size (kbytes): 0
        Maximum resident set size (kbytes): 1075208
        Average resident set size (kbytes): 0
        Major (requiring I/O) page faults: 6
        Minor (reclaiming a frame) page faults: 294711
        Voluntary context switches: 365
        Involuntary context switches: 3273
        Swaps: 0
        File system inputs: 2904
        File system outputs: 0
        Socket messages sent: 0
        Socket messages received: 0
        Signals delivered: 0
        Page size (bytes): 4096
        Exit status: 0
# env time -v cargo run --release -p rust-analyzer -- analysis-stats ../chalk/
    Finished release [optimized] target(s) in 0.10s
     Running `target/release/rust-analyzer analysis-stats ../chalk/`
[ERROR ra_project_model] cyclic dependency chalk-integration -> chalk-engine
[ERROR ra_project_model] cyclic dependency chalk-recursive -> chalk-integration
[ERROR ra_project_model] cyclic dependency chalk-solve -> chalk-integration
Database loaded 332.711598ms
Crates in this dir: 11
Total modules found: 159
Total declarations: 2631
Total functions: 1947
Item Collection: 9.895020518s, 0b allocated 0b resident
Total expressions: 57094
Expressions of unknown type: 2938 (5%)
Expressions of partially unknown type: 2427 (4%)
Type mismatches: 232
Inference: 218.5001697s, 0b allocated 0b resident
Total: 228.39519833s, 0b allocated 0b resident
        Command being timed: "cargo run --release -p rust-analyzer -- analysis-stats ../chalk/"
        User time (seconds): 228.26
        System time (seconds): 1.75
        Percent of CPU this job got: 99%
        Elapsed (wall clock) time (h:mm:ss or m:ss): 3:50.13
        Average shared text size (kbytes): 0
        Average unshared data size (kbytes): 0
        Average stack size (kbytes): 0
        Average total size (kbytes): 0
        Maximum resident set size (kbytes): 1074996
        Average resident set size (kbytes): 0
        Major (requiring I/O) page faults: 9
        Minor (reclaiming a frame) page faults: 294748
        Voluntary context switches: 330
        Involuntary context switches: 1561
        Swaps: 0
        File system inputs: 12608
        File system outputs: 0
        Socket messages sent: 0
        Socket messages received: 0
        Signals delivered: 0
        Page size (bytes): 4096
        Exit status: 0
```

## jemalloc
```
# env time -v cargo run --release -p rust-analyzer -- analysis-stats ../chalk/
    Finished release [optimized] target(s) in 0.11s
     Running `target/release/rust-analyzer analysis-stats ../chalk/`
[ERROR ra_project_model] cyclic dependency chalk-integration -> chalk-engine
[ERROR ra_project_model] cyclic dependency chalk-recursive -> chalk-integration
[ERROR ra_project_model] cyclic dependency chalk-solve -> chalk-integration
Database loaded 356.090374ms
Crates in this dir: 11
Total modules found: 159
Total declarations: 2631
Total functions: 1947
Item Collection: 10.176550183s, 439mb allocated 465mb resident
Total expressions: 57094
Expressions of unknown type: 2938 (5%)
Expressions of partially unknown type: 2427 (4%)
Type mismatches: 232
Inference: 191.607201827s, 993mb allocated 1036mb resident
Total: 201.783937913s, 993mb allocated 1036mb resident
        Command being timed: "cargo run --release -p rust-analyzer -- analysis-stats ../chalk/"
        User time (seconds): 201.07
        System time (seconds): 1.89
        Percent of CPU this job got: 99%
        Elapsed (wall clock) time (h:mm:ss or m:ss): 3:23.03
        Average shared text size (kbytes): 0
        Average unshared data size (kbytes): 0
        Average stack size (kbytes): 0
        Average total size (kbytes): 0
        Maximum resident set size (kbytes): 1055960
        Average resident set size (kbytes): 0
        Major (requiring I/O) page faults: 0
        Minor (reclaiming a frame) page faults: 357755
        Voluntary context switches: 240
        Involuntary context switches: 1889
        Swaps: 0
        File system inputs: 256
        File system outputs: 0
        Socket messages sent: 0
        Socket messages received: 0
        Signals delivered: 0
        Page size (bytes): 4096
        Exit status: 0
# env time -v cargo run --release -p rust-analyzer -- analysis-stats ../chalk/
    Finished release [optimized] target(s) in 0.10s
     Running `target/release/rust-analyzer analysis-stats ../chalk/`
[ERROR ra_project_model] cyclic dependency chalk-integration -> chalk-engine
[ERROR ra_project_model] cyclic dependency chalk-recursive -> chalk-integration
[ERROR ra_project_model] cyclic dependency chalk-solve -> chalk-integration
Database loaded 317.917622ms
Crates in this dir: 11
Total modules found: 159
Total declarations: 2631
Total functions: 1947
Item Collection: 9.902142185s, 439mb allocated 463mb resident
Total expressions: 57094
Expressions of unknown type: 2938 (5%)
Expressions of partially unknown type: 2427 (4%)
Type mismatches: 232
Inference: 189.295317017s, 993mb allocated 1046mb resident
Total: 199.197555943s, 993mb allocated 1046mb resident
        Command being timed: "cargo run --release -p rust-analyzer -- analysis-stats ../chalk/"
        User time (seconds): 198.64
        System time (seconds): 1.67
        Percent of CPU this job got: 99%
        Elapsed (wall clock) time (h:mm:ss or m:ss): 3:20.41
        Average shared text size (kbytes): 0
        Average unshared data size (kbytes): 0
        Average stack size (kbytes): 0
        Average total size (kbytes): 0
        Maximum resident set size (kbytes): 1065040
        Average resident set size (kbytes): 0
        Major (requiring I/O) page faults: 0
        Minor (reclaiming a frame) page faults: 369013
        Voluntary context switches: 243
        Involuntary context switches: 2835
        Swaps: 0
        File system inputs: 0
        File system outputs: 0
        Socket messages sent: 0
        Socket messages received: 0
        Signals delivered: 0
        Page size (bytes): 4096
        Exit status: 0
```

## mimalloc
```
# env time -v cargo run --release -p rust-analyzer -- analysis-stats ../chalk/
    Finished release [optimized] target(s) in 0.12s
     Running `target/release/rust-analyzer analysis-stats ../chalk/`
[ERROR ra_project_model] cyclic dependency chalk-integration -> chalk-engine
[ERROR ra_project_model] cyclic dependency chalk-recursive -> chalk-integration
[ERROR ra_project_model] cyclic dependency chalk-solve -> chalk-integration
Database loaded 332.116806ms
Crates in this dir: 11
Total modules found: 159
Total declarations: 2631
Total functions: 1947
Item Collection: 9.796643695s, 0b allocated 0b resident
Total expressions: 57094
Expressions of unknown type: 2938 (5%)
Expressions of partially unknown type: 2427 (4%)
Type mismatches: 232
Inference: 178.82132362s, 0b allocated 0b resident
Total: 188.617975605s, 0b allocated 0b resident
        Command being timed: "cargo run --release -p rust-analyzer -- analysis-stats ../chalk/"
        User time (seconds): 187.70
        System time (seconds): 1.97
        Percent of CPU this job got: 99%
        Elapsed (wall clock) time (h:mm:ss or m:ss): 3:09.77
        Average shared text size (kbytes): 0
        Average unshared data size (kbytes): 0
        Average stack size (kbytes): 0
        Average total size (kbytes): 0
        Maximum resident set size (kbytes): 1105584
        Average resident set size (kbytes): 0
        Major (requiring I/O) page faults: 0
        Minor (reclaiming a frame) page faults: 296481
        Voluntary context switches: 222
        Involuntary context switches: 1868
        Swaps: 0
        File system inputs: 256
        File system outputs: 0
        Socket messages sent: 0
        Socket messages received: 0
        Signals delivered: 0
        Page size (bytes): 4096
        Exit status: 0
# env time -v cargo run --release -p rust-analyzer -- analysis-stats ../chalk/
    Finished release [optimized] target(s) in 0.13s
     Running `target/release/rust-analyzer analysis-stats ../chalk/`
[ERROR ra_project_model] cyclic dependency chalk-integration -> chalk-engine
[ERROR ra_project_model] cyclic dependency chalk-recursive -> chalk-integration
[ERROR ra_project_model] cyclic dependency chalk-solve -> chalk-integration
Database loaded 320.046776ms
Crates in this dir: 11
Total modules found: 159
Total declarations: 2631
Total functions: 1947
Item Collection: 9.287690124s, 0b allocated 0b resident
Total expressions: 57094
Expressions of unknown type: 2938 (5%)
Expressions of partially unknown type: 2427 (4%)
Type mismatches: 232
Inference: 175.710939697s, 0b allocated 0b resident
Total: 184.998640033s, 0b allocated 0b resident
        Command being timed: "cargo run --release -p rust-analyzer -- analysis-stats ../chalk/"
        User time (seconds): 184.38
        System time (seconds): 1.81
        Percent of CPU this job got: 99%
        Elapsed (wall clock) time (h:mm:ss or m:ss): 3:06.23
        Average shared text size (kbytes): 0
        Average unshared data size (kbytes): 0
        Average stack size (kbytes): 0
        Average total size (kbytes): 0
        Maximum resident set size (kbytes): 1108132
        Average resident set size (kbytes): 0
        Major (requiring I/O) page faults: 0
        Minor (reclaiming a frame) page faults: 297055
        Voluntary context switches: 374
        Involuntary context switches: 2374
        Swaps: 0
        File system inputs: 0
        File system outputs: 0
        Socket messages sent: 0
        Socket messages received: 0
        Signals delivered: 0
        Page size (bytes): 4096
        Exit status: 0
```

## mimalloc + lto
```
# env time -v cargo run --release -p rust-analyzer -- analysis-stats ../chalk/
    Finished release [optimized] target(s) in 0.11s
     Running `target/release/rust-analyzer analysis-stats ../chalk/`
[ERROR ra_project_model] cyclic dependency chalk-integration -> chalk-engine
[ERROR ra_project_model] cyclic dependency chalk-recursive -> chalk-integration
[ERROR ra_project_model] cyclic dependency chalk-solve -> chalk-integration
Database loaded 369.600196ms
Crates in this dir: 11
Total modules found: 159
Total declarations: 2631
Total functions: 1947
Item Collection: 7.572726834s, 0b allocated 0b resident
Total expressions: 57094
Expressions of unknown type: 2938 (5%)
Expressions of partially unknown type: 2427 (4%)
Type mismatches: 232
Inference: 153.090899101s, 0b allocated 0b resident
Total: 160.663635235s, 0b allocated 0b resident
        Command being timed: "cargo run --release -p rust-analyzer -- analysis-stats ../chalk/"
        User time (seconds): 160.01
        System time (seconds): 1.70
        Percent of CPU this job got: 99%
        Elapsed (wall clock) time (h:mm:ss or m:ss): 2:41.80
        Average shared text size (kbytes): 0
        Average unshared data size (kbytes): 0
        Average stack size (kbytes): 0
        Average total size (kbytes): 0
        Maximum resident set size (kbytes): 1106076
        Average resident set size (kbytes): 0
        Major (requiring I/O) page faults: 1
        Minor (reclaiming a frame) page faults: 296610
        Voluntary context switches: 209
        Involuntary context switches: 2798
        Swaps: 0
        File system inputs: 8
        File system outputs: 0
        Socket messages sent: 0
        Socket messages received: 0
        Signals delivered: 0
        Page size (bytes): 4096
        Exit status: 0
# env time -v cargo run --release -p rust-analyzer -- analysis-stats ../chalk/
    Finished release [optimized] target(s) in 0.10s
     Running `target/release/rust-analyzer analysis-stats ../chalk/`
[ERROR ra_project_model] cyclic dependency chalk-integration -> chalk-engine
[ERROR ra_project_model] cyclic dependency chalk-recursive -> chalk-integration
[ERROR ra_project_model] cyclic dependency chalk-solve -> chalk-integration
Database loaded 334.630658ms
Crates in this dir: 11
Total modules found: 159
Total declarations: 2631
Total functions: 1947
Item Collection: 7.71699197s, 0b allocated 0b resident
Total expressions: 57094
Expressions of unknown type: 2938 (5%)
Expressions of partially unknown type: 2427 (4%)
Type mismatches: 232
Inference: 154.50351318s, 0b allocated 0b resident
Total: 162.220513775s, 0b allocated 0b resident
        Command being timed: "cargo run --release -p rust-analyzer -- analysis-stats ../chalk/"
        User time (seconds): 161.52
        System time (seconds): 1.74
        Percent of CPU this job got: 99%
        Elapsed (wall clock) time (h:mm:ss or m:ss): 2:43.31
        Average shared text size (kbytes): 0
        Average unshared data size (kbytes): 0
        Average stack size (kbytes): 0
        Average total size (kbytes): 0
        Maximum resident set size (kbytes): 1104268
        Average resident set size (kbytes): 0
        Major (requiring I/O) page faults: 0
        Minor (reclaiming a frame) page faults: 296183
        Voluntary context switches: 200
        Involuntary context switches: 1666
        Swaps: 0
        File system inputs: 0
        File system outputs: 0
        Socket messages sent: 0
        Socket messages received: 0
        Signals delivered: 0
        Page size (bytes): 4096
        Exit status: 0
```
</details>

matklad: Does mimalloc have a way to get total number of bytes allocated (what we use jemalloc_ctl for)? If it has, then we should just replace jemalloc with mimalloc. 

Patch:
diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index c915b9d144..3adf9e6ca0 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -34,7 +34,7 @@ jobs:
     name: Rust
     runs-on: ${{ matrix.os }}
     env:
-      CC: deny_c
+     CC: deny_c
 
     strategy:
       fail-fast: false
@@ -64,16 +64,20 @@ jobs:
     - if: matrix.os == 'ubuntu-latest'
       run: sudo chown -R $(whoami):$(id -ng) ~/.cargo/
 
-    - name: Cache cargo directories
-      uses: actions/cache@v2
+    - name: Cache cargo registry
+      uses: actions/cache@v1
       with:
-        path: |
-          ~/.cargo/registry
-          ~/.cargo/git
-        key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
+        path: ~/.cargo/registry
+        key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }}
+
+    - name: Cache cargo index
+      uses: actions/cache@v1
+      with:
+        path: ~/.cargo/git
+        key: ${{ runner.os }}-cargo-index-${{ hashFiles('**/Cargo.lock') }}
 
     - name: Cache cargo target dir
-      uses: actions/cache@v2
+      uses: actions/cache@v1
       with:
         path: target
         key: ${{ runner.os }}-cargo-build-target-${{ hashFiles('**/Cargo.lock') }}
@@ -91,36 +95,6 @@ jobs:
       if: matrix.os == 'windows-latest'
       run: Remove-Item ./target/debug/xtask.exe, ./target/debug/deps/xtask.exe
 
-  # Weird target to catch non-portable code
-  rust-power:
-    name: Rust Power
-    runs-on: ubuntu-latest
-
-    steps:
-    - name: Checkout repository
-      uses: actions/checkout@v2
-
-    - name: Install Rust toolchain
-      uses: actions-rs/toolchain@v1
-      with:
-        toolchain: stable
-        profile: minimal
-        override: true
-        target: 'powerpc-unknown-linux-gnu'
-
-    - run: sudo chown -R $(whoami):$(id -ng) ~/.cargo/
-
-    - name: Cache cargo directories
-      uses: actions/cache@v2
-      with:
-        path: |
-          ~/.cargo/registry
-          ~/.cargo/git
-        key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
-
-    - name: Check
-      run: cargo check --target=powerpc-unknown-linux-gnu --all-targets
-
   typescript:
     name: TypeScript
     strategy:
@@ -129,7 +103,7 @@ jobs:
         os: [ubuntu-latest, windows-latest, macos-latest]
 
     runs-on: ${{ matrix.os }}
-
+    
     steps:
     - name: Checkout repository
       uses: actions/checkout@v2
diff --git a/Cargo.lock b/Cargo.lock
index 11eb9a8233..a05c62e45e 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -214,6 +214,15 @@ dependencies = [
  "bitflags",
 ]
 
+[[package]]
+name = "cmake"
+version = "0.1.44"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0e56268c17a6248366d66d4a47a3381369d068cce8409bb1716ed77ea32163bb"
+dependencies = [
+ "cc",
+]
+
 [[package]]
 name = "console"
 version = "0.11.3"
@@ -674,6 +683,15 @@ dependencies = [
  "winapi 0.3.9",
 ]
 
+[[package]]
+name = "libmimalloc-sys"
+version = "0.1.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a27252ec1d0c4e0dd6142cbc572da50b363ab56fc334f7aa8fadf295b2e24e74"
+dependencies = [
+ "cmake",
+]
+
 [[package]]
 name = "linked-hash-map"
 version = "0.5.3"
@@ -712,9 +730,9 @@ dependencies = [
 
 [[package]]
 name = "lsp-types"
-version = "0.76.1"
+version = "0.76.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "55cfa1593e04985972e018890b2e1a9ed25c71efc800067fbf0990a9432421c8"
+checksum = "af5586f0631c7f7826c3ea39377c326d7b4623138be7ab1204dab22e47717449"
 dependencies = [
  "base64",
  "bitflags",
@@ -770,6 +788,15 @@ dependencies = [
  "autocfg",
 ]
 
+[[package]]
+name = "mimalloc"
+version = "0.1.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c52de2069999f01bd26436564dbe7de3a87898feeb7a0d0ff9eb20a05bb7ca0"
+dependencies = [
+ "libmimalloc-sys",
+]
+
 [[package]]
 name = "miniz_oxide"
 version = "0.4.0"
@@ -1248,6 +1275,7 @@ dependencies = [
  "backtrace",
  "jemalloc-ctl",
  "jemallocator",
+ "mimalloc",
  "once_cell",
  "ra_arena",
 ]
diff --git a/crates/expect/Cargo.toml b/crates/expect/Cargo.toml
index 77775630dc..f8fa29e4e9 100644
--- a/crates/expect/Cargo.toml
+++ b/crates/expect/Cargo.toml
@@ -3,7 +3,6 @@ name = "expect"
 version = "0.1.0"
 authors = ["rust-analyzer developers"]
 edition = "2018"
-license = "MIT OR Apache-2.0"
 
 [lib]
 doctest = false
diff --git a/crates/flycheck/Cargo.toml b/crates/flycheck/Cargo.toml
index bea4856948..dc26b8ce76 100644
--- a/crates/flycheck/Cargo.toml
+++ b/crates/flycheck/Cargo.toml
@@ -3,7 +3,6 @@ edition = "2018"
 name = "flycheck"
 version = "0.1.0"
 authors = ["rust-analyzer developers"]
-license = "MIT OR Apache-2.0"
 
 [lib]
 doctest = false
diff --git a/crates/paths/Cargo.toml b/crates/paths/Cargo.toml
index cbe2c26e20..646ee7fd54 100644
--- a/crates/paths/Cargo.toml
+++ b/crates/paths/Cargo.toml
@@ -3,7 +3,6 @@ name = "paths"
 version = "0.1.0"
 authors = ["rust-analyzer developers"]
 edition = "2018"
-license = "MIT OR Apache-2.0"
 
 [lib]
 doctest = false
diff --git a/crates/ra_arena/Cargo.toml b/crates/ra_arena/Cargo.toml
index 66c3738f4d..d287dbb735 100644
--- a/crates/ra_arena/Cargo.toml
+++ b/crates/ra_arena/Cargo.toml
@@ -3,7 +3,6 @@ edition = "2018"
 name = "ra_arena"
 version = "0.1.0"
 authors = ["rust-analyzer developers"]
-license = "MIT OR Apache-2.0"
 
 [lib]
 doctest = false
diff --git a/crates/ra_assists/Cargo.toml b/crates/ra_assists/Cargo.toml
index bd2905f080..3bcf58ba4b 100644
--- a/crates/ra_assists/Cargo.toml
+++ b/crates/ra_assists/Cargo.toml
@@ -3,7 +3,6 @@ edition = "2018"
 name = "ra_assists"
 version = "0.1.0"
 authors = ["rust-analyzer developers"]
-license = "MIT OR Apache-2.0"
 
 [lib]
 doctest = false
diff --git a/crates/ra_assists/src/handlers/add_missing_impl_members.rs b/crates/ra_assists/src/handlers/add_missing_impl_members.rs
index f185e61e59..d6aaf53f10 100644
--- a/crates/ra_assists/src/handlers/add_missing_impl_members.rs
+++ b/crates/ra_assists/src/handlers/add_missing_impl_members.rs
@@ -158,9 +158,6 @@ fn add_missing_impl_members_inner(
             .map(|it| ast_transform::apply(&*ast_transform, it))
             .map(|it| match it {
                 ast::AssocItem::FnDef(def) => ast::AssocItem::FnDef(add_body(def)),
-                ast::AssocItem::TypeAliasDef(def) => {
-                    ast::AssocItem::TypeAliasDef(def.remove_bounds())
-                }
                 _ => it,
             })
             .map(|it| edit::remove_attrs_and_docs(&it));
@@ -684,28 +681,6 @@ impl Foo<T> for S<T> {
     fn bar(&self, this: &T, that: &Self) {
         ${0:todo!()}
     }
-}"#,
-        )
-    }
-
-    #[test]
-    fn test_assoc_type_bounds_are_removed() {
-        check_assist(
-            add_missing_impl_members,
-            r#"
-trait Tr {
-    type Ty: Copy + 'static;
-}
-
-impl Tr for ()<|> {
-}"#,
-            r#"
-trait Tr {
-    type Ty: Copy + 'static;
-}
-
-impl Tr for () {
-    $0type Ty;
 }"#,
         )
     }
diff --git a/crates/ra_cfg/Cargo.toml b/crates/ra_cfg/Cargo.toml
index 6425cd6d6f..9165076a58 100644
--- a/crates/ra_cfg/Cargo.toml
+++ b/crates/ra_cfg/Cargo.toml
@@ -3,7 +3,6 @@ edition = "2018"
 name = "ra_cfg"
 version = "0.1.0"
 authors = ["rust-analyzer developers"]
-license = "MIT OR Apache-2.0"
 
 [lib]
 doctest = false
diff --git a/crates/ra_db/Cargo.toml b/crates/ra_db/Cargo.toml
index 5f334d04f1..889142442a 100644
--- a/crates/ra_db/Cargo.toml
+++ b/crates/ra_db/Cargo.toml
@@ -3,7 +3,6 @@ edition = "2018"
 name = "ra_db"
 version = "0.1.0"
 authors = ["rust-analyzer developers"]
-license = "MIT OR Apache-2.0"
 
 [lib]
 doctest = false
diff --git a/crates/ra_fmt/Cargo.toml b/crates/ra_fmt/Cargo.toml
index b4ef93f2b2..e9d057afc0 100644
--- a/crates/ra_fmt/Cargo.toml
+++ b/crates/ra_fmt/Cargo.toml
@@ -4,7 +4,6 @@ name = "ra_fmt"
 version = "0.1.0"
 authors = ["rust-analyzer developers"]
 publish = false
-license = "MIT OR Apache-2.0"
 
 [lib]
 doctest = false
diff --git a/crates/ra_hir/Cargo.toml b/crates/ra_hir/Cargo.toml
index c260bb193c..512676c996 100644
--- a/crates/ra_hir/Cargo.toml
+++ b/crates/ra_hir/Cargo.toml
@@ -3,7 +3,6 @@ edition = "2018"
 name = "ra_hir"
 version = "0.1.0"
 authors = ["rust-analyzer developers"]
-license = "MIT OR Apache-2.0"
 
 [lib]
 doctest = false
diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs
index 42c9ca189f..9222009fec 100644
--- a/crates/ra_hir/src/code_model.rs
+++ b/crates/ra_hir/src/code_model.rs
@@ -25,8 +25,11 @@ use hir_expand::{
 use hir_ty::{
     autoderef,
     display::{HirDisplayError, HirFormatter},
-    method_resolution, ApplicationTy, Canonical, GenericPredicate, InEnvironment, Substs,
-    TraitEnvironment, Ty, TyDefId, TypeCtor,
+    expr::ExprValidator,
+    method_resolution,
+    unsafe_validation::UnsafeValidator,
+    ApplicationTy, Canonical, GenericPredicate, InEnvironment, Substs, TraitEnvironment, Ty,
+    TyDefId, TypeCtor,
 };
 use ra_db::{CrateId, Edition, FileId};
 use ra_prof::profile;
@@ -677,7 +680,13 @@ impl Function {
     }
 
     pub fn diagnostics(self, db: &dyn HirDatabase, sink: &mut DiagnosticSink) {
-        hir_ty::diagnostics::validate_body(db, self.id.into(), sink)
+        let _p = profile("Function::diagnostics");
+        let infer = db.infer(self.id.into());
+        infer.add_diagnostics(db, self.id, sink);
+        let mut validator = ExprValidator::new(self.id, infer.clone(), sink);
+        validator.validate_body(db);
+        let mut validator = UnsafeValidator::new(self.id, infer, sink);
+        validator.validate_body(db);
     }
 }
 
diff --git a/crates/ra_hir/src/source_analyzer.rs b/crates/ra_hir/src/source_analyzer.rs
index d76345525f..f74b78b237 100644
--- a/crates/ra_hir/src/source_analyzer.rs
+++ b/crates/ra_hir/src/source_analyzer.rs
@@ -18,7 +18,7 @@ use hir_def::{
 };
 use hir_expand::{hygiene::Hygiene, name::AsName, HirFileId, InFile};
 use hir_ty::{
-    diagnostics::{record_literal_missing_fields, record_pattern_missing_fields},
+    expr::{record_literal_missing_fields, record_pattern_missing_fields},
     InferenceResult, Substs, Ty,
 };
 use ra_syntax::{
diff --git a/crates/ra_hir_def/Cargo.toml b/crates/ra_hir_def/Cargo.toml
index 1ec619b428..cf5ebbaf5a 100644
--- a/crates/ra_hir_def/Cargo.toml
+++ b/crates/ra_hir_def/Cargo.toml
@@ -3,7 +3,6 @@ edition = "2018"
 name = "ra_hir_def"
 version = "0.1.0"
 authors = ["rust-analyzer developers"]
-license = "MIT OR Apache-2.0"
 
 [lib]
 doctest = false
diff --git a/crates/ra_hir_def/src/data.rs b/crates/ra_hir_def/src/data.rs
index 88a8ef9bff..aa335f1e30 100644
--- a/crates/ra_hir_def/src/data.rs
+++ b/crates/ra_hir_def/src/data.rs
@@ -27,7 +27,6 @@ pub struct FunctionData {
     /// can be called as a method.
     pub has_self_param: bool,
     pub is_unsafe: bool,
-    pub is_varargs: bool,
     pub visibility: RawVisibility,
 }
 
@@ -44,7 +43,6 @@ impl FunctionData {
             attrs: item_tree.attrs(ModItem::from(loc.id.value).into()).clone(),
             has_self_param: func.has_self_param,
             is_unsafe: func.is_unsafe,
-            is_varargs: func.is_varargs,
             visibility: item_tree[func.visibility].clone(),
         })
     }
diff --git a/crates/ra_hir_def/src/item_tree.rs b/crates/ra_hir_def/src/item_tree.rs
index da79d8ffd8..3e603bd558 100644
--- a/crates/ra_hir_def/src/item_tree.rs
+++ b/crates/ra_hir_def/src/item_tree.rs
@@ -503,7 +503,6 @@ pub struct Function {
     pub has_self_param: bool,
     pub is_unsafe: bool,
     pub params: Box<[TypeRef]>,
-    pub is_varargs: bool,
     pub ret_type: TypeRef,
     pub ast_id: FileAstId<ast::FnDef>,
 }
diff --git a/crates/ra_hir_def/src/item_tree/lower.rs b/crates/ra_hir_def/src/item_tree/lower.rs
index f79b8fca3d..0b1509e132 100644
--- a/crates/ra_hir_def/src/item_tree/lower.rs
+++ b/crates/ra_hir_def/src/item_tree/lower.rs
@@ -219,20 +219,21 @@ impl Ctx {
     fn lower_tuple_fields(&mut self, fields: &ast::TupleFieldDefList) -> IdRange<Field> {
         let start = self.next_field_idx();
         for (i, field) in fields.fields().enumerate() {
-            let data = self.lower_tuple_field(i, &field);
-            let idx = self.data().fields.alloc(data);
-            self.add_attrs(idx.into(), Attrs::new(&field, &self.hygiene));
+            if let Some(data) = self.lower_tuple_field(i, &field) {
+                let idx = self.data().fields.alloc(data);
+                self.add_attrs(idx.into(), Attrs::new(&field, &self.hygiene));
+            }
         }
         let end = self.next_field_idx();
         IdRange::new(start..end)
     }
 
-    fn lower_tuple_field(&mut self, idx: usize, field: &ast::TupleFieldDef) -> Field {
+    fn lower_tuple_field(&mut self, idx: usize, field: &ast::TupleFieldDef) -> Option<Field> {
         let name = Name::new_tuple_field(idx);
         let visibility = self.lower_visibility(field);
-        let type_ref = self.lower_type_ref_opt(field.type_ref());
+        let type_ref = self.lower_type_ref(&field.type_ref()?);
         let res = Field { name, type_ref, visibility };
-        res
+        Some(res)
     }
 
     fn lower_union(&mut self, union: &ast::UnionDef) -> Option<FileItemTreeId<Union>> {
@@ -313,14 +314,6 @@ impl Ctx {
                 params.push(type_ref);
             }
         }
-
-        let mut is_varargs = false;
-        if let Some(params) = func.param_list() {
-            if let Some(last) = params.params().last() {
-                is_varargs = last.dotdotdot_token().is_some();
-            }
-        }
-
         let ret_type = match func.ret_type().and_then(|rt| rt.type_ref()) {
             Some(type_ref) => TypeRef::from_ast(&self.body_ctx, type_ref),
             _ => TypeRef::unit(),
@@ -342,7 +335,6 @@ impl Ctx {
             has_self_param,
             is_unsafe: func.unsafe_token().is_some(),
             params: params.into_boxed_slice(),
-            is_varargs,
             ret_type,
             ast_id,
         };
diff --git a/crates/ra_hir_def/src/nameres/collector.rs b/crates/ra_hir_def/src/nameres/collector.rs
index e55cc1e55b..a35ac1024b 100644
--- a/crates/ra_hir_def/src/nameres/collector.rs
+++ b/crates/ra_hir_def/src/nameres/collector.rs
@@ -717,11 +717,6 @@ impl DefCollector<'_> {
         macro_call_id: MacroCallId,
         depth: usize,
     ) {
-        if depth > 100 {
-            mark::hit!(macro_expansion_overflow);
-            log::warn!("macro expansion is too deep");
-            return;
-        }
         let file_id: HirFileId = macro_call_id.as_file();
         let item_tree = self.db.item_tree(file_id);
         let mod_dir = self.mod_dirs[&module_id].clone();
diff --git a/crates/ra_hir_def/src/type_ref.rs b/crates/ra_hir_def/src/type_ref.rs
index e90b2a0b92..86a77b7046 100644
--- a/crates/ra_hir_def/src/type_ref.rs
+++ b/crates/ra_hir_def/src/type_ref.rs
@@ -63,7 +63,7 @@ pub enum TypeRef {
     Array(Box<TypeRef> /*, Expr*/),
     Slice(Box<TypeRef>),
     /// A fn pointer. Last element of the vector is the return type.
-    Fn(Vec<TypeRef>, bool /*varargs*/),
+    Fn(Vec<TypeRef>),
     // For
     ImplTrait(Vec<TypeBound>),
     DynTrait(Vec<TypeBound>),
@@ -118,12 +118,7 @@ impl TypeRef {
                     .and_then(|rt| rt.type_ref())
                     .map(|it| TypeRef::from_ast(ctx, it))
                     .unwrap_or_else(|| TypeRef::Tuple(Vec::new()));
-                let mut is_varargs = false;
                 let mut params = if let Some(pl) = inner.param_list() {
-                    if let Some(param) = pl.params().last() {
-                        is_varargs = param.dotdotdot_token().is_some();
-                    }
-
                     pl.params()
                         .map(|p| p.ascribed_type())
                         .map(|it| TypeRef::from_ast_opt(&ctx, it))
@@ -132,7 +127,7 @@ impl TypeRef {
                     Vec::new()
                 };
                 params.push(ret_ty);
-                TypeRef::Fn(params, is_varargs)
+                TypeRef::Fn(params)
             }
             // for types are close enough for our purposes to the inner type for now...
             ast::TypeRef::ForType(inner) => TypeRef::from_ast_opt(&ctx, inner.type_ref()),
@@ -163,9 +158,7 @@ impl TypeRef {
         fn go(type_ref: &TypeRef, f: &mut impl FnMut(&TypeRef)) {
             f(type_ref);
             match type_ref {
-                TypeRef::Fn(types, _) | TypeRef::Tuple(types) => {
-                    types.iter().for_each(|t| go(t, f))
-                }
+                TypeRef::Fn(types) | TypeRef::Tuple(types) => types.iter().for_each(|t| go(t, f)),
                 TypeRef::RawPtr(type_ref, _)
                 | TypeRef::Reference(type_ref, _)
                 | TypeRef::Array(type_ref)
diff --git a/crates/ra_hir_expand/Cargo.toml b/crates/ra_hir_expand/Cargo.toml
index 6da0e2a165..e5c9f3e997 100644
--- a/crates/ra_hir_expand/Cargo.toml
+++ b/crates/ra_hir_expand/Cargo.toml
@@ -3,7 +3,6 @@ edition = "2018"
 name = "ra_hir_expand"
 version = "0.1.0"
 authors = ["rust-analyzer developers"]
-license = "MIT OR Apache-2.0"
 
 [lib]
 doctest = false
diff --git a/crates/ra_hir_ty/Cargo.toml b/crates/ra_hir_ty/Cargo.toml
index 78f5e55bb8..90368220b9 100644
--- a/crates/ra_hir_ty/Cargo.toml
+++ b/crates/ra_hir_ty/Cargo.toml
@@ -3,7 +3,6 @@ edition = "2018"
 name = "ra_hir_ty"
 version = "0.1.0"
 authors = ["rust-analyzer developers"]
-license = "MIT OR Apache-2.0"
 
 [lib]
 doctest = false
diff --git a/crates/ra_hir_ty/src/diagnostics.rs b/crates/ra_hir_ty/src/diagnostics.rs
index d3ee9cf556..5b0dda634f 100644
--- a/crates/ra_hir_ty/src/diagnostics.rs
+++ b/crates/ra_hir_ty/src/diagnostics.rs
@@ -1,30 +1,13 @@
 //! FIXME: write short doc here
-mod expr;
-mod match_check;
-mod unsafe_check;
 
 use std::any::Any;
 
-use hir_def::DefWithBodyId;
-use hir_expand::diagnostics::{AstDiagnostic, Diagnostic, DiagnosticSink};
 use hir_expand::{db::AstDatabase, name::Name, HirFileId, InFile};
-use ra_prof::profile;
 use ra_syntax::{ast, AstNode, AstPtr, SyntaxNodePtr};
 use stdx::format_to;
 
-use crate::db::HirDatabase;
-
-pub use crate::diagnostics::expr::{record_literal_missing_fields, record_pattern_missing_fields};
-
-pub fn validate_body(db: &dyn HirDatabase, owner: DefWithBodyId, sink: &mut DiagnosticSink<'_>) {
-    let _p = profile("validate_body");
-    let infer = db.infer(owner);
-    infer.add_diagnostics(db, owner, sink);
-    let mut validator = expr::ExprValidator::new(owner, infer.clone(), sink);
-    validator.validate_body(db);
-    let mut validator = unsafe_check::UnsafeValidator::new(owner, infer, sink);
-    validator.validate_body(db);
-}
+pub use hir_def::{diagnostics::UnresolvedModule, expr::MatchArm, path::Path};
+pub use hir_expand::diagnostics::{AstDiagnostic, Diagnostic, DiagnosticSink};
 
 #[derive(Debug)]
 pub struct NoSuchField {
@@ -244,235 +227,3 @@ impl AstDiagnostic for MismatchedArgCount {
         ast::CallExpr::cast(node).unwrap()
     }
 }
-
-#[cfg(test)]
-mod tests {
-    use hir_def::{db::DefDatabase, AssocItemId, ModuleDefId};
-    use hir_expand::diagnostics::{Diagnostic, DiagnosticSink};
-    use ra_db::{fixture::WithFixture, FileId, SourceDatabase, SourceDatabaseExt};
-    use ra_syntax::{TextRange, TextSize};
-    use rustc_hash::FxHashMap;
-
-    use crate::{diagnostics::validate_body, test_db::TestDB};
-
-    impl TestDB {
-        fn diagnostics<F: FnMut(&dyn Diagnostic)>(&self, mut cb: F) {
-            let crate_graph = self.crate_graph();
-            for krate in crate_graph.iter() {
-                let crate_def_map = self.crate_def_map(krate);
-
-                let mut fns = Vec::new();
-                for (module_id, _) in crate_def_map.modules.iter() {
-                    for decl in crate_def_map[module_id].scope.declarations() {
-                        if let ModuleDefId::FunctionId(f) = decl {
-                            fns.push(f)
-                        }
-                    }
-
-                    for impl_id in crate_def_map[module_id].scope.impls() {
-                        let impl_data = self.impl_data(impl_id);
-                        for item in impl_data.items.iter() {
-                            if let AssocItemId::FunctionId(f) = item {
-                                fns.push(*f)
-                            }
-                        }
-                    }
-                }
-
-                for f in fns {
-                    let mut sink = DiagnosticSink::new(&mut cb);
-                    validate_body(self, f.into(), &mut sink);
-                }
-            }
-        }
-    }
-
-    pub(crate) fn check_diagnostics(ra_fixture: &str) {
-        let db = TestDB::with_files(ra_fixture);
-        let annotations = db.extract_annotations();
-
-        let mut actual: FxHashMap<FileId, Vec<(TextRange, String)>> = FxHashMap::default();
-        db.diagnostics(|d| {
-            // FXIME: macros...
-            let file_id = d.source().file_id.original_file(&db);
-            let range = d.syntax_node(&db).text_range();
-            let message = d.message().to_owned();
-            actual.entry(file_id).or_default().push((range, message));
-        });
-
-        for (file_id, diags) in actual.iter_mut() {
-            diags.sort_by_key(|it| it.0.start());
-            let text = db.file_text(*file_id);
-            // For multiline spans, place them on line start
-            for (range, content) in diags {
-                if text[*range].contains('\n') {
-                    *range = TextRange::new(range.start(), range.start() + TextSize::from(1));
-                    *content = format!("... {}", content);
-                }
-            }
-        }
-
-        assert_eq!(annotations, actual);
-    }
-
-    #[test]
-    fn no_such_field_diagnostics() {
-        check_diagnostics(
-            r#"
-struct S { foo: i32, bar: () }
-impl S {
-    fn new() -> S {
-        S {
-        //^... Missing structure fields:
-        //|    - bar
-            foo: 92,
-            baz: 62,
-          //^^^^^^^ no such field
-        }
-    }
-}
-"#,
-        );
-    }
-    #[test]
-    fn no_such_field_with_feature_flag_diagnostics() {
-        check_diagnostics(
-            r#"
-//- /lib.rs crate:foo cfg:feature=foo
-struct MyStruct {
-    my_val: usize,
-    #[cfg(feature = "foo")]
-    bar: bool,
-}
-
-impl MyStruct {
-    #[cfg(feature = "foo")]
-    pub(crate) fn new(my_val: usize, bar: bool) -> Self {
-        Self { my_val, bar }
-    }
-    #[cfg(not(feature = "foo"))]
-    pub(crate) fn new(my_val: usize, _bar: bool) -> Self {
-        Self { my_val }
-    }
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn no_such_field_enum_with_feature_flag_diagnostics() {
-        check_diagnostics(
-            r#"
-//- /lib.rs crate:foo cfg:feature=foo
-enum Foo {
-    #[cfg(not(feature = "foo"))]
-    Buz,
-    #[cfg(feature = "foo")]
-    Bar,
-    Baz
-}
-
-fn test_fn(f: Foo) {
-    match f {
-        Foo::Bar => {},
-        Foo::Baz => {},
-    }
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn no_such_field_with_feature_flag_diagnostics_on_struct_lit() {
-        check_diagnostics(
-            r#"
-//- /lib.rs crate:foo cfg:feature=foo
-struct S {
-    #[cfg(feature = "foo")]
-    foo: u32,
-    #[cfg(not(feature = "foo"))]
-    bar: u32,
-}
-
-impl S {
-    #[cfg(feature = "foo")]
-    fn new(foo: u32) -> Self {
-        Self { foo }
-    }
-    #[cfg(not(feature = "foo"))]
-    fn new(bar: u32) -> Self {
-        Self { bar }
-    }
-    fn new2(bar: u32) -> Self {
-        #[cfg(feature = "foo")]
-        { Self { foo: bar } }
-        #[cfg(not(feature = "foo"))]
-        { Self { bar } }
-    }
-    fn new2(val: u32) -> Self {
-        Self {
-            #[cfg(feature = "foo")]
-            foo: val,
-            #[cfg(not(feature = "foo"))]
-            bar: val,
-        }
-    }
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn no_such_field_with_type_macro() {
-        check_diagnostics(
-            r#"
-macro_rules! Type { () => { u32 }; }
-struct Foo { bar: Type![] }
-
-impl Foo {
-    fn new() -> Self {
-        Foo { bar: 0 }
-    }
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn missing_record_pat_field_diagnostic() {
-        check_diagnostics(
-            r#"
-struct S { foo: i32, bar: () }
-fn baz(s: S) {
-    let S { foo: _ } = s;
-        //^^^^^^^^^^ Missing structure fields:
-        //         | - bar
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn missing_record_pat_field_no_diagnostic_if_not_exhaustive() {
-        check_diagnostics(
-            r"
-struct S { foo: i32, bar: () }
-fn baz(s: S) -> i32 {
-    match s {
-        S { foo, .. } => foo,
-    }
-}
-",
-        )
-    }
-
-    #[test]
-    fn break_outside_of_loop() {
-        check_diagnostics(
-            r#"
-fn foo() { break; }
-         //^^^^^ break outside of loop
-"#,
-        );
-    }
-}
diff --git a/crates/ra_hir_ty/src/display.rs b/crates/ra_hir_ty/src/display.rs
index c860c254ca..ac68c5661e 100644
--- a/crates/ra_hir_ty/src/display.rs
+++ b/crates/ra_hir_ty/src/display.rs
@@ -243,17 +243,10 @@ impl HirDisplay for ApplicationTy {
                     write!(f, ")")?;
                 }
             }
-            TypeCtor::FnPtr { is_varargs, .. } => {
-                let sig = FnSig::from_fn_ptr_substs(&self.parameters, is_varargs);
+            TypeCtor::FnPtr { .. } => {
+                let sig = FnSig::from_fn_ptr_substs(&self.parameters);
                 write!(f, "fn(")?;
                 f.write_joined(sig.params(), ", ")?;
-                if is_varargs {
-                    if sig.params().is_empty() {
-                        write!(f, "...")?;
-                    } else {
-                        write!(f, ", ...")?;
-                    }
-                }
                 write!(f, ")")?;
                 let ret = sig.ret();
                 if *ret != Ty::unit() {
diff --git a/crates/ra_hir_ty/src/diagnostics/expr.rs b/crates/ra_hir_ty/src/expr.rs
similarity index 79%
rename from crates/ra_hir_ty/src/diagnostics/expr.rs
rename to crates/ra_hir_ty/src/expr.rs
index 557d01cdc9..d44562b227 100644
--- a/crates/ra_hir_ty/src/diagnostics/expr.rs
+++ b/crates/ra_hir_ty/src/expr.rs
@@ -2,7 +2,7 @@
 
 use std::sync::Arc;
 
-use hir_def::{path::path, resolver::HasResolver, AdtId, DefWithBodyId};
+use hir_def::{path::path, resolver::HasResolver, AdtId, FunctionId};
 use hir_expand::diagnostics::DiagnosticSink;
 use ra_syntax::{ast, AstPtr};
 use rustc_hash::FxHashSet;
@@ -10,9 +10,9 @@ use rustc_hash::FxHashSet;
 use crate::{
     db::HirDatabase,
     diagnostics::{
-        match_check::{is_useful, MatchCheckCtx, Matrix, PatStack, Usefulness},
         MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkInTailExpr, MissingPatFields,
     },
+    match_checking::{is_useful, MatchCheckCtx, Matrix, PatStack, Usefulness},
     utils::variant_data,
     ApplicationTy, InferenceResult, Ty, TypeCtor,
 };
@@ -30,23 +30,23 @@ pub use hir_def::{
     LocalFieldId, Lookup, VariantId,
 };
 
-pub(super) struct ExprValidator<'a, 'b: 'a> {
-    owner: DefWithBodyId,
+pub struct ExprValidator<'a, 'b: 'a> {
+    func: FunctionId,
     infer: Arc<InferenceResult>,
     sink: &'a mut DiagnosticSink<'b>,
 }
 
 impl<'a, 'b> ExprValidator<'a, 'b> {
-    pub(super) fn new(
-        owner: DefWithBodyId,
+    pub fn new(
+        func: FunctionId,
         infer: Arc<InferenceResult>,
         sink: &'a mut DiagnosticSink<'b>,
     ) -> ExprValidator<'a, 'b> {
-        ExprValidator { owner, infer, sink }
+        ExprValidator { func, infer, sink }
     }
 
-    pub(super) fn validate_body(&mut self, db: &dyn HirDatabase) {
-        let body = db.body(self.owner.into());
+    pub fn validate_body(&mut self, db: &dyn HirDatabase) {
+        let body = db.body(self.func.into());
 
         for (id, expr) in body.exprs.iter() {
             if let Some((variant_def, missed_fields, true)) =
@@ -96,7 +96,7 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
         missed_fields: Vec<LocalFieldId>,
     ) {
         // XXX: only look at source_map if we do have missing fields
-        let (_, source_map) = db.body_with_source_map(self.owner.into());
+        let (_, source_map) = db.body_with_source_map(self.func.into());
 
         if let Ok(source_ptr) = source_map.expr_syntax(id) {
             let root = source_ptr.file_syntax(db.upcast());
@@ -125,7 +125,7 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
         missed_fields: Vec<LocalFieldId>,
     ) {
         // XXX: only look at source_map if we do have missing fields
-        let (_, source_map) = db.body_with_source_map(self.owner.into());
+        let (_, source_map) = db.body_with_source_map(self.func.into());
 
         if let Ok(source_ptr) = source_map.pat_syntax(id) {
             if let Some(expr) = source_ptr.value.as_ref().left() {
@@ -175,17 +175,13 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
         };
 
         let sig = db.callable_item_signature(callee);
-        if sig.value.is_varargs {
-            return None;
-        }
-
         let params = sig.value.params();
 
         let mut param_count = params.len();
         let mut arg_count = args.len();
 
         if arg_count != param_count {
-            let (_, source_map) = db.body_with_source_map(self.owner.into());
+            let (_, source_map) = db.body_with_source_map(self.func.into());
             if let Ok(source_ptr) = source_map.expr_syntax(call_id) {
                 if is_method_call {
                     param_count -= 1;
@@ -212,7 +208,7 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
         infer: Arc<InferenceResult>,
     ) {
         let (body, source_map): (Arc<Body>, Arc<BodySourceMap>) =
-            db.body_with_source_map(self.owner.into());
+            db.body_with_source_map(self.func.into());
 
         let match_expr_ty = match infer.type_of_expr.get(match_expr) {
             Some(ty) => ty,
@@ -293,7 +289,7 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
 
         let core_result_path = path![core::result::Result];
 
-        let resolver = self.owner.resolver(db.upcast());
+        let resolver = self.func.resolver(db.upcast());
         let core_result_enum = match resolver.resolve_known_enum(db.upcast(), &core_result_path) {
             Some(it) => it,
             _ => return,
@@ -308,7 +304,7 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
         };
 
         if params.len() == 2 && params[0] == mismatch.actual {
-            let (_, source_map) = db.body_with_source_map(self.owner.into());
+            let (_, source_map) = db.body_with_source_map(self.func.into());
 
             if let Ok(source_ptr) = source_map.expr_syntax(id) {
                 self.sink
@@ -380,166 +376,146 @@ pub fn record_pattern_missing_fields(
 
 #[cfg(test)]
 mod tests {
-    use crate::diagnostics::tests::check_diagnostics;
+    use expect::{expect, Expect};
+    use ra_db::fixture::WithFixture;
+
+    use crate::{diagnostics::MismatchedArgCount, test_db::TestDB};
+
+    fn check_diagnostic(ra_fixture: &str, expect: Expect) {
+        let msg = TestDB::with_single_file(ra_fixture).0.diagnostic::<MismatchedArgCount>().0;
+        expect.assert_eq(&msg);
+    }
+
+    fn check_no_diagnostic(ra_fixture: &str) {
+        let (s, diagnostic_count) =
+            TestDB::with_single_file(ra_fixture).0.diagnostic::<MismatchedArgCount>();
+
+        assert_eq!(0, diagnostic_count, "expected no diagnostic, found one: {}", s);
+    }
 
     #[test]
     fn simple_free_fn_zero() {
-        check_diagnostics(
-            r#"
-fn zero() {}
-fn f() { zero(1); }
-       //^^^^^^^ Expected 0 arguments, found 1
-"#,
+        check_diagnostic(
+            r"
+            fn zero() {}
+            fn f() { zero(1); }
+            ",
+            expect![["\"zero(1)\": Expected 0 arguments, found 1\n"]],
         );
 
-        check_diagnostics(
-            r#"
-fn zero() {}
-fn f() { zero(); }
-"#,
+        check_no_diagnostic(
+            r"
+            fn zero() {}
+            fn f() { zero(); }
+            ",
         );
     }
 
     #[test]
     fn simple_free_fn_one() {
-        check_diagnostics(
-            r#"
-fn one(arg: u8) {}
-fn f() { one(); }
-       //^^^^^ Expected 1 argument, found 0
-"#,
+        check_diagnostic(
+            r"
+            fn one(arg: u8) {}
+            fn f() { one(); }
+            ",
+            expect![["\"one()\": Expected 1 argument, found 0\n"]],
         );
 
-        check_diagnostics(
-            r#"
-fn one(arg: u8) {}
-fn f() { one(1); }
-"#,
+        check_no_diagnostic(
+            r"
+            fn one(arg: u8) {}
+            fn f() { one(1); }
+            ",
         );
     }
 
     #[test]
     fn method_as_fn() {
-        check_diagnostics(
-            r#"
-struct S;
-impl S { fn method(&self) {} }
-
-fn f() {
-    S::method();
-} //^^^^^^^^^^^ Expected 1 argument, found 0
-"#,
+        check_diagnostic(
+            r"
+            struct S;
+            impl S {
+                fn method(&self) {}
+            }
+
+            fn f() {
+                S::method();
+            }
+            ",
+            expect![["\"S::method()\": Expected 1 argument, found 0\n"]],
         );
 
-        check_diagnostics(
-            r#"
-struct S;
-impl S { fn method(&self) {} }
+        check_no_diagnostic(
+            r"
+            struct S;
+            impl S {
+                fn method(&self) {}
+            }
 
-fn f() {
-    S::method(&S);
-    S.method();
-}
-"#,
+            fn f() {
+                S::method(&S);
+                S.method();
+            }
+            ",
         );
     }
 
     #[test]
     fn method_with_arg() {
-        check_diagnostics(
-            r#"
-struct S;
-impl S { fn method(&self, arg: u8) {} }
+        check_diagnostic(
+            r"
+            struct S;
+            impl S {
+                fn method(&self, arg: u8) {}
+            }
 
             fn f() {
                 S.method();
-            } //^^^^^^^^^^ Expected 1 argument, found 0
-            "#,
+            }
+            ",
+            expect![["\"S.method()\": Expected 1 argument, found 0\n"]],
         );
 
-        check_diagnostics(
-            r#"
-struct S;
-impl S { fn method(&self, arg: u8) {} }
+        check_no_diagnostic(
+            r"
+            struct S;
+            impl S {
+                fn method(&self, arg: u8) {}
+            }
 
-fn f() {
-    S::method(&S, 0);
-    S.method(1);
-}
-"#,
+            fn f() {
+                S::method(&S, 0);
+                S.method(1);
+            }
+            ",
         );
     }
 
     #[test]
     fn tuple_struct() {
-        check_diagnostics(
-            r#"
-struct Tup(u8, u16);
-fn f() {
-    Tup(0);
-} //^^^^^^ Expected 2 arguments, found 1
-"#,
+        check_diagnostic(
+            r"
+            struct Tup(u8, u16);
+            fn f() {
+                Tup(0);
+            }
+            ",
+            expect![["\"Tup(0)\": Expected 2 arguments, found 1\n"]],
         )
     }
 
     #[test]
     fn enum_variant() {
-        check_diagnostics(
-            r#"
-enum En { Variant(u8, u16), }
-fn f() {
-    En::Variant(0);
-} //^^^^^^^^^^^^^^ Expected 2 arguments, found 1
-"#,
-        )
-    }
-
-    #[test]
-    fn enum_variant_type_macro() {
-        check_diagnostics(
-            r#"
-macro_rules! Type {
-    () => { u32 };
-}
-enum Foo {
-    Bar(Type![])
-}
-impl Foo {
-    fn new() {
-        Foo::Bar(0);
-        Foo::Bar(0, 1);
-      //^^^^^^^^^^^^^^ Expected 1 argument, found 2
-        Foo::Bar();
-      //^^^^^^^^^^ Expected 1 argument, found 0
-    }
-}
-        "#,
-        );
-    }
-
-    #[test]
-    fn varargs() {
-        check_diagnostics(
-            r#"
-extern "C" {
-    fn fixed(fixed: u8);
-    fn varargs(fixed: u8, ...);
-    fn varargs2(...);
-}
-
-fn f() {
-    unsafe {
-        fixed(0);
-        fixed(0, 1);
-      //^^^^^^^^^^^ Expected 1 argument, found 2
-        varargs(0);
-        varargs(0, 1);
-        varargs2();
-        varargs2(0);
-        varargs2(0, 1);
-    }
-}
-        "#,
+        check_diagnostic(
+            r"
+            enum En {
+                Variant(u8, u16),
+            }
+            fn f() {
+                En::Variant(0);
+            }
+            ",
+            expect![["\"En::Variant(0)\": Expected 2 arguments, found 1\n"]],
         )
     }
 }
diff --git a/crates/ra_hir_ty/src/infer.rs b/crates/ra_hir_ty/src/infer.rs
index 28f32a0a4d..2ce4f65ccc 100644
--- a/crates/ra_hir_ty/src/infer.rs
+++ b/crates/ra_hir_ty/src/infer.rs
@@ -168,7 +168,7 @@ impl InferenceResult {
     pub fn add_diagnostics(
         &self,
         db: &dyn HirDatabase,
-        owner: DefWithBodyId,
+        owner: FunctionId,
         sink: &mut DiagnosticSink,
     ) {
         self.diagnostics.iter().for_each(|it| it.add_to(db, owner, sink))
@@ -760,7 +760,7 @@ impl std::ops::BitOrAssign for Diverges {
 }
 
 mod diagnostics {
-    use hir_def::{expr::ExprId, DefWithBodyId};
+    use hir_def::{expr::ExprId, FunctionId};
     use hir_expand::diagnostics::DiagnosticSink;
 
     use crate::{
@@ -778,17 +778,17 @@ mod diagnostics {
         pub(super) fn add_to(
             &self,
             db: &dyn HirDatabase,
-            owner: DefWithBodyId,
+            owner: FunctionId,
             sink: &mut DiagnosticSink,
         ) {
             match self {
                 InferenceDiagnostic::NoSuchField { expr, field } => {
-                    let (_, source_map) = db.body_with_source_map(owner);
+                    let (_, source_map) = db.body_with_source_map(owner.into());
                     let field = source_map.field_syntax(*expr, *field);
                     sink.push(NoSuchField { file: field.file_id, field: field.value })
                 }
                 InferenceDiagnostic::BreakOutsideOfLoop { expr } => {
-                    let (_, source_map) = db.body_with_source_map(owner);
+                    let (_, source_map) = db.body_with_source_map(owner.into());
                     let ptr = source_map
                         .expr_syntax(*expr)
                         .expect("break outside of loop in synthetic syntax");
diff --git a/crates/ra_hir_ty/src/infer/expr.rs b/crates/ra_hir_ty/src/infer/expr.rs
index ab586b018d..bd9a387f58 100644
--- a/crates/ra_hir_ty/src/infer/expr.rs
+++ b/crates/ra_hir_ty/src/infer/expr.rs
@@ -220,7 +220,7 @@ impl<'a> InferenceContext<'a> {
                 };
                 sig_tys.push(ret_ty.clone());
                 let sig_ty = Ty::apply(
-                    TypeCtor::FnPtr { num_args: sig_tys.len() as u16 - 1, is_varargs: false },
+                    TypeCtor::FnPtr { num_args: sig_tys.len() as u16 - 1 },
                     Substs(sig_tys.clone().into()),
                 );
                 let closure_ty =
diff --git a/crates/ra_hir_ty/src/lib.rs b/crates/ra_hir_ty/src/lib.rs
index c4c24a83b0..2652d200f5 100644
--- a/crates/ra_hir_ty/src/lib.rs
+++ b/crates/ra_hir_ty/src/lib.rs
@@ -12,12 +12,15 @@ pub mod traits;
 pub mod method_resolution;
 mod op;
 mod lower;
+mod match_checking;
 pub(crate) mod infer;
 pub(crate) mod utils;
 
 pub mod display;
 pub mod db;
 pub mod diagnostics;
+pub mod expr;
+pub mod unsafe_validation;
 
 #[cfg(test)]
 mod tests;
@@ -112,7 +115,7 @@ pub enum TypeCtor {
     /// fn foo() -> i32 { 1 }
     /// let bar: fn() -> i32 = foo;
     /// ```
-    FnPtr { num_args: u16, is_varargs: bool },
+    FnPtr { num_args: u16 },
 
     /// The never type `!`.
     Never,
@@ -187,7 +190,7 @@ impl TypeCtor {
                     }
                 }
             }
-            TypeCtor::FnPtr { num_args, is_varargs: _ } => num_args as usize + 1,
+            TypeCtor::FnPtr { num_args } => num_args as usize + 1,
             TypeCtor::Tuple { cardinality } => cardinality as usize,
         }
     }
@@ -667,20 +670,19 @@ pub enum TyKind {
 #[derive(Clone, PartialEq, Eq, Debug)]
 pub struct FnSig {
     params_and_return: Arc<[Ty]>,
-    is_varargs: bool,
 }
 
 /// A polymorphic function signature.
 pub type PolyFnSig = Binders<FnSig>;
 
 impl FnSig {
-    pub fn from_params_and_return(mut params: Vec<Ty>, ret: Ty, is_varargs: bool) -> FnSig {
+    pub fn from_params_and_return(mut params: Vec<Ty>, ret: Ty) -> FnSig {
         params.push(ret);
-        FnSig { params_and_return: params.into(), is_varargs }
+        FnSig { params_and_return: params.into() }
     }
 
-    pub fn from_fn_ptr_substs(substs: &Substs, is_varargs: bool) -> FnSig {
-        FnSig { params_and_return: Arc::clone(&substs.0), is_varargs }
+    pub fn from_fn_ptr_substs(substs: &Substs) -> FnSig {
+        FnSig { params_and_return: Arc::clone(&substs.0) }
     }
 
     pub fn params(&self) -> &[Ty] {
@@ -725,7 +727,7 @@ impl Ty {
     }
     pub fn fn_ptr(sig: FnSig) -> Self {
         Ty::apply(
-            TypeCtor::FnPtr { num_args: sig.params().len() as u16, is_varargs: sig.is_varargs },
+            TypeCtor::FnPtr { num_args: sig.params().len() as u16 },
             Substs(sig.params_and_return),
         )
     }
@@ -822,9 +824,7 @@ impl Ty {
     fn callable_sig(&self, db: &dyn HirDatabase) -> Option<FnSig> {
         match self {
             Ty::Apply(a_ty) => match a_ty.ctor {
-                TypeCtor::FnPtr { is_varargs, .. } => {
-                    Some(FnSig::from_fn_ptr_substs(&a_ty.parameters, is_varargs))
-                }
+                TypeCtor::FnPtr { .. } => Some(FnSig::from_fn_ptr_substs(&a_ty.parameters)),
                 TypeCtor::FnDef(def) => {
                     let sig = db.callable_item_signature(def);
                     Some(sig.subst(&a_ty.parameters))
diff --git a/crates/ra_hir_ty/src/lower.rs b/crates/ra_hir_ty/src/lower.rs
index 6f4398e842..101b8aebe5 100644
--- a/crates/ra_hir_ty/src/lower.rs
+++ b/crates/ra_hir_ty/src/lower.rs
@@ -176,12 +176,9 @@ impl Ty {
                 Ty::apply_one(TypeCtor::Ref(*mutability), inner_ty)
             }
             TypeRef::Placeholder => Ty::Unknown,
-            TypeRef::Fn(params, is_varargs) => {
+            TypeRef::Fn(params) => {
                 let sig = Substs(params.iter().map(|tr| Ty::from_hir(ctx, tr)).collect());
-                Ty::apply(
-                    TypeCtor::FnPtr { num_args: sig.len() as u16 - 1, is_varargs: *is_varargs },
-                    sig,
-                )
+                Ty::apply(TypeCtor::FnPtr { num_args: sig.len() as u16 - 1 }, sig)
             }
             TypeRef::DynTrait(bounds) => {
                 let self_ty = Ty::Bound(BoundVar::new(DebruijnIndex::INNERMOST, 0));
@@ -999,7 +996,7 @@ fn fn_sig_for_fn(db: &dyn HirDatabase, def: FunctionId) -> PolyFnSig {
     let ret = Ty::from_hir(&ctx_ret, &data.ret_type);
     let generics = generics(db.upcast(), def.into());
     let num_binders = generics.len();
-    Binders::new(num_binders, FnSig::from_params_and_return(params, ret, data.is_varargs))
+    Binders::new(num_binders, FnSig::from_params_and_return(params, ret))
 }
 
 /// Build the declared type of a function. This should not need to look at the
@@ -1050,7 +1047,7 @@ fn fn_sig_for_struct_constructor(db: &dyn HirDatabase, def: StructId) -> PolyFnS
     let params =
         fields.iter().map(|(_, field)| Ty::from_hir(&ctx, &field.type_ref)).collect::<Vec<_>>();
     let ret = type_for_adt(db, def.into());
-    Binders::new(ret.num_binders, FnSig::from_params_and_return(params, ret.value, false))
+    Binders::new(ret.num_binders, FnSig::from_params_and_return(params, ret.value))
 }
 
 /// Build the type of a tuple struct constructor.
@@ -1074,7 +1071,7 @@ fn fn_sig_for_enum_variant_constructor(db: &dyn HirDatabase, def: EnumVariantId)
     let params =
         fields.iter().map(|(_, field)| Ty::from_hir(&ctx, &field.type_ref)).collect::<Vec<_>>();
     let ret = type_for_adt(db, def.parent.into());
-    Binders::new(ret.num_binders, FnSig::from_params_and_return(params, ret.value, false))
+    Binders::new(ret.num_binders, FnSig::from_params_and_return(params, ret.value))
 }
 
 /// Build the type of a tuple enum variant constructor.
diff --git a/crates/ra_hir_ty/src/diagnostics/match_check.rs b/crates/ra_hir_ty/src/match_checking.rs
similarity index 52%
rename from crates/ra_hir_ty/src/diagnostics/match_check.rs
rename to crates/ra_hir_ty/src/match_checking.rs
index 507edcb7de..5495ce284f 100644
--- a/crates/ra_hir_ty/src/diagnostics/match_check.rs
+++ b/crates/ra_hir_ty/src/match_checking.rs
@@ -41,9 +41,9 @@
 //! ```ignore
 //! // x: (Option<bool>, Result<()>)
 //! match x {
-//!     (Some(true), _) => (),
-//!     (None, Err(())) => (),
-//!     (None, Err(_)) => (),
+//!     (Some(true), _) => {}
+//!     (None, Err(())) => {}
+//!     (None, Err(_)) => {}
 //! }
 //! ```
 //!
@@ -218,16 +218,15 @@
 //!   ```
 use std::sync::Arc;
 
-use hir_def::{
-    adt::VariantData,
-    body::Body,
-    expr::{Expr, Literal, Pat, PatId},
-    AdtId, EnumVariantId, VariantId,
-};
-use ra_arena::Idx;
 use smallvec::{smallvec, SmallVec};
 
-use crate::{db::HirDatabase, ApplicationTy, InferenceResult, Ty, TypeCtor};
+use crate::{
+    db::HirDatabase,
+    expr::{Body, Expr, Literal, Pat, PatId},
+    ApplicationTy, InferenceResult, Ty, TypeCtor,
+};
+use hir_def::{adt::VariantData, AdtId, EnumVariantId, VariantId};
+use ra_arena::Idx;
 
 #[derive(Debug, Clone, Copy)]
 /// Either a pattern from the source code being analyzed, represented as
@@ -272,7 +271,7 @@ impl From<&PatId> for PatIdOrWild {
 }
 
 #[derive(Debug, Clone, Copy, PartialEq)]
-pub(super) enum MatchCheckErr {
+pub enum MatchCheckErr {
     NotImplemented,
     MalformedMatchArm,
     /// Used when type inference cannot resolve the type of
@@ -287,21 +286,21 @@ pub(super) enum MatchCheckErr {
 ///
 /// The `std::result::Result` type is used here rather than a custom enum
 /// to allow the use of `?`.
-pub(super) type MatchCheckResult<T> = Result<T, MatchCheckErr>;
+pub type MatchCheckResult<T> = Result<T, MatchCheckErr>;
 
 #[derive(Debug)]
 /// A row in a Matrix.
 ///
 /// This type is modeled from the struct of the same name in `rustc`.
-pub(super) struct PatStack(PatStackInner);
+pub(crate) struct PatStack(PatStackInner);
 type PatStackInner = SmallVec<[PatIdOrWild; 2]>;
 
 impl PatStack {
-    pub(super) fn from_pattern(pat_id: PatId) -> PatStack {
+    pub(crate) fn from_pattern(pat_id: PatId) -> PatStack {
         Self(smallvec!(pat_id.into()))
     }
 
-    pub(super) fn from_wild() -> PatStack {
+    pub(crate) fn from_wild() -> PatStack {
         Self(smallvec!(PatIdOrWild::Wild))
     }
 
@@ -510,14 +509,14 @@ impl PatStack {
 /// A collection of PatStack.
 ///
 /// This type is modeled from the struct of the same name in `rustc`.
-pub(super) struct Matrix(Vec<PatStack>);
+pub(crate) struct Matrix(Vec<PatStack>);
 
 impl Matrix {
-    pub(super) fn empty() -> Self {
+    pub(crate) fn empty() -> Self {
         Self(vec![])
     }
 
-    pub(super) fn push(&mut self, cx: &MatchCheckCtx, row: PatStack) {
+    pub(crate) fn push(&mut self, cx: &MatchCheckCtx, row: PatStack) {
         if let Some(Pat::Or(pat_ids)) = row.get_head().map(|pat_id| pat_id.as_pat(cx)) {
             // Or patterns are expanded here
             for pat_id in pat_ids {
@@ -579,16 +578,16 @@ impl Matrix {
 /// not matched by an prior match arms.
 ///
 /// We may eventually need an `Unknown` variant here.
-pub(super) enum Usefulness {
+pub enum Usefulness {
     Useful,
     NotUseful,
 }
 
-pub(super) struct MatchCheckCtx<'a> {
-    pub(super) match_expr: Idx<Expr>,
-    pub(super) body: Arc<Body>,
-    pub(super) infer: Arc<InferenceResult>,
-    pub(super) db: &'a dyn HirDatabase,
+pub struct MatchCheckCtx<'a> {
+    pub match_expr: Idx<Expr>,
+    pub body: Arc<Body>,
+    pub infer: Arc<InferenceResult>,
+    pub db: &'a dyn HirDatabase,
 }
 
 /// Given a set of patterns `matrix`, and pattern to consider `v`, determines
@@ -599,7 +598,7 @@ pub(super) struct MatchCheckCtx<'a> {
 /// expected that you have already type checked the match arms. All patterns in
 /// matrix should be the same type as v, as well as they should all be the same
 /// type as the match expression.
-pub(super) fn is_useful(
+pub(crate) fn is_useful(
     cx: &MatchCheckCtx,
     matrix: &Matrix,
     v: &PatStack,
@@ -837,226 +836,630 @@ fn enum_variant_matches(cx: &MatchCheckCtx, pat_id: PatId, enum_variant_id: Enum
 
 #[cfg(test)]
 mod tests {
-    use crate::diagnostics::tests::check_diagnostics;
+    pub(super) use insta::assert_snapshot;
+    pub(super) use ra_db::fixture::WithFixture;
+
+    pub(super) use crate::{diagnostics::MissingMatchArms, test_db::TestDB};
+
+    pub(super) fn check_diagnostic_message(ra_fixture: &str) -> String {
+        TestDB::with_single_file(ra_fixture).0.diagnostic::<MissingMatchArms>().0
+    }
+
+    pub(super) fn check_diagnostic(ra_fixture: &str) {
+        let diagnostic_count =
+            TestDB::with_single_file(ra_fixture).0.diagnostic::<MissingMatchArms>().1;
+
+        assert_eq!(1, diagnostic_count, "no diagnostic reported");
+    }
+
+    pub(super) fn check_no_diagnostic(ra_fixture: &str) {
+        let (s, diagnostic_count) =
+            TestDB::with_single_file(ra_fixture).0.diagnostic::<MissingMatchArms>();
+
+        assert_eq!(0, diagnostic_count, "expected no diagnostic, found one: {}", s);
+    }
 
     #[test]
-    fn empty_tuple() {
-        check_diagnostics(
-            r#"
-fn main() {
-    match () { }
-        //^^ Missing match arm
-   match (()) { }
-       //^^^^ Missing match arm
+    fn empty_tuple_no_arms_diagnostic_message() {
+        assert_snapshot!(
+            check_diagnostic_message(r"
+                fn test_fn() {
+                    match () {
+                    }
+                }
+            "),
+            @"\"()\": Missing match arm\n"
+        );
+    }
 
-    match () { _ => (), }
-    match () { () => (), }
-    match (()) { (()) => (), }
-}
-"#,
+    #[test]
+    fn empty_tuple_no_arms() {
+        check_diagnostic(
+            r"
+            fn test_fn() {
+                match () {
+                }
+            }
+        ",
         );
     }
 
     #[test]
-    fn tuple_of_two_empty_tuple() {
-        check_diagnostics(
-            r#"
-fn main() {
-    match ((), ()) { }
-        //^^^^^^^^ Missing match arm
+    fn empty_tuple_wild() {
+        check_no_diagnostic(
+            r"
+            fn test_fn() {
+                match () {
+                    _ => {}
+                }
+            }
+        ",
+        );
+    }
 
-    match ((), ()) { ((), ()) => (), }
-}
-"#,
-        );
-    }
-
-    #[test]
-    fn boolean() {
-        check_diagnostics(
-            r#"
-fn test_main() {
-    match false { }
-        //^^^^^ Missing match arm
-    match false { true => (), }
-        //^^^^^ Missing match arm
-    match (false, true) {}
-        //^^^^^^^^^^^^^ Missing match arm
-    match (false, true) { (true, true) => (), }
-        //^^^^^^^^^^^^^ Missing match arm
-    match (false, true) {
-        //^^^^^^^^^^^^^ Missing match arm
-        (false, true) => (),
-        (false, false) => (),
-        (true, false) => (),
-    }
-    match (false, true) { (true, _x) => (), }
-        //^^^^^^^^^^^^^ Missing match arm
-
-    match false { true => (), false => (), }
-    match (false, true) {
-        (false, _) => (),
-        (true, false) => (),
-        (_, true) => (),
-    }
-    match (false, true) {
-        (true, true) => (),
-        (true, false) => (),
-        (false, true) => (),
-        (false, false) => (),
-    }
-    match (false, true) {
-        (true, _x) => (),
-        (false, true) => (),
-        (false, false) => (),
-    }
-    match (false, true, false) {
-        (false, ..) => (),
-        (true, ..) => (),
-    }
-    match (false, true, false) {
-        (.., false) => (),
-        (.., true) => (),
-    }
-    match (false, true, false) { (..) => (), }
-}
-"#,
+    #[test]
+    fn empty_tuple_no_diagnostic() {
+        check_no_diagnostic(
+            r"
+            fn test_fn() {
+                match () {
+                    () => {}
+                }
+            }
+        ",
         );
     }
 
     #[test]
-    fn tuple_of_tuple_and_bools() {
-        check_diagnostics(
-            r#"
-fn main() {
-    match (false, ((), false)) {}
-        //^^^^^^^^^^^^^^^^^^^^ Missing match arm
-    match (false, ((), false)) { (true, ((), true)) => (), }
-        //^^^^^^^^^^^^^^^^^^^^ Missing match arm
-    match (false, ((), false)) { (true, _) => (), }
-        //^^^^^^^^^^^^^^^^^^^^ Missing match arm
+    fn tuple_of_empty_tuple_no_arms() {
+        check_diagnostic(
+            r"
+            fn test_fn() {
+                match (()) {
+                }
+            }
+        ",
+        );
+    }
 
-    match (false, ((), false)) {
-        (true, ((), true)) => (),
-        (true, ((), false)) => (),
-        (false, ((), true)) => (),
-        (false, ((), false)) => (),
+    #[test]
+    fn tuple_of_empty_tuple_no_diagnostic() {
+        check_no_diagnostic(
+            r"
+            fn test_fn() {
+                match (()) {
+                    (()) => {}
+                }
+            }
+        ",
+        );
     }
-    match (false, ((), false)) {
-        (true, ((), true)) => (),
-        (true, ((), false)) => (),
-        (false, _) => (),
+
+    #[test]
+    fn tuple_of_two_empty_tuple_no_arms() {
+        check_diagnostic(
+            r"
+            fn test_fn() {
+                match ((), ()) {
+                }
+            }
+        ",
+        );
     }
-}
-"#,
+
+    #[test]
+    fn tuple_of_two_empty_tuple_no_diagnostic() {
+        check_no_diagnostic(
+            r"
+            fn test_fn() {
+                match ((), ()) {
+                    ((), ()) => {}
+                }
+            }
+        ",
         );
     }
 
     #[test]
-    fn enums() {
-        check_diagnostics(
-            r#"
-enum Either { A, B, }
+    fn bool_no_arms() {
+        check_diagnostic(
+            r"
+            fn test_fn() {
+                match false {
+                }
+            }
+        ",
+        );
+    }
 
-fn main() {
-    match Either::A { }
-        //^^^^^^^^^ Missing match arm
-    match Either::B { Either::A => (), }
-        //^^^^^^^^^ Missing match arm
+    #[test]
+    fn bool_missing_arm() {
+        check_diagnostic(
+            r"
+            fn test_fn() {
+                match false {
+                    true => {}
+                }
+            }
+        ",
+        );
+    }
 
-    match &Either::B {
-        //^^^^^^^^^^ Missing match arm
-        Either::A => (),
+    #[test]
+    fn bool_no_diagnostic() {
+        check_no_diagnostic(
+            r"
+            fn test_fn() {
+                match false {
+                    true => {}
+                    false => {}
+                }
+            }
+        ",
+        );
     }
 
-    match Either::B {
-        Either::A => (), Either::B => (),
+    #[test]
+    fn tuple_of_bools_no_arms() {
+        check_diagnostic(
+            r"
+            fn test_fn() {
+                match (false, true) {
+                }
+            }
+        ",
+        );
     }
-    match &Either::B {
-        Either::A => (), Either::B => (),
+
+    #[test]
+    fn tuple_of_bools_missing_arms() {
+        check_diagnostic(
+            r"
+            fn test_fn() {
+                match (false, true) {
+                    (true, true) => {},
+                }
+            }
+        ",
+        );
     }
-}
-"#,
+
+    #[test]
+    fn tuple_of_bools_missing_arm() {
+        check_diagnostic(
+            r"
+            fn test_fn() {
+                match (false, true) {
+                    (false, true) => {},
+                    (false, false) => {},
+                    (true, false) => {},
+                }
+            }
+        ",
         );
     }
 
     #[test]
-    fn enum_containing_bool() {
-        check_diagnostics(
-            r#"
-enum Either { A(bool), B }
+    fn tuple_of_bools_with_wilds() {
+        check_no_diagnostic(
+            r"
+            fn test_fn() {
+                match (false, true) {
+                    (false, _) => {},
+                    (true, false) => {},
+                    (_, true) => {},
+                }
+            }
+        ",
+        );
+    }
 
-fn main() {
-    match Either::B { }
-        //^^^^^^^^^ Missing match arm
-    match Either::B {
-        //^^^^^^^^^ Missing match arm
-        Either::A(true) => (), Either::B => ()
+    #[test]
+    fn tuple_of_bools_no_diagnostic() {
+        check_no_diagnostic(
+            r"
+            fn test_fn() {
+                match (false, true) {
+                    (true, true) => {},
+                    (true, false) => {},
+                    (false, true) => {},
+                    (false, false) => {},
+                }
+            }
+        ",
+        );
     }
 
-    match Either::B {
-        Either::A(true) => (),
-        Either::A(false) => (),
-        Either::B => (),
+    #[test]
+    fn tuple_of_bools_binding_missing_arms() {
+        check_diagnostic(
+            r"
+            fn test_fn() {
+                match (false, true) {
+                    (true, _x) => {},
+                }
+            }
+        ",
+        );
     }
-    match Either::B {
-        Either::B => (),
-        _ => (),
+
+    #[test]
+    fn tuple_of_bools_binding_no_diagnostic() {
+        check_no_diagnostic(
+            r"
+            fn test_fn() {
+                match (false, true) {
+                    (true, _x) => {},
+                    (false, true) => {},
+                    (false, false) => {},
+                }
+            }
+        ",
+        );
     }
-    match Either::B {
-        Either::A(_) => (),
-        Either::B => (),
+
+    #[test]
+    fn tuple_of_bools_with_ellipsis_at_end_no_diagnostic() {
+        check_no_diagnostic(
+            r"
+            fn test_fn() {
+                match (false, true, false) {
+                    (false, ..) => {},
+                    (true, ..) => {},
+                }
+            }
+        ",
+        );
     }
 
-}
-        "#,
+    #[test]
+    fn tuple_of_bools_with_ellipsis_at_beginning_no_diagnostic() {
+        check_no_diagnostic(
+            r"
+            fn test_fn() {
+                match (false, true, false) {
+                    (.., false) => {},
+                    (.., true) => {},
+                }
+            }
+        ",
         );
     }
 
     #[test]
-    fn enum_different_sizes() {
-        check_diagnostics(
-            r#"
-enum Either { A(bool), B(bool, bool) }
+    fn tuple_of_bools_with_ellipsis_no_diagnostic() {
+        check_no_diagnostic(
+            r"
+            fn test_fn() {
+                match (false, true, false) {
+                    (..) => {},
+                }
+            }
+        ",
+        );
+    }
 
-fn main() {
-    match Either::A(false) {
-        //^^^^^^^^^^^^^^^^ Missing match arm
-        Either::A(_) => (),
-        Either::B(false, _) => (),
+    #[test]
+    fn tuple_of_tuple_and_bools_no_arms() {
+        check_diagnostic(
+            r"
+            fn test_fn() {
+                match (false, ((), false)) {
+                }
+            }
+        ",
+        );
     }
 
-    match Either::A(false) {
-        Either::A(_) => (),
-        Either::B(true, _) => (),
-        Either::B(false, _) => (),
+    #[test]
+    fn tuple_of_tuple_and_bools_missing_arms() {
+        check_diagnostic(
+            r"
+            fn test_fn() {
+                match (false, ((), false)) {
+                    (true, ((), true)) => {},
+                }
+            }
+        ",
+        );
     }
-    match Either::A(false) {
-        Either::A(true) | Either::A(false) => (),
-        Either::B(true, _) => (),
-        Either::B(false, _) => (),
+
+    #[test]
+    fn tuple_of_tuple_and_bools_no_diagnostic() {
+        check_no_diagnostic(
+            r"
+            fn test_fn() {
+                match (false, ((), false)) {
+                    (true, ((), true)) => {},
+                    (true, ((), false)) => {},
+                    (false, ((), true)) => {},
+                    (false, ((), false)) => {},
+                }
+            }
+        ",
+        );
     }
-}
-"#,
+
+    #[test]
+    fn tuple_of_tuple_and_bools_wildcard_missing_arms() {
+        check_diagnostic(
+            r"
+            fn test_fn() {
+                match (false, ((), false)) {
+                    (true, _) => {},
+                }
+            }
+        ",
         );
     }
 
     #[test]
-    fn tuple_of_enum_no_diagnostic() {
-        check_diagnostics(
-            r#"
-enum Either { A(bool), B(bool, bool) }
-enum Either2 { C, D }
-
-fn main() {
-    match (Either::A(false), Either2::C) {
-        (Either::A(true), _) | (Either::A(false), _) => (),
-        (Either::B(true, _), Either2::C) => (),
-        (Either::B(false, _), Either2::C) => (),
-        (Either::B(_, _), Either2::D) => (),
+    fn tuple_of_tuple_and_bools_wildcard_no_diagnostic() {
+        check_no_diagnostic(
+            r"
+            fn test_fn() {
+                match (false, ((), false)) {
+                    (true, ((), true)) => {},
+                    (true, ((), false)) => {},
+                    (false, _) => {},
+                }
+            }
+        ",
+        );
     }
-}
-"#,
+
+    #[test]
+    fn enum_no_arms() {
+        check_diagnostic(
+            r"
+            enum Either {
+                A,
+                B,
+            }
+            fn test_fn() {
+                match Either::A {
+                }
+            }
+        ",
+        );
+    }
+
+    #[test]
+    fn enum_missing_arms() {
+        check_diagnostic(
+            r"
+            enum Either {
+                A,
+                B,
+            }
+            fn test_fn() {
+                match Either::B {
+                    Either::A => {},
+                }
+            }
+        ",
+        );
+    }
+
+    #[test]
+    fn enum_no_diagnostic() {
+        check_no_diagnostic(
+            r"
+            enum Either {
+                A,
+                B,
+            }
+            fn test_fn() {
+                match Either::B {
+                    Either::A => {},
+                    Either::B => {},
+                }
+            }
+        ",
+        );
+    }
+
+    #[test]
+    fn enum_ref_missing_arms() {
+        check_diagnostic(
+            r"
+            enum Either {
+                A,
+                B,
+            }
+            fn test_fn() {
+                match &Either::B {
+                    Either::A => {},
+                }
+            }
+        ",
+        );
+    }
+
+    #[test]
+    fn enum_ref_no_diagnostic() {
+        check_no_diagnostic(
+            r"
+            enum Either {
+                A,
+                B,
+            }
+            fn test_fn() {
+                match &Either::B {
+                    Either::A => {},
+                    Either::B => {},
+                }
+            }
+        ",
+        );
+    }
+
+    #[test]
+    fn enum_containing_bool_no_arms() {
+        check_diagnostic(
+            r"
+            enum Either {
+                A(bool),
+                B,
+            }
+            fn test_fn() {
+                match Either::B {
+                }
+            }
+        ",
+        );
+    }
+
+    #[test]
+    fn enum_containing_bool_missing_arms() {
+        check_diagnostic(
+            r"
+            enum Either {
+                A(bool),
+                B,
+            }
+            fn test_fn() {
+                match Either::B {
+                    Either::A(true) => (),
+                    Either::B => (),
+                }
+            }
+        ",
+        );
+    }
+
+    #[test]
+    fn enum_containing_bool_no_diagnostic() {
+        check_no_diagnostic(
+            r"
+            enum Either {
+                A(bool),
+                B,
+            }
+            fn test_fn() {
+                match Either::B {
+                    Either::A(true) => (),
+                    Either::A(false) => (),
+                    Either::B => (),
+                }
+            }
+        ",
+        );
+    }
+
+    #[test]
+    fn enum_containing_bool_with_wild_no_diagnostic() {
+        check_no_diagnostic(
+            r"
+            enum Either {
+                A(bool),
+                B,
+            }
+            fn test_fn() {
+                match Either::B {
+                    Either::B => (),
+                    _ => (),
+                }
+            }
+        ",
+        );
+    }
+
+    #[test]
+    fn enum_containing_bool_with_wild_2_no_diagnostic() {
+        check_no_diagnostic(
+            r"
+            enum Either {
+                A(bool),
+                B,
+            }
+            fn test_fn() {
+                match Either::B {
+                    Either::A(_) => (),
+                    Either::B => (),
+                }
+            }
+        ",
+        );
+    }
+
+    #[test]
+    fn enum_different_sizes_missing_arms() {
+        check_diagnostic(
+            r"
+            enum Either {
+                A(bool),
+                B(bool, bool),
+            }
+            fn test_fn() {
+                match Either::A(false) {
+                    Either::A(_) => (),
+                    Either::B(false, _) => (),
+                }
+            }
+        ",
+        );
+    }
+
+    #[test]
+    fn enum_different_sizes_no_diagnostic() {
+        check_no_diagnostic(
+            r"
+            enum Either {
+                A(bool),
+                B(bool, bool),
+            }
+            fn test_fn() {
+                match Either::A(false) {
+                    Either::A(_) => (),
+                    Either::B(true, _) => (),
+                    Either::B(false, _) => (),
+                }
+            }
+        ",
+        );
+    }
+
+    #[test]
+    fn or_no_diagnostic() {
+        check_no_diagnostic(
+            r"
+            enum Either {
+                A(bool),
+                B(bool, bool),
+            }
+            fn test_fn() {
+                match Either::A(false) {
+                    Either::A(true) | Either::A(false) => (),
+                    Either::B(true, _) => (),
+                    Either::B(false, _) => (),
+                }
+            }
+        ",
+        );
+    }
+
+    #[test]
+    fn tuple_of_enum_no_diagnostic() {
+        check_no_diagnostic(
+            r"
+            enum Either {
+                A(bool),
+                B(bool, bool),
+            }
+            enum Either2 {
+                C,
+                D,
+            }
+            fn test_fn() {
+                match (Either::A(false), Either2::C) {
+                    (Either::A(true), _) | (Either::A(false), _) => (),
+                    (Either::B(true, _), Either2::C) => (),
+                    (Either::B(false, _), Either2::C) => (),
+                    (Either::B(_, _), Either2::D) => (),
+                }
+            }
+        ",
         );
     }
 
@@ -1064,24 +1467,54 @@ fn main() {
     fn mismatched_types() {
         // Match statements with arms that don't match the
         // expression pattern do not fire this diagnostic.
-        check_diagnostics(
-            r#"
-enum Either { A, B }
-enum Either2 { C, D }
-
-fn main() {
-    match Either::A {
-        Either2::C => (),
-        Either2::D => (),
-    }
-    match (true, false) {
-        (true, false, true) => (),
-        (true) => (),
-    }
-    match (0) { () => () }
-    match Unresolved::Bar { Unresolved::Baz => () }
-}
-        "#,
+        check_no_diagnostic(
+            r"
+            enum Either {
+                A,
+                B,
+            }
+            enum Either2 {
+                C,
+                D,
+            }
+            fn test_fn() {
+                match Either::A {
+                    Either2::C => (),
+                    Either2::D => (),
+                }
+            }
+        ",
+        );
+    }
+
+    #[test]
+    fn mismatched_types_with_different_arity() {
+        // Match statements with arms that don't match the
+        // expression pattern do not fire this diagnostic.
+        check_no_diagnostic(
+            r"
+            fn test_fn() {
+                match (true, false) {
+                    (true, false, true) => (),
+                    (true) => (),
+                }
+            }
+        ",
+        );
+    }
+
+    #[test]
+    fn malformed_match_arm_tuple_missing_pattern() {
+        // Match statements with arms that don't match the
+        // expression pattern do not fire this diagnostic.
+        check_no_diagnostic(
+            r"
+            fn test_fn() {
+                match (0) {
+                    () => (),
+                }
+            }
+        ",
         );
     }
 
@@ -1089,333 +1522,636 @@ fn main() {
     fn malformed_match_arm_tuple_enum_missing_pattern() {
         // We are testing to be sure we don't panic here when the match
         // arm `Either::B` is missing its pattern.
-        check_diagnostics(
-            r#"
-enum Either { A, B(u32) }
-
-fn main() {
-    match Either::A {
-        Either::A => (),
-        Either::B() => (),
+        check_no_diagnostic(
+            r"
+            enum Either {
+                A,
+                B(u32),
+            }
+            fn test_fn() {
+                match Either::A {
+                    Either::A => (),
+                    Either::B() => (),
+                }
+            }
+        ",
+        );
     }
-}
-"#,
+
+    #[test]
+    fn enum_not_in_scope() {
+        // The enum is not in scope so we don't perform exhaustiveness
+        // checking, but we want to be sure we don't panic here (and
+        // we don't create a diagnostic).
+        check_no_diagnostic(
+            r"
+            fn test_fn() {
+                match Foo::Bar {
+                    Foo::Baz => (),
+                }
+            }
+        ",
         );
     }
 
     #[test]
     fn expr_diverges() {
-        check_diagnostics(
-            r#"
-enum Either { A, B }
-
-fn main() {
-    match loop {} {
-        Either::A => (),
-        Either::B => (),
-    }
-    match loop {} {
-        Either::A => (),
-    }
-    match loop { break Foo::A } {
-        //^^^^^^^^^^^^^^^^^^^^^ Missing match arm
-        Either::A => (),
-    }
-    match loop { break Foo::A } {
-        Either::A => (),
-        Either::B => (),
+        check_no_diagnostic(
+            r"
+            enum Either {
+                A,
+                B,
+            }
+            fn test_fn() {
+                match loop {} {
+                    Either::A => (),
+                    Either::B => (),
+                }
+            }
+        ",
+        );
     }
-}
-"#,
+
+    #[test]
+    fn expr_loop_with_break() {
+        check_no_diagnostic(
+            r"
+            enum Either {
+                A,
+                B,
+            }
+            fn test_fn() {
+                match loop { break Foo::A } {
+                    Either::A => (),
+                    Either::B => (),
+                }
+            }
+        ",
         );
     }
 
     #[test]
     fn expr_partially_diverges() {
-        check_diagnostics(
-            r#"
-enum Either<T> { A(T), B }
+        check_no_diagnostic(
+            r"
+            enum Either<T> {
+                A(T),
+                B,
+            }
+            fn foo() -> Either<!> {
+                Either::B
+            }
+            fn test_fn() -> u32 {
+                match foo() {
+                    Either::A(val) => val,
+                    Either::B => 0,
+                }
+            }
+        ",
+        );
+    }
 
-fn foo() -> Either<!> { Either::B }
-fn main() -> u32 {
-    match foo() {
-        Either::A(val) => val,
-        Either::B => 0,
+    #[test]
+    fn enum_record_no_arms() {
+        check_diagnostic(
+            r"
+            enum Either {
+                A { foo: bool },
+                B,
+            }
+            fn test_fn() {
+                let a = Either::A { foo: true };
+                match a {
+                }
+            }
+        ",
+        );
     }
-}
-"#,
+
+    #[test]
+    fn enum_record_missing_arms() {
+        check_diagnostic(
+            r"
+            enum Either {
+                A { foo: bool },
+                B,
+            }
+            fn test_fn() {
+                let a = Either::A { foo: true };
+                match a {
+                    Either::A { foo: true } => (),
+                }
+            }
+        ",
         );
     }
 
     #[test]
-    fn enum_record() {
-        check_diagnostics(
-            r#"
-enum Either { A { foo: bool }, B }
+    fn enum_record_no_diagnostic() {
+        check_no_diagnostic(
+            r"
+            enum Either {
+                A { foo: bool },
+                B,
+            }
+            fn test_fn() {
+                let a = Either::A { foo: true };
+                match a {
+                    Either::A { foo: true } => (),
+                    Either::A { foo: false } => (),
+                    Either::B => (),
+                }
+            }
+        ",
+        );
+    }
 
-fn main() {
-    let a = Either::A { foo: true };
-    match a { }
-        //^ Missing match arm
-    match a { Either::A { foo: true } => () }
-        //^ Missing match arm
-    match a {
-        Either::A { } => (),
-                //^^^ Missing structure fields:
-                //  | - foo
-        Either::B => (),
+    #[test]
+    fn enum_record_missing_field_no_diagnostic() {
+        // When `Either::A` is missing a struct member, we don't want
+        // to fire the missing match arm diagnostic. This should fire
+        // some other diagnostic.
+        check_no_diagnostic(
+            r"
+            enum Either {
+                A { foo: bool },
+                B,
+            }
+            fn test_fn() {
+                let a = Either::B;
+                match a {
+                    Either::A { } => (),
+                    Either::B => (),
+                }
+            }
+        ",
+        );
     }
-    match a {
-        //^ Missing match arm
-        Either::A { } => (),
-    }           //^^^ Missing structure fields:
-                //  | - foo
 
-    match a {
-        Either::A { foo: true } => (),
-        Either::A { foo: false } => (),
-        Either::B => (),
+    #[test]
+    fn enum_record_missing_field_missing_match_arm() {
+        // Even though `Either::A` is missing fields, we still want to fire
+        // the missing arm diagnostic here, since we know `Either::B` is missing.
+        check_diagnostic(
+            r"
+            enum Either {
+                A { foo: bool },
+                B,
+            }
+            fn test_fn() {
+                let a = Either::B;
+                match a {
+                    Either::A { } => (),
+                }
+            }
+        ",
+        );
     }
-    match a {
-        Either::A { foo: _ } => (),
-        Either::B => (),
+
+    #[test]
+    fn enum_record_no_diagnostic_wild() {
+        check_no_diagnostic(
+            r"
+            enum Either {
+                A { foo: bool },
+                B,
+            }
+            fn test_fn() {
+                let a = Either::A { foo: true };
+                match a {
+                    Either::A { foo: _ } => (),
+                    Either::B => (),
+                }
+            }
+        ",
+        );
     }
-}
-"#,
+
+    #[test]
+    fn enum_record_fields_out_of_order_missing_arm() {
+        check_diagnostic(
+            r"
+            enum Either {
+                A { foo: bool, bar: () },
+                B,
+            }
+            fn test_fn() {
+                let a = Either::A { foo: true };
+                match a {
+                    Either::A { bar: (), foo: false } => (),
+                    Either::A { foo: true, bar: () } => (),
+                }
+            }
+        ",
         );
     }
 
     #[test]
-    fn enum_record_fields_out_of_order() {
-        check_diagnostics(
-            r#"
-enum Either {
-    A { foo: bool, bar: () },
-    B,
-}
+    fn enum_record_fields_out_of_order_no_diagnostic() {
+        check_no_diagnostic(
+            r"
+            enum Either {
+                A { foo: bool, bar: () },
+                B,
+            }
+            fn test_fn() {
+                let a = Either::A { foo: true };
+                match a {
+                    Either::A { bar: (), foo: false } => (),
+                    Either::A { foo: true, bar: () } => (),
+                    Either::B => (),
+                }
+            }
+        ",
+        );
+    }
 
-fn main() {
-    let a = Either::A { foo: true, bar: () };
-    match a {
-        //^ Missing match arm
-        Either::A { bar: (), foo: false } => (),
-        Either::A { foo: true, bar: () } => (),
+    #[test]
+    fn enum_record_ellipsis_missing_arm() {
+        check_diagnostic(
+            r"
+            enum Either {
+                A { foo: bool, bar: bool },
+                B,
+            }
+            fn test_fn() {
+                match Either::B {
+                    Either::A { foo: true, .. } => (),
+                    Either::B => (),
+                }
+            }
+        ",
+        );
     }
 
-    match a {
-        Either::A { bar: (), foo: false } => (),
-        Either::A { foo: true, bar: () } => (),
-        Either::B => (),
+    #[test]
+    fn enum_record_ellipsis_no_diagnostic() {
+        check_no_diagnostic(
+            r"
+            enum Either {
+                A { foo: bool, bar: bool },
+                B,
+            }
+            fn test_fn() {
+                let a = Either::A { foo: true };
+                match a {
+                    Either::A { foo: true, .. } => (),
+                    Either::A { foo: false, .. } => (),
+                    Either::B => (),
+                }
+            }
+        ",
+        );
     }
-}
-"#,
+
+    #[test]
+    fn enum_record_ellipsis_all_fields_missing_arm() {
+        check_diagnostic(
+            r"
+            enum Either {
+                A { foo: bool, bar: bool },
+                B,
+            }
+            fn test_fn() {
+                let a = Either::B;
+                match a {
+                    Either::A { .. } => (),
+                }
+            }
+        ",
         );
     }
 
     #[test]
-    fn enum_record_ellipsis() {
-        check_diagnostics(
-            r#"
-enum Either {
-    A { foo: bool, bar: bool },
-    B,
-}
+    fn enum_record_ellipsis_all_fields_no_diagnostic() {
+        check_no_diagnostic(
+            r"
+            enum Either {
+                A { foo: bool, bar: bool },
+                B,
+            }
+            fn test_fn() {
+                let a = Either::B;
+                match a {
+                    Either::A { .. } => (),
+                    Either::B => (),
+                }
+            }
+        ",
+        );
+    }
 
-fn main() {
-    let a = Either::B;
-    match a {
-        //^ Missing match arm
-        Either::A { foo: true, .. } => (),
-        Either::B => (),
+    #[test]
+    fn enum_tuple_partial_ellipsis_no_diagnostic() {
+        check_no_diagnostic(
+            r"
+            enum Either {
+                A(bool, bool, bool, bool),
+                B,
+            }
+            fn test_fn() {
+                match Either::B {
+                    Either::A(true, .., true) => {},
+                    Either::A(true, .., false) => {},
+                    Either::A(false, .., true) => {},
+                    Either::A(false, .., false) => {},
+                    Either::B => {},
+                }
+            }
+        ",
+        );
     }
-    match a {
-        //^ Missing match arm
-        Either::A { .. } => (),
+
+    #[test]
+    fn enum_tuple_partial_ellipsis_2_no_diagnostic() {
+        check_no_diagnostic(
+            r"
+            enum Either {
+                A(bool, bool, bool, bool),
+                B,
+            }
+            fn test_fn() {
+                match Either::B {
+                    Either::A(true, .., true) => {},
+                    Either::A(true, .., false) => {},
+                    Either::A(.., true) => {},
+                    Either::A(.., false) => {},
+                    Either::B => {},
+                }
+            }
+        ",
+        );
     }
 
-    match a {
-        Either::A { foo: true, .. } => (),
-        Either::A { foo: false, .. } => (),
-        Either::B => (),
+    #[test]
+    fn enum_tuple_partial_ellipsis_missing_arm() {
+        check_diagnostic(
+            r"
+            enum Either {
+                A(bool, bool, bool, bool),
+                B,
+            }
+            fn test_fn() {
+                match Either::B {
+                    Either::A(true, .., true) => {},
+                    Either::A(true, .., false) => {},
+                    Either::A(false, .., false) => {},
+                    Either::B => {},
+                }
+            }
+        ",
+        );
     }
 
-    match a {
-        Either::A { .. } => (),
-        Either::B => (),
+    #[test]
+    fn enum_tuple_partial_ellipsis_2_missing_arm() {
+        check_diagnostic(
+            r"
+            enum Either {
+                A(bool, bool, bool, bool),
+                B,
+            }
+            fn test_fn() {
+                match Either::B {
+                    Either::A(true, .., true) => {},
+                    Either::A(true, .., false) => {},
+                    Either::A(.., true) => {},
+                    Either::B => {},
+                }
+            }
+        ",
+        );
     }
-}
-"#,
+
+    #[test]
+    fn enum_tuple_ellipsis_no_diagnostic() {
+        check_no_diagnostic(
+            r"
+            enum Either {
+                A(bool, bool, bool, bool),
+                B,
+            }
+            fn test_fn() {
+                match Either::B {
+                    Either::A(..) => {},
+                    Either::B => {},
+                }
+            }
+        ",
         );
     }
 
     #[test]
-    fn enum_tuple_partial_ellipsis() {
-        check_diagnostics(
-            r#"
-enum Either {
-    A(bool, bool, bool, bool),
-    B,
-}
+    fn enum_never() {
+        check_no_diagnostic(
+            r"
+            enum Never {}
 
-fn main() {
-    match Either::B {
-        //^^^^^^^^^ Missing match arm
-        Either::A(true, .., true) => (),
-        Either::A(true, .., false) => (),
-        Either::A(false, .., false) => (),
-        Either::B => (),
-    }
-    match Either::B {
-        //^^^^^^^^^ Missing match arm
-        Either::A(true, .., true) => (),
-        Either::A(true, .., false) => (),
-        Either::A(.., true) => (),
-        Either::B => (),
-    }
-
-    match Either::B {
-        Either::A(true, .., true) => (),
-        Either::A(true, .., false) => (),
-        Either::A(false, .., true) => (),
-        Either::A(false, .., false) => (),
-        Either::B => (),
-    }
-    match Either::B {
-        Either::A(true, .., true) => (),
-        Either::A(true, .., false) => (),
-        Either::A(.., true) => (),
-        Either::A(.., false) => (),
-        Either::B => (),
+            fn test_fn(never: Never) {
+                match never {}
+            }
+        ",
+        );
     }
-}
-"#,
+
+    #[test]
+    fn type_never() {
+        check_no_diagnostic(
+            r"
+            fn test_fn(never: !) {
+                match never {}
+            }
+        ",
         );
     }
 
     #[test]
-    fn never() {
-        check_diagnostics(
-            r#"
-enum Never {}
+    fn enum_never_ref() {
+        check_no_diagnostic(
+            r"
+            enum Never {}
 
-fn enum_(never: Never) {
-    match never {}
-}
-fn enum_ref(never: &Never) {
-    match never {}
-}
-fn bang(never: !) {
-    match never {}
-}
-"#,
+            fn test_fn(never: &Never) {
+                match never {}
+            }
+        ",
+        );
+    }
+
+    #[test]
+    fn expr_diverges_missing_arm() {
+        check_no_diagnostic(
+            r"
+            enum Either {
+                A,
+                B,
+            }
+            fn test_fn() {
+                match loop {} {
+                    Either::A => (),
+                }
+            }
+        ",
         );
     }
 
     #[test]
     fn or_pattern_panic() {
-        check_diagnostics(
-            r#"
-pub enum Category { Infinity, Zero }
+        check_no_diagnostic(
+            r"
+            pub enum Category {
+                Infinity,
+                Zero,
+            }
 
-fn panic(a: Category, b: Category) {
-    match (a, b) {
-        (Category::Zero | Category::Infinity, _) => (),
-        (_, Category::Zero | Category::Infinity) => (),
+            fn panic(a: Category, b: Category) {
+                match (a, b) {
+                    (Category::Zero | Category::Infinity, _) => {}
+                    (_, Category::Zero | Category::Infinity) => {}
+                }
+            }
+        ",
+        );
     }
 
-    // FIXME: This is a false positive, but the code used to cause a panic in the match checker,
-    // so this acts as a regression test for that.
-    match (a, b) {
-        //^^^^^^ Missing match arm
-        (Category::Infinity, Category::Infinity) | (Category::Zero, Category::Zero) => (),
-        (Category::Infinity | Category::Zero, _) => (),
+    #[test]
+    fn or_pattern_panic_2() {
+        // FIXME: This is a false positive, but the code used to cause a panic in the match checker,
+        // so this acts as a regression test for that.
+        check_diagnostic(
+            r"
+            pub enum Category {
+                Infinity,
+                Zero,
+            }
+
+            fn panic(a: Category, b: Category) {
+                match (a, b) {
+                    (Category::Infinity, Category::Infinity) | (Category::Zero, Category::Zero) => {}
+
+                    (Category::Infinity | Category::Zero, _) => {}
+                }
+            }
+        ",
+        );
     }
 }
-"#,
-        );
-    }
-
-    mod false_negatives {
-        //! The implementation of match checking here is a work in progress. As we roll this out, we
-        //! prefer false negatives to false positives (ideally there would be no false positives). This
-        //! test module should document known false negatives. Eventually we will have a complete
-        //! implementation of match checking and this module will be empty.
-        //!
-        //! The reasons for documenting known false negatives:
-        //!
-        //!   1. It acts as a backlog of work that can be done to improve the behavior of the system.
-        //!   2. It ensures the code doesn't panic when handling these cases.
-        use super::*;
-
-        #[test]
-        fn integers() {
-            // We don't currently check integer exhaustiveness.
-            check_diagnostics(
-                r#"
-fn main() {
-    match 5 {
-        10 => (),
-        11..20 => (),
+
+#[cfg(test)]
+mod false_negatives {
+    //! The implementation of match checking here is a work in progress. As we roll this out, we
+    //! prefer false negatives to false positives (ideally there would be no false positives). This
+    //! test module should document known false negatives. Eventually we will have a complete
+    //! implementation of match checking and this module will be empty.
+    //!
+    //! The reasons for documenting known false negatives:
+    //!
+    //!   1. It acts as a backlog of work that can be done to improve the behavior of the system.
+    //!   2. It ensures the code doesn't panic when handling these cases.
+
+    use super::tests::*;
+
+    #[test]
+    fn integers() {
+        // This is a false negative.
+        // We don't currently check integer exhaustiveness.
+        check_no_diagnostic(
+            r"
+            fn test_fn() {
+                match 5 {
+                    10 => (),
+                    11..20 => (),
+                }
+            }
+        ",
+        );
     }
-}
-"#,
-            );
-        }
 
-        #[test]
-        fn internal_or() {
-            // We do not currently handle patterns with internal `or`s.
-            check_diagnostics(
-                r#"
-fn main() {
-    enum Either { A(bool), B }
-    match Either::B {
-        Either::A(true | false) => (),
+    #[test]
+    fn internal_or() {
+        // This is a false negative.
+        // We do not currently handle patterns with internal `or`s.
+        check_no_diagnostic(
+            r"
+            fn test_fn() {
+                enum Either {
+                    A(bool),
+                    B,
+                }
+                match Either::B {
+                    Either::A(true | false) => (),
+                }
+            }
+        ",
+        );
     }
-}
-"#,
-            );
-        }
 
-        #[test]
-        fn tuple_of_bools_with_ellipsis_at_end_missing_arm() {
-            // We don't currently handle tuple patterns with ellipsis.
-            check_diagnostics(
-                r#"
-fn main() {
-    match (false, true, false) {
-        (false, ..) => (),
+    #[test]
+    fn expr_loop_missing_arm() {
+        // This is a false negative.
+        // We currently infer the type of `loop { break Foo::A }` to `!`, which
+        // causes us to skip the diagnostic since `Either::A` doesn't type check
+        // with `!`.
+        check_diagnostic(
+            r"
+            enum Either {
+                A,
+                B,
+            }
+            fn test_fn() {
+                match loop { break Foo::A } {
+                    Either::A => (),
+                }
+            }
+        ",
+        );
     }
-}
-"#,
-            );
-        }
 
-        #[test]
-        fn tuple_of_bools_with_ellipsis_at_beginning_missing_arm() {
-            // We don't currently handle tuple patterns with ellipsis.
-            check_diagnostics(
-                r#"
-fn main() {
-    match (false, true, false) {
-        (.., false) => (),
+    #[test]
+    fn tuple_of_bools_with_ellipsis_at_end_missing_arm() {
+        // This is a false negative.
+        // We don't currently handle tuple patterns with ellipsis.
+        check_no_diagnostic(
+            r"
+            fn test_fn() {
+                match (false, true, false) {
+                    (false, ..) => {},
+                }
+            }
+        ",
+        );
     }
-}
-"#,
-            );
-        }
 
-        #[test]
-        fn struct_missing_arm() {
-            // We don't currently handle structs.
-            check_diagnostics(
-                r#"
-struct Foo { a: bool }
-fn main(f: Foo) {
-    match f { Foo { a: true } => () }
-}
-"#,
-            );
-        }
+    #[test]
+    fn tuple_of_bools_with_ellipsis_at_beginning_missing_arm() {
+        // This is a false negative.
+        // We don't currently handle tuple patterns with ellipsis.
+        check_no_diagnostic(
+            r"
+            fn test_fn() {
+                match (false, true, false) {
+                    (.., false) => {},
+                }
+            }
+        ",
+        );
+    }
+
+    #[test]
+    fn struct_missing_arm() {
+        // This is a false negative.
+        // We don't currently handle structs.
+        check_no_diagnostic(
+            r"
+            struct Foo {
+                a: bool,
+            }
+            fn test_fn(f: Foo) {
+                match f {
+                    Foo { a: true } => {},
+                }
+            }
+        ",
+        );
     }
 }
diff --git a/crates/ra_hir_ty/src/traits/builtin.rs b/crates/ra_hir_ty/src/traits/builtin.rs
index 60cc9a9f50..86e22e4595 100644
--- a/crates/ra_hir_ty/src/traits/builtin.rs
+++ b/crates/ra_hir_ty/src/traits/builtin.rs
@@ -121,7 +121,7 @@ fn closure_fn_trait_impl_datum(
             .build(),
     );
     let sig_ty = Ty::apply(
-        TypeCtor::FnPtr { num_args, is_varargs: false },
+        TypeCtor::FnPtr { num_args },
         Substs::builder(num_args as usize + 1)
             .fill_with_bound_vars(DebruijnIndex::INNERMOST, 0)
             .build(),
diff --git a/crates/ra_hir_ty/src/traits/chalk/mapping.rs b/crates/ra_hir_ty/src/traits/chalk/mapping.rs
index 3ebb55f774..06453ef820 100644
--- a/crates/ra_hir_ty/src/traits/chalk/mapping.rs
+++ b/crates/ra_hir_ty/src/traits/chalk/mapping.rs
@@ -30,8 +30,7 @@ impl ToChalk for Ty {
             Ty::Apply(apply_ty) => match apply_ty.ctor {
                 TypeCtor::Ref(m) => ref_to_chalk(db, m, apply_ty.parameters),
                 TypeCtor::Array => array_to_chalk(db, apply_ty.parameters),
-                TypeCtor::FnPtr { num_args: _, is_varargs: _ } => {
-                    // FIXME: handle is_varargs
+                TypeCtor::FnPtr { num_args: _ } => {
                     let substitution = apply_ty.parameters.to_chalk(db).shifted_in(&Interner);
                     chalk_ir::TyData::Function(chalk_ir::Fn { num_binders: 0, substitution })
                         .intern(&Interner)
@@ -125,10 +124,7 @@ impl ToChalk for Ty {
                     substitution.shifted_out(&Interner).expect("fn ptr should have no binders"),
                 );
                 Ty::Apply(ApplicationTy {
-                    ctor: TypeCtor::FnPtr {
-                        num_args: (parameters.len() - 1) as u16,
-                        is_varargs: false,
-                    },
+                    ctor: TypeCtor::FnPtr { num_args: (parameters.len() - 1) as u16 },
                     parameters,
                 })
             }
diff --git a/crates/ra_hir_ty/src/diagnostics/unsafe_check.rs b/crates/ra_hir_ty/src/unsafe_validation.rs
similarity index 67%
rename from crates/ra_hir_ty/src/diagnostics/unsafe_check.rs
rename to crates/ra_hir_ty/src/unsafe_validation.rs
index 9e4ed9a8b7..c512c4f8e9 100644
--- a/crates/ra_hir_ty/src/diagnostics/unsafe_check.rs
+++ b/crates/ra_hir_ty/src/unsafe_validation.rs
@@ -6,7 +6,7 @@ use std::sync::Arc;
 use hir_def::{
     body::Body,
     expr::{Expr, ExprId, UnaryOp},
-    DefWithBodyId,
+    DefWithBodyId, FunctionId,
 };
 use hir_expand::diagnostics::DiagnosticSink;
 
@@ -15,29 +15,26 @@ use crate::{
     InferenceResult, Ty, TypeCtor,
 };
 
-pub(super) struct UnsafeValidator<'a, 'b: 'a> {
-    owner: DefWithBodyId,
+pub struct UnsafeValidator<'a, 'b: 'a> {
+    func: FunctionId,
     infer: Arc<InferenceResult>,
     sink: &'a mut DiagnosticSink<'b>,
 }
 
 impl<'a, 'b> UnsafeValidator<'a, 'b> {
-    pub(super) fn new(
-        owner: DefWithBodyId,
+    pub fn new(
+        func: FunctionId,
         infer: Arc<InferenceResult>,
         sink: &'a mut DiagnosticSink<'b>,
     ) -> UnsafeValidator<'a, 'b> {
-        UnsafeValidator { owner, infer, sink }
+        UnsafeValidator { func, infer, sink }
     }
 
-    pub(super) fn validate_body(&mut self, db: &dyn HirDatabase) {
-        let def = self.owner.into();
+    pub fn validate_body(&mut self, db: &dyn HirDatabase) {
+        let def = self.func.into();
         let unsafe_expressions = unsafe_expressions(db, self.infer.as_ref(), def);
-        let is_unsafe = match self.owner {
-            DefWithBodyId::FunctionId(it) => db.function_data(it).is_unsafe,
-            DefWithBodyId::StaticId(_) | DefWithBodyId::ConstId(_) => false,
-        };
-        if is_unsafe
+        let func_data = db.function_data(self.func);
+        if func_data.is_unsafe
             || unsafe_expressions
                 .iter()
                 .filter(|unsafe_expr| !unsafe_expr.inside_unsafe_block)
@@ -121,53 +118,3 @@ fn walk_unsafe(
         walk_unsafe(unsafe_exprs, db, infer, body, child, inside_unsafe_block);
     });
 }
-
-#[cfg(test)]
-mod tests {
-    use crate::diagnostics::tests::check_diagnostics;
-
-    #[test]
-    fn missing_unsafe_diagnostic_with_raw_ptr() {
-        check_diagnostics(
-            r#"
-fn main() {
-    let x = &5 as *const usize;
-    unsafe { let y = *x; }
-    let z = *x;
-}         //^^ This operation is unsafe and requires an unsafe function or block
-"#,
-        )
-    }
-
-    #[test]
-    fn missing_unsafe_diagnostic_with_unsafe_call() {
-        check_diagnostics(
-            r#"
-struct HasUnsafe;
-
-impl HasUnsafe {
-    unsafe fn unsafe_fn(&self) {
-        let x = &5 as *const usize;
-        let y = *x;
-    }
-}
-
-unsafe fn unsafe_fn() {
-    let x = &5 as *const usize;
-    let y = *x;
-}
-
-fn main() {
-    unsafe_fn();
-  //^^^^^^^^^^^ This operation is unsafe and requires an unsafe function or block
-    HasUnsafe.unsafe_fn();
-  //^^^^^^^^^^^^^^^^^^^^^ This operation is unsafe and requires an unsafe function or block
-    unsafe {
-        unsafe_fn();
-        HasUnsafe.unsafe_fn();
-    }
-}
-"#,
-        );
-    }
-}
diff --git a/crates/ra_ide/Cargo.toml b/crates/ra_ide/Cargo.toml
index 6f81074912..df2fad5201 100644
--- a/crates/ra_ide/Cargo.toml
+++ b/crates/ra_ide/Cargo.toml
@@ -3,7 +3,6 @@ edition = "2018"
 name = "ra_ide"
 version = "0.1.0"
 authors = ["rust-analyzer developers"]
-license = "MIT OR Apache-2.0"
 
 [lib]
 doctest = false
diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs
index 9e82d68541..3d93f70674 100644
--- a/crates/ra_ide/src/completion/completion_context.rs
+++ b/crates/ra_ide/src/completion/completion_context.rs
@@ -63,8 +63,6 @@ pub(crate) struct CompletionContext<'a> {
     pub(super) dot_receiver_is_ambiguous_float_literal: bool,
     /// If this is a call (method or function) in particular, i.e. the () are already there.
     pub(super) is_call: bool,
-    /// Like `is_call`, but for tuple patterns.
-    pub(super) is_pattern_call: bool,
     /// If this is a macro call, i.e. the () are already there.
     pub(super) is_macro_call: bool,
     pub(super) is_path_type: bool,
@@ -138,7 +136,6 @@ impl<'a> CompletionContext<'a> {
             is_new_item: false,
             dot_receiver: None,
             is_call: false,
-            is_pattern_call: false,
             is_macro_call: false,
             is_path_type: false,
             has_type_args: false,
@@ -373,8 +370,6 @@ impl<'a> CompletionContext<'a> {
                 .and_then(|it| it.syntax().parent().and_then(ast::CallExpr::cast))
                 .is_some();
             self.is_macro_call = path.syntax().parent().and_then(ast::MacroCall::cast).is_some();
-            self.is_pattern_call =
-                path.syntax().parent().and_then(ast::TupleStructPat::cast).is_some();
 
             self.is_path_type = path.syntax().parent().and_then(ast::PathType::cast).is_some();
             self.has_type_args = segment.type_arg_list().is_some();
diff --git a/crates/ra_ide/src/completion/presentation.rs b/crates/ra_ide/src/completion/presentation.rs
index 64349dcb83..48afee5fb4 100644
--- a/crates/ra_ide/src/completion/presentation.rs
+++ b/crates/ra_ide/src/completion/presentation.rs
@@ -315,7 +315,6 @@ impl Completions {
         }
 
         if variant_kind == StructKind::Tuple {
-            mark::hit!(inserts_parens_for_tuple_enums);
             let params = Params::Anonymous(variant.fields(ctx.db).len());
             res = res.add_call_parens(ctx, qualified_name, params)
         }
@@ -384,17 +383,10 @@ impl Builder {
         if !ctx.config.add_call_parenthesis {
             return self;
         }
-        if ctx.use_item_syntax.is_some() {
+        if ctx.use_item_syntax.is_some() || ctx.is_call {
             mark::hit!(no_parens_in_use_item);
             return self;
         }
-        if ctx.is_pattern_call {
-            mark::hit!(dont_duplicate_pattern_parens);
-            return self;
-        }
-        if ctx.is_call {
-            return self;
-        }
 
         // Don't add parentheses if the expected type is some function reference.
         if let Some(ty) = &ctx.expected_type {
@@ -873,7 +865,6 @@ fn main() { foo(${1:foo}, ${2:bar}, ${3:ho_ge_})$0 }
 
     #[test]
     fn inserts_parens_for_tuple_enums() {
-        mark::check!(inserts_parens_for_tuple_enums);
         check_edit(
             "Some",
             r#"
@@ -914,30 +905,6 @@ fn main(value: Option<i32>) {
         );
     }
 
-    #[test]
-    fn dont_duplicate_pattern_parens() {
-        mark::check!(dont_duplicate_pattern_parens);
-        check_edit(
-            "Var",
-            r#"
-enum E { Var(i32) }
-fn main() {
-    match E::Var(92) {
-        E::<|>(92) => (),
-    }
-}
-"#,
-            r#"
-enum E { Var(i32) }
-fn main() {
-    match E::Var(92) {
-        E::Var(92) => (),
-    }
-}
-"#,
-        );
-    }
-
     #[test]
     fn no_call_parens_if_fn_ptr_needed() {
         mark::check!(no_call_parens_if_fn_ptr_needed);
diff --git a/crates/ra_ide/src/goto_definition.rs b/crates/ra_ide/src/goto_definition.rs
index c30b206115..f575d738fe 100644
--- a/crates/ra_ide/src/goto_definition.rs
+++ b/crates/ra_ide/src/goto_definition.rs
@@ -866,22 +866,4 @@ type Alias<T> = T<|>;
 "#,
         )
     }
-
-    #[test]
-    fn goto_def_for_macro_container() {
-        check(
-            r#"
-//- /lib.rs
-foo::module<|>::mac!();
-
-//- /foo/lib.rs
-pub mod module {
-      //^^^^^^
-    #[macro_export]
-    macro_rules! _mac { () => { () } }
-    pub use crate::_mac as mac;
-}
-"#,
-        );
-    }
 }
diff --git a/crates/ra_ide_db/Cargo.toml b/crates/ra_ide_db/Cargo.toml
index bcddad60cf..c3921bd406 100644
--- a/crates/ra_ide_db/Cargo.toml
+++ b/crates/ra_ide_db/Cargo.toml
@@ -3,7 +3,6 @@ edition = "2018"
 name = "ra_ide_db"
 version = "0.1.0"
 authors = ["rust-analyzer developers"]
-license = "MIT OR Apache-2.0"
 
 [lib]
 doctest = false
diff --git a/crates/ra_ide_db/src/defs.rs b/crates/ra_ide_db/src/defs.rs
index bcaabca92f..3ef5e74b69 100644
--- a/crates/ra_ide_db/src/defs.rs
+++ b/crates/ra_ide_db/src/defs.rs
@@ -255,14 +255,8 @@ pub fn classify_name_ref(
     }
 
     if let Some(macro_call) = parent.ancestors().find_map(ast::MacroCall::cast) {
-        if let Some(path) = macro_call.path() {
-            if path.qualifier().is_none() {
-                // Only use this to resolve single-segment macro calls like `foo!()`. Multi-segment
-                // paths are handled below (allowing `log<|>::info!` to resolve to the log crate).
-                if let Some(macro_def) = sema.resolve_macro_call(&macro_call) {
-                    return Some(NameRefClass::Definition(Definition::Macro(macro_def)));
-                }
-            }
+        if let Some(macro_def) = sema.resolve_macro_call(&macro_call) {
+            return Some(NameRefClass::Definition(Definition::Macro(macro_def)));
         }
     }
 
diff --git a/crates/ra_mbe/Cargo.toml b/crates/ra_mbe/Cargo.toml
index a26746a194..4dec24914b 100644
--- a/crates/ra_mbe/Cargo.toml
+++ b/crates/ra_mbe/Cargo.toml
@@ -3,7 +3,6 @@ edition = "2018"
 name = "ra_mbe"
 version = "0.1.0"
 authors = ["rust-analyzer developers"]
-license = "MIT OR Apache-2.0"
 
 [lib]
 doctest = false
diff --git a/crates/ra_parser/Cargo.toml b/crates/ra_parser/Cargo.toml
index 72ec3e4d9a..0da581fd53 100644
--- a/crates/ra_parser/Cargo.toml
+++ b/crates/ra_parser/Cargo.toml
@@ -4,7 +4,6 @@ name = "ra_parser"
 version = "0.1.0"
 authors = ["rust-analyzer developers"]
 publish = false
-license = "MIT OR Apache-2.0"
 
 [lib]
 doctest = false
diff --git a/crates/ra_proc_macro/Cargo.toml b/crates/ra_proc_macro/Cargo.toml
index c4b6e9e7ba..d009ceb82e 100644
--- a/crates/ra_proc_macro/Cargo.toml
+++ b/crates/ra_proc_macro/Cargo.toml
@@ -4,7 +4,6 @@ name = "ra_proc_macro"
 version = "0.1.0"
 authors = ["rust-analyzer developers"]
 publish = false
-license = "MIT OR Apache-2.0"
 
 [lib]
 doctest = false
diff --git a/crates/ra_proc_macro_srv/Cargo.toml b/crates/ra_proc_macro_srv/Cargo.toml
index 12ce497f87..5821029450 100644
--- a/crates/ra_proc_macro_srv/Cargo.toml
+++ b/crates/ra_proc_macro_srv/Cargo.toml
@@ -4,7 +4,6 @@ name = "ra_proc_macro_srv"
 version = "0.1.0"
 authors = ["rust-analyzer developers"]
 publish = false
-license = "MIT OR Apache-2.0"
 
 [lib]
 doctest = false
diff --git a/crates/ra_prof/Cargo.toml b/crates/ra_prof/Cargo.toml
index 69ac51c9e2..3cd8481ea8 100644
--- a/crates/ra_prof/Cargo.toml
+++ b/crates/ra_prof/Cargo.toml
@@ -4,7 +4,6 @@ name = "ra_prof"
 version = "0.1.0"
 authors = ["rust-analyzer developers"]
 publish = false
-license = "MIT OR Apache-2.0"
 
 [lib]
 doctest = false
@@ -13,6 +12,7 @@ doctest = false
 ra_arena = { path = "../ra_arena" }
 once_cell = "1.3.1"
 backtrace = { version = "0.3.44", optional = true }
+mimalloc = { version = "0.1.19", default-features = false, optional = true }
 
 [target.'cfg(not(target_env = "msvc"))'.dependencies]
 jemallocator = { version = "0.3.2", optional = true }
@@ -25,4 +25,5 @@ cpu_profiler = []
 # Uncomment to enable for the whole crate graph
 # default = [ "backtrace" ]
 # default = [ "jemalloc" ]
+# default = [ "mimalloc" ]
 # default = [ "cpu_profiler" ]
diff --git a/crates/ra_prof/src/lib.rs b/crates/ra_prof/src/lib.rs
index 7163a84243..b54531b4e1 100644
--- a/crates/ra_prof/src/lib.rs
+++ b/crates/ra_prof/src/lib.rs
@@ -19,6 +19,10 @@ pub use crate::{
 #[global_allocator]
 static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
 
+#[cfg(all(feature = "mimalloc"))]
+#[global_allocator]
+static ALLOC: mimalloc::MiMalloc = mimalloc::MiMalloc;
+
 /// Prints backtrace to stderr, useful for debugging.
 #[cfg(feature = "backtrace")]
 pub fn print_backtrace() {
diff --git a/crates/ra_project_model/Cargo.toml b/crates/ra_project_model/Cargo.toml
index 827eb7e28e..b1b44dcf78 100644
--- a/crates/ra_project_model/Cargo.toml
+++ b/crates/ra_project_model/Cargo.toml
@@ -3,7 +3,6 @@ edition = "2018"
 name = "ra_project_model"
 version = "0.1.0"
 authors = ["rust-analyzer developers"]
-license = "MIT OR Apache-2.0"
 
 [lib]
 doctest = false
diff --git a/crates/ra_syntax/src/ast/edit.rs b/crates/ra_syntax/src/ast/edit.rs
index abc7a646c2..940c30c7fb 100644
--- a/crates/ra_syntax/src/ast/edit.rs
+++ b/crates/ra_syntax/src/ast/edit.rs
@@ -189,21 +189,6 @@ impl ast::RecordFieldList {
     }
 }
 
-impl ast::TypeAliasDef {
-    #[must_use]
-    pub fn remove_bounds(&self) -> ast::TypeAliasDef {
-        let colon = match self.colon_token() {
-            Some(it) => it,
-            None => return self.clone(),
-        };
-        let end = match self.type_bound_list() {
-            Some(it) => it.syntax().clone().into(),
-            None => colon.clone().into(),
-        };
-        self.replace_children(colon.into()..=end, iter::empty())
-    }
-}
-
 impl ast::TypeParam {
     #[must_use]
     pub fn remove_bounds(&self) -> ast::TypeParam {
diff --git a/crates/ra_text_edit/Cargo.toml b/crates/ra_text_edit/Cargo.toml
index dbb2233504..46a2ab68f4 100644
--- a/crates/ra_text_edit/Cargo.toml
+++ b/crates/ra_text_edit/Cargo.toml
@@ -4,7 +4,6 @@ name = "ra_text_edit"
 version = "0.1.0"
 authors = ["rust-analyzer developers"]
 publish = false
-license = "MIT OR Apache-2.0"
 
 [lib]
 doctest = false
diff --git a/crates/ra_toolchain/Cargo.toml b/crates/ra_toolchain/Cargo.toml
index 84b748c0a4..038c29ded6 100644
--- a/crates/ra_toolchain/Cargo.toml
+++ b/crates/ra_toolchain/Cargo.toml
@@ -3,7 +3,6 @@ edition = "2018"
 name = "ra_toolchain"
 version = "0.1.0"
 authors = ["rust-analyzer developers"]
-license = "MIT OR Apache-2.0"
 
 [lib]
 doctest = false
diff --git a/crates/ra_tt/Cargo.toml b/crates/ra_tt/Cargo.toml
index 3c45248c3e..b5b12e3afe 100644
--- a/crates/ra_tt/Cargo.toml
+++ b/crates/ra_tt/Cargo.toml
@@ -3,7 +3,6 @@ edition = "2018"
 name = "ra_tt"
 version = "0.1.0"
 authors = ["rust-analyzer developers"]
-license = "MIT OR Apache-2.0"
 
 [lib]
 doctest = false
diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml
index 370b130901..5074740ccd 100644
--- a/crates/rust-analyzer/Cargo.toml
+++ b/crates/rust-analyzer/Cargo.toml
@@ -4,7 +4,6 @@ name = "rust-analyzer"
 version = "0.1.0"
 authors = ["rust-analyzer developers"]
 autobins = false
-license = "MIT OR Apache-2.0"
 
 [lib]
 doctest = false
@@ -66,3 +65,4 @@ tt = { path = "../ra_tt", package = "ra_tt" }
 
 [features]
 jemalloc = [ "ra_prof/jemalloc" ]
+mimalloc = [ "ra_prof/mimalloc" ]
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index 5afcc2d87a..702f25a192 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -122,33 +122,29 @@ impl GlobalState {
             );
         };
 
-        let save_registration_options = lsp_types::TextDocumentSaveRegistrationOptions {
-            include_text: Some(false),
-            text_document_registration_options: lsp_types::TextDocumentRegistrationOptions {
-                document_selector: Some(vec![
-                    lsp_types::DocumentFilter {
-                        language: None,
-                        scheme: None,
-                        pattern: Some("**/*.rs".into()),
-                    },
-                    lsp_types::DocumentFilter {
-                        language: None,
-                        scheme: None,
-                        pattern: Some("**/Cargo.toml".into()),
-                    },
-                    lsp_types::DocumentFilter {
-                        language: None,
-                        scheme: None,
-                        pattern: Some("**/Cargo.lock".into()),
-                    },
-                ]),
-            },
+        let registration_options = lsp_types::TextDocumentRegistrationOptions {
+            document_selector: Some(vec![
+                lsp_types::DocumentFilter {
+                    language: None,
+                    scheme: None,
+                    pattern: Some("**/*.rs".into()),
+                },
+                lsp_types::DocumentFilter {
+                    language: None,
+                    scheme: None,
+                    pattern: Some("**/Cargo.toml".into()),
+                },
+                lsp_types::DocumentFilter {
+                    language: None,
+                    scheme: None,
+                    pattern: Some("**/Cargo.lock".into()),
+                },
+            ]),
         };
-
         let registration = lsp_types::Registration {
             id: "textDocument/didSave".to_string(),
             method: "textDocument/didSave".to_string(),
-            register_options: Some(serde_json::to_value(save_registration_options).unwrap()),
+            register_options: Some(serde_json::to_value(registration_options).unwrap()),
         };
         self.send_request::<lsp_types::request::RegisterCapability>(
             lsp_types::RegistrationParams { registrations: vec![registration] },
diff --git a/crates/stdx/Cargo.toml b/crates/stdx/Cargo.toml
index 4c0b858617..f9e380c105 100644
--- a/crates/stdx/Cargo.toml
+++ b/crates/stdx/Cargo.toml
@@ -3,7 +3,6 @@ name = "stdx"
 version = "0.1.0"
 authors = ["rust-analyzer developers"]
 edition = "2018"
-license = "MIT OR Apache-2.0"
 
 [lib]
 doctest = false
diff --git a/crates/vfs-notify/Cargo.toml b/crates/vfs-notify/Cargo.toml
index 95c56ffa6d..eb1764c369 100644
--- a/crates/vfs-notify/Cargo.toml
+++ b/crates/vfs-notify/Cargo.toml
@@ -3,7 +3,6 @@ name = "vfs-notify"
 version = "0.1.0"
 authors = ["rust-analyzer developers"]
 edition = "2018"
-license = "MIT OR Apache-2.0"
 
 [lib]
 doctest = false
diff --git a/crates/vfs/Cargo.toml b/crates/vfs/Cargo.toml
index b74cdb7ffa..db99707b3f 100644
--- a/crates/vfs/Cargo.toml
+++ b/crates/vfs/Cargo.toml
@@ -3,7 +3,6 @@ name = "vfs"
 version = "0.1.0"
 authors = ["rust-analyzer developers"]
 edition = "2018"
-license = "MIT OR Apache-2.0"
 
 [lib]
 doctest = false
diff --git a/crates/vfs/src/file_set.rs b/crates/vfs/src/file_set.rs
index e5e2ef5307..37c479306e 100644
--- a/crates/vfs/src/file_set.rs
+++ b/crates/vfs/src/file_set.rs
@@ -62,7 +62,7 @@ impl FileSetConfig {
         let mut res = vec![FileSet::default(); self.len()];
         for (file_id, path) in vfs.iter() {
             let root = self.classify(&path, &mut scratch_space);
-            res[root].insert(file_id, path.clone())
+            res[root].insert(file_id, path)
         }
         res
     }
diff --git a/crates/vfs/src/lib.rs b/crates/vfs/src/lib.rs
index cdf6f1fd02..3bfecd08fb 100644
--- a/crates/vfs/src/lib.rs
+++ b/crates/vfs/src/lib.rs
@@ -90,12 +90,12 @@ impl Vfs {
     pub fn file_contents(&self, file_id: FileId) -> &[u8] {
         self.get(file_id).as_deref().unwrap()
     }
-    pub fn iter(&self) -> impl Iterator<Item = (FileId, &VfsPath)> + '_ {
+    pub fn iter(&self) -> impl Iterator<Item = (FileId, VfsPath)> + '_ {
         (0..self.data.len())
             .map(|it| FileId(it as u32))
             .filter(move |&file_id| self.get(file_id).is_some())
             .map(move |file_id| {
-                let path = self.interner.lookup(file_id);
+                let path = self.interner.lookup(file_id).clone();
                 (file_id, path)
             })
     }
diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml
index d1cfb59090..8045a98eaf 100644
--- a/xtask/Cargo.toml
+++ b/xtask/Cargo.toml
@@ -4,7 +4,6 @@ name = "xtask"
 version = "0.1.0"
 authors = ["rust-analyzer developers"]
 publish = false
-license = "MIT OR Apache-2.0"
 
 [lib]
 doctest = false
diff --git a/xtask/src/install.rs b/xtask/src/install.rs
index 9ba77a3aae..a0dc0c9c21 100644
--- a/xtask/src/install.rs
+++ b/xtask/src/install.rs
@@ -19,7 +19,13 @@ pub enum ClientOpt {
 }
 
 pub struct ServerOpt {
-    pub jemalloc: bool,
+    pub malloc: Malloc,
+}
+
+pub enum Malloc {
+    System,
+    Jemalloc,
+    Mimalloc,
 }
 
 impl InstallCmd {
@@ -130,8 +136,12 @@ fn install_server(opts: ServerOpt) -> Result<()> {
         )
     }
 
-    let jemalloc = if opts.jemalloc { "--features jemalloc" } else { "" };
-    let res = run!("cargo install --path crates/rust-analyzer --locked --force {}", jemalloc);
+    let malloc_feature = match opts.malloc {
+        Malloc::System => "",
+        Malloc::Jemalloc => "--features jemalloc",
+        Malloc::Mimalloc => "--features mimalloc",
+    };
+    let res = run!("cargo install --path crates/rust-analyzer --locked --force {}", malloc_feature);
 
     if res.is_err() && old_rust {
         eprintln!(
diff --git a/xtask/src/lib.rs b/xtask/src/lib.rs
index 94d451e231..747654c1fc 100644
--- a/xtask/src/lib.rs
+++ b/xtask/src/lib.rs
@@ -20,7 +20,7 @@ use walkdir::{DirEntry, WalkDir};
 
 use crate::{
     codegen::Mode,
-    not_bash::{fs2, pushd, pushenv, rm_rf},
+    not_bash::{fs2, pushd, pushenv, rm_rf, run},
 };
 
 pub use anyhow::{bail, Context as _, Result};
diff --git a/xtask/src/main.rs b/xtask/src/main.rs
index f447613d43..399ff72049 100644
--- a/xtask/src/main.rs
+++ b/xtask/src/main.rs
@@ -14,7 +14,7 @@ use pico_args::Arguments;
 use xtask::{
     codegen::{self, Mode},
     dist::run_dist,
-    install::{ClientOpt, InstallCmd, ServerOpt},
+    install::{ClientOpt, InstallCmd, Malloc, ServerOpt},
     not_bash::pushd,
     pre_commit, project_root,
     release::{PromoteCmd, ReleaseCmd},
@@ -46,6 +46,7 @@ FLAGS:
         --client-code    Install only VS Code plugin
         --server         Install only the language server
         --jemalloc       Use jemalloc for server
+        --mimalloc       Use mimalloc for server
     -h, --help           Prints help information
         "
                 );
@@ -61,13 +62,21 @@ FLAGS:
                 return Ok(());
             }
 
-            let jemalloc = args.contains("--jemalloc");
+            let malloc = match (args.contains("--jemalloc"), args.contains("--mimalloc")) {
+                (false, false) => Malloc::System,
+                (true, false) => Malloc::Jemalloc,
+                (false, true) => Malloc::Mimalloc,
+                (true, true) => {
+                    eprintln!("error: Cannot use both `--jemalloc` and `--mimalloc`");
+                    return Ok(());
+                }
+            };
 
             args.finish()?;
 
             InstallCmd {
                 client: if server { None } else { Some(ClientOpt::VsCode) },
-                server: if client_code { None } else { Some(ServerOpt { jemalloc }) },
+                server: if client_code { None } else { Some(ServerOpt { malloc }) },
             }
             .run()
         }
diff --git a/xtask/src/not_bash.rs b/xtask/src/not_bash.rs
index 0f3a56b25f..8844fa2168 100644
--- a/xtask/src/not_bash.rs
+++ b/xtask/src/not_bash.rs
@@ -54,8 +54,7 @@ pub mod fs2 {
     }
 }
 
-#[macro_export]
-macro_rules! run {
+macro_rules! _run {
     ($($expr:expr),*) => {
         run!($($expr),*; echo = true)
     };
@@ -66,7 +65,7 @@ macro_rules! run {
         $crate::not_bash::run_process(format!($($expr),*), false, Some($stdin))
     };
 }
-pub use crate::run;
+pub(crate) use _run as run;
 
 pub struct Pushd {
     _p: (),


Test patch:
diff --git a/crates/ra_hir_def/src/item_tree/tests.rs b/crates/ra_hir_def/src/item_tree/tests.rs
index f26982985f..a0431dbce9 100644
--- a/crates/ra_hir_def/src/item_tree/tests.rs
+++ b/crates/ra_hir_def/src/item_tree/tests.rs
@@ -240,9 +240,9 @@ fn smoke() {
             > #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("assoc_const"))] }, input: None }]) }]
             > Const { name: Some(Name(Text("CONST"))), visibility: RawVisibilityId("pub(self)"), type_ref: Path(Path { type_anchor: None, mod_path: ModPath { kind: Plain, segments: [Name(Text("u8"))] }, generic_args: [None] }), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::ConstDef>(9) }
             > #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("assoc_method"))] }, input: None }]) }]
-            > Function { name: Name(Text("method")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: true, is_unsafe: false, params: [Reference(Path(Path { type_anchor: None, mod_path: ModPath { kind: Plain, segments: [Name(Text("Self"))] }, generic_args: [None] }), Shared)], is_varargs: false, ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(10) }
+            > Function { name: Name(Text("method")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: true, is_unsafe: false, params: [Reference(Path(Path { type_anchor: None, mod_path: ModPath { kind: Plain, segments: [Name(Text("Self"))] }, generic_args: [None] }), Shared)], ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(10) }
             > #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("assoc_dfl_method"))] }, input: None }]) }]
-            > Function { name: Name(Text("dfl_method")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: true, is_unsafe: false, params: [Reference(Path(Path { type_anchor: None, mod_path: ModPath { kind: Plain, segments: [Name(Text("Self"))] }, generic_args: [None] }), Mut)], is_varargs: false, ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(11) }
+            > Function { name: Name(Text("dfl_method")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: true, is_unsafe: false, params: [Reference(Path(Path { type_anchor: None, mod_path: ModPath { kind: Plain, segments: [Name(Text("Self"))] }, generic_args: [None] }), Mut)], ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(11) }
             #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("struct0"))] }, input: None }]) }]
             Struct { name: Name(Text("Struct0")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(1), fields: Unit, ast_id: FileAstId::<ra_syntax::ast::generated::nodes::StructDef>(3), kind: Unit }
             #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("struct1"))] }, input: None }]) }]
@@ -275,12 +275,12 @@ fn simple_inner_items() {
 
             top-level items:
             Impl { generic_params: GenericParamsId(0), target_trait: Some(Path(Path { type_anchor: None, mod_path: ModPath { kind: Plain, segments: [Name(Text("D"))] }, generic_args: [None] })), target_type: Path(Path { type_anchor: None, mod_path: ModPath { kind: Plain, segments: [Name(Text("Response"))] }, generic_args: [Some(GenericArgs { args: [Type(Path(Path { type_anchor: None, mod_path: ModPath { kind: Plain, segments: [Name(Text("T"))] }, generic_args: [None] }))], has_self_type: false, bindings: [] })] }), is_negative: false, items: [Function(Idx::<Function>(1))], ast_id: FileAstId::<ra_syntax::ast::generated::nodes::ImplDef>(0) }
-            > Function { name: Name(Text("foo")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: false, is_unsafe: false, params: [], is_varargs: false, ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(1) }
+            > Function { name: Name(Text("foo")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: false, is_unsafe: false, params: [], ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(1) }
 
             inner items:
 
             for AST FileAstId::<ra_syntax::ast::generated::nodes::ModuleItem>(2):
-            Function { name: Name(Text("end")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(1), has_self_param: false, is_unsafe: false, params: [], is_varargs: false, ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(2) }
+            Function { name: Name(Text("end")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(1), has_self_param: false, is_unsafe: false, params: [], ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(2) }
 
         "#]],
     );
@@ -303,9 +303,9 @@ fn extern_attrs() {
 
             top-level items:
             #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("attr_a"))] }, input: None }, Attr { path: ModPath { kind: Plain, segments: [Name(Text("block_attr"))] }, input: None }]) }]
-            Function { name: Name(Text("a")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: false, is_unsafe: true, params: [], is_varargs: false, ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(1) }
+            Function { name: Name(Text("a")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: false, is_unsafe: true, params: [], ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(1) }
             #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("attr_b"))] }, input: None }, Attr { path: ModPath { kind: Plain, segments: [Name(Text("block_attr"))] }, input: None }]) }]
-            Function { name: Name(Text("b")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: false, is_unsafe: true, params: [], is_varargs: false, ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(2) }
+            Function { name: Name(Text("b")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: false, is_unsafe: true, params: [], ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(2) }
         "##]],
     );
 }
@@ -329,9 +329,9 @@ fn trait_attrs() {
             #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("trait_attr"))] }, input: None }]) }]
             Trait { name: Name(Text("Tr")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(0), auto: false, items: [Function(Idx::<Function>(0)), Function(Idx::<Function>(1))], ast_id: FileAstId::<ra_syntax::ast::generated::nodes::TraitDef>(0) }
             > #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("attr_a"))] }, input: None }]) }]
-            > Function { name: Name(Text("a")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: false, is_unsafe: false, params: [], is_varargs: false, ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(1) }
+            > Function { name: Name(Text("a")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: false, is_unsafe: false, params: [], ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(1) }
             > #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("attr_b"))] }, input: None }]) }]
-            > Function { name: Name(Text("b")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: false, is_unsafe: false, params: [], is_varargs: false, ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(2) }
+            > Function { name: Name(Text("b")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: false, is_unsafe: false, params: [], ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(2) }
         "##]],
     );
 }
@@ -355,9 +355,9 @@ fn impl_attrs() {
             #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("impl_attr"))] }, input: None }]) }]
             Impl { generic_params: GenericParamsId(4294967295), target_trait: None, target_type: Path(Path { type_anchor: None, mod_path: ModPath { kind: Plain, segments: [Name(Text("Ty"))] }, generic_args: [None] }), is_negative: false, items: [Function(Idx::<Function>(0)), Function(Idx::<Function>(1))], ast_id: FileAstId::<ra_syntax::ast::generated::nodes::ImplDef>(0) }
             > #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("attr_a"))] }, input: None }]) }]
-            > Function { name: Name(Text("a")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: false, is_unsafe: false, params: [], is_varargs: false, ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(1) }
+            > Function { name: Name(Text("a")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: false, is_unsafe: false, params: [], ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(1) }
             > #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("attr_b"))] }, input: None }]) }]
-            > Function { name: Name(Text("b")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: false, is_unsafe: false, params: [], is_varargs: false, ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(2) }
+            > Function { name: Name(Text("b")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: false, is_unsafe: false, params: [], ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(2) }
         "##]],
     );
 }
@@ -408,13 +408,13 @@ fn inner_item_attrs() {
             inner attrs: Attrs { entries: None }
 
             top-level items:
-            Function { name: Name(Text("foo")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: false, is_unsafe: false, params: [], is_varargs: false, ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(0) }
+            Function { name: Name(Text("foo")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: false, is_unsafe: false, params: [], ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(0) }
 
             inner items:
 
             for AST FileAstId::<ra_syntax::ast::generated::nodes::ModuleItem>(1):
             #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("on_inner"))] }, input: None }]) }]
-            Function { name: Name(Text("inner")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: false, is_unsafe: false, params: [], is_varargs: false, ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(1) }
+            Function { name: Name(Text("inner")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: false, is_unsafe: false, params: [], ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(1) }
 
         "##]],
     );
diff --git a/crates/ra_hir_def/src/nameres/tests/macros.rs b/crates/ra_hir_def/src/nameres/tests/macros.rs
index c52341a077..84480d9f6c 100644
--- a/crates/ra_hir_def/src/nameres/tests/macros.rs
+++ b/crates/ra_hir_def/src/nameres/tests/macros.rs
@@ -660,27 +660,3 @@ fn expand_multiple_derive() {
     );
     assert_eq!(map.modules[map.root].scope.impls().len(), 2);
 }
-
-#[test]
-fn macro_expansion_overflow() {
-    mark::check!(macro_expansion_overflow);
-    compute_crate_def_map(
-        "
-macro_rules! a {
-    ($e:expr; $($t:tt)*) => {
-        b!($($t)*);
-    };
-    () => {};
-}
-
-macro_rules! b {
-    (static = $e:expr; $($t:tt)*) => {
-        a!($e; $($t)*);
-    };
-    () => {};
-}
-
-b! { static = #[] (); }
-",
-    );
-}
diff --git a/crates/ra_hir_ty/src/test_db.rs b/crates/ra_hir_ty/src/test_db.rs
index a1714ff0fc..dc447955f7 100644
--- a/crates/ra_hir_ty/src/test_db.rs
+++ b/crates/ra_hir_ty/src/test_db.rs
@@ -5,13 +5,19 @@ use std::{
     sync::{Arc, Mutex},
 };
 
-use hir_def::{db::DefDatabase, ModuleId};
-use hir_expand::db::AstDatabase;
+use hir_def::{db::DefDatabase, AssocItemId, ModuleDefId, ModuleId};
+use hir_expand::{db::AstDatabase, diagnostics::DiagnosticSink};
 use ra_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, SourceDatabase, Upcast};
 use ra_syntax::TextRange;
 use rustc_hash::{FxHashMap, FxHashSet};
+use stdx::format_to;
 use test_utils::extract_annotations;
 
+use crate::{
+    db::HirDatabase, diagnostics::Diagnostic, expr::ExprValidator,
+    unsafe_validation::UnsafeValidator,
+};
+
 #[salsa::database(
     ra_db::SourceDatabaseExtStorage,
     ra_db::SourceDatabaseStorage,
@@ -76,7 +82,7 @@ impl FileLoader for TestDB {
 }
 
 impl TestDB {
-    pub(crate) fn module_for_file(&self, file_id: FileId) -> ModuleId {
+    pub fn module_for_file(&self, file_id: FileId) -> ModuleId {
         for &krate in self.relevant_crates(file_id).iter() {
             let crate_def_map = self.crate_def_map(krate);
             for (local_id, data) in crate_def_map.modules.iter() {
@@ -88,7 +94,67 @@ impl TestDB {
         panic!("Can't find module for file")
     }
 
-    pub(crate) fn extract_annotations(&self) -> FxHashMap<FileId, Vec<(TextRange, String)>> {
+    fn diag<F: FnMut(&dyn Diagnostic)>(&self, mut cb: F) {
+        let crate_graph = self.crate_graph();
+        for krate in crate_graph.iter() {
+            let crate_def_map = self.crate_def_map(krate);
+
+            let mut fns = Vec::new();
+            for (module_id, _) in crate_def_map.modules.iter() {
+                for decl in crate_def_map[module_id].scope.declarations() {
+                    if let ModuleDefId::FunctionId(f) = decl {
+                        fns.push(f)
+                    }
+                }
+
+                for impl_id in crate_def_map[module_id].scope.impls() {
+                    let impl_data = self.impl_data(impl_id);
+                    for item in impl_data.items.iter() {
+                        if let AssocItemId::FunctionId(f) = item {
+                            fns.push(*f)
+                        }
+                    }
+                }
+            }
+
+            for f in fns {
+                let infer = self.infer(f.into());
+                let mut sink = DiagnosticSink::new(&mut cb);
+                infer.add_diagnostics(self, f, &mut sink);
+                let mut validator = ExprValidator::new(f, infer.clone(), &mut sink);
+                validator.validate_body(self);
+                let mut validator = UnsafeValidator::new(f, infer, &mut sink);
+                validator.validate_body(self);
+            }
+        }
+    }
+
+    pub fn diagnostics(&self) -> (String, u32) {
+        let mut buf = String::new();
+        let mut count = 0;
+        self.diag(|d| {
+            format_to!(buf, "{:?}: {}\n", d.syntax_node(self).text(), d.message());
+            count += 1;
+        });
+        (buf, count)
+    }
+
+    /// Like `diagnostics`, but filtered for a single diagnostic.
+    pub fn diagnostic<D: Diagnostic>(&self) -> (String, u32) {
+        let mut buf = String::new();
+        let mut count = 0;
+        self.diag(|d| {
+            // We want to filter diagnostics by the particular one we are testing for, to
+            // avoid surprising results in tests.
+            if d.downcast_ref::<D>().is_some() {
+                format_to!(buf, "{:?}: {}\n", d.syntax_node(self).text(), d.message());
+                count += 1;
+            };
+        });
+        (buf, count)
+    }
+
+    pub fn extract_annotations(&self) -> FxHashMap<FileId, Vec<(TextRange, String)>> {
         let mut files = Vec::new();
         let crate_graph = self.crate_graph();
         for krate in crate_graph.iter() {
diff --git a/crates/ra_hir_ty/src/tests.rs b/crates/ra_hir_ty/src/tests.rs
index d57b3f2885..27f5a60bf6 100644
--- a/crates/ra_hir_ty/src/tests.rs
+++ b/crates/ra_hir_ty/src/tests.rs
@@ -20,6 +20,7 @@ use hir_def::{
     AssocItemId, DefWithBodyId, LocalModuleId, Lookup, ModuleDefId,
 };
 use hir_expand::{db::AstDatabase, InFile};
+use insta::assert_snapshot;
 use ra_db::{fixture::WithFixture, FileRange, SourceDatabase, SourceDatabaseExt};
 use ra_syntax::{
     algo,
@@ -340,3 +341,410 @@ fn typing_whitespace_inside_a_function_should_not_invalidate_types() {
         assert!(!format!("{:?}", events).contains("infer"), "{:#?}", events)
     }
 }
+
+#[test]
+fn no_such_field_diagnostics() {
+    let diagnostics = TestDB::with_files(
+        r"
+        //- /lib.rs
+        struct S { foo: i32, bar: () }
+        impl S {
+            fn new() -> S {
+                S {
+                    foo: 92,
+                    baz: 62,
+                }
+            }
+        }
+        ",
+    )
+    .diagnostics()
+    .0;
+
+    assert_snapshot!(diagnostics, @r###"
+    "baz: 62": no such field
+    "{\n            foo: 92,\n            baz: 62,\n        }": Missing structure fields:
+    - bar
+    "###
+    );
+}
+
+#[test]
+fn no_such_field_with_feature_flag_diagnostics() {
+    let diagnostics = TestDB::with_files(
+        r#"
+        //- /lib.rs crate:foo cfg:feature=foo
+        struct MyStruct {
+            my_val: usize,
+            #[cfg(feature = "foo")]
+            bar: bool,
+        }
+
+        impl MyStruct {
+            #[cfg(feature = "foo")]
+            pub(crate) fn new(my_val: usize, bar: bool) -> Self {
+                Self { my_val, bar }
+            }
+
+            #[cfg(not(feature = "foo"))]
+            pub(crate) fn new(my_val: usize, _bar: bool) -> Self {
+                Self { my_val }
+            }
+        }
+        "#,
+    )
+    .diagnostics()
+    .0;
+
+    assert_snapshot!(diagnostics, @r###""###);
+}
+
+#[test]
+fn no_such_field_enum_with_feature_flag_diagnostics() {
+    let diagnostics = TestDB::with_files(
+        r#"
+        //- /lib.rs crate:foo cfg:feature=foo
+        enum Foo {
+            #[cfg(not(feature = "foo"))]
+            Buz,
+            #[cfg(feature = "foo")]
+            Bar,
+            Baz
+        }
+
+        fn test_fn(f: Foo) {
+            match f {
+                Foo::Bar => {},
+                Foo::Baz => {},
+            }
+        }
+        "#,
+    )
+    .diagnostics()
+    .0;
+
+    assert_snapshot!(diagnostics, @r###""###);
+}
+
+#[test]
+fn no_such_field_with_feature_flag_diagnostics_on_struct_lit() {
+    let diagnostics = TestDB::with_files(
+        r#"
+        //- /lib.rs crate:foo cfg:feature=foo
+        struct S {
+            #[cfg(feature = "foo")]
+            foo: u32,
+            #[cfg(not(feature = "foo"))]
+            bar: u32,
+        }
+
+        impl S {
+            #[cfg(feature = "foo")]
+            fn new(foo: u32) -> Self {
+                Self { foo }
+            }
+            #[cfg(not(feature = "foo"))]
+            fn new(bar: u32) -> Self {
+                Self { bar }
+            }
+        }
+        "#,
+    )
+    .diagnostics()
+    .0;
+
+    assert_snapshot!(diagnostics, @r###""###);
+}
+
+#[test]
+fn no_such_field_with_feature_flag_diagnostics_on_block_expr() {
+    let diagnostics = TestDB::with_files(
+        r#"
+        //- /lib.rs crate:foo cfg:feature=foo
+        struct S {
+            #[cfg(feature = "foo")]
+            foo: u32,
+            #[cfg(not(feature = "foo"))]
+            bar: u32,
+        }
+
+        impl S {
+            fn new(bar: u32) -> Self {
+                #[cfg(feature = "foo")]
+                {
+                Self { foo: bar }
+                }
+                #[cfg(not(feature = "foo"))]
+                {
+                Self { bar }
+                }
+            }
+        }
+        "#,
+    )
+    .diagnostics()
+    .0;
+
+    assert_snapshot!(diagnostics, @r###""###);
+}
+
+#[test]
+fn no_such_field_with_feature_flag_diagnostics_on_struct_fields() {
+    let diagnostics = TestDB::with_files(
+        r#"
+        //- /lib.rs crate:foo cfg:feature=foo
+        struct S {
+            #[cfg(feature = "foo")]
+            foo: u32,
+            #[cfg(not(feature = "foo"))]
+            bar: u32,
+        }
+
+        impl S {
+            fn new(val: u32) -> Self {
+                Self {
+                    #[cfg(feature = "foo")]
+                    foo: val,
+                    #[cfg(not(feature = "foo"))]
+                    bar: val,
+                }
+            }
+        }
+        "#,
+    )
+    .diagnostics()
+    .0;
+
+    assert_snapshot!(diagnostics, @r###""###);
+}
+
+#[test]
+fn no_such_field_with_type_macro() {
+    let diagnostics = TestDB::with_files(
+        r"
+        macro_rules! Type {
+            () => { u32 };
+        }
+
+        struct Foo {
+            bar: Type![],
+        }
+        impl Foo {
+            fn new() -> Self {
+                Foo { bar: 0 }
+            }
+        }
+        ",
+    )
+    .diagnostics()
+    .0;
+
+    assert_snapshot!(diagnostics, @r###""###);
+}
+
+#[test]
+fn missing_record_pat_field_diagnostic() {
+    let diagnostics = TestDB::with_files(
+        r"
+        //- /lib.rs
+        struct S { foo: i32, bar: () }
+        fn baz(s: S) {
+            let S { foo: _ } = s;
+        }
+        ",
+    )
+    .diagnostics()
+    .0;
+
+    assert_snapshot!(diagnostics, @r###"
+    "{ foo: _ }": Missing structure fields:
+    - bar
+    "###
+    );
+}
+
+#[test]
+fn missing_record_pat_field_no_diagnostic_if_not_exhaustive() {
+    let diagnostics = TestDB::with_files(
+        r"
+        //- /lib.rs
+        struct S { foo: i32, bar: () }
+        fn baz(s: S) -> i32 {
+            match s {
+                S { foo, .. } => foo,
+            }
+        }
+        ",
+    )
+    .diagnostics()
+    .0;
+
+    assert_snapshot!(diagnostics, @"");
+}
+
+#[test]
+fn missing_unsafe_diagnostic_with_raw_ptr() {
+    let diagnostics = TestDB::with_files(
+        r"
+//- /lib.rs
+fn missing_unsafe() {
+    let x = &5 as *const usize;
+    let y = *x;
+}
+",
+    )
+    .diagnostics()
+    .0;
+
+    assert_snapshot!(diagnostics, @r#""*x": This operation is unsafe and requires an unsafe function or block"#);
+}
+
+#[test]
+fn missing_unsafe_diagnostic_with_unsafe_call() {
+    let diagnostics = TestDB::with_files(
+        r"
+//- /lib.rs
+unsafe fn unsafe_fn() {
+    let x = &5 as *const usize;
+    let y = *x;
+}
+
+fn missing_unsafe() {
+    unsafe_fn();
+}
+",
+    )
+    .diagnostics()
+    .0;
+
+    assert_snapshot!(diagnostics, @r#""unsafe_fn()": This operation is unsafe and requires an unsafe function or block"#);
+}
+
+#[test]
+fn missing_unsafe_diagnostic_with_unsafe_method_call() {
+    let diagnostics = TestDB::with_files(
+        r"
+struct HasUnsafe;
+
+impl HasUnsafe {
+    unsafe fn unsafe_fn(&self) {
+        let x = &5 as *const usize;
+        let y = *x;
+    }
+}
+
+fn missing_unsafe() {
+    HasUnsafe.unsafe_fn();
+}
+
+",
+    )
+    .diagnostics()
+    .0;
+
+    assert_snapshot!(diagnostics, @r#""HasUnsafe.unsafe_fn()": This operation is unsafe and requires an unsafe function or block"#);
+}
+
+#[test]
+fn no_missing_unsafe_diagnostic_with_raw_ptr_in_unsafe_block() {
+    let diagnostics = TestDB::with_files(
+        r"
+fn nothing_to_see_move_along() {
+    let x = &5 as *const usize;
+    unsafe {
+        let y = *x;
+    }
+}
+",
+    )
+    .diagnostics()
+    .0;
+
+    assert_snapshot!(diagnostics, @"");
+}
+
+#[test]
+fn missing_unsafe_diagnostic_with_raw_ptr_outside_unsafe_block() {
+    let diagnostics = TestDB::with_files(
+        r"
+fn nothing_to_see_move_along() {
+    let x = &5 as *const usize;
+    unsafe {
+        let y = *x;
+    }
+    let z = *x;
+}
+",
+    )
+    .diagnostics()
+    .0;
+
+    assert_snapshot!(diagnostics, @r#""*x": This operation is unsafe and requires an unsafe function or block"#);
+}
+
+#[test]
+fn no_missing_unsafe_diagnostic_with_unsafe_call_in_unsafe_block() {
+    let diagnostics = TestDB::with_files(
+        r"
+unsafe fn unsafe_fn() {
+    let x = &5 as *const usize;
+    let y = *x;
+}
+
+fn nothing_to_see_move_along() {
+    unsafe {
+        unsafe_fn();
+    }
+}
+",
+    )
+    .diagnostics()
+    .0;
+
+    assert_snapshot!(diagnostics, @"");
+}
+
+#[test]
+fn no_missing_unsafe_diagnostic_with_unsafe_method_call_in_unsafe_block() {
+    let diagnostics = TestDB::with_files(
+        r"
+struct HasUnsafe;
+
+impl HasUnsafe {
+    unsafe fn unsafe_fn() {
+        let x = &5 as *const usize;
+        let y = *x;
+    }
+}
+
+fn nothing_to_see_move_along() {
+    unsafe {
+        HasUnsafe.unsafe_fn();
+    }
+}
+
+",
+    )
+    .diagnostics()
+    .0;
+
+    assert_snapshot!(diagnostics, @"");
+}
+
+#[test]
+fn break_outside_of_loop() {
+    let diagnostics = TestDB::with_files(
+        r"
+        //- /lib.rs
+        fn foo() {
+            break;
+        }
+        ",
+    )
+    .diagnostics()
+    .0;
+
+    assert_snapshot!(diagnostics, @r###""break": break outside of loop
+    "###
+    );
+}
diff --git a/crates/test_utils/Cargo.toml b/crates/test_utils/Cargo.toml
index e719f4f7c1..6821db1e81 100644
--- a/crates/test_utils/Cargo.toml
+++ b/crates/test_utils/Cargo.toml
@@ -3,7 +3,6 @@ edition = "2018"
 name = "test_utils"
 version = "0.1.0"
 authors = ["rust-analyzer developers"]
-license = "MIT OR Apache-2.0"
 
 [lib]
 doctest = false
diff --git a/crates/test_utils/src/lib.rs b/crates/test_utils/src/lib.rs
index ad586c882b..e4aa894ace 100644
--- a/crates/test_utils/src/lib.rs
+++ b/crates/test_utils/src/lib.rs
@@ -179,80 +179,31 @@ pub fn extract_annotations(text: &str) -> Vec<(TextRange, String)> {
     let mut res = Vec::new();
     let mut prev_line_start: Option<TextSize> = None;
     let mut line_start: TextSize = 0.into();
-    let mut prev_line_annotations: Vec<(TextSize, usize)> = Vec::new();
     for line in lines_with_ends(text) {
-        let mut this_line_annotations = Vec::new();
-        if let Some(idx) = line.find("//") {
-            let annotation_offset = TextSize::of(&line[..idx + "//".len()]);
-            for annotation in extract_line_annotations(&line[idx + "//".len()..]) {
-                match annotation {
-                    LineAnnotation::Annotation { mut range, content } => {
-                        range += annotation_offset;
-                        this_line_annotations.push((range.end(), res.len()));
-                        res.push((range + prev_line_start.unwrap(), content))
-                    }
-                    LineAnnotation::Continuation { mut offset, content } => {
-                        offset += annotation_offset;
-                        let &(_, idx) = prev_line_annotations
-                            .iter()
-                            .find(|&&(off, _idx)| off == offset)
-                            .unwrap();
-                        res[idx].1.push('\n');
-                        res[idx].1.push_str(&content);
-                        res[idx].1.push('\n');
-                    }
-                }
+        if let Some(idx) = line.find("//^") {
+            let offset = prev_line_start.unwrap() + TextSize::of(&line[..idx + "//".len()]);
+            for (line_range, text) in extract_line_annotations(&line[idx + "//".len()..]) {
+                res.push((line_range + offset, text))
             }
         }
-
         prev_line_start = Some(line_start);
         line_start += TextSize::of(line);
-
-        prev_line_annotations = this_line_annotations;
     }
     res
 }
 
-enum LineAnnotation {
-    Annotation { range: TextRange, content: String },
-    Continuation { offset: TextSize, content: String },
-}
-
-fn extract_line_annotations(mut line: &str) -> Vec<LineAnnotation> {
+fn extract_line_annotations(mut line: &str) -> Vec<(TextRange, String)> {
     let mut res = Vec::new();
     let mut offset: TextSize = 0.into();
-    let marker: fn(char) -> bool = if line.contains('^') { |c| c == '^' } else { |c| c == '|' };
-    loop {
-        match line.find(marker) {
-            Some(idx) => {
-                offset += TextSize::try_from(idx).unwrap();
-                line = &line[idx..];
-            }
-            None => break,
-        };
-
-        let mut len = line.chars().take_while(|&it| it == '^').count();
-        let mut continuation = false;
-        if len == 0 {
-            assert!(line.starts_with('|'));
-            continuation = true;
-            len = 1;
-        }
+    while !line.is_empty() {
+        let len = line.chars().take_while(|&it| it == '^').count();
+        assert!(len > 0);
         let range = TextRange::at(offset, len.try_into().unwrap());
-        let next = line[len..].find(marker).map_or(line.len(), |it| it + len);
-        let content = line[len..][..next - len].trim().to_string();
-
-        let annotation = if continuation {
-            LineAnnotation::Continuation { offset: range.end(), content }
-        } else {
-            LineAnnotation::Annotation { range, content }
-        };
-        res.push(annotation);
-
+        let next = line[len..].find('^').map_or(line.len(), |it| it + len);
+        res.push((range, line[len..][..next - len].trim().to_string()));
         line = &line[next..];
         offset += TextSize::try_from(next).unwrap();
     }
-
     res
 }
 
@@ -264,18 +215,14 @@ fn main() {
     let (x,     y) = (9, 2);
        //^ def  ^ def
     zoo + 1
-} //^^^ type:
-  //  | i32
+} //^^^ i32
     "#,
     );
     let res = extract_annotations(&text)
         .into_iter()
         .map(|(range, ann)| (&text[range], ann))
         .collect::<Vec<_>>();
-    assert_eq!(
-        res,
-        vec![("x", "def".into()), ("y", "def".into()), ("zoo", "type:\ni32\n".into()),]
-    );
+    assert_eq!(res, vec![("x", "def".into()), ("y", "def".into()), ("zoo", "i32".into()),]);
 }
 
 // Comparison functionality borrowed from cargo:
diff --git a/crates/test_utils/src/mark.rs b/crates/test_utils/src/mark.rs
index 97f5a93ad9..7c309a8945 100644
--- a/crates/test_utils/src/mark.rs
+++ b/crates/test_utils/src/mark.rs
@@ -62,7 +62,7 @@ pub struct MarkChecker {
 
 impl MarkChecker {
     pub fn new(mark: &'static AtomicUsize) -> MarkChecker {
-        let value_on_entry = mark.load(Ordering::Relaxed);
+        let value_on_entry = mark.load(Ordering::SeqCst);
         MarkChecker { mark, value_on_entry }
     }
 }
@@ -72,7 +72,7 @@ impl Drop for MarkChecker {
         if std::thread::panicking() {
             return;
         }
-        let value_on_exit = self.mark.load(Ordering::Relaxed);
+        let value_on_exit = self.mark.load(Ordering::SeqCst);
         assert!(value_on_exit > self.value_on_entry, "mark was not hit")
     }
 }
diff --git a/xtask/tests/tidy.rs b/xtask/tests/tidy.rs
index 72088e4148..fcfad609d7 100644
--- a/xtask/tests/tidy.rs
+++ b/xtask/tests/tidy.rs
@@ -5,7 +5,7 @@ use std::{
 
 use xtask::{
     codegen::{self, Mode},
-    not_bash::{fs2, run},
+    not_bash::fs2,
     project_root, run_rustfmt, rust_files,
 };
 
@@ -49,45 +49,6 @@ fn rust_files_are_tidy() {
     tidy_docs.finish();
 }
 
-#[test]
-fn check_licenses() {
-    let expected = "
-0BSD OR MIT OR Apache-2.0
-Apache-2.0
-Apache-2.0 / MIT
-Apache-2.0 OR BSL-1.0
-Apache-2.0 OR MIT
-Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT
-Apache-2.0/MIT
-BSD-2-Clause
-BSD-3-Clause
-CC0-1.0
-ISC
-MIT
-MIT / Apache-2.0
-MIT OR Apache-2.0
-MIT/Apache-2.0
-MIT/Apache-2.0 AND BSD-2-Clause
-Unlicense OR MIT
-Unlicense/MIT
-Zlib
-"
-    .lines()
-    .filter(|it| !it.is_empty())
-    .collect::<Vec<_>>();
-
-    let meta = run!("cargo metadata --format-version 1"; echo = false).unwrap();
-    let mut licenses = meta
-        .split(|c| c == ',' || c == '{' || c == '}')
-        .filter(|it| it.contains(r#""license""#))
-        .map(|it| it.trim())
-        .map(|it| it[r#""license":"#.len()..].trim_matches('"'))
-        .collect::<Vec<_>>();
-    licenses.sort();
-    licenses.dedup();
-    assert_eq!(licenses, expected);
-}
-
 fn check_todo(path: &Path, text: &str) {
     let need_todo = &[
         // This file itself obviously needs to use todo (<- like this!).