diff options
143 files changed, 2784 insertions, 2078 deletions
diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 919d58925..7b9fbe6b8 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml | |||
@@ -15,12 +15,11 @@ env: | |||
15 | RUSTUP_MAX_RETRIES: 10 | 15 | RUSTUP_MAX_RETRIES: 10 |
16 | 16 | ||
17 | jobs: | 17 | jobs: |
18 | dist: | 18 | dist-windows: |
19 | name: dist | 19 | name: dist (Windows) |
20 | runs-on: ${{ matrix.os }} | 20 | runs-on: windows-latest |
21 | strategy: | 21 | env: |
22 | matrix: | 22 | RA_TARGET: x86_64-pc-windows-msvc |
23 | os: [ubuntu-16.04, windows-latest, macos-latest] | ||
24 | 23 | ||
25 | steps: | 24 | steps: |
26 | - name: Checkout repository | 25 | - name: Checkout repository |
@@ -30,8 +29,7 @@ jobs: | |||
30 | # which takes a long time. The fastest way to do this is to rename the | 29 | # which takes a long time. The fastest way to do this is to rename the |
31 | # existing folder, as deleting it takes about as much time as not doing | 30 | # existing folder, as deleting it takes about as much time as not doing |
32 | # anything and just updating rust-docs. | 31 | # anything and just updating rust-docs. |
33 | - name: Rename existing rust toolchain (Windows) | 32 | - name: Rename existing rust toolchain |
34 | if: matrix.os == 'windows-latest' | ||
35 | run: Rename-Item C:\Users\runneradmin\.rustup\toolchains\stable-x86_64-pc-windows-msvc C:\Users\runneradmin\.rustup\toolchains\stable-x86_64-pc-windows-msvc.old | 33 | run: Rename-Item C:\Users\runneradmin\.rustup\toolchains\stable-x86_64-pc-windows-msvc C:\Users\runneradmin\.rustup\toolchains\stable-x86_64-pc-windows-msvc.old |
36 | 34 | ||
37 | - name: Install Rust toolchain | 35 | - name: Install Rust toolchain |
@@ -41,38 +39,112 @@ jobs: | |||
41 | profile: minimal | 39 | profile: minimal |
42 | override: true | 40 | override: true |
43 | 41 | ||
42 | - name: Dist | ||
43 | run: cargo xtask dist | ||
44 | |||
45 | - name: Upload artifacts | ||
46 | uses: actions/upload-artifact@v1 | ||
47 | with: | ||
48 | name: dist-windows-latest | ||
49 | path: ./dist | ||
50 | |||
51 | dist-ubuntu: | ||
52 | name: dist (Ubuntu 16.04) | ||
53 | runs-on: ubuntu-16.04 | ||
54 | env: | ||
55 | RA_TARGET: x86_64-unknown-linux-gnu | ||
56 | |||
57 | steps: | ||
58 | - name: Checkout repository | ||
59 | uses: actions/checkout@v2 | ||
60 | |||
61 | - name: Install Rust toolchain | ||
62 | uses: actions-rs/toolchain@v1 | ||
63 | with: | ||
64 | toolchain: stable | ||
65 | profile: minimal | ||
66 | override: true | ||
67 | |||
44 | - name: Install Nodejs | 68 | - name: Install Nodejs |
45 | if: matrix.os == 'ubuntu-16.04' | ||
46 | uses: actions/setup-node@v1 | 69 | uses: actions/setup-node@v1 |
47 | with: | 70 | with: |
48 | node-version: 12.x | 71 | node-version: 12.x |
49 | 72 | ||
50 | - name: Dist | 73 | - name: Dist |
51 | if: matrix.os == 'ubuntu-16.04' && github.ref == 'refs/heads/release' | 74 | if: github.ref == 'refs/heads/release' |
52 | run: cargo xtask dist --client 0.2.$GITHUB_RUN_NUMBER | 75 | run: cargo xtask dist --client 0.2.$GITHUB_RUN_NUMBER |
53 | 76 | ||
54 | - name: Dist | 77 | - name: Dist |
55 | if: matrix.os == 'ubuntu-16.04' && github.ref != 'refs/heads/release' | 78 | if: github.ref != 'refs/heads/release' |
56 | run: cargo xtask dist --nightly --client 0.3.$GITHUB_RUN_NUMBER-nightly | 79 | run: cargo xtask dist --nightly --client 0.3.$GITHUB_RUN_NUMBER-nightly |
57 | 80 | ||
81 | - name: Nightly analysis-stats check | ||
82 | if: github.ref != 'refs/heads/release' | ||
83 | run: target/${{ env.RA_TARGET }}/release/rust-analyzer analysis-stats . | ||
84 | |||
85 | - name: Upload artifacts | ||
86 | uses: actions/upload-artifact@v1 | ||
87 | with: | ||
88 | name: dist-ubuntu-16.04 | ||
89 | path: ./dist | ||
90 | |||
91 | dist-macos-latest: | ||
92 | name: dist (MacOS latest) | ||
93 | runs-on: macos-latest | ||
94 | env: | ||
95 | RA_TARGET: x86_64-apple-darwin | ||
96 | |||
97 | steps: | ||
98 | - name: Checkout repository | ||
99 | uses: actions/checkout@v2 | ||
100 | |||
101 | - name: Install Rust toolchain | ||
102 | uses: actions-rs/toolchain@v1 | ||
103 | with: | ||
104 | toolchain: stable | ||
105 | profile: minimal | ||
106 | override: true | ||
107 | |||
58 | - name: Dist | 108 | - name: Dist |
59 | if: matrix.os != 'ubuntu-16.04' | ||
60 | run: cargo xtask dist | 109 | run: cargo xtask dist |
61 | 110 | ||
62 | - name: Nightly analysis-stats check | 111 | - name: Upload artifacts |
63 | if: matrix.os == 'ubuntu-16.04' && github.ref != 'refs/heads/release' | 112 | uses: actions/upload-artifact@v1 |
64 | run: ./dist/rust-analyzer-linux analysis-stats . | 113 | with: |
114 | name: dist-macos-latest | ||
115 | path: ./dist | ||
116 | |||
117 | dist-macos-11: | ||
118 | name: dist (MacOS 11.0) | ||
119 | runs-on: macos-11.0 | ||
120 | env: | ||
121 | RA_TARGET: aarch64-apple-darwin | ||
122 | |||
123 | steps: | ||
124 | - name: Checkout repository | ||
125 | uses: actions/checkout@v2 | ||
126 | |||
127 | - name: Install Rust toolchain (beta) | ||
128 | uses: actions-rs/toolchain@v1 | ||
129 | with: | ||
130 | toolchain: beta | ||
131 | target: aarch64-apple-darwin | ||
132 | profile: minimal | ||
133 | override: true | ||
134 | |||
135 | - name: Dist | ||
136 | run: cargo xtask dist | ||
65 | 137 | ||
66 | - name: Upload artifacts | 138 | - name: Upload artifacts |
67 | uses: actions/upload-artifact@v1 | 139 | uses: actions/upload-artifact@v1 |
68 | with: | 140 | with: |
69 | name: dist-${{ matrix.os }} | 141 | name: dist-macos-11.0 |
70 | path: ./dist | 142 | path: ./dist |
71 | 143 | ||
72 | publish: | 144 | publish: |
73 | name: publish | 145 | name: publish |
74 | runs-on: ubuntu-16.04 | 146 | runs-on: ubuntu-16.04 |
75 | needs: ['dist'] | 147 | needs: ['dist-windows', 'dist-ubuntu', 'dist-macos-latest', 'dist-macos-11'] |
76 | steps: | 148 | steps: |
77 | - name: Install Nodejs | 149 | - name: Install Nodejs |
78 | uses: actions/setup-node@v1 | 150 | uses: actions/setup-node@v1 |
@@ -93,6 +165,10 @@ jobs: | |||
93 | 165 | ||
94 | - uses: actions/download-artifact@v1 | 166 | - uses: actions/download-artifact@v1 |
95 | with: | 167 | with: |
168 | name: dist-macos-11.0 | ||
169 | path: dist | ||
170 | - uses: actions/download-artifact@v1 | ||
171 | with: | ||
96 | name: dist-macos-latest | 172 | name: dist-macos-latest |
97 | path: dist | 173 | path: dist |
98 | - uses: actions/download-artifact@v1 | 174 | - uses: actions/download-artifact@v1 |
@@ -103,7 +179,7 @@ jobs: | |||
103 | with: | 179 | with: |
104 | name: dist-windows-latest | 180 | name: dist-windows-latest |
105 | path: dist | 181 | path: dist |
106 | - run: ls -all ./dist | 182 | - run: ls -al ./dist |
107 | 183 | ||
108 | - name: Publish Release | 184 | - name: Publish Release |
109 | uses: ./.github/actions/github-release | 185 | uses: ./.github/actions/github-release |
diff --git a/Cargo.lock b/Cargo.lock index 1213e5762..1aa0c072d 100644 --- a/Cargo.lock +++ b/Cargo.lock | |||
@@ -26,9 +26,9 @@ dependencies = [ | |||
26 | 26 | ||
27 | [[package]] | 27 | [[package]] |
28 | name = "anyhow" | 28 | name = "anyhow" |
29 | version = "1.0.36" | 29 | version = "1.0.37" |
30 | source = "registry+https://github.com/rust-lang/crates.io-index" | 30 | source = "registry+https://github.com/rust-lang/crates.io-index" |
31 | checksum = "68803225a7b13e47191bab76f2687382b60d259e8cf37f6e1893658b84bb9479" | 31 | checksum = "ee67c11feeac938fae061b232e38e0b6d94f97a9df10e6271319325ac4c56a86" |
32 | 32 | ||
33 | [[package]] | 33 | [[package]] |
34 | name = "anymap" | 34 | name = "anymap" |
@@ -53,7 +53,7 @@ dependencies = [ | |||
53 | "either", | 53 | "either", |
54 | "hir", | 54 | "hir", |
55 | "ide_db", | 55 | "ide_db", |
56 | "itertools", | 56 | "itertools 0.10.0", |
57 | "profile", | 57 | "profile", |
58 | "rustc-hash", | 58 | "rustc-hash", |
59 | "stdx", | 59 | "stdx", |
@@ -168,9 +168,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" | |||
168 | 168 | ||
169 | [[package]] | 169 | [[package]] |
170 | name = "chalk-derive" | 170 | name = "chalk-derive" |
171 | version = "0.43.0" | 171 | version = "0.45.0" |
172 | source = "registry+https://github.com/rust-lang/crates.io-index" | 172 | source = "registry+https://github.com/rust-lang/crates.io-index" |
173 | checksum = "e2d9e0c8adcced1ab0fea5cb8a38647922893d5b495e363e1814299fd380469b" | 173 | checksum = "ec7dacf94958d1a930b95d049d9443860859af59eadc77849392093eb577bcee" |
174 | dependencies = [ | 174 | dependencies = [ |
175 | "proc-macro2", | 175 | "proc-macro2", |
176 | "quote", | 176 | "quote", |
@@ -180,19 +180,20 @@ dependencies = [ | |||
180 | 180 | ||
181 | [[package]] | 181 | [[package]] |
182 | name = "chalk-ir" | 182 | name = "chalk-ir" |
183 | version = "0.43.0" | 183 | version = "0.45.0" |
184 | source = "registry+https://github.com/rust-lang/crates.io-index" | 184 | source = "registry+https://github.com/rust-lang/crates.io-index" |
185 | checksum = "c5218266a5709bc4943de997e64d3fab41c9e9f68efd54a898de53135e987bd3" | 185 | checksum = "a1a5b38ede247def17da87f4badb62396a5753db6048e2011d3089d8b3796c67" |
186 | dependencies = [ | 186 | dependencies = [ |
187 | "bitflags", | ||
187 | "chalk-derive", | 188 | "chalk-derive", |
188 | "lazy_static", | 189 | "lazy_static", |
189 | ] | 190 | ] |
190 | 191 | ||
191 | [[package]] | 192 | [[package]] |
192 | name = "chalk-recursive" | 193 | name = "chalk-recursive" |
193 | version = "0.43.0" | 194 | version = "0.45.0" |
194 | source = "registry+https://github.com/rust-lang/crates.io-index" | 195 | source = "registry+https://github.com/rust-lang/crates.io-index" |
195 | checksum = "ed8f34f13fd4f30251f9f6f1dc56f80363201390ecbcac2fdfc8e33036cd9c4a" | 196 | checksum = "7a18db146d7a023edc20ad094e8c2284451f7888719645004979617d1f17c041" |
196 | dependencies = [ | 197 | dependencies = [ |
197 | "chalk-derive", | 198 | "chalk-derive", |
198 | "chalk-ir", | 199 | "chalk-ir", |
@@ -203,14 +204,14 @@ dependencies = [ | |||
203 | 204 | ||
204 | [[package]] | 205 | [[package]] |
205 | name = "chalk-solve" | 206 | name = "chalk-solve" |
206 | version = "0.43.0" | 207 | version = "0.45.0" |
207 | source = "registry+https://github.com/rust-lang/crates.io-index" | 208 | source = "registry+https://github.com/rust-lang/crates.io-index" |
208 | checksum = "379c9f584488346044709d4c638c38d61a06fe593d4de2ac5f15fd2b0ba4cd9d" | 209 | checksum = "7f73e0de04a0f394e47ed8118e00541bcf681d7c3c2ef500fa743eb4cf3a4850" |
209 | dependencies = [ | 210 | dependencies = [ |
210 | "chalk-derive", | 211 | "chalk-derive", |
211 | "chalk-ir", | 212 | "chalk-ir", |
212 | "ena", | 213 | "ena", |
213 | "itertools", | 214 | "itertools 0.9.0", |
214 | "petgraph", | 215 | "petgraph", |
215 | "rustc-hash", | 216 | "rustc-hash", |
216 | "tracing", | 217 | "tracing", |
@@ -249,7 +250,7 @@ dependencies = [ | |||
249 | "expect-test", | 250 | "expect-test", |
250 | "hir", | 251 | "hir", |
251 | "ide_db", | 252 | "ide_db", |
252 | "itertools", | 253 | "itertools 0.10.0", |
253 | "log", | 254 | "log", |
254 | "profile", | 255 | "profile", |
255 | "rustc-hash", | 256 | "rustc-hash", |
@@ -513,9 +514,9 @@ checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" | |||
513 | 514 | ||
514 | [[package]] | 515 | [[package]] |
515 | name = "heck" | 516 | name = "heck" |
516 | version = "0.3.1" | 517 | version = "0.3.2" |
517 | source = "registry+https://github.com/rust-lang/crates.io-index" | 518 | source = "registry+https://github.com/rust-lang/crates.io-index" |
518 | checksum = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" | 519 | checksum = "87cbf45460356b7deeb5e3415b5563308c0a9b057c85e12b06ad551f98d0a6ac" |
519 | dependencies = [ | 520 | dependencies = [ |
520 | "unicode-segmentation", | 521 | "unicode-segmentation", |
521 | ] | 522 | ] |
@@ -539,7 +540,7 @@ dependencies = [ | |||
539 | "hir_def", | 540 | "hir_def", |
540 | "hir_expand", | 541 | "hir_expand", |
541 | "hir_ty", | 542 | "hir_ty", |
542 | "itertools", | 543 | "itertools 0.10.0", |
543 | "log", | 544 | "log", |
544 | "profile", | 545 | "profile", |
545 | "rustc-hash", | 546 | "rustc-hash", |
@@ -562,7 +563,7 @@ dependencies = [ | |||
562 | "fst", | 563 | "fst", |
563 | "hir_expand", | 564 | "hir_expand", |
564 | "indexmap", | 565 | "indexmap", |
565 | "itertools", | 566 | "itertools 0.10.0", |
566 | "log", | 567 | "log", |
567 | "mbe", | 568 | "mbe", |
568 | "once_cell", | 569 | "once_cell", |
@@ -606,7 +607,7 @@ dependencies = [ | |||
606 | "expect-test", | 607 | "expect-test", |
607 | "hir_def", | 608 | "hir_def", |
608 | "hir_expand", | 609 | "hir_expand", |
609 | "itertools", | 610 | "itertools 0.10.0", |
610 | "log", | 611 | "log", |
611 | "once_cell", | 612 | "once_cell", |
612 | "profile", | 613 | "profile", |
@@ -642,7 +643,7 @@ dependencies = [ | |||
642 | "hir", | 643 | "hir", |
643 | "ide_db", | 644 | "ide_db", |
644 | "indexmap", | 645 | "indexmap", |
645 | "itertools", | 646 | "itertools 0.10.0", |
646 | "log", | 647 | "log", |
647 | "oorandom", | 648 | "oorandom", |
648 | "profile", | 649 | "profile", |
@@ -666,7 +667,7 @@ dependencies = [ | |||
666 | "expect-test", | 667 | "expect-test", |
667 | "fst", | 668 | "fst", |
668 | "hir", | 669 | "hir", |
669 | "itertools", | 670 | "itertools 0.10.0", |
670 | "log", | 671 | "log", |
671 | "once_cell", | 672 | "once_cell", |
672 | "profile", | 673 | "profile", |
@@ -747,10 +748,19 @@ dependencies = [ | |||
747 | ] | 748 | ] |
748 | 749 | ||
749 | [[package]] | 750 | [[package]] |
751 | name = "itertools" | ||
752 | version = "0.10.0" | ||
753 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
754 | checksum = "37d572918e350e82412fe766d24b15e6682fb2ed2bbe018280caa810397cb319" | ||
755 | dependencies = [ | ||
756 | "either", | ||
757 | ] | ||
758 | |||
759 | [[package]] | ||
750 | name = "itoa" | 760 | name = "itoa" |
751 | version = "0.4.6" | 761 | version = "0.4.7" |
752 | source = "registry+https://github.com/rust-lang/crates.io-index" | 762 | source = "registry+https://github.com/rust-lang/crates.io-index" |
753 | checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6" | 763 | checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" |
754 | 764 | ||
755 | [[package]] | 765 | [[package]] |
756 | name = "jod-thread" | 766 | name = "jod-thread" |
@@ -1080,9 +1090,9 @@ dependencies = [ | |||
1080 | 1090 | ||
1081 | [[package]] | 1091 | [[package]] |
1082 | name = "parking_lot_core" | 1092 | name = "parking_lot_core" |
1083 | version = "0.8.1" | 1093 | version = "0.8.2" |
1084 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1094 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1085 | checksum = "d7c6d9b8427445284a09c55be860a15855ab580a417ccad9da88f5a06787ced0" | 1095 | checksum = "9ccb628cad4f84851442432c60ad8e1f607e29752d0bf072cbd0baf28aa34272" |
1086 | dependencies = [ | 1096 | dependencies = [ |
1087 | "cfg-if 1.0.0", | 1097 | "cfg-if 1.0.0", |
1088 | "instant", | 1098 | "instant", |
@@ -1224,7 +1234,7 @@ dependencies = [ | |||
1224 | "base_db", | 1234 | "base_db", |
1225 | "cargo_metadata", | 1235 | "cargo_metadata", |
1226 | "cfg", | 1236 | "cfg", |
1227 | "itertools", | 1237 | "itertools 0.10.0", |
1228 | "log", | 1238 | "log", |
1229 | "paths", | 1239 | "paths", |
1230 | "proc_macro_api", | 1240 | "proc_macro_api", |
@@ -1348,7 +1358,7 @@ dependencies = [ | |||
1348 | "hir_ty", | 1358 | "hir_ty", |
1349 | "ide", | 1359 | "ide", |
1350 | "ide_db", | 1360 | "ide_db", |
1351 | "itertools", | 1361 | "itertools 0.10.0", |
1352 | "jod-thread", | 1362 | "jod-thread", |
1353 | "log", | 1363 | "log", |
1354 | "lsp-server", | 1364 | "lsp-server", |
@@ -1365,6 +1375,7 @@ dependencies = [ | |||
1365 | "rustc-hash", | 1375 | "rustc-hash", |
1366 | "serde", | 1376 | "serde", |
1367 | "serde_json", | 1377 | "serde_json", |
1378 | "serde_path_to_error", | ||
1368 | "ssr", | 1379 | "ssr", |
1369 | "stdx", | 1380 | "stdx", |
1370 | "syntax", | 1381 | "syntax", |
@@ -1383,9 +1394,9 @@ dependencies = [ | |||
1383 | 1394 | ||
1384 | [[package]] | 1395 | [[package]] |
1385 | name = "rustc-ap-rustc_lexer" | 1396 | name = "rustc-ap-rustc_lexer" |
1386 | version = "691.0.0" | 1397 | version = "695.0.0" |
1387 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1398 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1388 | checksum = "44bc89d9ca7a78fb82e103b389362c55f03800745f8ba14e068b805cfaf783ec" | 1399 | checksum = "390bad134705b0bff02cd9541ac66df751a91c3cc734c3369cd6151ca269caed" |
1389 | dependencies = [ | 1400 | dependencies = [ |
1390 | "unicode-xid", | 1401 | "unicode-xid", |
1391 | ] | 1402 | ] |
@@ -1523,9 +1534,9 @@ dependencies = [ | |||
1523 | 1534 | ||
1524 | [[package]] | 1535 | [[package]] |
1525 | name = "serde_json" | 1536 | name = "serde_json" |
1526 | version = "1.0.60" | 1537 | version = "1.0.61" |
1527 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1538 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1528 | checksum = "1500e84d27fe482ed1dc791a56eddc2f230046a040fa908c08bda1d9fb615779" | 1539 | checksum = "4fceb2595057b6891a4ee808f70054bd2d12f0e97f1cbb78689b59f676df325a" |
1529 | dependencies = [ | 1540 | dependencies = [ |
1530 | "indexmap", | 1541 | "indexmap", |
1531 | "itoa", | 1542 | "itoa", |
@@ -1534,6 +1545,15 @@ dependencies = [ | |||
1534 | ] | 1545 | ] |
1535 | 1546 | ||
1536 | [[package]] | 1547 | [[package]] |
1548 | name = "serde_path_to_error" | ||
1549 | version = "0.1.4" | ||
1550 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1551 | checksum = "42f6109f0506e20f7e0f910e51a0079acf41da8e0694e6442527c4ddf5a2b158" | ||
1552 | dependencies = [ | ||
1553 | "serde", | ||
1554 | ] | ||
1555 | |||
1556 | [[package]] | ||
1537 | name = "serde_repr" | 1557 | name = "serde_repr" |
1538 | version = "0.1.6" | 1558 | version = "0.1.6" |
1539 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1559 | source = "registry+https://github.com/rust-lang/crates.io-index" |
@@ -1582,7 +1602,7 @@ dependencies = [ | |||
1582 | "expect-test", | 1602 | "expect-test", |
1583 | "hir", | 1603 | "hir", |
1584 | "ide_db", | 1604 | "ide_db", |
1585 | "itertools", | 1605 | "itertools 0.10.0", |
1586 | "rustc-hash", | 1606 | "rustc-hash", |
1587 | "syntax", | 1607 | "syntax", |
1588 | "test_utils", | 1608 | "test_utils", |
@@ -1595,9 +1615,9 @@ version = "0.0.0" | |||
1595 | 1615 | ||
1596 | [[package]] | 1616 | [[package]] |
1597 | name = "syn" | 1617 | name = "syn" |
1598 | version = "1.0.55" | 1618 | version = "1.0.56" |
1599 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1619 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1600 | checksum = "a571a711dddd09019ccc628e1b17fe87c59b09d513c06c026877aa708334f37a" | 1620 | checksum = "a9802ddde94170d186eeee5005b798d9c159fa970403f1be19976d0cfb939b72" |
1601 | dependencies = [ | 1621 | dependencies = [ |
1602 | "proc-macro2", | 1622 | "proc-macro2", |
1603 | "quote", | 1623 | "quote", |
@@ -1623,7 +1643,7 @@ dependencies = [ | |||
1623 | "arrayvec", | 1643 | "arrayvec", |
1624 | "expect-test", | 1644 | "expect-test", |
1625 | "indexmap", | 1645 | "indexmap", |
1626 | "itertools", | 1646 | "itertools 0.10.0", |
1627 | "once_cell", | 1647 | "once_cell", |
1628 | "parser", | 1648 | "parser", |
1629 | "profile", | 1649 | "profile", |
@@ -1834,9 +1854,9 @@ checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" | |||
1834 | 1854 | ||
1835 | [[package]] | 1855 | [[package]] |
1836 | name = "ungrammar" | 1856 | name = "ungrammar" |
1837 | version = "1.4.0" | 1857 | version = "1.5.0" |
1838 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1858 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1839 | checksum = "68951379f3ced25754472ca5addbf74d7dab58c9818f49290a3d8caa3ab44fb7" | 1859 | checksum = "c11bffada52edc8f2a56160b286ea4640acf90ffcb21bded361ccb8ed43a1457" |
1840 | 1860 | ||
1841 | [[package]] | 1861 | [[package]] |
1842 | name = "unicase" | 1862 | name = "unicase" |
diff --git a/crates/assists/Cargo.toml b/crates/assists/Cargo.toml index 3fd8327d6..ed8ad666f 100644 --- a/crates/assists/Cargo.toml +++ b/crates/assists/Cargo.toml | |||
@@ -11,7 +11,7 @@ doctest = false | |||
11 | 11 | ||
12 | [dependencies] | 12 | [dependencies] |
13 | rustc-hash = "1.1.0" | 13 | rustc-hash = "1.1.0" |
14 | itertools = "0.9.0" | 14 | itertools = "0.10.0" |
15 | either = "1.6.1" | 15 | either = "1.6.1" |
16 | 16 | ||
17 | stdx = { path = "../stdx", version = "0.0.0" } | 17 | stdx = { path = "../stdx", version = "0.0.0" } |
diff --git a/crates/assists/src/assist_context.rs b/crates/assists/src/assist_context.rs index 69499ea32..4f59d39a9 100644 --- a/crates/assists/src/assist_context.rs +++ b/crates/assists/src/assist_context.rs | |||
@@ -4,10 +4,10 @@ use std::mem; | |||
4 | 4 | ||
5 | use algo::find_covering_element; | 5 | use algo::find_covering_element; |
6 | use hir::Semantics; | 6 | use hir::Semantics; |
7 | use ide_db::base_db::{FileId, FileRange}; | 7 | use ide_db::base_db::{AnchoredPathBuf, FileId, FileRange}; |
8 | use ide_db::{ | 8 | use ide_db::{ |
9 | label::Label, | 9 | label::Label, |
10 | source_change::{SourceChange, SourceFileEdit}, | 10 | source_change::{FileSystemEdit, SourceChange, SourceFileEdit}, |
11 | RootDatabase, | 11 | RootDatabase, |
12 | }; | 12 | }; |
13 | use syntax::{ | 13 | use syntax::{ |
@@ -19,7 +19,7 @@ use text_edit::{TextEdit, TextEditBuilder}; | |||
19 | 19 | ||
20 | use crate::{ | 20 | use crate::{ |
21 | assist_config::{AssistConfig, SnippetCap}, | 21 | assist_config::{AssistConfig, SnippetCap}, |
22 | Assist, AssistId, AssistKind, GroupLabel, ResolvedAssist, | 22 | Assist, AssistId, AssistKind, GroupLabel, |
23 | }; | 23 | }; |
24 | 24 | ||
25 | /// `AssistContext` allows to apply an assist or check if it could be applied. | 25 | /// `AssistContext` allows to apply an assist or check if it could be applied. |
@@ -105,46 +105,23 @@ impl<'a> AssistContext<'a> { | |||
105 | pub(crate) struct Assists { | 105 | pub(crate) struct Assists { |
106 | resolve: bool, | 106 | resolve: bool, |
107 | file: FileId, | 107 | file: FileId, |
108 | buf: Vec<(Assist, Option<SourceChange>)>, | 108 | buf: Vec<Assist>, |
109 | allowed: Option<Vec<AssistKind>>, | 109 | allowed: Option<Vec<AssistKind>>, |
110 | } | 110 | } |
111 | 111 | ||
112 | impl Assists { | 112 | impl Assists { |
113 | pub(crate) fn new_resolved(ctx: &AssistContext) -> Assists { | 113 | pub(crate) fn new(ctx: &AssistContext, resolve: bool) -> Assists { |
114 | Assists { | 114 | Assists { |
115 | resolve: true, | 115 | resolve, |
116 | file: ctx.frange.file_id, | 116 | file: ctx.frange.file_id, |
117 | buf: Vec::new(), | 117 | buf: Vec::new(), |
118 | allowed: ctx.config.allowed.clone(), | 118 | allowed: ctx.config.allowed.clone(), |
119 | } | 119 | } |
120 | } | 120 | } |
121 | 121 | ||
122 | pub(crate) fn new_unresolved(ctx: &AssistContext) -> Assists { | 122 | pub(crate) fn finish(mut self) -> Vec<Assist> { |
123 | Assists { | 123 | self.buf.sort_by_key(|assist| assist.target.len()); |
124 | resolve: false, | 124 | self.buf |
125 | file: ctx.frange.file_id, | ||
126 | buf: Vec::new(), | ||
127 | allowed: ctx.config.allowed.clone(), | ||
128 | } | ||
129 | } | ||
130 | |||
131 | pub(crate) fn finish_unresolved(self) -> Vec<Assist> { | ||
132 | assert!(!self.resolve); | ||
133 | self.finish() | ||
134 | .into_iter() | ||
135 | .map(|(label, edit)| { | ||
136 | assert!(edit.is_none()); | ||
137 | label | ||
138 | }) | ||
139 | .collect() | ||
140 | } | ||
141 | |||
142 | pub(crate) fn finish_resolved(self) -> Vec<ResolvedAssist> { | ||
143 | assert!(self.resolve); | ||
144 | self.finish() | ||
145 | .into_iter() | ||
146 | .map(|(label, edit)| ResolvedAssist { assist: label, source_change: edit.unwrap() }) | ||
147 | .collect() | ||
148 | } | 125 | } |
149 | 126 | ||
150 | pub(crate) fn add( | 127 | pub(crate) fn add( |
@@ -158,7 +135,7 @@ impl Assists { | |||
158 | return None; | 135 | return None; |
159 | } | 136 | } |
160 | let label = Label::new(label.into()); | 137 | let label = Label::new(label.into()); |
161 | let assist = Assist { id, label, group: None, target }; | 138 | let assist = Assist { id, label, group: None, target, source_change: None }; |
162 | self.add_impl(assist, f) | 139 | self.add_impl(assist, f) |
163 | } | 140 | } |
164 | 141 | ||
@@ -174,11 +151,11 @@ impl Assists { | |||
174 | return None; | 151 | return None; |
175 | } | 152 | } |
176 | let label = Label::new(label.into()); | 153 | let label = Label::new(label.into()); |
177 | let assist = Assist { id, label, group: Some(group.clone()), target }; | 154 | let assist = Assist { id, label, group: Some(group.clone()), target, source_change: None }; |
178 | self.add_impl(assist, f) | 155 | self.add_impl(assist, f) |
179 | } | 156 | } |
180 | 157 | ||
181 | fn add_impl(&mut self, assist: Assist, f: impl FnOnce(&mut AssistBuilder)) -> Option<()> { | 158 | fn add_impl(&mut self, mut assist: Assist, f: impl FnOnce(&mut AssistBuilder)) -> Option<()> { |
182 | let source_change = if self.resolve { | 159 | let source_change = if self.resolve { |
183 | let mut builder = AssistBuilder::new(self.file); | 160 | let mut builder = AssistBuilder::new(self.file); |
184 | f(&mut builder); | 161 | f(&mut builder); |
@@ -186,16 +163,12 @@ impl Assists { | |||
186 | } else { | 163 | } else { |
187 | None | 164 | None |
188 | }; | 165 | }; |
166 | assist.source_change = source_change.clone(); | ||
189 | 167 | ||
190 | self.buf.push((assist, source_change)); | 168 | self.buf.push(assist); |
191 | Some(()) | 169 | Some(()) |
192 | } | 170 | } |
193 | 171 | ||
194 | fn finish(mut self) -> Vec<(Assist, Option<SourceChange>)> { | ||
195 | self.buf.sort_by_key(|(label, _edit)| label.target.len()); | ||
196 | self.buf | ||
197 | } | ||
198 | |||
199 | fn is_allowed(&self, id: &AssistId) -> bool { | 172 | fn is_allowed(&self, id: &AssistId) -> bool { |
200 | match &self.allowed { | 173 | match &self.allowed { |
201 | Some(allowed) => allowed.iter().any(|kind| kind.contains(id.1)), | 174 | Some(allowed) => allowed.iter().any(|kind| kind.contains(id.1)), |
@@ -209,6 +182,7 @@ pub(crate) struct AssistBuilder { | |||
209 | file_id: FileId, | 182 | file_id: FileId, |
210 | is_snippet: bool, | 183 | is_snippet: bool, |
211 | source_file_edits: Vec<SourceFileEdit>, | 184 | source_file_edits: Vec<SourceFileEdit>, |
185 | file_system_edits: Vec<FileSystemEdit>, | ||
212 | } | 186 | } |
213 | 187 | ||
214 | impl AssistBuilder { | 188 | impl AssistBuilder { |
@@ -218,6 +192,7 @@ impl AssistBuilder { | |||
218 | file_id, | 192 | file_id, |
219 | is_snippet: false, | 193 | is_snippet: false, |
220 | source_file_edits: Vec::default(), | 194 | source_file_edits: Vec::default(), |
195 | file_system_edits: Vec::default(), | ||
221 | } | 196 | } |
222 | } | 197 | } |
223 | 198 | ||
@@ -282,12 +257,17 @@ impl AssistBuilder { | |||
282 | algo::diff(&node, &new).into_text_edit(&mut self.edit); | 257 | algo::diff(&node, &new).into_text_edit(&mut self.edit); |
283 | } | 258 | } |
284 | } | 259 | } |
260 | pub(crate) fn create_file(&mut self, dst: AnchoredPathBuf, content: impl Into<String>) { | ||
261 | let file_system_edit = | ||
262 | FileSystemEdit::CreateFile { dst: dst.clone(), initial_contents: content.into() }; | ||
263 | self.file_system_edits.push(file_system_edit); | ||
264 | } | ||
285 | 265 | ||
286 | fn finish(mut self) -> SourceChange { | 266 | fn finish(mut self) -> SourceChange { |
287 | self.commit(); | 267 | self.commit(); |
288 | SourceChange { | 268 | SourceChange { |
289 | source_file_edits: mem::take(&mut self.source_file_edits), | 269 | source_file_edits: mem::take(&mut self.source_file_edits), |
290 | file_system_edits: Default::default(), | 270 | file_system_edits: mem::take(&mut self.file_system_edits), |
291 | is_snippet: self.is_snippet, | 271 | is_snippet: self.is_snippet, |
292 | } | 272 | } |
293 | } | 273 | } |
diff --git a/crates/assists/src/handlers/add_missing_impl_members.rs b/crates/assists/src/handlers/add_missing_impl_members.rs index e413505d3..7df05b841 100644 --- a/crates/assists/src/handlers/add_missing_impl_members.rs +++ b/crates/assists/src/handlers/add_missing_impl_members.rs | |||
@@ -15,7 +15,7 @@ use crate::{ | |||
15 | // | 15 | // |
16 | // ``` | 16 | // ``` |
17 | // trait Trait<T> { | 17 | // trait Trait<T> { |
18 | // Type X; | 18 | // type X; |
19 | // fn foo(&self) -> T; | 19 | // fn foo(&self) -> T; |
20 | // fn bar(&self) {} | 20 | // fn bar(&self) {} |
21 | // } | 21 | // } |
@@ -27,14 +27,16 @@ use crate::{ | |||
27 | // -> | 27 | // -> |
28 | // ``` | 28 | // ``` |
29 | // trait Trait<T> { | 29 | // trait Trait<T> { |
30 | // Type X; | 30 | // type X; |
31 | // fn foo(&self) -> T; | 31 | // fn foo(&self) -> T; |
32 | // fn bar(&self) {} | 32 | // fn bar(&self) {} |
33 | // } | 33 | // } |
34 | // | 34 | // |
35 | // impl Trait<u32> for () { | 35 | // impl Trait<u32> for () { |
36 | // $0type X; | ||
37 | // | ||
36 | // fn foo(&self) -> u32 { | 38 | // fn foo(&self) -> u32 { |
37 | // ${0:todo!()} | 39 | // todo!() |
38 | // } | 40 | // } |
39 | // } | 41 | // } |
40 | // ``` | 42 | // ``` |
@@ -54,13 +56,13 @@ pub(crate) fn add_missing_impl_members(acc: &mut Assists, ctx: &AssistContext) - | |||
54 | // | 56 | // |
55 | // ``` | 57 | // ``` |
56 | // trait Trait { | 58 | // trait Trait { |
57 | // Type X; | 59 | // type X; |
58 | // fn foo(&self); | 60 | // fn foo(&self); |
59 | // fn bar(&self) {} | 61 | // fn bar(&self) {} |
60 | // } | 62 | // } |
61 | // | 63 | // |
62 | // impl Trait for () { | 64 | // impl Trait for () { |
63 | // Type X = (); | 65 | // type X = (); |
64 | // fn foo(&self) {}<|> | 66 | // fn foo(&self) {}<|> |
65 | // | 67 | // |
66 | // } | 68 | // } |
@@ -68,13 +70,13 @@ pub(crate) fn add_missing_impl_members(acc: &mut Assists, ctx: &AssistContext) - | |||
68 | // -> | 70 | // -> |
69 | // ``` | 71 | // ``` |
70 | // trait Trait { | 72 | // trait Trait { |
71 | // Type X; | 73 | // type X; |
72 | // fn foo(&self); | 74 | // fn foo(&self); |
73 | // fn bar(&self) {} | 75 | // fn bar(&self) {} |
74 | // } | 76 | // } |
75 | // | 77 | // |
76 | // impl Trait for () { | 78 | // impl Trait for () { |
77 | // Type X = (); | 79 | // type X = (); |
78 | // fn foo(&self) {} | 80 | // fn foo(&self) {} |
79 | // | 81 | // |
80 | // $0fn bar(&self) {} | 82 | // $0fn bar(&self) {} |
diff --git a/crates/assists/src/handlers/extract_module_to_file.rs b/crates/assists/src/handlers/extract_module_to_file.rs new file mode 100644 index 000000000..50bf67ef7 --- /dev/null +++ b/crates/assists/src/handlers/extract_module_to_file.rs | |||
@@ -0,0 +1,133 @@ | |||
1 | use ast::edit::IndentLevel; | ||
2 | use ide_db::base_db::AnchoredPathBuf; | ||
3 | use syntax::{ | ||
4 | ast::{self, edit::AstNodeEdit, NameOwner}, | ||
5 | AstNode, | ||
6 | }; | ||
7 | |||
8 | use crate::{AssistContext, AssistId, AssistKind, Assists}; | ||
9 | |||
10 | // Assist: extract_module_to_file | ||
11 | // | ||
12 | // This assist extract module to file. | ||
13 | // | ||
14 | // ``` | ||
15 | // mod foo {<|> | ||
16 | // fn t() {} | ||
17 | // } | ||
18 | // ``` | ||
19 | // -> | ||
20 | // ``` | ||
21 | // mod foo; | ||
22 | // ``` | ||
23 | pub(crate) fn extract_module_to_file(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | ||
24 | let module_ast = ctx.find_node_at_offset::<ast::Module>()?; | ||
25 | let module_name = module_ast.name()?; | ||
26 | |||
27 | let module_def = ctx.sema.to_def(&module_ast)?; | ||
28 | let parent_module = module_def.parent(ctx.db())?; | ||
29 | |||
30 | let module_items = module_ast.item_list()?; | ||
31 | let target = module_ast.syntax().text_range(); | ||
32 | let anchor_file_id = ctx.frange.file_id; | ||
33 | |||
34 | acc.add( | ||
35 | AssistId("extract_module_to_file", AssistKind::RefactorExtract), | ||
36 | "Extract module to file", | ||
37 | target, | ||
38 | |builder| { | ||
39 | let path = { | ||
40 | let dir = match parent_module.name(ctx.db()) { | ||
41 | Some(name) if !parent_module.is_mod_rs(ctx.db()) => format!("{}/", name), | ||
42 | _ => String::new(), | ||
43 | }; | ||
44 | format!("./{}{}.rs", dir, module_name) | ||
45 | }; | ||
46 | let contents = { | ||
47 | let items = module_items.dedent(IndentLevel(1)).to_string(); | ||
48 | let mut items = | ||
49 | items.trim_start_matches('{').trim_end_matches('}').trim().to_string(); | ||
50 | if !items.is_empty() { | ||
51 | items.push('\n'); | ||
52 | } | ||
53 | items | ||
54 | }; | ||
55 | |||
56 | builder.replace(target, format!("mod {};", module_name)); | ||
57 | |||
58 | let dst = AnchoredPathBuf { anchor: anchor_file_id, path }; | ||
59 | builder.create_file(dst, contents); | ||
60 | }, | ||
61 | ) | ||
62 | } | ||
63 | |||
64 | #[cfg(test)] | ||
65 | mod tests { | ||
66 | use crate::tests::check_assist; | ||
67 | |||
68 | use super::*; | ||
69 | |||
70 | #[test] | ||
71 | fn extract_from_root() { | ||
72 | check_assist( | ||
73 | extract_module_to_file, | ||
74 | r#" | ||
75 | mod tests {<|> | ||
76 | #[test] fn t() {} | ||
77 | } | ||
78 | "#, | ||
79 | r#" | ||
80 | //- /main.rs | ||
81 | mod tests; | ||
82 | //- /tests.rs | ||
83 | #[test] fn t() {} | ||
84 | "#, | ||
85 | ); | ||
86 | } | ||
87 | |||
88 | #[test] | ||
89 | fn extract_from_submodule() { | ||
90 | check_assist( | ||
91 | extract_module_to_file, | ||
92 | r#" | ||
93 | //- /main.rs | ||
94 | mod submod; | ||
95 | //- /submod.rs | ||
96 | mod inner<|> { | ||
97 | fn f() {} | ||
98 | } | ||
99 | fn g() {} | ||
100 | "#, | ||
101 | r#" | ||
102 | //- /submod.rs | ||
103 | mod inner; | ||
104 | fn g() {} | ||
105 | //- /submod/inner.rs | ||
106 | fn f() {} | ||
107 | "#, | ||
108 | ); | ||
109 | } | ||
110 | |||
111 | #[test] | ||
112 | fn extract_from_mod_rs() { | ||
113 | check_assist( | ||
114 | extract_module_to_file, | ||
115 | r#" | ||
116 | //- /main.rs | ||
117 | mod submodule; | ||
118 | //- /submodule/mod.rs | ||
119 | mod inner<|> { | ||
120 | fn f() {} | ||
121 | } | ||
122 | fn g() {} | ||
123 | "#, | ||
124 | r#" | ||
125 | //- /submodule/mod.rs | ||
126 | mod inner; | ||
127 | fn g() {} | ||
128 | //- /submodule/inner.rs | ||
129 | fn f() {} | ||
130 | "#, | ||
131 | ); | ||
132 | } | ||
133 | } | ||
diff --git a/crates/assists/src/handlers/extract_variable.rs b/crates/assists/src/handlers/extract_variable.rs index d2ae137cd..9957012fe 100644 --- a/crates/assists/src/handlers/extract_variable.rs +++ b/crates/assists/src/handlers/extract_variable.rs | |||
@@ -91,7 +91,7 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext) -> Option | |||
91 | // extra newlines in the indent block | 91 | // extra newlines in the indent block |
92 | let text = indent.text(); | 92 | let text = indent.text(); |
93 | if text.starts_with('\n') { | 93 | if text.starts_with('\n') { |
94 | buf.push_str("\n"); | 94 | buf.push('\n'); |
95 | buf.push_str(text.trim_start_matches('\n')); | 95 | buf.push_str(text.trim_start_matches('\n')); |
96 | } else { | 96 | } else { |
97 | buf.push_str(text); | 97 | buf.push_str(text); |
diff --git a/crates/assists/src/handlers/invert_if.rs b/crates/assists/src/handlers/invert_if.rs index 91e2f5c8c..f9c33b3f7 100644 --- a/crates/assists/src/handlers/invert_if.rs +++ b/crates/assists/src/handlers/invert_if.rs | |||
@@ -78,6 +78,15 @@ mod tests { | |||
78 | } | 78 | } |
79 | 79 | ||
80 | #[test] | 80 | #[test] |
81 | fn invert_if_remove_not_parentheses() { | ||
82 | check_assist( | ||
83 | invert_if, | ||
84 | "fn f() { i<|>f !(x == 3 || x == 4 || x == 5) { 3 * 2 } else { 1 } }", | ||
85 | "fn f() { if x == 3 || x == 4 || x == 5 { 1 } else { 3 * 2 } }", | ||
86 | ) | ||
87 | } | ||
88 | |||
89 | #[test] | ||
81 | fn invert_if_remove_inequality() { | 90 | fn invert_if_remove_inequality() { |
82 | check_assist( | 91 | check_assist( |
83 | invert_if, | 92 | invert_if, |
diff --git a/crates/assists/src/handlers/remove_unused_param.rs b/crates/assists/src/handlers/remove_unused_param.rs index 1ff5e92b0..f72dd49ed 100644 --- a/crates/assists/src/handlers/remove_unused_param.rs +++ b/crates/assists/src/handlers/remove_unused_param.rs | |||
@@ -2,9 +2,10 @@ use ide_db::{defs::Definition, search::Reference}; | |||
2 | use syntax::{ | 2 | use syntax::{ |
3 | algo::find_node_at_range, | 3 | algo::find_node_at_range, |
4 | ast::{self, ArgListOwner}, | 4 | ast::{self, ArgListOwner}, |
5 | AstNode, SyntaxNode, TextRange, T, | 5 | AstNode, SyntaxKind, SyntaxNode, TextRange, T, |
6 | }; | 6 | }; |
7 | use test_utils::mark; | 7 | use test_utils::mark; |
8 | use SyntaxKind::WHITESPACE; | ||
8 | 9 | ||
9 | use crate::{ | 10 | use crate::{ |
10 | assist_context::AssistBuilder, utils::next_prev, AssistContext, AssistId, AssistKind, Assists, | 11 | assist_context::AssistBuilder, utils::next_prev, AssistContext, AssistId, AssistKind, Assists, |
@@ -56,7 +57,7 @@ pub(crate) fn remove_unused_param(acc: &mut Assists, ctx: &AssistContext) -> Opt | |||
56 | "Remove unused parameter", | 57 | "Remove unused parameter", |
57 | param.syntax().text_range(), | 58 | param.syntax().text_range(), |
58 | |builder| { | 59 | |builder| { |
59 | builder.delete(range_with_coma(param.syntax())); | 60 | builder.delete(range_to_remove(param.syntax())); |
60 | for usage in fn_def.usages(&ctx.sema).all() { | 61 | for usage in fn_def.usages(&ctx.sema).all() { |
61 | process_usage(ctx, builder, usage, param_position); | 62 | process_usage(ctx, builder, usage, param_position); |
62 | } | 63 | } |
@@ -80,19 +81,34 @@ fn process_usage( | |||
80 | let arg = call_expr.arg_list()?.args().nth(arg_to_remove)?; | 81 | let arg = call_expr.arg_list()?.args().nth(arg_to_remove)?; |
81 | 82 | ||
82 | builder.edit_file(usage.file_range.file_id); | 83 | builder.edit_file(usage.file_range.file_id); |
83 | builder.delete(range_with_coma(arg.syntax())); | 84 | builder.delete(range_to_remove(arg.syntax())); |
84 | 85 | ||
85 | Some(()) | 86 | Some(()) |
86 | } | 87 | } |
87 | 88 | ||
88 | fn range_with_coma(node: &SyntaxNode) -> TextRange { | 89 | fn range_to_remove(node: &SyntaxNode) -> TextRange { |
89 | let up_to = next_prev().find_map(|dir| { | 90 | let up_to_comma = next_prev().find_map(|dir| { |
90 | node.siblings_with_tokens(dir) | 91 | node.siblings_with_tokens(dir) |
91 | .filter_map(|it| it.into_token()) | 92 | .filter_map(|it| it.into_token()) |
92 | .find(|it| it.kind() == T![,]) | 93 | .find(|it| it.kind() == T![,]) |
94 | .map(|it| (dir, it)) | ||
93 | }); | 95 | }); |
94 | let up_to = up_to.map_or(node.text_range(), |it| it.text_range()); | 96 | if let Some((dir, token)) = up_to_comma { |
95 | node.text_range().cover(up_to) | 97 | if node.next_sibling().is_some() { |
98 | let up_to_space = token | ||
99 | .siblings_with_tokens(dir) | ||
100 | .skip(1) | ||
101 | .take_while(|it| it.kind() == WHITESPACE) | ||
102 | .last() | ||
103 | .and_then(|it| it.into_token()); | ||
104 | return node | ||
105 | .text_range() | ||
106 | .cover(up_to_space.map_or(token.text_range(), |it| it.text_range())); | ||
107 | } | ||
108 | node.text_range().cover(token.text_range()) | ||
109 | } else { | ||
110 | node.text_range() | ||
111 | } | ||
96 | } | 112 | } |
97 | 113 | ||
98 | #[cfg(test)] | 114 | #[cfg(test)] |
@@ -119,6 +135,57 @@ fn b() { foo(9, ) } | |||
119 | } | 135 | } |
120 | 136 | ||
121 | #[test] | 137 | #[test] |
138 | fn remove_unused_first_param() { | ||
139 | check_assist( | ||
140 | remove_unused_param, | ||
141 | r#" | ||
142 | fn foo(<|>x: i32, y: i32) { y; } | ||
143 | fn a() { foo(1, 2) } | ||
144 | fn b() { foo(1, 2,) } | ||
145 | "#, | ||
146 | r#" | ||
147 | fn foo(y: i32) { y; } | ||
148 | fn a() { foo(2) } | ||
149 | fn b() { foo(2,) } | ||
150 | "#, | ||
151 | ); | ||
152 | } | ||
153 | |||
154 | #[test] | ||
155 | fn remove_unused_single_param() { | ||
156 | check_assist( | ||
157 | remove_unused_param, | ||
158 | r#" | ||
159 | fn foo(<|>x: i32) { 0; } | ||
160 | fn a() { foo(1) } | ||
161 | fn b() { foo(1, ) } | ||
162 | "#, | ||
163 | r#" | ||
164 | fn foo() { 0; } | ||
165 | fn a() { foo() } | ||
166 | fn b() { foo( ) } | ||
167 | "#, | ||
168 | ); | ||
169 | } | ||
170 | |||
171 | #[test] | ||
172 | fn remove_unused_surrounded_by_parms() { | ||
173 | check_assist( | ||
174 | remove_unused_param, | ||
175 | r#" | ||
176 | fn foo(x: i32, <|>y: i32, z: i32) { x; } | ||
177 | fn a() { foo(1, 2, 3) } | ||
178 | fn b() { foo(1, 2, 3,) } | ||
179 | "#, | ||
180 | r#" | ||
181 | fn foo(x: i32, z: i32) { x; } | ||
182 | fn a() { foo(1, 3) } | ||
183 | fn b() { foo(1, 3,) } | ||
184 | "#, | ||
185 | ); | ||
186 | } | ||
187 | |||
188 | #[test] | ||
122 | fn remove_unused_qualified_call() { | 189 | fn remove_unused_qualified_call() { |
123 | check_assist( | 190 | check_assist( |
124 | remove_unused_param, | 191 | remove_unused_param, |
diff --git a/crates/assists/src/handlers/replace_derive_with_manual_impl.rs b/crates/assists/src/handlers/replace_derive_with_manual_impl.rs index 4d6a1956b..cb7a5c104 100644 --- a/crates/assists/src/handlers/replace_derive_with_manual_impl.rs +++ b/crates/assists/src/handlers/replace_derive_with_manual_impl.rs | |||
@@ -62,21 +62,22 @@ pub(crate) fn replace_derive_with_manual_impl( | |||
62 | let current_module = ctx.sema.scope(annotated_name.syntax()).module()?; | 62 | let current_module = ctx.sema.scope(annotated_name.syntax()).module()?; |
63 | let current_crate = current_module.krate(); | 63 | let current_crate = current_module.krate(); |
64 | 64 | ||
65 | let found_traits = | 65 | let found_traits = imports_locator::find_exact_imports( |
66 | imports_locator::find_exact_imports(&ctx.sema, current_crate, trait_token.text()) | 66 | &ctx.sema, |
67 | .filter_map( | 67 | current_crate, |
68 | |candidate: either::Either<hir::ModuleDef, hir::MacroDef>| match candidate { | 68 | trait_token.text().to_string(), |
69 | either::Either::Left(hir::ModuleDef::Trait(trait_)) => Some(trait_), | 69 | ) |
70 | _ => None, | 70 | .filter_map(|candidate: either::Either<hir::ModuleDef, hir::MacroDef>| match candidate { |
71 | }, | 71 | either::Either::Left(hir::ModuleDef::Trait(trait_)) => Some(trait_), |
72 | ) | 72 | _ => None, |
73 | .flat_map(|trait_| { | 73 | }) |
74 | current_module | 74 | .flat_map(|trait_| { |
75 | .find_use_path(ctx.sema.db, hir::ModuleDef::Trait(trait_)) | 75 | current_module |
76 | .as_ref() | 76 | .find_use_path(ctx.sema.db, hir::ModuleDef::Trait(trait_)) |
77 | .map(mod_path_to_ast) | 77 | .as_ref() |
78 | .zip(Some(trait_)) | 78 | .map(mod_path_to_ast) |
79 | }); | 79 | .zip(Some(trait_)) |
80 | }); | ||
80 | 81 | ||
81 | let mut no_traits_found = true; | 82 | let mut no_traits_found = true; |
82 | for (trait_path, trait_) in found_traits.inspect(|_| no_traits_found = false) { | 83 | for (trait_path, trait_) in found_traits.inspect(|_| no_traits_found = false) { |
diff --git a/crates/assists/src/lib.rs b/crates/assists/src/lib.rs index 6e736ccb3..fdec886e9 100644 --- a/crates/assists/src/lib.rs +++ b/crates/assists/src/lib.rs | |||
@@ -73,45 +73,32 @@ pub struct Assist { | |||
73 | /// Target ranges are used to sort assists: the smaller the target range, | 73 | /// Target ranges are used to sort assists: the smaller the target range, |
74 | /// the more specific assist is, and so it should be sorted first. | 74 | /// the more specific assist is, and so it should be sorted first. |
75 | pub target: TextRange, | 75 | pub target: TextRange, |
76 | } | 76 | /// Computing source change sometimes is much more costly then computing the |
77 | 77 | /// other fields. Additionally, the actual change is not required to show | |
78 | #[derive(Debug, Clone)] | 78 | /// the lightbulb UI, it only is needed when the user tries to apply an |
79 | pub struct ResolvedAssist { | 79 | /// assist. So, we compute it lazily: the API allow requesting assists with |
80 | pub assist: Assist, | 80 | /// or without source change. We could (and in fact, used to) distinguish |
81 | pub source_change: SourceChange, | 81 | /// between resolved and unresolved assists at the type level, but this is |
82 | /// cumbersome, especially if you want to embed an assist into another data | ||
83 | /// structure, such as a diagnostic. | ||
84 | pub source_change: Option<SourceChange>, | ||
82 | } | 85 | } |
83 | 86 | ||
84 | impl Assist { | 87 | impl Assist { |
85 | /// Return all the assists applicable at the given position. | 88 | /// Return all the assists applicable at the given position. |
86 | /// | 89 | pub fn get( |
87 | /// Assists are returned in the "unresolved" state, that is only labels are | ||
88 | /// returned, without actual edits. | ||
89 | pub fn unresolved(db: &RootDatabase, config: &AssistConfig, range: FileRange) -> Vec<Assist> { | ||
90 | let sema = Semantics::new(db); | ||
91 | let ctx = AssistContext::new(sema, config, range); | ||
92 | let mut acc = Assists::new_unresolved(&ctx); | ||
93 | handlers::all().iter().for_each(|handler| { | ||
94 | handler(&mut acc, &ctx); | ||
95 | }); | ||
96 | acc.finish_unresolved() | ||
97 | } | ||
98 | |||
99 | /// Return all the assists applicable at the given position. | ||
100 | /// | ||
101 | /// Assists are returned in the "resolved" state, that is with edit fully | ||
102 | /// computed. | ||
103 | pub fn resolved( | ||
104 | db: &RootDatabase, | 90 | db: &RootDatabase, |
105 | config: &AssistConfig, | 91 | config: &AssistConfig, |
92 | resolve: bool, | ||
106 | range: FileRange, | 93 | range: FileRange, |
107 | ) -> Vec<ResolvedAssist> { | 94 | ) -> Vec<Assist> { |
108 | let sema = Semantics::new(db); | 95 | let sema = Semantics::new(db); |
109 | let ctx = AssistContext::new(sema, config, range); | 96 | let ctx = AssistContext::new(sema, config, range); |
110 | let mut acc = Assists::new_resolved(&ctx); | 97 | let mut acc = Assists::new(&ctx, resolve); |
111 | handlers::all().iter().for_each(|handler| { | 98 | handlers::all().iter().for_each(|handler| { |
112 | handler(&mut acc, &ctx); | 99 | handler(&mut acc, &ctx); |
113 | }); | 100 | }); |
114 | acc.finish_resolved() | 101 | acc.finish() |
115 | } | 102 | } |
116 | } | 103 | } |
117 | 104 | ||
@@ -129,6 +116,7 @@ mod handlers { | |||
129 | mod convert_integer_literal; | 116 | mod convert_integer_literal; |
130 | mod early_return; | 117 | mod early_return; |
131 | mod expand_glob_import; | 118 | mod expand_glob_import; |
119 | mod extract_module_to_file; | ||
132 | mod extract_struct_from_enum_variant; | 120 | mod extract_struct_from_enum_variant; |
133 | mod extract_variable; | 121 | mod extract_variable; |
134 | mod fill_match_arms; | 122 | mod fill_match_arms; |
@@ -179,6 +167,7 @@ mod handlers { | |||
179 | convert_integer_literal::convert_integer_literal, | 167 | convert_integer_literal::convert_integer_literal, |
180 | early_return::convert_to_guarded_return, | 168 | early_return::convert_to_guarded_return, |
181 | expand_glob_import::expand_glob_import, | 169 | expand_glob_import::expand_glob_import, |
170 | extract_module_to_file::extract_module_to_file, | ||
182 | extract_struct_from_enum_variant::extract_struct_from_enum_variant, | 171 | extract_struct_from_enum_variant::extract_struct_from_enum_variant, |
183 | extract_variable::extract_variable, | 172 | extract_variable::extract_variable, |
184 | fill_match_arms::fill_match_arms, | 173 | fill_match_arms::fill_match_arms, |
diff --git a/crates/assists/src/tests.rs b/crates/assists/src/tests.rs index 709a34d03..21e448fb8 100644 --- a/crates/assists/src/tests.rs +++ b/crates/assists/src/tests.rs | |||
@@ -2,6 +2,7 @@ mod generated; | |||
2 | 2 | ||
3 | use hir::Semantics; | 3 | use hir::Semantics; |
4 | use ide_db::base_db::{fixture::WithFixture, FileId, FileRange, SourceDatabaseExt}; | 4 | use ide_db::base_db::{fixture::WithFixture, FileId, FileRange, SourceDatabaseExt}; |
5 | use ide_db::source_change::FileSystemEdit; | ||
5 | use ide_db::RootDatabase; | 6 | use ide_db::RootDatabase; |
6 | use syntax::TextRange; | 7 | use syntax::TextRange; |
7 | use test_utils::{assert_eq_text, extract_offset, extract_range}; | 8 | use test_utils::{assert_eq_text, extract_offset, extract_range}; |
@@ -47,25 +48,29 @@ fn check_doc_test(assist_id: &str, before: &str, after: &str) { | |||
47 | let before = db.file_text(file_id).to_string(); | 48 | let before = db.file_text(file_id).to_string(); |
48 | let frange = FileRange { file_id, range: selection.into() }; | 49 | let frange = FileRange { file_id, range: selection.into() }; |
49 | 50 | ||
50 | let mut assist = Assist::resolved(&db, &AssistConfig::default(), frange) | 51 | let assist = Assist::get(&db, &AssistConfig::default(), true, frange) |
51 | .into_iter() | 52 | .into_iter() |
52 | .find(|assist| assist.assist.id.0 == assist_id) | 53 | .find(|assist| assist.id.0 == assist_id) |
53 | .unwrap_or_else(|| { | 54 | .unwrap_or_else(|| { |
54 | panic!( | 55 | panic!( |
55 | "\n\nAssist is not applicable: {}\nAvailable assists: {}", | 56 | "\n\nAssist is not applicable: {}\nAvailable assists: {}", |
56 | assist_id, | 57 | assist_id, |
57 | Assist::resolved(&db, &AssistConfig::default(), frange) | 58 | Assist::get(&db, &AssistConfig::default(), false, frange) |
58 | .into_iter() | 59 | .into_iter() |
59 | .map(|assist| assist.assist.id.0) | 60 | .map(|assist| assist.id.0) |
60 | .collect::<Vec<_>>() | 61 | .collect::<Vec<_>>() |
61 | .join(", ") | 62 | .join(", ") |
62 | ) | 63 | ) |
63 | }); | 64 | }); |
64 | 65 | ||
65 | let actual = { | 66 | let actual = { |
66 | let change = assist.source_change.source_file_edits.pop().unwrap(); | 67 | let source_change = assist.source_change.unwrap(); |
67 | let mut actual = before; | 68 | let mut actual = before; |
68 | change.edit.apply(&mut actual); | 69 | for source_file_edit in source_change.source_file_edits { |
70 | if source_file_edit.file_id == file_id { | ||
71 | source_file_edit.edit.apply(&mut actual) | ||
72 | } | ||
73 | } | ||
69 | actual | 74 | actual |
70 | }; | 75 | }; |
71 | assert_eq_text!(&after, &actual); | 76 | assert_eq_text!(&after, &actual); |
@@ -86,20 +91,21 @@ fn check(handler: Handler, before: &str, expected: ExpectedResult, assist_label: | |||
86 | let sema = Semantics::new(&db); | 91 | let sema = Semantics::new(&db); |
87 | let config = AssistConfig::default(); | 92 | let config = AssistConfig::default(); |
88 | let ctx = AssistContext::new(sema, &config, frange); | 93 | let ctx = AssistContext::new(sema, &config, frange); |
89 | let mut acc = Assists::new_resolved(&ctx); | 94 | let mut acc = Assists::new(&ctx, true); |
90 | handler(&mut acc, &ctx); | 95 | handler(&mut acc, &ctx); |
91 | let mut res = acc.finish_resolved(); | 96 | let mut res = acc.finish(); |
92 | 97 | ||
93 | let assist = match assist_label { | 98 | let assist = match assist_label { |
94 | Some(label) => res.into_iter().find(|resolved| resolved.assist.label == label), | 99 | Some(label) => res.into_iter().find(|resolved| resolved.label == label), |
95 | None => res.pop(), | 100 | None => res.pop(), |
96 | }; | 101 | }; |
97 | 102 | ||
98 | match (assist, expected) { | 103 | match (assist, expected) { |
99 | (Some(assist), ExpectedResult::After(after)) => { | 104 | (Some(assist), ExpectedResult::After(after)) => { |
100 | let mut source_change = assist.source_change; | 105 | let mut source_change = assist.source_change.unwrap(); |
101 | assert!(!source_change.source_file_edits.is_empty()); | 106 | assert!(!source_change.source_file_edits.is_empty()); |
102 | let skip_header = source_change.source_file_edits.len() == 1; | 107 | let skip_header = source_change.source_file_edits.len() == 1 |
108 | && source_change.file_system_edits.len() == 0; | ||
103 | source_change.source_file_edits.sort_by_key(|it| it.file_id); | 109 | source_change.source_file_edits.sort_by_key(|it| it.file_id); |
104 | 110 | ||
105 | let mut buf = String::new(); | 111 | let mut buf = String::new(); |
@@ -115,10 +121,25 @@ fn check(handler: Handler, before: &str, expected: ExpectedResult, assist_label: | |||
115 | buf.push_str(&text); | 121 | buf.push_str(&text); |
116 | } | 122 | } |
117 | 123 | ||
124 | for file_system_edit in source_change.file_system_edits.clone() { | ||
125 | match file_system_edit { | ||
126 | FileSystemEdit::CreateFile { dst, initial_contents } => { | ||
127 | let sr = db.file_source_root(dst.anchor); | ||
128 | let sr = db.source_root(sr); | ||
129 | let mut base = sr.path_for_file(&dst.anchor).unwrap().clone(); | ||
130 | base.pop(); | ||
131 | let created_file_path = format!("{}{}", base.to_string(), &dst.path[1..]); | ||
132 | format_to!(buf, "//- {}\n", created_file_path); | ||
133 | buf.push_str(&initial_contents); | ||
134 | } | ||
135 | _ => (), | ||
136 | } | ||
137 | } | ||
138 | |||
118 | assert_eq_text!(after, &buf); | 139 | assert_eq_text!(after, &buf); |
119 | } | 140 | } |
120 | (Some(assist), ExpectedResult::Target(target)) => { | 141 | (Some(assist), ExpectedResult::Target(target)) => { |
121 | let range = assist.assist.target; | 142 | let range = assist.target; |
122 | assert_eq_text!(&text_without_caret[range], target); | 143 | assert_eq_text!(&text_without_caret[range], target); |
123 | } | 144 | } |
124 | (Some(_), ExpectedResult::NotApplicable) => panic!("assist should not be applicable!"), | 145 | (Some(_), ExpectedResult::NotApplicable) => panic!("assist should not be applicable!"), |
@@ -135,14 +156,11 @@ fn assist_order_field_struct() { | |||
135 | let (before_cursor_pos, before) = extract_offset(before); | 156 | let (before_cursor_pos, before) = extract_offset(before); |
136 | let (db, file_id) = with_single_file(&before); | 157 | let (db, file_id) = with_single_file(&before); |
137 | let frange = FileRange { file_id, range: TextRange::empty(before_cursor_pos) }; | 158 | let frange = FileRange { file_id, range: TextRange::empty(before_cursor_pos) }; |
138 | let assists = Assist::resolved(&db, &AssistConfig::default(), frange); | 159 | let assists = Assist::get(&db, &AssistConfig::default(), false, frange); |
139 | let mut assists = assists.iter(); | 160 | let mut assists = assists.iter(); |
140 | 161 | ||
141 | assert_eq!( | 162 | assert_eq!(assists.next().expect("expected assist").label, "Change visibility to pub(crate)"); |
142 | assists.next().expect("expected assist").assist.label, | 163 | assert_eq!(assists.next().expect("expected assist").label, "Add `#[derive]`"); |
143 | "Change visibility to pub(crate)" | ||
144 | ); | ||
145 | assert_eq!(assists.next().expect("expected assist").assist.label, "Add `#[derive]`"); | ||
146 | } | 164 | } |
147 | 165 | ||
148 | #[test] | 166 | #[test] |
@@ -158,11 +176,11 @@ fn assist_order_if_expr() { | |||
158 | let (range, before) = extract_range(before); | 176 | let (range, before) = extract_range(before); |
159 | let (db, file_id) = with_single_file(&before); | 177 | let (db, file_id) = with_single_file(&before); |
160 | let frange = FileRange { file_id, range }; | 178 | let frange = FileRange { file_id, range }; |
161 | let assists = Assist::resolved(&db, &AssistConfig::default(), frange); | 179 | let assists = Assist::get(&db, &AssistConfig::default(), false, frange); |
162 | let mut assists = assists.iter(); | 180 | let mut assists = assists.iter(); |
163 | 181 | ||
164 | assert_eq!(assists.next().expect("expected assist").assist.label, "Extract into variable"); | 182 | assert_eq!(assists.next().expect("expected assist").label, "Extract into variable"); |
165 | assert_eq!(assists.next().expect("expected assist").assist.label, "Replace with match"); | 183 | assert_eq!(assists.next().expect("expected assist").label, "Replace with match"); |
166 | } | 184 | } |
167 | 185 | ||
168 | #[test] | 186 | #[test] |
@@ -183,27 +201,27 @@ fn assist_filter_works() { | |||
183 | let mut cfg = AssistConfig::default(); | 201 | let mut cfg = AssistConfig::default(); |
184 | cfg.allowed = Some(vec![AssistKind::Refactor]); | 202 | cfg.allowed = Some(vec![AssistKind::Refactor]); |
185 | 203 | ||
186 | let assists = Assist::resolved(&db, &cfg, frange); | 204 | let assists = Assist::get(&db, &cfg, false, frange); |
187 | let mut assists = assists.iter(); | 205 | let mut assists = assists.iter(); |
188 | 206 | ||
189 | assert_eq!(assists.next().expect("expected assist").assist.label, "Extract into variable"); | 207 | assert_eq!(assists.next().expect("expected assist").label, "Extract into variable"); |
190 | assert_eq!(assists.next().expect("expected assist").assist.label, "Replace with match"); | 208 | assert_eq!(assists.next().expect("expected assist").label, "Replace with match"); |
191 | } | 209 | } |
192 | 210 | ||
193 | { | 211 | { |
194 | let mut cfg = AssistConfig::default(); | 212 | let mut cfg = AssistConfig::default(); |
195 | cfg.allowed = Some(vec![AssistKind::RefactorExtract]); | 213 | cfg.allowed = Some(vec![AssistKind::RefactorExtract]); |
196 | let assists = Assist::resolved(&db, &cfg, frange); | 214 | let assists = Assist::get(&db, &cfg, false, frange); |
197 | assert_eq!(assists.len(), 1); | 215 | assert_eq!(assists.len(), 1); |
198 | 216 | ||
199 | let mut assists = assists.iter(); | 217 | let mut assists = assists.iter(); |
200 | assert_eq!(assists.next().expect("expected assist").assist.label, "Extract into variable"); | 218 | assert_eq!(assists.next().expect("expected assist").label, "Extract into variable"); |
201 | } | 219 | } |
202 | 220 | ||
203 | { | 221 | { |
204 | let mut cfg = AssistConfig::default(); | 222 | let mut cfg = AssistConfig::default(); |
205 | cfg.allowed = Some(vec![AssistKind::QuickFix]); | 223 | cfg.allowed = Some(vec![AssistKind::QuickFix]); |
206 | let assists = Assist::resolved(&db, &cfg, frange); | 224 | let assists = Assist::get(&db, &cfg, false, frange); |
207 | assert!(assists.is_empty(), "All asserts but quickfixes should be filtered out"); | 225 | assert!(assists.is_empty(), "All asserts but quickfixes should be filtered out"); |
208 | } | 226 | } |
209 | } | 227 | } |
diff --git a/crates/assists/src/tests/generated.rs b/crates/assists/src/tests/generated.rs index cc7c4a343..d3dfe24e7 100644 --- a/crates/assists/src/tests/generated.rs +++ b/crates/assists/src/tests/generated.rs | |||
@@ -42,26 +42,26 @@ fn doctest_add_impl_default_members() { | |||
42 | "add_impl_default_members", | 42 | "add_impl_default_members", |
43 | r#####" | 43 | r#####" |
44 | trait Trait { | 44 | trait Trait { |
45 | Type X; | 45 | type X; |
46 | fn foo(&self); | 46 | fn foo(&self); |
47 | fn bar(&self) {} | 47 | fn bar(&self) {} |
48 | } | 48 | } |
49 | 49 | ||
50 | impl Trait for () { | 50 | impl Trait for () { |
51 | Type X = (); | 51 | type X = (); |
52 | fn foo(&self) {}<|> | 52 | fn foo(&self) {}<|> |
53 | 53 | ||
54 | } | 54 | } |
55 | "#####, | 55 | "#####, |
56 | r#####" | 56 | r#####" |
57 | trait Trait { | 57 | trait Trait { |
58 | Type X; | 58 | type X; |
59 | fn foo(&self); | 59 | fn foo(&self); |
60 | fn bar(&self) {} | 60 | fn bar(&self) {} |
61 | } | 61 | } |
62 | 62 | ||
63 | impl Trait for () { | 63 | impl Trait for () { |
64 | Type X = (); | 64 | type X = (); |
65 | fn foo(&self) {} | 65 | fn foo(&self) {} |
66 | 66 | ||
67 | $0fn bar(&self) {} | 67 | $0fn bar(&self) {} |
@@ -76,7 +76,7 @@ fn doctest_add_impl_missing_members() { | |||
76 | "add_impl_missing_members", | 76 | "add_impl_missing_members", |
77 | r#####" | 77 | r#####" |
78 | trait Trait<T> { | 78 | trait Trait<T> { |
79 | Type X; | 79 | type X; |
80 | fn foo(&self) -> T; | 80 | fn foo(&self) -> T; |
81 | fn bar(&self) {} | 81 | fn bar(&self) {} |
82 | } | 82 | } |
@@ -87,14 +87,16 @@ impl Trait<u32> for () {<|> | |||
87 | "#####, | 87 | "#####, |
88 | r#####" | 88 | r#####" |
89 | trait Trait<T> { | 89 | trait Trait<T> { |
90 | Type X; | 90 | type X; |
91 | fn foo(&self) -> T; | 91 | fn foo(&self) -> T; |
92 | fn bar(&self) {} | 92 | fn bar(&self) {} |
93 | } | 93 | } |
94 | 94 | ||
95 | impl Trait<u32> for () { | 95 | impl Trait<u32> for () { |
96 | $0type X; | ||
97 | |||
96 | fn foo(&self) -> u32 { | 98 | fn foo(&self) -> u32 { |
97 | ${0:todo!()} | 99 | todo!() |
98 | } | 100 | } |
99 | } | 101 | } |
100 | "#####, | 102 | "#####, |
@@ -236,6 +238,21 @@ fn qux(bar: Bar, baz: Baz) {} | |||
236 | } | 238 | } |
237 | 239 | ||
238 | #[test] | 240 | #[test] |
241 | fn doctest_extract_module_to_file() { | ||
242 | check_doc_test( | ||
243 | "extract_module_to_file", | ||
244 | r#####" | ||
245 | mod foo {<|> | ||
246 | fn t() {} | ||
247 | } | ||
248 | "#####, | ||
249 | r#####" | ||
250 | mod foo; | ||
251 | "#####, | ||
252 | ) | ||
253 | } | ||
254 | |||
255 | #[test] | ||
239 | fn doctest_extract_struct_from_enum_variant() { | 256 | fn doctest_extract_struct_from_enum_variant() { |
240 | check_doc_test( | 257 | check_doc_test( |
241 | "extract_struct_from_enum_variant", | 258 | "extract_struct_from_enum_variant", |
diff --git a/crates/assists/src/utils.rs b/crates/assists/src/utils.rs index f2cacf7c8..d41084b59 100644 --- a/crates/assists/src/utils.rs +++ b/crates/assists/src/utils.rs | |||
@@ -232,7 +232,13 @@ fn invert_special_case(expr: &ast::Expr) -> Option<ast::Expr> { | |||
232 | }; | 232 | }; |
233 | Some(make::expr_method_call(receiver, method, arg_list)) | 233 | Some(make::expr_method_call(receiver, method, arg_list)) |
234 | } | 234 | } |
235 | ast::Expr::PrefixExpr(pe) if pe.op_kind()? == ast::PrefixOp::Not => pe.expr(), | 235 | ast::Expr::PrefixExpr(pe) if pe.op_kind()? == ast::PrefixOp::Not => { |
236 | if let ast::Expr::ParenExpr(parexpr) = pe.expr()? { | ||
237 | parexpr.expr() | ||
238 | } else { | ||
239 | pe.expr() | ||
240 | } | ||
241 | } | ||
236 | // FIXME: | 242 | // FIXME: |
237 | // ast::Expr::Literal(true | false ) | 243 | // ast::Expr::Literal(true | false ) |
238 | _ => None, | 244 | _ => None, |
diff --git a/crates/assists/src/utils/import_assets.rs b/crates/assists/src/utils/import_assets.rs index ff5c0e78e..4ce82c1ba 100644 --- a/crates/assists/src/utils/import_assets.rs +++ b/crates/assists/src/utils/import_assets.rs | |||
@@ -179,25 +179,24 @@ impl ImportAssets { | |||
179 | } | 179 | } |
180 | }; | 180 | }; |
181 | 181 | ||
182 | let mut res = | 182 | let mut res = imports_locator::find_exact_imports( |
183 | imports_locator::find_exact_imports(sema, current_crate, &self.get_search_query()) | 183 | sema, |
184 | .filter_map(filter) | 184 | current_crate, |
185 | .filter_map(|candidate| { | 185 | self.get_search_query().to_string(), |
186 | let item: hir::ItemInNs = candidate.either(Into::into, Into::into); | 186 | ) |
187 | if let Some(prefix_kind) = prefixed { | 187 | .filter_map(filter) |
188 | self.module_with_name_to_import.find_use_path_prefixed( | 188 | .filter_map(|candidate| { |
189 | db, | 189 | let item: hir::ItemInNs = candidate.either(Into::into, Into::into); |
190 | item, | 190 | if let Some(prefix_kind) = prefixed { |
191 | prefix_kind, | 191 | self.module_with_name_to_import.find_use_path_prefixed(db, item, prefix_kind) |
192 | ) | 192 | } else { |
193 | } else { | 193 | self.module_with_name_to_import.find_use_path(db, item) |
194 | self.module_with_name_to_import.find_use_path(db, item) | 194 | } |
195 | } | 195 | .map(|path| (path, item)) |
196 | .map(|path| (path, item)) | 196 | }) |
197 | }) | 197 | .filter(|(use_path, _)| use_path.len() > 1) |
198 | .filter(|(use_path, _)| use_path.len() > 1) | 198 | .take(20) |
199 | .take(20) | 199 | .collect::<Vec<_>>(); |
200 | .collect::<Vec<_>>(); | ||
201 | res.sort_by_key(|(path, _)| path.clone()); | 200 | res.sort_by_key(|(path, _)| path.clone()); |
202 | res | 201 | res |
203 | } | 202 | } |
diff --git a/crates/base_db/src/input.rs b/crates/base_db/src/input.rs index cda5e57dc..9567bcc42 100644 --- a/crates/base_db/src/input.rs +++ b/crates/base_db/src/input.rs | |||
@@ -6,12 +6,12 @@ | |||
6 | //! actual IO. See `vfs` and `project_model` in the `rust-analyzer` crate for how | 6 | //! actual IO. See `vfs` and `project_model` in the `rust-analyzer` crate for how |
7 | //! actual IO is done and lowered to input. | 7 | //! actual IO is done and lowered to input. |
8 | 8 | ||
9 | use std::{fmt, iter::FromIterator, ops, str::FromStr, sync::Arc}; | 9 | use std::{fmt, iter::FromIterator, ops, panic::RefUnwindSafe, str::FromStr, sync::Arc}; |
10 | 10 | ||
11 | use cfg::CfgOptions; | 11 | use cfg::CfgOptions; |
12 | use rustc_hash::{FxHashMap, FxHashSet}; | 12 | use rustc_hash::{FxHashMap, FxHashSet}; |
13 | use syntax::SmolStr; | 13 | use syntax::SmolStr; |
14 | use tt::TokenExpander; | 14 | use tt::{ExpansionError, Subtree}; |
15 | use vfs::{file_set::FileSet, FileId, VfsPath}; | 15 | use vfs::{file_set::FileSet, FileId, VfsPath}; |
16 | 16 | ||
17 | /// Files are grouped into source roots. A source root is a directory on the | 17 | /// Files are grouped into source roots. A source root is a directory on the |
@@ -150,11 +150,20 @@ pub enum ProcMacroKind { | |||
150 | Attr, | 150 | Attr, |
151 | } | 151 | } |
152 | 152 | ||
153 | pub trait ProcMacroExpander: fmt::Debug + Send + Sync + RefUnwindSafe { | ||
154 | fn expand( | ||
155 | &self, | ||
156 | subtree: &Subtree, | ||
157 | attrs: Option<&Subtree>, | ||
158 | env: &Env, | ||
159 | ) -> Result<Subtree, ExpansionError>; | ||
160 | } | ||
161 | |||
153 | #[derive(Debug, Clone)] | 162 | #[derive(Debug, Clone)] |
154 | pub struct ProcMacro { | 163 | pub struct ProcMacro { |
155 | pub name: SmolStr, | 164 | pub name: SmolStr, |
156 | pub kind: ProcMacroKind, | 165 | pub kind: ProcMacroKind, |
157 | pub expander: Arc<dyn TokenExpander>, | 166 | pub expander: Arc<dyn ProcMacroExpander>, |
158 | } | 167 | } |
159 | 168 | ||
160 | impl Eq for ProcMacro {} | 169 | impl Eq for ProcMacro {} |
@@ -413,6 +422,10 @@ impl Env { | |||
413 | pub fn get(&self, env: &str) -> Option<String> { | 422 | pub fn get(&self, env: &str) -> Option<String> { |
414 | self.entries.get(env).cloned() | 423 | self.entries.get(env).cloned() |
415 | } | 424 | } |
425 | |||
426 | pub fn iter(&self) -> impl Iterator<Item = (&str, &str)> { | ||
427 | self.entries.iter().map(|(k, v)| (k.as_str(), v.as_str())) | ||
428 | } | ||
416 | } | 429 | } |
417 | 430 | ||
418 | #[derive(Debug)] | 431 | #[derive(Debug)] |
diff --git a/crates/base_db/src/lib.rs b/crates/base_db/src/lib.rs index 595f28ada..5f77a0b1f 100644 --- a/crates/base_db/src/lib.rs +++ b/crates/base_db/src/lib.rs | |||
@@ -14,7 +14,7 @@ pub use crate::{ | |||
14 | change::Change, | 14 | change::Change, |
15 | input::{ | 15 | input::{ |
16 | CrateData, CrateDisplayName, CrateGraph, CrateId, CrateName, Dependency, Edition, Env, | 16 | CrateData, CrateDisplayName, CrateGraph, CrateId, CrateName, Dependency, Edition, Env, |
17 | ProcMacro, ProcMacroId, ProcMacroKind, SourceRoot, SourceRootId, | 17 | ProcMacro, ProcMacroExpander, ProcMacroId, ProcMacroKind, SourceRoot, SourceRootId, |
18 | }, | 18 | }, |
19 | }; | 19 | }; |
20 | pub use salsa; | 20 | pub use salsa; |
diff --git a/crates/completion/Cargo.toml b/crates/completion/Cargo.toml index 35e169a28..78e93e78e 100644 --- a/crates/completion/Cargo.toml +++ b/crates/completion/Cargo.toml | |||
@@ -10,7 +10,7 @@ edition = "2018" | |||
10 | doctest = false | 10 | doctest = false |
11 | 11 | ||
12 | [dependencies] | 12 | [dependencies] |
13 | itertools = "0.9.0" | 13 | itertools = "0.10.0" |
14 | log = "0.4.8" | 14 | log = "0.4.8" |
15 | rustc-hash = "1.1.0" | 15 | rustc-hash = "1.1.0" |
16 | either = "1.6.1" | 16 | either = "1.6.1" |
diff --git a/crates/completion/src/completions.rs b/crates/completion/src/completions.rs index 1ef6b5f48..d9fe13485 100644 --- a/crates/completion/src/completions.rs +++ b/crates/completion/src/completions.rs | |||
@@ -19,9 +19,14 @@ use hir::{ModPath, ScopeDef, Type}; | |||
19 | use crate::{ | 19 | use crate::{ |
20 | item::Builder, | 20 | item::Builder, |
21 | render::{ | 21 | render::{ |
22 | const_::render_const, enum_variant::render_variant, function::render_fn, | 22 | const_::render_const, |
23 | macro_::render_macro, render_field, render_resolution, render_tuple_field, | 23 | enum_variant::render_variant, |
24 | type_alias::render_type_alias, RenderContext, | 24 | function::render_fn, |
25 | macro_::render_macro, | ||
26 | pattern::{render_struct_pat, render_variant_pat}, | ||
27 | render_field, render_resolution, render_tuple_field, | ||
28 | type_alias::render_type_alias, | ||
29 | RenderContext, | ||
25 | }, | 30 | }, |
26 | CompletionContext, CompletionItem, | 31 | CompletionContext, CompletionItem, |
27 | }; | 32 | }; |
@@ -105,6 +110,28 @@ impl Completions { | |||
105 | self.add(item) | 110 | self.add(item) |
106 | } | 111 | } |
107 | 112 | ||
113 | pub(crate) fn add_variant_pat( | ||
114 | &mut self, | ||
115 | ctx: &CompletionContext, | ||
116 | variant: hir::Variant, | ||
117 | local_name: Option<hir::Name>, | ||
118 | ) { | ||
119 | if let Some(item) = render_variant_pat(RenderContext::new(ctx), variant, local_name) { | ||
120 | self.add(item); | ||
121 | } | ||
122 | } | ||
123 | |||
124 | pub(crate) fn add_struct_pat( | ||
125 | &mut self, | ||
126 | ctx: &CompletionContext, | ||
127 | strukt: hir::Struct, | ||
128 | local_name: Option<hir::Name>, | ||
129 | ) { | ||
130 | if let Some(item) = render_struct_pat(RenderContext::new(ctx), strukt, local_name) { | ||
131 | self.add(item); | ||
132 | } | ||
133 | } | ||
134 | |||
108 | pub(crate) fn add_const(&mut self, ctx: &CompletionContext, constant: hir::Const) { | 135 | pub(crate) fn add_const(&mut self, ctx: &CompletionContext, constant: hir::Const) { |
109 | if let Some(item) = render_const(RenderContext::new(ctx), constant) { | 136 | if let Some(item) = render_const(RenderContext::new(ctx), constant) { |
110 | self.add(item); | 137 | self.add(item); |
diff --git a/crates/completion/src/completions/attribute.rs b/crates/completion/src/completions/attribute.rs index 19ce2482f..8695eed39 100644 --- a/crates/completion/src/completions/attribute.rs +++ b/crates/completion/src/completions/attribute.rs | |||
@@ -234,7 +234,7 @@ fn parse_comma_sep_input(derive_input: ast::TokenTree) -> Result<FxHashSet<Strin | |||
234 | current_derive = String::new(); | 234 | current_derive = String::new(); |
235 | } | 235 | } |
236 | } else { | 236 | } else { |
237 | current_derive.push_str(token.to_string().trim()); | 237 | current_derive.push_str(token.text().trim()); |
238 | } | 238 | } |
239 | } | 239 | } |
240 | 240 | ||
diff --git a/crates/completion/src/completions/pattern.rs b/crates/completion/src/completions/pattern.rs index 4d56731ec..eee31098d 100644 --- a/crates/completion/src/completions/pattern.rs +++ b/crates/completion/src/completions/pattern.rs | |||
@@ -2,9 +2,9 @@ | |||
2 | 2 | ||
3 | use crate::{CompletionContext, Completions}; | 3 | use crate::{CompletionContext, Completions}; |
4 | 4 | ||
5 | /// Completes constats and paths in patterns. | 5 | /// Completes constants and paths in patterns. |
6 | pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) { | 6 | pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) { |
7 | if !(ctx.is_pat_binding_or_const || ctx.is_irrefutable_let_pat_binding) { | 7 | if !(ctx.is_pat_binding_or_const || ctx.is_irrefutable_pat_binding) { |
8 | return; | 8 | return; |
9 | } | 9 | } |
10 | if ctx.record_pat_syntax.is_some() { | 10 | if ctx.record_pat_syntax.is_some() { |
@@ -15,20 +15,21 @@ pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) { | |||
15 | // suggest variants + auto-imports | 15 | // suggest variants + auto-imports |
16 | ctx.scope.process_all_names(&mut |name, res| { | 16 | ctx.scope.process_all_names(&mut |name, res| { |
17 | let add_resolution = match &res { | 17 | let add_resolution = match &res { |
18 | hir::ScopeDef::ModuleDef(def) => { | 18 | hir::ScopeDef::ModuleDef(def) => match def { |
19 | if ctx.is_irrefutable_let_pat_binding { | 19 | hir::ModuleDef::Adt(hir::Adt::Struct(strukt)) => { |
20 | matches!(def, hir::ModuleDef::Adt(hir::Adt::Struct(_))) | 20 | acc.add_struct_pat(ctx, strukt.clone(), Some(name.clone())); |
21 | } else { | 21 | true |
22 | matches!( | ||
23 | def, | ||
24 | hir::ModuleDef::Adt(hir::Adt::Enum(..)) | ||
25 | | hir::ModuleDef::Adt(hir::Adt::Struct(..)) | ||
26 | | hir::ModuleDef::Variant(..) | ||
27 | | hir::ModuleDef::Const(..) | ||
28 | | hir::ModuleDef::Module(..) | ||
29 | ) | ||
30 | } | 22 | } |
31 | } | 23 | hir::ModuleDef::Variant(variant) if !ctx.is_irrefutable_pat_binding => { |
24 | acc.add_variant_pat(ctx, variant.clone(), Some(name.clone())); | ||
25 | true | ||
26 | } | ||
27 | hir::ModuleDef::Adt(hir::Adt::Enum(..)) | ||
28 | | hir::ModuleDef::Variant(..) | ||
29 | | hir::ModuleDef::Const(..) | ||
30 | | hir::ModuleDef::Module(..) => !ctx.is_irrefutable_pat_binding, | ||
31 | _ => false, | ||
32 | }, | ||
32 | hir::ScopeDef::MacroDef(_) => true, | 33 | hir::ScopeDef::MacroDef(_) => true, |
33 | _ => false, | 34 | _ => false, |
34 | }; | 35 | }; |
@@ -42,13 +43,21 @@ pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) { | |||
42 | mod tests { | 43 | mod tests { |
43 | use expect_test::{expect, Expect}; | 44 | use expect_test::{expect, Expect}; |
44 | 45 | ||
45 | use crate::{test_utils::completion_list, CompletionKind}; | 46 | use crate::{ |
47 | test_utils::{check_edit, completion_list}, | ||
48 | CompletionKind, | ||
49 | }; | ||
46 | 50 | ||
47 | fn check(ra_fixture: &str, expect: Expect) { | 51 | fn check(ra_fixture: &str, expect: Expect) { |
48 | let actual = completion_list(ra_fixture, CompletionKind::Reference); | 52 | let actual = completion_list(ra_fixture, CompletionKind::Reference); |
49 | expect.assert_eq(&actual) | 53 | expect.assert_eq(&actual) |
50 | } | 54 | } |
51 | 55 | ||
56 | fn check_snippet(ra_fixture: &str, expect: Expect) { | ||
57 | let actual = completion_list(ra_fixture, CompletionKind::Snippet); | ||
58 | expect.assert_eq(&actual) | ||
59 | } | ||
60 | |||
52 | #[test] | 61 | #[test] |
53 | fn completes_enum_variants_and_modules() { | 62 | fn completes_enum_variants_and_modules() { |
54 | check( | 63 | check( |
@@ -69,7 +78,7 @@ fn foo() { | |||
69 | en E | 78 | en E |
70 | ct Z | 79 | ct Z |
71 | st Bar | 80 | st Bar |
72 | ev X () | 81 | ev X |
73 | md m | 82 | md m |
74 | "#]], | 83 | "#]], |
75 | ); | 84 | ); |
@@ -114,4 +123,139 @@ fn foo() { | |||
114 | "#]], | 123 | "#]], |
115 | ); | 124 | ); |
116 | } | 125 | } |
126 | |||
127 | #[test] | ||
128 | fn completes_in_param() { | ||
129 | check( | ||
130 | r#" | ||
131 | enum E { X } | ||
132 | |||
133 | static FOO: E = E::X; | ||
134 | struct Bar { f: u32 } | ||
135 | |||
136 | fn foo(<|>) { | ||
137 | } | ||
138 | "#, | ||
139 | expect![[r#" | ||
140 | st Bar | ||
141 | "#]], | ||
142 | ); | ||
143 | } | ||
144 | |||
145 | #[test] | ||
146 | fn completes_pat_in_let() { | ||
147 | check_snippet( | ||
148 | r#" | ||
149 | struct Bar { f: u32 } | ||
150 | |||
151 | fn foo() { | ||
152 | let <|> | ||
153 | } | ||
154 | "#, | ||
155 | expect![[r#" | ||
156 | bn Bar Bar { f$1 }$0 | ||
157 | "#]], | ||
158 | ); | ||
159 | } | ||
160 | |||
161 | #[test] | ||
162 | fn completes_param_pattern() { | ||
163 | check_snippet( | ||
164 | r#" | ||
165 | struct Foo { bar: String, baz: String } | ||
166 | struct Bar(String, String); | ||
167 | struct Baz; | ||
168 | fn outer(<|>) {} | ||
169 | "#, | ||
170 | expect![[r#" | ||
171 | bn Foo Foo { bar$1, baz$2 }: Foo$0 | ||
172 | bn Bar Bar($1, $2): Bar$0 | ||
173 | "#]], | ||
174 | ) | ||
175 | } | ||
176 | |||
177 | #[test] | ||
178 | fn completes_let_pattern() { | ||
179 | check_snippet( | ||
180 | r#" | ||
181 | struct Foo { bar: String, baz: String } | ||
182 | struct Bar(String, String); | ||
183 | struct Baz; | ||
184 | fn outer() { | ||
185 | let <|> | ||
186 | } | ||
187 | "#, | ||
188 | expect![[r#" | ||
189 | bn Foo Foo { bar$1, baz$2 }$0 | ||
190 | bn Bar Bar($1, $2)$0 | ||
191 | "#]], | ||
192 | ) | ||
193 | } | ||
194 | |||
195 | #[test] | ||
196 | fn completes_refutable_pattern() { | ||
197 | check_snippet( | ||
198 | r#" | ||
199 | struct Foo { bar: i32, baz: i32 } | ||
200 | struct Bar(String, String); | ||
201 | struct Baz; | ||
202 | fn outer() { | ||
203 | match () { | ||
204 | <|> | ||
205 | } | ||
206 | } | ||
207 | "#, | ||
208 | expect![[r#" | ||
209 | bn Foo Foo { bar$1, baz$2 }$0 | ||
210 | bn Bar Bar($1, $2)$0 | ||
211 | "#]], | ||
212 | ) | ||
213 | } | ||
214 | |||
215 | #[test] | ||
216 | fn omits_private_fields_pat() { | ||
217 | check_snippet( | ||
218 | r#" | ||
219 | mod foo { | ||
220 | pub struct Foo { pub bar: i32, baz: i32 } | ||
221 | pub struct Bar(pub String, String); | ||
222 | pub struct Invisible(String, String); | ||
223 | } | ||
224 | use foo::*; | ||
225 | |||
226 | fn outer() { | ||
227 | match () { | ||
228 | <|> | ||
229 | } | ||
230 | } | ||
231 | "#, | ||
232 | expect![[r#" | ||
233 | bn Foo Foo { bar$1, .. }$0 | ||
234 | bn Bar Bar($1, ..)$0 | ||
235 | "#]], | ||
236 | ) | ||
237 | } | ||
238 | |||
239 | #[test] | ||
240 | fn only_shows_ident_completion() { | ||
241 | check_edit( | ||
242 | "Foo", | ||
243 | r#" | ||
244 | struct Foo(i32); | ||
245 | fn main() { | ||
246 | match Foo(92) { | ||
247 | <|>(92) => (), | ||
248 | } | ||
249 | } | ||
250 | "#, | ||
251 | r#" | ||
252 | struct Foo(i32); | ||
253 | fn main() { | ||
254 | match Foo(92) { | ||
255 | Foo(92) => (), | ||
256 | } | ||
257 | } | ||
258 | "#, | ||
259 | ); | ||
260 | } | ||
117 | } | 261 | } |
diff --git a/crates/completion/src/completions/postfix.rs b/crates/completion/src/completions/postfix.rs index d6db82a93..3883d6d21 100644 --- a/crates/completion/src/completions/postfix.rs +++ b/crates/completion/src/completions/postfix.rs | |||
@@ -502,7 +502,7 @@ fn main() { | |||
502 | #[test] | 502 | #[test] |
503 | fn postfix_completion_for_format_like_strings() { | 503 | fn postfix_completion_for_format_like_strings() { |
504 | check_edit( | 504 | check_edit( |
505 | "fmt", | 505 | "format", |
506 | r#"fn main() { "{some_var:?}".<|> }"#, | 506 | r#"fn main() { "{some_var:?}".<|> }"#, |
507 | r#"fn main() { format!("{:?}", some_var) }"#, | 507 | r#"fn main() { format!("{:?}", some_var) }"#, |
508 | ); | 508 | ); |
diff --git a/crates/completion/src/completions/postfix/format_like.rs b/crates/completion/src/completions/postfix/format_like.rs index 88ba86acb..def4b13fb 100644 --- a/crates/completion/src/completions/postfix/format_like.rs +++ b/crates/completion/src/completions/postfix/format_like.rs | |||
@@ -22,7 +22,7 @@ use syntax::ast::{self, AstToken}; | |||
22 | 22 | ||
23 | /// Mapping ("postfix completion item" => "macro to use") | 23 | /// Mapping ("postfix completion item" => "macro to use") |
24 | static KINDS: &[(&str, &str)] = &[ | 24 | static KINDS: &[(&str, &str)] = &[ |
25 | ("fmt", "format!"), | 25 | ("format", "format!"), |
26 | ("panic", "panic!"), | 26 | ("panic", "panic!"), |
27 | ("println", "println!"), | 27 | ("println", "println!"), |
28 | ("eprintln", "eprintln!"), | 28 | ("eprintln", "eprintln!"), |
@@ -108,7 +108,8 @@ impl FormatStrParser { | |||
108 | // "{MyStruct { val_a: 0, val_b: 1 }}". | 108 | // "{MyStruct { val_a: 0, val_b: 1 }}". |
109 | let mut inexpr_open_count = 0; | 109 | let mut inexpr_open_count = 0; |
110 | 110 | ||
111 | for chr in self.input.chars() { | 111 | let mut chars = self.input.chars().peekable(); |
112 | while let Some(chr) = chars.next() { | ||
112 | match (self.state, chr) { | 113 | match (self.state, chr) { |
113 | (State::NotExpr, '{') => { | 114 | (State::NotExpr, '{') => { |
114 | self.output.push(chr); | 115 | self.output.push(chr); |
@@ -157,6 +158,11 @@ impl FormatStrParser { | |||
157 | inexpr_open_count -= 1; | 158 | inexpr_open_count -= 1; |
158 | } | 159 | } |
159 | } | 160 | } |
161 | (State::Expr, ':') if chars.peek().copied() == Some(':') => { | ||
162 | // path seperator | ||
163 | current_expr.push_str("::"); | ||
164 | chars.next(); | ||
165 | } | ||
160 | (State::Expr, ':') => { | 166 | (State::Expr, ':') => { |
161 | if inexpr_open_count == 0 { | 167 | if inexpr_open_count == 0 { |
162 | // We're outside of braces, thus assume that it's a specifier, like "{Some(value):?}" | 168 | // We're outside of braces, thus assume that it's a specifier, like "{Some(value):?}" |
@@ -249,6 +255,9 @@ mod tests { | |||
249 | expect![["{:?}; SomeStruct { val_a: 0, val_b: 1 }"]], | 255 | expect![["{:?}; SomeStruct { val_a: 0, val_b: 1 }"]], |
250 | ), | 256 | ), |
251 | ("{ 2 + 2 }", expect![["{}; 2 + 2"]]), | 257 | ("{ 2 + 2 }", expect![["{}; 2 + 2"]]), |
258 | ("{strsim::jaro_winkle(a)}", expect![["{}; strsim::jaro_winkle(a)"]]), | ||
259 | ("{foo::bar::baz()}", expect![["{}; foo::bar::baz()"]]), | ||
260 | ("{foo::bar():?}", expect![["{:?}; foo::bar()"]]), | ||
252 | ]; | 261 | ]; |
253 | 262 | ||
254 | for (input, output) in test_vector { | 263 | for (input, output) in test_vector { |
diff --git a/crates/completion/src/completions/qualified_path.rs b/crates/completion/src/completions/qualified_path.rs index 1300f00b2..882c4dcbc 100644 --- a/crates/completion/src/completions/qualified_path.rs +++ b/crates/completion/src/completions/qualified_path.rs | |||
@@ -118,6 +118,12 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon | |||
118 | _ => return, | 118 | _ => return, |
119 | }; | 119 | }; |
120 | 120 | ||
121 | if let Some(Adt::Enum(e)) = ty.as_adt() { | ||
122 | for variant in e.variants(ctx.db) { | ||
123 | acc.add_enum_variant(ctx, variant, None); | ||
124 | } | ||
125 | } | ||
126 | |||
121 | let traits_in_scope = ctx.scope.traits_in_scope(); | 127 | let traits_in_scope = ctx.scope.traits_in_scope(); |
122 | let mut seen = FxHashSet::default(); | 128 | let mut seen = FxHashSet::default(); |
123 | ty.iterate_path_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, item| { | 129 | ty.iterate_path_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, item| { |
@@ -752,4 +758,27 @@ fn main() { | |||
752 | "#]], | 758 | "#]], |
753 | ); | 759 | ); |
754 | } | 760 | } |
761 | |||
762 | #[test] | ||
763 | fn completes_self_enum() { | ||
764 | check( | ||
765 | r#" | ||
766 | enum Foo { | ||
767 | Bar, | ||
768 | Baz, | ||
769 | } | ||
770 | |||
771 | impl Foo { | ||
772 | fn foo(self) { | ||
773 | Self::<|> | ||
774 | } | ||
775 | } | ||
776 | "#, | ||
777 | expect![[r#" | ||
778 | ev Bar () | ||
779 | ev Baz () | ||
780 | me foo(…) fn foo(self) | ||
781 | "#]], | ||
782 | ); | ||
783 | } | ||
755 | } | 784 | } |
diff --git a/crates/completion/src/completions/unqualified_path.rs b/crates/completion/src/completions/unqualified_path.rs index 099ffb4d4..81a6d00e2 100644 --- a/crates/completion/src/completions/unqualified_path.rs +++ b/crates/completion/src/completions/unqualified_path.rs | |||
@@ -1,5 +1,7 @@ | |||
1 | //! Completion of names from the current scope, e.g. locals and imported items. | 1 | //! Completion of names from the current scope, e.g. locals and imported items. |
2 | 2 | ||
3 | use std::iter; | ||
4 | |||
3 | use either::Either; | 5 | use either::Either; |
4 | use hir::{Adt, ModPath, ModuleDef, ScopeDef, Type}; | 6 | use hir::{Adt, ModPath, ModuleDef, ScopeDef, Type}; |
5 | use ide_db::helpers::insert_use::ImportScope; | 7 | use ide_db::helpers::insert_use::ImportScope; |
@@ -50,7 +52,9 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC | |||
50 | } | 52 | } |
51 | 53 | ||
52 | fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext, ty: &Type) { | 54 | fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext, ty: &Type) { |
53 | if let Some(Adt::Enum(enum_data)) = ty.as_adt() { | 55 | if let Some(Adt::Enum(enum_data)) = |
56 | iter::successors(Some(ty.clone()), |ty| ty.remove_ref()).last().and_then(|ty| ty.as_adt()) | ||
57 | { | ||
54 | let variants = enum_data.variants(ctx.db); | 58 | let variants = enum_data.variants(ctx.db); |
55 | 59 | ||
56 | let module = if let Some(module) = ctx.scope.module() { | 60 | let module = if let Some(module) = ctx.scope.module() { |
@@ -97,8 +101,9 @@ fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext, ty: &T | |||
97 | // | 101 | // |
98 | // .Fuzzy search details | 102 | // .Fuzzy search details |
99 | // | 103 | // |
100 | // To avoid an excessive amount of the results returned, completion input is checked for inclusion in the identifiers only | 104 | // To avoid an excessive amount of the results returned, completion input is checked for inclusion in the names only |
101 | // (i.e. in `HashMap` in the `std::collections::HashMap` path), also not in the module indentifiers. | 105 | // (i.e. in `HashMap` in the `std::collections::HashMap` path). |
106 | // For the same reasons, avoids searching for any imports for inputs with their length less that 2 symbols. | ||
102 | // | 107 | // |
103 | // .Merge Behavior | 108 | // .Merge Behavior |
104 | // | 109 | // |
@@ -122,15 +127,20 @@ fn fuzzy_completion(acc: &mut Completions, ctx: &CompletionContext) -> Option<() | |||
122 | let _p = profile::span("fuzzy_completion"); | 127 | let _p = profile::span("fuzzy_completion"); |
123 | let potential_import_name = ctx.token.to_string(); | 128 | let potential_import_name = ctx.token.to_string(); |
124 | 129 | ||
130 | if potential_import_name.len() < 2 { | ||
131 | return None; | ||
132 | } | ||
133 | |||
125 | let current_module = ctx.scope.module()?; | 134 | let current_module = ctx.scope.module()?; |
126 | let anchor = ctx.name_ref_syntax.as_ref()?; | 135 | let anchor = ctx.name_ref_syntax.as_ref()?; |
127 | let import_scope = ImportScope::find_insert_use_container(anchor.syntax(), &ctx.sema)?; | 136 | let import_scope = ImportScope::find_insert_use_container(anchor.syntax(), &ctx.sema)?; |
128 | 137 | ||
138 | let user_input_lowercased = potential_import_name.to_lowercase(); | ||
129 | let mut all_mod_paths = imports_locator::find_similar_imports( | 139 | let mut all_mod_paths = imports_locator::find_similar_imports( |
130 | &ctx.sema, | 140 | &ctx.sema, |
131 | ctx.krate?, | 141 | ctx.krate?, |
132 | Some(100), | 142 | Some(40), |
133 | &potential_import_name, | 143 | potential_import_name, |
134 | true, | 144 | true, |
135 | ) | 145 | ) |
136 | .filter_map(|import_candidate| { | 146 | .filter_map(|import_candidate| { |
@@ -146,7 +156,6 @@ fn fuzzy_completion(acc: &mut Completions, ctx: &CompletionContext) -> Option<() | |||
146 | .filter(|(mod_path, _)| mod_path.len() > 1) | 156 | .filter(|(mod_path, _)| mod_path.len() > 1) |
147 | .collect::<Vec<_>>(); | 157 | .collect::<Vec<_>>(); |
148 | 158 | ||
149 | let user_input_lowercased = potential_import_name.to_lowercase(); | ||
150 | all_mod_paths.sort_by_cached_key(|(mod_path, _)| { | 159 | all_mod_paths.sort_by_cached_key(|(mod_path, _)| { |
151 | compute_fuzzy_completion_order_key(mod_path, &user_input_lowercased) | 160 | compute_fuzzy_completion_order_key(mod_path, &user_input_lowercased) |
152 | }); | 161 | }); |
@@ -701,6 +710,7 @@ fn main() { <|> } | |||
701 | "#]], | 710 | "#]], |
702 | ); | 711 | ); |
703 | } | 712 | } |
713 | |||
704 | #[test] | 714 | #[test] |
705 | fn completes_enum_variant_matcharm() { | 715 | fn completes_enum_variant_matcharm() { |
706 | check( | 716 | check( |
@@ -722,6 +732,26 @@ fn main() { | |||
722 | } | 732 | } |
723 | 733 | ||
724 | #[test] | 734 | #[test] |
735 | fn completes_enum_variant_matcharm_ref() { | ||
736 | check( | ||
737 | r#" | ||
738 | enum Foo { Bar, Baz, Quux } | ||
739 | |||
740 | fn main() { | ||
741 | let foo = Foo::Quux; | ||
742 | match &foo { Qu<|> } | ||
743 | } | ||
744 | "#, | ||
745 | expect![[r#" | ||
746 | ev Foo::Bar () | ||
747 | ev Foo::Baz () | ||
748 | ev Foo::Quux () | ||
749 | en Foo | ||
750 | "#]], | ||
751 | ) | ||
752 | } | ||
753 | |||
754 | #[test] | ||
725 | fn completes_enum_variant_iflet() { | 755 | fn completes_enum_variant_iflet() { |
726 | check( | 756 | check( |
727 | r#" | 757 | r#" |
diff --git a/crates/completion/src/context.rs b/crates/completion/src/context.rs index 5cd11cf77..41de324d8 100644 --- a/crates/completion/src/context.rs +++ b/crates/completion/src/context.rs | |||
@@ -51,7 +51,7 @@ pub(crate) struct CompletionContext<'a> { | |||
51 | /// If a name-binding or reference to a const in a pattern. | 51 | /// If a name-binding or reference to a const in a pattern. |
52 | /// Irrefutable patterns (like let) are excluded. | 52 | /// Irrefutable patterns (like let) are excluded. |
53 | pub(super) is_pat_binding_or_const: bool, | 53 | pub(super) is_pat_binding_or_const: bool, |
54 | pub(super) is_irrefutable_let_pat_binding: bool, | 54 | pub(super) is_irrefutable_pat_binding: bool, |
55 | /// A single-indent path, like `foo`. `::foo` should not be considered a trivial path. | 55 | /// A single-indent path, like `foo`. `::foo` should not be considered a trivial path. |
56 | pub(super) is_trivial_path: bool, | 56 | pub(super) is_trivial_path: bool, |
57 | /// If not a trivial path, the prefix (qualifier). | 57 | /// If not a trivial path, the prefix (qualifier). |
@@ -147,7 +147,7 @@ impl<'a> CompletionContext<'a> { | |||
147 | active_parameter: ActiveParameter::at(db, position), | 147 | active_parameter: ActiveParameter::at(db, position), |
148 | is_param: false, | 148 | is_param: false, |
149 | is_pat_binding_or_const: false, | 149 | is_pat_binding_or_const: false, |
150 | is_irrefutable_let_pat_binding: false, | 150 | is_irrefutable_pat_binding: false, |
151 | is_trivial_path: false, | 151 | is_trivial_path: false, |
152 | path_qual: None, | 152 | path_qual: None, |
153 | after_if: false, | 153 | after_if: false, |
@@ -327,14 +327,19 @@ impl<'a> CompletionContext<'a> { | |||
327 | if bind_pat.syntax().parent().and_then(ast::RecordPatFieldList::cast).is_some() { | 327 | if bind_pat.syntax().parent().and_then(ast::RecordPatFieldList::cast).is_some() { |
328 | self.is_pat_binding_or_const = false; | 328 | self.is_pat_binding_or_const = false; |
329 | } | 329 | } |
330 | if let Some(let_stmt) = bind_pat.syntax().ancestors().find_map(ast::LetStmt::cast) { | 330 | if let Some(Some(pat)) = bind_pat.syntax().ancestors().find_map(|node| { |
331 | if let Some(pat) = let_stmt.pat() { | 331 | match_ast! { |
332 | if pat.syntax().text_range().contains_range(bind_pat.syntax().text_range()) | 332 | match node { |
333 | { | 333 | ast::LetStmt(it) => Some(it.pat()), |
334 | self.is_pat_binding_or_const = false; | 334 | ast::Param(it) => Some(it.pat()), |
335 | self.is_irrefutable_let_pat_binding = true; | 335 | _ => None, |
336 | } | 336 | } |
337 | } | 337 | } |
338 | }) { | ||
339 | if pat.syntax().text_range().contains_range(bind_pat.syntax().text_range()) { | ||
340 | self.is_pat_binding_or_const = false; | ||
341 | self.is_irrefutable_pat_binding = true; | ||
342 | } | ||
338 | } | 343 | } |
339 | } | 344 | } |
340 | if is_node::<ast::Param>(name.syntax()) { | 345 | if is_node::<ast::Param>(name.syntax()) { |
diff --git a/crates/completion/src/lib.rs b/crates/completion/src/lib.rs index 8e27bb153..c57d05bbe 100644 --- a/crates/completion/src/lib.rs +++ b/crates/completion/src/lib.rs | |||
@@ -137,7 +137,7 @@ pub fn resolve_completion_edits( | |||
137 | config: &CompletionConfig, | 137 | config: &CompletionConfig, |
138 | position: FilePosition, | 138 | position: FilePosition, |
139 | full_import_path: &str, | 139 | full_import_path: &str, |
140 | imported_name: &str, | 140 | imported_name: String, |
141 | ) -> Option<Vec<TextEdit>> { | 141 | ) -> Option<Vec<TextEdit>> { |
142 | let ctx = CompletionContext::new(db, position, config)?; | 142 | let ctx = CompletionContext::new(db, position, config)?; |
143 | let anchor = ctx.name_ref_syntax.as_ref()?; | 143 | let anchor = ctx.name_ref_syntax.as_ref()?; |
diff --git a/crates/completion/src/render.rs b/crates/completion/src/render.rs index 1092a4825..1ba7201a1 100644 --- a/crates/completion/src/render.rs +++ b/crates/completion/src/render.rs | |||
@@ -5,6 +5,7 @@ pub(crate) mod macro_; | |||
5 | pub(crate) mod function; | 5 | pub(crate) mod function; |
6 | pub(crate) mod enum_variant; | 6 | pub(crate) mod enum_variant; |
7 | pub(crate) mod const_; | 7 | pub(crate) mod const_; |
8 | pub(crate) mod pattern; | ||
8 | pub(crate) mod type_alias; | 9 | pub(crate) mod type_alias; |
9 | 10 | ||
10 | mod builder_ext; | 11 | mod builder_ext; |
@@ -159,6 +160,12 @@ impl<'a> Render<'a> { | |||
159 | let item = render_fn(self.ctx, import_to_add, Some(local_name), *func); | 160 | let item = render_fn(self.ctx, import_to_add, Some(local_name), *func); |
160 | return Some(item); | 161 | return Some(item); |
161 | } | 162 | } |
163 | ScopeDef::ModuleDef(Variant(_)) | ||
164 | if self.ctx.completion.is_pat_binding_or_const | ||
165 | | self.ctx.completion.is_irrefutable_pat_binding => | ||
166 | { | ||
167 | CompletionItemKind::EnumVariant | ||
168 | } | ||
162 | ScopeDef::ModuleDef(Variant(var)) => { | 169 | ScopeDef::ModuleDef(Variant(var)) => { |
163 | let item = render_variant(self.ctx, import_to_add, Some(local_name), *var, None); | 170 | let item = render_variant(self.ctx, import_to_add, Some(local_name), *var, None); |
164 | return Some(item); | 171 | return Some(item); |
diff --git a/crates/completion/src/render/builder_ext.rs b/crates/completion/src/render/builder_ext.rs index ce8718bd5..d053a988b 100644 --- a/crates/completion/src/render/builder_ext.rs +++ b/crates/completion/src/render/builder_ext.rs | |||
@@ -34,7 +34,6 @@ impl Builder { | |||
34 | return false; | 34 | return false; |
35 | } | 35 | } |
36 | if ctx.is_pattern_call { | 36 | if ctx.is_pattern_call { |
37 | mark::hit!(dont_duplicate_pattern_parens); | ||
38 | return false; | 37 | return false; |
39 | } | 38 | } |
40 | if ctx.is_call { | 39 | if ctx.is_call { |
diff --git a/crates/completion/src/render/enum_variant.rs b/crates/completion/src/render/enum_variant.rs index 7176fd9b3..732e139ec 100644 --- a/crates/completion/src/render/enum_variant.rs +++ b/crates/completion/src/render/enum_variant.rs | |||
@@ -126,50 +126,5 @@ fn main() -> Option<i32> { | |||
126 | } | 126 | } |
127 | "#, | 127 | "#, |
128 | ); | 128 | ); |
129 | check_edit( | ||
130 | "Some", | ||
131 | r#" | ||
132 | enum Option<T> { Some(T), None } | ||
133 | use Option::*; | ||
134 | fn main(value: Option<i32>) { | ||
135 | match value { | ||
136 | Som<|> | ||
137 | } | ||
138 | } | ||
139 | "#, | ||
140 | r#" | ||
141 | enum Option<T> { Some(T), None } | ||
142 | use Option::*; | ||
143 | fn main(value: Option<i32>) { | ||
144 | match value { | ||
145 | Some($0) | ||
146 | } | ||
147 | } | ||
148 | "#, | ||
149 | ); | ||
150 | } | ||
151 | |||
152 | #[test] | ||
153 | fn dont_duplicate_pattern_parens() { | ||
154 | mark::check!(dont_duplicate_pattern_parens); | ||
155 | check_edit( | ||
156 | "Var", | ||
157 | r#" | ||
158 | enum E { Var(i32) } | ||
159 | fn main() { | ||
160 | match E::Var(92) { | ||
161 | E::<|>(92) => (), | ||
162 | } | ||
163 | } | ||
164 | "#, | ||
165 | r#" | ||
166 | enum E { Var(i32) } | ||
167 | fn main() { | ||
168 | match E::Var(92) { | ||
169 | E::Var(92) => (), | ||
170 | } | ||
171 | } | ||
172 | "#, | ||
173 | ); | ||
174 | } | 129 | } |
175 | } | 130 | } |
diff --git a/crates/completion/src/render/pattern.rs b/crates/completion/src/render/pattern.rs new file mode 100644 index 000000000..a3b6a3cac --- /dev/null +++ b/crates/completion/src/render/pattern.rs | |||
@@ -0,0 +1,148 @@ | |||
1 | //! Renderer for patterns. | ||
2 | |||
3 | use hir::{db::HirDatabase, HasAttrs, HasVisibility, Name, StructKind}; | ||
4 | use itertools::Itertools; | ||
5 | |||
6 | use crate::{ | ||
7 | config::SnippetCap, item::CompletionKind, render::RenderContext, CompletionItem, | ||
8 | CompletionItemKind, | ||
9 | }; | ||
10 | |||
11 | fn visible_fields( | ||
12 | ctx: &RenderContext<'_>, | ||
13 | fields: &[hir::Field], | ||
14 | item: impl HasAttrs, | ||
15 | ) -> Option<(Vec<hir::Field>, bool)> { | ||
16 | let module = ctx.completion.scope.module()?; | ||
17 | let n_fields = fields.len(); | ||
18 | let fields = fields | ||
19 | .into_iter() | ||
20 | .filter(|field| field.is_visible_from(ctx.db(), module)) | ||
21 | .copied() | ||
22 | .collect::<Vec<_>>(); | ||
23 | |||
24 | let fields_omitted = | ||
25 | n_fields - fields.len() > 0 || item.attrs(ctx.db()).by_key("non_exhaustive").exists(); | ||
26 | Some((fields, fields_omitted)) | ||
27 | } | ||
28 | |||
29 | pub(crate) fn render_struct_pat( | ||
30 | ctx: RenderContext<'_>, | ||
31 | strukt: hir::Struct, | ||
32 | local_name: Option<Name>, | ||
33 | ) -> Option<CompletionItem> { | ||
34 | let _p = profile::span("render_struct_pat"); | ||
35 | |||
36 | let fields = strukt.fields(ctx.db()); | ||
37 | let (visible_fields, fields_omitted) = visible_fields(&ctx, &fields, strukt)?; | ||
38 | |||
39 | if visible_fields.is_empty() { | ||
40 | // Matching a struct without matching its fields is pointless, unlike matching a Variant without its fields | ||
41 | return None; | ||
42 | } | ||
43 | |||
44 | let name = local_name.unwrap_or_else(|| strukt.name(ctx.db())).to_string(); | ||
45 | let pat = render_pat(&ctx, &name, strukt.kind(ctx.db()), &visible_fields, fields_omitted)?; | ||
46 | |||
47 | Some(build_completion(ctx, name, pat, strukt)) | ||
48 | } | ||
49 | |||
50 | pub(crate) fn render_variant_pat( | ||
51 | ctx: RenderContext<'_>, | ||
52 | variant: hir::Variant, | ||
53 | local_name: Option<Name>, | ||
54 | ) -> Option<CompletionItem> { | ||
55 | let _p = profile::span("render_variant_pat"); | ||
56 | |||
57 | let fields = variant.fields(ctx.db()); | ||
58 | let (visible_fields, fields_omitted) = visible_fields(&ctx, &fields, variant)?; | ||
59 | |||
60 | let name = local_name.unwrap_or_else(|| variant.name(ctx.db())).to_string(); | ||
61 | let pat = render_pat(&ctx, &name, variant.kind(ctx.db()), &visible_fields, fields_omitted)?; | ||
62 | |||
63 | Some(build_completion(ctx, name, pat, variant)) | ||
64 | } | ||
65 | |||
66 | fn build_completion( | ||
67 | ctx: RenderContext<'_>, | ||
68 | name: String, | ||
69 | pat: String, | ||
70 | item: impl HasAttrs + Copy, | ||
71 | ) -> CompletionItem { | ||
72 | let completion = CompletionItem::new(CompletionKind::Snippet, ctx.source_range(), name) | ||
73 | .kind(CompletionItemKind::Binding) | ||
74 | .set_documentation(ctx.docs(item)) | ||
75 | .set_deprecated(ctx.is_deprecated(item)) | ||
76 | .detail(&pat); | ||
77 | let completion = if let Some(snippet_cap) = ctx.snippet_cap() { | ||
78 | completion.insert_snippet(snippet_cap, pat) | ||
79 | } else { | ||
80 | completion.insert_text(pat) | ||
81 | }; | ||
82 | completion.build() | ||
83 | } | ||
84 | |||
85 | fn render_pat( | ||
86 | ctx: &RenderContext<'_>, | ||
87 | name: &str, | ||
88 | kind: StructKind, | ||
89 | fields: &[hir::Field], | ||
90 | fields_omitted: bool, | ||
91 | ) -> Option<String> { | ||
92 | let mut pat = match kind { | ||
93 | StructKind::Tuple if ctx.snippet_cap().is_some() => { | ||
94 | render_tuple_as_pat(&fields, &name, fields_omitted) | ||
95 | } | ||
96 | StructKind::Record => { | ||
97 | render_record_as_pat(ctx.db(), ctx.snippet_cap(), &fields, &name, fields_omitted) | ||
98 | } | ||
99 | _ => return None, | ||
100 | }; | ||
101 | |||
102 | if ctx.completion.is_param { | ||
103 | pat.push(':'); | ||
104 | pat.push(' '); | ||
105 | pat.push_str(&name); | ||
106 | } | ||
107 | if ctx.snippet_cap().is_some() { | ||
108 | pat.push_str("$0"); | ||
109 | } | ||
110 | Some(pat) | ||
111 | } | ||
112 | |||
113 | fn render_record_as_pat( | ||
114 | db: &dyn HirDatabase, | ||
115 | snippet_cap: Option<SnippetCap>, | ||
116 | fields: &[hir::Field], | ||
117 | name: &str, | ||
118 | fields_omitted: bool, | ||
119 | ) -> String { | ||
120 | let fields = fields.iter(); | ||
121 | if snippet_cap.is_some() { | ||
122 | format!( | ||
123 | "{name} {{ {}{} }}", | ||
124 | fields | ||
125 | .enumerate() | ||
126 | .map(|(idx, field)| format!("{}${}", field.name(db), idx + 1)) | ||
127 | .format(", "), | ||
128 | if fields_omitted { ", .." } else { "" }, | ||
129 | name = name | ||
130 | ) | ||
131 | } else { | ||
132 | format!( | ||
133 | "{name} {{ {}{} }}", | ||
134 | fields.map(|field| field.name(db)).format(", "), | ||
135 | if fields_omitted { ", .." } else { "" }, | ||
136 | name = name | ||
137 | ) | ||
138 | } | ||
139 | } | ||
140 | |||
141 | fn render_tuple_as_pat(fields: &[hir::Field], name: &str, fields_omitted: bool) -> String { | ||
142 | format!( | ||
143 | "{name}({}{})", | ||
144 | fields.iter().enumerate().map(|(idx, _)| format!("${}", idx + 1)).format(", "), | ||
145 | if fields_omitted { ", .." } else { "" }, | ||
146 | name = name | ||
147 | ) | ||
148 | } | ||
diff --git a/crates/hir/Cargo.toml b/crates/hir/Cargo.toml index 6dc5ad63b..d4ea7327e 100644 --- a/crates/hir/Cargo.toml +++ b/crates/hir/Cargo.toml | |||
@@ -14,7 +14,7 @@ log = "0.4.8" | |||
14 | rustc-hash = "1.1.0" | 14 | rustc-hash = "1.1.0" |
15 | either = "1.5.3" | 15 | either = "1.5.3" |
16 | arrayvec = "0.5.1" | 16 | arrayvec = "0.5.1" |
17 | itertools = "0.9.0" | 17 | itertools = "0.10.0" |
18 | 18 | ||
19 | stdx = { path = "../stdx", version = "0.0.0" } | 19 | stdx = { path = "../stdx", version = "0.0.0" } |
20 | syntax = { path = "../syntax", version = "0.0.0" } | 20 | syntax = { path = "../syntax", version = "0.0.0" } |
diff --git a/crates/hir/src/code_model.rs b/crates/hir/src/code_model.rs index 73ca6ba9f..b7ded3478 100644 --- a/crates/hir/src/code_model.rs +++ b/crates/hir/src/code_model.rs | |||
@@ -9,7 +9,7 @@ use hir_def::{ | |||
9 | adt::StructKind, | 9 | adt::StructKind, |
10 | adt::VariantData, | 10 | adt::VariantData, |
11 | builtin_type::BuiltinType, | 11 | builtin_type::BuiltinType, |
12 | expr::{BindingAnnotation, Pat, PatId}, | 12 | expr::{BindingAnnotation, LabelId, Pat, PatId}, |
13 | import_map, | 13 | import_map, |
14 | item_tree::ItemTreeNode, | 14 | item_tree::ItemTreeNode, |
15 | lang_item::LangItemTarget, | 15 | lang_item::LangItemTarget, |
@@ -374,8 +374,6 @@ impl Module { | |||
374 | let crate_def_map = db.crate_def_map(self.id.krate); | 374 | let crate_def_map = db.crate_def_map(self.id.krate); |
375 | crate_def_map.add_diagnostics(db.upcast(), self.id.local_id, sink); | 375 | crate_def_map.add_diagnostics(db.upcast(), self.id.local_id, sink); |
376 | for decl in self.declarations(db) { | 376 | for decl in self.declarations(db) { |
377 | decl.diagnostics(db, sink); | ||
378 | |||
379 | match decl { | 377 | match decl { |
380 | crate::ModuleDef::Function(f) => f.diagnostics(db, sink), | 378 | crate::ModuleDef::Function(f) => f.diagnostics(db, sink), |
381 | crate::ModuleDef::Module(m) => { | 379 | crate::ModuleDef::Module(m) => { |
@@ -384,7 +382,9 @@ impl Module { | |||
384 | m.diagnostics(db, sink) | 382 | m.diagnostics(db, sink) |
385 | } | 383 | } |
386 | } | 384 | } |
387 | _ => (), | 385 | _ => { |
386 | decl.diagnostics(db, sink); | ||
387 | } | ||
388 | } | 388 | } |
389 | } | 389 | } |
390 | 390 | ||
@@ -511,6 +511,10 @@ impl Struct { | |||
511 | db.struct_data(self.id).repr.clone() | 511 | db.struct_data(self.id).repr.clone() |
512 | } | 512 | } |
513 | 513 | ||
514 | pub fn kind(self, db: &dyn HirDatabase) -> StructKind { | ||
515 | self.variant_data(db).kind() | ||
516 | } | ||
517 | |||
514 | fn variant_data(self, db: &dyn HirDatabase) -> Arc<VariantData> { | 518 | fn variant_data(self, db: &dyn HirDatabase) -> Arc<VariantData> { |
515 | db.struct_data(self.id).variant_data.clone() | 519 | db.struct_data(self.id).variant_data.clone() |
516 | } | 520 | } |
@@ -1202,6 +1206,34 @@ impl Local { | |||
1202 | } | 1206 | } |
1203 | 1207 | ||
1204 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] | 1208 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] |
1209 | pub struct Label { | ||
1210 | pub(crate) parent: DefWithBodyId, | ||
1211 | pub(crate) label_id: LabelId, | ||
1212 | } | ||
1213 | |||
1214 | impl Label { | ||
1215 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
1216 | self.parent(db).module(db) | ||
1217 | } | ||
1218 | |||
1219 | pub fn parent(self, _db: &dyn HirDatabase) -> DefWithBody { | ||
1220 | self.parent.into() | ||
1221 | } | ||
1222 | |||
1223 | pub fn name(self, db: &dyn HirDatabase) -> Name { | ||
1224 | let body = db.body(self.parent.into()); | ||
1225 | body[self.label_id].name.clone() | ||
1226 | } | ||
1227 | |||
1228 | pub fn source(self, db: &dyn HirDatabase) -> InFile<ast::Label> { | ||
1229 | let (_body, source_map) = db.body_with_source_map(self.parent.into()); | ||
1230 | let src = source_map.label_syntax(self.label_id); | ||
1231 | let root = src.file_syntax(db.upcast()); | ||
1232 | src.map(|ast| ast.to_node(&root)) | ||
1233 | } | ||
1234 | } | ||
1235 | |||
1236 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] | ||
1205 | pub enum GenericParam { | 1237 | pub enum GenericParam { |
1206 | TypeParam(TypeParam), | 1238 | TypeParam(TypeParam), |
1207 | LifetimeParam(LifetimeParam), | 1239 | LifetimeParam(LifetimeParam), |
diff --git a/crates/hir/src/from_id.rs b/crates/hir/src/from_id.rs index 8e0c571b8..a0792b9a6 100644 --- a/crates/hir/src/from_id.rs +++ b/crates/hir/src/from_id.rs | |||
@@ -4,12 +4,15 @@ | |||
4 | //! are splitting the hir. | 4 | //! are splitting the hir. |
5 | 5 | ||
6 | use hir_def::{ | 6 | use hir_def::{ |
7 | expr::PatId, item_scope::ItemInNs, AdtId, AssocItemId, DefWithBodyId, EnumVariantId, FieldId, | 7 | expr::{LabelId, PatId}, |
8 | GenericDefId, ModuleDefId, VariantId, | 8 | item_scope::ItemInNs, |
9 | AdtId, AssocItemId, DefWithBodyId, EnumVariantId, FieldId, GenericDefId, ModuleDefId, | ||
10 | VariantId, | ||
9 | }; | 11 | }; |
10 | 12 | ||
11 | use crate::{ | 13 | use crate::{ |
12 | Adt, AssocItem, DefWithBody, Field, GenericDef, Local, MacroDef, ModuleDef, Variant, VariantDef, | 14 | Adt, AssocItem, DefWithBody, Field, GenericDef, Label, Local, MacroDef, ModuleDef, Variant, |
15 | VariantDef, | ||
13 | }; | 16 | }; |
14 | 17 | ||
15 | macro_rules! from_id { | 18 | macro_rules! from_id { |
@@ -228,6 +231,12 @@ impl From<(DefWithBodyId, PatId)> for Local { | |||
228 | } | 231 | } |
229 | } | 232 | } |
230 | 233 | ||
234 | impl From<(DefWithBodyId, LabelId)> for Label { | ||
235 | fn from((parent, label_id): (DefWithBodyId, LabelId)) -> Self { | ||
236 | Label { parent, label_id } | ||
237 | } | ||
238 | } | ||
239 | |||
231 | impl From<MacroDef> for ItemInNs { | 240 | impl From<MacroDef> for ItemInNs { |
232 | fn from(macro_def: MacroDef) -> Self { | 241 | fn from(macro_def: MacroDef) -> Self { |
233 | ItemInNs::Macros(macro_def.into()) | 242 | ItemInNs::Macros(macro_def.into()) |
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index bdd270c58..7ac9fd507 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs | |||
@@ -35,8 +35,8 @@ pub use crate::{ | |||
35 | code_model::{ | 35 | code_model::{ |
36 | Access, Adt, AsAssocItem, AssocItem, AssocItemContainer, Callable, CallableKind, Const, | 36 | Access, Adt, AsAssocItem, AssocItem, AssocItemContainer, Callable, CallableKind, Const, |
37 | Crate, CrateDependency, DefWithBody, Enum, Field, FieldSource, Function, GenericDef, | 37 | Crate, CrateDependency, DefWithBody, Enum, Field, FieldSource, Function, GenericDef, |
38 | HasVisibility, Impl, LifetimeParam, Local, MacroDef, Module, ModuleDef, ScopeDef, Static, | 38 | HasVisibility, Impl, Label, LifetimeParam, Local, MacroDef, Module, ModuleDef, ScopeDef, |
39 | Struct, Trait, Type, TypeAlias, TypeParam, Union, Variant, VariantDef, | 39 | Static, Struct, Trait, Type, TypeAlias, TypeParam, Union, Variant, VariantDef, |
40 | }, | 40 | }, |
41 | has_source::HasSource, | 41 | has_source::HasSource, |
42 | semantics::{PathResolution, Semantics, SemanticsScope}, | 42 | semantics::{PathResolution, Semantics, SemanticsScope}, |
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index 25ebf73d8..67cd16e31 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs | |||
@@ -15,7 +15,7 @@ use itertools::Itertools; | |||
15 | use rustc_hash::{FxHashMap, FxHashSet}; | 15 | use rustc_hash::{FxHashMap, FxHashSet}; |
16 | use syntax::{ | 16 | use syntax::{ |
17 | algo::find_node_at_offset, | 17 | algo::find_node_at_offset, |
18 | ast::{self, GenericParamsOwner}, | 18 | ast::{self, GenericParamsOwner, LoopBodyOwner}, |
19 | match_ast, AstNode, SyntaxNode, SyntaxToken, TextSize, | 19 | match_ast, AstNode, SyntaxNode, SyntaxToken, TextSize, |
20 | }; | 20 | }; |
21 | 21 | ||
@@ -25,8 +25,8 @@ use crate::{ | |||
25 | diagnostics::Diagnostic, | 25 | diagnostics::Diagnostic, |
26 | semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx}, | 26 | semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx}, |
27 | source_analyzer::{resolve_hir_path, SourceAnalyzer}, | 27 | source_analyzer::{resolve_hir_path, SourceAnalyzer}, |
28 | AssocItem, Callable, Crate, Field, Function, HirFileId, Impl, InFile, LifetimeParam, Local, | 28 | AssocItem, Callable, Crate, Field, Function, HirFileId, Impl, InFile, Label, LifetimeParam, |
29 | MacroDef, Module, ModuleDef, Name, Path, ScopeDef, Trait, Type, TypeAlias, TypeParam, | 29 | Local, MacroDef, Module, ModuleDef, Name, Path, ScopeDef, Trait, Type, TypeAlias, TypeParam, |
30 | VariantDef, | 30 | VariantDef, |
31 | }; | 31 | }; |
32 | 32 | ||
@@ -182,6 +182,10 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> { | |||
182 | self.imp.resolve_lifetime_param(lifetime) | 182 | self.imp.resolve_lifetime_param(lifetime) |
183 | } | 183 | } |
184 | 184 | ||
185 | pub fn resolve_label(&self, lifetime: &ast::Lifetime) -> Option<Label> { | ||
186 | self.imp.resolve_label(lifetime) | ||
187 | } | ||
188 | |||
185 | pub fn type_of_expr(&self, expr: &ast::Expr) -> Option<Type> { | 189 | pub fn type_of_expr(&self, expr: &ast::Expr) -> Option<Type> { |
186 | self.imp.type_of_expr(expr) | 190 | self.imp.type_of_expr(expr) |
187 | } | 191 | } |
@@ -425,6 +429,28 @@ impl<'db> SemanticsImpl<'db> { | |||
425 | ToDef::to_def(self, src) | 429 | ToDef::to_def(self, src) |
426 | } | 430 | } |
427 | 431 | ||
432 | fn resolve_label(&self, lifetime: &ast::Lifetime) -> Option<Label> { | ||
433 | let text = lifetime.text(); | ||
434 | let label = lifetime.syntax().ancestors().find_map(|syn| { | ||
435 | let label = match_ast! { | ||
436 | match syn { | ||
437 | ast::ForExpr(it) => it.label(), | ||
438 | ast::WhileExpr(it) => it.label(), | ||
439 | ast::LoopExpr(it) => it.label(), | ||
440 | ast::EffectExpr(it) => it.label(), | ||
441 | _ => None, | ||
442 | } | ||
443 | }; | ||
444 | label.filter(|l| { | ||
445 | l.lifetime() | ||
446 | .and_then(|lt| lt.lifetime_ident_token()) | ||
447 | .map_or(false, |lt| lt.text() == text) | ||
448 | }) | ||
449 | })?; | ||
450 | let src = self.find_file(label.syntax().clone()).with_value(label); | ||
451 | ToDef::to_def(self, src) | ||
452 | } | ||
453 | |||
428 | fn type_of_expr(&self, expr: &ast::Expr) -> Option<Type> { | 454 | fn type_of_expr(&self, expr: &ast::Expr) -> Option<Type> { |
429 | self.analyze(expr.syntax()).type_of_expr(self.db, expr) | 455 | self.analyze(expr.syntax()).type_of_expr(self.db, expr) |
430 | } | 456 | } |
@@ -720,6 +746,7 @@ to_def_impls![ | |||
720 | (crate::LifetimeParam, ast::LifetimeParam, lifetime_param_to_def), | 746 | (crate::LifetimeParam, ast::LifetimeParam, lifetime_param_to_def), |
721 | (crate::MacroDef, ast::MacroRules, macro_rules_to_def), | 747 | (crate::MacroDef, ast::MacroRules, macro_rules_to_def), |
722 | (crate::Local, ast::IdentPat, bind_pat_to_def), | 748 | (crate::Local, ast::IdentPat, bind_pat_to_def), |
749 | (crate::Label, ast::Label, label_to_def), | ||
723 | ]; | 750 | ]; |
724 | 751 | ||
725 | fn find_root(node: &SyntaxNode) -> SyntaxNode { | 752 | fn find_root(node: &SyntaxNode) -> SyntaxNode { |
diff --git a/crates/hir/src/semantics/source_to_def.rs b/crates/hir/src/semantics/source_to_def.rs index 3efca5baa..424e6e8a9 100644 --- a/crates/hir/src/semantics/source_to_def.rs +++ b/crates/hir/src/semantics/source_to_def.rs | |||
@@ -4,7 +4,7 @@ use base_db::FileId; | |||
4 | use hir_def::{ | 4 | use hir_def::{ |
5 | child_by_source::ChildBySource, | 5 | child_by_source::ChildBySource, |
6 | dyn_map::DynMap, | 6 | dyn_map::DynMap, |
7 | expr::PatId, | 7 | expr::{LabelId, PatId}, |
8 | keys::{self, Key}, | 8 | keys::{self, Key}, |
9 | ConstId, DefWithBodyId, EnumId, EnumVariantId, FieldId, FunctionId, GenericDefId, ImplId, | 9 | ConstId, DefWithBodyId, EnumId, EnumVariantId, FieldId, FunctionId, GenericDefId, ImplId, |
10 | LifetimeParamId, ModuleId, StaticId, StructId, TraitId, TypeAliasId, TypeParamId, UnionId, | 10 | LifetimeParamId, ModuleId, StaticId, StructId, TraitId, TypeAliasId, TypeParamId, UnionId, |
@@ -108,12 +108,21 @@ impl SourceToDefCtx<'_, '_> { | |||
108 | &mut self, | 108 | &mut self, |
109 | src: InFile<ast::IdentPat>, | 109 | src: InFile<ast::IdentPat>, |
110 | ) -> Option<(DefWithBodyId, PatId)> { | 110 | ) -> Option<(DefWithBodyId, PatId)> { |
111 | let container = self.find_pat_container(src.as_ref().map(|it| it.syntax()))?; | 111 | let container = self.find_pat_or_label_container(src.as_ref().map(|it| it.syntax()))?; |
112 | let (_body, source_map) = self.db.body_with_source_map(container); | 112 | let (_body, source_map) = self.db.body_with_source_map(container); |
113 | let src = src.map(ast::Pat::from); | 113 | let src = src.map(ast::Pat::from); |
114 | let pat_id = source_map.node_pat(src.as_ref())?; | 114 | let pat_id = source_map.node_pat(src.as_ref())?; |
115 | Some((container, pat_id)) | 115 | Some((container, pat_id)) |
116 | } | 116 | } |
117 | pub(super) fn label_to_def( | ||
118 | &mut self, | ||
119 | src: InFile<ast::Label>, | ||
120 | ) -> Option<(DefWithBodyId, LabelId)> { | ||
121 | let container = self.find_pat_or_label_container(src.as_ref().map(|it| it.syntax()))?; | ||
122 | let (_body, source_map) = self.db.body_with_source_map(container); | ||
123 | let label_id = source_map.node_label(src.as_ref())?; | ||
124 | Some((container, label_id)) | ||
125 | } | ||
117 | 126 | ||
118 | fn to_def<Ast: AstNode + 'static, ID: Copy + 'static>( | 127 | fn to_def<Ast: AstNode + 'static, ID: Copy + 'static>( |
119 | &mut self, | 128 | &mut self, |
@@ -237,7 +246,7 @@ impl SourceToDefCtx<'_, '_> { | |||
237 | None | 246 | None |
238 | } | 247 | } |
239 | 248 | ||
240 | fn find_pat_container(&mut self, src: InFile<&SyntaxNode>) -> Option<DefWithBodyId> { | 249 | fn find_pat_or_label_container(&mut self, src: InFile<&SyntaxNode>) -> Option<DefWithBodyId> { |
241 | for container in src.cloned().ancestors_with_macros(self.db.upcast()).skip(1) { | 250 | for container in src.cloned().ancestors_with_macros(self.db.upcast()).skip(1) { |
242 | let res: DefWithBodyId = match_ast! { | 251 | let res: DefWithBodyId = match_ast! { |
243 | match (container.value) { | 252 | match (container.value) { |
diff --git a/crates/hir_def/Cargo.toml b/crates/hir_def/Cargo.toml index a88b5f57e..e8b581e2f 100644 --- a/crates/hir_def/Cargo.toml +++ b/crates/hir_def/Cargo.toml | |||
@@ -17,7 +17,7 @@ either = "1.5.3" | |||
17 | anymap = "0.12.1" | 17 | anymap = "0.12.1" |
18 | drop_bomb = "0.1.4" | 18 | drop_bomb = "0.1.4" |
19 | fst = { version = "0.4", default-features = false } | 19 | fst = { version = "0.4", default-features = false } |
20 | itertools = "0.9.0" | 20 | itertools = "0.10.0" |
21 | indexmap = "1.4.0" | 21 | indexmap = "1.4.0" |
22 | smallvec = "1.4.0" | 22 | smallvec = "1.4.0" |
23 | 23 | ||
diff --git a/crates/hir_def/src/body.rs b/crates/hir_def/src/body.rs index 998b82601..d07004b9d 100644 --- a/crates/hir_def/src/body.rs +++ b/crates/hir_def/src/body.rs | |||
@@ -26,7 +26,7 @@ pub(crate) use lower::LowerCtx; | |||
26 | use crate::{ | 26 | use crate::{ |
27 | attr::{Attrs, RawAttrs}, | 27 | attr::{Attrs, RawAttrs}, |
28 | db::DefDatabase, | 28 | db::DefDatabase, |
29 | expr::{Expr, ExprId, Pat, PatId}, | 29 | expr::{Expr, ExprId, Label, LabelId, Pat, PatId}, |
30 | item_scope::BuiltinShadowMode, | 30 | item_scope::BuiltinShadowMode, |
31 | item_scope::ItemScope, | 31 | item_scope::ItemScope, |
32 | nameres::CrateDefMap, | 32 | nameres::CrateDefMap, |
@@ -226,6 +226,7 @@ pub(crate) struct Mark { | |||
226 | pub struct Body { | 226 | pub struct Body { |
227 | pub exprs: Arena<Expr>, | 227 | pub exprs: Arena<Expr>, |
228 | pub pats: Arena<Pat>, | 228 | pub pats: Arena<Pat>, |
229 | pub labels: Arena<Label>, | ||
229 | /// The patterns for the function's parameters. While the parameter types are | 230 | /// The patterns for the function's parameters. While the parameter types are |
230 | /// part of the function signature, the patterns are not (they don't change | 231 | /// part of the function signature, the patterns are not (they don't change |
231 | /// the external type of the function). | 232 | /// the external type of the function). |
@@ -244,6 +245,8 @@ pub type ExprSource = InFile<ExprPtr>; | |||
244 | pub type PatPtr = Either<AstPtr<ast::Pat>, AstPtr<ast::SelfParam>>; | 245 | pub type PatPtr = Either<AstPtr<ast::Pat>, AstPtr<ast::SelfParam>>; |
245 | pub type PatSource = InFile<PatPtr>; | 246 | pub type PatSource = InFile<PatPtr>; |
246 | 247 | ||
248 | pub type LabelPtr = AstPtr<ast::Label>; | ||
249 | pub type LabelSource = InFile<LabelPtr>; | ||
247 | /// An item body together with the mapping from syntax nodes to HIR expression | 250 | /// An item body together with the mapping from syntax nodes to HIR expression |
248 | /// IDs. This is needed to go from e.g. a position in a file to the HIR | 251 | /// IDs. This is needed to go from e.g. a position in a file to the HIR |
249 | /// expression containing it; but for type inference etc., we want to operate on | 252 | /// expression containing it; but for type inference etc., we want to operate on |
@@ -261,6 +264,8 @@ pub struct BodySourceMap { | |||
261 | expr_map_back: ArenaMap<ExprId, Result<ExprSource, SyntheticSyntax>>, | 264 | expr_map_back: ArenaMap<ExprId, Result<ExprSource, SyntheticSyntax>>, |
262 | pat_map: FxHashMap<PatSource, PatId>, | 265 | pat_map: FxHashMap<PatSource, PatId>, |
263 | pat_map_back: ArenaMap<PatId, Result<PatSource, SyntheticSyntax>>, | 266 | pat_map_back: ArenaMap<PatId, Result<PatSource, SyntheticSyntax>>, |
267 | label_map: FxHashMap<LabelSource, LabelId>, | ||
268 | label_map_back: ArenaMap<LabelId, LabelSource>, | ||
264 | field_map: FxHashMap<(ExprId, usize), InFile<AstPtr<ast::RecordExprField>>>, | 269 | field_map: FxHashMap<(ExprId, usize), InFile<AstPtr<ast::RecordExprField>>>, |
265 | expansions: FxHashMap<InFile<AstPtr<ast::MacroCall>>, HirFileId>, | 270 | expansions: FxHashMap<InFile<AstPtr<ast::MacroCall>>, HirFileId>, |
266 | 271 | ||
@@ -334,6 +339,14 @@ impl Index<PatId> for Body { | |||
334 | } | 339 | } |
335 | } | 340 | } |
336 | 341 | ||
342 | impl Index<LabelId> for Body { | ||
343 | type Output = Label; | ||
344 | |||
345 | fn index(&self, label: LabelId) -> &Label { | ||
346 | &self.labels[label] | ||
347 | } | ||
348 | } | ||
349 | |||
337 | impl BodySourceMap { | 350 | impl BodySourceMap { |
338 | pub fn expr_syntax(&self, expr: ExprId) -> Result<ExprSource, SyntheticSyntax> { | 351 | pub fn expr_syntax(&self, expr: ExprId) -> Result<ExprSource, SyntheticSyntax> { |
339 | self.expr_map_back[expr].clone() | 352 | self.expr_map_back[expr].clone() |
@@ -363,6 +376,15 @@ impl BodySourceMap { | |||
363 | self.pat_map.get(&src).cloned() | 376 | self.pat_map.get(&src).cloned() |
364 | } | 377 | } |
365 | 378 | ||
379 | pub fn label_syntax(&self, label: LabelId) -> LabelSource { | ||
380 | self.label_map_back[label].clone() | ||
381 | } | ||
382 | |||
383 | pub fn node_label(&self, node: InFile<&ast::Label>) -> Option<LabelId> { | ||
384 | let src = node.map(|it| AstPtr::new(it)); | ||
385 | self.label_map.get(&src).cloned() | ||
386 | } | ||
387 | |||
366 | pub fn field_syntax(&self, expr: ExprId, field: usize) -> InFile<AstPtr<ast::RecordExprField>> { | 388 | pub fn field_syntax(&self, expr: ExprId, field: usize) -> InFile<AstPtr<ast::RecordExprField>> { |
367 | self.field_map[&(expr, field)].clone() | 389 | self.field_map[&(expr, field)].clone() |
368 | } | 390 | } |
diff --git a/crates/hir_def/src/body/lower.rs b/crates/hir_def/src/body/lower.rs index 0f404be1b..17c72779b 100644 --- a/crates/hir_def/src/body/lower.rs +++ b/crates/hir_def/src/body/lower.rs | |||
@@ -22,13 +22,14 @@ use test_utils::mark; | |||
22 | 22 | ||
23 | use crate::{ | 23 | use crate::{ |
24 | adt::StructKind, | 24 | adt::StructKind, |
25 | body::{Body, BodySourceMap, Expander, PatPtr, SyntheticSyntax}, | 25 | body::{Body, BodySourceMap, Expander, LabelSource, PatPtr, SyntheticSyntax}, |
26 | builtin_type::{BuiltinFloat, BuiltinInt}, | 26 | builtin_type::{BuiltinFloat, BuiltinInt}, |
27 | db::DefDatabase, | 27 | db::DefDatabase, |
28 | diagnostics::{InactiveCode, MacroError, UnresolvedProcMacro}, | 28 | diagnostics::{InactiveCode, MacroError, UnresolvedProcMacro}, |
29 | expr::{ | 29 | expr::{ |
30 | dummy_expr_id, ArithOp, Array, BinaryOp, BindingAnnotation, CmpOp, Expr, ExprId, Literal, | 30 | dummy_expr_id, ArithOp, Array, BinaryOp, BindingAnnotation, CmpOp, Expr, ExprId, Label, |
31 | LogicOp, MatchArm, Ordering, Pat, PatId, RecordFieldPat, RecordLitField, Statement, | 31 | LabelId, Literal, LogicOp, MatchArm, Ordering, Pat, PatId, RecordFieldPat, RecordLitField, |
32 | Statement, | ||
32 | }, | 33 | }, |
33 | item_scope::BuiltinShadowMode, | 34 | item_scope::BuiltinShadowMode, |
34 | item_tree::{ItemTree, ItemTreeId, ItemTreeNode}, | 35 | item_tree::{ItemTree, ItemTreeId, ItemTreeNode}, |
@@ -72,6 +73,7 @@ pub(super) fn lower( | |||
72 | body: Body { | 73 | body: Body { |
73 | exprs: Arena::default(), | 74 | exprs: Arena::default(), |
74 | pats: Arena::default(), | 75 | pats: Arena::default(), |
76 | labels: Arena::default(), | ||
75 | params: Vec::new(), | 77 | params: Vec::new(), |
76 | body_expr: dummy_expr_id(), | 78 | body_expr: dummy_expr_id(), |
77 | item_scope: Default::default(), | 79 | item_scope: Default::default(), |
@@ -175,6 +177,18 @@ impl ExprCollector<'_> { | |||
175 | id | 177 | id |
176 | } | 178 | } |
177 | 179 | ||
180 | fn alloc_label(&mut self, label: Label, ptr: AstPtr<ast::Label>) -> LabelId { | ||
181 | let src = self.expander.to_source(ptr); | ||
182 | let id = self.make_label(label, src.clone()); | ||
183 | self.source_map.label_map.insert(src, id); | ||
184 | id | ||
185 | } | ||
186 | fn make_label(&mut self, label: Label, src: LabelSource) -> LabelId { | ||
187 | let id = self.body.labels.alloc(label); | ||
188 | self.source_map.label_map_back.insert(id, src); | ||
189 | id | ||
190 | } | ||
191 | |||
178 | fn collect_expr(&mut self, expr: ast::Expr) -> ExprId { | 192 | fn collect_expr(&mut self, expr: ast::Expr) -> ExprId { |
179 | let syntax_ptr = AstPtr::new(&expr); | 193 | let syntax_ptr = AstPtr::new(&expr); |
180 | if self.check_cfg(&expr).is_none() { | 194 | if self.check_cfg(&expr).is_none() { |
@@ -228,37 +242,40 @@ impl ExprCollector<'_> { | |||
228 | self.alloc_expr(Expr::Unsafe { body }, syntax_ptr) | 242 | self.alloc_expr(Expr::Unsafe { body }, syntax_ptr) |
229 | } | 243 | } |
230 | // FIXME: we need to record these effects somewhere... | 244 | // FIXME: we need to record these effects somewhere... |
231 | ast::Effect::Label(label) => match e.block_expr() { | 245 | ast::Effect::Label(label) => { |
232 | Some(block) => { | 246 | let label = self.collect_label(label); |
233 | let res = self.collect_block(block); | 247 | match e.block_expr() { |
234 | match &mut self.body.exprs[res] { | 248 | Some(block) => { |
235 | Expr::Block { label: block_label, .. } => { | 249 | let res = self.collect_block(block); |
236 | *block_label = label.lifetime().map(|t| Name::new_lifetime(&t)) | 250 | match &mut self.body.exprs[res] { |
251 | Expr::Block { label: block_label, .. } => { | ||
252 | *block_label = Some(label); | ||
253 | } | ||
254 | _ => unreachable!(), | ||
237 | } | 255 | } |
238 | _ => unreachable!(), | 256 | res |
239 | } | 257 | } |
240 | res | 258 | None => self.missing_expr(), |
241 | } | 259 | } |
242 | None => self.missing_expr(), | 260 | } |
243 | }, | ||
244 | // FIXME: we need to record these effects somewhere... | 261 | // FIXME: we need to record these effects somewhere... |
245 | ast::Effect::Async(_) => { | 262 | ast::Effect::Async(_) => { |
246 | let body = self.collect_block_opt(e.block_expr()); | 263 | let body = self.collect_block_opt(e.block_expr()); |
247 | self.alloc_expr(Expr::Async { body }, syntax_ptr) | 264 | self.alloc_expr(Expr::Async { body }, syntax_ptr) |
248 | } | 265 | } |
266 | ast::Effect::Const(_) => { | ||
267 | let body = self.collect_block_opt(e.block_expr()); | ||
268 | self.alloc_expr(Expr::Const { body }, syntax_ptr) | ||
269 | } | ||
249 | }, | 270 | }, |
250 | ast::Expr::BlockExpr(e) => self.collect_block(e), | 271 | ast::Expr::BlockExpr(e) => self.collect_block(e), |
251 | ast::Expr::LoopExpr(e) => { | 272 | ast::Expr::LoopExpr(e) => { |
273 | let label = e.label().map(|label| self.collect_label(label)); | ||
252 | let body = self.collect_block_opt(e.loop_body()); | 274 | let body = self.collect_block_opt(e.loop_body()); |
253 | self.alloc_expr( | 275 | self.alloc_expr(Expr::Loop { body, label }, syntax_ptr) |
254 | Expr::Loop { | ||
255 | body, | ||
256 | label: e.label().and_then(|l| l.lifetime()).map(|l| Name::new_lifetime(&l)), | ||
257 | }, | ||
258 | syntax_ptr, | ||
259 | ) | ||
260 | } | 276 | } |
261 | ast::Expr::WhileExpr(e) => { | 277 | ast::Expr::WhileExpr(e) => { |
278 | let label = e.label().map(|label| self.collect_label(label)); | ||
262 | let body = self.collect_block_opt(e.loop_body()); | 279 | let body = self.collect_block_opt(e.loop_body()); |
263 | 280 | ||
264 | let condition = match e.condition() { | 281 | let condition = match e.condition() { |
@@ -279,42 +296,20 @@ impl ExprCollector<'_> { | |||
279 | ]; | 296 | ]; |
280 | let match_expr = | 297 | let match_expr = |
281 | self.alloc_expr_desugared(Expr::Match { expr: match_expr, arms }); | 298 | self.alloc_expr_desugared(Expr::Match { expr: match_expr, arms }); |
282 | return self.alloc_expr( | 299 | return self |
283 | Expr::Loop { | 300 | .alloc_expr(Expr::Loop { body: match_expr, label }, syntax_ptr); |
284 | body: match_expr, | ||
285 | label: e | ||
286 | .label() | ||
287 | .and_then(|l| l.lifetime()) | ||
288 | .map(|l| Name::new_lifetime(&l)), | ||
289 | }, | ||
290 | syntax_ptr, | ||
291 | ); | ||
292 | } | 301 | } |
293 | }, | 302 | }, |
294 | }; | 303 | }; |
295 | 304 | ||
296 | self.alloc_expr( | 305 | self.alloc_expr(Expr::While { condition, body, label }, syntax_ptr) |
297 | Expr::While { | ||
298 | condition, | ||
299 | body, | ||
300 | label: e.label().and_then(|l| l.lifetime()).map(|l| Name::new_lifetime(&l)), | ||
301 | }, | ||
302 | syntax_ptr, | ||
303 | ) | ||
304 | } | 306 | } |
305 | ast::Expr::ForExpr(e) => { | 307 | ast::Expr::ForExpr(e) => { |
308 | let label = e.label().map(|label| self.collect_label(label)); | ||
306 | let iterable = self.collect_expr_opt(e.iterable()); | 309 | let iterable = self.collect_expr_opt(e.iterable()); |
307 | let pat = self.collect_pat_opt(e.pat()); | 310 | let pat = self.collect_pat_opt(e.pat()); |
308 | let body = self.collect_block_opt(e.loop_body()); | 311 | let body = self.collect_block_opt(e.loop_body()); |
309 | self.alloc_expr( | 312 | self.alloc_expr(Expr::For { iterable, pat, body, label }, syntax_ptr) |
310 | Expr::For { | ||
311 | iterable, | ||
312 | pat, | ||
313 | body, | ||
314 | label: e.label().and_then(|l| l.lifetime()).map(|l| Name::new_lifetime(&l)), | ||
315 | }, | ||
316 | syntax_ptr, | ||
317 | ) | ||
318 | } | 313 | } |
319 | ast::Expr::CallExpr(e) => { | 314 | ast::Expr::CallExpr(e) => { |
320 | let callee = self.collect_expr_opt(e.expr()); | 315 | let callee = self.collect_expr_opt(e.expr()); |
@@ -814,6 +809,13 @@ impl ExprCollector<'_> { | |||
814 | } | 809 | } |
815 | } | 810 | } |
816 | 811 | ||
812 | fn collect_label(&mut self, ast_label: ast::Label) -> LabelId { | ||
813 | let label = Label { | ||
814 | name: ast_label.lifetime().as_ref().map_or_else(Name::missing, Name::new_lifetime), | ||
815 | }; | ||
816 | self.alloc_label(label, AstPtr::new(&ast_label)) | ||
817 | } | ||
818 | |||
817 | fn collect |