about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/release.yaml6
-rw-r--r--Cargo.lock240
-rw-r--r--crates/base-db/src/input.rs40
-rw-r--r--crates/base-db/src/lib.rs12
-rw-r--r--crates/flycheck/src/lib.rs36
-rw-r--r--crates/hir-def/src/data.rs70
-rw-r--r--crates/hir-def/src/db.rs9
-rw-r--r--crates/hir-def/src/nameres/diagnostics.rs2
-rw-r--r--crates/hir-def/src/nameres/mod_resolution.rs3
-rw-r--r--crates/hir-def/src/nameres/tests/mod_resolution.rs16
-rw-r--r--crates/hir-def/src/test_db.rs4
-rw-r--r--crates/hir-ty/Cargo.toml6
-rw-r--r--crates/hir-ty/src/infer.rs1
-rw-r--r--crates/hir-ty/src/lower.rs55
-rw-r--r--crates/hir-ty/src/method_resolution.rs142
-rw-r--r--crates/hir-ty/src/test_db.rs6
-rw-r--r--crates/hir-ty/src/tests/method_resolution.rs43
-rw-r--r--crates/hir-ty/src/tests/patterns.rs36
-rw-r--r--crates/hir-ty/src/tests/regression.rs28
-rw-r--r--crates/hir-ty/src/utils.rs21
-rw-r--r--crates/hir/src/lib.rs11
-rw-r--r--crates/ide-assists/src/handlers/extract_type_alias.rs44
-rw-r--r--crates/ide-assists/src/handlers/generate_function.rs142
-rw-r--r--crates/ide-assists/src/handlers/inline_call.rs64
-rw-r--r--crates/ide-assists/src/handlers/replace_turbofish_with_explicit_type.rs160
-rw-r--r--crates/ide-assists/src/handlers/unmerge_match_arm.rs293
-rw-r--r--crates/ide-assists/src/lib.rs2
-rw-r--r--crates/ide-assists/src/tests/generated.rs26
-rw-r--r--crates/ide-completion/src/context.rs3
-rw-r--r--crates/ide-completion/src/context/analysis.rs3
-rw-r--r--crates/ide-completion/src/render.rs27
-rw-r--r--crates/ide-completion/src/render/function.rs16
-rw-r--r--crates/ide-completion/src/render/literal.rs8
-rw-r--r--crates/ide-db/src/lib.rs3
-rw-r--r--crates/ide-db/src/line_index.rs6
-rw-r--r--crates/ide-db/src/search.rs22
-rw-r--r--crates/ide-db/src/source_change.rs11
-rw-r--r--crates/ide-diagnostics/src/handlers/inactive_code.rs5
-rw-r--r--crates/ide-ssr/Cargo.toml1
-rw-r--r--crates/ide-ssr/src/lib.rs10
-rw-r--r--crates/ide/src/doc_links.rs2
-rw-r--r--crates/ide/src/lib.rs20
-rw-r--r--crates/ide/src/moniker.rs131
-rw-r--r--crates/ide/src/prime_caches.rs5
-rw-r--r--crates/ide/src/references.rs5
-rw-r--r--crates/ide/src/syntax_highlighting.rs74
-rw-r--r--crates/ide/src/syntax_highlighting/html.rs21
-rw-r--r--crates/ide/src/syntax_highlighting/inject.rs21
-rw-r--r--crates/ide/src/syntax_highlighting/tags.rs8
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html2
-rw-r--r--crates/ide/src/syntax_highlighting/tests.rs28
-rw-r--r--crates/ide/src/view_crate_graph.rs5
-rw-r--r--crates/parser/src/grammar/patterns.rs13
-rw-r--r--crates/parser/test_data/parser/inline/ok/0024_slice_pat.rast23
-rw-r--r--crates/parser/test_data/parser/inline/ok/0024_slice_pat.rs1
-rw-r--r--crates/parser/test_data/parser/inline/ok/0026_tuple_pat_fields.rast23
-rw-r--r--crates/parser/test_data/parser/inline/ok/0026_tuple_pat_fields.rs1
-rw-r--r--crates/parser/test_data/parser/inline/ok/0111_tuple_pat.rast40
-rw-r--r--crates/parser/test_data/parser/inline/ok/0111_tuple_pat.rs1
-rw-r--r--crates/proc-macro-srv/Cargo.toml1
-rw-r--r--crates/proc-macro-srv/src/lib.rs17
-rw-r--r--crates/project-model/src/tests.rs674
-rw-r--r--crates/project-model/src/workspace.rs4
-rw-r--r--crates/rust-analyzer/Cargo.toml5
-rw-r--r--crates/rust-analyzer/src/bin/main.rs1
-rw-r--r--crates/rust-analyzer/src/cli.rs1
-rw-r--r--crates/rust-analyzer/src/cli/flags.rs10
-rw-r--r--crates/rust-analyzer/src/cli/scip.rs448
-rw-r--r--crates/rust-analyzer/src/config.rs54
-rw-r--r--crates/rust-analyzer/src/diagnostics.rs15
-rw-r--r--crates/rust-analyzer/src/global_state.rs7
-rw-r--r--crates/rust-analyzer/src/handlers.rs36
-rw-r--r--crates/rust-analyzer/src/lsp_ext.rs8
-rw-r--r--crates/rust-analyzer/src/main_loop.rs41
-rw-r--r--crates/rust-analyzer/src/semantic_tokens.rs191
-rw-r--r--crates/rust-analyzer/src/to_proto.rs55
-rw-r--r--crates/stdx/src/hash.rs80
-rw-r--r--crates/stdx/src/lib.rs1
-rw-r--r--crates/syntax/src/ast/make.rs263
-rw-r--r--crates/vfs-notify/Cargo.toml2
-rw-r--r--crates/vfs-notify/src/lib.rs11
-rw-r--r--crates/vfs/Cargo.toml3
-rw-r--r--crates/vfs/src/file_set.rs3
-rw-r--r--crates/vfs/src/lib.rs9
-rw-r--r--docs/dev/lsp-extensions.md2
-rw-r--r--docs/user/generated_config.adoc50
-rw-r--r--docs/user/manual.adoc11
-rw-r--r--editors/code/package.json37
-rw-r--r--editors/code/src/client.ts22
-rw-r--r--editors/code/src/commands.ts6
-rw-r--r--editors/code/src/config.ts102
-rw-r--r--editors/code/src/lsp_ext.ts30
-rw-r--r--editors/code/src/main.ts37
-rw-r--r--lib/lsp-server/src/socket.rs2
94 files changed, 3093 insertions, 1203 deletions
diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml
index 3c36c4fb84a..b2e5db6f689 100644
--- a/.github/workflows/release.yaml
+++ b/.github/workflows/release.yaml
@@ -31,13 +31,13 @@ jobs:
           - os: windows-latest
             target: aarch64-pc-windows-msvc
             code-target: win32-arm64
-          - os: ubuntu-18.04
+          - os: ubuntu-20.04
             target: x86_64-unknown-linux-gnu
             code-target: linux-x64
-          - os: ubuntu-18.04
+          - os: ubuntu-20.04
             target: aarch64-unknown-linux-gnu
             code-target: linux-arm64
-          - os: ubuntu-18.04
+          - os: ubuntu-20.04
             target: arm-unknown-linux-gnueabihf
             code-target: linux-armhf
           - os: macos-11
diff --git a/Cargo.lock b/Cargo.lock
index 7c6796d70bd..9f10d92c4e3 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -37,9 +37,9 @@ dependencies = [
 
 [[package]]
 name = "anyhow"
-version = "1.0.58"
+version = "1.0.62"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bb07d2053ccdbe10e2af2995a2f116c1330396493dc1269f6a91d0ae82e19704"
+checksum = "1485d4d2cc45e7b201ee3767015c96faa5904387c9d87c6efdd0fb511f12d305"
 
 [[package]]
 name = "anymap"
@@ -78,16 +78,16 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
 
 [[package]]
 name = "backtrace"
-version = "0.3.65"
+version = "0.3.66"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "11a17d453482a265fd5f8479f2a3f405566e6ca627837aaddb85af8b1ab8ef61"
+checksum = "cab84319d616cfb654d03394f38ab7e6f0919e181b1b57e1fd15e7fb4077d9a7"
 dependencies = [
  "addr2line",
  "cc",
  "cfg-if",
  "libc",
  "miniz_oxide",
- "object 0.28.4",
+ "object",
  "rustc-demangle",
 ]
 
@@ -114,9 +114,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
 
 [[package]]
 name = "camino"
-version = "1.0.9"
+version = "1.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "869119e97797867fd90f5e22af7d0bd274bd4635ebb9eb68c04f3f513ae6c412"
+checksum = "88ad0e1e3e88dd237a156ab9f571021b8a158caa0ae44b1968a241efb5144c1e"
 dependencies = [
  "serde",
 ]
@@ -171,9 +171,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
 
 [[package]]
 name = "chalk-derive"
-version = "0.83.0"
+version = "0.84.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "83553c2ef7717e58aecdf42dd9e3c876229f5a1f35a16435b5ddc4addef81827"
+checksum = "cf29c109d57f8d57b0e7675391be37a9285d86dd93278bd5f14a0ad3c447a6c2"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -183,9 +183,9 @@ dependencies = [
 
 [[package]]
 name = "chalk-ir"
-version = "0.83.0"
+version = "0.84.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2dd42107d579d8ec2a5af20a8de62a37524a67bf6a4c0ff08a950068f0bfea91"
+checksum = "d391763027b5e50a5e15caf6d2857ec585fd68160367bbeac9e1804209620918"
 dependencies = [
  "bitflags",
  "chalk-derive",
@@ -194,9 +194,9 @@ dependencies = [
 
 [[package]]
 name = "chalk-recursive"
-version = "0.83.0"
+version = "0.84.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c444031541a76c13c145e76d91f1548e9feb2240e7f0c3e77879ceb694994f2d"
+checksum = "afafd92dcdc7fe0ea940ee94bdd8cc5bd18f4a4a84c593d6d7025fe16c150478"
 dependencies = [
  "chalk-derive",
  "chalk-ir",
@@ -207,9 +207,9 @@ dependencies = [
 
 [[package]]
 name = "chalk-solve"
-version = "0.83.0"
+version = "0.84.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c76f2db19c5e8a3d42340cf5b4d90b8c218750536fca35e2bb285ab6653c0bc8"
+checksum = "3af1d111f11c91c48ace02e93e470c5bae6d2631bd112e4545317da53660d7fc"
 dependencies = [
  "chalk-derive",
  "chalk-ir",
@@ -248,24 +248,10 @@ dependencies = [
 ]
 
 [[package]]
-name = "crossbeam"
-version = "0.8.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4ae5588f6b3c3cb05239e90bd110f257254aecd01e4635400391aeae07497845"
-dependencies = [
- "cfg-if",
- "crossbeam-channel",
- "crossbeam-deque",
- "crossbeam-epoch",
- "crossbeam-queue",
- "crossbeam-utils",
-]
-
-[[package]]
 name = "crossbeam-channel"
-version = "0.5.5"
+version = "0.5.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4c02a4d71819009c192cf4872265391563fd6a84c81ff2c0f2a7026ca4c1d85c"
+checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521"
 dependencies = [
  "cfg-if",
  "crossbeam-utils",
@@ -273,9 +259,9 @@ dependencies = [
 
 [[package]]
 name = "crossbeam-deque"
-version = "0.8.1"
+version = "0.8.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e"
+checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc"
 dependencies = [
  "cfg-if",
  "crossbeam-epoch",
@@ -284,9 +270,9 @@ dependencies = [
 
 [[package]]
 name = "crossbeam-epoch"
-version = "0.9.9"
+version = "0.9.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "07db9d94cbd326813772c968ccd25999e5f8ae22f4f8d1b11effa37ef6ce281d"
+checksum = "045ebe27666471bb549370b4b0b3e51b07f56325befa4284db65fc89c02511b1"
 dependencies = [
  "autocfg",
  "cfg-if",
@@ -297,20 +283,10 @@ dependencies = [
 ]
 
 [[package]]
-name = "crossbeam-queue"
-version = "0.3.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1f25d8400f4a7a5778f0e4e52384a48cbd9b5c495d110786187fc750075277a2"
-dependencies = [
- "cfg-if",
- "crossbeam-utils",
-]
-
-[[package]]
 name = "crossbeam-utils"
-version = "0.8.10"
+version = "0.8.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7d82ee10ce34d7bc12c2122495e7593a9c41347ecdd64185af4ecf72cb1a7f83"
+checksum = "51887d4adc7b564537b15adcfb307936f8075dfcd5f00dde9a9f1d29383682bc"
 dependencies = [
  "cfg-if",
  "once_cell",
@@ -359,9 +335,9 @@ checksum = "9bda8e21c04aca2ae33ffc2fd8c23134f3cac46db123ba97bd9d3f3b8a4a85e1"
 
 [[package]]
 name = "either"
-version = "1.7.0"
+version = "1.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3f107b87b6afc2a64fd13cac55fe06d6c8859f12d4b14cbcdd2c67d0976781be"
+checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797"
 
 [[package]]
 name = "ena"
@@ -458,15 +434,15 @@ checksum = "7ab85b9b05e3978cc9a9cf8fea7f01b494e1a09ed3037e16ba39edc7a29eb61a"
 
 [[package]]
 name = "gimli"
-version = "0.26.1"
+version = "0.26.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "78cc372d058dcf6d5ecd98510e7fbc9e5aec4d21de70f65fea8fecebcd881bd4"
+checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d"
 
 [[package]]
 name = "hashbrown"
-version = "0.12.1"
+version = "0.12.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "db0d4cf898abf0081f964436dc980e96670a0f36863e4b83aaacdb65c9d7ccc3"
+checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
 
 [[package]]
 name = "heck"
@@ -728,6 +704,7 @@ dependencies = [
  "ide-db",
  "itertools",
  "parser",
+ "stdx",
  "syntax",
  "test-utils",
  "text-edit",
@@ -794,9 +771,9 @@ dependencies = [
 
 [[package]]
 name = "itoa"
-version = "1.0.2"
+version = "1.0.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d"
+checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754"
 
 [[package]]
 name = "jod-thread"
@@ -836,9 +813,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
 
 [[package]]
 name = "libc"
-version = "0.2.126"
+version = "0.2.132"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836"
+checksum = "8371e4e5341c3a96db127eb2465ac681ced4c433e01dd0e938adbef26ba93ba5"
 
 [[package]]
 name = "libloading"
@@ -895,9 +872,9 @@ dependencies = [
 
 [[package]]
 name = "lsp-types"
-version = "0.93.0"
+version = "0.93.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "70c74e2173b2b31f8655d33724b4b45ac13f439386f66290f539c22b144c2212"
+checksum = "a3bcfee315dde785ba887edb540b08765fd7df75a7d948844be6bf5712246734"
 dependencies = [
  "bitflags",
  "serde",
@@ -944,9 +921,9 @@ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
 
 [[package]]
 name = "memmap2"
-version = "0.5.4"
+version = "0.5.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d5172b50c23043ff43dd53e51392f36519d9b35a8f3a410d30ece5d1aedd58ae"
+checksum = "95af15f345b17af2efc8ead6080fb8bc376f8cec1b35277b935637595fe77498"
 dependencies = [
  "libc",
 ]
@@ -1001,9 +978,9 @@ dependencies = [
 
 [[package]]
 name = "notify"
-version = "5.0.0-pre.15"
+version = "5.0.0-pre.16"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "553f9844ad0b0824605c20fb55a661679782680410abfb1a8144c2e7e437e7a7"
+checksum = "530f6314d6904508082f4ea424a0275cf62d341e118b313663f266429cb19693"
 dependencies = [
  "bitflags",
  "crossbeam-channel",
@@ -1029,15 +1006,6 @@ dependencies = [
 
 [[package]]
 name = "object"
-version = "0.28.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e42c982f2d955fac81dd7e1d0e1426a7d702acd9c98d19ab01083a6a0328c424"
-dependencies = [
- "memchr",
-]
-
-[[package]]
-name = "object"
 version = "0.29.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53"
@@ -1047,9 +1015,9 @@ dependencies = [
 
 [[package]]
 name = "once_cell"
-version = "1.13.0"
+version = "1.13.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1"
+checksum = "074864da206b4973b84eb91683020dbefd6a8c3f0f38e054d93954e891935e4e"
 
 [[package]]
 name = "oorandom"
@@ -1118,9 +1086,9 @@ dependencies = [
 
 [[package]]
 name = "paste"
-version = "1.0.7"
+version = "1.0.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0c520e05135d6e763148b6426a837e239041653ba7becd2e538c076c738025fc"
+checksum = "9423e2b32f7a043629287a536f21951e8c6a82482d0acb1eeebfc90bc2225b22"
 
 [[package]]
 name = "paths"
@@ -1172,7 +1140,7 @@ name = "proc-macro-api"
 version = "0.0.0"
 dependencies = [
  "memmap2",
- "object 0.29.0",
+ "object",
  "paths",
  "profile",
  "serde",
@@ -1187,12 +1155,11 @@ dependencies = [
 name = "proc-macro-srv"
 version = "0.0.0"
 dependencies = [
- "crossbeam",
  "expect-test",
  "libloading",
  "mbe",
  "memmap2",
- "object 0.29.0",
+ "object",
  "paths",
  "proc-macro-api",
  "proc-macro-test",
@@ -1221,9 +1188,9 @@ version = "0.0.0"
 
 [[package]]
 name = "proc-macro2"
-version = "1.0.40"
+version = "1.0.43"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dd96a1e8ed2596c337f8eae5f24924ec83f5ad5ab21ea8e455d3566c69fbcaf7"
+checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab"
 dependencies = [
  "unicode-ident",
 ]
@@ -1264,10 +1231,30 @@ dependencies = [
 ]
 
 [[package]]
+name = "protobuf"
+version = "3.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ee4a7d8b91800c8f167a6268d1a1026607368e1adc84e98fe044aeb905302f7"
+dependencies = [
+ "once_cell",
+ "protobuf-support",
+ "thiserror",
+]
+
+[[package]]
+name = "protobuf-support"
+version = "3.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ca157fe12fc7ee2e315f2f735e27df41b3d97cdd70ea112824dac1ffb08ee1c"
+dependencies = [
+ "thiserror",
+]
+
+[[package]]
 name = "pulldown-cmark"
-version = "0.9.1"
+version = "0.9.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "34f197a544b0c9ab3ae46c359a7ec9cbbb5c7bf97054266fecb7ead794a181d6"
+checksum = "2d9cc634bc78768157b5cbfe988ffcd1dcba95cd2b2f03a88316c08c6d00ed63"
 dependencies = [
  "bitflags",
  "memchr",
@@ -1285,9 +1272,9 @@ dependencies = [
 
 [[package]]
 name = "quote"
-version = "1.0.20"
+version = "1.0.21"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804"
+checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
 dependencies = [
  "proc-macro2",
 ]
@@ -1318,18 +1305,18 @@ dependencies = [
 
 [[package]]
 name = "redox_syscall"
-version = "0.2.13"
+version = "0.2.16"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42"
+checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
 dependencies = [
  "bitflags",
 ]
 
 [[package]]
 name = "regex"
-version = "1.5.6"
+version = "1.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d83f127d94bdbcda4c8cc2e50f6f84f4b611f69c902699ca385a39c3a75f9ff1"
+checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b"
 dependencies = [
  "regex-syntax",
 ]
@@ -1345,9 +1332,9 @@ dependencies = [
 
 [[package]]
 name = "regex-syntax"
-version = "0.6.26"
+version = "0.6.27"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "49b3de9ec5dc0a3417da371aab17d729997c15010e7fd24ff707773a33bddb64"
+checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244"
 
 [[package]]
 name = "rowan"
@@ -1394,6 +1381,7 @@ dependencies = [
  "project-model",
  "rayon",
  "rustc-hash",
+ "scip",
  "serde",
  "serde_json",
  "sourcegen",
@@ -1438,9 +1426,9 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
 
 [[package]]
 name = "ryu"
-version = "1.0.10"
+version = "1.0.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695"
+checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09"
 
 [[package]]
 name = "salsa"
@@ -1481,6 +1469,15 @@ dependencies = [
 ]
 
 [[package]]
+name = "scip"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b2bfbb10286f69fad7c78db71004b7839bf957788359fe0c479f029f9849136b"
+dependencies = [
+ "protobuf",
+]
+
+[[package]]
 name = "scoped-tls"
 version = "1.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1494,27 +1491,27 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
 
 [[package]]
 name = "semver"
-version = "1.0.12"
+version = "1.0.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a2333e6df6d6598f2b1974829f853c2b4c5f4a6e503c10af918081aa6f8564e1"
+checksum = "93f6841e709003d68bb2deee8c343572bf446003ec20a583e76f7b15cebf3711"
 dependencies = [
  "serde",
 ]
 
 [[package]]
 name = "serde"
-version = "1.0.138"
+version = "1.0.143"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1578c6245786b9d168c5447eeacfb96856573ca56c9d68fdcf394be134882a47"
+checksum = "53e8e5d5b70924f74ff5c6d64d9a5acd91422117c60f48c4e07855238a254553"
 dependencies = [
  "serde_derive",
 ]
 
 [[package]]
 name = "serde_derive"
-version = "1.0.138"
+version = "1.0.143"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "023e9b1467aef8a10fb88f25611870ada9800ef7e22afce356bb0d2387b6f27c"
+checksum = "d3d8e8de557aee63c26b85b947f5e59b690d0454c753f3adeb5cd7835ab88391"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -1523,9 +1520,9 @@ dependencies = [
 
 [[package]]
 name = "serde_json"
-version = "1.0.82"
+version = "1.0.83"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "82c2c1fdcd807d1098552c5b9a36e425e42e9fbd7c6a37a8425f390f781f7fa7"
+checksum = "38dd04e3c8279e75b31ef29dbdceebfe5ad89f4d0937213c53f7d49d01b3d5a7"
 dependencies = [
  "indexmap",
  "itoa",
@@ -1535,9 +1532,9 @@ dependencies = [
 
 [[package]]
 name = "serde_repr"
-version = "0.1.8"
+version = "0.1.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a2ad84e47328a31223de7fed7a4f5087f2d6ddfe586cf3ca25b7a165bc0a5aed"
+checksum = "1fe39d9fbb0ebf5eb2c7cb7e2a47e4f462fad1379f1166b8ae49ad9eae89a7ca"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -1594,9 +1591,9 @@ dependencies = [
 
 [[package]]
 name = "syn"
-version = "1.0.98"
+version = "1.0.99"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd"
+checksum = "58dbef6ec655055e20b86b15a8cc6d439cca19b667537ac6a1369572d151ab13"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -1666,6 +1663,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "288cb548dbe72b652243ea797201f3d481a0609a967980fcc5b2315ea811560a"
 
 [[package]]
+name = "thiserror"
+version = "1.0.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
 name = "thread_local"
 version = "1.1.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1739,9 +1756,9 @@ dependencies = [
 
 [[package]]
 name = "tracing"
-version = "0.1.35"
+version = "0.1.36"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a400e31aa60b9d44a52a8ee0343b5b18566b03a8321e0d321f695cf56e940160"
+checksum = "2fce9567bd60a67d08a16488756721ba392f24f29006402881e43b19aac64307"
 dependencies = [
  "cfg-if",
  "pin-project-lite",
@@ -1762,9 +1779,9 @@ dependencies = [
 
 [[package]]
 name = "tracing-core"
-version = "0.1.28"
+version = "0.1.29"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7b7358be39f2f274f322d2aaed611acc57f382e8eb1e5b48cb9ae30933495ce7"
+checksum = "5aeea4303076558a00714b823f9ad67d58a3bbda1df83d8827d21193156e22f7"
 dependencies = [
  "once_cell",
  "valuable",
@@ -1783,9 +1800,9 @@ dependencies = [
 
 [[package]]
 name = "tracing-subscriber"
-version = "0.3.14"
+version = "0.3.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3a713421342a5a666b7577783721d3117f1b69a393df803ee17bb73b1e122a59"
+checksum = "60db860322da191b40952ad9affe65ea23e7dd6a5c442c2c42865810c6ab8e6b"
 dependencies = [
  "matchers",
  "once_cell",
@@ -1905,6 +1922,7 @@ dependencies = [
  "indexmap",
  "paths",
  "rustc-hash",
+ "stdx",
 ]
 
 [[package]]
diff --git a/crates/base-db/src/input.rs b/crates/base-db/src/input.rs
index 9580ce8007c..b388e47dee6 100644
--- a/crates/base-db/src/input.rs
+++ b/crates/base-db/src/input.rs
@@ -9,10 +9,11 @@
 use std::{fmt, ops, panic::RefUnwindSafe, str::FromStr, sync::Arc};
 
 use cfg::CfgOptions;
-use rustc_hash::{FxHashMap, FxHashSet};
+use rustc_hash::FxHashMap;
+use stdx::hash::{NoHashHashMap, NoHashHashSet};
 use syntax::SmolStr;
 use tt::Subtree;
-use vfs::{file_set::FileSet, FileId, VfsPath};
+use vfs::{file_set::FileSet, AnchoredPath, FileId, VfsPath};
 
 /// Files are grouped into source roots. A source root is a directory on the
 /// file systems which is watched for changes. Typically it corresponds to a
@@ -31,22 +32,30 @@ pub struct SourceRoot {
     /// Libraries are considered mostly immutable, this assumption is used to
     /// optimize salsa's query structure
     pub is_library: bool,
-    pub(crate) file_set: FileSet,
+    file_set: FileSet,
 }
 
 impl SourceRoot {
     pub fn new_local(file_set: FileSet) -> SourceRoot {
         SourceRoot { is_library: false, file_set }
     }
+
     pub fn new_library(file_set: FileSet) -> SourceRoot {
         SourceRoot { is_library: true, file_set }
     }
+
     pub fn path_for_file(&self, file: &FileId) -> Option<&VfsPath> {
         self.file_set.path_for_file(file)
     }
+
     pub fn file_for_path(&self, path: &VfsPath) -> Option<&FileId> {
         self.file_set.file_for_path(path)
     }
+
+    pub fn resolve_path(&self, path: AnchoredPath<'_>) -> Option<FileId> {
+        self.file_set.resolve_path(path)
+    }
+
     pub fn iter(&self) -> impl Iterator<Item = FileId> + '_ {
         self.file_set.iter()
     }
@@ -72,12 +81,19 @@ impl SourceRoot {
 /// <https://github.com/rust-lang/rust-analyzer/blob/master/docs/dev/architecture.md#serialization>
 #[derive(Debug, Clone, Default /* Serialize, Deserialize */)]
 pub struct CrateGraph {
-    arena: FxHashMap<CrateId, CrateData>,
+    arena: NoHashHashMap<CrateId, CrateData>,
 }
 
-#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
 pub struct CrateId(pub u32);
 
+impl stdx::hash::NoHashHashable for CrateId {}
+impl std::hash::Hash for CrateId {
+    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
+        self.0.hash(state);
+    }
+}
+
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub struct CrateName(SmolStr);
 
@@ -342,7 +358,7 @@ impl CrateGraph {
         // Check if adding a dep from `from` to `to` creates a cycle. To figure
         // that out, look for a  path in the *opposite* direction, from `to` to
         // `from`.
-        if let Some(path) = self.find_path(&mut FxHashSet::default(), dep.crate_id, from) {
+        if let Some(path) = self.find_path(&mut NoHashHashSet::default(), dep.crate_id, from) {
             let path = path.into_iter().map(|it| (it, self[it].display_name.clone())).collect();
             let err = CyclicDependenciesError { path };
             assert!(err.from().0 == from && err.to().0 == dep.crate_id);
@@ -365,7 +381,7 @@ impl CrateGraph {
     /// including the crate itself.
     pub fn transitive_deps(&self, of: CrateId) -> impl Iterator<Item = CrateId> {
         let mut worklist = vec![of];
-        let mut deps = FxHashSet::default();
+        let mut deps = NoHashHashSet::default();
 
         while let Some(krate) = worklist.pop() {
             if !deps.insert(krate) {
@@ -382,10 +398,10 @@ impl CrateGraph {
     /// including the crate itself.
     pub fn transitive_rev_deps(&self, of: CrateId) -> impl Iterator<Item = CrateId> {
         let mut worklist = vec![of];
-        let mut rev_deps = FxHashSet::default();
+        let mut rev_deps = NoHashHashSet::default();
         rev_deps.insert(of);
 
-        let mut inverted_graph = FxHashMap::<_, Vec<_>>::default();
+        let mut inverted_graph = NoHashHashMap::<_, Vec<_>>::default();
         self.arena.iter().for_each(|(&krate, data)| {
             data.dependencies
                 .iter()
@@ -409,7 +425,7 @@ impl CrateGraph {
     /// come before the crate itself).
     pub fn crates_in_topological_order(&self) -> Vec<CrateId> {
         let mut res = Vec::new();
-        let mut visited = FxHashSet::default();
+        let mut visited = NoHashHashSet::default();
 
         for krate in self.arena.keys().copied() {
             go(self, &mut visited, &mut res, krate);
@@ -419,7 +435,7 @@ impl CrateGraph {
 
         fn go(
             graph: &CrateGraph,
-            visited: &mut FxHashSet<CrateId>,
+            visited: &mut NoHashHashSet<CrateId>,
             res: &mut Vec<CrateId>,
             source: CrateId,
         ) {
@@ -459,7 +475,7 @@ impl CrateGraph {
 
     fn find_path(
         &self,
-        visited: &mut FxHashSet<CrateId>,
+        visited: &mut NoHashHashSet<CrateId>,
         from: CrateId,
         to: CrateId,
     ) -> Option<Vec<CrateId>> {
diff --git a/crates/base-db/src/lib.rs b/crates/base-db/src/lib.rs
index 2d0a95b09d9..da11e4ae7bb 100644
--- a/crates/base-db/src/lib.rs
+++ b/crates/base-db/src/lib.rs
@@ -8,7 +8,7 @@ pub mod fixture;
 
 use std::{panic, sync::Arc};
 
-use rustc_hash::FxHashSet;
+use stdx::hash::NoHashHashSet;
 use syntax::{ast, Parse, SourceFile, TextRange, TextSize};
 
 pub use crate::{
@@ -58,7 +58,7 @@ pub trait FileLoader {
     /// Text of the file.
     fn file_text(&self, file_id: FileId) -> Arc<String>;
     fn resolve_path(&self, path: AnchoredPath<'_>) -> Option<FileId>;
-    fn relevant_crates(&self, file_id: FileId) -> Arc<FxHashSet<CrateId>>;
+    fn relevant_crates(&self, file_id: FileId) -> Arc<NoHashHashSet<CrateId>>;
 }
 
 /// Database which stores all significant input facts: source code and project
@@ -94,10 +94,10 @@ pub trait SourceDatabaseExt: SourceDatabase {
     #[salsa::input]
     fn source_root(&self, id: SourceRootId) -> Arc<SourceRoot>;
 
-    fn source_root_crates(&self, id: SourceRootId) -> Arc<FxHashSet<CrateId>>;
+    fn source_root_crates(&self, id: SourceRootId) -> Arc<NoHashHashSet<CrateId>>;
 }
 
-fn source_root_crates(db: &dyn SourceDatabaseExt, id: SourceRootId) -> Arc<FxHashSet<CrateId>> {
+fn source_root_crates(db: &dyn SourceDatabaseExt, id: SourceRootId) -> Arc<NoHashHashSet<CrateId>> {
     let graph = db.crate_graph();
     let res = graph
         .iter()
@@ -120,10 +120,10 @@ impl<T: SourceDatabaseExt> FileLoader for FileLoaderDelegate<&'_ T> {
         // FIXME: this *somehow* should be platform agnostic...
         let source_root = self.0.file_source_root(path.anchor);
         let source_root = self.0.source_root(source_root);
-        source_root.file_set.resolve_path(path)
+        source_root.resolve_path(path)
     }
 
-    fn relevant_crates(&self, file_id: FileId) -> Arc<FxHashSet<CrateId>> {
+    fn relevant_crates(&self, file_id: FileId) -> Arc<NoHashHashSet<CrateId>> {
         let _p = profile::span("relevant_crates");
         let source_root = self.0.file_source_root(file_id);
         self.0.source_root_crates(source_root)
diff --git a/crates/flycheck/src/lib.rs b/crates/flycheck/src/lib.rs
index 2bebea2fbfe..d9f4ef5b7ff 100644
--- a/crates/flycheck/src/lib.rs
+++ b/crates/flycheck/src/lib.rs
@@ -77,8 +77,13 @@ impl FlycheckHandle {
     }
 
     /// Schedule a re-start of the cargo check worker.
-    pub fn update(&self) {
-        self.sender.send(Restart).unwrap();
+    pub fn restart(&self) {
+        self.sender.send(Restart::Yes).unwrap();
+    }
+
+    /// Stop this cargo check worker.
+    pub fn cancel(&self) {
+        self.sender.send(Restart::No).unwrap();
     }
 
     pub fn id(&self) -> usize {
@@ -120,9 +125,13 @@ pub enum Progress {
     DidCheckCrate(String),
     DidFinish(io::Result<()>),
     DidCancel,
+    DidFailToRestart(String),
 }
 
-struct Restart;
+enum Restart {
+    Yes,
+    No,
+}
 
 struct FlycheckActor {
     id: usize,
@@ -149,6 +158,7 @@ impl FlycheckActor {
         config: FlycheckConfig,
         workspace_root: AbsPathBuf,
     ) -> FlycheckActor {
+        tracing::info!(%id, ?workspace_root, "Spawning flycheck");
         FlycheckActor { id, sender, config, workspace_root, cargo_handle: None }
     }
     fn progress(&self, progress: Progress) {
@@ -164,10 +174,13 @@ impl FlycheckActor {
     fn run(mut self, inbox: Receiver<Restart>) {
         while let Some(event) = self.next_event(&inbox) {
             match event {
-                Event::Restart(Restart) => {
+                Event::Restart(Restart::No) => {
+                    self.cancel_check_process();
+                }
+                Event::Restart(Restart::Yes) => {
                     // Cancel the previously spawned process
                     self.cancel_check_process();
-                    while let Ok(Restart) = inbox.recv_timeout(Duration::from_millis(50)) {}
+                    while let Ok(_) = inbox.recv_timeout(Duration::from_millis(50)) {}
 
                     let command = self.check_command();
                     tracing::debug!(?command, "will restart flycheck");
@@ -181,10 +194,11 @@ impl FlycheckActor {
                             self.progress(Progress::DidStart);
                         }
                         Err(error) => {
-                            tracing::error!(
-                                command = ?self.check_command(),
-                                %error, "failed to restart flycheck"
-                            );
+                            self.progress(Progress::DidFailToRestart(format!(
+                                "Failed to run the following command: {:?} error={}",
+                                self.check_command(),
+                                error
+                            )));
                         }
                     }
                 }
@@ -223,6 +237,10 @@ impl FlycheckActor {
 
     fn cancel_check_process(&mut self) {
         if let Some(cargo_handle) = self.cargo_handle.take() {
+            tracing::debug!(
+                command = ?self.check_command(),
+                "did  cancel flycheck"
+            );
             cargo_handle.cancel();
             self.progress(Progress::DidCancel);
         }
diff --git a/crates/hir-def/src/data.rs b/crates/hir-def/src/data.rs
index 35c8708955a..631ae3cf11f 100644
--- a/crates/hir-def/src/data.rs
+++ b/crates/hir-def/src/data.rs
@@ -2,7 +2,7 @@
 
 use std::sync::Arc;
 
-use hir_expand::{name::Name, AstId, ExpandResult, HirFileId, MacroCallId, MacroDefKind};
+use hir_expand::{name::Name, AstId, ExpandResult, HirFileId, InFile, MacroCallId, MacroDefKind};
 use smallvec::SmallVec;
 use syntax::ast;
 
@@ -12,7 +12,10 @@ use crate::{
     db::DefDatabase,
     intern::Interned,
     item_tree::{self, AssocItem, FnFlags, ItemTree, ItemTreeId, ModItem, Param, TreeId},
-    nameres::{attr_resolution::ResolvedAttr, proc_macro::ProcMacroKind, DefMap},
+    nameres::{
+        attr_resolution::ResolvedAttr, diagnostics::DefDiagnostic, proc_macro::ProcMacroKind,
+        DefMap,
+    },
     type_ref::{TraitRef, TypeBound, TypeRef},
     visibility::RawVisibility,
     AssocItemId, AstIdWithPath, ConstId, ConstLoc, FunctionId, FunctionLoc, HasModule, ImplId,
@@ -210,6 +213,13 @@ pub struct TraitData {
 
 impl TraitData {
     pub(crate) fn trait_data_query(db: &dyn DefDatabase, tr: TraitId) -> Arc<TraitData> {
+        db.trait_data_with_diagnostics(tr).0
+    }
+
+    pub(crate) fn trait_data_with_diagnostics_query(
+        db: &dyn DefDatabase,
+        tr: TraitId,
+    ) -> (Arc<TraitData>, Arc<Vec<DefDiagnostic>>) {
         let tr_loc @ ItemLoc { container: module_id, id: tree_id } = tr.lookup(db);
         let item_tree = tree_id.item_tree(db);
         let tr_def = &item_tree[tree_id.value];
@@ -229,17 +239,20 @@ impl TraitData {
         let mut collector =
             AssocItemCollector::new(db, module_id, tree_id.file_id(), ItemContainerId::TraitId(tr));
         collector.collect(&item_tree, tree_id.tree_id(), &tr_def.items);
-        let (items, attribute_calls) = collector.finish();
-
-        Arc::new(TraitData {
-            name,
-            attribute_calls,
-            items,
-            is_auto,
-            is_unsafe,
-            visibility,
-            skip_array_during_method_dispatch,
-        })
+        let (items, attribute_calls, diagnostics) = collector.finish();
+
+        (
+            Arc::new(TraitData {
+                name,
+                attribute_calls,
+                items,
+                is_auto,
+                is_unsafe,
+                visibility,
+                skip_array_during_method_dispatch,
+            }),
+            Arc::new(diagnostics),
+        )
     }
 
     pub fn associated_types(&self) -> impl Iterator<Item = TypeAliasId> + '_ {
@@ -280,7 +293,14 @@ pub struct ImplData {
 
 impl ImplData {
     pub(crate) fn impl_data_query(db: &dyn DefDatabase, id: ImplId) -> Arc<ImplData> {
-        let _p = profile::span("impl_data_query");
+        db.impl_data_with_diagnostics(id).0
+    }
+
+    pub(crate) fn impl_data_with_diagnostics_query(
+        db: &dyn DefDatabase,
+        id: ImplId,
+    ) -> (Arc<ImplData>, Arc<Vec<DefDiagnostic>>) {
+        let _p = profile::span("impl_data_with_diagnostics_query");
         let ItemLoc { container: module_id, id: tree_id } = id.lookup(db);
 
         let item_tree = tree_id.item_tree(db);
@@ -293,10 +313,13 @@ impl ImplData {
             AssocItemCollector::new(db, module_id, tree_id.file_id(), ItemContainerId::ImplId(id));
         collector.collect(&item_tree, tree_id.tree_id(), &impl_def.items);
 
-        let (items, attribute_calls) = collector.finish();
+        let (items, attribute_calls, diagnostics) = collector.finish();
         let items = items.into_iter().map(|(_, item)| item).collect();
 
-        Arc::new(ImplData { target_trait, self_ty, items, is_negative, attribute_calls })
+        (
+            Arc::new(ImplData { target_trait, self_ty, items, is_negative, attribute_calls }),
+            Arc::new(diagnostics),
+        )
     }
 
     pub fn attribute_calls(&self) -> impl Iterator<Item = (AstId<ast::Item>, MacroCallId)> + '_ {
@@ -437,6 +460,7 @@ struct AssocItemCollector<'a> {
     db: &'a dyn DefDatabase,
     module_id: ModuleId,
     def_map: Arc<DefMap>,
+    inactive_diagnostics: Vec<DefDiagnostic>,
     container: ItemContainerId,
     expander: Expander,
 
@@ -459,15 +483,21 @@ impl<'a> AssocItemCollector<'a> {
             expander: Expander::new(db, file_id, module_id),
             items: Vec::new(),
             attr_calls: Vec::new(),
+            inactive_diagnostics: Vec::new(),
         }
     }
 
     fn finish(
         self,
-    ) -> (Vec<(Name, AssocItemId)>, Option<Box<Vec<(AstId<ast::Item>, MacroCallId)>>>) {
+    ) -> (
+        Vec<(Name, AssocItemId)>,
+        Option<Box<Vec<(AstId<ast::Item>, MacroCallId)>>>,
+        Vec<DefDiagnostic>,
+    ) {
         (
             self.items,
             if self.attr_calls.is_empty() { None } else { Some(Box::new(self.attr_calls)) },
+            self.inactive_diagnostics,
         )
     }
 
@@ -479,6 +509,12 @@ impl<'a> AssocItemCollector<'a> {
         'items: for &item in assoc_items {
             let attrs = item_tree.attrs(self.db, self.module_id.krate, ModItem::from(item).into());
             if !attrs.is_cfg_enabled(self.expander.cfg_options()) {
+                self.inactive_diagnostics.push(DefDiagnostic::unconfigured_code(
+                    self.module_id.local_id,
+                    InFile::new(self.expander.current_file_id(), item.ast_id(&item_tree).upcast()),
+                    attrs.cfg().unwrap(),
+                    self.expander.cfg_options().clone(),
+                ));
                 continue;
             }
 
diff --git a/crates/hir-def/src/db.rs b/crates/hir-def/src/db.rs
index df6dcb024b5..40b2f734b71 100644
--- a/crates/hir-def/src/db.rs
+++ b/crates/hir-def/src/db.rs
@@ -20,7 +20,7 @@ use crate::{
     intern::Interned,
     item_tree::{AttrOwner, ItemTree},
     lang_item::{LangItemTarget, LangItems},
-    nameres::DefMap,
+    nameres::{diagnostics::DefDiagnostic, DefMap},
     visibility::{self, Visibility},
     AttrDefId, BlockId, BlockLoc, ConstId, ConstLoc, DefWithBodyId, EnumId, EnumLoc, ExternBlockId,
     ExternBlockLoc, FunctionId, FunctionLoc, GenericDefId, ImplId, ImplLoc, LocalEnumVariantId,
@@ -106,9 +106,16 @@ pub trait DefDatabase: InternDatabase + AstDatabase + Upcast<dyn AstDatabase> {
     #[salsa::invoke(ImplData::impl_data_query)]
     fn impl_data(&self, e: ImplId) -> Arc<ImplData>;
 
+    #[salsa::invoke(ImplData::impl_data_with_diagnostics_query)]
+    fn impl_data_with_diagnostics(&self, e: ImplId) -> (Arc<ImplData>, Arc<Vec<DefDiagnostic>>);
+
     #[salsa::invoke(TraitData::trait_data_query)]
     fn trait_data(&self, e: TraitId) -> Arc<TraitData>;
 
+    #[salsa::invoke(TraitData::trait_data_with_diagnostics_query)]
+    fn trait_data_with_diagnostics(&self, tr: TraitId)
+        -> (Arc<TraitData>, Arc<Vec<DefDiagnostic>>);
+
     #[salsa::invoke(TypeAliasData::type_alias_data_query)]
     fn type_alias_data(&self, e: TypeAliasId) -> Arc<TypeAliasData>;
 
diff --git a/crates/hir-def/src/nameres/diagnostics.rs b/crates/hir-def/src/nameres/diagnostics.rs
index 0d01f6d0aba..ed7e920fd2b 100644
--- a/crates/hir-def/src/nameres/diagnostics.rs
+++ b/crates/hir-def/src/nameres/diagnostics.rs
@@ -73,7 +73,7 @@ impl DefDiagnostic {
         Self { in_module: container, kind: DefDiagnosticKind::UnresolvedImport { id, index } }
     }
 
-    pub(super) fn unconfigured_code(
+    pub fn unconfigured_code(
         container: LocalModuleId,
         ast: AstId<ast::Item>,
         cfg: CfgExpr,
diff --git a/crates/hir-def/src/nameres/mod_resolution.rs b/crates/hir-def/src/nameres/mod_resolution.rs
index 99f7f1b549e..ca7bcc814e8 100644
--- a/crates/hir-def/src/nameres/mod_resolution.rs
+++ b/crates/hir-def/src/nameres/mod_resolution.rs
@@ -65,6 +65,7 @@ impl ModDir {
         name: &Name,
         attr_path: Option<&SmolStr>,
     ) -> Result<(FileId, bool, ModDir), Box<[String]>> {
+        let name = name.unescaped();
         let orig_file_id = file_id.original_file(db.upcast());
 
         let mut candidate_files = ArrayVec::<_, 2>::new();
@@ -73,12 +74,10 @@ impl ModDir {
                 candidate_files.push(self.dir_path.join_attr(attr_path, self.root_non_dir_owner))
             }
             None if file_id.is_include_macro(db.upcast()) => {
-                let name = name.unescaped();
                 candidate_files.push(format!("{}.rs", name));
                 candidate_files.push(format!("{}/mod.rs", name));
             }
             None => {
-                let name = name.unescaped();
                 candidate_files.push(format!("{}{}.rs", self.dir_path.0, name));
                 candidate_files.push(format!("{}{}/mod.rs", self.dir_path.0, name));
             }
diff --git a/crates/hir-def/src/nameres/tests/mod_resolution.rs b/crates/hir-def/src/nameres/tests/mod_resolution.rs
index 3fa585574de..ba3bf8b5a5c 100644
--- a/crates/hir-def/src/nameres/tests/mod_resolution.rs
+++ b/crates/hir-def/src/nameres/tests/mod_resolution.rs
@@ -127,7 +127,15 @@ mod r#async;
 use self::r#async::Bar;
 
 //- /async.rs
+mod foo;
+mod r#async;
 pub struct Bar;
+
+//- /async/foo.rs
+pub struct Foo;
+
+//- /async/async.rs
+pub struct Baz;
 "#,
         expect![[r#"
             crate
@@ -136,6 +144,14 @@ pub struct Bar;
 
             crate::r#async
             Bar: t v
+            foo: t
+            r#async: t
+
+            crate::r#async::foo
+            Foo: t v
+
+            crate::r#async::r#async
+            Baz: t v
         "#]],
     );
 }
diff --git a/crates/hir-def/src/test_db.rs b/crates/hir-def/src/test_db.rs
index 9cdc18d6b66..b7908bddaa1 100644
--- a/crates/hir-def/src/test_db.rs
+++ b/crates/hir-def/src/test_db.rs
@@ -10,7 +10,7 @@ use base_db::{
     SourceDatabase, Upcast,
 };
 use hir_expand::{db::AstDatabase, InFile};
-use rustc_hash::FxHashSet;
+use stdx::hash::NoHashHashSet;
 use syntax::{algo, ast, AstNode};
 
 use crate::{
@@ -76,7 +76,7 @@ impl FileLoader for TestDB {
     fn resolve_path(&self, path: AnchoredPath<'_>) -> Option<FileId> {
         FileLoaderDelegate(self).resolve_path(path)
     }
-    fn relevant_crates(&self, file_id: FileId) -> Arc<FxHashSet<CrateId>> {
+    fn relevant_crates(&self, file_id: FileId) -> Arc<NoHashHashSet<CrateId>> {
         FileLoaderDelegate(self).relevant_crates(file_id)
     }
 }
diff --git a/crates/hir-ty/Cargo.toml b/crates/hir-ty/Cargo.toml
index 5cd444c1ad3..7f143f396c7 100644
--- a/crates/hir-ty/Cargo.toml
+++ b/crates/hir-ty/Cargo.toml
@@ -18,9 +18,9 @@ ena = "0.14.0"
 tracing = "0.1.35"
 rustc-hash = "1.1.0"
 scoped-tls = "1.0.0"
-chalk-solve = { version = "0.83.0", default-features = false }
-chalk-ir = "0.83.0"
-chalk-recursive = { version = "0.83.0", default-features = false }
+chalk-solve = { version = "0.84.0", default-features = false }
+chalk-ir = "0.84.0"
+chalk-recursive = { version = "0.84.0", default-features = false }
 la-arena = { version = "0.3.0", path = "../../lib/la-arena" }
 once_cell = "1.12.0"
 typed-arena = "2.0.1"
diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs
index 95a7229e879..5df48e5fdcb 100644
--- a/crates/hir-ty/src/infer.rs
+++ b/crates/hir-ty/src/infer.rs
@@ -734,6 +734,7 @@ impl<'a> InferenceContext<'a> {
                         let ty = self.insert_type_vars(ty.substitute(Interner, &substs));
                         return (ty, Some(strukt.into()));
                     }
+                    ValueNs::ImplSelf(impl_id) => (TypeNs::SelfType(impl_id), None),
                     _ => return (self.err_ty(), None),
                 },
                 Some(ResolveValueResult::Partial(typens, unresolved)) => (typens, Some(unresolved)),
diff --git a/crates/hir-ty/src/lower.rs b/crates/hir-ty/src/lower.rs
index 64a07070f02..4a37a794533 100644
--- a/crates/hir-ty/src/lower.rs
+++ b/crates/hir-ty/src/lower.rs
@@ -6,7 +6,7 @@
 //!
 //! This usually involves resolving names, collecting generic arguments etc.
 use std::{
-    cell::{Cell, RefCell},
+    cell::{Cell, RefCell, RefMut},
     iter,
     sync::Arc,
 };
@@ -330,26 +330,29 @@ impl<'a> TyLoweringContext<'a> {
                 }
             }
             TypeRef::Macro(macro_call) => {
-                let (expander, recursion_start) = {
-                    let mut expander = self.expander.borrow_mut();
-                    if expander.is_some() {
-                        (Some(expander), false)
-                    } else {
-                        *expander = Some(Expander::new(
-                            self.db.upcast(),
-                            macro_call.file_id,
-                            self.resolver.module(),
-                        ));
-                        (Some(expander), true)
+                let (mut expander, recursion_start) = {
+                    match RefMut::filter_map(self.expander.borrow_mut(), Option::as_mut) {
+                        // There already is an expander here, this means we are already recursing
+                        Ok(expander) => (expander, false),
+                        // No expander was created yet, so we are at the start of the expansion recursion
+                        // and therefore have to create an expander.
+                        Err(expander) => (
+                            RefMut::map(expander, |it| {
+                                it.insert(Expander::new(
+                                    self.db.upcast(),
+                                    macro_call.file_id,
+                                    self.resolver.module(),
+                                ))
+                            }),
+                            true,
+                        ),
                     }
                 };
-                let ty = if let Some(mut expander) = expander {
-                    let expander_mut = expander.as_mut().unwrap();
+                let ty = {
                     let macro_call = macro_call.to_node(self.db.upcast());
-                    match expander_mut.enter_expand::<ast::Type>(self.db.upcast(), macro_call) {
+                    match expander.enter_expand::<ast::Type>(self.db.upcast(), macro_call) {
                         Ok(ExpandResult { value: Some((mark, expanded)), .. }) => {
-                            let ctx =
-                                LowerCtx::new(self.db.upcast(), expander_mut.current_file_id());
+                            let ctx = LowerCtx::new(self.db.upcast(), expander.current_file_id());
                             let type_ref = TypeRef::from_ast(&ctx, expanded);
 
                             drop(expander);
@@ -362,11 +365,14 @@ impl<'a> TyLoweringContext<'a> {
                                 .exit(self.db.upcast(), mark);
                             Some(ty)
                         }
-                        _ => None,
+                        _ => {
+                            drop(expander);
+                            None
+                        }
                     }
-                } else {
-                    None
                 };
+
+                // drop the expander, resetting it to pre-recursion state
                 if recursion_start {
                     *self.expander.borrow_mut() = None;
                 }
@@ -479,7 +485,14 @@ impl<'a> TyLoweringContext<'a> {
                         TyKind::Placeholder(to_placeholder_idx(self.db, param_id.into()))
                     }
                     ParamLoweringMode::Variable => {
-                        let idx = generics.param_idx(param_id.into()).expect("matching generics");
+                        let idx = match generics.param_idx(param_id.into()) {
+                            None => {
+                                never!("no matching generics");
+                                return (TyKind::Error.intern(Interner), None);
+                            }
+                            Some(idx) => idx,
+                        };
+
                         TyKind::BoundVar(BoundVar::new(self.in_binders, idx))
                     }
                 }
diff --git a/crates/hir-ty/src/method_resolution.rs b/crates/hir-ty/src/method_resolution.rs
index 64622545f84..9a63d5013b4 100644
--- a/crates/hir-ty/src/method_resolution.rs
+++ b/crates/hir-ty/src/method_resolution.rs
@@ -1064,6 +1064,14 @@ pub fn resolve_indexing_op(
     None
 }
 
+macro_rules! check_that {
+    ($cond:expr) => {
+        if !$cond {
+            return false;
+        }
+    };
+}
+
 fn is_valid_candidate(
     table: &mut InferenceTable<'_>,
     name: Option<&Name>,
@@ -1072,54 +1080,10 @@ fn is_valid_candidate(
     self_ty: &Ty,
     visible_from_module: Option<ModuleId>,
 ) -> bool {
-    macro_rules! check_that {
-        ($cond:expr) => {
-            if !$cond {
-                return false;
-            }
-        };
-    }
-
     let db = table.db;
     match item {
         AssocItemId::FunctionId(m) => {
-            let data = db.function_data(m);
-
-            check_that!(name.map_or(true, |n| n == &data.name));
-            check_that!(visible_from_module.map_or(true, |from_module| {
-                let v = db.function_visibility(m).is_visible_from(db.upcast(), from_module);
-                if !v {
-                    cov_mark::hit!(autoderef_candidate_not_visible);
-                }
-                v
-            }));
-
-            table.run_in_snapshot(|table| {
-                let subst = TyBuilder::subst_for_def(db, m).fill_with_inference_vars(table).build();
-                let expect_self_ty = match m.lookup(db.upcast()).container {
-                    ItemContainerId::TraitId(_) => {
-                        subst.at(Interner, 0).assert_ty_ref(Interner).clone()
-                    }
-                    ItemContainerId::ImplId(impl_id) => {
-                        subst.apply(db.impl_self_ty(impl_id).skip_binders().clone(), Interner)
-                    }
-                    // We should only get called for associated items (impl/trait)
-                    ItemContainerId::ModuleId(_) | ItemContainerId::ExternBlockId(_) => {
-                        unreachable!()
-                    }
-                };
-                check_that!(table.unify(&expect_self_ty, self_ty));
-                if let Some(receiver_ty) = receiver_ty {
-                    check_that!(data.has_self_param());
-
-                    let sig = db.callable_item_signature(m.into());
-                    let expected_receiver =
-                        sig.map(|s| s.params()[0].clone()).substitute(Interner, &subst);
-
-                    check_that!(table.unify(&receiver_ty, &expected_receiver));
-                }
-                true
-            })
+            is_valid_fn_candidate(table, m, name, receiver_ty, self_ty, visible_from_module)
         }
         AssocItemId::ConstId(c) => {
             let data = db.const_data(c);
@@ -1152,6 +1116,94 @@ fn is_valid_candidate(
     }
 }
 
+fn is_valid_fn_candidate(
+    table: &mut InferenceTable<'_>,
+    fn_id: FunctionId,
+    name: Option<&Name>,
+    receiver_ty: Option<&Ty>,
+    self_ty: &Ty,
+    visible_from_module: Option<ModuleId>,
+) -> bool {
+    let db = table.db;
+    let data = db.function_data(fn_id);
+
+    check_that!(name.map_or(true, |n| n == &data.name));
+    check_that!(visible_from_module.map_or(true, |from_module| {
+        let v = db.function_visibility(fn_id).is_visible_from(db.upcast(), from_module);
+        if !v {
+            cov_mark::hit!(autoderef_candidate_not_visible);
+        }
+        v
+    }));
+
+    table.run_in_snapshot(|table| {
+        let container = fn_id.lookup(db.upcast()).container;
+        let impl_subst = match container {
+            ItemContainerId::ImplId(it) => {
+                TyBuilder::subst_for_def(db, it).fill_with_inference_vars(table).build()
+            }
+            ItemContainerId::TraitId(it) => {
+                TyBuilder::subst_for_def(db, it).fill_with_inference_vars(table).build()
+            }
+            _ => unreachable!(),
+        };
+
+        let fn_subst = TyBuilder::subst_for_def(db, fn_id)
+            .use_parent_substs(&impl_subst)
+            .fill_with_inference_vars(table)
+            .build();
+
+        let expect_self_ty = match container {
+            ItemContainerId::TraitId(_) => fn_subst.at(Interner, 0).assert_ty_ref(Interner).clone(),
+            ItemContainerId::ImplId(impl_id) => {
+                fn_subst.apply(db.impl_self_ty(impl_id).skip_binders().clone(), Interner)
+            }
+            // We should only get called for associated items (impl/trait)
+            ItemContainerId::ModuleId(_) | ItemContainerId::ExternBlockId(_) => {
+                unreachable!()
+            }
+        };
+        check_that!(table.unify(&expect_self_ty, self_ty));
+
+        if let Some(receiver_ty) = receiver_ty {
+            check_that!(data.has_self_param());
+
+            let sig = db.callable_item_signature(fn_id.into());
+            let expected_receiver =
+                sig.map(|s| s.params()[0].clone()).substitute(Interner, &fn_subst);
+
+            check_that!(table.unify(&receiver_ty, &expected_receiver));
+        }
+
+        if let ItemContainerId::ImplId(impl_id) = container {
+            // We need to consider the bounds on the impl to distinguish functions of the same name
+            // for a type.
+            let predicates = db.generic_predicates(impl_id.into());
+            predicates
+                .iter()
+                .map(|predicate| {
+                    let (p, b) = predicate
+                        .clone()
+                        .substitute(Interner, &impl_subst)
+                        // Skipping the inner binders is ok, as we don't handle quantified where
+                        // clauses yet.
+                        .into_value_and_skipped_binders();
+                    stdx::always!(b.len(Interner) == 0);
+                    p
+                })
+                // It's ok to get ambiguity here, as we may not have enough information to prove
+                // obligations. We'll check if the user is calling the selected method properly
+                // later anyway.
+                .all(|p| table.try_obligation(p.cast(Interner)).is_some())
+        } else {
+            // For `ItemContainerId::TraitId`, we check if `self_ty` implements the trait in
+            // `iterate_trait_method_candidates()`.
+            // For others, this function shouldn't be called.
+            true
+        }
+    })
+}
+
 pub fn implements_trait(
     ty: &Canonical<Ty>,
     db: &dyn HirDatabase,
diff --git a/crates/hir-ty/src/test_db.rs b/crates/hir-ty/src/test_db.rs
index dc7252f7072..118e5311e9a 100644
--- a/crates/hir-ty/src/test_db.rs
+++ b/crates/hir-ty/src/test_db.rs
@@ -10,7 +10,7 @@ use base_db::{
 };
 use hir_def::{db::DefDatabase, ModuleId};
 use hir_expand::db::AstDatabase;
-use rustc_hash::{FxHashMap, FxHashSet};
+use stdx::hash::{NoHashHashMap, NoHashHashSet};
 use syntax::TextRange;
 use test_utils::extract_annotations;
 
@@ -80,7 +80,7 @@ impl FileLoader for TestDB {
     fn resolve_path(&self, path: AnchoredPath<'_>) -> Option<FileId> {
         FileLoaderDelegate(self).resolve_path(path)
     }
-    fn relevant_crates(&self, file_id: FileId) -> Arc<FxHashSet<CrateId>> {
+    fn relevant_crates(&self, file_id: FileId) -> Arc<NoHashHashSet<CrateId>> {
         FileLoaderDelegate(self).relevant_crates(file_id)
     }
 }
@@ -102,7 +102,7 @@ impl TestDB {
         self.module_for_file_opt(file_id).unwrap()
     }
 
-    pub(crate) fn extract_annotations(&self) -> FxHashMap<FileId, Vec<(TextRange, String)>> {
+    pub(crate) fn extract_annotations(&self) -> NoHashHashMap<FileId, Vec<(TextRange, String)>> {
         let mut files = Vec::new();
         let crate_graph = self.crate_graph();
         for krate in crate_graph.iter() {
diff --git a/crates/hir-ty/src/tests/method_resolution.rs b/crates/hir-ty/src/tests/method_resolution.rs
index 68463dc068b..81588a7c4ff 100644
--- a/crates/hir-ty/src/tests/method_resolution.rs
+++ b/crates/hir-ty/src/tests/method_resolution.rs
@@ -1790,3 +1790,46 @@ impl u16 {
         "#,
     )
 }
+
+#[test]
+fn with_impl_bounds() {
+    check_types(
+        r#"
+trait Trait {}
+struct Foo<T>(T);
+impl Trait for isize {}
+
+impl<T: Trait> Foo<T> {
+  fn foo() -> isize { 0 }
+  fn bar(&self) -> isize { 0 }
+}
+
+impl Foo<()> {
+  fn foo() {}
+  fn bar(&self) {}
+}
+
+fn f() {
+  let _ = Foo::<isize>::foo();
+    //^isize
+  let _ = Foo(0isize).bar();
+    //^isize
+  let _ = Foo::<()>::foo();
+    //^()
+  let _ = Foo(()).bar();
+    //^()
+  let _ = Foo::<usize>::foo();
+    //^{unknown}
+  let _ = Foo(0usize).bar();
+    //^{unknown}
+}
+
+fn g<T: Trait>(a: T) {
+    let _ = Foo::<T>::foo();
+      //^isize
+    let _ = Foo(a).bar();
+      //^isize
+}
+        "#,
+    );
+}
diff --git a/crates/hir-ty/src/tests/patterns.rs b/crates/hir-ty/src/tests/patterns.rs
index 94efe7bc11a..eb04bf87783 100644
--- a/crates/hir-ty/src/tests/patterns.rs
+++ b/crates/hir-ty/src/tests/patterns.rs
@@ -489,6 +489,42 @@ fn infer_adt_pattern() {
 }
 
 #[test]
+fn tuple_struct_destructured_with_self() {
+    check_infer(
+        r#"
+struct Foo(usize,);
+impl Foo {
+    fn f() {
+        let Self(s,) = &Foo(0,);
+        let Self(s,) = &mut Foo(0,);
+        let Self(s,) = Foo(0,);
+    }
+}
+        "#,
+        expect![[r#"
+            42..151 '{     ...     }': ()
+            56..64 'Self(s,)': Foo
+            61..62 's': &usize
+            67..75 '&Foo(0,)': &Foo
+            68..71 'Foo': Foo(usize) -> Foo
+            68..75 'Foo(0,)': Foo
+            72..73 '0': usize
+            89..97 'Self(s,)': Foo
+            94..95 's': &mut usize
+            100..112 '&mut Foo(0,)': &mut Foo
+            105..108 'Foo': Foo(usize) -> Foo
+            105..112 'Foo(0,)': Foo
+            109..110 '0': usize
+            126..134 'Self(s,)': Foo
+            131..132 's': usize
+            137..140 'Foo': Foo(usize) -> Foo
+            137..144 'Foo(0,)': Foo
+            141..142 '0': usize
+        "#]],
+    );
+}
+
+#[test]
 fn enum_variant_through_self_in_pattern() {
     check_infer(
         r#"
diff --git a/crates/hir-ty/src/tests/regression.rs b/crates/hir-ty/src/tests/regression.rs
index 1b5ed0603bf..c7895db1afb 100644
--- a/crates/hir-ty/src/tests/regression.rs
+++ b/crates/hir-ty/src/tests/regression.rs
@@ -1527,6 +1527,34 @@ unsafe impl Storage for InlineStorage {
 }
 
 #[test]
+fn gat_crash_3() {
+    // FIXME: This test currently crashes rust analyzer in a debug build but not in a
+    // release build (i.e. for the user). With the assumption that tests will always be run
+    // in debug mode, we catch the unwind and expect that it panicked. See the
+    // [`crate::utils::generics`] function for more information.
+    cov_mark::check!(ignore_gats);
+    std::panic::catch_unwind(|| {
+        check_no_mismatches(
+            r#"
+trait Collection {
+    type Item;
+    type Member<T>: Collection<Item = T>;
+    fn add(&mut self, value: Self::Item) -> Result<(), Self::Error>;
+}
+struct ConstGen<T, const N: usize> {
+    data: [T; N],
+}
+impl<T, const N: usize> Collection for ConstGen<T, N> {
+    type Item = T;
+    type Member<U> = ConstGen<U, N>;
+}
+        "#,
+        );
+    })
+    .expect_err("must panic");
+}
+
+#[test]
 fn cfgd_out_self_param() {
     cov_mark::check!(cfgd_out_self_param);
     check_no_mismatches(
diff --git a/crates/hir-ty/src/utils.rs b/crates/hir-ty/src/utils.rs
index 83319755da7..d6638db0285 100644
--- a/crates/hir-ty/src/utils.rs
+++ b/crates/hir-ty/src/utils.rs
@@ -176,10 +176,19 @@ pub(crate) fn generics(db: &dyn DefDatabase, def: GenericDefId) -> Generics {
     let parent_generics = parent_generic_def(db, def).map(|def| Box::new(generics(db, def)));
     if parent_generics.is_some() && matches!(def, GenericDefId::TypeAliasId(_)) {
         let params = db.generic_params(def);
+        let parent_params = &parent_generics.as_ref().unwrap().params;
         let has_consts =
             params.iter().any(|(_, x)| matches!(x, TypeOrConstParamData::ConstParamData(_)));
-        return if has_consts {
-            // XXX: treat const generic associated types as not existing to avoid crashes (#11769)
+        let parent_has_consts =
+            parent_params.iter().any(|(_, x)| matches!(x, TypeOrConstParamData::ConstParamData(_)));
+        return if has_consts || parent_has_consts {
+            // XXX: treat const generic associated types as not existing to avoid crashes
+            // (#11769)
+            //
+            // Note: Also crashes when the parent has const generics (also even if the GAT
+            // doesn't use them), see `tests::regression::gat_crash_3` for an example.
+            // Avoids that by disabling GATs when the parent (i.e. `impl` block) has
+            // const generics (#12193).
             //
             // Chalk expects the inner associated type's parameters to come
             // *before*, not after the trait's generics as we've always done it.
@@ -264,12 +273,8 @@ impl Generics {
 
     fn find_param(&self, param: TypeOrConstParamId) -> Option<(usize, &TypeOrConstParamData)> {
         if param.parent == self.def {
-            let (idx, (_local_id, data)) = self
-                .params
-                .iter()
-                .enumerate()
-                .find(|(_, (idx, _))| *idx == param.local_id)
-                .unwrap();
+            let (idx, (_local_id, data)) =
+                self.params.iter().enumerate().find(|(_, (idx, _))| *idx == param.local_id)?;
             let parent_len = self.parent_generics().map_or(0, Generics::len);
             Some((parent_len + idx, data))
         } else {
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index aa019ca4838..6dccf2ed20b 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -511,6 +511,7 @@ impl Module {
             .collect()
     }
 
+    /// Fills `acc` with the module's diagnostics.
     pub fn diagnostics(self, db: &dyn HirDatabase, acc: &mut Vec<AnyDiagnostic>) {
         let _p = profile::span("Module::diagnostics").detail(|| {
             format!("{:?}", self.name(db).map_or("<unknown>".into(), |name| name.to_string()))
@@ -531,11 +532,21 @@ impl Module {
                         m.diagnostics(db, acc)
                     }
                 }
+                ModuleDef::Trait(t) => {
+                    for diag in db.trait_data_with_diagnostics(t.id).1.iter() {
+                        emit_def_diagnostic(db, acc, diag);
+                    }
+                    acc.extend(decl.diagnostics(db))
+                }
                 _ => acc.extend(decl.diagnostics(db)),
             }
         }
 
         for impl_def in self.impl_defs(db) {
+            for diag in db.impl_data_with_diagnostics(impl_def.id).1.iter() {
+                emit_def_diagnostic(db, acc, diag);
+            }
+
             for item in impl_def.items(db) {
                 let def: DefWithBody = match item {
                     AssocItem::Function(it) => it.into(),
diff --git a/crates/ide-assists/src/handlers/extract_type_alias.rs b/crates/ide-assists/src/handlers/extract_type_alias.rs
index af584cdb438..03aa8601d14 100644
--- a/crates/ide-assists/src/handlers/extract_type_alias.rs
+++ b/crates/ide-assists/src/handlers/extract_type_alias.rs
@@ -171,6 +171,25 @@ fn collect_used_generics<'gp>(
         ast::Type::RefType(ref_) => generics.extend(
             ref_.lifetime().and_then(|lt| known_generics.iter().find(find_lifetime(&lt.text()))),
         ),
+        ast::Type::ArrayType(ar) => {
+            if let Some(expr) = ar.expr() {
+                if let ast::Expr::PathExpr(p) = expr {
+                    if let Some(path) = p.path() {
+                        if let Some(name_ref) = path.as_single_name_ref() {
+                            if let Some(param) = known_generics.iter().find(|gp| {
+                                if let ast::GenericParam::ConstParam(cp) = gp {
+                                    cp.name().map_or(false, |n| n.text() == name_ref.text())
+                                } else {
+                                    false
+                                }
+                            }) {
+                                generics.push(param);
+                            }
+                        }
+                    }
+                }
+            }
+        }
         _ => (),
     });
     // stable resort to lifetime, type, const
@@ -357,4 +376,29 @@ impl<'outer, Outer, const OUTER: usize> () {
 "#,
         );
     }
+
+    #[test]
+    fn issue_11197() {
+        check_assist(
+            extract_type_alias,
+            r#"
+struct Foo<T, const N: usize>
+where
+    [T; N]: Sized,
+{
+    arr: $0[T; N]$0,
+}
+            "#,
+            r#"
+type $0Type<T, const N: usize> = [T; N];
+
+struct Foo<T, const N: usize>
+where
+    [T; N]: Sized,
+{
+    arr: Type<T, N>,
+}
+            "#,
+        );
+    }
 }
diff --git a/crates/ide-assists/src/handlers/generate_function.rs b/crates/ide-assists/src/handlers/generate_function.rs
index d564a054089..e26c76da189 100644
--- a/crates/ide-assists/src/handlers/generate_function.rs
+++ b/crates/ide-assists/src/handlers/generate_function.rs
@@ -61,56 +61,72 @@ fn gen_fn(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
     }
 
     let fn_name = &*name_ref.text();
-    let target_module;
-    let mut adt_name = None;
+    let TargetInfo { target_module, adt_name, target, file, insert_offset } =
+        fn_target_info(ctx, path, &call, fn_name)?;
+    let function_builder = FunctionBuilder::from_call(ctx, &call, fn_name, target_module, target)?;
+    let text_range = call.syntax().text_range();
+    let label = format!("Generate {} function", function_builder.fn_name);
+    add_func_to_accumulator(
+        acc,
+        ctx,
+        text_range,
+        function_builder,
+        insert_offset,
+        file,
+        adt_name,
+        label,
+    )
+}
+
+struct TargetInfo {
+    target_module: Option<Module>,
+    adt_name: Option<hir::Name>,
+    target: GeneratedFunctionTarget,
+    file: FileId,
+    insert_offset: TextSize,
+}
 
-    let (target, file, insert_offset) = match path.qualifier() {
+impl TargetInfo {
+    fn new(
+        target_module: Option<Module>,
+        adt_name: Option<hir::Name>,
+        target: GeneratedFunctionTarget,
+        file: FileId,
+        insert_offset: TextSize,
+    ) -> Self {
+        Self { target_module, adt_name, target, file, insert_offset }
+    }
+}
+
+fn fn_target_info(
+    ctx: &AssistContext<'_>,
+    path: ast::Path,
+    call: &CallExpr,
+    fn_name: &str,
+) -> Option<TargetInfo> {
+    match path.qualifier() {
         Some(qualifier) => match ctx.sema.resolve_path(&qualifier) {
             Some(hir::PathResolution::Def(hir::ModuleDef::Module(module))) => {
-                target_module = Some(module);
-                get_fn_target(ctx, &target_module, call.clone())?
+                get_fn_target_info(ctx, &Some(module), call.clone())
             }
             Some(hir::PathResolution::Def(hir::ModuleDef::Adt(adt))) => {
                 if let hir::Adt::Enum(_) = adt {
                     // Don't suggest generating function if the name starts with an uppercase letter
-                    if name_ref.text().starts_with(char::is_uppercase) {
+                    if fn_name.starts_with(char::is_uppercase) {
                         return None;
                     }
                 }
 
-                let current_module = ctx.sema.scope(call.syntax())?.module();
-                let module = adt.module(ctx.sema.db);
-                target_module = if current_module == module { None } else { Some(module) };
-                if current_module.krate() != module.krate() {
-                    return None;
-                }
-                let (impl_, file) = get_adt_source(ctx, &adt, fn_name)?;
-                let (target, insert_offset) = get_method_target(ctx, &module, &impl_)?;
-                adt_name = if impl_.is_none() { Some(adt.name(ctx.sema.db)) } else { None };
-                (target, file, insert_offset)
+                assoc_fn_target_info(ctx, call, adt, fn_name)
             }
-            _ => {
-                return None;
+            Some(hir::PathResolution::SelfType(impl_)) => {
+                let adt = impl_.self_ty(ctx.db()).as_adt()?;
+                assoc_fn_target_info(ctx, call, adt, fn_name)
             }
+            _ => None,
         },
-        _ => {
-            target_module = None;
-            get_fn_target(ctx, &target_module, call.clone())?
-        }
-    };
-    let function_builder = FunctionBuilder::from_call(ctx, &call, fn_name, target_module, target)?;
-    let text_range = call.syntax().text_range();
-    let label = format!("Generate {} function", function_builder.fn_name);
-    add_func_to_accumulator(
-        acc,
-        ctx,
-        text_range,
-        function_builder,
-        insert_offset,
-        file,
-        adt_name,
-        label,
-    )
+        _ => get_fn_target_info(ctx, &None, call.clone()),
+    }
 }
 
 fn gen_method(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
@@ -366,6 +382,15 @@ fn make_return_type(
     (ret_type, should_focus_return_type)
 }
 
+fn get_fn_target_info(
+    ctx: &AssistContext<'_>,
+    target_module: &Option<Module>,
+    call: CallExpr,
+) -> Option<TargetInfo> {
+    let (target, file, insert_offset) = get_fn_target(ctx, target_module, call)?;
+    Some(TargetInfo::new(*target_module, None, target, file, insert_offset))
+}
+
 fn get_fn_target(
     ctx: &AssistContext<'_>,
     target_module: &Option<Module>,
@@ -399,6 +424,24 @@ fn get_method_target(
     Some((target.clone(), get_insert_offset(&target)))
 }
 
+fn assoc_fn_target_info(
+    ctx: &AssistContext<'_>,
+    call: &CallExpr,
+    adt: hir::Adt,
+    fn_name: &str,
+) -> Option<TargetInfo> {
+    let current_module = ctx.sema.scope(call.syntax())?.module();
+    let module = adt.module(ctx.sema.db);
+    let target_module = if current_module == module { None } else { Some(module) };
+    if current_module.krate() != module.krate() {
+        return None;
+    }
+    let (impl_, file) = get_adt_source(ctx, &adt, fn_name)?;
+    let (target, insert_offset) = get_method_target(ctx, &module, &impl_)?;
+    let adt_name = if impl_.is_none() { Some(adt.name(ctx.sema.db)) } else { None };
+    Some(TargetInfo::new(target_module, adt_name, target, file, insert_offset))
+}
+
 fn get_insert_offset(target: &GeneratedFunctionTarget) -> TextSize {
     match &target {
         GeneratedFunctionTarget::BehindItem(it) => it.text_range().end(),
@@ -1634,6 +1677,33 @@ fn bar() ${0:-> _} {
     }
 
     #[test]
+    fn create_static_method_within_an_impl_with_self_syntax() {
+        check_assist(
+            generate_function,
+            r"
+struct S;
+impl S {
+    fn foo(&self) {
+        Self::bar$0();
+    }
+}
+",
+            r"
+struct S;
+impl S {
+    fn foo(&self) {
+        Self::bar();
+    }
+
+    fn bar() ${0:-> _} {
+        todo!()
+    }
+}
+",
+        )
+    }
+
+    #[test]
     fn no_panic_on_invalid_global_path() {
         check_assist(
             generate_function,
diff --git a/crates/ide-assists/src/handlers/inline_call.rs b/crates/ide-assists/src/handlers/inline_call.rs
index 80d3b925593..96890ad51a6 100644
--- a/crates/ide-assists/src/handlers/inline_call.rs
+++ b/crates/ide-assists/src/handlers/inline_call.rs
@@ -13,7 +13,7 @@ use ide_db::{
 use itertools::{izip, Itertools};
 use syntax::{
     ast::{self, edit_in_place::Indent, HasArgList, PathExpr},
-    ted, AstNode,
+    ted, AstNode, NodeOrToken, SyntaxKind,
 };
 
 use crate::{
@@ -311,6 +311,17 @@ fn inline(
     } else {
         fn_body.clone_for_update()
     };
+    if let Some(imp) = body.syntax().ancestors().find_map(ast::Impl::cast) {
+        if !node.syntax().ancestors().any(|anc| &anc == imp.syntax()) {
+            if let Some(t) = imp.self_ty() {
+                body.syntax()
+                    .descendants_with_tokens()
+                    .filter_map(NodeOrToken::into_token)
+                    .filter(|tok| tok.kind() == SyntaxKind::SELF_TYPE_KW)
+                    .for_each(|tok| ted::replace(tok, t.syntax()));
+            }
+        }
+    }
     let usages_for_locals = |local| {
         Definition::Local(local)
             .usages(sema)
@@ -345,6 +356,7 @@ fn inline(
             }
         })
         .collect();
+
     if function.self_param(sema.db).is_some() {
         let this = || make::name_ref("this").syntax().clone_for_update();
         if let Some(self_local) = params[0].2.as_local(sema.db) {
@@ -1191,4 +1203,54 @@ fn bar() -> u32 {
 "#,
         )
     }
+
+    #[test]
+    fn inline_call_with_self_type() {
+        check_assist(
+            inline_call,
+            r#"
+struct A(u32);
+impl A {
+    fn f() -> Self { Self(114514) }
+}
+fn main() {
+    A::f$0();
+}
+"#,
+            r#"
+struct A(u32);
+impl A {
+    fn f() -> Self { Self(114514) }
+}
+fn main() {
+    A(114514);
+}
+"#,
+        )
+    }
+
+    #[test]
+    fn inline_call_with_self_type_but_within_same_impl() {
+        check_assist(
+            inline_call,
+            r#"
+struct A(u32);
+impl A {
+    fn f() -> Self { Self(1919810) }
+    fn main() {
+        Self::f$0();
+    }
+}
+"#,
+            r#"
+struct A(u32);
+impl A {
+    fn f() -> Self { Self(1919810) }
+    fn main() {
+        Self(1919810);
+    }
+}
+"#,
+        )
+    }
 }
diff --git a/crates/ide-assists/src/handlers/replace_turbofish_with_explicit_type.rs b/crates/ide-assists/src/handlers/replace_turbofish_with_explicit_type.rs
index 5242f3b5100..521447c26df 100644
--- a/crates/ide-assists/src/handlers/replace_turbofish_with_explicit_type.rs
+++ b/crates/ide-assists/src/handlers/replace_turbofish_with_explicit_type.rs
@@ -1,5 +1,6 @@
+use hir::HirDisplay;
 use syntax::{
-    ast::{Expr, GenericArg},
+    ast::{Expr, GenericArg, GenericArgList},
     ast::{LetStmt, Type::InferType},
     AstNode, TextRange,
 };
@@ -34,21 +35,7 @@ pub(crate) fn replace_turbofish_with_explicit_type(
 
     let initializer = let_stmt.initializer()?;
 
-    let generic_args = match &initializer {
-        Expr::MethodCallExpr(ce) => ce.generic_arg_list()?,
-        Expr::CallExpr(ce) => {
-            if let Expr::PathExpr(pe) = ce.expr()? {
-                pe.path()?.segment()?.generic_arg_list()?
-            } else {
-                cov_mark::hit!(not_applicable_if_non_path_function_call);
-                return None;
-            }
-        }
-        _ => {
-            cov_mark::hit!(not_applicable_if_non_function_call_initializer);
-            return None;
-        }
-    };
+    let generic_args = generic_arg_list(&initializer)?;
 
     // Find range of ::<_>
     let colon2 = generic_args.coloncolon_token()?;
@@ -65,7 +52,16 @@ pub(crate) fn replace_turbofish_with_explicit_type(
 
     // An improvement would be to check that this is correctly part of the return value of the
     // function call, or sub in the actual return type.
-    let turbofish_type = &turbofish_args[0];
+    let returned_type = match ctx.sema.type_of_expr(&initializer) {
+        Some(returned_type) if !returned_type.original.contains_unknown() => {
+            let module = ctx.sema.scope(let_stmt.syntax())?.module();
+            returned_type.original.display_source_code(ctx.db(), module.into()).ok()?
+        }
+        _ => {
+            cov_mark::hit!(fallback_to_turbofish_type_if_type_info_not_available);
+            turbofish_args[0].to_string()
+        }
+    };
 
     let initializer_start = initializer.syntax().text_range().start();
     if ctx.offset() > turbofish_range.end() || ctx.offset() < initializer_start {
@@ -83,7 +79,7 @@ pub(crate) fn replace_turbofish_with_explicit_type(
             "Replace turbofish with explicit type",
             TextRange::new(initializer_start, turbofish_range.end()),
             |builder| {
-                builder.insert(ident_range.end(), format!(": {}", turbofish_type));
+                builder.insert(ident_range.end(), format!(": {}", returned_type));
                 builder.delete(turbofish_range);
             },
         );
@@ -98,7 +94,7 @@ pub(crate) fn replace_turbofish_with_explicit_type(
             "Replace `_` with turbofish type",
             turbofish_range,
             |builder| {
-                builder.replace(underscore_range, turbofish_type.to_string());
+                builder.replace(underscore_range, returned_type);
                 builder.delete(turbofish_range);
             },
         );
@@ -107,6 +103,26 @@ pub(crate) fn replace_turbofish_with_explicit_type(
     None
 }
 
+fn generic_arg_list(expr: &Expr) -> Option<GenericArgList> {
+    match expr {
+        Expr::MethodCallExpr(expr) => expr.generic_arg_list(),
+        Expr::CallExpr(expr) => {
+            if let Expr::PathExpr(pe) = expr.expr()? {
+                pe.path()?.segment()?.generic_arg_list()
+            } else {
+                cov_mark::hit!(not_applicable_if_non_path_function_call);
+                return None;
+            }
+        }
+        Expr::AwaitExpr(expr) => generic_arg_list(&expr.expr()?),
+        Expr::TryExpr(expr) => generic_arg_list(&expr.expr()?),
+        _ => {
+            cov_mark::hit!(not_applicable_if_non_function_call_initializer);
+            None
+        }
+    }
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;
@@ -115,6 +131,7 @@ mod tests {
 
     #[test]
     fn replaces_turbofish_for_vec_string() {
+        cov_mark::check!(fallback_to_turbofish_type_if_type_info_not_available);
         check_assist(
             replace_turbofish_with_explicit_type,
             r#"
@@ -135,6 +152,7 @@ fn main() {
     #[test]
     fn replaces_method_calls() {
         // foo.make() is a method call which uses a different expr in the let initializer
+        cov_mark::check!(fallback_to_turbofish_type_if_type_info_not_available);
         check_assist(
             replace_turbofish_with_explicit_type,
             r#"
@@ -240,4 +258,108 @@ fn main() {
 "#,
         );
     }
+
+    #[test]
+    fn replaces_turbofish_for_known_type() {
+        check_assist(
+            replace_turbofish_with_explicit_type,
+            r#"
+fn make<T>() -> T {}
+fn main() {
+    let a = make$0::<i32>();
+}
+"#,
+            r#"
+fn make<T>() -> T {}
+fn main() {
+    let a: i32 = make();
+}
+"#,
+        );
+        check_assist(
+            replace_turbofish_with_explicit_type,
+            r#"
+//- minicore: option
+fn make<T>() -> T {}
+fn main() {
+    let a = make$0::<Option<bool>>();
+}
+"#,
+            r#"
+fn make<T>() -> T {}
+fn main() {
+    let a: Option<bool> = make();
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn replaces_turbofish_not_same_type() {
+        check_assist(
+            replace_turbofish_with_explicit_type,
+            r#"
+//- minicore: option
+fn make<T>() -> Option<T> {}
+fn main() {
+    let a = make$0::<u128>();
+}
+"#,
+            r#"
+fn make<T>() -> Option<T> {}
+fn main() {
+    let a: Option<u128> = make();
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn replaces_turbofish_for_type_with_defaulted_generic_param() {
+        check_assist(
+            replace_turbofish_with_explicit_type,
+            r#"
+struct HasDefault<T, U = i32>(T, U);
+fn make<T>() -> HasDefault<T> {}
+fn main() {
+    let a = make$0::<bool>();
+}
+"#,
+            r#"
+struct HasDefault<T, U = i32>(T, U);
+fn make<T>() -> HasDefault<T> {}
+fn main() {
+    let a: HasDefault<bool> = make();
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn replaces_turbofish_try_await() {
+        check_assist(
+            replace_turbofish_with_explicit_type,
+            r#"
+//- minicore: option, future
+struct Fut<T>(T);
+impl<T> core::future::Future for Fut<T> {
+    type Output = Option<T>;
+}
+fn make<T>() -> Fut<T> {}
+fn main() {
+    let a = make$0::<bool>().await?;
+}
+"#,
+            r#"
+struct Fut<T>(T);
+impl<T> core::future::Future for Fut<T> {
+    type Output = Option<T>;
+}
+fn make<T>() -> Fut<T> {}
+fn main() {
+    let a: bool = make().await?;
+}
+"#,
+        );
+    }
 }
diff --git a/crates/ide-assists/src/handlers/unmerge_match_arm.rs b/crates/ide-assists/src/handlers/unmerge_match_arm.rs
new file mode 100644
index 00000000000..9565f0ee6f2
--- /dev/null
+++ b/crates/ide-assists/src/handlers/unmerge_match_arm.rs
@@ -0,0 +1,293 @@
+use syntax::{
+    algo::neighbor,
+    ast::{self, edit::IndentLevel, make, AstNode},
+    ted::{self, Position},
+    Direction, SyntaxKind, T,
+};
+
+use crate::{AssistContext, AssistId, AssistKind, Assists};
+
+// Assist: unmerge_match_arm
+//
+// Splits the current match with a `|` pattern into two arms with identical bodies.
+//
+// ```
+// enum Action { Move { distance: u32 }, Stop }
+//
+// fn handle(action: Action) {
+//     match action {
+//         Action::Move(..) $0| Action::Stop => foo(),
+//     }
+// }
+// ```
+// ->
+// ```
+// enum Action { Move { distance: u32 }, Stop }
+//
+// fn handle(action: Action) {
+//     match action {
+//         Action::Move(..) => foo(),
+//         Action::Stop => foo(),
+//     }
+// }
+// ```
+pub(crate) fn unmerge_match_arm(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+    let pipe_token = ctx.find_token_syntax_at_offset(T![|])?;
+    let or_pat = ast::OrPat::cast(pipe_token.parent()?)?.clone_for_update();
+    let match_arm = ast::MatchArm::cast(or_pat.syntax().parent()?)?;
+    let match_arm_body = match_arm.expr()?;
+
+    // We don't need to check for leading pipe because it is directly under `MatchArm`
+    // without `OrPat`.
+
+    let new_parent = match_arm.syntax().parent()?;
+    let old_parent_range = new_parent.text_range();
+
+    acc.add(
+        AssistId("unmerge_match_arm", AssistKind::RefactorRewrite),
+        "Unmerge match arm",
+        pipe_token.text_range(),
+        |edit| {
+            let pats_after = pipe_token
+                .siblings_with_tokens(Direction::Next)
+                .filter_map(|it| ast::Pat::cast(it.into_node()?));
+            // FIXME: We should add a leading pipe if the original arm has one.
+            let new_match_arm = make::match_arm(
+                pats_after,
+                match_arm.guard().and_then(|guard| guard.condition()),
+                match_arm_body,
+            )
+            .clone_for_update();
+
+            let mut pipe_index = pipe_token.index();
+            if pipe_token
+                .prev_sibling_or_token()
+                .map_or(false, |it| it.kind() == SyntaxKind::WHITESPACE)
+            {
+                pipe_index -= 1;
+            }
+            or_pat.syntax().splice_children(
+                pipe_index..or_pat.syntax().children_with_tokens().count(),
+                Vec::new(),
+            );
+
+            let mut insert_after_old_arm = Vec::new();
+
+            // A comma can be:
+            //  - After the arm. In this case we always want to insert a comma after the newly
+            //    inserted arm.
+            //  - Missing after the arm, with no arms after. In this case we want to insert a
+            //    comma before the newly inserted arm. It can not be necessary if there arm
+            //    body is a block, but we don't bother to check that.
+            //  - Missing after the arm with arms after, if the arm body is a block. In this case
+            //    we don't want to insert a comma at all.
+            let has_comma_after =
+                std::iter::successors(match_arm.syntax().last_child_or_token(), |it| {
+                    it.prev_sibling_or_token()
+                })
+                .map(|it| it.kind())
+                .skip_while(|it| it.is_trivia())
+                .next()
+                    == Some(T![,]);
+            let has_arms_after = neighbor(&match_arm, Direction::Next).is_some();
+            if !has_comma_after && !has_arms_after {
+                insert_after_old_arm.push(make::token(T![,]).into());
+            }
+
+            let indent = IndentLevel::from_node(match_arm.syntax());
+            insert_after_old_arm.push(make::tokens::whitespace(&format!("\n{indent}")).into());
+
+            insert_after_old_arm.push(new_match_arm.syntax().clone().into());
+
+            ted::insert_all_raw(Position::after(match_arm.syntax()), insert_after_old_arm);
+
+            if has_comma_after {
+                ted::insert_raw(
+                    Position::last_child_of(new_match_arm.syntax()),
+                    make::token(T![,]),
+                );
+            }
+
+            edit.replace(old_parent_range, new_parent.to_string());
+        },
+    )
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::tests::{check_assist, check_assist_not_applicable};
+
+    use super::*;
+
+    #[test]
+    fn unmerge_match_arm_single_pipe() {
+        check_assist(
+            unmerge_match_arm,
+            r#"
+#[derive(Debug)]
+enum X { A, B, C }
+
+fn main() {
+    let x = X::A;
+    let y = match x {
+        X::A $0| X::B => { 1i32 }
+        X::C => { 2i32 }
+    };
+}
+"#,
+            r#"
+#[derive(Debug)]
+enum X { A, B, C }
+
+fn main() {
+    let x = X::A;
+    let y = match x {
+        X::A => { 1i32 }
+        X::B => { 1i32 }
+        X::C => { 2i32 }
+    };
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn unmerge_match_arm_guard() {
+        check_assist(
+            unmerge_match_arm,
+            r#"
+#[derive(Debug)]
+enum X { A, B, C }
+
+fn main() {
+    let x = X::A;
+    let y = match x {
+        X::A $0| X::B if true => { 1i32 }
+        _ => { 2i32 }
+    };
+}
+"#,
+            r#"
+#[derive(Debug)]
+enum X { A, B, C }
+
+fn main() {
+    let x = X::A;
+    let y = match x {
+        X::A if true => { 1i32 }
+        X::B if true => { 1i32 }
+        _ => { 2i32 }
+    };
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn unmerge_match_arm_leading_pipe() {
+        check_assist_not_applicable(
+            unmerge_match_arm,
+            r#"
+
+fn main() {
+    let y = match 0 {
+        |$0 0 => { 1i32 }
+        1 => { 2i32 }
+    };
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn unmerge_match_arm_multiple_pipes() {
+        check_assist(
+            unmerge_match_arm,
+            r#"
+#[derive(Debug)]
+enum X { A, B, C, D, E }
+
+fn main() {
+    let x = X::A;
+    let y = match x {
+        X::A | X::B |$0 X::C | X::D => 1i32,
+        X::E => 2i32,
+    };
+}
+"#,
+            r#"
+#[derive(Debug)]
+enum X { A, B, C, D, E }
+
+fn main() {
+    let x = X::A;
+    let y = match x {
+        X::A | X::B => 1i32,
+        X::C | X::D => 1i32,
+        X::E => 2i32,
+    };
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn unmerge_match_arm_inserts_comma_if_required() {
+        check_assist(
+            unmerge_match_arm,
+            r#"
+#[derive(Debug)]
+enum X { A, B }
+
+fn main() {
+    let x = X::A;
+    let y = match x {
+        X::A $0| X::B => 1i32
+    };
+}
+"#,
+            r#"
+#[derive(Debug)]
+enum X { A, B }
+
+fn main() {
+    let x = X::A;
+    let y = match x {
+        X::A => 1i32,
+        X::B => 1i32
+    };
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn unmerge_match_arm_inserts_comma_if_had_after() {
+        check_assist(
+            unmerge_match_arm,
+            r#"
+#[derive(Debug)]
+enum X { A, B }
+
+fn main() {
+    let x = X::A;
+    match x {
+        X::A $0| X::B => {},
+    }
+}
+"#,
+            r#"
+#[derive(Debug)]
+enum X { A, B }
+
+fn main() {
+    let x = X::A;
+    match x {
+        X::A => {},
+        X::B => {},
+    }
+}
+"#,
+        );
+    }
+}
diff --git a/crates/ide-assists/src/lib.rs b/crates/ide-assists/src/lib.rs
index 7fb35143fa2..c558553b1cb 100644
--- a/crates/ide-assists/src/lib.rs
+++ b/crates/ide-assists/src/lib.rs
@@ -185,6 +185,7 @@ mod handlers {
     mod replace_string_with_char;
     mod replace_turbofish_with_explicit_type;
     mod split_import;
+    mod unmerge_match_arm;
     mod sort_items;
     mod toggle_ignore;
     mod unmerge_use;
@@ -278,6 +279,7 @@ mod handlers {
             sort_items::sort_items,
             split_import::split_import,
             toggle_ignore::toggle_ignore,
+            unmerge_match_arm::unmerge_match_arm,
             unmerge_use::unmerge_use,
             unnecessary_async::unnecessary_async,
             unwrap_block::unwrap_block,
diff --git a/crates/ide-assists/src/tests/generated.rs b/crates/ide-assists/src/tests/generated.rs
index 22319f36134..7b2c16806b2 100644
--- a/crates/ide-assists/src/tests/generated.rs
+++ b/crates/ide-assists/src/tests/generated.rs
@@ -2208,6 +2208,32 @@ fn arithmetics {
 }
 
 #[test]
+fn doctest_unmerge_match_arm() {
+    check_doc_test(
+        "unmerge_match_arm",
+        r#####"
+enum Action { Move { distance: u32 }, Stop }
+
+fn handle(action: Action) {
+    match action {
+        Action::Move(..) $0| Action::Stop => foo(),
+    }
+}
+"#####,
+        r#####"
+enum Action { Move { distance: u32 }, Stop }
+
+fn handle(action: Action) {
+    match action {
+        Action::Move(..) => foo(),
+        Action::Stop => foo(),
+    }
+}
+"#####,
+    )
+}
+
+#[test]
 fn doctest_unmerge_use() {
     check_doc_test(
         "unmerge_use",
diff --git a/crates/ide-completion/src/context.rs b/crates/ide-completion/src/context.rs
index 759742d3472..a5e854b74df 100644
--- a/crates/ide-completion/src/context.rs
+++ b/crates/ide-completion/src/context.rs
@@ -64,8 +64,11 @@ pub(crate) struct PathCompletionCtx {
     pub(super) qualified: Qualified,
     /// The parent of the path we are completing.
     pub(super) parent: Option<ast::Path>,
+    #[allow(dead_code)]
     /// The path of which we are completing the segment
     pub(super) path: ast::Path,
+    /// The path of which we are completing the segment in the original file
+    pub(crate) original_path: Option<ast::Path>,
     pub(super) kind: PathKind,
     /// Whether the path segment has type args or not.
     pub(super) has_type_args: bool,
diff --git a/crates/ide-completion/src/context/analysis.rs b/crates/ide-completion/src/context/analysis.rs
index 22ec7cead49..01dd9a234f5 100644
--- a/crates/ide-completion/src/context/analysis.rs
+++ b/crates/ide-completion/src/context/analysis.rs
@@ -588,12 +588,15 @@ impl<'a> CompletionContext<'a> {
         };
 
         let path = segment.parent_path();
+        let original_path = find_node_in_file_compensated(sema, original_file, &path);
+
         let mut path_ctx = PathCompletionCtx {
             has_call_parens: false,
             has_macro_bang: false,
             qualified: Qualified::No,
             parent: None,
             path: path.clone(),
+            original_path,
             kind: PathKind::Item { kind: ItemListKind::SourceFile },
             has_type_args: false,
             use_tree_parent: false,
diff --git a/crates/ide-completion/src/render.rs b/crates/ide-completion/src/render.rs
index a2cf6d68e5b..86302cb0678 100644
--- a/crates/ide-completion/src/render.rs
+++ b/crates/ide-completion/src/render.rs
@@ -323,9 +323,7 @@ fn render_resolution_path(
             ..CompletionRelevance::default()
         });
 
-        if let Some(ref_match) = compute_ref_match(completion, &ty) {
-            item.ref_match(ref_match, path_ctx.path.syntax().text_range().start());
-        }
+        path_ref_match(completion, path_ctx, &ty, &mut item);
     };
     item
 }
@@ -453,6 +451,29 @@ fn compute_ref_match(
     None
 }
 
+fn path_ref_match(
+    completion: &CompletionContext<'_>,
+    path_ctx: &PathCompletionCtx,
+    ty: &hir::Type,
+    item: &mut Builder,
+) {
+    if let Some(original_path) = &path_ctx.original_path {
+        // At least one char was typed by the user already, in that case look for the original path
+        if let Some(original_path) = completion.sema.original_ast_node(original_path.clone()) {
+            if let Some(ref_match) = compute_ref_match(completion, ty) {
+                item.ref_match(ref_match, original_path.syntax().text_range().start());
+            }
+        }
+    } else {
+        // completion requested on an empty identifier, there is no path here yet.
+        // FIXME: This might create inconsistent completions where we show a ref match in macro inputs
+        // as long as nothing was typed yet
+        if let Some(ref_match) = compute_ref_match(completion, ty) {
+            item.ref_match(ref_match, completion.position.offset);
+        }
+    }
+}
+
 #[cfg(test)]
 mod tests {
     use std::cmp;
diff --git a/crates/ide-completion/src/render/function.rs b/crates/ide-completion/src/render/function.rs
index 9cf64691298..37612084604 100644
--- a/crates/ide-completion/src/render/function.rs
+++ b/crates/ide-completion/src/render/function.rs
@@ -79,18 +79,18 @@ fn render(
         ..ctx.completion_relevance()
     });
 
-    if let Some(ref_match) = compute_ref_match(completion, &ret_type) {
-        match func_kind {
-            FuncKind::Function(path_ctx) => {
-                item.ref_match(ref_match, path_ctx.path.syntax().text_range().start());
-            }
-            FuncKind::Method(DotAccess { receiver: Some(receiver), .. }, _) => {
-                if let Some(original_expr) = completion.sema.original_ast_node(receiver.clone()) {
+    match func_kind {
+        FuncKind::Function(path_ctx) => {
+            super::path_ref_match(completion, path_ctx, &ret_type, &mut item);
+        }
+        FuncKind::Method(DotAccess { receiver: Some(receiver), .. }, _) => {
+            if let Some(original_expr) = completion.sema.original_ast_node(receiver.clone()) {
+                if let Some(ref_match) = compute_ref_match(completion, &ret_type) {
                     item.ref_match(ref_match, original_expr.syntax().text_range().start());
                 }
             }
-            _ => (),
         }
+        _ => (),
     }
 
     item.set_documentation(ctx.docs(func))
diff --git a/crates/ide-completion/src/render/literal.rs b/crates/ide-completion/src/render/literal.rs
index af9c88a7e0a..0c791ac570c 100644
--- a/crates/ide-completion/src/render/literal.rs
+++ b/crates/ide-completion/src/render/literal.rs
@@ -2,13 +2,12 @@
 
 use hir::{db::HirDatabase, Documentation, HasAttrs, StructKind};
 use ide_db::SymbolKind;
-use syntax::AstNode;
 
 use crate::{
     context::{CompletionContext, PathCompletionCtx, PathKind},
     item::{Builder, CompletionItem},
     render::{
-        compute_ref_match, compute_type_match,
+        compute_type_match,
         variant::{
             format_literal_label, format_literal_lookup, render_record_lit, render_tuple_lit,
             visible_fields, RenderedLiteral,
@@ -125,9 +124,8 @@ fn render(
         type_match: compute_type_match(ctx.completion, &ty),
         ..ctx.completion_relevance()
     });
-    if let Some(ref_match) = compute_ref_match(completion, &ty) {
-        item.ref_match(ref_match, path_ctx.path.syntax().text_range().start());
-    }
+
+    super::path_ref_match(completion, path_ctx, &ty, &mut item);
 
     if let Some(import_to_add) = ctx.import_to_add {
         item.add_import(import_to_add);
diff --git a/crates/ide-db/src/lib.rs b/crates/ide-db/src/lib.rs
index 966bba616f6..1ec62a8425a 100644
--- a/crates/ide-db/src/lib.rs
+++ b/crates/ide-db/src/lib.rs
@@ -52,6 +52,7 @@ use hir::{
     db::{AstDatabase, DefDatabase, HirDatabase},
     symbols::FileSymbolKind,
 };
+use stdx::hash::NoHashHashSet;
 
 use crate::{line_index::LineIndex, symbol_index::SymbolsDatabase};
 pub use rustc_hash::{FxHashMap, FxHashSet, FxHasher};
@@ -118,7 +119,7 @@ impl FileLoader for RootDatabase {
     fn resolve_path(&self, path: AnchoredPath<'_>) -> Option<FileId> {
         FileLoaderDelegate(self).resolve_path(path)
     }
-    fn relevant_crates(&self, file_id: FileId) -> Arc<FxHashSet<CrateId>> {
+    fn relevant_crates(&self, file_id: FileId) -> Arc<NoHashHashSet<CrateId>> {
         FileLoaderDelegate(self).relevant_crates(file_id)
     }
 }
diff --git a/crates/ide-db/src/line_index.rs b/crates/ide-db/src/line_index.rs
index 68ad07ee83f..75d49ff2fd7 100644
--- a/crates/ide-db/src/line_index.rs
+++ b/crates/ide-db/src/line_index.rs
@@ -2,7 +2,7 @@
 //! representation.
 use std::{iter, mem};
 
-use rustc_hash::FxHashMap;
+use stdx::hash::NoHashHashMap;
 use syntax::{TextRange, TextSize};
 
 #[derive(Clone, Debug, PartialEq, Eq)]
@@ -10,7 +10,7 @@ pub struct LineIndex {
     /// Offset the the beginning of each line, zero-based
     pub(crate) newlines: Vec<TextSize>,
     /// List of non-ASCII characters on each line
-    pub(crate) utf16_lines: FxHashMap<u32, Vec<Utf16Char>>,
+    pub(crate) utf16_lines: NoHashHashMap<u32, Vec<Utf16Char>>,
 }
 
 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
@@ -55,7 +55,7 @@ impl Utf16Char {
 
 impl LineIndex {
     pub fn new(text: &str) -> LineIndex {
-        let mut utf16_lines = FxHashMap::default();
+        let mut utf16_lines = NoHashHashMap::default();
         let mut utf16_chars = Vec::new();
 
         let mut newlines = vec![0.into()];
diff --git a/crates/ide-db/src/search.rs b/crates/ide-db/src/search.rs
index 2f4aa113170..7deffe8e0f6 100644
--- a/crates/ide-db/src/search.rs
+++ b/crates/ide-db/src/search.rs
@@ -9,7 +9,7 @@ use std::{mem, sync::Arc};
 use base_db::{FileId, FileRange, SourceDatabase, SourceDatabaseExt};
 use hir::{DefWithBody, HasAttrs, HasSource, InFile, ModuleSource, Semantics, Visibility};
 use once_cell::unsync::Lazy;
-use rustc_hash::FxHashMap;
+use stdx::hash::NoHashHashMap;
 use syntax::{ast, match_ast, AstNode, TextRange, TextSize};
 
 use crate::{
@@ -20,7 +20,7 @@ use crate::{
 
 #[derive(Debug, Default, Clone)]
 pub struct UsageSearchResult {
-    pub references: FxHashMap<FileId, Vec<FileReference>>,
+    pub references: NoHashHashMap<FileId, Vec<FileReference>>,
 }
 
 impl UsageSearchResult {
@@ -45,7 +45,7 @@ impl UsageSearchResult {
 
 impl IntoIterator for UsageSearchResult {
     type Item = (FileId, Vec<FileReference>);
-    type IntoIter = <FxHashMap<FileId, Vec<FileReference>> as IntoIterator>::IntoIter;
+    type IntoIter = <NoHashHashMap<FileId, Vec<FileReference>> as IntoIterator>::IntoIter;
 
     fn into_iter(self) -> Self::IntoIter {
         self.references.into_iter()
@@ -78,17 +78,17 @@ pub enum ReferenceCategory {
 /// e.g. for things like local variables.
 #[derive(Clone, Debug)]
 pub struct SearchScope {
-    entries: FxHashMap<FileId, Option<TextRange>>,
+    entries: NoHashHashMap<FileId, Option<TextRange>>,
 }
 
 impl SearchScope {
-    fn new(entries: FxHashMap<FileId, Option<TextRange>>) -> SearchScope {
+    fn new(entries: NoHashHashMap<FileId, Option<TextRange>>) -> SearchScope {
         SearchScope { entries }
     }
 
     /// Build a search scope spanning the entire crate graph of files.
     fn crate_graph(db: &RootDatabase) -> SearchScope {
-        let mut entries = FxHashMap::default();
+        let mut entries = NoHashHashMap::default();
 
         let graph = db.crate_graph();
         for krate in graph.iter() {
@@ -102,7 +102,7 @@ impl SearchScope {
 
     /// Build a search scope spanning all the reverse dependencies of the given crate.
     fn reverse_dependencies(db: &RootDatabase, of: hir::Crate) -> SearchScope {
-        let mut entries = FxHashMap::default();
+        let mut entries = NoHashHashMap::default();
         for rev_dep in of.transitive_reverse_dependencies(db) {
             let root_file = rev_dep.root_file(db);
             let source_root_id = db.file_source_root(root_file);
@@ -117,14 +117,12 @@ impl SearchScope {
         let root_file = of.root_file(db);
         let source_root_id = db.file_source_root(root_file);
         let source_root = db.source_root(source_root_id);
-        SearchScope {
-            entries: source_root.iter().map(|id| (id, None)).collect::<FxHashMap<_, _>>(),
-        }
+        SearchScope { entries: source_root.iter().map(|id| (id, None)).collect() }
     }
 
     /// Build a search scope spanning the given module and all its submodules.
     fn module_and_children(db: &RootDatabase, module: hir::Module) -> SearchScope {
-        let mut entries = FxHashMap::default();
+        let mut entries = NoHashHashMap::default();
 
         let (file_id, range) = {
             let InFile { file_id, value } = module.definition_source(db);
@@ -157,7 +155,7 @@ impl SearchScope {
 
     /// Build an empty search scope.
     pub fn empty() -> SearchScope {
-        SearchScope::new(FxHashMap::default())
+        SearchScope::new(NoHashHashMap::default())
     }
 
     /// Build a empty search scope spanning the given file.
diff --git a/crates/ide-db/src/source_change.rs b/crates/ide-db/src/source_change.rs
index 21314ad74ef..8e338061df4 100644
--- a/crates/ide-db/src/source_change.rs
+++ b/crates/ide-db/src/source_change.rs
@@ -6,8 +6,7 @@
 use std::{collections::hash_map::Entry, iter, mem};
 
 use base_db::{AnchoredPathBuf, FileId};
-use rustc_hash::FxHashMap;
-use stdx::never;
+use stdx::{hash::NoHashHashMap, never};
 use syntax::{algo, AstNode, SyntaxNode, SyntaxNodePtr, TextRange, TextSize};
 use text_edit::{TextEdit, TextEditBuilder};
 
@@ -15,7 +14,7 @@ use crate::SnippetCap;
 
 #[derive(Default, Debug, Clone)]
 pub struct SourceChange {
-    pub source_file_edits: FxHashMap<FileId, TextEdit>,
+    pub source_file_edits: NoHashHashMap<FileId, TextEdit>,
     pub file_system_edits: Vec<FileSystemEdit>,
     pub is_snippet: bool,
 }
@@ -24,7 +23,7 @@ impl SourceChange {
     /// Creates a new SourceChange with the given label
     /// from the edits.
     pub fn from_edits(
-        source_file_edits: FxHashMap<FileId, TextEdit>,
+        source_file_edits: NoHashHashMap<FileId, TextEdit>,
         file_system_edits: Vec<FileSystemEdit>,
     ) -> Self {
         SourceChange { source_file_edits, file_system_edits, is_snippet: false }
@@ -78,8 +77,8 @@ impl Extend<FileSystemEdit> for SourceChange {
     }
 }
 
-impl From<FxHashMap<FileId, TextEdit>> for SourceChange {
-    fn from(source_file_edits: FxHashMap<FileId, TextEdit>) -> SourceChange {
+impl From<NoHashHashMap<FileId, TextEdit>> for SourceChange {
+    fn from(source_file_edits: NoHashHashMap<FileId, TextEdit>) -> SourceChange {
         SourceChange { source_file_edits, file_system_edits: Vec::new(), is_snippet: false }
     }
 }
diff --git a/crates/ide-diagnostics/src/handlers/inactive_code.rs b/crates/ide-diagnostics/src/handlers/inactive_code.rs
index 5694f33525e..04918891b25 100644
--- a/crates/ide-diagnostics/src/handlers/inactive_code.rs
+++ b/crates/ide-diagnostics/src/handlers/inactive_code.rs
@@ -106,18 +106,17 @@ fn f() {
 
     #[test]
     fn inactive_assoc_item() {
-        // FIXME these currently don't work, hence the *
         check(
             r#"
 struct Foo;
 impl Foo {
     #[cfg(any())] pub fn f() {}
-  //*************************** weak: code is inactive due to #[cfg] directives
+  //^^^^^^^^^^^^^^^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives
 }
 
 trait Bar {
     #[cfg(any())] pub fn f() {}
-  //*************************** weak: code is inactive due to #[cfg] directives
+  //^^^^^^^^^^^^^^^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives
 }
 "#,
         );
diff --git a/crates/ide-ssr/Cargo.toml b/crates/ide-ssr/Cargo.toml
index d36dd02d45c..73314e0f330 100644
--- a/crates/ide-ssr/Cargo.toml
+++ b/crates/ide-ssr/Cargo.toml
@@ -20,6 +20,7 @@ parser = { path = "../parser", version = "0.0.0" }
 syntax = { path = "../syntax", version = "0.0.0" }
 ide-db = { path = "../ide-db", version = "0.0.0" }
 hir = { path = "../hir", version = "0.0.0" }
+stdx = { path = "../stdx", version = "0.0.0" }
 
 [dev-dependencies]
 test-utils = { path = "../test-utils" }
diff --git a/crates/ide-ssr/src/lib.rs b/crates/ide-ssr/src/lib.rs
index 739e0ccb436..d9834ee63ad 100644
--- a/crates/ide-ssr/src/lib.rs
+++ b/crates/ide-ssr/src/lib.rs
@@ -86,11 +86,9 @@ pub use crate::{errors::SsrError, from_comment::ssr_from_comment, matching::Matc
 
 use crate::{errors::bail, matching::MatchFailureReason};
 use hir::Semantics;
-use ide_db::{
-    base_db::{FileId, FilePosition, FileRange},
-    FxHashMap,
-};
+use ide_db::base_db::{FileId, FilePosition, FileRange};
 use resolving::ResolvedRule;
+use stdx::hash::NoHashHashMap;
 use syntax::{ast, AstNode, SyntaxNode, TextRange};
 use text_edit::TextEdit;
 
@@ -170,9 +168,9 @@ impl<'db> MatchFinder<'db> {
     }
 
     /// Finds matches for all added rules and returns edits for all found matches.
-    pub fn edits(&self) -> FxHashMap<FileId, TextEdit> {
+    pub fn edits(&self) -> NoHashHashMap<FileId, TextEdit> {
         use ide_db::base_db::SourceDatabaseExt;
-        let mut matches_by_file = FxHashMap::default();
+        let mut matches_by_file = NoHashHashMap::default();
         for m in self.matches().matches {
             matches_by_file
                 .entry(m.range.file_id)
diff --git a/crates/ide/src/doc_links.rs b/crates/ide/src/doc_links.rs
index 582e9fe7e80..92ce26b422e 100644
--- a/crates/ide/src/doc_links.rs
+++ b/crates/ide/src/doc_links.rs
@@ -184,10 +184,10 @@ pub(crate) fn resolve_doc_path_for_def(
         Definition::TypeAlias(it) => it.resolve_doc_path(db, link, ns),
         Definition::Macro(it) => it.resolve_doc_path(db, link, ns),
         Definition::Field(it) => it.resolve_doc_path(db, link, ns),
+        Definition::SelfType(it) => it.resolve_doc_path(db, link, ns),
         Definition::BuiltinAttr(_)
         | Definition::ToolModule(_)
         | Definition::BuiltinType(_)
-        | Definition::SelfType(_)
         | Definition::Local(_)
         | Definition::GenericParam(_)
         | Definition::Label(_)
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs
index dd108fa7999..d61d69a090b 100644
--- a/crates/ide/src/lib.rs
+++ b/crates/ide/src/lib.rs
@@ -87,7 +87,7 @@ pub use crate::{
     },
     join_lines::JoinLinesConfig,
     markup::Markup,
-    moniker::{MonikerKind, MonikerResult, PackageInformation},
+    moniker::{MonikerDescriptorKind, MonikerKind, MonikerResult, PackageInformation},
     move_item::Direction,
     navigation_target::NavigationTarget,
     prime_caches::ParallelPrimeCachesProgress,
@@ -98,7 +98,7 @@ pub use crate::{
     static_index::{StaticIndex, StaticIndexedFile, TokenId, TokenStaticData},
     syntax_highlighting::{
         tags::{Highlight, HlMod, HlMods, HlOperator, HlPunct, HlTag},
-        HlRange,
+        HighlightConfig, HlRange,
     },
 };
 pub use hir::{Documentation, Semantics};
@@ -517,8 +517,12 @@ impl Analysis {
     }
 
     /// Computes syntax highlighting for the given file
-    pub fn highlight(&self, file_id: FileId) -> Cancellable<Vec<HlRange>> {
-        self.with_db(|db| syntax_highlighting::highlight(db, file_id, None, false))
+    pub fn highlight(
+        &self,
+        highlight_config: HighlightConfig,
+        file_id: FileId,
+    ) -> Cancellable<Vec<HlRange>> {
+        self.with_db(|db| syntax_highlighting::highlight(db, highlight_config, file_id, None))
     }
 
     /// Computes all ranges to highlight for a given item in a file.
@@ -533,9 +537,13 @@ impl Analysis {
     }
 
     /// Computes syntax highlighting for the given file range.
-    pub fn highlight_range(&self, frange: FileRange) -> Cancellable<Vec<HlRange>> {
+    pub fn highlight_range(
+        &self,
+        highlight_config: HighlightConfig,
+        frange: FileRange,
+    ) -> Cancellable<Vec<HlRange>> {
         self.with_db(|db| {
-            syntax_highlighting::highlight(db, frange.file_id, Some(frange.range), false)
+            syntax_highlighting::highlight(db, highlight_config, frange.file_id, Some(frange.range))
         })
     }
 
diff --git a/crates/ide/src/moniker.rs b/crates/ide/src/moniker.rs
index 4f758967b46..600a526300c 100644
--- a/crates/ide/src/moniker.rs
+++ b/crates/ide/src/moniker.rs
@@ -14,16 +14,38 @@ use syntax::{AstNode, SyntaxKind::*, T};
 use crate::{doc_links::token_as_doc_comment, RangeInfo};
 
 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub enum MonikerDescriptorKind {
+    Namespace,
+    Type,
+    Term,
+    Method,
+    TypeParameter,
+    Parameter,
+    Macro,
+    Meta,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub struct MonikerDescriptor {
+    pub name: Name,
+    pub desc: MonikerDescriptorKind,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
 pub struct MonikerIdentifier {
-    crate_name: String,
-    path: Vec<Name>,
+    pub crate_name: String,
+    pub description: Vec<MonikerDescriptor>,
 }
 
 impl ToString for MonikerIdentifier {
     fn to_string(&self) -> String {
         match self {
-            MonikerIdentifier { path, crate_name } => {
-                format!("{}::{}", crate_name, path.iter().map(|x| x.to_string()).join("::"))
+            MonikerIdentifier { description, crate_name } => {
+                format!(
+                    "{}::{}",
+                    crate_name,
+                    description.iter().map(|x| x.name.to_string()).join("::")
+                )
             }
         }
     }
@@ -42,6 +64,12 @@ pub struct MonikerResult {
     pub package_information: PackageInformation,
 }
 
+impl MonikerResult {
+    pub fn from_def(db: &RootDatabase, def: Definition, from_crate: Crate) -> Option<Self> {
+        def_to_moniker(db, def, from_crate)
+    }
+}
+
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub struct PackageInformation {
     pub name: String,
@@ -105,13 +133,23 @@ pub(crate) fn def_to_moniker(
     def: Definition,
     from_crate: Crate,
 ) -> Option<MonikerResult> {
-    if matches!(def, Definition::GenericParam(_) | Definition::SelfType(_) | Definition::Local(_)) {
+    if matches!(
+        def,
+        Definition::GenericParam(_)
+            | Definition::Label(_)
+            | Definition::DeriveHelper(_)
+            | Definition::BuiltinAttr(_)
+            | Definition::ToolModule(_)
+    ) {
         return None;
     }
+
     let module = def.module(db)?;
     let krate = module.krate();
-    let mut path = vec![];
-    path.extend(module.path_to_root(db).into_iter().filter_map(|x| x.name(db)));
+    let mut description = vec![];
+    description.extend(module.path_to_root(db).into_iter().filter_map(|x| {
+        Some(MonikerDescriptor { name: x.name(db)?, desc: MonikerDescriptorKind::Namespace })
+    }));
 
     // Handle associated items within a trait
     if let Some(assoc) = def.as_assoc_item(db) {
@@ -120,31 +158,98 @@ pub(crate) fn def_to_moniker(
             AssocItemContainer::Trait(trait_) => {
                 // Because different traits can have functions with the same name,
                 // we have to include the trait name as part of the moniker for uniqueness.
-                path.push(trait_.name(db));
+                description.push(MonikerDescriptor {
+                    name: trait_.name(db),
+                    desc: MonikerDescriptorKind::Type,
+                });
             }
             AssocItemContainer::Impl(impl_) => {
                 // Because a struct can implement multiple traits, for implementations
                 // we add both the struct name and the trait name to the path
                 if let Some(adt) = impl_.self_ty(db).as_adt() {
-                    path.push(adt.name(db));
+                    description.push(MonikerDescriptor {
+                        name: adt.name(db),
+                        desc: MonikerDescriptorKind::Type,
+                    });
                 }
 
                 if let Some(trait_) = impl_.trait_(db) {
-                    path.push(trait_.name(db));
+                    description.push(MonikerDescriptor {
+                        name: trait_.name(db),
+                        desc: MonikerDescriptorKind::Type,
+                    });
                 }
             }
         }
     }
 
     if let Definition::Field(it) = def {
-        path.push(it.parent_def(db).name(db));
+        description.push(MonikerDescriptor {
+            name: it.parent_def(db).name(db),
+            desc: MonikerDescriptorKind::Type,
+        });
     }
 
-    path.push(def.name(db)?);
+    let name_desc = match def {
+        // These are handled by top-level guard (for performance).
+        Definition::GenericParam(_)
+        | Definition::Label(_)
+        | Definition::DeriveHelper(_)
+        | Definition::BuiltinAttr(_)
+        | Definition::ToolModule(_) => return None,
+
+        Definition::Local(local) => {
+            if !local.is_param(db) {
+                return None;
+            }
+
+            MonikerDescriptor { name: local.name(db), desc: MonikerDescriptorKind::Parameter }
+        }
+        Definition::Macro(m) => {
+            MonikerDescriptor { name: m.name(db), desc: MonikerDescriptorKind::Macro }
+        }
+        Definition::Function(f) => {
+            MonikerDescriptor { name: f.name(db), desc: MonikerDescriptorKind::Method }
+        }
+        Definition::Variant(v) => {
+            MonikerDescriptor { name: v.name(db), desc: MonikerDescriptorKind::Type }
+        }
+        Definition::Const(c) => {
+            MonikerDescriptor { name: c.name(db)?, desc: MonikerDescriptorKind::Term }
+        }
+        Definition::Trait(trait_) => {
+            MonikerDescriptor { name: trait_.name(db), desc: MonikerDescriptorKind::Type }
+        }
+        Definition::TypeAlias(ta) => {
+            MonikerDescriptor { name: ta.name(db), desc: MonikerDescriptorKind::TypeParameter }
+        }
+        Definition::Module(m) => {
+            MonikerDescriptor { name: m.name(db)?, desc: MonikerDescriptorKind::Namespace }
+        }
+        Definition::BuiltinType(b) => {
+            MonikerDescriptor { name: b.name(), desc: MonikerDescriptorKind::Type }
+        }
+        Definition::SelfType(imp) => MonikerDescriptor {
+            name: imp.self_ty(db).as_adt()?.name(db),
+            desc: MonikerDescriptorKind::Type,
+        },
+        Definition::Field(it) => {
+            MonikerDescriptor { name: it.name(db), desc: MonikerDescriptorKind::Term }
+        }
+        Definition::Adt(adt) => {
+            MonikerDescriptor { name: adt.name(db), desc: MonikerDescriptorKind::Type }
+        }
+        Definition::Static(s) => {
+            MonikerDescriptor { name: s.name(db), desc: MonikerDescriptorKind::Meta }
+        }
+    };
+
+    description.push(name_desc);
+
     Some(MonikerResult {
         identifier: MonikerIdentifier {
             crate_name: krate.display_name(db)?.crate_name().to_string(),
-            path,
+            description,
         },
         kind: if krate == from_crate { MonikerKind::Export } else { MonikerKind::Import },
         package_information: {
diff --git a/crates/ide/src/prime_caches.rs b/crates/ide/src/prime_caches.rs
index 29627003600..87b3ef380c5 100644
--- a/crates/ide/src/prime_caches.rs
+++ b/crates/ide/src/prime_caches.rs
@@ -12,8 +12,9 @@ use ide_db::{
         salsa::{Database, ParallelDatabase, Snapshot},
         Cancelled, CrateGraph, CrateId, SourceDatabase, SourceDatabaseExt,
     },
-    FxHashSet, FxIndexMap,
+    FxIndexMap,
 };
+use stdx::hash::NoHashHashSet;
 
 use crate::RootDatabase;
 
@@ -141,7 +142,7 @@ pub(crate) fn parallel_prime_caches(
     }
 }
 
-fn compute_crates_to_prime(db: &RootDatabase, graph: &CrateGraph) -> FxHashSet<CrateId> {
+fn compute_crates_to_prime(db: &RootDatabase, graph: &CrateGraph) -> NoHashHashSet<CrateId> {
     // We're only interested in the workspace crates and the `ImportMap`s of their direct
     // dependencies, though in practice the latter also compute the `DefMap`s.
     // We don't prime transitive dependencies because they're generally not visible in
diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs
index 1a6beec1881..99614b645e4 100644
--- a/crates/ide/src/references.rs
+++ b/crates/ide/src/references.rs
@@ -14,8 +14,9 @@ use ide_db::{
     base_db::FileId,
     defs::{Definition, NameClass, NameRefClass},
     search::{ReferenceCategory, SearchScope, UsageSearchResult},
-    FxHashMap, RootDatabase,
+    RootDatabase,
 };
+use stdx::hash::NoHashHashMap;
 use syntax::{
     algo::find_node_at_offset,
     ast::{self, HasName},
@@ -29,7 +30,7 @@ use crate::{FilePosition, NavigationTarget, TryToNav};
 #[derive(Debug, Clone)]
 pub struct ReferenceSearchResult {
     pub declaration: Option<Declaration>,
-    pub references: FxHashMap<FileId, Vec<(TextRange, Option<ReferenceCategory>)>>,
+    pub references: NoHashHashMap<FileId, Vec<(TextRange, Option<ReferenceCategory>)>>,
 }
 
 #[derive(Debug, Clone)]
diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs
index 3fb49b45d98..50371d620eb 100644
--- a/crates/ide/src/syntax_highlighting.rs
+++ b/crates/ide/src/syntax_highlighting.rs
@@ -14,7 +14,7 @@ mod html;
 mod tests;
 
 use hir::{Name, Semantics};
-use ide_db::{FxHashMap, RootDatabase};
+use ide_db::{FxHashMap, RootDatabase, SymbolKind};
 use syntax::{
     ast, AstNode, AstToken, NodeOrToken, SyntaxKind::*, SyntaxNode, TextRange, WalkEvent, T,
 };
@@ -24,7 +24,7 @@ use crate::{
         escape::highlight_escape_string, format::highlight_format_string, highlights::Highlights,
         macro_::MacroHighlighter, tags::Highlight,
     },
-    FileId, HlMod, HlTag,
+    FileId, HlMod, HlOperator, HlPunct, HlTag,
 };
 
 pub(crate) use html::highlight_as_html;
@@ -36,6 +36,26 @@ pub struct HlRange {
     pub binding_hash: Option<u64>,
 }
 
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub struct HighlightConfig {
+    /// Whether to highlight strings
+    pub strings: bool,
+    /// Whether to highlight punctuation
+    pub punctuation: bool,
+    /// Whether to specialize punctuation highlights
+    pub specialize_punctuation: bool,
+    /// Whether to highlight operator
+    pub operator: bool,
+    /// Whether to specialize operator highlights
+    pub specialize_operator: bool,
+    /// Whether to inject highlights into doc comments
+    pub inject_doc_comment: bool,
+    /// Whether to highlight the macro call bang
+    pub macro_bang: bool,
+    /// Whether to highlight unresolved things be their syntax
+    pub syntactic_name_ref_highlighting: bool,
+}
+
 // Feature: Semantic Syntax Highlighting
 //
 // rust-analyzer highlights the code semantically.
@@ -155,9 +175,9 @@ pub struct HlRange {
 // image::https://user-images.githubusercontent.com/48062697/113187625-f7f50100-9250-11eb-825e-91c58f236071.png[]
 pub(crate) fn highlight(
     db: &RootDatabase,
+    config: HighlightConfig,
     file_id: FileId,
     range_to_highlight: Option<TextRange>,
-    syntactic_name_ref_highlighting: bool,
 ) -> Vec<HlRange> {
     let _p = profile::span("highlight");
     let sema = Semantics::new(db);
@@ -183,26 +203,18 @@ pub(crate) fn highlight(
         Some(it) => it.krate(),
         None => return hl.to_vec(),
     };
-    traverse(
-        &mut hl,
-        &sema,
-        file_id,
-        &root,
-        krate,
-        range_to_highlight,
-        syntactic_name_ref_highlighting,
-    );
+    traverse(&mut hl, &sema, config, file_id, &root, krate, range_to_highlight);
     hl.to_vec()
 }
 
 fn traverse(
     hl: &mut Highlights,
     sema: &Semantics<'_, RootDatabase>,
+    config: HighlightConfig,
     file_id: FileId,
     root: &SyntaxNode,
     krate: hir::Crate,
     range_to_highlight: TextRange,
-    syntactic_name_ref_highlighting: bool,
 ) {
     let is_unlinked = sema.to_module_def(file_id).is_none();
     let mut bindings_shadow_count: FxHashMap<Name, u32> = FxHashMap::default();
@@ -323,9 +335,11 @@ fn traverse(
             Enter(it) => it,
             Leave(NodeOrToken::Token(_)) => continue,
             Leave(NodeOrToken::Node(node)) => {
-                // Doc comment highlighting injection, we do this when leaving the node
-                // so that we overwrite the highlighting of the doc comment itself.
-                inject::doc_comment(hl, sema, file_id, &node);
+                if config.inject_doc_comment {
+                    // Doc comment highlighting injection, we do this when leaving the node
+                    // so that we overwrite the highlighting of the doc comment itself.
+                    inject::doc_comment(hl, sema, config, file_id, &node);
+                }
                 continue;
             }
         };
@@ -400,7 +414,8 @@ fn traverse(
                 let string_to_highlight = ast::String::cast(descended_token.clone());
                 if let Some((string, expanded_string)) = string.zip(string_to_highlight) {
                     if string.is_raw() {
-                        if inject::ra_fixture(hl, sema, &string, &expanded_string).is_some() {
+                        if inject::ra_fixture(hl, sema, config, &string, &expanded_string).is_some()
+                        {
                             continue;
                         }
                     }
@@ -421,7 +436,7 @@ fn traverse(
                 sema,
                 krate,
                 &mut bindings_shadow_count,
-                syntactic_name_ref_highlighting,
+                config.syntactic_name_ref_highlighting,
                 name_like,
             ),
             NodeOrToken::Token(token) => highlight::token(sema, token).zip(Some(None)),
@@ -439,6 +454,29 @@ fn traverse(
                 // something unresolvable. FIXME: There should be a way to prevent that
                 continue;
             }
+
+            // apply config filtering
+            match &mut highlight.tag {
+                HlTag::StringLiteral if !config.strings => continue,
+                // If punctuation is disabled, make the macro bang part of the macro call again.
+                tag @ HlTag::Punctuation(HlPunct::MacroBang) => {
+                    if !config.macro_bang {
+                        *tag = HlTag::Symbol(SymbolKind::Macro);
+                    } else if !config.specialize_punctuation {
+                        *tag = HlTag::Punctuation(HlPunct::Other);
+                    }
+                }
+                HlTag::Punctuation(_) if !config.punctuation => continue,
+                tag @ HlTag::Punctuation(_) if !config.specialize_punctuation => {
+                    *tag = HlTag::Punctuation(HlPunct::Other);
+                }
+                HlTag::Operator(_) if !config.operator && highlight.mods.is_empty() => continue,
+                tag @ HlTag::Operator(_) if !config.specialize_operator => {
+                    *tag = HlTag::Operator(HlOperator::Other);
+                }
+                _ => (),
+            }
+
             if inside_attribute {
                 highlight |= HlMod::Attribute
             }
diff --git a/crates/ide/src/syntax_highlighting/html.rs b/crates/ide/src/syntax_highlighting/html.rs
index 9777c014c7a..e91fd7f1257 100644
--- a/crates/ide/src/syntax_highlighting/html.rs
+++ b/crates/ide/src/syntax_highlighting/html.rs
@@ -5,7 +5,10 @@ use oorandom::Rand32;
 use stdx::format_to;
 use syntax::AstNode;
 
-use crate::{syntax_highlighting::highlight, FileId, RootDatabase};
+use crate::{
+    syntax_highlighting::{highlight, HighlightConfig},
+    FileId, RootDatabase,
+};
 
 pub(crate) fn highlight_as_html(db: &RootDatabase, file_id: FileId, rainbow: bool) -> String {
     let parse = db.parse(file_id);
@@ -20,7 +23,21 @@ pub(crate) fn highlight_as_html(db: &RootDatabase, file_id: FileId, rainbow: boo
         )
     }
 
-    let hl_ranges = highlight(db, file_id, None, false);
+    let hl_ranges = highlight(
+        db,
+        HighlightConfig {
+            strings: true,
+            punctuation: true,
+            specialize_punctuation: true,
+            specialize_operator: true,
+            operator: true,
+            inject_doc_comment: true,
+            macro_bang: true,
+            syntactic_name_ref_highlighting: false,
+        },
+        file_id,
+        None,
+    );
     let text = parse.tree().syntax().to_string();
     let mut buf = String::new();
     buf.push_str(STYLE);
diff --git a/crates/ide/src/syntax_highlighting/inject.rs b/crates/ide/src/syntax_highlighting/inject.rs
index f376f9fda7a..9139528c7ed 100644
--- a/crates/ide/src/syntax_highlighting/inject.rs
+++ b/crates/ide/src/syntax_highlighting/inject.rs
@@ -15,13 +15,14 @@ use syntax::{
 
 use crate::{
     doc_links::{doc_attributes, extract_definitions_from_docs, resolve_doc_path_for_def},
-    syntax_highlighting::{highlights::Highlights, injector::Injector},
+    syntax_highlighting::{highlights::Highlights, injector::Injector, HighlightConfig},
     Analysis, HlMod, HlRange, HlTag, RootDatabase,
 };
 
 pub(super) fn ra_fixture(
     hl: &mut Highlights,
     sema: &Semantics<'_, RootDatabase>,
+    config: HighlightConfig,
     literal: &ast::String,
     expanded: &ast::String,
 ) -> Option<()> {
@@ -63,7 +64,13 @@ pub(super) fn ra_fixture(
 
     let (analysis, tmp_file_id) = Analysis::from_single_file(inj.take_text());
 
-    for mut hl_range in analysis.highlight(tmp_file_id).unwrap() {
+    for mut hl_range in analysis
+        .highlight(
+            HighlightConfig { syntactic_name_ref_highlighting: false, ..config },
+            tmp_file_id,
+        )
+        .unwrap()
+    {
         for range in inj.map_range_up(hl_range.range) {
             if let Some(range) = literal.map_range_up(range) {
                 hl_range.range = range;
@@ -86,6 +93,7 @@ const RUSTDOC_FENCES: [&str; 2] = ["```", "~~~"];
 pub(super) fn doc_comment(
     hl: &mut Highlights,
     sema: &Semantics<'_, RootDatabase>,
+    config: HighlightConfig,
     src_file_id: FileId,
     node: &SyntaxNode,
 ) {
@@ -206,7 +214,14 @@ pub(super) fn doc_comment(
 
     let (analysis, tmp_file_id) = Analysis::from_single_file(inj.take_text());
 
-    if let Ok(ranges) = analysis.with_db(|db| super::highlight(db, tmp_file_id, None, true)) {
+    if let Ok(ranges) = analysis.with_db(|db| {
+        super::highlight(
+            db,
+            HighlightConfig { syntactic_name_ref_highlighting: true, ..config },
+            tmp_file_id,
+            None,
+        )
+    }) {
         for HlRange { range, highlight, binding_hash } in ranges {
             for range in inj.map_range_up(range) {
                 hl.add(HlRange { range, highlight: highlight | HlMod::Injected, binding_hash });
diff --git a/crates/ide/src/syntax_highlighting/tags.rs b/crates/ide/src/syntax_highlighting/tags.rs
index 5262770f303..3949f1189bd 100644
--- a/crates/ide/src/syntax_highlighting/tags.rs
+++ b/crates/ide/src/syntax_highlighting/tags.rs
@@ -199,7 +199,7 @@ impl fmt::Display for HlTag {
 }
 
 impl HlMod {
-    const ALL: &'static [HlMod; HlMod::Unsafe as u8 as usize + 1] = &[
+    const ALL: &'static [HlMod; 19] = &[
         HlMod::Associated,
         HlMod::Async,
         HlMod::Attribute,
@@ -296,7 +296,7 @@ impl Highlight {
         Highlight { tag, mods: HlMods::default() }
     }
     pub fn is_empty(&self) -> bool {
-        self.tag == HlTag::None && self.mods == HlMods::default()
+        self.tag == HlTag::None && self.mods.is_empty()
     }
 }
 
@@ -330,6 +330,10 @@ impl ops::BitOr<HlMod> for Highlight {
 }
 
 impl HlMods {
+    pub fn is_empty(&self) -> bool {
+        self.0 == 0
+    }
+
     pub fn contains(self, m: HlMod) -> bool {
         self.0 & m.mask() == m.mask()
     }
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
index a747b4bc1f9..eef5baea983 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
@@ -56,7 +56,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
     <span class="field declaration">bar</span><span class="colon">:</span> <span class="builtin_type">bool</span><span class="comma">,</span>
 <span class="brace">}</span>
 
-<span class="comment documentation">/// This is an impl with a code block.</span>
+<span class="comment documentation">/// This is an impl of </span><span class="struct documentation injected intra_doc_link">[`Foo`]</span><span class="comment documentation"> with a code block.</span>
 <span class="comment documentation">///</span>
 <span class="comment documentation">/// ```</span>
 <span class="comment documentation">///</span><span class="comment documentation"> </span><span class="keyword injected">fn</span><span class="none injected"> </span><span class="function declaration injected">foo</span><span class="parenthesis injected">(</span><span class="parenthesis injected">)</span><span class="none injected"> </span><span class="brace injected">{</span>
diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs
index 382735cb368..46cc667fc45 100644
--- a/crates/ide/src/syntax_highlighting/tests.rs
+++ b/crates/ide/src/syntax_highlighting/tests.rs
@@ -4,7 +4,18 @@ use expect_test::{expect_file, ExpectFile};
 use ide_db::SymbolKind;
 use test_utils::{bench, bench_fixture, skip_slow_tests, AssertLinear};
 
-use crate::{fixture, FileRange, HlTag, TextRange};
+use crate::{fixture, FileRange, HighlightConfig, HlTag, TextRange};
+
+const HL_CONFIG: HighlightConfig = HighlightConfig {
+    strings: true,
+    punctuation: true,
+    specialize_punctuation: true,
+    specialize_operator: true,
+    operator: true,
+    inject_doc_comment: true,
+    macro_bang: true,
+    syntactic_name_ref_highlighting: false,
+};
 
 #[test]
 fn attributes() {
@@ -613,7 +624,7 @@ struct Foo {
     bar: bool,
 }
 
-/// This is an impl with a code block.
+/// This is an impl of [`Foo`] with a code block.
 ///
 /// ```
 /// fn foo() {
@@ -996,7 +1007,10 @@ struct Foo {
 
     // The "x"
     let highlights = &analysis
-        .highlight_range(FileRange { file_id, range: TextRange::at(45.into(), 1.into()) })
+        .highlight_range(
+            HL_CONFIG,
+            FileRange { file_id, range: TextRange::at(45.into(), 1.into()) },
+        )
         .unwrap();
 
     assert_eq!(&highlights[0].highlight.to_string(), "field.declaration.public");
@@ -1011,7 +1025,7 @@ macro_rules! test {}
 }"#
         .trim(),
     );
-    let _ = analysis.highlight(file_id).unwrap();
+    let _ = analysis.highlight(HL_CONFIG, file_id).unwrap();
 }
 
 /// Highlights the code given by the `ra_fixture` argument, renders the
@@ -1035,7 +1049,7 @@ fn benchmark_syntax_highlighting_long_struct() {
     let hash = {
         let _pt = bench("syntax highlighting long struct");
         analysis
-            .highlight(file_id)
+            .highlight(HL_CONFIG, file_id)
             .unwrap()
             .iter()
             .filter(|it| it.highlight.tag == HlTag::Symbol(SymbolKind::Struct))
@@ -1061,7 +1075,7 @@ fn syntax_highlighting_not_quadratic() {
             let time = Instant::now();
 
             let hash = analysis
-                .highlight(file_id)
+                .highlight(HL_CONFIG, file_id)
                 .unwrap()
                 .iter()
                 .filter(|it| it.highlight.tag == HlTag::Symbol(SymbolKind::Struct))
@@ -1086,7 +1100,7 @@ fn benchmark_syntax_highlighting_parser() {
     let hash = {
         let _pt = bench("syntax highlighting parser");
         analysis
-            .highlight(file_id)
+            .highlight(HL_CONFIG, file_id)
             .unwrap()
             .iter()
             .filter(|it| it.highlight.tag == HlTag::Symbol(SymbolKind::Function))
diff --git a/crates/ide/src/view_crate_graph.rs b/crates/ide/src/view_crate_graph.rs
index bf7b7efe282..17a1e385b77 100644
--- a/crates/ide/src/view_crate_graph.rs
+++ b/crates/ide/src/view_crate_graph.rs
@@ -3,8 +3,9 @@ use std::sync::Arc;
 use dot::{Id, LabelText};
 use ide_db::{
     base_db::{CrateGraph, CrateId, Dependency, SourceDatabase, SourceDatabaseExt},
-    FxHashSet, RootDatabase,
+    RootDatabase,
 };
+use stdx::hash::NoHashHashSet;
 
 // Feature: View Crate Graph
 //
@@ -41,7 +42,7 @@ pub(crate) fn view_crate_graph(db: &RootDatabase, full: bool) -> Result<String,
 
 struct DotCrateGraph {
     graph: Arc<CrateGraph>,
-    crates_to_render: FxHashSet<CrateId>,
+    crates_to_render: NoHashHashSet<CrateId>,
 }
 
 type Edge<'a> = (CrateId, &'a Dependency);
diff --git a/crates/parser/src/grammar/patterns.rs b/crates/parser/src/grammar/patterns.rs
index 7e21a808da0..bc1224af9b2 100644
--- a/crates/parser/src/grammar/patterns.rs
+++ b/crates/parser/src/grammar/patterns.rs
@@ -13,6 +13,8 @@ pub(super) const PATTERN_FIRST: TokenSet =
         T![.],
     ]));
 
+const PAT_TOP_FIRST: TokenSet = PATTERN_FIRST.union(TokenSet::new(&[T![|]]));
+
 pub(crate) fn pattern(p: &mut Parser<'_>) {
     pattern_r(p, PAT_RECOVERY_SET);
 }
@@ -228,6 +230,7 @@ fn path_or_macro_pat(p: &mut Parser<'_>) -> CompletedMarker {
 //     let S(_) = ();
 //     let S(_,) = ();
 //     let S(_, .. , x) = ();
+//     let S(| a) = ();
 // }
 fn tuple_pat_fields(p: &mut Parser<'_>) {
     assert!(p.at(T!['(']));
@@ -363,6 +366,7 @@ fn ref_pat(p: &mut Parser<'_>) -> CompletedMarker {
 //     let (a,) = ();
 //     let (..) = ();
 //     let () = ();
+//     let (| a | a, | b) = ((),());
 // }
 fn tuple_pat(p: &mut Parser<'_>) -> CompletedMarker {
     assert!(p.at(T!['(']));
@@ -373,13 +377,13 @@ fn tuple_pat(p: &mut Parser<'_>) -> CompletedMarker {
     let mut has_rest = false;
     while !p.at(EOF) && !p.at(T![')']) {
         has_pat = true;
-        if !p.at_ts(PATTERN_FIRST) {
+        if !p.at_ts(PAT_TOP_FIRST) {
             p.error("expected a pattern");
             break;
         }
         has_rest |= p.at(T![..]);
 
-        pattern(p);
+        pattern_top(p);
         if !p.at(T![')']) {
             has_comma = true;
             p.expect(T![,]);
@@ -393,6 +397,7 @@ fn tuple_pat(p: &mut Parser<'_>) -> CompletedMarker {
 // test slice_pat
 // fn main() {
 //     let [a, b, ..] = [];
+//     let [| a, ..] = [];
 // }
 fn slice_pat(p: &mut Parser<'_>) -> CompletedMarker {
     assert!(p.at(T!['[']));
@@ -405,12 +410,12 @@ fn slice_pat(p: &mut Parser<'_>) -> CompletedMarker {
 
 fn pat_list(p: &mut Parser<'_>, ket: SyntaxKind) {
     while !p.at(EOF) && !p.at(ket) {
-        if !p.at_ts(PATTERN_FIRST) {
+        if !p.at_ts(PAT_TOP_FIRST) {
             p.error("expected a pattern");
             break;
         }
 
-        pattern(p);
+        pattern_top(p);
         if !p.at(ket) {
             p.expect(T![,]);
         }
diff --git a/crates/parser/test_data/parser/inline/ok/0024_slice_pat.rast b/crates/parser/test_data/parser/inline/ok/0024_slice_pat.rast
index 235a9d7f404..dff72ba886f 100644
--- a/crates/parser/test_data/parser/inline/ok/0024_slice_pat.rast
+++ b/crates/parser/test_data/parser/inline/ok/0024_slice_pat.rast
@@ -37,6 +37,29 @@ SOURCE_FILE
             L_BRACK "["
             R_BRACK "]"
           SEMICOLON ";"
+        WHITESPACE "\n    "
+        LET_STMT
+          LET_KW "let"
+          WHITESPACE " "
+          SLICE_PAT
+            L_BRACK "["
+            PIPE "|"
+            WHITESPACE " "
+            IDENT_PAT
+              NAME
+                IDENT "a"
+            COMMA ","
+            WHITESPACE " "
+            REST_PAT
+              DOT2 ".."
+            R_BRACK "]"
+          WHITESPACE " "
+          EQ "="
+          WHITESPACE " "
+          ARRAY_EXPR
+            L_BRACK "["
+            R_BRACK "]"
+          SEMICOLON ";"
         WHITESPACE "\n"
         R_CURLY "}"
   WHITESPACE "\n"
diff --git a/crates/parser/test_data/parser/inline/ok/0024_slice_pat.rs b/crates/parser/test_data/parser/inline/ok/0024_slice_pat.rs
index 7955973b952..855ba89b1e9 100644
--- a/crates/parser/test_data/parser/inline/ok/0024_slice_pat.rs
+++ b/crates/parser/test_data/parser/inline/ok/0024_slice_pat.rs
@@ -1,3 +1,4 @@
 fn main() {
     let [a, b, ..] = [];
+    let [| a, ..] = [];
 }
diff --git a/crates/parser/test_data/parser/inline/ok/0026_tuple_pat_fields.rast b/crates/parser/test_data/parser/inline/ok/0026_tuple_pat_fields.rast
index 3cdaf32b572..55baf2fdcb4 100644
--- a/crates/parser/test_data/parser/inline/ok/0026_tuple_pat_fields.rast
+++ b/crates/parser/test_data/parser/inline/ok/0026_tuple_pat_fields.rast
@@ -100,6 +100,29 @@ SOURCE_FILE
             L_PAREN "("
             R_PAREN ")"
           SEMICOLON ";"
+        WHITESPACE "\n    "
+        LET_STMT
+          LET_KW "let"
+          WHITESPACE " "
+          TUPLE_STRUCT_PAT
+            PATH
+              PATH_SEGMENT
+                NAME_REF
+                  IDENT "S"
+            L_PAREN "("
+            PIPE "|"
+            WHITESPACE " "
+            IDENT_PAT
+              NAME
+                IDENT "a"
+            R_PAREN ")"
+          WHITESPACE " "
+          EQ "="
+          WHITESPACE " "
+          TUPLE_EXPR
+            L_PAREN "("
+            R_PAREN ")"
+          SEMICOLON ";"
         WHITESPACE "\n"
         R_CURLY "}"
   WHITESPACE "\n"
diff --git a/crates/parser/test_data/parser/inline/ok/0026_tuple_pat_fields.rs b/crates/parser/test_data/parser/inline/ok/0026_tuple_pat_fields.rs
index 0dfe6362967..8ec6f4ca93e 100644
--- a/crates/parser/test_data/parser/inline/ok/0026_tuple_pat_fields.rs
+++ b/crates/parser/test_data/parser/inline/ok/0026_tuple_pat_fields.rs
@@ -3,4 +3,5 @@ fn foo() {
     let S(_) = ();
     let S(_,) = ();
     let S(_, .. , x) = ();
+    let S(| a) = ();
 }
diff --git a/crates/parser/test_data/parser/inline/ok/0111_tuple_pat.rast b/crates/parser/test_data/parser/inline/ok/0111_tuple_pat.rast
index cebe98c43aa..1a01e0f6938 100644
--- a/crates/parser/test_data/parser/inline/ok/0111_tuple_pat.rast
+++ b/crates/parser/test_data/parser/inline/ok/0111_tuple_pat.rast
@@ -85,6 +85,46 @@ SOURCE_FILE
             L_PAREN "("
             R_PAREN ")"
           SEMICOLON ";"
+        WHITESPACE "\n    "
+        LET_STMT
+          LET_KW "let"
+          WHITESPACE " "
+          TUPLE_PAT
+            L_PAREN "("
+            PIPE "|"
+            WHITESPACE " "
+            OR_PAT
+              IDENT_PAT
+                NAME
+                  IDENT "a"
+              WHITESPACE " "
+              PIPE "|"
+              WHITESPACE " "
+              IDENT_PAT
+                NAME
+                  IDENT "a"
+            COMMA ","
+            WHITESPACE " "
+            PIPE "|"
+            WHITESPACE " "
+            IDENT_PAT
+              NAME
+                IDENT "b"
+            R_PAREN ")"
+          WHITESPACE " "
+          EQ "="
+          WHITESPACE " "
+          TUPLE_EXPR
+            L_PAREN "("
+            TUPLE_EXPR
+              L_PAREN "("
+              R_PAREN ")"
+            COMMA ","
+            TUPLE_EXPR
+              L_PAREN "("
+              R_PAREN ")"
+            R_PAREN ")"
+          SEMICOLON ";"
         WHITESPACE "\n"
         R_CURLY "}"
   WHITESPACE "\n"
diff --git a/crates/parser/test_data/parser/inline/ok/0111_tuple_pat.rs b/crates/parser/test_data/parser/inline/ok/0111_tuple_pat.rs
index ba719879d4c..fbd7f48f66b 100644
--- a/crates/parser/test_data/parser/inline/ok/0111_tuple_pat.rs
+++ b/crates/parser/test_data/parser/inline/ok/0111_tuple_pat.rs
@@ -3,4 +3,5 @@ fn main() {
     let (a,) = ();
     let (..) = ();
     let () = ();
+    let (| a | a, | b) = ((),());
 }
diff --git a/crates/proc-macro-srv/Cargo.toml b/crates/proc-macro-srv/Cargo.toml
index 5746eac0b37..e39026ac70b 100644
--- a/crates/proc-macro-srv/Cargo.toml
+++ b/crates/proc-macro-srv/Cargo.toml
@@ -24,7 +24,6 @@ tt = { path = "../tt", version = "0.0.0" }
 mbe = { path = "../mbe", version = "0.0.0" }
 paths = { path = "../paths", version = "0.0.0" }
 proc-macro-api = { path = "../proc-macro-api", version = "0.0.0" }
-crossbeam = "0.8.1"
 
 [dev-dependencies]
 expect-test = "1.4.0"
diff --git a/crates/proc-macro-srv/src/lib.rs b/crates/proc-macro-srv/src/lib.rs
index 4c205b9cada..3679bfc43c9 100644
--- a/crates/proc-macro-srv/src/lib.rs
+++ b/crates/proc-macro-srv/src/lib.rs
@@ -26,6 +26,7 @@ use std::{
     ffi::OsString,
     fs,
     path::{Path, PathBuf},
+    thread,
     time::SystemTime,
 };
 
@@ -65,18 +66,16 @@ impl ProcMacroSrv {
 
         let macro_body = task.macro_body.to_subtree();
         let attributes = task.attributes.map(|it| it.to_subtree());
-        // FIXME: replace this with std's scoped threads once they stabilize
-        // (then remove dependency on crossbeam)
-        let result = crossbeam::scope(|s| {
-            let res = match s
-                .builder()
+        let result = thread::scope(|s| {
+            let thread = thread::Builder::new()
                 .stack_size(EXPANDER_STACK_SIZE)
                 .name(task.macro_name.clone())
-                .spawn(|_| {
+                .spawn_scoped(s, || {
                     expander
                         .expand(&task.macro_name, &macro_body, attributes.as_ref())
                         .map(|it| FlatTree::new(&it))
-                }) {
+                });
+            let res = match thread {
                 Ok(handle) => handle.join(),
                 Err(e) => std::panic::resume_unwind(Box::new(e)),
             };
@@ -86,10 +85,6 @@ impl ProcMacroSrv {
                 Err(e) => std::panic::resume_unwind(e),
             }
         });
-        let result = match result {
-            Ok(result) => result,
-            Err(e) => std::panic::resume_unwind(e),
-        };
 
         prev_env.rollback();
 
diff --git a/crates/project-model/src/tests.rs b/crates/project-model/src/tests.rs
index 8d0fa757c2e..9ccb6e9101e 100644
--- a/crates/project-model/src/tests.rs
+++ b/crates/project-model/src/tests.rs
@@ -185,10 +185,10 @@ fn cargo_hello_world_project_model_with_wildcard_overrides() {
                         is_proc_macro: false,
                     },
                     CrateId(
-                        2,
+                        1,
                     ): CrateData {
                         root_file_id: FileId(
-                            3,
+                            2,
                         ),
                         edition: Edition2018,
                         version: Some(
@@ -197,9 +197,9 @@ fn cargo_hello_world_project_model_with_wildcard_overrides() {
                         display_name: Some(
                             CrateDisplayName {
                                 crate_name: CrateName(
-                                    "an_example",
+                                    "hello_world",
                                 ),
-                                canonical_name: "an-example",
+                                canonical_name: "hello-world",
                             },
                         ),
                         cfg_options: CfgOptions(
@@ -260,77 +260,85 @@ fn cargo_hello_world_project_model_with_wildcard_overrides() {
                         is_proc_macro: false,
                     },
                     CrateId(
-                        4,
+                        2,
                     ): CrateData {
                         root_file_id: FileId(
-                            5,
+                            3,
                         ),
-                        edition: Edition2015,
+                        edition: Edition2018,
                         version: Some(
-                            "0.2.98",
+                            "0.1.0",
                         ),
                         display_name: Some(
                             CrateDisplayName {
                                 crate_name: CrateName(
-                                    "libc",
+                                    "an_example",
                                 ),
-                                canonical_name: "libc",
+                                canonical_name: "an-example",
                             },
                         ),
                         cfg_options: CfgOptions(
                             [
                                 "debug_assertions",
-                                "feature=default",
-                                "feature=std",
                             ],
                         ),
                         potential_cfg_options: CfgOptions(
                             [
                                 "debug_assertions",
-                                "feature=align",
-                                "feature=const-extern-fn",
-                                "feature=default",
-                                "feature=extra_traits",
-                                "feature=rustc-dep-of-std",
-                                "feature=std",
-                                "feature=use_std",
                             ],
                         ),
                         env: Env {
                             entries: {
                                 "CARGO_PKG_LICENSE": "",
                                 "CARGO_PKG_VERSION_MAJOR": "0",
-                                "CARGO_MANIFEST_DIR": "$ROOT$.cargo/registry/src/github.com-1ecc6299db9ec823/libc-0.2.98",
-                                "CARGO_PKG_VERSION": "0.2.98",
+                                "CARGO_MANIFEST_DIR": "$ROOT$hello-world",
+                                "CARGO_PKG_VERSION": "0.1.0",
                                 "CARGO_PKG_AUTHORS": "",
-                                "CARGO_CRATE_NAME": "libc",
+                                "CARGO_CRATE_NAME": "hello_world",
                                 "CARGO_PKG_LICENSE_FILE": "",
                                 "CARGO_PKG_HOMEPAGE": "",
                                 "CARGO_PKG_DESCRIPTION": "",
-                                "CARGO_PKG_NAME": "libc",
-                                "CARGO_PKG_VERSION_PATCH": "98",
+                                "CARGO_PKG_NAME": "hello-world",
+                                "CARGO_PKG_VERSION_PATCH": "0",
                                 "CARGO": "cargo",
                                 "CARGO_PKG_REPOSITORY": "",
-                                "CARGO_PKG_VERSION_MINOR": "2",
+                                "CARGO_PKG_VERSION_MINOR": "1",
                                 "CARGO_PKG_VERSION_PRE": "",
                             },
                         },
-                        dependencies: [],
+                        dependencies: [
+                            Dependency {
+                                crate_id: CrateId(
+                                    0,
+                                ),
+                                name: CrateName(
+                                    "hello_world",
+                                ),
+                                prelude: true,
+                            },
+                            Dependency {
+                                crate_id: CrateId(
+                                    4,
+                                ),
+                                name: CrateName(
+                                    "libc",
+                                ),
+                                prelude: true,
+                            },
+                        ],
                         proc_macro: Err(
                             "crate has not (yet) been built",
                         ),
                         origin: CratesIo {
-                            repo: Some(
-                                "https://github.com/rust-lang/libc",
-                            ),
+                            repo: None,
                         },
                         is_proc_macro: false,
                     },
                     CrateId(
-                        1,
+                        3,
                     ): CrateData {
                         root_file_id: FileId(
-                            2,
+                            4,
                         ),
                         edition: Edition2018,
                         version: Some(
@@ -339,9 +347,9 @@ fn cargo_hello_world_project_model_with_wildcard_overrides() {
                         display_name: Some(
                             CrateDisplayName {
                                 crate_name: CrateName(
-                                    "hello_world",
+                                    "it",
                                 ),
-                                canonical_name: "hello-world",
+                                canonical_name: "it",
                             },
                         ),
                         cfg_options: CfgOptions(
@@ -402,77 +410,69 @@ fn cargo_hello_world_project_model_with_wildcard_overrides() {
                         is_proc_macro: false,
                     },
                     CrateId(
-                        3,
+                        4,
                     ): CrateData {
                         root_file_id: FileId(
-                            4,
+                            5,
                         ),
-                        edition: Edition2018,
+                        edition: Edition2015,
                         version: Some(
-                            "0.1.0",
+                            "0.2.98",
                         ),
                         display_name: Some(
                             CrateDisplayName {
                                 crate_name: CrateName(
-                                    "it",
+                                    "libc",
                                 ),
-                                canonical_name: "it",
+                                canonical_name: "libc",
                             },
                         ),
                         cfg_options: CfgOptions(
                             [
                                 "debug_assertions",
+                                "feature=default",
+                                "feature=std",
                             ],
                         ),
                         potential_cfg_options: CfgOptions(
                             [
                                 "debug_assertions",
+                                "feature=align",
+                                "feature=const-extern-fn",
+                                "feature=default",
+                                "feature=extra_traits",
+                                "feature=rustc-dep-of-std",
+                                "feature=std",
+                                "feature=use_std",
                             ],
                         ),
                         env: Env {
                             entries: {
                                 "CARGO_PKG_LICENSE": "",
                                 "CARGO_PKG_VERSION_MAJOR": "0",
-                                "CARGO_MANIFEST_DIR": "$ROOT$hello-world",
-                                "CARGO_PKG_VERSION": "0.1.0",
+                                "CARGO_MANIFEST_DIR": "$ROOT$.cargo/registry/src/github.com-1ecc6299db9ec823/libc-0.2.98",
+                                "CARGO_PKG_VERSION": "0.2.98",
                                 "CARGO_PKG_AUTHORS": "",
-                                "CARGO_CRATE_NAME": "hello_world",
+                                "CARGO_CRATE_NAME": "libc",
                                 "CARGO_PKG_LICENSE_FILE": "",
                                 "CARGO_PKG_HOMEPAGE": "",
                                 "CARGO_PKG_DESCRIPTION": "",
-                                "CARGO_PKG_NAME": "hello-world",
-                                "CARGO_PKG_VERSION_PATCH": "0",
+                                "CARGO_PKG_NAME": "libc",
+                                "CARGO_PKG_VERSION_PATCH": "98",
                                 "CARGO": "cargo",
                                 "CARGO_PKG_REPOSITORY": "",
-                                "CARGO_PKG_VERSION_MINOR": "1",
+                                "CARGO_PKG_VERSION_MINOR": "2",
                                 "CARGO_PKG_VERSION_PRE": "",
                             },
                         },
-                        dependencies: [
-                            Dependency {
-                                crate_id: CrateId(
-                                    0,
-                                ),
-                                name: CrateName(
-                                    "hello_world",
-                                ),
-                                prelude: true,
-                            },
-                            Dependency {
-                                crate_id: CrateId(
-                                    4,
-                                ),
-                                name: CrateName(
-                                    "libc",
-                                ),
-                                prelude: true,
-                            },
-                        ],
+                        dependencies: [],
                         proc_macro: Err(
                             "crate has not (yet) been built",
                         ),
                         origin: CratesIo {
-                            repo: None,
+                            repo: Some(
+                                "https://github.com/rust-lang/libc",
+                            ),
                         },
                         is_proc_macro: false,
                     },
@@ -567,10 +567,10 @@ fn cargo_hello_world_project_model_with_selective_overrides() {
                         is_proc_macro: false,
                     },
                     CrateId(
-                        2,
+                        1,
                     ): CrateData {
                         root_file_id: FileId(
-                            3,
+                            2,
                         ),
                         edition: Edition2018,
                         version: Some(
@@ -579,9 +579,9 @@ fn cargo_hello_world_project_model_with_selective_overrides() {
                         display_name: Some(
                             CrateDisplayName {
                                 crate_name: CrateName(
-                                    "an_example",
+                                    "hello_world",
                                 ),
-                                canonical_name: "an-example",
+                                canonical_name: "hello-world",
                             },
                         ),
                         cfg_options: CfgOptions(
@@ -644,77 +644,87 @@ fn cargo_hello_world_project_model_with_selective_overrides() {
                         is_proc_macro: false,
                     },
                     CrateId(
-                        4,
+                        2,
                     ): CrateData {
                         root_file_id: FileId(
-                            5,
+                            3,
                         ),
-                        edition: Edition2015,
+                        edition: Edition2018,
                         version: Some(
-                            "0.2.98",
+                            "0.1.0",
                         ),
                         display_name: Some(
                             CrateDisplayName {
                                 crate_name: CrateName(
-                                    "libc",
+                                    "an_example",
                                 ),
-                                canonical_name: "libc",
+                                canonical_name: "an-example",
                             },
                         ),
                         cfg_options: CfgOptions(
                             [
                                 "debug_assertions",
-                                "feature=default",
-                                "feature=std",
+                                "test",
                             ],
                         ),
                         potential_cfg_options: CfgOptions(
                             [
                                 "debug_assertions",
-                                "feature=align",
-                                "feature=const-extern-fn",
-                                "feature=default",
-                                "feature=extra_traits",
-                                "feature=rustc-dep-of-std",
-                                "feature=std",
-                                "feature=use_std",
+                                "test",
                             ],
                         ),
                         env: Env {
                             entries: {
                                 "CARGO_PKG_LICENSE": "",
                                 "CARGO_PKG_VERSION_MAJOR": "0",
-                                "CARGO_MANIFEST_DIR": "$ROOT$.cargo/registry/src/github.com-1ecc6299db9ec823/libc-0.2.98",
-                                "CARGO_PKG_VERSION": "0.2.98",
+                                "CARGO_MANIFEST_DIR": "$ROOT$hello-world",
+                                "CARGO_PKG_VERSION": "0.1.0",
                                 "CARGO_PKG_AUTHORS": "",
-                                "CARGO_CRATE_NAME": "libc",
+                                "CARGO_CRATE_NAME": "hello_world",
                                 "CARGO_PKG_LICENSE_FILE": "",
                                 "CARGO_PKG_HOMEPAGE": "",
                                 "CARGO_PKG_DESCRIPTION": "",
-                                "CARGO_PKG_NAME": "libc",
-                                "CARGO_PKG_VERSION_PATCH": "98",
+                                "CARGO_PKG_NAME": "hello-world",
+                                "CARGO_PKG_VERSION_PATCH": "0",
                                 "CARGO": "cargo",
                                 "CARGO_PKG_REPOSITORY": "",
-                                "CARGO_PKG_VERSION_MINOR": "2",
+                                "CARGO_PKG_VERSION_MINOR": "1",
                                 "CARGO_PKG_VERSION_PRE": "",
                             },
                         },
-                        dependencies: [],
+                        dependencies: [
+                            Dependency {
+                                crate_id: CrateId(
+                                    0,
+                                ),
+                                name: CrateName(
+                                    "hello_world",
+                                ),
+                                prelude: true,
+                            },
+                            Dependency {
+                                crate_id: CrateId(
+                                    4,
+                                ),
+                                name: CrateName(
+                                    "libc",
+                                ),
+                                prelude: true,
+                            },
+                        ],
                         proc_macro: Err(
                             "crate has not (yet) been built",
                         ),
                         origin: CratesIo {
-                            repo: Some(
-                                "https://github.com/rust-lang/libc",
-                            ),
+                            repo: None,
                         },
                         is_proc_macro: false,
                     },
                     CrateId(
-                        1,
+                        3,
                     ): CrateData {
                         root_file_id: FileId(
-                            2,
+                            4,
                         ),
                         edition: Edition2018,
                         version: Some(
@@ -723,9 +733,9 @@ fn cargo_hello_world_project_model_with_selective_overrides() {
                         display_name: Some(
                             CrateDisplayName {
                                 crate_name: CrateName(
-                                    "hello_world",
+                                    "it",
                                 ),
-                                canonical_name: "hello-world",
+                                canonical_name: "it",
                             },
                         ),
                         cfg_options: CfgOptions(
@@ -788,79 +798,69 @@ fn cargo_hello_world_project_model_with_selective_overrides() {
                         is_proc_macro: false,
                     },
                     CrateId(
-                        3,
+                        4,
                     ): CrateData {
                         root_file_id: FileId(
-                            4,
+                            5,
                         ),
-                        edition: Edition2018,
+                        edition: Edition2015,
                         version: Some(
-                            "0.1.0",
+                            "0.2.98",
                         ),
                         display_name: Some(
                             CrateDisplayName {
                                 crate_name: CrateName(
-                                    "it",
+                                    "libc",
                                 ),
-                                canonical_name: "it",
+                                canonical_name: "libc",
                             },
                         ),
                         cfg_options: CfgOptions(
                             [
                                 "debug_assertions",
-                                "test",
+                                "feature=default",
+                                "feature=std",
                             ],
                         ),
                         potential_cfg_options: CfgOptions(
                             [
                                 "debug_assertions",
-                                "test",
+                                "feature=align",
+                                "feature=const-extern-fn",
+                                "feature=default",
+                                "feature=extra_traits",
+                                "feature=rustc-dep-of-std",
+                                "feature=std",
+                                "feature=use_std",
                             ],
                         ),
                         env: Env {
                             entries: {
                                 "CARGO_PKG_LICENSE": "",
                                 "CARGO_PKG_VERSION_MAJOR": "0",
-                                "CARGO_MANIFEST_DIR": "$ROOT$hello-world",
-                                "CARGO_PKG_VERSION": "0.1.0",
+                                "CARGO_MANIFEST_DIR": "$ROOT$.cargo/registry/src/github.com-1ecc6299db9ec823/libc-0.2.98",
+                                "CARGO_PKG_VERSION": "0.2.98",
                                 "CARGO_PKG_AUTHORS": "",
-                                "CARGO_CRATE_NAME": "hello_world",
+                                "CARGO_CRATE_NAME": "libc",
                                 "CARGO_PKG_LICENSE_FILE": "",
                                 "CARGO_PKG_HOMEPAGE": "",
                                 "CARGO_PKG_DESCRIPTION": "",
-                                "CARGO_PKG_NAME": "hello-world",
-                                "CARGO_PKG_VERSION_PATCH": "0",
+                                "CARGO_PKG_NAME": "libc",
+                                "CARGO_PKG_VERSION_PATCH": "98",
                                 "CARGO": "cargo",
                                 "CARGO_PKG_REPOSITORY": "",
-                                "CARGO_PKG_VERSION_MINOR": "1",
+                                "CARGO_PKG_VERSION_MINOR": "2",
                                 "CARGO_PKG_VERSION_PRE": "",
                             },
                         },
-                        dependencies: [
-                            Dependency {
-                                crate_id: CrateId(
-                                    0,
-                                ),
-                                name: CrateName(
-                                    "hello_world",
-                                ),
-                                prelude: true,
-                            },
-                            Dependency {
-                                crate_id: CrateId(
-                                    4,
-                                ),
-                                name: CrateName(
-                                    "libc",
-                                ),
-                                prelude: true,
-                            },
-                        ],
+                        dependencies: [],
                         proc_macro: Err(
                             "crate has not (yet) been built",
                         ),
                         origin: CratesIo {
-                            repo: None,
+                            repo: Some(
+                                "https://github.com/rust-lang/libc",
+                            ),
                         },
                         is_proc_macro: false,
                     },
@@ -946,10 +946,10 @@ fn cargo_hello_world_project_model() {
                         is_proc_macro: false,
                     },
                     CrateId(
-                        2,
+                        1,
                     ): CrateData {
                         root_file_id: FileId(
-                            3,
+                            2,
                         ),
                         edition: Edition2018,
                         version: Some(
@@ -958,9 +958,9 @@ fn cargo_hello_world_project_model() {
                         display_name: Some(
                             CrateDisplayName {
                                 crate_name: CrateName(
-                                    "an_example",
+                                    "hello_world",
                                 ),
-                                canonical_name: "an-example",
+                                canonical_name: "hello-world",
                             },
                         ),
                         cfg_options: CfgOptions(
@@ -1023,77 +1023,87 @@ fn cargo_hello_world_project_model() {
                         is_proc_macro: false,
                     },
                     CrateId(
-                        4,
+                        2,
                     ): CrateData {
                         root_file_id: FileId(
-                            5,
+                            3,
                         ),
-                        edition: Edition2015,
+                        edition: Edition2018,
                         version: Some(
-                            "0.2.98",
+                            "0.1.0",
                         ),
                         display_name: Some(
                             CrateDisplayName {
                                 crate_name: CrateName(
-                                    "libc",
+                                    "an_example",
                                 ),
-                                canonical_name: "libc",
+                                canonical_name: "an-example",
                             },
                         ),
                         cfg_options: CfgOptions(
                             [
                                 "debug_assertions",
-                                "feature=default",
-                                "feature=std",
+                                "test",
                             ],
                         ),
                         potential_cfg_options: CfgOptions(
                             [
                                 "debug_assertions",
-                                "feature=align",
-                                "feature=const-extern-fn",
-                                "feature=default",
-                                "feature=extra_traits",
-                                "feature=rustc-dep-of-std",
-                                "feature=std",
-                                "feature=use_std",
+                                "test",
                             ],
                         ),
                         env: Env {
                             entries: {
                                 "CARGO_PKG_LICENSE": "",
                                 "CARGO_PKG_VERSION_MAJOR": "0",
-                                "CARGO_MANIFEST_DIR": "$ROOT$.cargo/registry/src/github.com-1ecc6299db9ec823/libc-0.2.98",
-                                "CARGO_PKG_VERSION": "0.2.98",
+                                "CARGO_MANIFEST_DIR": "$ROOT$hello-world",
+                                "CARGO_PKG_VERSION": "0.1.0",
                                 "CARGO_PKG_AUTHORS": "",
-                                "CARGO_CRATE_NAME": "libc",
+                                "CARGO_CRATE_NAME": "hello_world",
                                 "CARGO_PKG_LICENSE_FILE": "",
                                 "CARGO_PKG_HOMEPAGE": "",
                                 "CARGO_PKG_DESCRIPTION": "",
-                                "CARGO_PKG_NAME": "libc",
-                                "CARGO_PKG_VERSION_PATCH": "98",
+                                "CARGO_PKG_NAME": "hello-world",
+                                "CARGO_PKG_VERSION_PATCH": "0",
                                 "CARGO": "cargo",
                                 "CARGO_PKG_REPOSITORY": "",
-                                "CARGO_PKG_VERSION_MINOR": "2",
+                                "CARGO_PKG_VERSION_MINOR": "1",
                                 "CARGO_PKG_VERSION_PRE": "",
                             },
                         },
-                        dependencies: [],
+                        dependencies: [
+                            Dependency {
+                                crate_id: CrateId(
+                                    0,
+                                ),
+                                name: CrateName(
+                                    "hello_world",
+                                ),
+                                prelude: true,
+                            },
+                            Dependency {
+                                crate_id: CrateId(
+                                    4,
+                                ),
+                                name: CrateName(
+                                    "libc",
+                                ),
+                                prelude: true,
+                            },
+                        ],
                         proc_macro: Err(
                             "crate has not (yet) been built",
                         ),
                         origin: CratesIo {
-                            repo: Some(
-                                "https://github.com/rust-lang/libc",
-                            ),
+                            repo: None,
                         },
                         is_proc_macro: false,
                     },
                     CrateId(
-                        1,
+                        3,
                     ): CrateData {
                         root_file_id: FileId(
-                            2,
+                            4,
                         ),
                         edition: Edition2018,
                         version: Some(
@@ -1102,9 +1112,9 @@ fn cargo_hello_world_project_model() {
                         display_name: Some(
                             CrateDisplayName {
                                 crate_name: CrateName(
-                                    "hello_world",
+                                    "it",
                                 ),
-                                canonical_name: "hello-world",
+                                canonical_name: "it",
                             },
                         ),
                         cfg_options: CfgOptions(
@@ -1167,79 +1177,69 @@ fn cargo_hello_world_project_model() {
                         is_proc_macro: false,
                     },
                     CrateId(
-                        3,
+                        4,
                     ): CrateData {
                         root_file_id: FileId(
-                            4,
+                            5,
                         ),
-                        edition: Edition2018,
+                        edition: Edition2015,
                         version: Some(
-                            "0.1.0",
+                            "0.2.98",
                         ),
                         display_name: Some(
                             CrateDisplayName {
                                 crate_name: CrateName(
-                                    "it",
+                                    "libc",
                                 ),
-                                canonical_name: "it",
+                                canonical_name: "libc",
                             },
                         ),
                         cfg_options: CfgOptions(
                             [
                                 "debug_assertions",
-                                "test",
+                                "feature=default",
+                                "feature=std",
                             ],
                         ),
                         potential_cfg_options: CfgOptions(
                             [
                                 "debug_assertions",
-                                "test",
+                                "feature=align",
+                                "feature=const-extern-fn",
+                                "feature=default",
+                                "feature=extra_traits",
+                                "feature=rustc-dep-of-std",
+                                "feature=std",
+                                "feature=use_std",
                             ],
                         ),
                         env: Env {
                             entries: {
                                 "CARGO_PKG_LICENSE": "",
                                 "CARGO_PKG_VERSION_MAJOR": "0",
-                                "CARGO_MANIFEST_DIR": "$ROOT$hello-world",
-                                "CARGO_PKG_VERSION": "0.1.0",
+                                "CARGO_MANIFEST_DIR": "$ROOT$.cargo/registry/src/github.com-1ecc6299db9ec823/libc-0.2.98",
+                                "CARGO_PKG_VERSION": "0.2.98",
                                 "CARGO_PKG_AUTHORS": "",
-                                "CARGO_CRATE_NAME": "hello_world",
+                                "CARGO_CRATE_NAME": "libc",
                                 "CARGO_PKG_LICENSE_FILE": "",
                                 "CARGO_PKG_HOMEPAGE": "",
                                 "CARGO_PKG_DESCRIPTION": "",
-                                "CARGO_PKG_NAME": "hello-world",
-                                "CARGO_PKG_VERSION_PATCH": "0",
+                                "CARGO_PKG_NAME": "libc",
+                                "CARGO_PKG_VERSION_PATCH": "98",
                                 "CARGO": "cargo",
                                 "CARGO_PKG_REPOSITORY": "",
-                                "CARGO_PKG_VERSION_MINOR": "1",
+                                "CARGO_PKG_VERSION_MINOR": "2",
                                 "CARGO_PKG_VERSION_PRE": "",
                             },
                         },
-                        dependencies: [
-                            Dependency {
-                                crate_id: CrateId(
-                                    0,
-                                ),
-                                name: CrateName(
-                                    "hello_world",
-                                ),
-                                prelude: true,
-                            },
-                            Dependency {
-                                crate_id: CrateId(
-                                    4,
-                                ),
-                                name: CrateName(
-                                    "libc",
-                                ),
-                                prelude: true,
-                            },
-                        ],
+                        dependencies: [],
                         proc_macro: Err(
                             "crate has not (yet) been built",
                         ),
                         origin: CratesIo {
-                            repo: None,
+                            repo: Some(
+                                "https://github.com/rust-lang/libc",
+                            ),
                         },
                         is_proc_macro: false,
                     },
@@ -1301,19 +1301,53 @@ fn rust_project_hello_world_project_model() {
                         is_proc_macro: false,
                     },
                     CrateId(
-                        10,
+                        1,
                     ): CrateData {
                         root_file_id: FileId(
-                            11,
+                            2,
                         ),
                         edition: Edition2018,
                         version: None,
                         display_name: Some(
                             CrateDisplayName {
                                 crate_name: CrateName(
-                                    "unwind",
+                                    "core",
                                 ),
-                                canonical_name: "unwind",
+                                canonical_name: "core",
+                            },
+                        ),
+                        cfg_options: CfgOptions(
+                            [],
+                        ),
+                        potential_cfg_options: CfgOptions(
+                            [],
+                        ),
+                        env: Env {
+                            entries: {},
+                        },
+                        dependencies: [],
+                        proc_macro: Err(
+                            "no proc macro loaded for sysroot crate",
+                        ),
+                        origin: Lang(
+                            Core,
+                        ),
+                        is_proc_macro: false,
+                    },
+                    CrateId(
+                        2,
+                    ): CrateData {
+                        root_file_id: FileId(
+                            3,
+                        ),
+                        edition: Edition2018,
+                        version: None,
+                        display_name: Some(
+                            CrateDisplayName {
+                                crate_name: CrateName(
+                                    "panic_abort",
+                                ),
+                                canonical_name: "panic_abort",
                             },
                         ),
                         cfg_options: CfgOptions(
@@ -1335,19 +1369,19 @@ fn rust_project_hello_world_project_model() {
                         is_proc_macro: false,
                     },
                     CrateId(
-                        7,
+                        3,
                     ): CrateData {
                         root_file_id: FileId(
-                            8,
+                            4,
                         ),
                         edition: Edition2018,
                         version: None,
                         display_name: Some(
                             CrateDisplayName {
                                 crate_name: CrateName(
-                                    "std_detect",
+                                    "panic_unwind",
                                 ),
-                                canonical_name: "std_detect",
+                                canonical_name: "panic_unwind",
                             },
                         ),
                         cfg_options: CfgOptions(
@@ -1413,19 +1447,19 @@ fn rust_project_hello_world_project_model() {
                         is_proc_macro: false,
                     },
                     CrateId(
-                        1,
+                        5,
                     ): CrateData {
                         root_file_id: FileId(
-                            2,
+                            6,
                         ),
                         edition: Edition2018,
                         version: None,
                         display_name: Some(
                             CrateDisplayName {
                                 crate_name: CrateName(
-                                    "core",
+                                    "profiler_builtins",
                                 ),
-                                canonical_name: "core",
+                                canonical_name: "profiler_builtins",
                             },
                         ),
                         cfg_options: CfgOptions(
@@ -1442,24 +1476,24 @@ fn rust_project_hello_world_project_model() {
                             "no proc macro loaded for sysroot crate",
                         ),
                         origin: Lang(
-                            Core,
+                            Other,
                         ),
                         is_proc_macro: false,
                     },
                     CrateId(
-                        11,
+                        6,
                     ): CrateData {
                         root_file_id: FileId(
-                            12,
+                            7,
                         ),
                         edition: Edition2018,
                         version: None,
                         display_name: Some(
                             CrateDisplayName {
                                 crate_name: CrateName(
-                                    "hello_world",
+                                    "std",
                                 ),
-                                canonical_name: "hello_world",
+                                canonical_name: "std",
                             },
                         ),
                         cfg_options: CfgOptions(
@@ -1474,6 +1508,15 @@ fn rust_project_hello_world_project_model() {
                         dependencies: [
                             Dependency {
                                 crate_id: CrateId(
+                                    0,
+                                ),
+                                name: CrateName(
+                                    "alloc",
+                                ),
+                                prelude: true,
+                            },
+                            Dependency {
+                                crate_id: CrateId(
                                     1,
                                 ),
                                 name: CrateName(
@@ -1483,19 +1526,46 @@ fn rust_project_hello_world_project_model() {
                             },
                             Dependency {
                                 crate_id: CrateId(
-                                    0,
+                                    2,
                                 ),
                                 name: CrateName(
-                                    "alloc",
+                                    "panic_abort",
                                 ),
                                 prelude: true,
                             },
                             Dependency {
                                 crate_id: CrateId(
-                                    6,
+                                    3,
                                 ),
                                 name: CrateName(
-                                    "std",
+                                    "panic_unwind",
+                                ),
+                                prelude: true,
+                            },
+                            Dependency {
+                                crate_id: CrateId(
+                                    5,
+                                ),
+                                name: CrateName(
+                                    "profiler_builtins",
+                                ),
+                                prelude: true,
+                            },
+                            Dependency {
+                                crate_id: CrateId(
+                                    7,
+                                ),
+                                name: CrateName(
+                                    "std_detect",
+                                ),
+                                prelude: true,
+                            },
+                            Dependency {
+                                crate_id: CrateId(
+                                    8,
+                                ),
+                                name: CrateName(
+                                    "term",
                                 ),
                                 prelude: true,
                             },
@@ -1506,31 +1576,40 @@ fn rust_project_hello_world_project_model() {
                                 name: CrateName(
                                     "test",
                                 ),
-                                prelude: false,
+                                prelude: true,
+                            },
+                            Dependency {
+                                crate_id: CrateId(
+                                    10,
+                                ),
+                                name: CrateName(
+                                    "unwind",
+                                ),
+                                prelude: true,
                             },
                         ],
                         proc_macro: Err(
-                            "no proc macro dylib present",
+                            "no proc macro loaded for sysroot crate",
+                        ),
+                        origin: Lang(
+                            Std,
                         ),
-                        origin: CratesIo {
-                            repo: None,
-                        },
                         is_proc_macro: false,
                     },
                     CrateId(
-                        8,
+                        7,
                     ): CrateData {
                         root_file_id: FileId(
-                            9,
+                            8,
                         ),
                         edition: Edition2018,
                         version: None,
                         display_name: Some(
                             CrateDisplayName {
                                 crate_name: CrateName(
-                                    "term",
+                                    "std_detect",
                                 ),
-                                canonical_name: "term",
+                                canonical_name: "std_detect",
                             },
                         ),
                         cfg_options: CfgOptions(
@@ -1552,19 +1631,19 @@ fn rust_project_hello_world_project_model() {
                         is_proc_macro: false,
                     },
                     CrateId(
-                        5,
+                        8,
                     ): CrateData {
                         root_file_id: FileId(
-                            6,
+                            9,
                         ),
                         edition: Edition2018,
                         version: None,
                         display_name: Some(
                             CrateDisplayName {
                                 crate_name: CrateName(
-                                    "profiler_builtins",
+                                    "term",
                                 ),
-                                canonical_name: "profiler_builtins",
+                                canonical_name: "term",
                             },
                         ),
                         cfg_options: CfgOptions(
@@ -1586,19 +1665,19 @@ fn rust_project_hello_world_project_model() {
                         is_proc_macro: false,
                     },
                     CrateId(
-                        2,
+                        9,
                     ): CrateData {
                         root_file_id: FileId(
-                            3,
+                            10,
                         ),
                         edition: Edition2018,
                         version: None,
                         display_name: Some(
                             CrateDisplayName {
                                 crate_name: CrateName(
-                                    "panic_abort",
+                                    "test",
                                 ),
-                                canonical_name: "panic_abort",
+                                canonical_name: "test",
                             },
                         ),
                         cfg_options: CfgOptions(
@@ -1615,24 +1694,24 @@ fn rust_project_hello_world_project_model() {
                             "no proc macro loaded for sysroot crate",
                         ),
                         origin: Lang(
-                            Other,
+                            Test,
                         ),
                         is_proc_macro: false,
                     },
                     CrateId(
-                        9,
+                        10,
                     ): CrateData {
                         root_file_id: FileId(
-                            10,
+                            11,
                         ),
                         edition: Edition2018,
                         version: None,
                         display_name: Some(
                             CrateDisplayName {
                                 crate_name: CrateName(
-                                    "test",
+                                    "unwind",
                                 ),
-                                canonical_name: "test",
+                                canonical_name: "unwind",
                             },
                         ),
                         cfg_options: CfgOptions(
@@ -1649,24 +1728,24 @@ fn rust_project_hello_world_project_model() {
                             "no proc macro loaded for sysroot crate",
                         ),
                         origin: Lang(
-                            Test,
+                            Other,
                         ),
                         is_proc_macro: false,
                     },
                     CrateId(
-                        6,
+                        11,
                     ): CrateData {
                         root_file_id: FileId(
-                            7,
+                            12,
                         ),
                         edition: Edition2018,
                         version: None,
                         display_name: Some(
                             CrateDisplayName {
                                 crate_name: CrateName(
-                                    "std",
+                                    "hello_world",
                                 ),
-                                canonical_name: "std",
+                                canonical_name: "hello_world",
                             },
                         ),
                         cfg_options: CfgOptions(
@@ -1681,15 +1760,6 @@ fn rust_project_hello_world_project_model() {
                         dependencies: [
                             Dependency {
                                 crate_id: CrateId(
-                                    0,
-                                ),
-                                name: CrateName(
-                                    "alloc",
-                                ),
-                                prelude: true,
-                            },
-                            Dependency {
-                                crate_id: CrateId(
                                     1,
                                 ),
                                 name: CrateName(
@@ -1699,46 +1769,19 @@ fn rust_project_hello_world_project_model() {
                             },
                             Dependency {
                                 crate_id: CrateId(
-                                    2,
-                                ),
-                                name: CrateName(
-                                    "panic_abort",
-                                ),
-                                prelude: true,
-                            },
-                            Dependency {
-                                crate_id: CrateId(
-                                    3,
-                                ),
-                                name: CrateName(
-                                    "panic_unwind",
-                                ),
-                                prelude: true,
-                            },
-                            Dependency {
-                                crate_id: CrateId(
-                                    5,
-                                ),
-                                name: CrateName(
-                                    "profiler_builtins",
-                                ),
-                                prelude: true,
-                            },
-                            Dependency {
-                                crate_id: CrateId(
-                                    7,
+                                    0,
                                 ),
                                 name: CrateName(
-                                    "std_detect",
+                                    "alloc",
                                 ),
                                 prelude: true,
                             },
                             Dependency {
                                 crate_id: CrateId(
-                                    8,
+                                    6,
                                 ),
                                 name: CrateName(
-                                    "term",
+                                    "std",
                                 ),
                                 prelude: true,
                             },
@@ -1749,58 +1792,15 @@ fn rust_project_hello_world_project_model() {
                                 name: CrateName(
                                     "test",
                                 ),
-                                prelude: true,
-                            },
-                            Dependency {
-                                crate_id: CrateId(
-                                    10,
-                                ),
-                                name: CrateName(
-                                    "unwind",
-                                ),
-                                prelude: true,
+                                prelude: false,
                             },
                         ],
                         proc_macro: Err(
-                            "no proc macro loaded for sysroot crate",
-                        ),
-                        origin: Lang(
-                            Std,
-                        ),
-                        is_proc_macro: false,
-                    },
-                    CrateId(
-                        3,
-                    ): CrateData {
-                        root_file_id: FileId(
-                            4,
-                        ),
-                        edition: Edition2018,
-                        version: None,
-                        display_name: Some(
-                            CrateDisplayName {
-                                crate_name: CrateName(
-                                    "panic_unwind",
-                                ),
-                                canonical_name: "panic_unwind",
-                            },
-                        ),
-                        cfg_options: CfgOptions(
-                            [],
-                        ),
-                        potential_cfg_options: CfgOptions(
-                            [],
+                            "no proc macro dylib present",
                         ),
-                        env: Env {
-                            entries: {},
+                        origin: CratesIo {
+                            repo: None,
                         },
-                        dependencies: [],
-                        proc_macro: Err(
-                            "no proc macro loaded for sysroot crate",
-                        ),
-                        origin: Lang(
-                            Other,
-                        ),
                         is_proc_macro: false,
                     },
                 },
diff --git a/crates/project-model/src/workspace.rs b/crates/project-model/src/workspace.rs
index 8d6f50f5587..818bbed6af2 100644
--- a/crates/project-model/src/workspace.rs
+++ b/crates/project-model/src/workspace.rs
@@ -13,7 +13,7 @@ use cfg::{CfgDiff, CfgOptions};
 use paths::{AbsPath, AbsPathBuf};
 use rustc_hash::{FxHashMap, FxHashSet};
 use semver::Version;
-use stdx::always;
+use stdx::{always, hash::NoHashHashMap};
 
 use crate::{
     build_scripts::BuildScriptOutput,
@@ -471,7 +471,7 @@ fn project_json_to_crate_graph(
         .map(|sysroot| sysroot_to_crate_graph(&mut crate_graph, sysroot, rustc_cfg.clone(), load));
 
     let mut cfg_cache: FxHashMap<&str, Vec<CfgFlag>> = FxHashMap::default();
-    let crates: FxHashMap<CrateId, CrateId> = project
+    let crates: NoHashHashMap<CrateId, CrateId> = project
         .crates()
         .filter_map(|(crate_id, krate)| {
             let file_path = &krate.root_module;
diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml
index 07771d1b392..5392589186d 100644
--- a/crates/rust-analyzer/Cargo.toml
+++ b/crates/rust-analyzer/Cargo.toml
@@ -22,7 +22,8 @@ anyhow = "1.0.57"
 crossbeam-channel = "0.5.5"
 dissimilar = "1.0.4"
 itertools = "0.10.3"
-lsp-types = { version = "0.93.0", features = ["proposed"] }
+scip = "0.1.1"
+lsp-types = { version = "0.93.1", features = ["proposed"] }
 parking_lot = "0.12.1"
 xflags = "0.2.4"
 oorandom = "11.1.3"
@@ -88,5 +89,5 @@ in-rust-tree = [
     "proc-macro-srv/sysroot-abi",
     "sourcegen/in-rust-tree",
     "ide/in-rust-tree",
-    "syntax/in-rust-tree"
+    "syntax/in-rust-tree",
 ]
diff --git a/crates/rust-analyzer/src/bin/main.rs b/crates/rust-analyzer/src/bin/main.rs
index e9de23cb395..f6a68029725 100644
--- a/crates/rust-analyzer/src/bin/main.rs
+++ b/crates/rust-analyzer/src/bin/main.rs
@@ -93,6 +93,7 @@ fn try_main() -> Result<()> {
         flags::RustAnalyzerCmd::Ssr(cmd) => cmd.run()?,
         flags::RustAnalyzerCmd::Search(cmd) => cmd.run()?,
         flags::RustAnalyzerCmd::Lsif(cmd) => cmd.run()?,
+        flags::RustAnalyzerCmd::Scip(cmd) => cmd.run()?,
     }
     Ok(())
 }
diff --git a/crates/rust-analyzer/src/cli.rs b/crates/rust-analyzer/src/cli.rs
index 6ccdaa86dd6..60ba67e25f9 100644
--- a/crates/rust-analyzer/src/cli.rs
+++ b/crates/rust-analyzer/src/cli.rs
@@ -9,6 +9,7 @@ mod analysis_stats;
 mod diagnostics;
 mod ssr;
 mod lsif;
+mod scip;
 
 mod progress_report;
 
diff --git a/crates/rust-analyzer/src/cli/flags.rs b/crates/rust-analyzer/src/cli/flags.rs
index 080e2fb4438..aa32654fbdc 100644
--- a/crates/rust-analyzer/src/cli/flags.rs
+++ b/crates/rust-analyzer/src/cli/flags.rs
@@ -112,6 +112,10 @@ xflags::xflags! {
         cmd lsif
             required path: PathBuf
         {}
+
+        cmd scip
+            required path: PathBuf
+        {}
     }
 }
 
@@ -140,6 +144,7 @@ pub enum RustAnalyzerCmd {
     Search(Search),
     ProcMacro(ProcMacro),
     Lsif(Lsif),
+    Scip(Scip),
 }
 
 #[derive(Debug)]
@@ -207,6 +212,11 @@ pub struct Lsif {
     pub path: PathBuf,
 }
 
+#[derive(Debug)]
+pub struct Scip {
+    pub path: PathBuf,
+}
+
 impl RustAnalyzer {
     pub const HELP: &'static str = Self::HELP_;
 
diff --git a/crates/rust-analyzer/src/cli/scip.rs b/crates/rust-analyzer/src/cli/scip.rs
new file mode 100644
index 00000000000..65cc993c45e
--- /dev/null
+++ b/crates/rust-analyzer/src/cli/scip.rs
@@ -0,0 +1,448 @@
+//! SCIP generator
+
+use std::{
+    collections::{HashMap, HashSet},
+    time::Instant,
+};
+
+use crate::line_index::{LineEndings, LineIndex, OffsetEncoding};
+use hir::Name;
+use ide::{
+    LineCol, MonikerDescriptorKind, MonikerResult, StaticIndex, StaticIndexedFile, TextRange,
+    TokenId,
+};
+use ide_db::LineIndexDatabase;
+use project_model::{CargoConfig, ProjectManifest, ProjectWorkspace};
+use scip::types as scip_types;
+use std::env;
+
+use crate::cli::{
+    flags,
+    load_cargo::{load_workspace, LoadCargoConfig},
+    Result,
+};
+
+impl flags::Scip {
+    pub fn run(self) -> Result<()> {
+        eprintln!("Generating SCIP start...");
+        let now = Instant::now();
+        let cargo_config = CargoConfig::default();
+
+        let no_progress = &|s| (eprintln!("rust-analyzer: Loading {}", s));
+        let load_cargo_config = LoadCargoConfig {
+            load_out_dirs_from_check: true,
+            with_proc_macro: true,
+            prefill_caches: true,
+        };
+        let path = vfs::AbsPathBuf::assert(env::current_dir()?.join(&self.path));
+        let rootpath = path.normalize();
+        let manifest = ProjectManifest::discover_single(&path)?;
+
+        let workspace = ProjectWorkspace::load(manifest, &cargo_config, no_progress)?;
+
+        let (host, vfs, _) = load_workspace(workspace, &load_cargo_config)?;
+        let db = host.raw_database();
+        let analysis = host.analysis();
+
+        let si = StaticIndex::compute(&analysis);
+
+        let mut index = scip_types::Index {
+            metadata: Some(scip_types::Metadata {
+                version: scip_types::ProtocolVersion::UnspecifiedProtocolVersion.into(),
+                tool_info: Some(scip_types::ToolInfo {
+                    name: "rust-analyzer".to_owned(),
+                    version: "0.1".to_owned(),
+                    arguments: vec![],
+                    ..Default::default()
+                })
+                .into(),
+                project_root: format!(
+                    "file://{}",
+                    path.normalize()
+                        .as_os_str()
+                        .to_str()
+                        .ok_or(anyhow::anyhow!("Unable to normalize project_root path"))?
+                        .to_string()
+                ),
+                text_document_encoding: scip_types::TextEncoding::UTF8.into(),
+                ..Default::default()
+            })
+            .into(),
+            ..Default::default()
+        };
+
+        let mut symbols_emitted: HashSet<TokenId> = HashSet::default();
+        let mut tokens_to_symbol: HashMap<TokenId, String> = HashMap::new();
+
+        for file in si.files {
+            let mut local_count = 0;
+            let mut new_local_symbol = || {
+                let new_symbol = scip::types::Symbol::new_local(local_count);
+                local_count += 1;
+
+                new_symbol
+            };
+
+            let StaticIndexedFile { file_id, tokens, .. } = file;
+            let relative_path = match get_relative_filepath(&vfs, &rootpath, file_id) {
+                Some(relative_path) => relative_path,
+                None => continue,
+            };
+
+            let line_index = LineIndex {
+                index: db.line_index(file_id),
+                encoding: OffsetEncoding::Utf8,
+                endings: LineEndings::Unix,
+            };
+
+            let mut doc = scip_types::Document {
+                relative_path,
+                language: "rust".to_string(),
+                ..Default::default()
+            };
+
+            tokens.into_iter().for_each(|(range, id)| {
+                let token = si.tokens.get(id).unwrap();
+
+                let mut occurrence = scip_types::Occurrence::default();
+                occurrence.range = text_range_to_scip_range(&line_index, range);
+                occurrence.symbol = match tokens_to_symbol.get(&id) {
+                    Some(symbol) => symbol.clone(),
+                    None => {
+                        let symbol = match &token.moniker {
+                            Some(moniker) => moniker_to_symbol(&moniker),
+                            None => new_local_symbol(),
+                        };
+
+                        let symbol = scip::symbol::format_symbol(symbol);
+                        tokens_to_symbol.insert(id, symbol.clone());
+                        symbol
+                    }
+                };
+
+                if let Some(def) = token.definition {
+                    if def.range == range {
+                        occurrence.symbol_roles |= scip_types::SymbolRole::Definition as i32;
+                    }
+
+                    if !symbols_emitted.contains(&id) {
+                        symbols_emitted.insert(id);
+
+                        let mut symbol_info = scip_types::SymbolInformation::default();
+                        symbol_info.symbol = occurrence.symbol.clone();
+                        if let Some(hover) = &token.hover {
+                            if !hover.markup.as_str().is_empty() {
+                                symbol_info.documentation = vec![hover.markup.as_str().to_string()];
+                            }
+                        }
+
+                        doc.symbols.push(symbol_info)
+                    }
+                }
+
+                doc.occurrences.push(occurrence);
+            });
+
+            if doc.occurrences.is_empty() {
+                continue;
+            }
+
+            index.documents.push(doc);
+        }
+
+        scip::write_message_to_file("index.scip", index)
+            .map_err(|err| anyhow::anyhow!("Failed to write scip to file: {}", err))?;
+
+        eprintln!("Generating SCIP finished {:?}", now.elapsed());
+        Ok(())
+    }
+}
+
+fn get_relative_filepath(
+    vfs: &vfs::Vfs,
+    rootpath: &vfs::AbsPathBuf,
+    file_id: ide::FileId,
+) -> Option<String> {
+    Some(vfs.file_path(file_id).as_path()?.strip_prefix(&rootpath)?.as_ref().to_str()?.to_string())
+}
+
+// SCIP Ranges have a (very large) optimization that ranges if they are on the same line
+// only encode as a vector of [start_line, start_col, end_col].
+//
+// This transforms a line index into the optimized SCIP Range.
+fn text_range_to_scip_range(line_index: &LineIndex, range: TextRange) -> Vec<i32> {
+    let LineCol { line: start_line, col: start_col } = line_index.index.line_col(range.start());
+    let LineCol { line: end_line, col: end_col } = line_index.index.line_col(range.end());
+
+    if start_line == end_line {
+        vec![start_line as i32, start_col as i32, end_col as i32]
+    } else {
+        vec![start_line as i32, start_col as i32, end_line as i32, end_col as i32]
+    }
+}
+
+fn new_descriptor_str(
+    name: &str,
+    suffix: scip_types::descriptor::Suffix,
+) -> scip_types::Descriptor {
+    scip_types::Descriptor {
+        name: name.to_string(),
+        disambiguator: "".to_string(),
+        suffix: suffix.into(),
+        ..Default::default()
+    }
+}
+
+fn new_descriptor(name: Name, suffix: scip_types::descriptor::Suffix) -> scip_types::Descriptor {
+    let mut name = name.to_string();
+    if name.contains("'") {
+        name = format!("`{}`", name);
+    }
+
+    new_descriptor_str(name.as_str(), suffix)
+}
+
+/// Loosely based on `def_to_moniker`
+///
+/// Only returns a Symbol when it's a non-local symbol.
+///     So if the visibility isn't outside of a document, then it will return None
+fn moniker_to_symbol(moniker: &MonikerResult) -> scip_types::Symbol {
+    use scip_types::descriptor::Suffix::*;
+
+    let package_name = moniker.package_information.name.clone();
+    let version = moniker.package_information.version.clone();
+    let descriptors = moniker
+        .identifier
+        .description
+        .iter()
+        .map(|desc| {
+            new_descriptor(
+                desc.name.clone(),
+                match desc.desc {
+                    MonikerDescriptorKind::Namespace => Namespace,
+                    MonikerDescriptorKind::Type => Type,
+                    MonikerDescriptorKind::Term => Term,
+                    MonikerDescriptorKind::Method => Method,
+                    MonikerDescriptorKind::TypeParameter => TypeParameter,
+                    MonikerDescriptorKind::Parameter => Parameter,
+                    MonikerDescriptorKind::Macro => Macro,
+                    MonikerDescriptorKind::Meta => Meta,
+                },
+            )
+        })
+        .collect();
+
+    scip_types::Symbol {
+        scheme: "rust-analyzer".into(),
+        package: Some(scip_types::Package {
+            manager: "cargo".to_string(),
+            name: package_name,
+            version,
+            ..Default::default()
+        })
+        .into(),
+        descriptors,
+        ..Default::default()
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use hir::Semantics;
+    use ide::{AnalysisHost, FilePosition};
+    use ide_db::defs::IdentClass;
+    use ide_db::{base_db::fixture::ChangeFixture, helpers::pick_best_token};
+    use scip::symbol::format_symbol;
+    use syntax::SyntaxKind::*;
+    use syntax::{AstNode, T};
+
+    fn position(ra_fixture: &str) -> (AnalysisHost, FilePosition) {
+        let mut host = AnalysisHost::default();
+        let change_fixture = ChangeFixture::parse(ra_fixture);
+        host.raw_database_mut().apply_change(change_fixture.change);
+        let (file_id, range_or_offset) =
+            change_fixture.file_position.expect("expected a marker ($0)");
+        let offset = range_or_offset.expect_offset();
+        (host, FilePosition { file_id, offset })
+    }
+
+    /// If expected == "", then assert that there are no symbols (this is basically local symbol)
+    #[track_caller]
+    fn check_symbol(ra_fixture: &str, expected: &str) {
+        let (host, position) = position(ra_fixture);
+
+        let FilePosition { file_id, offset } = position;
+
+        let db = host.raw_database();
+        let sema = &Semantics::new(db);
+        let file = sema.parse(file_id).syntax().clone();
+        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]
+            | T![Self]
+            | COMMENT => 2,
+            kind if kind.is_trivia() => 0,
+            _ => 1,
+        })
+        .expect("OK OK");
+
+        let navs = sema
+            .descend_into_macros(original_token.clone())
+            .into_iter()
+            .filter_map(|token| {
+                IdentClass::classify_token(sema, &token).map(IdentClass::definitions).map(|it| {
+                    it.into_iter().flat_map(|def| {
+                        let module = def.module(db).unwrap();
+                        let current_crate = module.krate();
+
+                        match MonikerResult::from_def(sema.db, def, current_crate) {
+                            Some(moniker_result) => Some(moniker_to_symbol(&moniker_result)),
+                            None => None,
+                        }
+                    })
+                })
+            })
+            .flatten()
+            .collect::<Vec<_>>();
+
+        if expected == "" {
+            assert_eq!(0, navs.len(), "must have no symbols {:?}", navs);
+            return;
+        }
+
+        assert_eq!(1, navs.len(), "must have one symbol {:?}", navs);
+
+        let res = navs.get(0).unwrap();
+        let formatted = format_symbol(res.clone());
+        assert_eq!(formatted, expected);
+    }
+
+    #[test]
+    fn basic() {
+        check_symbol(
+            r#"
+//- /lib.rs crate:main deps:foo
+use foo::example_mod::func;
+fn main() {
+    func$0();
+}
+//- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git
+pub mod example_mod {
+    pub fn func() {}
+}
+"#,
+            "rust-analyzer cargo foo 0.1.0 example_mod/func().",
+        );
+    }
+
+    #[test]
+    fn symbol_for_trait() {
+        check_symbol(
+            r#"
+//- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git
+pub mod module {
+    pub trait MyTrait {
+        pub fn func$0() {}
+    }
+}
+"#,
+            "rust-analyzer cargo foo 0.1.0 module/MyTrait#func().",
+        );
+    }
+
+    #[test]
+    fn symbol_for_trait_constant() {
+        check_symbol(
+            r#"
+    //- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git
+    pub mod module {
+        pub trait MyTrait {
+            const MY_CONST$0: u8;
+        }
+    }
+    "#,
+            "rust-analyzer cargo foo 0.1.0 module/MyTrait#MY_CONST.",
+        );
+    }
+
+    #[test]
+    fn symbol_for_trait_type() {
+        check_symbol(
+            r#"
+    //- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git
+    pub mod module {
+        pub trait MyTrait {
+            type MyType$0;
+        }
+    }
+    "#,
+            // "foo::module::MyTrait::MyType",
+            "rust-analyzer cargo foo 0.1.0 module/MyTrait#[MyType]",
+        );
+    }
+
+    #[test]
+    fn symbol_for_trait_impl_function() {
+        check_symbol(
+            r#"
+    //- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git
+    pub mod module {
+        pub trait MyTrait {
+            pub fn func() {}
+        }
+
+        struct MyStruct {}
+
+        impl MyTrait for MyStruct {
+            pub fn func$0() {}
+        }
+    }
+    "#,
+            // "foo::module::MyStruct::MyTrait::func",
+            "rust-analyzer cargo foo 0.1.0 module/MyStruct#MyTrait#func().",
+        );
+    }
+
+    #[test]
+    fn symbol_for_field() {
+        check_symbol(
+            r#"
+    //- /lib.rs crate:main deps:foo
+    use foo::St;
+    fn main() {
+        let x = St { a$0: 2 };
+    }
+    //- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git
+    pub struct St {
+        pub a: i32,
+    }
+    "#,
+            "rust-analyzer cargo foo 0.1.0 St#a.",
+        );
+    }
+
+    #[test]
+    fn local_symbol_for_local() {
+        check_symbol(
+            r#"
+    //- /lib.rs crate:main deps:foo
+    use foo::module::func;
+    fn main() {
+        func();
+    }
+    //- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git
+    pub mod module {
+        pub fn func() {
+            let x$0 = 2;
+        }
+    }
+    "#,
+            "",
+        );
+    }
+}
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index e63cfb163ca..54dcb42d99c 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -12,8 +12,8 @@ use std::{ffi::OsString, fmt, iter, path::PathBuf};
 use flycheck::FlycheckConfig;
 use ide::{
     AssistConfig, CallableSnippets, CompletionConfig, DiagnosticsConfig, ExprFillDefaultMode,
-    HighlightRelatedConfig, HoverConfig, HoverDocFormat, InlayHintsConfig, JoinLinesConfig,
-    Snippet, SnippetScope,
+    HighlightConfig, HighlightRelatedConfig, HoverConfig, HoverDocFormat, InlayHintsConfig,
+    JoinLinesConfig, Snippet, SnippetScope,
 };
 use ide_db::{
     imports::insert_use::{ImportGranularity, InsertUseConfig, PrefixKind},
@@ -45,7 +45,8 @@ mod patch_old_style;
 //  - foo_command = overrides the subcommand, foo_overrideCommand allows full overwriting, extra args only applies for foo_command
 
 // Defines the server-side configuration of the rust-analyzer. We generate
-// *parts* of VS Code's `package.json` config from this.
+// *parts* of VS Code's `package.json` config from this. Run `cargo test` to
+// re-generate that file.
 //
 // However, editor specific config, which the server doesn't know about, should
 // be specified directly in `package.json`.
@@ -120,6 +121,10 @@ config_data! {
         /// Cargo, you might also want to change
         /// `#rust-analyzer.cargo.buildScripts.overrideCommand#`.
         ///
+        /// If there are multiple linked projects, this command is invoked for
+        /// each of them, with the working directory being the project root
+        /// (i.e., the folder containing the `Cargo.toml`).
+        ///
         /// An example command would be:
         ///
         /// ```bash
@@ -380,6 +385,34 @@ config_data! {
         /// available on a nightly build.
         rustfmt_rangeFormatting_enable: bool = "false",
 
+        /// Inject additional highlighting into doc comments.
+        ///
+        /// When enabled, rust-analyzer will highlight rust source in doc comments as well as intra
+        /// doc links.
+        semanticHighlighting_doc_comment_inject_enable: bool = "true",
+        /// Use semantic tokens for operators.
+        ///
+        /// When disabled, rust-analyzer will emit semantic tokens only for operator tokens when
+        /// they are tagged with modifiers.
+        semanticHighlighting_operator_enable: bool = "true",
+        /// Use specialized semantic tokens for operators.
+        ///
+        /// When enabled, rust-analyzer will emit special token types for operator tokens instead
+        /// of the generic `operator` token type.
+        semanticHighlighting_operator_specialization_enable: bool = "false",
+        /// Use semantic tokens for punctuations.
+        ///
+        /// When disabled, rust-analyzer will emit semantic tokens only for punctuation tokens when
+        /// they are tagged with modifiers or have a special role.
+        semanticHighlighting_punctuation_enable: bool = "false",
+        /// When enabled, rust-analyzer will emit a punctuation semantic token for the `!` of macro
+        /// calls.
+        semanticHighlighting_punctuation_separate_macro_bang: bool = "false",
+        /// Use specialized semantic tokens for punctuations.
+        ///
+        /// When enabled, rust-analyzer will emit special token types for punctuation tokens instead
+        /// of the generic `punctuation` token type.
+        semanticHighlighting_punctuation_specialization_enable: bool = "false",
         /// Use semantic tokens for strings.
         ///
         /// In some editors (e.g. vscode) semantic tokens override other highlighting grammars.
@@ -1166,8 +1199,19 @@ impl Config {
         }
     }
 
-    pub fn highlighting_strings(&self) -> bool {
-        self.data.semanticHighlighting_strings_enable
+    pub fn highlighting_config(&self) -> HighlightConfig {
+        HighlightConfig {
+            strings: self.data.semanticHighlighting_strings_enable,
+            punctuation: self.data.semanticHighlighting_punctuation_enable,
+            specialize_punctuation: self
+                .data
+                .semanticHighlighting_punctuation_specialization_enable,
+            macro_bang: self.data.semanticHighlighting_punctuation_separate_macro_bang,
+            operator: self.data.semanticHighlighting_operator_enable,
+            specialize_operator: self.data.semanticHighlighting_operator_specialization_enable,
+            inject_doc_comment: self.data.semanticHighlighting_doc_comment_inject_enable,
+            syntactic_name_ref_highlighting: false,
+        }
     }
 
     pub fn hover(&self) -> HoverConfig {
diff --git a/crates/rust-analyzer/src/diagnostics.rs b/crates/rust-analyzer/src/diagnostics.rs
index 09150c77d7d..f516c194da4 100644
--- a/crates/rust-analyzer/src/diagnostics.rs
+++ b/crates/rust-analyzer/src/diagnostics.rs
@@ -4,11 +4,12 @@ pub(crate) mod to_proto;
 use std::{mem, sync::Arc};
 
 use ide::FileId;
-use rustc_hash::{FxHashMap, FxHashSet};
+use ide_db::FxHashMap;
+use stdx::hash::{NoHashHashMap, NoHashHashSet};
 
 use crate::lsp_ext;
 
-pub(crate) type CheckFixes = Arc<FxHashMap<usize, FxHashMap<FileId, Vec<Fix>>>>;
+pub(crate) type CheckFixes = Arc<NoHashHashMap<usize, NoHashHashMap<FileId, Vec<Fix>>>>;
 
 #[derive(Debug, Default, Clone)]
 pub struct DiagnosticsMapConfig {
@@ -19,12 +20,12 @@ pub struct DiagnosticsMapConfig {
 
 #[derive(Debug, Default, Clone)]
 pub(crate) struct DiagnosticCollection {
-    // FIXME: should be FxHashMap<FileId, Vec<ra_id::Diagnostic>>
-    pub(crate) native: FxHashMap<FileId, Vec<lsp_types::Diagnostic>>,
+    // FIXME: should be NoHashHashMap<FileId, Vec<ra_id::Diagnostic>>
+    pub(crate) native: NoHashHashMap<FileId, Vec<lsp_types::Diagnostic>>,
     // FIXME: should be Vec<flycheck::Diagnostic>
-    pub(crate) check: FxHashMap<usize, FxHashMap<FileId, Vec<lsp_types::Diagnostic>>>,
+    pub(crate) check: NoHashHashMap<usize, NoHashHashMap<FileId, Vec<lsp_types::Diagnostic>>>,
     pub(crate) check_fixes: CheckFixes,
-    changes: FxHashSet<FileId>,
+    changes: NoHashHashSet<FileId>,
 }
 
 #[derive(Debug, Clone)]
@@ -105,7 +106,7 @@ impl DiagnosticCollection {
         native.chain(check)
     }
 
-    pub(crate) fn take_changes(&mut self) -> Option<FxHashSet<FileId>> {
+    pub(crate) fn take_changes(&mut self) -> Option<NoHashHashSet<FileId>> {
         if self.changes.is_empty() {
             return None;
         }
diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs
index c55bbbbe6ef..706e1742dff 100644
--- a/crates/rust-analyzer/src/global_state.rs
+++ b/crates/rust-analyzer/src/global_state.rs
@@ -14,6 +14,7 @@ use parking_lot::{Mutex, RwLock};
 use proc_macro_api::ProcMacroServer;
 use project_model::{CargoWorkspace, ProjectWorkspace, Target, WorkspaceBuildScripts};
 use rustc_hash::FxHashMap;
+use stdx::hash::NoHashHashMap;
 use vfs::AnchoredPathBuf;
 
 use crate::{
@@ -67,7 +68,7 @@ pub(crate) struct GlobalState {
     pub(crate) flycheck_sender: Sender<flycheck::Message>,
     pub(crate) flycheck_receiver: Receiver<flycheck::Message>,
 
-    pub(crate) vfs: Arc<RwLock<(vfs::Vfs, FxHashMap<FileId, LineEndings>)>>,
+    pub(crate) vfs: Arc<RwLock<(vfs::Vfs, NoHashHashMap<FileId, LineEndings>)>>,
     pub(crate) vfs_config_version: u32,
     pub(crate) vfs_progress_config_version: u32,
     pub(crate) vfs_progress_n_total: usize,
@@ -113,7 +114,7 @@ pub(crate) struct GlobalStateSnapshot {
     pub(crate) check_fixes: CheckFixes,
     mem_docs: MemDocs,
     pub(crate) semantic_tokens_cache: Arc<Mutex<FxHashMap<Url, SemanticTokens>>>,
-    vfs: Arc<RwLock<(vfs::Vfs, FxHashMap<FileId, LineEndings>)>>,
+    vfs: Arc<RwLock<(vfs::Vfs, NoHashHashMap<FileId, LineEndings>)>>,
     pub(crate) workspaces: Arc<Vec<ProjectWorkspace>>,
 }
 
@@ -157,7 +158,7 @@ impl GlobalState {
             flycheck_sender,
             flycheck_receiver,
 
-            vfs: Arc::new(RwLock::new((vfs::Vfs::default(), FxHashMap::default()))),
+            vfs: Arc::new(RwLock::new((vfs::Vfs::default(), NoHashHashMap::default()))),
             vfs_config_version: 0,
             vfs_progress_config_version: 0,
             vfs_progress_n_total: 0,
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs
index 6337d49c240..d89f0f5a3cf 100644
--- a/crates/rust-analyzer/src/handlers.rs
+++ b/crates/rust-analyzer/src/handlers.rs
@@ -51,6 +51,12 @@ pub(crate) fn handle_workspace_reload(state: &mut GlobalState, _: ()) -> Result<
     Ok(())
 }
 
+pub(crate) fn handle_cancel_flycheck(state: &mut GlobalState, _: ()) -> Result<()> {
+    let _p = profile::span("handle_stop_flycheck");
+    state.flycheck.iter().for_each(|flycheck| flycheck.cancel());
+    Ok(())
+}
+
 pub(crate) fn handle_analyzer_status(
     snap: GlobalStateSnapshot,
     params: lsp_ext::AnalyzerStatusParams,
@@ -1498,10 +1504,8 @@ pub(crate) fn handle_semantic_tokens_full(
     let text = snap.analysis.file_text(file_id)?;
     let line_index = snap.file_line_index(file_id)?;
 
-    let highlights = snap.analysis.highlight(file_id)?;
-    let highlight_strings = snap.config.highlighting_strings();
-    let semantic_tokens =
-        to_proto::semantic_tokens(&text, &line_index, highlights, highlight_strings);
+    let highlights = snap.analysis.highlight(snap.config.highlighting_config(), file_id)?;
+    let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights);
 
     // Unconditionally cache the tokens
     snap.semantic_tokens_cache.lock().insert(params.text_document.uri, semantic_tokens.clone());
@@ -1519,10 +1523,8 @@ pub(crate) fn handle_semantic_tokens_full_delta(
     let text = snap.analysis.file_text(file_id)?;
     let line_index = snap.file_line_index(file_id)?;
 
-    let highlights = snap.analysis.highlight(file_id)?;
-    let highlight_strings = snap.config.highlighting_strings();
-    let semantic_tokens =
-        to_proto::semantic_tokens(&text, &line_index, highlights, highlight_strings);
+    let highlights = snap.analysis.highlight(snap.config.highlighting_config(), file_id)?;
+    let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights);
 
     let mut cache = snap.semantic_tokens_cache.lock();
     let cached_tokens = cache.entry(params.text_document.uri).or_default();
@@ -1550,10 +1552,8 @@ pub(crate) fn handle_semantic_tokens_range(
     let text = snap.analysis.file_text(frange.file_id)?;
     let line_index = snap.file_line_index(frange.file_id)?;
 
-    let highlights = snap.analysis.highlight_range(frange)?;
-    let highlight_strings = snap.config.highlighting_strings();
-    let semantic_tokens =
-        to_proto::semantic_tokens(&text, &line_index, highlights, highlight_strings);
+    let highlights = snap.analysis.highlight_range(snap.config.highlighting_config(), frange)?;
+    let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights);
     Ok(Some(semantic_tokens.into()))
 }
 
@@ -1771,7 +1771,7 @@ fn run_rustfmt(
 
     let line_index = snap.file_line_index(file_id)?;
 
-    let mut rustfmt = match snap.config.rustfmt() {
+    let mut command = match snap.config.rustfmt() {
         RustfmtConfig::Rustfmt { extra_args, enable_range_formatting } => {
             let mut cmd = process::Command::new(toolchain::rustfmt());
             cmd.args(extra_args);
@@ -1836,12 +1836,12 @@ fn run_rustfmt(
         }
     };
 
-    let mut rustfmt = rustfmt
+    let mut rustfmt = command
         .stdin(Stdio::piped())
         .stdout(Stdio::piped())
         .stderr(Stdio::piped())
         .spawn()
-        .context(format!("Failed to spawn {:?}", rustfmt))?;
+        .context(format!("Failed to spawn {:?}", command))?;
 
     rustfmt.stdin.as_mut().unwrap().write_all(file.as_bytes())?;
 
@@ -1860,7 +1860,11 @@ fn run_rustfmt(
                 // formatting because otherwise an error is surfaced to the user on top of the
                 // syntax error diagnostics they're already receiving. This is especially jarring
                 // if they have format on save enabled.
-                tracing::info!("rustfmt exited with status 1, assuming parse error and ignoring");
+                tracing::warn!(
+                    ?command,
+                    %captured_stderr,
+                    "rustfmt exited with status 1"
+                );
                 Ok(None)
             }
             _ => {
diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs
index 5f0e108624b..e61c8b643d2 100644
--- a/crates/rust-analyzer/src/lsp_ext.rs
+++ b/crates/rust-analyzer/src/lsp_ext.rs
@@ -129,6 +129,14 @@ pub struct ExpandedMacro {
     pub expansion: String,
 }
 
+pub enum CancelFlycheck {}
+
+impl Request for CancelFlycheck {
+    type Params = ();
+    type Result = ();
+    const METHOD: &'static str = "rust-analyzer/cancelFlycheck";
+}
+
 pub enum MatchingBrace {}
 
 impl Request for MatchingBrace {
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index 77419998249..3cfbc2e4e45 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -288,7 +288,7 @@ impl GlobalState {
 
             if became_quiescent {
                 // Project has loaded properly, kick off initial flycheck
-                self.flycheck.iter().for_each(FlycheckHandle::update);
+                self.flycheck.iter().for_each(FlycheckHandle::restart);
                 if self.config.prefill_caches() {
                     self.prime_caches_queue.request_op("became quiescent".to_string());
                 }
@@ -328,8 +328,33 @@ impl GlobalState {
                 }
 
                 let uri = file_id_to_url(&self.vfs.read().0, file_id);
-                let diagnostics =
+                let mut diagnostics =
                     self.diagnostics.diagnostics_for(file_id).cloned().collect::<Vec<_>>();
+
+                // VSCode assumes diagnostic messages to be non-empty strings, so we need to patch
+                // empty diagnostics. Neither the docs of VSCode nor the LSP spec say whether
+                // diagnostic messages are actually allowed to be empty or not and patching this
+                // in the VSCode client does not work as the assertion happens in the protocol
+                // conversion. So this hack is here to stay, and will be considered a hack
+                // until the LSP decides to state that empty messages are allowed.
+
+                // See https://github.com/rust-lang/rust-analyzer/issues/11404
+                // See https://github.com/rust-lang/rust-analyzer/issues/13130
+                let patch_empty = |message: &mut String| {
+                    if message.is_empty() {
+                        *message = " ".to_string();
+                    }
+                };
+
+                for d in &mut diagnostics {
+                    patch_empty(&mut d.message);
+                    if let Some(dri) = &mut d.related_information {
+                        for dri in dri {
+                            patch_empty(&mut dri.message);
+                        }
+                    }
+                }
+
                 let version = from_proto::vfs_path(&uri)
                     .map(|path| self.mem_docs.get(&path).map(|it| it.version))
                     .unwrap_or_default();
@@ -529,6 +554,13 @@ impl GlobalState {
                     }
                     flycheck::Progress::DidCheckCrate(target) => (Progress::Report, Some(target)),
                     flycheck::Progress::DidCancel => (Progress::End, None),
+                    flycheck::Progress::DidFailToRestart(err) => {
+                        self.show_and_log_error(
+                            "cargo check failed".to_string(),
+                            Some(err.to_string()),
+                        );
+                        return;
+                    }
                     flycheck::Progress::DidFinish(result) => {
                         if let Err(err) = result {
                             self.show_and_log_error(
@@ -590,6 +622,7 @@ impl GlobalState {
             .on_sync_mut::<lsp_ext::ReloadWorkspace>(handlers::handle_workspace_reload)
             .on_sync_mut::<lsp_ext::MemoryUsage>(handlers::handle_memory_usage)
             .on_sync_mut::<lsp_ext::ShuffleCrateGraph>(handlers::handle_shuffle_crate_graph)
+            .on_sync_mut::<lsp_ext::CancelFlycheck>(handlers::handle_cancel_flycheck)
             .on_sync::<lsp_ext::JoinLines>(handlers::handle_join_lines)
             .on_sync::<lsp_ext::OnEnter>(handlers::handle_on_enter)
             .on_sync::<lsp_types::request::SelectionRangeRequest>(handlers::handle_selection_range)
@@ -779,7 +812,7 @@ impl GlobalState {
                             for (id, _) in workspace_ids.clone() {
                                 if id == flycheck.id() {
                                     updated = true;
-                                    flycheck.update();
+                                    flycheck.restart();
                                     continue;
                                 }
                             }
@@ -798,7 +831,7 @@ impl GlobalState {
                 // No specific flycheck was triggered, so let's trigger all of them.
                 if !updated {
                     for flycheck in &this.flycheck {
-                        flycheck.update();
+                        flycheck.restart();
                     }
                 }
                 Ok(())
diff --git a/crates/rust-analyzer/src/semantic_tokens.rs b/crates/rust-analyzer/src/semantic_tokens.rs
index 6c78b5df1a7..c48410ed55e 100644
--- a/crates/rust-analyzer/src/semantic_tokens.rs
+++ b/crates/rust-analyzer/src/semantic_tokens.rs
@@ -8,107 +8,130 @@ use lsp_types::{
 };
 
 macro_rules! define_semantic_token_types {
-    ($(($ident:ident, $string:literal)),*$(,)?) => {
-        $(pub(crate) const $ident: SemanticTokenType = SemanticTokenType::new($string);)*
+    (
+        standard {
+            $($standard:ident),*$(,)?
+        }
+        custom {
+            $(($custom:ident, $string:literal)),*$(,)?
+        }
+
+    ) => {
+        $(pub(crate) const $standard: SemanticTokenType = SemanticTokenType::$standard;)*
+        $(pub(crate) const $custom: SemanticTokenType = SemanticTokenType::new($string);)*
 
         pub(crate) const SUPPORTED_TYPES: &[SemanticTokenType] = &[
-            SemanticTokenType::COMMENT,
-            SemanticTokenType::KEYWORD,
-            SemanticTokenType::STRING,
-            SemanticTokenType::NUMBER,
-            SemanticTokenType::REGEXP,
-            SemanticTokenType::OPERATOR,
-            SemanticTokenType::NAMESPACE,
-            SemanticTokenType::TYPE,
-            SemanticTokenType::STRUCT,
-            SemanticTokenType::CLASS,
-            SemanticTokenType::INTERFACE,
-            SemanticTokenType::ENUM,
-            SemanticTokenType::ENUM_MEMBER,
-            SemanticTokenType::TYPE_PARAMETER,
-            SemanticTokenType::FUNCTION,
-            SemanticTokenType::METHOD,
-            SemanticTokenType::PROPERTY,
-            SemanticTokenType::MACRO,
-            SemanticTokenType::VARIABLE,
-            SemanticTokenType::PARAMETER,
-            $($ident),*
+            $(SemanticTokenType::$standard,)*
+            $($custom),*
         ];
     };
 }
 
 define_semantic_token_types![
-    (ANGLE, "angle"),
-    (ARITHMETIC, "arithmetic"),
-    (ATTRIBUTE, "attribute"),
-    (ATTRIBUTE_BRACKET, "attributeBracket"),
-    (BITWISE, "bitwise"),
-    (BOOLEAN, "boolean"),
-    (BRACE, "brace"),
-    (BRACKET, "bracket"),
-    (BUILTIN_ATTRIBUTE, "builtinAttribute"),
-    (BUILTIN_TYPE, "builtinType"),
-    (CHAR, "character"),
-    (COLON, "colon"),
-    (COMMA, "comma"),
-    (COMPARISON, "comparison"),
-    (CONST_PARAMETER, "constParameter"),
-    (DERIVE, "derive"),
-    (DERIVE_HELPER, "deriveHelper"),
-    (DOT, "dot"),
-    (ESCAPE_SEQUENCE, "escapeSequence"),
-    (FORMAT_SPECIFIER, "formatSpecifier"),
-    (GENERIC, "generic"),
-    (LABEL, "label"),
-    (LIFETIME, "lifetime"),
-    (LOGICAL, "logical"),
-    (MACRO_BANG, "macroBang"),
-    (OPERATOR, "operator"),
-    (PARENTHESIS, "parenthesis"),
-    (PUNCTUATION, "punctuation"),
-    (SELF_KEYWORD, "selfKeyword"),
-    (SELF_TYPE_KEYWORD, "selfTypeKeyword"),
-    (SEMICOLON, "semicolon"),
-    (TYPE_ALIAS, "typeAlias"),
-    (TOOL_MODULE, "toolModule"),
-    (UNION, "union"),
-    (UNRESOLVED_REFERENCE, "unresolvedReference"),
+    standard {
+        COMMENT,
+        DECORATOR,
+        ENUM_MEMBER,
+        ENUM,
+        FUNCTION,
+        INTERFACE,
+        KEYWORD,
+        MACRO,
+        METHOD,
+        NAMESPACE,
+        NUMBER,
+        OPERATOR,
+        PARAMETER,
+        PROPERTY,
+        STRING,
+        STRUCT,
+        TYPE_PARAMETER,
+        VARIABLE,
+    }
+
+    custom {
+        (ANGLE, "angle"),
+        (ARITHMETIC, "arithmetic"),
+        (ATTRIBUTE, "attribute"),
+        (ATTRIBUTE_BRACKET, "attributeBracket"),
+        (BITWISE, "bitwise"),
+        (BOOLEAN, "boolean"),
+        (BRACE, "brace"),
+        (BRACKET, "bracket"),
+        (BUILTIN_ATTRIBUTE, "builtinAttribute"),
+        (BUILTIN_TYPE, "builtinType"),
+        (CHAR, "character"),
+        (COLON, "colon"),
+        (COMMA, "comma"),
+        (COMPARISON, "comparison"),
+        (CONST_PARAMETER, "constParameter"),
+        (DERIVE, "derive"),
+        (DERIVE_HELPER, "deriveHelper"),
+        (DOT, "dot"),
+        (ESCAPE_SEQUENCE, "escapeSequence"),
+        (FORMAT_SPECIFIER, "formatSpecifier"),
+        (GENERIC, "generic"),
+        (LABEL, "label"),
+        (LIFETIME, "lifetime"),
+        (LOGICAL, "logical"),
+        (MACRO_BANG, "macroBang"),
+        (PARENTHESIS, "parenthesis"),
+        (PUNCTUATION, "punctuation"),
+        (SELF_KEYWORD, "selfKeyword"),
+        (SELF_TYPE_KEYWORD, "selfTypeKeyword"),
+        (SEMICOLON, "semicolon"),
+        (TYPE_ALIAS, "typeAlias"),
+        (TOOL_MODULE, "toolModule"),
+        (UNION, "union"),
+        (UNRESOLVED_REFERENCE, "unresolvedReference"),
+    }
 ];
 
 macro_rules! define_semantic_token_modifiers {
-    ($(($ident:ident, $string:literal)),*$(,)?) => {
-        $(pub(crate) const $ident: SemanticTokenModifier = SemanticTokenModifier::new($string);)*
+    (
+        standard {
+            $($standard:ident),*$(,)?
+        }
+        custom {
+            $(($custom:ident, $string:literal)),*$(,)?
+        }
+
+    ) => {
+
+        $(pub(crate) const $standard: SemanticTokenModifier = SemanticTokenModifier::$standard;)*
+        $(pub(crate) const $custom: SemanticTokenModifier = SemanticTokenModifier::new($string);)*
 
         pub(crate) const SUPPORTED_MODIFIERS: &[SemanticTokenModifier] = &[
-            SemanticTokenModifier::DOCUMENTATION,
-            SemanticTokenModifier::DECLARATION,
-            SemanticTokenModifier::DEFINITION,
-            SemanticTokenModifier::STATIC,
-            SemanticTokenModifier::ABSTRACT,
-            SemanticTokenModifier::DEPRECATED,
-            SemanticTokenModifier::READONLY,
-            SemanticTokenModifier::DEFAULT_LIBRARY,
-            $($ident),*
+            $(SemanticTokenModifier::$standard,)*
+            $($custom),*
         ];
     };
 }
 
 define_semantic_token_modifiers![
-    (ASYNC, "async"),
-    (ATTRIBUTE_MODIFIER, "attribute"),
-    (CALLABLE, "callable"),
-    (CONSTANT, "constant"),
-    (CONSUMING, "consuming"),
-    (CONTROL_FLOW, "controlFlow"),
-    (CRATE_ROOT, "crateRoot"),
-    (INJECTED, "injected"),
-    (INTRA_DOC_LINK, "intraDocLink"),
-    (LIBRARY, "library"),
-    (MUTABLE, "mutable"),
-    (PUBLIC, "public"),
-    (REFERENCE, "reference"),
-    (TRAIT_MODIFIER, "trait"),
-    (UNSAFE, "unsafe"),
+    standard {
+        DOCUMENTATION,
+        DECLARATION,
+        STATIC,
+        DEFAULT_LIBRARY,
+    }
+    custom {
+        (ASYNC, "async"),
+        (ATTRIBUTE_MODIFIER, "attribute"),
+        (CALLABLE, "callable"),
+        (CONSTANT, "constant"),
+        (CONSUMING, "consuming"),
+        (CONTROL_FLOW, "controlFlow"),
+        (CRATE_ROOT, "crateRoot"),
+        (INJECTED, "injected"),
+        (INTRA_DOC_LINK, "intraDocLink"),
+        (LIBRARY, "library"),
+        (MUTABLE, "mutable"),
+        (PUBLIC, "public"),
+        (REFERENCE, "reference"),
+        (TRAIT_MODIFIER, "trait"),
+        (UNSAFE, "unsafe"),
+    }
 ];
 
 #[derive(Default)]
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index e7115b0732e..102cd602950 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -517,7 +517,6 @@ pub(crate) fn semantic_tokens(
     text: &str,
     line_index: &LineIndex,
     highlights: Vec<HlRange>,
-    highlight_strings: bool,
 ) -> lsp_types::SemanticTokens {
     let id = TOKEN_RESULT_COUNTER.fetch_add(1, Ordering::SeqCst).to_string();
     let mut builder = semantic_tokens::SemanticTokensBuilder::new(id);
@@ -526,10 +525,8 @@ pub(crate) fn semantic_tokens(
         if highlight_range.highlight.is_empty() {
             continue;
         }
+
         let (ty, mods) = semantic_token_type_and_modifiers(highlight_range.highlight);
-        if !highlight_strings && ty == lsp_types::SemanticTokenType::STRING {
-            continue;
-        }
         let token_index = semantic_tokens::type_index(ty);
         let modifier_bitset = mods.0;
 
@@ -561,55 +558,55 @@ fn semantic_token_type_and_modifiers(
     let mut mods = semantic_tokens::ModifierSet::default();
     let type_ = match highlight.tag {
         HlTag::Symbol(symbol) => match symbol {
-            SymbolKind::Attribute => semantic_tokens::ATTRIBUTE,
+            SymbolKind::Attribute => semantic_tokens::DECORATOR,
             SymbolKind::Derive => semantic_tokens::DERIVE,
             SymbolKind::DeriveHelper => semantic_tokens::DERIVE_HELPER,
-            SymbolKind::Module => lsp_types::SemanticTokenType::NAMESPACE,
+            SymbolKind::Module => semantic_tokens::NAMESPACE,
             SymbolKind::Impl => semantic_tokens::TYPE_ALIAS,
-            SymbolKind::Field => lsp_types::SemanticTokenType::PROPERTY,
-            SymbolKind::TypeParam => lsp_types::SemanticTokenType::TYPE_PARAMETER,
+            SymbolKind::Field => semantic_tokens::PROPERTY,
+            SymbolKind::TypeParam => semantic_tokens::TYPE_PARAMETER,
             SymbolKind::ConstParam => semantic_tokens::CONST_PARAMETER,
             SymbolKind::LifetimeParam => semantic_tokens::LIFETIME,
             SymbolKind::Label => semantic_tokens::LABEL,
-            SymbolKind::ValueParam => lsp_types::SemanticTokenType::PARAMETER,
+            SymbolKind::ValueParam => semantic_tokens::PARAMETER,
             SymbolKind::SelfParam => semantic_tokens::SELF_KEYWORD,
             SymbolKind::SelfType => semantic_tokens::SELF_TYPE_KEYWORD,
-            SymbolKind::Local => lsp_types::SemanticTokenType::VARIABLE,
+            SymbolKind::Local => semantic_tokens::VARIABLE,
             SymbolKind::Function => {
                 if highlight.mods.contains(HlMod::Associated) {
-                    lsp_types::SemanticTokenType::METHOD
+                    semantic_tokens::METHOD
                 } else {
-                    lsp_types::SemanticTokenType::FUNCTION
+                    semantic_tokens::FUNCTION
                 }
             }
             SymbolKind::Const => {
                 mods |= semantic_tokens::CONSTANT;
-                mods |= lsp_types::SemanticTokenModifier::STATIC;
-                lsp_types::SemanticTokenType::VARIABLE
+                mods |= semantic_tokens::STATIC;
+                semantic_tokens::VARIABLE
             }
             SymbolKind::Static => {
-                mods |= lsp_types::SemanticTokenModifier::STATIC;
-                lsp_types::SemanticTokenType::VARIABLE
+                mods |= semantic_tokens::STATIC;
+                semantic_tokens::VARIABLE
             }
-            SymbolKind::Struct => lsp_types::SemanticTokenType::STRUCT,
-            SymbolKind::Enum => lsp_types::SemanticTokenType::ENUM,
-            SymbolKind::Variant => lsp_types::SemanticTokenType::ENUM_MEMBER,
+            SymbolKind::Struct => semantic_tokens::STRUCT,
+            SymbolKind::Enum => semantic_tokens::ENUM,
+            SymbolKind::Variant => semantic_tokens::ENUM_MEMBER,
             SymbolKind::Union => semantic_tokens::UNION,
             SymbolKind::TypeAlias => semantic_tokens::TYPE_ALIAS,
-            SymbolKind::Trait => lsp_types::SemanticTokenType::INTERFACE,
-            SymbolKind::Macro => lsp_types::SemanticTokenType::MACRO,
+            SymbolKind::Trait => semantic_tokens::INTERFACE,
+            SymbolKind::Macro => semantic_tokens::MACRO,
             SymbolKind::BuiltinAttr => semantic_tokens::BUILTIN_ATTRIBUTE,
             SymbolKind::ToolModule => semantic_tokens::TOOL_MODULE,
         },
         HlTag::AttributeBracket => semantic_tokens::ATTRIBUTE_BRACKET,
         HlTag::BoolLiteral => semantic_tokens::BOOLEAN,
         HlTag::BuiltinType => semantic_tokens::BUILTIN_TYPE,
-        HlTag::ByteLiteral | HlTag::NumericLiteral => lsp_types::SemanticTokenType::NUMBER,
+        HlTag::ByteLiteral | HlTag::NumericLiteral => semantic_tokens::NUMBER,
         HlTag::CharLiteral => semantic_tokens::CHAR,
-        HlTag::Comment => lsp_types::SemanticTokenType::COMMENT,
+        HlTag::Comment => semantic_tokens::COMMENT,
         HlTag::EscapeSequence => semantic_tokens::ESCAPE_SEQUENCE,
         HlTag::FormatSpecifier => semantic_tokens::FORMAT_SPECIFIER,
-        HlTag::Keyword => lsp_types::SemanticTokenType::KEYWORD,
+        HlTag::Keyword => semantic_tokens::KEYWORD,
         HlTag::None => semantic_tokens::GENERIC,
         HlTag::Operator(op) => match op {
             HlOperator::Bitwise => semantic_tokens::BITWISE,
@@ -618,7 +615,7 @@ fn semantic_token_type_and_modifiers(
             HlOperator::Comparison => semantic_tokens::COMPARISON,
             HlOperator::Other => semantic_tokens::OPERATOR,
         },
-        HlTag::StringLiteral => lsp_types::SemanticTokenType::STRING,
+        HlTag::StringLiteral => semantic_tokens::STRING,
         HlTag::UnresolvedReference => semantic_tokens::UNRESOLVED_REFERENCE,
         HlTag::Punctuation(punct) => match punct {
             HlPunct::Bracket => semantic_tokens::BRACKET,
@@ -643,16 +640,16 @@ fn semantic_token_type_and_modifiers(
             HlMod::Consuming => semantic_tokens::CONSUMING,
             HlMod::ControlFlow => semantic_tokens::CONTROL_FLOW,
             HlMod::CrateRoot => semantic_tokens::CRATE_ROOT,
-            HlMod::DefaultLibrary => lsp_types::SemanticTokenModifier::DEFAULT_LIBRARY,
-            HlMod::Definition => lsp_types::SemanticTokenModifier::DECLARATION,
-            HlMod::Documentation => lsp_types::SemanticTokenModifier::DOCUMENTATION,
+            HlMod::DefaultLibrary => semantic_tokens::DEFAULT_LIBRARY,
+            HlMod::Definition => semantic_tokens::DECLARATION,
+            HlMod::Documentation => semantic_tokens::DOCUMENTATION,
             HlMod::Injected => semantic_tokens::INJECTED,
             HlMod::IntraDocLink => semantic_tokens::INTRA_DOC_LINK,
             HlMod::Library => semantic_tokens::LIBRARY,
             HlMod::Mutable => semantic_tokens::MUTABLE,
             HlMod::Public => semantic_tokens::PUBLIC,
             HlMod::Reference => semantic_tokens::REFERENCE,
-            HlMod::Static => lsp_types::SemanticTokenModifier::STATIC,
+            HlMod::Static => semantic_tokens::STATIC,
             HlMod::Trait => semantic_tokens::TRAIT_MODIFIER,
             HlMod::Unsafe => semantic_tokens::UNSAFE,
         };
diff --git a/crates/stdx/src/hash.rs b/crates/stdx/src/hash.rs
new file mode 100644
index 00000000000..9909d71bdf0
--- /dev/null
+++ b/crates/stdx/src/hash.rs
@@ -0,0 +1,80 @@
+//! A none hashing [`Hasher`] implementation.
+use std::{
+    hash::{BuildHasher, Hasher},
+    marker::PhantomData,
+};
+
+pub type NoHashHashMap<K, V> = std::collections::HashMap<K, V, NoHashHasherBuilder<K>>;
+pub type NoHashHashSet<K> = std::collections::HashSet<K, NoHashHasherBuilder<K>>;
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub struct NoHashHasherBuilder<T>(PhantomData<T>);
+
+impl<T> Default for NoHashHasherBuilder<T> {
+    fn default() -> Self {
+        Self(Default::default())
+    }
+}
+
+pub trait NoHashHashable {}
+impl NoHashHashable for usize {}
+impl NoHashHashable for u32 {}
+
+pub struct NoHashHasher(u64);
+
+impl<T: NoHashHashable> BuildHasher for NoHashHasherBuilder<T> {
+    type Hasher = NoHashHasher;
+    fn build_hasher(&self) -> Self::Hasher {
+        NoHashHasher(0)
+    }
+}
+
+impl Hasher for NoHashHasher {
+    fn finish(&self) -> u64 {
+        self.0
+    }
+
+    fn write(&mut self, _: &[u8]) {
+        unimplemented!("NoHashHasher should only be used for hashing primitive integers")
+    }
+
+    fn write_u8(&mut self, i: u8) {
+        self.0 = i as u64;
+    }
+
+    fn write_u16(&mut self, i: u16) {
+        self.0 = i as u64;
+    }
+
+    fn write_u32(&mut self, i: u32) {
+        self.0 = i as u64;
+    }
+
+    fn write_u64(&mut self, i: u64) {
+        self.0 = i as u64;
+    }
+
+    fn write_usize(&mut self, i: usize) {
+        self.0 = i as u64;
+    }
+
+    fn write_i8(&mut self, i: i8) {
+        self.0 = i as u64;
+    }
+
+    fn write_i16(&mut self, i: i16) {
+        self.0 = i as u64;
+    }
+
+    fn write_i32(&mut self, i: i32) {
+        self.0 = i as u64;
+    }
+
+    fn write_i64(&mut self, i: i64) {
+        self.0 = i as u64;
+    }
+
+    fn write_isize(&mut self, i: isize) {
+        self.0 = i as u64;
+    }
+}
diff --git a/crates/stdx/src/lib.rs b/crates/stdx/src/lib.rs
index b4d45206c44..51e109798d1 100644
--- a/crates/stdx/src/lib.rs
+++ b/crates/stdx/src/lib.rs
@@ -7,6 +7,7 @@ use std::{cmp::Ordering, ops, time::Instant};
 use std::{io as sio, iter};
 
 mod macros;
+pub mod hash;
 pub mod process;
 pub mod panic_context;
 pub mod non_empty_vec;
diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs
index 037de876d45..83f8bbac588 100644
--- a/crates/syntax/src/ast/make.rs
+++ b/crates/syntax/src/ast/make.rs
@@ -25,7 +25,7 @@ pub mod ext {
         return from_text(&name.text());
 
         fn from_text(text: &str) -> ast::IdentPat {
-            ast_from_text(&format!("fn f({}: ())", text))
+            ast_from_text(&format!("fn f({text}: ())"))
         }
     }
     pub fn ident_path(ident: &str) -> ast::Path {
@@ -60,10 +60,10 @@ pub mod ext {
         expr_from_text("todo!()")
     }
     pub fn expr_ty_default(ty: &ast::Type) -> ast::Expr {
-        expr_from_text(&format!("{}::default()", ty))
+        expr_from_text(&format!("{ty}::default()"))
     }
     pub fn expr_ty_new(ty: &ast::Type) -> ast::Expr {
-        expr_from_text(&format!("{}::new()", ty))
+        expr_from_text(&format!("{ty}::new()"))
     }
 
     pub fn zero_number() -> ast::Expr {
@@ -92,18 +92,20 @@ pub mod ext {
         ty_path(ident_path("bool"))
     }
     pub fn ty_option(t: ast::Type) -> ast::Type {
-        ty_from_text(&format!("Option<{}>", t))
+        ty_from_text(&format!("Option<{t}>"))
     }
     pub fn ty_result(t: ast::Type, e: ast::Type) -> ast::Type {
-        ty_from_text(&format!("Result<{}, {}>", t, e))
+        ty_from_text(&format!("Result<{t}, {e}>"))
     }
 }
 
-pub fn name(text: &str) -> ast::Name {
-    ast_from_text(&format!("mod {}{};", raw_ident_esc(text), text))
+pub fn name(name: &str) -> ast::Name {
+    let raw_escape = raw_ident_esc(name);
+    ast_from_text(&format!("mod {raw_escape}{name};"))
 }
-pub fn name_ref(text: &str) -> ast::NameRef {
-    ast_from_text(&format!("fn f() {{ {}{}; }}", raw_ident_esc(text), text))
+pub fn name_ref(name_ref: &str) -> ast::NameRef {
+    let raw_escape = raw_ident_esc(name_ref);
+    ast_from_text(&format!("fn f() {{ {raw_escape}{name_ref}; }}"))
 }
 fn raw_ident_esc(ident: &str) -> &'static str {
     let is_keyword = parser::SyntaxKind::from_keyword(ident).is_some();
@@ -118,10 +120,10 @@ pub fn lifetime(text: &str) -> ast::Lifetime {
     let mut text = text;
     let tmp;
     if never!(!text.starts_with('\'')) {
-        tmp = format!("'{}", text);
+        tmp = format!("'{text}");
         text = &tmp;
     }
-    ast_from_text(&format!("fn f<{}>() {{ }}", text))
+    ast_from_text(&format!("fn f<{text}>() {{ }}"))
 }
 
 // FIXME: replace stringly-typed constructor with a family of typed ctors, a-la
@@ -142,16 +144,16 @@ pub fn ty_tuple(types: impl IntoIterator<Item = ast::Type>) -> ast::Type {
         contents.push(',');
     }
 
-    ty_from_text(&format!("({})", contents))
+    ty_from_text(&format!("({contents})"))
 }
 pub fn ty_ref(target: ast::Type, exclusive: bool) -> ast::Type {
-    ty_from_text(&if exclusive { format!("&mut {}", target) } else { format!("&{}", target) })
+    ty_from_text(&if exclusive { format!("&mut {target}") } else { format!("&{target}") })
 }
 pub fn ty_path(path: ast::Path) -> ast::Type {
     ty_from_text(&path.to_string())
 }
 fn ty_from_text(text: &str) -> ast::Type {
-    ast_from_text(&format!("type _T = {};", text))
+    ast_from_text(&format!("type _T = {text};"))
 }
 
 pub fn assoc_item_list() -> ast::AssocItemList {
@@ -171,7 +173,7 @@ pub fn impl_(
         Some(params) => params.to_string(),
         None => String::new(),
     };
-    ast_from_text(&format!("impl{} {}{} {{}}", params, ty, ty_params))
+    ast_from_text(&format!("impl{params} {ty}{ty_params} {{}}"))
 }
 
 pub fn impl_trait(
@@ -180,7 +182,7 @@ pub fn impl_trait(
     ty_params: Option<ast::GenericParamList>,
 ) -> ast::Impl {
     let ty_params = ty_params.map_or_else(String::new, |params| params.to_string());
-    ast_from_text(&format!("impl{2} {} for {}{2} {{}}", trait_, ty, ty_params))
+    ast_from_text(&format!("impl{ty_params} {trait_} for {ty}{ty_params} {{}}"))
 }
 
 pub(crate) fn generic_arg_list() -> ast::GenericArgList {
@@ -188,13 +190,13 @@ pub(crate) fn generic_arg_list() -> ast::GenericArgList {
 }
 
 pub fn path_segment(name_ref: ast::NameRef) -> ast::PathSegment {
-    ast_from_text(&format!("type __ = {};", name_ref))
+    ast_from_text(&format!("type __ = {name_ref};"))
 }
 
 pub fn path_segment_ty(type_ref: ast::Type, trait_ref: Option<ast::PathType>) -> ast::PathSegment {
     let text = match trait_ref {
-        Some(trait_ref) => format!("fn f(x: <{} as {}>) {{}}", type_ref, trait_ref),
-        None => format!("fn f(x: <{}>) {{}}", type_ref),
+        Some(trait_ref) => format!("fn f(x: <{type_ref} as {trait_ref}>) {{}}"),
+        None => format!("fn f(x: <{type_ref}>) {{}}"),
     };
     ast_from_text(&text)
 }
@@ -212,15 +214,15 @@ pub fn path_segment_crate() -> ast::PathSegment {
 }
 
 pub fn path_unqualified(segment: ast::PathSegment) -> ast::Path {
-    ast_from_text(&format!("type __ = {};", segment))
+    ast_from_text(&format!("type __ = {segment};"))
 }
 
 pub fn path_qualified(qual: ast::Path, segment: ast::PathSegment) -> ast::Path {
-    ast_from_text(&format!("{}::{}", qual, segment))
+    ast_from_text(&format!("{qual}::{segment}"))
 }
 // FIXME: path concatenation operation doesn't make sense as AST op.
 pub fn path_concat(first: ast::Path, second: ast::Path) -> ast::Path {
-    ast_from_text(&format!("type __ = {}::{};", first, second))
+    ast_from_text(&format!("type __ = {first}::{second};"))
 }
 
 pub fn path_from_segments(
@@ -229,20 +231,20 @@ pub fn path_from_segments(
 ) -> ast::Path {
     let segments = segments.into_iter().map(|it| it.syntax().clone()).join("::");
     ast_from_text(&if is_abs {
-        format!("fn f(x: ::{}) {{}}", segments)
+        format!("fn f(x: ::{segments}) {{}}")
     } else {
-        format!("fn f(x: {}) {{}}", segments)
+        format!("fn f(x: {segments}) {{}}")
     })
 }
 
 pub fn join_paths(paths: impl IntoIterator<Item = ast::Path>) -> ast::Path {
     let paths = paths.into_iter().map(|it| it.syntax().clone()).join("::");
-    ast_from_text(&format!("type __ = {};", paths))
+    ast_from_text(&format!("type __ = {paths};"))
 }
 
 // FIXME: should not be pub
 pub fn path_from_text(text: &str) -> ast::Path {
-    ast_from_text(&format!("fn main() {{ let test = {}; }}", text))
+    ast_from_text(&format!("fn main() {{ let test = {text}; }}"))
 }
 
 pub fn use_tree_glob() -> ast::UseTree {
@@ -257,50 +259,50 @@ pub fn use_tree(
     let mut buf = "use ".to_string();
     buf += &path.syntax().to_string();
     if let Some(use_tree_list) = use_tree_list {
-        format_to!(buf, "::{}", use_tree_list);
+        format_to!(buf, "::{use_tree_list}");
     }
     if add_star {
         buf += "::*";
     }
 
     if let Some(alias) = alias {
-        format_to!(buf, " {}", alias);
+        format_to!(buf, " {alias}");
     }
     ast_from_text(&buf)
 }
 
 pub fn use_tree_list(use_trees: impl IntoIterator<Item = ast::UseTree>) -> ast::UseTreeList {
     let use_trees = use_trees.into_iter().map(|it| it.syntax().clone()).join(", ");
-    ast_from_text(&format!("use {{{}}};", use_trees))
+    ast_from_text(&format!("use {{{use_trees}}};"))
 }
 
 pub fn use_(visibility: Option<ast::Visibility>, use_tree: ast::UseTree) -> ast::Use {
     let visibility = match visibility {
         None => String::new(),
-        Some(it) => format!("{} ", it),
+        Some(it) => format!("{it} "),
     };
-    ast_from_text(&format!("{}use {};", visibility, use_tree))
+    ast_from_text(&format!("{visibility}use {use_tree};"))
 }
 
 pub fn record_expr(path: ast::Path, fields: ast::RecordExprFieldList) -> ast::RecordExpr {
-    ast_from_text(&format!("fn f() {{ {} {} }}", path, fields))
+    ast_from_text(&format!("fn f() {{ {path} {fields} }}"))
 }
 
 pub fn record_expr_field_list(
     fields: impl IntoIterator<Item = ast::RecordExprField>,
 ) -> ast::RecordExprFieldList {
     let fields = fields.into_iter().join(", ");
-    ast_from_text(&format!("fn f() {{ S {{ {} }} }}", fields))
+    ast_from_text(&format!("fn f() {{ S {{ {fields} }} }}"))
 }
 
 pub fn record_expr_field(name: ast::NameRef, expr: Option<ast::Expr>) -> ast::RecordExprField {
     return match expr {
-        Some(expr) => from_text(&format!("{}: {}", name, expr)),
+        Some(expr) => from_text(&format!("{name}: {expr}")),
         None => from_text(&name.to_string()),
     };
 
     fn from_text(text: &str) -> ast::RecordExprField {
-        ast_from_text(&format!("fn f() {{ S {{ {}, }} }}", text))
+        ast_from_text(&format!("fn f() {{ S {{ {text}, }} }}"))
     }
 }
 
@@ -311,9 +313,9 @@ pub fn record_field(
 ) -> ast::RecordField {
     let visibility = match visibility {
         None => String::new(),
-        Some(it) => format!("{} ", it),
+        Some(it) => format!("{it} "),
     };
-    ast_from_text(&format!("struct S {{ {}{}: {}, }}", visibility, name, ty))
+    ast_from_text(&format!("struct S {{ {visibility}{name}: {ty}, }}"))
 }
 
 // TODO
@@ -323,13 +325,13 @@ pub fn block_expr(
 ) -> ast::BlockExpr {
     let mut buf = "{\n".to_string();
     for stmt in stmts.into_iter() {
-        format_to!(buf, "    {}\n", stmt);
+        format_to!(buf, "    {stmt}\n");
     }
     if let Some(tail_expr) = tail_expr {
-        format_to!(buf, "    {}\n", tail_expr);
+        format_to!(buf, "    {tail_expr}\n");
     }
     buf += "}";
-    ast_from_text(&format!("fn f() {}", buf))
+    ast_from_text(&format!("fn f() {buf}"))
 }
 
 /// Ideally this function wouldn't exist since it involves manual indenting.
@@ -343,18 +345,18 @@ pub fn hacky_block_expr_with_comments(
     let mut buf = "{\n".to_string();
     for node_or_token in elements.into_iter() {
         match node_or_token {
-            rowan::NodeOrToken::Node(n) => format_to!(buf, "    {}\n", n),
+            rowan::NodeOrToken::Node(n) => format_to!(buf, "    {n}\n"),
             rowan::NodeOrToken::Token(t) if t.kind() == SyntaxKind::COMMENT => {
-                format_to!(buf, "    {}\n", t)
+                format_to!(buf, "    {t}\n")
             }
             _ => (),
         }
     }
     if let Some(tail_expr) = tail_expr {
-        format_to!(buf, "    {}\n", tail_expr);
+        format_to!(buf, "    {tail_expr}\n");
     }
     buf += "}";
-    ast_from_text(&format!("fn f() {}", buf))
+    ast_from_text(&format!("fn f() {buf}"))
 }
 
 pub fn expr_unit() -> ast::Expr {
@@ -362,7 +364,7 @@ pub fn expr_unit() -> ast::Expr {
 }
 pub fn expr_literal(text: &str) -> ast::Literal {
     assert_eq!(text.trim(), text);
-    ast_from_text(&format!("fn f() {{ let _ = {}; }}", text))
+    ast_from_text(&format!("fn f() {{ let _ = {text}; }}"))
 }
 
 pub fn expr_empty_block() -> ast::Expr {
@@ -373,41 +375,41 @@ pub fn expr_path(path: ast::Path) -> ast::Expr {
 }
 pub fn expr_continue(label: Option<ast::Lifetime>) -> ast::Expr {
     match label {
-        Some(label) => expr_from_text(&format!("continue {}", label)),
+        Some(label) => expr_from_text(&format!("continue {label}")),
         None => expr_from_text("continue"),
     }
 }
 // Consider `op: SyntaxKind` instead for nicer syntax at the call-site?
 pub fn expr_bin_op(lhs: ast::Expr, op: ast::BinaryOp, rhs: ast::Expr) -> ast::Expr {
-    expr_from_text(&format!("{} {} {}", lhs, op, rhs))
+    expr_from_text(&format!("{lhs} {op} {rhs}"))
 }
 pub fn expr_break(label: Option<ast::Lifetime>, expr: Option<ast::Expr>) -> ast::Expr {
     let mut s = String::from("break");
 
     if let Some(label) = label {
-        format_to!(s, " {}", label);
+        format_to!(s, " {label}");
     }
 
     if let Some(expr) = expr {
-        format_to!(s, " {}", expr);
+        format_to!(s, " {expr}");
     }
 
     expr_from_text(&s)
 }
 pub fn expr_return(expr: Option<ast::Expr>) -> ast::Expr {
     match expr {
-        Some(expr) => expr_from_text(&format!("return {}", expr)),
+        Some(expr) => expr_from_text(&format!("return {expr}")),
         None => expr_from_text("return"),
     }
 }
 pub fn expr_try(expr: ast::Expr) -> ast::Expr {
-    expr_from_text(&format!("{}?", expr))
+    expr_from_text(&format!("{expr}?"))
 }
 pub fn expr_await(expr: ast::Expr) -> ast::Expr {
-    expr_from_text(&format!("{}.await", expr))
+    expr_from_text(&format!("{expr}.await"))
 }
 pub fn expr_match(expr: ast::Expr, match_arm_list: ast::MatchArmList) -> ast::Expr {
-    expr_from_text(&format!("match {} {}", expr, match_arm_list))
+    expr_from_text(&format!("match {expr} {match_arm_list}"))
 }
 pub fn expr_if(
     condition: ast::Expr,
@@ -415,66 +417,67 @@ pub fn expr_if(
     else_branch: Option<ast::ElseBranch>,
 ) -> ast::Expr {
     let else_branch = match else_branch {
-        Some(ast::ElseBranch::Block(block)) => format!("else {}", block),
-        Some(ast::ElseBranch::IfExpr(if_expr)) => format!("else {}", if_expr),
+        Some(ast::ElseBranch::Block(block)) => format!("else {block}"),
+        Some(ast::ElseBranch::IfExpr(if_expr)) => format!("else {if_expr}"),
         None => String::new(),
     };
-    expr_from_text(&format!("if {} {} {}", condition, then_branch, else_branch))
+    expr_from_text(&format!("if {condition} {then_branch} {else_branch}"))
 }
 pub fn expr_for_loop(pat: ast::Pat, expr: ast::Expr, block: ast::BlockExpr) -> ast::Expr {
-    expr_from_text(&format!("for {} in {} {}", pat, expr, block))
+    expr_from_text(&format!("for {pat} in {expr} {block}"))
 }
 
 pub fn expr_loop(block: ast::BlockExpr) -> ast::Expr {
-    expr_from_text(&format!("loop {}", block))
+    expr_from_text(&format!("loop {block}"))
 }
 
 pub fn expr_prefix(op: SyntaxKind, expr: ast::Expr) -> ast::Expr {
     let token = token(op);
-    expr_from_text(&format!("{}{}", token, expr))
+    expr_from_text(&format!("{token}{expr}"))
 }
 pub fn expr_call(f: ast::Expr, arg_list: ast::ArgList) -> ast::Expr {
-    expr_from_text(&format!("{}{}", f, arg_list))
+    expr_from_text(&format!("{f}{arg_list}"))
 }
 pub fn expr_method_call(
     receiver: ast::Expr,
     method: ast::NameRef,
     arg_list: ast::ArgList,
 ) -> ast::Expr {
-    expr_from_text(&format!("{}.{}{}", receiver, method, arg_list))
+    expr_from_text(&format!("{receiver}.{method}{arg_list}"))
 }
 pub fn expr_macro_call(f: ast::Expr, arg_list: ast::ArgList) -> ast::Expr {
-    expr_from_text(&format!("{}!{}", f, arg_list))
+    expr_from_text(&format!("{f}!{arg_list}"))
 }
 pub fn expr_ref(expr: ast::Expr, exclusive: bool) -> ast::Expr {
-    expr_from_text(&if exclusive { format!("&mut {}", expr) } else { format!("&{}", expr) })
+    expr_from_text(&if exclusive { format!("&mut {expr}") } else { format!("&{expr}") })
 }
 pub fn expr_closure(pats: impl IntoIterator<Item = ast::Param>, expr: ast::Expr) -> ast::Expr {
     let params = pats.into_iter().join(", ");
-    expr_from_text(&format!("|{}| {}", params, expr))
+    expr_from_text(&format!("|{params}| {expr}"))
 }
 pub fn expr_field(receiver: ast::Expr, field: &str) -> ast::Expr {
-    expr_from_text(&format!("{}.{}", receiver, field))
+    expr_from_text(&format!("{receiver}.{field}"))
 }
 pub fn expr_paren(expr: ast::Expr) -> ast::Expr {
-    expr_from_text(&format!("({})", expr))
+    expr_from_text(&format!("({expr})"))
 }
 pub fn expr_tuple(elements: impl IntoIterator<Item = ast::Expr>) -> ast::Expr {
     let expr = elements.into_iter().format(", ");
-    expr_from_text(&format!("({})", expr))
+    expr_from_text(&format!("({expr})"))
 }
 pub fn expr_assignment(lhs: ast::Expr, rhs: ast::Expr) -> ast::Expr {
-    expr_from_text(&format!("{} = {}", lhs, rhs))
+    expr_from_text(&format!("{lhs} = {rhs}"))
 }
 fn expr_from_text(text: &str) -> ast::Expr {
-    ast_from_text(&format!("const C: () = {};", text))
+    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))
+    ast_from_text(&format!("const _: () = while let {pattern} = {expr} {{}};"))
 }
 
 pub fn arg_list(args: impl IntoIterator<Item = ast::Expr>) -> ast::ArgList {
-    ast_from_text(&format!("fn main() {{ ()({}) }}", args.into_iter().format(", ")))
+    let args = args.into_iter().format(", ");
+    ast_from_text(&format!("fn main() {{ ()({args}) }}"))
 }
 
 pub fn ident_pat(ref_: bool, mut_: bool, name: ast::Name) -> ast::IdentPat {
@@ -485,7 +488,7 @@ pub fn ident_pat(ref_: bool, mut_: bool, name: ast::Name) -> ast::IdentPat {
     if mut_ {
         s.push_str("mut ");
     }
-    format_to!(s, "{}", name);
+    format_to!(s, "{name}");
     s.push_str(": ())");
     ast_from_text(&s)
 }
@@ -494,7 +497,7 @@ pub fn wildcard_pat() -> ast::WildcardPat {
     return from_text("_");
 
     fn from_text(text: &str) -> ast::WildcardPat {
-        ast_from_text(&format!("fn f({}: ())", text))
+        ast_from_text(&format!("fn f({text}: ())"))
     }
 }
 
@@ -502,7 +505,7 @@ pub fn literal_pat(lit: &str) -> ast::LiteralPat {
     return from_text(lit);
 
     fn from_text(text: &str) -> ast::LiteralPat {
-        ast_from_text(&format!("fn f() {{ match x {{ {} => {{}} }} }}", text))
+        ast_from_text(&format!("fn f() {{ match x {{ {text} => {{}} }} }}"))
     }
 }
 
@@ -515,10 +518,10 @@ pub fn tuple_pat(pats: impl IntoIterator<Item = ast::Pat>) -> ast::TuplePat {
     if count == 1 {
         pats_str.push(',');
     }
-    return from_text(&format!("({})", pats_str));
+    return from_text(&format!("({pats_str})"));
 
     fn from_text(text: &str) -> ast::TuplePat {
-        ast_from_text(&format!("fn f({}: ())", text))
+        ast_from_text(&format!("fn f({text}: ())"))
     }
 }
 
@@ -527,46 +530,46 @@ pub fn tuple_struct_pat(
     pats: impl IntoIterator<Item = ast::Pat>,
 ) -> ast::TupleStructPat {
     let pats_str = pats.into_iter().join(", ");
-    return from_text(&format!("{}({})", path, pats_str));
+    return from_text(&format!("{path}({pats_str})"));
 
     fn from_text(text: &str) -> ast::TupleStructPat {
-        ast_from_text(&format!("fn f({}: ())", text))
+        ast_from_text(&format!("fn f({text}: ())"))
     }
 }
 
 pub fn record_pat(path: ast::Path, pats: impl IntoIterator<Item = ast::Pat>) -> ast::RecordPat {
     let pats_str = pats.into_iter().join(", ");
-    return from_text(&format!("{} {{ {} }}", path, pats_str));
+    return from_text(&format!("{path} {{ {pats_str} }}"));
 
     fn from_text(text: &str) -> ast::RecordPat {
-        ast_from_text(&format!("fn f({}: ())", text))
+        ast_from_text(&format!("fn f({text}: ())"))
     }
 }
 
 pub fn record_pat_with_fields(path: ast::Path, fields: ast::RecordPatFieldList) -> ast::RecordPat {
-    ast_from_text(&format!("fn f({} {}: ()))", path, fields))
+    ast_from_text(&format!("fn f({path} {fields}: ()))"))
 }
 
 pub fn record_pat_field_list(
     fields: impl IntoIterator<Item = ast::RecordPatField>,
 ) -> ast::RecordPatFieldList {
     let fields = fields.into_iter().join(", ");
-    ast_from_text(&format!("fn f(S {{ {} }}: ()))", fields))
+    ast_from_text(&format!("fn f(S {{ {fields} }}: ()))"))
 }
 
 pub fn record_pat_field(name_ref: ast::NameRef, pat: ast::Pat) -> ast::RecordPatField {
-    ast_from_text(&format!("fn f(S {{ {}: {} }}: ()))", name_ref, pat))
+    ast_from_text(&format!("fn f(S {{ {name_ref}: {pat} }}: ()))"))
 }
 
 pub fn record_pat_field_shorthand(name_ref: ast::NameRef) -> ast::RecordPatField {
-    ast_from_text(&format!("fn f(S {{ {} }}: ()))", name_ref))
+    ast_from_text(&format!("fn f(S {{ {name_ref} }}: ()))"))
 }
 
 /// Returns a `BindPat` if the path has just one segment, a `PathPat` otherwise.
 pub fn path_pat(path: ast::Path) -> ast::Pat {
     return from_text(&path.to_string());
     fn from_text(text: &str) -> ast::Pat {
-        ast_from_text(&format!("fn f({}: ())", text))
+        ast_from_text(&format!("fn f({text}: ())"))
     }
 }
 
@@ -577,12 +580,12 @@ pub fn match_arm(
 ) -> ast::MatchArm {
     let pats_str = pats.into_iter().join(" | ");
     return match guard {
-        Some(guard) => from_text(&format!("{} if {} => {}", pats_str, guard, expr)),
-        None => from_text(&format!("{} => {}", pats_str, expr)),
+        Some(guard) => from_text(&format!("{pats_str} if {guard} => {expr}")),
+        None => from_text(&format!("{pats_str} => {expr}")),
     };
 
     fn from_text(text: &str) -> ast::MatchArm {
-        ast_from_text(&format!("fn f() {{ match () {{{}}} }}", text))
+        ast_from_text(&format!("fn f() {{ match () {{{text}}} }}"))
     }
 }
 
@@ -592,10 +595,10 @@ pub fn match_arm_with_guard(
     expr: ast::Expr,
 ) -> ast::MatchArm {
     let pats_str = pats.into_iter().join(" | ");
-    return from_text(&format!("{} if {} => {}", pats_str, guard, expr));
+    return from_text(&format!("{pats_str} if {guard} => {expr}"));
 
     fn from_text(text: &str) -> ast::MatchArm {
-        ast_from_text(&format!("fn f() {{ match () {{{}}} }}", text))
+        ast_from_text(&format!("fn f() {{ match () {{{text}}} }}"))
     }
 }
 
@@ -605,13 +608,14 @@ pub fn match_arm_list(arms: impl IntoIterator<Item = ast::MatchArm>) -> ast::Mat
         .map(|arm| {
             let needs_comma = arm.expr().map_or(true, |it| !it.is_block_like());
             let comma = if needs_comma { "," } else { "" };
-            format!("    {}{}\n", arm.syntax(), comma)
+            let arm = arm.syntax();
+            format!("    {arm}{comma}\n")
         })
         .collect::<String>();
     return from_text(&arms_str);
 
     fn from_text(text: &str) -> ast::MatchArmList {
-        ast_from_text(&format!("fn f() {{ match () {{\n{}}} }}", text))
+        ast_from_text(&format!("fn f() {{ match () {{\n{text}}} }}"))
     }
 }
 
@@ -620,10 +624,10 @@ pub fn where_pred(
     bounds: impl IntoIterator<Item = ast::TypeBound>,
 ) -> ast::WherePred {
     let bounds = bounds.into_iter().join(" + ");
-    return from_text(&format!("{}: {}", path, bounds));
+    return from_text(&format!("{path}: {bounds}"));
 
     fn from_text(text: &str) -> ast::WherePred {
-        ast_from_text(&format!("fn f() where {} {{ }}", text))
+        ast_from_text(&format!("fn f() where {text} {{ }}"))
     }
 }
 
@@ -632,7 +636,7 @@ pub fn where_clause(preds: impl IntoIterator<Item = ast::WherePred>) -> ast::Whe
     return from_text(preds.as_str());
 
     fn from_text(text: &str) -> ast::WhereClause {
-        ast_from_text(&format!("fn f() where {} {{ }}", text))
+        ast_from_text(&format!("fn f() where {text} {{ }}"))
     }
 }
 
@@ -642,19 +646,19 @@ pub fn let_stmt(
     initializer: Option<ast::Expr>,
 ) -> ast::LetStmt {
     let mut text = String::new();
-    format_to!(text, "let {}", pattern);
+    format_to!(text, "let {pattern}");
     if let Some(ty) = ty {
-        format_to!(text, ": {}", ty);
+        format_to!(text, ": {ty}");
     }
     match initializer {
-        Some(it) => format_to!(text, " = {};", it),
+        Some(it) => format_to!(text, " = {it};"),
         None => format_to!(text, ";"),
     };
-    ast_from_text(&format!("fn f() {{ {} }}", text))
+    ast_from_text(&format!("fn f() {{ {text} }}"))
 }
 pub fn expr_stmt(expr: ast::Expr) -> ast::ExprStmt {
     let semi = if expr.is_block_like() { "" } else { ";" };
-    ast_from_text(&format!("fn f() {{ {}{} (); }}", expr, semi))
+    ast_from_text(&format!("fn f() {{ {expr}{semi} (); }}"))
 }
 
 pub fn item_const(
@@ -665,13 +669,13 @@ pub fn item_const(
 ) -> ast::Const {
     let visibility = match visibility {
         None => String::new(),
-        Some(it) => format!("{} ", it),
+        Some(it) => format!("{it} "),
     };
-    ast_from_text(&format!("{} const {}: {} = {};", visibility, name, ty, expr))
+    ast_from_text(&format!("{visibility} const {name}: {ty} = {expr};"))
 }
 
 pub fn param(pat: ast::Pat, ty: ast::Type) -> ast::Param {
-    ast_from_text(&format!("fn f({}: {}) {{ }}", pat, ty))
+    ast_from_text(&format!("fn f({pat}: {ty}) {{ }}"))
 }
 
 pub fn self_param() -> ast::SelfParam {
@@ -679,7 +683,7 @@ pub fn self_param() -> ast::SelfParam {
 }
 
 pub fn ret_type(ty: ast::Type) -> ast::RetType {
-    ast_from_text(&format!("fn f() -> {} {{ }}", ty))
+    ast_from_text(&format!("fn f() -> {ty} {{ }}"))
 }
 
 pub fn param_list(
@@ -688,30 +692,30 @@ pub fn param_list(
 ) -> ast::ParamList {
     let args = pats.into_iter().join(", ");
     let list = match self_param {
-        Some(self_param) if args.is_empty() => format!("fn f({}) {{ }}", self_param),
-        Some(self_param) => format!("fn f({}, {}) {{ }}", self_param, args),
-        None => format!("fn f({}) {{ }}", args),
+        Some(self_param) if args.is_empty() => format!("fn f({self_param}) {{ }}"),
+        Some(self_param) => format!("fn f({self_param}, {args}) {{ }}"),
+        None => format!("fn f({args}) {{ }}"),
     };
     ast_from_text(&list)
 }
 
 pub fn type_param(name: ast::Name, ty: Option<ast::TypeBoundList>) -> ast::TypeParam {
     let bound = match ty {
-        Some(it) => format!(": {}", it),
+        Some(it) => format!(": {it}"),
         None => String::new(),
     };
-    ast_from_text(&format!("fn f<{}{}>() {{ }}", name, bound))
+    ast_from_text(&format!("fn f<{name}{bound}>() {{ }}"))
 }
 
 pub fn lifetime_param(lifetime: ast::Lifetime) -> ast::LifetimeParam {
-    ast_from_text(&format!("fn f<{}>() {{ }}", lifetime))
+    ast_from_text(&format!("fn f<{lifetime}>() {{ }}"))
 }
 
 pub fn generic_param_list(
     pats: impl IntoIterator<Item = ast::GenericParam>,
 ) -> ast::GenericParamList {
     let args = pats.into_iter().join(", ");
-    ast_from_text(&format!("fn f<{}>() {{ }}", args))
+    ast_from_text(&format!("fn f<{args}>() {{ }}"))
 }
 
 pub fn visibility_pub_crate() -> ast::Visibility {
@@ -724,33 +728,33 @@ pub fn visibility_pub() -> ast::Visibility {
 
 pub fn tuple_field_list(fields: impl IntoIterator<Item = ast::TupleField>) -> ast::TupleFieldList {
     let fields = fields.into_iter().join(", ");
-    ast_from_text(&format!("struct f({});", fields))
+    ast_from_text(&format!("struct f({fields});"))
 }
 
 pub fn record_field_list(
     fields: impl IntoIterator<Item = ast::RecordField>,
 ) -> ast::RecordFieldList {
     let fields = fields.into_iter().join(", ");
-    ast_from_text(&format!("struct f {{ {} }}", fields))
+    ast_from_text(&format!("struct f {{ {fields} }}"))
 }
 
 pub fn tuple_field(visibility: Option<ast::Visibility>, ty: ast::Type) -> ast::TupleField {
     let visibility = match visibility {
         None => String::new(),
-        Some(it) => format!("{} ", it),
+        Some(it) => format!("{it} "),
     };
-    ast_from_text(&format!("struct f({}{});", visibility, ty))
+    ast_from_text(&format!("struct f({visibility}{ty});"))
 }
 
 pub fn variant(name: ast::Name, field_list: Option<ast::FieldList>) -> ast::Variant {
     let field_list = match field_list {
         None => String::new(),
         Some(it) => match it {
-            ast::FieldList::RecordFieldList(record) => format!(" {}", record),
-            ast::FieldList::TupleFieldList(tuple) => format!("{}", tuple),
+            ast::FieldList::RecordFieldList(record) => format!(" {record}"),
+            ast::FieldList::TupleFieldList(tuple) => format!("{tuple}"),
         },
     };
-    ast_from_text(&format!("enum f {{ {}{} }}", name, field_list))
+    ast_from_text(&format!("enum f {{ {name}{field_list} }}"))
 }
 
 pub fn fn_(
@@ -763,23 +767,22 @@ pub fn fn_(
     is_async: bool,
 ) -> ast::Fn {
     let type_params = match type_params {
-        Some(type_params) => format!("{}", type_params),
+        Some(type_params) => format!("{type_params}"),
         None => "".into(),
     };
     let ret_type = match ret_type {
-        Some(ret_type) => format!("{} ", ret_type),
+        Some(ret_type) => format!("{ret_type} "),
         None => "".into(),
     };
     let visibility = match visibility {
         None => String::new(),
-        Some(it) => format!("{} ", it),
+        Some(it) => format!("{it} "),
     };
 
     let async_literal = if is_async { "async " } else { "" };
 
     ast_from_text(&format!(
-        "{}{}fn {}{}{} {}{}",
-        visibility, async_literal, fn_name, type_params, params, ret_type, body
+        "{visibility}{async_literal}fn {fn_name}{type_params}{params} {ret_type}{body}",
     ))
 }
 
@@ -793,13 +796,10 @@ pub fn struct_(
     let type_params = generic_param_list.map_or_else(String::new, |it| it.to_string());
     let visibility = match visibility {
         None => String::new(),
-        Some(it) => format!("{} ", it),
+        Some(it) => format!("{it} "),
     };
 
-    ast_from_text(&format!(
-        "{}struct {}{}{}{}",
-        visibility, strukt_name, type_params, field_list, semicolon
-    ))
+    ast_from_text(&format!("{visibility}struct {strukt_name}{type_params}{field_list}{semicolon}",))
 }
 
 #[track_caller]
@@ -808,7 +808,8 @@ fn ast_from_text<N: AstNode>(text: &str) -> N {
     let node = match parse.tree().syntax().descendants().find_map(N::cast) {
         Some(it) => it,
         None => {
-            panic!("Failed to make ast node `{}` from text {}", std::any::type_name::<N>(), text)
+            let node = std::any::type_name::<N>();
+            panic!("Failed to make ast node `{node}` from text {text}")
         }
     };
     let node = node.clone_subtree();
@@ -824,7 +825,7 @@ pub fn token(kind: SyntaxKind) -> SyntaxToken {
         .descendants_with_tokens()
         .filter_map(|it| it.into_token())
         .find(|it| it.kind() == kind)
-        .unwrap_or_else(|| panic!("unhandled token: {:?}", kind))
+        .unwrap_or_else(|| panic!("unhandled token: {kind:?}"))
 }
 
 pub mod tokens {
@@ -863,7 +864,7 @@ pub mod tokens {
 
     pub fn literal(text: &str) -> SyntaxToken {
         assert_eq!(text.trim(), text);
-        let lit: ast::Literal = super::ast_from_text(&format!("fn f() {{ let _ = {}; }}", text));
+        let lit: ast::Literal = super::ast_from_text(&format!("fn f() {{ let _ = {text}; }}"));
         lit.syntax().first_child_or_token().unwrap().into_token().unwrap()
     }
 
diff --git a/crates/vfs-notify/Cargo.toml b/crates/vfs-notify/Cargo.toml
index 9ee4415dcad..fcc693a7dda 100644
--- a/crates/vfs-notify/Cargo.toml
+++ b/crates/vfs-notify/Cargo.toml
@@ -14,7 +14,7 @@ tracing = "0.1.35"
 jod-thread = "0.1.2"
 walkdir = "2.3.2"
 crossbeam-channel = "0.5.5"
-notify = "=5.0.0-pre.15"
+notify = "=5.0.0-pre.16"
 
 vfs = { path = "../vfs", version = "0.0.0" }
 paths = { path = "../paths", version = "0.0.0" }
diff --git a/crates/vfs-notify/src/lib.rs b/crates/vfs-notify/src/lib.rs
index d6d9c66159f..c95304e55ac 100644
--- a/crates/vfs-notify/src/lib.rs
+++ b/crates/vfs-notify/src/lib.rs
@@ -12,7 +12,7 @@
 use std::fs;
 
 use crossbeam_channel::{never, select, unbounded, Receiver, Sender};
-use notify::{RecommendedWatcher, RecursiveMode, Watcher};
+use notify::{Config, RecommendedWatcher, RecursiveMode, Watcher};
 use paths::{AbsPath, AbsPathBuf};
 use vfs::loader;
 use walkdir::WalkDir;
@@ -91,9 +91,12 @@ impl NotifyActor {
                         self.watcher = None;
                         if !config.watch.is_empty() {
                             let (watcher_sender, watcher_receiver) = unbounded();
-                            let watcher = log_notify_error(RecommendedWatcher::new(move |event| {
-                                watcher_sender.send(event).unwrap();
-                            }));
+                            let watcher = log_notify_error(RecommendedWatcher::new(
+                                move |event| {
+                                    watcher_sender.send(event).unwrap();
+                                },
+                                Config::default(),
+                            ));
                             self.watcher = watcher.map(|it| (it, watcher_receiver));
                         }
 
diff --git a/crates/vfs/Cargo.toml b/crates/vfs/Cargo.toml
index c6377348784..d7549a28415 100644
--- a/crates/vfs/Cargo.toml
+++ b/crates/vfs/Cargo.toml
@@ -12,6 +12,7 @@ doctest = false
 [dependencies]
 rustc-hash = "1.1.0"
 fst = "0.4.7"
+indexmap = "1.9.1"
 
 paths = { path = "../paths", version = "0.0.0" }
-indexmap = "1.9.1"
+stdx = { path = "../stdx", version = "0.0.0" }
diff --git a/crates/vfs/src/file_set.rs b/crates/vfs/src/file_set.rs
index 6a89263e539..e0ef737b3fc 100644
--- a/crates/vfs/src/file_set.rs
+++ b/crates/vfs/src/file_set.rs
@@ -6,6 +6,7 @@ use std::fmt;
 
 use fst::{IntoStreamer, Streamer};
 use rustc_hash::FxHashMap;
+use stdx::hash::NoHashHashMap;
 
 use crate::{AnchoredPath, FileId, Vfs, VfsPath};
 
@@ -13,7 +14,7 @@ use crate::{AnchoredPath, FileId, Vfs, VfsPath};
 #[derive(Default, Clone, Eq, PartialEq)]
 pub struct FileSet {
     files: FxHashMap<VfsPath, FileId>,
-    paths: FxHashMap<FileId, VfsPath>,
+    paths: NoHashHashMap<FileId, VfsPath>,
 }
 
 impl FileSet {
diff --git a/crates/vfs/src/lib.rs b/crates/vfs/src/lib.rs
index 7badb1c363b..afc9a0fa6fb 100644
--- a/crates/vfs/src/lib.rs
+++ b/crates/vfs/src/lib.rs
@@ -59,9 +59,16 @@ pub use paths::{AbsPath, AbsPathBuf};
 /// Handle to a file in [`Vfs`]
 ///
 /// Most functions in rust-analyzer use this when they need to refer to a file.
-#[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Hash)]
+#[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq)]
 pub struct FileId(pub u32);
 
+impl stdx::hash::NoHashHashable for FileId {}
+impl std::hash::Hash for FileId {
+    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
+        self.0.hash(state);
+    }
+}
+
 /// Storage for all files read by rust-analyzer.
 ///
 /// For more information see the [crate-level](crate) documentation.
diff --git a/docs/dev/lsp-extensions.md b/docs/dev/lsp-extensions.md
index 5040643d34a..6d2c7d7b063 100644
--- a/docs/dev/lsp-extensions.md
+++ b/docs/dev/lsp-extensions.md
@@ -1,5 +1,5 @@
 <!---
-lsp_ext.rs hash: 2a188defec26cc7c
+lsp_ext.rs hash: 7b710095d773b978
 
 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:
diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc
index e3dbeec1fbd..72b92572647 100644
--- a/docs/user/generated_config.adoc
+++ b/docs/user/generated_config.adoc
@@ -118,6 +118,10 @@ If you're changing this because you're using some tool wrapping
 Cargo, you might also want to change
 `#rust-analyzer.cargo.buildScripts.overrideCommand#`.
 
+If there are multiple linked projects, this command is invoked for
+each of them, with the working directory being the project root
+(i.e., the folder containing the `Cargo.toml`).
+
 An example command would be:
 
 ```bash
@@ -583,6 +587,52 @@ Enables the use of rustfmt's unstable range formatting command for the
 `textDocument/rangeFormatting` request. The rustfmt option is unstable and only
 available on a nightly build.
 --
+[[rust-analyzer.semanticHighlighting.doc.comment.inject.enable]]rust-analyzer.semanticHighlighting.doc.comment.inject.enable (default: `true`)::
++
+--
+Inject additional highlighting into doc comments.
+
+When enabled, rust-analyzer will highlight rust source in doc comments as well as intra
+doc links.
+--
+[[rust-analyzer.semanticHighlighting.operator.enable]]rust-analyzer.semanticHighlighting.operator.enable (default: `true`)::
++
+--
+Use semantic tokens for operators.
+
+When disabled, rust-analyzer will emit semantic tokens only for operator tokens when
+they are tagged with modifiers.
+--
+[[rust-analyzer.semanticHighlighting.operator.specialization.enable]]rust-analyzer.semanticHighlighting.operator.specialization.enable (default: `false`)::
++
+--
+Use specialized semantic tokens for operators.
+
+When enabled, rust-analyzer will emit special token types for operator tokens instead
+of the generic `operator` token type.
+--
+[[rust-analyzer.semanticHighlighting.punctuation.enable]]rust-analyzer.semanticHighlighting.punctuation.enable (default: `false`)::
++
+--
+Use semantic tokens for punctuations.
+
+When disabled, rust-analyzer will emit semantic tokens only for punctuation tokens when
+they are tagged with modifiers or have a special role.
+--
+[[rust-analyzer.semanticHighlighting.punctuation.separate.macro.bang]]rust-analyzer.semanticHighlighting.punctuation.separate.macro.bang (default: `false`)::
++
+--
+When enabled, rust-analyzer will emit a punctuation semantic token for the `!` of macro
+calls.
+--
+[[rust-analyzer.semanticHighlighting.punctuation.specialization.enable]]rust-analyzer.semanticHighlighting.punctuation.specialization.enable (default: `false`)::
++
+--
+Use specialized semantic tokens for punctuations.
+
+When enabled, rust-analyzer will emit special token types for punctuation tokens instead
+of the generic `punctuation` token type.
+--
 [[rust-analyzer.semanticHighlighting.strings.enable]]rust-analyzer.semanticHighlighting.strings.enable (default: `true`)::
 +
 --
diff --git a/docs/user/manual.adoc b/docs/user/manual.adoc
index c482fcbed0e..9bd3b6a692b 100644
--- a/docs/user/manual.adoc
+++ b/docs/user/manual.adoc
@@ -861,3 +861,14 @@ For example, if you want to run https://crates.io/crates/cargo-watch[`cargo watc
     "isBackground": true
 }
 ```
+
+==== Live Share
+
+VS Code Live Share has partial support for rust-analyzer.
+
+Live Share _requires_ the official Microsoft build of VS Code, OSS builds will not work correctly.
+
+The host's rust-analyzer instance will be shared with all guests joining the session.
+The guests do not have to have the rust-analyzer extension installed for this to work.
+
+If you are joining a Live Share session and _do_ have rust-analyzer installed locally, commands from the command palette will not work correctly since they will attempt to communicate with the local server.
diff --git a/editors/code/package.json b/editors/code/package.json
index 0714b356fe0..767c5875bf7 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -235,6 +235,11 @@
                 "command": "rust-analyzer.moveItemDown",
                 "title": "Move item down",
                 "category": "rust-analyzer"
+            },
+            {
+                "command": "rust-analyzer.cancelFlycheck",
+                "title": "Cancel running flychecks",
+                "category": "rust-analyzer"
             }
         ],
         "keybindings": [
@@ -542,7 +547,7 @@
                     ]
                 },
                 "rust-analyzer.checkOnSave.overrideCommand": {
-                    "markdownDescription": "Override the command rust-analyzer uses instead of `cargo check` for\ndiagnostics on save. The command is required to output json and\nshould therefor include `--message-format=json` or a similar option.\n\nIf you're changing this because you're using some tool wrapping\nCargo, you might also want to change\n`#rust-analyzer.cargo.buildScripts.overrideCommand#`.\n\nAn example command would be:\n\n```bash\ncargo check --workspace --message-format=json --all-targets\n```\n.",
+                    "markdownDescription": "Override the command rust-analyzer uses instead of `cargo check` for\ndiagnostics on save. The command is required to output json and\nshould therefor include `--message-format=json` or a similar option.\n\nIf you're changing this because you're using some tool wrapping\nCargo, you might also want to change\n`#rust-analyzer.cargo.buildScripts.overrideCommand#`.\n\nIf there are multiple linked projects, this command is invoked for\neach of them, with the working directory being the project root\n(i.e., the folder containing the `Cargo.toml`).\n\nAn example command would be:\n\n```bash\ncargo check --workspace --message-format=json --all-targets\n```\n.",
                     "default": null,
                     "type": [
                         "null",
@@ -1079,6 +1084,36 @@
                     "default": false,
                     "type": "boolean"
                 },
+                "rust-analyzer.semanticHighlighting.doc.comment.inject.enable": {
+                    "markdownDescription": "Inject additional highlighting into doc comments.\n\nWhen enabled, rust-analyzer will highlight rust source in doc comments as well as intra\ndoc links.",
+                    "default": true,
+                    "type": "boolean"
+                },
+                "rust-analyzer.semanticHighlighting.operator.enable": {
+                    "markdownDescription": "Use semantic tokens for operators.\n\nWhen disabled, rust-analyzer will emit semantic tokens only for operator tokens when\nthey are tagged with modifiers.",
+                    "default": true,
+                    "type": "boolean"
+                },
+                "rust-analyzer.semanticHighlighting.operator.specialization.enable": {
+                    "markdownDescription": "Use specialized semantic tokens for operators.\n\nWhen enabled, rust-analyzer will emit special token types for operator tokens instead\nof the generic `operator` token type.",
+                    "default": false,
+                    "type": "boolean"
+                },
+                "rust-analyzer.semanticHighlighting.punctuation.enable": {
+                    "markdownDescription": "Use semantic tokens for punctuations.\n\nWhen disabled, rust-analyzer will emit semantic tokens only for punctuation tokens when\nthey are tagged with modifiers or have a special role.",
+                    "default": false,
+                    "type": "boolean"
+                },
+                "rust-analyzer.semanticHighlighting.punctuation.separate.macro.bang": {
+                    "markdownDescription": "When enabled, rust-analyzer will emit a punctuation semantic token for the `!` of macro\ncalls.",
+                    "default": false,
+                    "type": "boolean"
+                },
+                "rust-analyzer.semanticHighlighting.punctuation.specialization.enable": {
+                    "markdownDescription": "Use specialized semantic tokens for punctuations.\n\nWhen enabled, rust-analyzer will emit special token types for punctuation tokens instead\nof the generic `punctuation` token type.",
+                    "default": false,
+                    "type": "boolean"
+                },
                 "rust-analyzer.semanticHighlighting.strings.enable": {
                     "markdownDescription": "Use semantic tokens for strings.\n\nIn some editors (e.g. vscode) semantic tokens override other highlighting grammars.\nBy disabling semantic tokens for strings, other grammars can be used to highlight\ntheir contents.",
                     "default": true,
diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts
index 27ab31db8db..05d4d08f70b 100644
--- a/editors/code/src/client.ts
+++ b/editors/code/src/client.ts
@@ -5,7 +5,6 @@ import * as Is from "vscode-languageclient/lib/common/utils/is";
 import { assert } from "./util";
 import { WorkspaceEdit } from "vscode";
 import { Workspace } from "./ctx";
-import { updateConfig } from "./config";
 import { substituteVariablesInEnv } from "./config";
 import { outputChannel, traceOutputChannel } from "./main";
 import { randomUUID } from "crypto";
@@ -86,11 +85,6 @@ export async function createClient(
 
     let initializationOptions = vscode.workspace.getConfiguration("rust-analyzer");
 
-    // Update outdated user configs
-    await updateConfig(initializationOptions).catch((err) => {
-        void vscode.window.showErrorMessage(`Failed updating old config keys: ${err.message}`);
-    });
-
     if (workspace.kind === "Detached Files") {
         initializationOptions = {
             detachedFiles: workspace.files.map((file) => file.uri.fsPath),
@@ -105,22 +99,6 @@ export async function createClient(
         traceOutputChannel: traceOutputChannel(),
         outputChannel: outputChannel(),
         middleware: {
-            async handleDiagnostics(uri, diagnostics, next) {
-                // Workaround for https://github.com/microsoft/vscode/issues/155531
-                for (const diagnostic of diagnostics) {
-                    if (!diagnostic.message) {
-                        diagnostic.message = " ";
-                    }
-                    if (diagnostic.relatedInformation) {
-                        for (const relatedInformation of diagnostic.relatedInformation) {
-                            if (!relatedInformation.message) {
-                                relatedInformation.message = " ";
-                            }
-                        }
-                    }
-                }
-                next(uri, diagnostics);
-            },
             async provideHover(
                 document: vscode.TextDocument,
                 position: vscode.Position,
diff --git a/editors/code/src/commands.ts b/editors/code/src/commands.ts
index 12f666401fd..a21b304bbda 100644
--- a/editors/code/src/commands.ts
+++ b/editors/code/src/commands.ts
@@ -817,6 +817,12 @@ export function openDocs(ctx: Ctx): Cmd {
     };
 }
 
+export function cancelFlycheck(ctx: Ctx): Cmd {
+    return async () => {
+        await ctx.client.sendRequest(ra.cancelFlycheck);
+    };
+}
+
 export function resolveCodeAction(ctx: Ctx): Cmd {
     const client = ctx.client;
     return async (params: lc.CodeAction) => {
diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts
index 1c58040d58c..a9c0f079b3d 100644
--- a/editors/code/src/config.ts
+++ b/editors/code/src/config.ts
@@ -3,10 +3,6 @@ import * as vscode from "vscode";
 import { Env } from "./client";
 import { log } from "./util";
 
-export type UpdatesChannel = "stable" | "nightly";
-
-const NIGHTLY_TAG = "nightly";
-
 export type RunnableEnvCfg =
     | undefined
     | Record<string, string>
@@ -175,104 +171,6 @@ export class Config {
             gotoTypeDef: this.get<boolean>("hover.actions.gotoTypeDef.enable"),
         };
     }
-
-    get currentExtensionIsNightly() {
-        return this.package.releaseTag === NIGHTLY_TAG;
-    }
-}
-
-export async function updateConfig(config: vscode.WorkspaceConfiguration) {
-    const renames = [
-        ["assist.allowMergingIntoGlobImports", "imports.merge.glob"],
-        ["assist.exprFillDefault", "assist.expressionFillDefault"],
-        ["assist.importEnforceGranularity", "imports.granularity.enforce"],
-        ["assist.importGranularity", "imports.granularity.group"],
-        ["assist.importMergeBehavior", "imports.granularity.group"],
-        ["assist.importMergeBehaviour", "imports.granularity.group"],
-        ["assist.importGroup", "imports.group.enable"],
-        ["assist.importPrefix", "imports.prefix"],
-        ["primeCaches.enable", "cachePriming.enable"],
-        ["cache.warmup", "cachePriming.enable"],
-        ["cargo.loadOutDirsFromCheck", "cargo.buildScripts.enable"],
-        ["cargo.runBuildScripts", "cargo.buildScripts.enable"],
-        ["cargo.runBuildScriptsCommand", "cargo.buildScripts.overrideCommand"],
-        ["cargo.useRustcWrapperForBuildScripts", "cargo.buildScripts.useRustcWrapper"],
-        ["completion.snippets", "completion.snippets.custom"],
-        ["diagnostics.enableExperimental", "diagnostics.experimental.enable"],
-        ["experimental.procAttrMacros", "procMacro.attributes.enable"],
-        ["highlighting.strings", "semanticHighlighting.strings.enable"],
-        ["highlightRelated.breakPoints", "highlightRelated.breakPoints.enable"],
-        ["highlightRelated.exitPoints", "highlightRelated.exitPoints.enable"],
-        ["highlightRelated.yieldPoints", "highlightRelated.yieldPoints.enable"],
-        ["highlightRelated.references", "highlightRelated.references.enable"],
-        ["hover.documentation", "hover.documentation.enable"],
-        ["hover.linksInHover", "hover.links.enable"],
-        ["hoverActions.linksInHover", "hover.links.enable"],
-        ["hoverActions.debug", "hover.actions.debug.enable"],
-        ["hoverActions.enable", "hover.actions.enable.enable"],
-        ["hoverActions.gotoTypeDef", "hover.actions.gotoTypeDef.enable"],
-        ["hoverActions.implementations", "hover.actions.implementations.enable"],
-        ["hoverActions.references", "hover.actions.references.enable"],
-        ["hoverActions.run", "hover.actions.run.enable"],
-        ["inlayHints.chainingHints", "inlayHints.chainingHints.enable"],
-        ["inlayHints.closureReturnTypeHints", "inlayHints.closureReturnTypeHints.enable"],
-        ["inlayHints.hideNamedConstructorHints", "inlayHints.typeHints.hideNamedConstructor"],
-        ["inlayHints.parameterHints", "inlayHints.parameterHints.enable"],
-        ["inlayHints.reborrowHints", "inlayHints.reborrowHints.enable"],
-        ["inlayHints.typeHints", "inlayHints.typeHints.enable"],
-        ["lruCapacity", "lru.capacity"],
-        ["runnables.cargoExtraArgs", "runnables.extraArgs"],
-        ["runnables.overrideCargo", "runnables.command"],
-        ["rustcSource", "rustc.source"],
-        ["rustfmt.enableRangeFormatting", "rustfmt.rangeFormatting.enable"],
-    ];
-
-    for (const [oldKey, newKey] of renames) {
-        const inspect = config.inspect(oldKey);
-        if (inspect !== undefined) {
-            const valMatrix = [
-                {
-                    val: inspect.globalValue,
-                    langVal: inspect.globalLanguageValue,
-                    target: vscode.ConfigurationTarget.Global,
-                },
-                {
-                    val: inspect.workspaceFolderValue,
-                    langVal: inspect.workspaceFolderLanguageValue,
-                    target: vscode.ConfigurationTarget.WorkspaceFolder,
-                },
-                {
-                    val: inspect.workspaceValue,
-                    langVal: inspect.workspaceLanguageValue,
-                    target: vscode.ConfigurationTarget.Workspace,
-                },
-            ];
-            for (const { val, langVal, target } of valMatrix) {
-                const patch = (val: unknown) => {
-                    // some of the updates we do only append "enable" or "custom"
-                    // that means on the next run we would find these again, but as objects with
-                    // these properties causing us to destroy the config
-                    // so filter those already updated ones out
-                    return (
-                        val !== undefined &&
-                        !(
-                            typeof val === "object" &&
-                            val !== null &&
-                            (oldKey === "completion.snippets" || !val.hasOwnProperty("custom"))
-                        )
-                    );
-                };
-                if (patch(val)) {
-                    await config.update(newKey, val, target, false);
-                    await config.update(oldKey, undefined, target, false);
-                }
-                if (patch(langVal)) {
-                    await config.update(newKey, langVal, target, true);
-                    await config.update(oldKey, undefined, target, true);
-                }
-            }
-        }
-    }
 }
 
 export function substituteVariablesInEnv(env: Env): Env {
diff --git a/editors/code/src/lsp_ext.ts b/editors/code/src/lsp_ext.ts
index f80af78a74a..875261c48a6 100644
--- a/editors/code/src/lsp_ext.ts
+++ b/editors/code/src/lsp_ext.ts
@@ -75,6 +75,23 @@ export const expandMacro = new lc.RequestType<ExpandMacroParams, ExpandedMacro |
     "rust-analyzer/expandMacro"
 );
 
+export const relatedTests = new lc.RequestType<lc.TextDocumentPositionParams, TestInfo[], void>(
+    "rust-analyzer/relatedTests"
+);
+
+export const cancelFlycheck = new lc.RequestType0<void, void>("rust-analyzer/cancelFlycheck");
+
+// Experimental extensions
+
+export interface SsrParams {
+    query: string;
+    parseOnly: boolean;
+    textDocument: lc.TextDocumentIdentifier;
+    position: lc.Position;
+    selections: readonly lc.Range[];
+}
+export const ssr = new lc.RequestType<SsrParams, lc.WorkspaceEdit, void>("experimental/ssr");
+
 export interface MatchingBraceParams {
     textDocument: lc.TextDocumentIdentifier;
     positions: lc.Position[];
@@ -127,19 +144,6 @@ export interface TestInfo {
     runnable: Runnable;
 }
 
-export const relatedTests = new lc.RequestType<lc.TextDocumentPositionParams, TestInfo[], void>(
-    "rust-analyzer/relatedTests"
-);
-
-export interface SsrParams {
-    query: string;
-    parseOnly: boolean;
-    textDocument: lc.TextDocumentIdentifier;
-    position: lc.Position;
-    selections: readonly lc.Range[];
-}
-export const ssr = new lc.RequestType<SsrParams, lc.WorkspaceEdit, void>("experimental/ssr");
-
 export interface CommandLink extends lc.Command {
     /**
      * A tooltip for the command, when represented in the UI.
diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts
index d78b711a47a..e9b62e0cc25 100644
--- a/editors/code/src/main.ts
+++ b/editors/code/src/main.ts
@@ -33,7 +33,7 @@ export function outputChannel() {
 }
 
 export interface RustAnalyzerExtensionApi {
-    client: lc.LanguageClient;
+    client?: lc.LanguageClient;
 }
 
 export async function activate(
@@ -48,6 +48,23 @@ export async function activate(
 }
 
 async function tryActivate(context: vscode.ExtensionContext): Promise<RustAnalyzerExtensionApi> {
+    // We only support local folders, not eg. Live Share (`vlsl:` scheme), so don't activate if
+    // only those are in use.
+    // (r-a still somewhat works with Live Share, because commands are tunneled to the host)
+    const folders = (vscode.workspace.workspaceFolders || []).filter(
+        (folder) => folder.uri.scheme === "file"
+    );
+    const rustDocuments = vscode.workspace.textDocuments.filter((document) =>
+        isRustDocument(document)
+    );
+
+    if (folders.length === 0 && rustDocuments.length === 0) {
+        // FIXME: Ideally we would choose not to activate at all (and avoid registering
+        // non-functional editor commands), but VS Code doesn't seem to have a good way of doing
+        // that
+        return {};
+    }
+
     const config = new Config(context);
     const state = new PersistentState(context.globalState);
     const serverPath = await bootstrap(context, config, state).catch((err) => {
@@ -60,18 +77,11 @@ async function tryActivate(context: vscode.ExtensionContext): Promise<RustAnalyz
         throw new Error(message);
     });
 
-    if ((vscode.workspace.workspaceFolders || []).length === 0) {
-        const rustDocuments = vscode.workspace.textDocuments.filter((document) =>
-            isRustDocument(document)
-        );
-        if (rustDocuments.length > 0) {
-            ctx = await Ctx.create(config, context, serverPath, {
-                kind: "Detached Files",
-                files: rustDocuments,
-            });
-        } else {
-            throw new Error("no rust files are opened");
-        }
+    if (folders.length === 0) {
+        ctx = await Ctx.create(config, context, serverPath, {
+            kind: "Detached Files",
+            files: rustDocuments,
+        });
     } else {
         // Note: we try to start the server before we activate type hints so that it
         // registers its `onDidChangeDocument` handler before us.
@@ -163,6 +173,7 @@ async function initCommonContext(context: vscode.ExtensionContext, ctx: Ctx) {
     ctx.registerCommand("peekTests", commands.peekTests);
     ctx.registerCommand("moveItemUp", commands.moveItemUp);
     ctx.registerCommand("moveItemDown", commands.moveItemDown);
+    ctx.registerCommand("cancelFlycheck", commands.cancelFlycheck);
 
     defaultOnEnter.dispose();
     ctx.registerCommand("onEnter", commands.onEnter);
diff --git a/lib/lsp-server/src/socket.rs b/lib/lsp-server/src/socket.rs
index 4a59c4c0fad..36d728456f7 100644
--- a/lib/lsp-server/src/socket.rs
+++ b/lib/lsp-server/src/socket.rs
@@ -15,7 +15,7 @@ pub(crate) fn socket_transport(
     stream: TcpStream,
 ) -> (Sender<Message>, Receiver<Message>, IoThreads) {
     let (reader_receiver, reader) = make_reader(stream.try_clone().unwrap());
-    let (writer_sender, writer) = make_write(stream.try_clone().unwrap());
+    let (writer_sender, writer) = make_write(stream);
     let io_threads = make_io_threads(reader, writer);
     (writer_sender, reader_receiver, io_threads)
 }