Repository: rust-analyzer/rust-analyzer
Pull request #11522 (2022-02-21 17:08:02)

fix: Make code lenses work on attributed items
Fixes https://github.com/rust-analyzer/rust-analyzer/issues/11213
bors r+


Patch:
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
index 7ba06356a3..f84f284073 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -14,7 +14,8 @@ Forum for questions: https://users.rust-lang.org/c/ide/14
 Before submitting, please make sure that you're not running into one of these known issues:
 
  1. extension doesn't load in VSCodium: #11080
- 2. on-the-fly diagnostics are mostly unimplemented (`cargo check` diagnostics will be shown when saving a file): #3107
+ 2. VS Code Marketplace serves old stable version: #11098
+ 3. on-the-fly diagnostics are mostly unimplemented (`cargo check` diagnostics will be shown when saving a file): #3107
 
 Otherwise please try to provide information which will help us to fix the issue faster. Minimal reproducible examples with few dependencies are especially lovely <3.
 -->
@@ -22,5 +23,3 @@ Otherwise please try to provide information which will help us to fix the issue
 **rust-analyzer version**: (eg. output of "Rust Analyzer: Show RA Version" command)
 
 **rustc version**: (eg. output of `rustc -V`)
-
-**relevant settings**: (eg. client settings, or environment variables like `CARGO`, `RUSTUP_HOME` or `CARGO_HOME`)
diff --git a/Cargo.lock b/Cargo.lock
index 64654c9961..0ab9f89fd2 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -37,9 +37,9 @@ dependencies = [
 
 [[package]]
 name = "anyhow"
-version = "1.0.53"
+version = "1.0.51"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "94a45b455c14666b85fc40a019e8ab9eb75e3a124e05494f5397122bc9eb06e0"
+checksum = "8b26702f315f53b6071259e15dd9d64528213b44d61de1ec926eca7715d62203"
 
 [[package]]
 name = "anymap"
@@ -49,9 +49,9 @@ checksum = "33954243bd79057c2de7338850b85983a44588021f8a5fee574a8888c6de4344"
 
 [[package]]
 name = "arbitrary"
-version = "1.1.0"
+version = "1.0.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c38b6b6b79f671c25e1a3e785b7b82d7562ffc9cd3efdc98627e5668a2472490"
+checksum = "510c76ecefdceada737ea728f4f9a84bd2e1ef29f1ba555e560940fe279954de"
 
 [[package]]
 name = "arrayvec"
@@ -72,15 +72,15 @@ dependencies = [
 
 [[package]]
 name = "autocfg"
-version = "1.1.0"
+version = "1.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
+checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
 
 [[package]]
 name = "backtrace"
-version = "0.3.64"
+version = "0.3.63"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5e121dee8023ce33ab248d9ce1493df03c3b38a659b240096fcbd7048ff9c31f"
+checksum = "321629d8ba6513061f26707241fa9bc89524ff1cd7a915a97ef0c62c666ce1b6"
 dependencies = [
  "addr2line",
  "cc",
@@ -114,9 +114,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
 
 [[package]]
 name = "camino"
-version = "1.0.7"
+version = "1.0.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6f3132262930b0522068049f5870a856ab8affc80c70d08b6ecb785771a6fc23"
+checksum = "52d74260d9bf6944e2208aa46841b4b8f0d7ffc0849a06837b2f510337f86b2b"
 dependencies = [
  "serde",
 ]
@@ -227,7 +227,7 @@ version = "3.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "03746e0c6dd9b5d2d9132ffe0bede35fb5f815604fd371bb42599fd37bc8e483"
 dependencies = [
- "dashmap 4.0.2",
+ "dashmap",
  "once_cell",
  "rustc-hash",
 ]
@@ -240,18 +240,18 @@ checksum = "0d48d8f76bd9331f19fe2aaf3821a9f9fb32c3963e1e3d6ce82a8c09cef7444a"
 
 [[package]]
 name = "crc32fast"
-version = "1.3.2"
+version = "1.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d"
+checksum = "738c290dfaea84fc1ca15ad9c168d083b05a714e1efddd8edaab678dc28d2836"
 dependencies = [
  "cfg-if",
 ]
 
 [[package]]
 name = "crossbeam-channel"
-version = "0.5.2"
+version = "0.5.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e54ea8bc3fb1ee042f5aace6e3c6e025d3874866da222930f70ce62aceba0bfa"
+checksum = "06ed27e177f16d65f0f0c22a213e17c696ace5dd64b14258b52f9417ccb52db4"
 dependencies = [
  "cfg-if",
  "crossbeam-utils",
@@ -270,9 +270,9 @@ dependencies = [
 
 [[package]]
 name = "crossbeam-epoch"
-version = "0.9.7"
+version = "0.9.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c00d6d2ea26e8b151d99093005cb442fb9a37aeaca582a03ec70946f49ab5ed9"
+checksum = "4ec02e091aa634e2c3ada4a392989e7c3116673ef0ac5b72232439094d73b7fd"
 dependencies = [
  "cfg-if",
  "crossbeam-utils",
@@ -283,9 +283,9 @@ dependencies = [
 
 [[package]]
 name = "crossbeam-utils"
-version = "0.8.7"
+version = "0.8.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b5e5bed1f1c269533fa816a0a5492b3545209a205ca1a54842be180eb63a16a6"
+checksum = "d82cfc11ce7f2c3faef78d8a684447b40d503d9681acebed6cb728d45940c4db"
 dependencies = [
  "cfg-if",
  "lazy_static",
@@ -301,22 +301,11 @@ dependencies = [
  "num_cpus",
 ]
 
-[[package]]
-name = "dashmap"
-version = "5.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c0834a35a3fce649144119e18da2a4d8ed12ef3862f47183fd46f625d072d96c"
-dependencies = [
- "cfg-if",
- "num_cpus",
- "parking_lot 0.12.0",
-]
-
 [[package]]
 name = "derive_arbitrary"
-version = "1.1.0"
+version = "1.0.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "98e23c06c035dac87bd802d98f368df73a7f2cb05a66ffbd1f377e821fac4af9"
+checksum = "b24629208e87a2d8b396ff43b15c4afb0a69cea3fbbaa9ed9b92b7c02f0aed73"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -429,9 +418,9 @@ checksum = "2022715d62ab30faffd124d40b76f4134a550a87792276512b18d63272333394"
 
 [[package]]
 name = "fsevent-sys"
-version = "4.1.0"
+version = "4.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "76ee7a02da4d231650c7cea31349b889be2f45ddb3ef3032d2ec8185f6313fd2"
+checksum = "5c0e564d24da983c053beff1bb7178e237501206840a3e6bf4e267b9e8ae734a"
 dependencies = [
  "libc",
 ]
@@ -507,7 +496,7 @@ dependencies = [
  "base_db",
  "cfg",
  "cov-mark",
- "dashmap 5.1.0",
+ "dashmap",
  "drop_bomb",
  "either",
  "expect-test",
@@ -517,10 +506,8 @@ dependencies = [
  "itertools",
  "la-arena",
  "limit",
- "lock_api",
  "mbe",
  "once_cell",
- "parking_lot 0.12.0",
  "profile",
  "rustc-hash",
  "smallvec",
@@ -539,7 +526,6 @@ dependencies = [
  "cfg",
  "cov-mark",
  "either",
- "expect-test",
  "hashbrown 0.12.0",
  "itertools",
  "la-arena",
@@ -740,9 +726,9 @@ dependencies = [
 
 [[package]]
 name = "indexmap"
-version = "1.8.0"
+version = "1.7.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "282a6247722caba404c065016bbfa522806e51714c34f5dfc3e4a3a46fcb4223"
+checksum = "bc633605454125dec4b66843673f01c7df2b89479b32e0ed634e43a91cff62a5"
 dependencies = [
  "autocfg",
  "hashbrown 0.11.2",
@@ -830,15 +816,15 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
 
 [[package]]
 name = "libc"
-version = "0.2.117"
+version = "0.2.112"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e74d72e0f9b65b5b4ca49a346af3976df0f9c61d550727f349ecd559f251a26c"
+checksum = "1b03d17f364a3a042d5e5d46b053bbbf82c92c9430c592dd4c064dc6ee997125"
 
 [[package]]
 name = "libloading"
-version = "0.7.3"
+version = "0.7.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "efbc0f03f9a775e9f6aed295c6a1ba2253c5757a9e03d55c6caa46a681abcddd"
+checksum = "afe203d669ec979b7128619bae5a63b7b42e9203c1b29146079ee05e2f604b52"
 dependencies = [
  "cfg-if",
  "winapi",
@@ -859,9 +845,9 @@ version = "0.0.0"
 
 [[package]]
 name = "lock_api"
-version = "0.4.6"
+version = "0.4.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "88943dd7ef4a2e5a4bfa2753aaab3013e34ce2533d1996fb18ef591e315e2b3b"
+checksum = "712a4d093c9976e24e7dbca41db895dabcbac38eb5f4045393d17a95bdfb1109"
 dependencies = [
  "scopeguard",
 ]
@@ -889,9 +875,9 @@ dependencies = [
 
 [[package]]
 name = "lsp-types"
-version = "0.92.0"
+version = "0.91.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e8a69d4142d51b208c9fc3cea68b1a7fcef30354e7aa6ccad07250fd8430fc76"
+checksum = "2368312c59425dd133cb9a327afee65be0a633a8ce471d248e2202a48f8f68ae"
 dependencies = [
  "bitflags",
  "serde",
@@ -938,9 +924,9 @@ checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
 
 [[package]]
 name = "memmap2"
-version = "0.5.3"
+version = "0.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "057a3db23999c867821a7a59feb06a578fcb03685e983dff90daf9e7d24ac08f"
+checksum = "4647a11b578fead29cdbb34d4adef8dd3dc35b876c9c6d5240d83f205abfe96e"
 dependencies = [
  "libc",
 ]
@@ -1001,7 +987,7 @@ version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a7377f7792b3afb6a3cba68daa54ca23c032137010460d667fda53a8d66be00e"
 dependencies = [
- "windows-sys 0.28.0",
+ "windows-sys",
 ]
 
 [[package]]
@@ -1024,9 +1010,9 @@ dependencies = [
 
 [[package]]
 name = "ntapi"
-version = "0.3.7"
+version = "0.3.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c28774a7fd2fbb4f0babd8237ce554b73af68021b5f695a3cebd6c59bac0980f"
+checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44"
 dependencies = [
  "winapi",
 ]
@@ -1052,9 +1038,9 @@ dependencies = [
 
 [[package]]
 name = "object"
-version = "0.28.3"
+version = "0.28.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "40bec70ba014595f99f7aa110b84331ffe1ee9aece7fe6f387cc7e3ecda4d456"
+checksum = "7ce8b38d41f9f3618fc23f908faae61510f8d8ce2d99cbe910641e8f1971f084"
 dependencies = [
  "memchr",
 ]
@@ -1079,17 +1065,7 @@ checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99"
 dependencies = [
  "instant",
  "lock_api",
- "parking_lot_core 0.8.5",
-]
-
-[[package]]
-name = "parking_lot"
-version = "0.12.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "87f5ec2493a61ac0506c0f4199f99070cbe83857b0337006a30f3e6719b8ef58"
-dependencies = [
- "lock_api",
- "parking_lot_core 0.9.1",
+ "parking_lot_core",
 ]
 
 [[package]]
@@ -1106,19 +1082,6 @@ dependencies = [
  "winapi",
 ]
 
-[[package]]
-name = "parking_lot_core"
-version = "0.9.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "28141e0cc4143da2443301914478dc976a61ffdb3f043058310c70df2fed8954"
-dependencies = [
- "cfg-if",
- "libc",
- "redox_syscall",
- "smallvec",
- "windows-sys 0.32.0",
-]
-
 [[package]]
 name = "parser"
 version = "0.0.0"
@@ -1177,15 +1140,15 @@ dependencies = [
 
 [[package]]
 name = "pin-project-lite"
-version = "0.2.8"
+version = "0.2.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e280fbe77cc62c91527259e9442153f4688736748d24660126286329742b4c6c"
+checksum = "8d31d11c69a6b52a174b42bdc0c30e5e11670f90788b2c471c31c1d17d449443"
 
 [[package]]
 name = "proc-macro2"
-version = "1.0.36"
+version = "1.0.34"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029"
+checksum = "2f84e92c0f7c9d58328b85a78557813e4bd845130db68d7184635344399423b1"
 dependencies = [
  "unicode-xid",
 ]
@@ -1195,7 +1158,7 @@ name = "proc_macro_api"
 version = "0.0.0"
 dependencies = [
  "memmap2",
- "object 0.28.3",
+ "object 0.28.1",
  "paths",
  "profile",
  "serde",
@@ -1214,7 +1177,7 @@ dependencies = [
  "libloading",
  "mbe",
  "memmap2",
- "object 0.28.3",
+ "object 0.28.1",
  "paths",
  "proc_macro_api",
  "proc_macro_test",
@@ -1291,9 +1254,9 @@ dependencies = [
 
 [[package]]
 name = "quote"
-version = "1.0.15"
+version = "1.0.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "864d3e96a899863136fc6e99f3d7cae289dafe43bf2c5ac19b70df7210c0a145"
+checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05"
 dependencies = [
  "proc-macro2",
 ]
@@ -1358,12 +1321,12 @@ checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
 
 [[package]]
 name = "rowan"
-version = "0.15.3"
+version = "0.15.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c72a0c863fef62cfd9ca7b72a35b422d7672dbffac4ac8003dcc3be7837d871d"
+checksum = "71ea8fec43656b71f8bf4a0351ab18604488ee13b0bf72cad2965f5bb2ca9dc6"
 dependencies = [
  "countme",
- "hashbrown 0.12.0",
+ "hashbrown 0.11.2",
  "memoffset",
  "rustc-hash",
  "text-size",
@@ -1394,7 +1357,7 @@ dependencies = [
  "mimalloc",
  "num_cpus",
  "oorandom",
- "parking_lot 0.12.0",
+ "parking_lot",
  "proc_macro_api",
  "proc_macro_srv",
  "profile",
@@ -1460,7 +1423,7 @@ dependencies = [
  "lock_api",
  "log",
  "oorandom",
- "parking_lot 0.11.2",
+ "parking_lot",
  "rustc-hash",
  "salsa-macros",
  "smallvec",
@@ -1501,27 +1464,27 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
 
 [[package]]
 name = "semver"
-version = "1.0.5"
+version = "1.0.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0486718e92ec9a68fbed73bb5ef687d71103b142595b406835649bebd33f72c7"
+checksum = "568a8e6258aa33c13358f81fd834adb854c6f7c9468520910a9b1e8fac068012"
 dependencies = [
  "serde",
 ]
 
 [[package]]
 name = "serde"
-version = "1.0.136"
+version = "1.0.132"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789"
+checksum = "8b9875c23cf305cd1fd7eb77234cbb705f21ea6a72c637a5c6db5fe4b8e7f008"
 dependencies = [
  "serde_derive",
 ]
 
 [[package]]
 name = "serde_derive"
-version = "1.0.136"
+version = "1.0.132"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9"
+checksum = "ecc0db5cb2556c0e558887d9bbdcf6ac4471e83ff66cf696e5419024d1606276"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -1530,9 +1493,9 @@ dependencies = [
 
 [[package]]
 name = "serde_json"
-version = "1.0.79"
+version = "1.0.73"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8e8d9fa5c3b304765ce1fd9c4c8a3de2c8db365a5b91be52f186efc675681d95"
+checksum = "bcbd0344bc6533bc7ec56df11d42fb70f1b912351c0825ccb7211b59d8af7cf5"
 dependencies = [
  "indexmap",
  "itoa",
@@ -1562,9 +1525,9 @@ dependencies = [
 
 [[package]]
 name = "smallvec"
-version = "1.8.0"
+version = "1.7.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83"
+checksum = "1ecab6c735a6bb4139c0caafd0cc3635748bbb3acf4550e8138122099251f309"
 
 [[package]]
 name = "smol_str"
@@ -1601,9 +1564,9 @@ dependencies = [
 
 [[package]]
 name = "syn"
-version = "1.0.86"
+version = "1.0.82"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8a65b3f4ffa0092e9887669db0eae07941f023991ab58ea44da8fe8e2d511c6b"
+checksum = "8daf5dd0bb60cbd4137b1b587d2fc0ae729bc07cf01cd70b36a1ed5ade3b9d59"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -1745,9 +1708,9 @@ dependencies = [
 
 [[package]]
 name = "tracing"
-version = "0.1.30"
+version = "0.1.29"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2d8d93354fe2a8e50d5953f5ae2e47a3fc2ef03292e7ea46e3cc38f549525fb9"
+checksum = "375a639232caf30edfc78e8d89b2d4c375515393e7af7e16f01cd96917fb2105"
 dependencies = [
  "cfg-if",
  "pin-project-lite",
@@ -1757,9 +1720,9 @@ dependencies = [
 
 [[package]]
 name = "tracing-attributes"
-version = "0.1.19"
+version = "0.1.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8276d9a4a3a558d7b7ad5303ad50b53d58264641b82914b7ada36bd762e7a716"
+checksum = "f4f480b8f81512e825f337ad51e94c1eb5d3bbdf2b363dcd01e2b19a9ffe3f8e"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -1768,12 +1731,11 @@ dependencies = [
 
 [[package]]
 name = "tracing-core"
-version = "0.1.22"
+version = "0.1.21"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "03cfcb51380632a72d3111cb8d3447a8d908e577d31beeac006f836383d29a23"
+checksum = "1f4ed65637b8390770814083d20756f87bfa2c21bf2f110babdc5438351746e4"
 dependencies = [
  "lazy_static",
- "valuable",
 ]
 
 [[package]]
@@ -1789,9 +1751,9 @@ dependencies = [
 
 [[package]]
 name = "tracing-subscriber"
-version = "0.3.8"
+version = "0.3.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "74786ce43333fcf51efe947aed9718fbe46d5c7328ec3f1029e818083966d9aa"
+checksum = "245da694cc7fc4729f3f418b304cb57789f1bed2a78c575407ab8a23f53cb4d3"
 dependencies = [
  "lazy_static",
  "matchers",
@@ -1832,9 +1794,9 @@ checksum = "0685c84d5d54d1c26f7d3eb96cd41550adb97baed141a761cf335d3d33bcd0ae"
 
 [[package]]
 name = "ungrammar"
-version = "1.15.0"
+version = "1.14.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ed01567101450f7d600508e7680df6005ae4fe97119d79b0364cc5910ff39732"
+checksum = "66be59c2fd880e3d76d1a6cf6d34114008f1d8af2748d4ad9d39ea712f14fda9"
 
 [[package]]
 name = "unicase"
@@ -1862,9 +1824,9 @@ dependencies = [
 
 [[package]]
 name = "unicode-segmentation"
-version = "1.9.0"
+version = "1.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99"
+checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b"
 
 [[package]]
 name = "unicode-xid"
@@ -1885,17 +1847,11 @@ dependencies = [
  "serde",
 ]
 
-[[package]]
-name = "valuable"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
-
 [[package]]
 name = "version_check"
-version = "0.9.4"
+version = "0.9.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
+checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe"
 
 [[package]]
 name = "vfs"
@@ -1968,24 +1924,11 @@ version = "0.28.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "82ca39602d5cbfa692c4b67e3bcbb2751477355141c1ed434c94da4186836ff6"
 dependencies = [
- "windows_aarch64_msvc 0.28.0",
- "windows_i686_gnu 0.28.0",
- "windows_i686_msvc 0.28.0",
- "windows_x86_64_gnu 0.28.0",
- "windows_x86_64_msvc 0.28.0",
-]
-
-[[package]]
-name = "windows-sys"
-version = "0.32.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3df6e476185f92a12c072be4a189a0210dcdcf512a1891d6dff9edb874deadc6"
-dependencies = [
- "windows_aarch64_msvc 0.32.0",
- "windows_i686_gnu 0.32.0",
- "windows_i686_msvc 0.32.0",
- "windows_x86_64_gnu 0.32.0",
- "windows_x86_64_msvc 0.32.0",
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_msvc",
 ]
 
 [[package]]
@@ -1994,60 +1937,30 @@ version = "0.28.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "52695a41e536859d5308cc613b4a022261a274390b25bd29dfff4bf08505f3c2"
 
-[[package]]
-name = "windows_aarch64_msvc"
-version = "0.32.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d8e92753b1c443191654ec532f14c199742964a061be25d77d7a96f09db20bf5"
-
 [[package]]
 name = "windows_i686_gnu"
 version = "0.28.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f54725ac23affef038fecb177de6c9bf065787c2f432f79e3c373da92f3e1d8a"
 
-[[package]]
-name = "windows_i686_gnu"
-version = "0.32.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6a711c68811799e017b6038e0922cb27a5e2f43a2ddb609fe0b6f3eeda9de615"
-
 [[package]]
 name = "windows_i686_msvc"
 version = "0.28.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "51d5158a43cc43623c0729d1ad6647e62fa384a3d135fd15108d37c683461f64"
 
-[[package]]
-name = "windows_i686_msvc"
-version = "0.32.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "146c11bb1a02615db74680b32a68e2d61f553cc24c4eb5b4ca10311740e44172"
-
 [[package]]
 name = "windows_x86_64_gnu"
 version = "0.28.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "bc31f409f565611535130cfe7ee8e6655d3fa99c1c61013981e491921b5ce954"
 
-[[package]]
-name = "windows_x86_64_gnu"
-version = "0.32.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c912b12f7454c6620635bbff3450962753834be2a594819bd5e945af18ec64bc"
-
 [[package]]
 name = "windows_x86_64_msvc"
 version = "0.28.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3f2b8c7cbd3bfdddd9ab98769f9746a7fad1bca236554cd032b78d768bc0e89f"
 
-[[package]]
-name = "windows_x86_64_msvc"
-version = "0.32.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "504a2476202769977a040c6364301a3f65d0cc9e3fb08600b2bda150a0488316"
-
 [[package]]
 name = "write-json"
 version = "0.1.2"
diff --git a/crates/base_db/src/fixture.rs b/crates/base_db/src/fixture.rs
index af825a2e00..5326a5a440 100644
--- a/crates/base_db/src/fixture.rs
+++ b/crates/base_db/src/fixture.rs
@@ -43,17 +43,6 @@ pub trait WithFixture: Default + SourceDatabaseExt + 'static {
         db
     }
 
-    fn with_files_extra_proc_macros(
-        ra_fixture: &str,
-        proc_macros: Vec<(String, ProcMacro)>,
-    ) -> Self {
-        let fixture = ChangeFixture::parse_with_proc_macros(ra_fixture, proc_macros);
-        let mut db = Self::default();
-        fixture.change.apply(&mut db);
-        assert!(fixture.file_position.is_none());
-        db
-    }
-
     fn with_position(ra_fixture: &str) -> (Self, FilePosition) {
         let (db, file_id, range_or_offset) = Self::with_range_or_offset(ra_fixture);
         let offset = range_or_offset.expect_offset();
@@ -95,14 +84,7 @@ pub struct ChangeFixture {
 
 impl ChangeFixture {
     pub fn parse(ra_fixture: &str) -> ChangeFixture {
-        Self::parse_with_proc_macros(ra_fixture, Vec::new())
-    }
-
-    pub fn parse_with_proc_macros(
-        ra_fixture: &str,
-        mut proc_macros: Vec<(String, ProcMacro)>,
-    ) -> ChangeFixture {
-        let (mini_core, proc_macro_names, fixture) = Fixture::parse(ra_fixture);
+        let (mini_core, proc_macros, fixture) = Fixture::parse(ra_fixture);
         let mut change = Change::new();
 
         let mut files = Vec::new();
@@ -240,12 +222,11 @@ impl ChangeFixture {
             }
         }
 
-        if !proc_macro_names.is_empty() {
+        if !proc_macros.is_empty() {
             let proc_lib_file = file_id;
             file_id.0 += 1;
 
-            proc_macros.extend(default_test_proc_macros());
-            let (proc_macro, source) = filter_test_proc_macros(&proc_macro_names, proc_macros);
+            let (proc_macro, source) = test_proc_macros(&proc_macros);
             let mut fs = FileSet::default();
             fs.insert(
                 proc_lib_file,
@@ -291,84 +272,52 @@ impl ChangeFixture {
     }
 }
 
-fn default_test_proc_macros() -> [(String, ProcMacro); 4] {
-    [
-        (
-            r#"
+fn test_proc_macros(proc_macros: &[String]) -> (Vec<ProcMacro>, String) {
+    // The source here is only required so that paths to the macros exist and are resolvable.
+    let source = r#"
 #[proc_macro_attribute]
 pub fn identity(_attr: TokenStream, item: TokenStream) -> TokenStream {
     item
 }
-"#
-            .into(),
-            ProcMacro {
-                name: "identity".into(),
-                kind: crate::ProcMacroKind::Attr,
-                expander: Arc::new(IdentityProcMacroExpander),
-            },
-        ),
-        (
-            r#"
 #[proc_macro_derive(DeriveIdentity)]
 pub fn derive_identity(item: TokenStream) -> TokenStream {
     item
 }
-"#
-            .into(),
-            ProcMacro {
-                name: "DeriveIdentity".into(),
-                kind: crate::ProcMacroKind::CustomDerive,
-                expander: Arc::new(IdentityProcMacroExpander),
-            },
-        ),
-        (
-            r#"
 #[proc_macro_attribute]
 pub fn input_replace(attr: TokenStream, _item: TokenStream) -> TokenStream {
     attr
 }
-"#
-            .into(),
-            ProcMacro {
-                name: "input_replace".into(),
-                kind: crate::ProcMacroKind::Attr,
-                expander: Arc::new(AttributeInputReplaceProcMacroExpander),
-            },
-        ),
-        (
-            r#"
 #[proc_macro]
 pub fn mirror(input: TokenStream) -> TokenStream {
     input
 }
-"#
-            .into(),
-            ProcMacro {
-                name: "mirror".into(),
-                kind: crate::ProcMacroKind::FuncLike,
-                expander: Arc::new(MirrorProcMacroExpander),
-            },
-        ),
+"#;
+    let proc_macros = [
+        ProcMacro {
+            name: "identity".into(),
+            kind: crate::ProcMacroKind::Attr,
+            expander: Arc::new(IdentityProcMacroExpander),
+        },
+        ProcMacro {
+            name: "DeriveIdentity".into(),
+            kind: crate::ProcMacroKind::CustomDerive,
+            expander: Arc::new(IdentityProcMacroExpander),
+        },
+        ProcMacro {
+            name: "input_replace".into(),
+            kind: crate::ProcMacroKind::Attr,
+            expander: Arc::new(AttributeInputReplaceProcMacroExpander),
+        },
+        ProcMacro {
+            name: "mirror".into(),
+            kind: crate::ProcMacroKind::FuncLike,
+            expander: Arc::new(MirrorProcMacroExpander),
+        },
     ]
-}
-
-fn filter_test_proc_macros(
-    proc_macro_names: &[String],
-    proc_macro_defs: Vec<(String, ProcMacro)>,
-) -> (Vec<ProcMacro>, String) {
-    // The source here is only required so that paths to the macros exist and are resolvable.
-    let mut source = String::new();
-    let mut proc_macros = Vec::new();
-
-    for (c, p) in proc_macro_defs {
-        if !proc_macro_names.iter().any(|name| name == &stdx::to_lower_snake_case(&p.name)) {
-            continue;
-        }
-        proc_macros.push(p);
-        source += &c;
-    }
-
-    (proc_macros, source)
+    .into_iter()
+    .filter(|pm| proc_macros.iter().any(|name| name == &stdx::to_lower_snake_case(&pm.name)))
+    .collect();
+    (proc_macros, source.into())
 }
 
 #[derive(Debug, Clone, Copy)]
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index f047971a11..aeb9f2b0ca 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -74,7 +74,7 @@ use rustc_hash::FxHashSet;
 use stdx::{format_to, impl_from};
 use syntax::{
     ast::{self, HasAttrs as _, HasDocComments, HasName},
-    AstNode, AstPtr, SmolStr, SyntaxNodePtr, T,
+    AstNode, AstPtr, SmolStr, SyntaxKind, SyntaxNodePtr,
 };
 use tt::{Ident, Leaf, Literal, TokenTree};
 
@@ -628,38 +628,43 @@ fn emit_def_diagnostic(db: &dyn HirDatabase, acc: &mut Vec<AnyDiagnostic>, diag:
 
         DefDiagnosticKind::UnresolvedProcMacro { ast } => {
             let mut precise_location = None;
-            let (node, macro_name) = match ast {
+            let (node, name) = match ast {
                 MacroCallKind::FnLike { ast_id, .. } => {
                     let node = ast_id.to_node(db.upcast());
                     (ast_id.with_value(SyntaxNodePtr::from(AstPtr::new(&node))), None)
                 }
-                MacroCallKind::Derive { ast_id, derive_attr_index, derive_index } => {
+                MacroCallKind::Derive { ast_id, derive_name, .. } => {
                     let node = ast_id.to_node(db.upcast());
 
                     // Compute the precise location of the macro name's token in the derive
                     // list.
-                    let token = (|| {
-                        let derive_attr = node.attrs().nth(*derive_attr_index as usize)?;
-                        derive_attr
-                            .syntax()
-                            .children_with_tokens()
-                            .filter_map(|elem| match elem {
+                    // FIXME: This does not handle paths to the macro, but neither does the
+                    // rest of r-a.
+                    let derive_attrs =
+                        node.attrs().filter_map(|attr| match attr.as_simple_call() {
+                            Some((name, args)) if name == "derive" => Some(args),
+                            _ => None,
+                        });
+                    'outer: for attr in derive_attrs {
+                        let tokens =
+                            attr.syntax().children_with_tokens().filter_map(|elem| match elem {
+                                syntax::NodeOrToken::Node(_) => None,
                                 syntax::NodeOrToken::Token(tok) => Some(tok),
-                                _ => None,
-                            })
-                            .group_by(|t| t.kind() == T![,])
-                            .into_iter()
-                            .filter(|&(comma, _)| !comma)
-                            .nth(*derive_index as usize)
-                            .and_then(|(_, mut g)| g.find(|t| t.kind() == T![ident]))
-                    })();
-                    precise_location = token.as_ref().map(|tok| tok.text_range());
+                            });
+                        for token in tokens {
+                            if token.kind() == SyntaxKind::IDENT && token.text() == &**derive_name {
+                                precise_location = Some(token.text_range());
+                                break 'outer;
+                            }
+                        }
+                    }
+
                     (
                         ast_id.with_value(SyntaxNodePtr::from(AstPtr::new(&node))),
-                        token.as_ref().map(ToString::to_string),
+                        Some(derive_name.clone()),
                     )
                 }
-                MacroCallKind::Attr { ast_id, invoc_attr_index, .. } => {
+                MacroCallKind::Attr { ast_id, invoc_attr_index, attr_name, .. } => {
                     let node = ast_id.to_node(db.upcast());
                     let attr = node
                         .doc_comments_and_attrs()
@@ -668,15 +673,14 @@ fn emit_def_diagnostic(db: &dyn HirDatabase, acc: &mut Vec<AnyDiagnostic>, diag:
                         .unwrap_or_else(|| panic!("cannot find attribute #{}", invoc_attr_index));
                     (
                         ast_id.with_value(SyntaxNodePtr::from(AstPtr::new(&attr))),
-                        attr.path()
-                            .and_then(|path| path.segment())
-                            .and_then(|seg| seg.name_ref())
-                            .as_ref()
-                            .map(ToString::to_string),
+                        Some(attr_name.clone()),
                     )
                 }
             };
-            acc.push(UnresolvedProcMacro { node, precise_location, macro_name }.into());
+            acc.push(
+                UnresolvedProcMacro { node, precise_location, macro_name: name.map(Into::into) }
+                    .into(),
+            );
         }
 
         DefDiagnosticKind::UnresolvedMacroCall { ast, path } => {
diff --git a/crates/hir_def/Cargo.toml b/crates/hir_def/Cargo.toml
index 794f92b233..0fca025e18 100644
--- a/crates/hir_def/Cargo.toml
+++ b/crates/hir_def/Cargo.toml
@@ -11,9 +11,7 @@ doctest = false
 
 [dependencies]
 cov-mark = "2.0.0-pre.1"
-dashmap = { version = "5.0", features = ["raw-api"] }
-lock_api = "0.4"
-parking_lot = "0.12"
+dashmap = { version = "4.0.2", features = ["raw-api"] }
 tracing = "0.1"
 once_cell = "1.3.1"
 rustc-hash = "1.1.0"
diff --git a/crates/hir_def/src/body/lower.rs b/crates/hir_def/src/body/lower.rs
index 06ad7ce4cd..7cbeef1488 100644
--- a/crates/hir_def/src/body/lower.rs
+++ b/crates/hir_def/src/body/lower.rs
@@ -28,7 +28,7 @@ use crate::{
     db::DefDatabase,
     expr::{
         dummy_expr_id, Array, BindingAnnotation, Expr, ExprId, Label, LabelId, Literal, MatchArm,
-        Pat, PatId, RecordFieldPat, RecordLitField, Statement,
+        MatchGuard, Pat, PatId, RecordFieldPat, RecordLitField, Statement,
     },
     intern::Interned,
     item_scope::BuiltinShadowMode,
@@ -155,6 +155,9 @@ impl ExprCollector<'_> {
     fn alloc_expr_desugared(&mut self, expr: Expr) -> ExprId {
         self.make_expr(expr, Err(SyntheticSyntax))
     }
+    fn unit(&mut self) -> ExprId {
+        self.alloc_expr_desugared(Expr::Tuple { exprs: Box::default() })
+    }
     fn missing_expr(&mut self) -> ExprId {
         self.alloc_expr_desugared(Expr::Missing)
     }
@@ -212,15 +215,33 @@ impl ExprCollector<'_> {
                     }
                 });
 
-                let condition = self.collect_expr_opt(e.condition());
+                let condition = match e.condition() {
+                    None => self.missing_expr(),
+                    Some(condition) => match condition.pat() {
+                        None => self.collect_expr_opt(condition.expr()),
+                        // if let -- desugar to match
+                        Some(pat) => {
+                            let pat = self.collect_pat(pat);
+                            let match_expr = self.collect_expr_opt(condition.expr());
+                            let placeholder_pat = self.missing_pat();
+                            let arms = vec![
+                                MatchArm { pat, expr: then_branch, guard: None },
+                                MatchArm {
+                                    pat: placeholder_pat,
+                                    expr: else_branch.unwrap_or_else(|| self.unit()),
+                                    guard: None,
+                                },
+                            ]
+                            .into();
+                            return Some(
+                                self.alloc_expr(Expr::Match { expr: match_expr, arms }, syntax_ptr),
+                            );
+                        }
+                    },
+                };
 
                 self.alloc_expr(Expr::If { condition, then_branch, else_branch }, syntax_ptr)
             }
-            ast::Expr::LetExpr(e) => {
-                let pat = self.collect_pat_opt(e.pat());
-                let expr = self.collect_expr_opt(e.expr());
-                self.alloc_expr(Expr::Let { pat, expr }, syntax_ptr)
-            }
             ast::Expr::BlockExpr(e) => match e.modifier() {
                 Some(ast::BlockModifier::Try(_)) => {
                     let body = self.collect_block(e);
@@ -261,7 +282,31 @@ impl ExprCollector<'_> {
                 let label = e.label().map(|label| self.collect_label(label));
                 let body = self.collect_block_opt(e.loop_body());
 
-                let condition = self.collect_expr_opt(e.condition());
+                let condition = match e.condition() {
+                    None => self.missing_expr(),
+                    Some(condition) => match condition.pat() {
+                        None => self.collect_expr_opt(condition.expr()),
+                        // if let -- desugar to match
+                        Some(pat) => {
+                            cov_mark::hit!(infer_resolve_while_let);
+                            let pat = self.collect_pat(pat);
+                            let match_expr = self.collect_expr_opt(condition.expr());
+                            let placeholder_pat = self.missing_pat();
+                            let break_ =
+                                self.alloc_expr_desugared(Expr::Break { expr: None, label: None });
+                            let arms = vec![
+                                MatchArm { pat, expr: body, guard: None },
+                                MatchArm { pat: placeholder_pat, expr: break_, guard: None },
+                            ]
+                            .into();
+                            let match_expr =
+                                self.alloc_expr_desugared(Expr::Match { expr: match_expr, arms });
+                            return Some(
+                                self.alloc_expr(Expr::Loop { body: match_expr, label }, syntax_ptr),
+                            );
+                        }
+                    },
+                };
 
                 self.alloc_expr(Expr::While { condition, body, label }, syntax_ptr)
             }
@@ -307,9 +352,15 @@ impl ExprCollector<'_> {
                             self.check_cfg(&arm).map(|()| MatchArm {
                                 pat: self.collect_pat_opt(arm.pat()),
                                 expr: self.collect_expr_opt(arm.expr()),
-                                guard: arm
-                                    .guard()
-                                    .map(|guard| self.collect_expr_opt(guard.condition())),
+                                guard: arm.guard().map(|guard| match guard.pat() {
+                                    Some(pat) => MatchGuard::IfLet {
+                                        pat: self.collect_pat(pat),
+                                        expr: self.collect_expr_opt(guard.expr()),
+                                    },
+                                    None => {
+                                        MatchGuard::If { expr: self.collect_expr_opt(guard.expr()) }
+                                    }
+                                }),
                             })
                         })
                         .collect()
diff --git a/crates/hir_def/src/body/scope.rs b/crates/hir_def/src/body/scope.rs
index fc36f1ae52..2658eece8e 100644
--- a/crates/hir_def/src/body/scope.rs
+++ b/crates/hir_def/src/body/scope.rs
@@ -8,7 +8,7 @@ use rustc_hash::FxHashMap;
 use crate::{
     body::Body,
     db::DefDatabase,
-    expr::{Expr, ExprId, LabelId, Pat, PatId, Statement},
+    expr::{Expr, ExprId, LabelId, MatchGuard, Pat, PatId, Statement},
     BlockId, DefWithBodyId,
 };
 
@@ -53,9 +53,9 @@ impl ExprScopes {
     fn new(body: &Body) -> ExprScopes {
         let mut scopes =
             ExprScopes { scopes: Arena::default(), scope_by_expr: FxHashMap::default() };
-        let mut root = scopes.root_scope();
+        let root = scopes.root_scope();
         scopes.add_params_bindings(body, root, &body.params);
-        compute_expr_scopes(body.body_expr, body, &mut scopes, &mut root);
+        compute_expr_scopes(body.body_expr, body, &mut scopes, root);
         scopes
     }
 
@@ -151,32 +151,32 @@ fn compute_block_scopes(
         match stmt {
             Statement::Let { pat, initializer, else_branch, .. } => {
                 if let Some(expr) = initializer {
-                    compute_expr_scopes(*expr, body, scopes, &mut scope);
+                    compute_expr_scopes(*expr, body, scopes, scope);
                 }
                 if let Some(expr) = else_branch {
-                    compute_expr_scopes(*expr, body, scopes, &mut scope);
+                    compute_expr_scopes(*expr, body, scopes, scope);
                 }
                 scope = scopes.new_scope(scope);
                 scopes.add_bindings(body, scope, *pat);
             }
             Statement::Expr { expr, .. } => {
-                compute_expr_scopes(*expr, body, scopes, &mut scope);
+                compute_expr_scopes(*expr, body, scopes, scope);
             }
         }
     }
     if let Some(expr) = tail {
-        compute_expr_scopes(expr, body, scopes, &mut scope);
+        compute_expr_scopes(expr, body, scopes, scope);
     }
 }
 
-fn compute_expr_scopes(expr: ExprId, body: &Body, scopes: &mut ExprScopes, scope: &mut ScopeId) {
+fn compute_expr_scopes(expr: ExprId, body: &Body, scopes: &mut ExprScopes, scope: ScopeId) {
     let make_label =
         |label: &Option<LabelId>| label.map(|label| (label, body.labels[label].name.clone()));
 
-    scopes.set_scope(expr, *scope);
+    scopes.set_scope(expr, scope);
     match &body[expr] {
         Expr::Block { statements, tail, id, label } => {
-            let scope = scopes.new_block_scope(*scope, *id, make_label(label));
+            let scope = scopes.new_block_scope(scope, *id, make_label(label));
             // Overwrite the old scope for the block expr, so that every block scope can be found
             // via the block itself (important for blocks that only contain items, no expressions).
             scopes.set_scope(expr, scope);
@@ -184,49 +184,46 @@ fn compute_expr_scopes(expr: ExprId, body: &Body, scopes: &mut ExprScopes, scope
         }
         Expr::For { iterable, pat, body: body_expr, label } => {
             compute_expr_scopes(*iterable, body, scopes, scope);
-            let mut scope = scopes.new_labeled_scope(*scope, make_label(label));
+            let scope = scopes.new_labeled_scope(scope, make_label(label));
             scopes.add_bindings(body, scope, *pat);
-            compute_expr_scopes(*body_expr, body, scopes, &mut scope);
+            compute_expr_scopes(*body_expr, body, scopes, scope);
         }
         Expr::While { condition, body: body_expr, label } => {
-            let mut scope = scopes.new_labeled_scope(*scope, make_label(label));
-            compute_expr_scopes(*condition, body, scopes, &mut scope);
-            compute_expr_scopes(*body_expr, body, scopes, &mut scope);
+            let scope = scopes.new_labeled_scope(scope, make_label(label));
+            compute_expr_scopes(*condition, body, scopes, scope);
+            compute_expr_scopes(*body_expr, body, scopes, scope);
         }
         Expr::Loop { body: body_expr, label } => {
-            let mut scope = scopes.new_labeled_scope(*scope, make_label(label));
-            compute_expr_scopes(*body_expr, body, scopes, &mut scope);
+            let scope = scopes.new_labeled_scope(scope, make_label(label));
+            compute_expr_scopes(*body_expr, body, scopes, scope);
         }
         Expr::Lambda { args, body: body_expr, .. } => {
-            let mut scope = scopes.new_scope(*scope);
+            let scope = scopes.new_scope(scope);
             scopes.add_params_bindings(body, scope, args);
-            compute_expr_scopes(*body_expr, body, scopes, &mut scope);
+            compute_expr_scopes(*body_expr, body, scopes, scope);
         }
         Expr::Match { expr, arms } => {
             compute_expr_scopes(*expr, body, scopes, scope);
             for arm in arms.iter() {
-                let mut scope = scopes.new_scope(*scope);
+                let mut scope = scopes.new_scope(scope);
                 scopes.add_bindings(body, scope, arm.pat);
-                if let Some(guard) = arm.guard {
-                    scope = scopes.new_scope(scope);
-                    compute_expr_scopes(guard, body, scopes, &mut scope);
-                }
-                compute_expr_scopes(arm.expr, body, scopes, &mut scope);
-            }
-        }
-        &Expr::If { condition, then_branch, else_branch } => {
-            let mut then_branch_scope = scopes.new_scope(*scope);
-            compute_expr_scopes(condition, body, scopes, &mut then_branch_scope);
-            compute_expr_scopes(then_branch, body, scopes, &mut then_branch_scope);
-            if let Some(else_branch) = else_branch {
-                compute_expr_scopes(else_branch, body, scopes, scope);
+                match arm.guard {
+                    Some(MatchGuard::If { expr: guard }) => {
+                        scopes.set_scope(guard, scope);
+                        compute_expr_scopes(guard, body, scopes, scope);
+                    }
+                    Some(MatchGuard::IfLet { pat, expr: guard }) => {
+                        scopes.set_scope(guard, scope);
+                        compute_expr_scopes(guard, body, scopes, scope);
+                        scope = scopes.new_scope(scope);
+                        scopes.add_bindings(body, scope, pat);
+                    }
+                    _ => {}
+                };
+                scopes.set_scope(arm.expr, scope);
+                compute_expr_scopes(arm.expr, body, scopes, scope);
             }
         }
-        &Expr::Let { pat, expr } => {
-            compute_expr_scopes(expr, body, scopes, scope);
-            *scope = scopes.new_scope(*scope);
-            scopes.add_bindings(body, *scope, pat);
-        }
         e => e.walk_child_exprs(|e| compute_expr_scopes(e, body, scopes, scope)),
     };
 }
@@ -503,7 +500,8 @@ fn foo() {
     }
 
     #[test]
-    fn while_let_adds_binding() {
+    fn while_let_desugaring() {
+        cov_mark::check!(infer_resolve_while_let);
         do_check_local_name(
             r#"
 fn test() {
@@ -515,53 +513,5 @@ fn test() {
 "#,
             75,
         );
-        do_check_local_name(
-            r#"
-fn test() {
-    let foo: Option<f32> = None;
-    while (((let Option::Some(_) = foo))) && let Option::Some(spam) = foo {
-        spam$0
-    }
-}
-"#,
-            107,
-        );
-    }
-
-    #[test]
-    fn match_guard_if_let() {
-        do_check_local_name(
-            r#"
-fn test() {
-    let foo: Option<f32> = None;
-    match foo {
-        _ if let Option::Some(spam) = foo => spam$0,
-    }
-}
-"#,
-            93,
-        );
-    }
-
-    #[test]
-    fn let_chains_can_reference_previous_lets() {
-        do_check_local_name(
-            r#"
-fn test() {
-    let foo: Option<i32> = None;
-    if let Some(spam) = foo && spa$0m > 1 && let Some(spam) = foo && spam > 1 {}
-}
-"#,
-            61,
-        );
-        do_check_local_name(
-            r#"
-fn test() {
-    let foo: Option<i32> = None;
-    if let Some(spam) = foo && spam > 1 && let Some(spam) = foo && sp$0am > 1 {}
-}
-"#,
-            100,
-        );
     }
 }
diff --git a/crates/hir_def/src/expr.rs b/crates/hir_def/src/expr.rs
index 4dca823888..6534f970ee 100644
--- a/crates/hir_def/src/expr.rs
+++ b/crates/hir_def/src/expr.rs
@@ -59,10 +59,6 @@ pub enum Expr {
         then_branch: ExprId,
         else_branch: Option<ExprId>,
     },
-    Let {
-        pat: PatId,
-        expr: ExprId,
-    },
     Block {
         id: BlockId,
         statements: Box<[Statement]>,
@@ -193,10 +189,17 @@ pub enum Array {
 #[derive(Debug, Clone, Eq, PartialEq)]
 pub struct MatchArm {
     pub pat: PatId,
-    pub guard: Option<ExprId>,
+    pub guard: Option<MatchGuard>,
     pub expr: ExprId,
 }
 
+#[derive(Debug, Clone, Eq, PartialEq)]
+pub enum MatchGuard {
+    If { expr: ExprId },
+
+    IfLet { pat: PatId, expr: ExprId },
+}
+
 #[derive(Debug, Clone, Eq, PartialEq)]
 pub struct RecordLitField {
     pub name: Name,
@@ -229,9 +232,6 @@ impl Expr {
                     f(else_branch);
                 }
             }
-            Expr::Let { expr, .. } => {
-                f(*expr);
-            }
             Expr::Block { statements, tail, .. } => {
                 for stmt in statements.iter() {
                     match stmt {
diff --git a/crates/hir_def/src/intern.rs b/crates/hir_def/src/intern.rs
index f5a4f5ec4e..79ba970e7b 100644
--- a/crates/hir_def/src/intern.rs
+++ b/crates/hir_def/src/intern.rs
@@ -10,20 +10,15 @@ use std::{
     sync::Arc,
 };
 
-use dashmap::{DashMap, SharedValue};
-use lock_api::RwLockWriteGuard;
+use dashmap::{lock::RwLockWriteGuard, DashMap, SharedValue};
 use once_cell::sync::OnceCell;
-use parking_lot::RawRwLock;
 use rustc_hash::FxHasher;
 
 use crate::generics::GenericParams;
 
 type InternMap<T> = DashMap<Arc<T>, (), BuildHasherDefault<FxHasher>>;
-type Guard<T> = RwLockWriteGuard<
-    'static,
-    RawRwLock,
-    HashMap<Arc<T>, SharedValue<()>, BuildHasherDefault<FxHasher>>,
->;
+type Guard<T> =
+    RwLockWriteGuard<'static, HashMap<Arc<T>, SharedValue<()>, BuildHasherDefault<FxHasher>>>;
 
 pub struct Interned<T: Internable + ?Sized> {
     arc: Arc<T>,
diff --git a/crates/hir_def/src/item_tree/pretty.rs b/crates/hir_def/src/item_tree/pretty.rs
index 0df6e97dd4..eaaff5a21f 100644
--- a/crates/hir_def/src/item_tree/pretty.rs
+++ b/crates/hir_def/src/item_tree/pretty.rs
@@ -493,14 +493,14 @@ impl<'a> Printer<'a> {
                 w!(self, "]");
             }
             TypeRef::Fn(args_and_ret, varargs) => {
-                let ((_, return_type), args) =
+                let (ret, args) =
                     args_and_ret.split_last().expect("TypeRef::Fn is missing return type");
                 w!(self, "fn(");
-                for (i, (_, typeref)) in args.iter().enumerate() {
+                for (i, arg) in args.iter().enumerate() {
                     if i != 0 {
                         w!(self, ", ");
                     }
-                    self.print_type_ref(&typeref);
+                    self.print_type_ref(arg);
                 }
                 if *varargs {
                     if !args.is_empty() {
@@ -509,7 +509,7 @@ impl<'a> Printer<'a> {
                     w!(self, "...");
                 }
                 w!(self, ") -> ");
-                self.print_type_ref(&return_type);
+                self.print_type_ref(ret);
             }
             TypeRef::Macro(_ast_id) => {
                 w!(self, "<macro>");
diff --git a/crates/hir_def/src/lib.rs b/crates/hir_def/src/lib.rs
index 452a3712bc..db1e65d080 100644
--- a/crates/hir_def/src/lib.rs
+++ b/crates/hir_def/src/lib.rs
@@ -741,19 +741,23 @@ fn macro_call_as_call_id(
 fn derive_macro_as_call_id(
     item_attr: &AstIdWithPath<ast::Adt>,
     derive_attr: AttrId,
-    derive_pos: u32,
     db: &dyn db::DefDatabase,
     krate: CrateId,
     resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
 ) -> Result<MacroCallId, UnresolvedMacro> {
     let def: MacroDefId = resolver(item_attr.path.clone())
         .ok_or_else(|| UnresolvedMacro { path: item_attr.path.clone() })?;
+    let last_segment = item_attr
+        .path
+        .segments()
+        .last()
+        .ok_or_else(|| UnresolvedMacro { path: item_attr.path.clone() })?;
     let res = def.as_lazy_macro(
         db.upcast(),
         krate,
         MacroCallKind::Derive {
             ast_id: item_attr.ast_id,
-            derive_index: derive_pos,
+            derive_name: last_segment.to_string().into_boxed_str(),
             derive_attr_index: derive_attr.ast_index,
         },
     );
@@ -767,6 +771,8 @@ fn attr_macro_as_call_id(
     krate: CrateId,
     def: MacroDefId,
 ) -> MacroCallId {
+    let attr_path = &item_attr.path;
+    let last_segment = attr_path.segments().last().expect("empty attribute path");
     let mut arg = match macro_attr.input.as_deref() {
         Some(attr::AttrInput::TokenTree(tt, map)) => (tt.clone(), map.clone()),
         _ => Default::default(),
@@ -780,7 +786,8 @@ fn attr_macro_as_call_id(
         krate,
         MacroCallKind::Attr {
             ast_id: item_attr.ast_id,
-            attr_args: Arc::new(arg),
+            attr_name: last_segment.to_string().into_boxed_str(),
+            attr_args: arg,
             invoc_attr_index: macro_attr.id.ast_index,
         },
     );
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs
index ec6af65a92..7f7213f4c3 100644
--- a/crates/hir_def/src/nameres/collector.rs
+++ b/crates/hir_def/src/nameres/collector.rs
@@ -1036,9 +1036,6 @@ impl DefCollector<'_> {
     fn resolve_macros(&mut self) -> ReachedFixedPoint {
         let mut macros = std::mem::take(&mut self.unresolved_macros);
         let mut resolved = Vec::new();
-        let mut push_resolved = |directive: &MacroDirective, call_id| {
-            resolved.push((directive.module_id, directive.depth, directive.container, call_id));
-        };
         let mut res = ReachedFixedPoint::Yes;
         macros.retain(|directive| {
             let resolver = |path| {
@@ -1063,7 +1060,12 @@ impl DefCollector<'_> {
                         &mut |_err| (),
                     );
                     if let Ok(Ok(call_id)) = call_id {
-                        push_resolved(directive, call_id);
+                        resolved.push((
+                            directive.module_id,
+                            call_id,
+                            directive.depth,
+                            directive.container,
+                        ));
                         res = ReachedFixedPoint::No;
                         return false;
                     }
@@ -1072,7 +1074,6 @@ impl DefCollector<'_> {
                     let call_id = derive_macro_as_call_id(
                         ast_id,
                         *derive_attr,
-                        *derive_pos as u32,
                         self.db,
                         self.def_map.krate,
                         &resolver,
@@ -1085,7 +1086,12 @@ impl DefCollector<'_> {
                             *derive_pos,
                         );
 
-                        push_resolved(directive, call_id);
+                        resolved.push((
+                            directive.module_id,
+                            call_id,
+                            directive.depth,
+                            directive.container,
+                        ));
                         res = ReachedFixedPoint::No;
                         return false;
                     }
@@ -1223,7 +1229,12 @@ impl DefCollector<'_> {
                         .scope
                         .add_attr_macro_invoc(ast_id, call_id);
 
-                    push_resolved(directive, call_id);
+                    resolved.push((
+                        directive.module_id,
+                        call_id,
+                        directive.depth,
+                        directive.container,
+                    ));
                     res = ReachedFixedPoint::No;
                     return false;
                 }
@@ -1234,7 +1245,7 @@ impl DefCollector<'_> {
         // Attribute resolution can add unresolved macro invocations, so concatenate the lists.
         self.unresolved_macros.extend(macros);
 
-        for (module_id, depth, container, macro_call_id) in resolved {
+        for (module_id, macro_call_id, depth, container) in resolved {
             self.collect_macro_expansion(module_id, macro_call_id, depth, container);
         }
 
diff --git a/crates/hir_def/src/type_ref.rs b/crates/hir_def/src/type_ref.rs
index ee8ef6caa3..cfc69feccc 100644
--- a/crates/hir_def/src/type_ref.rs
+++ b/crates/hir_def/src/type_ref.rs
@@ -1,12 +1,9 @@
 //! HIR for references to types. Paths in these are not yet resolved. They can
 //! be directly created from an ast::TypeRef, without further queries.
 
-use hir_expand::{
-    name::{AsName, Name},
-    AstId, InFile,
-};
+use hir_expand::{name::Name, AstId, InFile};
 use std::convert::TryInto;
-use syntax::ast::{self, HasName};
+use syntax::ast;
 
 use crate::{body::LowerCtx, intern::Interned, path::Path};
 
@@ -92,7 +89,7 @@ pub enum TypeRef {
     Array(Box<TypeRef>, ConstScalar),
     Slice(Box<TypeRef>),
     /// A fn pointer. Last element of the vector is the return type.
-    Fn(Vec<(Option<Name>, TypeRef)>, bool /*varargs*/),
+    Fn(Vec<TypeRef>, bool /*varargs*/),
     // For
     ImplTrait(Vec<Interned<TypeBound>>),
     DynTrait(Vec<Interned<TypeBound>>),
@@ -191,22 +188,11 @@ impl TypeRef {
                         is_varargs = param.dotdotdot_token().is_some();
                     }
 
-                    pl.params()
-                        .map(|it| {
-                            let type_ref = TypeRef::from_ast_opt(ctx, it.ty());
-                            let name = match it.pat() {
-                                Some(ast::Pat::IdentPat(it)) => Some(
-                                    it.name().map(|nr| nr.as_name()).unwrap_or_else(Name::missing),
-                                ),
-                                _ => None,
-                            };
-                            (name, type_ref)
-                        })
-                        .collect()
+                    pl.params().map(|p| p.ty()).map(|it| TypeRef::from_ast_opt(ctx, it)).collect()
                 } else {
                     Vec::new()
                 };
-                params.push((None, ret_ty));
+                params.push(ret_ty);
                 TypeRef::Fn(params, is_varargs)
             }
             // for types are close enough for our purposes to the inner type for now...
@@ -244,10 +230,9 @@ impl TypeRef {
         fn go(type_ref: &TypeRef, f: &mut impl FnMut(&TypeRef)) {
             f(type_ref);
             match type_ref {
-                TypeRef::Fn(params, _) => {
-                    params.iter().for_each(|(_, param_type)| go(&param_type, f))
+                TypeRef::Fn(types, _) | TypeRef::Tuple(types) => {
+                    types.iter().for_each(|t| go(t, f))
                 }
-                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/hir_expand/Cargo.toml b/crates/hir_expand/Cargo.toml
index d7b4cbf82e..e5455660d3 100644
--- a/crates/hir_expand/Cargo.toml
+++ b/crates/hir_expand/Cargo.toml
@@ -27,6 +27,3 @@ profile = { path = "../profile", version = "0.0.0" }
 tt = { path = "../tt", version = "0.0.0" }
 mbe = { path = "../mbe", version = "0.0.0" }
 limit = { path = "../limit", version = "0.0.0" }
-
-[dev-dependencies]
-expect-test = "1.2.0-pre.1"
diff --git a/crates/hir_expand/src/db.rs b/crates/hir_expand/src/db.rs
index 7d82b33db8..d9bfdd6fd4 100644
--- a/crates/hir_expand/src/db.rs
+++ b/crates/hir_expand/src/db.rs
@@ -14,7 +14,7 @@ use syntax::{
 };
 
 use crate::{
-    ast_id_map::AstIdMap, fixup, hygiene::HygieneFrame, BuiltinAttrExpander, BuiltinDeriveExpander,
+    ast_id_map::AstIdMap, hygiene::HygieneFrame, BuiltinAttrExpander, BuiltinDeriveExpander,
     BuiltinFnLikeExpander, ExpandTo, HirFileId, HirFileIdRepr, MacroCallId, MacroCallKind,
     MacroCallLoc, MacroDefId, MacroDefKind, MacroFile, ProcMacroExpander,
 };
@@ -108,10 +108,7 @@ pub trait AstDatabase: SourceDatabase {
 
     /// Lowers syntactic macro call to a token tree representation.
     #[salsa::transparent]
-    fn macro_arg(
-        &self,
-        id: MacroCallId,
-    ) -> Option<Arc<(tt::Subtree, mbe::TokenMap, fixup::SyntaxFixupUndoInfo)>>;
+    fn macro_arg(&self, id: MacroCallId) -> Option<Arc<(tt::Subtree, mbe::TokenMap)>>;
     /// Extracts syntax node, corresponding to a macro call. That's a firewall
     /// query, only typing in the macro call itself changes the returned
     /// subtree.
@@ -149,15 +146,8 @@ pub fn expand_speculative(
 
     // Build the subtree and token mapping for the speculative args
     let censor = censor_for_macro_input(&loc, &speculative_args);
-    let mut fixups = fixup::fixup_syntax(&speculative_args);
-    fixups.replace.extend(censor.into_iter().map(|node| (node, Vec::new())));
-    let (mut tt, spec_args_tmap, _) = mbe::syntax_node_to_token_tree_with_modifications(
-        &speculative_args,
-        fixups.token_map,
-        fixups.next_id,
-        fixups.replace,
-        fixups.append,
-    );
+    let (mut tt, spec_args_tmap) =
+        mbe::syntax_node_to_token_tree_censored(&speculative_args, &censor);
 
     let (attr_arg, token_id) = match loc.kind {
         MacroCallKind::Attr { invoc_attr_index, .. } => {
@@ -204,7 +194,7 @@ pub fn expand_speculative(
 
     // Do the actual expansion, we need to directly expand the proc macro due to the attribute args
     // Otherwise the expand query will fetch the non speculative attribute args and pass those instead.
-    let mut speculative_expansion = if let MacroDefKind::ProcMacro(expander, ..) = loc.def.kind {
+    let speculative_expansion = if let MacroDefKind::ProcMacro(expander, ..) = loc.def.kind {
         tt.delimiter = None;
         expander.expand(db, loc.krate, &tt, attr_arg.as_ref())
     } else {
@@ -212,7 +202,6 @@ pub fn expand_speculative(
     };
 
     let expand_to = macro_expand_to(db, actual_macro_call);
-    fixup::reverse_fixups(&mut speculative_expansion.value, &spec_args_tmap, &fixups.undo_info);
     let (node, rev_tmap) = token_tree_to_syntax_node(&speculative_expansion.value, expand_to);
 
     let range = rev_tmap.first_range_by_token(token_id, token_to_map.kind())?;
@@ -300,31 +289,20 @@ fn parse_macro_expansion(
     }
 }
 
-fn macro_arg(
-    db: &dyn AstDatabase,
-    id: MacroCallId,
-) -> Option<Arc<(tt::Subtree, mbe::TokenMap, fixup::SyntaxFixupUndoInfo)>> {
+fn macro_arg(db: &dyn AstDatabase, id: MacroCallId) -> Option<Arc<(tt::Subtree, mbe::TokenMap)>> {
     let arg = db.macro_arg_text(id)?;
     let loc = db.lookup_intern_macro_call(id);
 
     let node = SyntaxNode::new_root(arg);
     let censor = censor_for_macro_input(&loc, &node);
-    let mut fixups = fixup::fixup_syntax(&node);
-    fixups.replace.extend(censor.into_iter().map(|node| (node, Vec::new())));
-    let (mut tt, tmap, _) = mbe::syntax_node_to_token_tree_with_modifications(
-        &node,
-        fixups.token_map,
-        fixups.next_id,
-        fixups.replace,
-        fixups.append,
-    );
+    let (mut tt, tmap) = mbe::syntax_node_to_token_tree_censored(&node, &censor);
 
     if loc.def.is_proc_macro() {
         // proc macros expect their inputs without parentheses, MBEs expect it with them included
         tt.delimiter = None;
     }
 
-    Some(Arc::new((tt, tmap, fixups.undo_info)))
+    Some(Arc::new((tt, tmap)))
 }
 
 fn censor_for_macro_input(loc: &MacroCallLoc, node: &SyntaxNode) -> FxHashSet<SyntaxNode> {
@@ -336,7 +314,6 @@ fn censor_for_macro_input(loc: &MacroCallLoc, node: &SyntaxNode) -> FxHashSet<Sy
                 ast::Item::cast(node.clone())?
                     .attrs()
                     .take(derive_attr_index as usize + 1)
-                    // FIXME
                     .filter(|attr| attr.simple_name().as_deref() == Some("derive"))
                     .map(|it| it.syntax().clone())
                     .collect()
@@ -442,9 +419,10 @@ fn macro_expand(db: &dyn AstDatabase, id: MacroCallId) -> ExpandResult<Option<Ar
         // be reported at the definition site (when we construct a def map).
         Err(err) => return ExpandResult::str_err(format!("invalid macro definition: {}", err)),
     };
-    let ExpandResult { value: mut tt, err } = expander.expand(db, id, &macro_arg.0);
+    let ExpandResult { value: tt, err } = expander.expand(db, id, &macro_arg.0);
     // Set a hard limit for the expanded tt
     let count = tt.count();
+    // XXX: Make ExpandResult a real error and use .map_err instead?
     if TOKEN_LIMIT.check(count).is_err() {
         return ExpandResult::str_err(format!(
             "macro invocation exceeds token limit: produced {} tokens, limit is {}",
@@ -453,8 +431,6 @@ fn macro_expand(db: &dyn AstDatabase, id: MacroCallId) -> ExpandResult<Option<Ar
         ));
     }
 
-    fixup::reverse_fixups(&mut tt, &macro_arg.1, &macro_arg.2);
-
     ExpandResult { value: Some(Arc::new(tt)), err }
 }
 
diff --git a/crates/hir_expand/src/fixup.rs b/crates/hir_expand/src/fixup.rs
deleted file mode 100644
index 2eb3da79dc..0000000000
--- a/crates/hir_expand/src/fixup.rs
+++ /dev/null
@@ -1,261 +0,0 @@
-//! To make attribute macros work reliably when typing, we need to take care to
-//! fix up syntax errors in the code we're passing to them.
-use std::mem;
-
-use mbe::{SyntheticToken, SyntheticTokenId, TokenMap};
-use rustc_hash::FxHashMap;
-use syntax::{
-    ast::{self, AstNode},
-    match_ast, SyntaxKind, SyntaxNode, TextRange,
-};
-use tt::Subtree;
-
-/// The result of calculating fixes for a syntax node -- a bunch of changes
-/// (appending to and replacing nodes), the information that is needed to
-/// reverse those changes afterwards, and a token map.
-#[derive(Debug)]
-pub(crate) struct SyntaxFixups {
-    pub(crate) append: FxHashMap<SyntaxNode, Vec<SyntheticToken>>,
-    pub(crate) replace: FxHashMap<SyntaxNode, Vec<SyntheticToken>>,
-    pub(crate) undo_info: SyntaxFixupUndoInfo,
-    pub(crate) token_map: TokenMap,
-    pub(crate) next_id: u32,
-}
-
-/// This is the information needed to reverse the fixups.
-#[derive(Debug, PartialEq, Eq)]
-pub struct SyntaxFixupUndoInfo {
-    original: Vec<Subtree>,
-}
-
-const EMPTY_ID: SyntheticTokenId = SyntheticTokenId(!0);
-
-pub(crate) fn fixup_syntax(node: &SyntaxNode) -> SyntaxFixups {
-    let mut append = FxHashMap::default();
-    let mut replace = FxHashMap::default();
-    let mut preorder = node.preorder();
-    let mut original = Vec::new();
-    let mut token_map = TokenMap::default();
-    let mut next_id = 0;
-    while let Some(event) = preorder.next() {
-        let node = match event {
-            syntax::WalkEvent::Enter(node) => node,
-            syntax::WalkEvent::Leave(_) => continue,
-        };
-
-        if can_handle_error(&node) && has_error_to_handle(&node) {
-            // the node contains an error node, we have to completely replace it by something valid
-            let (original_tree, new_tmap, new_next_id) =
-                mbe::syntax_node_to_token_tree_with_modifications(
-                    &node,
-                    mem::take(&mut token_map),
-                    next_id,
-                    Default::default(),
-                    Default::default(),
-                );
-            token_map = new_tmap;
-            next_id = new_next_id;
-            let idx = original.len() as u32;
-            original.push(original_tree);
-            let replacement = SyntheticToken {
-                kind: SyntaxKind::IDENT,
-                text: "__ra_fixup".into(),
-                range: node.text_range(),
-                id: SyntheticTokenId(idx),
-            };
-            replace.insert(node.clone(), vec![replacement]);
-            preorder.skip_subtree();
-            continue;
-        }
-
-        // In some other situations, we can fix things by just appending some tokens.
-        let end_range = TextRange::empty(node.text_range().end());
-        match_ast! {
-            match node {
-                ast::FieldExpr(it) => {
-                    if it.name_ref().is_none() {
-                        // incomplete field access: some_expr.|
-                        append.insert(node.clone(), vec![
-                            SyntheticToken {
-                                kind: SyntaxKind::IDENT,
-                                text: "__ra_fixup".into(),
-                                range: end_range,
-                                id: EMPTY_ID,
-                            },
-                        ]);
-                    }
-                },
-                ast::ExprStmt(it) => {
-                    if it.semicolon_token().is_none() {
-                        append.insert(node.clone(), vec![
-                            SyntheticToken {
-                                kind: SyntaxKind::SEMICOLON,
-                                text: ";".into(),
-                                range: end_range,
-                                id: EMPTY_ID,
-                            },
-                        ]);
-                    }
-                },
-                _ => (),
-            }
-        }
-    }
-    SyntaxFixups {
-        append,
-        replace,
-        token_map,
-        next_id,
-        undo_info: SyntaxFixupUndoInfo { original },
-    }
-}
-
-fn has_error(node: &SyntaxNode) -> bool {
-    node.children().any(|c| c.kind() == SyntaxKind::ERROR)
-}
-
-fn can_handle_error(node: &SyntaxNode) -> bool {
-    ast::Expr::can_cast(node.kind())
-}
-
-fn has_error_to_handle(node: &SyntaxNode) -> bool {
-    has_error(node) || node.children().any(|c| !can_handle_error(&c) && has_error_to_handle(&c))
-}
-
-pub(crate) fn reverse_fixups(
-    tt: &mut Subtree,
-    token_map: &TokenMap,
-    undo_info: &SyntaxFixupUndoInfo,
-) {
-    tt.token_trees.retain(|tt| match tt {
-        tt::TokenTree::Leaf(leaf) => {
-            token_map.synthetic_token_id(leaf.id()).is_none()
-                || token_map.synthetic_token_id(leaf.id()) != Some(EMPTY_ID)
-        }
-        _ => true,
-    });
-    tt.token_trees.iter_mut().for_each(|tt| match tt {
-        tt::TokenTree::Subtree(tt) => reverse_fixups(tt, token_map, undo_info),
-        tt::TokenTree::Leaf(leaf) => {
-            if let Some(id) = token_map.synthetic_token_id(leaf.id()) {
-                let original = &undo_info.original[id.0 as usize];
-                *tt = tt::TokenTree::Subtree(original.clone());
-            }
-        }
-    });
-}
-
-#[cfg(test)]
-mod tests {
-    use expect_test::{expect, Expect};
-
-    use super::reverse_fixups;
-
-    #[track_caller]
-    fn check(ra_fixture: &str, mut expect: Expect) {
-        let parsed = syntax::SourceFile::parse(ra_fixture);
-        let fixups = super::fixup_syntax(&parsed.syntax_node());
-        let (mut tt, tmap, _) = mbe::syntax_node_to_token_tree_with_modifications(
-            &parsed.syntax_node(),
-            fixups.token_map,
-            fixups.next_id,
-            fixups.replace,
-            fixups.append,
-        );
-
-        let mut actual = tt.to_string();
-        actual.push_str("\n");
-
-        expect.indent(false);
-        expect.assert_eq(&actual);
-
-        // the fixed-up tree should be syntactically valid
-        let (parse, _) = mbe::token_tree_to_syntax_node(&tt, ::mbe::TopEntryPoint::MacroItems);
-        assert_eq!(
-            parse.errors(),
-            &[],
-            "parse has syntax errors. parse tree:\n{:#?}",
-            parse.syntax_node()
-        );
-
-        reverse_fixups(&mut tt, &tmap, &fixups.undo_info);
-
-        // the fixed-up + reversed version should be equivalent to the original input
-        // (but token IDs don't matter)
-        let (original_as_tt, _) = mbe::syntax_node_to_token_tree(&parsed.syntax_node());
-        assert_eq!(tt.to_string(), original_as_tt.to_string());
-    }
-
-    #[test]
-    fn incomplete_field_expr_1() {
-        check(
-            r#"
-fn foo() {
-    a.
-}
-"#,
-            expect![[r#"
-fn foo () {a . __ra_fixup}
-"#]],
-        )
-    }
-
-    #[test]
-    fn incomplete_field_expr_2() {
-        check(
-            r#"
-fn foo() {
-    a. ;
-}
-"#,
-            expect![[r#"
-fn foo () {a . __ra_fixup ;}
-"#]],
-        )
-    }
-
-    #[test]
-    fn incomplete_field_expr_3() {
-        check(
-            r#"
-fn foo() {
-    a. ;
-    bar();
-}
-"#,
-            expect![[r#"
-fn foo () {a . __ra_fixup ; bar () ;}
-"#]],
-        )
-    }
-
-    #[test]
-    fn field_expr_before_call() {
-        // another case that easily happens while typing
-        check(
-            r#"
-fn foo() {
-    a.b
-    bar();
-}
-"#,
-            expect![[r#"
-fn foo () {a . b ; bar () ;}
-"#]],
-        )
-    }
-
-    #[test]
-    fn extraneous_comma() {
-        check(
-            r#"
-fn foo() {
-    bar(,);
-}
-"#,
-            expect![[r#"
-fn foo () {__ra_fixup ;}
-"#]],
-        )
-    }
-}
diff --git a/crates/hir_expand/src/hygiene.rs b/crates/hir_expand/src/hygiene.rs
index d60734372c..d2b719bf57 100644
--- a/crates/hir_expand/src/hygiene.rs
+++ b/crates/hir_expand/src/hygiene.rs
@@ -15,7 +15,6 @@ use syntax::{
 
 use crate::{
     db::{self, AstDatabase},
-    fixup,
     name::{AsName, Name},
     HirFileId, HirFileIdRepr, InFile, MacroCallKind, MacroCallLoc, MacroDefKind, MacroFile,
 };
@@ -128,7 +127,7 @@ struct HygieneInfo {
     attr_input_or_mac_def_start: Option<InFile<TextSize>>,
 
     macro_def: Arc<TokenExpander>,
-    macro_arg: Arc<(tt::Subtree, mbe::TokenMap, fixup::SyntaxFixupUndoInfo)>,
+    macro_arg: Arc<(tt::Subtree, mbe::TokenMap)>,
     macro_arg_shift: mbe::Shift,
     exp_map: Arc<mbe::TokenMap>,
 }
diff --git a/crates/hir_expand/src/lib.rs b/crates/hir_expand/src/lib.rs
index e33c2565c3..80067dcc5d 100644
--- a/crates/hir_expand/src/lib.rs
+++ b/crates/hir_expand/src/lib.rs
@@ -15,7 +15,6 @@ pub mod proc_macro;
 pub mod quote;
 pub mod eager;
 pub mod mod_path;
-mod fixup;
 
 pub use mbe::{ExpandError, ExpandResult, Origin};
 
@@ -125,17 +124,17 @@ pub enum MacroCallKind {
     },
     Derive {
         ast_id: AstId<ast::Adt>,
+        derive_name: Box<str>,
         /// Syntactical index of the invoking `#[derive]` attribute.
         ///
         /// Outer attributes are counted first, then inner attributes. This does not support
         /// out-of-line modules, which may have attributes spread across 2 files!
         derive_attr_index: u32,
-        /// Index of the derive macro in the derive attribute
-        derive_index: u32,
     },
     Attr {
         ast_id: AstId<ast::Item>,
-        attr_args: Arc<(tt::Subtree, mbe::TokenMap)>,
+        attr_name: Box<str>,
+        attr_args: (tt::Subtree, mbe::TokenMap),
         /// Syntactical index of the invoking `#[attribute]`.
         ///
         /// Outer attributes are counted first, then inner attributes. This does not support
@@ -427,7 +426,7 @@ pub struct ExpansionInfo {
     attr_input_or_mac_def: Option<InFile<ast::TokenTree>>,
 
     macro_def: Arc<TokenExpander>,
-    macro_arg: Arc<(tt::Subtree, mbe::TokenMap, fixup::SyntaxFixupUndoInfo)>,
+    macro_arg: Arc<(tt::Subtree, mbe::TokenMap)>,
     /// A shift built from `macro_arg`'s subtree, relevant for attributes as the item is the macro arg
     /// and as such we need to shift tokens if they are part of an attributes input instead of their item.
     macro_arg_shift: mbe::Shift,
@@ -472,7 +471,7 @@ impl ExpansionInfo {
 
             let token_range = token.value.text_range();
             match &loc.kind {
-                MacroCallKind::Attr { attr_args, invoc_attr_index, .. } => {
+                MacroCallKind::Attr { attr_args: (_, map), invoc_attr_index, .. } => {
                     let attr = item
                         .doc_comments_and_attrs()
                         .nth(*invoc_attr_index as usize)
@@ -486,9 +485,8 @@ impl ExpansionInfo {
                             let relative_range =
                                 token.value.text_range().checked_sub(attr_input_start)?;
                             // shift by the item's tree's max id
-                            let token_id = self
-                                .macro_arg_shift
-                                .shift(attr_args.1.token_by_range(relative_range)?);
+                            let token_id =
+                                self.macro_arg_shift.shift(map.token_by_range(relative_range)?);
                             Some(token_id)
                         }
                         _ => None,
@@ -536,13 +534,13 @@ impl ExpansionInfo {
 
         // Attributes are a bit special for us, they have two inputs, the input tokentree and the annotated item.
         let (token_map, tt) = match &loc.kind {
-            MacroCallKind::Attr { attr_args, .. } => {
+            MacroCallKind::Attr { attr_args: (_, arg_token_map), .. } => {
                 // try unshifting the the token id, if unshifting fails, the token resides in the non-item attribute input
                 // note that the `TokenExpander::map_id_up` earlier only unshifts for declarative macros, so we don't double unshift with this
                 match self.macro_arg_shift.unshift(token_id) {
                     Some(unshifted) => {
                         token_id = unshifted;
-                        (&attr_args.1, self.attr_input_or_mac_def.clone()?.syntax().cloned())
+                        (arg_token_map, self.attr_input_or_mac_def.clone()?.syntax().cloned())
                     }
                     None => (&self.macro_arg.1, self.arg.clone()),
                 }
@@ -712,14 +710,14 @@ fn ascend_call_token(
     expansion: &ExpansionInfo,
     token: InFile<SyntaxToken>,
 ) -> Option<InFile<SyntaxToken>> {
-    let mut mapping = expansion.map_token_up(db, token.as_ref())?;
-    while let (mapped, Origin::Call) = mapping {
-        match mapped.file_id.expansion_info(db) {
-            Some(info) => mapping = info.map_token_up(db, mapped.as_ref())?,
-            None => return Some(mapped),
-        }
+    let (mapped, origin) = expansion.map_token_up(db, token.as_ref())?;
+    if origin != Origin::Call {
+        return None;
+    }
+    if let Some(info) = mapped.file_id.expansion_info(db) {
+        return ascend_call_token(db, &info, mapped);
     }
-    None
+    Some(mapped)
 }
 
 impl InFile<SyntaxToken> {
@@ -811,10 +809,10 @@ impl ExpandTo {
             MACRO_TYPE => ExpandTo::Type,
 
             ARG_LIST | TRY_EXPR | TUPLE_EXPR | PAREN_EXPR | ARRAY_EXPR | FOR_EXPR | PATH_EXPR
-            | CLOSURE_EXPR | BREAK_EXPR | RETURN_EXPR | MATCH_EXPR | MATCH_ARM | MATCH_GUARD
-            | RECORD_EXPR_FIELD | CALL_EXPR | INDEX_EXPR | METHOD_CALL_EXPR | FIELD_EXPR
-            | AWAIT_EXPR | CAST_EXPR | REF_EXPR | PREFIX_EXPR | RANGE_EXPR | BIN_EXPR
-            | LET_EXPR => ExpandTo::Expr,
+            | CLOSURE_EXPR | CONDITION | BREAK_EXPR | RETURN_EXPR | MATCH_EXPR | MATCH_ARM
+            | MATCH_GUARD | RECORD_EXPR_FIELD | CALL_EXPR | INDEX_EXPR | METHOD_CALL_EXPR
+            | FIELD_EXPR | AWAIT_EXPR | CAST_EXPR | REF_EXPR | PREFIX_EXPR | RANGE_EXPR
+            | BIN_EXPR => ExpandTo::Expr,
             LET_STMT => {
                 // FIXME: Handle LHS Pattern
                 ExpandTo::Expr
diff --git a/crates/hir_ty/src/display.rs b/crates/hir_ty/src/display.rs
index 0e75ddeabc..f02f4ac024 100644
--- a/crates/hir_ty/src/display.rs
+++ b/crates/hir_ty/src/display.rs
@@ -1094,32 +1094,20 @@ impl HirDisplay for TypeRef {
                 inner.hir_fmt(f)?;
                 write!(f, "]")?;
             }
-            TypeRef::Fn(parameters, is_varargs) => {
+            TypeRef::Fn(tys, is_varargs) => {
                 // FIXME: Function pointer qualifiers.
                 write!(f, "fn(")?;
-                if let Some(((_, return_type), function_parameters)) = parameters.split_last() {
-                    for index in 0..function_parameters.len() {
-                        let (param_name, param_type) = &function_parameters[index];
-                        if let Some(name) = param_name {
-                            write!(f, "{}: ", name)?;
-                        }
-
-                        param_type.hir_fmt(f)?;
-
-                        if index != function_parameters.len() - 1 {
-                            write!(f, ", ")?;
-                        }
-                    }
-                    if *is_varargs {
-                        write!(f, "{}...", if parameters.len() == 1 { "" } else { ", " })?;
-                    }
-                    write!(f, ")")?;
-                    match &return_type {
-                        TypeRef::Tuple(tup) if tup.is_empty() => {}
-                        _ => {
-                            write!(f, " -> ")?;
-                            return_type.hir_fmt(f)?;
-                        }
+                f.write_joined(&tys[..tys.len() - 1], ", ")?;
+                if *is_varargs {
+                    write!(f, "{}...", if tys.len() == 1 { "" } else { ", " })?;
+                }
+                write!(f, ")")?;
+                let ret_ty = tys.last().unwrap();
+                match ret_ty {
+                    TypeRef::Tuple(tup) if tup.is_empty() => {}
+                    _ => {
+                        write!(f, " -> ")?;
+                        ret_ty.hir_fmt(f)?;
                     }
                 }
             }
diff --git a/crates/hir_ty/src/infer/expr.rs b/crates/hir_ty/src/infer/expr.rs
index 13f64d6825..4f1bdee705 100644
--- a/crates/hir_ty/src/infer/expr.rs
+++ b/crates/hir_ty/src/infer/expr.rs
@@ -8,7 +8,10 @@ use std::{
 
 use chalk_ir::{cast::Cast, fold::Shift, Mutability, TyVariableKind};
 use hir_def::{
-    expr::{ArithOp, Array, BinaryOp, CmpOp, Expr, ExprId, Literal, Ordering, Statement, UnaryOp},
+    expr::{
+        ArithOp, Array, BinaryOp, CmpOp, Expr, ExprId, Literal, MatchGuard, Ordering, Statement,
+        UnaryOp,
+    },
     path::{GenericArg, GenericArgs},
     resolver::resolver_for_expr,
     FieldId, FunctionId, ItemContainerId, Lookup,
@@ -155,11 +158,6 @@ impl<'a> InferenceContext<'a> {
 
                 coerce.complete()
             }
-            &Expr::Let { pat, expr } => {
-                let input_ty = self.infer_expr(expr, &Expectation::none());
-                self.infer_pat(pat, &input_ty, BindingMode::default());
-                TyKind::Scalar(Scalar::Bool).intern(Interner)
-            }
             Expr::Block { statements, tail, label, id: _ } => {
                 let old_resolver = mem::replace(
                     &mut self.resolver,
@@ -380,11 +378,20 @@ impl<'a> InferenceContext<'a> {
                 for arm in arms.iter() {
                     self.diverges = Diverges::Maybe;
                     let _pat_ty = self.infer_pat(arm.pat, &input_ty, BindingMode::default());
-                    if let Some(guard_expr) = arm.guard {
-                        self.infer_expr(
-                            guard_expr,
-                            &Expectation::has_type(TyKind::Scalar(Scalar::Bool).intern(Interner)),
-                        );
+                    match arm.guard {
+                        Some(MatchGuard::If { expr: guard_expr }) => {
+                            self.infer_expr(
+                                guard_expr,
+                                &Expectation::has_type(
+                                    TyKind::Scalar(Scalar::Bool).intern(Interner),
+                                ),
+                            );
+                        }
+                        Some(MatchGuard::IfLet { expr, pat }) => {
+                            let input_ty = self.infer_expr(expr, &Expectation::none());
+                            let _pat_ty = self.infer_pat(pat, &input_ty, BindingMode::default());
+                        }
+                        _ => {}
                     }
 
                     let arm_ty = self.infer_expr_inner(arm.expr, &expected);
diff --git a/crates/hir_ty/src/infer/pat.rs b/crates/hir_ty/src/infer/pat.rs
index 9821815ca3..50fd2dd749 100644
--- a/crates/hir_ty/src/infer/pat.rs
+++ b/crates/hir_ty/src/infer/pat.rs
@@ -6,7 +6,6 @@ use chalk_ir::Mutability;
 use hir_def::{
     expr::{BindingAnnotation, Expr, Literal, Pat, PatId, RecordFieldPat},
     path::Path,
-    type_ref::ConstScalar,
 };
 use hir_expand::name::Name;
 
@@ -15,8 +14,7 @@ use crate::{
         Adjust, Adjustment, AutoBorrow, BindingMode, Expectation, InferenceContext, TypeMismatch,
     },
     lower::lower_to_chalk_mutability,
-    static_lifetime, ConcreteConst, ConstValue, Interner, Substitution, Ty, TyBuilder, TyExt,
-    TyKind,
+    static_lifetime, Interner, Substitution, Ty, TyBuilder, TyExt, TyKind,
 };
 
 impl<'a> InferenceContext<'a> {
@@ -234,28 +232,16 @@ impl<'a> InferenceContext<'a> {
                     self.infer_pat(pat_id, &elem_ty, default_bm);
                 }
 
-                if let &Some(slice_pat_id) = slice {
-                    let rest_pat_ty = match expected.kind(Interner) {
-                        TyKind::Array(_, length) => {
-                            let length = match length.data(Interner).value {
-                                ConstValue::Concrete(ConcreteConst {
-                                    interned: ConstScalar::Usize(length),
-                                }) => length.checked_sub((prefix.len() + suffix.len()) as u64),
-                                _ => None,
-                            };
-                            TyKind::Array(elem_ty.clone(), crate::consteval::usize_const(length))
-                        }
-                        _ => TyKind::Slice(elem_ty.clone()),
-                    }
-                    .intern(Interner);
-                    self.infer_pat(slice_pat_id, &rest_pat_ty, default_bm);
-                }
-
-                match expected.kind(Interner) {
+                let pat_ty = match expected.kind(Interner) {
                     TyKind::Array(_, const_) => TyKind::Array(elem_ty, const_.clone()),
                     _ => TyKind::Slice(elem_ty),
                 }
-                .intern(Interner)
+                .intern(Interner);
+                if let &Some(slice_pat_id) = slice {
+                    self.infer_pat(slice_pat_id, &pat_ty, default_bm);
+                }
+
+                pat_ty
             }
             Pat::Wild => expected.clone(),
             Pat::Range { start, end } => {
diff --git a/crates/hir_ty/src/lower.rs b/crates/hir_ty/src/lower.rs
index a140dd4057..55b1a67ea7 100644
--- a/crates/hir_ty/src/lower.rs
+++ b/crates/hir_ty/src/lower.rs
@@ -201,7 +201,7 @@ impl<'a> TyLoweringContext<'a> {
             TypeRef::Placeholder => TyKind::Error.intern(Interner),
             TypeRef::Fn(params, is_varargs) => {
                 let substs = self.with_shifted_in(DebruijnIndex::ONE, |ctx| {
-                    Substitution::from_iter(Interner, params.iter().map(|(_, tr)| ctx.lower_ty(tr)))
+                    Substitution::from_iter(Interner, params.iter().map(|tr| ctx.lower_ty(tr)))
                 });
                 TyKind::Function(FnPointer {
                     num_binders: 0, // FIXME lower `for<'a> fn()` correctly
diff --git a/crates/ide/src/annotations.rs b/crates/ide/src/annotations.rs
index a7c12c1636..986db75c61 100644
--- a/crates/ide/src/annotations.rs
+++ b/crates/ide/src/annotations.rs
@@ -65,10 +65,10 @@ pub(crate) fn annotations(
     visit_file_defs(&Semantics::new(db), file_id, &mut |def| {
         let range = match def {
             Definition::Const(konst) if config.annotate_references => {
-                konst.source(db).and_then(|node| name_range(&node, file_id))
+                konst.source(db).and_then(|node| name_range(db, node, file_id))
             }
             Definition::Trait(trait_) if config.annotate_references || config.annotate_impls => {
-                trait_.source(db).and_then(|node| name_range(&node, file_id))
+                trait_.source(db).and_then(|node| name_range(db, node, file_id))
             }
             Definition::Adt(adt) => match adt {
                 hir::Adt::Enum(enum_) => {
@@ -77,7 +77,7 @@ pub(crate) fn annotations(
                             .variants(db)
                             .into_iter()
                             .map(|variant| {
-                                variant.source(db).and_then(|node| name_range(&node, file_id))
+                                variant.source(db).and_then(|node| name_range(db, node, file_id))
                             })
                             .filter_map(std::convert::identity)
                             .for_each(|range| {
@@ -91,14 +91,14 @@ pub(crate) fn annotations(
                             })
                     }
                     if config.annotate_references || config.annotate_impls {
-                        enum_.source(db).and_then(|node| name_range(&node, file_id))
+                        enum_.source(db).and_then(|node| name_range(db, node, file_id))
                     } else {
                         None
                     }
                 }
                 _ => {
                     if config.annotate_references || config.annotate_impls {
-                        adt.source(db).and_then(|node| name_range(&node, file_id))
+                        adt.source(db).and_then(|node| name_range(db, node, file_id))
                     } else {
                         None
                     }
@@ -131,13 +131,17 @@ pub(crate) fn annotations(
             });
         }
 
-        fn name_range<T: HasName>(node: &InFile<T>, file_id: FileId) -> Option<TextRange> {
-            if node.file_id == file_id.into() {
-                node.value.name().map(|it| it.syntax().text_range())
-            } else {
-                // Node is outside the file we are adding annotations to (e.g. macros).
-                None
+        fn name_range<T: HasName>(
+            db: &RootDatabase,
+            node: InFile<T>,
+            source_file_id: FileId,
+        ) -> Option<TextRange> {
+            if let Some(InFile { file_id, value }) = node.original_ast_node(db) {
+                if file_id == source_file_id.into() {
+                    return value.name().map(|it| it.syntax().text_range());
+                }
             }
+            None
         }
     });
 
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs
index 0eba0b09ba..21ff480df4 100644
--- a/crates/ide/src/hover.rs
+++ b/crates/ide/src/hover.rs
@@ -94,10 +94,11 @@ pub(crate) fn hover(
     let sema = &hir::Semantics::new(db);
     let file = sema.parse(file_id).syntax().clone();
 
-    if !range.is_empty() {
+    let offset = if !range.is_empty() {
         return hover_ranged(&file, range, sema, config);
-    }
-    let offset = range.start();
+    } else {
+        range.start()
+    };
 
     let original_token = pick_best_token(file.token_at_offset(offset), |kind| match kind {
         IDENT | INT_NUMBER | LIFETIME_IDENT | T![self] | T![super] | T![crate] => 3,
@@ -117,11 +118,10 @@ pub(crate) fn hover(
     let descended = sema.descend_into_macros(original_token.clone());
 
     // FIXME: Definition should include known lints and the like instead of having this special case here
-    let hovered_lint = descended.iter().find_map(|token| {
+    if let Some(res) = descended.iter().find_map(|token| {
         let attr = token.ancestors().find_map(ast::Attr::cast)?;
         render::try_for_lint(&attr, token)
-    });
-    if let Some(res) = hovered_lint {
+    }) {
         return Some(RangeInfo::new(original_token.text_range(), res));
     }
 
@@ -143,9 +143,7 @@ pub(crate) fn hover(
 
     if result.is_none() {
         // fallbacks, show keywords or types
-
-        let res = descended.iter().find_map(|token| render::keyword(sema, config, &token));
-        if let Some(res) = res {
+        if let Some(res) = render::keyword(sema, config, &original_token) {
             return Some(RangeInfo::new(original_token.text_range(), res));
         }
         let res = descended
@@ -201,7 +199,6 @@ fn hover_ranged(
     sema: &Semantics<RootDatabase>,
     config: &HoverConfig,
 ) -> Option<RangeInfo<HoverResult>> {
-    // FIXME: make this work in attributes
     let expr_or_pat = file.covering_element(range).ancestors().find_map(|it| {
         match_ast! {
             match it {
diff --git a/crates/ide/src/hover/render.rs b/crates/ide/src/hover/render.rs
index ce9055c090..d29833a65b 100644
--- a/crates/ide/src/hover/render.rs
+++ b/crates/ide/src/hover/render.rs
@@ -18,7 +18,7 @@ use syntax::{
     algo, ast,
     display::{fn_as_proc_macro_label, macro_label},
     match_ast, AstNode, Direction,
-    SyntaxKind::{LET_EXPR, LET_STMT},
+    SyntaxKind::{CONDITION, LET_STMT},
     SyntaxToken, T,
 };
 
@@ -239,18 +239,22 @@ pub(super) fn keyword(
     }
     let parent = token.parent()?;
     let famous_defs = FamousDefs(sema, sema.scope(&parent).krate());
-
-    let KeywordHint { description, keyword_mod, actions } = keyword_hints(sema, token, parent);
-
+    let keyword_mod = if token.kind() == T![fn] && ast::FnPtrType::cast(parent).is_some() {
+        // treat fn keyword inside function pointer type as primitive
+        format!("prim_{}", token.text())
+    } else {
+        // std exposes {}_keyword modules with docstrings on the root to document keywords
+        format!("{}_keyword", token.text())
+    };
     let doc_owner = find_std_module(&famous_defs, &keyword_mod)?;
     let docs = doc_owner.attrs(sema.db).docs()?;
     let markup = process_markup(
         sema.db,
         Definition::Module(doc_owner),
-        &markup(Some(docs.into()), description, None)?,
+        &markup(Some(docs.into()), token.text().into(), None)?,
         config,
     );
-    Some(HoverResult { markup, actions })
+    Some(HoverResult { markup, actions: Default::default() })
 }
 
 pub(super) fn try_for_lint(attr: &ast::Attr, token: &SyntaxToken) -> Option<HoverResult> {
@@ -484,7 +488,7 @@ fn local(db: &RootDatabase, it: hir::Local) -> Option<Markup> {
             let let_kw = if ident
                 .syntax()
                 .parent()
-                .map_or(false, |p| p.kind() == LET_STMT || p.kind() == LET_EXPR)
+                .map_or(false, |p| p.kind() == LET_STMT || p.kind() == CONDITION)
             {
                 "let "
             } else {
@@ -496,65 +500,3 @@ fn local(db: &RootDatabase, it: hir::Local) -> Option<Markup> {
     };
     markup(None, desc, None)
 }
-
-struct KeywordHint {
-    description: String,
-    keyword_mod: String,
-    actions: Vec<HoverAction>,
-}
-
-impl KeywordHint {
-    fn new(description: String, keyword_mod: String) -> Self {
-        Self { description, keyword_mod, actions: Vec::default() }
-    }
-}
-
-fn keyword_hints(
-    sema: &Semantics<RootDatabase>,
-    token: &SyntaxToken,
-    parent: syntax::SyntaxNode,
-) -> KeywordHint {
-    match token.kind() {
-        T![await] | T![loop] | T![match] | T![unsafe] | T![as] | T![try] | T![if] | T![else] => {
-            let keyword_mod = format!("{}_keyword", token.text());
-
-            match ast::Expr::cast(parent).and_then(|site| sema.type_of_expr(&site)) {
-                // ignore the unit type ()
-                Some(ty) if !ty.adjusted.as_ref().unwrap_or(&ty.original).is_unit() => {
-                    let mut targets: Vec<hir::ModuleDef> = Vec::new();
-                    let mut push_new_def = |item: hir::ModuleDef| {
-                        if !targets.contains(&item) {
-                            targets.push(item);
-                        }
-                    };
-                    walk_and_push_ty(sema.db, &ty.original, &mut push_new_def);
-
-                    let ty = ty.adjusted();
-                    let description = format!("{}: {}", token.text(), ty.display(sema.db));
-
-                    KeywordHint {
-                        description,
-                        keyword_mod,
-                        actions: vec![HoverAction::goto_type_from_targets(sema.db, targets)],
-                    }
-                }
-                _ => KeywordHint {
-                    description: token.text().to_string(),
-                    keyword_mod,
-                    actions: Vec::new(),
-                },
-            }
-        }
-
-        T![fn] => {
-            let module = match ast::FnPtrType::cast(parent) {
-                // treat fn keyword inside function pointer type as primitive
-                Some(_) => format!("prim_{}", token.text()),
-                None => format!("{}_keyword", token.text()),
-            };
-            KeywordHint::new(token.text().to_string(), module)
-        }
-
-        _ => KeywordHint::new(token.text().to_string(), format!("{}_keyword", token.text())),
-    }
-}
diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs
index 2ca756cbe0..cc304cb10a 100644
--- a/crates/ide/src/inlay_hints.rs
+++ b/crates/ide/src/inlay_hints.rs
@@ -243,7 +243,7 @@ fn is_named_constructor(
     let expr = match_ast! {
         match let_node {
             ast::LetStmt(it) => it.initializer(),
-            ast::LetExpr(it) => it.expr(),
+            ast::Condition(it) => it.expr(),
             _ => None,
         }
     }?;
@@ -372,10 +372,15 @@ fn should_not_display_type_hint(
             match node {
                 ast::LetStmt(it) => return it.ty().is_some(),
                 ast::Param(it) => return it.ty().is_some(),
-                ast::MatchArm(_) => return pat_is_enum_variant(db, bind_pat, pat_ty),
-                ast::LetExpr(_) => return pat_is_enum_variant(db, bind_pat, pat_ty),
-                ast::IfExpr(_) => return false,
-                ast::WhileExpr(_) => return false,
+                ast::MatchArm(_it) => return pat_is_enum_variant(db, bind_pat, pat_ty),
+                ast::IfExpr(it) => {
+                    return it.condition().and_then(|condition| condition.pat()).is_some()
+                        && pat_is_enum_variant(db, bind_pat, pat_ty);
+                },
+                ast::WhileExpr(it) => {
+                    return it.condition().and_then(|condition| condition.pat()).is_some()
+                        && pat_is_enum_variant(db, bind_pat, pat_ty);
+                },
                 ast::ForExpr(it) => {
                     // We *should* display hint only if user provided "in {expr}" and we know the type of expr (and it's not unit).
                     // Type of expr should be iterable.
diff --git a/crates/ide/src/rename.rs b/crates/ide/src/rename.rs
index daba317e42..f9f9c0832c 100644
--- a/crates/ide/src/rename.rs
+++ b/crates/ide/src/rename.rs
@@ -1108,59 +1108,6 @@ pub mod foo$0;
         );
     }
 
-    #[test]
-    fn test_rename_mod_ref_by_super() {
-        check(
-            "baz",
-            r#"
-        mod $0foo {
-        struct X;
-
-        mod bar {
-            use super::X;
-        }
-    }
-            "#,
-            r#"
-        mod baz {
-        struct X;
-
-        mod bar {
-            use super::X;
-        }
-    }
-            "#,
-        )
-    }
-
-    #[test]
-    fn test_rename_mod_in_macro() {
-        check(
-            "bar",
-            r#"
-//- /foo.rs
-
-//- /lib.rs
-macro_rules! submodule {
-    ($name:ident) => {
-        mod $name;
-    };
-}
-
-submodule!($0foo);
-"#,
-            r#"
-macro_rules! submodule {
-    ($name:ident) => {
-        mod $name;
-    };
-}
-
-submodule!(bar);
-"#,
-        )
-    }
-
     #[test]
     fn test_enum_variant_from_module_1() {
         cov_mark::check!(rename_non_local);
diff --git a/crates/ide_assists/src/handlers/convert_bool_then.rs b/crates/ide_assists/src/handlers/convert_bool_then.rs
index 274718e6ea..b8c55eb852 100644
--- a/crates/ide_assists/src/handlers/convert_bool_then.rs
+++ b/crates/ide_assists/src/handlers/convert_bool_then.rs
@@ -2,7 +2,7 @@ use hir::{known, AsAssocItem, Semantics};
 use ide_db::{
     helpers::{
         for_each_tail_expr,
-        node_ext::{block_as_lone_tail, is_pattern_cond, preorder_expr},
+        node_ext::{block_as_lone_tail, preorder_expr},
         FamousDefs,
     },
     RootDatabase,
@@ -45,7 +45,8 @@ pub(crate) fn convert_if_to_bool_then(acc: &mut Assists, ctx: &AssistContext) ->
         return None;
     }
 
-    let cond = expr.condition().filter(|cond| !is_pattern_cond(cond.clone()))?;
+    let cond = expr.condition().filter(|cond| !cond.is_pattern_cond())?;
+    let cond = cond.expr()?;
     let then = expr.then_branch()?;
     let else_ = match expr.else_branch()? {
         ast::ElseBranch::Block(b) => b,
@@ -208,7 +209,7 @@ pub(crate) fn convert_bool_then_to_if(acc: &mut Assists, ctx: &AssistContext) ->
                 _ => receiver,
             };
             let if_expr = make::expr_if(
-                cond,
+                make::condition(cond, None),
                 closure_body.reset_indent(),
                 Some(ast::ElseBranch::Block(make::block_expr(None, Some(none_path)))),
             )
diff --git a/crates/ide_assists/src/handlers/convert_to_guarded_return.rs b/crates/ide_assists/src/handlers/convert_to_guarded_return.rs
index 193d1cdfb2..884905a918 100644
--- a/crates/ide_assists/src/handlers/convert_to_guarded_return.rs
+++ b/crates/ide_assists/src/handlers/convert_to_guarded_return.rs
@@ -1,6 +1,5 @@
 use std::iter::once;
 
-use ide_db::helpers::node_ext::{is_pattern_cond, single_let};
 use syntax::{
     ast::{
         self,
@@ -49,28 +48,25 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext)
     let cond = if_expr.condition()?;
 
     // Check if there is an IfLet that we can handle.
-    let (if_let_pat, cond_expr) = if is_pattern_cond(cond.clone()) {
-        let let_ = single_let(cond)?;
-        match let_.pat() {
-            Some(ast::Pat::TupleStructPat(pat)) if pat.fields().count() == 1 => {
-                let path = pat.path()?;
-                if path.qualifier().is_some() {
-                    return None;
-                }
-
-                let bound_ident = pat.fields().next().unwrap();
-                if !ast::IdentPat::can_cast(bound_ident.syntax().kind()) {
-                    return None;
-                }
+    let if_let_pat = match cond.pat() {
+        None => None, // No IfLet, supported.
+        Some(ast::Pat::TupleStructPat(pat)) if pat.fields().count() == 1 => {
+            let path = pat.path()?;
+            if path.qualifier().is_some() {
+                return None;
+            }
 
-                (Some((path, bound_ident)), let_.expr()?)
+            let bound_ident = pat.fields().next().unwrap();
+            if !ast::IdentPat::can_cast(bound_ident.syntax().kind()) {
+                return None;
             }
-            _ => return None, // Unsupported IfLet.
+
+            Some((path, bound_ident))
         }
-    } else {
-        (None, cond)
+        Some(_) => return None, // Unsupported IfLet.
     };
 
+    let cond_expr = cond.expr()?;
     let then_block = if_expr.then_branch()?;
     let then_block = then_block.stmt_list()?;
 
@@ -123,7 +119,8 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext)
                         let then_branch =
                             make::block_expr(once(make::expr_stmt(early_expression).into()), None);
                         let cond = invert_boolean_expression(cond_expr);
-                        make::expr_if(cond, then_branch, None).indent(if_indent_level)
+                        make::expr_if(make::condition(cond, None), then_branch, None)
+                            .indent(if_indent_level)
                     };
                     new_expr.syntax().clone_for_update()
                 }
diff --git a/crates/ide_assists/src/handlers/convert_while_to_loop.rs b/crates/ide_assists/src/handlers/convert_while_to_loop.rs
index 0fa2dcfbde..2bc64e77a3 100644
--- a/crates/ide_assists/src/handlers/convert_while_to_loop.rs
+++ b/crates/ide_assists/src/handlers/convert_while_to_loop.rs
@@ -1,6 +1,5 @@
 use std::iter::once;
 
-use ide_db::helpers::node_ext::is_pattern_cond;
 use syntax::{
     ast::{
         self,
@@ -43,6 +42,7 @@ pub(crate) fn convert_while_to_loop(acc: &mut Assists, ctx: &AssistContext) -> O
     let while_expr = while_kw.parent().and_then(ast::WhileExpr::cast)?;
     let while_body = while_expr.loop_body()?;
     let while_cond = while_expr.condition()?;
+    let while_cond_expr = while_cond.expr()?;
 
     let target = while_expr.syntax().text_range();
     acc.add(
@@ -55,15 +55,19 @@ pub(crate) fn convert_while_to_loop(acc: &mut Assists, ctx: &AssistContext) -> O
             let break_block =
                 make::block_expr(once(make::expr_stmt(make::expr_break(None)).into()), None)
                     .indent(while_indent_level);
-            let block_expr = if is_pattern_cond(while_cond.clone()) {
-                let if_expr = make::expr_if(while_cond, while_body, Some(break_block.into()));
-                let stmts = once(make::expr_stmt(if_expr).into());
-                make::block_expr(stmts, None)
-            } else {
-                let if_cond = invert_boolean_expression(while_cond);
-                let if_expr = make::expr_if(if_cond, break_block, None);
-                let stmts = once(make::expr_stmt(if_expr).into()).chain(while_body.statements());
-                make::block_expr(stmts, while_body.tail_expr())
+            let block_expr = match while_cond.pat() {
+                Some(_) => {
+                    let if_expr = make::expr_if(while_cond, while_body, Some(break_block.into()));
+                    let stmts = once(make::expr_stmt(if_expr).into());
+                    make::block_expr(stmts, None)
+                }
+                None => {
+                    let if_cond = make::condition(invert_boolean_expression(while_cond_expr), None);
+                    let if_expr = make::expr_if(if_cond, break_block, None);
+                    let stmts =
+                        once(make::expr_stmt(if_expr).into()).chain(while_body.statements());
+                    make::block_expr(stmts, while_body.tail_expr())
+                }
             };
 
             let replacement = make::expr_loop(block_expr.indent(while_indent_level));
diff --git a/crates/ide_assists/src/handlers/extract_function.rs b/crates/ide_assists/src/handlers/extract_function.rs
index 21cfc76ac9..877c5b0cef 100644
--- a/crates/ide_assists/src/handlers/extract_function.rs
+++ b/crates/ide_assists/src/handlers/extract_function.rs
@@ -1219,26 +1219,28 @@ impl FlowHandler {
                 let stmt = make::expr_stmt(action);
                 let block = make::block_expr(iter::once(stmt.into()), None);
                 let controlflow_break_path = make::path_from_text("ControlFlow::Break");
-                let condition = make::expr_let(
-                    make::tuple_struct_pat(
-                        controlflow_break_path,
-                        iter::once(make::wildcard_pat().into()),
-                    )
-                    .into(),
+                let condition = make::condition(
                     call_expr,
+                    Some(
+                        make::tuple_struct_pat(
+                            controlflow_break_path,
+                            iter::once(make::wildcard_pat().into()),
+                        )
+                        .into(),
+                    ),
                 );
-                make::expr_if(condition.into(), block, None)
+                make::expr_if(condition, block, None)
             }
             FlowHandler::IfOption { action } => {
                 let path = make::ext::ident_path("Some");
                 let value_pat = make::ext::simple_ident_pat(make::name("value"));
                 let pattern = make::tuple_struct_pat(path, iter::once(value_pat.into()));
-                let cond = make::expr_let(pattern.into(), call_expr);
+                let cond = make::condition(call_expr, Some(pattern.into()));
                 let value = make::expr_path(make::ext::ident_path("value"));
                 let action_expr = action.make_result_handler(Some(value));
                 let action_stmt = make::expr_stmt(action_expr);
                 let then = make::block_expr(iter::once(action_stmt.into()), None);
-                make::expr_if(cond.into(), then, None)
+                make::expr_if(cond, then, None)
             }
             FlowHandler::MatchOption { none } => {
                 let some_name = "value";
diff --git a/crates/ide_assists/src/handlers/invert_if.rs b/crates/ide_assists/src/handlers/invert_if.rs
index 46f11f4af3..20f6b0c54c 100644
--- a/crates/ide_assists/src/handlers/invert_if.rs
+++ b/crates/ide_assists/src/handlers/invert_if.rs
@@ -1,4 +1,3 @@
-use ide_db::helpers::node_ext::is_pattern_cond;
 use syntax::{
     ast::{self, AstNode},
     T,
@@ -35,12 +34,12 @@ pub(crate) fn invert_if(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
         return None;
     }
 
-    let cond = expr.condition()?;
     // This assist should not apply for if-let.
-    if is_pattern_cond(cond.clone()) {
+    if expr.condition()?.is_pattern_cond() {
         return None;
     }
 
+    let cond = expr.condition()?.expr()?;
     let then_node = expr.then_branch()?.syntax().clone();
     let else_block = match expr.else_branch()? {
         ast::ElseBranch::Block(it) => it,
diff --git a/crates/ide_assists/src/handlers/merge_imports.rs b/crates/ide_assists/src/handlers/merge_imports.rs
index 68aa741fac..94400be7a5 100644
--- a/crates/ide_assists/src/handlers/merge_imports.rs
+++ b/crates/ide_assists/src/handlers/merge_imports.rs
@@ -252,89 +252,6 @@ use std::{fmt::{Display, Debug}};
         );
     }
 
-    #[test]
-    fn test_merge_with_nested_self_item() {
-        check_assist(
-            merge_imports,
-            r"
-use std$0::{fmt::{Write, Display}};
-use std::{fmt::{self, Debug}};
-",
-            r"
-use std::{fmt::{Write, Display, self, Debug}};
-",
-        );
-    }
-
-    #[test]
-    fn test_merge_with_nested_self_item2() {
-        check_assist(
-            merge_imports,
-            r"
-use std$0::{fmt::{self, Debug}};
-use std::{fmt::{Write, Display}};
-",
-            r"
-use std::{fmt::{self, Debug, Write, Display}};
-",
-        );
-    }
-
-    #[test]
-    fn test_merge_self_with_nested_self_item() {
-        check_assist(
-            merge_imports,
-            r"
-use std::{fmt$0::{self, Debug}, fmt::{Write, Display}};
-",
-            r"
-use std::{fmt::{self, Debug, Write, Display}};
-",
-        );
-    }
-
-    #[test]
-    fn test_merge_nested_self_and_empty() {
-        check_assist(
-            merge_imports,
-            r"
-use foo::$0{bar::{self}};
-use foo::{bar};
-",
-            r"
-use foo::{bar::{self}};
-",
-        )
-    }
-
-    #[test]
-    fn test_merge_nested_empty_and_self() {
-        check_assist(
-            merge_imports,
-            r"
-use foo::$0{bar};
-use foo::{bar::{self}};
-",
-            r"
-use foo::{bar::{self}};
-",
-        )
-    }
-
-    #[test]
-    fn test_merge_nested_list_self_and_glob() {
-        check_assist(
-            merge_imports,
-            r"
-use std$0::{fmt::*};
-use std::{fmt::{self, Display}};
-",
-            r"
-use std::{fmt::{self, *, Display}};
-",
-        )
-    }
-
     #[test]
     fn test_merge_single_wildcard_diff_prefixes() {
         check_assist(
diff --git a/crates/ide_assists/src/handlers/move_guard.rs b/crates/ide_assists/src/handlers/move_guard.rs
index 5c05cb921d..366f308f6e 100644
--- a/crates/ide_assists/src/handlers/move_guard.rs
+++ b/crates/ide_assists/src/handlers/move_guard.rs
@@ -1,5 +1,8 @@
 use syntax::{
-    ast::{edit::AstNodeEdit, make, AstNode, BlockExpr, ElseBranch, Expr, IfExpr, MatchArm, Pat},
+    ast::{
+        edit::AstNodeEdit, make, AstNode, BlockExpr, Condition, ElseBranch, Expr, IfExpr, MatchArm,
+        Pat,
+    },
     SyntaxKind::WHITESPACE,
 };
 
@@ -41,11 +44,18 @@ pub(crate) fn move_guard_to_arm_body(acc: &mut Assists, ctx: &AssistContext) ->
     }
     let space_before_guard = guard.syntax().prev_sibling_or_token();
 
-    let guard_condition = guard.condition()?;
+    // FIXME: support `if let` guards too
+    if guard.let_token().is_some() {
+        return None;
+    }
+    let guard_condition = guard.expr()?;
     let arm_expr = match_arm.expr()?;
-    let if_expr =
-        make::expr_if(guard_condition, make::block_expr(None, Some(arm_expr.clone())), None)
-            .indent(arm_expr.indent_level());
+    let if_expr = make::expr_if(
+        make::condition(guard_condition, None),
+        make::block_expr(None, Some(arm_expr.clone())),
+        None,
+    )
+    .indent(arm_expr.indent_level());
 
     let target = guard.syntax().text_range();
     acc.add(
@@ -183,13 +193,17 @@ pub(crate) fn move_arm_cond_to_match_guard(acc: &mut Assists, ctx: &AssistContex
     )
 }
 
-// Parses an if-else-if chain to get the conditions and the then branches until we encounter an else
+// Parses an if-else-if chain to get the conditons and the then branches until we encounter an else
 // branch or the end.
-fn parse_if_chain(if_expr: IfExpr) -> Option<(Vec<(Expr, BlockExpr)>, Option<BlockExpr>)> {
+fn parse_if_chain(if_expr: IfExpr) -> Option<(Vec<(Condition, BlockExpr)>, Option<BlockExpr>)> {
     let mut conds_blocks = Vec::new();
     let mut curr_if = if_expr;
     let tail = loop {
         let cond = curr_if.condition()?;
+        // Not support moving if let to arm guard
+        if cond.is_pattern_cond() {
+            return None;
+        }
         conds_blocks.push((cond, curr_if.then_branch()?));
         match curr_if.else_branch() {
             Some(ElseBranch::IfExpr(e)) => {
@@ -266,31 +280,6 @@ fn main() {
         );
     }
 
-    #[test]
-    fn move_let_guard_to_arm_body_works() {
-        check_assist(
-            move_guard_to_arm_body,
-            r#"
-fn main() {
-    match 92 {
-        x $0if (let 1 = x) => false,
-        _ => true
-    }
-}
-"#,
-            r#"
-fn main() {
-    match 92 {
-        x => if (let 1 = x) {
-            false
-        },
-        _ => true
-    }
-}
-"#,
-        );
-    }
-
     #[test]
     fn move_guard_to_arm_body_works_complex_match() {
         check_assist(
@@ -451,21 +440,13 @@ fn main() {
     }
 
     #[test]
-    fn move_arm_cond_to_match_guard_if_let_works() {
-        check_assist(
+    fn move_arm_cond_to_match_guard_if_let_not_works() {
+        check_assist_not_applicable(
             move_arm_cond_to_match_guard,
             r#"
 fn main() {
     match 92 {
-        x => if let 62 = x && true { $0false },
-        _ => true
-    }
-}
-"#,
-            r#"
-fn main() {
-    match 92 {
-        x if let 62 = x && true => false,
+        x => if let 62 = x { $0false },
         _ => true
     }
 }
@@ -917,7 +898,7 @@ fn main() {
 
     #[test]
     fn move_arm_cond_to_match_guard_elseif_iflet() {
-        check_assist(
+        check_assist_not_applicable(
             move_arm_cond_to_match_guard,
             r#"
 fn main() {
@@ -934,21 +915,9 @@ fn main() {
             4
         },
     }
-}"#,
-            r#"
-fn main() {
-    match 92 {
-        3 => 0,
-        x if x > 10 => 1,
-        x if x > 5 => 2,
-        x if let 4 = 4 => {
-            42;
-            3
-        }
-        x => 4,
-    }
-}"#,
-        );
+}
+"#,
+        )
     }
 
     #[test]
diff --git a/crates/ide_assists/src/handlers/replace_if_let_with_match.rs b/crates/ide_assists/src/handlers/replace_if_let_with_match.rs
index b594c64c41..7790934792 100644
--- a/crates/ide_assists/src/handlers/replace_if_let_with_match.rs
+++ b/crates/ide_assists/src/handlers/replace_if_let_with_match.rs
@@ -1,12 +1,7 @@
 use std::iter::{self, successors};
 
 use either::Either;
-use ide_db::{
-    defs::NameClass,
-    helpers::node_ext::{is_pattern_cond, single_let},
-    ty_filter::TryEnum,
-    RootDatabase,
-};
+use ide_db::{defs::NameClass, ty_filter::TryEnum, RootDatabase};
 use syntax::{
     ast::{
         self,
@@ -65,22 +60,15 @@ pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext)
             None
         }
     });
-    let scrutinee_to_be_expr = if_expr.condition()?;
-    let scrutinee_to_be_expr = match single_let(scrutinee_to_be_expr.clone()) {
-        Some(cond) => cond.expr()?,
-        None => scrutinee_to_be_expr,
-    };
+    let scrutinee_to_be_expr = if_expr.condition()?.expr()?;
 
     let mut pat_seen = false;
     let mut cond_bodies = Vec::new();
     for if_expr in if_exprs {
         let cond = if_expr.condition()?;
-        let cond = match single_let(cond.clone()) {
-            Some(let_) => {
-                let pat = let_.pat()?;
-                let expr = let_.expr()?;
-                // FIXME: If one `let` is wrapped in parentheses and the second is not,
-                // we'll exit here.
+        let expr = cond.expr()?;
+        let cond = match cond.pat() {
+            Some(pat) => {
                 if scrutinee_to_be_expr.syntax().text() != expr.syntax().text() {
                     // Only if all condition expressions are equal we can merge them into a match
                     return None;
@@ -88,9 +76,7 @@ pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext)
                 pat_seen = true;
                 Either::Left(pat)
             }
-            // Multiple `let`, unsupported.
-            None if is_pattern_cond(cond.clone()) => return None,
-            None => Either::Right(cond),
+            None => Either::Right(expr),
         };
         let body = if_expr.then_branch()?;
         cond_bodies.push((cond, body));
@@ -231,11 +217,11 @@ pub(crate) fn replace_match_with_if_let(acc: &mut Assists, ctx: &AssistContext)
                 }
             }
 
-            let condition = make::expr_let(if_let_pat, scrutinee);
+            let condition = make::condition(scrutinee, Some(if_let_pat));
             let then_block = make_block_expr(then_expr.reset_indent());
             let else_expr = if is_empty_expr(&else_expr) { None } else { Some(else_expr) };
             let if_let_expr = make::expr_if(
-                condition.into(),
+                condition,
                 then_block,
                 else_expr.map(make_block_expr).map(ast::ElseBranch::Block),
             )
@@ -386,18 +372,6 @@ impl VariantData {
         )
     }
 
-    #[test]
-    fn test_if_let_with_match_let_chain() {
-        check_assist_not_applicable(
-            replace_if_let_with_match,
-            r#"
-fn main() {
-    if $0let true = true && let Some(1) = None {}
-}
-"#,
-        )
-    }
-
     #[test]
     fn test_if_let_with_match_basic() {
         check_assist(
diff --git a/crates/ide_assists/src/handlers/replace_let_with_if_let.rs b/crates/ide_assists/src/handlers/replace_let_with_if_let.rs
index a5fa8a110d..1062cc3953 100644
--- a/crates/ide_assists/src/handlers/replace_let_with_if_let.rs
+++ b/crates/ide_assists/src/handlers/replace_let_with_if_let.rs
@@ -62,7 +62,7 @@ pub(crate) fn replace_let_with_if_let(acc: &mut Assists, ctx: &AssistContext) ->
 
             let block =
                 make::ext::empty_block_expr().indent(IndentLevel::from_node(let_stmt.syntax()));
-            let if_ = make::expr_if(make::expr_let(pat, init).into(), block, None);
+            let if_ = make::expr_if(make::condition(init, Some(pat)), block, None);
             let stmt = make::expr_stmt(if_);
 
             edit.replace_ast(ast::Stmt::from(let_stmt), ast::Stmt::from(stmt));
diff --git a/crates/ide_completion/src/completions/record.rs b/crates/ide_completion/src/completions/record.rs
index 78d0623106..13b4735619 100644
--- a/crates/ide_completion/src/completions/record.rs
+++ b/crates/ide_completion/src/completions/record.rs
@@ -63,12 +63,11 @@ pub(crate) fn complete_record_literal(
     }
 
     if let hir::Adt::Struct(strukt) = ctx.expected_type.as_ref()?.as_adt()? {
-        if ctx.path_qual().is_none() {
-            let module = if let Some(module) = ctx.module { module } else { strukt.module(ctx.db) };
-            let path = module.find_use_path(ctx.db, hir::ModuleDef::from(strukt));
+        let module = if let Some(module) = ctx.module { module } else { strukt.module(ctx.db) };
 
-            acc.add_struct_literal(ctx, strukt, path, None);
-        }
+        let path = module.find_use_path(ctx.db, hir::ModuleDef::from(strukt));
+
+        acc.add_struct_literal(ctx, strukt, path, None);
     }
 
     Some(())
diff --git a/crates/ide_completion/src/context.rs b/crates/ide_completion/src/context.rs
index e986c28b14..d711215491 100644
--- a/crates/ide_completion/src/context.rs
+++ b/crates/ide_completion/src/context.rs
@@ -575,14 +575,6 @@ impl<'a> CompletionContext<'a> {
 
                         (ty, name)
                     },
-                    ast::LetExpr(it) => {
-                        cov_mark::hit!(expected_type_if_let_without_leading_char);
-                        let ty = it.pat()
-                            .and_then(|pat| self.sema.type_of_pat(&pat))
-                            .or_else(|| it.expr().and_then(|it| self.sema.type_of_expr(&it)))
-                            .map(TypeInfo::original);
-                        (ty, None)
-                    },
                     ast::ArgList(_) => {
                         cov_mark::hit!(expected_type_fn_param);
                         ActiveParameter::at_token(
@@ -649,7 +641,9 @@ impl<'a> CompletionContext<'a> {
                         (ty, None)
                     },
                     ast::IfExpr(it) => {
+                        cov_mark::hit!(expected_type_if_let_without_leading_char);
                         let ty = it.condition()
+                            .and_then(|cond| cond.expr())
                             .and_then(|e| self.sema.type_of_expr(&e))
                             .map(TypeInfo::original);
                         (ty, None)
@@ -945,7 +939,7 @@ fn pattern_context_for(original_file: &SyntaxNode, pat: ast::Pat) -> PatternCont
                         return (PatternRefutability::Irrefutable, has_type_ascription)
                     },
                     ast::MatchArm(_) => PatternRefutability::Refutable,
-                    ast::LetExpr(_) => PatternRefutability::Refutable,
+                    ast::Condition(_) => PatternRefutability::Refutable,
                     ast::ForExpr(_) => PatternRefutability::Irrefutable,
                     _ => PatternRefutability::Irrefutable,
                 }
diff --git a/crates/ide_completion/src/render/function.rs b/crates/ide_completion/src/render/function.rs
index c1908ba0c8..20c7fe657c 100644
--- a/crates/ide_completion/src/render/function.rs
+++ b/crates/ide_completion/src/render/function.rs
@@ -110,19 +110,7 @@ fn render(
 
 fn detail(db: &dyn HirDatabase, func: hir::Function) -> String {
     let ret_ty = func.ret_type(db);
-    let mut detail = String::new();
-
-    if func.is_const(db) {
-        format_to!(detail, "const ");
-    }
-    if func.is_async(db) {
-        format_to!(detail, "async ");
-    }
-    if func.is_unsafe(db) {
-        format_to!(detail, "unsafe ");
-    }
-
-    format_to!(detail, "fn({})", params_display(db, func));
+    let mut detail = format!("fn({})", params_display(db, func));
     if !ret_ty.is_unit() {
         format_to!(detail, " -> {}", ret_ty.display(db));
     }
diff --git a/crates/ide_db/src/helpers.rs b/crates/ide_db/src/helpers.rs
index c355016c5d..2c1545e1a9 100644
--- a/crates/ide_db/src/helpers.rs
+++ b/crates/ide_db/src/helpers.rs
@@ -229,7 +229,6 @@ pub fn for_each_tail_expr(expr: &ast::Expr, cb: &mut dyn FnMut(&ast::Expr)) {
         | ast::Expr::TryExpr(_)
         | ast::Expr::TupleExpr(_)
         | ast::Expr::WhileExpr(_)
-        | ast::Expr::LetExpr(_)
         | ast::Expr::YieldExpr(_) => cb(expr),
     }
 }
diff --git a/crates/ide_db/src/helpers/merge_imports.rs b/crates/ide_db/src/helpers/merge_imports.rs
index dfaf578cb1..8581a83436 100644
--- a/crates/ide_db/src/helpers/merge_imports.rs
+++ b/crates/ide_db/src/helpers/merge_imports.rs
@@ -115,13 +115,11 @@ fn recursive_merge(lhs: &ast::UseTree, rhs: &ast::UseTree, merge: MergeBehavior)
                     let tree_contains_self = |tree: &ast::UseTree| {
                         tree.use_tree_list()
                             .map(|tree_list| tree_list.use_trees().any(|it| tree_is_self(&it)))
-                            // Glob imports aren't part of the use-tree lists,
-                            // so they need to be handled explicitly
-                            .or_else(|| tree.star_token().map(|_| false))
+                            .unwrap_or(false)
                     };
                     match (tree_contains_self(lhs_t), tree_contains_self(&rhs_t)) {
-                        (Some(true), None) => continue,
-                        (None, Some(true)) => {
+                        (true, false) => continue,
+                        (false, true) => {
                             ted::replace(lhs_t.syntax(), rhs_t.syntax());
                             *lhs_t = rhs_t;
                             continue;
diff --git a/crates/ide_db/src/helpers/node_ext.rs b/crates/ide_db/src/helpers/node_ext.rs
index 5df3ed1366..82178ed749 100644
--- a/crates/ide_db/src/helpers/node_ext.rs
+++ b/crates/ide_db/src/helpers/node_ext.rs
@@ -216,29 +216,3 @@ pub fn vis_eq(this: &ast::Visibility, other: &ast::Visibility) -> bool {
         _ => false,
     }
 }
-
-/// Returns the `let` only if there is exactly one (that is, `let pat = expr`
-/// or `((let pat = expr))`, but not `let pat = expr && expr` or `non_let_expr`).
-pub fn single_let(expr: ast::Expr) -> Option<ast::LetExpr> {
-    match expr {
-        ast::Expr::ParenExpr(expr) => expr.expr().and_then(single_let),
-        ast::Expr::LetExpr(expr) => Some(expr),
-        _ => None,
-    }
-}
-
-pub fn is_pattern_cond(expr: ast::Expr) -> bool {
-    match expr {
-        ast::Expr::BinExpr(expr)
-            if expr.op_kind() == Some(ast::BinaryOp::LogicOp(ast::LogicOp::And)) =>
-        {
-            expr.lhs()
-                .map(is_pattern_cond)
-                .or_else(|| expr.rhs().map(is_pattern_cond))
-                .unwrap_or(false)
-        }
-        ast::Expr::ParenExpr(expr) => expr.expr().map_or(false, is_pattern_cond),
-        ast::Expr::LetExpr(_) => true,
-        _ => false,
-    }
-}
diff --git a/crates/ide_db/src/rename.rs b/crates/ide_db/src/rename.rs
index 404e17c022..188499db72 100644
--- a/crates/ide_db/src/rename.rs
+++ b/crates/ide_db/src/rename.rs
@@ -188,23 +188,16 @@ fn rename_mod(
         source_change.push_file_system_edit(move_file);
     }
 
-    if let Some(src) = module.declaration_source(sema.db) {
-        let file_id = src.file_id.original_file(sema.db);
-        match src.value.name() {
-            Some(name) => {
-                if let Some(file_range) =
-                    src.with_value(name.syntax()).original_file_range_opt(sema.db)
-                {
-                    source_change.insert_source_edit(
-                        file_id,
-                        TextEdit::replace(file_range.range, new_name.to_string()),
-                    )
-                };
-            }
+    if let Some(InFile { file_id, value: decl_source }) = module.declaration_source(sema.db) {
+        let file_id = file_id.original_file(sema.db);
+        match decl_source.name() {
+            Some(name) => source_change.insert_source_edit(
+                file_id,
+                TextEdit::replace(name.syntax().text_range(), new_name.to_string()),
+            ),
             _ => never!("Module source node is missing a name"),
         }
     }
-
     let def = Definition::Module(module);
     let usages = def.usages(sema).all();
     let ref_edits = usages.iter().map(|(&file_id, references)| {
@@ -350,10 +343,6 @@ fn source_edit_from_name_ref(
     new_name: &str,
     def: Definition,
 ) -> bool {
-    if name_ref.super_token().is_some() {
-        return true;
-    }
-
     if let Some(record_field) = ast::RecordExprField::for_name_ref(name_ref) {
         let rcf_name_ref = record_field.name_ref();
         let rcf_expr = record_field.expr();
diff --git a/crates/mbe/src/expander/matcher.rs b/crates/mbe/src/expander/matcher.rs
index b4c6d3bf61..c5c555e302 100644
--- a/crates/mbe/src/expander/matcher.rs
+++ b/crates/mbe/src/expander/matcher.rs
@@ -437,12 +437,11 @@ fn match_loop_inner<'t>(
                     let mut new_item = item.clone();
                     new_item.bindings = bindings_builder.copy(&new_item.bindings);
                     new_item.dot.next();
-                    collect_vars(
-                        &mut |s| {
-                            bindings_builder.push_empty(&mut new_item.bindings, &s);
-                        },
-                        tokens,
-                    );
+                    let mut vars = Vec::new();
+                    collect_vars(&mut vars, tokens);
+                    for var in vars {
+                        bindings_builder.push_empty(&mut new_item.bindings, &var);
+                    }
                     cur_items.push(new_item);
                 }
                 cur_items.push(MatchState {
@@ -730,16 +729,17 @@ fn match_meta_var(kind: &str, input: &mut TtIter) -> ExpandResult<Option<Fragmen
     input.expect_fragment(fragment).map(|it| it.map(Fragment::Tokens))
 }
 
-fn collect_vars(collector_fun: &mut impl FnMut(SmolStr), pattern: &MetaTemplate) {
+fn collect_vars(buf: &mut Vec<SmolStr>, pattern: &MetaTemplate) {
     for op in pattern.iter() {
         match op {
-            Op::Var { name, .. } => collector_fun(name.clone()),
+            Op::Var { name, .. } => buf.push(name.clone()),
             Op::Leaf(_) => (),
-            Op::Subtree { tokens, .. } => collect_vars(collector_fun, tokens),
-            Op::Repeat { tokens, .. } => collect_vars(collector_fun, tokens),
+            Op::Subtree { tokens, .. } => collect_vars(buf, tokens),
+            Op::Repeat { tokens, .. } => collect_vars(buf, tokens),
         }
     }
 }
+
 impl MetaTemplate {
     fn iter_delimited<'a>(&'a self, delimited: Option<&'a tt::Delimiter>) -> OpDelimitedIter<'a> {
         OpDelimitedIter { inner: &self.0, idx: 0, delimited }
diff --git a/crates/mbe/src/lib.rs b/crates/mbe/src/lib.rs
index 07b7f4d1a5..135282a3cb 100644
--- a/crates/mbe/src/lib.rs
+++ b/crates/mbe/src/lib.rs
@@ -30,8 +30,7 @@ pub use tt::{Delimiter, DelimiterKind, Punct};
 pub use crate::{
     syntax_bridge::{
         parse_exprs_with_sep, parse_to_token_tree, syntax_node_to_token_tree,
-        syntax_node_to_token_tree_with_modifications, token_tree_to_syntax_node, SyntheticToken,
-        SyntheticTokenId,
+        syntax_node_to_token_tree_censored, token_tree_to_syntax_node,
     },
     token_map::TokenMap,
 };
diff --git a/crates/mbe/src/syntax_bridge.rs b/crates/mbe/src/syntax_bridge.rs
index 7e12647cd8..bac1d4ba98 100644
--- a/crates/mbe/src/syntax_bridge.rs
+++ b/crates/mbe/src/syntax_bridge.rs
@@ -1,7 +1,7 @@
 //! Conversions between [`SyntaxNode`] and [`tt::TokenTree`].
 
-use rustc_hash::FxHashMap;
-use stdx::{always, non_empty_vec::NonEmptyVec};
+use rustc_hash::{FxHashMap, FxHashSet};
+use stdx::non_empty_vec::NonEmptyVec;
 use syntax::{
     ast::{self, make::tokens::doc_comment},
     AstToken, Parse, PreorderWithTokens, SmolStr, SyntaxElement, SyntaxKind,
@@ -15,43 +15,20 @@ use crate::{to_parser_input::to_parser_input, tt_iter::TtIter, TokenMap};
 /// Convert the syntax node to a `TokenTree` (what macro
 /// will consume).
 pub fn syntax_node_to_token_tree(node: &SyntaxNode) -> (tt::Subtree, TokenMap) {
-    let (subtree, token_map, _) = syntax_node_to_token_tree_with_modifications(
-        node,
-        Default::default(),
-        0,
-        Default::default(),
-        Default::default(),
-    );
-    (subtree, token_map)
+    syntax_node_to_token_tree_censored(node, &Default::default())
 }
 
 /// Convert the syntax node to a `TokenTree` (what macro will consume)
 /// with the censored range excluded.
-pub fn syntax_node_to_token_tree_with_modifications(
+pub fn syntax_node_to_token_tree_censored(
     node: &SyntaxNode,
-    existing_token_map: TokenMap,
-    next_id: u32,
-    replace: FxHashMap<SyntaxNode, Vec<SyntheticToken>>,
-    append: FxHashMap<SyntaxNode, Vec<SyntheticToken>>,
-) -> (tt::Subtree, TokenMap, u32) {
+    censor: &FxHashSet<SyntaxNode>,
+) -> (tt::Subtree, TokenMap) {
     let global_offset = node.text_range().start();
-    let mut c = Convertor::new(node, global_offset, existing_token_map, next_id, replace, append);
+    let mut c = Convertor::new(node, global_offset, censor);
     let subtree = convert_tokens(&mut c);
     c.id_alloc.map.shrink_to_fit();
-    always!(c.replace.is_empty(), "replace: {:?}", c.replace);
-    always!(c.append.is_empty(), "append: {:?}", c.append);
-    (subtree, c.id_alloc.map, c.id_alloc.next_id)
-}
-
-#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
-pub struct SyntheticTokenId(pub u32);
-
-#[derive(Debug, Clone)]
-pub struct SyntheticToken {
-    pub kind: SyntaxKind,
-    pub text: SmolStr,
-    pub range: TextRange,
-    pub id: SyntheticTokenId,
+    (subtree, c.id_alloc.map)
 }
 
 // The following items are what `rustc` macro can be parsed into :
@@ -170,14 +147,13 @@ fn convert_tokens<C: TokenConvertor>(conv: &mut C) -> tt::Subtree {
             Some(it) => it,
             None => break,
         };
-        let synth_id = token.synthetic_id(&conv);
 
         let kind = token.kind(&conv);
         if kind == COMMENT {
             if let Some(tokens) = conv.convert_doc_comment(&token) {
                 // FIXME: There has to be a better way to do this
                 // Add the comments token id to the converted doc string
-                let id = conv.id_alloc().alloc(range, synth_id);
+                let id = conv.id_alloc().alloc(range);
                 result.extend(tokens.into_iter().map(|mut tt| {
                     if let tt::TokenTree::Subtree(sub) = &mut tt {
                         if let Some(tt::TokenTree::Leaf(tt::Leaf::Literal(lit))) =
@@ -192,9 +168,7 @@ fn convert_tokens<C: TokenConvertor>(conv: &mut C) -> tt::Subtree {
             continue;
         }
         let tt = if kind.is_punct() && kind != UNDERSCORE {
-            if synth_id.is_none() {
-                assert_eq!(range.len(), TextSize::of('.'));
-            }
+            assert_eq!(range.len(), TextSize::of('.'));
 
             if let Some(delim) = subtree.delimiter {
                 let expected = match delim.kind {
@@ -246,13 +220,11 @@ fn convert_tokens<C: TokenConvertor>(conv: &mut C) -> tt::Subtree {
                     panic!("Token from lexer must be single char: token = {:#?}", token);
                 }
             };
-            tt::Leaf::from(tt::Punct { char, spacing, id: conv.id_alloc().alloc(range, synth_id) })
-                .into()
+            tt::Leaf::from(tt::Punct { char, spacing, id: conv.id_alloc().alloc(range) }).into()
         } else {
             macro_rules! make_leaf {
                 ($i:ident) => {
-                    tt::$i { id: conv.id_alloc().alloc(range, synth_id), text: token.to_text(conv) }
-                        .into()
+                    tt::$i { id: conv.id_alloc().alloc(range), text: token.to_text(conv) }.into()
                 };
             }
             let leaf: tt::Leaf = match kind {
@@ -267,14 +239,14 @@ fn convert_tokens<C: TokenConvertor>(conv: &mut C) -> tt::Subtree {
                     let apostrophe = tt::Leaf::from(tt::Punct {
                         char: '\'',
                         spacing: tt::Spacing::Joint,
-                        id: conv.id_alloc().alloc(r, synth_id),
+                        id: conv.id_alloc().alloc(r),
                     });
                     result.push(apostrophe.into());
 
                     let r = TextRange::at(range.start() + char_unit, range.len() - char_unit);
                     let ident = tt::Leaf::from(tt::Ident {
                         text: SmolStr::new(&token.to_text(conv)[1..]),
-                        id: conv.id_alloc().alloc(r, synth_id),
+                        id: conv.id_alloc().alloc(r),
                     });
                     result.push(ident.into());
                     continue;
@@ -295,7 +267,7 @@ fn convert_tokens<C: TokenConvertor>(conv: &mut C) -> tt::Subtree {
 
         conv.id_alloc().close_delim(entry.idx, None);
         let leaf: tt::Leaf = tt::Punct {
-            id: conv.id_alloc().alloc(entry.open_range, None),
+            id: conv.id_alloc().alloc(entry.open_range),
             char: match entry.subtree.delimiter.unwrap().kind {
                 tt::DelimiterKind::Parenthesis => '(',
                 tt::DelimiterKind::Brace => '{',
@@ -389,18 +361,11 @@ struct TokenIdAlloc {
 }
 
 impl TokenIdAlloc {
-    fn alloc(
-        &mut self,
-        absolute_range: TextRange,
-        synthetic_id: Option<SyntheticTokenId>,
-    ) -> tt::TokenId {
+    fn alloc(&mut self, absolute_range: TextRange) -> tt::TokenId {
         let relative_range = absolute_range - self.global_offset;
         let token_id = tt::TokenId(self.next_id);
         self.next_id += 1;
         self.map.insert(token_id, relative_range);
-        if let Some(id) = synthetic_id {
-            self.map.insert_synthetic(token_id, id);
-        }
         token_id
     }
 
@@ -440,8 +405,6 @@ trait SrcToken<Ctx>: std::fmt::Debug {
     fn to_char(&self, ctx: &Ctx) -> Option<char>;
 
     fn to_text(&self, ctx: &Ctx) -> SmolStr;
-
-    fn synthetic_id(&self, ctx: &Ctx) -> Option<SyntheticTokenId>;
 }
 
 trait TokenConvertor: Sized {
@@ -468,10 +431,6 @@ impl<'a> SrcToken<RawConvertor<'a>> for usize {
     fn to_text(&self, ctx: &RawConvertor<'_>) -> SmolStr {
         ctx.lexed.text(*self).into()
     }
-
-    fn synthetic_id(&self, _ctx: &RawConvertor<'a>) -> Option<SyntheticTokenId> {
-        None
-    }
 }
 
 impl<'a> TokenConvertor for RawConvertor<'a> {
@@ -506,130 +465,86 @@ impl<'a> TokenConvertor for RawConvertor<'a> {
     }
 }
 
-struct Convertor {
+struct Convertor<'c> {
     id_alloc: TokenIdAlloc,
     current: Option<SyntaxToken>,
-    current_synthetic: Vec<SyntheticToken>,
     preorder: PreorderWithTokens,
-    replace: FxHashMap<SyntaxNode, Vec<SyntheticToken>>,
-    append: FxHashMap<SyntaxNode, Vec<SyntheticToken>>,
+    censor: &'c FxHashSet<SyntaxNode>,
     range: TextRange,
     punct_offset: Option<(SyntaxToken, TextSize)>,
 }
 
-impl Convertor {
+impl<'c> Convertor<'c> {
     fn new(
         node: &SyntaxNode,
         global_offset: TextSize,
-        existing_token_map: TokenMap,
-        next_id: u32,
-        mut replace: FxHashMap<SyntaxNode, Vec<SyntheticToken>>,
-        mut append: FxHashMap<SyntaxNode, Vec<SyntheticToken>>,
-    ) -> Convertor {
+        censor: &'c FxHashSet<SyntaxNode>,
+    ) -> Convertor<'c> {
         let range = node.text_range();
         let mut preorder = node.preorder_with_tokens();
-        let (first, synthetic) = Self::next_token(&mut preorder, &mut replace, &mut append);
+        let first = Self::next_token(&mut preorder, censor);
         Convertor {
-            id_alloc: { TokenIdAlloc { map: existing_token_map, global_offset, next_id } },
+            id_alloc: { TokenIdAlloc { map: TokenMap::default(), global_offset, next_id: 0 } },
             current: first,
-            current_synthetic: synthetic,
             preorder,
             range,
-            replace,
-            append,
+            censor,
             punct_offset: None,
         }
     }
 
     fn next_token(
         preorder: &mut PreorderWithTokens,
-        replace: &mut FxHashMap<SyntaxNode, Vec<SyntheticToken>>,
-        append: &mut FxHashMap<SyntaxNode, Vec<SyntheticToken>>,
-    ) -> (Option<SyntaxToken>, Vec<SyntheticToken>) {
+        censor: &FxHashSet<SyntaxNode>,
+    ) -> Option<SyntaxToken> {
         while let Some(ev) = preorder.next() {
             let ele = match ev {
                 WalkEvent::Enter(ele) => ele,
-                WalkEvent::Leave(SyntaxElement::Node(node)) => {
-                    if let Some(mut v) = append.remove(&node) {
-                        if !v.is_empty() {
-                            v.reverse();
-                            return (None, v);
-                        }
-                    }
-                    continue;
-                }
                 _ => continue,
             };
             match ele {
-                SyntaxElement::Token(t) => return (Some(t), Vec::new()),
-                SyntaxElement::Node(node) => {
-                    if let Some(mut v) = replace.remove(&node) {
-                        preorder.skip_subtree();
-                        if !v.is_empty() {
-                            v.reverse();
-                            return (None, v);
-                        }
-                    }
-                }
+                SyntaxElement::Token(t) => return Some(t),
+                SyntaxElement::Node(node) if censor.contains(&node) => preorder.skip_subtree(),
+                SyntaxElement::Node(_) => (),
             }
         }
-        (None, Vec::new())
+        None
     }
 }
 
 #[derive(Debug)]
 enum SynToken {
     Ordinary(SyntaxToken),
-    // FIXME is this supposed to be `Punct`?
     Punch(SyntaxToken, TextSize),
-    Synthetic(SyntheticToken),
 }
 
 impl SynToken {
-    fn token(&self) -> Option<&SyntaxToken> {
+    fn token(&self) -> &SyntaxToken {
         match self {
-            SynToken::Ordinary(it) | SynToken::Punch(it, _) => Some(it),
-            SynToken::Synthetic(_) => None,
+            SynToken::Ordinary(it) | SynToken::Punch(it, _) => it,
         }
     }
 }
 
-impl SrcToken<Convertor> for SynToken {
-    fn kind(&self, _ctx: &Convertor) -> SyntaxKind {
-        match self {
-            SynToken::Ordinary(token) => token.kind(),
-            SynToken::Punch(token, _) => token.kind(),
-            SynToken::Synthetic(token) => token.kind,
-        }
+impl<'a> SrcToken<Convertor<'a>> for SynToken {
+    fn kind(&self, _ctx: &Convertor<'a>) -> SyntaxKind {
+        self.token().kind()
     }
-    fn to_char(&self, _ctx: &Convertor) -> Option<char> {
+    fn to_char(&self, _ctx: &Convertor<'a>) -> Option<char> {
         match self {
             SynToken::Ordinary(_) => None,
             SynToken::Punch(it, i) => it.text().chars().nth((*i).into()),
-            SynToken::Synthetic(token) if token.text.len() == 1 => token.text.chars().next(),
-            SynToken::Synthetic(_) => None,
         }
     }
-    fn to_text(&self, _ctx: &Convertor) -> SmolStr {
-        match self {
-            SynToken::Ordinary(token) => token.text().into(),
-            SynToken::Punch(token, _) => token.text().into(),
-            SynToken::Synthetic(token) => token.text.clone(),
-        }
-    }
-
-    fn synthetic_id(&self, _ctx: &Convertor) -> Option<SyntheticTokenId> {
-        match self {
-            SynToken::Synthetic(token) => Some(token.id),
-            _ => None,
-        }
+    fn to_text(&self, _ctx: &Convertor<'a>) -> SmolStr {
+        self.token().text().into()
     }
 }
 
-impl TokenConvertor for Convertor {
+impl TokenConvertor for Convertor<'_> {
     type Token = SynToken;
     fn convert_doc_comment(&self, token: &Self::Token) -> Option<Vec<tt::TokenTree>> {
-        convert_doc_comment(token.token()?)
+        convert_doc_comment(token.token())
     }
 
     fn bump(&mut self) -> Option<(Self::Token, TextRange)> {
@@ -643,25 +558,11 @@ impl TokenConvertor for Convertor {
             }
         }
 
-        if let Some(synth_token) = self.current_synthetic.pop() {
-            if self.current_synthetic.is_empty() {
-                let (new_current, new_synth) =
-                    Self::next_token(&mut self.preorder, &mut self.replace, &mut self.append);
-                self.current = new_current;
-                self.current_synthetic = new_synth;
-            }
-            let range = synth_token.range;
-            return Some((SynToken::Synthetic(synth_token), range));
-        }
-
         let curr = self.current.clone()?;
         if !&self.range.contains_range(curr.text_range()) {
             return None;
         }
-        let (new_current, new_synth) =
-            Self::next_token(&mut self.preorder, &mut self.replace, &mut self.append);
-        self.current = new_current;
-        self.current_synthetic = new_synth;
+        self.current = Self::next_token(&mut self.preorder, self.censor);
         let token = if curr.kind().is_punct() {
             self.punct_offset = Some((curr.clone(), 0.into()));
             let range = curr.text_range();
@@ -684,10 +585,6 @@ impl TokenConvertor for Convertor {
             }
         }
 
-        if let Some(synth_token) = self.current_synthetic.last() {
-            return Some(SynToken::Synthetic(synth_token.clone()));
-        }
-
         let curr = self.current.clone()?;
         if !self.range.contains_range(curr.text_range()) {
             return None;
diff --git a/crates/mbe/src/token_map.rs b/crates/mbe/src/token_map.rs
index c923e7a69a..9053526d20 100644
--- a/crates/mbe/src/token_map.rs
+++ b/crates/mbe/src/token_map.rs
@@ -5,8 +5,6 @@ use std::hash::Hash;
 use parser::{SyntaxKind, T};
 use syntax::{TextRange, TextSize};
 
-use crate::syntax_bridge::SyntheticTokenId;
-
 #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
 enum TokenTextRange {
     Token(TextRange),
@@ -33,7 +31,6 @@ impl TokenTextRange {
 pub struct TokenMap {
     /// Maps `tt::TokenId` to the *relative* source range.
     entries: Vec<(tt::TokenId, TokenTextRange)>,
-    pub synthetic_entries: Vec<(tt::TokenId, SyntheticTokenId)>,
 }
 
 impl TokenMap {
@@ -60,10 +57,6 @@ impl TokenMap {
             .filter_map(move |(_, range)| range.by_kind(kind))
     }
 
-    pub fn synthetic_token_id(&self, token_id: tt::TokenId) -> Option<SyntheticTokenId> {
-        self.synthetic_entries.iter().find(|(tid, _)| *tid == token_id).map(|(_, id)| *id)
-    }
-
     pub fn first_range_by_token(
         &self,
         token_id: tt::TokenId,
@@ -74,17 +67,12 @@ impl TokenMap {
 
     pub(crate) fn shrink_to_fit(&mut self) {
         self.entries.shrink_to_fit();
-        self.synthetic_entries.shrink_to_fit();
     }
 
     pub(crate) fn insert(&mut self, token_id: tt::TokenId, relative_range: TextRange) {
         self.entries.push((token_id, TokenTextRange::Token(relative_range)));
     }
 
-    pub(crate) fn insert_synthetic(&mut self, token_id: tt::TokenId, id: SyntheticTokenId) {
-        self.synthetic_entries.push((token_id, id));
-    }
-
     pub(crate) fn insert_delim(
         &mut self,
         token_id: tt::TokenId,
diff --git a/crates/parser/src/grammar/expressions.rs b/crates/parser/src/grammar/expressions.rs
index a40db15049..9dbba89c56 100644
--- a/crates/parser/src/grammar/expressions.rs
+++ b/crates/parser/src/grammar/expressions.rs
@@ -29,15 +29,6 @@ fn expr_no_struct(p: &mut Parser) {
     expr_bp(p, None, r, 1);
 }
 
-/// Parses the expression in `let pattern = expression`.
-/// It needs to be parsed with lower precedence than `&&`, so that
-/// `if let true = true && false` is parsed as `if (let true = true) && (true)`
-/// and not `if let true = (true && true)`.
-fn expr_let(p: &mut Parser) {
-    let r = Restrictions { forbid_structs: true, prefer_stmt: false };
-    expr_bp(p, None, r, 5);
-}
-
 pub(super) fn stmt(p: &mut Parser, semicolon: Semicolon) {
     if p.eat(T![;]) {
         return;
@@ -194,7 +185,6 @@ fn current_op(p: &Parser) -> (u8, SyntaxKind) {
         T![%] if p.at(T![%=])  => (1,  T![%=]),
         T![%]                  => (11, T![%]),
         T![&] if p.at(T![&=])  => (1,  T![&=]),
-        // If you update this, remember to update `expr_let()` too.
         T![&] if p.at(T![&&])  => (4,  T![&&]),
         T![&]                  => (8,  T![&]),
         T![/] if p.at(T![/=])  => (1,  T![/=]),
diff --git a/crates/parser/src/grammar/expressions/atom.rs b/crates/parser/src/grammar/expressions/atom.rs
index e2c1b1fec5..640caa0778 100644
--- a/crates/parser/src/grammar/expressions/atom.rs
+++ b/crates/parser/src/grammar/expressions/atom.rs
@@ -72,14 +72,9 @@ pub(super) fn atom_expr(p: &mut Parser, r: Restrictions) -> Option<(CompletedMar
         T!['('] => tuple_expr(p),
         T!['['] => array_expr(p),
         T![|] => closure_expr(p),
-        T![static] | T![async] | T![move] if la == T![|] => closure_expr(p),
-        T![static] | T![async] if la == T![move] && p.nth(2) == T![|] => closure_expr(p),
-        T![static] if la == T![async] && p.nth(2) == T![|] => closure_expr(p),
-        T![static] if la == T![async] && p.nth(2) == T![move] && p.nth(3) == T![|] => {
-            closure_expr(p)
-        }
+        T![move] if la == T![|] => closure_expr(p),
+        T![async] if la == T![|] || (la == T![move] && p.nth(2) == T![|]) => closure_expr(p),
         T![if] => if_expr(p),
-        T![let] => let_expr(p),
 
         T![loop] => loop_expr(p, None),
         T![box] => box_expr(p, None),
@@ -239,10 +234,6 @@ fn array_expr(p: &mut Parser) -> CompletedMarker {
 //     async || {};
 //     move || {};
 //     async move || {};
-//     static || {};
-//     static move || {};
-//     static async || {};
-//     static async move || {};
 // }
 fn closure_expr(p: &mut Parser) -> CompletedMarker {
     assert!(
@@ -250,16 +241,8 @@ fn closure_expr(p: &mut Parser) -> CompletedMarker {
             || (p.at(T![move]) && p.nth(1) == T![|])
             || (p.at(T![async]) && p.nth(1) == T![|])
             || (p.at(T![async]) && p.nth(1) == T![move] && p.nth(2) == T![|])
-            || (p.at(T![static]) && p.nth(1) == T![|])
-            || (p.at(T![static]) && p.nth(1) == T![move] && p.nth(2) == T![|])
-            || (p.at(T![static]) && p.nth(1) == T![async] && p.nth(2) == T![|])
-            || (p.at(T![static])
-                && p.nth(1) == T![async]
-                && p.nth(2) == T![move]
-                && p.nth(3) == T![|])
     );
     let m = p.start();
-    p.eat(T![static]);
     p.eat(T![async]);
     p.eat(T![move]);
     params::param_list_closure(p);
@@ -287,7 +270,7 @@ fn if_expr(p: &mut Parser) -> CompletedMarker {
     assert!(p.at(T![if]));
     let m = p.start();
     p.bump(T![if]);
-    expr_no_struct(p);
+    condition(p);
     block_expr(p);
     if p.at(T![else]) {
         p.bump(T![else]);
@@ -336,7 +319,7 @@ fn while_expr(p: &mut Parser, m: Option<Marker>) -> CompletedMarker {
     assert!(p.at(T![while]));
     let m = m.unwrap_or_else(|| p.start());
     p.bump(T![while]);
-    expr_no_struct(p);
+    condition(p);
     block_expr(p);
     m.complete(p, WHILE_EXPR)
 }
@@ -356,18 +339,22 @@ fn for_expr(p: &mut Parser, m: Option<Marker>) -> CompletedMarker {
     m.complete(p, FOR_EXPR)
 }
 
-// test let_expr
-// fn foo() {
-//     if let Some(_) = None && true {}
-//     while 1 == 5 && (let None = None) {}
+// test cond
+// fn foo() { if let Some(_) = None {} }
+// fn bar() {
+//     if let Some(_) | Some(_) = None {}
+//     if let | Some(_) = None {}
+//     while let Some(_) | Some(_) = None {}
+//     while let | Some(_) = None {}
 // }
-fn let_expr(p: &mut Parser) -> CompletedMarker {
+fn condition(p: &mut Parser) {
     let m = p.start();
-    p.bump(T![let]);
-    patterns::pattern_top(p);
-    p.expect(T![=]);
-    expr_let(p);
-    m.complete(p, LET_EXPR)
+    if p.eat(T![let]) {
+        patterns::pattern_top(p);
+        p.expect(T![=]);
+    }
+    expr_no_struct(p);
+    m.complete(p, CONDITION);
 }
 
 // test match_expr
@@ -479,6 +466,10 @@ fn match_guard(p: &mut Parser) -> CompletedMarker {
     assert!(p.at(T![if]));
     let m = p.start();
     p.bump(T![if]);
+    if p.eat(T![let]) {
+        patterns::pattern_top(p);
+        p.expect(T![=]);
+    }
     expr(p);
     m.complete(p, MATCH_GUARD)
 }
diff --git a/crates/parser/src/grammar/items.rs b/crates/parser/src/grammar/items.rs
index 36d13cc977..896efaf375 100644
--- a/crates/parser/src/grammar/items.rs
+++ b/crates/parser/src/grammar/items.rs
@@ -230,7 +230,7 @@ fn opt_item_without_modifiers(p: &mut Parser, m: Marker) -> Result<(), Marker> {
         IDENT if p.at_contextual_kw(T![macro_rules]) && p.nth(1) == BANG => macro_rules(p, m),
 
         T![const] if (la == IDENT || la == T![_] || la == T![mut]) => consts::konst(p, m),
-        T![static] if (la == IDENT || la == T![_] || la == T![mut]) => consts::static_(p, m),
+        T![static] => consts::static_(p, m),
 
         _ => return Err(m),
     };
diff --git a/crates/parser/src/syntax_kind/generated.rs b/crates/parser/src/syntax_kind/generated.rs
index d04b5dbf00..601a5792af 100644
--- a/crates/parser/src/syntax_kind/generated.rs
+++ b/crates/parser/src/syntax_kind/generated.rs
@@ -178,6 +178,7 @@ pub enum SyntaxKind {
     CLOSURE_EXPR,
     IF_EXPR,
     WHILE_EXPR,
+    CONDITION,
     LOOP_EXPR,
     FOR_EXPR,
     CONTINUE_EXPR,
@@ -187,7 +188,6 @@ pub enum SyntaxKind {
     STMT_LIST,
     RETURN_EXPR,
     YIELD_EXPR,
-    LET_EXPR,
     MATCH_EXPR,
     MATCH_ARM_LIST,
     MATCH_ARM,
diff --git a/crates/project_model/src/cargo_workspace.rs b/crates/project_model/src/cargo_workspace.rs
index 48051e4b5e..c2cf3c4ce3 100644
--- a/crates/project_model/src/cargo_workspace.rs
+++ b/crates/project_model/src/cargo_workspace.rs
@@ -210,8 +210,6 @@ pub struct TargetData {
     pub kind: TargetKind,
     /// Is this target a proc-macro
     pub is_proc_macro: bool,
-    /// Required features of the target without which it won't build
-    pub required_features: Vec<String>,
 }
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
@@ -350,7 +348,6 @@ impl CargoWorkspace {
                     root: AbsPathBuf::assert(PathBuf::from(&meta_tgt.src_path)),
                     kind: TargetKind::new(meta_tgt.kind.as_slice()),
                     is_proc_macro,
-                    required_features: meta_tgt.required_features.clone(),
                 });
                 pkg_data.targets.push(tgt);
             }
diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml
index 87b6cd7186..014001397d 100644
--- a/crates/rust-analyzer/Cargo.toml
+++ b/crates/rust-analyzer/Cargo.toml
@@ -22,8 +22,8 @@ anyhow = "1.0.26"
 crossbeam-channel = "0.5.0"
 dissimilar = "1.0.2"
 itertools = "0.10.0"
-lsp-types = { version = "0.92", features = ["proposed"] }
-parking_lot = "0.12"
+lsp-types = { version = "0.91", features = ["proposed"] }
+parking_lot = "0.11.0"
 xflags = "0.2.1"
 oorandom = "11.1.2"
 rustc-hash = "1.1.0"
diff --git a/crates/rust-analyzer/src/caps.rs b/crates/rust-analyzer/src/caps.rs
index 457399a618..648f6aeebe 100644
--- a/crates/rust-analyzer/src/caps.rs
+++ b/crates/rust-analyzer/src/caps.rs
@@ -113,18 +113,15 @@ pub fn server_capabilities(config: &Config) -> ServerCapabilities {
         ),
         moniker_provider: None,
         experimental: Some(json!({
-            "externalDocs": true,
-            "hoverRange": true,
             "joinLines": true,
-            "matchingBrace": true,
-            "moveItem": true,
-            "onEnter": true,
             "openCargoToml": true,
+            "ssr": true,
+            "onEnter": true,
             "parentModule": true,
+            "hoverRange": true,
             "runnables": {
                 "kinds": [ "cargo" ],
             },
-            "ssr": true,
             "workspaceSymbolScopeKindFiltering": true,
         })),
     }
diff --git a/crates/rust-analyzer/src/cargo_target_spec.rs b/crates/rust-analyzer/src/cargo_target_spec.rs
index ec5dd16d00..a8894e0f02 100644
--- a/crates/rust-analyzer/src/cargo_target_spec.rs
+++ b/crates/rust-analyzer/src/cargo_target_spec.rs
@@ -1,7 +1,5 @@
 //! See `CargoTargetSpec`
 
-use std::mem;
-
 use cfg::{CfgAtom, CfgExpr};
 use ide::{FileId, RunnableKind, TestId};
 use project_model::{self, ManifestPath, TargetKind};
@@ -20,22 +18,17 @@ pub(crate) struct CargoTargetSpec {
     pub(crate) package: String,
     pub(crate) target: String,
     pub(crate) target_kind: TargetKind,
-    pub(crate) required_features: Vec<String>,
 }
 
 impl CargoTargetSpec {
     pub(crate) fn runnable_args(
         snap: &GlobalStateSnapshot,
-        mut spec: Option<CargoTargetSpec>,
+        spec: Option<CargoTargetSpec>,
         kind: &RunnableKind,
         cfg: &Option<CfgExpr>,
     ) -> Result<(Vec<String>, Vec<String>)> {
         let mut args = Vec::new();
         let mut extra_args = Vec::new();
-
-        let target_required_features =
-            spec.as_mut().map(|spec| mem::take(&mut spec.required_features)).unwrap_or(Vec::new());
-
         match kind {
             RunnableKind::Test { test_id, attr } => {
                 args.push("test".to_string());
@@ -94,20 +87,14 @@ impl CargoTargetSpec {
         let cargo_config = snap.config.cargo();
         if cargo_config.all_features {
             args.push("--all-features".to_string());
-
-            for feature in target_required_features {
-                args.push("--features".to_string());
-                args.push(feature);
-            }
         } else {
             let mut features = Vec::new();
             if let Some(cfg) = cfg.as_ref() {
                 required_features(cfg, &mut features);
             }
-
-            features.extend(cargo_config.features);
-            features.extend(target_required_features);
-
+            for feature in cargo_config.features {
+                features.push(feature.clone());
+            }
             features.dedup();
             for feature in features {
                 args.push("--features".to_string());
@@ -139,7 +126,6 @@ impl CargoTargetSpec {
             package: cargo_ws.package_flag(package_data),
             target: target_data.name.clone(),
             target_kind: target_data.kind,
-            required_features: target_data.required_features.clone(),
         };
 
         Ok(Some(res))
diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs
index 9b4f8b4333..bef943464a 100644
--- a/crates/rust-analyzer/src/global_state.rs
+++ b/crates/rust-analyzer/src/global_state.rs
@@ -46,7 +46,7 @@ pub(crate) type ReqQueue = lsp_server::ReqQueue<(String, Instant), ReqHandler>;
 /// snapshot of the file systems, and `analysis_host`, which stores our
 /// incremental salsa database.
 ///
-/// Note that this struct has more than one impl in various modules!
+/// Note that this struct has more than on impl in various modules!
 pub(crate) struct GlobalState {
     sender: Sender<lsp_server::Message>,
     req_queue: ReqQueue,
diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs
index ea98d09935..0e23094199 100644
--- a/crates/rust-analyzer/src/lsp_ext.rs
+++ b/crates/rust-analyzer/src/lsp_ext.rs
@@ -496,7 +496,6 @@ impl Request for WorkspaceSymbol {
 }
 
 #[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
-#[serde(rename_all = "camelCase")]
 pub struct WorkspaceSymbolParams {
     #[serde(flatten)]
     pub partial_result_params: PartialResultParams,
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index 7bfac4daad..5bec301736 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -455,7 +455,7 @@ impl GlobalState {
                 // Refresh semantic tokens if the client supports it.
                 if self.config.semantic_tokens_refresh() {
                     self.semantic_tokens_cache.lock().clear();
-                    self.send_request::<lsp_types::request::SemanticTokensRefresh>((), |_, _| ());
+                    self.send_request::<lsp_types::request::SemanticTokensRefesh>((), |_, _| ());
                 }
 
                 // Refresh code lens if the client supports it.
diff --git a/crates/syntax/Cargo.toml b/crates/syntax/Cargo.toml
index f59cd4f257..43c1f2fa80 100644
--- a/crates/syntax/Cargo.toml
+++ b/crates/syntax/Cargo.toml
@@ -30,7 +30,7 @@ rayon = "1"
 expect-test = "1.2.0-pre.1"
 proc-macro2 = "1.0.8"
 quote = "1.0.2"
-ungrammar = "=1.15.0"
+ungrammar = "=1.14.9"
 
 test_utils = { path = "../test_utils" }
 sourcegen = { path = "../sourcegen" }
diff --git a/crates/syntax/src/ast/generated/nodes.rs b/crates/syntax/src/ast/generated/nodes.rs
index 6c4729ef36..09c5af210f 100644
--- a/crates/syntax/src/ast/generated/nodes.rs
+++ b/crates/syntax/src/ast/generated/nodes.rs
@@ -884,7 +884,7 @@ pub struct IfExpr {
 impl ast::HasAttrs for IfExpr {}
 impl IfExpr {
     pub fn if_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![if]) }
-    pub fn condition(&self) -> Option<Expr> { support::child(&self.syntax) }
+    pub fn condition(&self) -> Option<Condition> { support::child(&self.syntax) }
     pub fn else_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![else]) }
 }
 
@@ -1038,7 +1038,7 @@ impl ast::HasAttrs for WhileExpr {}
 impl ast::HasLoopBody for WhileExpr {}
 impl WhileExpr {
     pub fn while_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![while]) }
-    pub fn condition(&self) -> Option<Expr> { support::child(&self.syntax) }
+    pub fn condition(&self) -> Option<Condition> { support::child(&self.syntax) }
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -1051,18 +1051,6 @@ impl YieldExpr {
     pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) }
 }
 
-#[derive(Debug, Clone, PartialEq, Eq, Hash)]
-pub struct LetExpr {
-    pub(crate) syntax: SyntaxNode,
-}
-impl ast::HasAttrs for LetExpr {}
-impl LetExpr {
-    pub fn let_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![let]) }
-    pub fn pat(&self) -> Option<Pat> { support::child(&self.syntax) }
-    pub fn eq_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![=]) }
-    pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) }
-}
-
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub struct StmtList {
     pub(crate) syntax: SyntaxNode,
@@ -1118,6 +1106,17 @@ impl ArgList {
     pub fn r_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![')']) }
 }
 
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct Condition {
+    pub(crate) syntax: SyntaxNode,
+}
+impl Condition {
+    pub fn let_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![let]) }
+    pub fn pat(&self) -> Option<Pat> { support::child(&self.syntax) }
+    pub fn eq_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![=]) }
+    pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) }
+}
+
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub struct MatchArmList {
     pub(crate) syntax: SyntaxNode,
@@ -1148,7 +1147,10 @@ pub struct MatchGuard {
 }
 impl MatchGuard {
     pub fn if_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![if]) }
-    pub fn condition(&self) -> Option<Expr> { support::child(&self.syntax) }
+    pub fn let_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![let]) }
+    pub fn pat(&self) -> Option<Pat> { support::child(&self.syntax) }
+    pub fn eq_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![=]) }
+    pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) }
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -1522,7 +1524,6 @@ pub enum Expr {
     TupleExpr(TupleExpr),
     WhileExpr(WhileExpr),
     YieldExpr(YieldExpr),
-    LetExpr(LetExpr),
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -2663,17 +2664,6 @@ impl AstNode for YieldExpr {
     }
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
-impl AstNode for LetExpr {
-    fn can_cast(kind: SyntaxKind) -> bool { kind == LET_EXPR }
-    fn cast(syntax: SyntaxNode) -> Option<Self> {
-        if Self::can_cast(syntax.kind()) {
-            Some(Self { syntax })
-        } else {
-            None
-        }
-    }
-    fn syntax(&self) -> &SyntaxNode { &self.syntax }
-}
 impl AstNode for StmtList {
     fn can_cast(kind: SyntaxKind) -> bool { kind == STMT_LIST }
     fn cast(syntax: SyntaxNode) -> Option<Self> {
@@ -2729,6 +2719,17 @@ impl AstNode for ArgList {
     }
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
+impl AstNode for Condition {
+    fn can_cast(kind: SyntaxKind) -> bool { kind == CONDITION }
+    fn cast(syntax: SyntaxNode) -> Option<Self> {
+        if Self::can_cast(syntax.kind()) {
+            Some(Self { syntax })
+        } else {
+            None
+        }
+    }
+    fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
 impl AstNode for MatchArmList {
     fn can_cast(kind: SyntaxKind) -> bool { kind == MATCH_ARM_LIST }
     fn cast(syntax: SyntaxNode) -> Option<Self> {
@@ -3335,9 +3336,6 @@ impl From<WhileExpr> for Expr {
 impl From<YieldExpr> for Expr {
     fn from(node: YieldExpr) -> Expr { Expr::YieldExpr(node) }
 }
-impl From<LetExpr> for Expr {
-    fn from(node: LetExpr) -> Expr { Expr::LetExpr(node) }
-}
 impl AstNode for Expr {
     fn can_cast(kind: SyntaxKind) -> bool {
         match kind {
@@ -3346,7 +3344,7 @@ impl AstNode for Expr {
             | INDEX_EXPR | LITERAL | LOOP_EXPR | MACRO_CALL | MACRO_STMTS | MATCH_EXPR
             | METHOD_CALL_EXPR | PAREN_EXPR | PATH_EXPR | PREFIX_EXPR | RANGE_EXPR
             | RECORD_EXPR | REF_EXPR | RETURN_EXPR | TRY_EXPR | TUPLE_EXPR | WHILE_EXPR
-            | YIELD_EXPR | LET_EXPR => true,
+            | YIELD_EXPR => true,
             _ => false,
         }
     }
@@ -3383,7 +3381,6 @@ impl AstNode for Expr {
             TUPLE_EXPR => Expr::TupleExpr(TupleExpr { syntax }),
             WHILE_EXPR => Expr::WhileExpr(WhileExpr { syntax }),
             YIELD_EXPR => Expr::YieldExpr(YieldExpr { syntax }),
-            LET_EXPR => Expr::LetExpr(LetExpr { syntax }),
             _ => return None,
         };
         Some(res)
@@ -3421,7 +3418,6 @@ impl AstNode for Expr {
             Expr::TupleExpr(it) => &it.syntax,
             Expr::WhileExpr(it) => &it.syntax,
             Expr::YieldExpr(it) => &it.syntax,
-            Expr::LetExpr(it) => &it.syntax,
         }
     }
 }
@@ -3887,7 +3883,6 @@ impl AstNode for AnyHasAttrs {
             | TUPLE_EXPR
             | WHILE_EXPR
             | YIELD_EXPR
-            | LET_EXPR
             | STMT_LIST
             | RECORD_EXPR_FIELD_LIST
             | RECORD_EXPR_FIELD
@@ -4542,11 +4537,6 @@ impl std::fmt::Display for YieldExpr {
         std::fmt::Display::fmt(self.syntax(), f)
     }
 }
-impl std::fmt::Display for LetExpr {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        std::fmt::Display::fmt(self.syntax(), f)
-    }
-}
 impl std::fmt::Display for StmtList {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         std::fmt::Display::fmt(self.syntax(), f)
@@ -4572,6 +4562,11 @@ impl std::fmt::Display for ArgList {
         std::fmt::Display::fmt(self.syntax(), f)
     }
 }
+impl std::fmt::Display for Condition {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        std::fmt::Display::fmt(self.syntax(), f)
+    }
+}
 impl std::fmt::Display for MatchArmList {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         std::fmt::Display::fmt(self.syntax(), f)
diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs
index 1a754ef460..8a1bcebbf6 100644
--- a/crates/syntax/src/ast/make.rs
+++ b/crates/syntax/src/ast/make.rs
@@ -397,7 +397,7 @@ pub fn expr_match(expr: ast::Expr, match_arm_list: ast::MatchArmList) -> ast::Ex
     expr_from_text(&format!("match {} {}", expr, match_arm_list))
 }
 pub fn expr_if(
-    condition: ast::Expr,
+    condition: ast::Condition,
     then_branch: ast::BlockExpr,
     else_branch: Option<ast::ElseBranch>,
 ) -> ast::Expr {
@@ -456,8 +456,14 @@ pub fn expr_assignment(lhs: ast::Expr, rhs: ast::Expr) -> ast::Expr {
 fn expr_from_text(text: &str) -> ast::Expr {
     ast_from_text(&format!("const C: () = {};", text))
 }
-pub fn expr_let(pattern: ast::Pat, expr: ast::Expr) -> ast::LetExpr {
-    ast_from_text(&format!("const _: () = while let {} = {} {{}};", pattern, expr))
+
+pub fn condition(expr: ast::Expr, pattern: Option<ast::Pat>) -> ast::Condition {
+    match pattern {
+        None => ast_from_text(&format!("const _: () = while {} {{}};", expr)),
+        Some(pattern) => {
+            ast_from_text(&format!("const _: () = while let {} = {} {{}};", pattern, expr))
+        }
+    }
 }
 
 pub fn arg_list(args: impl IntoIterator<Item = ast::Expr>) -> ast::ArgList {
diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs
index 5ff6519c9c..067e13ee14 100644
--- a/crates/syntax/src/ast/node_ext.rs
+++ b/crates/syntax/src/ast/node_ext.rs
@@ -528,6 +528,12 @@ impl ast::Item {
     }
 }
 
+impl ast::Condition {
+    pub fn is_pattern_cond(&self) -> bool {
+        self.let_token().is_some()
+    }
+}
+
 #[derive(Debug, Clone, PartialEq, Eq)]
 pub enum FieldKind {
     Name(ast::NameRef),
diff --git a/crates/syntax/src/validation.rs b/crates/syntax/src/validation.rs
index 3ea5844c95..8dc47e0bd3 100644
--- a/crates/syntax/src/validation.rs
+++ b/crates/syntax/src/validation.rs
@@ -38,7 +38,6 @@ pub(crate) fn validate(root: &SyntaxNode) -> Vec<SyntaxError> {
                 ast::PtrType(it) => validate_trait_object_ptr_ty(it, &mut errors),
                 ast::FnPtrType(it) => validate_trait_object_fn_ptr_ret_ty(it, &mut errors),
                 ast::MacroRules(it) => validate_macro_rules(it, &mut errors),
-                ast::LetExpr(it) => validate_let_expr(it, &mut errors),
                 _ => (),
             }
         }
@@ -344,33 +343,3 @@ fn validate_const(const_: ast::Const, errors: &mut Vec<SyntaxError>) {
         errors.push(SyntaxError::new("const globals cannot be mutable", mut_token.text_range()));
     }
 }
-
-fn validate_let_expr(let_: ast::LetExpr, errors: &mut Vec<SyntaxError>) {
-    let mut token = let_.syntax().clone();
-    loop {
-        token = match token.parent() {
-            Some(it) => it,
-            None => break,
-        };
-
-        if ast::ParenExpr::can_cast(token.kind()) {
-            continue;
-        } else if let Some(it) = ast::BinExpr::cast(token.clone()) {
-            if it.op_kind() == Some(ast::BinaryOp::LogicOp(ast::LogicOp::And)) {
-                continue;
-            }
-        } else if ast::IfExpr::can_cast(token.kind())
-            || ast::WhileExpr::can_cast(token.kind())
-            || ast::MatchGuard::can_cast(token.kind())
-        {
-            // It must be part of the condition since the expressions are inside a block.
-            return;
-        }
-
-        break;
-    }
-    errors.push(SyntaxError::new(
-        "`let` expressions are not supported here",
-        let_.syntax().text_range(),
-    ));
-}
diff --git a/crates/tt/src/lib.rs b/crates/tt/src/lib.rs
index 0316b15038..9eca970ee2 100644
--- a/crates/tt/src/lib.rs
+++ b/crates/tt/src/lib.rs
@@ -87,16 +87,6 @@ pub struct Ident {
     pub id: TokenId,
 }
 
-impl Leaf {
-    pub fn id(&self) -> TokenId {
-        match self {
-            Leaf::Literal(l) => l.id,
-            Leaf::Punct(p) => p.id,
-            Leaf::Ident(i) => i.id,
-        }
-    }
-}
-
 fn print_debug_subtree(f: &mut fmt::Formatter<'_>, subtree: &Subtree, level: usize) -> fmt::Result {
     let align = "  ".repeat(level);
 
diff --git a/docs/dev/lsp-extensions.md b/docs/dev/lsp-extensions.md
index 85df1188a8..c92395553c 100644
--- a/docs/dev/lsp-extensions.md
+++ b/docs/dev/lsp-extensions.md
@@ -1,5 +1,5 @@
 <!---
-lsp_ext.rs hash: 5b53b92c9f9d6650
+lsp_ext.rs hash: 49ffd619919ed74
 
 If you need to change the above hash to make the test pass, please check if you
 need to adjust this doc as well and ping this issue:
@@ -278,8 +278,6 @@ interface SsrParams {
     textDocument: TextDocumentIdentifier;
     /// Position where SSR was invoked.
     position: Position;
-    /// Current selections. Search/replace will be restricted to these if non-empty.
-    selections: Range[];
 }
 ```
 
diff --git a/docs/user/manual.adoc b/docs/user/manual.adoc
index 2b1eae1e2a..c631ae4655 100644
--- a/docs/user/manual.adoc
+++ b/docs/user/manual.adoc
@@ -240,6 +240,7 @@ The are several LSP client implementations for vim or neovim:
    * same configurations as VSCode extension, `rust-analyzer.server.path`, `rust-analyzer.cargo.features` etc.
    * same commands too, `rust-analyzer.analyzerStatus`, `rust-analyzer.ssr` etc.
    * inlay hints for variables and method chaining, _Neovim Only_
+   * semantic highlighting is not implemented yet
 
 Note: for code actions, use `coc-codeaction-cursor` and `coc-codeaction-selected`; `coc-codeaction` and `coc-codeaction-line` are unlikely to be useful.
 
@@ -361,19 +362,22 @@ if executable('rust-analyzer')
 endif
 ----
 
-=== Sublime Text
+=== Sublime Text 3
 
-==== Sublime Text 4:
-* Follow the instructions in link:https://github.com/sublimelsp/LSP-rust-analyzer[LSP-rust-analyzer].
+Prerequisites: You have installed the <<rust-analyzer-language-server-binary,`rust-analyzer` binary>>.
+
+You also need the `LSP` package.
+To install it:
 
-NOTE: Install link:https://packagecontrol.io/packages/LSP-file-watcher-chokidar[LSP-file-watcher-chokidar] to enable file watching (`workspace/didChangeWatchedFiles`).
+1. If you've never installed a Sublime Text package, install Package Control:
+   * Open the command palette (Win/Linux: `ctrl+shift+p`, Mac: `cmd+shift+p`)
+   * Type `Install Package Control`, press enter
+2. In the command palette, run `Package control: Install package`, and in the list that pops up, type `LSP` and press enter.
 
-==== Sublime Text 3:
-* Install the <<rust-analyzer-language-server-binary,`rust-analyzer` binary>>.
-* Install the link:https://packagecontrol.io/packages/LSP[LSP package].
-* From the command palette, run `LSP: Enable Language Server Globally` and select `rust-analyzer`.
+Finally, with your Rust project open, in the command palette, run `LSP: Enable Language Server In Project` or `LSP: Enable Language Server Globally`, then select `rust-analyzer` in the list that pops up to enable the rust-analyzer LSP.
+The latter means that rust-analyzer is enabled by default in Rust projects.
 
-If it worked, you should see "rust-analyzer, Line X, Column Y" on the left side of the status bar, and after waiting a bit, functionalities like tooltips on hovering over variables should become available.
+If it worked, you should see "rust-analyzer, Line X, Column Y" on the left side of the bottom bar, and after waiting a bit, functionality like tooltips on hovering over variables should become available.
 
 If you get an error saying `No such file or directory: 'rust-analyzer'`, see the <<rust-analyzer-language-server-binary,`rust-analyzer` binary>> section on installing the language server binary.
 
diff --git a/editors/code/.eslintrc.js b/editors/code/.eslintrc.js
index 631d956da2..b145330a02 100644
--- a/editors/code/.eslintrc.js
+++ b/editors/code/.eslintrc.js
@@ -5,8 +5,7 @@ module.exports = {
     },
     "parser": "@typescript-eslint/parser",
     "parserOptions": {
-        "project": "tsconfig.eslint.json",
-        "tsconfigRootDir": __dirname,
+        "project": "tsconfig.json",
         "sourceType": "module"
     },
     "plugins": [
diff --git a/editors/code/tsconfig.eslint.json b/editors/code/tsconfig.eslint.json
deleted file mode 100644
index 9eddf27986..0000000000
--- a/editors/code/tsconfig.eslint.json
+++ /dev/null
@@ -1,11 +0,0 @@
-// Special typescript project file, used by eslint only.
-{
-	"extends": "./tsconfig.json",
-	"include": [
-		// repeated from base config's "include" setting
-		"src",
-		"tests",
-		// these are the eslint-only inclusions
-		".eslintrc.js",
-	]
-}


Test patch:
diff --git a/crates/hir_def/src/macro_expansion_tests.rs b/crates/hir_def/src/macro_expansion_tests.rs
index 1a7d9aa841..c3116edc88 100644
--- a/crates/hir_def/src/macro_expansion_tests.rs
+++ b/crates/hir_def/src/macro_expansion_tests.rs
@@ -14,10 +14,10 @@ mod builtin_fn_macro;
 mod builtin_derive_macro;
 mod proc_macros;
 
-use std::{iter, ops::Range, sync::Arc};
+use std::{iter, ops::Range};
 
 use ::mbe::TokenMap;
-use base_db::{fixture::WithFixture, ProcMacro, SourceDatabase};
+use base_db::{fixture::WithFixture, SourceDatabase};
 use expect_test::Expect;
 use hir_expand::{
     db::{AstDatabase, TokenExpander},
@@ -39,21 +39,7 @@ use crate::{
 
 #[track_caller]
 fn check(ra_fixture: &str, mut expect: Expect) {
-    let extra_proc_macros = vec![(
-        r#"
-#[proc_macro_attribute]
-pub fn identity_when_valid(_attr: TokenStream, item: TokenStream) -> TokenStream {
-    item
-}
-"#
-        .into(),
-        ProcMacro {
-            name: "identity_when_valid".into(),
-            kind: base_db::ProcMacroKind::Attr,
-            expander: Arc::new(IdentityWhenValidProcMacroExpander),
-        },
-    )];
-    let db = TestDB::with_files_extra_proc_macros(ra_fixture, extra_proc_macros);
+    let db = TestDB::with_files(ra_fixture);
     let krate = db.crate_graph().iter().next().unwrap();
     let def_map = db.crate_def_map(krate);
     let local_id = def_map.root();
@@ -186,7 +172,7 @@ pub fn identity_when_valid(_attr: TokenStream, item: TokenStream) -> TokenStream
         let range: Range<usize> = range.into();
 
         if show_token_ids {
-            if let Some((tree, map, _)) = arg.as_deref() {
+            if let Some((tree, map)) = arg.as_deref() {
                 let tt_range = call.token_tree().unwrap().syntax().text_range();
                 let mut ranges = Vec::new();
                 extract_id_ranges(&mut ranges, &map, &tree);
@@ -215,19 +201,10 @@ pub fn identity_when_valid(_attr: TokenStream, item: TokenStream) -> TokenStream
     }
 
     for decl_id in def_map[local_id].scope.declarations() {
-        // FIXME: I'm sure there's already better way to do this
-        let src = match decl_id {
-            ModuleDefId::AdtId(AdtId::StructId(struct_id)) => {
-                Some(struct_id.lookup(&db).source(&db).syntax().cloned())
-            }
-            ModuleDefId::FunctionId(function_id) => {
-                Some(function_id.lookup(&db).source(&db).syntax().cloned())
-            }
-            _ => None,
-        };
-        if let Some(src) = src {
+        if let ModuleDefId::AdtId(AdtId::StructId(struct_id)) = decl_id {
+            let src = struct_id.lookup(&db).source(&db);
             if src.file_id.is_attr_macro(&db) || src.file_id.is_custom_derive(&db) {
-                let pp = pretty_print_macro_expansion(src.value, None);
+                let pp = pretty_print_macro_expansion(src.value.syntax().clone(), None);
                 format_to!(expanded_text, "\n{}", pp)
             }
         }
@@ -327,25 +304,3 @@ fn pretty_print_macro_expansion(expn: SyntaxNode, map: Option<&TokenMap>) -> Str
     }
     res
 }
-
-// Identity mapping, but only works when the input is syntactically valid. This
-// simulates common proc macros that unnecessarily parse their input and return
-// compile errors.
-#[derive(Debug)]
-struct IdentityWhenValidProcMacroExpander;
-impl base_db::ProcMacroExpander for IdentityWhenValidProcMacroExpander {
-    fn expand(
-        &self,
-        subtree: &Subtree,
-        _: Option<&Subtree>,
-        _: &base_db::Env,
-    ) -> Result<Subtree, base_db::ProcMacroExpansionError> {
-        let (parse, _) =
-            ::mbe::token_tree_to_syntax_node(subtree, ::mbe::TopEntryPoint::MacroItems);
-        if parse.errors().is_empty() {
-            Ok(subtree.clone())
-        } else {
-            panic!("got invalid macro input: {:?}", parse.errors());
-        }
-    }
-}
diff --git a/crates/hir_def/src/macro_expansion_tests/mbe/tt_conversion.rs b/crates/hir_def/src/macro_expansion_tests/mbe/tt_conversion.rs
index 84cc3f3872..5f4b7d6d0b 100644
--- a/crates/hir_def/src/macro_expansion_tests/mbe/tt_conversion.rs
+++ b/crates/hir_def/src/macro_expansion_tests/mbe/tt_conversion.rs
@@ -108,18 +108,18 @@ fn expansion_does_not_parse_as_expression() {
     check(
         r#"
 macro_rules! stmts {
-    () => { fn foo() {} }
+    () => { let _ = 0; }
 }
 
 fn f() { let _ = stmts!/*+errors*/(); }
 "#,
         expect![[r#"
 macro_rules! stmts {
-    () => { fn foo() {} }
+    () => { let _ = 0; }
 }
 
 fn f() { let _ = /* parse error: expected expression */
-fn foo() {}; }
+let _ = 0;; }
 "#]],
     )
 }
diff --git a/crates/hir_def/src/macro_expansion_tests/proc_macros.rs b/crates/hir_def/src/macro_expansion_tests/proc_macros.rs
index 0ca30fb799..901872edda 100644
--- a/crates/hir_def/src/macro_expansion_tests/proc_macros.rs
+++ b/crates/hir_def/src/macro_expansion_tests/proc_macros.rs
@@ -52,43 +52,3 @@ struct S;
 #[attr2] struct S;"##]],
     );
 }
-
-#[test]
-fn attribute_macro_syntax_completion_1() {
-    // this is just the case where the input is actually valid
-    check(
-        r#"
-//- proc_macros: identity_when_valid
-#[proc_macros::identity_when_valid]
-fn foo() { bar.baz(); blub }
-"#,
-        expect![[r##"
-#[proc_macros::identity_when_valid]
-fn foo() { bar.baz(); blub }
-
-fn foo() {
-    bar.baz();
-    blub
-}"##]],
-    );
-}
-
-#[test]
-fn attribute_macro_syntax_completion_2() {
-    // common case of dot completion while typing
-    check(
-        r#"
-//- proc_macros: identity_when_valid
-#[proc_macros::identity_when_valid]
-fn foo() { bar.; blub }
-"#,
-        expect![[r##"
-#[proc_macros::identity_when_valid]
-fn foo() { bar.; blub }
-
-fn foo() {
-    bar. ;
-    blub
-}"##]],
-    );
-}
diff --git a/crates/hir_ty/src/tests/macros.rs b/crates/hir_ty/src/tests/macros.rs
index 344e7293c5..a61175f273 100644
--- a/crates/hir_ty/src/tests/macros.rs
+++ b/crates/hir_ty/src/tests/macros.rs
@@ -190,6 +190,7 @@ fn expr_macro_def_expanded_in_various_places() {
             !0..6 '1isize': isize
             !0..6 '1isize': isize
             !0..6 '1isize': isize
+            !0..6 '1isize': isize
             39..442 '{     ...!(); }': ()
             73..94 'spam!(...am!())': {unknown}
             100..119 'for _ ...!() {}': ()
@@ -197,7 +198,6 @@ fn expr_macro_def_expanded_in_various_places() {
             117..119 '{}': ()
             124..134 '|| spam!()': || -> isize
             140..156 'while ...!() {}': ()
-            146..153 'spam!()': bool
             154..156 '{}': ()
             161..174 'break spam!()': !
             180..194 'return spam!()': !
@@ -271,6 +271,7 @@ fn expr_macro_rules_expanded_in_various_places() {
             !0..6 '1isize': isize
             !0..6 '1isize': isize
             !0..6 '1isize': isize
+            !0..6 '1isize': isize
             53..456 '{     ...!(); }': ()
             87..108 'spam!(...am!())': {unknown}
             114..133 'for _ ...!() {}': ()
@@ -278,7 +279,6 @@ fn expr_macro_rules_expanded_in_various_places() {
             131..133 '{}': ()
             138..148 '|| spam!()': || -> isize
             154..170 'while ...!() {}': ()
-            160..167 'spam!()': bool
             168..170 '{}': ()
             175..188 'break spam!()': !
             194..208 'return spam!()': !
diff --git a/crates/hir_ty/src/tests/patterns.rs b/crates/hir_ty/src/tests/patterns.rs
index acdd8f50ef..fb687cf20c 100644
--- a/crates/hir_ty/src/tests/patterns.rs
+++ b/crates/hir_ty/src/tests/patterns.rs
@@ -55,7 +55,6 @@ fn infer_pattern() {
             139..140 'g': {unknown}
             143..144 'e': {unknown}
             157..204 'if let...     }': ()
-            160..175 'let [val] = opt': bool
             164..169 '[val]': [{unknown}]
             165..168 'val': {unknown}
             172..175 'opt': [{unknown}]
@@ -63,7 +62,6 @@ fn infer_pattern() {
             190..191 'h': {unknown}
             194..197 'val': {unknown}
             210..236 'if let...rue {}': ()
-            213..233 'let x ... &true': bool
             217..225 'x @ true': &bool
             221..225 'true': bool
             221..225 'true': bool
@@ -113,42 +111,36 @@ fn infer_literal_pattern() {
             37..38 'x': &i32
             46..208 '{     ...) {} }': ()
             52..75 'if let...y() {}': ()
-            55..72 'let "f... any()': bool
             59..64 '"foo"': &str
             59..64 '"foo"': &str
             67..70 'any': fn any<&str>() -> &str
             67..72 'any()': &str
             73..75 '{}': ()
             80..99 'if let...y() {}': ()
-            83..96 'let 1 = any()': bool
             87..88 '1': i32
             87..88 '1': i32
             91..94 'any': fn any<i32>() -> i32
             91..96 'any()': i32
             97..99 '{}': ()
             104..126 'if let...y() {}': ()
-            107..123 'let 1u... any()': bool
             111..115 '1u32': u32
             111..115 '1u32': u32
             118..121 'any': fn any<u32>() -> u32
             118..123 'any()': u32
             124..126 '{}': ()
             131..153 'if let...y() {}': ()
-            134..150 'let 1f... any()': bool
             138..142 '1f32': f32
             138..142 '1f32': f32
             145..148 'any': fn any<f32>() -> f32
             145..150 'any()': f32
             151..153 '{}': ()
             158..179 'if let...y() {}': ()
-            161..176 'let 1.0 = any()': bool
             165..168 '1.0': f64
             165..168 '1.0': f64
             171..174 'any': fn any<f64>() -> f64
             171..176 'any()': f64
             177..179 '{}': ()
             184..206 'if let...y() {}': ()
-            187..203 'let tr... any()': bool
             191..195 'true': bool
             191..195 'true': bool
             198..201 'any': fn any<bool>() -> bool
@@ -171,12 +163,10 @@ fn infer_range_pattern() {
             8..9 'x': &i32
             17..75 '{     ...2 {} }': ()
             23..45 'if let...u32 {}': ()
-            26..42 'let 1....= 2u32': bool
             30..35 '1..76': u32
             38..42 '2u32': u32
             43..45 '{}': ()
             50..73 'if let...u32 {}': ()
-            53..70 'let 1....= 2u32': bool
             57..63 '1..=76': u32
             66..70 '2u32': u32
             71..73 '{}': ()
@@ -900,32 +890,6 @@ fn main() {
     );
 }
 
-#[test]
-fn slice_pattern_correctly_handles_array_length() {
-    check_infer(
-        r#"
-fn main() {
-    let [head, middle @ .., tail, tail2] = [1, 2, 3, 4, 5];
-}
-    "#,
-        expect![[r#"
-            10..73 '{     ... 5]; }': ()
-            20..52 '[head,...tail2]': [i32; 5]
-            21..25 'head': i32
-            27..38 'middle @ ..': [i32; 2]
-            36..38 '..': [i32; 2]
-            40..44 'tail': i32
-            46..51 'tail2': i32
-            55..70 '[1, 2, 3, 4, 5]': [i32; 5]
-            56..57 '1': i32
-            59..60 '2': i32
-            62..63 '3': i32
-            65..66 '4': i32
-            68..69 '5': i32
-        "#]],
-    );
-}
-
 #[test]
 fn pattern_lookup_in_value_ns() {
     check_types(
diff --git a/crates/hir_ty/src/tests/simple.rs b/crates/hir_ty/src/tests/simple.rs
index c11a70fa66..f4d082ea8c 100644
--- a/crates/hir_ty/src/tests/simple.rs
+++ b/crates/hir_ty/src/tests/simple.rs
@@ -2248,7 +2248,6 @@ fn generic_default_in_struct_literal() {
             176..193 'Thing ...1i32 }': Thing<i32>
             187..191 '1i32': i32
             199..240 'if let...     }': ()
-            202..221 'let Th... } = z': bool
             206..217 'Thing { t }': Thing<i32>
             214..215 't': i32
             220..221 'z': Thing<i32>
diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs
index c5c531c30b..ed76c84ab4 100644
--- a/crates/ide/src/hover/tests.rs
+++ b/crates/ide/src/hover/tests.rs
@@ -1310,60 +1310,6 @@ fn test_hover_function_show_qualifiers() {
     );
 }
 
-#[test]
-fn test_hover_function_show_types() {
-    check(
-        r#"fn foo$0(a: i32, b:i32) -> i32 { 0 }"#,
-        expect![[r#"
-                *foo*
-
-                ```rust
-                test
-                ```
-
-                ```rust
-                fn foo(a: i32, b: i32) -> i32
-                ```
-            "#]],
-    );
-}
-
-#[test]
-fn test_hover_function_pointer_show_identifiers() {
-    check(
-        r#"type foo$0 = fn(a: i32, b: i32) -> i32;"#,
-        expect![[r#"
-                *foo*
-
-                ```rust
-                test
-                ```
-
-                ```rust
-                type foo = fn(a: i32, b: i32) -> i32
-                ```
-            "#]],
-    );
-}
-
-#[test]
-fn test_hover_function_pointer_no_identifier() {
-    check(
-        r#"type foo$0 = fn(i32, _: i32) -> i32;"#,
-        expect![[r#"
-                *foo*
-
-                ```rust
-                test
-                ```
-
-                ```rust
-                type foo = fn(i32, i32) -> i32
-                ```
-            "#]],
-    );
-}
-
 #[test]
 fn test_hover_trait_show_qualifiers() {
     check_actions(
diff --git a/crates/ide_completion/src/tests/attribute.rs b/crates/ide_completion/src/tests/attribute.rs
index 2e643453af..5a9c48a327 100644
--- a/crates/ide_completion/src/tests/attribute.rs
+++ b/crates/ide_completion/src/tests/attribute.rs
@@ -62,7 +62,8 @@ fn proc_macros_qualified() {
 struct Foo;
 "#,
         expect![[r#"
-            at identity pub macro identity
+            at input_replace pub macro input_replace
+            at identity      pub macro identity
         "#]],
     )
 }
diff --git a/crates/parser/src/tests/top_entries.rs b/crates/parser/src/tests/top_entries.rs
index eb640dc7fc..24e41b46f8 100644
--- a/crates/parser/src/tests/top_entries.rs
+++ b/crates/parser/src/tests/top_entries.rs
@@ -289,19 +289,17 @@ fn expr() {
         TopEntryPoint::Expr,
         "let _ = 0;",
         expect![[r#"
-            ERROR
-              LET_EXPR
-                LET_KW "let"
-                WHITESPACE " "
-                WILDCARD_PAT
-                  UNDERSCORE "_"
-                WHITESPACE " "
-                EQ "="
-                WHITESPACE " "
-                LITERAL
-                  INT_NUMBER "0"
-              SEMICOLON ";"
-        "#]],
+        ERROR
+          LET_KW "let"
+          WHITESPACE " "
+          UNDERSCORE "_"
+          WHITESPACE " "
+          EQ "="
+          WHITESPACE " "
+          INT_NUMBER "0"
+          SEMICOLON ";"
+        error 0: expected expression
+    "#]],
     );
 }
 
diff --git a/crates/parser/test_data/parser/err/0008_item_block_recovery.txt b/crates/parser/test_data/parser/err/0008_item_block_recovery.txt
index 60b2fe9875..6dd70e7cd9 100644
--- a/crates/parser/test_data/parser/err/0008_item_block_recovery.txt
+++ b/crates/parser/test_data/parser/err/0008_item_block_recovery.txt
@@ -29,8 +29,9 @@ SOURCE_FILE
     IF_EXPR
       IF_KW "if"
       WHITESPACE " "
-      LITERAL
-        TRUE_KW "true"
+      CONDITION
+        LITERAL
+          TRUE_KW "true"
       WHITESPACE " "
       BLOCK_EXPR
         STMT_LIST
diff --git a/crates/parser/test_data/parser/err/0019_let_recover.rs b/crates/parser/test_data/parser/err/0019_let_recover.rs
index 5108d5a49b..48bf3d68bd 100644
--- a/crates/parser/test_data/parser/err/0019_let_recover.rs
+++ b/crates/parser/test_data/parser/err/0019_let_recover.rs
@@ -1,5 +1,5 @@
 fn foo() {
-    let foo = 11
+    let foo =
     let bar = 1;
     let
     let baz = 92;
diff --git a/crates/parser/test_data/parser/err/0019_let_recover.txt b/crates/parser/test_data/parser/err/0019_let_recover.txt
index 7d62e0cc14..25722b1355 100644
--- a/crates/parser/test_data/parser/err/0019_let_recover.txt
+++ b/crates/parser/test_data/parser/err/0019_let_recover.txt
@@ -20,9 +20,6 @@ SOURCE_FILE
               IDENT "foo"
           WHITESPACE " "
           EQ "="
-          WHITESPACE " "
-          LITERAL
-            INT_NUMBER "11"
         WHITESPACE "\n    "
         LET_STMT
           LET_KW "let"
@@ -60,8 +57,9 @@ SOURCE_FILE
           IF_EXPR
             IF_KW "if"
             WHITESPACE " "
-            LITERAL
-              TRUE_KW "true"
+            CONDITION
+              LITERAL
+                TRUE_KW "true"
             WHITESPACE " "
             BLOCK_EXPR
               STMT_LIST
@@ -75,8 +73,9 @@ SOURCE_FILE
           WHILE_EXPR
             WHILE_KW "while"
             WHITESPACE " "
-            LITERAL
-              TRUE_KW "true"
+            CONDITION
+              LITERAL
+                TRUE_KW "true"
             WHITESPACE " "
             BLOCK_EXPR
               STMT_LIST
@@ -96,12 +95,13 @@ SOURCE_FILE
         WHITESPACE "\n"
         R_CURLY "}"
   WHITESPACE "\n"
-error 27: expected SEMICOLON
-error 52: expected pattern
-error 52: expected SEMICOLON
-error 78: expected pattern
-error 78: expected SEMICOLON
-error 101: expected pattern
-error 101: expected SEMICOLON
-error 127: expected pattern
-error 127: expected SEMICOLON
+error 24: expected expression
+error 24: expected SEMICOLON
+error 49: expected pattern
+error 49: expected SEMICOLON
+error 75: expected pattern
+error 75: expected SEMICOLON
+error 98: expected pattern
+error 98: expected SEMICOLON
+error 124: expected pattern
+error 124: expected SEMICOLON
diff --git a/crates/parser/test_data/parser/err/0024_many_type_parens.txt b/crates/parser/test_data/parser/err/0024_many_type_parens.txt
index 82e6a11249..446e1a8233 100644
--- a/crates/parser/test_data/parser/err/0024_many_type_parens.txt
+++ b/crates/parser/test_data/parser/err/0024_many_type_parens.txt
@@ -180,118 +180,116 @@ SOURCE_FILE
         ERROR
           PLUS "+"
         WHITESPACE " "
-        TUPLE_EXPR
-          L_PAREN "("
-          FOR_EXPR
-            FOR_KW "for"
-            PATH_PAT
-              PATH
-                PATH_SEGMENT
-                  L_ANGLE "<"
-                  ERROR
-                    LIFETIME_IDENT "'a"
-                  R_ANGLE ">"
-            WHITESPACE " "
-            BIN_EXPR
+        EXPR_STMT
+          TUPLE_EXPR
+            L_PAREN "("
+            FOR_EXPR
+              FOR_KW "for"
+              PATH_PAT
+                PATH
+                  PATH_SEGMENT
+                    L_ANGLE "<"
+                    ERROR
+                      LIFETIME_IDENT "'a"
+                    R_ANGLE ">"
+              WHITESPACE " "
               BIN_EXPR
                 BIN_EXPR
                   BIN_EXPR
+                    BIN_EXPR
+                      PATH_EXPR
+                        PATH
+                          PATH_SEGMENT
+                            NAME_REF
+                              IDENT "Trait"
+                      L_ANGLE "<"
+                      ERROR
+                        LIFETIME_IDENT "'a"
+                    R_ANGLE ">"
+                    ERROR
+                      R_PAREN ")"
+                  WHITESPACE " "
+                  PLUS "+"
+                  WHITESPACE " "
+                  PAREN_EXPR
+                    L_PAREN "("
                     PATH_EXPR
                       PATH
                         PATH_SEGMENT
                           NAME_REF
-                            IDENT "Trait"
-                    L_ANGLE "<"
-                    ERROR
-                      LIFETIME_IDENT "'a"
-                  R_ANGLE ">"
-                  ERROR
+                            IDENT "Copy"
                     R_PAREN ")"
-                WHITESPACE " "
-                PLUS "+"
-                WHITESPACE " "
-                PAREN_EXPR
-                  L_PAREN "("
-                  PATH_EXPR
-                    PATH
-                      PATH_SEGMENT
-                        NAME_REF
-                          IDENT "Copy"
-                  R_PAREN ")"
-              R_ANGLE ">"
-              ERROR
-                SEMICOLON ";"
-          WHITESPACE "\n    "
-          LET_EXPR
-            LET_KW "let"
-            WHITESPACE " "
-            WILDCARD_PAT
-              UNDERSCORE "_"
-            ERROR
-              COLON ":"
+                R_ANGLE ">"
+                ERROR
+                  SEMICOLON ";"
+        WHITESPACE "\n    "
+        LET_STMT
+          LET_KW "let"
           WHITESPACE " "
-          BIN_EXPR
-            BIN_EXPR
-              PATH_EXPR
-                PATH
-                  PATH_SEGMENT
-                    NAME_REF
-                      IDENT "Box"
-              L_ANGLE "<"
-              TUPLE_EXPR
-                L_PAREN "("
-                FOR_EXPR
-                  FOR_KW "for"
-                  PATH_PAT
-                    PATH
-                      PATH_SEGMENT
+          WILDCARD_PAT
+            UNDERSCORE "_"
+          COLON ":"
+          WHITESPACE " "
+          DYN_TRAIT_TYPE
+            TYPE_BOUND_LIST
+              TYPE_BOUND
+                PATH_TYPE
+                  PATH
+                    PATH_SEGMENT
+                      NAME_REF
+                        IDENT "Box"
+                      GENERIC_ARG_LIST
                         L_ANGLE "<"
-                        ERROR
-                          LIFETIME_IDENT "'a"
-                        R_ANGLE ">"
-                  WHITESPACE " "
-                  BIN_EXPR
-                    BIN_EXPR
-                      BIN_EXPR
-                        BIN_EXPR
-                          PATH_EXPR
-                            PATH
-                              PATH_SEGMENT
-                                NAME_REF
-                                  IDENT "Trait"
-                          L_ANGLE "<"
-                          ERROR
-                            LIFETIME_IDENT "'a"
-                        R_ANGLE ">"
-                        ERROR
-                          R_PAREN ")"
-                      WHITESPACE " "
-                      PLUS "+"
-                      WHITESPACE " "
-                      PAREN_EXPR
-                        L_PAREN "("
-                        PATH_EXPR
-                          PATH
-                            PATH_SEGMENT
-                              NAME_REF
-                                IDENT "Copy"
-                        R_PAREN ")"
-                    WHITESPACE " "
-                    PLUS "+"
-                    WHITESPACE " "
-                    PAREN_EXPR
-                      L_PAREN "("
-                      ERROR
-                        QUESTION "?"
-                PATH_EXPR
+                        TYPE_ARG
+                          PAREN_TYPE
+                            L_PAREN "("
+                            FOR_TYPE
+                              FOR_KW "for"
+                              GENERIC_PARAM_LIST
+                                L_ANGLE "<"
+                                LIFETIME_PARAM
+                                  LIFETIME
+                                    LIFETIME_IDENT "'a"
+                                R_ANGLE ">"
+                              WHITESPACE " "
+                              PATH_TYPE
+                                PATH
+                                  PATH_SEGMENT
+                                    NAME_REF
+                                      IDENT "Trait"
+                                    GENERIC_ARG_LIST
+                                      L_ANGLE "<"
+                                      LIFETIME_ARG
+                                        LIFETIME
+                                          LIFETIME_IDENT "'a"
+                                      R_ANGLE ">"
+                            R_PAREN ")"
+              WHITESPACE " "
+              PLUS "+"
+              WHITESPACE " "
+              TYPE_BOUND
+                L_PAREN "("
+                PATH_TYPE
+                  PATH
+                    PATH_SEGMENT
+                      NAME_REF
+                        IDENT "Copy"
+                R_PAREN ")"
+              WHITESPACE " "
+              PLUS "+"
+              WHITESPACE " "
+              TYPE_BOUND
+                L_PAREN "("
+                QUESTION "?"
+                PATH_TYPE
                   PATH
                     PATH_SEGMENT
                       NAME_REF
                         IDENT "Sized"
                 R_PAREN ")"
-            R_ANGLE ">"
-            ERROR
-              SEMICOLON ";"
+        ERROR
+          R_ANGLE ">"
+        SEMICOLON ";"
         WHITESPACE "\n"
         R_CURLY "}"
   WHITESPACE "\n"
@@ -314,18 +312,10 @@ error 168: expected expression
 error 179: expected expression
 error 180: expected a block
 error 180: expected COMMA
-error 190: expected EQ
-error 190: expected expression
-error 191: expected COMMA
-error 201: expected type
-error 204: expected IN_KW
-error 211: expected expression
-error 214: expected expression
-error 228: expected expression
-error 229: expected R_PAREN
-error 229: expected a block
-error 229: expected COMMA
-error 236: expected expression
-error 237: expected COMMA
-error 237: expected expression
-error 237: expected R_PAREN
+error 180: expected expression
+error 180: expected R_PAREN
+error 180: expected SEMICOLON
+error 215: expected COMMA
+error 215: expected R_ANGLE
+error 235: expected SEMICOLON
+error 235: expected expression
diff --git a/crates/parser/test_data/parser/inline/ok/0030_cond.rast b/crates/parser/test_data/parser/inline/ok/0030_cond.rast
new file mode 100644
index 0000000000..3aa330f55d
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/ok/0030_cond.rast
@@ -0,0 +1,209 @@
+SOURCE_FILE@0..197
+  FN@0..37
+    FN_KW@0..2 "fn"
+    WHITESPACE@2..3 " "
+    NAME@3..6
+      IDENT@3..6 "foo"
+    PARAM_LIST@6..8
+      L_PAREN@6..7 "("
+      R_PAREN@7..8 ")"
+    WHITESPACE@8..9 " "
+    BLOCK_EXPR@9..37
+      STMT_LIST@9..37
+        L_CURLY@9..10 "{"
+        WHITESPACE@10..11 " "
+        IF_EXPR@11..35
+          IF_KW@11..13 "if"
+          WHITESPACE@13..14 " "
+          CONDITION@14..32
+            LET_KW@14..17 "let"
+            WHITESPACE@17..18 " "
+            TUPLE_STRUCT_PAT@18..25
+              PATH@18..22
+                PATH_SEGMENT@18..22
+                  NAME_REF@18..22
+                    IDENT@18..22 "Some"
+              L_PAREN@22..23 "("
+              WILDCARD_PAT@23..24
+                UNDERSCORE@23..24 "_"
+              R_PAREN@24..25 ")"
+            WHITESPACE@25..26 " "
+            EQ@26..27 "="
+            WHITESPACE@27..28 " "
+            PATH_EXPR@28..32
+              PATH@28..32
+                PATH_SEGMENT@28..32
+                  NAME_REF@28..32
+                    IDENT@28..32 "None"
+          WHITESPACE@32..33 " "
+          BLOCK_EXPR@33..35
+            STMT_LIST@33..35
+              L_CURLY@33..34 "{"
+              R_CURLY@34..35 "}"
+        WHITESPACE@35..36 " "
+        R_CURLY@36..37 "}"
+  WHITESPACE@37..38 "\n"
+  FN@38..196
+    FN_KW@38..40 "fn"
+    WHITESPACE@40..41 " "
+    NAME@41..44
+      IDENT@41..44 "bar"
+    PARAM_LIST@44..46
+      L_PAREN@44..45 "("
+      R_PAREN@45..46 ")"
+    WHITESPACE@46..47 " "
+    BLOCK_EXPR@47..196
+      STMT_LIST@47..196
+        L_CURLY@47..48 "{"
+        WHITESPACE@48..53 "\n    "
+        EXPR_STMT@53..87
+          IF_EXPR@53..87
+            IF_KW@53..55 "if"
+            WHITESPACE@55..56 " "
+            CONDITION@56..84
+              LET_KW@56..59 "let"
+              WHITESPACE@59..60 " "
+              OR_PAT@60..77
+                TUPLE_STRUCT_PAT@60..67
+                  PATH@60..64
+                    PATH_SEGMENT@60..64
+                      NAME_REF@60..64
+                        IDENT@60..64 "Some"
+                  L_PAREN@64..65 "("
+                  WILDCARD_PAT@65..66
+                    UNDERSCORE@65..66 "_"
+                  R_PAREN@66..67 ")"
+                WHITESPACE@67..68 " "
+                PIPE@68..69 "|"
+                WHITESPACE@69..70 " "
+                TUPLE_STRUCT_PAT@70..77
+                  PATH@70..74
+                    PATH_SEGMENT@70..74
+                      NAME_REF@70..74
+                        IDENT@70..74 "Some"
+                  L_PAREN@74..75 "("
+                  WILDCARD_PAT@75..76
+                    UNDERSCORE@75..76 "_"
+                  R_PAREN@76..77 ")"
+              WHITESPACE@77..78 " "
+              EQ@78..79 "="
+              WHITESPACE@79..80 " "
+              PATH_EXPR@80..84
+                PATH@80..84
+                  PATH_SEGMENT@80..84
+                    NAME_REF@80..84
+                      IDENT@80..84 "None"
+            WHITESPACE@84..85 " "
+            BLOCK_EXPR@85..87
+              STMT_LIST@85..87
+                L_CURLY@85..86 "{"
+                R_CURLY@86..87 "}"
+        WHITESPACE@87..92 "\n    "
+        EXPR_STMT@92..118
+          IF_EXPR@92..118
+            IF_KW@92..94 "if"
+            WHITESPACE@94..95 " "
+            CONDITION@95..115
+              LET_KW@95..98 "let"
+              WHITESPACE@98..99 " "
+              PIPE@99..100 "|"
+              WHITESPACE@100..101 " "
+              TUPLE_STRUCT_PAT@101..108
+                PATH@101..105
+                  PATH_SEGMENT@101..105
+                    NAME_REF@101..105
+                      IDENT@101..105 "Some"
+                L_PAREN@105..106 "("
+                WILDCARD_PAT@106..107
+                  UNDERSCORE@106..107 "_"
+                R_PAREN@107..108 ")"
+              WHITESPACE@108..109 " "
+              EQ@109..110 "="
+              WHITESPACE@110..111 " "
+              PATH_EXPR@111..115
+                PATH@111..115
+                  PATH_SEGMENT@111..115
+                    NAME_REF@111..115
+                      IDENT@111..115 "None"
+            WHITESPACE@115..116 " "
+            BLOCK_EXPR@116..118
+              STMT_LIST@116..118
+                L_CURLY@116..117 "{"
+                R_CURLY@117..118 "}"
+        WHITESPACE@118..123 "\n    "
+        EXPR_STMT@123..160
+          WHILE_EXPR@123..160
+            WHILE_KW@123..128 "while"
+            WHITESPACE@128..129 " "
+            CONDITION@129..157
+              LET_KW@129..132 "let"
+              WHITESPACE@132..133 " "
+              OR_PAT@133..150
+                TUPLE_STRUCT_PAT@133..140
+                  PATH@133..137
+                    PATH_SEGMENT@133..137
+                      NAME_REF@133..137
+                        IDENT@133..137 "Some"
+                  L_PAREN@137..138 "("
+                  WILDCARD_PAT@138..139
+                    UNDERSCORE@138..139 "_"
+                  R_PAREN@139..140 ")"
+                WHITESPACE@140..141 " "
+                PIPE@141..142 "|"
+                WHITESPACE@142..143 " "
+                TUPLE_STRUCT_PAT@143..150
+                  PATH@143..147
+                    PATH_SEGMENT@143..147
+                      NAME_REF@143..147
+                        IDENT@143..147 "Some"
+                  L_PAREN@147..148 "("
+                  WILDCARD_PAT@148..149
+                    UNDERSCORE@148..149 "_"
+                  R_PAREN@149..150 ")"
+              WHITESPACE@150..151 " "
+              EQ@151..152 "="
+              WHITESPACE@152..153 " "
+              PATH_EXPR@153..157
+                PATH@153..157
+                  PATH_SEGMENT@153..157
+                    NAME_REF@153..157
+                      IDENT@153..157 "None"
+            WHITESPACE@157..158 " "
+            BLOCK_EXPR@158..160
+              STMT_LIST@158..160
+                L_CURLY@158..159 "{"
+                R_CURLY@159..160 "}"
+        WHITESPACE@160..165 "\n    "
+        WHILE_EXPR@165..194
+          WHILE_KW@165..170 "while"
+          WHITESPACE@170..171 " "
+          CONDITION@171..191
+            LET_KW@171..174 "let"
+            WHITESPACE@174..175 " "
+            PIPE@175..176 "|"
+            WHITESPACE@176..177 " "
+            TUPLE_STRUCT_PAT@177..184
+              PATH@177..181
+                PATH_SEGMENT@177..181
+                  NAME_REF@177..181
+                    IDENT@177..181 "Some"
+              L_PAREN@181..182 "("
+              WILDCARD_PAT@182..183
+                UNDERSCORE@182..183 "_"
+              R_PAREN@183..184 ")"
+            WHITESPACE@184..185 " "
+            EQ@185..186 "="
+            WHITESPACE@186..187 " "
+            PATH_EXPR@187..191
+              PATH@187..191
+                PATH_SEGMENT@187..191
+                  NAME_REF@187..191
+                    IDENT@187..191 "None"
+          WHITESPACE@191..192 " "
+          BLOCK_EXPR@192..194
+            STMT_LIST@192..194
+              L_CURLY@192..193 "{"
+              R_CURLY@193..194 "}"
+        WHITESPACE@194..195 "\n"
+        R_CURLY@195..196 "}"
+  WHITESPACE@196..197 "\n"
diff --git a/crates/parser/test_data/parser/inline/ok/0030_cond.rs b/crates/parser/test_data/parser/inline/ok/0030_cond.rs
new file mode 100644
index 0000000000..2552a2621f
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/ok/0030_cond.rs
@@ -0,0 +1,7 @@
+fn foo() { if let Some(_) = None {} }
+fn bar() {
+    if let Some(_) | Some(_) = None {}
+    if let | Some(_) = None {}
+    while let Some(_) | Some(_) = None {}
+    while let | Some(_) = None {}
+}
diff --git a/crates/parser/test_data/parser/inline/ok/0030_cond.txt b/crates/parser/test_data/parser/inline/ok/0030_cond.txt
new file mode 100644
index 0000000000..5b89937013
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/ok/0030_cond.txt
@@ -0,0 +1,209 @@
+SOURCE_FILE
+  FN
+    FN_KW "fn"
+    WHITESPACE " "
+    NAME
+      IDENT "foo"
+    PARAM_LIST
+      L_PAREN "("
+      R_PAREN ")"
+    WHITESPACE " "
+    BLOCK_EXPR
+      STMT_LIST
+        L_CURLY "{"
+        WHITESPACE " "
+        IF_EXPR
+          IF_KW "if"
+          WHITESPACE " "
+          CONDITION
+            LET_KW "let"
+            WHITESPACE " "
+            TUPLE_STRUCT_PAT
+              PATH
+                PATH_SEGMENT
+                  NAME_REF
+                    IDENT "Some"
+              L_PAREN "("
+              WILDCARD_PAT
+                UNDERSCORE "_"
+              R_PAREN ")"
+            WHITESPACE " "
+            EQ "="
+            WHITESPACE " "
+            PATH_EXPR
+              PATH
+                PATH_SEGMENT
+                  NAME_REF
+                    IDENT "None"
+          WHITESPACE " "
+          BLOCK_EXPR
+            STMT_LIST
+              L_CURLY "{"
+              R_CURLY "}"
+        WHITESPACE " "
+        R_CURLY "}"
+  WHITESPACE "\n"
+  FN
+    FN_KW "fn"
+    WHITESPACE " "
+    NAME
+      IDENT "bar"
+    PARAM_LIST
+      L_PAREN "("
+      R_PAREN ")"
+    WHITESPACE " "
+    BLOCK_EXPR
+      STMT_LIST
+        L_CURLY "{"
+        WHITESPACE "\n    "
+        EXPR_STMT
+          IF_EXPR
+            IF_KW "if"
+            WHITESPACE " "
+            CONDITION
+              LET_KW "let"
+              WHITESPACE " "
+              OR_PAT
+                TUPLE_STRUCT_PAT
+                  PATH
+                    PATH_SEGMENT
+                      NAME_REF
+                        IDENT "Some"
+                  L_PAREN "("
+                  WILDCARD_PAT
+                    UNDERSCORE "_"
+                  R_PAREN ")"
+                WHITESPACE " "
+                PIPE "|"
+                WHITESPACE " "
+                TUPLE_STRUCT_PAT
+                  PATH
+                    PATH_SEGMENT
+                      NAME_REF
+                        IDENT "Some"
+                  L_PAREN "("
+                  WILDCARD_PAT
+                    UNDERSCORE "_"
+                  R_PAREN ")"
+              WHITESPACE " "
+              EQ "="
+              WHITESPACE " "
+              PATH_EXPR
+                PATH
+                  PATH_SEGMENT
+                    NAME_REF
+                      IDENT "None"
+            WHITESPACE " "
+            BLOCK_EXPR
+              STMT_LIST
+                L_CURLY "{"
+                R_CURLY "}"
+        WHITESPACE "\n    "
+        EXPR_STMT
+          IF_EXPR
+            IF_KW "if"
+            WHITESPACE " "
+            CONDITION
+              LET_KW "let"
+              WHITESPACE " "
+              PIPE "|"
+              WHITESPACE " "
+              TUPLE_STRUCT_PAT
+                PATH
+                  PATH_SEGMENT
+                    NAME_REF
+                      IDENT "Some"
+                L_PAREN "("
+                WILDCARD_PAT
+                  UNDERSCORE "_"
+                R_PAREN ")"
+              WHITESPACE " "
+              EQ "="
+              WHITESPACE " "
+              PATH_EXPR
+                PATH
+                  PATH_SEGMENT
+                    NAME_REF
+                      IDENT "None"
+            WHITESPACE " "
+            BLOCK_EXPR
+              STMT_LIST
+                L_CURLY "{"
+                R_CURLY "}"
+        WHITESPACE "\n    "
+        EXPR_STMT
+          WHILE_EXPR
+            WHILE_KW "while"
+            WHITESPACE " "
+            CONDITION
+              LET_KW "let"
+              WHITESPACE " "
+              OR_PAT
+                TUPLE_STRUCT_PAT
+                  PATH
+                    PATH_SEGMENT
+                      NAME_REF
+                        IDENT "Some"
+                  L_PAREN "("
+                  WILDCARD_PAT
+                    UNDERSCORE "_"
+                  R_PAREN ")"
+                WHITESPACE " "
+                PIPE "|"
+                WHITESPACE " "
+                TUPLE_STRUCT_PAT
+                  PATH
+                    PATH_SEGMENT
+                      NAME_REF
+                        IDENT "Some"
+                  L_PAREN "("
+                  WILDCARD_PAT
+                    UNDERSCORE "_"
+                  R_PAREN ")"
+              WHITESPACE " "
+              EQ "="
+              WHITESPACE " "
+              PATH_EXPR
+                PATH
+                  PATH_SEGMENT
+                    NAME_REF
+                      IDENT "None"
+            WHITESPACE " "
+            BLOCK_EXPR
+              STMT_LIST
+                L_CURLY "{"
+                R_CURLY "}"
+        WHITESPACE "\n    "
+        WHILE_EXPR
+          WHILE_KW "while"
+          WHITESPACE " "
+          CONDITION
+            LET_KW "let"
+            WHITESPACE " "
+            PIPE "|"
+            WHITESPACE " "
+            TUPLE_STRUCT_PAT
+              PATH
+                PATH_SEGMENT
+                  NAME_REF
+                    IDENT "Some"
+              L_PAREN "("
+              WILDCARD_PAT
+                UNDERSCORE "_"
+              R_PAREN ")"
+            WHITESPACE " "
+            EQ "="
+            WHITESPACE " "
+            PATH_EXPR
+              PATH
+                PATH_SEGMENT
+                  NAME_REF
+                    IDENT "None"
+          WHITESPACE " "
+          BLOCK_EXPR
+            STMT_LIST
+              L_CURLY "{"
+              R_CURLY "}"
+        WHITESPACE "\n"
+        R_CURLY "}"
+  WHITESPACE "\n"
diff --git a/crates/parser/test_data/parser/inline/ok/0030_let_expr.rast b/crates/parser/test_data/parser/inline/ok/0030_let_expr.rast
deleted file mode 100644
index dcffcb1ce2..0000000000
--- a/crates/parser/test_data/parser/inline/ok/0030_let_expr.rast
+++ /dev/null
@@ -1,90 +0,0 @@
-SOURCE_FILE
-  FN
-    FN_KW "fn"
-    WHITESPACE " "
-    NAME
-      IDENT "foo"
-    PARAM_LIST
-      L_PAREN "("
-      R_PAREN ")"
-    WHITESPACE " "
-    BLOCK_EXPR
-      STMT_LIST
-        L_CURLY "{"
-        WHITESPACE "\n    "
-        EXPR_STMT
-          IF_EXPR
-            IF_KW "if"
-            WHITESPACE " "
-            BIN_EXPR
-              LET_EXPR
-                LET_KW "let"
-                WHITESPACE " "
-                TUPLE_STRUCT_PAT
-                  PATH
-                    PATH_SEGMENT
-                      NAME_REF
-                        IDENT "Some"
-                  L_PAREN "("
-                  WILDCARD_PAT
-                    UNDERSCORE "_"
-                  R_PAREN ")"
-                WHITESPACE " "
-                EQ "="
-                WHITESPACE " "
-                PATH_EXPR
-                  PATH
-                    PATH_SEGMENT
-                      NAME_REF
-                        IDENT "None"
-              WHITESPACE " "
-              AMP2 "&&"
-              WHITESPACE " "
-              LITERAL
-                TRUE_KW "true"
-            WHITESPACE " "
-            BLOCK_EXPR
-              STMT_LIST
-                L_CURLY "{"
-                R_CURLY "}"
-        WHITESPACE "\n    "
-        WHILE_EXPR
-          WHILE_KW "while"
-          WHITESPACE " "
-          BIN_EXPR
-            BIN_EXPR
-              LITERAL
-                INT_NUMBER "1"
-              WHITESPACE " "
-              EQ2 "=="
-              WHITESPACE " "
-              LITERAL
-                INT_NUMBER "5"
-            WHITESPACE " "
-            AMP2 "&&"
-            WHITESPACE " "
-            PAREN_EXPR
-              L_PAREN "("
-              LET_EXPR
-                LET_KW "let"
-                WHITESPACE " "
-                IDENT_PAT
-                  NAME
-                    IDENT "None"
-                WHITESPACE " "
-                EQ "="
-                WHITESPACE " "
-                PATH_EXPR
-                  PATH
-                    PATH_SEGMENT
-                      NAME_REF
-                        IDENT "None"
-              R_PAREN ")"
-          WHITESPACE " "
-          BLOCK_EXPR
-            STMT_LIST
-              L_CURLY "{"
-              R_CURLY "}"
-        WHITESPACE "\n"
-        R_CURLY "}"
-  WHITESPACE "\n"
diff --git a/crates/parser/test_data/parser/inline/ok/0030_let_expr.rs b/crates/parser/test_data/parser/inline/ok/0030_let_expr.rs
deleted file mode 100644
index 0131d5e338..0000000000
--- a/crates/parser/test_data/parser/inline/ok/0030_let_expr.rs
+++ /dev/null
@@ -1,4 +0,0 @@
-fn foo() {
-    if let Some(_) = None && true {}
-    while 1 == 5 && (let None = None) {}
-}
diff --git a/crates/parser/test_data/parser/inline/ok/0030_let_expr.txt b/crates/parser/test_data/parser/inline/ok/0030_let_expr.txt
deleted file mode 100644
index dcffcb1ce2..0000000000
--- a/crates/parser/test_data/parser/inline/ok/0030_let_expr.txt
+++ /dev/null
@@ -1,90 +0,0 @@
-SOURCE_FILE
-  FN
-    FN_KW "fn"
-    WHITESPACE " "
-    NAME
-      IDENT "foo"
-    PARAM_LIST
-      L_PAREN "("
-      R_PAREN ")"
-    WHITESPACE " "
-    BLOCK_EXPR
-      STMT_LIST
-        L_CURLY "{"
-        WHITESPACE "\n    "
-        EXPR_STMT
-          IF_EXPR
-            IF_KW "if"
-            WHITESPACE " "
-            BIN_EXPR
-              LET_EXPR
-                LET_KW "let"
-                WHITESPACE " "
-                TUPLE_STRUCT_PAT
-                  PATH
-                    PATH_SEGMENT
-                      NAME_REF
-                        IDENT "Some"
-                  L_PAREN "("
-                  WILDCARD_PAT
-                    UNDERSCORE "_"
-                  R_PAREN ")"
-                WHITESPACE " "
-                EQ "="
-                WHITESPACE " "
-                PATH_EXPR
-                  PATH
-                    PATH_SEGMENT
-                      NAME_REF
-                        IDENT "None"
-              WHITESPACE " "
-              AMP2 "&&"
-              WHITESPACE " "
-              LITERAL
-                TRUE_KW "true"
-            WHITESPACE " "
-            BLOCK_EXPR
-              STMT_LIST
-                L_CURLY "{"
-                R_CURLY "}"
-        WHITESPACE "\n    "
-        WHILE_EXPR
-          WHILE_KW "while"
-          WHITESPACE " "
-          BIN_EXPR
-            BIN_EXPR
-              LITERAL
-                INT_NUMBER "1"
-              WHITESPACE " "
-              EQ2 "=="
-              WHITESPACE " "
-              LITERAL
-                INT_NUMBER "5"
-            WHITESPACE " "
-            AMP2 "&&"
-            WHITESPACE " "
-            PAREN_EXPR
-              L_PAREN "("
-              LET_EXPR
-                LET_KW "let"
-                WHITESPACE " "
-                IDENT_PAT
-                  NAME
-                    IDENT "None"
-                WHITESPACE " "
-                EQ "="
-                WHITESPACE " "
-                PATH_EXPR
-                  PATH
-                    PATH_SEGMENT
-                      NAME_REF
-                        IDENT "None"
-              R_PAREN ")"
-          WHITESPACE " "
-          BLOCK_EXPR
-            STMT_LIST
-              L_CURLY "{"
-              R_CURLY "}"
-        WHITESPACE "\n"
-        R_CURLY "}"
-  WHITESPACE "\n"
diff --git a/crates/parser/test_data/parser/inline/ok/0031_while_expr.txt b/crates/parser/test_data/parser/inline/ok/0031_while_expr.txt
index 16c522414a..fc1ca4934c 100644
--- a/crates/parser/test_data/parser/inline/ok/0031_while_expr.txt
+++ b/crates/parser/test_data/parser/inline/ok/0031_while_expr.txt
@@ -16,8 +16,9 @@ SOURCE_FILE
           WHILE_EXPR
             WHILE_KW "while"
             WHITESPACE " "
-            LITERAL
-              TRUE_KW "true"
+            CONDITION
+              LITERAL
+                TRUE_KW "true"
             WHITESPACE " "
             BLOCK_EXPR
               STMT_LIST
@@ -29,7 +30,7 @@ SOURCE_FILE
           WHILE_EXPR
             WHILE_KW "while"
             WHITESPACE " "
-            LET_EXPR
+            CONDITION
               LET_KW "let"
               WHITESPACE " "
               TUPLE_STRUCT_PAT
@@ -68,14 +69,15 @@ SOURCE_FILE
           WHILE_EXPR
             WHILE_KW "while"
             WHITESPACE " "
-            BLOCK_EXPR
-              STMT_LIST
-                L_CURLY "{"
-                WHITESPACE " "
-                LITERAL
-                  TRUE_KW "true"
-                WHITESPACE " "
-                R_CURLY "}"
+            CONDITION
+              BLOCK_EXPR
+                STMT_LIST
+                  L_CURLY "{"
+                  WHITESPACE " "
+                  LITERAL
+                    TRUE_KW "true"
+                  WHITESPACE " "
+                  R_CURLY "}"
             WHITESPACE " "
             BLOCK_EXPR
               STMT_LIST
diff --git a/crates/parser/test_data/parser/inline/ok/0064_if_expr.txt b/crates/parser/test_data/parser/inline/ok/0064_if_expr.txt
index e2e964e44d..c1f8381271 100644
--- a/crates/parser/test_data/parser/inline/ok/0064_if_expr.txt
+++ b/crates/parser/test_data/parser/inline/ok/0064_if_expr.txt
@@ -16,8 +16,9 @@ SOURCE_FILE
           IF_EXPR
             IF_KW "if"
             WHITESPACE " "
-            LITERAL
-              TRUE_KW "true"
+            CONDITION
+              LITERAL
+                TRUE_KW "true"
             WHITESPACE " "
             BLOCK_EXPR
               STMT_LIST
@@ -29,8 +30,9 @@ SOURCE_FILE
           IF_EXPR
             IF_KW "if"
             WHITESPACE " "
-            LITERAL
-              TRUE_KW "true"
+            CONDITION
+              LITERAL
+                TRUE_KW "true"
             WHITESPACE " "
             BLOCK_EXPR
               STMT_LIST
@@ -49,8 +51,9 @@ SOURCE_FILE
           IF_EXPR
             IF_KW "if"
             WHITESPACE " "
-            LITERAL
-              TRUE_KW "true"
+            CONDITION
+              LITERAL
+                TRUE_KW "true"
             WHITESPACE " "
             BLOCK_EXPR
               STMT_LIST
@@ -62,8 +65,9 @@ SOURCE_FILE
             IF_EXPR
               IF_KW "if"
               WHITESPACE " "
-              LITERAL
-                FALSE_KW "false"
+              CONDITION
+                LITERAL
+                  FALSE_KW "false"
               WHITESPACE " "
               BLOCK_EXPR
                 STMT_LIST
@@ -82,11 +86,12 @@ SOURCE_FILE
           IF_EXPR
             IF_KW "if"
             WHITESPACE " "
-            PATH_EXPR
-              PATH
-                PATH_SEGMENT
-                  NAME_REF
-                    IDENT "S"
+            CONDITION
+              PATH_EXPR
+                PATH
+                  PATH_SEGMENT
+                    NAME_REF
+                      IDENT "S"
             WHITESPACE " "
             BLOCK_EXPR
               STMT_LIST
@@ -98,14 +103,15 @@ SOURCE_FILE
           IF_EXPR
             IF_KW "if"
             WHITESPACE " "
-            BLOCK_EXPR
-              STMT_LIST
-                L_CURLY "{"
-                WHITESPACE " "
-                LITERAL
-                  TRUE_KW "true"
-                WHITESPACE " "
-                R_CURLY "}"
+            CONDITION
+              BLOCK_EXPR
+                STMT_LIST
+                  L_CURLY "{"
+                  WHITESPACE " "
+                  LITERAL
+                    TRUE_KW "true"
+                  WHITESPACE " "
+                  R_CURLY "}"
             WHITESPACE " "
             BLOCK_EXPR
               STMT_LIST
diff --git a/crates/parser/test_data/parser/inline/ok/0088_break_ambiguity.txt b/crates/parser/test_data/parser/inline/ok/0088_break_ambiguity.txt
index cbf5e84e8c..50ce9933b9 100644
--- a/crates/parser/test_data/parser/inline/ok/0088_break_ambiguity.txt
+++ b/crates/parser/test_data/parser/inline/ok/0088_break_ambiguity.txt
@@ -15,8 +15,9 @@ SOURCE_FILE
           IF_EXPR
             IF_KW "if"
             WHITESPACE " "
-            BREAK_EXPR
-              BREAK_KW "break"
+            CONDITION
+              BREAK_EXPR
+                BREAK_KW "break"
             WHITESPACE " "
             BLOCK_EXPR
               STMT_LIST
@@ -27,8 +28,9 @@ SOURCE_FILE
           WHILE_EXPR
             WHILE_KW "while"
             WHITESPACE " "
-            BREAK_EXPR
-              BREAK_KW "break"
+            CONDITION
+              BREAK_EXPR
+                BREAK_KW "break"
             WHITESPACE " "
             BLOCK_EXPR
               STMT_LIST
diff --git a/crates/parser/test_data/parser/inline/ok/0096_no_semi_after_block.txt b/crates/parser/test_data/parser/inline/ok/0096_no_semi_after_block.txt
index e4e215593e..2a85371393 100644
--- a/crates/parser/test_data/parser/inline/ok/0096_no_semi_after_block.txt
+++ b/crates/parser/test_data/parser/inline/ok/0096_no_semi_after_block.txt
@@ -16,8 +16,9 @@ SOURCE_FILE
           IF_EXPR
             IF_KW "if"
             WHITESPACE " "
-            LITERAL
-              TRUE_KW "true"
+            CONDITION
+              LITERAL
+                TRUE_KW "true"
             WHITESPACE " "
             BLOCK_EXPR
               STMT_LIST
@@ -49,8 +50,9 @@ SOURCE_FILE
           WHILE_EXPR
             WHILE_KW "while"
             WHITESPACE " "
-            LITERAL
-              TRUE_KW "true"
+            CONDITION
+              LITERAL
+                TRUE_KW "true"
             WHITESPACE " "
             BLOCK_EXPR
               STMT_LIST
diff --git a/crates/parser/test_data/parser/inline/ok/0106_lambda_expr.rs b/crates/parser/test_data/parser/inline/ok/0106_lambda_expr.rs
index d01f921664..0757178239 100644
--- a/crates/parser/test_data/parser/inline/ok/0106_lambda_expr.rs
+++ b/crates/parser/test_data/parser/inline/ok/0106_lambda_expr.rs
@@ -6,8 +6,4 @@ fn foo() {
     async || {};
     move || {};
     async move || {};
-    static || {};
-    static move || {};
-    static async || {};
-    static async move || {};
 }
diff --git a/crates/parser/test_data/parser/inline/ok/0106_lambda_expr.txt b/crates/parser/test_data/parser/inline/ok/0106_lambda_expr.txt
index bc54b01869..4571788948 100644
--- a/crates/parser/test_data/parser/inline/ok/0106_lambda_expr.txt
+++ b/crates/parser/test_data/parser/inline/ok/0106_lambda_expr.txt
@@ -135,70 +135,6 @@ SOURCE_FILE
                 L_CURLY "{"
                 R_CURLY "}"
           SEMICOLON ";"
-        WHITESPACE "\n    "
-        EXPR_STMT
-          CLOSURE_EXPR
-            STATIC_KW "static"
-            WHITESPACE " "
-            PARAM_LIST
-              PIPE "|"
-              PIPE "|"
-            WHITESPACE " "
-            BLOCK_EXPR
-              STMT_LIST
-                L_CURLY "{"
-                R_CURLY "}"
-          SEMICOLON ";"
-        WHITESPACE "\n    "
-        EXPR_STMT
-          CLOSURE_EXPR
-            STATIC_KW "static"
-            WHITESPACE " "
-            MOVE_KW "move"
-            WHITESPACE " "
-            PARAM_LIST
-              PIPE "|"
-              PIPE "|"
-            WHITESPACE " "
-            BLOCK_EXPR
-              STMT_LIST
-                L_CURLY "{"
-                R_CURLY "}"
-          SEMICOLON ";"
-        WHITESPACE "\n    "
-        EXPR_STMT
-          CLOSURE_EXPR
-            STATIC_KW "static"
-            WHITESPACE " "
-            ASYNC_KW "async"
-            WHITESPACE " "
-            PARAM_LIST
-              PIPE "|"
-              PIPE "|"
-            WHITESPACE " "
-            BLOCK_EXPR
-              STMT_LIST
-                L_CURLY "{"
-                R_CURLY "}"
-          SEMICOLON ";"
-        WHITESPACE "\n    "
-        EXPR_STMT
-          CLOSURE_EXPR
-            STATIC_KW "static"
-            WHITESPACE " "
-            ASYNC_KW "async"
-            WHITESPACE " "
-            MOVE_KW "move"
-            WHITESPACE " "
-            PARAM_LIST
-              PIPE "|"
-              PIPE "|"
-            WHITESPACE " "
-            BLOCK_EXPR
-              STMT_LIST
-                L_CURLY "{"
-                R_CURLY "}"
-          SEMICOLON ";"
         WHITESPACE "\n"
         R_CURLY "}"
   WHITESPACE "\n"
diff --git a/crates/parser/test_data/parser/inline/ok/0109_label.txt b/crates/parser/test_data/parser/inline/ok/0109_label.txt
index 48d0bde845..bd57fa9d4a 100644
--- a/crates/parser/test_data/parser/inline/ok/0109_label.txt
+++ b/crates/parser/test_data/parser/inline/ok/0109_label.txt
@@ -35,8 +35,9 @@ SOURCE_FILE
             WHITESPACE " "
             WHILE_KW "while"
             WHITESPACE " "
-            LITERAL
-              TRUE_KW "true"
+            CONDITION
+              LITERAL
+                TRUE_KW "true"
             WHITESPACE " "
             BLOCK_EXPR
               STMT_LIST
diff --git a/crates/parser/test_data/parser/inline/ok/0118_match_guard.txt b/crates/parser/test_data/parser/inline/ok/0118_match_guard.txt
index 96318b5219..a28b6ea5d1 100644
--- a/crates/parser/test_data/parser/inline/ok/0118_match_guard.txt
+++ b/crates/parser/test_data/parser/inline/ok/0118_match_guard.txt
@@ -49,20 +49,19 @@ SOURCE_FILE
               MATCH_GUARD
                 IF_KW "if"
                 WHITESPACE " "
-                LET_EXPR
-                  LET_KW "let"
-                  WHITESPACE " "
-                  IDENT_PAT
-                    NAME
-                      IDENT "foo"
-                  WHITESPACE " "
-                  EQ "="
-                  WHITESPACE " "
-                  PATH_EXPR
-                    PATH
-                      PATH_SEGMENT
-                        NAME_REF
-                          IDENT "bar"
+                LET_KW "let"
+                WHITESPACE " "
+                IDENT_PAT
+                  NAME
+                    IDENT "foo"
+                WHITESPACE " "
+                EQ "="
+                WHITESPACE " "
+                PATH_EXPR
+                  PATH
+                    PATH_SEGMENT
+                      NAME_REF
+                        IDENT "bar"
               WHITESPACE " "
               FAT_ARROW "=>"
               WHITESPACE " "
diff --git a/crates/parser/test_data/parser/ok/0033_label_break.txt b/crates/parser/test_data/parser/ok/0033_label_break.txt
index df1acd6b83..9807bf0d9a 100644
--- a/crates/parser/test_data/parser/ok/0033_label_break.txt
+++ b/crates/parser/test_data/parser/ok/0033_label_break.txt
@@ -51,15 +51,16 @@ SOURCE_FILE
                 IF_EXPR
                   IF_KW "if"
                   WHITESPACE " "
-                  CALL_EXPR
-                    PATH_EXPR
-                      PATH
-                        PATH_SEGMENT
-                          NAME_REF
-                            IDENT "condition_not_met"
-                    ARG_LIST
-                      L_PAREN "("
-                      R_PAREN ")"
+                  CONDITION
+                    CALL_EXPR
+                      PATH_EXPR
+                        PATH
+                          PATH_SEGMENT
+                            NAME_REF
+                              IDENT "condition_not_met"
+                      ARG_LIST
+                        L_PAREN "("
+                        R_PAREN ")"
                   WHITESPACE " "
                   BLOCK_EXPR
                     STMT_LIST
@@ -91,15 +92,16 @@ SOURCE_FILE
                 IF_EXPR
                   IF_KW "if"
                   WHITESPACE " "
-                  CALL_EXPR
-                    PATH_EXPR
-                      PATH
-                        PATH_SEGMENT
-                          NAME_REF
-                            IDENT "condition_not_met"
-                    ARG_LIST
-                      L_PAREN "("
-                      R_PAREN ")"
+                  CONDITION
+                    CALL_EXPR
+                      PATH_EXPR
+                        PATH
+                          PATH_SEGMENT
+                            NAME_REF
+                              IDENT "condition_not_met"
+                      ARG_LIST
+                        L_PAREN "("
+                        R_PAREN ")"
                   WHITESPACE " "
                   BLOCK_EXPR
                     STMT_LIST
@@ -151,15 +153,16 @@ SOURCE_FILE
                 IF_EXPR
                   IF_KW "if"
                   WHITESPACE " "
-                  CALL_EXPR
-                    PATH_EXPR
-                      PATH
-                        PATH_SEGMENT
-                          NAME_REF
-                            IDENT "foo"
-                    ARG_LIST
-                      L_PAREN "("
-                      R_PAREN ")"
+                  CONDITION
+                    CALL_EXPR
+                      PATH_EXPR
+                        PATH
+                          PATH_SEGMENT
+                            NAME_REF
+                              IDENT "foo"
+                      ARG_LIST
+                        L_PAREN "("
+                        R_PAREN ")"
                   WHITESPACE " "
                   BLOCK_EXPR
                     STMT_LIST
@@ -184,15 +187,16 @@ SOURCE_FILE
                 IF_EXPR
                   IF_KW "if"
                   WHITESPACE " "
-                  CALL_EXPR
-                    PATH_EXPR
-                      PATH
-                        PATH_SEGMENT
-                          NAME_REF
-                            IDENT "bar"
-                    ARG_LIST
-                      L_PAREN "("
-                      R_PAREN ")"
+                  CONDITION
+                    CALL_EXPR
+                      PATH_EXPR
+                        PATH
+                          PATH_SEGMENT
+                            NAME_REF
+                              IDENT "bar"
+                      ARG_LIST
+                        L_PAREN "("
+                        R_PAREN ")"
                   WHITESPACE " "
                   BLOCK_EXPR
                     STMT_LIST
diff --git a/crates/parser/test_data/parser/ok/0035_weird_exprs.txt b/crates/parser/test_data/parser/ok/0035_weird_exprs.txt
index 4ec703e517..5f62748c47 100644
--- a/crates/parser/test_data/parser/ok/0035_weird_exprs.txt
+++ b/crates/parser/test_data/parser/ok/0035_weird_exprs.txt
@@ -280,20 +280,21 @@ SOURCE_FILE
                   WHILE_EXPR
                     WHILE_KW "while"
                     WHITESPACE " "
-                    PREFIX_EXPR
-                      BANG "!"
-                      METHOD_CALL_EXPR
-                        PATH_EXPR
-                          PATH
-                            PATH_SEGMENT
-                              NAME_REF
-                                IDENT "x"
-                        DOT "."
-                        NAME_REF
-                          IDENT "get"
-                        ARG_LIST
-                          L_PAREN "("
-                          R_PAREN ")"
+                    CONDITION
+                      PREFIX_EXPR
+                        BANG "!"
+                        METHOD_CALL_EXPR
+                          PATH_EXPR
+                            PATH
+                              PATH_SEGMENT
+                                NAME_REF
+                                  IDENT "x"
+                          DOT "."
+                          NAME_REF
+                            IDENT "get"
+                          ARG_LIST
+                            L_PAREN "("
+                            R_PAREN ")"
                     WHITESPACE " "
                     BLOCK_EXPR
                       STMT_LIST
@@ -442,11 +443,12 @@ SOURCE_FILE
                 WHILE_EXPR
                   WHILE_KW "while"
                   WHITESPACE " "
-                  PAREN_EXPR
-                    L_PAREN "("
-                    RETURN_EXPR
-                      RETURN_KW "return"
-                    R_PAREN ")"
+                  CONDITION
+                    PAREN_EXPR
+                      L_PAREN "("
+                      RETURN_EXPR
+                        RETURN_KW "return"
+                      R_PAREN ")"
                   WHITESPACE " "
                   BLOCK_EXPR
                     STMT_LIST
@@ -455,11 +457,12 @@ SOURCE_FILE
                       IF_EXPR
                         IF_KW "if"
                         WHITESPACE " "
-                        PAREN_EXPR
-                          L_PAREN "("
-                          RETURN_EXPR
-                            RETURN_KW "return"
-                          R_PAREN ")"
+                        CONDITION
+                          PAREN_EXPR
+                            L_PAREN "("
+                            RETURN_EXPR
+                              RETURN_KW "return"
+                            R_PAREN ")"
                         WHITESPACE " "
                         BLOCK_EXPR
                           STMT_LIST
@@ -492,11 +495,12 @@ SOURCE_FILE
                                         IF_EXPR
                                           IF_KW "if"
                                           WHITESPACE " "
-                                          PAREN_EXPR
-                                            L_PAREN "("
-                                            RETURN_EXPR
-                                              RETURN_KW "return"
-                                            R_PAREN ")"
+                                          CONDITION
+                                            PAREN_EXPR
+                                              L_PAREN "("
+                                              RETURN_EXPR
+                                                RETURN_KW "return"
+                                              R_PAREN ")"
                                           WHITESPACE " "
                                           BLOCK_EXPR
                                             STMT_LIST
@@ -545,11 +549,12 @@ SOURCE_FILE
                         IF_EXPR
                           IF_KW "if"
                           WHITESPACE " "
-                          PAREN_EXPR
-                            L_PAREN "("
-                            RETURN_EXPR
-                              RETURN_KW "return"
-                            R_PAREN ")"
+                          CONDITION
+                            PAREN_EXPR
+                              L_PAREN "("
+                              RETURN_EXPR
+                                RETURN_KW "return"
+                              R_PAREN ")"
                           WHITESPACE " "
                           BLOCK_EXPR
                             STMT_LIST
@@ -567,11 +572,12 @@ SOURCE_FILE
               IF_EXPR
                 IF_KW "if"
                 WHITESPACE " "
-                PAREN_EXPR
-                  L_PAREN "("
-                  RETURN_EXPR
-                    RETURN_KW "return"
-                  R_PAREN ")"
+                CONDITION
+                  PAREN_EXPR
+                    L_PAREN "("
+                    RETURN_EXPR
+                      RETURN_KW "return"
+                    R_PAREN ")"
                 WHITESPACE " "
                 BLOCK_EXPR
                   STMT_LIST
@@ -1031,8 +1037,9 @@ SOURCE_FILE
                 IF_EXPR
                   IF_KW "if"
                   WHITESPACE " "
-                  BREAK_EXPR
-                    BREAK_KW "break"
+                  CONDITION
+                    BREAK_EXPR
+                      BREAK_KW "break"
                   WHITESPACE " "
                   BLOCK_EXPR
                     STMT_LIST
@@ -1082,17 +1089,18 @@ SOURCE_FILE
                 IF_EXPR
                   IF_KW "if"
                   WHITESPACE " "
-                  BIN_EXPR
-                    PATH_EXPR
-                      PATH
-                        PATH_SEGMENT
-                          NAME_REF
-                            IDENT "i"
-                    WHITESPACE " "
-                    EQ2 "=="
-                    WHITESPACE " "
-                    LITERAL
-                      INT_NUMBER "1"
+                  CONDITION
+                    BIN_EXPR
+                      PATH_EXPR
+                        PATH
+                          PATH_SEGMENT
+                            NAME_REF
+                              IDENT "i"
+                      WHITESPACE " "
+                      EQ2 "=="
+                      WHITESPACE " "
+                      LITERAL
+                        INT_NUMBER "1"
                   WHITESPACE " "
                   BLOCK_EXPR
                     STMT_LIST
@@ -1336,17 +1344,18 @@ SOURCE_FILE
         IF_EXPR
           IF_KW "if"
           WHITESPACE " "
-          BIN_EXPR
-            PATH_EXPR
-              PATH
-                PATH_SEGMENT
-                  NAME_REF
-                    IDENT "u8"
-            WHITESPACE " "
-            NEQ "!="
-            WHITESPACE " "
-            LITERAL
-              INT_NUMBER "0u8"
+          CONDITION
+            BIN_EXPR
+              PATH_EXPR
+                PATH
+                  PATH_SEGMENT
+                    NAME_REF
+                      IDENT "u8"
+              WHITESPACE " "
+              NEQ "!="
+              WHITESPACE " "
+              LITERAL
+                INT_NUMBER "0u8"
           WHITESPACE " "
           BLOCK_EXPR
             STMT_LIST
diff --git a/crates/parser/test_data/parser/ok/0047_minus_in_inner_pattern.txt b/crates/parser/test_data/parser/ok/0047_minus_in_inner_pattern.txt
index aecc71d482..ac23e7d1d9 100644
--- a/crates/parser/test_data/parser/ok/0047_minus_in_inner_pattern.txt
+++ b/crates/parser/test_data/parser/ok/0047_minus_in_inner_pattern.txt
@@ -219,7 +219,7 @@ SOURCE_FILE
         IF_EXPR
           IF_KW "if"
           WHITESPACE " "
-          LET_EXPR
+          CONDITION
             LET_KW "let"
             WHITESPACE " "
             TUPLE_STRUCT_PAT
diff --git a/crates/parser/test_data/parser/ok/0056_neq_in_type.txt b/crates/parser/test_data/parser/ok/0056_neq_in_type.txt
index 55ce31275f..2d78eaffc5 100644
--- a/crates/parser/test_data/parser/ok/0056_neq_in_type.txt
+++ b/crates/parser/test_data/parser/ok/0056_neq_in_type.txt
@@ -15,46 +15,47 @@ SOURCE_FILE
         IF_EXPR
           IF_KW "if"
           WHITESPACE " "
-          BIN_EXPR
-            CAST_EXPR
-              METHOD_CALL_EXPR
-                LITERAL
-                  FLOAT_NUMBER "1.0f32"
-                DOT "."
-                NAME_REF
-                  IDENT "floor"
-                ARG_LIST
-                  L_PAREN "("
-                  R_PAREN ")"
+          CONDITION
+            BIN_EXPR
+              CAST_EXPR
+                METHOD_CALL_EXPR
+                  LITERAL
+                    FLOAT_NUMBER "1.0f32"
+                  DOT "."
+                  NAME_REF
+                    IDENT "floor"
+                  ARG_LIST
+                    L_PAREN "("
+                    R_PAREN ")"
+                WHITESPACE " "
+                AS_KW "as"
+                WHITESPACE " "
+                PATH_TYPE
+                  PATH
+                    PATH_SEGMENT
+                      NAME_REF
+                        IDENT "i64"
               WHITESPACE " "
-              AS_KW "as"
+              NEQ "!="
               WHITESPACE " "
-              PATH_TYPE
-                PATH
-                  PATH_SEGMENT
-                    NAME_REF
-                      IDENT "i64"
-            WHITESPACE " "
-            NEQ "!="
-            WHITESPACE " "
-            CAST_EXPR
-              METHOD_CALL_EXPR
-                LITERAL
-                  FLOAT_NUMBER "1.0f32"
-                DOT "."
-                NAME_REF
-                  IDENT "floor"
-                ARG_LIST
-                  L_PAREN "("
-                  R_PAREN ")"
-              WHITESPACE " "
-              AS_KW "as"
-              WHITESPACE " "
-              PATH_TYPE
-                PATH
-                  PATH_SEGMENT
-                    NAME_REF
-                      IDENT "i64"
+              CAST_EXPR
+                METHOD_CALL_EXPR
+                  LITERAL
+                    FLOAT_NUMBER "1.0f32"
+                  DOT "."
+                  NAME_REF
+                    IDENT "floor"
+                  ARG_LIST
+                    L_PAREN "("
+                    R_PAREN ")"
+                WHITESPACE " "
+                AS_KW "as"
+                WHITESPACE " "
+                PATH_TYPE
+                  PATH
+                    PATH_SEGMENT
+                      NAME_REF
+                        IDENT "i64"
           WHITESPACE " "
           BLOCK_EXPR
             STMT_LIST
diff --git a/crates/parser/test_data/parser/ok/0059_loops_in_parens.txt b/crates/parser/test_data/parser/ok/0059_loops_in_parens.txt
index 79bc7f971d..1eeb6c957f 100644
--- a/crates/parser/test_data/parser/ok/0059_loops_in_parens.txt
+++ b/crates/parser/test_data/parser/ok/0059_loops_in_parens.txt
@@ -86,8 +86,9 @@ SOURCE_FILE
               WHILE_EXPR
                 WHILE_KW "while"
                 WHITESPACE " "
-                LITERAL
-                  TRUE_KW "true"
+                CONDITION
+                  LITERAL
+                    TRUE_KW "true"
                 WHITESPACE " "
                 BLOCK_EXPR
                   STMT_LIST
diff --git a/crates/syntax/src/tests/ast_src.rs b/crates/syntax/src/tests/ast_src.rs
index aeff851ce4..c0f1d5ef56 100644
--- a/crates/syntax/src/tests/ast_src.rs
+++ b/crates/syntax/src/tests/ast_src.rs
@@ -133,6 +133,7 @@ pub(crate) const KINDS_SRC: KindsSrc = KindsSrc {
         "CLOSURE_EXPR",
         "IF_EXPR",
         "WHILE_EXPR",
+        "CONDITION",
         "LOOP_EXPR",
         "FOR_EXPR",
         "CONTINUE_EXPR",
@@ -142,7 +143,6 @@ pub(crate) const KINDS_SRC: KindsSrc = KindsSrc {
         "STMT_LIST",
         "RETURN_EXPR",
         "YIELD_EXPR",
-        "LET_EXPR",
         "MATCH_EXPR",
         "MATCH_ARM_LIST",
         "MATCH_ARM",
diff --git a/crates/syntax/test_data/parser/validation/0031_block_inner_attrs.rast b/crates/syntax/test_data/parser/validation/0031_block_inner_attrs.rast
index 50057a02d8..d4963979c8 100644
--- a/crates/syntax/test_data/parser/validation/0031_block_inner_attrs.rast
+++ b/crates/syntax/test_data/parser/validation/0031_block_inner_attrs.rast
@@ -49,8 +49,9 @@ SOURCE_FILE@0..350
           IF_EXPR@134..257
             IF_KW@134..136 "if"
             WHITESPACE@136..137 " "
-            LITERAL@137..141
-              TRUE_KW@137..141 "true"
+            CONDITION@137..141
+              LITERAL@137..141
+                TRUE_KW@137..141 "true"
             WHITESPACE@141..142 " "
             BLOCK_EXPR@142..257
               STMT_LIST@142..257
@@ -93,8 +94,9 @@ SOURCE_FILE@0..350
         WHILE_EXPR@262..347
           WHILE_KW@262..267 "while"
           WHITESPACE@267..268 " "
-          LITERAL@268..272
-            TRUE_KW@268..272 "true"
+          CONDITION@268..272
+            LITERAL@268..272
+              TRUE_KW@268..272 "true"
           WHITESPACE@272..273 " "
           BLOCK_EXPR@273..347
             STMT_LIST@273..347
diff --git a/crates/syntax/test_data/parser/validation/invalid_let_expr.rast b/crates/syntax/test_data/parser/validation/invalid_let_expr.rast
deleted file mode 100644
index 5b37b59783..0000000000
--- a/crates/syntax/test_data/parser/validation/invalid_let_expr.rast
+++ /dev/null
@@ -1,216 +0,0 @@
-SOURCE_FILE@0..282
-  FN@0..281
-    FN_KW@0..2 "fn"
-    WHITESPACE@2..3 " "
-    NAME@3..6
-      IDENT@3..6 "foo"
-    PARAM_LIST@6..8
-      L_PAREN@6..7 "("
-      R_PAREN@7..8 ")"
-    WHITESPACE@8..9 " "
-    BLOCK_EXPR@9..281
-      STMT_LIST@9..281
-        L_CURLY@9..10 "{"
-        WHITESPACE@10..15 "\n    "
-        CONST@15..42
-          CONST_KW@15..20 "const"
-          WHITESPACE@20..21 " "
-          UNDERSCORE@21..22 "_"
-          COLON@22..23 ":"
-          WHITESPACE@23..24 " "
-          TUPLE_TYPE@24..26
-            L_PAREN@24..25 "("
-            R_PAREN@25..26 ")"
-          WHITESPACE@26..27 " "
-          EQ@27..28 "="
-          WHITESPACE@28..29 " "
-          LET_EXPR@29..41
-            LET_KW@29..32 "let"
-            WHITESPACE@32..33 " "
-            WILDCARD_PAT@33..34
-              UNDERSCORE@33..34 "_"
-            WHITESPACE@34..35 " "
-            EQ@35..36 "="
-            WHITESPACE@36..37 " "
-            PATH_EXPR@37..41
-              PATH@37..41
-                PATH_SEGMENT@37..41
-                  NAME_REF@37..41
-                    IDENT@37..41 "None"
-          SEMICOLON@41..42 ";"
-        WHITESPACE@42..48 "\n\n    "
-        LET_STMT@48..83
-          LET_KW@48..51 "let"
-          WHITESPACE@51..52 " "
-          WILDCARD_PAT@52..53
-            UNDERSCORE@52..53 "_"
-          WHITESPACE@53..54 " "
-          EQ@54..55 "="
-          WHITESPACE@55..56 " "
-          IF_EXPR@56..82
-            IF_KW@56..58 "if"
-            WHITESPACE@58..59 " "
-            LITERAL@59..63
-              TRUE_KW@59..63 "true"
-            WHITESPACE@63..64 " "
-            BLOCK_EXPR@64..82
-              STMT_LIST@64..82
-                L_CURLY@64..65 "{"
-                WHITESPACE@65..66 " "
-                PAREN_EXPR@66..80
-                  L_PAREN@66..67 "("
-                  LET_EXPR@67..79
-                    LET_KW@67..70 "let"
-                    WHITESPACE@70..71 " "
-                    WILDCARD_PAT@71..72
-                      UNDERSCORE@71..72 "_"
-                    WHITESPACE@72..73 " "
-                    EQ@73..74 "="
-                    WHITESPACE@74..75 " "
-                    PATH_EXPR@75..79
-                      PATH@75..79
-                        PATH_SEGMENT@75..79
-                          NAME_REF@75..79
-                            IDENT@75..79 "None"
-                  R_PAREN@79..80 ")"
-                WHITESPACE@80..81 " "
-                R_CURLY@81..82 "}"
-          SEMICOLON@82..83 ";"
-        WHITESPACE@83..89 "\n\n    "
-        IF_EXPR@89..279
-          IF_KW@89..91 "if"
-          WHITESPACE@91..92 " "
-          BIN_EXPR@92..114
-            LITERAL@92..96
-              TRUE_KW@92..96 "true"
-            WHITESPACE@96..97 " "
-            AMP2@97..99 "&&"
-            WHITESPACE@99..100 " "
-            PAREN_EXPR@100..114
-              L_PAREN@100..101 "("
-              LET_EXPR@101..113
-                LET_KW@101..104 "let"
-                WHITESPACE@104..105 " "
-                WILDCARD_PAT@105..106
-                  UNDERSCORE@105..106 "_"
-                WHITESPACE@106..107 " "
-                EQ@107..108 "="
-                WHITESPACE@108..109 " "
-                PATH_EXPR@109..113
-                  PATH@109..113
-                    PATH_SEGMENT@109..113
-                      NAME_REF@109..113
-                        IDENT@109..113 "None"
-              R_PAREN@113..114 ")"
-          WHITESPACE@114..115 " "
-          BLOCK_EXPR@115..279
-            STMT_LIST@115..279
-              L_CURLY@115..116 "{"
-              WHITESPACE@116..125 "\n        "
-              EXPR_STMT@125..140
-                PAREN_EXPR@125..139
-                  L_PAREN@125..126 "("
-                  LET_EXPR@126..138
-                    LET_KW@126..129 "let"
-                    WHITESPACE@129..130 " "
-                    WILDCARD_PAT@130..131
-                      UNDERSCORE@130..131 "_"
-                    WHITESPACE@131..132 " "
-                    EQ@132..133 "="
-                    WHITESPACE@133..134 " "
-                    PATH_EXPR@134..138
-                      PATH@134..138
-                        PATH_SEGMENT@134..138
-                          NAME_REF@134..138
-                            IDENT@134..138 "None"
-                  R_PAREN@138..139 ")"
-                SEMICOLON@139..140 ";"
-              WHITESPACE@140..149 "\n        "
-              WHILE_EXPR@149..273
-                WHILE_KW@149..154 "while"
-                WHITESPACE@154..155 " "
-                LET_EXPR@155..167
-                  LET_KW@155..158 "let"
-                  WHITESPACE@158..159 " "
-                  WILDCARD_PAT@159..160
-                    UNDERSCORE@159..160 "_"
-                  WHITESPACE@160..161 " "
-                  EQ@161..162 "="
-                  WHITESPACE@162..163 " "
-                  PATH_EXPR@163..167
-                    PATH@163..167
-                      PATH_SEGMENT@163..167
-                        NAME_REF@163..167
-                          IDENT@163..167 "None"
-                WHITESPACE@167..168 " "
-                BLOCK_EXPR@168..273
-                  STMT_LIST@168..273
-                    L_CURLY@168..169 "{"
-                    WHITESPACE@169..182 "\n            "
-                    MATCH_EXPR@182..263
-                      MATCH_KW@182..187 "match"
-                      WHITESPACE@187..188 " "
-                      PATH_EXPR@188..192
-                        PATH@188..192
-                          PATH_SEGMENT@188..192
-                            NAME_REF@188..192
-                              IDENT@188..192 "None"
-                      WHITESPACE@192..193 " "
-                      MATCH_ARM_LIST@193..263
-                        L_CURLY@193..194 "{"
-                        WHITESPACE@194..211 "\n                "
-                        MATCH_ARM@211..249
-                          WILDCARD_PAT@211..212
-                            UNDERSCORE@211..212 "_"
-                          WHITESPACE@212..213 " "
-                          MATCH_GUARD@213..228
-                            IF_KW@213..215 "if"
-                            WHITESPACE@215..216 " "
-                            LET_EXPR@216..228
-                              LET_KW@216..219 "let"
-                              WHITESPACE@219..220 " "
-                              WILDCARD_PAT@220..221
-                                UNDERSCORE@220..221 "_"
-                              WHITESPACE@221..222 " "
-                              EQ@222..223 "="
-                              WHITESPACE@223..224 " "
-                              PATH_EXPR@224..228
-                                PATH@224..228
-                                  PATH_SEGMENT@224..228
-                                    NAME_REF@224..228
-                                      IDENT@224..228 "None"
-                          WHITESPACE@228..229 " "
-                          FAT_ARROW@229..231 "=>"
-                          WHITESPACE@231..232 " "
-                          BLOCK_EXPR@232..249
-                            STMT_LIST@232..249
-                              L_CURLY@232..233 "{"
-                              WHITESPACE@233..234 " "
-                              LET_STMT@234..247
-                                LET_KW@234..237 "let"
-                                WHITESPACE@237..238 " "
-                                WILDCARD_PAT@238..239
-                                  UNDERSCORE@238..239 "_"
-                                WHITESPACE@239..240 " "
-                                EQ@240..241 "="
-                                WHITESPACE@241..242 " "
-                                PATH_EXPR@242..246
-                                  PATH@242..246
-                                    PATH_SEGMENT@242..246
-                                      NAME_REF@242..246
-                                        IDENT@242..246 "None"
-                                SEMICOLON@246..247 ";"
-                              WHITESPACE@247..248 " "
-                              R_CURLY@248..249 "}"
-                        WHITESPACE@249..262 "\n            "
-                        R_CURLY@262..263 "}"
-                    WHITESPACE@263..272 "\n        "
-                    R_CURLY@272..273 "}"
-              WHITESPACE@273..278 "\n    "
-              R_CURLY@278..279 "}"
-        WHITESPACE@279..280 "\n"
-        R_CURLY@280..281 "}"
-  WHITESPACE@281..282 "\n"
-error 29..41: `let` expressions are not supported here
-error 67..79: `let` expressions are not supported here
-error 126..138: `let` expressions are not supported here
diff --git a/crates/syntax/test_data/parser/validation/invalid_let_expr.rs b/crates/syntax/test_data/parser/validation/invalid_let_expr.rs
deleted file mode 100644
index 1515ae5334..0000000000
--- a/crates/syntax/test_data/parser/validation/invalid_let_expr.rs
+++ /dev/null
@@ -1,14 +0,0 @@
-fn foo() {
-    const _: () = let _ = None;
-
-    let _ = if true { (let _ = None) };
-
-    if true && (let _ = None) {
-        (let _ = None);
-        while let _ = None {
-            match None {
-                _ if let _ = None => { let _ = None; }
-            }
-        }
-    }
-}