diff options
106 files changed, 2691 insertions, 822 deletions
diff --git a/Cargo.lock b/Cargo.lock index e819297bd..5c7bcb17b 100644 --- a/Cargo.lock +++ b/Cargo.lock | |||
@@ -42,15 +42,14 @@ version = "0.0.0" | |||
42 | 42 | ||
43 | [[package]] | 43 | [[package]] |
44 | name = "arrayvec" | 44 | name = "arrayvec" |
45 | version = "0.5.1" | 45 | version = "0.5.2" |
46 | source = "registry+https://github.com/rust-lang/crates.io-index" | 46 | source = "registry+https://github.com/rust-lang/crates.io-index" |
47 | checksum = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8" | 47 | checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" |
48 | 48 | ||
49 | [[package]] | 49 | [[package]] |
50 | name = "assists" | 50 | name = "assists" |
51 | version = "0.0.0" | 51 | version = "0.0.0" |
52 | dependencies = [ | 52 | dependencies = [ |
53 | "base_db", | ||
54 | "either", | 53 | "either", |
55 | "hir", | 54 | "hir", |
56 | "ide_db", | 55 | "ide_db", |
@@ -128,26 +127,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | |||
128 | checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" | 127 | checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" |
129 | 128 | ||
130 | [[package]] | 129 | [[package]] |
131 | name = "call_info" | ||
132 | version = "0.0.0" | ||
133 | dependencies = [ | ||
134 | "base_db", | ||
135 | "either", | ||
136 | "expect-test", | ||
137 | "hir", | ||
138 | "ide_db", | ||
139 | "stdx", | ||
140 | "syntax", | ||
141 | "test_utils", | ||
142 | ] | ||
143 | |||
144 | [[package]] | ||
145 | name = "cargo_metadata" | 130 | name = "cargo_metadata" |
146 | version = "0.12.0" | 131 | version = "0.12.0" |
147 | source = "registry+https://github.com/rust-lang/crates.io-index" | 132 | source = "registry+https://github.com/rust-lang/crates.io-index" |
148 | checksum = "d5a5f7b42f606b7f23674f6f4d877628350682bc40687d3fae65679a58d55345" | 133 | checksum = "d5a5f7b42f606b7f23674f6f4d877628350682bc40687d3fae65679a58d55345" |
149 | dependencies = [ | 134 | dependencies = [ |
150 | "semver", | 135 | "semver 0.11.0", |
151 | "serde", | 136 | "serde", |
152 | "serde_json", | 137 | "serde_json", |
153 | ] | 138 | ] |
@@ -162,6 +147,7 @@ checksum = "ed67cbde08356238e75fc4656be4749481eeffb09e19f320a25237d5221c985d" | |||
162 | name = "cfg" | 147 | name = "cfg" |
163 | version = "0.0.0" | 148 | version = "0.0.0" |
164 | dependencies = [ | 149 | dependencies = [ |
150 | "expect-test", | ||
165 | "mbe", | 151 | "mbe", |
166 | "rustc-hash", | 152 | "rustc-hash", |
167 | "syntax", | 153 | "syntax", |
@@ -182,9 +168,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" | |||
182 | 168 | ||
183 | [[package]] | 169 | [[package]] |
184 | name = "chalk-derive" | 170 | name = "chalk-derive" |
185 | version = "0.33.0" | 171 | version = "0.34.0" |
186 | source = "registry+https://github.com/rust-lang/crates.io-index" | 172 | source = "registry+https://github.com/rust-lang/crates.io-index" |
187 | checksum = "569014cab9084a6b826fe2507cc6d08f7897ba144fb1bc74e71b593dc8a0b952" | 173 | checksum = "0e9f986750ecb4df889d0a95d4586bd921889497b33908cc75bb80eadb4c600a" |
188 | dependencies = [ | 174 | dependencies = [ |
189 | "proc-macro2", | 175 | "proc-macro2", |
190 | "quote", | 176 | "quote", |
@@ -194,9 +180,9 @@ dependencies = [ | |||
194 | 180 | ||
195 | [[package]] | 181 | [[package]] |
196 | name = "chalk-ir" | 182 | name = "chalk-ir" |
197 | version = "0.33.0" | 183 | version = "0.34.0" |
198 | source = "registry+https://github.com/rust-lang/crates.io-index" | 184 | source = "registry+https://github.com/rust-lang/crates.io-index" |
199 | checksum = "8d9eab2a6590b696419f89c9ca3616fe8e8266ef676e6a6da8818c94963c9541" | 185 | checksum = "5c352c4649f1408bb3de5d86a248fda78d3d9cd1cbbd9502e7eca1be1e7ac368" |
200 | dependencies = [ | 186 | dependencies = [ |
201 | "chalk-derive", | 187 | "chalk-derive", |
202 | "lazy_static", | 188 | "lazy_static", |
@@ -204,9 +190,9 @@ dependencies = [ | |||
204 | 190 | ||
205 | [[package]] | 191 | [[package]] |
206 | name = "chalk-recursive" | 192 | name = "chalk-recursive" |
207 | version = "0.33.0" | 193 | version = "0.34.0" |
208 | source = "registry+https://github.com/rust-lang/crates.io-index" | 194 | source = "registry+https://github.com/rust-lang/crates.io-index" |
209 | checksum = "4a4671bcc70aa2d7e12ff4fe03f91d0c3c9ce387de915915e57fdf0c91dc5abd" | 195 | checksum = "7294bb2ac5446fcb83ec9524b9113f59a8913f174a9c1dea6db60532f17a1579" |
210 | dependencies = [ | 196 | dependencies = [ |
211 | "chalk-derive", | 197 | "chalk-derive", |
212 | "chalk-ir", | 198 | "chalk-ir", |
@@ -217,9 +203,9 @@ dependencies = [ | |||
217 | 203 | ||
218 | [[package]] | 204 | [[package]] |
219 | name = "chalk-solve" | 205 | name = "chalk-solve" |
220 | version = "0.33.0" | 206 | version = "0.34.0" |
221 | source = "registry+https://github.com/rust-lang/crates.io-index" | 207 | source = "registry+https://github.com/rust-lang/crates.io-index" |
222 | checksum = "45f75cc603f2fd302576c8b2976437f334e159e26d0afbb108a565b96c52184e" | 208 | checksum = "ffbca06963ed6f3d22faed840847a685f02feefa3825c0b94f9b791d03a0ac6f" |
223 | dependencies = [ | 209 | dependencies = [ |
224 | "chalk-derive", | 210 | "chalk-derive", |
225 | "chalk-ir", | 211 | "chalk-ir", |
@@ -267,9 +253,7 @@ dependencies = [ | |||
267 | name = "completion" | 253 | name = "completion" |
268 | version = "0.0.0" | 254 | version = "0.0.0" |
269 | dependencies = [ | 255 | dependencies = [ |
270 | "assists", | ||
271 | "base_db", | 256 | "base_db", |
272 | "call_info", | ||
273 | "expect-test", | 257 | "expect-test", |
274 | "hir", | 258 | "hir", |
275 | "ide_db", | 259 | "ide_db", |
@@ -291,11 +275,11 @@ checksum = "ce90df4c658c62f12d78f7508cf92f9173e5184a539c10bfe54a3107b3ffd0f2" | |||
291 | 275 | ||
292 | [[package]] | 276 | [[package]] |
293 | name = "crc32fast" | 277 | name = "crc32fast" |
294 | version = "1.2.0" | 278 | version = "1.2.1" |
295 | source = "registry+https://github.com/rust-lang/crates.io-index" | 279 | source = "registry+https://github.com/rust-lang/crates.io-index" |
296 | checksum = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1" | 280 | checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a" |
297 | dependencies = [ | 281 | dependencies = [ |
298 | "cfg-if 0.1.10", | 282 | "cfg-if 1.0.0", |
299 | ] | 283 | ] |
300 | 284 | ||
301 | [[package]] | 285 | [[package]] |
@@ -320,26 +304,25 @@ dependencies = [ | |||
320 | 304 | ||
321 | [[package]] | 305 | [[package]] |
322 | name = "crossbeam-deque" | 306 | name = "crossbeam-deque" |
323 | version = "0.7.3" | 307 | version = "0.8.0" |
324 | source = "registry+https://github.com/rust-lang/crates.io-index" | 308 | source = "registry+https://github.com/rust-lang/crates.io-index" |
325 | checksum = "9f02af974daeee82218205558e51ec8768b48cf524bd01d550abe5573a608285" | 309 | checksum = "94af6efb46fef72616855b036a624cf27ba656ffc9be1b9a3c931cfc7749a9a9" |
326 | dependencies = [ | 310 | dependencies = [ |
311 | "cfg-if 1.0.0", | ||
327 | "crossbeam-epoch", | 312 | "crossbeam-epoch", |
328 | "crossbeam-utils 0.7.2", | 313 | "crossbeam-utils 0.8.0", |
329 | "maybe-uninit", | ||
330 | ] | 314 | ] |
331 | 315 | ||
332 | [[package]] | 316 | [[package]] |
333 | name = "crossbeam-epoch" | 317 | name = "crossbeam-epoch" |
334 | version = "0.8.2" | 318 | version = "0.9.0" |
335 | source = "registry+https://github.com/rust-lang/crates.io-index" | 319 | source = "registry+https://github.com/rust-lang/crates.io-index" |
336 | checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace" | 320 | checksum = "ec0f606a85340376eef0d6d8fec399e6d4a544d648386c6645eb6d0653b27d9f" |
337 | dependencies = [ | 321 | dependencies = [ |
338 | "autocfg", | 322 | "cfg-if 1.0.0", |
339 | "cfg-if 0.1.10", | 323 | "const_fn", |
340 | "crossbeam-utils 0.7.2", | 324 | "crossbeam-utils 0.8.0", |
341 | "lazy_static", | 325 | "lazy_static", |
342 | "maybe-uninit", | ||
343 | "memoffset", | 326 | "memoffset", |
344 | "scopeguard", | 327 | "scopeguard", |
345 | ] | 328 | ] |
@@ -497,6 +480,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | |||
497 | checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" | 480 | checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" |
498 | 481 | ||
499 | [[package]] | 482 | [[package]] |
483 | name = "generator" | ||
484 | version = "0.6.23" | ||
485 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
486 | checksum = "8cdc09201b2e8ca1b19290cf7e65de2246b8e91fb6874279722189c4de7b94dc" | ||
487 | dependencies = [ | ||
488 | "cc", | ||
489 | "libc", | ||
490 | "log", | ||
491 | "rustc_version", | ||
492 | "winapi 0.3.9", | ||
493 | ] | ||
494 | |||
495 | [[package]] | ||
500 | name = "gimli" | 496 | name = "gimli" |
501 | version = "0.22.0" | 497 | version = "0.22.0" |
502 | source = "registry+https://github.com/rust-lang/crates.io-index" | 498 | source = "registry+https://github.com/rust-lang/crates.io-index" |
@@ -642,8 +638,6 @@ name = "ide" | |||
642 | version = "0.0.0" | 638 | version = "0.0.0" |
643 | dependencies = [ | 639 | dependencies = [ |
644 | "assists", | 640 | "assists", |
645 | "base_db", | ||
646 | "call_info", | ||
647 | "cfg", | 641 | "cfg", |
648 | "completion", | 642 | "completion", |
649 | "either", | 643 | "either", |
@@ -672,6 +666,7 @@ version = "0.0.0" | |||
672 | dependencies = [ | 666 | dependencies = [ |
673 | "base_db", | 667 | "base_db", |
674 | "either", | 668 | "either", |
669 | "expect-test", | ||
675 | "fst", | 670 | "fst", |
676 | "hir", | 671 | "hir", |
677 | "log", | 672 | "log", |
@@ -795,11 +790,11 @@ checksum = "2448f6066e80e3bfc792e9c98bf705b4b0fc6e8ef5b43e5889aff0eaa9c58743" | |||
795 | 790 | ||
796 | [[package]] | 791 | [[package]] |
797 | name = "libloading" | 792 | name = "libloading" |
798 | version = "0.6.4" | 793 | version = "0.6.5" |
799 | source = "registry+https://github.com/rust-lang/crates.io-index" | 794 | source = "registry+https://github.com/rust-lang/crates.io-index" |
800 | checksum = "3557c9384f7f757f6d139cd3a4c62ef4e850696c16bf27924a5538c8a09717a1" | 795 | checksum = "1090080fe06ec2648d0da3881d9453d97e71a45f00eb179af7fdd7e3f686fdb0" |
801 | dependencies = [ | 796 | dependencies = [ |
802 | "cfg-if 0.1.10", | 797 | "cfg-if 1.0.0", |
803 | "winapi 0.3.9", | 798 | "winapi 0.3.9", |
804 | ] | 799 | ] |
805 | 800 | ||
@@ -831,6 +826,19 @@ dependencies = [ | |||
831 | ] | 826 | ] |
832 | 827 | ||
833 | [[package]] | 828 | [[package]] |
829 | name = "loom" | ||
830 | version = "0.3.6" | ||
831 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
832 | checksum = "a0e8460f2f2121162705187214720353c517b97bdfb3494c0b1e33d83ebe4bed" | ||
833 | dependencies = [ | ||
834 | "cfg-if 0.1.10", | ||
835 | "generator", | ||
836 | "scoped-tls", | ||
837 | "serde", | ||
838 | "serde_json", | ||
839 | ] | ||
840 | |||
841 | [[package]] | ||
834 | name = "lsp-server" | 842 | name = "lsp-server" |
835 | version = "0.4.1" | 843 | version = "0.4.1" |
836 | source = "registry+https://github.com/rust-lang/crates.io-index" | 844 | source = "registry+https://github.com/rust-lang/crates.io-index" |
@@ -1259,9 +1267,9 @@ dependencies = [ | |||
1259 | 1267 | ||
1260 | [[package]] | 1268 | [[package]] |
1261 | name = "rayon" | 1269 | name = "rayon" |
1262 | version = "1.4.1" | 1270 | version = "1.5.0" |
1263 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1271 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1264 | checksum = "dcf6960dc9a5b4ee8d3e4c5787b4a112a8818e0290a42ff664ad60692fdf2032" | 1272 | checksum = "8b0d8e0819fadc20c74ea8373106ead0600e3a67ef1fe8da56e39b9ae7275674" |
1265 | dependencies = [ | 1273 | dependencies = [ |
1266 | "autocfg", | 1274 | "autocfg", |
1267 | "crossbeam-deque", | 1275 | "crossbeam-deque", |
@@ -1271,13 +1279,13 @@ dependencies = [ | |||
1271 | 1279 | ||
1272 | [[package]] | 1280 | [[package]] |
1273 | name = "rayon-core" | 1281 | name = "rayon-core" |
1274 | version = "1.8.1" | 1282 | version = "1.9.0" |
1275 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1283 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1276 | checksum = "e8c4fec834fb6e6d2dd5eece3c7b432a52f0ba887cf40e595190c4107edc08bf" | 1284 | checksum = "9ab346ac5921dc62ffa9f89b7a773907511cdfa5490c572ae9be1be33e8afa4a" |
1277 | dependencies = [ | 1285 | dependencies = [ |
1278 | "crossbeam-channel 0.4.4", | 1286 | "crossbeam-channel 0.5.0", |
1279 | "crossbeam-deque", | 1287 | "crossbeam-deque", |
1280 | "crossbeam-utils 0.7.2", | 1288 | "crossbeam-utils 0.8.0", |
1281 | "lazy_static", | 1289 | "lazy_static", |
1282 | "num_cpus", | 1290 | "num_cpus", |
1283 | ] | 1291 | ] |
@@ -1330,7 +1338,6 @@ name = "rust-analyzer" | |||
1330 | version = "0.0.0" | 1338 | version = "0.0.0" |
1331 | dependencies = [ | 1339 | dependencies = [ |
1332 | "anyhow", | 1340 | "anyhow", |
1333 | "base_db", | ||
1334 | "cfg", | 1341 | "cfg", |
1335 | "crossbeam-channel 0.5.0", | 1342 | "crossbeam-channel 0.5.0", |
1336 | "env_logger", | 1343 | "env_logger", |
@@ -1365,6 +1372,9 @@ dependencies = [ | |||
1365 | "text_edit", | 1372 | "text_edit", |
1366 | "threadpool", | 1373 | "threadpool", |
1367 | "toolchain", | 1374 | "toolchain", |
1375 | "tracing", | ||
1376 | "tracing-subscriber", | ||
1377 | "tracing-tree", | ||
1368 | "tt", | 1378 | "tt", |
1369 | "vfs", | 1379 | "vfs", |
1370 | "vfs-notify", | 1380 | "vfs-notify", |
@@ -1373,18 +1383,18 @@ dependencies = [ | |||
1373 | 1383 | ||
1374 | [[package]] | 1384 | [[package]] |
1375 | name = "rustc-ap-rustc_lexer" | 1385 | name = "rustc-ap-rustc_lexer" |
1376 | version = "683.0.0" | 1386 | version = "685.0.0" |
1377 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1387 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1378 | checksum = "c0c9a47e24d4b7d74ec583d8813803b36985ee082fe7debe55f257df92d5fe50" | 1388 | checksum = "4d6465438127d20bf9d0f5148f806c029934a74c9bce1103a42d47e81d3fe89a" |
1379 | dependencies = [ | 1389 | dependencies = [ |
1380 | "unicode-xid", | 1390 | "unicode-xid", |
1381 | ] | 1391 | ] |
1382 | 1392 | ||
1383 | [[package]] | 1393 | [[package]] |
1384 | name = "rustc-demangle" | 1394 | name = "rustc-demangle" |
1385 | version = "0.1.17" | 1395 | version = "0.1.18" |
1386 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1396 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1387 | checksum = "b2610b7f643d18c87dff3b489950269617e6601a51f1f05aa5daefee36f64f0b" | 1397 | checksum = "6e3bad0ee36814ca07d7968269dd4b7ec89ec2da10c4bb613928d3077083c232" |
1388 | 1398 | ||
1389 | [[package]] | 1399 | [[package]] |
1390 | name = "rustc-hash" | 1400 | name = "rustc-hash" |
@@ -1393,6 +1403,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | |||
1393 | checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" | 1403 | checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" |
1394 | 1404 | ||
1395 | [[package]] | 1405 | [[package]] |
1406 | name = "rustc_version" | ||
1407 | version = "0.2.3" | ||
1408 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1409 | checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" | ||
1410 | dependencies = [ | ||
1411 | "semver 0.9.0", | ||
1412 | ] | ||
1413 | |||
1414 | [[package]] | ||
1396 | name = "ryu" | 1415 | name = "ryu" |
1397 | version = "1.0.5" | 1416 | version = "1.0.5" |
1398 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1417 | source = "registry+https://github.com/rust-lang/crates.io-index" |
@@ -1459,9 +1478,9 @@ dependencies = [ | |||
1459 | 1478 | ||
1460 | [[package]] | 1479 | [[package]] |
1461 | name = "scroll_derive" | 1480 | name = "scroll_derive" |
1462 | version = "0.10.3" | 1481 | version = "0.10.4" |
1463 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1482 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1464 | checksum = "6dfde5d1531034db129e95c76ac857e2baecea3443579d493d02224950b0fb6d" | 1483 | checksum = "b12bd20b94c7cdfda8c7ba9b92ad0d9a56e3fa018c25fca83b51aa664c9b4c0d" |
1465 | dependencies = [ | 1484 | dependencies = [ |
1466 | "proc-macro2", | 1485 | "proc-macro2", |
1467 | "quote", | 1486 | "quote", |
@@ -1470,16 +1489,31 @@ dependencies = [ | |||
1470 | 1489 | ||
1471 | [[package]] | 1490 | [[package]] |
1472 | name = "semver" | 1491 | name = "semver" |
1492 | version = "0.9.0" | ||
1493 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1494 | checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" | ||
1495 | dependencies = [ | ||
1496 | "semver-parser 0.7.0", | ||
1497 | ] | ||
1498 | |||
1499 | [[package]] | ||
1500 | name = "semver" | ||
1473 | version = "0.11.0" | 1501 | version = "0.11.0" |
1474 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1502 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1475 | checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" | 1503 | checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" |
1476 | dependencies = [ | 1504 | dependencies = [ |
1477 | "semver-parser", | 1505 | "semver-parser 0.10.1", |
1478 | "serde", | 1506 | "serde", |
1479 | ] | 1507 | ] |
1480 | 1508 | ||
1481 | [[package]] | 1509 | [[package]] |
1482 | name = "semver-parser" | 1510 | name = "semver-parser" |
1511 | version = "0.7.0" | ||
1512 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1513 | checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" | ||
1514 | |||
1515 | [[package]] | ||
1516 | name = "semver-parser" | ||
1483 | version = "0.10.1" | 1517 | version = "0.10.1" |
1484 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1518 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1485 | checksum = "42ef146c2ad5e5f4b037cd6ce2ebb775401729b19a82040c1beac9d36c7d1428" | 1519 | checksum = "42ef146c2ad5e5f4b037cd6ce2ebb775401729b19a82040c1beac9d36c7d1428" |
@@ -1531,11 +1565,12 @@ dependencies = [ | |||
1531 | 1565 | ||
1532 | [[package]] | 1566 | [[package]] |
1533 | name = "sharded-slab" | 1567 | name = "sharded-slab" |
1534 | version = "0.0.9" | 1568 | version = "0.1.0" |
1535 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1569 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1536 | checksum = "06d5a3f5166fb5b42a5439f2eee8b9de149e235961e3eb21c5808fc3ea17ff3e" | 1570 | checksum = "7b4921be914e16899a80adefb821f8ddb7974e3f1250223575a44ed994882127" |
1537 | dependencies = [ | 1571 | dependencies = [ |
1538 | "lazy_static", | 1572 | "lazy_static", |
1573 | "loom", | ||
1539 | ] | 1574 | ] |
1540 | 1575 | ||
1541 | [[package]] | 1576 | [[package]] |
@@ -1563,7 +1598,6 @@ dependencies = [ | |||
1563 | name = "ssr" | 1598 | name = "ssr" |
1564 | version = "0.0.0" | 1599 | version = "0.0.0" |
1565 | dependencies = [ | 1600 | dependencies = [ |
1566 | "base_db", | ||
1567 | "expect-test", | 1601 | "expect-test", |
1568 | "hir", | 1602 | "hir", |
1569 | "ide_db", | 1603 | "ide_db", |
@@ -1580,9 +1614,9 @@ version = "0.0.0" | |||
1580 | 1614 | ||
1581 | [[package]] | 1615 | [[package]] |
1582 | name = "syn" | 1616 | name = "syn" |
1583 | version = "1.0.45" | 1617 | version = "1.0.46" |
1584 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1618 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1585 | checksum = "ea9c5432ff16d6152371f808fb5a871cd67368171b09bb21b43df8e4a47a3556" | 1619 | checksum = "5ad5de3220ea04da322618ded2c42233d02baca219d6f160a3e9c87cda16c942" |
1586 | dependencies = [ | 1620 | dependencies = [ |
1587 | "proc-macro2", | 1621 | "proc-macro2", |
1588 | "quote", | 1622 | "quote", |
@@ -1607,6 +1641,7 @@ version = "0.0.0" | |||
1607 | dependencies = [ | 1641 | dependencies = [ |
1608 | "arrayvec", | 1642 | "arrayvec", |
1609 | "expect-test", | 1643 | "expect-test", |
1644 | "indexmap", | ||
1610 | "itertools", | 1645 | "itertools", |
1611 | "once_cell", | 1646 | "once_cell", |
1612 | "parser", | 1647 | "parser", |
@@ -1758,9 +1793,9 @@ dependencies = [ | |||
1758 | 1793 | ||
1759 | [[package]] | 1794 | [[package]] |
1760 | name = "tracing-subscriber" | 1795 | name = "tracing-subscriber" |
1761 | version = "0.2.13" | 1796 | version = "0.2.14" |
1762 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1797 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1763 | checksum = "4ef0a5e15477aa303afbfac3a44cba9b6430fdaad52423b1e6c0dbbe28c3eedd" | 1798 | checksum = "2810660b9d5b18895d140caba6401765749a6a162e5d0736cfc44ea50db9d79d" |
1764 | dependencies = [ | 1799 | dependencies = [ |
1765 | "ansi_term", | 1800 | "ansi_term", |
1766 | "chrono", | 1801 | "chrono", |
diff --git a/crates/assists/Cargo.toml b/crates/assists/Cargo.toml index 264125651..108f656e9 100644 --- a/crates/assists/Cargo.toml +++ b/crates/assists/Cargo.toml | |||
@@ -18,7 +18,6 @@ stdx = { path = "../stdx", version = "0.0.0" } | |||
18 | syntax = { path = "../syntax", version = "0.0.0" } | 18 | syntax = { path = "../syntax", version = "0.0.0" } |
19 | text_edit = { path = "../text_edit", version = "0.0.0" } | 19 | text_edit = { path = "../text_edit", version = "0.0.0" } |
20 | profile = { path = "../profile", version = "0.0.0" } | 20 | profile = { path = "../profile", version = "0.0.0" } |
21 | base_db = { path = "../base_db", version = "0.0.0" } | ||
22 | ide_db = { path = "../ide_db", version = "0.0.0" } | 21 | ide_db = { path = "../ide_db", version = "0.0.0" } |
23 | hir = { path = "../hir", version = "0.0.0" } | 22 | hir = { path = "../hir", version = "0.0.0" } |
24 | test_utils = { path = "../test_utils", version = "0.0.0" } | 23 | test_utils = { path = "../test_utils", version = "0.0.0" } |
diff --git a/crates/assists/src/assist_context.rs b/crates/assists/src/assist_context.rs index bf520069e..d11fee196 100644 --- a/crates/assists/src/assist_context.rs +++ b/crates/assists/src/assist_context.rs | |||
@@ -3,8 +3,8 @@ | |||
3 | use std::mem; | 3 | use std::mem; |
4 | 4 | ||
5 | use algo::find_covering_element; | 5 | use algo::find_covering_element; |
6 | use base_db::{FileId, FileRange}; | ||
7 | use hir::Semantics; | 6 | use hir::Semantics; |
7 | use ide_db::base_db::{FileId, FileRange}; | ||
8 | use ide_db::{ | 8 | use ide_db::{ |
9 | label::Label, | 9 | label::Label, |
10 | source_change::{SourceChange, SourceFileEdit}, | 10 | source_change::{SourceChange, SourceFileEdit}, |
diff --git a/crates/assists/src/handlers/add_missing_impl_members.rs b/crates/assists/src/handlers/add_missing_impl_members.rs index 4c400f287..b82fb30ad 100644 --- a/crates/assists/src/handlers/add_missing_impl_members.rs +++ b/crates/assists/src/handlers/add_missing_impl_members.rs | |||
@@ -1,4 +1,5 @@ | |||
1 | use hir::HasSource; | 1 | use hir::HasSource; |
2 | use ide_db::traits::{get_missing_assoc_items, resolve_target_trait}; | ||
2 | use syntax::{ | 3 | use syntax::{ |
3 | ast::{ | 4 | ast::{ |
4 | self, | 5 | self, |
@@ -11,7 +12,7 @@ use syntax::{ | |||
11 | use crate::{ | 12 | use crate::{ |
12 | assist_context::{AssistContext, Assists}, | 13 | assist_context::{AssistContext, Assists}, |
13 | ast_transform::{self, AstTransform, QualifyPaths, SubstituteTypeParams}, | 14 | ast_transform::{self, AstTransform, QualifyPaths, SubstituteTypeParams}, |
14 | utils::{get_missing_assoc_items, render_snippet, resolve_target_trait, Cursor}, | 15 | utils::{render_snippet, Cursor}, |
15 | AssistId, AssistKind, | 16 | AssistId, AssistKind, |
16 | }; | 17 | }; |
17 | 18 | ||
diff --git a/crates/assists/src/handlers/extract_struct_from_enum_variant.rs b/crates/assists/src/handlers/extract_struct_from_enum_variant.rs index 7f4f80b23..48433feb9 100644 --- a/crates/assists/src/handlers/extract_struct_from_enum_variant.rs +++ b/crates/assists/src/handlers/extract_struct_from_enum_variant.rs | |||
@@ -1,5 +1,5 @@ | |||
1 | use base_db::FileId; | ||
2 | use hir::{EnumVariant, Module, ModuleDef, Name}; | 1 | use hir::{EnumVariant, Module, ModuleDef, Name}; |
2 | use ide_db::base_db::FileId; | ||
3 | use ide_db::{defs::Definition, search::Reference, RootDatabase}; | 3 | use ide_db::{defs::Definition, search::Reference, RootDatabase}; |
4 | use itertools::Itertools; | 4 | use itertools::Itertools; |
5 | use rustc_hash::FxHashSet; | 5 | use rustc_hash::FxHashSet; |
diff --git a/crates/assists/src/handlers/fix_visibility.rs b/crates/assists/src/handlers/fix_visibility.rs index 66f74150c..c86720787 100644 --- a/crates/assists/src/handlers/fix_visibility.rs +++ b/crates/assists/src/handlers/fix_visibility.rs | |||
@@ -1,5 +1,5 @@ | |||
1 | use base_db::FileId; | ||
2 | use hir::{db::HirDatabase, HasSource, HasVisibility, PathResolution}; | 1 | use hir::{db::HirDatabase, HasSource, HasVisibility, PathResolution}; |
2 | use ide_db::base_db::FileId; | ||
3 | use syntax::{ | 3 | use syntax::{ |
4 | ast::{self, VisibilityOwner}, | 4 | ast::{self, VisibilityOwner}, |
5 | AstNode, TextRange, TextSize, | 5 | AstNode, TextRange, TextSize, |
diff --git a/crates/assists/src/handlers/generate_function.rs b/crates/assists/src/handlers/generate_function.rs index d23f4293b..758188a42 100644 --- a/crates/assists/src/handlers/generate_function.rs +++ b/crates/assists/src/handlers/generate_function.rs | |||
@@ -1,5 +1,5 @@ | |||
1 | use base_db::FileId; | ||
2 | use hir::HirDisplay; | 1 | use hir::HirDisplay; |
2 | use ide_db::base_db::FileId; | ||
3 | use rustc_hash::{FxHashMap, FxHashSet}; | 3 | use rustc_hash::{FxHashMap, FxHashSet}; |
4 | use syntax::{ | 4 | use syntax::{ |
5 | ast::{ | 5 | ast::{ |
diff --git a/crates/assists/src/handlers/remove_dbg.rs b/crates/assists/src/handlers/remove_dbg.rs index e10616779..9731344b8 100644 --- a/crates/assists/src/handlers/remove_dbg.rs +++ b/crates/assists/src/handlers/remove_dbg.rs | |||
@@ -93,8 +93,9 @@ fn needs_parentheses_around_macro_contents(macro_contents: Vec<SyntaxElement>) - | |||
93 | if macro_contents.len() < 2 { | 93 | if macro_contents.len() < 2 { |
94 | return false; | 94 | return false; |
95 | } | 95 | } |
96 | let mut macro_contents = macro_contents.into_iter().peekable(); | ||
96 | let mut unpaired_brackets_in_contents = Vec::new(); | 97 | let mut unpaired_brackets_in_contents = Vec::new(); |
97 | for element in macro_contents { | 98 | while let Some(element) = macro_contents.next() { |
98 | match element.kind() { | 99 | match element.kind() { |
99 | T!['('] | T!['['] | T!['{'] => unpaired_brackets_in_contents.push(element), | 100 | T!['('] | T!['['] | T!['{'] => unpaired_brackets_in_contents.push(element), |
100 | T![')'] => { | 101 | T![')'] => { |
@@ -118,8 +119,14 @@ fn needs_parentheses_around_macro_contents(macro_contents: Vec<SyntaxElement>) - | |||
118 | symbol_kind => { | 119 | symbol_kind => { |
119 | let symbol_not_in_bracket = unpaired_brackets_in_contents.is_empty(); | 120 | let symbol_not_in_bracket = unpaired_brackets_in_contents.is_empty(); |
120 | if symbol_not_in_bracket | 121 | if symbol_not_in_bracket |
121 | && symbol_kind != SyntaxKind::COLON | 122 | && symbol_kind != SyntaxKind::COLON // paths |
122 | && symbol_kind.is_punct() | 123 | && (symbol_kind != SyntaxKind::DOT // field/method access |
124 | || macro_contents // range expressions consist of two SyntaxKind::Dot in macro invocations | ||
125 | .peek() | ||
126 | .map(|element| element.kind() == SyntaxKind::DOT) | ||
127 | .unwrap_or(false)) | ||
128 | && symbol_kind != SyntaxKind::QUESTION // try operator | ||
129 | && (symbol_kind.is_punct() || symbol_kind == SyntaxKind::AS_KW) | ||
123 | { | 130 | { |
124 | return true; | 131 | return true; |
125 | } | 132 | } |
@@ -243,6 +250,25 @@ fn main() { | |||
243 | } | 250 | } |
244 | 251 | ||
245 | #[test] | 252 | #[test] |
253 | fn test_remove_dbg_method_chaining() { | ||
254 | check_assist( | ||
255 | remove_dbg, | ||
256 | r#"let res = <|>dbg!(foo().bar()).baz();"#, | ||
257 | r#"let res = foo().bar().baz();"#, | ||
258 | ); | ||
259 | check_assist( | ||
260 | remove_dbg, | ||
261 | r#"let res = <|>dbg!(foo.bar()).baz();"#, | ||
262 | r#"let res = foo.bar().baz();"#, | ||
263 | ); | ||
264 | } | ||
265 | |||
266 | #[test] | ||
267 | fn test_remove_dbg_field_chaining() { | ||
268 | check_assist(remove_dbg, r#"let res = <|>dbg!(foo.bar).baz;"#, r#"let res = foo.bar.baz;"#); | ||
269 | } | ||
270 | |||
271 | #[test] | ||
246 | fn test_remove_dbg_from_inside_fn() { | 272 | fn test_remove_dbg_from_inside_fn() { |
247 | check_assist_target( | 273 | check_assist_target( |
248 | remove_dbg, | 274 | remove_dbg, |
@@ -280,4 +306,59 @@ fn main() { | |||
280 | }"#, | 306 | }"#, |
281 | ); | 307 | ); |
282 | } | 308 | } |
309 | |||
310 | #[test] | ||
311 | fn test_remove_dbg_try_expr() { | ||
312 | check_assist( | ||
313 | remove_dbg, | ||
314 | r#"let res = <|>dbg!(result?).foo();"#, | ||
315 | r#"let res = result?.foo();"#, | ||
316 | ); | ||
317 | } | ||
318 | |||
319 | #[test] | ||
320 | fn test_remove_dbg_await_expr() { | ||
321 | check_assist( | ||
322 | remove_dbg, | ||
323 | r#"let res = <|>dbg!(fut.await).foo();"#, | ||
324 | r#"let res = fut.await.foo();"#, | ||
325 | ); | ||
326 | } | ||
327 | |||
328 | #[test] | ||
329 | fn test_remove_dbg_as_cast() { | ||
330 | check_assist( | ||
331 | remove_dbg, | ||
332 | r#"let res = <|>dbg!(3 as usize).foo();"#, | ||
333 | r#"let res = (3 as usize).foo();"#, | ||
334 | ); | ||
335 | } | ||
336 | |||
337 | #[test] | ||
338 | fn test_remove_dbg_index_expr() { | ||
339 | check_assist( | ||
340 | remove_dbg, | ||
341 | r#"let res = <|>dbg!(array[3]).foo();"#, | ||
342 | r#"let res = array[3].foo();"#, | ||
343 | ); | ||
344 | check_assist( | ||
345 | remove_dbg, | ||
346 | r#"let res = <|>dbg!(tuple.3).foo();"#, | ||
347 | r#"let res = tuple.3.foo();"#, | ||
348 | ); | ||
349 | } | ||
350 | |||
351 | #[test] | ||
352 | fn test_remove_dbg_range_expr() { | ||
353 | check_assist( | ||
354 | remove_dbg, | ||
355 | r#"let res = <|>dbg!(foo..bar).foo();"#, | ||
356 | r#"let res = (foo..bar).foo();"#, | ||
357 | ); | ||
358 | check_assist( | ||
359 | remove_dbg, | ||
360 | r#"let res = <|>dbg!(foo..=bar).foo();"#, | ||
361 | r#"let res = (foo..=bar).foo();"#, | ||
362 | ); | ||
363 | } | ||
283 | } | 364 | } |
diff --git a/crates/assists/src/handlers/replace_if_let_with_match.rs b/crates/assists/src/handlers/replace_if_let_with_match.rs index 79097621e..9a49c48c1 100644 --- a/crates/assists/src/handlers/replace_if_let_with_match.rs +++ b/crates/assists/src/handlers/replace_if_let_with_match.rs | |||
@@ -7,10 +7,8 @@ use syntax::{ | |||
7 | AstNode, | 7 | AstNode, |
8 | }; | 8 | }; |
9 | 9 | ||
10 | use crate::{ | 10 | use crate::{utils::unwrap_trivial_block, AssistContext, AssistId, AssistKind, Assists}; |
11 | utils::{unwrap_trivial_block, TryEnum}, | 11 | use ide_db::ty_filter::TryEnum; |
12 | AssistContext, AssistId, AssistKind, Assists, | ||
13 | }; | ||
14 | 12 | ||
15 | // Assist: replace_if_let_with_match | 13 | // Assist: replace_if_let_with_match |
16 | // | 14 | // |
diff --git a/crates/assists/src/handlers/replace_let_with_if_let.rs b/crates/assists/src/handlers/replace_let_with_if_let.rs index ed6d0c29b..a5bcbda24 100644 --- a/crates/assists/src/handlers/replace_let_with_if_let.rs +++ b/crates/assists/src/handlers/replace_let_with_if_let.rs | |||
@@ -9,7 +9,8 @@ use syntax::{ | |||
9 | AstNode, T, | 9 | AstNode, T, |
10 | }; | 10 | }; |
11 | 11 | ||
12 | use crate::{utils::TryEnum, AssistContext, AssistId, AssistKind, Assists}; | 12 | use crate::{AssistContext, AssistId, AssistKind, Assists}; |
13 | use ide_db::ty_filter::TryEnum; | ||
13 | 14 | ||
14 | // Assist: replace_let_with_if_let | 15 | // Assist: replace_let_with_if_let |
15 | // | 16 | // |
diff --git a/crates/assists/src/handlers/replace_unwrap_with_match.rs b/crates/assists/src/handlers/replace_unwrap_with_match.rs index 4043c219c..f547066f0 100644 --- a/crates/assists/src/handlers/replace_unwrap_with_match.rs +++ b/crates/assists/src/handlers/replace_unwrap_with_match.rs | |||
@@ -10,9 +10,10 @@ use syntax::{ | |||
10 | }; | 10 | }; |
11 | 11 | ||
12 | use crate::{ | 12 | use crate::{ |
13 | utils::{render_snippet, Cursor, TryEnum}, | 13 | utils::{render_snippet, Cursor}, |
14 | AssistContext, AssistId, AssistKind, Assists, | 14 | AssistContext, AssistId, AssistKind, Assists, |
15 | }; | 15 | }; |
16 | use ide_db::ty_filter::TryEnum; | ||
16 | 17 | ||
17 | // Assist: replace_unwrap_with_match | 18 | // Assist: replace_unwrap_with_match |
18 | // | 19 | // |
diff --git a/crates/assists/src/lib.rs b/crates/assists/src/lib.rs index 8a664f654..70a651e10 100644 --- a/crates/assists/src/lib.rs +++ b/crates/assists/src/lib.rs | |||
@@ -17,8 +17,8 @@ mod tests; | |||
17 | pub mod utils; | 17 | pub mod utils; |
18 | pub mod ast_transform; | 18 | pub mod ast_transform; |
19 | 19 | ||
20 | use base_db::FileRange; | ||
21 | use hir::Semantics; | 20 | use hir::Semantics; |
21 | use ide_db::base_db::FileRange; | ||
22 | use ide_db::{label::Label, source_change::SourceChange, RootDatabase}; | 22 | use ide_db::{label::Label, source_change::SourceChange, RootDatabase}; |
23 | use syntax::TextRange; | 23 | use syntax::TextRange; |
24 | 24 | ||
diff --git a/crates/assists/src/tests.rs b/crates/assists/src/tests.rs index 2b687decf..849d85e76 100644 --- a/crates/assists/src/tests.rs +++ b/crates/assists/src/tests.rs | |||
@@ -1,7 +1,7 @@ | |||
1 | mod generated; | 1 | mod generated; |
2 | 2 | ||
3 | use base_db::{fixture::WithFixture, FileId, FileRange, SourceDatabaseExt}; | ||
4 | use hir::Semantics; | 3 | use hir::Semantics; |
4 | use ide_db::base_db::{fixture::WithFixture, FileId, FileRange, SourceDatabaseExt}; | ||
5 | use ide_db::RootDatabase; | 5 | use ide_db::RootDatabase; |
6 | use syntax::TextRange; | 6 | use syntax::TextRange; |
7 | use test_utils::{assert_eq_text, extract_offset, extract_range}; | 7 | use test_utils::{assert_eq_text, extract_offset, extract_range}; |
diff --git a/crates/assists/src/utils.rs b/crates/assists/src/utils.rs index 1a6b48b45..56f925ee6 100644 --- a/crates/assists/src/utils.rs +++ b/crates/assists/src/utils.rs | |||
@@ -2,14 +2,13 @@ | |||
2 | pub(crate) mod insert_use; | 2 | pub(crate) mod insert_use; |
3 | pub(crate) mod import_assets; | 3 | pub(crate) mod import_assets; |
4 | 4 | ||
5 | use std::{iter, ops}; | 5 | use std::ops; |
6 | 6 | ||
7 | use hir::{Adt, Crate, Enum, Module, ScopeDef, Semantics, Trait, Type}; | 7 | use hir::{Crate, Enum, Module, ScopeDef, Semantics, Trait}; |
8 | use ide_db::RootDatabase; | 8 | use ide_db::RootDatabase; |
9 | use itertools::Itertools; | 9 | use itertools::Itertools; |
10 | use rustc_hash::FxHashSet; | ||
11 | use syntax::{ | 10 | use syntax::{ |
12 | ast::{self, make, ArgListOwner, NameOwner}, | 11 | ast::{self, make, ArgListOwner}, |
13 | AstNode, Direction, | 12 | AstNode, Direction, |
14 | SyntaxKind::*, | 13 | SyntaxKind::*, |
15 | SyntaxNode, TextSize, T, | 14 | SyntaxNode, TextSize, T, |
@@ -115,72 +114,6 @@ pub(crate) fn render_snippet(_cap: SnippetCap, node: &SyntaxNode, cursor: Cursor | |||
115 | } | 114 | } |
116 | } | 115 | } |
117 | 116 | ||
118 | pub fn get_missing_assoc_items( | ||
119 | sema: &Semantics<RootDatabase>, | ||
120 | impl_def: &ast::Impl, | ||
121 | ) -> Vec<hir::AssocItem> { | ||
122 | // Names must be unique between constants and functions. However, type aliases | ||
123 | // may share the same name as a function or constant. | ||
124 | let mut impl_fns_consts = FxHashSet::default(); | ||
125 | let mut impl_type = FxHashSet::default(); | ||
126 | |||
127 | if let Some(item_list) = impl_def.assoc_item_list() { | ||
128 | for item in item_list.assoc_items() { | ||
129 | match item { | ||
130 | ast::AssocItem::Fn(f) => { | ||
131 | if let Some(n) = f.name() { | ||
132 | impl_fns_consts.insert(n.syntax().to_string()); | ||
133 | } | ||
134 | } | ||
135 | |||
136 | ast::AssocItem::TypeAlias(t) => { | ||
137 | if let Some(n) = t.name() { | ||
138 | impl_type.insert(n.syntax().to_string()); | ||
139 | } | ||
140 | } | ||
141 | |||
142 | ast::AssocItem::Const(c) => { | ||
143 | if let Some(n) = c.name() { | ||
144 | impl_fns_consts.insert(n.syntax().to_string()); | ||
145 | } | ||
146 | } | ||
147 | ast::AssocItem::MacroCall(_) => (), | ||
148 | } | ||
149 | } | ||
150 | } | ||
151 | |||
152 | resolve_target_trait(sema, impl_def).map_or(vec![], |target_trait| { | ||
153 | target_trait | ||
154 | .items(sema.db) | ||
155 | .iter() | ||
156 | .filter(|i| match i { | ||
157 | hir::AssocItem::Function(f) => { | ||
158 | !impl_fns_consts.contains(&f.name(sema.db).to_string()) | ||
159 | } | ||
160 | hir::AssocItem::TypeAlias(t) => !impl_type.contains(&t.name(sema.db).to_string()), | ||
161 | hir::AssocItem::Const(c) => c | ||
162 | .name(sema.db) | ||
163 | .map(|n| !impl_fns_consts.contains(&n.to_string())) | ||
164 | .unwrap_or_default(), | ||
165 | }) | ||
166 | .cloned() | ||
167 | .collect() | ||
168 | }) | ||
169 | } | ||
170 | |||
171 | pub(crate) fn resolve_target_trait( | ||
172 | sema: &Semantics<RootDatabase>, | ||
173 | impl_def: &ast::Impl, | ||
174 | ) -> Option<hir::Trait> { | ||
175 | let ast_path = | ||
176 | impl_def.trait_().map(|it| it.syntax().clone()).and_then(ast::PathType::cast)?.path()?; | ||
177 | |||
178 | match sema.resolve_path(&ast_path) { | ||
179 | Some(hir::PathResolution::Def(hir::ModuleDef::Trait(def))) => Some(def), | ||
180 | _ => None, | ||
181 | } | ||
182 | } | ||
183 | |||
184 | pub(crate) fn vis_offset(node: &SyntaxNode) -> TextSize { | 117 | pub(crate) fn vis_offset(node: &SyntaxNode) -> TextSize { |
185 | node.children_with_tokens() | 118 | node.children_with_tokens() |
186 | .find(|it| !matches!(it.kind(), WHITESPACE | COMMENT | ATTR)) | 119 | .find(|it| !matches!(it.kind(), WHITESPACE | COMMENT | ATTR)) |
@@ -223,54 +156,6 @@ fn invert_special_case(expr: &ast::Expr) -> Option<ast::Expr> { | |||
223 | } | 156 | } |
224 | } | 157 | } |
225 | 158 | ||
226 | #[derive(Clone, Copy)] | ||
227 | pub enum TryEnum { | ||
228 | Result, | ||
229 | Option, | ||
230 | } | ||
231 | |||
232 | impl TryEnum { | ||
233 | const ALL: [TryEnum; 2] = [TryEnum::Option, TryEnum::Result]; | ||
234 | |||
235 | pub fn from_ty(sema: &Semantics<RootDatabase>, ty: &Type) -> Option<TryEnum> { | ||
236 | let enum_ = match ty.as_adt() { | ||
237 | Some(Adt::Enum(it)) => it, | ||
238 | _ => return None, | ||
239 | }; | ||
240 | TryEnum::ALL.iter().find_map(|&var| { | ||
241 | if &enum_.name(sema.db).to_string() == var.type_name() { | ||
242 | return Some(var); | ||
243 | } | ||
244 | None | ||
245 | }) | ||
246 | } | ||
247 | |||
248 | pub(crate) fn happy_case(self) -> &'static str { | ||
249 | match self { | ||
250 | TryEnum::Result => "Ok", | ||
251 | TryEnum::Option => "Some", | ||
252 | } | ||
253 | } | ||
254 | |||
255 | pub(crate) fn sad_pattern(self) -> ast::Pat { | ||
256 | match self { | ||
257 | TryEnum::Result => make::tuple_struct_pat( | ||
258 | make::path_unqualified(make::path_segment(make::name_ref("Err"))), | ||
259 | iter::once(make::wildcard_pat().into()), | ||
260 | ) | ||
261 | .into(), | ||
262 | TryEnum::Option => make::ident_pat(make::name("None")).into(), | ||
263 | } | ||
264 | } | ||
265 | |||
266 | fn type_name(self) -> &'static str { | ||
267 | match self { | ||
268 | TryEnum::Result => "Result", | ||
269 | TryEnum::Option => "Option", | ||
270 | } | ||
271 | } | ||
272 | } | ||
273 | |||
274 | /// Helps with finding well-know things inside the standard library. This is | 159 | /// Helps with finding well-know things inside the standard library. This is |
275 | /// somewhat similar to the known paths infra inside hir, but it different; We | 160 | /// somewhat similar to the known paths infra inside hir, but it different; We |
276 | /// want to make sure that IDE specific paths don't become interesting inside | 161 | /// want to make sure that IDE specific paths don't become interesting inside |
diff --git a/crates/assists/src/utils/insert_use.rs b/crates/assists/src/utils/insert_use.rs index 409985b3b..033fbcedc 100644 --- a/crates/assists/src/utils/insert_use.rs +++ b/crates/assists/src/utils/insert_use.rs | |||
@@ -14,6 +14,7 @@ use syntax::{ | |||
14 | }, | 14 | }, |
15 | InsertPosition, SyntaxElement, SyntaxNode, | 15 | InsertPosition, SyntaxElement, SyntaxNode, |
16 | }; | 16 | }; |
17 | use test_utils::mark; | ||
17 | 18 | ||
18 | #[derive(Debug)] | 19 | #[derive(Debug)] |
19 | pub enum ImportScope { | 20 | pub enum ImportScope { |
@@ -109,6 +110,12 @@ pub(crate) fn insert_use( | |||
109 | // so look for the place we have to insert to | 110 | // so look for the place we have to insert to |
110 | let (insert_position, add_blank) = find_insert_position(scope, path); | 111 | let (insert_position, add_blank) = find_insert_position(scope, path); |
111 | 112 | ||
113 | let indent = if let ident_level @ 1..=usize::MAX = scope.indent_level().0 as usize { | ||
114 | Some(make::tokens::whitespace(&" ".repeat(4 * ident_level)).into()) | ||
115 | } else { | ||
116 | None | ||
117 | }; | ||
118 | |||
112 | let to_insert: Vec<SyntaxElement> = { | 119 | let to_insert: Vec<SyntaxElement> = { |
113 | let mut buf = Vec::new(); | 120 | let mut buf = Vec::new(); |
114 | 121 | ||
@@ -120,9 +127,13 @@ pub(crate) fn insert_use( | |||
120 | _ => (), | 127 | _ => (), |
121 | } | 128 | } |
122 | 129 | ||
123 | if let ident_level @ 1..=usize::MAX = scope.indent_level().0 as usize { | 130 | if add_blank.has_before() { |
124 | buf.push(make::tokens::whitespace(&" ".repeat(4 * ident_level)).into()); | 131 | if let Some(indent) = indent.clone() { |
132 | mark::hit!(insert_use_indent_before); | ||
133 | buf.push(indent); | ||
134 | } | ||
125 | } | 135 | } |
136 | |||
126 | buf.push(use_item.syntax().clone().into()); | 137 | buf.push(use_item.syntax().clone().into()); |
127 | 138 | ||
128 | match add_blank { | 139 | match add_blank { |
@@ -133,6 +144,16 @@ pub(crate) fn insert_use( | |||
133 | _ => (), | 144 | _ => (), |
134 | } | 145 | } |
135 | 146 | ||
147 | // only add indentation *after* our stuff if there's another node directly after it | ||
148 | if add_blank.has_after() && matches!(insert_position, InsertPosition::Before(_)) { | ||
149 | if let Some(indent) = indent { | ||
150 | mark::hit!(insert_use_indent_after); | ||
151 | buf.push(indent); | ||
152 | } | ||
153 | } else if add_blank.has_after() && matches!(insert_position, InsertPosition::After(_)) { | ||
154 | mark::hit!(insert_use_no_indent_after); | ||
155 | } | ||
156 | |||
136 | buf | 157 | buf |
137 | }; | 158 | }; |
138 | 159 | ||
@@ -470,6 +491,15 @@ enum AddBlankLine { | |||
470 | AfterTwice, | 491 | AfterTwice, |
471 | } | 492 | } |
472 | 493 | ||
494 | impl AddBlankLine { | ||
495 | fn has_before(&self) -> bool { | ||
496 | matches!(self, AddBlankLine::Before | AddBlankLine::BeforeTwice | AddBlankLine::Around) | ||
497 | } | ||
498 | fn has_after(&self) -> bool { | ||
499 | matches!(self, AddBlankLine::After | AddBlankLine::AfterTwice | AddBlankLine::Around) | ||
500 | } | ||
501 | } | ||
502 | |||
473 | fn find_insert_position( | 503 | fn find_insert_position( |
474 | scope: &ImportScope, | 504 | scope: &ImportScope, |
475 | insert_path: ast::Path, | 505 | insert_path: ast::Path, |
@@ -562,6 +592,21 @@ use std::bar::G;", | |||
562 | } | 592 | } |
563 | 593 | ||
564 | #[test] | 594 | #[test] |
595 | fn insert_start_indent() { | ||
596 | mark::check!(insert_use_indent_after); | ||
597 | check_none( | ||
598 | "std::bar::AA", | ||
599 | r" | ||
600 | use std::bar::B; | ||
601 | use std::bar::D;", | ||
602 | r" | ||
603 | use std::bar::AA; | ||
604 | use std::bar::B; | ||
605 | use std::bar::D;", | ||
606 | ) | ||
607 | } | ||
608 | |||
609 | #[test] | ||
565 | fn insert_middle() { | 610 | fn insert_middle() { |
566 | check_none( | 611 | check_none( |
567 | "std::bar::EE", | 612 | "std::bar::EE", |
@@ -580,6 +625,24 @@ use std::bar::G;", | |||
580 | } | 625 | } |
581 | 626 | ||
582 | #[test] | 627 | #[test] |
628 | fn insert_middle_indent() { | ||
629 | check_none( | ||
630 | "std::bar::EE", | ||
631 | r" | ||
632 | use std::bar::A; | ||
633 | use std::bar::D; | ||
634 | use std::bar::F; | ||
635 | use std::bar::G;", | ||
636 | r" | ||
637 | use std::bar::A; | ||
638 | use std::bar::D; | ||
639 | use std::bar::EE; | ||
640 | use std::bar::F; | ||
641 | use std::bar::G;", | ||
642 | ) | ||
643 | } | ||
644 | |||
645 | #[test] | ||
583 | fn insert_end() { | 646 | fn insert_end() { |
584 | check_none( | 647 | check_none( |
585 | "std::bar::ZZ", | 648 | "std::bar::ZZ", |
@@ -598,6 +661,25 @@ use std::bar::ZZ;", | |||
598 | } | 661 | } |
599 | 662 | ||
600 | #[test] | 663 | #[test] |
664 | fn insert_end_indent() { | ||
665 | mark::check!(insert_use_indent_before); | ||
666 | check_none( | ||
667 | "std::bar::ZZ", | ||
668 | r" | ||
669 | use std::bar::A; | ||
670 | use std::bar::D; | ||
671 | use std::bar::F; | ||
672 | use std::bar::G;", | ||
673 | r" | ||
674 | use std::bar::A; | ||
675 | use std::bar::D; | ||
676 | use std::bar::F; | ||
677 | use std::bar::G; | ||
678 | use std::bar::ZZ;", | ||
679 | ) | ||
680 | } | ||
681 | |||
682 | #[test] | ||
601 | fn insert_middle_nested() { | 683 | fn insert_middle_nested() { |
602 | check_none( | 684 | check_none( |
603 | "std::bar::EE", | 685 | "std::bar::EE", |
@@ -620,18 +702,18 @@ use std::bar::G;", | |||
620 | check_none( | 702 | check_none( |
621 | "foo::bar::GG", | 703 | "foo::bar::GG", |
622 | r" | 704 | r" |
623 | use std::bar::A; | 705 | use std::bar::A; |
624 | use std::bar::D; | 706 | use std::bar::D; |
625 | 707 | ||
626 | use foo::bar::F; | 708 | use foo::bar::F; |
627 | use foo::bar::H;", | 709 | use foo::bar::H;", |
628 | r" | 710 | r" |
629 | use std::bar::A; | 711 | use std::bar::A; |
630 | use std::bar::D; | 712 | use std::bar::D; |
631 | 713 | ||
632 | use foo::bar::F; | 714 | use foo::bar::F; |
633 | use foo::bar::GG; | 715 | use foo::bar::GG; |
634 | use foo::bar::H;", | 716 | use foo::bar::H;", |
635 | ) | 717 | ) |
636 | } | 718 | } |
637 | 719 | ||
@@ -640,22 +722,22 @@ use foo::bar::H;", | |||
640 | check_none( | 722 | check_none( |
641 | "foo::bar::GG", | 723 | "foo::bar::GG", |
642 | r" | 724 | r" |
643 | use foo::bar::A; | 725 | use foo::bar::A; |
644 | use foo::bar::D; | 726 | use foo::bar::D; |
645 | 727 | ||
646 | use std; | 728 | use std; |
647 | 729 | ||
648 | use foo::bar::F; | 730 | use foo::bar::F; |
649 | use foo::bar::H;", | 731 | use foo::bar::H;", |
650 | r" | 732 | r" |
651 | use foo::bar::A; | 733 | use foo::bar::A; |
652 | use foo::bar::D; | 734 | use foo::bar::D; |
653 | use foo::bar::GG; | 735 | use foo::bar::GG; |
654 | 736 | ||
655 | use std; | 737 | use std; |
656 | 738 | ||
657 | use foo::bar::F; | 739 | use foo::bar::F; |
658 | use foo::bar::H;", | 740 | use foo::bar::H;", |
659 | ) | 741 | ) |
660 | } | 742 | } |
661 | 743 | ||
@@ -664,13 +746,13 @@ use foo::bar::H;", | |||
664 | check_none( | 746 | check_none( |
665 | "std::fmt", | 747 | "std::fmt", |
666 | r" | 748 | r" |
667 | use foo::bar::A; | 749 | use foo::bar::A; |
668 | use foo::bar::D;", | 750 | use foo::bar::D;", |
669 | r" | 751 | r" |
670 | use std::fmt; | 752 | use std::fmt; |
671 | 753 | ||
672 | use foo::bar::A; | 754 | use foo::bar::A; |
673 | use foo::bar::D;", | 755 | use foo::bar::D;", |
674 | ) | 756 | ) |
675 | } | 757 | } |
676 | 758 | ||
@@ -714,6 +796,20 @@ fn main() {}", | |||
714 | } | 796 | } |
715 | 797 | ||
716 | #[test] | 798 | #[test] |
799 | fn insert_empty_module() { | ||
800 | mark::check!(insert_use_no_indent_after); | ||
801 | check( | ||
802 | "foo::bar", | ||
803 | "mod x {}", | ||
804 | r"{ | ||
805 | use foo::bar; | ||
806 | }", | ||
807 | None, | ||
808 | true, | ||
809 | ) | ||
810 | } | ||
811 | |||
812 | #[test] | ||
717 | fn insert_after_inner_attr() { | 813 | fn insert_after_inner_attr() { |
718 | check_full( | 814 | check_full( |
719 | "foo::bar", | 815 | "foo::bar", |
@@ -991,11 +1087,13 @@ use foo::bar::baz::Qux;", | |||
991 | ra_fixture_before: &str, | 1087 | ra_fixture_before: &str, |
992 | ra_fixture_after: &str, | 1088 | ra_fixture_after: &str, |
993 | mb: Option<MergeBehaviour>, | 1089 | mb: Option<MergeBehaviour>, |
1090 | module: bool, | ||
994 | ) { | 1091 | ) { |
995 | let file = super::ImportScope::from( | 1092 | let mut syntax = ast::SourceFile::parse(ra_fixture_before).tree().syntax().clone(); |
996 | ast::SourceFile::parse(ra_fixture_before).tree().syntax().clone(), | 1093 | if module { |
997 | ) | 1094 | syntax = syntax.descendants().find_map(ast::Module::cast).unwrap().syntax().clone(); |
998 | .unwrap(); | 1095 | } |
1096 | let file = super::ImportScope::from(syntax).unwrap(); | ||
999 | let path = ast::SourceFile::parse(&format!("use {};", path)) | 1097 | let path = ast::SourceFile::parse(&format!("use {};", path)) |
1000 | .tree() | 1098 | .tree() |
1001 | .syntax() | 1099 | .syntax() |
@@ -1008,15 +1106,15 @@ use foo::bar::baz::Qux;", | |||
1008 | } | 1106 | } |
1009 | 1107 | ||
1010 | fn check_full(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) { | 1108 | fn check_full(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) { |
1011 | check(path, ra_fixture_before, ra_fixture_after, Some(MergeBehaviour::Full)) | 1109 | check(path, ra_fixture_before, ra_fixture_after, Some(MergeBehaviour::Full), false) |
1012 | } | 1110 | } |
1013 | 1111 | ||
1014 | fn check_last(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) { | 1112 | fn check_last(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) { |
1015 | check(path, ra_fixture_before, ra_fixture_after, Some(MergeBehaviour::Last)) | 1113 | check(path, ra_fixture_before, ra_fixture_after, Some(MergeBehaviour::Last), false) |
1016 | } | 1114 | } |
1017 | 1115 | ||
1018 | fn check_none(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) { | 1116 | fn check_none(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) { |
1019 | check(path, ra_fixture_before, ra_fixture_after, None) | 1117 | check(path, ra_fixture_before, ra_fixture_after, None, false) |
1020 | } | 1118 | } |
1021 | 1119 | ||
1022 | fn check_merge_only_fail(ra_fixture0: &str, ra_fixture1: &str, mb: MergeBehaviour) { | 1120 | fn check_merge_only_fail(ra_fixture0: &str, ra_fixture1: &str, mb: MergeBehaviour) { |
diff --git a/crates/call_info/Cargo.toml b/crates/call_info/Cargo.toml deleted file mode 100644 index 98c0bd6db..000000000 --- a/crates/call_info/Cargo.toml +++ /dev/null | |||
@@ -1,26 +0,0 @@ | |||
1 | [package] | ||
2 | name = "call_info" | ||
3 | version = "0.0.0" | ||
4 | description = "TBD" | ||
5 | license = "MIT OR Apache-2.0" | ||
6 | authors = ["rust-analyzer developers"] | ||
7 | edition = "2018" | ||
8 | |||
9 | [lib] | ||
10 | doctest = false | ||
11 | |||
12 | [dependencies] | ||
13 | either = "1.5.3" | ||
14 | |||
15 | stdx = { path = "../stdx", version = "0.0.0" } | ||
16 | syntax = { path = "../syntax", version = "0.0.0" } | ||
17 | base_db = { path = "../base_db", version = "0.0.0" } | ||
18 | ide_db = { path = "../ide_db", version = "0.0.0" } | ||
19 | test_utils = { path = "../test_utils", version = "0.0.0" } | ||
20 | |||
21 | # call_info crate should depend only on the top-level `hir` package. if you need | ||
22 | # something from some `hir_xxx` subpackage, reexport the API via `hir`. | ||
23 | hir = { path = "../hir", version = "0.0.0" } | ||
24 | |||
25 | [dev-dependencies] | ||
26 | expect-test = "1.0" | ||
diff --git a/crates/cfg/Cargo.toml b/crates/cfg/Cargo.toml index a6785ee8e..c68e391c1 100644 --- a/crates/cfg/Cargo.toml +++ b/crates/cfg/Cargo.toml | |||
@@ -17,3 +17,4 @@ tt = { path = "../tt", version = "0.0.0" } | |||
17 | [dev-dependencies] | 17 | [dev-dependencies] |
18 | mbe = { path = "../mbe" } | 18 | mbe = { path = "../mbe" } |
19 | syntax = { path = "../syntax" } | 19 | syntax = { path = "../syntax" } |
20 | expect-test = "1.0" | ||
diff --git a/crates/cfg/src/cfg_expr.rs b/crates/cfg/src/cfg_expr.rs index 336fe25bc..42327f1e1 100644 --- a/crates/cfg/src/cfg_expr.rs +++ b/crates/cfg/src/cfg_expr.rs | |||
@@ -2,30 +2,77 @@ | |||
2 | //! | 2 | //! |
3 | //! See: https://doc.rust-lang.org/reference/conditional-compilation.html#conditional-compilation | 3 | //! See: https://doc.rust-lang.org/reference/conditional-compilation.html#conditional-compilation |
4 | 4 | ||
5 | use std::slice::Iter as SliceIter; | 5 | use std::{fmt, slice::Iter as SliceIter}; |
6 | 6 | ||
7 | use tt::SmolStr; | 7 | use tt::SmolStr; |
8 | 8 | ||
9 | /// A simple configuration value passed in from the outside. | ||
10 | #[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)] | ||
11 | pub enum CfgAtom { | ||
12 | /// eg. `#[cfg(test)]` | ||
13 | Flag(SmolStr), | ||
14 | /// eg. `#[cfg(target_os = "linux")]` | ||
15 | /// | ||
16 | /// Note that a key can have multiple values that are all considered "active" at the same time. | ||
17 | /// For example, `#[cfg(target_feature = "sse")]` and `#[cfg(target_feature = "sse2")]`. | ||
18 | KeyValue { key: SmolStr, value: SmolStr }, | ||
19 | } | ||
20 | |||
21 | impl CfgAtom { | ||
22 | /// Returns `true` when the atom comes from the target specification. | ||
23 | /// | ||
24 | /// If this returns `true`, then changing this atom requires changing the compilation target. If | ||
25 | /// it returns `false`, the atom might come from a build script or the build system. | ||
26 | pub fn is_target_defined(&self) -> bool { | ||
27 | match self { | ||
28 | CfgAtom::Flag(flag) => matches!(&**flag, "unix" | "windows"), | ||
29 | CfgAtom::KeyValue { key, value: _ } => matches!( | ||
30 | &**key, | ||
31 | "target_arch" | ||
32 | | "target_os" | ||
33 | | "target_env" | ||
34 | | "target_family" | ||
35 | | "target_endian" | ||
36 | | "target_pointer_width" | ||
37 | | "target_vendor" // NOTE: `target_feature` is left out since it can be configured via `-Ctarget-feature` | ||
38 | ), | ||
39 | } | ||
40 | } | ||
41 | } | ||
42 | |||
43 | impl fmt::Display for CfgAtom { | ||
44 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
45 | match self { | ||
46 | CfgAtom::Flag(name) => write!(f, "{}", name), | ||
47 | CfgAtom::KeyValue { key, value } => write!(f, "{} = {:?}", key, value), | ||
48 | } | ||
49 | } | ||
50 | } | ||
51 | |||
9 | #[derive(Debug, Clone, PartialEq, Eq)] | 52 | #[derive(Debug, Clone, PartialEq, Eq)] |
10 | pub enum CfgExpr { | 53 | pub enum CfgExpr { |
11 | Invalid, | 54 | Invalid, |
12 | Atom(SmolStr), | 55 | Atom(CfgAtom), |
13 | KeyValue { key: SmolStr, value: SmolStr }, | ||
14 | All(Vec<CfgExpr>), | 56 | All(Vec<CfgExpr>), |
15 | Any(Vec<CfgExpr>), | 57 | Any(Vec<CfgExpr>), |
16 | Not(Box<CfgExpr>), | 58 | Not(Box<CfgExpr>), |
17 | } | 59 | } |
18 | 60 | ||
61 | impl From<CfgAtom> for CfgExpr { | ||
62 | fn from(atom: CfgAtom) -> Self { | ||
63 | CfgExpr::Atom(atom) | ||
64 | } | ||
65 | } | ||
66 | |||
19 | impl CfgExpr { | 67 | impl CfgExpr { |
20 | pub fn parse(tt: &tt::Subtree) -> CfgExpr { | 68 | pub fn parse(tt: &tt::Subtree) -> CfgExpr { |
21 | next_cfg_expr(&mut tt.token_trees.iter()).unwrap_or(CfgExpr::Invalid) | 69 | next_cfg_expr(&mut tt.token_trees.iter()).unwrap_or(CfgExpr::Invalid) |
22 | } | 70 | } |
23 | /// Fold the cfg by querying all basic `Atom` and `KeyValue` predicates. | 71 | /// Fold the cfg by querying all basic `Atom` and `KeyValue` predicates. |
24 | pub fn fold(&self, query: &dyn Fn(&SmolStr, Option<&SmolStr>) -> bool) -> Option<bool> { | 72 | pub fn fold(&self, query: &dyn Fn(&CfgAtom) -> bool) -> Option<bool> { |
25 | match self { | 73 | match self { |
26 | CfgExpr::Invalid => None, | 74 | CfgExpr::Invalid => None, |
27 | CfgExpr::Atom(name) => Some(query(name, None)), | 75 | CfgExpr::Atom(atom) => Some(query(atom)), |
28 | CfgExpr::KeyValue { key, value } => Some(query(key, Some(value))), | ||
29 | CfgExpr::All(preds) => { | 76 | CfgExpr::All(preds) => { |
30 | preds.iter().try_fold(true, |s, pred| Some(s && pred.fold(query)?)) | 77 | preds.iter().try_fold(true, |s, pred| Some(s && pred.fold(query)?)) |
31 | } | 78 | } |
@@ -54,7 +101,7 @@ fn next_cfg_expr(it: &mut SliceIter<tt::TokenTree>) -> Option<CfgExpr> { | |||
54 | // FIXME: escape? raw string? | 101 | // FIXME: escape? raw string? |
55 | let value = | 102 | let value = |
56 | SmolStr::new(literal.text.trim_start_matches('"').trim_end_matches('"')); | 103 | SmolStr::new(literal.text.trim_start_matches('"').trim_end_matches('"')); |
57 | CfgExpr::KeyValue { key: name, value } | 104 | CfgAtom::KeyValue { key: name, value }.into() |
58 | } | 105 | } |
59 | _ => return Some(CfgExpr::Invalid), | 106 | _ => return Some(CfgExpr::Invalid), |
60 | } | 107 | } |
@@ -70,7 +117,7 @@ fn next_cfg_expr(it: &mut SliceIter<tt::TokenTree>) -> Option<CfgExpr> { | |||
70 | _ => CfgExpr::Invalid, | 117 | _ => CfgExpr::Invalid, |
71 | } | 118 | } |
72 | } | 119 | } |
73 | _ => CfgExpr::Atom(name), | 120 | _ => CfgAtom::Flag(name).into(), |
74 | }; | 121 | }; |
75 | 122 | ||
76 | // Eat comma separator | 123 | // Eat comma separator |
@@ -81,53 +128,3 @@ fn next_cfg_expr(it: &mut SliceIter<tt::TokenTree>) -> Option<CfgExpr> { | |||
81 | } | 128 | } |
82 | Some(ret) | 129 | Some(ret) |
83 | } | 130 | } |
84 | |||
85 | #[cfg(test)] | ||
86 | mod tests { | ||
87 | use super::*; | ||
88 | |||
89 | use mbe::ast_to_token_tree; | ||
90 | use syntax::ast::{self, AstNode}; | ||
91 | |||
92 | fn assert_parse_result(input: &str, expected: CfgExpr) { | ||
93 | let (tt, _) = { | ||
94 | let source_file = ast::SourceFile::parse(input).ok().unwrap(); | ||
95 | let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap(); | ||
96 | ast_to_token_tree(&tt).unwrap() | ||
97 | }; | ||
98 | let cfg = CfgExpr::parse(&tt); | ||
99 | assert_eq!(cfg, expected); | ||
100 | } | ||
101 | |||
102 | #[test] | ||
103 | fn test_cfg_expr_parser() { | ||
104 | assert_parse_result("#![cfg(foo)]", CfgExpr::Atom("foo".into())); | ||
105 | assert_parse_result("#![cfg(foo,)]", CfgExpr::Atom("foo".into())); | ||
106 | assert_parse_result( | ||
107 | "#![cfg(not(foo))]", | ||
108 | CfgExpr::Not(Box::new(CfgExpr::Atom("foo".into()))), | ||
109 | ); | ||
110 | assert_parse_result("#![cfg(foo(bar))]", CfgExpr::Invalid); | ||
111 | |||
112 | // Only take the first | ||
113 | assert_parse_result(r#"#![cfg(foo, bar = "baz")]"#, CfgExpr::Atom("foo".into())); | ||
114 | |||
115 | assert_parse_result( | ||
116 | r#"#![cfg(all(foo, bar = "baz"))]"#, | ||
117 | CfgExpr::All(vec![ | ||
118 | CfgExpr::Atom("foo".into()), | ||
119 | CfgExpr::KeyValue { key: "bar".into(), value: "baz".into() }, | ||
120 | ]), | ||
121 | ); | ||
122 | |||
123 | assert_parse_result( | ||
124 | r#"#![cfg(any(not(), all(), , bar = "baz",))]"#, | ||
125 | CfgExpr::Any(vec![ | ||
126 | CfgExpr::Not(Box::new(CfgExpr::Invalid)), | ||
127 | CfgExpr::All(vec![]), | ||
128 | CfgExpr::Invalid, | ||
129 | CfgExpr::KeyValue { key: "bar".into(), value: "baz".into() }, | ||
130 | ]), | ||
131 | ); | ||
132 | } | ||
133 | } | ||
diff --git a/crates/cfg/src/dnf.rs b/crates/cfg/src/dnf.rs new file mode 100644 index 000000000..580c9a9a2 --- /dev/null +++ b/crates/cfg/src/dnf.rs | |||
@@ -0,0 +1,320 @@ | |||
1 | //! Disjunctive Normal Form construction. | ||
2 | //! | ||
3 | //! Algorithm from <https://www.cs.drexel.edu/~jjohnson/2015-16/fall/CS270/Lectures/3/dnf.pdf>, | ||
4 | //! which would have been much easier to read if it used pattern matching. It's also missing the | ||
5 | //! entire "distribute ANDs over ORs" part, which is not trivial. Oh well. | ||
6 | //! | ||
7 | //! This is currently both messy and inefficient. Feel free to improve, there are unit tests. | ||
8 | |||
9 | use std::fmt; | ||
10 | |||
11 | use rustc_hash::FxHashSet; | ||
12 | |||
13 | use crate::{CfgAtom, CfgDiff, CfgExpr, CfgOptions, InactiveReason}; | ||
14 | |||
15 | /// A `#[cfg]` directive in Disjunctive Normal Form (DNF). | ||
16 | pub struct DnfExpr { | ||
17 | conjunctions: Vec<Conjunction>, | ||
18 | } | ||
19 | |||
20 | struct Conjunction { | ||
21 | literals: Vec<Literal>, | ||
22 | } | ||
23 | |||
24 | struct Literal { | ||
25 | negate: bool, | ||
26 | var: Option<CfgAtom>, // None = Invalid | ||
27 | } | ||
28 | |||
29 | impl DnfExpr { | ||
30 | pub fn new(expr: CfgExpr) -> Self { | ||
31 | let builder = Builder { expr: DnfExpr { conjunctions: Vec::new() } }; | ||
32 | |||
33 | builder.lower(expr.clone()) | ||
34 | } | ||
35 | |||
36 | /// Computes a list of present or absent atoms in `opts` that cause this expression to evaluate | ||
37 | /// to `false`. | ||
38 | /// | ||
39 | /// Note that flipping a subset of these atoms might be sufficient to make the whole expression | ||
40 | /// evaluate to `true`. For that, see `compute_enable_hints`. | ||
41 | /// | ||
42 | /// Returns `None` when `self` is already true, or contains errors. | ||
43 | pub fn why_inactive(&self, opts: &CfgOptions) -> Option<InactiveReason> { | ||
44 | let mut res = InactiveReason { enabled: Vec::new(), disabled: Vec::new() }; | ||
45 | |||
46 | for conj in &self.conjunctions { | ||
47 | let mut conj_is_true = true; | ||
48 | for lit in &conj.literals { | ||
49 | let atom = lit.var.as_ref()?; | ||
50 | let enabled = opts.enabled.contains(atom); | ||
51 | if lit.negate == enabled { | ||
52 | // Literal is false, but needs to be true for this conjunction. | ||
53 | conj_is_true = false; | ||
54 | |||
55 | if enabled { | ||
56 | res.enabled.push(atom.clone()); | ||
57 | } else { | ||
58 | res.disabled.push(atom.clone()); | ||
59 | } | ||
60 | } | ||
61 | } | ||
62 | |||
63 | if conj_is_true { | ||
64 | // This expression is not actually inactive. | ||
65 | return None; | ||
66 | } | ||
67 | } | ||
68 | |||
69 | res.enabled.sort_unstable(); | ||
70 | res.enabled.dedup(); | ||
71 | res.disabled.sort_unstable(); | ||
72 | res.disabled.dedup(); | ||
73 | Some(res) | ||
74 | } | ||
75 | |||
76 | /// Returns `CfgDiff` objects that would enable this directive if applied to `opts`. | ||
77 | pub fn compute_enable_hints<'a>( | ||
78 | &'a self, | ||
79 | opts: &'a CfgOptions, | ||
80 | ) -> impl Iterator<Item = CfgDiff> + 'a { | ||
81 | // A cfg is enabled if any of `self.conjunctions` evaluate to `true`. | ||
82 | |||
83 | self.conjunctions.iter().filter_map(move |conj| { | ||
84 | let mut enable = FxHashSet::default(); | ||
85 | let mut disable = FxHashSet::default(); | ||
86 | for lit in &conj.literals { | ||
87 | let atom = lit.var.as_ref()?; | ||
88 | let enabled = opts.enabled.contains(atom); | ||
89 | if lit.negate && enabled { | ||
90 | disable.insert(atom.clone()); | ||
91 | } | ||
92 | if !lit.negate && !enabled { | ||
93 | enable.insert(atom.clone()); | ||
94 | } | ||
95 | } | ||
96 | |||
97 | // Check that this actually makes `conj` true. | ||
98 | for lit in &conj.literals { | ||
99 | let atom = lit.var.as_ref()?; | ||
100 | let enabled = enable.contains(atom) | ||
101 | || (opts.enabled.contains(atom) && !disable.contains(atom)); | ||
102 | if enabled == lit.negate { | ||
103 | return None; | ||
104 | } | ||
105 | } | ||
106 | |||
107 | if enable.is_empty() && disable.is_empty() { | ||
108 | return None; | ||
109 | } | ||
110 | |||
111 | let mut diff = CfgDiff { | ||
112 | enable: enable.into_iter().collect(), | ||
113 | disable: disable.into_iter().collect(), | ||
114 | }; | ||
115 | |||
116 | // Undo the FxHashMap randomization for consistent output. | ||
117 | diff.enable.sort_unstable(); | ||
118 | diff.disable.sort_unstable(); | ||
119 | |||
120 | Some(diff) | ||
121 | }) | ||
122 | } | ||
123 | } | ||
124 | |||
125 | impl fmt::Display for DnfExpr { | ||
126 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
127 | if self.conjunctions.len() != 1 { | ||
128 | write!(f, "any(")?; | ||
129 | } | ||
130 | for (i, conj) in self.conjunctions.iter().enumerate() { | ||
131 | if i != 0 { | ||
132 | f.write_str(", ")?; | ||
133 | } | ||
134 | |||
135 | write!(f, "{}", conj)?; | ||
136 | } | ||
137 | if self.conjunctions.len() != 1 { | ||
138 | write!(f, ")")?; | ||
139 | } | ||
140 | |||
141 | Ok(()) | ||
142 | } | ||
143 | } | ||
144 | |||
145 | impl Conjunction { | ||
146 | fn new(parts: Vec<CfgExpr>) -> Self { | ||
147 | let mut literals = Vec::new(); | ||
148 | for part in parts { | ||
149 | match part { | ||
150 | CfgExpr::Invalid | CfgExpr::Atom(_) | CfgExpr::Not(_) => { | ||
151 | literals.push(Literal::new(part)); | ||
152 | } | ||
153 | CfgExpr::All(conj) => { | ||
154 | // Flatten. | ||
155 | literals.extend(Conjunction::new(conj).literals); | ||
156 | } | ||
157 | CfgExpr::Any(_) => unreachable!("disjunction in conjunction"), | ||
158 | } | ||
159 | } | ||
160 | |||
161 | Self { literals } | ||
162 | } | ||
163 | } | ||
164 | |||
165 | impl fmt::Display for Conjunction { | ||
166 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
167 | if self.literals.len() != 1 { | ||
168 | write!(f, "all(")?; | ||
169 | } | ||
170 | for (i, lit) in self.literals.iter().enumerate() { | ||
171 | if i != 0 { | ||
172 | f.write_str(", ")?; | ||
173 | } | ||
174 | |||
175 | write!(f, "{}", lit)?; | ||
176 | } | ||
177 | if self.literals.len() != 1 { | ||
178 | write!(f, ")")?; | ||
179 | } | ||
180 | |||
181 | Ok(()) | ||
182 | } | ||
183 | } | ||
184 | |||
185 | impl Literal { | ||
186 | fn new(expr: CfgExpr) -> Self { | ||
187 | match expr { | ||
188 | CfgExpr::Invalid => Self { negate: false, var: None }, | ||
189 | CfgExpr::Atom(atom) => Self { negate: false, var: Some(atom) }, | ||
190 | CfgExpr::Not(expr) => match *expr { | ||
191 | CfgExpr::Invalid => Self { negate: true, var: None }, | ||
192 | CfgExpr::Atom(atom) => Self { negate: true, var: Some(atom) }, | ||
193 | _ => unreachable!("non-atom {:?}", expr), | ||
194 | }, | ||
195 | CfgExpr::Any(_) | CfgExpr::All(_) => unreachable!("non-literal {:?}", expr), | ||
196 | } | ||
197 | } | ||
198 | } | ||
199 | |||
200 | impl fmt::Display for Literal { | ||
201 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
202 | if self.negate { | ||
203 | write!(f, "not(")?; | ||
204 | } | ||
205 | |||
206 | match &self.var { | ||
207 | Some(var) => write!(f, "{}", var)?, | ||
208 | None => f.write_str("<invalid>")?, | ||
209 | } | ||
210 | |||
211 | if self.negate { | ||
212 | write!(f, ")")?; | ||
213 | } | ||
214 | |||
215 | Ok(()) | ||
216 | } | ||
217 | } | ||
218 | |||
219 | struct Builder { | ||
220 | expr: DnfExpr, | ||
221 | } | ||
222 | |||
223 | impl Builder { | ||
224 | fn lower(mut self, expr: CfgExpr) -> DnfExpr { | ||
225 | let expr = make_nnf(expr); | ||
226 | let expr = make_dnf(expr); | ||
227 | |||
228 | match expr { | ||
229 | CfgExpr::Invalid | CfgExpr::Atom(_) | CfgExpr::Not(_) => { | ||
230 | self.expr.conjunctions.push(Conjunction::new(vec![expr])); | ||
231 | } | ||
232 | CfgExpr::All(conj) => { | ||
233 | self.expr.conjunctions.push(Conjunction::new(conj)); | ||
234 | } | ||
235 | CfgExpr::Any(mut disj) => { | ||
236 | disj.reverse(); | ||
237 | while let Some(conj) = disj.pop() { | ||
238 | match conj { | ||
239 | CfgExpr::Invalid | CfgExpr::Atom(_) | CfgExpr::All(_) | CfgExpr::Not(_) => { | ||
240 | self.expr.conjunctions.push(Conjunction::new(vec![conj])); | ||
241 | } | ||
242 | CfgExpr::Any(inner_disj) => { | ||
243 | // Flatten. | ||
244 | disj.extend(inner_disj.into_iter().rev()); | ||
245 | } | ||
246 | } | ||
247 | } | ||
248 | } | ||
249 | } | ||
250 | |||
251 | self.expr | ||
252 | } | ||
253 | } | ||
254 | |||
255 | fn make_dnf(expr: CfgExpr) -> CfgExpr { | ||
256 | match expr { | ||
257 | CfgExpr::Invalid | CfgExpr::Atom(_) | CfgExpr::Not(_) => expr, | ||
258 | CfgExpr::Any(e) => CfgExpr::Any(e.into_iter().map(|expr| make_dnf(expr)).collect()), | ||
259 | CfgExpr::All(e) => { | ||
260 | let e = e.into_iter().map(|expr| make_nnf(expr)).collect::<Vec<_>>(); | ||
261 | |||
262 | CfgExpr::Any(distribute_conj(&e)) | ||
263 | } | ||
264 | } | ||
265 | } | ||
266 | |||
267 | /// Turns a conjunction of expressions into a disjunction of expressions. | ||
268 | fn distribute_conj(conj: &[CfgExpr]) -> Vec<CfgExpr> { | ||
269 | fn go(out: &mut Vec<CfgExpr>, with: &mut Vec<CfgExpr>, rest: &[CfgExpr]) { | ||
270 | match rest { | ||
271 | [head, tail @ ..] => match head { | ||
272 | CfgExpr::Any(disj) => { | ||
273 | for part in disj { | ||
274 | with.push(part.clone()); | ||
275 | go(out, with, tail); | ||
276 | with.pop(); | ||
277 | } | ||
278 | } | ||
279 | _ => { | ||
280 | with.push(head.clone()); | ||
281 | go(out, with, tail); | ||
282 | with.pop(); | ||
283 | } | ||
284 | }, | ||
285 | _ => { | ||
286 | // Turn accumulated parts into a new conjunction. | ||
287 | out.push(CfgExpr::All(with.clone())); | ||
288 | } | ||
289 | } | ||
290 | } | ||
291 | |||
292 | let mut out = Vec::new(); | ||
293 | let mut with = Vec::new(); | ||
294 | |||
295 | go(&mut out, &mut with, conj); | ||
296 | |||
297 | out | ||
298 | } | ||
299 | |||
300 | fn make_nnf(expr: CfgExpr) -> CfgExpr { | ||
301 | match expr { | ||
302 | CfgExpr::Invalid | CfgExpr::Atom(_) => expr, | ||
303 | CfgExpr::Any(expr) => CfgExpr::Any(expr.into_iter().map(|expr| make_nnf(expr)).collect()), | ||
304 | CfgExpr::All(expr) => CfgExpr::All(expr.into_iter().map(|expr| make_nnf(expr)).collect()), | ||
305 | CfgExpr::Not(operand) => match *operand { | ||
306 | CfgExpr::Invalid | CfgExpr::Atom(_) => CfgExpr::Not(operand.clone()), // Original negated expr | ||
307 | CfgExpr::Not(expr) => { | ||
308 | // Remove double negation. | ||
309 | make_nnf(*expr) | ||
310 | } | ||
311 | // Convert negated conjunction/disjunction using DeMorgan's Law. | ||
312 | CfgExpr::Any(inner) => CfgExpr::All( | ||
313 | inner.into_iter().map(|expr| make_nnf(CfgExpr::Not(Box::new(expr)))).collect(), | ||
314 | ), | ||
315 | CfgExpr::All(inner) => CfgExpr::Any( | ||
316 | inner.into_iter().map(|expr| make_nnf(CfgExpr::Not(Box::new(expr)))).collect(), | ||
317 | ), | ||
318 | }, | ||
319 | } | ||
320 | } | ||
diff --git a/crates/cfg/src/lib.rs b/crates/cfg/src/lib.rs index a9d50e698..d0e08cf5f 100644 --- a/crates/cfg/src/lib.rs +++ b/crates/cfg/src/lib.rs | |||
@@ -1,11 +1,17 @@ | |||
1 | //! cfg defines conditional compiling options, `cfg` attibute parser and evaluator | 1 | //! cfg defines conditional compiling options, `cfg` attibute parser and evaluator |
2 | 2 | ||
3 | mod cfg_expr; | 3 | mod cfg_expr; |
4 | mod dnf; | ||
5 | #[cfg(test)] | ||
6 | mod tests; | ||
7 | |||
8 | use std::fmt; | ||
4 | 9 | ||
5 | use rustc_hash::FxHashSet; | 10 | use rustc_hash::FxHashSet; |
6 | use tt::SmolStr; | 11 | use tt::SmolStr; |
7 | 12 | ||
8 | pub use cfg_expr::CfgExpr; | 13 | pub use cfg_expr::{CfgAtom, CfgExpr}; |
14 | pub use dnf::DnfExpr; | ||
9 | 15 | ||
10 | /// Configuration options used for conditional compilition on items with `cfg` attributes. | 16 | /// Configuration options used for conditional compilition on items with `cfg` attributes. |
11 | /// We have two kind of options in different namespaces: atomic options like `unix`, and | 17 | /// We have two kind of options in different namespaces: atomic options like `unix`, and |
@@ -19,33 +25,131 @@ pub use cfg_expr::CfgExpr; | |||
19 | /// See: https://doc.rust-lang.org/reference/conditional-compilation.html#set-configuration-options | 25 | /// See: https://doc.rust-lang.org/reference/conditional-compilation.html#set-configuration-options |
20 | #[derive(Debug, Clone, PartialEq, Eq, Default)] | 26 | #[derive(Debug, Clone, PartialEq, Eq, Default)] |
21 | pub struct CfgOptions { | 27 | pub struct CfgOptions { |
22 | atoms: FxHashSet<SmolStr>, | 28 | enabled: FxHashSet<CfgAtom>, |
23 | key_values: FxHashSet<(SmolStr, SmolStr)>, | ||
24 | } | 29 | } |
25 | 30 | ||
26 | impl CfgOptions { | 31 | impl CfgOptions { |
27 | pub fn check(&self, cfg: &CfgExpr) -> Option<bool> { | 32 | pub fn check(&self, cfg: &CfgExpr) -> Option<bool> { |
28 | cfg.fold(&|key, value| match value { | 33 | cfg.fold(&|atom| self.enabled.contains(atom)) |
29 | None => self.atoms.contains(key), | ||
30 | Some(value) => self.key_values.contains(&(key.clone(), value.clone())), | ||
31 | }) | ||
32 | } | 34 | } |
33 | 35 | ||
34 | pub fn insert_atom(&mut self, key: SmolStr) { | 36 | pub fn insert_atom(&mut self, key: SmolStr) { |
35 | self.atoms.insert(key); | 37 | self.enabled.insert(CfgAtom::Flag(key)); |
36 | } | 38 | } |
37 | 39 | ||
38 | pub fn insert_key_value(&mut self, key: SmolStr, value: SmolStr) { | 40 | pub fn insert_key_value(&mut self, key: SmolStr, value: SmolStr) { |
39 | self.key_values.insert((key, value)); | 41 | self.enabled.insert(CfgAtom::KeyValue { key, value }); |
40 | } | 42 | } |
41 | 43 | ||
42 | pub fn append(&mut self, other: &CfgOptions) { | 44 | pub fn append(&mut self, other: &CfgOptions) { |
43 | for atom in &other.atoms { | 45 | for atom in &other.enabled { |
44 | self.atoms.insert(atom.clone()); | 46 | self.enabled.insert(atom.clone()); |
47 | } | ||
48 | } | ||
49 | |||
50 | pub fn apply_diff(&mut self, diff: CfgDiff) { | ||
51 | for atom in diff.enable { | ||
52 | self.enabled.insert(atom); | ||
45 | } | 53 | } |
46 | 54 | ||
47 | for (key, value) in &other.key_values { | 55 | for atom in diff.disable { |
48 | self.key_values.insert((key.clone(), value.clone())); | 56 | self.enabled.remove(&atom); |
57 | } | ||
58 | } | ||
59 | } | ||
60 | |||
61 | pub struct CfgDiff { | ||
62 | // Invariants: No duplicates, no atom that's both in `enable` and `disable`. | ||
63 | enable: Vec<CfgAtom>, | ||
64 | disable: Vec<CfgAtom>, | ||
65 | } | ||
66 | |||
67 | impl CfgDiff { | ||
68 | /// Returns the total number of atoms changed by this diff. | ||
69 | pub fn len(&self) -> usize { | ||
70 | self.enable.len() + self.disable.len() | ||
71 | } | ||
72 | } | ||
73 | |||
74 | impl fmt::Display for CfgDiff { | ||
75 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
76 | if !self.enable.is_empty() { | ||
77 | f.write_str("enable ")?; | ||
78 | for (i, atom) in self.enable.iter().enumerate() { | ||
79 | let sep = match i { | ||
80 | 0 => "", | ||
81 | _ if i == self.enable.len() - 1 => " and ", | ||
82 | _ => ", ", | ||
83 | }; | ||
84 | f.write_str(sep)?; | ||
85 | |||
86 | write!(f, "{}", atom)?; | ||
87 | } | ||
88 | |||
89 | if !self.disable.is_empty() { | ||
90 | f.write_str("; ")?; | ||
91 | } | ||
49 | } | 92 | } |
93 | |||
94 | if !self.disable.is_empty() { | ||
95 | f.write_str("disable ")?; | ||
96 | for (i, atom) in self.disable.iter().enumerate() { | ||
97 | let sep = match i { | ||
98 | 0 => "", | ||
99 | _ if i == self.enable.len() - 1 => " and ", | ||
100 | _ => ", ", | ||
101 | }; | ||
102 | f.write_str(sep)?; | ||
103 | |||
104 | write!(f, "{}", atom)?; | ||
105 | } | ||
106 | } | ||
107 | |||
108 | Ok(()) | ||
109 | } | ||
110 | } | ||
111 | |||
112 | pub struct InactiveReason { | ||
113 | enabled: Vec<CfgAtom>, | ||
114 | disabled: Vec<CfgAtom>, | ||
115 | } | ||
116 | |||
117 | impl fmt::Display for InactiveReason { | ||
118 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
119 | if !self.enabled.is_empty() { | ||
120 | for (i, atom) in self.enabled.iter().enumerate() { | ||
121 | let sep = match i { | ||
122 | 0 => "", | ||
123 | _ if i == self.enabled.len() - 1 => " and ", | ||
124 | _ => ", ", | ||
125 | }; | ||
126 | f.write_str(sep)?; | ||
127 | |||
128 | write!(f, "{}", atom)?; | ||
129 | } | ||
130 | let is_are = if self.enabled.len() == 1 { "is" } else { "are" }; | ||
131 | write!(f, " {} enabled", is_are)?; | ||
132 | |||
133 | if !self.disabled.is_empty() { | ||
134 | f.write_str(" and ")?; | ||
135 | } | ||
136 | } | ||
137 | |||
138 | if !self.disabled.is_empty() { | ||
139 | for (i, atom) in self.disabled.iter().enumerate() { | ||
140 | let sep = match i { | ||
141 | 0 => "", | ||
142 | _ if i == self.disabled.len() - 1 => " and ", | ||
143 | _ => ", ", | ||
144 | }; | ||
145 | f.write_str(sep)?; | ||
146 | |||
147 | write!(f, "{}", atom)?; | ||
148 | } | ||
149 | let is_are = if self.disabled.len() == 1 { "is" } else { "are" }; | ||
150 | write!(f, " {} disabled", is_are)?; | ||
151 | } | ||
152 | |||
153 | Ok(()) | ||
50 | } | 154 | } |
51 | } | 155 | } |
diff --git a/crates/cfg/src/tests.rs b/crates/cfg/src/tests.rs new file mode 100644 index 000000000..bd0f9ec48 --- /dev/null +++ b/crates/cfg/src/tests.rs | |||
@@ -0,0 +1,193 @@ | |||
1 | use expect_test::{expect, Expect}; | ||
2 | use mbe::ast_to_token_tree; | ||
3 | use syntax::{ast, AstNode}; | ||
4 | |||
5 | use crate::{CfgAtom, CfgExpr, CfgOptions, DnfExpr}; | ||
6 | |||
7 | fn assert_parse_result(input: &str, expected: CfgExpr) { | ||
8 | let (tt, _) = { | ||
9 | let source_file = ast::SourceFile::parse(input).ok().unwrap(); | ||
10 | let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap(); | ||
11 | ast_to_token_tree(&tt).unwrap() | ||
12 | }; | ||
13 | let cfg = CfgExpr::parse(&tt); | ||
14 | assert_eq!(cfg, expected); | ||
15 | } | ||
16 | |||
17 | fn check_dnf(input: &str, expect: Expect) { | ||
18 | let (tt, _) = { | ||
19 | let source_file = ast::SourceFile::parse(input).ok().unwrap(); | ||
20 | let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap(); | ||
21 | ast_to_token_tree(&tt).unwrap() | ||
22 | }; | ||
23 | let cfg = CfgExpr::parse(&tt); | ||
24 | let actual = format!("#![cfg({})]", DnfExpr::new(cfg)); | ||
25 | expect.assert_eq(&actual); | ||
26 | } | ||
27 | |||
28 | fn check_why_inactive(input: &str, opts: &CfgOptions, expect: Expect) { | ||
29 | let (tt, _) = { | ||
30 | let source_file = ast::SourceFile::parse(input).ok().unwrap(); | ||
31 | let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap(); | ||
32 | ast_to_token_tree(&tt).unwrap() | ||
33 | }; | ||
34 | let cfg = CfgExpr::parse(&tt); | ||
35 | let dnf = DnfExpr::new(cfg); | ||
36 | let why_inactive = dnf.why_inactive(opts).unwrap().to_string(); | ||
37 | expect.assert_eq(&why_inactive); | ||
38 | } | ||
39 | |||
40 | #[track_caller] | ||
41 | fn check_enable_hints(input: &str, opts: &CfgOptions, expected_hints: &[&str]) { | ||
42 | let (tt, _) = { | ||
43 | let source_file = ast::SourceFile::parse(input).ok().unwrap(); | ||
44 | let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap(); | ||
45 | ast_to_token_tree(&tt).unwrap() | ||
46 | }; | ||
47 | let cfg = CfgExpr::parse(&tt); | ||
48 | let dnf = DnfExpr::new(cfg); | ||
49 | let hints = dnf.compute_enable_hints(opts).map(|diff| diff.to_string()).collect::<Vec<_>>(); | ||
50 | assert_eq!(hints, expected_hints); | ||
51 | } | ||
52 | |||
53 | #[test] | ||
54 | fn test_cfg_expr_parser() { | ||
55 | assert_parse_result("#![cfg(foo)]", CfgAtom::Flag("foo".into()).into()); | ||
56 | assert_parse_result("#![cfg(foo,)]", CfgAtom::Flag("foo".into()).into()); | ||
57 | assert_parse_result( | ||
58 | "#![cfg(not(foo))]", | ||
59 | CfgExpr::Not(Box::new(CfgAtom::Flag("foo".into()).into())), | ||
60 | ); | ||
61 | assert_parse_result("#![cfg(foo(bar))]", CfgExpr::Invalid); | ||
62 | |||
63 | // Only take the first | ||
64 | assert_parse_result(r#"#![cfg(foo, bar = "baz")]"#, CfgAtom::Flag("foo".into()).into()); | ||
65 | |||
66 | assert_parse_result( | ||
67 | r#"#![cfg(all(foo, bar = "baz"))]"#, | ||
68 | CfgExpr::All(vec![ | ||
69 | CfgAtom::Flag("foo".into()).into(), | ||
70 | CfgAtom::KeyValue { key: "bar".into(), value: "baz".into() }.into(), | ||
71 | ]), | ||
72 | ); | ||
73 | |||
74 | assert_parse_result( | ||
75 | r#"#![cfg(any(not(), all(), , bar = "baz",))]"#, | ||
76 | CfgExpr::Any(vec![ | ||
77 | CfgExpr::Not(Box::new(CfgExpr::Invalid)), | ||
78 | CfgExpr::All(vec![]), | ||
79 | CfgExpr::Invalid, | ||
80 | CfgAtom::KeyValue { key: "bar".into(), value: "baz".into() }.into(), | ||
81 | ]), | ||
82 | ); | ||
83 | } | ||
84 | |||
85 | #[test] | ||
86 | fn smoke() { | ||
87 | check_dnf("#![cfg(test)]", expect![[r#"#![cfg(test)]"#]]); | ||
88 | check_dnf("#![cfg(not(test))]", expect![[r#"#![cfg(not(test))]"#]]); | ||
89 | check_dnf("#![cfg(not(not(test)))]", expect![[r#"#![cfg(test)]"#]]); | ||
90 | |||
91 | check_dnf("#![cfg(all(a, b))]", expect![[r#"#![cfg(all(a, b))]"#]]); | ||
92 | check_dnf("#![cfg(any(a, b))]", expect![[r#"#![cfg(any(a, b))]"#]]); | ||
93 | |||
94 | check_dnf("#![cfg(not(a))]", expect![[r#"#![cfg(not(a))]"#]]); | ||
95 | } | ||
96 | |||
97 | #[test] | ||
98 | fn distribute() { | ||
99 | check_dnf("#![cfg(all(any(a, b), c))]", expect![[r#"#![cfg(any(all(a, c), all(b, c)))]"#]]); | ||
100 | check_dnf("#![cfg(all(c, any(a, b)))]", expect![[r#"#![cfg(any(all(c, a), all(c, b)))]"#]]); | ||
101 | check_dnf( | ||
102 | "#![cfg(all(any(a, b), any(c, d)))]", | ||
103 | expect![[r#"#![cfg(any(all(a, c), all(a, d), all(b, c), all(b, d)))]"#]], | ||
104 | ); | ||
105 | |||
106 | check_dnf( | ||
107 | "#![cfg(all(any(a, b, c), any(d, e, f), g))]", | ||
108 | expect![[ | ||
109 | r#"#![cfg(any(all(a, d, g), all(a, e, g), all(a, f, g), all(b, d, g), all(b, e, g), all(b, f, g), all(c, d, g), all(c, e, g), all(c, f, g)))]"# | ||
110 | ]], | ||
111 | ); | ||
112 | } | ||
113 | |||
114 | #[test] | ||
115 | fn demorgan() { | ||
116 | check_dnf("#![cfg(not(all(a, b)))]", expect![[r#"#![cfg(any(not(a), not(b)))]"#]]); | ||
117 | check_dnf("#![cfg(not(any(a, b)))]", expect![[r#"#![cfg(all(not(a), not(b)))]"#]]); | ||
118 | |||
119 | check_dnf("#![cfg(not(all(not(a), b)))]", expect![[r#"#![cfg(any(a, not(b)))]"#]]); | ||
120 | check_dnf("#![cfg(not(any(a, not(b))))]", expect![[r#"#![cfg(all(not(a), b))]"#]]); | ||
121 | } | ||
122 | |||
123 | #[test] | ||
124 | fn nested() { | ||
125 | check_dnf("#![cfg(all(any(a), not(all(any(b)))))]", expect![[r#"#![cfg(all(a, not(b)))]"#]]); | ||
126 | |||
127 | check_dnf("#![cfg(any(any(a, b)))]", expect![[r#"#![cfg(any(a, b))]"#]]); | ||
128 | check_dnf("#![cfg(not(any(any(a, b))))]", expect![[r#"#![cfg(all(not(a), not(b)))]"#]]); | ||
129 | check_dnf("#![cfg(all(all(a, b)))]", expect![[r#"#![cfg(all(a, b))]"#]]); | ||
130 | check_dnf("#![cfg(not(all(all(a, b))))]", expect![[r#"#![cfg(any(not(a), not(b)))]"#]]); | ||
131 | } | ||
132 | |||
133 | #[test] | ||
134 | fn hints() { | ||
135 | let mut opts = CfgOptions::default(); | ||
136 | |||
137 | check_enable_hints("#![cfg(test)]", &opts, &["enable test"]); | ||
138 | check_enable_hints("#![cfg(not(test))]", &opts, &[]); | ||
139 | |||
140 | check_enable_hints("#![cfg(any(a, b))]", &opts, &["enable a", "enable b"]); | ||
141 | check_enable_hints("#![cfg(any(b, a))]", &opts, &["enable b", "enable a"]); | ||
142 | |||
143 | check_enable_hints("#![cfg(all(a, b))]", &opts, &["enable a and b"]); | ||
144 | |||
145 | opts.insert_atom("test".into()); | ||
146 | |||
147 | check_enable_hints("#![cfg(test)]", &opts, &[]); | ||
148 | check_enable_hints("#![cfg(not(test))]", &opts, &["disable test"]); | ||
149 | } | ||
150 | |||
151 | /// Tests that we don't suggest hints for cfgs that express an inconsistent formula. | ||
152 | #[test] | ||
153 | fn hints_impossible() { | ||
154 | let mut opts = CfgOptions::default(); | ||
155 | |||
156 | check_enable_hints("#![cfg(all(test, not(test)))]", &opts, &[]); | ||
157 | |||
158 | opts.insert_atom("test".into()); | ||
159 | |||
160 | check_enable_hints("#![cfg(all(test, not(test)))]", &opts, &[]); | ||
161 | } | ||
162 | |||
163 | #[test] | ||
164 | fn why_inactive() { | ||
165 | let mut opts = CfgOptions::default(); | ||
166 | opts.insert_atom("test".into()); | ||
167 | opts.insert_atom("test2".into()); | ||
168 | |||
169 | check_why_inactive("#![cfg(a)]", &opts, expect![["a is disabled"]]); | ||
170 | check_why_inactive("#![cfg(not(test))]", &opts, expect![["test is enabled"]]); | ||
171 | |||
172 | check_why_inactive( | ||
173 | "#![cfg(all(not(test), not(test2)))]", | ||
174 | &opts, | ||
175 | expect![["test and test2 are enabled"]], | ||
176 | ); | ||
177 | check_why_inactive("#![cfg(all(a, b))]", &opts, expect![["a and b are disabled"]]); | ||
178 | check_why_inactive( | ||
179 | "#![cfg(all(not(test), a))]", | ||
180 | &opts, | ||
181 | expect![["test is enabled and a is disabled"]], | ||
182 | ); | ||
183 | check_why_inactive( | ||
184 | "#![cfg(all(not(test), test2, a))]", | ||
185 | &opts, | ||
186 | expect![["test is enabled and a is disabled"]], | ||
187 | ); | ||
188 | check_why_inactive( | ||
189 | "#![cfg(all(not(test), not(test2), a))]", | ||
190 | &opts, | ||
191 | expect![["test and test2 are enabled and a is disabled"]], | ||
192 | ); | ||
193 | } | ||
diff --git a/crates/completion/Cargo.toml b/crates/completion/Cargo.toml index 25192456a..b79ee33f7 100644 --- a/crates/completion/Cargo.toml +++ b/crates/completion/Cargo.toml | |||
@@ -21,8 +21,6 @@ base_db = { path = "../base_db", version = "0.0.0" } | |||
21 | ide_db = { path = "../ide_db", version = "0.0.0" } | 21 | ide_db = { path = "../ide_db", version = "0.0.0" } |
22 | profile = { path = "../profile", version = "0.0.0" } | 22 | profile = { path = "../profile", version = "0.0.0" } |
23 | test_utils = { path = "../test_utils", version = "0.0.0" } | 23 | test_utils = { path = "../test_utils", version = "0.0.0" } |
24 | assists = { path = "../assists", version = "0.0.0" } | ||
25 | call_info = { path = "../call_info", version = "0.0.0" } | ||
26 | 24 | ||
27 | # completions crate should depend only on the top-level `hir` package. if you need | 25 | # completions crate should depend only on the top-level `hir` package. if you need |
28 | # something from some `hir_xxx` subpackage, reexport the API via `hir`. | 26 | # something from some `hir_xxx` subpackage, reexport the API via `hir`. |
diff --git a/crates/completion/src/complete_mod.rs b/crates/completion/src/complete_mod.rs index 35a57aba3..385911afa 100644 --- a/crates/completion/src/complete_mod.rs +++ b/crates/completion/src/complete_mod.rs | |||
@@ -1,7 +1,7 @@ | |||
1 | //! Completes mod declarations. | 1 | //! Completes mod declarations. |
2 | 2 | ||
3 | use base_db::{SourceDatabaseExt, VfsPath}; | ||
4 | use hir::{Module, ModuleSource}; | 3 | use hir::{Module, ModuleSource}; |
4 | use ide_db::base_db::{SourceDatabaseExt, VfsPath}; | ||
5 | use ide_db::RootDatabase; | 5 | use ide_db::RootDatabase; |
6 | use rustc_hash::FxHashSet; | 6 | use rustc_hash::FxHashSet; |
7 | 7 | ||
diff --git a/crates/completion/src/complete_postfix.rs b/crates/completion/src/complete_postfix.rs index 700573cf2..2622f12ab 100644 --- a/crates/completion/src/complete_postfix.rs +++ b/crates/completion/src/complete_postfix.rs | |||
@@ -2,7 +2,7 @@ | |||
2 | 2 | ||
3 | mod format_like; | 3 | mod format_like; |
4 | 4 | ||
5 | use assists::utils::TryEnum; | 5 | use ide_db::ty_filter::TryEnum; |
6 | use syntax::{ | 6 | use syntax::{ |
7 | ast::{self, AstNode, AstToken}, | 7 | ast::{self, AstNode, AstToken}, |
8 | TextRange, TextSize, | 8 | TextRange, TextSize, |
diff --git a/crates/completion/src/complete_trait_impl.rs b/crates/completion/src/complete_trait_impl.rs index c06af99e2..a14be9c73 100644 --- a/crates/completion/src/complete_trait_impl.rs +++ b/crates/completion/src/complete_trait_impl.rs | |||
@@ -31,8 +31,8 @@ | |||
31 | //! } | 31 | //! } |
32 | //! ``` | 32 | //! ``` |
33 | 33 | ||
34 | use assists::utils::get_missing_assoc_items; | ||
35 | use hir::{self, HasAttrs, HasSource}; | 34 | use hir::{self, HasAttrs, HasSource}; |
35 | use ide_db::traits::get_missing_assoc_items; | ||
36 | use syntax::{ | 36 | use syntax::{ |
37 | ast::{self, edit, Impl}, | 37 | ast::{self, edit, Impl}, |
38 | display::function_declaration, | 38 | display::function_declaration, |
diff --git a/crates/completion/src/completion_context.rs b/crates/completion/src/completion_context.rs index e4f86d0e0..dca304a8f 100644 --- a/crates/completion/src/completion_context.rs +++ b/crates/completion/src/completion_context.rs | |||
@@ -1,9 +1,8 @@ | |||
1 | //! See `CompletionContext` structure. | 1 | //! See `CompletionContext` structure. |
2 | 2 | ||
3 | use base_db::{FilePosition, SourceDatabase}; | ||
4 | use call_info::ActiveParameter; | ||
5 | use hir::{Local, ScopeDef, Semantics, SemanticsScope, Type}; | 3 | use hir::{Local, ScopeDef, Semantics, SemanticsScope, Type}; |
6 | use ide_db::RootDatabase; | 4 | use ide_db::base_db::{FilePosition, SourceDatabase}; |
5 | use ide_db::{call_info::ActiveParameter, RootDatabase}; | ||
7 | use syntax::{ | 6 | use syntax::{ |
8 | algo::{find_covering_element, find_node_at_offset}, | 7 | algo::{find_covering_element, find_node_at_offset}, |
9 | ast, match_ast, AstNode, NodeOrToken, | 8 | ast, match_ast, AstNode, NodeOrToken, |
diff --git a/crates/completion/src/lib.rs b/crates/completion/src/lib.rs index 0a60ea7f2..b72fd249d 100644 --- a/crates/completion/src/lib.rs +++ b/crates/completion/src/lib.rs | |||
@@ -23,7 +23,7 @@ mod complete_macro_in_item_position; | |||
23 | mod complete_trait_impl; | 23 | mod complete_trait_impl; |
24 | mod complete_mod; | 24 | mod complete_mod; |
25 | 25 | ||
26 | use base_db::FilePosition; | 26 | use ide_db::base_db::FilePosition; |
27 | use ide_db::RootDatabase; | 27 | use ide_db::RootDatabase; |
28 | 28 | ||
29 | use crate::{ | 29 | use crate::{ |
diff --git a/crates/completion/src/presentation.rs b/crates/completion/src/presentation.rs index 2a19281cf..0a6f5a1ea 100644 --- a/crates/completion/src/presentation.rs +++ b/crates/completion/src/presentation.rs | |||
@@ -304,9 +304,14 @@ impl Completions { | |||
304 | ) { | 304 | ) { |
305 | let is_deprecated = is_deprecated(variant, ctx.db); | 305 | let is_deprecated = is_deprecated(variant, ctx.db); |
306 | let name = local_name.unwrap_or_else(|| variant.name(ctx.db).to_string()); | 306 | let name = local_name.unwrap_or_else(|| variant.name(ctx.db).to_string()); |
307 | let qualified_name = match &path { | 307 | let (qualified_name, short_qualified_name) = match &path { |
308 | Some(it) => it.to_string(), | 308 | Some(path) => { |
309 | None => name.to_string(), | 309 | let full = path.to_string(); |
310 | let short = | ||
311 | path.segments[path.segments.len().saturating_sub(2)..].iter().join("::"); | ||
312 | (full, short) | ||
313 | } | ||
314 | None => (name.to_string(), name.to_string()), | ||
310 | }; | 315 | }; |
311 | let detail_types = variant | 316 | let detail_types = variant |
312 | .fields(ctx.db) | 317 | .fields(ctx.db) |
@@ -335,14 +340,12 @@ impl Completions { | |||
335 | .set_deprecated(is_deprecated) | 340 | .set_deprecated(is_deprecated) |
336 | .detail(detail); | 341 | .detail(detail); |
337 | 342 | ||
338 | if path.is_some() { | ||
339 | res = res.lookup_by(name); | ||
340 | } | ||
341 | |||
342 | if variant_kind == StructKind::Tuple { | 343 | if variant_kind == StructKind::Tuple { |
343 | mark::hit!(inserts_parens_for_tuple_enums); | 344 | mark::hit!(inserts_parens_for_tuple_enums); |
344 | let params = Params::Anonymous(variant.fields(ctx.db).len()); | 345 | let params = Params::Anonymous(variant.fields(ctx.db).len()); |
345 | res = res.add_call_parens(ctx, qualified_name, params) | 346 | res = res.add_call_parens(ctx, short_qualified_name, params) |
347 | } else if path.is_some() { | ||
348 | res = res.lookup_by(short_qualified_name); | ||
346 | } | 349 | } |
347 | 350 | ||
348 | res.add_to(self); | 351 | res.add_to(self); |
@@ -607,6 +610,57 @@ fn main() { Foo::Fo<|> } | |||
607 | } | 610 | } |
608 | 611 | ||
609 | #[test] | 612 | #[test] |
613 | fn lookup_enums_by_two_qualifiers() { | ||
614 | check( | ||
615 | r#" | ||
616 | mod m { | ||
617 | pub enum Spam { Foo, Bar(i32) } | ||
618 | } | ||
619 | fn main() { let _: m::Spam = S<|> } | ||
620 | "#, | ||
621 | expect![[r#" | ||
622 | [ | ||
623 | CompletionItem { | ||
624 | label: "Spam::Bar(…)", | ||
625 | source_range: 75..76, | ||
626 | delete: 75..76, | ||
627 | insert: "Spam::Bar($0)", | ||
628 | kind: EnumVariant, | ||
629 | lookup: "Spam::Bar", | ||
630 | detail: "(i32)", | ||
631 | trigger_call_info: true, | ||
632 | }, | ||
633 | CompletionItem { | ||
634 | label: "m", | ||
635 | source_range: 75..76, | ||
636 | delete: 75..76, | ||
637 | insert: "m", | ||
638 | kind: Module, | ||
639 | }, | ||
640 | CompletionItem { | ||
641 | label: "m::Spam::Foo", | ||
642 | source_range: 75..76, | ||
643 | delete: 75..76, | ||
644 | insert: "m::Spam::Foo", | ||
645 | kind: EnumVariant, | ||
646 | lookup: "Spam::Foo", | ||
647 | detail: "()", | ||
648 | }, | ||
649 | CompletionItem { | ||
650 | label: "main()", | ||
651 | source_range: 75..76, | ||
652 | delete: 75..76, | ||
653 | insert: "main()$0", | ||
654 | kind: Function, | ||
655 | lookup: "main", | ||
656 | detail: "fn main()", | ||
657 | }, | ||
658 | ] | ||
659 | "#]], | ||
660 | ) | ||
661 | } | ||
662 | |||
663 | #[test] | ||
610 | fn sets_deprecated_flag_in_completion_items() { | 664 | fn sets_deprecated_flag_in_completion_items() { |
611 | check( | 665 | check( |
612 | r#" | 666 | r#" |
diff --git a/crates/completion/src/test_utils.rs b/crates/completion/src/test_utils.rs index f2cf2561f..b02556797 100644 --- a/crates/completion/src/test_utils.rs +++ b/crates/completion/src/test_utils.rs | |||
@@ -1,7 +1,7 @@ | |||
1 | //! Runs completion for testing purposes. | 1 | //! Runs completion for testing purposes. |
2 | 2 | ||
3 | use base_db::{fixture::ChangeFixture, FileLoader, FilePosition}; | ||
4 | use hir::Semantics; | 3 | use hir::Semantics; |
4 | use ide_db::base_db::{fixture::ChangeFixture, FileLoader, FilePosition}; | ||
5 | use ide_db::RootDatabase; | 5 | use ide_db::RootDatabase; |
6 | use itertools::Itertools; | 6 | use itertools::Itertools; |
7 | use stdx::{format_to, trim_indent}; | 7 | use stdx::{format_to, trim_indent}; |
diff --git a/crates/hir/src/code_model.rs b/crates/hir/src/code_model.rs index 7f169ccd2..63c1a8ebf 100644 --- a/crates/hir/src/code_model.rs +++ b/crates/hir/src/code_model.rs | |||
@@ -31,8 +31,7 @@ use hir_ty::{ | |||
31 | autoderef, | 31 | autoderef, |
32 | display::{HirDisplayError, HirFormatter}, | 32 | display::{HirDisplayError, HirFormatter}, |
33 | method_resolution, | 33 | method_resolution, |
34 | traits::Solution, | 34 | traits::{FnTrait, Solution, SolutionVariables}, |
35 | traits::SolutionVariables, | ||
36 | ApplicationTy, BoundVar, CallableDefId, Canonical, DebruijnIndex, FnSig, GenericPredicate, | 35 | ApplicationTy, BoundVar, CallableDefId, Canonical, DebruijnIndex, FnSig, GenericPredicate, |
37 | InEnvironment, Obligation, ProjectionPredicate, ProjectionTy, Substs, TraitEnvironment, Ty, | 36 | InEnvironment, Obligation, ProjectionPredicate, ProjectionTy, Substs, TraitEnvironment, Ty, |
38 | TyDefId, TyKind, TypeCtor, | 37 | TyDefId, TyKind, TypeCtor, |
@@ -781,6 +780,7 @@ impl Function { | |||
781 | } | 780 | } |
782 | 781 | ||
783 | pub fn diagnostics(self, db: &dyn HirDatabase, sink: &mut DiagnosticSink) { | 782 | pub fn diagnostics(self, db: &dyn HirDatabase, sink: &mut DiagnosticSink) { |
783 | hir_def::diagnostics::validate_body(db.upcast(), self.id.into(), sink); | ||
784 | hir_ty::diagnostics::validate_module_item(db, self.id.into(), sink); | 784 | hir_ty::diagnostics::validate_module_item(db, self.id.into(), sink); |
785 | hir_ty::diagnostics::validate_body(db, self.id.into(), sink); | 785 | hir_ty::diagnostics::validate_body(db, self.id.into(), sink); |
786 | } | 786 | } |
@@ -1385,6 +1385,28 @@ impl Type { | |||
1385 | ) | 1385 | ) |
1386 | } | 1386 | } |
1387 | 1387 | ||
1388 | /// Checks that particular type `ty` implements `std::ops::FnOnce`. | ||
1389 | /// | ||
1390 | /// This function can be used to check if a particular type is callable, since FnOnce is a | ||
1391 | /// supertrait of Fn and FnMut, so all callable types implements at least FnOnce. | ||
1392 | pub fn impls_fnonce(&self, db: &dyn HirDatabase) -> bool { | ||
1393 | let krate = self.krate; | ||
1394 | |||
1395 | let fnonce_trait = match FnTrait::FnOnce.get_id(db, krate) { | ||
1396 | Some(it) => it, | ||
1397 | None => return false, | ||
1398 | }; | ||
1399 | |||
1400 | let canonical_ty = Canonical { value: self.ty.value.clone(), kinds: Arc::new([]) }; | ||
1401 | method_resolution::implements_trait( | ||
1402 | &canonical_ty, | ||
1403 | db, | ||
1404 | self.ty.environment.clone(), | ||
1405 | krate, | ||
1406 | fnonce_trait, | ||
1407 | ) | ||
1408 | } | ||
1409 | |||
1388 | pub fn impls_trait(&self, db: &dyn HirDatabase, trait_: Trait, args: &[Type]) -> bool { | 1410 | pub fn impls_trait(&self, db: &dyn HirDatabase, trait_: Trait, args: &[Type]) -> bool { |
1389 | let trait_ref = hir_ty::TraitRef { | 1411 | let trait_ref = hir_ty::TraitRef { |
1390 | trait_: trait_.id, | 1412 | trait_: trait_.id, |
diff --git a/crates/hir_def/src/attr.rs b/crates/hir_def/src/attr.rs index dea552a60..b2ce7ca3c 100644 --- a/crates/hir_def/src/attr.rs +++ b/crates/hir_def/src/attr.rs | |||
@@ -125,12 +125,20 @@ impl Attrs { | |||
125 | AttrQuery { attrs: self, key } | 125 | AttrQuery { attrs: self, key } |
126 | } | 126 | } |
127 | 127 | ||
128 | pub fn cfg(&self) -> impl Iterator<Item = CfgExpr> + '_ { | 128 | pub fn cfg(&self) -> Option<CfgExpr> { |
129 | // FIXME: handle cfg_attr :-) | 129 | // FIXME: handle cfg_attr :-) |
130 | self.by_key("cfg").tt_values().map(CfgExpr::parse) | 130 | let mut cfgs = self.by_key("cfg").tt_values().map(CfgExpr::parse).collect::<Vec<_>>(); |
131 | match cfgs.len() { | ||
132 | 0 => None, | ||
133 | 1 => Some(cfgs.pop().unwrap()), | ||
134 | _ => Some(CfgExpr::All(cfgs)), | ||
135 | } | ||
131 | } | 136 | } |
132 | pub(crate) fn is_cfg_enabled(&self, cfg_options: &CfgOptions) -> bool { | 137 | pub(crate) fn is_cfg_enabled(&self, cfg_options: &CfgOptions) -> bool { |
133 | self.cfg().all(|cfg| cfg_options.check(&cfg) != Some(false)) | 138 | match self.cfg() { |
139 | None => true, | ||
140 | Some(cfg) => cfg_options.check(&cfg) != Some(false), | ||
141 | } | ||
134 | } | 142 | } |
135 | } | 143 | } |
136 | 144 | ||
diff --git a/crates/hir_def/src/body.rs b/crates/hir_def/src/body.rs index d51036e4f..d10b1af01 100644 --- a/crates/hir_def/src/body.rs +++ b/crates/hir_def/src/body.rs | |||
@@ -1,6 +1,9 @@ | |||
1 | //! Defines `Body`: a lowered representation of bodies of functions, statics and | 1 | //! Defines `Body`: a lowered representation of bodies of functions, statics and |
2 | //! consts. | 2 | //! consts. |
3 | mod lower; | 3 | mod lower; |
4 | mod diagnostics; | ||
5 | #[cfg(test)] | ||
6 | mod tests; | ||
4 | pub mod scope; | 7 | pub mod scope; |
5 | 8 | ||
6 | use std::{mem, ops::Index, sync::Arc}; | 9 | use std::{mem, ops::Index, sync::Arc}; |
@@ -10,7 +13,10 @@ use base_db::CrateId; | |||
10 | use cfg::CfgOptions; | 13 | use cfg::CfgOptions; |
11 | use drop_bomb::DropBomb; | 14 | use drop_bomb::DropBomb; |
12 | use either::Either; | 15 | use either::Either; |
13 | use hir_expand::{ast_id_map::AstIdMap, hygiene::Hygiene, AstId, HirFileId, InFile, MacroDefId}; | 16 | use hir_expand::{ |
17 | ast_id_map::AstIdMap, diagnostics::DiagnosticSink, hygiene::Hygiene, AstId, HirFileId, InFile, | ||
18 | MacroDefId, | ||
19 | }; | ||
14 | use rustc_hash::FxHashMap; | 20 | use rustc_hash::FxHashMap; |
15 | use syntax::{ast, AstNode, AstPtr}; | 21 | use syntax::{ast, AstNode, AstPtr}; |
16 | use test_utils::mark; | 22 | use test_utils::mark; |
@@ -150,8 +156,12 @@ impl Expander { | |||
150 | InFile { file_id: self.current_file_id, value } | 156 | InFile { file_id: self.current_file_id, value } |
151 | } | 157 | } |
152 | 158 | ||
153 | pub(crate) fn is_cfg_enabled(&self, owner: &dyn ast::AttrsOwner) -> bool { | 159 | pub(crate) fn parse_attrs(&self, owner: &dyn ast::AttrsOwner) -> Attrs { |
154 | self.cfg_expander.is_cfg_enabled(owner) | 160 | self.cfg_expander.parse_attrs(owner) |
161 | } | ||
162 | |||
163 | pub(crate) fn cfg_options(&self) -> &CfgOptions { | ||
164 | &self.cfg_expander.cfg_options | ||
155 | } | 165 | } |
156 | 166 | ||
157 | fn parse_path(&mut self, path: ast::Path) -> Option<Path> { | 167 | fn parse_path(&mut self, path: ast::Path) -> Option<Path> { |
@@ -219,6 +229,10 @@ pub struct BodySourceMap { | |||
219 | pat_map_back: ArenaMap<PatId, Result<PatSource, SyntheticSyntax>>, | 229 | pat_map_back: ArenaMap<PatId, Result<PatSource, SyntheticSyntax>>, |
220 | field_map: FxHashMap<(ExprId, usize), InFile<AstPtr<ast::RecordExprField>>>, | 230 | field_map: FxHashMap<(ExprId, usize), InFile<AstPtr<ast::RecordExprField>>>, |
221 | expansions: FxHashMap<InFile<AstPtr<ast::MacroCall>>, HirFileId>, | 231 | expansions: FxHashMap<InFile<AstPtr<ast::MacroCall>>, HirFileId>, |
232 | |||
233 | /// Diagnostics accumulated during body lowering. These contain `AstPtr`s and so are stored in | ||
234 | /// the source map (since they're just as volatile). | ||
235 | diagnostics: Vec<diagnostics::BodyDiagnostic>, | ||
222 | } | 236 | } |
223 | 237 | ||
224 | #[derive(Default, Debug, Eq, PartialEq, Clone, Copy)] | 238 | #[derive(Default, Debug, Eq, PartialEq, Clone, Copy)] |
@@ -318,45 +332,10 @@ impl BodySourceMap { | |||
318 | pub fn field_syntax(&self, expr: ExprId, field: usize) -> InFile<AstPtr<ast::RecordExprField>> { | 332 | pub fn field_syntax(&self, expr: ExprId, field: usize) -> InFile<AstPtr<ast::RecordExprField>> { |
319 | self.field_map[&(expr, field)].clone() | 333 | self.field_map[&(expr, field)].clone() |
320 | } | 334 | } |
321 | } | ||
322 | |||
323 | #[cfg(test)] | ||
324 | mod tests { | ||
325 | use base_db::{fixture::WithFixture, SourceDatabase}; | ||
326 | use test_utils::mark; | ||
327 | 335 | ||
328 | use crate::ModuleDefId; | 336 | pub(crate) fn add_diagnostics(&self, _db: &dyn DefDatabase, sink: &mut DiagnosticSink<'_>) { |
329 | 337 | for diag in &self.diagnostics { | |
330 | use super::*; | 338 | diag.add_to(sink); |
331 | 339 | } | |
332 | fn lower(ra_fixture: &str) -> Arc<Body> { | ||
333 | let (db, file_id) = crate::test_db::TestDB::with_single_file(ra_fixture); | ||
334 | |||
335 | let krate = db.crate_graph().iter().next().unwrap(); | ||
336 | let def_map = db.crate_def_map(krate); | ||
337 | let module = def_map.modules_for_file(file_id).next().unwrap(); | ||
338 | let module = &def_map[module]; | ||
339 | let fn_def = match module.scope.declarations().next().unwrap() { | ||
340 | ModuleDefId::FunctionId(it) => it, | ||
341 | _ => panic!(), | ||
342 | }; | ||
343 | |||
344 | db.body(fn_def.into()) | ||
345 | } | ||
346 | |||
347 | #[test] | ||
348 | fn your_stack_belongs_to_me() { | ||
349 | mark::check!(your_stack_belongs_to_me); | ||
350 | lower( | ||
351 | " | ||
352 | macro_rules! n_nuple { | ||
353 | ($e:tt) => (); | ||
354 | ($($rest:tt)*) => {{ | ||
355 | (n_nuple!($($rest)*)None,) | ||
356 | }}; | ||
357 | } | ||
358 | fn main() { n_nuple!(1,2,3); } | ||
359 | ", | ||
360 | ); | ||
361 | } | 340 | } |
362 | } | 341 | } |
diff --git a/crates/hir_def/src/body/diagnostics.rs b/crates/hir_def/src/body/diagnostics.rs new file mode 100644 index 000000000..cfa47d189 --- /dev/null +++ b/crates/hir_def/src/body/diagnostics.rs | |||
@@ -0,0 +1,20 @@ | |||
1 | //! Diagnostics emitted during body lowering. | ||
2 | |||
3 | use hir_expand::diagnostics::DiagnosticSink; | ||
4 | |||
5 | use crate::diagnostics::InactiveCode; | ||
6 | |||
7 | #[derive(Debug, Eq, PartialEq)] | ||
8 | pub enum BodyDiagnostic { | ||
9 | InactiveCode(InactiveCode), | ||
10 | } | ||
11 | |||
12 | impl BodyDiagnostic { | ||
13 | pub fn add_to(&self, sink: &mut DiagnosticSink<'_>) { | ||
14 | match self { | ||
15 | BodyDiagnostic::InactiveCode(diag) => { | ||
16 | sink.push(diag.clone()); | ||
17 | } | ||
18 | } | ||
19 | } | ||
20 | } | ||
diff --git a/crates/hir_def/src/body/lower.rs b/crates/hir_def/src/body/lower.rs index 01e72690a..ddc267b83 100644 --- a/crates/hir_def/src/body/lower.rs +++ b/crates/hir_def/src/body/lower.rs | |||
@@ -16,7 +16,7 @@ use syntax::{ | |||
16 | self, ArgListOwner, ArrayExprKind, AstChildren, LiteralKind, LoopBodyOwner, NameOwner, | 16 | self, ArgListOwner, ArrayExprKind, AstChildren, LiteralKind, LoopBodyOwner, NameOwner, |
17 | SlicePatComponents, | 17 | SlicePatComponents, |
18 | }, | 18 | }, |
19 | AstNode, AstPtr, | 19 | AstNode, AstPtr, SyntaxNodePtr, |
20 | }; | 20 | }; |
21 | use test_utils::mark; | 21 | use test_utils::mark; |
22 | 22 | ||
@@ -25,6 +25,7 @@ use crate::{ | |||
25 | body::{Body, BodySourceMap, Expander, PatPtr, SyntheticSyntax}, | 25 | body::{Body, BodySourceMap, Expander, PatPtr, SyntheticSyntax}, |
26 | builtin_type::{BuiltinFloat, BuiltinInt}, | 26 | builtin_type::{BuiltinFloat, BuiltinInt}, |
27 | db::DefDatabase, | 27 | db::DefDatabase, |
28 | diagnostics::InactiveCode, | ||
28 | expr::{ | 29 | expr::{ |
29 | dummy_expr_id, ArithOp, Array, BinaryOp, BindingAnnotation, CmpOp, Expr, ExprId, Literal, | 30 | dummy_expr_id, ArithOp, Array, BinaryOp, BindingAnnotation, CmpOp, Expr, ExprId, Literal, |
30 | LogicOp, MatchArm, Ordering, Pat, PatId, RecordFieldPat, RecordLitField, Statement, | 31 | LogicOp, MatchArm, Ordering, Pat, PatId, RecordFieldPat, RecordLitField, Statement, |
@@ -37,7 +38,7 @@ use crate::{ | |||
37 | StaticLoc, StructLoc, TraitLoc, TypeAliasLoc, UnionLoc, | 38 | StaticLoc, StructLoc, TraitLoc, TypeAliasLoc, UnionLoc, |
38 | }; | 39 | }; |
39 | 40 | ||
40 | use super::{ExprSource, PatSource}; | 41 | use super::{diagnostics::BodyDiagnostic, ExprSource, PatSource}; |
41 | 42 | ||
42 | pub(crate) struct LowerCtx { | 43 | pub(crate) struct LowerCtx { |
43 | hygiene: Hygiene, | 44 | hygiene: Hygiene, |
@@ -176,7 +177,7 @@ impl ExprCollector<'_> { | |||
176 | 177 | ||
177 | fn collect_expr(&mut self, expr: ast::Expr) -> ExprId { | 178 | fn collect_expr(&mut self, expr: ast::Expr) -> ExprId { |
178 | let syntax_ptr = AstPtr::new(&expr); | 179 | let syntax_ptr = AstPtr::new(&expr); |
179 | if !self.expander.is_cfg_enabled(&expr) { | 180 | if self.check_cfg(&expr).is_none() { |
180 | return self.missing_expr(); | 181 | return self.missing_expr(); |
181 | } | 182 | } |
182 | 183 | ||
@@ -354,13 +355,15 @@ impl ExprCollector<'_> { | |||
354 | let arms = if let Some(match_arm_list) = e.match_arm_list() { | 355 | let arms = if let Some(match_arm_list) = e.match_arm_list() { |
355 | match_arm_list | 356 | match_arm_list |
356 | .arms() | 357 | .arms() |
357 | .map(|arm| MatchArm { | 358 | .filter_map(|arm| { |
358 | pat: self.collect_pat_opt(arm.pat()), | 359 | self.check_cfg(&arm).map(|()| MatchArm { |
359 | expr: self.collect_expr_opt(arm.expr()), | 360 | pat: self.collect_pat_opt(arm.pat()), |
360 | guard: arm | 361 | expr: self.collect_expr_opt(arm.expr()), |
361 | .guard() | 362 | guard: arm |
362 | .and_then(|guard| guard.expr()) | 363 | .guard() |
363 | .map(|e| self.collect_expr(e)), | 364 | .and_then(|guard| guard.expr()) |
365 | .map(|e| self.collect_expr(e)), | ||
366 | }) | ||
364 | }) | 367 | }) |
365 | .collect() | 368 | .collect() |
366 | } else { | 369 | } else { |
@@ -406,9 +409,8 @@ impl ExprCollector<'_> { | |||
406 | .fields() | 409 | .fields() |
407 | .inspect(|field| field_ptrs.push(AstPtr::new(field))) | 410 | .inspect(|field| field_ptrs.push(AstPtr::new(field))) |
408 | .filter_map(|field| { | 411 | .filter_map(|field| { |
409 | if !self.expander.is_cfg_enabled(&field) { | 412 | self.check_cfg(&field)?; |
410 | return None; | 413 | |
411 | } | ||
412 | let name = field.field_name()?.as_name(); | 414 | let name = field.field_name()?.as_name(); |
413 | 415 | ||
414 | Some(RecordLitField { | 416 | Some(RecordLitField { |
@@ -620,15 +622,23 @@ impl ExprCollector<'_> { | |||
620 | .filter_map(|s| { | 622 | .filter_map(|s| { |
621 | let stmt = match s { | 623 | let stmt = match s { |
622 | ast::Stmt::LetStmt(stmt) => { | 624 | ast::Stmt::LetStmt(stmt) => { |
625 | self.check_cfg(&stmt)?; | ||
626 | |||
623 | let pat = self.collect_pat_opt(stmt.pat()); | 627 | let pat = self.collect_pat_opt(stmt.pat()); |
624 | let type_ref = stmt.ty().map(|it| TypeRef::from_ast(&self.ctx(), it)); | 628 | let type_ref = stmt.ty().map(|it| TypeRef::from_ast(&self.ctx(), it)); |
625 | let initializer = stmt.initializer().map(|e| self.collect_expr(e)); | 629 | let initializer = stmt.initializer().map(|e| self.collect_expr(e)); |
626 | Statement::Let { pat, type_ref, initializer } | 630 | Statement::Let { pat, type_ref, initializer } |
627 | } | 631 | } |
628 | ast::Stmt::ExprStmt(stmt) => { | 632 | ast::Stmt::ExprStmt(stmt) => { |
633 | self.check_cfg(&stmt)?; | ||
634 | |||
629 | Statement::Expr(self.collect_expr_opt(stmt.expr())) | 635 | Statement::Expr(self.collect_expr_opt(stmt.expr())) |
630 | } | 636 | } |
631 | ast::Stmt::Item(_) => return None, | 637 | ast::Stmt::Item(item) => { |
638 | self.check_cfg(&item)?; | ||
639 | |||
640 | return None; | ||
641 | } | ||
632 | }; | 642 | }; |
633 | Some(stmt) | 643 | Some(stmt) |
634 | }) | 644 | }) |
@@ -872,6 +882,28 @@ impl ExprCollector<'_> { | |||
872 | 882 | ||
873 | (args, ellipsis) | 883 | (args, ellipsis) |
874 | } | 884 | } |
885 | |||
886 | /// Returns `None` (and emits diagnostics) when `owner` if `#[cfg]`d out, and `Some(())` when | ||
887 | /// not. | ||
888 | fn check_cfg(&mut self, owner: &dyn ast::AttrsOwner) -> Option<()> { | ||
889 | match self.expander.parse_attrs(owner).cfg() { | ||
890 | Some(cfg) => { | ||
891 | if self.expander.cfg_options().check(&cfg) != Some(false) { | ||
892 | return Some(()); | ||
893 | } | ||
894 | |||
895 | self.source_map.diagnostics.push(BodyDiagnostic::InactiveCode(InactiveCode { | ||
896 | file: self.expander.current_file_id, | ||
897 | node: SyntaxNodePtr::new(owner.syntax()), | ||
898 | cfg, | ||
899 | opts: self.expander.cfg_options().clone(), | ||
900 | })); | ||
901 | |||
902 | None | ||
903 | } | ||
904 | None => Some(()), | ||
905 | } | ||
906 | } | ||
875 | } | 907 | } |
876 | 908 | ||
877 | impl From<ast::BinOp> for BinaryOp { | 909 | impl From<ast::BinOp> for BinaryOp { |
diff --git a/crates/hir_def/src/body/tests.rs b/crates/hir_def/src/body/tests.rs new file mode 100644 index 000000000..f07df5cee --- /dev/null +++ b/crates/hir_def/src/body/tests.rs | |||
@@ -0,0 +1,75 @@ | |||
1 | use base_db::{fixture::WithFixture, SourceDatabase}; | ||
2 | use test_utils::mark; | ||
3 | |||
4 | use crate::{test_db::TestDB, ModuleDefId}; | ||
5 | |||
6 | use super::*; | ||
7 | |||
8 | fn lower(ra_fixture: &str) -> Arc<Body> { | ||
9 | let (db, file_id) = crate::test_db::TestDB::with_single_file(ra_fixture); | ||
10 | |||
11 | let krate = db.crate_graph().iter().next().unwrap(); | ||
12 | let def_map = db.crate_def_map(krate); | ||
13 | let module = def_map.modules_for_file(file_id).next().unwrap(); | ||
14 | let module = &def_map[module]; | ||
15 | let fn_def = match module.scope.declarations().next().unwrap() { | ||
16 | ModuleDefId::FunctionId(it) => it, | ||
17 | _ => panic!(), | ||
18 | }; | ||
19 | |||
20 | db.body(fn_def.into()) | ||
21 | } | ||
22 | |||
23 | fn check_diagnostics(ra_fixture: &str) { | ||
24 | let db: TestDB = TestDB::with_files(ra_fixture); | ||
25 | db.check_diagnostics(); | ||
26 | } | ||
27 | |||
28 | #[test] | ||
29 | fn your_stack_belongs_to_me() { | ||
30 | mark::check!(your_stack_belongs_to_me); | ||
31 | lower( | ||
32 | " | ||
33 | macro_rules! n_nuple { | ||
34 | ($e:tt) => (); | ||
35 | ($($rest:tt)*) => {{ | ||
36 | (n_nuple!($($rest)*)None,) | ||
37 | }}; | ||
38 | } | ||
39 | fn main() { n_nuple!(1,2,3); } | ||
40 | ", | ||
41 | ); | ||
42 | } | ||
43 | |||
44 | #[test] | ||
45 | fn cfg_diagnostics() { | ||
46 | check_diagnostics( | ||
47 | r" | ||
48 | fn f() { | ||
49 | // The three g̶e̶n̶d̶e̶r̶s̶ statements: | ||
50 | |||
51 | #[cfg(a)] fn f() {} // Item statement | ||
52 | //^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: a is disabled | ||
53 | #[cfg(a)] {} // Expression statement | ||
54 | //^^^^^^^^^^^^ code is inactive due to #[cfg] directives: a is disabled | ||
55 | #[cfg(a)] let x = 0; // let statement | ||
56 | //^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: a is disabled | ||
57 | |||
58 | abc(#[cfg(a)] 0); | ||
59 | //^^^^^^^^^^^ code is inactive due to #[cfg] directives: a is disabled | ||
60 | let x = Struct { | ||
61 | #[cfg(a)] f: 0, | ||
62 | //^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: a is disabled | ||
63 | }; | ||
64 | match () { | ||
65 | () => (), | ||
66 | #[cfg(a)] () => (), | ||
67 | //^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: a is disabled | ||
68 | } | ||
69 | |||
70 | #[cfg(a)] 0 // Trailing expression of block | ||
71 | //^^^^^^^^^^^ code is inactive due to #[cfg] directives: a is disabled | ||
72 | } | ||
73 | ", | ||
74 | ); | ||
75 | } | ||
diff --git a/crates/hir_def/src/diagnostics.rs b/crates/hir_def/src/diagnostics.rs index c9c08b01f..b221b290c 100644 --- a/crates/hir_def/src/diagnostics.rs +++ b/crates/hir_def/src/diagnostics.rs | |||
@@ -1,11 +1,19 @@ | |||
1 | //! Diagnostics produced by `hir_def`. | 1 | //! Diagnostics produced by `hir_def`. |
2 | 2 | ||
3 | use std::any::Any; | 3 | use std::any::Any; |
4 | use stdx::format_to; | ||
4 | 5 | ||
5 | use hir_expand::diagnostics::{Diagnostic, DiagnosticCode}; | 6 | use cfg::{CfgExpr, CfgOptions, DnfExpr}; |
7 | use hir_expand::diagnostics::{Diagnostic, DiagnosticCode, DiagnosticSink}; | ||
8 | use hir_expand::{HirFileId, InFile}; | ||
6 | use syntax::{ast, AstPtr, SyntaxNodePtr}; | 9 | use syntax::{ast, AstPtr, SyntaxNodePtr}; |
7 | 10 | ||
8 | use hir_expand::{HirFileId, InFile}; | 11 | use crate::{db::DefDatabase, DefWithBodyId}; |
12 | |||
13 | pub fn validate_body(db: &dyn DefDatabase, owner: DefWithBodyId, sink: &mut DiagnosticSink<'_>) { | ||
14 | let source_map = db.body_with_source_map(owner).1; | ||
15 | source_map.add_diagnostics(db, sink); | ||
16 | } | ||
9 | 17 | ||
10 | // Diagnostic: unresolved-module | 18 | // Diagnostic: unresolved-module |
11 | // | 19 | // |
@@ -87,13 +95,15 @@ impl Diagnostic for UnresolvedImport { | |||
87 | } | 95 | } |
88 | } | 96 | } |
89 | 97 | ||
90 | // Diagnostic: unconfigured-code | 98 | // Diagnostic: inactive-code |
91 | // | 99 | // |
92 | // This diagnostic is shown for code with inactive `#[cfg]` attributes. | 100 | // This diagnostic is shown for code with inactive `#[cfg]` attributes. |
93 | #[derive(Debug)] | 101 | #[derive(Debug, Clone, Eq, PartialEq)] |
94 | pub struct InactiveCode { | 102 | pub struct InactiveCode { |
95 | pub file: HirFileId, | 103 | pub file: HirFileId, |
96 | pub node: SyntaxNodePtr, | 104 | pub node: SyntaxNodePtr, |
105 | pub cfg: CfgExpr, | ||
106 | pub opts: CfgOptions, | ||
97 | } | 107 | } |
98 | 108 | ||
99 | impl Diagnostic for InactiveCode { | 109 | impl Diagnostic for InactiveCode { |
@@ -101,8 +111,14 @@ impl Diagnostic for InactiveCode { | |||
101 | DiagnosticCode("inactive-code") | 111 | DiagnosticCode("inactive-code") |
102 | } | 112 | } |
103 | fn message(&self) -> String { | 113 | fn message(&self) -> String { |
104 | // FIXME: say *why* it is configured out | 114 | let inactive = DnfExpr::new(self.cfg.clone()).why_inactive(&self.opts); |
105 | "code is inactive due to #[cfg] directives".to_string() | 115 | let mut buf = "code is inactive due to #[cfg] directives".to_string(); |
116 | |||
117 | if let Some(inactive) = inactive { | ||
118 | format_to!(buf, ": {}", inactive); | ||
119 | } | ||
120 | |||
121 | buf | ||
106 | } | 122 | } |
107 | fn display_source(&self) -> InFile<SyntaxNodePtr> { | 123 | fn display_source(&self) -> InFile<SyntaxNodePtr> { |
108 | InFile::new(self.file, self.node.clone()) | 124 | InFile::new(self.file, self.node.clone()) |
diff --git a/crates/hir_def/src/nameres.rs b/crates/hir_def/src/nameres.rs index 01a28aeeb..eb41d324e 100644 --- a/crates/hir_def/src/nameres.rs +++ b/crates/hir_def/src/nameres.rs | |||
@@ -283,6 +283,7 @@ pub enum ModuleSource { | |||
283 | } | 283 | } |
284 | 284 | ||
285 | mod diagnostics { | 285 | mod diagnostics { |
286 | use cfg::{CfgExpr, CfgOptions}; | ||
286 | use hir_expand::diagnostics::DiagnosticSink; | 287 | use hir_expand::diagnostics::DiagnosticSink; |
287 | use hir_expand::hygiene::Hygiene; | 288 | use hir_expand::hygiene::Hygiene; |
288 | use hir_expand::InFile; | 289 | use hir_expand::InFile; |
@@ -299,7 +300,7 @@ mod diagnostics { | |||
299 | 300 | ||
300 | UnresolvedImport { ast: AstId<ast::Use>, index: usize }, | 301 | UnresolvedImport { ast: AstId<ast::Use>, index: usize }, |
301 | 302 | ||
302 | UnconfiguredCode { ast: InFile<SyntaxNodePtr> }, | 303 | UnconfiguredCode { ast: InFile<SyntaxNodePtr>, cfg: CfgExpr, opts: CfgOptions }, |
303 | } | 304 | } |
304 | 305 | ||
305 | #[derive(Debug, PartialEq, Eq)] | 306 | #[derive(Debug, PartialEq, Eq)] |
@@ -341,8 +342,10 @@ mod diagnostics { | |||
341 | pub(super) fn unconfigured_code( | 342 | pub(super) fn unconfigured_code( |
342 | container: LocalModuleId, | 343 | container: LocalModuleId, |
343 | ast: InFile<SyntaxNodePtr>, | 344 | ast: InFile<SyntaxNodePtr>, |
345 | cfg: CfgExpr, | ||
346 | opts: CfgOptions, | ||
344 | ) -> Self { | 347 | ) -> Self { |
345 | Self { in_module: container, kind: DiagnosticKind::UnconfiguredCode { ast } } | 348 | Self { in_module: container, kind: DiagnosticKind::UnconfiguredCode { ast, cfg, opts } } |
346 | } | 349 | } |
347 | 350 | ||
348 | pub(super) fn add_to( | 351 | pub(super) fn add_to( |
@@ -395,8 +398,13 @@ mod diagnostics { | |||
395 | } | 398 | } |
396 | } | 399 | } |
397 | 400 | ||
398 | DiagnosticKind::UnconfiguredCode { ast } => { | 401 | DiagnosticKind::UnconfiguredCode { ast, cfg, opts } => { |
399 | sink.push(InactiveCode { file: ast.file_id, node: ast.value.clone() }); | 402 | sink.push(InactiveCode { |
403 | file: ast.file_id, | ||
404 | node: ast.value.clone(), | ||
405 | cfg: cfg.clone(), | ||
406 | opts: opts.clone(), | ||
407 | }); | ||
400 | } | 408 | } |
401 | } | 409 | } |
402 | } | 410 | } |
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs index bff8edb62..f30172d90 100644 --- a/crates/hir_def/src/nameres/collector.rs +++ b/crates/hir_def/src/nameres/collector.rs | |||
@@ -6,7 +6,7 @@ | |||
6 | use std::iter; | 6 | use std::iter; |
7 | 7 | ||
8 | use base_db::{CrateId, FileId, ProcMacroId}; | 8 | use base_db::{CrateId, FileId, ProcMacroId}; |
9 | use cfg::CfgOptions; | 9 | use cfg::{CfgExpr, CfgOptions}; |
10 | use hir_expand::InFile; | 10 | use hir_expand::InFile; |
11 | use hir_expand::{ | 11 | use hir_expand::{ |
12 | ast_id_map::FileAstId, | 12 | ast_id_map::FileAstId, |
@@ -900,7 +900,8 @@ impl ModCollector<'_, '_> { | |||
900 | // `#[macro_use] extern crate` is hoisted to imports macros before collecting | 900 | // `#[macro_use] extern crate` is hoisted to imports macros before collecting |
901 | // any other items. | 901 | // any other items. |
902 | for item in items { | 902 | for item in items { |
903 | if self.is_cfg_enabled(self.item_tree.attrs((*item).into())) { | 903 | let attrs = self.item_tree.attrs((*item).into()); |
904 | if attrs.cfg().map_or(true, |cfg| self.is_cfg_enabled(&cfg)) { | ||
904 | if let ModItem::ExternCrate(id) = item { | 905 | if let ModItem::ExternCrate(id) = item { |
905 | let import = self.item_tree[*id].clone(); | 906 | let import = self.item_tree[*id].clone(); |
906 | if import.is_macro_use { | 907 | if import.is_macro_use { |
@@ -912,9 +913,11 @@ impl ModCollector<'_, '_> { | |||
912 | 913 | ||
913 | for &item in items { | 914 | for &item in items { |
914 | let attrs = self.item_tree.attrs(item.into()); | 915 | let attrs = self.item_tree.attrs(item.into()); |
915 | if !self.is_cfg_enabled(attrs) { | 916 | if let Some(cfg) = attrs.cfg() { |
916 | self.emit_unconfigured_diagnostic(item); | 917 | if !self.is_cfg_enabled(&cfg) { |
917 | continue; | 918 | self.emit_unconfigured_diagnostic(item, &cfg); |
919 | continue; | ||
920 | } | ||
918 | } | 921 | } |
919 | let module = | 922 | let module = |
920 | ModuleId { krate: self.def_collector.def_map.krate, local_id: self.module_id }; | 923 | ModuleId { krate: self.def_collector.def_map.krate, local_id: self.module_id }; |
@@ -1321,20 +1324,22 @@ impl ModCollector<'_, '_> { | |||
1321 | } | 1324 | } |
1322 | } | 1325 | } |
1323 | 1326 | ||
1324 | fn is_cfg_enabled(&self, attrs: &Attrs) -> bool { | 1327 | fn is_cfg_enabled(&self, cfg: &CfgExpr) -> bool { |
1325 | attrs.is_cfg_enabled(self.def_collector.cfg_options) | 1328 | self.def_collector.cfg_options.check(cfg) != Some(false) |
1326 | } | 1329 | } |
1327 | 1330 | ||
1328 | fn emit_unconfigured_diagnostic(&mut self, item: ModItem) { | 1331 | fn emit_unconfigured_diagnostic(&mut self, item: ModItem, cfg: &CfgExpr) { |
1329 | let ast_id = item.ast_id(self.item_tree); | 1332 | let ast_id = item.ast_id(self.item_tree); |
1330 | let id_map = self.def_collector.db.ast_id_map(self.file_id); | 1333 | let id_map = self.def_collector.db.ast_id_map(self.file_id); |
1331 | let syntax_ptr = id_map.get(ast_id).syntax_node_ptr(); | 1334 | let syntax_ptr = id_map.get(ast_id).syntax_node_ptr(); |
1332 | 1335 | ||
1333 | let ast_node = InFile::new(self.file_id, syntax_ptr); | 1336 | let ast_node = InFile::new(self.file_id, syntax_ptr); |
1334 | self.def_collector | 1337 | self.def_collector.def_map.diagnostics.push(DefDiagnostic::unconfigured_code( |
1335 | .def_map | 1338 | self.module_id, |
1336 | .diagnostics | 1339 | ast_node, |
1337 | .push(DefDiagnostic::unconfigured_code(self.module_id, ast_node)); | 1340 | cfg.clone(), |
1341 | self.def_collector.cfg_options.clone(), | ||
1342 | )); | ||
1338 | } | 1343 | } |
1339 | } | 1344 | } |
1340 | 1345 | ||
diff --git a/crates/hir_def/src/nameres/tests/diagnostics.rs b/crates/hir_def/src/nameres/tests/diagnostics.rs index 576b813d2..1a7b98831 100644 --- a/crates/hir_def/src/nameres/tests/diagnostics.rs +++ b/crates/hir_def/src/nameres/tests/diagnostics.rs | |||
@@ -1,42 +1,10 @@ | |||
1 | use base_db::fixture::WithFixture; | 1 | use base_db::fixture::WithFixture; |
2 | use base_db::FileId; | ||
3 | use base_db::SourceDatabaseExt; | ||
4 | use hir_expand::db::AstDatabase; | ||
5 | use rustc_hash::FxHashMap; | ||
6 | use syntax::TextRange; | ||
7 | use syntax::TextSize; | ||
8 | 2 | ||
9 | use crate::test_db::TestDB; | 3 | use crate::test_db::TestDB; |
10 | 4 | ||
11 | fn check_diagnostics(ra_fixture: &str) { | 5 | fn check_diagnostics(ra_fixture: &str) { |
12 | let db: TestDB = TestDB::with_files(ra_fixture); | 6 | let db: TestDB = TestDB::with_files(ra_fixture); |
13 | let annotations = db.extract_annotations(); | 7 | db.check_diagnostics(); |
14 | assert!(!annotations.is_empty()); | ||
15 | |||
16 | let mut actual: FxHashMap<FileId, Vec<(TextRange, String)>> = FxHashMap::default(); | ||
17 | db.diagnostics(|d| { | ||
18 | let src = d.display_source(); | ||
19 | let root = db.parse_or_expand(src.file_id).unwrap(); | ||
20 | // FIXME: macros... | ||
21 | let file_id = src.file_id.original_file(&db); | ||
22 | let range = src.value.to_node(&root).text_range(); | ||
23 | let message = d.message().to_owned(); | ||
24 | actual.entry(file_id).or_default().push((range, message)); | ||
25 | }); | ||
26 | |||
27 | for (file_id, diags) in actual.iter_mut() { | ||
28 | diags.sort_by_key(|it| it.0.start()); | ||
29 | let text = db.file_text(*file_id); | ||
30 | // For multiline spans, place them on line start | ||
31 | for (range, content) in diags { | ||
32 | if text[*range].contains('\n') { | ||
33 | *range = TextRange::new(range.start(), range.start() + TextSize::from(1)); | ||
34 | *content = format!("... {}", content); | ||
35 | } | ||
36 | } | ||
37 | } | ||
38 | |||
39 | assert_eq!(annotations, actual); | ||
40 | } | 8 | } |
41 | 9 | ||
42 | #[test] | 10 | #[test] |
@@ -129,3 +97,25 @@ fn unresolved_module() { | |||
129 | ", | 97 | ", |
130 | ); | 98 | ); |
131 | } | 99 | } |
100 | |||
101 | #[test] | ||
102 | fn inactive_item() { | ||
103 | // Additional tests in `cfg` crate. This only tests disabled cfgs. | ||
104 | |||
105 | check_diagnostics( | ||
106 | r#" | ||
107 | //- /lib.rs | ||
108 | #[cfg(no)] pub fn f() {} | ||
109 | //^^^^^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: no is disabled | ||
110 | |||
111 | #[cfg(no)] #[cfg(no2)] mod m; | ||
112 | //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: no and no2 are disabled | ||
113 | |||
114 | #[cfg(all(not(a), b))] enum E {} | ||
115 | //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: b is disabled | ||
116 | |||
117 | #[cfg(feature = "std")] use std; | ||
118 | //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: feature = "std" is disabled | ||
119 | "#, | ||
120 | ); | ||
121 | } | ||
diff --git a/crates/hir_def/src/test_db.rs b/crates/hir_def/src/test_db.rs index fb1d3c974..2b36c824a 100644 --- a/crates/hir_def/src/test_db.rs +++ b/crates/hir_def/src/test_db.rs | |||
@@ -12,10 +12,10 @@ use hir_expand::diagnostics::Diagnostic; | |||
12 | use hir_expand::diagnostics::DiagnosticSinkBuilder; | 12 | use hir_expand::diagnostics::DiagnosticSinkBuilder; |
13 | use rustc_hash::FxHashMap; | 13 | use rustc_hash::FxHashMap; |
14 | use rustc_hash::FxHashSet; | 14 | use rustc_hash::FxHashSet; |
15 | use syntax::TextRange; | 15 | use syntax::{TextRange, TextSize}; |
16 | use test_utils::extract_annotations; | 16 | use test_utils::extract_annotations; |
17 | 17 | ||
18 | use crate::db::DefDatabase; | 18 | use crate::{db::DefDatabase, ModuleDefId}; |
19 | 19 | ||
20 | #[salsa::database( | 20 | #[salsa::database( |
21 | base_db::SourceDatabaseExtStorage, | 21 | base_db::SourceDatabaseExtStorage, |
@@ -135,9 +135,47 @@ impl TestDB { | |||
135 | let crate_def_map = self.crate_def_map(krate); | 135 | let crate_def_map = self.crate_def_map(krate); |
136 | 136 | ||
137 | let mut sink = DiagnosticSinkBuilder::new().build(&mut cb); | 137 | let mut sink = DiagnosticSinkBuilder::new().build(&mut cb); |
138 | for (module_id, _) in crate_def_map.modules.iter() { | 138 | for (module_id, module) in crate_def_map.modules.iter() { |
139 | crate_def_map.add_diagnostics(self, module_id, &mut sink); | 139 | crate_def_map.add_diagnostics(self, module_id, &mut sink); |
140 | |||
141 | for decl in module.scope.declarations() { | ||
142 | if let ModuleDefId::FunctionId(it) = decl { | ||
143 | let source_map = self.body_with_source_map(it.into()).1; | ||
144 | source_map.add_diagnostics(self, &mut sink); | ||
145 | } | ||
146 | } | ||
140 | } | 147 | } |
141 | } | 148 | } |
142 | } | 149 | } |
150 | |||
151 | pub fn check_diagnostics(&self) { | ||
152 | let db: &TestDB = self; | ||
153 | let annotations = db.extract_annotations(); | ||
154 | assert!(!annotations.is_empty()); | ||
155 | |||
156 | let mut actual: FxHashMap<FileId, Vec<(TextRange, String)>> = FxHashMap::default(); | ||
157 | db.diagnostics(|d| { | ||
158 | let src = d.display_source(); | ||
159 | let root = db.parse_or_expand(src.file_id).unwrap(); | ||
160 | // FIXME: macros... | ||
161 | let file_id = src.file_id.original_file(db); | ||
162 | let range = src.value.to_node(&root).text_range(); | ||
163 | let message = d.message().to_owned(); | ||
164 | actual.entry(file_id).or_default().push((range, message)); | ||
165 | }); | ||
166 | |||
167 | for (file_id, diags) in actual.iter_mut() { | ||
168 | diags.sort_by_key(|it| it.0.start()); | ||
169 | let text = db.file_text(*file_id); | ||
170 | // For multiline spans, place them on line start | ||
171 | for (range, content) in diags { | ||
172 | if text[*range].contains('\n') { | ||
173 | *range = TextRange::new(range.start(), range.start() + TextSize::from(1)); | ||
174 | *content = format!("... {}", content); | ||
175 | } | ||
176 | } | ||
177 | } | ||
178 | |||
179 |