diff options
198 files changed, 6054 insertions, 4469 deletions
diff --git a/Cargo.lock b/Cargo.lock index 39f27098a..b1fef2e80 100644 --- a/Cargo.lock +++ b/Cargo.lock | |||
@@ -107,9 +107,9 @@ checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" | |||
107 | 107 | ||
108 | [[package]] | 108 | [[package]] |
109 | name = "byteorder" | 109 | name = "byteorder" |
110 | version = "1.4.2" | 110 | version = "1.4.3" |
111 | source = "registry+https://github.com/rust-lang/crates.io-index" | 111 | source = "registry+https://github.com/rust-lang/crates.io-index" |
112 | checksum = "ae44d1a3d5a19df61dd0c8beb138458ac2a53a7ac09eba97d55592540004306b" | 112 | checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" |
113 | 113 | ||
114 | [[package]] | 114 | [[package]] |
115 | name = "camino" | 115 | name = "camino" |
@@ -168,9 +168,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" | |||
168 | 168 | ||
169 | [[package]] | 169 | [[package]] |
170 | name = "chalk-derive" | 170 | name = "chalk-derive" |
171 | version = "0.59.0" | 171 | version = "0.60.0" |
172 | source = "registry+https://github.com/rust-lang/crates.io-index" | 172 | source = "registry+https://github.com/rust-lang/crates.io-index" |
173 | checksum = "4b9000fbcb67353dc8973ab9fd136277d321d85b79bd36b8756bb3ae0979a94a" | 173 | checksum = "ab0f74445d4fbeaf0217bc1d23978cc73b95b28e8a738b81894580dd646822d2" |
174 | dependencies = [ | 174 | dependencies = [ |
175 | "proc-macro2", | 175 | "proc-macro2", |
176 | "quote", | 176 | "quote", |
@@ -180,9 +180,9 @@ dependencies = [ | |||
180 | 180 | ||
181 | [[package]] | 181 | [[package]] |
182 | name = "chalk-ir" | 182 | name = "chalk-ir" |
183 | version = "0.59.0" | 183 | version = "0.60.0" |
184 | source = "registry+https://github.com/rust-lang/crates.io-index" | 184 | source = "registry+https://github.com/rust-lang/crates.io-index" |
185 | checksum = "b23528d61b3557c676eccf508fa0771a38453b379f0b780154eaa7f70afe8dfc" | 185 | checksum = "294b1fc6210a5b3bd06c1d01dda48a581e2cafec80b8d659139ce45456644be2" |
186 | dependencies = [ | 186 | dependencies = [ |
187 | "bitflags", | 187 | "bitflags", |
188 | "chalk-derive", | 188 | "chalk-derive", |
@@ -191,9 +191,9 @@ dependencies = [ | |||
191 | 191 | ||
192 | [[package]] | 192 | [[package]] |
193 | name = "chalk-recursive" | 193 | name = "chalk-recursive" |
194 | version = "0.59.0" | 194 | version = "0.60.0" |
195 | source = "registry+https://github.com/rust-lang/crates.io-index" | 195 | source = "registry+https://github.com/rust-lang/crates.io-index" |
196 | checksum = "a8bdd37afc666b771de8b4429fe014363d0e74aae5cc26f320f60a3eab34d744" | 196 | checksum = "1b9386936070be4545bfa22b094b7065af79aa2aeaccc945438f1c5ffe74c30a" |
197 | dependencies = [ | 197 | dependencies = [ |
198 | "chalk-derive", | 198 | "chalk-derive", |
199 | "chalk-ir", | 199 | "chalk-ir", |
@@ -204,9 +204,9 @@ dependencies = [ | |||
204 | 204 | ||
205 | [[package]] | 205 | [[package]] |
206 | name = "chalk-solve" | 206 | name = "chalk-solve" |
207 | version = "0.59.0" | 207 | version = "0.60.0" |
208 | source = "registry+https://github.com/rust-lang/crates.io-index" | 208 | source = "registry+https://github.com/rust-lang/crates.io-index" |
209 | checksum = "4182c42ca319cb71c89898ebc3d2671d1fa7d928123b171b66f1797a2000b9c8" | 209 | checksum = "7c12a1ec7e850b50a049f27ef9cf5df3056bbd1acbb3eeb44d024e501a641f3a" |
210 | dependencies = [ | 210 | dependencies = [ |
211 | "chalk-derive", | 211 | "chalk-derive", |
212 | "chalk-ir", | 212 | "chalk-ir", |
@@ -232,15 +232,6 @@ dependencies = [ | |||
232 | ] | 232 | ] |
233 | 233 | ||
234 | [[package]] | 234 | [[package]] |
235 | name = "cmake" | ||
236 | version = "0.1.45" | ||
237 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
238 | checksum = "eb6210b637171dfba4cda12e579ac6dc73f5165ad56133e5d72ef3131f320855" | ||
239 | dependencies = [ | ||
240 | "cc", | ||
241 | ] | ||
242 | |||
243 | [[package]] | ||
244 | name = "countme" | 235 | name = "countme" |
245 | version = "2.0.4" | 236 | version = "2.0.4" |
246 | source = "registry+https://github.com/rust-lang/crates.io-index" | 237 | source = "registry+https://github.com/rust-lang/crates.io-index" |
@@ -252,6 +243,12 @@ dependencies = [ | |||
252 | ] | 243 | ] |
253 | 244 | ||
254 | [[package]] | 245 | [[package]] |
246 | name = "cov-mark" | ||
247 | version = "1.1.0" | ||
248 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
249 | checksum = "9ffa3d3e0138386cd4361f63537765cac7ee40698028844635a54495a92f67f3" | ||
250 | |||
251 | [[package]] | ||
255 | name = "crc32fast" | 252 | name = "crc32fast" |
256 | version = "1.2.1" | 253 | version = "1.2.1" |
257 | source = "registry+https://github.com/rust-lang/crates.io-index" | 254 | source = "registry+https://github.com/rust-lang/crates.io-index" |
@@ -501,6 +498,7 @@ dependencies = [ | |||
501 | "anymap", | 498 | "anymap", |
502 | "base_db", | 499 | "base_db", |
503 | "cfg", | 500 | "cfg", |
501 | "cov-mark", | ||
504 | "drop_bomb", | 502 | "drop_bomb", |
505 | "either", | 503 | "either", |
506 | "expect-test", | 504 | "expect-test", |
@@ -526,7 +524,9 @@ name = "hir_expand" | |||
526 | version = "0.0.0" | 524 | version = "0.0.0" |
527 | dependencies = [ | 525 | dependencies = [ |
528 | "base_db", | 526 | "base_db", |
527 | "cfg", | ||
529 | "either", | 528 | "either", |
529 | "expect-test", | ||
530 | "la-arena", | 530 | "la-arena", |
531 | "log", | 531 | "log", |
532 | "mbe", | 532 | "mbe", |
@@ -547,6 +547,7 @@ dependencies = [ | |||
547 | "chalk-ir", | 547 | "chalk-ir", |
548 | "chalk-recursive", | 548 | "chalk-recursive", |
549 | "chalk-solve", | 549 | "chalk-solve", |
550 | "cov-mark", | ||
550 | "ena", | 551 | "ena", |
551 | "expect-test", | 552 | "expect-test", |
552 | "hir_def", | 553 | "hir_def", |
@@ -581,6 +582,7 @@ name = "ide" | |||
581 | version = "0.0.0" | 582 | version = "0.0.0" |
582 | dependencies = [ | 583 | dependencies = [ |
583 | "cfg", | 584 | "cfg", |
585 | "cov-mark", | ||
584 | "either", | 586 | "either", |
585 | "expect-test", | 587 | "expect-test", |
586 | "hir", | 588 | "hir", |
@@ -607,6 +609,7 @@ dependencies = [ | |||
607 | name = "ide_assists" | 609 | name = "ide_assists" |
608 | version = "0.0.0" | 610 | version = "0.0.0" |
609 | dependencies = [ | 611 | dependencies = [ |
612 | "cov-mark", | ||
610 | "either", | 613 | "either", |
611 | "expect-test", | 614 | "expect-test", |
612 | "hir", | 615 | "hir", |
@@ -625,6 +628,7 @@ name = "ide_completion" | |||
625 | version = "0.0.0" | 628 | version = "0.0.0" |
626 | dependencies = [ | 629 | dependencies = [ |
627 | "base_db", | 630 | "base_db", |
631 | "cov-mark", | ||
628 | "either", | 632 | "either", |
629 | "expect-test", | 633 | "expect-test", |
630 | "hir", | 634 | "hir", |
@@ -644,6 +648,7 @@ name = "ide_db" | |||
644 | version = "0.0.0" | 648 | version = "0.0.0" |
645 | dependencies = [ | 649 | dependencies = [ |
646 | "base_db", | 650 | "base_db", |
651 | "cov-mark", | ||
647 | "either", | 652 | "either", |
648 | "expect-test", | 653 | "expect-test", |
649 | "fst", | 654 | "fst", |
@@ -664,6 +669,7 @@ dependencies = [ | |||
664 | name = "ide_ssr" | 669 | name = "ide_ssr" |
665 | version = "0.0.0" | 670 | version = "0.0.0" |
666 | dependencies = [ | 671 | dependencies = [ |
672 | "cov-mark", | ||
667 | "expect-test", | 673 | "expect-test", |
668 | "hir", | 674 | "hir", |
669 | "ide_db", | 675 | "ide_db", |
@@ -687,9 +693,9 @@ dependencies = [ | |||
687 | 693 | ||
688 | [[package]] | 694 | [[package]] |
689 | name = "indexmap" | 695 | name = "indexmap" |
690 | version = "1.6.1" | 696 | version = "1.6.2" |
691 | source = "registry+https://github.com/rust-lang/crates.io-index" | 697 | source = "registry+https://github.com/rust-lang/crates.io-index" |
692 | checksum = "4fb1fa934250de4de8aef298d81c729a7d33d8c239daa3a7575e6b92bfc7313b" | 698 | checksum = "824845a0bf897a9042383849b02c1bc219c2383772efcd5c6f9766fa4b81aef3" |
693 | dependencies = [ | 699 | dependencies = [ |
694 | "autocfg", | 700 | "autocfg", |
695 | "hashbrown", | 701 | "hashbrown", |
@@ -805,11 +811,11 @@ dependencies = [ | |||
805 | 811 | ||
806 | [[package]] | 812 | [[package]] |
807 | name = "libmimalloc-sys" | 813 | name = "libmimalloc-sys" |
808 | version = "0.1.20" | 814 | version = "0.1.21" |
809 | source = "registry+https://github.com/rust-lang/crates.io-index" | 815 | source = "registry+https://github.com/rust-lang/crates.io-index" |
810 | checksum = "e58f42b6424a0ed536678c65fd97cd64b4344bcf86251e284f7c0ce9eee40e64" | 816 | checksum = "2396cf99d2f58611cd69f0efeee4af3d2e2c7b61bed433515029163aa567e65c" |
811 | dependencies = [ | 817 | dependencies = [ |
812 | "cmake", | 818 | "cc", |
813 | ] | 819 | ] |
814 | 820 | ||
815 | [[package]] | 821 | [[package]] |
@@ -874,6 +880,7 @@ checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" | |||
874 | name = "mbe" | 880 | name = "mbe" |
875 | version = "0.0.0" | 881 | version = "0.0.0" |
876 | dependencies = [ | 882 | dependencies = [ |
883 | "cov-mark", | ||
877 | "log", | 884 | "log", |
878 | "parser", | 885 | "parser", |
879 | "profile", | 886 | "profile", |
@@ -892,6 +899,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | |||
892 | checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" | 899 | checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" |
893 | 900 | ||
894 | [[package]] | 901 | [[package]] |
902 | name = "memmap" | ||
903 | version = "0.7.0" | ||
904 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
905 | checksum = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b" | ||
906 | dependencies = [ | ||
907 | "libc", | ||
908 | "winapi", | ||
909 | ] | ||
910 | |||
911 | [[package]] | ||
895 | name = "memmap2" | 912 | name = "memmap2" |
896 | version = "0.2.1" | 913 | version = "0.2.1" |
897 | source = "registry+https://github.com/rust-lang/crates.io-index" | 914 | source = "registry+https://github.com/rust-lang/crates.io-index" |
@@ -911,9 +928,9 @@ dependencies = [ | |||
911 | 928 | ||
912 | [[package]] | 929 | [[package]] |
913 | name = "mimalloc" | 930 | name = "mimalloc" |
914 | version = "0.1.24" | 931 | version = "0.1.25" |
915 | source = "registry+https://github.com/rust-lang/crates.io-index" | 932 | source = "registry+https://github.com/rust-lang/crates.io-index" |
916 | checksum = "757efec188b3d2088949d912e01ea2fe87164ed6376b6c5d7dd4f3ce1668a93d" | 933 | checksum = "1e7c6b11afd1e5e689ac96b6d18b1fc763398fe3d7eed99e8773426bc2033dfb" |
917 | dependencies = [ | 934 | dependencies = [ |
918 | "libmimalloc-sys", | 935 | "libmimalloc-sys", |
919 | ] | 936 | ] |
@@ -1154,8 +1171,11 @@ dependencies = [ | |||
1154 | "crossbeam-channel", | 1171 | "crossbeam-channel", |
1155 | "jod-thread", | 1172 | "jod-thread", |
1156 | "log", | 1173 | "log", |
1174 | "memmap", | ||
1175 | "object", | ||
1157 | "serde", | 1176 | "serde", |
1158 | "serde_json", | 1177 | "serde_json", |
1178 | "snap", | ||
1159 | "stdx", | 1179 | "stdx", |
1160 | "tt", | 1180 | "tt", |
1161 | ] | 1181 | ] |
@@ -1370,9 +1390,9 @@ dependencies = [ | |||
1370 | 1390 | ||
1371 | [[package]] | 1391 | [[package]] |
1372 | name = "rustc-ap-rustc_lexer" | 1392 | name = "rustc-ap-rustc_lexer" |
1373 | version = "709.0.0" | 1393 | version = "710.0.0" |
1374 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1394 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1375 | checksum = "f69f83314702aaccf29c7401cc63bb0d9fa7869a185a23b8379f08c91514b3f3" | 1395 | checksum = "b0bba1ca6787b6d4af505b7a940eae9ecb084dd03e07f03bf3ddbf78e738b617" |
1376 | dependencies = [ | 1396 | dependencies = [ |
1377 | "unicode-xid", | 1397 | "unicode-xid", |
1378 | ] | 1398 | ] |
@@ -1466,18 +1486,18 @@ dependencies = [ | |||
1466 | 1486 | ||
1467 | [[package]] | 1487 | [[package]] |
1468 | name = "serde" | 1488 | name = "serde" |
1469 | version = "1.0.123" | 1489 | version = "1.0.124" |
1470 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1490 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1471 | checksum = "92d5161132722baa40d802cc70b15262b98258453e85e5d1d365c757c73869ae" | 1491 | checksum = "bd761ff957cb2a45fbb9ab3da6512de9de55872866160b23c25f1a841e99d29f" |
1472 | dependencies = [ | 1492 | dependencies = [ |
1473 | "serde_derive", | 1493 | "serde_derive", |
1474 | ] | 1494 | ] |
1475 | 1495 | ||
1476 | [[package]] | 1496 | [[package]] |
1477 | name = "serde_derive" | 1497 | name = "serde_derive" |
1478 | version = "1.0.123" | 1498 | version = "1.0.124" |
1479 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1499 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1480 | checksum = "9391c295d64fc0abb2c556bad848f33cb8296276b1ad2677d1ae1ace4f258f31" | 1500 | checksum = "1800f7693e94e186f5e25a28291ae1570da908aff7d97a095dec1e56ff99069b" |
1481 | dependencies = [ | 1501 | dependencies = [ |
1482 | "proc-macro2", | 1502 | "proc-macro2", |
1483 | "quote", | 1503 | "quote", |
@@ -1541,6 +1561,12 @@ dependencies = [ | |||
1541 | ] | 1561 | ] |
1542 | 1562 | ||
1543 | [[package]] | 1563 | [[package]] |
1564 | name = "snap" | ||
1565 | version = "1.0.4" | ||
1566 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1567 | checksum = "dc725476a1398f0480d56cd0ad381f6f32acf2642704456f8f59a35df464b59a" | ||
1568 | |||
1569 | [[package]] | ||
1544 | name = "socket2" | 1570 | name = "socket2" |
1545 | version = "0.3.19" | 1571 | version = "0.3.19" |
1546 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1572 | source = "registry+https://github.com/rust-lang/crates.io-index" |
@@ -1561,9 +1587,9 @@ dependencies = [ | |||
1561 | 1587 | ||
1562 | [[package]] | 1588 | [[package]] |
1563 | name = "syn" | 1589 | name = "syn" |
1564 | version = "1.0.61" | 1590 | version = "1.0.63" |
1565 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1591 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1566 | checksum = "ed22b90a0e734a23a7610f4283ac9e5acfb96cbb30dfefa540d66f866f1c09c5" | 1592 | checksum = "8fd9bc7ccc2688b3344c2f48b9b546648b25ce0b20fc717ee7fa7981a8ca9717" |
1567 | dependencies = [ | 1593 | dependencies = [ |
1568 | "proc-macro2", | 1594 | "proc-macro2", |
1569 | "quote", | 1595 | "quote", |
@@ -1587,6 +1613,7 @@ name = "syntax" | |||
1587 | version = "0.0.0" | 1613 | version = "0.0.0" |
1588 | dependencies = [ | 1614 | dependencies = [ |
1589 | "arrayvec", | 1615 | "arrayvec", |
1616 | "cov-mark", | ||
1590 | "expect-test", | 1617 | "expect-test", |
1591 | "indexmap", | 1618 | "indexmap", |
1592 | "itertools", | 1619 | "itertools", |
@@ -1621,7 +1648,6 @@ dependencies = [ | |||
1621 | "dissimilar", | 1648 | "dissimilar", |
1622 | "profile", | 1649 | "profile", |
1623 | "rustc-hash", | 1650 | "rustc-hash", |
1624 | "serde_json", | ||
1625 | "stdx", | 1651 | "stdx", |
1626 | "text-size", | 1652 | "text-size", |
1627 | ] | 1653 | ] |
@@ -1756,9 +1782,9 @@ dependencies = [ | |||
1756 | 1782 | ||
1757 | [[package]] | 1783 | [[package]] |
1758 | name = "tracing-tree" | 1784 | name = "tracing-tree" |
1759 | version = "0.1.8" | 1785 | version = "0.1.9" |
1760 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1786 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1761 | checksum = "1a60657cfbf397c603257a8230b3f427e6a2a4e5911a59331b9bb4dffff5b608" | 1787 | checksum = "1712b40907f8d9bc2bc66763ab61dec914b7123d7149e59feb0d4e2a95fc4967" |
1762 | dependencies = [ | 1788 | dependencies = [ |
1763 | "ansi_term", | 1789 | "ansi_term", |
1764 | "atty", | 1790 | "atty", |
@@ -1919,18 +1945,18 @@ checksum = "06069a848f95fceae3e5e03c0ddc8cb78452b56654ee0c8e68f938cf790fb9e3" | |||
1919 | 1945 | ||
1920 | [[package]] | 1946 | [[package]] |
1921 | name = "xflags" | 1947 | name = "xflags" |
1922 | version = "0.1.4" | 1948 | version = "0.2.1" |
1923 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1949 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1924 | checksum = "222e914b43cec5d7305ac5116d10a14b3a52c50e9062d642c92631f3beabc729" | 1950 | checksum = "59ad6ce6a0b7224130015b4ebac796478ac04e0079f5d222a690efea06a9208a" |
1925 | dependencies = [ | 1951 | dependencies = [ |
1926 | "xflags-macros", | 1952 | "xflags-macros", |
1927 | ] | 1953 | ] |
1928 | 1954 | ||
1929 | [[package]] | 1955 | [[package]] |
1930 | name = "xflags-macros" | 1956 | name = "xflags-macros" |
1931 | version = "0.1.4" | 1957 | version = "0.2.1" |
1932 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1958 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1933 | checksum = "52f18f5b4aa7f95e209d5b9274f6164c3938920b4d5c75f97f0dd16daee25ddd" | 1959 | checksum = "c8037d3ca14996158b03c0fa905d0834906ef0fc7044df72c1f5ff690e5e62c9" |
1934 | dependencies = [ | 1960 | dependencies = [ |
1935 | "proc-macro2", | 1961 | "proc-macro2", |
1936 | ] | 1962 | ] |
diff --git a/crates/hir/src/code_model.rs b/crates/hir/src/code_model.rs deleted file mode 100644 index 9ee4b3059..000000000 --- a/crates/hir/src/code_model.rs +++ /dev/null | |||
@@ -1,2095 +0,0 @@ | |||
1 | //! FIXME: write short doc here | ||
2 | use std::{iter, sync::Arc}; | ||
3 | |||
4 | use arrayvec::ArrayVec; | ||
5 | use base_db::{CrateDisplayName, CrateId, Edition, FileId}; | ||
6 | use either::Either; | ||
7 | use hir_def::{ | ||
8 | adt::{ReprKind, StructKind, VariantData}, | ||
9 | expr::{BindingAnnotation, LabelId, Pat, PatId}, | ||
10 | import_map, | ||
11 | item_tree::ItemTreeNode, | ||
12 | lang_item::LangItemTarget, | ||
13 | path::ModPath, | ||
14 | per_ns::PerNs, | ||
15 | resolver::{HasResolver, Resolver}, | ||
16 | src::HasSource as _, | ||
17 | type_ref::TypeRef, | ||
18 | AdtId, AssocContainerId, AssocItemId, AssocItemLoc, AttrDefId, ConstId, ConstParamId, | ||
19 | DefWithBodyId, EnumId, FunctionId, GenericDefId, HasModule, ImplId, LifetimeParamId, | ||
20 | LocalEnumVariantId, LocalFieldId, Lookup, ModuleId, StaticId, StructId, TraitId, TypeAliasId, | ||
21 | TypeParamId, UnionId, | ||
22 | }; | ||
23 | use hir_def::{find_path::PrefixKind, item_scope::ItemInNs, visibility::Visibility}; | ||
24 | use hir_expand::{ | ||
25 | diagnostics::DiagnosticSink, | ||
26 | name::{name, AsName}, | ||
27 | MacroDefId, MacroDefKind, | ||
28 | }; | ||
29 | use hir_ty::{ | ||
30 | autoderef, | ||
31 | display::{write_bounds_like_dyn_trait_with_prefix, HirDisplayError, HirFormatter}, | ||
32 | method_resolution, | ||
33 | traits::{FnTrait, Solution, SolutionVariables}, | ||
34 | AliasTy, BoundVar, CallableDefId, CallableSig, Canonical, DebruijnIndex, GenericPredicate, | ||
35 | InEnvironment, Mutability, Obligation, ProjectionPredicate, ProjectionTy, Scalar, Substs, | ||
36 | TraitEnvironment, Ty, TyDefId, TyVariableKind, | ||
37 | }; | ||
38 | use rustc_hash::FxHashSet; | ||
39 | use stdx::{format_to, impl_from}; | ||
40 | use syntax::{ | ||
41 | ast::{self, AttrsOwner, NameOwner}, | ||
42 | AstNode, SmolStr, | ||
43 | }; | ||
44 | use tt::{Ident, Leaf, Literal, TokenTree}; | ||
45 | |||
46 | use crate::{ | ||
47 | db::{DefDatabase, HirDatabase}, | ||
48 | has_source::HasSource, | ||
49 | HirDisplay, InFile, Name, | ||
50 | }; | ||
51 | |||
52 | /// hir::Crate describes a single crate. It's the main interface with which | ||
53 | /// a crate's dependencies interact. Mostly, it should be just a proxy for the | ||
54 | /// root module. | ||
55 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
56 | pub struct Crate { | ||
57 | pub(crate) id: CrateId, | ||
58 | } | ||
59 | |||
60 | #[derive(Debug)] | ||
61 | pub struct CrateDependency { | ||
62 | pub krate: Crate, | ||
63 | pub name: Name, | ||
64 | } | ||
65 | |||
66 | impl Crate { | ||
67 | pub fn dependencies(self, db: &dyn HirDatabase) -> Vec<CrateDependency> { | ||
68 | db.crate_graph()[self.id] | ||
69 | .dependencies | ||
70 | .iter() | ||
71 | .map(|dep| { | ||
72 | let krate = Crate { id: dep.crate_id }; | ||
73 | let name = dep.as_name(); | ||
74 | CrateDependency { krate, name } | ||
75 | }) | ||
76 | .collect() | ||
77 | } | ||
78 | |||
79 | // FIXME: add `transitive_reverse_dependencies`. | ||
80 | pub fn reverse_dependencies(self, db: &dyn HirDatabase) -> Vec<Crate> { | ||
81 | let crate_graph = db.crate_graph(); | ||
82 | crate_graph | ||
83 | .iter() | ||
84 | .filter(|&krate| { | ||
85 | crate_graph[krate].dependencies.iter().any(|it| it.crate_id == self.id) | ||
86 | }) | ||
87 | .map(|id| Crate { id }) | ||
88 | .collect() | ||
89 | } | ||
90 | |||
91 | pub fn root_module(self, db: &dyn HirDatabase) -> Module { | ||
92 | let def_map = db.crate_def_map(self.id); | ||
93 | Module { id: def_map.module_id(def_map.root()) } | ||
94 | } | ||
95 | |||
96 | pub fn root_file(self, db: &dyn HirDatabase) -> FileId { | ||
97 | db.crate_graph()[self.id].root_file_id | ||
98 | } | ||
99 | |||
100 | pub fn edition(self, db: &dyn HirDatabase) -> Edition { | ||
101 | db.crate_graph()[self.id].edition | ||
102 | } | ||
103 | |||
104 | pub fn display_name(self, db: &dyn HirDatabase) -> Option<CrateDisplayName> { | ||
105 | db.crate_graph()[self.id].display_name.clone() | ||
106 | } | ||
107 | |||
108 | pub fn query_external_importables( | ||
109 | self, | ||
110 | db: &dyn DefDatabase, | ||
111 | query: import_map::Query, | ||
112 | ) -> impl Iterator<Item = Either<ModuleDef, MacroDef>> { | ||
113 | import_map::search_dependencies(db, self.into(), query).into_iter().map(|item| match item { | ||
114 | ItemInNs::Types(mod_id) | ItemInNs::Values(mod_id) => Either::Left(mod_id.into()), | ||
115 | ItemInNs::Macros(mac_id) => Either::Right(mac_id.into()), | ||
116 | }) | ||
117 | } | ||
118 | |||
119 | pub fn all(db: &dyn HirDatabase) -> Vec<Crate> { | ||
120 | db.crate_graph().iter().map(|id| Crate { id }).collect() | ||
121 | } | ||
122 | |||
123 | /// Try to get the root URL of the documentation of a crate. | ||
124 | pub fn get_html_root_url(self: &Crate, db: &dyn HirDatabase) -> Option<String> { | ||
125 | // Look for #![doc(html_root_url = "...")] | ||
126 | let attrs = db.attrs(AttrDefId::ModuleId(self.root_module(db).into())); | ||
127 | let doc_attr_q = attrs.by_key("doc"); | ||
128 | |||
129 | if !doc_attr_q.exists() { | ||
130 | return None; | ||
131 | } | ||
132 | |||
133 | let doc_url = doc_attr_q.tt_values().map(|tt| { | ||
134 | let name = tt.token_trees.iter() | ||
135 | .skip_while(|tt| !matches!(tt, TokenTree::Leaf(Leaf::Ident(Ident{text: ref ident, ..})) if ident == "html_root_url")) | ||
136 | .skip(2) | ||
137 | .next(); | ||
138 | |||
139 | match name { | ||
140 | Some(TokenTree::Leaf(Leaf::Literal(Literal{ref text, ..}))) => Some(text), | ||
141 | _ => None | ||
142 | } | ||
143 | }).flat_map(|t| t).next(); | ||
144 | |||
145 | doc_url.map(|s| s.trim_matches('"').trim_end_matches('/').to_owned() + "/") | ||
146 | } | ||
147 | } | ||
148 | |||
149 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
150 | pub struct Module { | ||
151 | pub(crate) id: ModuleId, | ||
152 | } | ||
153 | |||
154 | /// The defs which can be visible in the module. | ||
155 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
156 | pub enum ModuleDef { | ||
157 | Module(Module), | ||
158 | Function(Function), | ||
159 | Adt(Adt), | ||
160 | // Can't be directly declared, but can be imported. | ||
161 | Variant(Variant), | ||
162 | Const(Const), | ||
163 | Static(Static), | ||
164 | Trait(Trait), | ||
165 | TypeAlias(TypeAlias), | ||
166 | BuiltinType(BuiltinType), | ||
167 | } | ||
168 | impl_from!( | ||
169 | Module, | ||
170 | Function, | ||
171 | Adt(Struct, Enum, Union), | ||
172 | Variant, | ||
173 | Const, | ||
174 | Static, | ||
175 | Trait, | ||
176 | TypeAlias, | ||
177 | BuiltinType | ||
178 | for ModuleDef | ||
179 | ); | ||
180 | |||
181 | impl From<VariantDef> for ModuleDef { | ||
182 | fn from(var: VariantDef) -> Self { | ||
183 | match var { | ||
184 | VariantDef::Struct(t) => Adt::from(t).into(), | ||
185 | VariantDef::Union(t) => Adt::from(t).into(), | ||
186 | VariantDef::Variant(t) => t.into(), | ||
187 | } | ||
188 | } | ||
189 | } | ||
190 | |||
191 | impl ModuleDef { | ||
192 | pub fn module(self, db: &dyn HirDatabase) -> Option<Module> { | ||
193 | match self { | ||
194 | ModuleDef::Module(it) => it.parent(db), | ||
195 | ModuleDef::Function(it) => Some(it.module(db)), | ||
196 | ModuleDef::Adt(it) => Some(it.module(db)), | ||
197 | ModuleDef::Variant(it) => Some(it.module(db)), | ||
198 | ModuleDef::Const(it) => Some(it.module(db)), | ||
199 | ModuleDef::Static(it) => Some(it.module(db)), | ||
200 | ModuleDef::Trait(it) => Some(it.module(db)), | ||
201 | ModuleDef::TypeAlias(it) => Some(it.module(db)), | ||
202 | ModuleDef::BuiltinType(_) => None, | ||
203 | } | ||
204 | } | ||
205 | |||
206 | pub fn canonical_path(&self, db: &dyn HirDatabase) -> Option<String> { | ||
207 | let mut segments = Vec::new(); | ||
208 | segments.push(self.name(db)?.to_string()); | ||
209 | for m in self.module(db)?.path_to_root(db) { | ||
210 | segments.extend(m.name(db).map(|it| it.to_string())) | ||
211 | } | ||
212 | segments.reverse(); | ||
213 | Some(segments.join("::")) | ||
214 | } | ||
215 | |||
216 | pub fn definition_visibility(&self, db: &dyn HirDatabase) -> Option<Visibility> { | ||
217 | let module = match self { | ||
218 | ModuleDef::Module(it) => it.parent(db)?, | ||
219 | ModuleDef::Function(it) => return Some(it.visibility(db)), | ||
220 | ModuleDef::Adt(it) => it.module(db), | ||
221 | ModuleDef::Variant(it) => { | ||
222 | let parent = it.parent_enum(db); | ||
223 | let module = it.module(db); | ||
224 | return module.visibility_of(db, &ModuleDef::Adt(Adt::Enum(parent))); | ||
225 | } | ||
226 | ModuleDef::Const(it) => return Some(it.visibility(db)), | ||
227 | ModuleDef::Static(it) => it.module(db), | ||
228 | ModuleDef::Trait(it) => it.module(db), | ||
229 | ModuleDef::TypeAlias(it) => return Some(it.visibility(db)), | ||
230 | ModuleDef::BuiltinType(_) => return None, | ||
231 | }; | ||
232 | |||
233 | module.visibility_of(db, self) | ||
234 | } | ||
235 | |||
236 | pub fn name(self, db: &dyn HirDatabase) -> Option<Name> { | ||
237 | match self { | ||
238 | ModuleDef::Adt(it) => Some(it.name(db)), | ||
239 | ModuleDef::Trait(it) => Some(it.name(db)), | ||
240 | ModuleDef::Function(it) => Some(it.name(db)), | ||
241 | ModuleDef::Variant(it) => Some(it.name(db)), | ||
242 | ModuleDef::TypeAlias(it) => Some(it.name(db)), | ||
243 | ModuleDef::Module(it) => it.name(db), | ||
244 | ModuleDef::Const(it) => it.name(db), | ||
245 | ModuleDef::Static(it) => it.name(db), | ||
246 | |||
247 | ModuleDef::BuiltinType(it) => Some(it.name()), | ||
248 | } | ||
249 | } | ||
250 | |||
251 | pub fn diagnostics(self, db: &dyn HirDatabase, sink: &mut DiagnosticSink) { | ||
252 | let id = match self { | ||
253 | ModuleDef::Adt(it) => match it { | ||
254 | Adt::Struct(it) => it.id.into(), | ||
255 | Adt::Enum(it) => it.id.into(), | ||
256 | Adt::Union(it) => it.id.into(), | ||
257 | }, | ||
258 | ModuleDef::Trait(it) => it.id.into(), | ||
259 | ModuleDef::Function(it) => it.id.into(), | ||
260 | ModuleDef::TypeAlias(it) => it.id.into(), | ||
261 | ModuleDef::Module(it) => it.id.into(), | ||
262 | ModuleDef::Const(it) => it.id.into(), | ||
263 | ModuleDef::Static(it) => it.id.into(), | ||
264 | _ => return, | ||
265 | }; | ||
266 | |||
267 | let module = match self.module(db) { | ||
268 | Some(it) => it, | ||
269 | None => return, | ||
270 | }; | ||
271 | |||
272 | hir_ty::diagnostics::validate_module_item(db, module.id.krate(), id, sink) | ||
273 | } | ||
274 | } | ||
275 | |||
276 | impl Module { | ||
277 | /// Name of this module. | ||
278 | pub fn name(self, db: &dyn HirDatabase) -> Option<Name> { | ||
279 | let def_map = self.id.def_map(db.upcast()); | ||
280 | let parent = def_map[self.id.local_id].parent?; | ||
281 | def_map[parent].children.iter().find_map(|(name, module_id)| { | ||
282 | if *module_id == self.id.local_id { | ||
283 | Some(name.clone()) | ||
284 | } else { | ||
285 | None | ||
286 | } | ||
287 | }) | ||
288 | } | ||
289 | |||
290 | /// Returns the crate this module is part of. | ||
291 | pub fn krate(self) -> Crate { | ||
292 | Crate { id: self.id.krate() } | ||
293 | } | ||
294 | |||
295 | /// Topmost parent of this module. Every module has a `crate_root`, but some | ||
296 | /// might be missing `krate`. This can happen if a module's file is not included | ||
297 | /// in the module tree of any target in `Cargo.toml`. | ||
298 | pub fn crate_root(self, db: &dyn HirDatabase) -> Module { | ||
299 | let def_map = db.crate_def_map(self.id.krate()); | ||
300 | Module { id: def_map.module_id(def_map.root()) } | ||
301 | } | ||
302 | |||
303 | /// Iterates over all child modules. | ||
304 | pub fn children(self, db: &dyn HirDatabase) -> impl Iterator<Item = Module> { | ||
305 | let def_map = self.id.def_map(db.upcast()); | ||
306 | let children = def_map[self.id.local_id] | ||
307 | .children | ||
308 | .iter() | ||
309 | .map(|(_, module_id)| Module { id: def_map.module_id(*module_id) }) | ||
310 | .collect::<Vec<_>>(); | ||
311 | children.into_iter() | ||
312 | } | ||
313 | |||
314 | /// Finds a parent module. | ||
315 | pub fn parent(self, db: &dyn HirDatabase) -> Option<Module> { | ||
316 | // FIXME: handle block expressions as modules (their parent is in a different DefMap) | ||
317 | let def_map = self.id.def_map(db.upcast()); | ||
318 | let parent_id = def_map[self.id.local_id].parent?; | ||
319 | Some(Module { id: def_map.module_id(parent_id) }) | ||
320 | } | ||
321 | |||
322 | pub fn path_to_root(self, db: &dyn HirDatabase) -> Vec<Module> { | ||
323 | let mut res = vec![self]; | ||
324 | let mut curr = self; | ||
325 | while let Some(next) = curr.parent(db) { | ||
326 | res.push(next); | ||
327 | curr = next | ||
328 | } | ||
329 | res | ||
330 | } | ||
331 | |||
332 | /// Returns a `ModuleScope`: a set of items, visible in this module. | ||
333 | pub fn scope( | ||
334 | self, | ||
335 | db: &dyn HirDatabase, | ||
336 | visible_from: Option<Module>, | ||
337 | ) -> Vec<(Name, ScopeDef)> { | ||
338 | self.id.def_map(db.upcast())[self.id.local_id] | ||
339 | .scope | ||
340 | .entries() | ||
341 | .filter_map(|(name, def)| { | ||
342 | if let Some(m) = visible_from { | ||
343 | let filtered = | ||
344 | def.filter_visibility(|vis| vis.is_visible_from(db.upcast(), m.id)); | ||
345 | if filtered.is_none() && !def.is_none() { | ||
346 | None | ||
347 | } else { | ||
348 | Some((name, filtered)) | ||
349 | } | ||
350 | } else { | ||
351 | Some((name, def)) | ||
352 | } | ||
353 | }) | ||
354 | .flat_map(|(name, def)| { | ||
355 | ScopeDef::all_items(def).into_iter().map(move |item| (name.clone(), item)) | ||
356 | }) | ||
357 | .collect() | ||
358 | } | ||
359 | |||
360 | pub fn visibility_of(self, db: &dyn HirDatabase, def: &ModuleDef) -> Option<Visibility> { | ||
361 | self.id.def_map(db.upcast())[self.id.local_id].scope.visibility_of(def.clone().into()) | ||
362 | } | ||
363 | |||
364 | pub fn diagnostics(self, db: &dyn HirDatabase, sink: &mut DiagnosticSink) { | ||
365 | let _p = profile::span("Module::diagnostics").detail(|| { | ||
366 | format!("{:?}", self.name(db).map_or("<unknown>".into(), |name| name.to_string())) | ||
367 | }); | ||
368 | let def_map = self.id.def_map(db.upcast()); | ||
369 | def_map.add_diagnostics(db.upcast(), self.id.local_id, sink); | ||
370 | for decl in self.declarations(db) { | ||
371 | match decl { | ||
372 | crate::ModuleDef::Function(f) => f.diagnostics(db, sink), | ||
373 | crate::ModuleDef::Module(m) => { | ||
374 | // Only add diagnostics from inline modules | ||
375 | if def_map[m.id.local_id].origin.is_inline() { | ||
376 | m.diagnostics(db, sink) | ||
377 | } | ||
378 | } | ||
379 | _ => { | ||
380 | decl.diagnostics(db, sink); | ||
381 | } | ||
382 | } | ||
383 | } | ||
384 | |||
385 | for impl_def in self.impl_defs(db) { | ||
386 | for item in impl_def.items(db) { | ||
387 | if let AssocItem::Function(f) = item { | ||
388 | f.diagnostics(db, sink); | ||
389 | } | ||
390 | } | ||
391 | } | ||
392 | } | ||
393 | |||
394 | pub fn declarations(self, db: &dyn HirDatabase) -> Vec<ModuleDef> { | ||
395 | let def_map = self.id.def_map(db.upcast()); | ||
396 | def_map[self.id.local_id].scope.declarations().map(ModuleDef::from).collect() | ||
397 | } | ||
398 | |||
399 | pub fn impl_defs(self, db: &dyn HirDatabase) -> Vec<Impl> { | ||
400 | let def_map = self.id.def_map(db.upcast()); | ||
401 | def_map[self.id.local_id].scope.impls().map(Impl::from).collect() | ||
402 | } | ||
403 | |||
404 | /// Finds a path that can be used to refer to the given item from within | ||
405 | /// this module, if possible. | ||
406 | pub fn find_use_path(self, db: &dyn DefDatabase, item: impl Into<ItemInNs>) -> Option<ModPath> { | ||
407 | hir_def::find_path::find_path(db, item.into(), self.into()) | ||
408 | } | ||
409 | |||
410 | /// Finds a path that can be used to refer to the given item from within | ||
411 | /// this module, if possible. This is used for returning import paths for use-statements. | ||
412 | pub fn find_use_path_prefixed( | ||
413 | self, | ||
414 | db: &dyn DefDatabase, | ||
415 | item: impl Into<ItemInNs>, | ||
416 | prefix_kind: PrefixKind, | ||
417 | ) -> Option<ModPath> { | ||
418 | hir_def::find_path::find_path_prefixed(db, item.into(), self.into(), prefix_kind) | ||
419 | } | ||
420 | } | ||
421 | |||
422 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
423 | pub struct Field { | ||
424 | pub(crate) parent: VariantDef, | ||
425 | pub(crate) id: LocalFieldId, | ||
426 | } | ||
427 | |||
428 | #[derive(Debug, PartialEq, Eq)] | ||
429 | pub enum FieldSource { | ||
430 | Named(ast::RecordField), | ||
431 | Pos(ast::TupleField), | ||
432 | } | ||
433 | |||
434 | impl Field { | ||
435 | pub fn name(&self, db: &dyn HirDatabase) -> Name { | ||
436 | self.parent.variant_data(db).fields()[self.id].name.clone() | ||
437 | } | ||
438 | |||
439 | /// Returns the type as in the signature of the struct (i.e., with | ||
440 | /// placeholder types for type parameters). This is good for showing | ||
441 | /// signature help, but not so good to actually get the type of the field | ||
442 | /// when you actually have a variable of the struct. | ||
443 | pub fn signature_ty(&self, db: &dyn HirDatabase) -> Type { | ||
444 | let var_id = self.parent.into(); | ||
445 | let generic_def_id: GenericDefId = match self.parent { | ||
446 | VariantDef::Struct(it) => it.id.into(), | ||
447 | VariantDef::Union(it) => it.id.into(), | ||
448 | VariantDef::Variant(it) => it.parent.id.into(), | ||
449 | }; | ||
450 | let substs = Substs::type_params(db, generic_def_id); | ||
451 | let ty = db.field_types(var_id)[self.id].clone().subst(&substs); | ||
452 | Type::new(db, self.parent.module(db).id.krate(), var_id, ty) | ||
453 | } | ||
454 | |||
455 | pub fn parent_def(&self, _db: &dyn HirDatabase) -> VariantDef { | ||
456 | self.parent | ||
457 | } | ||
458 | } | ||
459 | |||
460 | impl HasVisibility for Field { | ||
461 | fn visibility(&self, db: &dyn HirDatabase) -> Visibility { | ||
462 | let variant_data = self.parent.variant_data(db); | ||
463 | let visibility = &variant_data.fields()[self.id].visibility; | ||
464 | let parent_id: hir_def::VariantId = self.parent.into(); | ||
465 | visibility.resolve(db.upcast(), &parent_id.resolver(db.upcast())) | ||
466 | } | ||
467 | } | ||
468 | |||
469 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
470 | pub struct Struct { | ||
471 | pub(crate) id: StructId, | ||
472 | } | ||
473 | |||
474 | impl Struct { | ||
475 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
476 | Module { id: self.id.lookup(db.upcast()).container.module(db.upcast()) } | ||
477 | } | ||
478 | |||
479 | pub fn krate(self, db: &dyn HirDatabase) -> Option<Crate> { | ||
480 | Some(self.module(db).krate()) | ||
481 | } | ||
482 | |||
483 | pub fn name(self, db: &dyn HirDatabase) -> Name { | ||
484 | db.struct_data(self.id).name.clone() | ||
485 | } | ||
486 | |||
487 | pub fn fields(self, db: &dyn HirDatabase) -> Vec<Field> { | ||
488 | db.struct_data(self.id) | ||
489 | .variant_data | ||
490 | .fields() | ||
491 | .iter() | ||
492 | .map(|(id, _)| Field { parent: self.into(), id }) | ||
493 | .collect() | ||
494 | } | ||
495 | |||
496 | pub fn ty(self, db: &dyn HirDatabase) -> Type { | ||
497 | Type::from_def( | ||
498 | db, | ||
499 | self.id.lookup(db.upcast()).container.module(db.upcast()).krate(), | ||
500 | self.id, | ||
501 | ) | ||
502 | } | ||
503 | |||
504 | pub fn repr(self, db: &dyn HirDatabase) -> Option<ReprKind> { | ||
505 | db.struct_data(self.id).repr.clone() | ||
506 | } | ||
507 | |||
508 | pub fn kind(self, db: &dyn HirDatabase) -> StructKind { | ||
509 | self.variant_data(db).kind() | ||
510 | } | ||
511 | |||
512 | fn variant_data(self, db: &dyn HirDatabase) -> Arc<VariantData> { | ||
513 | db.struct_data(self.id).variant_data.clone() | ||
514 | } | ||
515 | } | ||
516 | |||
517 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
518 | pub struct Union { | ||
519 | pub(crate) id: UnionId, | ||
520 | } | ||
521 | |||
522 | impl Union { | ||
523 | pub fn name(self, db: &dyn HirDatabase) -> Name { | ||
524 | db.union_data(self.id).name.clone() | ||
525 | } | ||
526 | |||
527 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
528 | Module { id: self.id.lookup(db.upcast()).container.module(db.upcast()) } | ||
529 | } | ||
530 | |||
531 | pub fn ty(self, db: &dyn HirDatabase) -> Type { | ||
532 | Type::from_def( | ||
533 | db, | ||
534 | self.id.lookup(db.upcast()).container.module(db.upcast()).krate(), | ||
535 | self.id, | ||
536 | ) | ||
537 | } | ||
538 | |||
539 | pub fn fields(self, db: &dyn HirDatabase) -> Vec<Field> { | ||
540 | db.union_data(self.id) | ||
541 | .variant_data | ||
542 | .fields() | ||
543 | .iter() | ||
544 | .map(|(id, _)| Field { parent: self.into(), id }) | ||
545 | .collect() | ||
546 | } | ||
547 | |||
548 | fn variant_data(self, db: &dyn HirDatabase) -> Arc<VariantData> { | ||
549 | db.union_data(self.id).variant_data.clone() | ||
550 | } | ||
551 | } | ||
552 | |||
553 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
554 | pub struct Enum { | ||
555 | pub(crate) id: EnumId, | ||
556 | } | ||
557 | |||
558 | impl Enum { | ||
559 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
560 | Module { id: self.id.lookup(db.upcast()).container.module(db.upcast()) } | ||
561 | } | ||
562 | |||
563 | pub fn krate(self, db: &dyn HirDatabase) -> Option<Crate> { | ||
564 | Some(self.module(db).krate()) | ||
565 | } | ||
566 | |||
567 | pub fn name(self, db: &dyn HirDatabase) -> Name { | ||
568 | db.enum_data(self.id).name.clone() | ||
569 | } | ||
570 | |||
571 | pub fn variants(self, db: &dyn HirDatabase) -> Vec<Variant> { | ||
572 | db.enum_data(self.id).variants.iter().map(|(id, _)| Variant { parent: self, id }).collect() | ||
573 | } | ||
574 | |||
575 | pub fn ty(self, db: &dyn HirDatabase) -> Type { | ||
576 | Type::from_def( | ||
577 | db, | ||
578 | self.id.lookup(db.upcast()).container.module(db.upcast()).krate(), | ||
579 | self.id, | ||
580 | ) | ||
581 | } | ||
582 | } | ||
583 | |||
584 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
585 | pub struct Variant { | ||
586 | pub(crate) parent: Enum, | ||
587 | pub(crate) id: LocalEnumVariantId, | ||
588 | } | ||
589 | |||
590 | impl Variant { | ||
591 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
592 | self.parent.module(db) | ||
593 | } | ||
594 | pub fn parent_enum(self, _db: &dyn HirDatabase) -> Enum { | ||
595 | self.parent | ||
596 | } | ||
597 | |||
598 | pub fn name(self, db: &dyn HirDatabase) -> Name { | ||
599 | db.enum_data(self.parent.id).variants[self.id].name.clone() | ||
600 | } | ||
601 | |||
602 | pub fn fields(self, db: &dyn HirDatabase) -> Vec<Field> { | ||
603 | self.variant_data(db) | ||
604 | .fields() | ||
605 | .iter() | ||
606 | .map(|(id, _)| Field { parent: self.into(), id }) | ||
607 | .collect() | ||
608 | } | ||
609 | |||
610 | pub fn kind(self, db: &dyn HirDatabase) -> StructKind { | ||
611 | self.variant_data(db).kind() | ||
612 | } | ||
613 | |||
614 | pub(crate) fn variant_data(self, db: &dyn HirDatabase) -> Arc<VariantData> { | ||
615 | db.enum_data(self.parent.id).variants[self.id].variant_data.clone() | ||
616 | } | ||
617 | } | ||
618 | |||
619 | /// A Data Type | ||
620 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] | ||
621 | pub enum Adt { | ||
622 | Struct(Struct), | ||
623 | Union(Union), | ||
624 | Enum(Enum), | ||
625 | } | ||
626 | impl_from!(Struct, Union, Enum for Adt); | ||
627 | |||
628 | impl Adt { | ||
629 | pub fn has_non_default_type_params(self, db: &dyn HirDatabase) -> bool { | ||
630 | let subst = db.generic_defaults(self.into()); | ||
631 | subst.iter().any(|ty| &ty.value == &Ty::Unknown) | ||
632 | } | ||
633 | |||
634 | /// Turns this ADT into a type. Any type parameters of the ADT will be | ||
635 | /// turned into unknown types, which is good for e.g. finding the most | ||
636 | /// general set of completions, but will not look very nice when printed. | ||
637 | pub fn ty(self, db: &dyn HirDatabase) -> Type { | ||
638 | let id = AdtId::from(self); | ||
639 | Type::from_def(db, id.module(db.upcast()).krate(), id) | ||
640 | } | ||
641 | |||
642 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
643 | match self { | ||
644 | Adt::Struct(s) => s.module(db), | ||
645 | Adt::Union(s) => s.module(db), | ||
646 | Adt::Enum(e) => e.module(db), | ||
647 | } | ||
648 | } | ||
649 | |||
650 | pub fn krate(self, db: &dyn HirDatabase) -> Option<Crate> { | ||
651 | Some(self.module(db).krate()) | ||
652 | } | ||
653 | |||
654 | pub fn name(self, db: &dyn HirDatabase) -> Name { | ||
655 | match self { | ||
656 | Adt::Struct(s) => s.name(db), | ||
657 | Adt::Union(u) => u.name(db), | ||
658 | Adt::Enum(e) => e.name(db), | ||
659 | } | ||
660 | } | ||
661 | } | ||
662 | |||
663 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] | ||
664 | pub enum VariantDef { | ||
665 | Struct(Struct), | ||
666 | Union(Union), | ||
667 | Variant(Variant), | ||
668 | } | ||
669 | impl_from!(Struct, Union, Variant for VariantDef); | ||
670 | |||
671 | impl VariantDef { | ||
672 | pub fn fields(self, db: &dyn HirDatabase) -> Vec<Field> { | ||
673 | match self { | ||
674 | VariantDef::Struct(it) => it.fields(db), | ||
675 | VariantDef::Union(it) => it.fields(db), | ||
676 | VariantDef::Variant(it) => it.fields(db), | ||
677 | } | ||
678 | } | ||
679 | |||
680 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
681 | match self { | ||
682 | VariantDef::Struct(it) => it.module(db), | ||
683 | VariantDef::Union(it) => it.module(db), | ||
684 | VariantDef::Variant(it) => it.module(db), | ||
685 | } | ||
686 | } | ||
687 | |||
688 | pub fn name(&self, db: &dyn HirDatabase) -> Name { | ||
689 | match self { | ||
690 | VariantDef::Struct(s) => s.name(db), | ||
691 | VariantDef::Union(u) => u.name(db), | ||
692 | VariantDef::Variant(e) => e.name(db), | ||
693 | } | ||
694 | } | ||
695 | |||
696 | pub(crate) fn variant_data(self, db: &dyn HirDatabase) -> Arc<VariantData> { | ||
697 | match self { | ||
698 | VariantDef::Struct(it) => it.variant_data(db), | ||
699 | VariantDef::Union(it) => it.variant_data(db), | ||
700 | VariantDef::Variant(it) => it.variant_data(db), | ||
701 | } | ||
702 | } | ||
703 | } | ||
704 | |||
705 | /// The defs which have a body. | ||
706 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
707 | pub enum DefWithBody { | ||
708 | Function(Function), | ||
709 | Static(Static), | ||
710 | Const(Const), | ||
711 | } | ||
712 | impl_from!(Function, Const, Static for DefWithBody); | ||
713 | |||
714 | impl DefWithBody { | ||
715 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
716 | match self { | ||
717 | DefWithBody::Const(c) => c.module(db), | ||
718 | DefWithBody::Function(f) => f.module(db), | ||
719 | DefWithBody::Static(s) => s.module(db), | ||
720 | } | ||
721 | } | ||
722 | |||
723 | pub fn name(self, db: &dyn HirDatabase) -> Option<Name> { | ||
724 | match self { | ||
725 | DefWithBody::Function(f) => Some(f.name(db)), | ||
726 | DefWithBody::Static(s) => s.name(db), | ||
727 | DefWithBody::Const(c) => c.name(db), | ||
728 | } | ||
729 | } | ||
730 | } | ||
731 | |||
732 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
733 | pub struct Function { | ||
734 | pub(crate) id: FunctionId, | ||
735 | } | ||
736 | |||
737 | impl Function { | ||
738 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
739 | self.id.lookup(db.upcast()).module(db.upcast()).into() | ||
740 | } | ||
741 | |||
742 | pub fn krate(self, db: &dyn HirDatabase) -> Option<Crate> { | ||
743 | Some(self.module(db).krate()) | ||
744 | } | ||
745 | |||
746 | pub fn name(self, db: &dyn HirDatabase) -> Name { | ||
747 | db.function_data(self.id).name.clone() | ||
748 | } | ||
749 | |||
750 | /// Get this function's return type | ||
751 | pub fn ret_type(self, db: &dyn HirDatabase) -> Type { | ||
752 | let resolver = self.id.resolver(db.upcast()); | ||
753 | let ret_type = &db.function_data(self.id).ret_type; | ||
754 | let ctx = hir_ty::TyLoweringContext::new(db, &resolver); | ||
755 | let environment = TraitEnvironment::lower(db, &resolver); | ||
756 | Type { | ||
757 | krate: self.id.lookup(db.upcast()).container.module(db.upcast()).krate(), | ||
758 | ty: InEnvironment { value: Ty::from_hir_ext(&ctx, ret_type).0, environment }, | ||
759 | } | ||
760 | } | ||
761 | |||
762 | pub fn self_param(self, db: &dyn HirDatabase) -> Option<SelfParam> { | ||
763 | if !db.function_data(self.id).has_self_param { | ||
764 | return None; | ||
765 | } | ||
766 | Some(SelfParam { func: self.id }) | ||
767 | } | ||
768 | |||
769 | pub fn assoc_fn_params(self, db: &dyn HirDatabase) -> Vec<Param> { | ||
770 | let resolver = self.id.resolver(db.upcast()); | ||
771 | let ctx = hir_ty::TyLoweringContext::new(db, &resolver); | ||
772 | let environment = TraitEnvironment::lower(db, &resolver); | ||
773 | db.function_data(self.id) | ||
774 | .params | ||
775 | .iter() | ||
776 | .map(|type_ref| { | ||
777 | let ty = Type { | ||
778 | krate: self.id.lookup(db.upcast()).container.module(db.upcast()).krate(), | ||
779 | ty: InEnvironment { | ||
780 | value: Ty::from_hir_ext(&ctx, type_ref).0, | ||
781 | environment: environment.clone(), | ||
782 | }, | ||
783 | }; | ||
784 | Param { ty } | ||
785 | }) | ||
786 | .collect() | ||
787 | } | ||
788 | pub fn method_params(self, db: &dyn HirDatabase) -> Option<Vec<Param>> { | ||
789 | if self.self_param(db).is_none() { | ||
790 | return None; | ||
791 | } | ||
792 | let mut res = self.assoc_fn_params(db); | ||
793 | res.remove(0); | ||
794 | Some(res) | ||
795 | } | ||
796 | |||
797 | pub fn is_unsafe(self, db: &dyn HirDatabase) -> bool { | ||
798 | db.function_data(self.id).is_unsafe | ||
799 | } | ||
800 | |||
801 | pub fn diagnostics(self, db: &dyn HirDatabase, sink: &mut DiagnosticSink) { | ||
802 | let krate = self.module(db).id.krate(); | ||
803 | hir_def::diagnostics::validate_body(db.upcast(), self.id.into(), sink); | ||
804 | hir_ty::diagnostics::validate_module_item(db, krate, self.id.into(), sink); | ||
805 | hir_ty::diagnostics::validate_body(db, self.id.into(), sink); | ||
806 | } | ||
807 | |||
808 | /// Whether this function declaration has a definition. | ||
809 | /// | ||
810 | /// This is false in the case of required (not provided) trait methods. | ||
811 | pub fn has_body(self, db: &dyn HirDatabase) -> bool { | ||
812 | db.function_data(self.id).has_body | ||
813 | } | ||
814 | |||
815 | /// A textual representation of the HIR of this function for debugging purposes. | ||
816 | pub fn debug_hir(self, db: &dyn HirDatabase) -> String { | ||
817 | let body = db.body(self.id.into()); | ||
818 | |||
819 | let mut result = String::new(); | ||
820 | format_to!(result, "HIR expressions in the body of `{}`:\n", self.name(db)); | ||
821 | for (id, expr) in body.exprs.iter() { | ||
822 | format_to!(result, "{:?}: {:?}\n", id, expr); | ||
823 | } | ||
824 | |||
825 | result | ||
826 | } | ||
827 | } | ||
828 | |||
829 | // Note: logically, this belongs to `hir_ty`, but we are not using it there yet. | ||
830 | pub enum Access { | ||
831 | Shared, | ||
832 | Exclusive, | ||
833 | Owned, | ||
834 | } | ||
835 | |||
836 | impl From<Mutability> for Access { | ||
837 | fn from(mutability: Mutability) -> Access { | ||
838 | match mutability { | ||
839 | Mutability::Not => Access::Shared, | ||
840 | Mutability::Mut => Access::Exclusive, | ||
841 | } | ||
842 | } | ||
843 | } | ||
844 | |||
845 | #[derive(Debug)] | ||
846 | pub struct Param { | ||
847 | ty: Type, | ||
848 | } | ||
849 | |||
850 | impl Param { | ||
851 | pub fn ty(&self) -> &Type { | ||
852 | &self.ty | ||
853 | } | ||
854 | } | ||
855 | |||
856 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
857 | pub struct SelfParam { | ||
858 | func: FunctionId, | ||
859 | } | ||
860 | |||
861 | impl SelfParam { | ||
862 | pub fn access(self, db: &dyn HirDatabase) -> Access { | ||
863 | let func_data = db.function_data(self.func); | ||
864 | func_data | ||
865 | .params | ||
866 | .first() | ||
867 | .map(|param| match *param { | ||
868 | TypeRef::Reference(.., mutability) => match mutability { | ||
869 | hir_def::type_ref::Mutability::Shared => Access::Shared, | ||
870 | hir_def::type_ref::Mutability::Mut => Access::Exclusive, | ||
871 | }, | ||
872 | _ => Access::Owned, | ||
873 | }) | ||
874 | .unwrap_or(Access::Owned) | ||
875 | } | ||
876 | } | ||
877 | |||
878 | impl HasVisibility for Function { | ||
879 | fn visibility(&self, db: &dyn HirDatabase) -> Visibility { | ||
880 | let function_data = db.function_data(self.id); | ||
881 | let visibility = &function_data.visibility; | ||
882 | visibility.resolve(db.upcast(), &self.id.resolver(db.upcast())) | ||
883 | } | ||
884 | } | ||
885 | |||
886 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
887 | pub struct Const { | ||
888 | pub(crate) id: ConstId, | ||
889 | } | ||
890 | |||
891 | impl Const { | ||
892 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
893 | Module { id: self.id.lookup(db.upcast()).module(db.upcast()) } | ||
894 | } | ||
895 | |||
896 | pub fn krate(self, db: &dyn HirDatabase) -> Option<Crate> { | ||
897 | Some(self.module(db).krate()) | ||
898 | } | ||
899 | |||
900 | pub fn name(self, db: &dyn HirDatabase) -> Option<Name> { | ||
901 | db.const_data(self.id).name.clone() | ||
902 | } | ||
903 | } | ||
904 | |||
905 | impl HasVisibility for Const { | ||
906 | fn visibility(&self, db: &dyn HirDatabase) -> Visibility { | ||
907 | let function_data = db.const_data(self.id); | ||
908 | let visibility = &function_data.visibility; | ||
909 | visibility.resolve(db.upcast(), &self.id.resolver(db.upcast())) | ||
910 | } | ||
911 | } | ||
912 | |||
913 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
914 | pub struct Static { | ||
915 | pub(crate) id: StaticId, | ||
916 | } | ||
917 | |||
918 | impl Static { | ||
919 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
920 | Module { id: self.id.lookup(db.upcast()).module(db.upcast()) } | ||
921 | } | ||
922 | |||
923 | pub fn krate(self, db: &dyn HirDatabase) -> Option<Crate> { | ||
924 | Some(self.module(db).krate()) | ||
925 | } | ||
926 | |||
927 | pub fn name(self, db: &dyn HirDatabase) -> Option<Name> { | ||
928 | db.static_data(self.id).name.clone() | ||
929 | } | ||
930 | |||
931 | pub fn is_mut(self, db: &dyn HirDatabase) -> bool { | ||
932 | db.static_data(self.id).mutable | ||
933 | } | ||
934 | } | ||
935 | |||
936 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
937 | pub struct Trait { | ||
938 | pub(crate) id: TraitId, | ||
939 | } | ||
940 | |||
941 | impl Trait { | ||
942 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
943 | Module { id: self.id.lookup(db.upcast()).container.module(db.upcast()) } | ||
944 | } | ||
945 | |||
946 | pub fn name(self, db: &dyn HirDatabase) -> Name { | ||
947 | db.trait_data(self.id).name.clone() | ||
948 | } | ||
949 | |||
950 | pub fn items(self, db: &dyn HirDatabase) -> Vec<AssocItem> { | ||
951 | db.trait_data(self.id).items.iter().map(|(_name, it)| (*it).into()).collect() | ||
952 | } | ||
953 | |||
954 | pub fn is_auto(self, db: &dyn HirDatabase) -> bool { | ||
955 | db.trait_data(self.id).auto | ||
956 | } | ||
957 | } | ||
958 | |||
959 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
960 | pub struct TypeAlias { | ||
961 | pub(crate) id: TypeAliasId, | ||
962 | } | ||
963 | |||
964 | impl TypeAlias { | ||
965 | pub fn has_non_default_type_params(self, db: &dyn HirDatabase) -> bool { | ||
966 | let subst = db.generic_defaults(self.id.into()); | ||
967 | subst.iter().any(|ty| &ty.value == &Ty::Unknown) | ||
968 | } | ||
969 | |||
970 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
971 | Module { id: self.id.lookup(db.upcast()).module(db.upcast()) } | ||
972 | } | ||
973 | |||
974 | pub fn krate(self, db: &dyn HirDatabase) -> Option<Crate> { | ||
975 | Some(self.module(db).krate()) | ||
976 | } | ||
977 | |||
978 | pub fn type_ref(self, db: &dyn HirDatabase) -> Option<TypeRef> { | ||
979 | db.type_alias_data(self.id).type_ref.clone() | ||
980 | } | ||
981 | |||
982 | pub fn ty(self, db: &dyn HirDatabase) -> Type { | ||
983 | Type::from_def(db, self.id.lookup(db.upcast()).module(db.upcast()).krate(), self.id) | ||
984 | } | ||
985 | |||
986 | pub fn name(self, db: &dyn HirDatabase) -> Name { | ||
987 | db.type_alias_data(self.id).name.clone() | ||
988 | } | ||
989 | } | ||
990 | |||
991 | impl HasVisibility for TypeAlias { | ||
992 | fn visibility(&self, db: &dyn HirDatabase) -> Visibility { | ||
993 | let function_data = db.type_alias_data(self.id); | ||
994 | let visibility = &function_data.visibility; | ||
995 | visibility.resolve(db.upcast(), &self.id.resolver(db.upcast())) | ||
996 | } | ||
997 | } | ||
998 | |||
999 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
1000 | pub struct BuiltinType { | ||
1001 | pub(crate) inner: hir_def::builtin_type::BuiltinType, | ||
1002 | } | ||
1003 | |||
1004 | impl BuiltinType { | ||
1005 | pub fn ty(self, db: &dyn HirDatabase, module: Module) -> Type { | ||
1006 | let resolver = module.id.resolver(db.upcast()); | ||
1007 | Type::new_with_resolver(db, &resolver, Ty::builtin(self.inner)) | ||
1008 | .expect("crate not present in resolver") | ||
1009 | } | ||
1010 | |||
1011 | pub fn name(self) -> Name { | ||
1012 | self.inner.as_name() | ||
1013 | } | ||
1014 | } | ||
1015 | |||
1016 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
1017 | pub struct MacroDef { | ||
1018 | pub(crate) id: MacroDefId, | ||
1019 | } | ||
1020 | |||
1021 | impl MacroDef { | ||
1022 | /// FIXME: right now, this just returns the root module of the crate that | ||
1023 | /// defines this macro. The reasons for this is that macros are expanded | ||
1024 | /// early, in `hir_expand`, where modules simply do not exist yet. | ||
1025 | pub fn module(self, db: &dyn HirDatabase) -> Option<Module> { | ||
1026 | let krate = self.id.krate; | ||
1027 | let def_map = db.crate_def_map(krate); | ||
1028 | let module_id = def_map.root(); | ||
1029 | Some(Module { id: def_map.module_id(module_id) }) | ||
1030 | } | ||
1031 | |||
1032 | /// XXX: this parses the file | ||
1033 | pub fn name(self, db: &dyn HirDatabase) -> Option<Name> { | ||
1034 | self.source(db)?.value.name().map(|it| it.as_name()) | ||
1035 | } | ||
1036 | |||
1037 | /// Indicate it is a proc-macro | ||
1038 | pub fn is_proc_macro(&self) -> bool { | ||
1039 | matches!(self.id.kind, MacroDefKind::ProcMacro(_)) | ||
1040 | } | ||
1041 | |||
1042 | /// Indicate it is a derive macro | ||
1043 | pub fn is_derive_macro(&self) -> bool { | ||
1044 | matches!(self.id.kind, MacroDefKind::ProcMacro(_) | MacroDefKind::BuiltInDerive(_)) | ||
1045 | } | ||
1046 | } | ||
1047 | |||
1048 | /// Invariant: `inner.as_assoc_item(db).is_some()` | ||
1049 | /// We do not actively enforce this invariant. | ||
1050 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] | ||
1051 | pub enum AssocItem { | ||
1052 | Function(Function), | ||
1053 | Const(Const), | ||
1054 | TypeAlias(TypeAlias), | ||
1055 | } | ||
1056 | pub enum AssocItemContainer { | ||
1057 | Trait(Trait), | ||
1058 | Impl(Impl), | ||
1059 | } | ||
1060 | pub trait AsAssocItem { | ||
1061 | fn as_assoc_item(self, db: &dyn HirDatabase) -> Option<AssocItem>; | ||
1062 | } | ||
1063 | |||
1064 | impl AsAssocItem for Function { | ||
1065 | fn as_assoc_item(self, db: &dyn HirDatabase) -> Option<AssocItem> { | ||
1066 | as_assoc_item(db, AssocItem::Function, self.id) | ||
1067 | } | ||
1068 | } | ||
1069 | impl AsAssocItem for Const { | ||
1070 | fn as_assoc_item(self, db: &dyn HirDatabase) -> Option<AssocItem> { | ||
1071 | as_assoc_item(db, AssocItem::Const, self.id) | ||
1072 | } | ||
1073 | } | ||
1074 | impl AsAssocItem for TypeAlias { | ||
1075 | fn as_assoc_item(self, db: &dyn HirDatabase) -> Option<AssocItem> { | ||
1076 | as_assoc_item(db, AssocItem::TypeAlias, self.id) | ||
1077 | } | ||
1078 | } | ||
1079 | impl AsAssocItem for ModuleDef { | ||
1080 | fn as_assoc_item(self, db: &dyn HirDatabase) -> Option<AssocItem> { | ||
1081 | match self { | ||
1082 | ModuleDef::Function(it) => it.as_assoc_item(db), | ||
1083 | ModuleDef::Const(it) => it.as_assoc_item(db), | ||
1084 | ModuleDef::TypeAlias(it) => it.as_assoc_item(db), | ||
1085 | _ => None, | ||
1086 | } | ||
1087 | } | ||
1088 | } | ||
1089 | fn as_assoc_item<ID, DEF, CTOR, AST>(db: &dyn HirDatabase, ctor: CTOR, id: ID) -> Option<AssocItem> | ||
1090 | where | ||
1091 | ID: Lookup<Data = AssocItemLoc<AST>>, | ||
1092 | DEF: From<ID>, | ||
1093 | CTOR: FnOnce(DEF) -> AssocItem, | ||
1094 | AST: ItemTreeNode, | ||
1095 | { | ||
1096 | match id.lookup(db.upcast()).container { | ||
1097 | AssocContainerId::TraitId(_) | AssocContainerId::ImplId(_) => Some(ctor(DEF::from(id))), | ||
1098 | AssocContainerId::ContainerId(_) => None, | ||
1099 | } | ||
1100 | } | ||
1101 | |||
1102 | impl AssocItem { | ||
1103 | pub fn name(self, db: &dyn HirDatabase) -> Option<Name> { | ||
1104 | match self { | ||
1105 | AssocItem::Function(it) => Some(it.name(db)), | ||
1106 | AssocItem::Const(it) => it.name(db), | ||
1107 | AssocItem::TypeAlias(it) => Some(it.name(db)), | ||
1108 | } | ||
1109 | } | ||
1110 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
1111 | match self { | ||
1112 | AssocItem::Function(f) => f.module(db), | ||
1113 | AssocItem::Const(c) => c.module(db), | ||
1114 | AssocItem::TypeAlias(t) => t.module(db), | ||
1115 | } | ||
1116 | } | ||
1117 | pub fn container(self, db: &dyn HirDatabase) -> AssocItemContainer { | ||
1118 | let container = match self { | ||
1119 | AssocItem::Function(it) => it.id.lookup(db.upcast()).container, | ||
1120 | AssocItem::Const(it) => it.id.lookup(db.upcast()).container, | ||
1121 | AssocItem::TypeAlias(it) => it.id.lookup(db.upcast()).container, | ||
1122 | }; | ||
1123 | match container { | ||
1124 | AssocContainerId::TraitId(id) => AssocItemContainer::Trait(id.into()), | ||
1125 | AssocContainerId::ImplId(id) => AssocItemContainer::Impl(id.into()), | ||
1126 | AssocContainerId::ContainerId(_) => panic!("invalid AssocItem"), | ||
1127 | } | ||
1128 | } | ||
1129 | |||
1130 | pub fn containing_trait(self, db: &dyn HirDatabase) -> Option<Trait> { | ||
1131 | match self.container(db) { | ||
1132 | AssocItemContainer::Trait(t) => Some(t), | ||
1133 | _ => None, | ||
1134 | } | ||
1135 | } | ||
1136 | } | ||
1137 | |||
1138 | impl HasVisibility for AssocItem { | ||
1139 | fn visibility(&self, db: &dyn HirDatabase) -> Visibility { | ||
1140 | match self { | ||
1141 | AssocItem::Function(f) => f.visibility(db), | ||
1142 | AssocItem::Const(c) => c.visibility(db), | ||
1143 | AssocItem::TypeAlias(t) => t.visibility(db), | ||
1144 | } | ||
1145 | } | ||
1146 | } | ||
1147 | |||
1148 | #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] | ||
1149 | pub enum GenericDef { | ||
1150 | Function(Function), | ||
1151 | Adt(Adt), | ||
1152 | Trait(Trait), | ||
1153 | TypeAlias(TypeAlias), | ||
1154 | Impl(Impl), | ||
1155 | // enum variants cannot have generics themselves, but their parent enums | ||
1156 | // can, and this makes some code easier to write | ||
1157 | Variant(Variant), | ||
1158 | // consts can have type parameters from their parents (i.e. associated consts of traits) | ||
1159 | Const(Const), | ||
1160 | } | ||
1161 | impl_from!( | ||
1162 | Function, | ||
1163 | Adt(Struct, Enum, Union), | ||
1164 | Trait, | ||
1165 | TypeAlias, | ||
1166 | Impl, | ||
1167 | Variant, | ||
1168 | Const | ||
1169 | for GenericDef | ||
1170 | ); | ||
1171 | |||
1172 | impl GenericDef { | ||
1173 | pub fn params(self, db: &dyn HirDatabase) -> Vec<GenericParam> { | ||
1174 | let generics = db.generic_params(self.into()); | ||
1175 | let ty_params = generics | ||
1176 | .types | ||
1177 | .iter() | ||
1178 | .map(|(local_id, _)| TypeParam { id: TypeParamId { parent: self.into(), local_id } }) | ||
1179 | .map(GenericParam::TypeParam); | ||
1180 | let lt_params = generics | ||
1181 | .lifetimes | ||
1182 | .iter() | ||
1183 | .map(|(local_id, _)| LifetimeParam { | ||
1184 | id: LifetimeParamId { parent: self.into(), local_id }, | ||
1185 | }) | ||
1186 | .map(GenericParam::LifetimeParam); | ||
1187 | let const_params = generics | ||
1188 | .consts | ||
1189 | .iter() | ||
1190 | .map(|(local_id, _)| ConstParam { id: ConstParamId { parent: self.into(), local_id } }) | ||
1191 | .map(GenericParam::ConstParam); | ||
1192 | ty_params.chain(lt_params).chain(const_params).collect() | ||
1193 | } | ||
1194 | |||
1195 | pub fn type_params(self, db: &dyn HirDatabase) -> Vec<TypeParam> { | ||
1196 | let generics = db.generic_params(self.into()); | ||
1197 | generics | ||
1198 | .types | ||
1199 | .iter() | ||
1200 | .map(|(local_id, _)| TypeParam { id: TypeParamId { parent: self.into(), local_id } }) | ||
1201 | .collect() | ||
1202 | } | ||
1203 | } | ||
1204 | |||
1205 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] | ||
1206 | pub struct Local { | ||
1207 | pub(crate) parent: DefWithBodyId, | ||
1208 | pub(crate) pat_id: PatId, | ||
1209 | } | ||
1210 | |||
1211 | impl Local { | ||
1212 | pub fn is_param(self, db: &dyn HirDatabase) -> bool { | ||
1213 | let src = self.source(db); | ||
1214 | match src.value { | ||
1215 | Either::Left(bind_pat) => { | ||
1216 | bind_pat.syntax().ancestors().any(|it| ast::Param::can_cast(it.kind())) | ||
1217 | } | ||
1218 | Either::Right(_self_param) => true, | ||
1219 | } | ||
1220 | } | ||
1221 | |||
1222 | // FIXME: why is this an option? It shouldn't be? | ||
1223 | pub fn name(self, db: &dyn HirDatabase) -> Option<Name> { | ||
1224 | let body = db.body(self.parent.into()); | ||
1225 | match &body[self.pat_id] { | ||
1226 | Pat::Bind { name, .. } => Some(name.clone()), | ||
1227 | _ => None, | ||
1228 | } | ||
1229 | } | ||
1230 | |||
1231 | pub fn is_self(self, db: &dyn HirDatabase) -> bool { | ||
1232 | self.name(db) == Some(name![self]) | ||
1233 | } | ||
1234 | |||
1235 | pub fn is_mut(self, db: &dyn HirDatabase) -> bool { | ||
1236 | let body = db.body(self.parent.into()); | ||
1237 | match &body[self.pat_id] { | ||
1238 | Pat::Bind { mode, .. } => match mode { | ||
1239 | BindingAnnotation::Mutable | BindingAnnotation::RefMut => true, | ||
1240 | _ => false, | ||
1241 | }, | ||
1242 | _ => false, | ||
1243 | } | ||
1244 | } | ||
1245 | |||
1246 | pub fn parent(self, _db: &dyn HirDatabase) -> DefWithBody { | ||
1247 | self.parent.into() | ||
1248 | } | ||
1249 | |||
1250 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
1251 | self.parent(db).module(db) | ||
1252 | } | ||
1253 | |||
1254 | pub fn ty(self, db: &dyn HirDatabase) -> Type { | ||
1255 | let def = DefWithBodyId::from(self.parent); | ||
1256 | let infer = db.infer(def); | ||
1257 | let ty = infer[self.pat_id].clone(); | ||
1258 | let krate = def.module(db.upcast()).krate(); | ||
1259 | Type::new(db, krate, def, ty) | ||
1260 | } | ||
1261 | |||
1262 | pub fn source(self, db: &dyn HirDatabase) -> InFile<Either<ast::IdentPat, ast::SelfParam>> { | ||
1263 | let (_body, source_map) = db.body_with_source_map(self.parent.into()); | ||
1264 | let src = source_map.pat_syntax(self.pat_id).unwrap(); // Hmm... | ||
1265 | let root = src.file_syntax(db.upcast()); | ||
1266 | src.map(|ast| { | ||
1267 | ast.map_left(|it| it.cast().unwrap().to_node(&root)).map_right(|it| it.to_node(&root)) | ||
1268 | }) | ||
1269 | } | ||
1270 | } | ||
1271 | |||
1272 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] | ||
1273 | pub struct Label { | ||
1274 | pub(crate) parent: DefWithBodyId, | ||
1275 | pub(crate) label_id: LabelId, | ||
1276 | } | ||
1277 | |||
1278 | impl Label { | ||
1279 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
1280 | self.parent(db).module(db) | ||
1281 | } | ||
1282 | |||
1283 | pub fn parent(self, _db: &dyn HirDatabase) -> DefWithBody { | ||
1284 | self.parent.into() | ||
1285 | } | ||
1286 | |||
1287 | pub fn name(self, db: &dyn HirDatabase) -> Name { | ||
1288 | let body = db.body(self.parent.into()); | ||
1289 | body[self.label_id].name.clone() | ||
1290 | } | ||
1291 | |||
1292 | pub fn source(self, db: &dyn HirDatabase) -> InFile<ast::Label> { | ||
1293 | let (_body, source_map) = db.body_with_source_map(self.parent.into()); | ||
1294 | let src = source_map.label_syntax(self.label_id); | ||
1295 | let root = src.file_syntax(db.upcast()); | ||
1296 | src.map(|ast| ast.to_node(&root)) | ||
1297 | } | ||
1298 | } | ||
1299 | |||
1300 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] | ||
1301 | pub enum GenericParam { | ||
1302 | TypeParam(TypeParam), | ||
1303 | LifetimeParam(LifetimeParam), | ||
1304 | ConstParam(ConstParam), | ||
1305 | } | ||
1306 | impl_from!(TypeParam, LifetimeParam, ConstParam for GenericParam); | ||
1307 | |||
1308 | impl GenericParam { | ||
1309 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
1310 | match self { | ||
1311 | GenericParam::TypeParam(it) => it.module(db), | ||
1312 | GenericParam::LifetimeParam(it) => it.module(db), | ||
1313 | GenericParam::ConstParam(it) => it.module(db), | ||
1314 | } | ||
1315 | } | ||
1316 | |||
1317 | pub fn name(self, db: &dyn HirDatabase) -> Name { | ||
1318 | match self { | ||
1319 | GenericParam::TypeParam(it) => it.name(db), | ||
1320 | GenericParam::LifetimeParam(it) => it.name(db), | ||
1321 | GenericParam::ConstParam(it) => it.name(db), | ||
1322 | } | ||
1323 | } | ||
1324 | } | ||
1325 | |||
1326 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] | ||
1327 | pub struct TypeParam { | ||
1328 | pub(crate) id: TypeParamId, | ||
1329 | } | ||
1330 | |||
1331 | impl TypeParam { | ||
1332 | pub fn name(self, db: &dyn HirDatabase) -> Name { | ||
1333 | let params = db.generic_params(self.id.parent); | ||
1334 | params.types[self.id.local_id].name.clone().unwrap_or_else(Name::missing) | ||
1335 | } | ||
1336 | |||
1337 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
1338 | self.id.parent.module(db.upcast()).into() | ||
1339 | } | ||
1340 | |||
1341 | pub fn ty(self, db: &dyn HirDatabase) -> Type { | ||
1342 | let resolver = self.id.parent.resolver(db.upcast()); | ||
1343 | let environment = TraitEnvironment::lower(db, &resolver); | ||
1344 | let ty = Ty::Placeholder(self.id); | ||
1345 | Type { | ||
1346 | krate: self.id.parent.module(db.upcast()).krate(), | ||
1347 | ty: InEnvironment { value: ty, environment }, | ||
1348 | } | ||
1349 | } | ||
1350 | |||
1351 | pub fn trait_bounds(self, db: &dyn HirDatabase) -> Vec<Trait> { | ||
1352 | db.generic_predicates_for_param(self.id) | ||
1353 | .into_iter() | ||
1354 | .filter_map(|pred| match &pred.value { | ||
1355 | hir_ty::GenericPredicate::Implemented(trait_ref) => { | ||
1356 | Some(Trait::from(trait_ref.trait_)) | ||
1357 | } | ||
1358 | _ => None, | ||
1359 | }) | ||
1360 | .collect() | ||
1361 | } | ||
1362 | |||
1363 | pub fn default(self, db: &dyn HirDatabase) -> Option<Type> { | ||
1364 | let params = db.generic_defaults(self.id.parent); | ||
1365 | let local_idx = hir_ty::param_idx(db, self.id)?; | ||
1366 | let resolver = self.id.parent.resolver(db.upcast()); | ||
1367 | let environment = TraitEnvironment::lower(db, &resolver); | ||
1368 | let ty = params.get(local_idx)?.clone(); | ||
1369 | let subst = Substs::type_params(db, self.id.parent); | ||
1370 | let ty = ty.subst(&subst.prefix(local_idx)); | ||
1371 | Some(Type { | ||
1372 | krate: self.id.parent.module(db.upcast()).krate(), | ||
1373 | ty: InEnvironment { value: ty, environment }, | ||
1374 | }) | ||
1375 | } | ||
1376 | } | ||
1377 | |||
1378 | impl HirDisplay for TypeParam { | ||
1379 | fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { | ||
1380 | write!(f, "{}", self.name(f.db))?; | ||
1381 | let bounds = f.db.generic_predicates_for_param(self.id); | ||
1382 | let substs = Substs::type_params(f.db, self.id.parent); | ||
1383 | let predicates = bounds.iter().cloned().map(|b| b.subst(&substs)).collect::<Vec<_>>(); | ||
1384 | if !(predicates.is_empty() || f.omit_verbose_types()) { | ||
1385 | write_bounds_like_dyn_trait_with_prefix(":", &predicates, f)?; | ||
1386 | } | ||
1387 | Ok(()) | ||
1388 | } | ||
1389 | } | ||
1390 | |||
1391 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] | ||
1392 | pub struct LifetimeParam { | ||
1393 | pub(crate) id: LifetimeParamId, | ||
1394 | } | ||
1395 | |||
1396 | impl LifetimeParam { | ||
1397 | pub fn name(self, db: &dyn HirDatabase) -> Name { | ||
1398 | let params = db.generic_params(self.id.parent); | ||
1399 | params.lifetimes[self.id.local_id].name.clone() | ||
1400 | } | ||
1401 | |||
1402 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
1403 | self.id.parent.module(db.upcast()).into() | ||
1404 | } | ||
1405 | |||
1406 | pub fn parent(self, _db: &dyn HirDatabase) -> GenericDef { | ||
1407 | self.id.parent.into() | ||
1408 | } | ||
1409 | } | ||
1410 | |||
1411 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] | ||
1412 | pub struct ConstParam { | ||
1413 | pub(crate) id: ConstParamId, | ||
1414 | } | ||
1415 | |||
1416 | impl ConstParam { | ||
1417 | pub fn name(self, db: &dyn HirDatabase) -> Name { | ||
1418 | let params = db.generic_params(self.id.parent); | ||
1419 | params.consts[self.id.local_id].name.clone() | ||
1420 | } | ||
1421 | |||
1422 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
1423 | self.id.parent.module(db.upcast()).into() | ||
1424 | } | ||
1425 | |||
1426 | pub fn parent(self, _db: &dyn HirDatabase) -> GenericDef { | ||
1427 | self.id.parent.into() | ||
1428 | } | ||
1429 | |||
1430 | pub fn ty(self, db: &dyn HirDatabase) -> Type { | ||
1431 | let def = self.id.parent; | ||
1432 | let krate = def.module(db.upcast()).krate(); | ||
1433 | Type::new(db, krate, def, db.const_param_ty(self.id)) | ||
1434 | } | ||
1435 | } | ||
1436 | |||
1437 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
1438 | pub struct Impl { | ||
1439 | pub(crate) id: ImplId, | ||
1440 | } | ||
1441 | |||
1442 | impl Impl { | ||
1443 | pub fn all_in_crate(db: &dyn HirDatabase, krate: Crate) -> Vec<Impl> { | ||
1444 | let inherent = db.inherent_impls_in_crate(krate.id); | ||
1445 | let trait_ = db.trait_impls_in_crate(krate.id); | ||
1446 | |||
1447 | inherent.all_impls().chain(trait_.all_impls()).map(Self::from).collect() | ||
1448 | } | ||
1449 | pub fn for_trait(db: &dyn HirDatabase, krate: Crate, trait_: Trait) -> Vec<Impl> { | ||
1450 | let impls = db.trait_impls_in_crate(krate.id); | ||
1451 | impls.for_trait(trait_.id).map(Self::from).collect() | ||
1452 | } | ||
1453 | |||
1454 | // FIXME: the return type is wrong. This should be a hir version of | ||
1455 | // `TraitRef` (ie, resolved `TypeRef`). | ||
1456 | pub fn target_trait(self, db: &dyn HirDatabase) -> Option<TypeRef> { | ||
1457 | db.impl_data(self.id).target_trait.clone() | ||
1458 | } | ||
1459 | |||
1460 | pub fn target_ty(self, db: &dyn HirDatabase) -> Type { | ||
1461 | let impl_data = db.impl_data(self.id); | ||
1462 | let resolver = self.id.resolver(db.upcast()); | ||
1463 | let ctx = hir_ty::TyLoweringContext::new(db, &resolver); | ||
1464 | let environment = TraitEnvironment::lower(db, &resolver); | ||
1465 | let ty = Ty::from_hir(&ctx, &impl_data.target_type); | ||
1466 | Type { | ||
1467 | krate: self.id.lookup(db.upcast()).container.module(db.upcast()).krate(), | ||
1468 | ty: InEnvironment { value: ty, environment }, | ||
1469 | } | ||
1470 | } | ||
1471 | |||
1472 | pub fn items(self, db: &dyn HirDatabase) -> Vec<AssocItem> { | ||
1473 | db.impl_data(self.id).items.iter().map(|it| (*it).into()).collect() | ||
1474 | } | ||
1475 | |||
1476 | pub fn is_negative(self, db: &dyn HirDatabase) -> bool { | ||
1477 | db.impl_data(self.id).is_negative | ||
1478 | } | ||
1479 | |||
1480 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
1481 | self.id.lookup(db.upcast()).container.module(db.upcast()).into() | ||
1482 | } | ||
1483 | |||
1484 | pub fn krate(self, db: &dyn HirDatabase) -> Crate { | ||
1485 | Crate { id: self.module(db).id.krate() } | ||
1486 | } | ||
1487 | |||
1488 | pub fn is_builtin_derive(self, db: &dyn HirDatabase) -> Option<InFile<ast::Attr>> { | ||
1489 | let src = self.source(db)?; | ||
1490 | let item = src.file_id.is_builtin_derive(db.upcast())?; | ||
1491 | let hygenic = hir_expand::hygiene::Hygiene::new(db.upcast(), item.file_id); | ||
1492 | |||
1493 | // FIXME: handle `cfg_attr` | ||
1494 | let attr = item | ||
1495 | .value | ||
1496 | .attrs() | ||
1497 | .filter_map(|it| { | ||
1498 | let path = ModPath::from_src(it.path()?, &hygenic)?; | ||
1499 | if path.as_ident()?.to_string() == "derive" { | ||
1500 | Some(it) | ||
1501 | } else { | ||
1502 | None | ||
1503 | } | ||
1504 | }) | ||
1505 | .last()?; | ||
1506 | |||
1507 | Some(item.with_value(attr)) | ||
1508 | } | ||
1509 | } | ||
1510 | |||
1511 | #[derive(Clone, PartialEq, Eq, Debug)] | ||
1512 | pub struct Type { | ||
1513 | krate: CrateId, | ||
1514 | ty: InEnvironment<Ty>, | ||
1515 | } | ||
1516 | |||
1517 | impl Type { | ||
1518 | pub(crate) fn new_with_resolver( | ||
1519 | db: &dyn HirDatabase, | ||
1520 | resolver: &Resolver, | ||
1521 | ty: Ty, | ||
1522 | ) -> Option<Type> { | ||
1523 | let krate = resolver.krate()?; | ||
1524 | Some(Type::new_with_resolver_inner(db, krate, resolver, ty)) | ||
1525 | } | ||
1526 | pub(crate) fn new_with_resolver_inner( | ||
1527 | db: &dyn HirDatabase, | ||
1528 | krate: CrateId, | ||
1529 | resolver: &Resolver, | ||
1530 | ty: Ty, | ||
1531 | ) -> Type { | ||
1532 | let environment = TraitEnvironment::lower(db, &resolver); | ||
1533 | Type { krate, ty: InEnvironment { value: ty, environment } } | ||
1534 | } | ||
1535 | |||
1536 | fn new(db: &dyn HirDatabase, krate: CrateId, lexical_env: impl HasResolver, ty: Ty) -> Type { | ||
1537 | let resolver = lexical_env.resolver(db.upcast()); | ||
1538 | let environment = TraitEnvironment::lower(db, &resolver); | ||
1539 | Type { krate, ty: InEnvironment { value: ty, environment } } | ||
1540 | } | ||
1541 | |||
1542 | fn from_def( | ||
1543 | db: &dyn HirDatabase, | ||
1544 | krate: CrateId, | ||
1545 | def: impl HasResolver + Into<TyDefId> + Into<GenericDefId>, | ||
1546 | ) -> Type { | ||
1547 | let substs = Substs::build_for_def(db, def).fill_with_unknown().build(); | ||
1548 | let ty = db.ty(def.into()).subst(&substs); | ||
1549 | Type::new(db, krate, def, ty) | ||
1550 | } | ||
1551 | |||
1552 | pub fn is_unit(&self) -> bool { | ||
1553 | matches!(self.ty.value, Ty::Tuple(0, ..)) | ||
1554 | } | ||
1555 | pub fn is_bool(&self) -> bool { | ||
1556 | matches!(self.ty.value, Ty::Scalar(Scalar::Bool)) | ||
1557 | } | ||
1558 | |||
1559 | pub fn is_mutable_reference(&self) -> bool { | ||
1560 | matches!(self.ty.value, Ty::Ref(Mutability::Mut, ..)) | ||
1561 | } | ||
1562 | |||
1563 | pub fn remove_ref(&self) -> Option<Type> { | ||
1564 | if let Ty::Ref(.., substs) = &self.ty.value { | ||
1565 | Some(self.derived(substs[0].clone())) | ||
1566 | } else { | ||
1567 | None | ||
1568 | } | ||
1569 | } | ||
1570 | |||
1571 | pub fn is_unknown(&self) -> bool { | ||
1572 | matches!(self.ty.value, Ty::Unknown) | ||
1573 | } | ||
1574 | |||
1575 | /// Checks that particular type `ty` implements `std::future::Future`. | ||
1576 | /// This function is used in `.await` syntax completion. | ||
1577 | pub fn impls_future(&self, db: &dyn HirDatabase) -> bool { | ||
1578 | // No special case for the type of async block, since Chalk can figure it out. | ||
1579 | |||
1580 | let krate = self.krate; | ||
1581 | |||
1582 | let std_future_trait = | ||
1583 | db.lang_item(krate, "future_trait".into()).and_then(|it| it.as_trait()); | ||
1584 | let std_future_trait = match std_future_trait { | ||
1585 | Some(it) => it, | ||
1586 | None => return false, | ||
1587 | }; | ||
1588 | |||
1589 | let canonical_ty = Canonical { value: self.ty.value.clone(), kinds: Arc::new([]) }; | ||
1590 | method_resolution::implements_trait( | ||
1591 | &canonical_ty, | ||
1592 | db, | ||
1593 | self.ty.environment.clone(), | ||
1594 | krate, | ||
1595 | std_future_trait, | ||
1596 | ) | ||
1597 | } | ||
1598 | |||
1599 | /// Checks that particular type `ty` implements `std::ops::FnOnce`. | ||
1600 | /// | ||
1601 | /// This function can be used to check if a particular type is callable, since FnOnce is a | ||
1602 | /// supertrait of Fn and FnMut, so all callable types implements at least FnOnce. | ||
1603 | pub fn impls_fnonce(&self, db: &dyn HirDatabase) -> bool { | ||
1604 | let krate = self.krate; | ||
1605 | |||
1606 | let fnonce_trait = match FnTrait::FnOnce.get_id(db, krate) { | ||
1607 | Some(it) => it, | ||
1608 | None => return false, | ||
1609 | }; | ||
1610 | |||
1611 | let canonical_ty = Canonical { value: self.ty.value.clone(), kinds: Arc::new([]) }; | ||
1612 | method_resolution::implements_trait_unique( | ||
1613 | &canonical_ty, | ||
1614 | db, | ||
1615 | self.ty.environment.clone(), | ||
1616 | krate, | ||
1617 | fnonce_trait, | ||
1618 | ) | ||
1619 | } | ||
1620 | |||
1621 | pub fn impls_trait(&self, db: &dyn HirDatabase, trait_: Trait, args: &[Type]) -> bool { | ||
1622 | let trait_ref = hir_ty::TraitRef { | ||
1623 | trait_: trait_.id, | ||
1624 | substs: Substs::build_for_def(db, trait_.id) | ||
1625 | .push(self.ty.value.clone()) | ||
1626 | .fill(args.iter().map(|t| t.ty.value.clone())) | ||
1627 | .build(), | ||
1628 | }; | ||
1629 | |||
1630 | let goal = Canonical { | ||
1631 | value: hir_ty::InEnvironment::new( | ||
1632 | self.ty.environment.clone(), | ||
1633 | hir_ty::Obligation::Trait(trait_ref), | ||
1634 | ), | ||
1635 | kinds: Arc::new([]), | ||
1636 | }; | ||
1637 | |||
1638 | db.trait_solve(self.krate, goal).is_some() | ||
1639 | } | ||
1640 | |||
1641 | pub fn normalize_trait_assoc_type( | ||
1642 | &self, | ||
1643 | db: &dyn HirDatabase, | ||
1644 | trait_: Trait, | ||
1645 | args: &[Type], | ||
1646 | alias: TypeAlias, | ||
1647 | ) -> Option<Type> { | ||
1648 | let subst = Substs::build_for_def(db, trait_.id) | ||
1649 | .push(self.ty.value.clone()) | ||
1650 | .fill(args.iter().map(|t| t.ty.value.clone())) | ||
1651 | .build(); | ||
1652 | let predicate = ProjectionPredicate { | ||
1653 | projection_ty: ProjectionTy { associated_ty: alias.id, parameters: subst }, | ||
1654 | ty: Ty::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 0)), | ||
1655 | }; | ||
1656 | let goal = Canonical { | ||
1657 | value: InEnvironment::new( | ||
1658 | self.ty.environment.clone(), | ||
1659 | Obligation::Projection(predicate), | ||
1660 | ), | ||
1661 | kinds: Arc::new([TyVariableKind::General]), | ||
1662 | }; | ||
1663 | |||
1664 | match db.trait_solve(self.krate, goal)? { | ||
1665 | Solution::Unique(SolutionVariables(subst)) => subst.value.first().cloned(), | ||
1666 | Solution::Ambig(_) => None, | ||
1667 | } | ||
1668 | .map(|ty| Type { | ||
1669 | krate: self.krate, | ||
1670 | ty: InEnvironment { value: ty, environment: Arc::clone(&self.ty.environment) }, | ||
1671 | }) | ||
1672 | } | ||
1673 | |||
1674 | pub fn is_copy(&self, db: &dyn HirDatabase) -> bool { | ||
1675 | let lang_item = db.lang_item(self.krate, SmolStr::new("copy")); | ||
1676 | let copy_trait = match lang_item { | ||
1677 | Some(LangItemTarget::TraitId(it)) => it, | ||
1678 | _ => return false, | ||
1679 | }; | ||
1680 | self.impls_trait(db, copy_trait.into(), &[]) | ||
1681 | } | ||
1682 | |||
1683 | pub fn as_callable(&self, db: &dyn HirDatabase) -> Option<Callable> { | ||
1684 | let def = match self.ty.value { | ||
1685 | Ty::FnDef(def, _) => Some(def), | ||
1686 | _ => None, | ||
1687 | }; | ||
1688 | |||
1689 | let sig = self.ty.value.callable_sig(db)?; | ||
1690 | Some(Callable { ty: self.clone(), sig, def, is_bound_method: false }) | ||
1691 | } | ||
1692 | |||
1693 | pub fn is_closure(&self) -> bool { | ||
1694 | matches!(&self.ty.value, Ty::Closure { .. }) | ||
1695 | } | ||
1696 | |||
1697 | pub fn is_fn(&self) -> bool { | ||
1698 | matches!(&self.ty.value, Ty::FnDef(..) | Ty::Function { .. }) | ||
1699 | } | ||
1700 | |||
1701 | pub fn is_packed(&self, db: &dyn HirDatabase) -> bool { | ||
1702 | let adt_id = match self.ty.value { | ||
1703 | Ty::Adt(hir_ty::AdtId(adt_id), ..) => adt_id, | ||
1704 | _ => return false, | ||
1705 | }; | ||
1706 | |||
1707 | let adt = adt_id.into(); | ||
1708 | match adt { | ||
1709 | Adt::Struct(s) => matches!(s.repr(db), Some(ReprKind::Packed)), | ||
1710 | _ => false, | ||
1711 | } | ||
1712 | } | ||
1713 | |||
1714 | pub fn is_raw_ptr(&self) -> bool { | ||
1715 | matches!(&self.ty.value, Ty::Raw(..)) | ||
1716 | } | ||
1717 | |||
1718 | pub fn contains_unknown(&self) -> bool { | ||
1719 | return go(&self.ty.value); | ||
1720 | |||
1721 | fn go(ty: &Ty) -> bool { | ||
1722 | match ty { | ||
1723 | Ty::Unknown => true, | ||
1724 | _ => ty.substs().map_or(false, |substs| substs.iter().any(go)), | ||
1725 | } | ||
1726 | } | ||
1727 | } | ||
1728 | |||
1729 | pub fn fields(&self, db: &dyn HirDatabase) -> Vec<(Field, Type)> { | ||
1730 | let (variant_id, substs) = match self.ty.value { | ||
1731 | Ty::Adt(hir_ty::AdtId(AdtId::StructId(s)), ref substs) => (s.into(), substs), | ||
1732 | Ty::Adt(hir_ty::AdtId(AdtId::UnionId(u)), ref substs) => (u.into(), substs), | ||
1733 | _ => return Vec::new(), | ||
1734 | }; | ||
1735 | |||
1736 | db.field_types(variant_id) | ||
1737 | .iter() | ||
1738 | .map(|(local_id, ty)| { | ||
1739 | let def = Field { parent: variant_id.into(), id: local_id }; | ||
1740 | let ty = ty.clone().subst(substs); | ||
1741 | (def, self.derived(ty)) | ||
1742 | }) | ||
1743 | .collect() | ||
1744 | } | ||
1745 | |||
1746 | pub fn tuple_fields(&self, _db: &dyn HirDatabase) -> Vec<Type> { | ||
1747 | if let Ty::Tuple(_, substs) = &self.ty.value { | ||
1748 | substs.iter().map(|ty| self.derived(ty.clone())).collect() | ||
1749 | } else { | ||
1750 | Vec::new() | ||
1751 | } | ||
1752 | } | ||
1753 | |||
1754 | pub fn autoderef<'a>(&'a self, db: &'a dyn HirDatabase) -> impl Iterator<Item = Type> + 'a { | ||
1755 | // There should be no inference vars in types passed here | ||
1756 | // FIXME check that? | ||
1757 | let canonical = Canonical { value: self.ty.value.clone(), kinds: Arc::new([]) }; | ||
1758 | let environment = self.ty.environment.clone(); | ||
1759 | let ty = InEnvironment { value: canonical, environment }; | ||
1760 | autoderef(db, Some(self.krate), ty) | ||
1761 | .map(|canonical| canonical.value) | ||
1762 | .map(move |ty| self.derived(ty)) | ||
1763 | } | ||
1764 | |||
1765 | // This would be nicer if it just returned an iterator, but that runs into | ||
1766 | // lifetime problems, because we need to borrow temp `CrateImplDefs`. | ||
1767 | pub fn iterate_assoc_items<T>( | ||
1768 | self, | ||
1769 | db: &dyn HirDatabase, | ||
1770 | krate: Crate, | ||
1771 | mut callback: impl FnMut(AssocItem) -> Option<T>, | ||
1772 | ) -> Option<T> { | ||
1773 | for krate in self.ty.value.def_crates(db, krate.id)? { | ||
1774 | let impls = db.inherent_impls_in_crate(krate); | ||
1775 | |||
1776 | for impl_def in impls.for_self_ty(&self.ty.value) { | ||
1777 | for &item in db.impl_data(*impl_def).items.iter() { | ||
1778 | if let Some(result) = callback(item.into()) { | ||
1779 | return Some(result); | ||
1780 | } | ||
1781 | } | ||
1782 | } | ||
1783 | } | ||
1784 | None | ||
1785 | } | ||
1786 | |||
1787 | pub fn type_parameters(&self) -> impl Iterator<Item = Type> + '_ { | ||
1788 | self.ty | ||
1789 | .value | ||
1790 | .strip_references() | ||
1791 | .substs() | ||
1792 | .into_iter() | ||
1793 | .flat_map(|substs| substs.iter()) | ||
1794 | .map(move |ty| self.derived(ty.clone())) | ||
1795 | } | ||
1796 | |||
1797 | pub fn iterate_method_candidates<T>( | ||
1798 | &self, | ||
1799 | db: &dyn HirDatabase, | ||
1800 | krate: Crate, | ||
1801 | traits_in_scope: &FxHashSet<TraitId>, | ||
1802 | name: Option<&Name>, | ||
1803 | mut callback: impl FnMut(&Ty, Function) -> Option<T>, | ||
1804 | ) -> Option<T> { | ||
1805 | // There should be no inference vars in types passed here | ||
1806 | // FIXME check that? | ||
1807 | // FIXME replace Unknown by bound vars here | ||
1808 | let canonical = Canonical { value: self.ty.value.clone(), kinds: Arc::new([]) }; | ||
1809 | |||
1810 | let env = self.ty.environment.clone(); | ||
1811 | let krate = krate.id; | ||
1812 | |||
1813 | method_resolution::iterate_method_candidates( | ||
1814 | &canonical, | ||
1815 | db, | ||
1816 | env, | ||
1817 | krate, | ||
1818 | traits_in_scope, | ||
1819 | name, | ||
1820 | method_resolution::LookupMode::MethodCall, | ||
1821 | |ty, it| match it { | ||
1822 | AssocItemId::FunctionId(f) => callback(ty, f.into()), | ||
1823 | _ => None, | ||
1824 | }, | ||
1825 | ) | ||
1826 | } | ||
1827 | |||
1828 | pub fn iterate_path_candidates<T>( | ||
1829 | &self, | ||
1830 | db: &dyn HirDatabase, | ||
1831 | krate: Crate, | ||
1832 | traits_in_scope: &FxHashSet<TraitId>, | ||
1833 | name: Option<&Name>, | ||
1834 | mut callback: impl FnMut(&Ty, AssocItem) -> Option<T>, | ||
1835 | ) -> Option<T> { | ||
1836 | // There should be no inference vars in types passed here | ||
1837 | // FIXME check that? | ||
1838 | // FIXME replace Unknown by bound vars here | ||
1839 | let canonical = Canonical { value: self.ty.value.clone(), kinds: Arc::new([]) }; | ||
1840 | |||
1841 | let env = self.ty.environment.clone(); | ||
1842 | let krate = krate.id; | ||
1843 | |||
1844 | method_resolution::iterate_method_candidates( | ||
1845 | &canonical, | ||
1846 | db, | ||
1847 | env, | ||
1848 | krate, | ||
1849 | traits_in_scope, | ||
1850 | name, | ||
1851 | method_resolution::LookupMode::Path, | ||
1852 | |ty, it| callback(ty, it.into()), | ||
1853 | ) | ||
1854 | } | ||
1855 | |||
1856 | pub fn as_adt(&self) -> Option<Adt> { | ||
1857 | let (adt, _subst) = self.ty.value.as_adt()?; | ||
1858 | Some(adt.into()) | ||
1859 | } | ||
1860 | |||
1861 | pub fn as_dyn_trait(&self) -> Option<Trait> { | ||
1862 | self.ty.value.dyn_trait().map(Into::into) | ||
1863 | } | ||
1864 | |||
1865 | pub fn as_impl_traits(&self, db: &dyn HirDatabase) -> Option<Vec<Trait>> { | ||
1866 | self.ty.value.impl_trait_bounds(db).map(|it| { | ||
1867 | it.into_iter() | ||
1868 | .filter_map(|pred| match pred { | ||
1869 | hir_ty::GenericPredicate::Implemented(trait_ref) => { | ||
1870 | Some(Trait::from(trait_ref.trait_)) | ||
1871 | } | ||
1872 | _ => None, | ||
1873 | }) | ||
1874 | .collect() | ||
1875 | }) | ||
1876 | } | ||
1877 | |||
1878 | pub fn as_associated_type_parent_trait(&self, db: &dyn HirDatabase) -> Option<Trait> { | ||
1879 | self.ty.value.associated_type_parent_trait(db).map(Into::into) | ||
1880 | } | ||
1881 | |||
1882 | // FIXME: provide required accessors such that it becomes implementable from outside. | ||
1883 | pub fn is_equal_for_find_impls(&self, other: &Type) -> bool { | ||
1884 | let rref = other.remove_ref(); | ||
1885 | self.ty.value.equals_ctor(rref.as_ref().map_or(&other.ty.value, |it| &it.ty.value)) | ||
1886 | } | ||
1887 | |||
1888 | fn derived(&self, ty: Ty) -> Type { | ||
1889 | Type { | ||
1890 | krate: self.krate, | ||
1891 | ty: InEnvironment { value: ty, environment: self.ty.environment.clone() }, | ||
1892 | } | ||
1893 | } | ||
1894 | |||
1895 | pub fn walk(&self, db: &dyn HirDatabase, mut cb: impl FnMut(Type)) { | ||
1896 | // TypeWalk::walk for a Ty at first visits parameters and only after that the Ty itself. | ||
1897 | // We need a different order here. | ||
1898 | |||
1899 | fn walk_substs( | ||
1900 | db: &dyn HirDatabase, | ||
1901 | type_: &Type, | ||
1902 | substs: &Substs, | ||
1903 | cb: &mut impl FnMut(Type), | ||
1904 | ) { | ||
1905 | for ty in substs.iter() { | ||
1906 | walk_type(db, &type_.derived(ty.clone()), cb); | ||
1907 | } | ||
1908 | } | ||
1909 | |||
1910 | fn walk_bounds( | ||
1911 | db: &dyn HirDatabase, | ||
1912 | type_: &Type, | ||
1913 | bounds: &[GenericPredicate], | ||
1914 | cb: &mut impl FnMut(Type), | ||
1915 | ) { | ||
1916 | for pred in bounds { | ||
1917 | match pred { | ||
1918 | GenericPredicate::Implemented(trait_ref) => { | ||
1919 | cb(type_.clone()); | ||
1920 | walk_substs(db, type_, &trait_ref.substs, cb); | ||
1921 | } | ||
1922 | _ => (), | ||
1923 | } | ||
1924 | } | ||
1925 | } | ||
1926 | |||
1927 | fn walk_type(db: &dyn HirDatabase, type_: &Type, cb: &mut impl FnMut(Type)) { | ||
1928 | let ty = type_.ty.value.strip_references(); | ||
1929 | match ty { | ||
1930 | Ty::Adt(..) => { | ||
1931 | cb(type_.derived(ty.clone())); | ||
1932 | } | ||
1933 | Ty::AssociatedType(..) => { | ||
1934 | if let Some(_) = ty.associated_type_parent_trait(db) { | ||
1935 | cb(type_.derived(ty.clone())); | ||
1936 | } | ||
1937 | } | ||
1938 | Ty::OpaqueType(..) => { | ||
1939 | if let Some(bounds) = ty.impl_trait_bounds(db) { | ||
1940 | walk_bounds(db, &type_.derived(ty.clone()), &bounds, cb); | ||
1941 | } | ||
1942 | } | ||
1943 | Ty::Alias(AliasTy::Opaque(opaque_ty)) => { | ||
1944 | if let Some(bounds) = ty.impl_trait_bounds(db) { | ||
1945 | walk_bounds(db, &type_.derived(ty.clone()), &bounds, cb); | ||
1946 | } | ||
1947 | |||
1948 | walk_substs(db, type_, &opaque_ty.parameters, cb); | ||
1949 | } | ||
1950 | Ty::Placeholder(_) => { | ||
1951 | if let Some(bounds) = ty.impl_trait_bounds(db) { | ||
1952 | walk_bounds(db, &type_.derived(ty.clone()), &bounds, cb); | ||
1953 | } | ||
1954 | } | ||
1955 | Ty::Dyn(bounds) => { | ||
1956 | walk_bounds(db, &type_.derived(ty.clone()), bounds.as_ref(), cb); | ||
1957 | } | ||
1958 | |||
1959 | _ => {} | ||
1960 | } | ||
1961 | if let Some(substs) = ty.substs() { | ||
1962 | walk_substs(db, type_, &substs, cb); | ||
1963 | } | ||
1964 | } | ||
1965 | |||
1966 | walk_type(db, self, &mut cb); | ||
1967 | } | ||
1968 | } | ||
1969 | |||
1970 | impl HirDisplay for Type { | ||
1971 | fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { | ||
1972 | self.ty.value.hir_fmt(f) | ||
1973 | } | ||
1974 | } | ||
1975 | |||
1976 | // FIXME: closures | ||
1977 | #[derive(Debug)] | ||
1978 | pub struct Callable { | ||
1979 | ty: Type, | ||
1980 | sig: CallableSig, | ||
1981 | def: Option<CallableDefId>, | ||
1982 | pub(crate) is_bound_method: bool, | ||
1983 | } | ||
1984 | |||
1985 | pub enum CallableKind { | ||
1986 | Function(Function), | ||
1987 | TupleStruct(Struct), | ||
1988 | TupleEnumVariant(Variant), | ||
1989 | Closure, | ||
1990 | } | ||
1991 | |||
1992 | impl Callable { | ||
1993 | pub fn kind(&self) -> CallableKind { | ||
1994 | match self.def { | ||
1995 | Some(CallableDefId::FunctionId(it)) => CallableKind::Function(it.into()), | ||
1996 | Some(CallableDefId::StructId(it)) => CallableKind::TupleStruct(it.into()), | ||
1997 | Some(CallableDefId::EnumVariantId(it)) => CallableKind::TupleEnumVariant(it.into()), | ||
1998 | None => CallableKind::Closure, | ||
1999 | } | ||
2000 | } | ||
2001 | pub fn receiver_param(&self, db: &dyn HirDatabase) -> Option<ast::SelfParam> { | ||
2002 | let func = match self.def { | ||
2003 | Some(CallableDefId::FunctionId(it)) if self.is_bound_method => it, | ||
2004 | _ => return None, | ||
2005 | }; | ||
2006 | let src = func.lookup(db.upcast()).source(db.upcast()); | ||
2007 | let param_list = src.value.param_list()?; | ||
2008 | param_list.self_param() | ||
2009 | } | ||
2010 | pub fn n_params(&self) -> usize { | ||
2011 | self.sig.params().len() - if self.is_bound_method { 1 } else { 0 } | ||
2012 | } | ||
2013 | pub fn params( | ||
2014 | &self, | ||
2015 | db: &dyn HirDatabase, | ||
2016 | ) -> Vec<(Option<Either<ast::SelfParam, ast::Pat>>, Type)> { | ||
2017 | let types = self | ||
2018 | .sig | ||
2019 | .params() | ||
2020 | .iter() | ||
2021 | .skip(if self.is_bound_method { 1 } else { 0 }) | ||
2022 | .map(|ty| self.ty.derived(ty.clone())); | ||
2023 | let patterns = match self.def { | ||
2024 | Some(CallableDefId::FunctionId(func)) => { | ||
2025 | let src = func.lookup(db.upcast()).source(db.upcast()); | ||
2026 | src.value.param_list().map(|param_list| { | ||
2027 | param_list | ||
2028 | .self_param() | ||
2029 | .map(|it| Some(Either::Left(it))) | ||
2030 | .filter(|_| !self.is_bound_method) | ||
2031 | .into_iter() | ||
2032 | .chain(param_list.params().map(|it| it.pat().map(Either::Right))) | ||
2033 | }) | ||
2034 | } | ||
2035 | _ => None, | ||
2036 | }; | ||
2037 | patterns.into_iter().flatten().chain(iter::repeat(None)).zip(types).collect() | ||
2038 | } | ||
2039 | pub fn return_type(&self) -> Type { | ||
2040 | self.ty.derived(self.sig.ret().clone()) | ||
2041 | } | ||
2042 | } | ||
2043 | |||
2044 | /// For IDE only | ||
2045 | #[derive(Debug, PartialEq, Eq, Hash)] | ||
2046 | pub enum ScopeDef { | ||
2047 | ModuleDef(ModuleDef), | ||
2048 | MacroDef(MacroDef), | ||
2049 | GenericParam(GenericParam), | ||
2050 | ImplSelfType(Impl), | ||
2051 | AdtSelfType(Adt), | ||
2052 | Local(Local), | ||
2053 | Unknown, | ||
2054 | } | ||
2055 | |||
2056 | impl ScopeDef { | ||
2057 | pub fn all_items(def: PerNs) -> ArrayVec<[Self; 3]> { | ||
2058 | let mut items = ArrayVec::new(); | ||
2059 | |||
2060 | match (def.take_types(), def.take_values()) { | ||
2061 | (Some(m1), None) => items.push(ScopeDef::ModuleDef(m1.into())), | ||
2062 | (None, Some(m2)) => items.push(ScopeDef::ModuleDef(m2.into())), | ||
2063 | (Some(m1), Some(m2)) => { | ||
2064 | // Some items, like unit structs and enum variants, are | ||
2065 | // returned as both a type and a value. Here we want | ||
2066 | // to de-duplicate them. | ||
2067 | if m1 != m2 { | ||
2068 | items.push(ScopeDef::ModuleDef(m1.into())); | ||
2069 | items.push(ScopeDef::ModuleDef(m2.into())); | ||
2070 | } else { | ||
2071 | items.push(ScopeDef::ModuleDef(m1.into())); | ||
2072 | } | ||
2073 | } | ||
2074 | (None, None) => {} | ||
2075 | }; | ||
2076 | |||
2077 | if let Some(macro_def_id) = def.take_macros() { | ||
2078 | items.push(ScopeDef::MacroDef(macro_def_id.into())); | ||
2079 | } | ||
2080 | |||
2081 | if items.is_empty() { | ||
2082 | items.push(ScopeDef::Unknown); | ||
2083 | } | ||
2084 | |||
2085 | items | ||
2086 | } | ||
2087 | } | ||
2088 | |||
2089 | pub trait HasVisibility { | ||
2090 | fn visibility(&self, db: &dyn HirDatabase) -> Visibility; | ||
2091 | fn is_visible_from(&self, db: &dyn HirDatabase, module: Module) -> bool { | ||
2092 | let vis = self.visibility(db); | ||
2093 | vis.is_visible_from(db.upcast(), module.id) | ||
2094 | } | ||
2095 | } | ||
diff --git a/crates/hir/src/from_id.rs b/crates/hir/src/from_id.rs index b5814da11..179b9d51e 100644 --- a/crates/hir/src/from_id.rs +++ b/crates/hir/src/from_id.rs | |||
@@ -11,9 +11,8 @@ use hir_def::{ | |||
11 | }; | 11 | }; |
12 | 12 | ||
13 | use crate::{ | 13 | use crate::{ |
14 | code_model::{BuiltinType, GenericParam}, | 14 | Adt, AssocItem, BuiltinType, DefWithBody, Field, GenericDef, GenericParam, Label, Local, |
15 | Adt, AssocItem, DefWithBody, Field, GenericDef, Label, Local, MacroDef, ModuleDef, Variant, | 15 | MacroDef, ModuleDef, Variant, VariantDef, |
16 | VariantDef, | ||
17 | }; | 16 | }; |
18 | 17 | ||
19 | macro_rules! from_id { | 18 | macro_rules! from_id { |
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 69fcdab07..58adc8fd3 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs | |||
@@ -20,49 +20,2117 @@ | |||
20 | #![recursion_limit = "512"] | 20 | #![recursion_limit = "512"] |
21 | 21 | ||
22 | mod semantics; | 22 | mod semantics; |
23 | pub mod db; | ||
24 | mod source_analyzer; | 23 | mod source_analyzer; |
25 | 24 | ||
26 | pub mod diagnostics; | ||
27 | |||
28 | mod from_id; | 25 | mod from_id; |
29 | mod code_model; | ||
30 | mod attrs; | 26 | mod attrs; |
31 | mod has_source; | 27 | mod has_source; |
32 | 28 | ||
29 | pub mod diagnostics; | ||
30 | pub mod db; | ||
31 | |||
32 | use std::{iter, sync::Arc}; | ||
33 | |||
34 | use arrayvec::ArrayVec; | ||
35 | use base_db::{CrateDisplayName, CrateId, Edition, FileId}; | ||
36 | use either::Either; | ||
37 | use hir_def::{ | ||
38 | adt::{ReprKind, VariantData}, | ||
39 | expr::{BindingAnnotation, LabelId, Pat, PatId}, | ||
40 | item_tree::ItemTreeNode, | ||
41 | lang_item::LangItemTarget, | ||
42 | per_ns::PerNs, | ||
43 | resolver::{HasResolver, Resolver}, | ||
44 | src::HasSource as _, | ||
45 | AdtId, AssocContainerId, AssocItemId, AssocItemLoc, AttrDefId, ConstId, ConstParamId, | ||
46 | DefWithBodyId, EnumId, FunctionId, GenericDefId, HasModule, ImplId, LifetimeParamId, | ||
47 | LocalEnumVariantId, LocalFieldId, Lookup, ModuleId, StaticId, StructId, TraitId, TypeAliasId, | ||
48 | TypeParamId, UnionId, | ||
49 | }; | ||
50 | use hir_expand::{diagnostics::DiagnosticSink, name::name, MacroDefKind}; | ||
51 | use hir_ty::{ | ||
52 | autoderef, | ||
53 | display::{write_bounds_like_dyn_trait_with_prefix, HirDisplayError, HirFormatter}, | ||
54 | method_resolution, | ||
55 | traits::{FnTrait, Solution, SolutionVariables}, | ||
56 | AliasTy, BoundVar, CallableDefId, CallableSig, Canonical, DebruijnIndex, GenericPredicate, | ||
57 | InEnvironment, Obligation, ProjectionPredicate, ProjectionTy, Scalar, Substs, TraitEnvironment, | ||
58 | Ty, TyDefId, TyVariableKind, | ||
59 | }; | ||
60 | use rustc_hash::FxHashSet; | ||
61 | use stdx::{format_to, impl_from}; | ||
62 | use syntax::{ | ||
63 | ast::{self, AttrsOwner, NameOwner}, | ||
64 | AstNode, SmolStr, | ||
65 | }; | ||
66 | use tt::{Ident, Leaf, Literal, TokenTree}; | ||
67 | |||
68 | use crate::db::{DefDatabase, HirDatabase}; | ||
69 | |||
33 | pub use crate::{ | 70 | pub use crate::{ |
34 | attrs::{HasAttrs, Namespace}, | 71 | attrs::{HasAttrs, Namespace}, |
35 | code_model::{ | ||
36 | Access, Adt, AsAssocItem, AssocItem, AssocItemContainer, BuiltinType, Callable, | ||
37 | CallableKind, Const, ConstParam, Crate, CrateDependency, DefWithBody, Enum, Field, | ||
38 | FieldSource, Function, GenericDef, GenericParam, HasVisibility, Impl, Label, LifetimeParam, | ||
39 | Local, MacroDef, Module, ModuleDef, ScopeDef, Static, Struct, Trait, Type, TypeAlias, | ||
40 | TypeParam, Union, Variant, VariantDef, | ||
41 | }, | ||
42 | has_source::HasSource, | 72 | has_source::HasSource, |
43 | semantics::{PathResolution, Semantics, SemanticsScope}, | 73 | semantics::{PathResolution, Semantics, SemanticsScope}, |
44 | }; | 74 | }; |
45 | 75 | ||
46 | pub use hir_def::{ | 76 | // Be careful with these re-exports. |
47 | adt::StructKind, | 77 | // |
48 | attr::{Attrs, Documentation}, | 78 | // `hir` is the boundary between the compiler and the IDE. It should try hard to |
49 | body::scope::ExprScopes, | 79 | // isolate the compiler from the ide, to allow the two to be refactored |
50 | find_path::PrefixKind, | 80 | // independently. Re-exporting something from the compiler is the sure way to |
51 | import_map, | 81 | // breach the boundary. |
52 | item_scope::ItemInNs, | 82 | // |
53 | nameres::ModuleSource, | 83 | // Generally, a refactoring which *removes* a name from this list is a good |
54 | path::{ModPath, PathKind}, | 84 | // idea! |
55 | type_ref::{Mutability, TypeRef}, | 85 | pub use { |
56 | visibility::Visibility, | 86 | hir_def::{ |
57 | }; | 87 | adt::StructKind, |
58 | pub use hir_expand::{ | 88 | attr::{Attrs, Documentation}, |
59 | name::{known, AsName, Name}, | 89 | body::scope::ExprScopes, |
60 | ExpandResult, HirFileId, InFile, MacroCallId, MacroCallLoc, /* FIXME */ MacroDefId, | 90 | find_path::PrefixKind, |
61 | MacroFile, Origin, | 91 | import_map, |
92 | item_scope::ItemInNs, | ||
93 | nameres::ModuleSource, | ||
94 | path::{ModPath, PathKind}, | ||
95 | type_ref::{Mutability, TypeRef}, | ||
96 | visibility::Visibility, | ||
97 | }, | ||
98 | hir_expand::{ | ||
99 | name::{known, Name}, | ||
100 | ExpandResult, HirFileId, InFile, MacroCallId, MacroCallLoc, /* FIXME */ MacroDefId, | ||
101 | MacroFile, Origin, | ||
102 | }, | ||
103 | hir_ty::display::HirDisplay, | ||
62 | }; | 104 | }; |
63 | pub use hir_ty::display::HirDisplay; | ||
64 | 105 | ||
65 | // These are negative re-exports: pub using these names is forbidden, they | 106 | // These are negative re-exports: pub using these names is forbidden, they |
66 | // should remain private to hir internals. | 107 | // should remain private to hir internals. |
67 | #[allow(unused)] | 108 | #[allow(unused)] |
68 | use {hir_def::path::Path, hir_expand::hygiene::Hygiene}; | 109 | use { |
110 | hir_def::path::Path, | ||
111 | hir_expand::{hygiene::Hygiene, name::AsName}, | ||
112 | }; | ||
113 | |||
114 | /// hir::Crate describes a single crate. It's the main interface with which | ||
115 | /// a crate's dependencies interact. Mostly, it should be just a proxy for the | ||
116 | /// root module. | ||
117 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
118 | pub struct Crate { | ||
119 | pub(crate) id: CrateId, | ||
120 | } | ||
121 | |||
122 | #[derive(Debug)] | ||
123 | pub struct CrateDependency { | ||
124 | pub krate: Crate, | ||
125 | pub name: Name, | ||
126 | } | ||
127 | |||
128 | impl Crate { | ||
129 | pub fn dependencies(self, db: &dyn HirDatabase) -> Vec<CrateDependency> { | ||
130 | db.crate_graph()[self.id] | ||
131 | .dependencies | ||
132 | .iter() | ||
133 | .map(|dep| { | ||
134 | let krate = Crate { id: dep.crate_id }; | ||
135 | let name = dep.as_name(); | ||
136 | CrateDependency { krate, name } | ||
137 | }) | ||
138 | .collect() | ||
139 | } | ||
140 | |||
141 | // FIXME: add `transitive_reverse_dependencies`. | ||
142 | pub fn reverse_dependencies(self, db: &dyn HirDatabase) -> Vec<Crate> { | ||
143 | let crate_graph = db.crate_graph(); | ||
144 | crate_graph | ||
145 | .iter() | ||
146 | .filter(|&krate| { | ||
147 | crate_graph[krate].dependencies.iter().any(|it| it.crate_id == self.id) | ||
148 | }) | ||
149 | .map(|id| Crate { id }) | ||
150 | .collect() | ||
151 | } | ||
152 | |||
153 | pub fn root_module(self, db: &dyn HirDatabase) -> Module { | ||
154 | let def_map = db.crate_def_map(self.id); | ||
155 | Module { id: def_map.module_id(def_map.root()) } | ||
156 | } | ||
157 | |||
158 | pub fn root_file(self, db: &dyn HirDatabase) -> FileId { | ||
159 | db.crate_graph()[self.id].root_file_id | ||
160 | } | ||
161 | |||
162 | pub fn edition(self, db: &dyn HirDatabase) -> Edition { | ||
163 | db.crate_graph()[self.id].edition | ||
164 | } | ||
165 | |||
166 | pub fn display_name(self, db: &dyn HirDatabase) -> Option<CrateDisplayName> { | ||
167 | db.crate_graph()[self.id].display_name.clone() | ||
168 | } | ||
169 | |||
170 | pub fn query_external_importables( | ||
171 | self, | ||
172 | db: &dyn DefDatabase, | ||
173 | query: import_map::Query, | ||
174 | ) -> impl Iterator<Item = Either<ModuleDef, MacroDef>> { | ||
175 | import_map::search_dependencies(db, self.into(), query).into_iter().map(|item| match item { | ||
176 | ItemInNs::Types(mod_id) | ItemInNs::Values(mod_id) => Either::Left(mod_id.into()), | ||
177 | ItemInNs::Macros(mac_id) => Either::Right(mac_id.into()), | ||
178 | }) | ||
179 | } | ||
180 | |||
181 | pub fn all(db: &dyn HirDatabase) -> Vec<Crate> { | ||
182 | db.crate_graph().iter().map(|id| Crate { id }).collect() | ||
183 | } | ||
184 | |||
185 | /// Try to get the root URL of the documentation of a crate. | ||
186 | pub fn get_html_root_url(self: &Crate, db: &dyn HirDatabase) -> Option<String> { | ||
187 | // Look for #![doc(html_root_url = "...")] | ||
188 | let attrs = db.attrs(AttrDefId::ModuleId(self.root_module(db).into())); | ||
189 | let doc_attr_q = attrs.by_key("doc"); | ||
190 | |||
191 | if !doc_attr_q.exists() { | ||
192 | return None; | ||
193 | } | ||
194 | |||
195 | let doc_url = doc_attr_q.tt_values().map(|tt| { | ||
196 | let name = tt.token_trees.iter() | ||
197 | .skip_while(|tt| !matches!(tt, TokenTree::Leaf(Leaf::Ident(Ident{text: ref ident, ..})) if ident == "html_root_url")) | ||
198 | .skip(2) | ||
199 | .next(); | ||
200 | |||
201 | match name { | ||
202 | Some(TokenTree::Leaf(Leaf::Literal(Literal{ref text, ..}))) => Some(text), | ||
203 | _ => None | ||
204 | } | ||
205 | }).flat_map(|t| t).next(); | ||
206 | |||
207 | doc_url.map(|s| s.trim_matches('"').trim_end_matches('/').to_owned() + "/") | ||
208 | } | ||
209 | } | ||
210 | |||
211 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
212 | pub struct Module { | ||
213 | pub(crate) id: ModuleId, | ||
214 | } | ||
215 | |||
216 | /// The defs which can be visible in the module. | ||
217 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
218 | pub enum ModuleDef { | ||
219 | Module(Module), | ||
220 | Function(Function), | ||
221 | Adt(Adt), | ||
222 | // Can't be directly declared, but can be imported. | ||
223 | Variant(Variant), | ||
224 | Const(Const), | ||
225 | Static(Static), | ||
226 | Trait(Trait), | ||
227 | TypeAlias(TypeAlias), | ||
228 | BuiltinType(BuiltinType), | ||
229 | } | ||
230 | impl_from!( | ||
231 | Module, | ||
232 | Function, | ||
233 | Adt(Struct, Enum, Union), | ||
234 | Variant, | ||
235 | Const, | ||
236 | Static, | ||
237 | Trait, | ||
238 | TypeAlias, | ||
239 | BuiltinType | ||
240 | for ModuleDef | ||
241 | ); | ||
242 | |||
243 | impl From<VariantDef> for ModuleDef { | ||
244 | fn from(var: VariantDef) -> Self { | ||
245 | match var { | ||
246 | VariantDef::Struct(t) => Adt::from(t).into(), | ||
247 | VariantDef::Union(t) => Adt::from(t).into(), | ||
248 | VariantDef::Variant(t) => t.into(), | ||
249 | } | ||
250 | } | ||
251 | } | ||
252 | |||
253 | impl ModuleDef { | ||
254 | pub fn module(self, db: &dyn HirDatabase) -> Option<Module> { | ||
255 | match self { | ||
256 | ModuleDef::Module(it) => it.parent(db), | ||
257 | ModuleDef::Function(it) => Some(it.module(db)), | ||
258 | ModuleDef::Adt(it) => Some(it.module(db)), | ||
259 | ModuleDef::Variant(it) => Some(it.module(db)), | ||
260 | ModuleDef::Const(it) => Some(it.module(db)), | ||
261 | ModuleDef::Static(it) => Some(it.module(db)), | ||
262 | ModuleDef::Trait(it) => Some(it.module(db)), | ||
263 | ModuleDef::TypeAlias(it) => Some(it.module(db)), | ||
264 | ModuleDef::BuiltinType(_) => None, | ||
265 | } | ||
266 | } | ||
267 | |||
268 | pub fn canonical_path(&self, db: &dyn HirDatabase) -> Option<String> { | ||
269 | let mut segments = Vec::new(); | ||
270 | segments.push(self.name(db)?.to_string()); | ||
271 | for m in self.module(db)?.path_to_root(db) { | ||
272 | segments.extend(m.name(db).map(|it| it.to_string())) | ||
273 | } | ||
274 | segments.reverse(); | ||
275 | Some(segments.join("::")) | ||
276 | } | ||
277 | |||
278 | pub fn definition_visibility(&self, db: &dyn HirDatabase) -> Option<Visibility> { | ||
279 | let module = match self { | ||
280 | ModuleDef::Module(it) => it.parent(db)?, | ||
281 | ModuleDef::Function(it) => return Some(it.visibility(db)), | ||
282 | ModuleDef::Adt(it) => it.module(db), | ||
283 | ModuleDef::Variant(it) => { | ||
284 | let parent = it.parent_enum(db); | ||
285 | let module = it.module(db); | ||
286 | return module.visibility_of(db, &ModuleDef::Adt(Adt::Enum(parent))); | ||
287 | } | ||
288 | ModuleDef::Const(it) => return Some(it.visibility(db)), | ||
289 | ModuleDef::Static(it) => it.module(db), | ||
290 | ModuleDef::Trait(it) => it.module(db), | ||
291 | ModuleDef::TypeAlias(it) => return Some(it.visibility(db)), | ||
292 | ModuleDef::BuiltinType(_) => return None, | ||
293 | }; | ||
294 | |||
295 | module.visibility_of(db, self) | ||
296 | } | ||
297 | |||
298 | pub fn name(self, db: &dyn HirDatabase) -> Option<Name> { | ||
299 | match self { | ||
300 | ModuleDef::Adt(it) => Some(it.name(db)), | ||
301 | ModuleDef::Trait(it) => Some(it.name(db)), | ||
302 | ModuleDef::Function(it) => Some(it.name(db)), | ||
303 | ModuleDef::Variant(it) => Some(it.name(db)), | ||
304 | ModuleDef::TypeAlias(it) => Some(it.name(db)), | ||
305 | ModuleDef::Module(it) => it.name(db), | ||
306 | ModuleDef::Const(it) => it.name(db), | ||
307 | ModuleDef::Static(it) => it.name(db), | ||
308 | ModuleDef::BuiltinType(it) => Some(it.name()), | ||
309 | } | ||
310 | } | ||
311 | |||
312 | pub fn diagnostics(self, db: &dyn HirDatabase, sink: &mut DiagnosticSink) { | ||
313 | let id = match self { | ||
314 | ModuleDef::Adt(it) => match it { | ||
315 | Adt::Struct(it) => it.id.into(), | ||
316 | Adt::Enum(it) => it.id.into(), | ||
317 | Adt::Union(it) => it.id.into(), | ||
318 | }, | ||
319 | ModuleDef::Trait(it) => it.id.into(), | ||
320 | ModuleDef::Function(it) => it.id.into(), | ||
321 | ModuleDef::TypeAlias(it) => it.id.into(), | ||
322 | ModuleDef::Module(it) => it.id.into(), | ||
323 | ModuleDef::Const(it) => it.id.into(), | ||
324 | ModuleDef::Static(it) => it.id.into(), | ||
325 | _ => return, | ||
326 | }; | ||
327 | |||
328 | let module = match self.module(db) { | ||
329 | Some(it) => it, | ||
330 | None => return, | ||
331 | }; | ||
332 | |||
333 | hir_ty::diagnostics::validate_module_item(db, module.id.krate(), id, sink) | ||
334 | } | ||
335 | } | ||
336 | |||
337 | impl Module { | ||
338 | /// Name of this module. | ||
339 | pub fn name(self, db: &dyn HirDatabase) -> Option<Name> { | ||
340 | let def_map = self.id.def_map(db.upcast()); | ||
341 | let parent = def_map[self.id.local_id].parent?; | ||
342 | def_map[parent].children.iter().find_map(|(name, module_id)| { | ||
343 | if *module_id == self.id.local_id { | ||
344 | Some(name.clone()) | ||
345 | } else { | ||
346 | None | ||
347 | } | ||
348 | }) | ||
349 | } | ||
350 | |||
351 | /// Returns the crate this module is part of. | ||
352 | pub fn krate(self) -> Crate { | ||
353 | Crate { id: self.id.krate() } | ||
354 | } | ||
355 | |||
356 | /// Topmost parent of this module. Every module has a `crate_root`, but some | ||
357 | /// might be missing `krate`. This can happen if a module's file is not included | ||
358 | /// in the module tree of any target in `Cargo.toml`. | ||
359 | pub fn crate_root(self, db: &dyn HirDatabase) -> Module { | ||
360 | let def_map = db.crate_def_map(self.id.krate()); | ||
361 | Module { id: def_map.module_id(def_map.root()) } | ||
362 | } | ||
363 | |||
364 | /// Iterates over all child modules. | ||
365 | pub fn children(self, db: &dyn HirDatabase) -> impl Iterator<Item = Module> { | ||
366 | let def_map = self.id.def_map(db.upcast()); | ||
367 | let children = def_map[self.id.local_id] | ||
368 | .children | ||
369 | .iter() | ||
370 | .map(|(_, module_id)| Module { id: def_map.module_id(*module_id) }) | ||
371 | .collect::<Vec<_>>(); | ||
372 | children.into_iter() | ||
373 | } | ||
374 | |||
375 | /// Finds a parent module. | ||
376 | pub fn parent(self, db: &dyn HirDatabase) -> Option<Module> { | ||
377 | // FIXME: handle block expressions as modules (their parent is in a different DefMap) | ||
378 | let def_map = self.id.def_map(db.upcast()); | ||
379 | let parent_id = def_map[self.id.local_id].parent?; | ||
380 | Some(Module { id: def_map.module_id(parent_id) }) | ||
381 | } | ||
382 | |||
383 | pub fn path_to_root(self, db: &dyn HirDatabase) -> Vec<Module> { | ||
384 | let mut res = vec![self]; | ||
385 | let mut curr = self; | ||
386 | while let Some(next) = curr.parent(db) { | ||
387 | res.push(next); | ||
388 | curr = next | ||
389 | } | ||
390 | res | ||
391 | } | ||
392 | |||
393 | /// Returns a `ModuleScope`: a set of items, visible in this module. | ||
394 | pub fn scope( | ||
395 | self, | ||
396 | db: &dyn HirDatabase, | ||
397 | visible_from: Option<Module>, | ||
398 | ) -> Vec<(Name, ScopeDef)> { | ||
399 | self.id.def_map(db.upcast())[self.id.local_id] | ||
400 | .scope | ||
401 | .entries() | ||
402 | .filter_map(|(name, def)| { | ||
403 | if let Some(m) = visible_from { | ||
404 | let filtered = | ||
405 | def.filter_visibility(|vis| vis.is_visible_from(db.upcast(), m.id)); | ||
406 | if filtered.is_none() && !def.is_none() { | ||
407 | None | ||
408 | } else { | ||
409 | Some((name, filtered)) | ||
410 | } | ||
411 | } else { | ||
412 | Some((name, def)) | ||
413 | } | ||
414 | }) | ||
415 | .flat_map(|(name, def)| { | ||
416 | ScopeDef::all_items(def).into_iter().map(move |item| (name.clone(), item)) | ||
417 | }) | ||
418 | .collect() | ||
419 | } | ||
420 | |||
421 | pub fn visibility_of(self, db: &dyn HirDatabase, def: &ModuleDef) -> Option<Visibility> { | ||
422 | self.id.def_map(db.upcast())[self.id.local_id].scope.visibility_of(def.clone().into()) | ||
423 | } | ||
424 | |||
425 | pub fn diagnostics(self, db: &dyn HirDatabase, sink: &mut DiagnosticSink) { | ||
426 | let _p = profile::span("Module::diagnostics").detail(|| { | ||
427 | format!("{:?}", self.name(db).map_or("<unknown>".into(), |name| name.to_string())) | ||
428 | }); | ||
429 | let def_map = self.id.def_map(db.upcast()); | ||
430 | def_map.add_diagnostics(db.upcast(), self.id.local_id, sink); | ||
431 | for decl in self.declarations(db) { | ||
432 | match decl { | ||
433 | crate::ModuleDef::Function(f) => f.diagnostics(db, sink), | ||
434 | crate::ModuleDef::Module(m) => { | ||
435 | // Only add diagnostics from inline modules | ||
436 | if def_map[m.id.local_id].origin.is_inline() { | ||
437 | m.diagnostics(db, sink) | ||
438 | } | ||
439 | } | ||
440 | _ => { | ||
441 | decl.diagnostics(db, sink); | ||
442 | } | ||
443 | } | ||
444 | } | ||
445 | |||
446 | for impl_def in self.impl_defs(db) { | ||
447 | for item in impl_def.items(db) { | ||
448 | if let AssocItem::Function(f) = item { | ||
449 | f.diagnostics(db, sink); | ||
450 | } | ||
451 | } | ||
452 | } | ||
453 | } | ||
454 | |||
455 | pub fn declarations(self, db: &dyn HirDatabase) -> Vec<ModuleDef> { | ||
456 | let def_map = self.id.def_map(db.upcast()); | ||
457 | def_map[self.id.local_id].scope.declarations().map(ModuleDef::from).collect() | ||
458 | } | ||
459 | |||
460 | pub fn impl_defs(self, db: &dyn HirDatabase) -> Vec<Impl> { | ||
461 | let def_map = self.id.def_map(db.upcast()); | ||
462 | def_map[self.id.local_id].scope.impls().map(Impl::from).collect() | ||
463 | } | ||
464 | |||
465 | /// Finds a path that can be used to refer to the given item from within | ||
466 | /// this module, if possible. | ||
467 | pub fn find_use_path(self, db: &dyn DefDatabase, item: impl Into<ItemInNs>) -> Option<ModPath> { | ||
468 | hir_def::find_path::find_path(db, item.into(), self.into()) | ||
469 | } | ||
470 | |||
471 | /// Finds a path that can be used to refer to the given item from within | ||
472 | /// this module, if possible. This is used for returning import paths for use-statements. | ||
473 | pub fn find_use_path_prefixed( | ||
474 | self, | ||
475 | db: &dyn DefDatabase, | ||
476 | item: impl Into<ItemInNs>, | ||
477 | prefix_kind: PrefixKind, | ||
478 | ) -> Option<ModPath> { | ||
479 | hir_def::find_path::find_path_prefixed(db, item.into(), self.into(), prefix_kind) | ||
480 | } | ||
481 | } | ||
482 | |||
483 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
484 | pub struct Field { | ||
485 | pub(crate) parent: VariantDef, | ||
486 | pub(crate) id: LocalFieldId, | ||
487 | } | ||
488 | |||
489 | #[derive(Debug, PartialEq, Eq)] | ||
490 | pub enum FieldSource { | ||
491 | Named(ast::RecordField), | ||
492 | Pos(ast::TupleField), | ||
493 | } | ||
494 | |||
495 | impl Field { | ||
496 | pub fn name(&self, db: &dyn HirDatabase) -> Name { | ||
497 | self.parent.variant_data(db).fields()[self.id].name.clone() | ||
498 | } | ||
499 | |||
500 | /// Returns the type as in the signature of the struct (i.e., with | ||
501 | /// placeholder types for type parameters). This is good for showing | ||
502 | /// signature help, but not so good to actually get the type of the field | ||
503 | /// when you actually have a variable of the struct. | ||
504 | pub fn signature_ty(&self, db: &dyn HirDatabase) -> Type { | ||
505 | let var_id = self.parent.into(); | ||
506 | let generic_def_id: GenericDefId = match self.parent { | ||
507 | VariantDef::Struct(it) => it.id.into(), | ||
508 | VariantDef::Union(it) => it.id.into(), | ||
509 | VariantDef::Variant(it) => it.parent.id.into(), | ||
510 | }; | ||
511 | let substs = Substs::type_params(db, generic_def_id); | ||
512 | let ty = db.field_types(var_id)[self.id].clone().subst(&substs); | ||
513 | Type::new(db, self.parent.module(db).id.krate(), var_id, ty) | ||
514 | } | ||
515 | |||
516 | pub fn parent_def(&self, _db: &dyn HirDatabase) -> VariantDef { | ||
517 | self.parent | ||
518 | } | ||
519 | } | ||
520 | |||
521 | impl HasVisibility for Field { | ||
522 | fn visibility(&self, db: &dyn HirDatabase) -> Visibility { | ||
523 | let variant_data = self.parent.variant_data(db); | ||
524 | let visibility = &variant_data.fields()[self.id].visibility; | ||
525 | let parent_id: hir_def::VariantId = self.parent.into(); | ||
526 | visibility.resolve(db.upcast(), &parent_id.resolver(db.upcast())) | ||
527 | } | ||
528 | } | ||
529 | |||
530 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
531 | pub struct Struct { | ||
532 | pub(crate) id: StructId, | ||
533 | } | ||
534 | |||
535 | impl Struct { | ||
536 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
537 | Module { id: self.id.lookup(db.upcast()).container } | ||
538 | } | ||
539 | |||
540 | pub fn krate(self, db: &dyn HirDatabase) -> Option<Crate> { | ||
541 | Some(self.module(db).krate()) | ||
542 | } | ||
543 | |||
544 | pub fn name(self, db: &dyn HirDatabase) -> Name { | ||
545 | db.struct_data(self.id).name.clone() | ||
546 | } | ||
547 | |||
548 | pub fn fields(self, db: &dyn HirDatabase) -> Vec<Field> { | ||
549 | db.struct_data(self.id) | ||
550 | .variant_data | ||
551 | .fields() | ||
552 | .iter() | ||
553 | .map(|(id, _)| Field { parent: self.into(), id }) | ||
554 | .collect() | ||
555 | } | ||
556 | |||
557 | pub fn ty(self, db: &dyn HirDatabase) -> Type { | ||
558 | Type::from_def(db, self.id.lookup(db.upcast()).container.krate(), self.id) | ||
559 | } | ||
560 | |||
561 | pub fn repr(self, db: &dyn HirDatabase) -> Option<ReprKind> { | ||
562 | db.struct_data(self.id).repr.clone() | ||
563 | } | ||
564 | |||
565 | pub fn kind(self, db: &dyn HirDatabase) -> StructKind { | ||
566 | self.variant_data(db).kind() | ||
567 | } | ||
568 | |||
569 | fn variant_data(self, db: &dyn HirDatabase) -> Arc<VariantData> { | ||
570 | db.struct_data(self.id).variant_data.clone() | ||
571 | } | ||
572 | } | ||
573 | |||
574 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
575 | pub struct Union { | ||
576 | pub(crate) id: UnionId, | ||
577 | } | ||
578 | |||
579 | impl Union { | ||
580 | pub fn name(self, db: &dyn HirDatabase) -> Name { | ||
581 | db.union_data(self.id).name.clone() | ||
582 | } | ||
583 | |||
584 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
585 | Module { id: self.id.lookup(db.upcast()).container } | ||
586 | } | ||
587 | |||
588 | pub fn ty(self, db: &dyn HirDatabase) -> Type { | ||
589 | Type::from_def(db, self.id.lookup(db.upcast()).container.krate(), self.id) | ||
590 | } | ||
591 | |||
592 | pub fn fields(self, db: &dyn HirDatabase) -> Vec<Field> { | ||
593 | db.union_data(self.id) | ||
594 | .variant_data | ||
595 | .fields() | ||
596 | .iter() | ||
597 | .map(|(id, _)| Field { parent: self.into(), id }) | ||
598 | .collect() | ||
599 | } | ||
600 | |||
601 | fn variant_data(self, db: &dyn HirDatabase) -> Arc<VariantData> { | ||
602 | db.union_data(self.id).variant_data.clone() | ||
603 | } | ||
604 | } | ||
605 | |||
606 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
607 | pub struct Enum { | ||
608 | pub(crate) id: EnumId, | ||
609 | } | ||
610 | |||
611 | impl Enum { | ||
612 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
613 | Module { id: self.id.lookup(db.upcast()).container } | ||
614 | } | ||
615 | |||
616 | pub fn krate(self, db: &dyn HirDatabase) -> Option<Crate> { | ||
617 | Some(self.module(db).krate()) | ||
618 | } | ||
619 | |||
620 | pub fn name(self, db: &dyn HirDatabase) -> Name { | ||
621 | db.enum_data(self.id).name.clone() | ||
622 | } | ||
623 | |||
624 | pub fn variants(self, db: &dyn HirDatabase) -> Vec<Variant> { | ||
625 | db.enum_data(self.id).variants.iter().map(|(id, _)| Variant { parent: self, id }).collect() | ||
626 | } | ||
627 | |||
628 | pub fn ty(self, db: &dyn HirDatabase) -> Type { | ||
629 | Type::from_def(db, self.id.lookup(db.upcast()).container.krate(), self.id) | ||
630 | } | ||
631 | } | ||
632 | |||
633 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
634 | pub struct Variant { | ||
635 | pub(crate) parent: Enum, | ||
636 | pub(crate) id: LocalEnumVariantId, | ||
637 | } | ||
638 | |||
639 | impl Variant { | ||
640 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
641 | self.parent.module(db) | ||
642 | } | ||
643 | pub fn parent_enum(self, _db: &dyn HirDatabase) -> Enum { | ||
644 | self.parent | ||
645 | } | ||
646 | |||
647 | pub fn name(self, db: &dyn HirDatabase) -> Name { | ||
648 | db.enum_data(self.parent.id).variants[self.id].name.clone() | ||
649 | } | ||
650 | |||
651 | pub fn fields(self, db: &dyn HirDatabase) -> Vec<Field> { | ||
652 | self.variant_data(db) | ||
653 | .fields() | ||
654 | .iter() | ||
655 | .map(|(id, _)| Field { parent: self.into(), id }) | ||
656 | .collect() | ||
657 | } | ||
658 | |||
659 | pub fn kind(self, db: &dyn HirDatabase) -> StructKind { | ||
660 | self.variant_data(db).kind() | ||
661 | } | ||
662 | |||
663 | pub(crate) fn variant_data(self, db: &dyn HirDatabase) -> Arc<VariantData> { | ||
664 | db.enum_data(self.parent.id).variants[self.id].variant_data.clone() | ||
665 | } | ||
666 | } | ||
667 | |||
668 | /// A Data Type | ||
669 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] | ||
670 | pub enum Adt { | ||
671 | Struct(Struct), | ||
672 | Union(Union), | ||
673 | Enum(Enum), | ||
674 | } | ||
675 | impl_from!(Struct, Union, Enum for Adt); | ||
676 | |||
677 | impl Adt { | ||
678 | pub fn has_non_default_type_params(self, db: &dyn HirDatabase) -> bool { | ||
679 | let subst = db.generic_defaults(self.into()); | ||
680 | subst.iter().any(|ty| &ty.value == &Ty::Unknown) | ||
681 | } | ||
682 | |||
683 | /// Turns this ADT into a type. Any type parameters of the ADT will be | ||
684 | /// turned into unknown types, which is good for e.g. finding the most | ||
685 | /// general set of completions, but will not look very nice when printed. | ||
686 | pub fn ty(self, db: &dyn HirDatabase) -> Type { | ||
687 | let id = AdtId::from(self); | ||
688 | Type::from_def(db, id.module(db.upcast()).krate(), id) | ||
689 | } | ||
690 | |||
691 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
692 | match self { | ||
693 | Adt::Struct(s) => s.module(db), | ||
694 | Adt::Union(s) => s.module(db), | ||
695 | Adt::Enum(e) => e.module(db), | ||
696 | } | ||
697 | } | ||
698 | |||
699 | pub fn krate(self, db: &dyn HirDatabase) -> Option<Crate> { | ||
700 | Some(self.module(db).krate()) | ||
701 | } | ||
702 | |||
703 | pub fn name(self, db: &dyn HirDatabase) -> Name { | ||
704 | match self { | ||
705 | Adt::Struct(s) => s.name(db), | ||
706 | Adt::Union(u) => u.name(db), | ||
707 | Adt::Enum(e) => e.name(db), | ||
708 | } | ||
709 | } | ||
710 | } | ||
711 | |||
712 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] | ||
713 | pub enum VariantDef { | ||
714 | Struct(Struct), | ||
715 | Union(Union), | ||
716 | Variant(Variant), | ||
717 | } | ||
718 | impl_from!(Struct, Union, Variant for VariantDef); | ||
719 | |||
720 | impl VariantDef { | ||
721 | pub fn fields(self, db: &dyn HirDatabase) -> Vec<Field> { | ||
722 | match self { | ||
723 | VariantDef::Struct(it) => it.fields(db), | ||
724 | VariantDef::Union(it) => it.fields(db), | ||
725 | VariantDef::Variant(it) => it.fields(db), | ||
726 | } | ||
727 | } | ||
728 | |||
729 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
730 | match self { | ||
731 | VariantDef::Struct(it) => it.module(db), | ||
732 | VariantDef::Union(it) => it.module(db), | ||
733 | VariantDef::Variant(it) => it.module(db), | ||
734 | } | ||
735 | } | ||
736 | |||
737 | pub fn name(&self, db: &dyn HirDatabase) -> Name { | ||
738 | match self { | ||
739 | VariantDef::Struct(s) => s.name(db), | ||
740 | VariantDef::Union(u) => u.name(db), | ||
741 | VariantDef::Variant(e) => e.name(db), | ||
742 | } | ||
743 | } | ||
744 | |||
745 | pub(crate) fn variant_data(self, db: &dyn HirDatabase) -> Arc<VariantData> { | ||
746 | match self { | ||
747 | VariantDef::Struct(it) => it.variant_data(db), | ||
748 | VariantDef::Union(it) => it.variant_data(db), | ||
749 | VariantDef::Variant(it) => it.variant_data(db), | ||
750 | } | ||
751 | } | ||
752 | } | ||
753 | |||
754 | /// The defs which have a body. | ||
755 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
756 | pub enum DefWithBody { | ||
757 | Function(Function), | ||
758 | Static(Static), | ||
759 | Const(Const), | ||
760 | } | ||
761 | impl_from!(Function, Const, Static for DefWithBody); | ||
762 | |||
763 | impl DefWithBody { | ||
764 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
765 | match self { | ||
766 | DefWithBody::Const(c) => c.module(db), | ||
767 | DefWithBody::Function(f) => f.module(db), | ||
768 | DefWithBody::Static(s) => s.module(db), | ||
769 | } | ||
770 | } | ||
771 | |||
772 | pub fn name(self, db: &dyn HirDatabase) -> Option<Name> { | ||
773 | match self { | ||
774 | DefWithBody::Function(f) => Some(f.name(db)), | ||
775 | DefWithBody::Static(s) => s.name(db), | ||
776 | DefWithBody::Const(c) => c.name(db), | ||
777 | } | ||
778 | } | ||
779 | } | ||
780 | |||
781 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
782 | pub struct Function { | ||
783 | pub(crate) id: FunctionId, | ||
784 | } | ||
785 | |||
786 | impl Function { | ||
787 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
788 | self.id.lookup(db.upcast()).module(db.upcast()).into() | ||
789 | } | ||
790 | |||
791 | pub fn krate(self, db: &dyn HirDatabase) -> Option<Crate> { | ||
792 | Some(self.module(db).krate()) | ||
793 | } | ||
794 | |||
795 | pub fn name(self, db: &dyn HirDatabase) -> Name { | ||
796 | db.function_data(self.id).name.clone() | ||
797 | } | ||
798 | |||
799 | /// Get this function's return type | ||
800 | pub fn ret_type(self, db: &dyn HirDatabase) -> Type { | ||
801 | let resolver = self.id.resolver(db.upcast()); | ||
802 | let krate = self.id.lookup(db.upcast()).container.module(db.upcast()).krate(); | ||
803 | let ret_type = &db.function_data(self.id).ret_type; | ||
804 | let ctx = hir_ty::TyLoweringContext::new(db, &resolver); | ||
805 | let ty = Ty::from_hir_ext(&ctx, ret_type).0; | ||
806 | Type::new_with_resolver_inner(db, krate, &resolver, ty) | ||
807 | } | ||
808 | |||
809 | pub fn self_param(self, db: &dyn HirDatabase) -> Option<SelfParam> { | ||
810 | if !db.function_data(self.id).has_self_param { | ||
811 | return None; | ||
812 | } | ||
813 | Some(SelfParam { func: self.id }) | ||
814 | } | ||
815 | |||
816 | pub fn assoc_fn_params(self, db: &dyn HirDatabase) -> Vec<Param> { | ||
817 | let resolver = self.id.resolver(db.upcast()); | ||
818 | let krate = self.id.lookup(db.upcast()).container.module(db.upcast()).krate(); | ||
819 | let ctx = hir_ty::TyLoweringContext::new(db, &resolver); | ||
820 | let environment = TraitEnvironment::lower(db, &resolver); | ||
821 | db.function_data(self.id) | ||
822 | .params | ||
823 | .iter() | ||
824 | .map(|type_ref| { | ||
825 | let ty = Type { | ||
826 | krate, | ||
827 | ty: InEnvironment { | ||
828 | value: Ty::from_hir_ext(&ctx, type_ref).0, | ||
829 | environment: environment.clone(), | ||
830 | }, | ||
831 | }; | ||
832 | Param { ty } | ||
833 | }) | ||
834 | .collect() | ||
835 | } | ||
836 | pub fn method_params(self, db: &dyn HirDatabase) -> Option<Vec<Param>> { | ||
837 | if self.self_param(db).is_none() { | ||
838 | return None; | ||
839 | } | ||
840 | let mut res = self.assoc_fn_params(db); | ||
841 | res.remove(0); | ||
842 | Some(res) | ||
843 | } | ||
844 | |||
845 | pub fn is_unsafe(self, db: &dyn HirDatabase) -> bool { | ||
846 | db.function_data(self.id).is_unsafe | ||
847 | } | ||
848 | |||
849 | pub fn diagnostics(self, db: &dyn HirDatabase, sink: &mut DiagnosticSink) { | ||
850 | let krate = self.module(db).id.krate(); | ||
851 | hir_def::diagnostics::validate_body(db.upcast(), self.id.into(), sink); | ||
852 | hir_ty::diagnostics::validate_module_item(db, krate, self.id.into(), sink); | ||
853 | hir_ty::diagnostics::validate_body(db, self.id.into(), sink); | ||
854 | } | ||
855 | |||
856 | /// Whether this function declaration has a definition. | ||
857 | /// | ||
858 | /// This is false in the case of required (not provided) trait methods. | ||
859 | pub fn has_body(self, db: &dyn HirDatabase) -> bool { | ||
860 | db.function_data(self.id).has_body | ||
861 | } | ||
862 | |||
863 | /// A textual representation of the HIR of this function for debugging purposes. | ||
864 | pub fn debug_hir(self, db: &dyn HirDatabase) -> String { | ||
865 | let body = db.body(self.id.into()); | ||
866 | |||
867 | let mut result = String::new(); | ||
868 | format_to!(result, "HIR expressions in the body of `{}`:\n", self.name(db)); | ||
869 | for (id, expr) in body.exprs.iter() { | ||
870 | format_to!(result, "{:?}: {:?}\n", id, expr); | ||
871 | } | ||
872 | |||
873 | result | ||
874 | } | ||
875 | } | ||
876 | |||
877 | // Note: logically, this belongs to `hir_ty`, but we are not using it there yet. | ||
878 | pub enum Access { | ||
879 | Shared, | ||
880 | Exclusive, | ||
881 | Owned, | ||
882 | } | ||
883 | |||
884 | impl From<hir_ty::Mutability> for Access { | ||
885 | fn from(mutability: hir_ty::Mutability) -> Access { | ||
886 | match mutability { | ||
887 | hir_ty::Mutability::Not => Access::Shared, | ||
888 | hir_ty::Mutability::Mut => Access::Exclusive, | ||
889 | } | ||
890 | } | ||
891 | } | ||
892 | |||
893 | #[derive(Debug)] | ||
894 | pub struct Param { | ||
895 | ty: Type, | ||
896 | } | ||
897 | |||
898 | impl Param { | ||
899 | pub fn ty(&self) -> &Type { | ||
900 | &self.ty | ||
901 | } | ||
902 | } | ||
903 | |||
904 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
905 | pub struct SelfParam { | ||
906 | func: FunctionId, | ||
907 | } | ||
908 | |||
909 | impl SelfParam { | ||
910 | pub fn access(self, db: &dyn HirDatabase) -> Access { | ||
911 | let func_data = db.function_data(self.func); | ||
912 | func_data | ||
913 | .params | ||
914 | .first() | ||
915 | .map(|param| match *param { | ||
916 | TypeRef::Reference(.., mutability) => match mutability { | ||
917 | hir_def::type_ref::Mutability::Shared => Access::Shared, | ||
918 | hir_def::type_ref::Mutability::Mut => Access::Exclusive, | ||
919 | }, | ||
920 | _ => Access::Owned, | ||
921 | }) | ||
922 | .unwrap_or(Access::Owned) | ||
923 | } | ||
924 | } | ||
925 | |||
926 | impl HasVisibility for Function { | ||
927 | fn visibility(&self, db: &dyn HirDatabase) -> Visibility { | ||
928 | let function_data = db.function_data(self.id); | ||
929 | let visibility = &function_data.visibility; | ||
930 | visibility.resolve(db.upcast(), &self.id.resolver(db.upcast())) | ||
931 | } | ||
932 | } | ||
933 | |||
934 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
935 | pub struct Const { | ||
936 | pub(crate) id: ConstId, | ||
937 | } | ||
938 | |||
939 | impl Const { | ||
940 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
941 | Module { id: self.id.lookup(db.upcast()).module(db.upcast()) } | ||
942 | } | ||
943 | |||
944 | pub fn krate(self, db: &dyn HirDatabase) -> Option<Crate> { | ||
945 | Some(self.module(db).krate()) | ||
946 | } | ||
947 | |||
948 | pub fn name(self, db: &dyn HirDatabase) -> Option<Name> { | ||
949 | db.const_data(self.id).name.clone() | ||
950 | } | ||
951 | } | ||
952 | |||
953 | impl HasVisibility for Const { | ||
954 | fn visibility(&self, db: &dyn HirDatabase) -> Visibility { | ||
955 | let function_data = db.const_data(self.id); | ||
956 | let visibility = &function_data.visibility; | ||
957 | visibility.resolve(db.upcast(), &self.id.resolver(db.upcast())) | ||
958 | } | ||
959 | } | ||
960 | |||
961 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
962 | pub struct Static { | ||
963 | pub(crate) id: StaticId, | ||
964 | } | ||
965 | |||
966 | impl Static { | ||
967 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
968 | Module { id: self.id.lookup(db.upcast()).module(db.upcast()) } | ||
969 | } | ||
970 | |||
971 | pub fn krate(self, db: &dyn HirDatabase) -> Option<Crate> { | ||
972 | Some(self.module(db).krate()) | ||
973 | } | ||
974 | |||
975 | pub fn name(self, db: &dyn HirDatabase) -> Option<Name> { | ||
976 | db.static_data(self.id).name.clone() | ||
977 | } | ||
978 | |||
979 | pub fn is_mut(self, db: &dyn HirDatabase) -> bool { | ||
980 | db.static_data(self.id).mutable | ||
981 | } | ||
982 | } | ||
983 | |||
984 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
985 | pub struct Trait { | ||
986 | pub(crate) id: TraitId, | ||
987 | } | ||
988 | |||
989 | impl Trait { | ||
990 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
991 | Module { id: self.id.lookup(db.upcast()).container } | ||
992 | } | ||
993 | |||
994 | pub fn name(self, db: &dyn HirDatabase) -> Name { | ||
995 | db.trait_data(self.id).name.clone() | ||
996 | } | ||
997 | |||
998 | pub fn items(self, db: &dyn HirDatabase) -> Vec<AssocItem> { | ||
999 | db.trait_data(self.id).items.iter().map(|(_name, it)| (*it).into()).collect() | ||
1000 | } | ||
1001 | |||
1002 | pub fn is_auto(self, db: &dyn HirDatabase) -> bool { | ||
1003 | db.trait_data(self.id).auto | ||
1004 | } | ||
1005 | } | ||
1006 | |||
1007 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
1008 | pub struct TypeAlias { | ||
1009 | pub(crate) id: TypeAliasId, | ||
1010 | } | ||
1011 | |||
1012 | impl TypeAlias { | ||
1013 | pub fn has_non_default_type_params(self, db: &dyn HirDatabase) -> bool { | ||
1014 | let subst = db.generic_defaults(self.id.into()); | ||
1015 | subst.iter().any(|ty| &ty.value == &Ty::Unknown) | ||
1016 | } | ||
1017 | |||
1018 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
1019 | Module { id: self.id.lookup(db.upcast()).module(db.upcast()) } | ||
1020 | } | ||
1021 | |||
1022 | pub fn krate(self, db: &dyn HirDatabase) -> Option<Crate> { | ||
1023 | Some(self.module(db).krate()) | ||
1024 | } | ||
1025 | |||
1026 | pub fn type_ref(self, db: &dyn HirDatabase) -> Option<TypeRef> { | ||
1027 | db.type_alias_data(self.id).type_ref.clone() | ||
1028 | } | ||
1029 | |||
1030 | pub fn ty(self, db: &dyn HirDatabase) -> Type { | ||
1031 | Type::from_def(db, self.id.lookup(db.upcast()).module(db.upcast()).krate(), self.id) | ||
1032 | } | ||
1033 | |||
1034 | pub fn name(self, db: &dyn HirDatabase) -> Name { | ||
1035 | db.type_alias_data(self.id).name.clone() | ||
1036 | } | ||
1037 | } | ||
1038 | |||
1039 | impl HasVisibility for TypeAlias { | ||
1040 | fn visibility(&self, db: &dyn HirDatabase) -> Visibility { | ||
1041 | let function_data = db.type_alias_data(self.id); | ||
1042 | let visibility = &function_data.visibility; | ||
1043 | visibility.resolve(db.upcast(), &self.id.resolver(db.upcast())) | ||
1044 | } | ||
1045 | } | ||
1046 | |||
1047 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
1048 | pub struct BuiltinType { | ||
1049 | pub(crate) inner: hir_def::builtin_type::BuiltinType, | ||
1050 | } | ||
1051 | |||
1052 | impl BuiltinType { | ||
1053 | pub fn ty(self, db: &dyn HirDatabase, module: Module) -> Type { | ||
1054 | let resolver = module.id.resolver(db.upcast()); | ||
1055 | Type::new_with_resolver(db, &resolver, Ty::builtin(self.inner)) | ||
1056 | .expect("crate not present in resolver") | ||
1057 | } | ||
1058 | |||
1059 | pub fn name(self) -> Name { | ||
1060 | self.inner.as_name() | ||
1061 | } | ||
1062 | } | ||
1063 | |||
1064 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
1065 | pub struct MacroDef { | ||
1066 | pub(crate) id: MacroDefId, | ||
1067 | } | ||
1068 | |||
1069 | impl MacroDef { | ||
1070 | /// FIXME: right now, this just returns the root module of the crate that | ||
1071 | /// defines this macro. The reasons for this is that macros are expanded | ||
1072 | /// early, in `hir_expand`, where modules simply do not exist yet. | ||
1073 | pub fn module(self, db: &dyn HirDatabase) -> Option<Module> { | ||
1074 | let krate = self.id.krate; | ||
1075 | let def_map = db.crate_def_map(krate); | ||
1076 | let module_id = def_map.root(); | ||
1077 | Some(Module { id: def_map.module_id(module_id) }) | ||
1078 | } | ||
1079 | |||
1080 | /// XXX: this parses the file | ||
1081 | pub fn name(self, db: &dyn HirDatabase) -> Option<Name> { | ||
1082 | self.source(db)?.value.name().map(|it| it.as_name()) | ||
1083 | } | ||
1084 | |||
1085 | /// Indicate it is a proc-macro | ||
1086 | pub fn is_proc_macro(&self) -> bool { | ||
1087 | matches!(self.id.kind, MacroDefKind::ProcMacro(_)) | ||
1088 | } | ||
1089 | |||
1090 | /// Indicate it is a derive macro | ||
1091 | pub fn is_derive_macro(&self) -> bool { | ||
1092 | matches!(self.id.kind, MacroDefKind::ProcMacro(_) | MacroDefKind::BuiltInDerive(_)) | ||
1093 | } | ||
1094 | } | ||
1095 | |||
1096 | /// Invariant: `inner.as_assoc_item(db).is_some()` | ||
1097 | /// We do not actively enforce this invariant. | ||
1098 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] | ||
1099 | pub enum AssocItem { | ||
1100 | Function(Function), | ||
1101 | Const(Const), | ||
1102 | TypeAlias(TypeAlias), | ||
1103 | } | ||
1104 | #[derive(Debug)] | ||
1105 | pub enum AssocItemContainer { | ||
1106 | Trait(Trait), | ||
1107 | Impl(Impl), | ||
1108 | } | ||
1109 | pub trait AsAssocItem { | ||
1110 | fn as_assoc_item(self, db: &dyn HirDatabase) -> Option<AssocItem>; | ||
1111 | } | ||
1112 | |||
1113 | impl AsAssocItem for Function { | ||
1114 | fn as_assoc_item(self, db: &dyn HirDatabase) -> Option<AssocItem> { | ||
1115 | as_assoc_item(db, AssocItem::Function, self.id) | ||
1116 | } | ||
1117 | } | ||
1118 | impl AsAssocItem for Const { | ||
1119 | fn as_assoc_item(self, db: &dyn HirDatabase) -> Option<AssocItem> { | ||
1120 | as_assoc_item(db, AssocItem::Const, self.id) | ||
1121 | } | ||
1122 | } | ||
1123 | impl AsAssocItem for TypeAlias { | ||
1124 | fn as_assoc_item(self, db: &dyn HirDatabase) -> Option<AssocItem> { | ||
1125 | as_assoc_item(db, AssocItem::TypeAlias, self.id) | ||
1126 | } | ||
1127 | } | ||
1128 | impl AsAssocItem for ModuleDef { | ||
1129 | fn as_assoc_item(self, db: &dyn HirDatabase) -> Option<AssocItem> { | ||
1130 | match self { | ||
1131 | ModuleDef::Function(it) => it.as_assoc_item(db), | ||
1132 | ModuleDef::Const(it) => it.as_assoc_item(db), | ||
1133 | ModuleDef::TypeAlias(it) => it.as_assoc_item(db), | ||
1134 | _ => None, | ||
1135 | } | ||
1136 | } | ||
1137 | } | ||
1138 | fn as_assoc_item<ID, DEF, CTOR, AST>(db: &dyn HirDatabase, ctor: CTOR, id: ID) -> Option<AssocItem> | ||
1139 | where | ||
1140 | ID: Lookup<Data = AssocItemLoc<AST>>, | ||
1141 | DEF: From<ID>, | ||
1142 | CTOR: FnOnce(DEF) -> AssocItem, | ||
1143 | AST: ItemTreeNode, | ||
1144 | { | ||
1145 | match id.lookup(db.upcast()).container { | ||
1146 | AssocContainerId::TraitId(_) | AssocContainerId::ImplId(_) => Some(ctor(DEF::from(id))), | ||
1147 | AssocContainerId::ModuleId(_) => None, | ||
1148 | } | ||
1149 | } | ||
1150 | |||
1151 | impl AssocItem { | ||
1152 | pub fn name(self, db: &dyn HirDatabase) -> Option<Name> { | ||
1153 | match self { | ||
1154 | AssocItem::Function(it) => Some(it.name(db)), | ||
1155 | AssocItem::Const(it) => it.name(db), | ||
1156 | AssocItem::TypeAlias(it) => Some(it.name(db)), | ||
1157 | } | ||
1158 | } | ||
1159 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
1160 | match self { | ||
1161 | AssocItem::Function(f) => f.module(db), | ||
1162 | AssocItem::Const(c) => c.module(db), | ||
1163 | AssocItem::TypeAlias(t) => t.module(db), | ||
1164 | } | ||
1165 | } | ||
1166 | pub fn container(self, db: &dyn HirDatabase) -> AssocItemContainer { | ||
1167 | let container = match self { | ||
1168 | AssocItem::Function(it) => it.id.lookup(db.upcast()).container, | ||
1169 | AssocItem::Const(it) => it.id.lookup(db.upcast()).container, | ||
1170 | AssocItem::TypeAlias(it) => it.id.lookup(db.upcast()).container, | ||
1171 | }; | ||
1172 | match container { | ||
1173 | AssocContainerId::TraitId(id) => AssocItemContainer::Trait(id.into()), | ||
1174 | AssocContainerId::ImplId(id) => AssocItemContainer::Impl(id.into()), | ||
1175 | AssocContainerId::ModuleId(_) => panic!("invalid AssocItem"), | ||
1176 | } | ||
1177 | } | ||
1178 | |||
1179 | pub fn containing_trait(self, db: &dyn HirDatabase) -> Option<Trait> { | ||
1180 | match self.container(db) { | ||
1181 | AssocItemContainer::Trait(t) => Some(t), | ||
1182 | _ => None, | ||
1183 | } | ||
1184 | } | ||
1185 | } | ||
1186 | |||
1187 | impl HasVisibility for AssocItem { | ||
1188 | fn visibility(&self, db: &dyn HirDatabase) -> Visibility { | ||
1189 | match self { | ||
1190 | AssocItem::Function(f) => f.visibility(db), | ||
1191 | AssocItem::Const(c) => c.visibility(db), | ||
1192 | AssocItem::TypeAlias(t) => t.visibility(db), | ||
1193 | } | ||
1194 | } | ||
1195 | } | ||
1196 | |||
1197 | #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] | ||
1198 | pub enum GenericDef { | ||
1199 | Function(Function), | ||
1200 | Adt(Adt), | ||
1201 | Trait(Trait), | ||
1202 | TypeAlias(TypeAlias), | ||
1203 | Impl(Impl), | ||
1204 | // enum variants cannot have generics themselves, but their parent enums | ||
1205 | // can, and this makes some code easier to write | ||
1206 | Variant(Variant), | ||
1207 | // consts can have type parameters from their parents (i.e. associated consts of traits) | ||
1208 | Const(Const), | ||
1209 | } | ||
1210 | impl_from!( | ||
1211 | Function, | ||
1212 | Adt(Struct, Enum, Union), | ||
1213 | Trait, | ||
1214 | TypeAlias, | ||
1215 | Impl, | ||
1216 | Variant, | ||
1217 | Const | ||
1218 | for GenericDef | ||
1219 | ); | ||
1220 | |||
1221 | impl GenericDef { | ||
1222 | pub fn params(self, db: &dyn HirDatabase) -> Vec<GenericParam> { | ||
1223 | let generics = db.generic_params(self.into()); | ||
1224 | let ty_params = generics | ||
1225 | .types | ||
1226 | .iter() | ||
1227 | .map(|(local_id, _)| TypeParam { id: TypeParamId { parent: self.into(), local_id } }) | ||
1228 | .map(GenericParam::TypeParam); | ||
1229 | let lt_params = generics | ||
1230 | .lifetimes | ||
1231 | .iter() | ||
1232 | .map(|(local_id, _)| LifetimeParam { | ||
1233 | id: LifetimeParamId { parent: self.into(), local_id }, | ||
1234 | }) | ||
1235 | .map(GenericParam::LifetimeParam); | ||
1236 | let const_params = generics | ||
1237 | .consts | ||
1238 | .iter() | ||
1239 | .map(|(local_id, _)| ConstParam { id: ConstParamId { parent: self.into(), local_id } }) | ||
1240 | .map(GenericParam::ConstParam); | ||
1241 | ty_params.chain(lt_params).chain(const_params).collect() | ||
1242 | } | ||
1243 | |||
1244 | pub fn type_params(self, db: &dyn HirDatabase) -> Vec<TypeParam> { | ||
1245 | let generics = db.generic_params(self.into()); | ||
1246 | generics | ||
1247 | .types | ||
1248 | .iter() | ||
1249 | .map(|(local_id, _)| TypeParam { id: TypeParamId { parent: self.into(), local_id } }) | ||
1250 | .collect() | ||
1251 | } | ||
1252 | } | ||
1253 | |||
1254 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] | ||
1255 | pub struct Local { | ||
1256 | pub(crate) parent: DefWithBodyId, | ||
1257 | pub(crate) pat_id: PatId, | ||
1258 | } | ||
1259 | |||
1260 | impl Local { | ||
1261 | pub fn is_param(self, db: &dyn HirDatabase) -> bool { | ||
1262 | let src = self.source(db); | ||
1263 | match src.value { | ||
1264 | Either::Left(bind_pat) => { | ||
1265 | bind_pat.syntax().ancestors().any(|it| ast::Param::can_cast(it.kind())) | ||
1266 | } | ||
1267 | Either::Right(_self_param) => true, | ||
1268 | } | ||
1269 | } | ||
1270 | |||
1271 | // FIXME: why is this an option? It shouldn't be? | ||
1272 | pub fn name(self, db: &dyn HirDatabase) -> Option<Name> { | ||
1273 | let body = db.body(self.parent.into()); | ||
1274 | match &body[self.pat_id] { | ||
1275 | Pat::Bind { name, .. } => Some(name.clone()), | ||
1276 | _ => None, | ||
1277 | } | ||
1278 | } | ||
1279 | |||
1280 | pub fn is_self(self, db: &dyn HirDatabase) -> bool { | ||
1281 | self.name(db) == Some(name![self]) | ||
1282 | } | ||
1283 | |||
1284 | pub fn is_mut(self, db: &dyn HirDatabase) -> bool { | ||
1285 | let body = db.body(self.parent.into()); | ||
1286 | matches!(&body[self.pat_id], Pat::Bind { mode: BindingAnnotation::Mutable, .. }) | ||
1287 | } | ||
1288 | |||
1289 | pub fn parent(self, _db: &dyn HirDatabase) -> DefWithBody { | ||
1290 | self.parent.into() | ||
1291 | } | ||
1292 | |||
1293 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
1294 | self.parent(db).module(db) | ||
1295 | } | ||
1296 | |||
1297 | pub fn ty(self, db: &dyn HirDatabase) -> Type { | ||
1298 | let def = DefWithBodyId::from(self.parent); | ||
1299 | let infer = db.infer(def); | ||
1300 | let ty = infer[self.pat_id].clone(); | ||
1301 | let krate = def.module(db.upcast()).krate(); | ||
1302 | Type::new(db, krate, def, ty) | ||
1303 | } | ||
1304 | |||
1305 | pub fn source(self, db: &dyn HirDatabase) -> InFile<Either<ast::IdentPat, ast::SelfParam>> { | ||
1306 | let (_body, source_map) = db.body_with_source_map(self.parent.into()); | ||
1307 | let src = source_map.pat_syntax(self.pat_id).unwrap(); // Hmm... | ||
1308 | let root = src.file_syntax(db.upcast()); | ||
1309 | src.map(|ast| { | ||
1310 | ast.map_left(|it| it.cast().unwrap().to_node(&root)).map_right(|it| it.to_node(&root)) | ||
1311 | }) | ||
1312 | } | ||
1313 | } | ||
1314 | |||
1315 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] | ||
1316 | pub struct Label { | ||
1317 | pub(crate) parent: DefWithBodyId, | ||
1318 | pub(crate) label_id: LabelId, | ||
1319 | } | ||
1320 | |||
1321 | impl Label { | ||
1322 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
1323 | self.parent(db).module(db) | ||
1324 | } | ||
1325 | |||
1326 | pub fn parent(self, _db: &dyn HirDatabase) -> DefWithBody { | ||
1327 | self.parent.into() | ||
1328 | } | ||
1329 | |||
1330 | pub fn name(self, db: &dyn HirDatabase) -> Name { | ||
1331 | let body = db.body(self.parent.into()); | ||
1332 | body[self.label_id].name.clone() | ||
1333 | } | ||
1334 | |||
1335 | pub fn source(self, db: &dyn HirDatabase) -> InFile<ast::Label> { | ||
1336 | let (_body, source_map) = db.body_with_source_map(self.parent.into()); | ||
1337 | let src = source_map.label_syntax(self.label_id); | ||
1338 | let root = src.file_syntax(db.upcast()); | ||
1339 | src.map(|ast| ast.to_node(&root)) | ||
1340 | } | ||
1341 | } | ||
1342 | |||
1343 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] | ||
1344 | pub enum GenericParam { | ||
1345 | TypeParam(TypeParam), | ||
1346 | LifetimeParam(LifetimeParam), | ||
1347 | ConstParam(ConstParam), | ||
1348 | } | ||
1349 | impl_from!(TypeParam, LifetimeParam, ConstParam for GenericParam); | ||
1350 | |||
1351 | impl GenericParam { | ||
1352 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
1353 | match self { | ||
1354 | GenericParam::TypeParam(it) => it.module(db), | ||
1355 | GenericParam::LifetimeParam(it) => it.module(db), | ||
1356 | GenericParam::ConstParam(it) => it.module(db), | ||
1357 | } | ||
1358 | } | ||
1359 | |||
1360 | pub fn name(self, db: &dyn HirDatabase) -> Name { | ||
1361 | match self { | ||
1362 | GenericParam::TypeParam(it) => it.name(db), | ||
1363 | GenericParam::LifetimeParam(it) => it.name(db), | ||
1364 | GenericParam::ConstParam(it) => it.name(db), | ||
1365 | } | ||
1366 | } | ||
1367 | } | ||
1368 | |||
1369 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] | ||
1370 | pub struct TypeParam { | ||
1371 | pub(crate) id: TypeParamId, | ||
1372 | } | ||
1373 | |||
1374 | impl TypeParam { | ||
1375 | pub fn name(self, db: &dyn HirDatabase) -> Name { | ||
1376 | let params = db.generic_params(self.id.parent); | ||
1377 | params.types[self.id.local_id].name.clone().unwrap_or_else(Name::missing) | ||
1378 | } | ||
1379 | |||
1380 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
1381 | self.id.parent.module(db.upcast()).into() | ||
1382 | } | ||
1383 | |||
1384 | pub fn ty(self, db: &dyn HirDatabase) -> Type { | ||
1385 | let resolver = self.id.parent.resolver(db.upcast()); | ||
1386 | let krate = self.id.parent.module(db.upcast()).krate(); | ||
1387 | let ty = Ty::Placeholder(self.id); | ||
1388 | Type::new_with_resolver_inner(db, krate, &resolver, ty) | ||
1389 | } | ||
1390 | |||
1391 | pub fn trait_bounds(self, db: &dyn HirDatabase) -> Vec<Trait> { | ||
1392 | db.generic_predicates_for_param(self.id) | ||
1393 | .into_iter() | ||
1394 | .filter_map(|pred| match &pred.value { | ||
1395 | hir_ty::GenericPredicate::Implemented(trait_ref) => { | ||
1396 | Some(Trait::from(trait_ref.trait_)) | ||
1397 | } | ||
1398 | _ => None, | ||
1399 | }) | ||
1400 | .collect() | ||
1401 | } | ||
1402 | |||
1403 | pub fn default(self, db: &dyn HirDatabase) -> Option<Type> { | ||
1404 | let params = db.generic_defaults(self.id.parent); | ||
1405 | let local_idx = hir_ty::param_idx(db, self.id)?; | ||
1406 | let resolver = self.id.parent.resolver(db.upcast()); | ||
1407 | let krate = self.id.parent.module(db.upcast()).krate(); | ||
1408 | let ty = params.get(local_idx)?.clone(); | ||
1409 | let subst = Substs::type_params(db, self.id.parent); | ||
1410 | let ty = ty.subst(&subst.prefix(local_idx)); | ||
1411 | Some(Type::new_with_resolver_inner(db, krate, &resolver, ty)) | ||
1412 | } | ||
1413 | } | ||
1414 | |||
1415 | impl HirDisplay for TypeParam { | ||
1416 | fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { | ||
1417 | write!(f, "{}", self.name(f.db))?; | ||
1418 | let bounds = f.db.generic_predicates_for_param(self.id); | ||
1419 | let substs = Substs::type_params(f.db, self.id.parent); | ||
1420 | let predicates = bounds.iter().cloned().map(|b| b.subst(&substs)).collect::<Vec<_>>(); | ||
1421 | if !(predicates.is_empty() || f.omit_verbose_types()) { | ||
1422 | write_bounds_like_dyn_trait_with_prefix(":", &predicates, f)?; | ||
1423 | } | ||
1424 | Ok(()) | ||
1425 | } | ||
1426 | } | ||
1427 | |||
1428 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] | ||
1429 | pub struct LifetimeParam { | ||
1430 | pub(crate) id: LifetimeParamId, | ||
1431 | } | ||
1432 | |||
1433 | impl LifetimeParam { | ||
1434 | pub fn name(self, db: &dyn HirDatabase) -> Name { | ||
1435 | let params = db.generic_params(self.id.parent); | ||
1436 | params.lifetimes[self.id.local_id].name.clone() | ||
1437 | } | ||
1438 | |||
1439 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
1440 | self.id.parent.module(db.upcast()).into() | ||
1441 | } | ||
1442 | |||
1443 | pub fn parent(self, _db: &dyn HirDatabase) -> GenericDef { | ||
1444 | self.id.parent.into() | ||
1445 | } | ||
1446 | } | ||
1447 | |||
1448 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] | ||
1449 | pub struct ConstParam { | ||
1450 | pub(crate) id: ConstParamId, | ||
1451 | } | ||
1452 | |||
1453 | impl ConstParam { | ||
1454 | pub fn name(self, db: &dyn HirDatabase) -> Name { | ||
1455 | let params = db.generic_params(self.id.parent); | ||
1456 | params.consts[self.id.local_id].name.clone() | ||
1457 | } | ||
1458 | |||
1459 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
1460 | self.id.parent.module(db.upcast()).into() | ||
1461 | } | ||
1462 | |||
1463 | pub fn parent(self, _db: &dyn HirDatabase) -> GenericDef { | ||
1464 | self.id.parent.into() | ||
1465 | } | ||
1466 | |||
1467 | pub fn ty(self, db: &dyn HirDatabase) -> Type { | ||
1468 | let def = self.id.parent; | ||
1469 | let krate = def.module(db.upcast()).krate(); | ||
1470 | Type::new(db, krate, def, db.const_param_ty(self.id)) | ||
1471 | } | ||
1472 | } | ||
1473 | |||
1474 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
1475 | pub struct Impl { | ||
1476 | pub(crate) id: ImplId, | ||
1477 | } | ||
1478 | |||
1479 | impl Impl { | ||
1480 | pub fn all_in_crate(db: &dyn HirDatabase, krate: Crate) -> Vec<Impl> { | ||
1481 | let inherent = db.inherent_impls_in_crate(krate.id); | ||
1482 | let trait_ = db.trait_impls_in_crate(krate.id); | ||
1483 | |||
1484 | inherent.all_impls().chain(trait_.all_impls()).map(Self::from).collect() | ||
1485 | } | ||
1486 | pub fn for_trait(db: &dyn HirDatabase, krate: Crate, trait_: Trait) -> Vec<Impl> { | ||
1487 | let impls = db.trait_impls_in_crate(krate.id); | ||
1488 | impls.for_trait(trait_.id).map(Self::from).collect() | ||
1489 | } | ||
1490 | |||
1491 | // FIXME: the return type is wrong. This should be a hir version of | ||
1492 | // `TraitRef` (ie, resolved `TypeRef`). | ||
1493 | pub fn target_trait(self, db: &dyn HirDatabase) -> Option<TypeRef> { | ||
1494 | db.impl_data(self.id).target_trait.clone() | ||
1495 | } | ||
1496 | |||
1497 | pub fn target_ty(self, db: &dyn HirDatabase) -> Type { | ||
1498 | let impl_data = db.impl_data(self.id); | ||
1499 | let resolver = self.id.resolver(db.upcast()); | ||
1500 | let krate = self.id.lookup(db.upcast()).container.krate(); | ||
1501 | let ctx = hir_ty::TyLoweringContext::new(db, &resolver); | ||
1502 | let ty = Ty::from_hir(&ctx, &impl_data.target_type); | ||
1503 | Type::new_with_resolver_inner(db, krate, &resolver, ty) | ||
1504 | } | ||
1505 | |||
1506 | pub fn items(self, db: &dyn HirDatabase) -> Vec<AssocItem> { | ||
1507 | db.impl_data(self.id).items.iter().map(|it| (*it).into()).collect() | ||
1508 | } | ||
1509 | |||
1510 | pub fn is_negative(self, db: &dyn HirDatabase) -> bool { | ||
1511 | db.impl_data(self.id).is_negative | ||
1512 | } | ||
1513 | |||
1514 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
1515 | self.id.lookup(db.upcast()).container.into() | ||
1516 | } | ||
1517 | |||
1518 | pub fn krate(self, db: &dyn HirDatabase) -> Crate { | ||
1519 | Crate { id: self.module(db).id.krate() } | ||
1520 | } | ||
1521 | |||
1522 | pub fn is_builtin_derive(self, db: &dyn HirDatabase) -> Option<InFile<ast::Attr>> { | ||
1523 | let src = self.source(db)?; | ||
1524 | let item = src.file_id.is_builtin_derive(db.upcast())?; | ||
1525 | let hygenic = hir_expand::hygiene::Hygiene::new(db.upcast(), item.file_id); | ||
1526 | |||
1527 | // FIXME: handle `cfg_attr` | ||
1528 | let attr = item | ||
1529 | .value | ||
1530 | .attrs() | ||
1531 | .filter_map(|it| { | ||
1532 | let path = ModPath::from_src(it.path()?, &hygenic)?; | ||
1533 | if path.as_ident()?.to_string() == "derive" { | ||
1534 | Some(it) | ||
1535 | } else { | ||
1536 | None | ||
1537 | } | ||
1538 | }) | ||
1539 | .last()?; | ||
1540 | |||
1541 | Some(item.with_value(attr)) | ||
1542 | } | ||
1543 | } | ||
1544 | |||
1545 | #[derive(Clone, PartialEq, Eq, Debug)] | ||
1546 | pub struct Type { | ||
1547 | krate: CrateId, | ||
1548 | ty: InEnvironment<Ty>, | ||
1549 | } | ||
1550 | |||
1551 | impl Type { | ||
1552 | pub(crate) fn new_with_resolver( | ||
1553 | db: &dyn HirDatabase, | ||
1554 | resolver: &Resolver, | ||
1555 | ty: Ty, | ||
1556 | ) -> Option<Type> { | ||
1557 | let krate = resolver.krate()?; | ||
1558 | Some(Type::new_with_resolver_inner(db, krate, resolver, ty)) | ||
1559 | } | ||
1560 | pub(crate) fn new_with_resolver_inner( | ||
1561 | db: &dyn HirDatabase, | ||
1562 | krate: CrateId, | ||
1563 | resolver: &Resolver, | ||
1564 | ty: Ty, | ||
1565 | ) -> Type { | ||
1566 | let environment = TraitEnvironment::lower(db, &resolver); | ||
1567 | Type { krate, ty: InEnvironment { value: ty, environment } } | ||
1568 | } | ||
1569 | |||
1570 | fn new(db: &dyn HirDatabase, krate: CrateId, lexical_env: impl HasResolver, ty: Ty) -> Type { | ||
1571 | let resolver = lexical_env.resolver(db.upcast()); | ||
1572 | let environment = TraitEnvironment::lower(db, &resolver); | ||
1573 | Type { krate, ty: InEnvironment { value: ty, environment } } | ||
1574 | } | ||
1575 | |||
1576 | fn from_def( | ||
1577 | db: &dyn HirDatabase, | ||
1578 | krate: CrateId, | ||
1579 | def: impl HasResolver + Into<TyDefId> + Into<GenericDefId>, | ||
1580 | ) -> Type { | ||
1581 | let substs = Substs::build_for_def(db, def).fill_with_unknown().build(); | ||
1582 | let ty = db.ty(def.into()).subst(&substs); | ||
1583 | Type::new(db, krate, def, ty) | ||
1584 | } | ||
1585 | |||
1586 | pub fn is_unit(&self) -> bool { | ||
1587 | matches!(self.ty.value, Ty::Tuple(0, ..)) | ||
1588 | } | ||
1589 | pub fn is_bool(&self) -> bool { | ||
1590 | matches!(self.ty.value, Ty::Scalar(Scalar::Bool)) | ||
1591 | } | ||
1592 | |||
1593 | pub fn is_mutable_reference(&self) -> bool { | ||
1594 | matches!(self.ty.value, Ty::Ref(hir_ty::Mutability::Mut, ..)) | ||
1595 | } | ||
1596 | |||
1597 | pub fn remove_ref(&self) -> Option<Type> { | ||
1598 | match &self.ty.value { | ||
1599 | Ty::Ref(.., substs) => Some(self.derived(substs[0].clone())), | ||
1600 | _ => None, | ||
1601 | } | ||
1602 | } | ||
1603 | |||
1604 | pub fn is_unknown(&self) -> bool { | ||
1605 | matches!(self.ty.value, Ty::Unknown) | ||
1606 | } | ||
1607 | |||
1608 | /// Checks that particular type `ty` implements `std::future::Future`. | ||
1609 | /// This function is used in `.await` syntax completion. | ||
1610 | pub fn impls_future(&self, db: &dyn HirDatabase) -> bool { | ||
1611 | // No special case for the type of async block, since Chalk can figure it out. | ||
1612 | |||
1613 | let krate = self.krate; | ||
1614 | |||
1615 | let std_future_trait = | ||
1616 | db.lang_item(krate, "future_trait".into()).and_then(|it| it.as_trait()); | ||
1617 | let std_future_trait = match std_future_trait { | ||
1618 | Some(it) => it, | ||
1619 | None => return false, | ||
1620 | }; | ||
1621 | |||
1622 | let canonical_ty = Canonical { value: self.ty.value.clone(), kinds: Arc::new([]) }; | ||
1623 | method_resolution::implements_trait( | ||
1624 | &canonical_ty, | ||
1625 | db, | ||
1626 | self.ty.environment.clone(), | ||
1627 | krate, | ||
1628 | std_future_trait, | ||
1629 | ) | ||
1630 | } | ||
1631 | |||
1632 | /// Checks that particular type `ty` implements `std::ops::FnOnce`. | ||
1633 | /// | ||
1634 | /// This function can be used to check if a particular type is callable, since FnOnce is a | ||
1635 | /// supertrait of Fn and FnMut, so all callable types implements at least FnOnce. | ||
1636 | pub fn impls_fnonce(&self, db: &dyn HirDatabase) -> bool { | ||
1637 | let krate = self.krate; | ||
1638 | |||
1639 | let fnonce_trait = match FnTrait::FnOnce.get_id(db, krate) { | ||
1640 | Some(it) => it, | ||
1641 | None => return false, | ||
1642 | }; | ||
1643 | |||
1644 | let canonical_ty = Canonical { value: self.ty.value.clone(), kinds: Arc::new([]) }; | ||
1645 | method_resolution::implements_trait_unique( | ||
1646 | &canonical_ty, | ||
1647 | db, | ||
1648 | self.ty.environment.clone(), | ||
1649 | krate, | ||
1650 | fnonce_trait, | ||
1651 | ) | ||
1652 | } | ||
1653 | |||
1654 | pub fn impls_trait(&self, db: &dyn HirDatabase, trait_: Trait, args: &[Type]) -> bool { | ||
1655 | let trait_ref = hir_ty::TraitRef { | ||
1656 | trait_: trait_.id, | ||
1657 | substs: Substs::build_for_def(db, trait_.id) | ||
1658 | .push(self.ty.value.clone()) | ||
1659 | .fill(args.iter().map(|t| t.ty.value.clone())) | ||
1660 | .build(), | ||
1661 | }; | ||
1662 | |||
1663 | let goal = Canonical { | ||
1664 | value: hir_ty::InEnvironment::new( | ||
1665 | self.ty.environment.clone(), | ||
1666 | hir_ty::Obligation::Trait(trait_ref), | ||
1667 | ), | ||
1668 | kinds: Arc::new([]), | ||
1669 | }; | ||
1670 | |||
1671 | db.trait_solve(self.krate, goal).is_some() | ||
1672 | } | ||
1673 | |||
1674 | pub fn normalize_trait_assoc_type( | ||
1675 | &self, | ||
1676 | db: &dyn HirDatabase, | ||
1677 | trait_: Trait, | ||
1678 | args: &[Type], | ||
1679 | alias: TypeAlias, | ||
1680 | ) -> Option<Type> { | ||
1681 | let subst = Substs::build_for_def(db, trait_.id) | ||
1682 | .push(self.ty.value.clone()) | ||
1683 | .fill(args.iter().map(|t| t.ty.value.clone())) | ||
1684 | .build(); | ||
1685 | let predicate = ProjectionPredicate { | ||
1686 | projection_ty: ProjectionTy { associated_ty: alias.id, parameters: subst }, | ||
1687 | ty: Ty::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 0)), | ||
1688 | }; | ||
1689 | let goal = Canonical { | ||
1690 | value: InEnvironment::new( | ||
1691 | self.ty.environment.clone(), | ||
1692 | Obligation::Projection(predicate), | ||
1693 | ), | ||
1694 | kinds: Arc::new([TyVariableKind::General]), | ||
1695 | }; | ||
1696 | |||
1697 | match db.trait_solve(self.krate, goal)? { | ||
1698 | Solution::Unique(SolutionVariables(subst)) => { | ||
1699 | subst.value.first().map(|ty| self.derived(ty.clone())) | ||
1700 | } | ||
1701 | Solution::Ambig(_) => None, | ||
1702 | } | ||
1703 | } | ||
1704 | |||
1705 | pub fn is_copy(&self, db: &dyn HirDatabase) -> bool { | ||
1706 | let lang_item = db.lang_item(self.krate, SmolStr::new("copy")); | ||
1707 | let copy_trait = match lang_item { | ||
1708 | Some(LangItemTarget::TraitId(it)) => it, | ||
1709 | _ => return false, | ||
1710 | }; | ||
1711 | self.impls_trait(db, copy_trait.into(), &[]) | ||
1712 | } | ||
1713 | |||
1714 | pub fn as_callable(&self, db: &dyn HirDatabase) -> Option<Callable> { | ||
1715 | let def = match self.ty.value { | ||
1716 | Ty::FnDef(def, _) => Some(def), | ||
1717 | _ => None, | ||
1718 | }; | ||
1719 | |||
1720 | let sig = self.ty.value.callable_sig(db)?; | ||
1721 | Some(Callable { ty: self.clone(), sig, def, is_bound_method: false }) | ||
1722 | } | ||
1723 | |||
1724 | pub fn is_closure(&self) -> bool { | ||
1725 | matches!(&self.ty.value, Ty::Closure { .. }) | ||
1726 | } | ||
1727 | |||
1728 | pub fn is_fn(&self) -> bool { | ||
1729 | matches!(&self.ty.value, Ty::FnDef(..) | Ty::Function { .. }) | ||
1730 | } | ||
1731 | |||
1732 | pub fn is_packed(&self, db: &dyn HirDatabase) -> bool { | ||
1733 | let adt_id = match self.ty.value { | ||
1734 | Ty::Adt(hir_ty::AdtId(adt_id), ..) => adt_id, | ||
1735 | _ => return false, | ||
1736 | }; | ||
1737 | |||
1738 | let adt = adt_id.into(); | ||
1739 | match adt { | ||
1740 | Adt::Struct(s) => matches!(s.repr(db), Some(ReprKind::Packed)), | ||
1741 | _ => false, | ||
1742 | } | ||
1743 | } | ||
1744 | |||
1745 | pub fn is_raw_ptr(&self) -> bool { | ||
1746 | matches!(&self.ty.value, Ty::Raw(..)) | ||
1747 | } | ||
1748 | |||
1749 | pub fn contains_unknown(&self) -> bool { | ||
1750 | return go(&self.ty.value); | ||
1751 | |||
1752 | fn go(ty: &Ty) -> bool { | ||
1753 | match ty { | ||
1754 | Ty::Unknown => true, | ||
1755 | _ => ty.substs().map_or(false, |substs| substs.iter().any(go)), | ||
1756 | } | ||
1757 | } | ||
1758 | } | ||
1759 | |||
1760 | pub fn fields(&self, db: &dyn HirDatabase) -> Vec<(Field, Type)> { | ||
1761 | let (variant_id, substs) = match self.ty.value { | ||
1762 | Ty::Adt(hir_ty::AdtId(AdtId::StructId(s)), ref substs) => (s.into(), substs), | ||
1763 | Ty::Adt(hir_ty::AdtId(AdtId::UnionId(u)), ref substs) => (u.into(), substs), | ||
1764 | _ => return Vec::new(), | ||
1765 | }; | ||
1766 | |||
1767 | db.field_types(variant_id) | ||
1768 | .iter() | ||
1769 | .map(|(local_id, ty)| { | ||
1770 | let def = Field { parent: variant_id.into(), id: local_id }; | ||
1771 | let ty = ty.clone().subst(substs); | ||
1772 | (def, self.derived(ty)) | ||
1773 | }) | ||
1774 | .collect() | ||
1775 | } | ||
1776 | |||
1777 | pub fn tuple_fields(&self, _db: &dyn HirDatabase) -> Vec<Type> { | ||
1778 | if let Ty::Tuple(_, substs) = &self.ty.value { | ||
1779 | substs.iter().map(|ty| self.derived(ty.clone())).collect() | ||
1780 | } else { | ||
1781 | Vec::new() | ||
1782 | } | ||
1783 | } | ||
1784 | |||
1785 | pub fn autoderef<'a>(&'a self, db: &'a dyn HirDatabase) -> impl Iterator<Item = Type> + 'a { | ||
1786 | // There should be no inference vars in types passed here | ||
1787 | // FIXME check that? | ||
1788 | let canonical = Canonical { value: self.ty.value.clone(), kinds: Arc::new([]) }; | ||
1789 | let environment = self.ty.environment.clone(); | ||
1790 | let ty = InEnvironment { value: canonical, environment }; | ||
1791 | autoderef(db, Some(self.krate), ty) | ||
1792 | .map(|canonical| canonical.value) | ||
1793 | .map(move |ty| self.derived(ty)) | ||
1794 | } | ||
1795 | |||
1796 | // This would be nicer if it just returned an iterator, but that runs into | ||
1797 | // lifetime problems, because we need to borrow temp `CrateImplDefs`. | ||
1798 | pub fn iterate_assoc_items<T>( | ||
1799 | self, | ||
1800 | db: &dyn HirDatabase, | ||
1801 | krate: Crate, | ||
1802 | mut callback: impl FnMut(AssocItem) -> Option<T>, | ||
1803 | ) -> Option<T> { | ||
1804 | for krate in self.ty.value.def_crates(db, krate.id)? { | ||
1805 | let impls = db.inherent_impls_in_crate(krate); | ||
1806 | |||
1807 | for impl_def in impls.for_self_ty(&self.ty.value) { | ||
1808 | for &item in db.impl_data(*impl_def).items.iter() { | ||
1809 | if let Some(result) = callback(item.into()) { | ||
1810 | return Some(result); | ||
1811 | } | ||
1812 | } | ||
1813 | } | ||
1814 | } | ||
1815 | None | ||
1816 | } | ||
1817 | |||
1818 | pub fn type_parameters(&self) -> impl Iterator<Item = Type> + '_ { | ||
1819 | self.ty | ||
1820 | .value | ||
1821 | .strip_references() | ||
1822 | .substs() | ||
1823 | .into_iter() | ||
1824 | .flat_map(|substs| substs.iter()) | ||
1825 | .map(move |ty| self.derived(ty.clone())) | ||
1826 | } | ||
1827 | |||
1828 | pub fn iterate_method_candidates<T>( | ||
1829 | &self, | ||
1830 | db: &dyn HirDatabase, | ||
1831 | krate: Crate, | ||
1832 | traits_in_scope: &FxHashSet<TraitId>, | ||
1833 | name: Option<&Name>, | ||
1834 | mut callback: impl FnMut(&Ty, Function) -> Option<T>, | ||
1835 | ) -> Option<T> { | ||
1836 | // There should be no inference vars in types passed here | ||
1837 | // FIXME check that? | ||
1838 | // FIXME replace Unknown by bound vars here | ||
1839 | let canonical = Canonical { value: self.ty.value.clone(), kinds: Arc::new([]) }; | ||
1840 | |||
1841 | let env = self.ty.environment.clone(); | ||
1842 | let krate = krate.id; | ||
1843 | |||
1844 | method_resolution::iterate_method_candidates( | ||
1845 | &canonical, | ||
1846 | db, | ||
1847 | env, | ||
1848 | krate, | ||
1849 | traits_in_scope, | ||
1850 | name, | ||
1851 | method_resolution::LookupMode::MethodCall, | ||
1852 | |ty, it| match it { | ||
1853 | AssocItemId::FunctionId(f) => callback(ty, f.into()), | ||
1854 | _ => None, | ||
1855 | }, | ||
1856 | ) | ||
1857 | } | ||
1858 | |||
1859 | pub fn iterate_path_candidates<T>( | ||
1860 | &self, | ||
1861 | db: &dyn HirDatabase, | ||
1862 | krate: Crate, | ||
1863 | traits_in_scope: &FxHashSet<TraitId>, | ||
1864 | name: Option<&Name>, | ||
1865 | mut callback: impl FnMut(&Ty, AssocItem) -> Option<T>, | ||
1866 | ) -> Option<T> { | ||
1867 | // There should be no inference vars in types passed here | ||
1868 | // FIXME check that? | ||
1869 | // FIXME replace Unknown by bound vars here | ||
1870 | let canonical = Canonical { value: self.ty.value.clone(), kinds: Arc::new([]) }; | ||
1871 | |||
1872 | let env = self.ty.environment.clone(); | ||
1873 | let krate = krate.id; | ||
1874 | |||
1875 | method_resolution::iterate_method_candidates( | ||
1876 | &canonical, | ||
1877 | db, | ||
1878 | env, | ||
1879 | krate, | ||
1880 | traits_in_scope, | ||
1881 | name, | ||
1882 | method_resolution::LookupMode::Path, | ||
1883 | |ty, it| callback(ty, it.into()), | ||
1884 | ) | ||
1885 | } | ||
1886 | |||
1887 | pub fn as_adt(&self) -> Option<Adt> { | ||
1888 | let (adt, _subst) = self.ty.value.as_adt()?; | ||
1889 | Some(adt.into()) | ||
1890 | } | ||
1891 | |||
1892 | pub fn as_dyn_trait(&self) -> Option<Trait> { | ||
1893 | self.ty.value.dyn_trait().map(Into::into) | ||
1894 | } | ||
1895 | |||
1896 | pub fn as_impl_traits(&self, db: &dyn HirDatabase) -> Option<Vec<Trait>> { | ||
1897 | self.ty.value.impl_trait_bounds(db).map(|it| { | ||
1898 | it.into_iter() | ||
1899 | .filter_map(|pred| match pred { | ||
1900 | hir_ty::GenericPredicate::Implemented(trait_ref) => { | ||
1901 | Some(Trait::from(trait_ref.trait_)) | ||
1902 | } | ||
1903 | _ => None, | ||
1904 | }) | ||
1905 | .collect() | ||
1906 | }) | ||
1907 | } | ||
1908 | |||
1909 | pub fn as_associated_type_parent_trait(&self, db: &dyn HirDatabase) -> Option<Trait> { | ||
1910 | self.ty.value.associated_type_parent_trait(db).map(Into::into) | ||
1911 | } | ||
1912 | |||
1913 | // FIXME: provide required accessors such that it becomes implementable from outside. | ||
1914 | pub fn is_equal_for_find_impls(&self, other: &Type) -> bool { | ||
1915 | let rref = other.remove_ref(); | ||
1916 | self.ty.value.equals_ctor(rref.as_ref().map_or(&other.ty.value, |it| &it.ty.value)) | ||
1917 | } | ||
1918 | |||
1919 | fn derived(&self, ty: Ty) -> Type { | ||
1920 | Type { | ||
1921 | krate: self.krate, | ||
1922 | ty: InEnvironment { value: ty, environment: self.ty.environment.clone() }, | ||
1923 | } | ||
1924 | } | ||
1925 | |||
1926 | pub fn walk(&self, db: &dyn HirDatabase, mut cb: impl FnMut(Type)) { | ||
1927 | // TypeWalk::walk for a Ty at first visits parameters and only after that the Ty itself. | ||
1928 | // We need a different order here. | ||
1929 | |||
1930 | fn walk_substs( | ||
1931 | db: &dyn HirDatabase, | ||
1932 | type_: &Type, | ||
1933 | substs: &Substs, | ||
1934 | cb: &mut impl FnMut(Type), | ||
1935 | ) { | ||
1936 | for ty in substs.iter() { | ||
1937 | walk_type(db, &type_.derived(ty.clone()), cb); | ||
1938 | } | ||
1939 | } | ||
1940 | |||
1941 | fn walk_bounds( | ||
1942 | db: &dyn HirDatabase, | ||
1943 | type_: &Type, | ||
1944 | bounds: &[GenericPredicate], | ||
1945 | cb: &mut impl FnMut(Type), | ||
1946 | ) { | ||
1947 | for pred in bounds { | ||
1948 | match pred { | ||
1949 | GenericPredicate::Implemented(trait_ref) => { | ||
1950 | cb(type_.clone()); | ||
1951 | walk_substs(db, type_, &trait_ref.substs, cb); | ||
1952 | } | ||
1953 | _ => (), | ||
1954 | } | ||
1955 | } | ||
1956 | } | ||
1957 | |||
1958 | fn walk_type(db: &dyn HirDatabase, type_: &Type, cb: &mut impl FnMut(Type)) { | ||
1959 | let ty = type_.ty.value.strip_references(); | ||
1960 | match ty { | ||
1961 | Ty::Adt(..) => { | ||
1962 | cb(type_.derived(ty.clone())); | ||
1963 | } | ||
1964 | Ty::AssociatedType(..) => { | ||
1965 | if let Some(_) = ty.associated_type_parent_trait(db) { | ||
1966 | cb(type_.derived(ty.clone())); | ||
1967 | } | ||
1968 | } | ||
1969 | Ty::OpaqueType(..) => { | ||
1970 | if let Some(bounds) = ty.impl_trait_bounds(db) { | ||
1971 | walk_bounds(db, &type_.derived(ty.clone()), &bounds, cb); | ||
1972 | } | ||
1973 | } | ||
1974 | Ty::Alias(AliasTy::Opaque(opaque_ty)) => { | ||
1975 | if let Some(bounds) = ty.impl_trait_bounds(db) { | ||
1976 | walk_bounds(db, &type_.derived(ty.clone()), &bounds, cb); | ||
1977 | } | ||
1978 | |||
1979 | walk_substs(db, type_, &opaque_ty.parameters, cb); | ||
1980 | } | ||
1981 | Ty::Placeholder(_) => { | ||
1982 | if let Some(bounds) = ty.impl_trait_bounds(db) { | ||
1983 | walk_bounds(db, &type_.derived(ty.clone()), &bounds, cb); | ||
1984 | } | ||
1985 | } | ||
1986 | Ty::Dyn(bounds) => { | ||
1987 | walk_bounds(db, &type_.derived(ty.clone()), bounds.as_ref(), cb); | ||
1988 | } | ||
1989 | |||
1990 | _ => {} | ||
1991 | } | ||
1992 | if let Some(substs) = ty.substs() { | ||
1993 | walk_substs(db, type_, &substs, cb); | ||
1994 | } | ||
1995 | } | ||
1996 | |||
1997 | walk_type(db, self, &mut cb); | ||
1998 | } | ||
1999 | } | ||
2000 | |||
2001 | impl HirDisplay for Type { | ||
2002 | fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { | ||
2003 | self.ty.value.hir_fmt(f) | ||
2004 | } | ||
2005 | } | ||
2006 | |||
2007 | // FIXME: closures | ||
2008 | #[derive(Debug)] | ||
2009 | pub struct Callable { | ||
2010 | ty: Type, | ||
2011 | sig: CallableSig, | ||
2012 | def: Option<CallableDefId>, | ||
2013 | pub(crate) is_bound_method: bool, | ||
2014 | } | ||
2015 | |||
2016 | pub enum CallableKind { | ||
2017 | Function(Function), | ||
2018 | TupleStruct(Struct), | ||
2019 | TupleEnumVariant(Variant), | ||
2020 | Closure, | ||
2021 | } | ||
2022 | |||
2023 | impl Callable { | ||
2024 | pub fn kind(&self) -> CallableKind { | ||
2025 | match self.def { | ||
2026 | Some(CallableDefId::FunctionId(it)) => CallableKind::Function(it.into()), | ||
2027 | Some(CallableDefId::StructId(it)) => CallableKind::TupleStruct(it.into()), | ||
2028 | Some(CallableDefId::EnumVariantId(it)) => CallableKind::TupleEnumVariant(it.into()), | ||
2029 | None => CallableKind::Closure, | ||
2030 | } | ||
2031 | } | ||
2032 | pub fn receiver_param(&self, db: &dyn HirDatabase) -> Option<ast::SelfParam> { | ||
2033 | let func = match self.def { | ||
2034 | Some(CallableDefId::FunctionId(it)) if self.is_bound_method => it, | ||
2035 | _ => return None, | ||
2036 | }; | ||
2037 | let src = func.lookup(db.upcast()).source(db.upcast()); | ||
2038 | let param_list = src.value.param_list()?; | ||
2039 | param_list.self_param() | ||
2040 | } | ||
2041 | pub fn n_params(&self) -> usize { | ||
2042 | self.sig.params().len() - if self.is_bound_method { 1 } else { 0 } | ||
2043 | } | ||
2044 | pub fn params( | ||
2045 | &self, | ||
2046 | db: &dyn HirDatabase, | ||
2047 | ) -> Vec<(Option<Either<ast::SelfParam, ast::Pat>>, Type)> { | ||
2048 | let types = self | ||
2049 | .sig | ||
2050 | .params() | ||
2051 | .iter() | ||
2052 | .skip(if self.is_bound_method { 1 } else { 0 }) | ||
2053 | .map(|ty| self.ty.derived(ty.clone())); | ||
2054 | let patterns = match self.def { | ||
2055 | Some(CallableDefId::FunctionId(func)) => { | ||
2056 | let src = func.lookup(db.upcast()).source(db.upcast()); | ||
2057 | src.value.param_list().map(|param_list| { | ||
2058 | param_list | ||
2059 | .self_param() | ||
2060 | .map(|it| Some(Either::Left(it))) | ||
2061 | .filter(|_| !self.is_bound_method) | ||
2062 | .into_iter() | ||
2063 | .chain(param_list.params().map(|it| it.pat().map(Either::Right))) | ||
2064 | }) | ||
2065 | } | ||
2066 | _ => None, | ||
2067 | }; | ||
2068 | patterns.into_iter().flatten().chain(iter::repeat(None)).zip(types).collect() | ||
2069 | } | ||
2070 | pub fn return_type(&self) -> Type { | ||
2071 | self.ty.derived(self.sig.ret().clone()) | ||
2072 | } | ||
2073 | } | ||
2074 | |||
2075 | /// For IDE only | ||
2076 | #[derive(Debug, PartialEq, Eq, Hash)] | ||
2077 | pub enum ScopeDef { | ||
2078 | ModuleDef(ModuleDef), | ||
2079 | MacroDef(MacroDef), | ||
2080 | GenericParam(GenericParam), | ||
2081 | ImplSelfType(Impl), | ||
2082 | AdtSelfType(Adt), | ||
2083 | Local(Local), | ||
2084 | Unknown, | ||
2085 | } | ||
2086 | |||
2087 | impl ScopeDef { | ||
2088 | pub fn all_items(def: PerNs) -> ArrayVec<[Self; 3]> { | ||
2089 | let mut items = ArrayVec::new(); | ||
2090 | |||
2091 | match (def.take_types(), def.take_values()) { | ||
2092 | (Some(m1), None) => items.push(ScopeDef::ModuleDef(m1.into())), | ||
2093 | (None, Some(m2)) => items.push(ScopeDef::ModuleDef(m2.into())), | ||
2094 | (Some(m1), Some(m2)) => { | ||
2095 | // Some items, like unit structs and enum variants, are | ||
2096 | // returned as both a type and a value. Here we want | ||
2097 | // to de-duplicate them. | ||
2098 | if m1 != m2 { | ||
2099 | items.push(ScopeDef::ModuleDef(m1.into())); | ||
2100 | items.push(ScopeDef::ModuleDef(m2.into())); | ||
2101 | } else { | ||
2102 | items.push(ScopeDef::ModuleDef(m1.into())); | ||
2103 | } | ||
2104 | } | ||
2105 | (None, None) => {} | ||
2106 | }; | ||
2107 | |||
2108 | if let Some(macro_def_id) = def.take_macros() { | ||
2109 | items.push(ScopeDef::MacroDef(macro_def_id.into())); | ||
2110 | } | ||
2111 | |||
2112 | if items.is_empty() { | ||
2113 | items.push(ScopeDef::Unknown); | ||
2114 | } | ||
2115 | |||
2116 | items | ||
2117 | } | ||
2118 | } | ||
2119 | |||
2120 | impl From<ItemInNs> for ScopeDef { | ||
2121 | fn from(item: ItemInNs) -> Self { | ||
2122 | match item { | ||
2123 | ItemInNs::Types(id) => ScopeDef::ModuleDef(id.into()), | ||
2124 | ItemInNs::Values(id) => ScopeDef::ModuleDef(id.into()), | ||
2125 | ItemInNs::Macros(id) => ScopeDef::MacroDef(id.into()), | ||
2126 | } | ||
2127 | } | ||
2128 | } | ||
2129 | |||
2130 | pub trait HasVisibility { | ||
2131 | fn visibility(&self, db: &dyn HirDatabase) -> Visibility; | ||
2132 | fn is_visible_from(&self, db: &dyn HirDatabase, module: Module) -> bool { | ||
2133 | let vis = self.visibility(db); | ||
2134 | vis.is_visible_from(db.upcast(), module.id) | ||
2135 | } | ||
2136 | } | ||
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index 144851f83..945638cc5 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs | |||
@@ -20,12 +20,11 @@ use syntax::{ | |||
20 | }; | 20 | }; |
21 | 21 | ||
22 | use crate::{ | 22 | use crate::{ |
23 | code_model::Access, | ||
24 | db::HirDatabase, | 23 | db::HirDatabase, |
25 | semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx}, | 24 | semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx}, |
26 | source_analyzer::{resolve_hir_path, SourceAnalyzer}, | 25 | source_analyzer::{resolve_hir_path, SourceAnalyzer}, |
27 | AssocItem, Callable, ConstParam, Crate, Field, Function, HirFileId, Impl, InFile, Label, | 26 | Access, AssocItem, Callable, ConstParam, Crate, Field, Function, HirFileId, Impl, InFile, |
28 | LifetimeParam, Local, MacroDef, Module, ModuleDef, Name, Path, ScopeDef, Trait, Type, | 27 | Label, LifetimeParam, Local, MacroDef, Module, ModuleDef, Name, Path, ScopeDef, Trait, Type, |
29 | TypeAlias, TypeParam, VariantDef, | 28 | TypeAlias, TypeParam, VariantDef, |
30 | }; | 29 | }; |
31 | 30 | ||
diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs index 64ce4add1..d546512cb 100644 --- a/crates/hir/src/source_analyzer.rs +++ b/crates/hir/src/source_analyzer.rs | |||
@@ -28,9 +28,8 @@ use syntax::{ | |||
28 | }; | 28 | }; |
29 | 29 | ||
30 | use crate::{ | 30 | use crate::{ |
31 | code_model::BuiltinType, db::HirDatabase, semantics::PathResolution, Adt, Const, Field, | 31 | db::HirDatabase, semantics::PathResolution, Adt, BuiltinType, Const, Field, Function, Local, |
32 | Function, Local, MacroDef, ModuleDef, Static, Struct, Trait, Type, TypeAlias, TypeParam, | 32 | MacroDef, ModuleDef, Static, Struct, Trait, Type, TypeAlias, TypeParam, Variant, |
33 | Variant, | ||
34 | }; | 33 | }; |
35 | use base_db::CrateId; | 34 | use base_db::CrateId; |
36 | 35 | ||
diff --git a/crates/hir_def/Cargo.toml b/crates/hir_def/Cargo.toml index 535221294..c2d99280f 100644 --- a/crates/hir_def/Cargo.toml +++ b/crates/hir_def/Cargo.toml | |||
@@ -10,6 +10,7 @@ edition = "2018" | |||
10 | doctest = false | 10 | doctest = false |
11 | 11 | ||
12 | [dependencies] | 12 | [dependencies] |
13 | cov-mark = "1.1" | ||
13 | log = "0.4.8" | 14 | log = "0.4.8" |
14 | once_cell = "1.3.1" | 15 | once_cell = "1.3.1" |
15 | rustc-hash = "1.1.0" | 16 | rustc-hash = "1.1.0" |
@@ -27,10 +28,10 @@ base_db = { path = "../base_db", version = "0.0.0" } | |||
27 | syntax = { path = "../syntax", version = "0.0.0" } | 28 | syntax = { path = "../syntax", version = "0.0.0" } |
28 | profile = { path = "../profile", version = "0.0.0" } | 29 | profile = { path = "../profile", version = "0.0.0" } |
29 | hir_expand = { path = "../hir_expand", version = "0.0.0" } | 30 | hir_expand = { path = "../hir_expand", version = "0.0.0" } |
30 | test_utils = { path = "../test_utils", version = "0.0.0" } | ||
31 | mbe = { path = "../mbe", version = "0.0.0" } | 31 | mbe = { path = "../mbe", version = "0.0.0" } |
32 | cfg = { path = "../cfg", version = "0.0.0" } | 32 | cfg = { path = "../cfg", version = "0.0.0" } |
33 | tt = { path = "../tt", version = "0.0.0" } | 33 | tt = { path = "../tt", version = "0.0.0" } |
34 | 34 | ||
35 | [dev-dependencies] | 35 | [dev-dependencies] |
36 | test_utils = { path = "../test_utils" } | ||
36 | expect-test = "1.1" | 37 | expect-test = "1.1" |
diff --git a/crates/hir_def/src/adt.rs b/crates/hir_def/src/adt.rs index ed36c3109..efbde17d8 100644 --- a/crates/hir_def/src/adt.rs +++ b/crates/hir_def/src/adt.rs | |||
@@ -21,8 +21,7 @@ use crate::{ | |||
21 | trace::Trace, | 21 | trace::Trace, |
22 | type_ref::TypeRef, | 22 | type_ref::TypeRef, |
23 | visibility::RawVisibility, | 23 | visibility::RawVisibility, |
24 | EnumId, HasModule, LocalEnumVariantId, LocalFieldId, Lookup, ModuleId, StructId, UnionId, | 24 | EnumId, LocalEnumVariantId, LocalFieldId, Lookup, ModuleId, StructId, UnionId, VariantId, |
25 | VariantId, | ||
26 | }; | 25 | }; |
27 | use cfg::CfgOptions; | 26 | use cfg::CfgOptions; |
28 | 27 | ||
@@ -92,10 +91,10 @@ fn parse_repr_tt(tt: &Subtree) -> Option<ReprKind> { | |||
92 | impl StructData { | 91 | impl StructData { |
93 | pub(crate) fn struct_data_query(db: &dyn DefDatabase, id: StructId) -> Arc<StructData> { | 92 | pub(crate) fn struct_data_query(db: &dyn DefDatabase, id: StructId) -> Arc<StructData> { |
94 | let loc = id.lookup(db); | 93 | let loc = id.lookup(db); |
95 | let krate = loc.container.module(db).krate; | 94 | let krate = loc.container.krate; |
96 | let item_tree = db.item_tree(loc.id.file_id); | 95 | let item_tree = db.item_tree(loc.id.file_id); |
97 | let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into()); | 96 | let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into()); |
98 | let cfg_options = db.crate_graph()[loc.container.module(db).krate].cfg_options.clone(); | 97 | let cfg_options = db.crate_graph()[loc.container.krate].cfg_options.clone(); |
99 | 98 | ||
100 | let strukt = &item_tree[loc.id.value]; | 99 | let strukt = &item_tree[loc.id.value]; |
101 | let variant_data = lower_fields(db, krate, &item_tree, &cfg_options, &strukt.fields, None); | 100 | let variant_data = lower_fields(db, krate, &item_tree, &cfg_options, &strukt.fields, None); |
@@ -107,10 +106,10 @@ impl StructData { | |||
107 | } | 106 | } |
108 | pub(crate) fn union_data_query(db: &dyn DefDatabase, id: UnionId) -> Arc<StructData> { | 107 | pub(crate) fn union_data_query(db: &dyn DefDatabase, id: UnionId) -> Arc<StructData> { |
109 | let loc = id.lookup(db); | 108 | let loc = id.lookup(db); |
110 | let krate = loc.container.module(db).krate; | 109 | let krate = loc.container.krate; |
111 | let item_tree = db.item_tree(loc.id.file_id); | 110 | let item_tree = db.item_tree(loc.id.file_id); |
112 | let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into()); | 111 | let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into()); |
113 | let cfg_options = db.crate_graph()[loc.container.module(db).krate].cfg_options.clone(); | 112 | let cfg_options = db.crate_graph()[loc.container.krate].cfg_options.clone(); |
114 | 113 | ||
115 | let union = &item_tree[loc.id.value]; | 114 | let union = &item_tree[loc.id.value]; |
116 | let variant_data = lower_fields(db, krate, &item_tree, &cfg_options, &union.fields, None); | 115 | let variant_data = lower_fields(db, krate, &item_tree, &cfg_options, &union.fields, None); |
@@ -126,7 +125,7 @@ impl StructData { | |||
126 | impl EnumData { | 125 | impl EnumData { |
127 | pub(crate) fn enum_data_query(db: &dyn DefDatabase, e: EnumId) -> Arc<EnumData> { | 126 | pub(crate) fn enum_data_query(db: &dyn DefDatabase, e: EnumId) -> Arc<EnumData> { |
128 | let loc = e.lookup(db); | 127 | let loc = e.lookup(db); |
129 | let krate = loc.container.module(db).krate; | 128 | let krate = loc.container.krate; |
130 | let item_tree = db.item_tree(loc.id.file_id); | 129 | let item_tree = db.item_tree(loc.id.file_id); |
131 | let cfg_options = db.crate_graph()[krate].cfg_options.clone(); | 130 | let cfg_options = db.crate_graph()[krate].cfg_options.clone(); |
132 | 131 | ||
@@ -168,7 +167,7 @@ impl HasChildSource<LocalEnumVariantId> for EnumId { | |||
168 | ) -> InFile<ArenaMap<LocalEnumVariantId, Self::Value>> { | 167 | ) -> InFile<ArenaMap<LocalEnumVariantId, Self::Value>> { |
169 | let src = self.lookup(db).source(db); | 168 | let src = self.lookup(db).source(db); |
170 | let mut trace = Trace::new_for_map(); | 169 | let mut trace = Trace::new_for_map(); |
171 | lower_enum(db, &mut trace, &src, self.lookup(db).container.module(db)); | 170 | lower_enum(db, &mut trace, &src, self.lookup(db).container); |
172 | src.with_value(trace.into_map()) | 171 | src.with_value(trace.into_map()) |
173 | } | 172 | } |
174 | } | 173 | } |
@@ -238,10 +237,10 @@ impl HasChildSource<LocalFieldId> for VariantId { | |||
238 | // I don't really like the fact that we call into parent source | 237 | // I don't really like the fact that we call into parent source |
239 | // here, this might add to more queries then necessary. | 238 | // here, this might add to more queries then necessary. |
240 | let src = it.parent.child_source(db); | 239 | let src = it.parent.child_source(db); |
241 | (src.map(|map| map[it.local_id].kind()), it.parent.lookup(db).container.module(db)) | 240 | (src.map(|map| map[it.local_id].kind()), it.parent.lookup(db).container) |
242 | } | 241 | } |
243 | VariantId::StructId(it) => { | 242 | VariantId::StructId(it) => { |
244 | (it.lookup(db).source(db).map(|it| it.kind()), it.lookup(db).container.module(db)) | 243 | (it.lookup(db).source(db).map(|it| it.kind()), it.lookup(db).container) |
245 | } | 244 | } |
246 | VariantId::UnionId(it) => ( | 245 | VariantId::UnionId(it) => ( |
247 | it.lookup(db).source(db).map(|it| { | 246 | it.lookup(db).source(db).map(|it| { |
@@ -249,7 +248,7 @@ impl HasChildSource<LocalFieldId> for VariantId { | |||
249 | .map(ast::StructKind::Record) | 248 | .map(ast::StructKind::Record) |
250 | .unwrap_or(ast::StructKind::Unit) | 249 | .unwrap_or(ast::StructKind::Unit) |
251 | }), | 250 | }), |
252 | it.lookup(db).container.module(db), | 251 | it.lookup(db).container, |
253 | ), | 252 | ), |
254 | }; | 253 | }; |
255 | let mut expander = CfgExpander::new(db, src.file_id, module_id.krate); | 254 | let mut expander = CfgExpander::new(db, src.file_id, module_id.krate); |
diff --git a/crates/hir_def/src/attr.rs b/crates/hir_def/src/attr.rs index 24ffa6c3a..97cdbbb9e 100644 --- a/crates/hir_def/src/attr.rs +++ b/crates/hir_def/src/attr.rs | |||
@@ -13,7 +13,6 @@ use syntax::{ | |||
13 | ast::{self, AstNode, AttrsOwner}, | 13 | ast::{self, AstNode, AttrsOwner}, |
14 | match_ast, AstToken, SmolStr, SyntaxNode, | 14 | match_ast, AstToken, SmolStr, SyntaxNode, |
15 | }; | 15 | }; |
16 | use test_utils::mark; | ||
17 | use tt::Subtree; | 16 | use tt::Subtree; |
18 | 17 | ||
19 | use crate::{ | 18 | use crate::{ |
@@ -177,7 +176,7 @@ impl RawAttrs { | |||
177 | if cfg_options.check(&cfg) == Some(false) { | 176 | if cfg_options.check(&cfg) == Some(false) { |
178 | None | 177 | None |
179 | } else { | 178 | } else { |
180 | mark::hit!(cfg_attr_active); | 179 | cov_mark::hit!(cfg_attr_active); |
181 | 180 | ||
182 | let attr = ast::Attr::parse(&format!("#[{}]", attr)).ok()?; | 181 | let attr = ast::Attr::parse(&format!("#[{}]", attr)).ok()?; |
183 | let hygiene = Hygiene::new_unhygienic(); // FIXME | 182 | let hygiene = Hygiene::new_unhygienic(); // FIXME |
@@ -268,7 +267,7 @@ impl Attrs { | |||
268 | db: &dyn DefDatabase, | 267 | db: &dyn DefDatabase, |
269 | e: EnumId, | 268 | e: EnumId, |
270 | ) -> Arc<ArenaMap<LocalEnumVariantId, Attrs>> { | 269 | ) -> Arc<ArenaMap<LocalEnumVariantId, Attrs>> { |
271 | let krate = e.lookup(db).container.module(db).krate; | 270 | let krate = e.lookup(db).container.krate; |
272 | let src = e.child_source(db); | 271 | let src = e.child_source(db); |
273 | let mut res = ArenaMap::default(); | 272 | let mut res = ArenaMap::default(); |
274 | 273 | ||
diff --git a/crates/hir_def/src/body.rs b/crates/hir_def/src/body.rs index 16e1bac40..19c4eb521 100644 --- a/crates/hir_def/src/body.rs +++ b/crates/hir_def/src/body.rs | |||
@@ -20,7 +20,6 @@ use la_arena::{Arena, ArenaMap}; | |||
20 | use profile::Count; | 20 | use profile::Count; |
21 | use rustc_hash::FxHashMap; | 21 | use rustc_hash::FxHashMap; |
22 | use syntax::{ast, AstNode, AstPtr}; | 22 | use syntax::{ast, AstNode, AstPtr}; |
23 | use test_utils::mark; | ||
24 | 23 | ||
25 | pub(crate) use lower::LowerCtx; | 24 | pub(crate) use lower::LowerCtx; |
26 | 25 | ||
@@ -29,11 +28,10 @@ use crate::{ | |||
29 | db::DefDatabase, | 28 | db::DefDatabase, |
30 | expr::{Expr, ExprId, Label, LabelId, Pat, PatId}, | 29 | expr::{Expr, ExprId, Label, LabelId, Pat, PatId}, |
31 | item_scope::BuiltinShadowMode, | 30 | item_scope::BuiltinShadowMode, |
32 | item_scope::ItemScope, | ||
33 | nameres::DefMap, | 31 | nameres::DefMap, |
34 | path::{ModPath, Path}, | 32 | path::{ModPath, Path}, |
35 | src::HasSource, | 33 | src::HasSource, |
36 | AsMacroCall, DefWithBodyId, HasModule, LocalModuleId, Lookup, ModuleId, | 34 | AsMacroCall, BlockId, DefWithBodyId, HasModule, LocalModuleId, Lookup, ModuleId, |
37 | }; | 35 | }; |
38 | 36 | ||
39 | /// A subset of Expander that only deals with cfg attributes. We only need it to | 37 | /// A subset of Expander that only deals with cfg attributes. We only need it to |
@@ -105,7 +103,7 @@ impl Expander { | |||
105 | macro_call: ast::MacroCall, | 103 | macro_call: ast::MacroCall, |
106 | ) -> ExpandResult<Option<(Mark, T)>> { | 104 | ) -> ExpandResult<Option<(Mark, T)>> { |
107 | if self.recursion_limit + 1 > EXPANSION_RECURSION_LIMIT { | 105 | if self.recursion_limit + 1 > EXPANSION_RECURSION_LIMIT { |
108 | mark::hit!(your_stack_belongs_to_me); | 106 | cov_mark::hit!(your_stack_belongs_to_me); |
109 | return ExpandResult::str_err("reached recursion limit during macro expansion".into()); | 107 | return ExpandResult::str_err("reached recursion limit during macro expansion".into()); |
110 | } | 108 | } |
111 | 109 | ||
@@ -227,7 +225,8 @@ pub struct Body { | |||
227 | pub params: Vec<PatId>, | 225 | pub params: Vec<PatId>, |
228 | /// The `ExprId` of the actual body expression. | 226 | /// The `ExprId` of the actual body expression. |
229 | pub body_expr: ExprId, | 227 | pub body_expr: ExprId, |
230 | pub item_scope: ItemScope, | 228 | /// Block expressions in this body that may contain inner items. |
229 | pub block_scopes: Vec<BlockId>, | ||
231 | _c: Count<Self>, | 230 | _c: Count<Self>, |
232 | } | 231 | } |
233 | 232 | ||
@@ -296,7 +295,7 @@ impl Body { | |||
296 | } | 295 | } |
297 | }; | 296 | }; |
298 | let expander = Expander::new(db, file_id, module); | 297 | let expander = Expander::new(db, file_id, module); |
299 | let (body, source_map) = Body::new(db, def, expander, params, body); | 298 | let (body, source_map) = Body::new(db, expander, params, body); |
300 | (Arc::new(body), Arc::new(source_map)) | 299 | (Arc::new(body), Arc::new(source_map)) |
301 | } | 300 | } |
302 | 301 | ||
@@ -306,12 +305,11 @@ impl Body { | |||
306 | 305 | ||
307 | fn new( | 306 | fn new( |
308 | db: &dyn DefDatabase, | 307 | db: &dyn DefDatabase, |
309 | def: DefWithBodyId, | ||
310 | expander: Expander, | 308 | expander: Expander, |
311 | params: Option<ast::ParamList>, | 309 | params: Option<ast::ParamList>, |
312 | body: Option<ast::Expr>, | 310 | body: Option<ast::Expr>, |
313 | ) -> (Body, BodySourceMap) { | 311 | ) -> (Body, BodySourceMap) { |
314 | lower::lower(db, def, expander, params, body) | 312 | lower::lower(db, expander, params, body) |
315 | } | 313 | } |
316 | } | 314 | } |
317 | 315 | ||
diff --git a/crates/hir_def/src/body/lower.rs b/crates/hir_def/src/body/lower.rs index 40beb2f7a..8c8eb8007 100644 --- a/crates/hir_def/src/body/lower.rs +++ b/crates/hir_def/src/body/lower.rs | |||
@@ -1,17 +1,16 @@ | |||
1 | //! Transforms `ast::Expr` into an equivalent `hir_def::expr::Expr` | 1 | //! Transforms `ast::Expr` into an equivalent `hir_def::expr::Expr` |
2 | //! representation. | 2 | //! representation. |
3 | 3 | ||
4 | use std::{any::type_name, mem, sync::Arc}; | 4 | use std::mem; |
5 | 5 | ||
6 | use either::Either; | 6 | use either::Either; |
7 | use hir_expand::{ | 7 | use hir_expand::{ |
8 | hygiene::Hygiene, | 8 | hygiene::Hygiene, |
9 | name::{name, AsName, Name}, | 9 | name::{name, AsName, Name}, |
10 | ExpandError, HirFileId, MacroDefId, MacroDefKind, | 10 | ExpandError, HirFileId, |
11 | }; | 11 | }; |
12 | use la_arena::Arena; | 12 | use la_arena::Arena; |
13 | use profile::Count; | 13 | use profile::Count; |
14 | use rustc_hash::FxHashMap; | ||
15 | use syntax::{ | 14 | use syntax::{ |
16 | ast::{ | 15 | ast::{ |
17 | self, ArgListOwner, ArrayExprKind, AstChildren, LiteralKind, LoopBodyOwner, NameOwner, | 16 | self, ArgListOwner, ArrayExprKind, AstChildren, LiteralKind, LoopBodyOwner, NameOwner, |
@@ -19,7 +18,6 @@ use syntax::{ | |||
19 | }, | 18 | }, |
20 | AstNode, AstPtr, SyntaxNodePtr, | 19 | AstNode, AstPtr, SyntaxNodePtr, |
21 | }; | 20 | }; |
22 | use test_utils::mark; | ||
23 | 21 | ||
24 | use crate::{ | 22 | use crate::{ |
25 | adt::StructKind, | 23 | adt::StructKind, |
@@ -33,11 +31,9 @@ use crate::{ | |||
33 | Statement, | 31 | Statement, |
34 | }, | 32 | }, |
35 | item_scope::BuiltinShadowMode, | 33 | item_scope::BuiltinShadowMode, |
36 | item_tree::{ItemTree, ItemTreeId, ItemTreeNode}, | ||
37 | path::{GenericArgs, Path}, | 34 | path::{GenericArgs, Path}, |
38 | type_ref::{Mutability, Rawness, TypeRef}, | 35 | type_ref::{Mutability, Rawness, TypeRef}, |
39 | AdtId, BlockLoc, ConstLoc, ContainerId, DefWithBodyId, EnumLoc, FunctionLoc, Intern, | 36 | AdtId, BlockLoc, ModuleDefId, |
40 | ModuleDefId, StaticLoc, StructLoc, TraitLoc, TypeAliasLoc, UnionLoc, | ||
41 | }; | 37 | }; |
42 | 38 | ||
43 | use super::{diagnostics::BodyDiagnostic, ExprSource, PatSource}; | 39 | use super::{diagnostics::BodyDiagnostic, ExprSource, PatSource}; |
@@ -61,15 +57,12 @@ impl LowerCtx { | |||
61 | 57 | ||
62 | pub(super) fn lower( | 58 | pub(super) fn lower( |
63 | db: &dyn DefDatabase, | 59 | db: &dyn DefDatabase, |
64 | def: DefWithBodyId, | ||
65 | expander: Expander, | 60 | expander: Expander, |
66 | params: Option<ast::ParamList>, | 61 | params: Option<ast::ParamList>, |
67 | body: Option<ast::Expr>, | 62 | body: Option<ast::Expr>, |
68 | ) -> (Body, BodySourceMap) { | 63 | ) -> (Body, BodySourceMap) { |
69 | let item_tree = db.item_tree(expander.current_file_id); | ||
70 | ExprCollector { | 64 | ExprCollector { |
71 | db, | 65 | db, |
72 | def, | ||
73 | source_map: BodySourceMap::default(), | 66 | source_map: BodySourceMap::default(), |
74 | body: Body { | 67 | body: Body { |
75 | exprs: Arena::default(), | 68 | exprs: Arena::default(), |
@@ -77,14 +70,9 @@ pub(super) fn lower( | |||
77 | labels: Arena::default(), | 70 | labels: Arena::default(), |
78 | params: Vec::new(), | 71 | params: Vec::new(), |
79 | body_expr: dummy_expr_id(), | 72 | body_expr: dummy_expr_id(), |
80 | item_scope: Default::default(), | 73 | block_scopes: Vec::new(), |
81 | _c: Count::new(), | 74 | _c: Count::new(), |
82 | }, | 75 | }, |
83 | item_trees: { | ||
84 | let mut map = FxHashMap::default(); | ||
85 | map.insert(expander.current_file_id, item_tree); | ||
86 | map | ||
87 | }, | ||
88 | expander, | 76 | expander, |
89 | } | 77 | } |
90 | .collect(params, body) | 78 | .collect(params, body) |
@@ -92,12 +80,9 @@ pub(super) fn lower( | |||
92 | 80 | ||
93 | struct ExprCollector<'a> { | 81 | struct ExprCollector<'a> { |
94 | db: &'a dyn DefDatabase, | 82 | db: &'a dyn DefDatabase, |
95 | def: DefWithBodyId, | ||
96 | expander: Expander, | 83 | expander: Expander, |
97 | body: Body, | 84 | body: Body, |
98 | source_map: BodySourceMap, | 85 | source_map: BodySourceMap, |
99 | |||
100 | item_trees: FxHashMap<HirFileId, Arc<ItemTree>>, | ||
101 | } | 86 | } |
102 | 87 | ||
103 | impl ExprCollector<'_> { | 88 | impl ExprCollector<'_> { |
@@ -286,7 +271,7 @@ impl ExprCollector<'_> { | |||
286 | None => self.collect_expr_opt(condition.expr()), | 271 | None => self.collect_expr_opt(condition.expr()), |
287 | // if let -- desugar to match | 272 | // if let -- desugar to match |
288 | Some(pat) => { | 273 | Some(pat) => { |
289 | mark::hit!(infer_resolve_while_let); | 274 | cov_mark::hit!(infer_resolve_while_let); |
290 | let pat = self.collect_pat(pat); | 275 | let pat = self.collect_pat(pat); |
291 | let match_expr = self.collect_expr_opt(condition.expr()); | 276 | let match_expr = self.collect_expr_opt(condition.expr()); |
292 | let placeholder_pat = self.missing_pat(); | 277 | let placeholder_pat = self.missing_pat(); |
@@ -594,9 +579,6 @@ impl ExprCollector<'_> { | |||
594 | } else { | 579 | } else { |
595 | self.source_map.expansions.insert(macro_call, self.expander.current_file_id); | 580 | self.source_map.expansions.insert(macro_call, self.expander.current_file_id); |
596 | 581 | ||
597 | let item_tree = self.db.item_tree(self.expander.current_file_id); | ||
598 | self.item_trees.insert(self.expander.current_file_id, item_tree); | ||
599 | |||
600 | let id = collector(self, Some(expansion)); | 582 | let id = collector(self, Some(expansion)); |
601 | self.expander.exit(self.db, mark); | 583 | self.expander.exit(self.db, mark); |
602 | id | 584 | id |
@@ -606,32 +588,6 @@ impl ExprCollector<'_> { | |||
606 | } | 588 | } |
607 | } | 589 | } |
608 | 590 | ||
609 | fn find_inner_item<N: ItemTreeNode>(&self, ast: &N::Source) -> Option<ItemTreeId<N>> { | ||
610 | let id = self.expander.ast_id(ast); | ||
611 | let tree = &self.item_trees[&id.file_id]; | ||
612 | |||
613 | // FIXME: This probably breaks with `use` items, since they produce multiple item tree nodes | ||
614 | |||
615 | // Root file (non-macro). | ||
616 | let item_tree_id = tree | ||
617 | .all_inner_items() | ||
618 | .chain(tree.top_level_items().iter().copied()) | ||
619 | .filter_map(|mod_item| mod_item.downcast::<N>()) | ||
620 | .find(|tree_id| tree[*tree_id].ast_id().upcast() == id.value.upcast()) | ||
621 | .or_else(|| { | ||
622 | log::debug!( | ||
623 | "couldn't find inner {} item for {:?} (AST: `{}` - {:?})", | ||
624 | type_name::<N>(), | ||
625 | id, | ||
626 | ast.syntax(), | ||
627 | ast.syntax(), | ||
628 | ); | ||
629 | None | ||
630 | })?; | ||
631 | |||
632 | Some(ItemTreeId::new(id.file_id, item_tree_id)) | ||
633 | } | ||
634 | |||
635 | fn collect_expr_opt(&mut self, expr: Option<ast::Expr>) -> ExprId { | 591 | fn collect_expr_opt(&mut self, expr: Option<ast::Expr>) -> ExprId { |
636 | if let Some(expr) = expr { | 592 | if let Some(expr) = expr { |
637 | self.collect_expr(expr) | 593 | self.collect_expr(expr) |
@@ -663,7 +619,6 @@ impl ExprCollector<'_> { | |||
663 | match expansion { | 619 | match expansion { |
664 | Some(expansion) => { | 620 | Some(expansion) => { |
665 | let statements: ast::MacroStmts = expansion; | 621 | let statements: ast::MacroStmts = expansion; |
666 | this.collect_stmts_items(statements.statements()); | ||
667 | 622 | ||
668 | statements.statements().for_each(|stmt| { | 623 | statements.statements().for_each(|stmt| { |
669 | if let Some(mut r) = this.collect_stmt(stmt) { | 624 | if let Some(mut r) = this.collect_stmt(stmt) { |
@@ -701,6 +656,8 @@ impl ExprCollector<'_> { | |||
701 | let block_loc = | 656 | let block_loc = |
702 | BlockLoc { ast_id, module: self.expander.def_map.module_id(self.expander.module) }; | 657 | BlockLoc { ast_id, module: self.expander.def_map.module_id(self.expander.module) }; |
703 | let block_id = self.db.intern_block(block_loc); | 658 | let block_id = self.db.intern_block(block_loc); |
659 | self.body.block_scopes.push(block_id); | ||
660 | |||
704 | let opt_def_map = self.db.block_def_map(block_id); | 661 | let opt_def_map = self.db.block_def_map(block_id); |
705 | let has_def_map = opt_def_map.is_some(); | 662 | let has_def_map = opt_def_map.is_some(); |
706 | let def_map = opt_def_map.unwrap_or_else(|| self.expander.def_map.clone()); | 663 | let def_map = opt_def_map.unwrap_or_else(|| self.expander.def_map.clone()); |
@@ -708,7 +665,6 @@ impl ExprCollector<'_> { | |||
708 | let prev_def_map = mem::replace(&mut self.expander.def_map, def_map); | 665 | let prev_def_map = mem::replace(&mut self.expander.def_map, def_map); |
709 | let prev_local_module = mem::replace(&mut self.expander.module, module); | 666 | let prev_local_module = mem::replace(&mut self.expander.module, module); |
710 | 667 | ||
711 | self.collect_stmts_items(block.statements()); | ||
712 | let statements = | 668 | let statements = |
713 | block.statements().filter_map(|s| self.collect_stmt(s)).flatten().collect(); | 669 | block.statements().filter_map(|s| self.collect_stmt(s)).flatten().collect(); |
714 | let tail = block.tail_expr().map(|e| self.collect_expr(e)); | 670 | let tail = block.tail_expr().map(|e| self.collect_expr(e)); |
@@ -723,108 +679,6 @@ impl ExprCollector<'_> { | |||
723 | expr_id | 679 | expr_id |
724 | } | 680 | } |
725 | 681 | ||
726 | fn collect_stmts_items(&mut self, stmts: ast::AstChildren<ast::Stmt>) { | ||
727 | let container = ContainerId::DefWithBodyId(self.def); | ||
728 | |||
729 | let items = stmts | ||
730 | .filter_map(|stmt| match stmt { | ||
731 | ast::Stmt::Item(it) => Some(it), | ||
732 | ast::Stmt::LetStmt(_) | ast::Stmt::ExprStmt(_) => None, | ||
733 | }) | ||
734 | .filter_map(|item| { | ||
735 | let (def, name): (ModuleDefId, Option<ast::Name>) = match item { | ||
736 | ast::Item::Fn(def) => { | ||
737 | let id = self.find_inner_item(&def)?; | ||
738 | ( | ||
739 | FunctionLoc { container: container.into(), id }.intern(self.db).into(), | ||
740 | def.name(), | ||
741 | ) | ||
742 | } | ||
743 | ast::Item::TypeAlias(def) => { | ||
744 | let id = self.find_inner_item(&def)?; | ||
745 | ( | ||
746 | TypeAliasLoc { container: container.into(), id }.intern(self.db).into(), | ||
747 | def.name(), | ||
748 | ) | ||
749 | } | ||
750 | ast::Item::Const(def) => { | ||
751 | let id = self.find_inner_item(&def)?; | ||
752 | ( | ||
753 | ConstLoc { container: container.into(), id }.intern(self.db).into(), | ||
754 | def.name(), | ||
755 | ) | ||
756 | } | ||
757 | ast::Item::Static(def) => { | ||
758 | let id = self.find_inner_item(&def)?; | ||
759 | (StaticLoc { container, id }.intern(self.db).into(), def.name()) | ||
760 | } | ||
761 | ast::Item::Struct(def) => { | ||
762 | let id = self.find_inner_item(&def)?; | ||
763 | (StructLoc { container, id }.intern(self.db).into(), def.name()) | ||
764 | } | ||
765 | ast::Item::Enum(def) => { | ||
766 | let id = self.find_inner_item(&def)?; | ||
767 | (EnumLoc { container, id }.intern(self.db).into(), def.name()) | ||
768 | } | ||
769 | ast::Item::Union(def) => { | ||
770 | let id = self.find_inner_item(&def)?; | ||
771 | (UnionLoc { container, id }.intern(self.db).into(), def.name()) | ||
772 | } | ||
773 | ast::Item::Trait(def) => { | ||
774 | let id = self.find_inner_item(&def)?; | ||
775 | (TraitLoc { container, id }.intern(self.db).into(), def.name()) | ||
776 | } | ||
777 | ast::Item::ExternBlock(_) => return None, // FIXME: collect from extern blocks | ||
778 | ast::Item::Impl(_) | ||
779 | | ast::Item::Use(_) | ||
780 | | ast::Item::ExternCrate(_) | ||
781 | | ast::Item::Module(_) | ||
782 | | ast::Item::MacroCall(_) => return None, | ||
783 | ast::Item::MacroRules(def) => { | ||
784 | return Some(Either::Right(ast::Macro::from(def))); | ||
785 | } | ||
786 | ast::Item::MacroDef(def) => { | ||
787 | return Some(Either::Right(ast::Macro::from(def))); | ||
788 | } | ||
789 | }; | ||
790 | |||
791 | Some(Either::Left((def, name))) | ||
792 | }) | ||
793 | .collect::<Vec<_>>(); | ||
794 | |||
795 | for either in items { | ||
796 | match either { | ||
797 | Either::Left((def, name)) => { | ||
798 | self.body.item_scope.define_def(def); | ||
799 | if let Some(name) = name { | ||
800 | let vis = crate::visibility::Visibility::Public; // FIXME determine correctly | ||
801 | let has_constructor = match def { | ||
802 | ModuleDefId::AdtId(AdtId::StructId(s)) => { | ||
803 | self.db.struct_data(s).variant_data.kind() != StructKind::Record | ||
804 | } | ||
805 | _ => true, | ||
806 | }; | ||
807 | self.body.item_scope.push_res( | ||
808 | name.as_name(), | ||
809 | crate::per_ns::PerNs::from_def(def, vis, has_constructor), | ||
810 | ); | ||
811 | } | ||
812 | } | ||
813 | Either::Right(e) => { | ||
814 | let mac = MacroDefId { | ||
815 | krate: self.expander.def_map.krate(), | ||
816 | ast_id: Some(self.expander.ast_id(&e)), | ||
817 | kind: MacroDefKind::Declarative, | ||
818 | local_inner: false, | ||
819 | }; | ||
820 | if let Some(name) = e.name() { | ||
821 | self.body.item_scope.define_legacy_macro(name.as_name(), mac); | ||
822 | } | ||
823 | } | ||
824 | } | ||
825 | } | ||
826 | } | ||
827 | |||
828 | fn collect_block_opt(&mut self, expr: Option<ast::BlockExpr>) -> ExprId { | 682 | fn collect_block_opt(&mut self, expr: Option<ast::BlockExpr>) -> ExprId { |
829 | if let Some(block) = expr { | 683 | if let Some(block) = expr { |
830 | self.collect_block(block) | 684 | self.collect_block(block) |
diff --git a/crates/hir_def/src/body/scope.rs b/crates/hir_def/src/body/scope.rs index 210b4a617..1bbb54fc6 100644 --- a/crates/hir_def/src/body/scope.rs +++ b/crates/hir_def/src/body/scope.rs | |||
@@ -186,7 +186,7 @@ mod tests { | |||
186 | use base_db::{fixture::WithFixture, FileId, SourceDatabase}; | 186 | use base_db::{fixture::WithFixture, FileId, SourceDatabase}; |
187 | use hir_expand::{name::AsName, InFile}; | 187 | use hir_expand::{name::AsName, InFile}; |
188 | use syntax::{algo::find_node_at_offset, ast, AstNode}; | 188 | use syntax::{algo::find_node_at_offset, ast, AstNode}; |
189 | use test_utils::{assert_eq_text, extract_offset, mark}; | 189 | use test_utils::{assert_eq_text, extract_offset}; |
190 | 190 | ||
191 | use crate::{db::DefDatabase, test_db::TestDB, FunctionId, ModuleDefId}; | 191 | use crate::{db::DefDatabase, test_db::TestDB, FunctionId, ModuleDefId}; |
192 | 192 | ||
@@ -454,7 +454,7 @@ fn foo() { | |||
454 | 454 | ||
455 | #[test] | 455 | #[test] |
456 | fn while_let_desugaring() { | 456 | fn while_let_desugaring() { |
457 | mark::check!(infer_resolve_while_let); | 457 | cov_mark::check!(infer_resolve_while_let); |
458 | do_check_local_name( | 458 | do_check_local_name( |
459 | r#" | 459 | r#" |
460 | fn test() { | 460 | fn test() { |
diff --git a/crates/hir_def/src/body/tests.rs b/crates/hir_def/src/body/tests.rs index bb43569d7..991a32b15 100644 --- a/crates/hir_def/src/body/tests.rs +++ b/crates/hir_def/src/body/tests.rs | |||
@@ -2,7 +2,6 @@ mod block; | |||
2 | 2 | ||
3 | use base_db::{fixture::WithFixture, SourceDatabase}; | 3 | use base_db::{fixture::WithFixture, SourceDatabase}; |
4 | use expect_test::Expect; | 4 | use expect_test::Expect; |
5 | use test_utils::mark; | ||
6 | 5 | ||
7 | use crate::{test_db::TestDB, ModuleDefId}; | 6 | use crate::{test_db::TestDB, ModuleDefId}; |
8 | 7 | ||
@@ -48,7 +47,7 @@ fn check_at(ra_fixture: &str, expect: Expect) { | |||
48 | 47 | ||
49 | #[test] | 48 | #[test] |
50 | fn your_stack_belongs_to_me() { | 49 | fn your_stack_belongs_to_me() { |
51 | mark::check!(your_stack_belongs_to_me); | 50 | cov_mark::check!(your_stack_belongs_to_me); |
52 | lower( | 51 | lower( |
53 | " | 52 | " |
54 | macro_rules! n_nuple { | 53 | macro_rules! n_nuple { |
diff --git a/crates/hir_def/src/body/tests/block.rs b/crates/hir_def/src/body/tests/block.rs index 8bca72a17..3b6ba4cde 100644 --- a/crates/hir_def/src/body/tests/block.rs +++ b/crates/hir_def/src/body/tests/block.rs | |||
@@ -165,16 +165,16 @@ fn macro_resolve() { | |||
165 | check_at( | 165 | check_at( |
166 | r#" | 166 | r#" |
167 | //- /lib.rs crate:lib deps:core | 167 | //- /lib.rs crate:lib deps:core |
168 | use core::mark; | 168 | use core::cov_mark; |
169 | 169 | ||
170 | fn f() { | 170 | fn f() { |
171 | fn nested() { | 171 | fn nested() { |
172 | mark::hit!(Hit); | 172 | cov_mark::hit!(Hit); |
173 | $0 | 173 | $0 |
174 | } | 174 | } |
175 | } | 175 | } |
176 | //- /core.rs crate:core | 176 | //- /core.rs crate:core |
177 | pub mod mark { | 177 | pub mod cov_mark { |
178 | #[macro_export] | 178 | #[macro_export] |
179 | macro_rules! _hit { | 179 | macro_rules! _hit { |
180 | ($name:ident) => { | 180 | ($name:ident) => { |
@@ -193,8 +193,8 @@ pub mod mark { | |||
193 | nested: v | 193 | nested: v |
194 | 194 | ||
195 | crate | 195 | crate |
196 | cov_mark: t | ||
196 | f: v | 197 | f: v |
197 | mark: t | ||
198 | "#]], | 198 | "#]], |
199 | ); | 199 | ); |
200 | } | 200 | } |
@@ -264,7 +264,7 @@ fn main() { | |||
264 | fn underscore_import() { | 264 | fn underscore_import() { |
265 | // This used to panic, because the default (private) visibility inside block expressions would | 265 | // This used to panic, because the default (private) visibility inside block expressions would |
266 | // point into the containing `DefMap`, which visibilities should never be able to do. | 266 | // point into the containing `DefMap`, which visibilities should never be able to do. |
267 | mark::check!(adjust_vis_in_block_def_map); | 267 | cov_mark::check!(adjust_vis_in_block_def_map); |
268 | check_at( | 268 | check_at( |
269 | r#" | 269 | r#" |
270 | mod m { | 270 | mod m { |
diff --git a/crates/hir_def/src/child_by_source.rs b/crates/hir_def/src/child_by_source.rs index 75c2d756b..2a331dcaf 100644 --- a/crates/hir_def/src/child_by_source.rs +++ b/crates/hir_def/src/child_by_source.rs | |||
@@ -17,13 +17,16 @@ use crate::{ | |||
17 | }; | 17 | }; |
18 | 18 | ||
19 | pub trait ChildBySource { | 19 | pub trait ChildBySource { |
20 | fn child_by_source(&self, db: &dyn DefDatabase) -> DynMap; | ||
21 | } | ||
22 | |||
23 | impl ChildBySource for TraitId { | ||
24 | fn child_by_source(&self, db: &dyn DefDatabase) -> DynMap { | 20 | fn child_by_source(&self, db: &dyn DefDatabase) -> DynMap { |
25 | let mut res = DynMap::default(); | 21 | let mut res = DynMap::default(); |
22 | self.child_by_source_to(db, &mut res); | ||
23 | res | ||
24 | } | ||
25 | fn child_by_source_to(&self, db: &dyn DefDatabase, map: &mut DynMap); | ||
26 | } | ||
26 | 27 | ||
28 | impl ChildBySource for TraitId { | ||
29 | fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap) { | ||
27 | let data = db.trait_data(*self); | 30 | let data = db.trait_data(*self); |
28 | for (_name, item) in data.items.iter() { | 31 | for (_name, item) in data.items.iter() { |
29 | match *item { | 32 | match *item { |
@@ -41,15 +44,11 @@ impl ChildBySource for TraitId { | |||
41 | } | 44 | } |
42 | } | 45 | } |
43 | } | 46 | } |
44 | |||
45 | res | ||
46 | } | 47 | } |
47 | } | 48 | } |
48 | 49 | ||
49 | impl ChildBySource for ImplId { | 50 | impl ChildBySource for ImplId { |
50 | fn child_by_source(&self, db: &dyn DefDatabase) -> DynMap { | 51 | fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap) { |
51 | let mut res = DynMap::default(); | ||
52 | |||
53 | let data = db.impl_data(*self); | 52 | let data = db.impl_data(*self); |
54 | for &item in data.items.iter() { | 53 | for &item in data.items.iter() { |
55 | match item { | 54 | match item { |
@@ -67,25 +66,21 @@ impl ChildBySource for ImplId { | |||
67 | } | 66 | } |
68 | } | 67 | } |
69 | } | 68 | } |
70 | |||
71 | res | ||
72 | } | 69 | } |
73 | } | 70 | } |
74 | 71 | ||
75 | impl ChildBySource for ModuleId { | 72 | impl ChildBySource for ModuleId { |
76 | fn child_by_source(&self, db: &dyn DefDatabase) -> DynMap { | 73 | fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap) { |
77 | let def_map = self.def_map(db); | 74 | let def_map = self.def_map(db); |
78 | let module_data = &def_map[self.local_id]; | 75 | let module_data = &def_map[self.local_id]; |
79 | module_data.scope.child_by_source(db) | 76 | module_data.scope.child_by_source_to(db, res); |
80 | } | 77 | } |
81 | } | 78 | } |
82 | 79 | ||
83 | impl ChildBySource for ItemScope { | 80 | impl ChildBySource for ItemScope { |
84 | fn child_by_source(&self, db: &dyn DefDatabase) -> DynMap { | 81 | fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap) { |
85 | let mut res = DynMap::default(); | 82 | self.declarations().for_each(|item| add_module_def(db, res, item)); |
86 | self.declarations().for_each(|item| add_module_def(db, &mut res, item)); | 83 | self.impls().for_each(|imp| add_impl(db, res, imp)); |
87 | self.impls().for_each(|imp| add_impl(db, &mut res, imp)); | ||
88 | return res; | ||
89 | 84 | ||
90 | fn add_module_def(db: &dyn DefDatabase, map: &mut DynMap, item: ModuleDefId) { | 85 | fn add_module_def(db: &dyn DefDatabase, map: &mut DynMap, item: ModuleDefId) { |
91 | match item { | 86 | match item { |
@@ -134,9 +129,7 @@ impl ChildBySource for ItemScope { | |||
134 | } | 129 | } |
135 | 130 | ||
136 | impl ChildBySource for VariantId { | 131 | impl ChildBySource for VariantId { |
137 | fn child_by_source(&self, db: &dyn DefDatabase) -> DynMap { | 132 | fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap) { |
138 | let mut res = DynMap::default(); | ||
139 | |||
140 | let arena_map = self.child_source(db); | 133 | let arena_map = self.child_source(db); |
141 | let arena_map = arena_map.as_ref(); | 134 | let arena_map = arena_map.as_ref(); |
142 | for (local_id, source) in arena_map.value.iter() { | 135 | for (local_id, source) in arena_map.value.iter() { |
@@ -150,28 +143,27 @@ impl ChildBySource for VariantId { | |||
150 | } | 143 | } |
151 | } | 144 | } |
152 | } | 145 | } |
153 | res | ||
154 | } | 146 | } |
155 | } | 147 | } |
156 | 148 | ||
157 | impl ChildBySource for EnumId { | 149 | impl ChildBySource for EnumId { |
158 | fn child_by_source(&self, db: &dyn DefDatabase) -> DynMap { | 150 | fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap) { |
159 | let mut res = DynMap::default(); | ||
160 | |||
161 | let arena_map = self.child_source(db); | 151 | let arena_map = self.child_source(db); |
162 | let arena_map = arena_map.as_ref(); | 152 | let arena_map = arena_map.as_ref(); |
163 | for (local_id, source) in arena_map.value.iter() { | 153 | for (local_id, source) in arena_map.value.iter() { |
164 | let id = EnumVariantId { parent: *self, local_id }; | 154 | let id = EnumVariantId { parent: *self, local_id }; |
165 | res[keys::VARIANT].insert(arena_map.with_value(source.clone()), id) | 155 | res[keys::VARIANT].insert(arena_map.with_value(source.clone()), id) |
166 | } | 156 | } |
167 | |||
168 | res | ||
169 | } | 157 | } |
170 | } | 158 | } |
171 | 159 | ||
172 | impl ChildBySource for DefWithBodyId { | 160 | impl ChildBySource for DefWithBodyId { |
173 | fn child_by_source(&self, db: &dyn DefDatabase) -> DynMap { | 161 | fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap) { |
174 | let body = db.body(*self); | 162 | let body = db.body(*self); |
175 | body.item_scope.child_by_source(db) | 163 | for def_map in body.block_scopes.iter().filter_map(|block| db.block_def_map(*block)) { |
164 | // All block expressions are merged into the same map, because they logically all add | ||
165 | // inner items to the containing `DefWithBodyId`. | ||
166 | def_map[def_map.root()].scope.child_by_source_to(db, res); | ||
167 | } | ||
176 | } | 168 | } |
177 | } | 169 | } |
diff --git a/crates/hir_def/src/data.rs b/crates/hir_def/src/data.rs index d3380e0f4..aea53d527 100644 --- a/crates/hir_def/src/data.rs +++ b/crates/hir_def/src/data.rs | |||
@@ -97,7 +97,7 @@ impl TraitData { | |||
97 | let tr_def = &item_tree[tr_loc.id.value]; | 97 | let tr_def = &item_tree[tr_loc.id.value]; |
98 | let name = tr_def.name.clone(); | 98 | let name = tr_def.name.clone(); |
99 | let auto = tr_def.auto; | 99 | let auto = tr_def.auto; |
100 | let module_id = tr_loc.container.module(db); | 100 | let module_id = tr_loc.container; |
101 | let container = AssocContainerId::TraitId(tr); | 101 | let container = AssocContainerId::TraitId(tr); |
102 | let mut expander = Expander::new(db, tr_loc.id.file_id, module_id); | 102 | let mut expander = Expander::new(db, tr_loc.id.file_id, module_id); |
103 | 103 | ||
@@ -147,7 +147,7 @@ impl ImplData { | |||
147 | let target_trait = impl_def.target_trait.map(|id| item_tree[id].clone()); | 147 | let target_trait = impl_def.target_trait.map(|id| item_tree[id].clone()); |
148 | let target_type = item_tree[impl_def.target_type].clone(); | 148 | let target_type = item_tree[impl_def.target_type].clone(); |
149 | let is_negative = impl_def.is_negative; | 149 | let is_negative = impl_def.is_negative; |
150 | let module_id = impl_loc.container.module(db); | 150 | let module_id = impl_loc.container; |
151 | let container = AssocContainerId::ImplId(id); | 151 | let container = AssocContainerId::ImplId(id); |
152 | let mut expander = Expander::new(db, impl_loc.id.file_id, module_id); | 152 | let mut expander = Expander::new(db, impl_loc.id.file_id, module_id); |
153 | 153 | ||
diff --git a/crates/hir_def/src/find_path.rs b/crates/hir_def/src/find_path.rs index 3a98ffbaa..de08e2737 100644 --- a/crates/hir_def/src/find_path.rs +++ b/crates/hir_def/src/find_path.rs | |||
@@ -4,7 +4,6 @@ use std::iter; | |||
4 | 4 | ||
5 | use hir_expand::name::{known, AsName, Name}; | 5 | use hir_expand::name::{known, AsName, Name}; |
6 | use rustc_hash::FxHashSet; | 6 | use rustc_hash::FxHashSet; |
7 | use test_utils::mark; | ||
8 | 7 | ||
9 | use crate::nameres::DefMap; | 8 | use crate::nameres::DefMap; |
10 | use crate::{ | 9 | use crate::{ |
@@ -215,7 +214,7 @@ fn find_path_inner( | |||
215 | best_path_len - 1, | 214 | best_path_len - 1, |
216 | prefixed, | 215 | prefixed, |
217 | )?; | 216 | )?; |
218 | mark::hit!(partially_imported); | 217 | cov_mark::hit!(partially_imported); |
219 | path.push_segment(info.path.segments.last().unwrap().clone()); | 218 | path.push_segment(info.path.segments.last().unwrap().clone()); |
220 | Some(path) | 219 | Some(path) |
221 | }) | 220 | }) |
@@ -235,7 +234,7 @@ fn find_path_inner( | |||
235 | // that correctly (FIXME). | 234 | // that correctly (FIXME). |
236 | if let Some(item_module) = item.as_module_def_id().and_then(|did| did.module(db)) { | 235 | if let Some(item_module) = item.as_module_def_id().and_then(|did| did.module(db)) { |
237 | if item_module.def_map(db).block_id().is_some() && prefixed.is_some() { | 236 | if item_module.def_map(db).block_id().is_some() && prefixed.is_some() { |
238 | mark::hit!(prefixed_in_block_expression); | 237 | cov_mark::hit!(prefixed_in_block_expression); |
239 | prefixed = Some(PrefixKind::Plain); | 238 | prefixed = Some(PrefixKind::Plain); |
240 | } | 239 | } |
241 | } | 240 | } |
@@ -252,18 +251,18 @@ fn find_path_inner( | |||
252 | fn select_best_path(old_path: ModPath, new_path: ModPath, prefer_no_std: bool) -> ModPath { | 251 | fn select_best_path(old_path: ModPath, new_path: ModPath, prefer_no_std: bool) -> ModPath { |
253 | if old_path.starts_with_std() && new_path.can_start_with_std() { | 252 | if old_path.starts_with_std() && new_path.can_start_with_std() { |
254 | if prefer_no_std { | 253 | if prefer_no_std { |
255 | mark::hit!(prefer_no_std_paths); | 254 | cov_mark::hit!(prefer_no_std_paths); |
256 | new_path | 255 | new_path |
257 | } else { | 256 | } else { |
258 | mark::hit!(prefer_std_paths); | 257 | cov_mark::hit!(prefer_std_paths); |
259 | old_path | 258 | old_path |
260 | } | 259 | } |
261 | } else if new_path.starts_with_std() && old_path.can_start_with_std() { | 260 | } else if new_path.starts_with_std() && old_path.can_start_with_std() { |
262 | if prefer_no_std { | 261 | if prefer_no_std { |
263 | mark::hit!(prefer_no_std_paths); | 262 | cov_mark::hit!(prefer_no_std_paths); |
264 | old_path | 263 | old_path |
265 | } else { | 264 | } else { |
266 | mark::hit!(prefer_std_paths); | 265 | cov_mark::hit!(prefer_std_paths); |
267 | new_path | 266 | new_path |
268 | } | 267 | } |
269 | } else if new_path.len() < old_path.len() { | 268 | } else if new_path.len() < old_path.len() { |
@@ -364,7 +363,6 @@ mod tests { | |||
364 | use base_db::fixture::WithFixture; | 363 | use base_db::fixture::WithFixture; |
365 | use hir_expand::hygiene::Hygiene; | 364 | use hir_expand::hygiene::Hygiene; |
366 | use syntax::ast::AstNode; | 365 | use syntax::ast::AstNode; |
367 | use test_utils::mark; | ||
368 | 366 | ||
369 | use crate::test_db::TestDB; | 367 | use crate::test_db::TestDB; |
370 | 368 | ||
@@ -522,7 +520,7 @@ mod tests { | |||
522 | 520 | ||
523 | #[test] | 521 | #[test] |
524 | fn partially_imported() { | 522 | fn partially_imported() { |
525 | mark::check!(partially_imported); | 523 | cov_mark::check!(partially_imported); |
526 | // Tests that short paths are used even for external items, when parts of the path are | 524 | // Tests that short paths are used even for external items, when parts of the path are |
527 | // already in scope. | 525 | // already in scope. |
528 | let code = r#" | 526 | let code = r#" |
@@ -686,7 +684,7 @@ mod tests { | |||
686 | 684 | ||
687 | #[test] | 685 | #[test] |
688 | fn prefer_std_paths_over_alloc() { | 686 | fn prefer_std_paths_over_alloc() { |
689 | mark::check!(prefer_std_paths); | 687 | cov_mark::check!(prefer_std_paths); |
690 | let code = r#" | 688 | let code = r#" |
691 | //- /main.rs crate:main deps:alloc,std | 689 | //- /main.rs crate:main deps:alloc,std |
692 | $0 | 690 | $0 |
@@ -712,7 +710,7 @@ mod tests { | |||
712 | 710 | ||
713 | #[test] | 711 | #[test] |
714 | fn prefer_core_paths_over_std() { | 712 | fn prefer_core_paths_over_std() { |
715 | mark::check!(prefer_no_std_paths); | 713 | cov_mark::check!(prefer_no_std_paths); |
716 | let code = r#" | 714 | let code = r#" |
717 | //- /main.rs crate:main deps:core,std | 715 | //- /main.rs crate:main deps:core,std |
718 | #![no_std] | 716 | #![no_std] |
@@ -842,7 +840,7 @@ mod tests { | |||
842 | 840 | ||
843 | #[test] | 841 | #[test] |
844 | fn inner_items_from_inner_module() { | 842 | fn inner_items_from_inner_module() { |
845 | mark::check!(prefixed_in_block_expression); | 843 | cov_mark::check!(prefixed_in_block_expression); |
846 | check_found_path( | 844 | check_found_path( |
847 | r#" | 845 | r#" |
848 | fn main() { | 846 | fn main() { |
diff --git a/crates/hir_def/src/generics.rs b/crates/hir_def/src/generics.rs index 3ace3be1f..a056ab797 100644 --- a/crates/hir_def/src/generics.rs +++ b/crates/hir_def/src/generics.rs | |||
@@ -421,8 +421,7 @@ impl HasChildSource<LocalConstParamId> for GenericDefId { | |||
421 | } | 421 | } |
422 | 422 | ||
423 | impl ChildBySource for GenericDefId { | 423 | impl ChildBySource for GenericDefId { |
424 | fn child_by_source(&self, db: &dyn DefDatabase) -> DynMap { | 424 | fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap) { |
425 | let mut res = DynMap::default(); | ||
426 | let (_, sm) = GenericParams::new(db, *self); | 425 | let (_, sm) = GenericParams::new(db, *self); |
427 | 426 | ||
428 | let sm = sm.as_ref(); | 427 | let sm = sm.as_ref(); |
@@ -440,6 +439,5 @@ impl ChildBySource for GenericDefId { | |||
440 | let id = ConstParamId { parent: *self, local_id }; | 439 | let id = ConstParamId { parent: *self, local_id }; |
441 | res[keys::CONST_PARAM].insert(sm.with_value(src.clone()), id); | 440 | res[keys::CONST_PARAM].insert(sm.with_value(src.clone()), id); |
442 | } | 441 | } |
443 | res | ||
444 | } | 442 | } |
445 | } | 443 | } |
diff --git a/crates/hir_def/src/import_map.rs b/crates/hir_def/src/import_map.rs index 0a3dc7956..369bc3350 100644 --- a/crates/hir_def/src/import_map.rs +++ b/crates/hir_def/src/import_map.rs | |||
@@ -8,7 +8,6 @@ use hir_expand::name::Name; | |||
8 | use indexmap::{map::Entry, IndexMap}; | 8 | use indexmap::{map::Entry, IndexMap}; |
9 | use itertools::Itertools; | 9 | use itertools::Itertools; |
10 | use rustc_hash::{FxHashSet, FxHasher}; | 10 | use rustc_hash::{FxHashSet, FxHasher}; |
11 | use test_utils::mark; | ||
12 | 11 | ||
13 | use crate::{ | 12 | use crate::{ |
14 | db::DefDatabase, item_scope::ItemInNs, visibility::Visibility, AssocItemId, ModuleDefId, | 13 | db::DefDatabase, item_scope::ItemInNs, visibility::Visibility, AssocItemId, ModuleDefId, |
@@ -193,7 +192,7 @@ impl ImportMap { | |||
193 | // cannot use associated type aliases directly: need a `<Struct as Trait>::TypeAlias` | 192 | // cannot use associated type aliases directly: need a `<Struct as Trait>::TypeAlias` |
194 | // qualifier, ergo no need to store it for imports in import_map | 193 | // qualifier, ergo no need to store it for imports in import_map |
195 | AssocItemId::TypeAliasId(_) => { | 194 | AssocItemId::TypeAliasId(_) => { |
196 | mark::hit!(type_aliases_ignored); | 195 | cov_mark::hit!(type_aliases_ignored); |
197 | continue; | 196 | continue; |
198 | } | 197 | } |
199 | }; | 198 | }; |
@@ -388,7 +387,7 @@ pub fn search_dependencies<'a>( | |||
388 | db: &'a dyn DefDatabase, | 387 | db: &'a dyn DefDatabase, |
389 | krate: CrateId, | 388 | krate: CrateId, |
390 | query: Query, | 389 | query: Query, |
391 | ) -> Vec<ItemInNs> { | 390 | ) -> FxHashSet<ItemInNs> { |
392 | let _p = profile::span("search_dependencies").detail(|| format!("{:?}", query)); | 391 | let _p = profile::span("search_dependencies").detail(|| format!("{:?}", query)); |
393 | 392 | ||
394 | let graph = db.crate_graph(); | 393 | let graph = db.crate_graph(); |
@@ -403,41 +402,42 @@ pub fn search_dependencies<'a>( | |||
403 | } | 402 | } |
404 | 403 | ||
405 | let mut stream = op.union(); | 404 | let mut stream = op.union(); |
406 | let mut res = Vec::new(); | 405 | |
406 | let mut all_indexed_values = FxHashSet::default(); | ||
407 | while let Some((_, indexed_values)) = stream.next() { | 407 | while let Some((_, indexed_values)) = stream.next() { |
408 | for indexed_value in indexed_values { | 408 | all_indexed_values.extend(indexed_values.iter().copied()); |
409 | let import_map = &import_maps[indexed_value.index]; | 409 | } |
410 | let importables = &import_map.importables[indexed_value.value as usize..]; | ||
411 | 410 | ||
412 | let common_importable_data = &import_map.map[&importables[0]]; | 411 | let mut res = FxHashSet::default(); |
413 | if !query.import_matches(common_importable_data, true) { | 412 | for indexed_value in all_indexed_values { |
414 | continue; | 413 | let import_map = &import_maps[indexed_value.index]; |
415 | } | 414 | let importables = &import_map.importables[indexed_value.value as usize..]; |
416 | 415 | ||
417 | // Path shared by the importable items in this group. | 416 | let common_importable_data = &import_map.map[&importables[0]]; |
418 | let common_importables_path_fst = fst_path(&common_importable_data.path); | 417 | if !query.import_matches(common_importable_data, true) { |
419 | // Add the items from this `ModPath` group. Those are all subsequent items in | 418 | continue; |
420 | // `importables` whose paths match `path`. | 419 | } |
421 | let iter = importables | 420 | |
422 | .iter() | 421 | // Path shared by the importable items in this group. |
423 | .copied() | 422 | let common_importables_path_fst = fst_path(&common_importable_data.path); |
424 | .take_while(|item| { | 423 | // Add the items from this `ModPath` group. Those are all subsequent items in |
425 | common_importables_path_fst == fst_path(&import_map.map[item].path) | 424 | // `importables` whose paths match `path`. |
426 | }) | 425 | let iter = importables |
427 | .filter(|&item| match item_import_kind(item) { | 426 | .iter() |
428 | Some(import_kind) => !query.exclude_import_kinds.contains(&import_kind), | 427 | .copied() |
429 | None => true, | 428 | .take_while(|item| common_importables_path_fst == fst_path(&import_map.map[item].path)) |
430 | }) | 429 | .filter(|&item| match item_import_kind(item) { |
431 | .filter(|item| { | 430 | Some(import_kind) => !query.exclude_import_kinds.contains(&import_kind), |
432 | !query.case_sensitive // we've already checked the common importables path case-insensitively | 431 | None => true, |
432 | }) | ||
433 | .filter(|item| { | ||
434 | !query.case_sensitive // we've already checked the common importables path case-insensitively | ||
433 | || query.import_matches(&import_map.map[item], false) | 435 | || query.import_matches(&import_map.map[item], false) |
434 | }); | 436 | }); |
435 | res.extend(iter); | 437 | res.extend(iter); |
436 | 438 | ||
437 | if res.len() >= query.limit { | 439 | if res.len() >= query.limit { |
438 | res.truncate(query.limit); | 440 | return res; |
439 | return res; | ||
440 | } | ||
441 | } | 441 | } |
442 | } | 442 | } |
443 | 443 | ||
@@ -462,7 +462,6 @@ fn item_import_kind(item: ItemInNs) -> Option<ImportKind> { | |||
462 | mod tests { | 462 | mod tests { |
463 | use base_db::{fixture::WithFixture, SourceDatabase, Upcast}; | 463 | use base_db::{fixture::WithFixture, SourceDatabase, Upcast}; |
464 | use expect_test::{expect, Expect}; | 464 | use expect_test::{expect, Expect}; |
465 | use test_utils::mark; | ||
466 | 465 | ||
467 | use crate::{test_db::TestDB, AssocContainerId, Lookup}; | 466 | use crate::{test_db::TestDB, AssocContainerId, Lookup}; |
468 | 467 | ||
@@ -800,7 +799,7 @@ mod tests { | |||
800 | 799 | ||
801 | #[test] | 800 | #[test] |
802 | fn fuzzy_import_trait_and_assoc_items() { | 801 | fn fuzzy_import_trait_and_assoc_items() { |
803 | mark::check!(type_aliases_ignored); | 802 | cov_mark::check!(type_aliases_ignored); |
804 | let ra_fixture = r#" | 803 | let ra_fixture = r#" |
805 | //- /main.rs crate:main deps:dep | 804 | //- /main.rs crate:main deps:dep |
806 | //- /dep.rs crate:dep | 805 | //- /dep.rs crate:dep |
@@ -821,10 +820,10 @@ mod tests { | |||
821 | Query::new("fmt".to_string()).search_mode(SearchMode::Fuzzy), | 820 | Query::new("fmt".to_string()).search_mode(SearchMode::Fuzzy), |
822 | expect![[r#" | 821 | expect![[r#" |
823 | dep::fmt (t) | 822 | dep::fmt (t) |
823 | dep::fmt::Display::format_method (a) | ||
824 | dep::fmt::Display (t) | 824 | dep::fmt::Display (t) |
825 | dep::fmt::Display::FMT_CONST (a) | 825 | dep::fmt::Display::FMT_CONST (a) |
826 | dep::fmt::Display::format_function (a) | 826 | dep::fmt::Display::format_function (a) |
827 | dep::fmt::Display::format_method (a) | ||
828 | "#]], | 827 | "#]], |
829 | ); | 828 | ); |
830 | } | 829 | } |
@@ -850,9 +849,9 @@ mod tests { | |||
850 | "main", | 849 | "main", |
851 | Query::new("fmt".to_string()).search_mode(SearchMode::Fuzzy).assoc_items_only(), | 850 | Query::new("fmt".to_string()).search_mode(SearchMode::Fuzzy).assoc_items_only(), |
852 | expect![[r#" | 851 | expect![[r#" |
852 | dep::fmt::Display::format_method (a) | ||
853 | dep::fmt::Display::FMT_CONST (a) | 853 | dep::fmt::Display::FMT_CONST (a) |
854 | dep::fmt::Display::format_function (a) | 854 | dep::fmt::Display::format_function (a) |
855 | dep::fmt::Display::format_method (a) | ||
856 | "#]], | 855 | "#]], |
857 | ); | 856 | ); |
858 | 857 | ||
@@ -911,12 +910,12 @@ mod tests { | |||
911 | Query::new("fmt".to_string()).search_mode(SearchMode::Fuzzy), | 910 | Query::new("fmt".to_string()).search_mode(SearchMode::Fuzzy), |
912 | expect![[r#" | 911 | expect![[r#" |
913 | dep::fmt (t) | 912 | dep::fmt (t) |
914 | dep::Fmt (t) | 913 | dep::format (f) |
915 | dep::Fmt (v) | 914 | dep::Fmt (v) |
916 | dep::Fmt (m) | ||
917 | dep::fmt::Display (t) | 915 | dep::fmt::Display (t) |
916 | dep::Fmt (t) | ||
918 | dep::fmt::Display::fmt (a) | 917 | dep::fmt::Display::fmt (a) |
919 | dep::format (f) | 918 | dep::Fmt (m) |
920 | "#]], | 919 | "#]], |
921 | ); | 920 | ); |
922 | 921 | ||
@@ -926,10 +925,10 @@ mod tests { | |||
926 | Query::new("fmt".to_string()).search_mode(SearchMode::Equals), | 925 | Query::new("fmt".to_string()).search_mode(SearchMode::Equals), |
927 | expect![[r#" | 926 | expect![[r#" |
928 | dep::fmt (t) | 927 | dep::fmt (t) |
929 | dep::Fmt (t) | ||
930 | dep::Fmt (v) | 928 | dep::Fmt (v) |
931 | dep::Fmt (m) | 929 | dep::Fmt (t) |
932 | dep::fmt::Display::fmt (a) | 930 | dep::fmt::Display::fmt (a) |
931 | dep::Fmt (m) | ||
933 | "#]], | 932 | "#]], |
934 | ); | 933 | ); |
935 | 934 | ||
@@ -939,11 +938,11 @@ mod tests { | |||
939 | Query::new("fmt".to_string()).search_mode(SearchMode::Contains), | 938 | Query::new("fmt".to_string()).search_mode(SearchMode::Contains), |
940 | expect![[r#" | 939 | expect![[r#" |
941 | dep::fmt (t) | 940 | dep::fmt (t) |
942 | dep::Fmt (t) | ||
943 | dep::Fmt (v) | 941 | dep::Fmt (v) |
944 | dep::Fmt (m) | ||
945 | dep::fmt::Display (t) | 942 | dep::fmt::Display (t) |
943 | dep::Fmt (t) | ||
946 | dep::fmt::Display::fmt (a) | 944 | dep::fmt::Display::fmt (a) |
945 | dep::Fmt (m) | ||
947 | "#]], | 946 | "#]], |
948 | ); | 947 | ); |
949 | } | 948 | } |
@@ -980,11 +979,11 @@ mod tests { | |||
980 | Query::new("fmt".to_string()), | 979 | Query::new("fmt".to_string()), |
981 | expect![[r#" | 980 | expect![[r#" |
982 | dep::fmt (t) | 981 | dep::fmt (t) |
983 | dep::Fmt (t) | ||
984 | dep::Fmt (v) | 982 | dep::Fmt (v) |
985 | dep::Fmt (m) | ||
986 | dep::fmt::Display (t) | 983 | dep::fmt::Display (t) |
984 | dep::Fmt (t) | ||
987 | dep::fmt::Display::fmt (a) | 985 | dep::fmt::Display::fmt (a) |
986 | dep::Fmt (m) | ||
988 | "#]], | 987 | "#]], |
989 | ); | 988 | ); |
990 | 989 | ||
@@ -994,10 +993,10 @@ mod tests { | |||
994 | Query::new("fmt".to_string()).name_only(), | 993 | Query::new("fmt".to_string()).name_only(), |
995 | expect![[r#" | 994 | expect![[r#" |
996 | dep::fmt (t) | 995 | dep::fmt (t) |
997 | dep::Fmt (t) | ||
998 | dep::Fmt (v) | 996 | dep::Fmt (v) |
999 | dep::Fmt (m) | 997 | dep::Fmt (t) |
1000 | dep::fmt::Display::fmt (a) | 998 | dep::fmt::Display::fmt (a) |
999 | dep::Fmt (m) | ||
1001 | "#]], | 1000 | "#]], |
1002 | ); | 1001 | ); |
1003 | } | 1002 | } |
@@ -1018,9 +1017,9 @@ mod tests { | |||
1018 | Query::new("FMT".to_string()), | 1017 | Query::new("FMT".to_string()), |
1019 | expect![[r#" | 1018 | expect![[r#" |
1020 | dep::fmt (t) | 1019 | dep::fmt (t) |
1020 | dep::FMT (v) | ||
1021 | dep::fmt (v) | 1021 | dep::fmt (v) |
1022 | dep::FMT (t) | 1022 | dep::FMT (t) |
1023 | dep::FMT (v) | ||
1024 | "#]], | 1023 | "#]], |
1025 | ); | 1024 | ); |
1026 | 1025 | ||
@@ -1060,6 +1059,8 @@ mod tests { | |||
1060 | expect![[r#" | 1059 | expect![[r#" |
1061 | dep::fmt (t) | 1060 | dep::fmt (t) |
1062 | dep::Fmt (t) | 1061 | dep::Fmt (t) |
1062 | dep::Fmt (m) | ||
1063 | dep::Fmt (v) | ||
1063 | "#]], | 1064 | "#]], |
1064 | ); | 1065 | ); |
1065 | } | 1066 | } |
@@ -1080,9 +1081,9 @@ mod tests { | |||
1080 | Query::new("FMT".to_string()), | 1081 | Query::new("FMT".to_string()), |
1081 | expect![[r#" | 1082 | expect![[r#" |
1082 | dep::fmt (t) | 1083 | dep::fmt (t) |
1084 | dep::FMT (v) | ||
1083 | dep::fmt (v) | 1085 | dep::fmt (v) |
1084 | dep::FMT (t) | 1086 | dep::FMT (t) |
1085 | dep::FMT (v) | ||
1086 | "#]], | 1087 | "#]], |
1087 | ); | 1088 | ); |
1088 | 1089 | ||
diff --git a/crates/hir_def/src/item_scope.rs b/crates/hir_def/src/item_scope.rs index 4e5daa2ff..aafd73b60 100644 --- a/crates/hir_def/src/item_scope.rs +++ b/crates/hir_def/src/item_scope.rs | |||
@@ -9,7 +9,6 @@ use hir_expand::MacroDefKind; | |||
9 | use once_cell::sync::Lazy; | 9 | use once_cell::sync::Lazy; |
10 | use rustc_hash::{FxHashMap, FxHashSet}; | 10 | use rustc_hash::{FxHashMap, FxHashSet}; |
11 | use stdx::format_to; | 11 | use stdx::format_to; |
12 | use test_utils::mark; | ||
13 | 12 | ||
14 | use crate::{ | 13 | use crate::{ |
15 | db::DefDatabase, per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType, ImplId, | 14 | db::DefDatabase, per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType, ImplId, |
@@ -169,37 +168,6 @@ impl ItemScope { | |||
169 | self.unnamed_trait_imports.insert(tr, vis); | 168 | self.unnamed_trait_imports.insert(tr, vis); |
170 | } | 169 | } |
171 | 170 | ||
172 | pub(crate) fn push_res(&mut self, name: Name, def: PerNs) -> bool { | ||
173 | let mut changed = false; | ||
174 | |||
175 | if let Some(types) = def.types { | ||
176 | self.types.entry(name.clone()).or_insert_with(|| { | ||
177 | changed = true; | ||
178 | types | ||
179 | }); | ||
180 | } | ||
181 | if let Some(values) = def.values { | ||
182 | self.values.entry(name.clone()).or_insert_with(|| { | ||
183 | changed = true; | ||
184 | values | ||
185 | }); | ||
186 | } | ||
187 | if let Some(macros) = def.macros { | ||
188 | self.macros.entry(name.clone()).or_insert_with(|| { | ||
189 | changed = true; | ||
190 | macros | ||
191 | }); | ||
192 | } | ||
193 | |||
194 | if def.is_none() { | ||
195 | if self.unresolved.insert(name) { | ||
196 | changed = true; | ||
197 | } | ||
198 | } | ||
199 | |||
200 | changed | ||
201 | } | ||
202 | |||
203 | pub(crate) fn push_res_with_import( | 171 | pub(crate) fn push_res_with_import( |
204 | &mut self, | 172 | &mut self, |
205 | glob_imports: &mut PerNsGlobImports, | 173 | glob_imports: &mut PerNsGlobImports, |
@@ -237,7 +205,7 @@ impl ItemScope { | |||
237 | if $glob_imports.$field.contains(&$lookup) | 205 | if $glob_imports.$field.contains(&$lookup) |
238 | && matches!($def_import_type, ImportType::Named) => | 206 | && matches!($def_import_type, ImportType::Named) => |
239 | { | 207 | { |
240 | mark::hit!(import_shadowed); | 208 | cov_mark::hit!(import_shadowed); |
241 | $glob_imports.$field.remove(&$lookup); | 209 | $glob_imports.$field.remove(&$lookup); |
242 | if let Some(fld) = $def.$field { | 210 | if let Some(fld) = $def.$field { |
243 | entry.insert(fld); | 211 | entry.insert(fld); |
diff --git a/crates/hir_def/src/item_tree.rs b/crates/hir_def/src/item_tree.rs index 3233b1957..09bcb10dc 100644 --- a/crates/hir_def/src/item_tree.rs +++ b/crates/hir_def/src/item_tree.rs | |||
@@ -25,7 +25,6 @@ use profile::Count; | |||
25 | use rustc_hash::FxHashMap; | 25 | use rustc_hash::FxHashMap; |
26 | use smallvec::SmallVec; | 26 | use smallvec::SmallVec; |
27 | use syntax::{ast, match_ast, SyntaxKind}; | 27 | use syntax::{ast, match_ast, SyntaxKind}; |
28 | use test_utils::mark; | ||
29 | 28 | ||
30 | use crate::{ | 29 | use crate::{ |
31 | attr::{Attrs, RawAttrs}, | 30 | attr::{Attrs, RawAttrs}, |
@@ -210,18 +209,6 @@ impl ItemTree { | |||
210 | } | 209 | } |
211 | } | 210 | } |
212 | 211 | ||
213 | pub fn source<S: ItemTreeNode>(&self, db: &dyn DefDatabase, of: ItemTreeId<S>) -> S::Source { | ||
214 | // This unwrap cannot fail, since it has either succeeded above, or resulted in an empty | ||
215 | // ItemTree (in which case there is no valid `FileItemTreeId` to call this method with). | ||
216 | let root = | ||
217 | db.parse_or_expand(of.file_id).expect("parse_or_expand failed on constructed ItemTree"); | ||
218 | |||
219 | let id = self[of.value].ast_id(); | ||
220 | let map = db.ast_id_map(of.file_id); | ||
221 | let ptr = map.get(id); | ||
222 | ptr.to_node(&root) | ||
223 | } | ||
224 | |||
225 | fn data(&self) -> &ItemTreeData { | 212 | fn data(&self) -> &ItemTreeData { |
226 | self.data.as_ref().expect("attempted to access data of empty ItemTree") | 213 | self.data.as_ref().expect("attempted to access data of empty ItemTree") |
227 | } | 214 | } |
diff --git a/crates/hir_def/src/item_tree/lower.rs b/crates/hir_def/src/item_tree/lower.rs index 8f2f0b340..240fdacf9 100644 --- a/crates/hir_def/src/item_tree/lower.rs +++ b/crates/hir_def/src/item_tree/lower.rs | |||
@@ -466,7 +466,7 @@ impl Ctx { | |||
466 | .collect() | 466 | .collect() |
467 | }) | 467 | }) |
468 | .unwrap_or_else(|| { | 468 | .unwrap_or_else(|| { |
469 | mark::hit!(name_res_works_for_broken_modules); | 469 | cov_mark::hit!(name_res_works_for_broken_modules); |
470 | Box::new([]) as Box<[_]> | 470 | Box::new([]) as Box<[_]> |
471 | }), | 471 | }), |
472 | } | 472 | } |
diff --git a/crates/hir_def/src/lib.rs b/crates/hir_def/src/lib.rs index 4498d94bb..6d11c5be4 100644 --- a/crates/hir_def/src/lib.rs +++ b/crates/hir_def/src/lib.rs | |||
@@ -108,7 +108,7 @@ pub type LocalModuleId = Idx<nameres::ModuleData>; | |||
108 | 108 | ||
109 | #[derive(Debug)] | 109 | #[derive(Debug)] |
110 | pub struct ItemLoc<N: ItemTreeNode> { | 110 | pub struct ItemLoc<N: ItemTreeNode> { |
111 | pub container: ContainerId, | 111 | pub container: ModuleId, |
112 | pub id: ItemTreeId<N>, | 112 | pub id: ItemTreeId<N>, |
113 | } | 113 | } |
114 | 114 | ||
@@ -279,18 +279,12 @@ pub struct ConstParamId { | |||
279 | pub type LocalConstParamId = Idx<generics::ConstParamData>; | 279 | pub type LocalConstParamId = Idx<generics::ConstParamData>; |
280 | 280 | ||
281 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | 281 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
282 | pub enum ContainerId { | ||
283 | ModuleId(ModuleId), | ||
284 | DefWithBodyId(DefWithBodyId), | ||
285 | } | ||
286 | |||
287 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
288 | pub enum AssocContainerId { | 282 | pub enum AssocContainerId { |
289 | ContainerId(ContainerId), | 283 | ModuleId(ModuleId), |
290 | ImplId(ImplId), | 284 | ImplId(ImplId), |
291 | TraitId(TraitId), | 285 | TraitId(TraitId), |
292 | } | 286 | } |
293 | impl_from!(ContainerId for AssocContainerId); | 287 | impl_from!(ModuleId for AssocContainerId); |
294 | 288 | ||
295 | /// A Data Type | 289 | /// A Data Type |
296 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] | 290 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] |
@@ -447,21 +441,12 @@ pub trait HasModule { | |||
447 | fn module(&self, db: &dyn db::DefDatabase) -> ModuleId; | 441 | fn module(&self, db: &dyn db::DefDatabase) -> ModuleId; |
448 | } | 442 | } |
449 | 443 | ||
450 | impl HasModule for ContainerId { | ||
451 | fn module(&self, db: &dyn db::DefDatabase) -> ModuleId { | ||
452 | match *self { | ||
453 | ContainerId::ModuleId(it) => it, | ||
454 | ContainerId::DefWithBodyId(it) => it.module(db), | ||
455 | } | ||
456 | } | ||
457 | } | ||
458 | |||
459 | impl HasModule for AssocContainerId { | 444 | impl HasModule for AssocContainerId { |
460 | fn module(&self, db: &dyn db::DefDatabase) -> ModuleId { | 445 | fn module(&self, db: &dyn db::DefDatabase) -> ModuleId { |
461 | match *self { | 446 | match *self { |
462 | AssocContainerId::ContainerId(it) => it.module(db), | 447 | AssocContainerId::ModuleId(it) => it, |
463 | AssocContainerId::ImplId(it) => it.lookup(db).container.module(db), | 448 | AssocContainerId::ImplId(it) => it.lookup(db).container, |
464 | AssocContainerId::TraitId(it) => it.lookup(db).container.module(db), | 449 | AssocContainerId::TraitId(it) => it.lookup(db).container, |
465 | } | 450 | } |
466 | } | 451 | } |
467 | } | 452 | } |
@@ -479,16 +464,15 @@ impl HasModule for AdtId { | |||
479 | AdtId::UnionId(it) => it.lookup(db).container, | 464 | AdtId::UnionId(it) => it.lookup(db).container, |
480 | AdtId::EnumId(it) => it.lookup(db).container, | 465 | AdtId::EnumId(it) => it.lookup(db).container, |
481 | } | 466 | } |
482 | .module(db) | ||
483 | } | 467 | } |
484 | } | 468 | } |
485 | 469 | ||
486 | impl HasModule for VariantId { | 470 | impl HasModule for VariantId { |
487 | fn module(&self, db: &dyn db::DefDatabase) -> ModuleId { | 471 | fn module(&self, db: &dyn db::DefDatabase) -> ModuleId { |
488 | match self { | 472 | match self { |
489 | VariantId::EnumVariantId(it) => it.parent.lookup(db).container.module(db), | 473 | VariantId::EnumVariantId(it) => it.parent.lookup(db).container, |
490 | VariantId::StructId(it) => it.lookup(db).container.module(db), | 474 | VariantId::StructId(it) => it.lookup(db).container, |
491 | VariantId::UnionId(it) => it.lookup(db).container.module(db), | 475 | VariantId::UnionId(it) => it.lookup(db).container, |
492 | } | 476 | } |
493 | } | 477 | } |
494 | } | 478 | } |
@@ -518,18 +502,18 @@ impl HasModule for GenericDefId { | |||
518 | match self { | 502 | match self { |
519 | GenericDefId::FunctionId(it) => it.lookup(db).module(db), | 503 | GenericDefId::FunctionId(it) => it.lookup(db).module(db), |
520 | GenericDefId::AdtId(it) => it.module(db), | 504 | GenericDefId::AdtId(it) => it.module(db), |
521 | GenericDefId::TraitId(it) => it.lookup(db).container.module(db), | 505 | GenericDefId::TraitId(it) => it.lookup(db).container, |
522 | GenericDefId::TypeAliasId(it) => it.lookup(db).module(db), | 506 | GenericDefId::TypeAliasId(it) => it.lookup(db).module(db), |
523 | GenericDefId::ImplId(it) => it.lookup(db).container.module(db), | 507 | GenericDefId::ImplId(it) => it.lookup(db).container, |
524 | GenericDefId::EnumVariantId(it) => it.parent.lookup(db).container.module(db), | 508 | GenericDefId::EnumVariantId(it) => it.parent.lookup(db).container, |
525 | GenericDefId::ConstId(it) => it.lookup(db).module(db), | 509 | GenericDefId::ConstId(it) => it.lookup(db).module(db), |
526 | } | 510 | } |
527 | } | 511 | } |
528 | } | 512 | } |
529 | 513 | ||
530 | impl HasModule for StaticLoc { | 514 | impl HasModule for StaticLoc { |
531 | fn module(&self, db: &dyn db::DefDatabase) -> ModuleId { | 515 | fn module(&self, _db: &dyn db::DefDatabase) -> ModuleId { |
532 | self.container.module(db) | 516 | self.container |
533 | } | 517 | } |
534 | } | 518 | } |
535 | 519 | ||
@@ -542,10 +526,10 @@ impl ModuleDefId { | |||
542 | ModuleDefId::ModuleId(id) => *id, | 526 | ModuleDefId::ModuleId(id) => *id, |
543 | ModuleDefId::FunctionId(id) => id.lookup(db).module(db), | 527 | ModuleDefId::FunctionId(id) => id.lookup(db).module(db), |
544 | ModuleDefId::AdtId(id) => id.module(db), | 528 | ModuleDefId::AdtId(id) => id.module(db), |
545 | ModuleDefId::EnumVariantId(id) => id.parent.lookup(db).container.module(db), | 529 | ModuleDefId::EnumVariantId(id) => id.parent.lookup(db).container, |
546 | ModuleDefId::ConstId(id) => id.lookup(db).container.module(db), | 530 | ModuleDefId::ConstId(id) => id.lookup(db).container.module(db), |
547 | ModuleDefId::StaticId(id) => id.lookup(db).container.module(db), | 531 | ModuleDefId::StaticId(id) => id.lookup(db).container, |
548 | ModuleDefId::TraitId(id) => id.lookup(db).container.module(db), | 532 | ModuleDefId::TraitId(id) => id.lookup(db).container, |
549 | ModuleDefId::TypeAliasId(id) => id.lookup(db).module(db), | 533 | ModuleDefId::TypeAliasId(id) => id.lookup(db).module(db), |
550 | ModuleDefId::BuiltinType(_) => return None, | 534 | ModuleDefId::BuiltinType(_) => return None, |
551 | }) | 535 | }) |
@@ -559,12 +543,12 @@ impl AttrDefId { | |||
559 | AttrDefId::FieldId(it) => it.parent.module(db).krate, | 543 | AttrDefId::FieldId(it) => it.parent.module(db).krate, |
560 | AttrDefId::AdtId(it) => it.module(db).krate, | 544 | AttrDefId::AdtId(it) => it.module(db).krate, |
561 | AttrDefId::FunctionId(it) => it.lookup(db).module(db).krate, | 545 | AttrDefId::FunctionId(it) => it.lookup(db).module(db).krate, |
562 | AttrDefId::EnumVariantId(it) => it.parent.lookup(db).container.module(db).krate, | 546 | AttrDefId::EnumVariantId(it) => it.parent.lookup(db).container.krate, |
563 | AttrDefId::StaticId(it) => it.lookup(db).module(db).krate, | 547 | AttrDefId::StaticId(it) => it.lookup(db).module(db).krate, |
564 | AttrDefId::ConstId(it) => it.lookup(db).module(db).krate, | 548 | AttrDefId::ConstId(it) => it.lookup(db).module(db).krate, |
565 | AttrDefId::TraitId(it) => it.lookup(db).container.module(db).krate, | 549 | AttrDefId::TraitId(it) => it.lookup(db).container.krate, |
566 | AttrDefId::TypeAliasId(it) => it.lookup(db).module(db).krate, | 550 | AttrDefId::TypeAliasId(it) => it.lookup(db).module(db).krate, |
567 | AttrDefId::ImplId(it) => it.lookup(db).container.module(db).krate, | 551 | AttrDefId::ImplId(it) => it.lookup(db).container.krate, |
568 | AttrDefId::GenericParamId(it) => { | 552 | AttrDefId::GenericParamId(it) => { |
569 | match it { | 553 | match it { |
570 | GenericParamId::TypeParamId(it) => it.parent, | 554 | GenericParamId::TypeParamId(it) => it.parent, |
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs index e51d89b43..9ed48c506 100644 --- a/crates/hir_def/src/nameres/collector.rs +++ b/crates/hir_def/src/nameres/collector.rs | |||
@@ -18,7 +18,6 @@ use hir_expand::{ | |||
18 | use hir_expand::{InFile, MacroCallLoc}; | 18 | use hir_expand::{InFile, MacroCallLoc}; |
19 | use rustc_hash::{FxHashMap, FxHashSet}; | 19 | use rustc_hash::{FxHashMap, FxHashSet}; |
20 | use syntax::ast; | 20 | use syntax::ast; |
21 | use test_utils::mark; | ||
22 | use tt::{Leaf, TokenTree}; | 21 | use tt::{Leaf, TokenTree}; |
23 | 22 | ||
24 | use crate::{ | 23 | use crate::{ |
@@ -38,9 +37,9 @@ use crate::{ | |||
38 | path::{ImportAlias, ModPath, PathKind}, | 37 | path::{ImportAlias, ModPath, PathKind}, |
39 | per_ns::PerNs, | 38 | per_ns::PerNs, |
40 | visibility::{RawVisibility, Visibility}, | 39 | visibility::{RawVisibility, Visibility}, |
41 | AdtId, AstId, AstIdWithPath, ConstLoc, ContainerId, EnumLoc, EnumVariantId, FunctionLoc, | 40 | AdtId, AstId, AstIdWithPath, ConstLoc, EnumLoc, EnumVariantId, FunctionLoc, ImplLoc, Intern, |
42 | ImplLoc, Intern, LocalModuleId, ModuleDefId, StaticLoc, StructLoc, TraitLoc, TypeAliasLoc, | 41 | LocalModuleId, ModuleDefId, StaticLoc, StructLoc, TraitLoc, TypeAliasLoc, UnionLoc, |
43 | UnionLoc, UnresolvedMacro, | 42 | UnresolvedMacro, |
44 | }; | 43 | }; |
45 | 44 | ||
46 | const GLOB_RECURSION_LIMIT: usize = 100; | 45 | const GLOB_RECURSION_LIMIT: usize = 100; |
@@ -462,7 +461,7 @@ impl DefCollector<'_> { | |||
462 | let res = self.def_map.resolve_name_in_extern_prelude(&extern_crate.name); | 461 | let res = self.def_map.resolve_name_in_extern_prelude(&extern_crate.name); |
463 | 462 | ||
464 | if let Some(ModuleDefId::ModuleId(m)) = res.take_types() { | 463 | if let Some(ModuleDefId::ModuleId(m)) = res.take_types() { |
465 | mark::hit!(macro_rules_from_other_crates_are_visible_with_macro_use); | 464 | cov_mark::hit!(macro_rules_from_other_crates_are_visible_with_macro_use); |
466 | self.import_all_macros_exported(current_module_id, m.krate); | 465 | self.import_all_macros_exported(current_module_id, m.krate); |
467 | } | 466 | } |
468 | } | 467 | } |
@@ -571,10 +570,10 @@ impl DefCollector<'_> { | |||
571 | match def.take_types() { | 570 | match def.take_types() { |
572 | Some(ModuleDefId::ModuleId(m)) => { | 571 | Some(ModuleDefId::ModuleId(m)) => { |
573 | if import.is_prelude { | 572 | if import.is_prelude { |
574 | mark::hit!(std_prelude); | 573 | cov_mark::hit!(std_prelude); |
575 | self.def_map.prelude = Some(m); | 574 | self.def_map.prelude = Some(m); |
576 | } else if m.krate != self.def_map.krate { | 575 | } else if m.krate != self.def_map.krate { |
577 | mark::hit!(glob_across_crates); | 576 | cov_mark::hit!(glob_across_crates); |
578 | // glob import from other crate => we can just import everything once | 577 | // glob import from other crate => we can just import everything once |
579 | let item_map = m.def_map(self.db); | 578 | let item_map = m.def_map(self.db); |
580 | let scope = &item_map[m.local_id].scope; | 579 | let scope = &item_map[m.local_id].scope; |
@@ -626,7 +625,7 @@ impl DefCollector<'_> { | |||
626 | } | 625 | } |
627 | } | 626 | } |
628 | Some(ModuleDefId::AdtId(AdtId::EnumId(e))) => { | 627 | Some(ModuleDefId::AdtId(AdtId::EnumId(e))) => { |
629 | mark::hit!(glob_enum); | 628 | cov_mark::hit!(glob_enum); |
630 | // glob import from enum => just import all the variants | 629 | // glob import from enum => just import all the variants |
631 | 630 | ||
632 | // XXX: urgh, so this works by accident! Here, we look at | 631 | // XXX: urgh, so this works by accident! Here, we look at |
@@ -675,7 +674,7 @@ impl DefCollector<'_> { | |||
675 | 674 | ||
676 | self.update(module_id, &[(name, def)], vis, ImportType::Named); | 675 | self.update(module_id, &[(name, def)], vis, ImportType::Named); |
677 | } | 676 | } |
678 | None => mark::hit!(bogus_paths), | 677 | None => cov_mark::hit!(bogus_paths), |
679 | } | 678 | } |
680 | } | 679 | } |
681 | } | 680 | } |
@@ -738,7 +737,7 @@ impl DefCollector<'_> { | |||
738 | if max_vis == old_vis { | 737 | if max_vis == old_vis { |
739 | false | 738 | false |
740 | } else { | 739 | } else { |
741 | mark::hit!(upgrade_underscore_visibility); | 740 | cov_mark::hit!(upgrade_underscore_visibility); |
742 | true | 741 | true |
743 | } | 742 | } |
744 | } | 743 | } |
@@ -866,7 +865,7 @@ impl DefCollector<'_> { | |||
866 | depth: usize, | 865 | depth: usize, |
867 | ) { | 866 | ) { |
868 | if depth > EXPANSION_DEPTH_LIMIT { | 867 | if depth > EXPANSION_DEPTH_LIMIT { |
869 | mark::hit!(macro_expansion_overflow); | 868 | cov_mark::hit!(macro_expansion_overflow); |
870 | log::warn!("macro expansion is too deep"); | 869 | log::warn!("macro expansion is too deep"); |
871 | return; | 870 | return; |
872 | } | 871 | } |
@@ -1009,7 +1008,7 @@ impl ModCollector<'_, '_> { | |||
1009 | // Prelude module is always considered to be `#[macro_use]`. | 1008 | // Prelude module is always considered to be `#[macro_use]`. |
1010 | if let Some(prelude_module) = self.def_collector.def_map.prelude { | 1009 | if let Some(prelude_module) = self.def_collector.def_map.prelude { |
1011 | if prelude_module.krate != self.def_collector.def_map.krate { | 1010 | if prelude_module.krate != self.def_collector.def_map.krate { |
1012 | mark::hit!(prelude_is_macro_use); | 1011 | cov_mark::hit!(prelude_is_macro_use); |
1013 | self.def_collector.import_all_macros_exported(self.module_id, prelude_module.krate); | 1012 | self.def_collector.import_all_macros_exported(self.module_id, prelude_module.krate); |
1014 | } | 1013 | } |
1015 | } | 1014 | } |
@@ -1043,7 +1042,6 @@ impl ModCollector<'_, '_> { | |||
1043 | } | 1042 | } |
1044 | } | 1043 | } |
1045 | let module = self.def_collector.def_map.module_id(self.module_id); | 1044 | let module = self.def_collector.def_map.module_id(self.module_id); |
1046 | let container = ContainerId::ModuleId(module); | ||
1047 | 1045 | ||
1048 | let mut def = None; | 1046 | let mut def = None; |
1049 | match item { | 1047 | match item { |
@@ -1110,9 +1108,9 @@ impl ModCollector<'_, '_> { | |||
1110 | } | 1108 | } |
1111 | ModItem::Impl(imp) => { | 1109 | ModItem::Impl(imp) => { |
1112 | let module = self.def_collector.def_map.module_id(self.module_id); | 1110 | let module = self.def_collector.def_map.module_id(self.module_id); |
1113 | let container = ContainerId::ModuleId(module); | 1111 | let impl_id = |
1114 | let impl_id = ImplLoc { container, id: ItemTreeId::new(self.file_id, imp) } | 1112 | ImplLoc { container: module, id: ItemTreeId::new(self.file_id, imp) } |
1115 | .intern(self.def_collector.db); | 1113 | .intern(self.def_collector.db); |
1116 | self.def_collector.def_map.modules[self.module_id].scope.define_impl(impl_id) | 1114 | self.def_collector.def_map.modules[self.module_id].scope.define_impl(impl_id) |
1117 | } | 1115 | } |
1118 | ModItem::Function(id) => { | 1116 | ModItem::Function(id) => { |
@@ -1122,7 +1120,7 @@ impl ModCollector<'_, '_> { | |||
1122 | 1120 | ||
1123 | def = Some(DefData { | 1121 | def = Some(DefData { |
1124 | id: FunctionLoc { | 1122 | id: FunctionLoc { |
1125 | container: container.into(), | 1123 | container: module.into(), |
1126 | id: ItemTreeId::new(self.file_id, id), | 1124 | id: ItemTreeId::new(self.file_id, id), |
1127 | } | 1125 | } |
1128 | .intern(self.def_collector.db) | 1126 | .intern(self.def_collector.db) |
@@ -1141,7 +1139,7 @@ impl ModCollector<'_, '_> { | |||
1141 | self.collect_derives(&attrs, it.ast_id.upcast()); | 1139 | self.collect_derives(&attrs, it.ast_id.upcast()); |
1142 | 1140 | ||
1143 | def = Some(DefData { | 1141 | def = Some(DefData { |
1144 | id: StructLoc { container, id: ItemTreeId::new(self.file_id, id) } | 1142 | id: StructLoc { container: module, id: ItemTreeId::new(self.file_id, id) } |
1145 | .intern(self.def_collector.db) | 1143 | .intern(self.def_collector.db) |
1146 | .into(), | 1144 | .into(), |
1147 | name: &it.name, | 1145 | name: &it.name, |
@@ -1158,7 +1156,7 @@ impl ModCollector<'_, '_> { | |||
1158 | self.collect_derives(&attrs, it.ast_id.upcast()); | 1156 | self.collect_derives(&attrs, it.ast_id.upcast()); |
1159 | 1157 | ||
1160 | def = Some(DefData { | 1158 | def = Some(DefData { |
1161 | id: UnionLoc { container, id: ItemTreeId::new(self.file_id, id) } | 1159 | id: UnionLoc { container: module, id: ItemTreeId::new(self.file_id, id) } |
1162 | .intern(self.def_collector.db) | 1160 | .intern(self.def_collector.db) |
1163 | .into(), | 1161 | .into(), |
1164 | name: &it.name, | 1162 | name: &it.name, |
@@ -1175,7 +1173,7 @@ impl ModCollector<'_, '_> { | |||
1175 | self.collect_derives(&attrs, it.ast_id.upcast()); | 1173 | self.collect_derives(&attrs, it.ast_id.upcast()); |
1176 | 1174 | ||
1177 | def = Some(DefData { | 1175 | def = Some(DefData { |
1178 | id: EnumLoc { container, id: ItemTreeId::new(self.file_id, id) } | 1176 | id: EnumLoc { container: module, id: ItemTreeId::new(self.file_id, id) } |
1179 | .intern(self.def_collector.db) | 1177 | .intern(self.def_collector.db) |
1180 | .into(), | 1178 | .into(), |
1181 | name: &it.name, | 1179 | name: &it.name, |
@@ -1189,7 +1187,7 @@ impl ModCollector<'_, '_> { | |||
1189 | if let Some(name) = &it.name { | 1187 | if let Some(name) = &it.name { |
1190 | def = Some(DefData { | 1188 | def = Some(DefData { |
1191 | id: ConstLoc { | 1189 | id: ConstLoc { |
1192 | container: container.into(), | 1190 | container: module.into(), |
1193 | id: ItemTreeId::new(self.file_id, id), | 1191 | id: ItemTreeId::new(self.file_id, id), |
1194 | } | 1192 | } |
1195 | .intern(self.def_collector.db) | 1193 | .intern(self.def_collector.db) |
@@ -1204,7 +1202,7 @@ impl ModCollector<'_, '_> { | |||
1204 | let it = &self.item_tree[id]; | 1202 | let it = &self.item_tree[id]; |
1205 | 1203 | ||
1206 | def = Some(DefData { | 1204 | def = Some(DefData { |
1207 | id: StaticLoc { container, id: ItemTreeId::new(self.file_id, id) } | 1205 | id: StaticLoc { container: module, id: ItemTreeId::new(self.file_id, id) } |
1208 | .intern(self.def_collector.db) | 1206 | .intern(self.def_collector.db) |
1209 | .into(), | 1207 | .into(), |
1210 | name: &it.name, | 1208 | name: &it.name, |
@@ -1216,7 +1214,7 @@ impl ModCollector<'_, '_> { | |||
1216 | let it = &self.item_tree[id]; | 1214 | let it = &self.item_tree[id]; |
1217 | 1215 | ||
1218 | def = Some(DefData { | 1216 | def = Some(DefData { |
1219 | id: TraitLoc { container, id: ItemTreeId::new(self.file_id, id) } | 1217 | id: TraitLoc { container: module, id: ItemTreeId::new(self.file_id, id) } |
1220 | .intern(self.def_collector.db) | 1218 | .intern(self.def_collector.db) |
1221 | .into(), | 1219 | .into(), |
1222 | name: &it.name, | 1220 | name: &it.name, |
@@ -1229,7 +1227,7 @@ impl ModCollector<'_, '_> { | |||
1229 | 1227 | ||
1230 | def = Some(DefData { | 1228 | def = Some(DefData { |
1231 | id: TypeAliasLoc { | 1229 | id: TypeAliasLoc { |
1232 | container: container.into(), | 1230 | container: module.into(), |
1233 | id: ItemTreeId::new(self.file_id, id), | 1231 | id: ItemTreeId::new(self.file_id, id), |
1234 | } | 1232 | } |
1235 | .intern(self.def_collector.db) | 1233 | .intern(self.def_collector.db) |
diff --git a/crates/hir_def/src/nameres/mod_resolution.rs b/crates/hir_def/src/nameres/mod_resolution.rs index af3262439..d5de9899c 100644 --- a/crates/hir_def/src/nameres/mod_resolution.rs +++ b/crates/hir_def/src/nameres/mod_resolution.rs | |||
@@ -2,7 +2,6 @@ | |||
2 | use base_db::{AnchoredPath, FileId}; | 2 | use base_db::{AnchoredPath, FileId}; |
3 | use hir_expand::name::Name; | 3 | use hir_expand::name::Name; |
4 | use syntax::SmolStr; | 4 | use syntax::SmolStr; |
5 | use test_utils::mark; | ||
6 | 5 | ||
7 | use crate::{db::DefDatabase, HirFileId}; | 6 | use crate::{db::DefDatabase, HirFileId}; |
8 | 7 | ||
@@ -28,7 +27,7 @@ impl ModDir { | |||
28 | let depth = self.depth + 1; | 27 | let depth = self.depth + 1; |
29 | if depth > MOD_DEPTH_LIMIT { | 28 | if depth > MOD_DEPTH_LIMIT { |
30 | log::error!("MOD_DEPTH_LIMIT exceeded"); | 29 | log::error!("MOD_DEPTH_LIMIT exceeded"); |
31 | mark::hit!(circular_mods); | 30 | cov_mark::hit!(circular_mods); |
32 | return None; | 31 | return None; |
33 | } | 32 | } |
34 | Some(ModDir { dir_path, root_non_dir_owner, depth }) | 33 | Some(ModDir { dir_path, root_non_dir_owner, depth }) |
diff --git a/crates/hir_def/src/nameres/path_resolution.rs b/crates/hir_def/src/nameres/path_resolution.rs index dd1db0094..db459b1ed 100644 --- a/crates/hir_def/src/nameres/path_resolution.rs +++ b/crates/hir_def/src/nameres/path_resolution.rs | |||
@@ -13,7 +13,6 @@ | |||
13 | use base_db::Edition; | 13 | use base_db::Edition; |
14 | use hir_expand::name; | 14 | use hir_expand::name; |
15 | use hir_expand::name::Name; | 15 | use hir_expand::name::Name; |
16 | use test_utils::mark; | ||
17 | 16 | ||
18 | use crate::{ | 17 | use crate::{ |
19 | db::DefDatabase, | 18 | db::DefDatabase, |
@@ -63,7 +62,7 @@ impl ResolvePathResult { | |||
63 | impl DefMap { | 62 | impl DefMap { |
64 | pub(super) fn resolve_name_in_extern_prelude(&self, name: &Name) -> PerNs { | 63 | pub(super) fn resolve_name_in_extern_prelude(&self, name: &Name) -> PerNs { |
65 | if name == &name!(self) { | 64 | if name == &name!(self) { |
66 | mark::hit!(extern_crate_self_as); | 65 | cov_mark::hit!(extern_crate_self_as); |
67 | return PerNs::types(self.module_id(self.root).into(), Visibility::Public); | 66 | return PerNs::types(self.module_id(self.root).into(), Visibility::Public); |
68 | } | 67 | } |
69 | self.extern_prelude | 68 | self.extern_prelude |
@@ -101,7 +100,7 @@ impl DefMap { | |||
101 | // DefMap they're written in, so we restrict them when that happens. | 100 | // DefMap they're written in, so we restrict them when that happens. |
102 | if let Visibility::Module(m) = vis { | 101 | if let Visibility::Module(m) = vis { |
103 | if self.block_id() != m.block { | 102 | if self.block_id() != m.block { |
104 | mark::hit!(adjust_vis_in_block_def_map); | 103 | cov_mark::hit!(adjust_vis_in_block_def_map); |
105 | vis = Visibility::Module(self.module_id(self.root())); | 104 | vis = Visibility::Module(self.module_id(self.root())); |
106 | log::debug!("visibility {:?} points outside DefMap, adjusting to {:?}", m, vis); | 105 | log::debug!("visibility {:?} points outside DefMap, adjusting to {:?}", m, vis); |
107 | } | 106 | } |
@@ -157,7 +156,7 @@ impl DefMap { | |||
157 | } | 156 | } |
158 | } | 157 | } |
159 | 158 | ||
160 | pub(super) fn resolve_path_fp_with_macro_single( | 159 | fn resolve_path_fp_with_macro_single( |
161 | &self, | 160 | &self, |
162 | db: &dyn DefDatabase, | 161 | db: &dyn DefDatabase, |
163 | mode: ResolveMode, | 162 | mode: ResolveMode, |
@@ -169,12 +168,12 @@ impl DefMap { | |||
169 | let mut curr_per_ns: PerNs = match path.kind { | 168 | let mut curr_per_ns: PerNs = match path.kind { |
170 | PathKind::DollarCrate(krate) => { | 169 | PathKind::DollarCrate(krate) => { |
171 | if krate == self.krate { | 170 | if krate == self.krate { |
172 | mark::hit!(macro_dollar_crate_self); | 171 | cov_mark::hit!(macro_dollar_crate_self); |
173 | PerNs::types(self.crate_root(db).into(), Visibility::Public) | 172 | PerNs::types(self.crate_root(db).into(), Visibility::Public) |
174 | } else { | 173 | } else { |
175 | let def_map = db.crate_def_map(krate); | 174 | let def_map = db.crate_def_map(krate); |
176 | let module = def_map.module_id(def_map.root); | 175 | let module = def_map.module_id(def_map.root); |
177 | mark::hit!(macro_dollar_crate_other); | 176 | cov_mark::hit!(macro_dollar_crate_other); |
178 | PerNs::types(module.into(), Visibility::Public) | 177 | PerNs::types(module.into(), Visibility::Public) |
179 | } | 178 | } |
180 | } | 179 | } |
@@ -310,7 +309,7 @@ impl DefMap { | |||
310 | } | 309 | } |
311 | ModuleDefId::AdtId(AdtId::EnumId(e)) => { | 310 | ModuleDefId::AdtId(AdtId::EnumId(e)) => { |
312 | // enum variant | 311 | // enum variant |
313 | mark::hit!(can_import_enum_variant); | 312 | cov_mark::hit!(can_import_enum_variant); |
314 | let enum_data = db.enum_data(e); | 313 | let enum_data = db.enum_data(e); |
315 | match enum_data.variant(&segment) { | 314 | match enum_data.variant(&segment) { |
316 | Some(local_id) => { | 315 | Some(local_id) => { |
@@ -385,10 +384,16 @@ impl DefMap { | |||
385 | } | 384 | } |
386 | } | 385 | } |
387 | }; | 386 | }; |
388 | let from_extern_prelude = self | 387 | // Give precedence to names in outer `DefMap`s over the extern prelude; only check prelude |
389 | .extern_prelude | 388 | // from the crate DefMap. |
390 | .get(name) | 389 | let from_extern_prelude = match self.block { |
391 | .map_or(PerNs::none(), |&it| PerNs::types(it, Visibility::Public)); | 390 | Some(_) => PerNs::none(), |
391 | None => self | ||
392 | .extern_prelude | ||
393 | .get(name) | ||
394 | .map_or(PerNs::none(), |&it| PerNs::types(it, Visibility::Public)), | ||
395 | }; | ||
396 | |||
392 | let from_prelude = self.resolve_in_prelude(db, name); | 397 | let from_prelude = self.resolve_in_prelude(db, name); |
393 | 398 | ||
394 | from_legacy_macro.or(from_scope_or_builtin).or(from_extern_prelude).or(from_prelude) | 399 | from_legacy_macro.or(from_scope_or_builtin).or(from_extern_prelude).or(from_prelude) |
diff --git a/crates/hir_def/src/nameres/tests.rs b/crates/hir_def/src/nameres/tests.rs index bd3e2701b..de3aa4f9a 100644 --- a/crates/hir_def/src/nameres/tests.rs +++ b/crates/hir_def/src/nameres/tests.rs | |||
@@ -9,7 +9,6 @@ use std::sync::Arc; | |||
9 | 9 | ||
10 | use base_db::{fixture::WithFixture, SourceDatabase}; | 10 | use base_db::{fixture::WithFixture, SourceDatabase}; |
11 | use expect_test::{expect, Expect}; | 11 | use expect_test::{expect, Expect}; |
12 | use test_utils::mark; | ||
13 | 12 | ||
14 | use crate::{db::DefDatabase, test_db::TestDB}; | 13 | use crate::{db::DefDatabase, test_db::TestDB}; |
15 | 14 | ||
@@ -136,7 +135,7 @@ mod m { | |||
136 | 135 | ||
137 | #[test] | 136 | #[test] |
138 | fn bogus_paths() { | 137 | fn bogus_paths() { |
139 | mark::check!(bogus_paths); | 138 | cov_mark::check!(bogus_paths); |
140 | check( | 139 | check( |
141 | r#" | 140 | r#" |
142 | //- /lib.rs | 141 | //- /lib.rs |
@@ -243,7 +242,7 @@ pub struct Baz; | |||
243 | 242 | ||
244 | #[test] | 243 | #[test] |
245 | fn std_prelude() { | 244 | fn std_prelude() { |
246 | mark::check!(std_prelude); | 245 | cov_mark::check!(std_prelude); |
247 | check( | 246 | check( |
248 | r#" | 247 | r#" |
249 | //- /main.rs crate:main deps:test_crate | 248 | //- /main.rs crate:main deps:test_crate |
@@ -267,7 +266,7 @@ pub enum Foo { Bar, Baz }; | |||
267 | 266 | ||
268 | #[test] | 267 | #[test] |
269 | fn can_import_enum_variant() { | 268 | fn can_import_enum_variant() { |
270 | mark::check!(can_import_enum_variant); | 269 | cov_mark::check!(can_import_enum_variant); |
271 | check( | 270 | check( |
272 | r#" | 271 | r#" |
273 | enum E { V } | 272 | enum E { V } |
@@ -628,7 +627,7 @@ use crate::reex::*; | |||
628 | 627 | ||
629 | #[test] | 628 | #[test] |
630 | fn underscore_pub_crate_reexport() { | 629 | fn underscore_pub_crate_reexport() { |
631 | mark::check!(upgrade_underscore_visibility); | 630 | cov_mark::check!(upgrade_underscore_visibility); |
632 | check( | 631 | check( |
633 | r#" | 632 | r#" |
634 | //- /main.rs crate:main deps:lib | 633 | //- /main.rs crate:main deps:lib |
diff --git a/crates/hir_def/src/nameres/tests/diagnostics.rs b/crates/hir_def/src/nameres/tests/diagnostics.rs index e8e72e5ef..d5ef8ceb5 100644 --- a/crates/hir_def/src/nameres/tests/diagnostics.rs +++ b/crates/hir_def/src/nameres/tests/diagnostics.rs | |||
@@ -1,5 +1,4 @@ | |||
1 | use base_db::fixture::WithFixture; | 1 | use base_db::fixture::WithFixture; |
2 | use test_utils::mark; | ||
3 | 2 | ||
4 | use crate::test_db::TestDB; | 3 | use crate::test_db::TestDB; |
5 | 4 | ||
@@ -63,7 +62,7 @@ fn unresolved_extern_crate() { | |||
63 | 62 | ||
64 | #[test] | 63 | #[test] |
65 | fn extern_crate_self_as() { | 64 | fn extern_crate_self_as() { |
66 | mark::check!(extern_crate_self_as); | 65 | cov_mark::check!(extern_crate_self_as); |
67 | check_diagnostics( | 66 | check_diagnostics( |
68 | r" | 67 | r" |
69 | //- /lib.rs | 68 | //- /lib.rs |
@@ -140,7 +139,7 @@ fn inactive_item() { | |||
140 | /// Tests that `cfg` attributes behind `cfg_attr` is handled properly. | 139 | /// Tests that `cfg` attributes behind `cfg_attr` is handled properly. |
141 | #[test] | 140 | #[test] |
142 | fn inactive_via_cfg_attr() { | 141 | fn inactive_via_cfg_attr() { |
143 | mark::check!(cfg_attr_active); | 142 | cov_mark::check!(cfg_attr_active); |
144 | check_diagnostics( | 143 | check_diagnostics( |
145 | r#" | 144 | r#" |
146 | //- /lib.rs | 145 | //- /lib.rs |
diff --git a/crates/hir_def/src/nameres/tests/globs.rs b/crates/hir_def/src/nameres/tests/globs.rs index 2ae836e3c..17426d54d 100644 --- a/crates/hir_def/src/nameres/tests/globs.rs +++ b/crates/hir_def/src/nameres/tests/globs.rs | |||
@@ -148,7 +148,7 @@ pub(crate) struct PubCrateStruct; | |||
148 | 148 | ||
149 | #[test] | 149 | #[test] |
150 | fn glob_across_crates() { | 150 | fn glob_across_crates() { |
151 | mark::check!(glob_across_crates); | 151 | cov_mark::check!(glob_across_crates); |
152 | check( | 152 | check( |
153 | r#" | 153 | r#" |
154 | //- /main.rs crate:main deps:test_crate | 154 | //- /main.rs crate:main deps:test_crate |
@@ -184,7 +184,7 @@ struct Foo; | |||
184 | 184 | ||
185 | #[test] | 185 | #[test] |
186 | fn glob_enum() { | 186 | fn glob_enum() { |
187 | mark::check!(glob_enum); | 187 | cov_mark::check!(glob_enum); |
188 | check( | 188 | check( |
189 | r#" | 189 | r#" |
190 | enum Foo { Bar, Baz } | 190 | enum Foo { Bar, Baz } |
@@ -201,7 +201,7 @@ use self::Foo::*; | |||
201 | 201 | ||
202 | #[test] | 202 | #[test] |
203 | fn glob_enum_group() { | 203 | fn glob_enum_group() { |
204 | mark::check!(glob_enum_group); | 204 | cov_mark::check!(glob_enum_group); |
205 | check( | 205 | check( |
206 | r#" | 206 | r#" |
207 | enum Foo { Bar, Baz } | 207 | enum Foo { Bar, Baz } |
@@ -218,7 +218,7 @@ use self::Foo::{*}; | |||
218 | 218 | ||
219 | #[test] | 219 | #[test] |
220 | fn glob_shadowed_def() { | 220 | fn glob_shadowed_def() { |
221 | mark::check!(import_shadowed); | 221 | cov_mark::check!(import_shadowed); |
222 | check( | 222 | check( |
223 | r#" | 223 | r#" |
224 | //- /lib.rs | 224 | //- /lib.rs |
diff --git a/crates/hir_def/src/nameres/tests/macros.rs b/crates/hir_def/src/nameres/tests/macros.rs index 36ed5e8ce..f65a655bf 100644 --- a/crates/hir_def/src/nameres/tests/macros.rs +++ b/crates/hir_def/src/nameres/tests/macros.rs | |||
@@ -210,7 +210,7 @@ macro_rules! bar { | |||
210 | 210 | ||
211 | #[test] | 211 | #[test] |
212 | fn macro_rules_from_other_crates_are_visible_with_macro_use() { | 212 | fn macro_rules_from_other_crates_are_visible_with_macro_use() { |
213 | mark::check!(macro_rules_from_other_crates_are_visible_with_macro_use); | 213 | cov_mark::check!(macro_rules_from_other_crates_are_visible_with_macro_use); |
214 | check( | 214 | check( |
215 | r#" | 215 | r#" |
216 | //- /main.rs crate:main deps:foo | 216 | //- /main.rs crate:main deps:foo |
@@ -260,7 +260,7 @@ mod priv_mod { | |||
260 | 260 | ||
261 | #[test] | 261 | #[test] |
262 | fn prelude_is_macro_use() { | 262 | fn prelude_is_macro_use() { |
263 | mark::check!(prelude_is_macro_use); | 263 | cov_mark::check!(prelude_is_macro_use); |
264 | check( | 264 | check( |
265 | r#" | 265 | r#" |
266 | //- /main.rs crate:main deps:foo | 266 | //- /main.rs crate:main deps:foo |
@@ -550,7 +550,7 @@ mod m { | |||
550 | 550 | ||
551 | #[test] | 551 | #[test] |
552 | fn macro_dollar_crate_is_correct_in_item() { | 552 | fn macro_dollar_crate_is_correct_in_item() { |
553 | mark::check!(macro_dollar_crate_self); | 553 | cov_mark::check!(macro_dollar_crate_self); |
554 | check( | 554 | check( |
555 | r#" | 555 | r#" |
556 | //- /main.rs crate:main deps:foo | 556 | //- /main.rs crate:main deps:foo |
@@ -608,7 +608,7 @@ struct Baz; | |||
608 | 608 | ||
609 | #[test] | 609 | #[test] |
610 | fn macro_dollar_crate_is_correct_in_indirect_deps() { | 610 | fn macro_dollar_crate_is_correct_in_indirect_deps() { |
611 | mark::check!(macro_dollar_crate_other); | 611 | cov_mark::check!(macro_dollar_crate_other); |
612 | // From std | 612 | // From std |
613 | check( | 613 | check( |
614 | r#" | 614 | r#" |
@@ -686,7 +686,7 @@ pub trait Clone {} | |||
686 | 686 | ||
687 | #[test] | 687 | #[test] |
688 | fn macro_expansion_overflow() { | 688 | fn macro_expansion_overflow() { |
689 | mark::check!(macro_expansion_overflow); | 689 | cov_mark::check!(macro_expansion_overflow); |
690 | check( | 690 | check( |
691 | r#" | 691 | r#" |
692 | macro_rules! a { | 692 | macro_rules! a { |
diff --git a/crates/hir_def/src/nameres/tests/mod_resolution.rs b/crates/hir_def/src/nameres/tests/mod_resolution.rs index e80b593aa..dfbbad1f9 100644 --- a/crates/hir_def/src/nameres/tests/mod_resolution.rs +++ b/crates/hir_def/src/nameres/tests/mod_resolution.rs | |||
@@ -2,7 +2,7 @@ use super::*; | |||
2 | 2 | ||
3 | #[test] | 3 | #[test] |
4 | fn name_res_works_for_broken_modules() { | 4 | fn name_res_works_for_broken_modules() { |
5 | mark::check!(name_res_works_for_broken_modules); | 5 | cov_mark::check!(name_res_works_for_broken_modules); |
6 | check( | 6 | check( |
7 | r" | 7 | r" |
8 | //- /lib.rs | 8 | //- /lib.rs |
@@ -774,7 +774,7 @@ struct X; | |||
774 | 774 | ||
775 | #[test] | 775 | #[test] |
776 | fn circular_mods() { | 776 | fn circular_mods() { |
777 | mark::check!(circular_mods); | 777 | cov_mark::check!(circular_mods); |
778 | compute_crate_def_map( | 778 | compute_crate_def_map( |
779 | r#" | 779 | r#" |
780 | //- /lib.rs | 780 | //- /lib.rs |
diff --git a/crates/hir_def/src/path/lower/lower_use.rs b/crates/hir_def/src/path/lower/lower_use.rs index d584b0b70..e2965b033 100644 --- a/crates/hir_def/src/path/lower/lower_use.rs +++ b/crates/hir_def/src/path/lower/lower_use.rs | |||
@@ -6,7 +6,6 @@ use std::iter; | |||
6 | use either::Either; | 6 | use either::Either; |
7 | use hir_expand::{hygiene::Hygiene, name::AsName}; | 7 | use hir_expand::{hygiene::Hygiene, name::AsName}; |
8 | use syntax::ast::{self, NameOwner}; | 8 | use syntax::ast::{self, NameOwner}; |
9 | use test_utils::mark; | ||
10 | 9 | ||
11 | use crate::path::{ImportAlias, ModPath, PathKind}; | 10 | use crate::path::{ImportAlias, ModPath, PathKind}; |
12 | 11 | ||
@@ -54,7 +53,7 @@ pub(crate) fn lower_use_tree( | |||
54 | // FIXME: report errors somewhere | 53 | // FIXME: report errors somewhere |
55 | // We get here if we do | 54 | // We get here if we do |
56 | } else if is_glob { | 55 | } else if is_glob { |
57 | mark::hit!(glob_enum_group); | 56 | cov_mark::hit!(glob_enum_group); |
58 | if let Some(prefix) = prefix { | 57 | if let Some(prefix) = prefix { |
59 | cb(prefix, &tree, is_glob, None) | 58 | cb(prefix, &tree, is_glob, None) |
60 | } | 59 | } |
diff --git a/crates/hir_def/src/resolver.rs b/crates/hir_def/src/resolver.rs index 77ff21739..42736171e 100644 --- a/crates/hir_def/src/resolver.rs +++ b/crates/hir_def/src/resolver.rs | |||
@@ -19,10 +19,10 @@ use crate::{ | |||
19 | path::{ModPath, PathKind}, | 19 | path::{ModPath, PathKind}, |
20 | per_ns::PerNs, | 20 | per_ns::PerNs, |
21 | visibility::{RawVisibility, Visibility}, | 21 | visibility::{RawVisibility, Visibility}, |
22 | AdtId, AssocContainerId, ConstId, ConstParamId, ContainerId, DefWithBodyId, EnumId, | 22 | AdtId, AssocContainerId, ConstId, ConstParamId, DefWithBodyId, EnumId, EnumVariantId, |
23 | EnumVariantId, FunctionId, GenericDefId, GenericParamId, HasModule, ImplId, LifetimeParamId, | 23 | FunctionId, GenericDefId, GenericParamId, HasModule, ImplId, LifetimeParamId, LocalModuleId, |
24 | LocalModuleId, Lookup, ModuleDefId, ModuleId, StaticId, StructId, TraitId, TypeAliasId, | 24 | Lookup, ModuleDefId, ModuleId, StaticId, StructId, TraitId, TypeAliasId, TypeParamId, |
25 | TypeParamId, VariantId, | 25 | VariantId, |
26 | }; | 26 | }; |
27 | 27 | ||
28 | #[derive(Debug, Clone, Default)] | 28 | #[derive(Debug, Clone, Default)] |
@@ -342,6 +342,16 @@ impl Resolver { | |||
342 | traits.extend(prelude_def_map[prelude.local_id].scope.traits()); | 342 | traits.extend(prelude_def_map[prelude.local_id].scope.traits()); |
343 | } | 343 | } |
344 | traits.extend(m.def_map[m.module_id].scope.traits()); | 344 | traits.extend(m.def_map[m.module_id].scope.traits()); |
345 | |||
346 | // Add all traits that are in scope because of the containing DefMaps | ||
347 | m.def_map.with_ancestor_maps(db, m.module_id, &mut |def_map, module| { | ||
348 | if let Some(prelude) = def_map.prelude() { | ||
349 | let prelude_def_map = prelude.def_map(db); | ||
350 | traits.extend(prelude_def_map[prelude.local_id].scope.traits()); | ||
351 | } | ||
352 | traits.extend(def_map[module].scope.traits()); | ||
353 | None::<()> | ||
354 | }); | ||
345 | } | 355 | } |
346 | } | 356 | } |
347 | traits | 357 | traits |
@@ -678,19 +688,10 @@ impl HasResolver for DefWithBodyId { | |||
678 | } | 688 | } |
679 | } | 689 | } |
680 | 690 | ||
681 | impl HasResolver for ContainerId { | ||
682 | fn resolver(self, db: &dyn DefDatabase) -> Resolver { | ||
683 | match self { | ||
684 | ContainerId::ModuleId(it) => it.resolver(db), | ||
685 | ContainerId::DefWithBodyId(it) => it.module(db).resolver(db), | ||
686 | } | ||
687 | } | ||
688 | } | ||
689 | |||
690 | impl HasResolver for AssocContainerId { | 691 | impl HasResolver for AssocContainerId { |
691 | fn resolver(self, db: &dyn DefDatabase) -> Resolver { | 692 | fn resolver(self, db: &dyn DefDatabase) -> Resolver { |
692 | match self { | 693 | match self { |
693 | AssocContainerId::ContainerId(it) => it.resolver(db), | 694 | AssocContainerId::ModuleId(it) => it.resolver(db), |
694 | AssocContainerId::TraitId(it) => it.resolver(db), | 695 | AssocContainerId::TraitId(it) => it.resolver(db), |
695 | AssocContainerId::ImplId(it) => it.resolver(db), | 696 | AssocContainerId::ImplId(it) => it.resolver(db), |
696 | } | 697 | } |
diff --git a/crates/hir_def/src/test_db.rs b/crates/hir_def/src/test_db.rs index eda982c85..10977761c 100644 --- a/crates/hir_def/src/test_db.rs +++ b/crates/hir_def/src/test_db.rs | |||
@@ -15,7 +15,7 @@ use rustc_hash::FxHashSet; | |||
15 | use syntax::{algo, ast, AstNode, TextRange, TextSize}; | 15 | use syntax::{algo, ast, AstNode, TextRange, TextSize}; |
16 | use test_utils::extract_annotations; | 16 | use test_utils::extract_annotations; |
17 | 17 | ||
18 | use crate::{db::DefDatabase, nameres::DefMap, Lookup, ModuleDefId, ModuleId}; | 18 | use crate::{db::DefDatabase, nameres::DefMap, src::HasSource, Lookup, ModuleDefId, ModuleId}; |
19 | 19 | ||
20 | #[salsa::database( | 20 | #[salsa::database( |
21 | base_db::SourceDatabaseExtStorage, | 21 | base_db::SourceDatabaseExtStorage, |
@@ -115,14 +115,9 @@ impl TestDB { | |||
115 | if file_id != position.file_id.into() { | 115 | if file_id != position.file_id.into() { |
116 | continue; | 116 | continue; |
117 | } | 117 | } |
118 | let root = self.parse_or_expand(file_id).unwrap(); | ||
119 | let ast_map = self.ast_id_map(file_id); | ||
120 | let item_tree = self.item_tree(file_id); | ||
121 | for decl in module.scope.declarations() { | 118 | for decl in module.scope.declarations() { |
122 | if let ModuleDefId::FunctionId(it) = decl { | 119 | if let ModuleDefId::FunctionId(it) = decl { |
123 | let ast = | 120 | let range = it.lookup(self).source(self).value.syntax().text_range(); |
124 | ast_map.get(item_tree[it.lookup(self).id.value].ast_id).to_node(&root); | ||
125 | let range = ast.syntax().text_range(); | ||
126 | 121 | ||
127 | if !range.contains(position.offset) { | 122 | if !range.contains(position.offset) { |
128 | continue; | 123 | continue; |
diff --git a/crates/hir_expand/Cargo.toml b/crates/hir_expand/Cargo.toml index 5271110d2..f649ab925 100644 --- a/crates/hir_expand/Cargo.toml +++ b/crates/hir_expand/Cargo.toml | |||
@@ -16,9 +16,13 @@ rustc-hash = "1.0.0" | |||
16 | la-arena = { version = "0.2.0", path = "../../lib/arena" } | 16 | la-arena = { version = "0.2.0", path = "../../lib/arena" } |
17 | 17 | ||
18 | base_db = { path = "../base_db", version = "0.0.0" } | 18 | base_db = { path = "../base_db", version = "0.0.0" } |
19 | cfg = { path = "../cfg", version = "0.0.0" } | ||
19 | syntax = { path = "../syntax", version = "0.0.0" } | 20 | syntax = { path = "../syntax", version = "0.0.0" } |
20 | parser = { path = "../parser", version = "0.0.0" } | 21 | parser = { path = "../parser", version = "0.0.0" } |
21 | profile = { path = "../profile", version = "0.0.0" } | 22 | profile = { path = "../profile", version = "0.0.0" } |
22 | tt = { path = "../tt", version = "0.0.0" } | 23 | tt = { path = "../tt", version = "0.0.0" } |
23 | mbe = { path = "../mbe", version = "0.0.0" } | 24 | mbe = { path = "../mbe", version = "0.0.0" } |
24 | test_utils = { path = "../test_utils", version = "0.0.0" } | 25 | |
26 | [dev-dependencies] | ||
27 | test_utils = { path = "../test_utils" } | ||
28 | expect-test = "1.1" | ||
diff --git a/crates/hir_expand/src/builtin_derive.rs b/crates/hir_expand/src/builtin_derive.rs index b7f1aae8f..dfdb9cf59 100644 --- a/crates/hir_expand/src/builtin_derive.rs +++ b/crates/hir_expand/src/builtin_derive.rs | |||
@@ -267,13 +267,14 @@ fn partial_ord_expand( | |||
267 | #[cfg(test)] | 267 | #[cfg(test)] |
268 | mod tests { | 268 | mod tests { |
269 | use base_db::{fixture::WithFixture, CrateId, SourceDatabase}; | 269 | use base_db::{fixture::WithFixture, CrateId, SourceDatabase}; |
270 | use expect_test::{expect, Expect}; | ||
270 | use name::{known, Name}; | 271 | use name::{known, Name}; |
271 | 272 | ||
272 | use crate::{test_db::TestDB, AstId, MacroCallId, MacroCallKind, MacroCallLoc}; | 273 | use crate::{test_db::TestDB, AstId, MacroCallId, MacroCallKind, MacroCallLoc}; |
273 | 274 | ||
274 | use super::*; | 275 | use super::*; |
275 | 276 | ||
276 | fn expand_builtin_derive(s: &str, name: Name) -> String { | 277 | fn expand_builtin_derive(ra_fixture: &str, name: Name) -> String { |
277 | let expander = BuiltinDeriveExpander::find_by_name(&name).unwrap(); | 278 | let expander = BuiltinDeriveExpander::find_by_name(&name).unwrap(); |
278 | let fixture = format!( | 279 | let fixture = format!( |
279 | r#"//- /main.rs crate:main deps:core | 280 | r#"//- /main.rs crate:main deps:core |
@@ -282,7 +283,7 @@ $0 | |||
282 | //- /lib.rs crate:core | 283 | //- /lib.rs crate:core |
283 | // empty | 284 | // empty |
284 | "#, | 285 | "#, |
285 | s | 286 | ra_fixture |
286 | ); | 287 | ); |
287 | 288 | ||
288 | let (db, file_pos) = TestDB::with_position(&fixture); | 289 | let (db, file_pos) = TestDB::with_position(&fixture); |
@@ -314,66 +315,57 @@ $0 | |||
314 | parsed.text().to_string() | 315 | parsed.text().to_string() |
315 | } | 316 | } |
316 | 317 | ||
318 | fn check_derive(ra_fixture: &str, name: Name, expected: Expect) { | ||
319 | let expanded = expand_builtin_derive(ra_fixture, name); | ||
320 | expected.assert_eq(&expanded); | ||
321 | } | ||
322 | |||
317 | #[test] | 323 | #[test] |
318 | fn test_copy_expand_simple() { | 324 | fn test_copy_expand_simple() { |
319 | let expanded = expand_builtin_derive( | 325 | check_derive( |
320 | r#" | 326 | r#" |
321 | #[derive(Copy)] | 327 | #[derive(Copy)] |
322 | struct Foo; | 328 | struct Foo; |
323 | "#, | 329 | "#, |
324 | known::Copy, | 330 | known::Copy, |
331 | expect![["impl< >core::marker::CopyforFoo< >{}"]], | ||
325 | ); | 332 | ); |
326 | |||
327 | assert_eq!(expanded, "impl< >core::marker::CopyforFoo< >{}"); | ||
328 | } | 333 | } |
329 | 334 | ||
330 | #[test] | 335 | #[test] |
331 | fn test_copy_expand_with_type_params() { | 336 | fn test_copy_expand_with_type_params() { |
332 | let expanded = expand_builtin_derive( | 337 | check_derive( |
333 | r#" | 338 | r#" |
334 | #[derive(Copy)] | 339 | #[derive(Copy)] |
335 | struct Foo<A, B>; | 340 | struct Foo<A, B>; |
336 | "#, | 341 | "#, |
337 | known::Copy, | 342 | known::Copy, |
338 | ); | 343 | expect![["impl<T0:core::marker::Copy,T1:core::marker::Copy>core::marker::CopyforFoo<T0,T1>{}"]], |
339 | |||
340 | assert_eq!( | ||
341 | expanded, | ||
342 | "impl<T0:core::marker::Copy,T1:core::marker::Copy>core::marker::CopyforFoo<T0,T1>{}" | ||
343 | ); | 344 | ); |
344 | } | 345 | } |
345 | 346 | ||
346 | #[test] | 347 | #[test] |
347 | fn test_copy_expand_with_lifetimes() { | 348 | fn test_copy_expand_with_lifetimes() { |
348 | let expanded = expand_builtin_derive( | 349 | check_derive( |
349 | r#" | 350 | r#" |
350 | #[derive(Copy)] | 351 | #[derive(Copy)] |
351 | struct Foo<A, B, 'a, 'b>; | 352 | struct Foo<A, B, 'a, 'b>; |
352 | "#, | 353 | "#, |
353 | known::Copy, | 354 | known::Copy, |
354 | ); | 355 | // We currently just ignore lifetimes |
355 | 356 | expect![["impl<T0:core::marker::Copy,T1:core::marker::Copy>core::marker::CopyforFoo<T0,T1>{}"]], | |
356 | // We currently just ignore lifetimes | ||
357 | |||
358 | assert_eq!( | ||
359 | expanded, | ||
360 | "impl<T0:core::marker::Copy,T1:core::marker::Copy>core::marker::CopyforFoo<T0,T1>{}" | ||
361 | ); | 357 | ); |
362 | } | 358 | } |
363 | 359 | ||
364 | #[test] | 360 | #[test] |
365 | fn test_clone_expand() { | 361 | fn test_clone_expand() { |
366 | let expanded = expand_builtin_derive( | 362 | check_derive( |
367 | r#" | 363 | r#" |
368 | #[derive(Clone)] | 364 | #[derive(Clone)] |
369 | struct Foo<A, B>; | 365 | struct Foo<A, B>; |
370 | "#, | 366 | "#, |
371 | known::Clone, | 367 | known::Clone, |
372 | ); | 368 | expect![["impl<T0:core::clone::Clone,T1:core::clone::Clone>core::clone::CloneforFoo<T0,T1>{}"]], |
373 | |||
374 | assert_eq!( | ||
375 | expanded, | ||
376 | "impl<T0:core::clone::Clone,T1:core::clone::Clone>core::clone::CloneforFoo<T0,T1>{}" | ||
377 | ); | 369 | ); |
378 | } | 370 | } |
379 | } | 371 | } |
diff --git a/crates/hir_expand/src/builtin_macro.rs b/crates/hir_expand/src/builtin_macro.rs index eb57ea7d6..2a79c892b 100644 --- a/crates/hir_expand/src/builtin_macro.rs +++ b/crates/hir_expand/src/builtin_macro.rs | |||
@@ -5,6 +5,7 @@ use crate::{ | |||
5 | }; | 5 | }; |
6 | 6 | ||
7 | use base_db::{AnchoredPath, FileId}; | 7 | use base_db::{AnchoredPath, FileId}; |
8 | use cfg::CfgExpr; | ||
8 | use either::Either; | 9 | use either::Either; |
9 | use mbe::{parse_exprs_with_sep, parse_to_token_tree, ExpandResult}; | 10 | use mbe::{parse_exprs_with_sep, parse_to_token_tree, ExpandResult}; |
10 | use parser::FragmentKind; | 11 | use parser::FragmentKind; |
@@ -97,6 +98,7 @@ register_builtin! { | |||
97 | (format_args_nl, FormatArgsNl) => format_args_expand, | 98 | (format_args_nl, FormatArgsNl) => format_args_expand, |
98 | (llvm_asm, LlvmAsm) => asm_expand, | 99 | (llvm_asm, LlvmAsm) => asm_expand, |
99 | (asm, Asm) => asm_expand, | 100 | (asm, Asm) => asm_expand, |
101 | (cfg, Cfg) => cfg_expand, | ||
100 | 102 | ||
101 | EAGER: | 103 | EAGER: |
102 | (compile_error, CompileError) => compile_error_expand, | 104 | (compile_error, CompileError) => compile_error_expand, |
@@ -258,6 +260,18 @@ fn asm_expand( | |||
258 | ExpandResult::ok(expanded) | 260 | ExpandResult::ok(expanded) |
259 | } | 261 | } |
260 | 262 | ||
263 | fn cfg_expand( | ||
264 | db: &dyn AstDatabase, | ||
265 | id: LazyMacroId, | ||
266 | tt: &tt::Subtree, | ||
267 | ) -> ExpandResult<tt::Subtree> { | ||
268 | let loc = db.lookup_intern_macro(id); | ||
269 | let expr = CfgExpr::parse(tt); | ||
270 | let enabled = db.crate_graph()[loc.krate].cfg_options.check(&expr) != Some(false); | ||
271 | let expanded = if enabled { quote!(true) } else { quote!(false) }; | ||
272 | ExpandResult::ok(expanded) | ||
273 | } | ||
274 | |||
261 | fn unquote_str(lit: &tt::Literal) -> Option<String> { | 275 | fn unquote_str(lit: &tt::Literal) -> Option<String> { |
262 | let lit = ast::make::tokens::literal(&lit.to_string()); | 276 | let lit = ast::make::tokens::literal(&lit.to_string()); |
263 | let token = ast::String::cast(lit)?; | 277 | let token = ast::String::cast(lit)?; |
@@ -477,6 +491,7 @@ mod tests { | |||
477 | MacroCallLoc, | 491 | MacroCallLoc, |
478 | }; | 492 | }; |
479 | use base_db::{fixture::WithFixture, SourceDatabase}; | 493 | use base_db::{fixture::WithFixture, SourceDatabase}; |
494 | use expect_test::{expect, Expect}; | ||
480 | use std::sync::Arc; | 495 | use std::sync::Arc; |
481 | use syntax::ast::NameOwner; | 496 | use syntax::ast::NameOwner; |
482 | 497 | ||
@@ -560,87 +575,86 @@ mod tests { | |||
560 | db.parse_or_expand(file_id).unwrap().to_string() | 575 | db.parse_or_expand(file_id).unwrap().to_string() |
561 | } | 576 | } |
562 | 577 | ||
578 | fn check_expansion(ra_fixture: &str, expect: Expect) { | ||
579 | let expansion = expand_builtin_macro(ra_fixture); | ||
580 | expect.assert_eq(&expansion); | ||
581 | } | ||
582 | |||
563 | #[test] | 583 | #[test] |
564 | fn test_column_expand() { | 584 | fn test_column_expand() { |
565 | let expanded = expand_builtin_macro( | 585 | check_expansion( |
566 | r#" | 586 | r#" |
567 | #[rustc_builtin_macro] | 587 | #[rustc_builtin_macro] |
568 | macro_rules! column {() => {}} | 588 | macro_rules! column {() => {}} |
569 | column!() | 589 | column!() |
570 | "#, | 590 | "#, |
591 | expect![["0"]], | ||
571 | ); | 592 | ); |
572 | |||
573 | assert_eq!(expanded, "0"); | ||
574 | } | 593 | } |
575 | 594 | ||
576 | #[test] | 595 | #[test] |
577 | fn test_line_expand() { | 596 | fn test_line_expand() { |
578 | let expanded = expand_builtin_macro( | 597 | check_expansion( |
579 | r#" | 598 | r#" |
580 | #[rustc_builtin_macro] | 599 | #[rustc_builtin_macro] |
581 | macro_rules! line {() => {}} | 600 | macro_rules! line {() => {}} |
582 | line!() | 601 | line!() |
583 | "#, | 602 | "#, |
603 | expect![["0"]], | ||
584 | ); | 604 | ); |
585 | |||
586 | assert_eq!(expanded, "0"); | ||
587 | } | 605 | } |
588 | 606 | ||
589 | #[test] | 607 | #[test] |
590 | fn test_stringify_expand() { | 608 | fn test_stringify_expand() { |
591 | let expanded = expand_builtin_macro( | 609 | check_expansion( |
592 | r#" | 610 | r#" |
593 | #[rustc_builtin_macro] | 611 | #[rustc_builtin_macro] |
594 | macro_rules! stringify {() => {}} | 612 | macro_rules! stringify {() => {}} |
595 | stringify!(a b c) | 613 | stringify!(a b c) |
596 | "#, | 614 | "#, |
615 | expect![["\"a b c\""]], | ||
597 | ); | 616 | ); |
598 | |||
599 | assert_eq!(expanded, "\"a b c\""); | ||
600 | } | 617 | } |
601 | 618 | ||
602 | #[test] | 619 | #[test] |
603 | fn test_env_expand() { | 620 | fn test_env_expand() { |
604 | let expanded = expand_builtin_macro( | 621 | check_expansion( |
605 | r#" | 622 | r#" |
606 | #[rustc_builtin_macro] | 623 | #[rustc_builtin_macro] |
607 | macro_rules! env {() => {}} | 624 | macro_rules! env {() => {}} |
608 | env!("TEST_ENV_VAR") | 625 | env!("TEST_ENV_VAR") |
609 | "#, | 626 | "#, |
627 | expect![["\"__RA_UNIMPLEMENTED__\""]], | ||
610 | ); | 628 | ); |
611 | |||
612 | assert_eq!(expanded, "\"__RA_UNIMPLEMENTED__\""); | ||
613 | } | 629 | } |
614 | 630 | ||
615 | #[test] | 631 | #[test] |
616 | fn test_option_env_expand() { | 632 | fn test_option_env_expand() { |
617 | let expanded = expand_builtin_macro( | 633 | check_expansion( |
618 | r#" | 634 | r#" |
619 | #[rustc_builtin_macro] | 635 | #[rustc_builtin_macro] |
620 | macro_rules! option_env {() => {}} | 636 | macro_rules! option_env {() => {}} |
621 | option_env!("TEST_ENV_VAR") | 637 | option_env!("TEST_ENV_VAR") |
622 | "#, | 638 | "#, |
639 | expect![["std::option::Option::None:: < &str>"]], | ||
623 | ); | 640 | ); |
624 | |||
625 | assert_eq!(expanded, "std::option::Option::None:: < &str>"); | ||
626 | } | 641 | } |
627 | 642 | ||
628 | #[test] | 643 | #[test] |
629 | fn test_file_expand() { | 644 | fn test_file_expand() { |
630 | let expanded = expand_builtin_macro( | 645 | check_expansion( |
631 | r#" | 646 | r#" |
632 | #[rustc_builtin_macro] | 647 | #[rustc_builtin_macro] |
633 | macro_rules! file {() => {}} | 648 | macro_rules! file {() => {}} |
634 | file!() | 649 | file!() |
635 | "#, | 650 | "#, |
651 | expect![[r#""""#]], | ||
636 | ); | 652 | ); |
637 | |||
638 | assert_eq!(expanded, "\"\""); | ||
639 | } | 653 | } |
640 | 654 | ||
641 | #[test] | 655 | #[test] |
642 | fn test_assert_expand() { | 656 | fn test_assert_expand() { |
643 | let expanded = expand_builtin_macro( | 657 | check_expansion( |
644 | r#" | 658 | r#" |
645 | #[rustc_builtin_macro] | 659 | #[rustc_builtin_macro] |
646 | macro_rules! assert { | 660 | macro_rules! assert { |
@@ -649,14 +663,13 @@ mod tests { | |||
649 | } | 663 | } |
650 | assert!(true, "{} {:?}", arg1(a, b, c), arg2); | 664 | assert!(true, "{} {:?}", arg1(a, b, c), arg2); |
651 | "#, | 665 | "#, |
666 | expect![["{{(&(true), &(\"{} {:?}\"), &(arg1(a,b,c)), &(arg2),);}}"]], | ||
652 | ); | 667 | ); |
653 | |||
654 | assert_eq!(expanded, "{{(&(true), &(\"{} {:?}\"), &(arg1(a,b,c)), &(arg2),);}}"); | ||
655 | } | 668 | } |
656 | 669 | ||
657 | #[test] | 670 | #[test] |
658 | fn test_compile_error_expand() { | 671 | fn test_compile_error_expand() { |
659 | let expanded = expand_builtin_macro( | 672 | check_expansion( |
660 | r#" | 673 | r#" |
661 | #[rustc_builtin_macro] | 674 | #[rustc_builtin_macro] |
662 | macro_rules! compile_error { | 675 | macro_rules! compile_error { |
@@ -665,15 +678,14 @@ mod tests { | |||
665 | } | 678 | } |
666 | compile_error!("error!"); | 679 | compile_error!("error!"); |
667 | "#, | 680 | "#, |
681 | // This expands to nothing (since it's in item position), but emits an error. | ||
682 | expect![[""]], | ||
668 | ); | 683 | ); |
669 | |||
670 | // This expands to nothing (since it's in item position), but emits an error. | ||
671 | assert_eq!(expanded, ""); | ||
672 | } | 684 | } |
673 | 685 | ||
674 | #[test] | 686 | #[test] |
675 | fn test_format_args_expand() { | 687 | fn test_format_args_expand() { |
676 | let expanded = expand_builtin_macro( | 688 | check_expansion( |
677 | r#" | 689 | r#" |
678 | #[rustc_builtin_macro] | 690 | #[rustc_builtin_macro] |
679 | macro_rules! format_args { | 691 | macro_rules! format_args { |
@@ -682,17 +694,15 @@ mod tests { | |||
682 | } | 694 | } |
683 | format_args!("{} {:?}", arg1(a, b, c), arg2); | 695 | format_args!("{} {:?}", arg1(a, b, c), arg2); |
684 | "#, | 696 | "#, |
685 | ); | 697 | expect![[ |
686 | 698 | r#"std::fmt::Arguments::new_v1(&[], &[std::fmt::ArgumentV1::new(&(arg1(a,b,c)),std::fmt::Display::fmt),std::fmt::ArgumentV1::new(&(arg2),std::fmt::Display::fmt),])"# | |
687 | assert_eq!( | 699 | ]], |
688 | expanded, | ||
689 | r#"std::fmt::Arguments::new_v1(&[], &[std::fmt::ArgumentV1::new(&(arg1(a,b,c)),std::fmt::Display::fmt),std::fmt::ArgumentV1::new(&(arg2),std::fmt::Display::fmt),])"# | ||
690 | ); | 700 | ); |
691 | } | 701 | } |
692 | 702 | ||
693 | #[test] | 703 | #[test] |
694 | fn test_format_args_expand_with_comma_exprs() { | 704 | fn test_format_args_expand_with_comma_exprs() { |
695 | let expanded = expand_builtin_macro( | 705 | check_expansion( |
696 | r#" | 706 | r#" |
697 | #[rustc_builtin_macro] | 707 | #[rustc_builtin_macro] |
698 | macro_rules! format_args { | 708 | macro_rules! format_args { |
@@ -701,17 +711,15 @@ mod tests { | |||
701 | } | 711 | } |
702 | format_args!("{} {:?}", a::<A,B>(), b); | 712 | format_args!("{} {:?}", a::<A,B>(), b); |
703 | "#, | 713 | "#, |
704 | ); | 714 | expect![[ |
705 | 715 | r#"std::fmt::Arguments::new_v1(&[], &[std::fmt::ArgumentV1::new(&(a::<A,B>()),std::fmt::Display::fmt),std::fmt::ArgumentV1::new(&(b),std::fmt::Display::fmt),])"# | |
706 | assert_eq!( | 716 | ]], |
707 | expanded, | ||
708 | r#"std::fmt::Arguments::new_v1(&[], &[std::fmt::ArgumentV1::new(&(a::<A,B>()),std::fmt::Display::fmt),std::fmt::ArgumentV1::new(&(b),std::fmt::Display::fmt),])"# | ||
709 | ); | 717 | ); |
710 | } | 718 | } |
711 | 719 | ||
712 | #[test] | 720 | #[test] |
713 | fn test_include_bytes_expand() { | 721 | fn test_include_bytes_expand() { |
714 | let expanded = expand_builtin_macro( | 722 | check_expansion( |
715 | r#" | 723 | r#" |
716 | #[rustc_builtin_macro] | 724 | #[rustc_builtin_macro] |
717 | macro_rules! include_bytes { | 725 | macro_rules! include_bytes { |
@@ -720,21 +728,19 @@ mod tests { | |||
720 | } | 728 | } |
721 | include_bytes("foo"); | 729 | include_bytes("foo"); |
722 | "#, | 730 | "#, |
731 | expect![[r#"b"""#]], | ||
723 | ); | 732 | ); |
724 | |||
725 | assert_eq!(expanded, r#"b"""#); | ||
726 | } | 733 | } |
727 | 734 | ||
728 | #[test] | 735 | #[test] |
729 | fn test_concat_expand() { | 736 | fn test_concat_expand() { |
730 | let expanded = expand_builtin_macro( | 737 | check_expansion( |
731 | r##" | 738 | r##" |
732 | #[rustc_builtin_macro] | 739 | #[rustc_builtin_macro] |
733 | macro_rules! concat {} | 740 | macro_rules! concat {} |
734 | concat!("foo", "r", 0, r#"bar"#, false); | 741 | concat!("foo", "r", 0, r#"bar"#, false); |
735 | "##, | 742 | "##, |
743 | expect![[r#""foor0barfalse""#]], | ||
736 | ); | 744 | ); |
737 | |||
738 | assert_eq!(expanded, r#""foor0barfalse""#); | ||
739 | } | 745 | } |
740 | } | 746 | } |
diff --git a/crates/hir_expand/src/name.rs b/crates/hir_expand/src/name.rs index c94fb580a..e833e032c 100644 --- a/crates/hir_expand/src/name.rs +++ b/crates/hir_expand/src/name.rs | |||
@@ -154,6 +154,7 @@ pub mod known { | |||
154 | macro_rules, | 154 | macro_rules, |
155 | derive, | 155 | derive, |
156 | doc, | 156 | doc, |
157 | cfg, | ||
157 | cfg_attr, | 158 | cfg_attr, |
158 | // Components of known path (value or mod name) | 159 | // Components of known path (value or mod name) |
159 | std, | 160 | std, |
diff --git a/crates/hir_expand/src/quote.rs b/crates/hir_expand/src/quote.rs index 219bc2097..08bc5aa49 100644 --- a/crates/hir_expand/src/quote.rs +++ b/crates/hir_expand/src/quote.rs | |||
@@ -188,8 +188,9 @@ macro_rules! impl_to_to_tokentrees { | |||
188 | 188 | ||
189 | impl_to_to_tokentrees! { | 189 | impl_to_to_tokentrees! { |
190 | u32 => self { tt::Literal{text: self.to_string().into(), id: tt::TokenId::unspecified()} }; | 190 | u32 => self { tt::Literal{text: self.to_string().into(), id: tt::TokenId::unspecified()} }; |
191 | usize => self { tt::Literal{text: self.to_string().into(), id: tt::TokenId::unspecified()}}; | 191 | usize => self { tt::Literal{text: self.to_string().into(), id: tt::TokenId::unspecified()} }; |
192 | i32 => self { tt::Literal{text: self.to_string().into(), id: tt::TokenId::unspecified()}}; | 192 | i32 => self { tt::Literal{text: self.to_string().into(), id: tt::TokenId::unspecified()} }; |
193 | bool => self { tt::Ident{text: self.to_string().into(), id: tt::TokenId::unspecified()} }; | ||
193 | tt::Leaf => self { self }; | 194 | tt::Leaf => self { self }; |
194 | tt::Literal => self { self }; | 195 | tt::Literal => self { self }; |
195 | tt::Ident => self { self }; | 196 | tt::Ident => self { self }; |
diff --git a/crates/hir_ty/Cargo.toml b/crates/hir_ty/Cargo.toml index d1302d749..b9c93f56f 100644 --- a/crates/hir_ty/Cargo.toml +++ b/crates/hir_ty/Cargo.toml | |||
@@ -10,6 +10,7 @@ edition = "2018" | |||
10 | doctest = false | 10 | doctest = false |
11 | 11 | ||
12 | [dependencies] | 12 | [dependencies] |
13 | cov-mark = "1.1" | ||
13 | itertools = "0.10.0" | 14 | itertools = "0.10.0" |
14 | arrayvec = "0.5.1" | 15 | arrayvec = "0.5.1" |
15 | smallvec = "1.2.0" | 16 | smallvec = "1.2.0" |
@@ -17,9 +18,9 @@ ena = "0.14.0" | |||
17 | log = "0.4.8" | 18 | log = "0.4.8" |
18 | rustc-hash = "1.1.0" | 19 | rustc-hash = "1.1.0" |
19 | scoped-tls = "1" | 20 | scoped-tls = "1" |
20 | chalk-solve = { version = "0.59", default-features = false } | 21 | chalk-solve = { version = "0.60", default-features = false } |
21 | chalk-ir = "0.59" | 22 | chalk-ir = "0.60" |
22 | chalk-recursive = "0.59" | 23 | chalk-recursive = "0.60" |
23 | la-arena = { version = "0.2.0", path = "../../lib/arena" } | 24 | la-arena = { version = "0.2.0", path = "../../lib/arena" } |
24 | 25 | ||
25 | stdx = { path = "../stdx", version = "0.0.0" } | 26 | stdx = { path = "../stdx", version = "0.0.0" } |
@@ -28,9 +29,9 @@ hir_expand = { path = "../hir_expand", version = "0.0.0" } | |||
28 | base_db = { path = "../base_db", version = "0.0.0" } | 29 | base_db = { path = "../base_db", version = "0.0.0" } |
29 | profile = { path = "../profile", version = "0.0.0" } | 30 | profile = { path = "../profile", version = "0.0.0" } |
30 | syntax = { path = "../syntax", version = "0.0.0" } | 31 | syntax = { path = "../syntax", version = "0.0.0" } |
31 | test_utils = { path = "../test_utils", version = "0.0.0" } | ||
32 | 32 | ||
33 | [dev-dependencies] | 33 | [dev-dependencies] |
34 | test_utils = { path = "../test_utils" } | ||
34 | expect-test = "1.1" | 35 | expect-test = "1.1" |
35 | tracing = "0.1" | 36 | tracing = "0.1" |
36 | tracing-subscriber = { version = "0.2", default-features = false, features = ["env-filter", "registry"] } | 37 | tracing-subscriber = { version = "0.2", default-features = false, features = ["env-filter", "registry"] } |
diff --git a/crates/hir_ty/src/diagnostics.rs b/crates/hir_ty/src/diagnostics.rs index 6bca7aa0d..86f937e1d 100644 --- a/crates/hir_ty/src/diagnostics.rs +++ b/crates/hir_ty/src/diagnostics.rs | |||
@@ -706,6 +706,35 @@ fn x(a: S) { | |||
706 | } | 706 | } |
707 | 707 | ||
708 | #[test] | 708 | #[test] |
709 | fn import_extern_crate_clash_with_inner_item() { | ||
710 | // This is more of a resolver test, but doesn't really work with the hir_def testsuite. | ||
711 | |||
712 | check_diagnostics( | ||
713 | r#" | ||
714 | //- /lib.rs crate:lib deps:jwt | ||
715 | mod permissions; | ||
716 | |||
717 | use permissions::jwt; | ||
718 | |||
719 | fn f() { | ||
720 | fn inner() {} | ||
721 | jwt::Claims {}; // should resolve to the local one with 0 fields, and not get a diagnostic | ||
722 | } | ||
723 | |||
724 | //- /permissions.rs | ||
725 | pub mod jwt { | ||
726 | pub struct Claims {} | ||
727 | } | ||
728 | |||
729 | //- /jwt/lib.rs crate:jwt | ||
730 | pub struct Claims { | ||
731 | field: u8, | ||
732 | } | ||
733 | "#, | ||
734 | ); | ||
735 | } | ||
736 | |||
737 | #[test] | ||
709 | fn break_outside_of_loop() { | 738 | fn break_outside_of_loop() { |
710 | check_diagnostics( | 739 | check_diagnostics( |
711 | r#" | 740 | r#" |
diff --git a/crates/hir_ty/src/diagnostics/decl_check.rs b/crates/hir_ty/src/diagnostics/decl_check.rs index 6773ddea3..3605ca581 100644 --- a/crates/hir_ty/src/diagnostics/decl_check.rs +++ b/crates/hir_ty/src/diagnostics/decl_check.rs | |||
@@ -28,7 +28,6 @@ use syntax::{ | |||
28 | ast::{self, NameOwner}, | 28 | ast::{self, NameOwner}, |
29 | AstNode, AstPtr, | 29 | AstNode, AstPtr, |
30 | }; | 30 | }; |
31 | use test_utils::mark; | ||
32 | 31 | ||
33 | use crate::{ | 32 | use crate::{ |
34 | db::HirDatabase, | 33 | db::HirDatabase, |
@@ -93,16 +92,21 @@ impl<'a, 'b> DeclValidator<'a, 'b> { | |||
93 | fn validate_func(&mut self, func: FunctionId) { | 92 | fn validate_func(&mut self, func: FunctionId) { |
94 | let data = self.db.function_data(func); | 93 | let data = self.db.function_data(func); |
95 | if data.is_extern { | 94 | if data.is_extern { |
96 | mark::hit!(extern_func_incorrect_case_ignored); | 95 | cov_mark::hit!(extern_func_incorrect_case_ignored); |
97 | return; | 96 | return; |
98 | } | 97 | } |
99 | 98 | ||
100 | let body = self.db.body(func.into()); | 99 | let body = self.db.body(func.into()); |
101 | 100 | ||
102 | // Recursively validate inner scope items, such as static variables and constants. | 101 | // Recursively validate inner scope items, such as static variables and constants. |
103 | for (item_id, _) in body.item_scope.values() { | 102 | let db = self.db; |
104 | let mut validator = DeclValidator::new(self.db, self.krate, self.sink); | 103 | for block_def_map in body.block_scopes.iter().filter_map(|block| db.block_def_map(*block)) { |
105 | validator.validate_item(item_id); | 104 | for (_, module) in block_def_map.modules() { |
105 | for (def_id, _) in module.scope.values() { | ||
106 | let mut validator = DeclValidator::new(self.db, self.krate, self.sink); | ||
107 | validator.validate_item(def_id); | ||
108 | } | ||
109 | } | ||
106 | } | 110 | } |
107 | 111 | ||
108 | // Check whether non-snake case identifiers are allowed for this function. | 112 | // Check whether non-snake case identifiers are allowed for this function. |
@@ -625,7 +629,7 @@ impl<'a, 'b> DeclValidator<'a, 'b> { | |||
625 | fn validate_static(&mut self, static_id: StaticId) { | 629 | fn validate_static(&mut self, static_id: StaticId) { |
626 | let data = self.db.static_data(static_id); | 630 | let data = self.db.static_data(static_id); |
627 | if data.is_extern { | 631 | if data.is_extern { |
628 | mark::hit!(extern_static_incorrect_case_ignored); | 632 | cov_mark::hit!(extern_static_incorrect_case_ignored); |
629 | return; | 633 | return; |
630 | } | 634 | } |
631 | 635 | ||
@@ -673,8 +677,6 @@ impl<'a, 'b> DeclValidator<'a, 'b> { | |||
673 | 677 | ||
674 | #[cfg(test)] | 678 | #[cfg(test)] |
675 | mod tests { | 679 | mod tests { |
676 | use test_utils::mark; | ||
677 | |||
678 | use crate::diagnostics::tests::check_diagnostics; | 680 | use crate::diagnostics::tests::check_diagnostics; |
679 | 681 | ||
680 | #[test] | 682 | #[test] |
@@ -889,8 +891,8 @@ fn main() { | |||
889 | 891 | ||
890 | #[test] | 892 | #[test] |
891 | fn ignores_extern_items() { | 893 | fn ignores_extern_items() { |
892 | mark::check!(extern_func_incorrect_case_ignored); | 894 | cov_mark::check!(extern_func_incorrect_case_ignored); |
893 | mark::check!(extern_static_incorrect_case_ignored); | 895 | cov_mark::check!(extern_static_incorrect_case_ignored); |
894 | check_diagnostics( | 896 | check_diagnostics( |
895 | r#" | 897 | r#" |
896 | extern { | 898 | extern { |
diff --git a/crates/hir_ty/src/display.rs b/crates/hir_ty/src/display.rs index a0882a2a1..ab51cb0a6 100644 --- a/crates/hir_ty/src/display.rs +++ b/crates/hir_ty/src/display.rs | |||
@@ -6,7 +6,7 @@ use arrayvec::ArrayVec; | |||
6 | use chalk_ir::Mutability; | 6 | use chalk_ir::Mutability; |
7 | use hir_def::{ | 7 | use hir_def::{ |
8 | db::DefDatabase, find_path, generics::TypeParamProvenance, item_scope::ItemInNs, | 8 | db::DefDatabase, find_path, generics::TypeParamProvenance, item_scope::ItemInNs, |
9 | AssocContainerId, HasModule, Lookup, ModuleId, TraitId, | 9 | AssocContainerId, Lookup, ModuleId, TraitId, |
10 | }; | 10 | }; |
11 | use hir_expand::name::Name; | 11 | use hir_expand::name::Name; |
12 | 12 | ||
@@ -611,7 +611,7 @@ impl HirDisplay for CallableSig { | |||
611 | } | 611 | } |
612 | 612 | ||
613 | fn fn_traits(db: &dyn DefDatabase, trait_: TraitId) -> impl Iterator<Item = TraitId> { | 613 | fn fn_traits(db: &dyn DefDatabase, trait_: TraitId) -> impl Iterator<Item = TraitId> { |
614 | let krate = trait_.lookup(db).container.module(db).krate(); | 614 | let krate = trait_.lookup(db).container.krate(); |
615 | let fn_traits = [ | 615 | let fn_traits = [ |
616 | db.lang_item(krate, "fn".into()), | 616 | db.lang_item(krate, "fn".into()), |
617 | db.lang_item(krate, "fn_mut".into()), | 617 | db.lang_item(krate, "fn_mut".into()), |
diff --git a/crates/hir_ty/src/infer/coerce.rs b/crates/hir_ty/src/infer/coerce.rs index cf0a3add4..7e8846f27 100644 --- a/crates/hir_ty/src/infer/coerce.rs +++ b/crates/hir_ty/src/infer/coerce.rs | |||
@@ -6,7 +6,6 @@ | |||
6 | 6 | ||
7 | use chalk_ir::{Mutability, TyVariableKind}; | 7 | use chalk_ir::{Mutability, TyVariableKind}; |
8 | use hir_def::lang_item::LangItemTarget; | 8 | use hir_def::lang_item::LangItemTarget; |
9 | use test_utils::mark; | ||
10 | 9 | ||
11 | use crate::{autoderef, traits::Solution, Obligation, Substs, TraitRef, Ty}; | 10 | use crate::{autoderef, traits::Solution, Obligation, Substs, TraitRef, Ty}; |
12 | 11 | ||
@@ -35,7 +34,7 @@ impl<'a> InferenceContext<'a> { | |||
35 | ty1.clone() | 34 | ty1.clone() |
36 | } else { | 35 | } else { |
37 | if let (Ty::FnDef(..), Ty::FnDef(..)) = (ty1, ty2) { | 36 | if let (Ty::FnDef(..), Ty::FnDef(..)) = (ty1, ty2) { |
38 | mark::hit!(coerce_fn_reification); | 37 | cov_mark::hit!(coerce_fn_reification); |
39 | // Special case: two function types. Try to coerce both to | 38 | // Special case: two function types. Try to coerce both to |
40 | // pointers to have a chance at getting a match. See | 39 | // pointers to have a chance at getting a match. See |
41 | // https://github.com/rust-lang/rust/blob/7b805396bf46dce972692a6846ce2ad8481c5f85/src/librustc_typeck/check/coercion.rs#L877-L916 | 40 | // https://github.com/rust-lang/rust/blob/7b805396bf46dce972692a6846ce2ad8481c5f85/src/librustc_typeck/check/coercion.rs#L877-L916 |
@@ -45,7 +44,7 @@ impl<'a> InferenceContext<'a> { | |||
45 | let ptr_ty2 = Ty::fn_ptr(sig2); | 44 | let ptr_ty2 = Ty::fn_ptr(sig2); |
46 | self.coerce_merge_branch(&ptr_ty1, &ptr_ty2) | 45 | self.coerce_merge_branch(&ptr_ty1, &ptr_ty2) |
47 | } else { | 46 | } else { |
48 | mark::hit!(coerce_merge_fail_fallback); | 47 | cov_mark::hit!(coerce_merge_fail_fallback); |
49 | ty1.clone() | 48 | ty1.clone() |
50 | } | 49 | } |
51 | } | 50 | } |
diff --git a/crates/hir_ty/src/infer/expr.rs b/crates/hir_ty/src/infer/expr.rs index ec2c13154..262177ffb 100644 --- a/crates/hir_ty/src/infer/expr.rs +++ b/crates/hir_ty/src/infer/expr.rs | |||
@@ -12,7 +12,6 @@ use hir_def::{ | |||
12 | }; | 12 | }; |
13 | use hir_expand::name::{name, Name}; | 13 | use hir_expand::name::{name, Name}; |
14 | use syntax::ast::RangeOp; | 14 | use syntax::ast::RangeOp; |
15 | use test_utils::mark; | ||
16 | 15 | ||
17 | use crate::{ | 16 | use crate::{ |
18 | autoderef, | 17 | autoderef, |
@@ -565,7 +564,7 @@ impl<'a> InferenceContext<'a> { | |||
565 | let ret = op::binary_op_return_ty(*op, lhs_ty.clone(), rhs_ty.clone()); | 564 | let ret = op::binary_op_return_ty(*op, lhs_ty.clone(), rhs_ty.clone()); |
566 | 565 | ||
567 | if ret == Ty::Unknown { | 566 | if ret == Ty::Unknown { |
568 | mark::hit!(infer_expr_inner_binary_operator_overload); | 567 | cov_mark::hit!(infer_expr_inner_binary_operator_overload); |
569 | 568 | ||
570 | self.resolve_associated_type_with_params( | 569 | self.resolve_associated_type_with_params( |
571 | lhs_ty, | 570 | lhs_ty, |
diff --git a/crates/hir_ty/src/infer/pat.rs b/crates/hir_ty/src/infer/pat.rs index 987793e2e..a0ac8d80f 100644 --- a/crates/hir_ty/src/infer/pat.rs +++ b/crates/hir_ty/src/infer/pat.rs | |||
@@ -10,7 +10,6 @@ use hir_def::{ | |||
10 | FieldId, | 10 | FieldId, |
11 | }; | 11 | }; |
12 | use hir_expand::name::Name; | 12 | use hir_expand::name::Name; |
13 | use test_utils::mark; | ||
14 | 13 | ||
15 | use super::{BindingMode, Expectation, InferenceContext}; | 14 | use super::{BindingMode, Expectation, InferenceContext}; |
16 | use crate::{lower::lower_to_chalk_mutability, utils::variant_data, Substs, Ty}; | 15 | use crate::{lower::lower_to_chalk_mutability, utils::variant_data, Substs, Ty}; |
@@ -108,7 +107,7 @@ impl<'a> InferenceContext<'a> { | |||
108 | } | 107 | } |
109 | } | 108 | } |
110 | } else if let Pat::Ref { .. } = &body[pat] { | 109 | } else if let Pat::Ref { .. } = &body[pat] { |
111 | mark::hit!(match_ergonomics_ref); | 110 | cov_mark::hit!(match_ergonomics_ref); |
112 | // When you encounter a `&pat` pattern, reset to Move. | 111 | // When you encounter a `&pat` pattern, reset to Move. |
113 | // This is so that `w` is by value: `let (_, &w) = &(1, &2);` | 112 | // This is so that `w` is by value: `let (_, &w) = &(1, &2);` |
114 | default_bm = BindingMode::Move; | 113 | default_bm = BindingMode::Move; |
diff --git a/crates/hir_ty/src/infer/path.rs b/crates/hir_ty/src/infer/path.rs index 5d541104e..ae3554bac 100644 --- a/crates/hir_ty/src/infer/path.rs +++ b/crates/hir_ty/src/infer/path.rs | |||
@@ -260,7 +260,7 @@ impl<'a> InferenceContext<'a> { | |||
260 | })); | 260 | })); |
261 | Some(trait_substs) | 261 | Some(trait_substs) |
262 | } | 262 | } |
263 | AssocContainerId::ContainerId(_) => None, | 263 | AssocContainerId::ModuleId(_) => None, |
264 | }; | 264 | }; |
265 | 265 | ||
266 | self.write_assoc_resolution(id, item); | 266 | self.write_assoc_resolution(id, item); |
diff --git a/crates/hir_ty/src/infer/unify.rs b/crates/hir_ty/src/infer/unify.rs index 99a89a7f3..54fcfed10 100644 --- a/crates/hir_ty/src/infer/unify.rs +++ b/crates/hir_ty/src/infer/unify.rs | |||
@@ -5,8 +5,6 @@ use std::borrow::Cow; | |||
5 | use chalk_ir::{FloatTy, IntTy, TyVariableKind}; | 5 | use chalk_ir::{FloatTy, IntTy, TyVariableKind}; |
6 | use ena::unify::{InPlaceUnificationTable, NoError, UnifyKey, UnifyValue}; | 6 | use ena::unify::{InPlaceUnificationTable, NoError, UnifyKey, UnifyValue}; |
7 | 7 | ||
8 | use test_utils::mark; | ||
9 | |||
10 | use super::{InferenceContext, Obligation}; | 8 | use super::{InferenceContext, Obligation}; |
11 | use crate::{ | 9 | use crate::{ |
12 | BoundVar, Canonical, DebruijnIndex, GenericPredicate, InEnvironment, InferenceVar, Scalar, | 10 | BoundVar, Canonical, DebruijnIndex, GenericPredicate, InEnvironment, InferenceVar, Scalar, |
@@ -387,7 +385,7 @@ impl InferenceTable { | |||
387 | // more than once | 385 | // more than once |
388 | for i in 0..3 { | 386 | for i in 0..3 { |
389 | if i > 0 { | 387 | if i > 0 { |
390 | mark::hit!(type_var_resolves_to_int_var); | 388 | cov_mark::hit!(type_var_resolves_to_int_var); |
391 | } | 389 | } |
392 | match &*ty { | 390 | match &*ty { |
393 | Ty::InferenceVar(tv, _) => { | 391 | Ty::InferenceVar(tv, _) => { |
@@ -416,7 +414,7 @@ impl InferenceTable { | |||
416 | Ty::InferenceVar(tv, kind) => { | 414 | Ty::InferenceVar(tv, kind) => { |
417 | let inner = tv.to_inner(); | 415 | let inner = tv.to_inner(); |
418 | if tv_stack.contains(&inner) { | 416 | if tv_stack.contains(&inner) { |
419 | mark::hit!(type_var_cycles_resolve_as_possible); | 417 | cov_mark::hit!(type_var_cycles_resolve_as_possible); |
420 | // recursive type | 418 | // recursive type |
421 | return self.type_variable_table.fallback_value(tv, kind); | 419 | return self.type_variable_table.fallback_value(tv, kind); |
422 | } | 420 | } |
@@ -443,7 +441,7 @@ impl InferenceTable { | |||
443 | Ty::InferenceVar(tv, kind) => { | 441 | Ty::InferenceVar(tv, kind) => { |
444 | let inner = tv.to_inner(); | 442 | let inner = tv.to_inner(); |
445 | if tv_stack.contains(&inner) { | 443 | if tv_stack.contains(&inner) { |
446 | mark::hit!(type_var_cycles_resolve_completely); | 444 | cov_mark::hit!(type_var_cycles_resolve_completely); |
447 | // recursive type | 445 | // recursive type |
448 | return self.type_variable_table.fallback_value(tv, kind); | 446 | return self.type_variable_table.fallback_value(tv, kind); |
449 | } | 447 | } |
diff --git a/crates/hir_ty/src/lower.rs b/crates/hir_ty/src/lower.rs index 5fe5b8ad1..d84ec9b7a 100644 --- a/crates/hir_ty/src/lower.rs +++ b/crates/hir_ty/src/lower.rs | |||
@@ -8,7 +8,7 @@ | |||
8 | use std::{iter, sync::Arc}; | 8 | use std::{iter, sync::Arc}; |
9 | 9 | ||
10 | use base_db::CrateId; | 10 | use base_db::CrateId; |
11 | use chalk_ir::Mutability; | 11 | use chalk_ir::{cast::Cast, Mutability}; |
12 | use hir_def::{ | 12 | use hir_def::{ |
13 | adt::StructKind, | 13 | adt::StructKind, |
14 | builtin_type::BuiltinType, | 14 | builtin_type::BuiltinType, |
@@ -24,10 +24,10 @@ use hir_expand::name::Name; | |||
24 | use la_arena::ArenaMap; | 24 | use la_arena::ArenaMap; |
25 | use smallvec::SmallVec; | 25 | use smallvec::SmallVec; |
26 | use stdx::impl_from; | 26 | use stdx::impl_from; |
27 | use test_utils::mark; | ||
28 | 27 | ||
29 | use crate::{ | 28 | use crate::{ |
30 | db::HirDatabase, | 29 | db::HirDatabase, |
30 | traits::chalk::{Interner, ToChalk}, | ||
31 | utils::{ | 31 | utils::{ |
32 | all_super_trait_refs, associated_type_by_name_including_super_traits, generics, | 32 | all_super_trait_refs, associated_type_by_name_including_super_traits, generics, |
33 | make_mut_slice, variant_data, | 33 | make_mut_slice, variant_data, |
@@ -760,7 +760,7 @@ fn assoc_type_bindings_from_type_bound<'a>( | |||
760 | 760 | ||
761 | impl ReturnTypeImplTrait { | 761 | impl ReturnTypeImplTrait { |
762 | fn from_hir(ctx: &TyLoweringContext, bounds: &[TypeBound]) -> Self { | 762 | fn from_hir(ctx: &TyLoweringContext, bounds: &[TypeBound]) -> Self { |
763 | mark::hit!(lower_rpit); | 763 | cov_mark::hit!(lower_rpit); |
764 | let self_ty = Ty::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 0)); | 764 | let self_ty = Ty::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 0)); |
765 | let predicates = ctx.with_shifted_in(DebruijnIndex::ONE, |ctx| { | 765 | let predicates = ctx.with_shifted_in(DebruijnIndex::ONE, |ctx| { |
766 | bounds | 766 | bounds |
@@ -915,10 +915,21 @@ impl TraitEnvironment { | |||
915 | pub fn lower(db: &dyn HirDatabase, resolver: &Resolver) -> Arc<TraitEnvironment> { | 915 | pub fn lower(db: &dyn HirDatabase, resolver: &Resolver) -> Arc<TraitEnvironment> { |
916 | let ctx = TyLoweringContext::new(db, &resolver) | 916 | let ctx = TyLoweringContext::new(db, &resolver) |
917 | .with_type_param_mode(TypeParamLoweringMode::Placeholder); | 917 | .with_type_param_mode(TypeParamLoweringMode::Placeholder); |
918 | let mut predicates = resolver | 918 | let mut traits_in_scope = Vec::new(); |
919 | .where_predicates_in_scope() | 919 | let mut clauses = Vec::new(); |
920 | .flat_map(|pred| GenericPredicate::from_where_predicate(&ctx, pred)) | 920 | for pred in resolver.where_predicates_in_scope() { |
921 | .collect::<Vec<_>>(); | 921 | for pred in GenericPredicate::from_where_predicate(&ctx, pred) { |
922 | if pred.is_error() { | ||
923 | continue; | ||
924 | } | ||
925 | if let GenericPredicate::Implemented(tr) = &pred { | ||
926 | traits_in_scope.push((tr.self_ty().clone(), tr.trait_)); | ||
927 | } | ||
928 | let program_clause: chalk_ir::ProgramClause<Interner> = | ||
929 | pred.clone().to_chalk(db).cast(&Interner); | ||
930 | clauses.push(program_clause.into_from_env_clause(&Interner)); | ||
931 | } | ||
932 | } | ||
922 | 933 | ||
923 | if let Some(def) = resolver.generic_def() { | 934 | if let Some(def) = resolver.generic_def() { |
924 | let container: Option<AssocContainerId> = match def { | 935 | let container: Option<AssocContainerId> = match def { |
@@ -935,16 +946,19 @@ impl TraitEnvironment { | |||
935 | // add `Self: Trait<T1, T2, ...>` to the environment in trait | 946 | // add `Self: Trait<T1, T2, ...>` to the environment in trait |
936 | // function default implementations (and hypothetical code | 947 | // function default implementations (and hypothetical code |
937 | // inside consts or type aliases) | 948 | // inside consts or type aliases) |
938 | test_utils::mark::hit!(trait_self_implements_self); | 949 | cov_mark::hit!(trait_self_implements_self); |
939 | let substs = Substs::type_params(db, trait_id); | 950 | let substs = Substs::type_params(db, trait_id); |
940 | let trait_ref = TraitRef { trait_: trait_id, substs }; | 951 | let trait_ref = TraitRef { trait_: trait_id, substs }; |
941 | let pred = GenericPredicate::Implemented(trait_ref); | 952 | let pred = GenericPredicate::Implemented(trait_ref); |
942 | 953 | let program_clause: chalk_ir::ProgramClause<Interner> = | |
943 | predicates.push(pred); | 954 | pred.clone().to_chalk(db).cast(&Interner); |
955 | clauses.push(program_clause.into_from_env_clause(&Interner)); | ||
944 | } | 956 | } |
945 | } | 957 | } |
946 | 958 | ||
947 | Arc::new(TraitEnvironment { predicates }) | 959 | let env = chalk_ir::Environment::new(&Interner).add_clauses(&Interner, clauses); |
960 | |||
961 | Arc::new(TraitEnvironment { traits_from_clauses: traits_in_scope, env }) | ||
948 | } | 962 | } |
949 | } | 963 | } |
950 | 964 | ||
@@ -1131,8 +1145,8 @@ impl CallableDefId { | |||
1131 | let db = db.upcast(); | 1145 | let db = db.upcast(); |
1132 | match self { | 1146 | match self { |
1133 | CallableDefId::FunctionId(f) => f.lookup(db).module(db), | 1147 | CallableDefId::FunctionId(f) => f.lookup(db).module(db), |
1134 | CallableDefId::StructId(s) => s.lookup(db).container.module(db), | 1148 | CallableDefId::StructId(s) => s.lookup(db).container, |
1135 | CallableDefId::EnumVariantId(e) => e.parent.lookup(db).container.module(db), | 1149 | CallableDefId::EnumVariantId(e) => e.parent.lookup(db).container, |
1136 | } | 1150 | } |
1137 | .krate() | 1151 | .krate() |
1138 | } | 1152 | } |
diff --git a/crates/hir_ty/src/method_resolution.rs b/crates/hir_ty/src/method_resolution.rs index dfcf346fb..d57c6de70 100644 --- a/crates/hir_ty/src/method_resolution.rs +++ b/crates/hir_ty/src/method_resolution.rs | |||
@@ -267,7 +267,7 @@ impl Ty { | |||
267 | LangItemTarget::ImplDefId(it) => Some(it), | 267 | LangItemTarget::ImplDefId(it) => Some(it), |
268 | _ => None, | 268 | _ => None, |
269 | }) | 269 | }) |
270 | .map(|it| it.lookup(db.upcast()).container.module(db.upcast()).krate()) | 270 | .map(|it| it.lookup(db.upcast()).container.krate()) |
271 | .collect(); | 271 | .collect(); |
272 | Some(res) | 272 | Some(res) |
273 | } | 273 | } |
@@ -528,8 +528,7 @@ fn iterate_trait_method_candidates( | |||
528 | self_ty.value.dyn_trait().into_iter().flat_map(|t| all_super_traits(db.upcast(), t)); | 528 | self_ty.value.dyn_trait().into_iter().flat_map(|t| all_super_traits(db.upcast(), t)); |
529 | let env_traits = if let Ty::Placeholder(_) = self_ty.value { | 529 | let env_traits = if let Ty::Placeholder(_) = self_ty.value { |
530 | // if we have `T: Trait` in the param env, the trait doesn't need to be in scope | 530 | // if we have `T: Trait` in the param env, the trait doesn't need to be in scope |
531 | env.trait_predicates_for_self_ty(&self_ty.value) | 531 | env.traits_in_scope_from_clauses(&self_ty.value) |
532 | .map(|tr| tr.trait_) | ||
533 | .flat_map(|t| all_super_traits(db.upcast(), t)) | 532 | .flat_map(|t| all_super_traits(db.upcast(), t)) |
534 | .collect() | 533 | .collect() |
535 | } else { | 534 | } else { |
@@ -588,7 +587,7 @@ fn iterate_inherent_methods( | |||
588 | // already happens in `is_valid_candidate` above; if not, we | 587 | // already happens in `is_valid_candidate` above; if not, we |
589 | // check it here | 588 | // check it here |
590 | if receiver_ty.is_none() && inherent_impl_substs(db, impl_def, self_ty).is_none() { | 589 | if receiver_ty.is_none() && inherent_impl_substs(db, impl_def, self_ty).is_none() { |
591 | test_utils::mark::hit!(impl_self_type_match_without_receiver); | 590 | cov_mark::hit!(impl_self_type_match_without_receiver); |
592 | continue; | 591 | continue; |
593 | } | 592 | } |
594 | if callback(&self_ty.value, item) { | 593 | if callback(&self_ty.value, item) { |
@@ -715,7 +714,7 @@ fn transform_receiver_ty( | |||
715 | .fill_with_unknown() | 714 | .fill_with_unknown() |
716 | .build() | 715 | .build() |
717 | } | 716 | } |
718 | AssocContainerId::ContainerId(_) => unreachable!(), | 717 | AssocContainerId::ModuleId(_) => unreachable!(), |
719 | }; | 718 | }; |
720 | let sig = db.callable_item_signature(function_id.into()); | 719 | let sig = db.callable_item_signature(function_id.into()); |
721 | Some(sig.value.params()[0].clone().subst_bound_vars(&substs)) | 720 | Some(sig.value.params()[0].clone().subst_bound_vars(&substs)) |
diff --git a/crates/hir_ty/src/tests.rs b/crates/hir_ty/src/tests.rs index 7386a4e7b..0a4141e69 100644 --- a/crates/hir_ty/src/tests.rs +++ b/crates/hir_ty/src/tests.rs | |||
@@ -13,12 +13,13 @@ use std::{env, sync::Arc}; | |||
13 | use base_db::{fixture::WithFixture, FileRange, SourceDatabase, SourceDatabaseExt}; | 13 | use base_db::{fixture::WithFixture, FileRange, SourceDatabase, SourceDatabaseExt}; |
14 | use expect_test::Expect; | 14 | use expect_test::Expect; |
15 | use hir_def::{ | 15 | use hir_def::{ |
16 | body::{BodySourceMap, SyntheticSyntax}, | 16 | body::{Body, BodySourceMap, SyntheticSyntax}, |
17 | child_by_source::ChildBySource, | 17 | child_by_source::ChildBySource, |
18 | db::DefDatabase, | 18 | db::DefDatabase, |
19 | item_scope::ItemScope, | 19 | item_scope::ItemScope, |
20 | keys, | 20 | keys, |
21 | nameres::DefMap, | 21 | nameres::DefMap, |
22 | src::HasSource, | ||
22 | AssocItemId, DefWithBodyId, LocalModuleId, Lookup, ModuleDefId, | 23 | AssocItemId, DefWithBodyId, LocalModuleId, Lookup, ModuleDefId, |
23 | }; | 24 | }; |
24 | use hir_expand::{db::AstDatabase, InFile}; | 25 | use hir_expand::{db::AstDatabase, InFile}; |
@@ -195,18 +196,15 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String { | |||
195 | defs.sort_by_key(|def| match def { | 196 | defs.sort_by_key(|def| match def { |
196 | DefWithBodyId::FunctionId(it) => { | 197 | DefWithBodyId::FunctionId(it) => { |
197 | let loc = it.lookup(&db); | 198 | let loc = it.lookup(&db); |
198 | let tree = db.item_tree(loc.id.file_id); | 199 | loc.source(&db).value.syntax().text_range().start() |
199 | tree.source(&db, loc.id).syntax().text_range().start() | ||
200 | } | 200 | } |
201 | DefWithBodyId::ConstId(it) => { | 201 | DefWithBodyId::ConstId(it) => { |
202 | let loc = it.lookup(&db); | 202 | let loc = it.lookup(&db); |
203 | let tree = db.item_tree(loc.id.file_id); | 203 | loc.source(&db).value.syntax().text_range().start() |
204 | tree.source(&db, loc.id).syntax().text_range().start() | ||
205 | } | 204 | } |
206 | DefWithBodyId::StaticId(it) => { | 205 | DefWithBodyId::StaticId(it) => { |
207 | let loc = it.lookup(&db); | 206 | let loc = it.lookup(&db); |
208 | let tree = db.item_tree(loc.id.file_id); | 207 | loc.source(&db).value.syntax().text_range().start() |
209 | tree.source(&db, loc.id).syntax().text_range().start() | ||
210 | } | 208 | } |
211 | }); | 209 | }); |
212 | for def in defs { | 210 | for def in defs { |
@@ -234,13 +232,13 @@ fn visit_module( | |||
234 | let def = it.into(); | 232 | let def = it.into(); |
235 | cb(def); | 233 | cb(def); |
236 | let body = db.body(def); | 234 | let body = db.body(def); |
237 | visit_scope(db, crate_def_map, &body.item_scope, cb); | 235 | visit_body(db, &body, cb); |
238 | } | 236 | } |
239 | AssocItemId::ConstId(it) => { | 237 | AssocItemId::ConstId(it) => { |
240 | let def = it.into(); | 238 | let def = it.into(); |
241 | cb(def); | 239 | cb(def); |
242 | let body = db.body(def); | 240 | let body = db.body(def); |
243 | visit_scope(db, crate_def_map, &body.item_scope, cb); | 241 | visit_body(db, &body, cb); |
244 | } | 242 | } |
245 | AssocItemId::TypeAliasId(_) => (), | 243 | AssocItemId::TypeAliasId(_) => (), |
246 | } | 244 | } |
@@ -259,19 +257,19 @@ fn visit_module( | |||
259 | let def = it.into(); | 257 | let def = it.into(); |
260 | cb(def); | 258 | cb(def); |
261 | let body = db.body(def); | 259 | let body = db.body(def); |
262 | visit_scope(db, crate_def_map, &body.item_scope, cb); | 260 | visit_body(db, &body, cb); |
263 | } | 261 | } |
264 | ModuleDefId::ConstId(it) => { | 262 | ModuleDefId::ConstId(it) => { |
265 | let def = it.into(); | 263 | let def = it.into(); |
266 | cb(def); | 264 | cb(def); |
267 | let body = db.body(def); | 265 | let body = db.body(def); |
268 | visit_scope(db, crate_def_map, &body.item_scope, cb); | 266 | visit_body(db, &body, cb); |
269 | } | 267 | } |
270 | ModuleDefId::StaticId(it) => { | 268 | ModuleDefId::StaticId(it) => { |
271 | let def = it.into(); | 269 | let def = it.into(); |
272 | cb(def); | 270 | cb(def); |
273 | let body = db.body(def); | 271 | let body = db.body(def); |
274 | visit_scope(db, crate_def_map, &body.item_scope, cb); | 272 | visit_body(db, &body, cb); |
275 | } | 273 | } |
276 | ModuleDefId::TraitId(it) => { | 274 | ModuleDefId::TraitId(it) => { |
277 | let trait_data = db.trait_data(it); | 275 | let trait_data = db.trait_data(it); |
@@ -288,6 +286,14 @@ fn visit_module( | |||
288 | } | 286 | } |
289 | } | 287 | } |
290 | } | 288 | } |
289 | |||
290 | fn visit_body(db: &TestDB, body: &Body, cb: &mut dyn FnMut(DefWithBodyId)) { | ||
291 | for def_map in body.block_scopes.iter().filter_map(|block| db.block_def_map(*block)) { | ||
292 | for (mod_id, _) in def_map.modules() { | ||
293 | visit_module(db, &def_map, mod_id, cb); | ||
294 | } | ||
295 | } | ||
296 | } | ||
291 | } | 297 | } |
292 | 298 | ||
293 | fn ellipsize(mut text: String, max_len: usize) -> String { | 299 | fn ellipsize(mut text: String, max_len: usize) -> String { |
diff --git a/crates/hir_ty/src/tests/coercion.rs b/crates/hir_ty/src/tests/coercion.rs index 7bc6c79f3..63d9d4e0b 100644 --- a/crates/hir_ty/src/tests/coercion.rs +++ b/crates/hir_ty/src/tests/coercion.rs | |||
@@ -1,5 +1,4 @@ | |||
1 | use expect_test::expect; | 1 | use expect_test::expect; |
2 | use test_utils::mark; | ||
3 | 2 | ||
4 | use super::{check_infer, check_infer_with_mismatches}; | 3 | use super::{check_infer, check_infer_with_mismatches}; |
5 | 4 | ||
@@ -381,7 +380,7 @@ fn infer_match_second_coerce() { | |||
381 | 380 | ||
382 | #[test] | 381 | #[test] |
383 | fn coerce_merge_one_by_one1() { | 382 | fn coerce_merge_one_by_one1() { |
384 | mark::check!(coerce_merge_fail_fallback); | 383 | cov_mark::check!(coerce_merge_fail_fallback); |
385 | 384 | ||
386 | check_infer( | 385 | check_infer( |
387 | r" | 386 | r" |
@@ -589,7 +588,7 @@ fn coerce_fn_item_to_fn_ptr() { | |||
589 | 588 | ||
590 | #[test] | 589 | #[test] |
591 | fn coerce_fn_items_in_match_arms() { | 590 | fn coerce_fn_items_in_match_arms() { |
592 | mark::check!(coerce_fn_reification); | 591 | cov_mark::check!(coerce_fn_reification); |
593 | 592 | ||
594 | check_infer_with_mismatches( | 593 | check_infer_with_mismatches( |
595 | r" | 594 | r" |
diff --git a/crates/hir_ty/src/tests/method_resolution.rs b/crates/hir_ty/src/tests/method_resolution.rs index a9901d7b8..4e3f9a9b6 100644 --- a/crates/hir_ty/src/tests/method_resolution.rs +++ b/crates/hir_ty/src/tests/method_resolution.rs | |||
@@ -913,7 +913,7 @@ fn test() { S2.into(); } | |||
913 | 913 | ||
914 | #[test] | 914 | #[test] |
915 | fn method_resolution_overloaded_method() { | 915 | fn method_resolution_overloaded_method() { |
916 | test_utils::mark::check!(impl_self_type_match_without_receiver); | 916 | cov_mark::check!(impl_self_type_match_without_receiver); |
917 | check_types( | 917 | check_types( |
918 | r#" | 918 | r#" |
919 | struct Wrapper<T>(T); | 919 | struct Wrapper<T>(T); |
diff --git a/crates/hir_ty/src/tests/patterns.rs b/crates/hir_ty/src/tests/patterns.rs index 2053d8f56..5da19ba5f 100644 --- a/crates/hir_ty/src/tests/patterns.rs +++ b/crates/hir_ty/src/tests/patterns.rs | |||
@@ -1,5 +1,4 @@ | |||
1 | use expect_test::expect; | 1 | use expect_test::expect; |
2 | use test_utils::mark; | ||
3 | 2 | ||
4 | use super::{check_infer, check_infer_with_mismatches}; | 3 | use super::{check_infer, check_infer_with_mismatches}; |
5 | 4 | ||
@@ -197,7 +196,7 @@ fn infer_pattern_match_ergonomics() { | |||
197 | 196 | ||
198 | #[test] | 197 | #[test] |
199 | fn infer_pattern_match_ergonomics_ref() { | 198 | fn infer_pattern_match_ergonomics_ref() { |
200 | mark::check!(match_ergonomics_ref); | 199 | cov_mark::check!(match_ergonomics_ref); |
201 | check_infer( | 200 | check_infer( |
202 | r#" | 201 | r#" |
203 | fn test() { | 202 | fn test() { |
diff --git a/crates/hir_ty/src/tests/regression.rs b/crates/hir_ty/src/tests/regression.rs index cffe8630b..69314e245 100644 --- a/crates/hir_ty/src/tests/regression.rs +++ b/crates/hir_ty/src/tests/regression.rs | |||
@@ -1,5 +1,4 @@ | |||
1 | use expect_test::expect; | 1 | use expect_test::expect; |
2 | use test_utils::mark; | ||
3 | 2 | ||
4 | use super::{check_infer, check_types}; | 3 | use super::{check_infer, check_types}; |
5 | 4 | ||
@@ -87,8 +86,8 @@ fn bug_651() { | |||
87 | 86 | ||
88 | #[test] | 87 | #[test] |
89 | fn recursive_vars() { | 88 | fn recursive_vars() { |
90 | mark::check!(type_var_cycles_resolve_completely); | 89 | cov_mark::check!(type_var_cycles_resolve_completely); |
91 | mark::check!(type_var_cycles_resolve_as_possible); | 90 | cov_mark::check!(type_var_cycles_resolve_as_possible); |
92 | check_infer( | 91 | check_infer( |
93 | r#" | 92 | r#" |
94 | fn test() { | 93 | fn test() { |
@@ -166,7 +165,7 @@ fn infer_std_crash_1() { | |||
166 | 165 | ||
167 | #[test] | 166 | #[test] |
168 | fn infer_std_crash_2() { | 167 | fn infer_std_crash_2() { |
169 | mark::check!(type_var_resolves_to_int_var); | 168 | cov_mark::check!(type_var_resolves_to_int_var); |
170 | // caused "equating two type variables, ...", taken from std | 169 | // caused "equating two type variables, ...", taken from std |
171 | check_infer( | 170 | check_infer( |
172 | r#" | 171 | r#" |
diff --git a/crates/hir_ty/src/tests/simple.rs b/crates/hir_ty/src/tests/simple.rs index 2947857a5..f5069eba5 100644 --- a/crates/hir_ty/src/tests/simple.rs +++ b/crates/hir_ty/src/tests/simple.rs | |||
@@ -1,5 +1,4 @@ | |||
1 | use expect_test::expect; | 1 | use expect_test::expect; |
2 | use test_utils::mark; | ||
3 | 2 | ||
4 | use super::{check_infer, check_types}; | 3 | use super::{check_infer, check_types}; |
5 | 4 | ||
@@ -2314,7 +2313,7 @@ fn generic_default_depending_on_other_type_arg_forward() { | |||
2314 | 2313 | ||
2315 | #[test] | 2314 | #[test] |
2316 | fn infer_operator_overload() { | 2315 | fn infer_operator_overload() { |
2317 | mark::check!(infer_expr_inner_binary_operator_overload); | 2316 | cov_mark::check!(infer_expr_inner_binary_operator_overload); |
2318 | 2317 | ||
2319 | check_infer( | 2318 | check_infer( |
2320 | r#" | 2319 | r#" |
diff --git a/crates/hir_ty/src/tests/traits.rs b/crates/hir_ty/src/tests/traits.rs index 1298e5a88..e185b1c0a 100644 --- a/crates/hir_ty/src/tests/traits.rs +++ b/crates/hir_ty/src/tests/traits.rs | |||
@@ -1,5 +1,4 @@ | |||
1 | use expect_test::expect; | 1 | use expect_test::expect; |
2 | use test_utils::mark; | ||
3 | 2 | ||
4 | use super::{check_infer, check_infer_with_mismatches, check_types}; | 3 | use super::{check_infer, check_infer_with_mismatches, check_types}; |
5 | 4 | ||
@@ -319,7 +318,7 @@ fn infer_from_bound_2() { | |||
319 | 318 | ||
320 | #[test] | 319 | #[test] |
321 | fn trait_default_method_self_bound_implements_trait() { | 320 | fn trait_default_method_self_bound_implements_trait() { |
322 | mark::check!(trait_self_implements_self); | 321 | cov_mark::check!(trait_self_implements_self); |
323 | check_infer( | 322 | check_infer( |
324 | r#" | 323 | r#" |
325 | trait Trait { | 324 | trait Trait { |
@@ -1189,7 +1188,7 @@ fn impl_trait() { | |||
1189 | 1188 | ||
1190 | #[test] | 1189 | #[test] |
1191 | fn simple_return_pos_impl_trait() { | 1190 | fn simple_return_pos_impl_trait() { |
1192 | mark::check!(lower_rpit); | 1191 | cov_mark::check!(lower_rpit); |
1193 | check_infer( | 1192 | check_infer( |
1194 | r#" | 1193 | r#" |
1195 | trait Trait<T> { | 1194 | trait Trait<T> { |
@@ -3175,6 +3174,39 @@ fn f() { | |||
3175 | } | 3174 | } |
3176 | 3175 | ||
3177 | #[test] | 3176 | #[test] |
3177 | fn trait_in_scope_with_inner_item() { | ||
3178 | check_infer( | ||
3179 | r#" | ||
3180 | mod m { | ||
3181 | pub trait Tr { | ||
3182 | fn method(&self) -> u8 { 0 } | ||
3183 | } | ||
3184 | |||
3185 | impl Tr for () {} | ||
3186 | } | ||
3187 | |||
3188 | use m::Tr; | ||
3189 | |||
3190 | fn f() { | ||
3191 | fn inner() { | ||
3192 | ().method(); | ||
3193 | //^^^^^^^^^^^ u8 | ||
3194 | } | ||
3195 | } | ||
3196 | "#, | ||
3197 | expect![[r#" | ||
3198 | 46..50 'self': &Self | ||
3199 | 58..63 '{ 0 }': u8 | ||
3200 | 60..61 '0': u8 | ||
3201 | 115..185 '{ ... } }': () | ||
3202 | 132..183 '{ ... }': () | ||
3203 | 142..144 '()': () | ||
3204 | 142..153 '().method()': u8 | ||
3205 | "#]], | ||
3206 | ); | ||
3207 | } | ||
3208 | |||
3209 | #[test] | ||
3178 | fn inner_use_in_block() { | 3210 | fn inner_use_in_block() { |
3179 | check_types( | 3211 | check_types( |
3180 | r#" | 3212 | r#" |
diff --git a/crates/hir_ty/src/traits.rs b/crates/hir_ty/src/traits.rs index e4cdb6d53..27f350f70 100644 --- a/crates/hir_ty/src/traits.rs +++ b/crates/hir_ty/src/traits.rs | |||
@@ -38,22 +38,25 @@ fn create_chalk_solver() -> chalk_recursive::RecursiveSolver<Interner> { | |||
38 | /// fn foo<T: Default>(t: T) {} | 38 | /// fn foo<T: Default>(t: T) {} |
39 | /// ``` | 39 | /// ``` |
40 | /// we assume that `T: Default`. | 40 | /// we assume that `T: Default`. |
41 | #[derive(Clone, Debug, PartialEq, Eq, Hash)] | 41 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
42 | pub struct TraitEnvironment { | 42 | pub struct TraitEnvironment { |
43 | pub predicates: Vec<GenericPredicate>, | 43 | // When we're using Chalk's Ty we can make this a BTreeMap since it's Ord, |
44 | // but for now it's too annoying... | ||
45 | pub(crate) traits_from_clauses: Vec<(Ty, TraitId)>, | ||
46 | pub(crate) env: chalk_ir::Environment<Interner>, | ||
44 | } | 47 | } |
45 | 48 | ||
46 | impl TraitEnvironment { | 49 | impl TraitEnvironment { |
47 | /// Returns trait refs with the given self type which are supposed to hold | 50 | pub(crate) fn traits_in_scope_from_clauses<'a>( |
48 | /// in this trait env. E.g. if we are in `foo<T: SomeTrait>()`, this will | ||
49 | /// find that `T: SomeTrait` if we call it for `T`. | ||
50 | pub(crate) fn trait_predicates_for_self_ty<'a>( | ||
51 | &'a self, | 51 | &'a self, |
52 | ty: &'a Ty, | 52 | ty: &'a Ty, |
53 | ) -> impl Iterator<Item = &'a TraitRef> + 'a { | 53 | ) -> impl Iterator<Item = TraitId> + 'a { |
54 | self.predicates.iter().filter_map(move |pred| match pred { | 54 | self.traits_from_clauses.iter().filter_map(move |(self_ty, trait_id)| { |
55 | GenericPredicate::Implemented(tr) if tr.self_ty() == ty => Some(tr), | 55 | if self_ty == ty { |
56 | _ => None, | 56 | Some(*trait_id) |
57 | } else { | ||
58 | None | ||
59 | } | ||
57 | }) | 60 | }) |
58 | } | 61 | } |
59 | } | 62 | } |
diff --git a/crates/hir_ty/src/traits/chalk.rs b/crates/hir_ty/src/traits/chalk.rs index 4378a9723..1a2a3a8c7 100644 --- a/crates/hir_ty/src/traits/chalk.rs +++ b/crates/hir_ty/src/traits/chalk.rs | |||
@@ -33,13 +33,13 @@ pub(super) mod tls; | |||
33 | mod interner; | 33 | mod interner; |
34 | mod mapping; | 34 | mod mapping; |
35 | 35 | ||
36 | pub(super) trait ToChalk { | 36 | pub(crate) trait ToChalk { |
37 | type Chalk; | 37 | type Chalk; |
38 | fn to_chalk(self, db: &dyn HirDatabase) -> Self::Chalk; | 38 | fn to_chalk(self, db: &dyn HirDatabase) -> Self::Chalk; |
39 | fn from_chalk(db: &dyn HirDatabase, chalk: Self::Chalk) -> Self; | 39 | fn from_chalk(db: &dyn HirDatabase, chalk: Self::Chalk) -> Self; |
40 | } | 40 | } |
41 | 41 | ||
42 | pub(super) fn from_chalk<T, ChalkT>(db: &dyn HirDatabase, chalk: ChalkT) -> T | 42 | pub(crate) fn from_chalk<T, ChalkT>(db: &dyn HirDatabase, chalk: ChalkT) -> T |
43 | where | 43 | where |
44 | T: ToChalk<Chalk = ChalkT>, | 44 | T: ToChalk<Chalk = ChalkT>, |
45 | { | 45 | { |
@@ -424,7 +424,7 @@ pub(crate) fn trait_datum_query( | |||
424 | let bound_vars = Substs::bound_vars(&generic_params, DebruijnIndex::INNERMOST); | 424 | let bound_vars = Substs::bound_vars(&generic_params, DebruijnIndex::INNERMOST); |
425 | let flags = rust_ir::TraitFlags { | 425 | let flags = rust_ir::TraitFlags { |
426 | auto: trait_data.auto, | 426 | auto: trait_data.auto, |
427 | upstream: trait_.lookup(db.upcast()).container.module(db.upcast()).krate() != krate, | 427 | upstream: trait_.lookup(db.upcast()).container.krate() != krate, |
428 | non_enumerable: true, | 428 | non_enumerable: true, |
429 | coinductive: false, // only relevant for Chalk testing | 429 | coinductive: false, // only relevant for Chalk testing |
430 | // FIXME: set these flags correctly | 430 | // FIXME: set these flags correctly |
@@ -487,19 +487,14 @@ pub(crate) fn struct_datum_query( | |||
487 | struct_id: AdtId, | 487 | struct_id: AdtId, |
488 | ) -> Arc<StructDatum> { | 488 | ) -> Arc<StructDatum> { |
489 | debug!("struct_datum {:?}", struct_id); | 489 | debug!("struct_datum {:?}", struct_id); |
490 | let type_ctor = Ty::Adt(struct_id, Substs::empty()); | ||
491 | let chalk_ir::AdtId(adt_id) = struct_id; | 490 | let chalk_ir::AdtId(adt_id) = struct_id; |
492 | debug!("struct {:?} = {:?}", struct_id, type_ctor); | ||
493 | let num_params = generics(db.upcast(), adt_id.into()).len(); | 491 | let num_params = generics(db.upcast(), adt_id.into()).len(); |
494 | let upstream = adt_id.module(db.upcast()).krate() != krate; | 492 | let upstream = adt_id.module(db.upcast()).krate() != krate; |
495 | let where_clauses = type_ctor | 493 | let where_clauses = { |
496 | .as_generic_def() | 494 | let generic_params = generics(db.upcast(), adt_id.into()); |
497 | .map(|generic_def| { | 495 | let bound_vars = Substs::bound_vars(&generic_params, DebruijnIndex::INNERMOST); |
498 | let generic_params = generics(db.upcast(), generic_def); | 496 | convert_where_clauses(db, adt_id.into(), &bound_vars) |
499 | let bound_vars = Substs::bound_vars(&generic_params, DebruijnIndex::INNERMOST); | 497 | }; |
500 | convert_where_clauses(db, generic_def, &bound_vars) | ||
501 | }) | ||
502 | .unwrap_or_else(Vec::new); | ||
503 | let flags = rust_ir::AdtFlags { | 498 | let flags = rust_ir::AdtFlags { |
504 | upstream, | 499 | upstream, |
505 | // FIXME set fundamental and phantom_data flags correctly | 500 | // FIXME set fundamental and phantom_data flags correctly |
@@ -548,7 +543,7 @@ fn impl_def_datum( | |||
548 | let generic_params = generics(db.upcast(), impl_id.into()); | 543 | let generic_params = generics(db.upcast(), impl_id.into()); |
549 | let bound_vars = Substs::bound_vars(&generic_params, DebruijnIndex::INNERMOST); | 544 | let bound_vars = Substs::bound_vars(&generic_params, DebruijnIndex::INNERMOST); |
550 | let trait_ = trait_ref.trait_; | 545 | let trait_ = trait_ref.trait_; |
551 | let impl_type = if impl_id.lookup(db.upcast()).container.module(db.upcast()).krate() == krate { | 546 | let impl_type = if impl_id.lookup(db.upcast()).container.krate() == krate { |
552 | rust_ir::ImplType::Local | 547 | rust_ir::ImplType::Local |
553 | } else { | 548 | } else { |
554 | rust_ir::ImplType::External | 549 | rust_ir::ImplType::External |
diff --git a/crates/hir_ty/src/traits/chalk/mapping.rs b/crates/hir_ty/src/traits/chalk/mapping.rs index 3a08b67e9..b0415e8b0 100644 --- a/crates/hir_ty/src/traits/chalk/mapping.rs +++ b/crates/hir_ty/src/traits/chalk/mapping.rs | |||
@@ -17,7 +17,7 @@ use crate::{ | |||
17 | primitive::UintTy, | 17 | primitive::UintTy, |
18 | traits::{Canonical, Obligation}, | 18 | traits::{Canonical, Obligation}, |
19 | AliasTy, CallableDefId, FnPointer, FnSig, GenericPredicate, InEnvironment, OpaqueTy, | 19 | AliasTy, CallableDefId, FnPointer, FnSig, GenericPredicate, InEnvironment, OpaqueTy, |
20 | OpaqueTyId, ProjectionPredicate, ProjectionTy, Scalar, Substs, TraitEnvironment, TraitRef, Ty, | 20 | OpaqueTyId, ProjectionPredicate, ProjectionTy, Scalar, Substs, TraitRef, Ty, |
21 | }; | 21 | }; |
22 | 22 | ||
23 | use super::interner::*; | 23 | use super::interner::*; |
@@ -536,31 +536,6 @@ where | |||
536 | } | 536 | } |
537 | } | 537 | } |
538 | 538 | ||
539 | impl ToChalk for Arc<TraitEnvironment> { | ||
540 | type Chalk = chalk_ir::Environment<Interner>; | ||
541 | |||
542 | fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::Environment<Interner> { | ||
543 | let mut clauses = Vec::new(); | ||
544 | for pred in &self.predicates { | ||
545 | if pred.is_error() { | ||
546 | // for env, we just ignore errors | ||
547 | continue; | ||
548 | } | ||
549 | let program_clause: chalk_ir::ProgramClause<Interner> = | ||
550 | pred.clone().to_chalk(db).cast(&Interner); | ||
551 | clauses.push(program_clause.into_from_env_clause(&Interner)); | ||
552 | } | ||
553 | chalk_ir::Environment::new(&Interner).add_clauses(&Interner, clauses) | ||
554 | } | ||
555 | |||
556 | fn from_chalk( | ||
557 | _db: &dyn HirDatabase, | ||
558 | _env: chalk_ir::Environment<Interner>, | ||
559 | ) -> Arc<TraitEnvironment> { | ||
560 | unimplemented!() | ||
561 | } | ||
562 | } | ||
563 | |||
564 | impl<T: ToChalk> ToChalk for InEnvironment<T> | 539 | impl<T: ToChalk> ToChalk for InEnvironment<T> |
565 | where | 540 | where |
566 | T::Chalk: chalk_ir::interner::HasInterner<Interner = Interner>, | 541 | T::Chalk: chalk_ir::interner::HasInterner<Interner = Interner>, |
@@ -569,19 +544,16 @@ where | |||
569 | 544 | ||
570 | fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::InEnvironment<T::Chalk> { | 545 | fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::InEnvironment<T::Chalk> { |
571 | chalk_ir::InEnvironment { | 546 | chalk_ir::InEnvironment { |
572 | environment: self.environment.to_chalk(db), | 547 | environment: self.environment.env.clone(), |
573 | goal: self.value.to_chalk(db), | 548 | goal: self.value.to_chalk(db), |
574 | } | 549 | } |
575 | } | 550 | } |
576 | 551 | ||
577 | fn from_chalk( | 552 | fn from_chalk( |
578 | db: &dyn HirDatabase, | 553 | _db: &dyn HirDatabase, |
579 | in_env: chalk_ir::InEnvironment<T::Chalk>, | 554 | _in_env: chalk_ir::InEnvironment<T::Chalk>, |
580 | ) -> InEnvironment<T> { | 555 | ) -> InEnvironment<T> { |
581 | InEnvironment { | 556 | unimplemented!() |
582 | environment: from_chalk(db, in_env.environment), | ||
583 | value: from_chalk(db, in_env.goal), | ||
584 | } | ||
585 | } | 557 | } |
586 | } | 558 | } |
587 | 559 | ||
diff --git a/crates/hir_ty/src/utils.rs b/crates/hir_ty/src/utils.rs index 65b79df0d..7351e4e54 100644 --- a/crates/hir_ty/src/utils.rs +++ b/crates/hir_ty/src/utils.rs | |||
@@ -259,6 +259,6 @@ fn parent_generic_def(db: &dyn DefDatabase, def: GenericDefId) -> Option<Generic | |||
259 | match container { | 259 | match container { |
260 | AssocContainerId::ImplId(it) => Some(it.into()), | 260 | AssocContainerId::ImplId(it) => Some(it.into()), |
261 | AssocContainerId::TraitId(it) => Some(it.into()), | 261 | AssocContainerId::TraitId(it) => Some(it.into()), |
262 | AssocContainerId::ContainerId(_) => None, | 262 | AssocContainerId::ModuleId(_) => None, |
263 | } | 263 | } |
264 | } | 264 | } |
diff --git a/crates/ide/Cargo.toml b/crates/ide/Cargo.toml index f6aaaeda4..107bd8432 100644 --- a/crates/ide/Cargo.toml +++ b/crates/ide/Cargo.toml | |||
@@ -10,6 +10,7 @@ edition = "2018" | |||
10 | doctest = false | 10 | doctest = false |
11 | 11 | ||
12 | [dependencies] | 12 | [dependencies] |
13 | cov-mark = "1.1" | ||
13 | either = "1.5.3" | 14 | either = "1.5.3" |
14 | indexmap = "1.4.0" | 15 | indexmap = "1.4.0" |
15 | itertools = "0.10.0" | 16 | itertools = "0.10.0" |
@@ -26,7 +27,6 @@ text_edit = { path = "../text_edit", version = "0.0.0" } | |||
26 | ide_db = { path = "../ide_db", version = "0.0.0" } | 27 | ide_db = { path = "../ide_db", version = "0.0.0" } |
27 | cfg = { path = "../cfg", version = "0.0.0" } | 28 | cfg = { path = "../cfg", version = "0.0.0" } |
28 | profile = { path = "../profile", version = "0.0.0" } | 29 | profile = { path = "../profile", version = "0.0.0" } |
29 | test_utils = { path = "../test_utils", version = "0.0.0" } | ||
30 | ide_assists = { path = "../ide_assists", version = "0.0.0" } | 30 | ide_assists = { path = "../ide_assists", version = "0.0.0" } |
31 | ide_ssr = { path = "../ide_ssr", version = "0.0.0" } | 31 | ide_ssr = { path = "../ide_ssr", version = "0.0.0" } |
32 | ide_completion = { path = "../ide_completion", version = "0.0.0" } | 32 | ide_completion = { path = "../ide_completion", version = "0.0.0" } |
@@ -36,4 +36,5 @@ ide_completion = { path = "../ide_completion", version = "0.0.0" } | |||
36 | hir = { path = "../hir", version = "0.0.0" } | 36 | hir = { path = "../hir", version = "0.0.0" } |
37 | 37 | ||
38 | [dev-dependencies] | 38 | [dev-dependencies] |
39 | test_utils = { path = "../test_utils" } | ||
39 | expect-test = "1.1" | 40 | expect-test = "1.1" |
diff --git a/crates/ide/src/display/short_label.rs b/crates/ide/src/display/short_label.rs index 84b8883de..2df9266b4 100644 --- a/crates/ide/src/display/short_label.rs +++ b/crates/ide/src/display/short_label.rs | |||
@@ -71,11 +71,7 @@ impl ShortLabel for ast::TypeAlias { | |||
71 | 71 | ||
72 | impl ShortLabel for ast::Const { | 72 | impl ShortLabel for ast::Const { |
73 | fn short_label(&self) -> Option<String> { | 73 | fn short_label(&self) -> Option<String> { |
74 | let mut new_buf = short_label_from_ty(self, self.ty(), "const ")?; | 74 | short_label_from_ty(self, self.ty(), "const ") |
75 | if let Some(expr) = self.body() { | ||
76 | format_to!(new_buf, " = {}", expr.syntax()); | ||
77 | } | ||
78 | Some(new_buf) | ||
79 | } | 75 | } |
80 | } | 76 | } |
81 | 77 | ||
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs index a9454cfa3..ea45086ce 100644 --- a/crates/ide/src/hover.rs +++ b/crates/ide/src/hover.rs | |||
@@ -1,3 +1,4 @@ | |||
1 | use either::Either; | ||
1 | use hir::{ | 2 | use hir::{ |
2 | Adt, AsAssocItem, AssocItemContainer, FieldSource, GenericParam, HasAttrs, HasSource, | 3 | Adt, AsAssocItem, AssocItemContainer, FieldSource, GenericParam, HasAttrs, HasSource, |
3 | HirDisplay, Module, ModuleDef, ModuleSource, Semantics, | 4 | HirDisplay, Module, ModuleDef, ModuleSource, Semantics, |
@@ -11,7 +12,6 @@ use ide_db::{ | |||
11 | use itertools::Itertools; | 12 | use itertools::Itertools; |
12 | use stdx::format_to; | 13 | use stdx::format_to; |
13 | use syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset, T}; | 14 | use syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset, T}; |
14 | use test_utils::mark; | ||
15 | 15 | ||
16 | use crate::{ | 16 | use crate::{ |
17 | display::{macro_label, ShortLabel, TryToNav}, | 17 | display::{macro_label, ShortLabel, TryToNav}, |
@@ -193,8 +193,8 @@ fn runnable_action( | |||
193 | ModuleDef::Function(func) => { | 193 | ModuleDef::Function(func) => { |
194 | let src = func.source(sema.db)?; | 194 | let src = func.source(sema.db)?; |
195 | if src.file_id != file_id.into() { | 195 | if src.file_id != file_id.into() { |
196 | mark::hit!(hover_macro_generated_struct_fn_doc_comment); | 196 | cov_mark::hit!(hover_macro_generated_struct_fn_doc_comment); |
197 | mark::hit!(hover_macro_generated_struct_fn_doc_attr); | 197 | cov_mark::hit!(hover_macro_generated_struct_fn_doc_attr); |
198 | return None; | 198 | return None; |
199 | } | 199 | } |
200 | 200 | ||
@@ -367,7 +367,7 @@ fn hover_for_definition( | |||
367 | .and_then(|fd| hover_for_builtin(fd, it)) | 367 | .and_then(|fd| hover_for_builtin(fd, it)) |
368 | .or_else(|| Some(Markup::fenced_block(&it.name()))), | 368 | .or_else(|| Some(Markup::fenced_block(&it.name()))), |
369 | }, | 369 | }, |
370 | Definition::Local(it) => Some(Markup::fenced_block(&it.ty(db).display(db))), | 370 | Definition::Local(it) => hover_for_local(it, db), |
371 | Definition::SelfType(impl_def) => { | 371 | Definition::SelfType(impl_def) => { |
372 | impl_def.target_ty(db).as_adt().and_then(|adt| match adt { | 372 | impl_def.target_ty(db).as_adt().and_then(|adt| match adt { |
373 | Adt::Struct(it) => from_def_source(db, it, mod_path), | 373 | Adt::Struct(it) => from_def_source(db, it, mod_path), |
@@ -406,6 +406,29 @@ fn hover_for_definition( | |||
406 | } | 406 | } |
407 | } | 407 | } |
408 | 408 | ||
409 | fn hover_for_local(it: hir::Local, db: &RootDatabase) -> Option<Markup> { | ||
410 | let ty = it.ty(db); | ||
411 | let ty = ty.display(db); | ||
412 | let is_mut = if it.is_mut(db) { "mut " } else { "" }; | ||
413 | let desc = match it.source(db).value { | ||
414 | Either::Left(ident) => { | ||
415 | let name = it.name(db).unwrap(); | ||
416 | let let_kw = if ident | ||
417 | .syntax() | ||
418 | .parent() | ||
419 | .map_or(false, |p| p.kind() == LET_STMT || p.kind() == CONDITION) | ||
420 | { | ||
421 | "let " | ||
422 | } else { | ||
423 | "" | ||
424 | }; | ||
425 | format!("{}{}{}: {}", let_kw, is_mut, name, ty) | ||
426 | } | ||
427 | Either::Right(_) => format!("{}self: {}", is_mut, ty), | ||
428 | }; | ||
429 | hover_markup(None, Some(desc), None) | ||
430 | } | ||
431 | |||
409 | fn hover_for_keyword( | 432 | fn hover_for_keyword( |
410 | sema: &Semantics<RootDatabase>, | 433 | sema: &Semantics<RootDatabase>, |
411 | links_in_hover: bool, | 434 | links_in_hover: bool, |
@@ -575,7 +598,7 @@ fn main() { | |||
575 | *iter* | 598 | *iter* |
576 | 599 | ||
577 | ```rust | 600 | ```rust |
578 | Iter<Scan<OtherStruct<OtherStruct<i32>>, |&mut u32, &u32, &mut u32| -> Option<u32>, u32>> | 601 | let mut iter: Iter<Scan<OtherStruct<OtherStruct<i32>>, |&mut u32, &u32, &mut u32| -> Option<u32>, u32>> |
579 | ``` | 602 | ``` |
580 | "#]], | 603 | "#]], |
581 | ); | 604 | ); |
@@ -799,7 +822,7 @@ fn main() { | |||
799 | ``` | 822 | ``` |
800 | 823 | ||
801 | ```rust | 824 | ```rust |
802 | const foo: u32 = 123 | 825 | const foo: u32 |
803 | ``` | 826 | ``` |
804 | "#]], | 827 | "#]], |
805 | ); | 828 | ); |
@@ -832,7 +855,7 @@ fn main() { | |||
832 | *zz* | 855 | *zz* |
833 | 856 | ||
834 | ```rust | 857 | ```rust |
835 | Test<i32, u8> | 858 | let zz: Test<i32, u8> |
836 | ``` | 859 | ``` |
837 | "#]], | 860 | "#]], |
838 | ); | 861 | ); |
@@ -871,7 +894,7 @@ fn main() { let b$0ar = Some(12); } | |||
871 | *bar* | 894 | *bar* |
872 | 895 | ||
873 | ```rust | 896 | ```rust |
874 | Option<i32> | 897 | let bar: Option<i32> |
875 | ``` | 898 | ``` |
876 | "#]], | 899 | "#]], |
877 | ); | 900 | ); |
@@ -939,7 +962,7 @@ fn main() { | |||
939 | *foo* | 962 | *foo* |
940 | 963 | ||
941 | ```rust | 964 | ```rust |
942 | i32 | 965 | foo: i32 |
943 | ``` | 966 | ``` |
944 | "#]], | 967 | "#]], |
945 | ) | 968 | ) |
@@ -953,7 +976,7 @@ fn main() { | |||
953 | *foo* | 976 | *foo* |
954 | 977 | ||
955 | ```rust | 978 | ```rust |
956 | i32 | 979 | foo: i32 |
957 | ``` | 980 | ``` |
958 | "#]], | 981 | "#]], |
959 | ) | 982 | ) |
@@ -967,7 +990,7 @@ fn main() { | |||
967 | *foo* | 990 | *foo* |
968 | 991 | ||
969 | ```rust | 992 | ```rust |
970 | i32 | 993 | foo: i32 |
971 | ``` | 994 | ``` |
972 | "#]], | 995 | "#]], |
973 | ) | 996 | ) |
@@ -981,7 +1004,7 @@ fn main() { | |||
981 | *foo* | 1004 | *foo* |
982 | 1005 | ||
983 | ```rust | 1006 | ```rust |
984 | i32 | 1007 | foo: i32 |
985 | ``` | 1008 | ``` |
986 | "#]], | 1009 | "#]], |
987 | ) | 1010 | ) |
@@ -1001,7 +1024,7 @@ fn main() { | |||
1001 | *_x* | 1024 | *_x* |
1002 | 1025 | ||
1003 | ```rust | 1026 | ```rust |
1004 | impl Deref<Target = u8> + DerefMut<Target = u8> | 1027 | _x: impl Deref<Target = u8> + DerefMut<Target = u8> |
1005 | ``` | 1028 | ``` |
1006 | "#]], | 1029 | "#]], |
1007 | ) | 1030 | ) |
@@ -1023,7 +1046,7 @@ fn main() { let foo_$0test = Thing::new(); } | |||
1023 | *foo_test* | 1046 | *foo_test* |
1024 | 1047 | ||
1025 | ```rust | 1048 | ```rust |
1026 | Thing | 1049 | let foo_test: Thing |
1027 | ``` | 1050 | ``` |
1028 | "#]], | 1051 | "#]], |
1029 | ) | 1052 | ) |
@@ -1082,7 +1105,7 @@ fn main() { | |||
1082 | ``` | 1105 | ``` |
1083 | 1106 | ||
1084 | ```rust | 1107 | ```rust |
1085 | const C: u32 = 1 | 1108 | const C: u32 |
1086 | ``` | 1109 | ``` |
1087 | "#]], | 1110 | "#]], |
1088 | ) | 1111 | ) |
@@ -1183,7 +1206,7 @@ fn y() { | |||
1183 | *x* | 1206 | *x* |
1184 | 1207 | ||
1185 | ```rust | 1208 | ```rust |
1186 | i32 | 1209 | let x: i32 |
1187 | ``` | 1210 | ``` |
1188 | "#]], | 1211 | "#]], |
1189 | ) | 1212 | ) |
@@ -1260,7 +1283,7 @@ fn foo(bar:u32) { let a = id!(ba$0r); } | |||
1260 | *bar* | 1283 | *bar* |
1261 | 1284 | ||
1262 | ```rust | 1285 | ```rust |
1263 | u32 | 1286 | bar: u32 |
1264 | ``` | 1287 | ``` |
1265 | "#]], | 1288 | "#]], |
1266 | ); | 1289 | ); |
@@ -1278,7 +1301,7 @@ fn foo(bar:u32) { let a = id!(ba$0r); } | |||
1278 | *bar* | 1301 | *bar* |
1279 | 1302 | ||
1280 | ```rust | 1303 | ```rust |
1281 | u32 | 1304 | bar: u32 |
1282 | ``` | 1305 | ``` |
1283 | "#]], | 1306 | "#]], |
1284 | ); | 1307 | ); |
@@ -2101,7 +2124,7 @@ pub fn fo$0o() {} | |||
2101 | 2124 | ||
2102 | #[test] | 2125 | #[test] |
2103 | fn test_hover_macro_generated_struct_fn_doc_comment() { | 2126 | fn test_hover_macro_generated_struct_fn_doc_comment() { |
2104 | mark::check!(hover_macro_generated_struct_fn_doc_comment); | 2127 | cov_mark::check!(hover_macro_generated_struct_fn_doc_comment); |
2105 | 2128 | ||
2106 | check( | 2129 | check( |
2107 | r#" | 2130 | r#" |
@@ -2139,7 +2162,7 @@ fn foo() { let bar = Bar; bar.fo$0o(); } | |||
2139 | 2162 | ||
2140 | #[test] | 2163 | #[test] |
2141 | fn test_hover_macro_generated_struct_fn_doc_attr() { | 2164 | fn test_hover_macro_generated_struct_fn_doc_attr() { |
2142 | mark::check!(hover_macro_generated_struct_fn_doc_attr); | 2165 | cov_mark::check!(hover_macro_generated_struct_fn_doc_attr); |
2143 | 2166 | ||
2144 | check( | 2167 | check( |
2145 | r#" | 2168 | r#" |
@@ -3303,7 +3326,7 @@ fn main() { | |||
3303 | *f* | 3326 | *f* |
3304 | 3327 | ||
3305 | ```rust | 3328 | ```rust |
3306 | &i32 | 3329 | f: &i32 |
3307 | ``` | 3330 | ``` |
3308 | "#]], | 3331 | "#]], |
3309 | ); | 3332 | ); |
@@ -3322,7 +3345,7 @@ impl Foo { | |||
3322 | *self* | 3345 | *self* |
3323 | 3346 | ||
3324 | ```rust | 3347 | ```rust |
3325 | &Foo | 3348 | self: &Foo |
3326 | ``` | 3349 | ``` |
3327 | "#]], | 3350 | "#]], |
3328 | ); | 3351 | ); |
@@ -3342,7 +3365,7 @@ impl Foo { | |||
3342 | *self* | 3365 | *self* |
3343 | 3366 | ||
3344 | ```rust | 3367 | ```rust |
3345 | Arc<Foo> | 3368 | self: Arc<Foo> |
3346 | ``` | 3369 | ``` |
3347 | "#]], | 3370 | "#]], |
3348 | ); | 3371 | ); |
@@ -3538,7 +3561,7 @@ fn foo() { | |||
3538 | ``` | 3561 | ``` |
3539 | 3562 | ||
3540 | ```rust | 3563 | ```rust |
3541 | const FOO: usize = 3 | 3564 | const FOO: usize |
3542 | ``` | 3565 | ``` |
3543 | 3566 | ||
3544 | --- | 3567 | --- |
diff --git a/crates/ide/src/join_lines.rs b/crates/ide/src/join_lines.rs index 7fcae13e0..20a920ddb 100644 --- a/crates/ide/src/join_lines.rs +++ b/crates/ide/src/join_lines.rs | |||
@@ -7,7 +7,7 @@ use syntax::{ | |||
7 | SyntaxKind::{self, USE_TREE, WHITESPACE}, | 7 | SyntaxKind::{self, USE_TREE, WHITESPACE}, |
8 | SyntaxNode, SyntaxToken, TextRange, TextSize, T, | 8 | SyntaxNode, SyntaxToken, TextRange, TextSize, T, |
9 | }; | 9 | }; |
10 | use test_utils::mark; | 10 | |
11 | use text_edit::{TextEdit, TextEditBuilder}; | 11 | use text_edit::{TextEdit, TextEditBuilder}; |
12 | 12 | ||
13 | // Feature: Join Lines | 13 | // Feature: Join Lines |
@@ -60,7 +60,7 @@ fn remove_newline(edit: &mut TextEditBuilder, token: &SyntaxToken, offset: TextS | |||
60 | let mut string_open_quote = false; | 60 | let mut string_open_quote = false; |
61 | if let Some(string) = ast::String::cast(token.clone()) { | 61 | if let Some(string) = ast::String::cast(token.clone()) { |
62 | if let Some(range) = string.open_quote_text_range() { | 62 | if let Some(range) = string.open_quote_text_range() { |
63 | mark::hit!(join_string_literal); | 63 | cov_mark::hit!(join_string_literal); |
64 | string_open_quote = range.end() == offset; | 64 | string_open_quote = range.end() == offset; |
65 | } | 65 | } |
66 | } | 66 | } |
@@ -206,7 +206,7 @@ fn compute_ws(left: SyntaxKind, right: SyntaxKind) -> &'static str { | |||
206 | #[cfg(test)] | 206 | #[cfg(test)] |
207 | mod tests { | 207 | mod tests { |
208 | use syntax::SourceFile; | 208 | use syntax::SourceFile; |
209 | use test_utils::{add_cursor, assert_eq_text, extract_offset, extract_range, mark}; | 209 | use test_utils::{add_cursor, assert_eq_text, extract_offset, extract_range}; |
210 | 210 | ||
211 | use super::*; | 211 | use super::*; |
212 | 212 | ||
@@ -786,7 +786,7 @@ fn foo() { | |||
786 | 786 | ||
787 | #[test] | 787 | #[test] |
788 | fn join_string_literal() { | 788 | fn join_string_literal() { |
789 | mark::check!(join_string_literal); | 789 | cov_mark::check!(join_string_literal); |
790 | check_join_lines( | 790 | check_join_lines( |
791 | r#" | 791 | r#" |
792 | fn main() { | 792 | fn main() { |
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs index b600178ee..0a493d2f3 100644 --- a/crates/ide/src/lib.rs +++ b/crates/ide/src/lib.rs | |||
@@ -41,6 +41,7 @@ mod parent_module; | |||
41 | mod references; | 41 | mod references; |
42 | mod fn_references; | 42 | mod fn_references; |
43 | mod runnables; | 43 | mod runnables; |
44 | mod ssr; | ||
44 | mod status; | 45 | mod status; |
45 | mod syntax_highlighting; | 46 | mod syntax_highlighting; |
46 | mod syntax_tree; | 47 | mod syntax_tree; |
@@ -51,6 +52,7 @@ mod doc_links; | |||
51 | use std::sync::Arc; | 52 | use std::sync::Arc; |
52 | 53 | ||
53 | use cfg::CfgOptions; | 54 | use cfg::CfgOptions; |
55 | |||
54 | use ide_db::base_db::{ | 56 | use ide_db::base_db::{ |
55 | salsa::{self, ParallelDatabase}, | 57 | salsa::{self, ParallelDatabase}, |
56 | CheckCanceled, Env, FileLoader, FileSet, SourceDatabase, VfsPath, | 58 | CheckCanceled, Env, FileLoader, FileSet, SourceDatabase, VfsPath, |
@@ -85,7 +87,7 @@ pub use crate::{ | |||
85 | pub use hir::{Documentation, Semantics}; | 87 | pub use hir::{Documentation, Semantics}; |
86 | pub use ide_assists::{Assist, AssistConfig, AssistId, AssistKind}; | 88 | pub use ide_assists::{Assist, AssistConfig, AssistId, AssistKind}; |
87 | pub use ide_completion::{ | 89 | pub use ide_completion::{ |
88 | CompletionConfig, CompletionItem, CompletionItemKind, CompletionScore, ImportEdit, | 90 | CompletionConfig, CompletionItem, CompletionItemKind, CompletionRelevance, ImportEdit, |
89 | InsertTextFormat, | 91 | InsertTextFormat, |
90 | }; | 92 | }; |
91 | pub use ide_db::{ | 93 | pub use ide_db::{ |
@@ -478,7 +480,6 @@ impl Analysis { | |||
478 | position: FilePosition, | 480 | position: FilePosition, |
479 | full_import_path: &str, | 481 | full_import_path: &str, |
480 | imported_name: String, | 482 | imported_name: String, |
481 | import_for_trait_assoc_item: bool, | ||
482 | ) -> Cancelable<Vec<TextEdit>> { | 483 | ) -> Cancelable<Vec<TextEdit>> { |
483 | Ok(self | 484 | Ok(self |
484 | .with_db(|db| { | 485 | .with_db(|db| { |
@@ -488,7 +489,6 @@ impl Analysis { | |||
488 | position, | 489 | position, |
489 | full_import_path, | 490 | full_import_path, |
490 | imported_name, | 491 | imported_name, |
491 | import_for_trait_assoc_item, | ||
492 | ) | 492 | ) |
493 | })? | 493 | })? |
494 | .unwrap_or_default()) | 494 | .unwrap_or_default()) |
@@ -504,7 +504,11 @@ impl Analysis { | |||
504 | resolve: bool, | 504 | resolve: bool, |
505 | frange: FileRange, | 505 | frange: FileRange, |
506 | ) -> Cancelable<Vec<Assist>> { | 506 | ) -> Cancelable<Vec<Assist>> { |
507 | self.with_db(|db| Assist::get(db, config, resolve, frange)) | 507 | self.with_db(|db| { |
508 | let mut acc = Assist::get(db, config, resolve, frange); | ||
509 | ssr::add_ssr_assist(db, &mut acc, resolve, frange); | ||
510 | acc | ||
511 | }) | ||
508 | } | 512 | } |
509 | 513 | ||
510 | /// Computes the set of diagnostics for the given file. | 514 | /// Computes the set of diagnostics for the given file. |
diff --git a/crates/ide/src/matching_brace.rs b/crates/ide/src/matching_brace.rs index 1bfa1439d..000c412d9 100644 --- a/crates/ide/src/matching_brace.rs +++ b/crates/ide/src/matching_brace.rs | |||
@@ -2,7 +2,6 @@ use syntax::{ | |||
2 | ast::{self, AstNode}, | 2 | ast::{self, AstNode}, |
3 | SourceFile, SyntaxKind, TextSize, T, | 3 | SourceFile, SyntaxKind, TextSize, T, |
4 | }; | 4 | }; |
5 | use test_utils::mark; | ||
6 | 5 | ||
7 | // Feature: Matching Brace | 6 | // Feature: Matching Brace |
8 | // | 7 | // |
@@ -28,7 +27,7 @@ pub(crate) fn matching_brace(file: &SourceFile, offset: TextSize) -> Option<Text | |||
28 | .next()?; | 27 | .next()?; |
29 | let parent = brace_token.parent(); | 28 | let parent = brace_token.parent(); |
30 | if brace_token.kind() == T![|] && !ast::ParamList::can_cast(parent.kind()) { | 29 | if brace_token.kind() == T![|] && !ast::ParamList::can_cast(parent.kind()) { |
31 | mark::hit!(pipes_not_braces); | 30 | cov_mark::hit!(pipes_not_braces); |
32 | return None; | 31 | return None; |
33 | } | 32 | } |
34 | let matching_kind = BRACES[brace_idx ^ 1]; | 33 | let matching_kind = BRACES[brace_idx ^ 1]; |
@@ -63,7 +62,7 @@ mod tests { | |||
63 | do_check("fn main() { $0|x: i32| x * 2;}", "fn main() { |x: i32$0| x * 2;}"); | 62 | do_check("fn main() { $0|x: i32| x * 2;}", "fn main() { |x: i32$0| x * 2;}"); |
64 | 63 | ||
65 | { | 64 | { |
66 | mark::check!(pipes_not_braces); | 65 | cov_mark::check!(pipes_not_braces); |
67 | do_check( | 66 | do_check( |
68 | "fn main() { match 92 { 1 | 2 |$0 3 => 92 } }", | 67 | "fn main() { match 92 { 1 | 2 |$0 3 => 92 } }", |
69 | "fn main() { match 92 { 1 | 2 |$0 3 => 92 } }", | 68 | "fn main() { match 92 { 1 | 2 |$0 3 => 92 } }", |
diff --git a/crates/ide/src/parent_module.rs b/crates/ide/src/parent_module.rs index ddbaf22b7..03d71b380 100644 --- a/crates/ide/src/parent_module.rs +++ b/crates/ide/src/parent_module.rs | |||
@@ -5,7 +5,6 @@ use syntax::{ | |||
5 | algo::find_node_at_offset, | 5 | algo::find_node_at_offset, |
6 | ast::{self, AstNode}, | 6 | ast::{self, AstNode}, |
7 | }; | 7 | }; |
8 | use test_utils::mark; | ||
9 | 8 | ||
10 | use crate::NavigationTarget; | 9 | use crate::NavigationTarget; |
11 | 10 | ||
@@ -33,7 +32,7 @@ pub(crate) fn parent_module(db: &RootDatabase, position: FilePosition) -> Vec<Na | |||
33 | .item_list() | 32 | .item_list() |
34 | .map_or(false, |it| it.syntax().text_range().contains_inclusive(position.offset)) | 33 | .map_or(false, |it| it.syntax().text_range().contains_inclusive(position.offset)) |
35 | { | 34 | { |
36 | mark::hit!(test_resolve_parent_module_on_module_decl); | 35 | cov_mark::hit!(test_resolve_parent_module_on_module_decl); |
37 | module = m.syntax().ancestors().skip(1).find_map(ast::Module::cast); | 36 | module = m.syntax().ancestors().skip(1).find_map(ast::Module::cast); |
38 | } | 37 | } |
39 | } | 38 | } |
@@ -64,7 +63,6 @@ pub(crate) fn crate_for(db: &RootDatabase, file_id: FileId) -> Vec<CrateId> { | |||
64 | #[cfg(test)] | 63 | #[cfg(test)] |
65 | mod tests { | 64 | mod tests { |
66 | use ide_db::base_db::FileRange; | 65 | use ide_db::base_db::FileRange; |
67 | use test_utils::mark; | ||
68 | 66 | ||
69 | use crate::fixture; | 67 | use crate::fixture; |
70 | 68 | ||
@@ -92,7 +90,7 @@ $0// empty | |||
92 | 90 | ||
93 | #[test] | 91 | #[test] |
94 | fn test_resolve_parent_module_on_module_decl() { | 92 | fn test_resolve_parent_module_on_module_decl() { |
95 | mark::check!(test_resolve_parent_module_on_module_decl); | 93 | cov_mark::check!(test_resolve_parent_module_on_module_decl); |
96 | check( | 94 | check( |
97 | r#" | 95 | r#" |
98 | //- /lib.rs | 96 | //- /lib.rs |
diff --git a/crates/ide/src/references/rename.rs b/crates/ide/src/references/rename.rs index 1919639a3..1e378279d 100644 --- a/crates/ide/src/references/rename.rs +++ b/crates/ide/src/references/rename.rs | |||
@@ -14,14 +14,14 @@ use syntax::{ | |||
14 | ast::{self, NameOwner}, | 14 | ast::{self, NameOwner}, |
15 | lex_single_syntax_kind, AstNode, SyntaxKind, SyntaxNode, T, | 15 | lex_single_syntax_kind, AstNode, SyntaxKind, SyntaxNode, T, |
16 | }; | 16 | }; |
17 | use test_utils::mark; | 17 | |
18 | use text_edit::TextEdit; | 18 | use text_edit::TextEdit; |
19 | 19 | ||
20 | use crate::{display::TryToNav, FilePosition, FileSystemEdit, RangeInfo, SourceChange, TextRange}; | 20 | use crate::{display::TryToNav, FilePosition, FileSystemEdit, RangeInfo, SourceChange, TextRange}; |
21 | 21 | ||
22 | type RenameResult<T> = Result<T, RenameError>; | 22 | type RenameResult<T> = Result<T, RenameError>; |
23 | #[derive(Debug)] | 23 | #[derive(Debug)] |
24 | pub struct RenameError(pub(crate) String); | 24 | pub struct RenameError(String); |
25 | 25 | ||
26 | impl fmt::Display for RenameError { | 26 | impl fmt::Display for RenameError { |
27 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | 27 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
@@ -47,16 +47,15 @@ pub(crate) fn prepare_rename( | |||
47 | let sema = Semantics::new(db); | 47 | let sema = Semantics::new(db); |
48 | let source_file = sema.parse(position.file_id); | 48 | let source_file = sema.parse(position.file_id); |
49 | let syntax = source_file.syntax(); | 49 | let syntax = source_file.syntax(); |
50 | let range = match &sema | 50 | let name_like = sema |
51 | .find_node_at_offset_with_descend(&syntax, position.offset) | 51 | .find_node_at_offset_with_descend(&syntax, position.offset) |
52 | .ok_or_else(|| format_err!("No references found at position"))? | 52 | .ok_or_else(|| format_err!("No references found at position"))?; |
53 | { | 53 | let node = match &name_like { |
54 | ast::NameLike::Name(it) => it.syntax(), | 54 | ast::NameLike::Name(it) => it.syntax(), |
55 | ast::NameLike::NameRef(it) => it.syntax(), | 55 | ast::NameLike::NameRef(it) => it.syntax(), |
56 | ast::NameLike::Lifetime(it) => it.syntax(), | 56 | ast::NameLike::Lifetime(it) => it.syntax(), |
57 | } | 57 | }; |
58 | .text_range(); | 58 | Ok(RangeInfo::new(sema.original_range(node).range, ())) |
59 | Ok(RangeInfo::new(range, ())) | ||
60 | } | 59 | } |
61 | 60 | ||
62 | // Feature: Rename | 61 | // Feature: Rename |
@@ -94,6 +93,7 @@ pub(crate) fn rename_with_semantics( | |||
94 | } | 93 | } |
95 | } | 94 | } |
96 | 95 | ||
96 | /// Called by the client when it is about to rename a file. | ||
97 | pub(crate) fn will_rename_file( | 97 | pub(crate) fn will_rename_file( |
98 | db: &RootDatabase, | 98 | db: &RootDatabase, |
99 | file_id: FileId, | 99 | file_id: FileId, |
@@ -226,34 +226,36 @@ fn rename_reference( | |||
226 | | (IdentifierKind::Ident, _) | 226 | | (IdentifierKind::Ident, _) |
227 | if def_is_lbl_or_lt => | 227 | if def_is_lbl_or_lt => |
228 | { | 228 | { |
229 | mark::hit!(rename_not_a_lifetime_ident_ref); | 229 | cov_mark::hit!(rename_not_a_lifetime_ident_ref); |
230 | bail!("Invalid name `{}`: not a lifetime identifier", new_name) | 230 | bail!("Invalid name `{}`: not a lifetime identifier", new_name) |
231 | } | 231 | } |
232 | (IdentifierKind::Lifetime, _) if def_is_lbl_or_lt => mark::hit!(rename_lifetime), | 232 | (IdentifierKind::Lifetime, _) if def_is_lbl_or_lt => cov_mark::hit!(rename_lifetime), |
233 | (IdentifierKind::Lifetime, _) => { | 233 | (IdentifierKind::Lifetime, _) => { |
234 | mark::hit!(rename_not_an_ident_ref); | 234 | cov_mark::hit!(rename_not_an_ident_ref); |
235 | bail!("Invalid name `{}`: not an identifier", new_name) | 235 | bail!("Invalid name `{}`: not an identifier", new_name) |
236 | } | 236 | } |
237 | (IdentifierKind::ToSelf, Definition::Local(local)) if local.is_self(sema.db) => { | 237 | (IdentifierKind::ToSelf, Definition::Local(local)) if local.is_self(sema.db) => { |
238 | // no-op | 238 | // no-op |
239 | mark::hit!(rename_self_to_self); | 239 | cov_mark::hit!(rename_self_to_self); |
240 | return Ok(SourceChange::default()); | 240 | return Ok(SourceChange::default()); |
241 | } | 241 | } |
242 | (ident_kind, Definition::Local(local)) if local.is_self(sema.db) => { | 242 | (ident_kind, Definition::Local(local)) if local.is_self(sema.db) => { |
243 | mark::hit!(rename_self_to_param); | 243 | cov_mark::hit!(rename_self_to_param); |
244 | return rename_self_to_param(sema, local, new_name, ident_kind); | 244 | return rename_self_to_param(sema, local, new_name, ident_kind); |
245 | } | 245 | } |
246 | (IdentifierKind::ToSelf, Definition::Local(local)) => { | 246 | (IdentifierKind::ToSelf, Definition::Local(local)) => { |
247 | mark::hit!(rename_to_self); | 247 | cov_mark::hit!(rename_to_self); |
248 | return rename_to_self(sema, local); | 248 | return rename_to_self(sema, local); |
249 | } | 249 | } |
250 | (IdentifierKind::ToSelf, _) => bail!("Invalid name `{}`: not an identifier", new_name), | 250 | (IdentifierKind::ToSelf, _) => bail!("Invalid name `{}`: not an identifier", new_name), |
251 | (IdentifierKind::Ident, _) | (IdentifierKind::Underscore, _) => mark::hit!(rename_ident), | 251 | (IdentifierKind::Ident, _) | (IdentifierKind::Underscore, _) => { |
252 | cov_mark::hit!(rename_ident) | ||
253 | } | ||
252 | } | 254 | } |
253 | 255 | ||
254 | let usages = def.usages(sema).all(); | 256 | let usages = def.usages(sema).all(); |
255 | if !usages.is_empty() && ident_kind == IdentifierKind::Underscore { | 257 | if !usages.is_empty() && ident_kind == IdentifierKind::Underscore { |
256 | mark::hit!(rename_underscore_multiple); | 258 | cov_mark::hit!(rename_underscore_multiple); |
257 | bail!("Cannot rename reference to `_` as it is being referenced multiple times"); | 259 | bail!("Cannot rename reference to `_` as it is being referenced multiple times"); |
258 | } | 260 | } |
259 | let mut source_change = SourceChange::default(); | 261 | let mut source_change = SourceChange::default(); |
@@ -444,7 +446,7 @@ fn source_edit_from_name_ref( | |||
444 | (Some(field_name), Some(init)) => { | 446 | (Some(field_name), Some(init)) => { |
445 | if field_name == *name_ref { | 447 | if field_name == *name_ref { |
446 | if init.text() == new_name { | 448 | if init.text() == new_name { |
447 | mark::hit!(test_rename_field_put_init_shorthand); | 449 | cov_mark::hit!(test_rename_field_put_init_shorthand); |
448 | // same names, we can use a shorthand here instead. | 450 | // same names, we can use a shorthand here instead. |
449 | // we do not want to erase attributes hence this range start | 451 | // we do not want to erase attributes hence this range start |
450 | let s = field_name.syntax().text_range().start(); | 452 | let s = field_name.syntax().text_range().start(); |
@@ -453,7 +455,7 @@ fn source_edit_from_name_ref( | |||
453 | } | 455 | } |
454 | } else if init == *name_ref { | 456 | } else if init == *name_ref { |
455 | if field_name.text() == new_name { | 457 | if field_name.text() == new_name { |
456 | mark::hit!(test_rename_local_put_init_shorthand); | 458 | cov_mark::hit!(test_rename_local_put_init_shorthand); |
457 | // same names, we can use a shorthand here instead. | 459 | // same names, we can use a shorthand here instead. |
458 | // we do not want to erase attributes hence this range start | 460 | // we do not want to erase attributes hence this range start |
459 | let s = field_name.syntax().text_range().start(); | 461 | let s = field_name.syntax().text_range().start(); |
@@ -467,12 +469,12 @@ fn source_edit_from_name_ref( | |||
467 | // FIXME: instead of splitting the shorthand, recursively trigger a rename of the | 469 | // FIXME: instead of splitting the shorthand, recursively trigger a rename of the |
468 | // other name https://github.com/rust-analyzer/rust-analyzer/issues/6547 | 470 | // other name https://github.com/rust-analyzer/rust-analyzer/issues/6547 |
469 | (None, Some(_)) if matches!(def, Definition::Field(_)) => { | 471 | (None, Some(_)) if matches!(def, Definition::Field(_)) => { |
470 | mark::hit!(test_rename_field_in_field_shorthand); | 472 | cov_mark::hit!(test_rename_field_in_field_shorthand); |
471 | let s = name_ref.syntax().text_range().start(); | 473 | let s = name_ref.syntax().text_range().start(); |
472 | Some((TextRange::empty(s), format!("{}: ", new_name))) | 474 | Some((TextRange::empty(s), format!("{}: ", new_name))) |
473 | } | 475 | } |
474 | (None, Some(_)) if matches!(def, Definition::Local(_)) => { | 476 | (None, Some(_)) if matches!(def, Definition::Local(_)) => { |
475 | mark::hit!(test_rename_local_in_field_shorthand); | 477 | cov_mark::hit!(test_rename_local_in_field_shorthand); |
476 | let s = name_ref.syntax().text_range().end(); | 478 | let s = name_ref.syntax().text_range().end(); |
477 | Some((TextRange::empty(s), format!(": {}", new_name))) | 479 | Some((TextRange::empty(s), format!(": {}", new_name))) |
478 | } | 480 | } |
@@ -486,7 +488,7 @@ fn source_edit_from_name_ref( | |||
486 | (Some(field_name), Some(ast::Pat::IdentPat(pat))) if field_name == *name_ref => { | 488 | (Some(field_name), Some(ast::Pat::IdentPat(pat))) if field_name == *name_ref => { |
487 | // field name is being renamed | 489 | // field name is being renamed |
488 | if pat.name().map_or(false, |it| it.text() == new_name) { | 490 | if pat.name().map_or(false, |it| it.text() == new_name) { |
489 | mark::hit!(test_rename_field_put_init_shorthand_pat); | 491 | cov_mark::hit!(test_rename_field_put_init_shorthand_pat); |
490 | // same names, we can use a shorthand here instead/ | 492 | // same names, we can use a shorthand here instead/ |
491 | // we do not want to erase attributes hence this range start | 493 | // we do not want to erase attributes hence this range start |
492 | let s = field_name.syntax().text_range().start(); | 494 | let s = field_name.syntax().text_range().start(); |
@@ -538,11 +540,13 @@ fn source_edit_from_def( | |||
538 | mod tests { | 540 | mod tests { |
539 | use expect_test::{expect, Expect}; | 541 | use expect_test::{expect, Expect}; |
540 | use stdx::trim_indent; | 542 | use stdx::trim_indent; |
541 | use test_utils::{assert_eq_text, mark}; | 543 | use test_utils::assert_eq_text; |
542 | use text_edit::TextEdit; | 544 | use text_edit::TextEdit; |
543 | 545 | ||
544 | use crate::{fixture, FileId}; | 546 | use crate::{fixture, FileId}; |
545 | 547 | ||
548 | use super::{RangeInfo, RenameError}; | ||
549 | |||
546 | fn check(new_name: &str, ra_fixture_before: &str, ra_fixture_after: &str) { | 550 | fn check(new_name: &str, ra_fixture_before: &str, ra_fixture_after: &str) { |
547 | let ra_fixture_after = &trim_indent(ra_fixture_after); | 551 | let ra_fixture_after = &trim_indent(ra_fixture_after); |
548 | let (analysis, position) = fixture::position(ra_fixture_before); | 552 | let (analysis, position) = fixture::position(ra_fixture_before); |
@@ -588,6 +592,45 @@ mod tests { | |||
588 | expect.assert_debug_eq(&source_change) | 592 | expect.assert_debug_eq(&source_change) |
589 | } | 593 | } |
590 | 594 | ||
595 | fn check_prepare(ra_fixture: &str, expect: Expect) { | ||
596 | let (analysis, position) = fixture::position(ra_fixture); | ||
597 | let result = analysis | ||
598 | .prepare_rename(position) | ||
599 | .unwrap_or_else(|err| panic!("PrepareRename was cancelled: {}", err)); | ||
600 | match result { | ||
601 | Ok(RangeInfo { range, info: () }) => { | ||
602 | let source = analysis.file_text(position.file_id).unwrap(); | ||
603 | expect.assert_eq(&format!("{:?}: {}", range, &source[range])) | ||
604 | } | ||
605 | Err(RenameError(err)) => expect.assert_eq(&err), | ||
606 | }; | ||
607 | } | ||
608 | |||
609 | #[test] | ||
610 | fn test_prepare_rename_namelikes() { | ||
611 | check_prepare(r"fn name$0<'lifetime>() {}", expect![[r#"3..7: name"#]]); | ||
612 | check_prepare(r"fn name<'lifetime$0>() {}", expect![[r#"8..17: 'lifetime"#]]); | ||
613 | check_prepare(r"fn name<'lifetime>() { name$0(); }", expect![[r#"23..27: name"#]]); | ||
614 | } | ||
615 | |||
616 | #[test] | ||
617 | fn test_prepare_rename_in_macro() { | ||
618 | check_prepare( | ||
619 | r"macro_rules! foo { | ||
620 | ($ident:ident) => { | ||
621 | pub struct $ident; | ||
622 | } | ||
623 | } | ||
624 | foo!(Foo$0);", | ||
625 | expect![[r#"83..86: Foo"#]], | ||
626 | ); | ||
627 | } | ||
628 | |||
629 | #[test] | ||
630 | fn test_prepare_rename_keyword() { | ||
631 | check_prepare(r"struct$0 Foo;", expect![[r#"No references found at position"#]]); | ||
632 | } | ||
633 | |||
591 | #[test] | 634 | #[test] |
592 | fn test_rename_to_underscore() { | 635 | fn test_rename_to_underscore() { |
593 | check("_", r#"fn main() { let i$0 = 1; }"#, r#"fn main() { let _ = 1; }"#); | 636 | check("_", r#"fn main() { let i$0 = 1; }"#, r#"fn main() { let _ = 1; }"#); |
@@ -627,7 +670,7 @@ mod tests { | |||
627 | 670 | ||
628 | #[test] | 671 | #[test] |
629 | fn test_rename_to_invalid_identifier_lifetime() { | 672 | fn test_rename_to_invalid_identifier_lifetime() { |
630 | mark::check!(rename_not_an_ident_ref); | 673 | cov_mark::check!(rename_not_an_ident_ref); |
631 | check( | 674 | check( |
632 | "'foo", | 675 | "'foo", |
633 | r#"fn main() { let i$0 = 1; }"#, | 676 | r#"fn main() { let i$0 = 1; }"#, |
@@ -637,7 +680,7 @@ mod tests { | |||
637 | 680 | ||
638 | #[test] | 681 | #[test] |
639 | fn test_rename_to_invalid_identifier_lifetime2() { | 682 | fn test_rename_to_invalid_identifier_lifetime2() { |
640 | mark::check!(rename_not_a_lifetime_ident_ref); | 683 | cov_mark::check!(rename_not_a_lifetime_ident_ref); |
641 | check( | 684 | check( |
642 | "foo", | 685 | "foo", |
643 | r#"fn main<'a>(_: &'a$0 ()) {}"#, | 686 | r#"fn main<'a>(_: &'a$0 ()) {}"#, |
@@ -647,7 +690,7 @@ mod tests { | |||
647 | 690 | ||
648 | #[test] | 691 | #[test] |
649 | fn test_rename_to_underscore_invalid() { | 692 | fn test_rename_to_underscore_invalid() { |
650 | mark::check!(rename_underscore_multiple); | 693 | cov_mark::check!(rename_underscore_multiple); |
651 | check( | 694 | check( |
652 | "_", | 695 | "_", |
653 | r#"fn main(foo$0: ()) {foo;}"#, | 696 | r#"fn main(foo$0: ()) {foo;}"#, |
@@ -666,7 +709,7 @@ mod tests { | |||
666 | 709 | ||
667 | #[test] | 710 | #[test] |
668 | fn test_rename_for_local() { | 711 | fn test_rename_for_local() { |
669 | mark::check!(rename_ident); | 712 | cov_mark::check!(rename_ident); |
670 | check( | 713 | check( |
671 | "k", | 714 | "k", |
672 | r#" | 715 | r#" |
@@ -829,7 +872,7 @@ impl Foo { | |||
829 | 872 | ||
830 | #[test] | 873 | #[test] |
831 | fn test_rename_field_in_field_shorthand() { | 874 | fn test_rename_field_in_field_shorthand() { |
832 | mark::check!(test_rename_field_in_field_shorthand); | 875 | cov_mark::check!(test_rename_field_in_field_shorthand); |
833 | check( | 876 | check( |
834 | "j", | 877 | "j", |
835 | r#" | 878 | r#" |
@@ -855,7 +898,7 @@ impl Foo { | |||
855 | 898 | ||
856 | #[test] | 899 | #[test] |
857 | fn test_rename_local_in_field_shorthand() { | 900 | fn test_rename_local_in_field_shorthand() { |
858 | mark::check!(test_rename_local_in_field_shorthand); | 901 | cov_mark::check!(test_rename_local_in_field_shorthand); |
859 | check( | 902 | check( |
860 | "j", | 903 | "j", |
861 | r#" | 904 | r#" |
@@ -1261,7 +1304,7 @@ fn foo(f: foo::Foo) { | |||
1261 | 1304 | ||
1262 | #[test] | 1305 | #[test] |
1263 | fn test_parameter_to_self() { | 1306 | fn test_parameter_to_self() { |
1264 | mark::check!(rename_to_self); | 1307 | cov_mark::check!(rename_to_self); |
1265 | check( | 1308 | check( |
1266 | "self", | 1309 | "self", |
1267 | r#" | 1310 | r#" |
@@ -1401,7 +1444,7 @@ impl Foo { | |||
1401 | 1444 | ||
1402 | #[test] | 1445 | #[test] |
1403 | fn test_owned_self_to_parameter() { | 1446 | fn test_owned_self_to_parameter() { |
1404 | mark::check!(rename_self_to_param); | 1447 | cov_mark::check!(rename_self_to_param); |
1405 | check( | 1448 | check( |
1406 | "foo", | 1449 | "foo", |
1407 | r#" | 1450 | r#" |
@@ -1454,7 +1497,7 @@ impl Foo { | |||
1454 | 1497 | ||
1455 | #[test] | 1498 | #[test] |
1456 | fn test_rename_field_put_init_shorthand() { | 1499 | fn test_rename_field_put_init_shorthand() { |
1457 | mark::check!(test_rename_field_put_init_shorthand); | 1500 | cov_mark::check!(test_rename_field_put_init_shorthand); |
1458 | check( | 1501 | check( |
1459 | "bar", | 1502 | "bar", |
1460 | r#" | 1503 | r#" |
@@ -1476,7 +1519,7 @@ fn foo(bar: i32) -> Foo { | |||
1476 | 1519 | ||
1477 | #[test] | 1520 | #[test] |
1478 | fn test_rename_local_put_init_shorthand() { | 1521 | fn test_rename_local_put_init_shorthand() { |
1479 | mark::check!(test_rename_local_put_init_shorthand); | 1522 | cov_mark::check!(test_rename_local_put_init_shorthand); |
1480 | check( | 1523 | check( |
1481 | "i", | 1524 | "i", |
1482 | r#" | 1525 | r#" |
@@ -1498,7 +1541,7 @@ fn foo(i: i32) -> Foo { | |||
1498 | 1541 | ||
1499 | #[test] | 1542 | #[test] |
1500 | fn test_struct_field_pat_into_shorthand() { | 1543 | fn test_struct_field_pat_into_shorthand() { |
1501 | mark::check!(test_rename_field_put_init_shorthand_pat); | 1544 | cov_mark::check!(test_rename_field_put_init_shorthand_pat); |
1502 | check( | 1545 | check( |
1503 | "baz", | 1546 | "baz", |
1504 | r#" | 1547 | r#" |
@@ -1610,7 +1653,7 @@ fn foo(foo: Foo) { | |||
1610 | 1653 | ||
1611 | #[test] | 1654 | #[test] |
1612 | fn test_rename_lifetimes() { | 1655 | fn test_rename_lifetimes() { |
1613 | mark::check!(rename_lifetime); | 1656 | cov_mark::check!(rename_lifetime); |
1614 | check( | 1657 | check( |
1615 | "'yeeee", | 1658 | "'yeeee", |
1616 | r#" | 1659 | r#" |
@@ -1698,7 +1741,7 @@ fn foo<'a>() -> &'a () { | |||
1698 | 1741 | ||
1699 | #[test] | 1742 | #[test] |
1700 | fn test_self_to_self() { | 1743 | fn test_self_to_self() { |
1701 | mark::check!(rename_self_to_self); | 1744 | cov_mark::check!(rename_self_to_self); |
1702 | check( | 1745 | check( |
1703 | "self", | 1746 | "self", |
1704 | r#" | 1747 | r#" |
diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs index 65f60891e..280565563 100644 --- a/crates/ide/src/runnables.rs +++ b/crates/ide/src/runnables.rs | |||
@@ -9,7 +9,6 @@ use syntax::{ | |||
9 | ast::{self, AstNode, AttrsOwner}, | 9 | ast::{self, AstNode, AttrsOwner}, |
10 | match_ast, SyntaxNode, | 10 | match_ast, SyntaxNode, |
11 | }; | 11 | }; |
12 | use test_utils::mark; | ||
13 | 12 | ||
14 | use crate::{ | 13 | use crate::{ |
15 | display::{ToNav, TryToNav}, | 14 | display::{ToNav, TryToNav}, |
@@ -130,7 +129,9 @@ fn runnables_mod(sema: &Semantics<RootDatabase>, acc: &mut Vec<Runnable>, module | |||
130 | if let hir::ModuleDef::Module(submodule) = def { | 129 | if let hir::ModuleDef::Module(submodule) = def { |
131 | match submodule.definition_source(sema.db).value { | 130 | match submodule.definition_source(sema.db).value { |
132 | hir::ModuleSource::Module(_) => runnables_mod(sema, acc, submodule), | 131 | hir::ModuleSource::Module(_) => runnables_mod(sema, acc, submodule), |
133 | hir::ModuleSource::SourceFile(_) => mark::hit!(dont_recurse_in_outline_submodules), | 132 | hir::ModuleSource::SourceFile(_) => { |
133 | cov_mark::hit!(dont_recurse_in_outline_submodules) | ||
134 | } | ||
134 | hir::ModuleSource::BlockExpr(_) => {} // inner items aren't runnable | 135 | hir::ModuleSource::BlockExpr(_) => {} // inner items aren't runnable |
135 | } | 136 | } |
136 | } | 137 | } |
@@ -328,7 +329,6 @@ fn has_test_function_or_multiple_test_submodules( | |||
328 | #[cfg(test)] | 329 | #[cfg(test)] |
329 | mod tests { | 330 | mod tests { |
330 | use expect_test::{expect, Expect}; | 331 | use expect_test::{expect, Expect}; |
331 | use test_utils::mark; | ||
332 | 332 | ||
333 | use crate::fixture; | 333 | use crate::fixture; |
334 | 334 | ||
@@ -1056,7 +1056,7 @@ mod tests { | |||
1056 | 1056 | ||
1057 | #[test] | 1057 | #[test] |
1058 | fn dont_recurse_in_outline_submodules() { | 1058 | fn dont_recurse_in_outline_submodules() { |
1059 | mark::check!(dont_recurse_in_outline_submodules); | 1059 | cov_mark::check!(dont_recurse_in_outline_submodules); |
1060 | check( | 1060 | check( |
1061 | r#" | 1061 | r#" |
1062 | //- /lib.rs | 1062 | //- /lib.rs |
diff --git a/crates/ide/src/ssr.rs b/crates/ide/src/ssr.rs new file mode 100644 index 000000000..f3638d928 --- /dev/null +++ b/crates/ide/src/ssr.rs | |||
@@ -0,0 +1,259 @@ | |||
1 | //! This module provides an SSR assist. It is not desirable to include this | ||
2 | //! assist in ide_assists because that would require the ide_assists crate | ||
3 | //! depend on the ide_ssr crate. | ||
4 | |||
5 | use ide_assists::{Assist, AssistId, AssistKind, GroupLabel}; | ||
6 | use ide_db::{base_db::FileRange, label::Label, source_change::SourceChange, RootDatabase}; | ||
7 | |||
8 | pub(crate) fn add_ssr_assist( | ||
9 | db: &RootDatabase, | ||
10 | base: &mut Vec<Assist>, | ||
11 | resolve: bool, | ||
12 | frange: FileRange, | ||
13 | ) -> Option<()> { | ||
14 | let (match_finder, comment_range) = ide_ssr::ssr_from_comment(db, frange)?; | ||
15 | |||
16 | let (source_change_for_file, source_change_for_workspace) = if resolve { | ||
17 | let edits = match_finder.edits(); | ||
18 | |||
19 | let source_change_for_file = { | ||
20 | let text_edit_for_file = edits.get(&frange.file_id).cloned().unwrap_or_default(); | ||
21 | SourceChange::from_text_edit(frange.file_id, text_edit_for_file) | ||
22 | }; | ||
23 | |||
24 | let source_change_for_workspace = SourceChange::from(match_finder.edits()); | ||
25 | |||
26 | (Some(source_change_for_file), Some(source_change_for_workspace)) | ||
27 | } else { | ||
28 | (None, None) | ||
29 | }; | ||
30 | |||
31 | let assists = vec![ | ||
32 | ("Apply SSR in file", source_change_for_file), | ||
33 | ("Apply SSR in workspace", source_change_for_workspace), | ||
34 | ]; | ||
35 | |||
36 | for (label, source_change) in assists.into_iter() { | ||
37 | let assist = Assist { | ||
38 | id: AssistId("ssr", AssistKind::RefactorRewrite), | ||
39 | label: Label::new(label), | ||
40 | group: Some(GroupLabel("Apply SSR".into())), | ||
41 | target: comment_range, | ||
42 | source_change, | ||
43 | }; | ||
44 | |||
45 | base.push(assist); | ||
46 | } | ||
47 | Some(()) | ||
48 | } | ||
49 | |||
50 | #[cfg(test)] | ||
51 | mod tests { | ||
52 | use std::sync::Arc; | ||
53 | |||
54 | use expect_test::expect; | ||
55 | use ide_assists::Assist; | ||
56 | use ide_db::{ | ||
57 | base_db::{fixture::WithFixture, salsa::Durability, FileRange}, | ||
58 | symbol_index::SymbolsDatabase, | ||
59 | RootDatabase, | ||
60 | }; | ||
61 | use rustc_hash::FxHashSet; | ||
62 | |||
63 | use super::add_ssr_assist; | ||
64 | |||
65 | fn get_assists(ra_fixture: &str, resolve: bool) -> Vec<Assist> { | ||
66 | let (mut db, file_id, range_or_offset) = RootDatabase::with_range_or_offset(ra_fixture); | ||
67 | let mut local_roots = FxHashSet::default(); | ||
68 | local_roots.insert(ide_db::base_db::fixture::WORKSPACE); | ||
69 | db.set_local_roots_with_durability(Arc::new(local_roots), Durability::HIGH); | ||
70 | |||
71 | let mut assists = vec![]; | ||
72 | |||
73 | add_ssr_assist( | ||
74 | &db, | ||
75 | &mut assists, | ||
76 | resolve, | ||
77 | FileRange { file_id, range: range_or_offset.into() }, | ||
78 | ); | ||
79 | |||
80 | assists | ||
81 | } | ||
82 | |||
83 | #[test] | ||
84 | fn not_applicable_comment_not_ssr() { | ||
85 | let ra_fixture = r#" | ||
86 | //- /lib.rs | ||
87 | |||
88 | // This is foo $0 | ||
89 | fn foo() {} | ||
90 | "#; | ||
91 | let resolve = true; | ||
92 | |||
93 | let assists = get_assists(ra_fixture, resolve); | ||
94 | |||
95 | assert_eq!(0, assists.len()); | ||
96 | } | ||
97 | |||
98 | #[test] | ||
99 | fn resolve_edits_true() { | ||
100 | let resolve = true; | ||
101 | let assists = get_assists( | ||
102 | r#" | ||
103 | //- /lib.rs | ||
104 | mod bar; | ||
105 | |||
106 | // 2 ==>> 3$0 | ||
107 | fn foo() { 2 } | ||
108 | |||
109 | //- /bar.rs | ||
110 | fn bar() { 2 } | ||
111 | "#, | ||
112 | resolve, | ||
113 | ); | ||
114 | |||
115 | assert_eq!(2, assists.len()); | ||
116 | let mut assists = assists.into_iter(); | ||
117 | |||
118 | let apply_in_file_assist = assists.next().unwrap(); | ||
119 | expect![[r#" | ||
120 | Assist { | ||
121 | id: AssistId( | ||
122 | "ssr", | ||
123 | RefactorRewrite, | ||
124 | ), | ||
125 | label: "Apply SSR in file", | ||
126 | group: Some( | ||
127 | GroupLabel( | ||
128 | "Apply SSR", | ||
129 | ), | ||
130 | ), | ||
131 | target: 10..21, | ||
132 | source_change: Some( | ||
133 | SourceChange { | ||
134 | source_file_edits: { | ||
135 | FileId( | ||
136 | 0, | ||
137 | ): TextEdit { | ||
138 | indels: [ | ||
139 | Indel { | ||
140 | insert: "3", | ||
141 | delete: 33..34, | ||
142 | }, | ||
143 | ], | ||
144 | }, | ||
145 | }, | ||
146 | file_system_edits: [], | ||
147 | is_snippet: false, | ||
148 | }, | ||
149 | ), | ||
150 | } | ||
151 | "#]] | ||
152 | .assert_debug_eq(&apply_in_file_assist); | ||
153 | |||
154 | let apply_in_workspace_assist = assists.next().unwrap(); | ||
155 | expect![[r#" | ||
156 | Assist { | ||
157 | id: AssistId( | ||
158 | "ssr", | ||
159 | RefactorRewrite, | ||
160 | ), | ||
161 | label: "Apply SSR in workspace", | ||
162 | group: Some( | ||
163 | GroupLabel( | ||
164 | "Apply SSR", | ||
165 | ), | ||
166 | ), | ||
167 | target: 10..21, | ||
168 | source_change: Some( | ||
169 | SourceChange { | ||
170 | source_file_edits: { | ||
171 | FileId( | ||
172 | 0, | ||
173 | ): TextEdit { | ||
174 | indels: [ | ||
175 | Indel { | ||
176 | insert: "3", | ||
177 | delete: 33..34, | ||
178 | }, | ||
179 | ], | ||
180 | }, | ||
181 | FileId( | ||
182 | 1, | ||
183 | ): TextEdit { | ||
184 | indels: [ | ||
185 | Indel { | ||
186 | insert: "3", | ||
187 | delete: 11..12, | ||
188 | }, | ||
189 | ], | ||
190 | }, | ||
191 | }, | ||
192 | file_system_edits: [], | ||
193 | is_snippet: false, | ||
194 | }, | ||
195 | ), | ||
196 | } | ||
197 | "#]] | ||
198 | .assert_debug_eq(&apply_in_workspace_assist); | ||
199 | } | ||
200 | |||
201 | #[test] | ||
202 | fn resolve_edits_false() { | ||
203 | let resolve = false; | ||
204 | let assists = get_assists( | ||
205 | r#" | ||
206 | //- /lib.rs | ||
207 | mod bar; | ||
208 | |||
209 | // 2 ==>> 3$0 | ||
210 | fn foo() { 2 } | ||
211 | |||
212 | //- /bar.rs | ||
213 | fn bar() { 2 } | ||
214 | "#, | ||
215 | resolve, | ||
216 | ); | ||
217 | |||
218 | assert_eq!(2, assists.len()); | ||
219 | let mut assists = assists.into_iter(); | ||
220 | |||
221 | let apply_in_file_assist = assists.next().unwrap(); | ||
222 | expect![[r#" | ||
223 | Assist { | ||
224 | id: AssistId( | ||
225 | "ssr", | ||
226 | RefactorRewrite, | ||
227 | ), | ||
228 | label: "Apply SSR in file", | ||
229 | group: Some( | ||
230 | GroupLabel( | ||
231 | "Apply SSR", | ||
232 | ), | ||
233 | ), | ||
234 | target: 10..21, | ||
235 | source_change: None, | ||
236 | } | ||
237 | "#]] | ||
238 | .assert_debug_eq(&apply_in_file_assist); | ||
239 | |||
240 | let apply_in_workspace_assist = assists.next().unwrap(); | ||
241 | expect![[r#" | ||
242 | Assist { | ||
243 | id: AssistId( | ||
244 | "ssr", | ||
245 | RefactorRewrite, | ||
246 | ), | ||
247 | label: "Apply SSR in workspace", | ||
248 | group: Some( | ||
249 | GroupLabel( | ||
250 | "Apply SSR", | ||
251 | ), | ||
252 | ), | ||
253 | target: 10..21, | ||
254 | source_change: None, | ||
255 | } | ||
256 | "#]] | ||
257 | .assert_debug_eq(&apply_in_workspace_assist); | ||
258 | } | ||
259 | } | ||
diff --git a/crates/ide/src/syntax_highlighting/highlight.rs b/crates/ide/src/syntax_highlighting/highlight.rs index 24fcbb584..b0cfdd8b7 100644 --- a/crates/ide/src/syntax_highlighting/highlight.rs +++ b/crates/ide/src/syntax_highlighting/highlight.rs | |||
@@ -330,10 +330,11 @@ fn highlight_def(db: &RootDatabase, def: Definition) -> Highlight { | |||
330 | HlTag::Symbol(SymbolKind::Local) | 330 | HlTag::Symbol(SymbolKind::Local) |
331 | }; | 331 | }; |
332 | let mut h = Highlight::new(tag); | 332 | let mut h = Highlight::new(tag); |
333 | if local.is_mut(db) || local.ty(db).is_mutable_reference() { | 333 | let ty = local.ty(db); |
334 | if local.is_mut(db) || ty.is_mutable_reference() { | ||
334 | h |= HlMod::Mutable; | 335 | h |= HlMod::Mutable; |
335 | } | 336 | } |
336 | if local.ty(db).as_callable(db).is_some() || local.ty(db).impls_fnonce(db) { | 337 | if ty.as_callable(db).is_some() || ty.impls_fnonce(db) { |
337 | h |= HlMod::Callable; | 338 | h |= HlMod::Callable; |
338 | } | 339 | } |
339 | return h; | 340 | return h; |
diff --git a/crates/ide/src/typing/on_enter.rs b/crates/ide/src/typing/on_enter.rs index 63cd51b69..978c479de 100644 --- a/crates/ide/src/typing/on_enter.rs +++ b/crates/ide/src/typing/on_enter.rs | |||
@@ -9,7 +9,7 @@ use syntax::{ | |||
9 | SyntaxKind::*, | 9 | SyntaxKind::*, |
10 | SyntaxToken, TextRange, TextSize, TokenAtOffset, | 10 | SyntaxToken, TextRange, TextSize, TokenAtOffset, |
11 | }; | 11 | }; |
12 | use test_utils::mark; | 12 | |
13 | use text_edit::TextEdit; | 13 | use text_edit::TextEdit; |
14 | 14 | ||
15 | // Feature: On Enter | 15 | // Feature: On Enter |
@@ -55,7 +55,7 @@ pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option<Text | |||
55 | // Continuing single-line non-doc comments (like this one :) ) is annoying | 55 | // Continuing single-line non-doc comments (like this one :) ) is annoying |
56 | if prefix == "//" && comment_range.end() == position.offset { | 56 | if prefix == "//" && comment_range.end() == position.offset { |
57 | if comment.text().ends_with(' ') { | 57 | if comment.text().ends_with(' ') { |
58 | mark::hit!(continues_end_of_line_comment_with_space); | 58 | cov_mark::hit!(continues_end_of_line_comment_with_space); |
59 | remove_trailing_whitespace = true; | 59 | remove_trailing_whitespace = true; |
60 | } else if !followed_by_comment(&comment) { | 60 | } else if !followed_by_comment(&comment) { |
61 | return None; | 61 | return None; |
@@ -109,7 +109,7 @@ fn node_indent(file: &SourceFile, token: &SyntaxToken) -> Option<SmolStr> { | |||
109 | #[cfg(test)] | 109 | #[cfg(test)] |
110 | mod tests { | 110 | mod tests { |
111 | use stdx::trim_indent; | 111 | use stdx::trim_indent; |
112 | use test_utils::{assert_eq_text, mark}; | 112 | use test_utils::assert_eq_text; |
113 | 113 | ||
114 | use crate::fixture; | 114 | use crate::fixture; |
115 | 115 | ||
@@ -238,7 +238,7 @@ fn main() { | |||
238 | 238 | ||
239 | #[test] | 239 | #[test] |
240 | fn continues_end_of_line_comment_with_space() { | 240 | fn continues_end_of_line_comment_with_space() { |
241 | mark::check!(continues_end_of_line_comment_with_space); | 241 | cov_mark::check!(continues_end_of_line_comment_with_space); |
242 | do_check( | 242 | do_check( |
243 | r#" | 243 | r#" |
244 | fn main() { | 244 | fn main() { |
diff --git a/crates/ide_assists/Cargo.toml b/crates/ide_assists/Cargo.toml index a34bdd6c3..dd9aa27c6 100644 --- a/crates/ide_assists/Cargo.toml +++ b/crates/ide_assists/Cargo.toml | |||
@@ -10,6 +10,7 @@ edition = "2018" | |||
10 | doctest = false | 10 | doctest = false |
11 | 11 | ||
12 | [dependencies] | 12 | [dependencies] |
13 | cov-mark = "1.1" | ||
13 | rustc-hash = "1.1.0" | 14 | rustc-hash = "1.1.0" |
14 | itertools = "0.10.0" | 15 | itertools = "0.10.0" |
15 | either = "1.6.1" | 16 | either = "1.6.1" |
@@ -20,7 +21,7 @@ text_edit = { path = "../text_edit", version = "0.0.0" } | |||
20 | profile = { path = "../profile", version = "0.0.0" } | 21 | profile = { path = "../profile", version = "0.0.0" } |
21 | ide_db = { path = "../ide_db", version = "0.0.0" } | 22 | ide_db = { path = "../ide_db", version = "0.0.0" } |
22 | hir = { path = "../hir", version = "0.0.0" } | 23 | hir = { path = "../hir", version = "0.0.0" } |
23 | test_utils = { path = "../test_utils", version = "0.0.0" } | ||
24 | 24 | ||
25 | [dev-dependencies] | 25 | [dev-dependencies] |
26 | test_utils = { path = "../test_utils" } | ||
26 | expect-test = "1.1" | 27 | expect-test = "1.1" |
diff --git a/crates/ide_assists/src/handlers/add_turbo_fish.rs b/crates/ide_assists/src/handlers/add_turbo_fish.rs index a08b55ebb..ee879c151 100644 --- a/crates/ide_assists/src/handlers/add_turbo_fish.rs +++ b/crates/ide_assists/src/handlers/add_turbo_fish.rs | |||
@@ -1,6 +1,5 @@ | |||
1 | use ide_db::defs::{Definition, NameRefClass}; | 1 | use ide_db::defs::{Definition, NameRefClass}; |
2 | use syntax::{ast, AstNode, SyntaxKind, T}; | 2 | use syntax::{ast, AstNode, SyntaxKind, T}; |
3 | use test_utils::mark; | ||
4 | 3 | ||
5 | use crate::{ | 4 | use crate::{ |
6 | assist_context::{AssistContext, Assists}, | 5 | assist_context::{AssistContext, Assists}, |
@@ -30,13 +29,13 @@ pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext) -> Option<( | |||
30 | if arg_list.args().count() > 0 { | 29 | if arg_list.args().count() > 0 { |
31 | return None; | 30 | return None; |
32 | } | 31 | } |
33 | mark::hit!(add_turbo_fish_after_call); | 32 | cov_mark::hit!(add_turbo_fish_after_call); |
34 | mark::hit!(add_type_ascription_after_call); | 33 | cov_mark::hit!(add_type_ascription_after_call); |
35 | arg_list.l_paren_token()?.prev_token().filter(|it| it.kind() == SyntaxKind::IDENT) | 34 | arg_list.l_paren_token()?.prev_token().filter(|it| it.kind() == SyntaxKind::IDENT) |
36 | })?; | 35 | })?; |
37 | let next_token = ident.next_token()?; | 36 | let next_token = ident.next_token()?; |
38 | if next_token.kind() == T![::] { | 37 | if next_token.kind() == T![::] { |
39 | mark::hit!(add_turbo_fish_one_fish_is_enough); | 38 | cov_mark::hit!(add_turbo_fish_one_fish_is_enough); |
40 | return None; | 39 | return None; |
41 | } | 40 | } |
42 | let name_ref = ast::NameRef::cast(ident.parent())?; | 41 | let name_ref = ast::NameRef::cast(ident.parent())?; |
@@ -50,24 +49,31 @@ pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext) -> Option<( | |||
50 | }; | 49 | }; |
51 | let generics = hir::GenericDef::Function(fun).params(ctx.sema.db); | 50 | let generics = hir::GenericDef::Function(fun).params(ctx.sema.db); |
52 | if generics.is_empty() { | 51 | if generics.is_empty() { |
53 | mark::hit!(add_turbo_fish_non_generic); | 52 | cov_mark::hit!(add_turbo_fish_non_generic); |
54 | return None; | 53 | return None; |
55 | } | 54 | } |
56 | 55 | ||
57 | if let Some(let_stmt) = ctx.find_node_at_offset::<ast::LetStmt>() { | 56 | if let Some(let_stmt) = ctx.find_node_at_offset::<ast::LetStmt>() { |
58 | if let_stmt.colon_token().is_none() { | 57 | if let_stmt.colon_token().is_none() { |
59 | let type_pos = let_stmt.pat()?.syntax().last_token()?.text_range().end(); | 58 | let type_pos = let_stmt.pat()?.syntax().last_token()?.text_range().end(); |
59 | let semi_pos = let_stmt.syntax().last_token()?.text_range().end(); | ||
60 | |||
60 | acc.add( | 61 | acc.add( |
61 | AssistId("add_type_ascription", AssistKind::RefactorRewrite), | 62 | AssistId("add_type_ascription", AssistKind::RefactorRewrite), |
62 | "Add `: _` before assignment operator", | 63 | "Add `: _` before assignment operator", |
63 | ident.text_range(), | 64 | ident.text_range(), |
64 | |builder| match ctx.config.snippet_cap { | 65 | |builder| { |
65 | Some(cap) => builder.insert_snippet(cap, type_pos, ": ${0:_}"), | 66 | if let_stmt.semicolon_token().is_none() { |
66 | None => builder.insert(type_pos, ": _"), | 67 | builder.insert(semi_pos, ";"); |
68 | } | ||
69 | match ctx.config.snippet_cap { | ||
70 | Some(cap) => builder.insert_snippet(cap, type_pos, ": ${0:_}"), | ||
71 | None => builder.insert(type_pos, ": _"), | ||
72 | } | ||
67 | }, | 73 | }, |
68 | )? | 74 | )? |
69 | } else { | 75 | } else { |
70 | mark::hit!(add_type_ascription_already_typed); | 76 | cov_mark::hit!(add_type_ascription_already_typed); |
71 | } | 77 | } |
72 | } | 78 | } |
73 | 79 | ||
@@ -87,7 +93,6 @@ mod tests { | |||
87 | use crate::tests::{check_assist, check_assist_by_label, check_assist_not_applicable}; | 93 | use crate::tests::{check_assist, check_assist_by_label, check_assist_not_applicable}; |
88 | 94 | ||
89 | use super::*; | 95 | use super::*; |
90 | use test_utils::mark; | ||
91 | 96 | ||
92 | #[test] | 97 | #[test] |
93 | fn add_turbo_fish_function() { | 98 | fn add_turbo_fish_function() { |
@@ -110,7 +115,7 @@ fn main() { | |||
110 | 115 | ||
111 | #[test] | 116 | #[test] |
112 | fn add_turbo_fish_after_call() { | 117 | fn add_turbo_fish_after_call() { |
113 | mark::check!(add_turbo_fish_after_call); | 118 | cov_mark::check!(add_turbo_fish_after_call); |
114 | check_assist( | 119 | check_assist( |
115 | add_turbo_fish, | 120 | add_turbo_fish, |
116 | r#" | 121 | r#" |
@@ -155,7 +160,7 @@ fn main() { | |||
155 | 160 | ||
156 | #[test] | 161 | #[test] |
157 | fn add_turbo_fish_one_fish_is_enough() { | 162 | fn add_turbo_fish_one_fish_is_enough() { |
158 | mark::check!(add_turbo_fish_one_fish_is_enough); | 163 | cov_mark::check!(add_turbo_fish_one_fish_is_enough); |
159 | check_assist_not_applicable( | 164 | check_assist_not_applicable( |
160 | add_turbo_fish, | 165 | add_turbo_fish, |
161 | r#" | 166 | r#" |
@@ -169,7 +174,7 @@ fn main() { | |||
169 | 174 | ||
170 | #[test] | 175 | #[test] |
171 | fn add_turbo_fish_non_generic() { | 176 | fn add_turbo_fish_non_generic() { |
172 | mark::check!(add_turbo_fish_non_generic); | 177 | cov_mark::check!(add_turbo_fish_non_generic); |
173 | check_assist_not_applicable( | 178 | check_assist_not_applicable( |
174 | add_turbo_fish, | 179 | add_turbo_fish, |
175 | r#" | 180 | r#" |
@@ -203,7 +208,7 @@ fn main() { | |||
203 | 208 | ||
204 | #[test] | 209 | #[test] |
205 | fn add_type_ascription_after_call() { | 210 | fn add_type_ascription_after_call() { |
206 | mark::check!(add_type_ascription_after_call); | 211 | cov_mark::check!(add_type_ascription_after_call); |
207 | check_assist_by_label( | 212 | check_assist_by_label( |
208 | add_turbo_fish, | 213 | add_turbo_fish, |
209 | r#" | 214 | r#" |
@@ -250,7 +255,7 @@ fn main() { | |||
250 | 255 | ||
251 | #[test] | 256 | #[test] |
252 | fn add_type_ascription_already_typed() { | 257 | fn add_type_ascription_already_typed() { |
253 | mark::check!(add_type_ascription_already_typed); | 258 | cov_mark::check!(add_type_ascription_already_typed); |
254 | check_assist( | 259 | check_assist( |
255 | add_turbo_fish, | 260 | add_turbo_fish, |
256 | r#" | 261 | r#" |
@@ -267,4 +272,24 @@ fn main() { | |||
267 | "#, | 272 | "#, |
268 | ); | 273 | ); |
269 | } | 274 | } |
275 | |||
276 | #[test] | ||
277 | fn add_type_ascription_append_semicolon() { | ||
278 | check_assist_by_label( | ||
279 | add_turbo_fish, | ||
280 | r#" | ||
281 | fn make<T>() -> T {} | ||
282 | fn main() { | ||
283 | let x = make$0() | ||
284 | } | ||
285 | "#, | ||
286 | r#" | ||
287 | fn make<T>() -> T {} | ||
288 | fn main() { | ||
289 | let x: ${0:_} = make(); | ||
290 | } | ||
291 | "#, | ||
292 | "Add `: _` before assignment operator", | ||
293 | ); | ||
294 | } | ||
270 | } | 295 | } |
diff --git a/crates/ide_assists/src/handlers/apply_demorgan.rs b/crates/ide_assists/src/handlers/apply_demorgan.rs index 128b1eb56..a1c339603 100644 --- a/crates/ide_assists/src/handlers/apply_demorgan.rs +++ b/crates/ide_assists/src/handlers/apply_demorgan.rs | |||
@@ -1,5 +1,4 @@ | |||
1 | use syntax::ast::{self, AstNode}; | 1 | use syntax::ast::{self, AstNode}; |
2 | use test_utils::mark; | ||
3 | 2 | ||
4 | use crate::{utils::invert_boolean_expression, AssistContext, AssistId, AssistKind, Assists}; | 3 | use crate::{utils::invert_boolean_expression, AssistContext, AssistId, AssistKind, Assists}; |
5 | 4 | ||
@@ -64,10 +63,10 @@ pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext) -> Option<( | |||
64 | edit.replace(lhs_range, not_lhs.syntax().text()); | 63 | edit.replace(lhs_range, not_lhs.syntax().text()); |
65 | edit.replace(rhs_range, not_rhs.syntax().text()); | 64 | edit.replace(rhs_range, not_rhs.syntax().text()); |
66 | if let Some(neg_expr) = neg_expr { | 65 | if let Some(neg_expr) = neg_expr { |
67 | mark::hit!(demorgan_double_negation); | 66 | cov_mark::hit!(demorgan_double_negation); |
68 | edit.replace(neg_expr.op_token().unwrap().text_range(), ""); | 67 | edit.replace(neg_expr.op_token().unwrap().text_range(), ""); |
69 | } else { | 68 | } else { |
70 | mark::hit!(demorgan_double_parens); | 69 | cov_mark::hit!(demorgan_double_parens); |
71 | edit.replace(paren_expr.l_paren_token().unwrap().text_range(), "!("); | 70 | edit.replace(paren_expr.l_paren_token().unwrap().text_range(), "!("); |
72 | } | 71 | } |
73 | } else { | 72 | } else { |
@@ -90,7 +89,6 @@ fn opposite_logic_op(kind: ast::BinOp) -> Option<&'static str> { | |||
90 | #[cfg(test)] | 89 | #[cfg(test)] |
91 | mod tests { | 90 | mod tests { |
92 | use ide_db::helpers::FamousDefs; | 91 | use ide_db::helpers::FamousDefs; |
93 | use test_utils::mark; | ||
94 | 92 | ||
95 | use super::*; | 93 | use super::*; |
96 | 94 | ||
@@ -188,13 +186,13 @@ fn f() { | |||
188 | 186 | ||
189 | #[test] | 187 | #[test] |
190 | fn demorgan_doesnt_double_negation() { | 188 | fn demorgan_doesnt_double_negation() { |
191 | mark::check!(demorgan_double_negation); | 189 | cov_mark::check!(demorgan_double_negation); |
192 | check_assist(apply_demorgan, "fn f() { !(x ||$0 x) }", "fn f() { (!x && !x) }") | 190 | check_assist(apply_demorgan, "fn f() { !(x ||$0 x) }", "fn f() { (!x && !x) }") |
193 | } | 191 | } |
194 | 192 | ||
195 | #[test] | 193 | #[test] |
196 | fn demorgan_doesnt_double_parens() { | 194 | fn demorgan_doesnt_double_parens() { |
197 | mark::check!(demorgan_double_parens); | 195 | cov_mark::check!(demorgan_double_parens); |
198 | check_assist(apply_demorgan, "fn f() { (x ||$0 x) }", "fn f() { !(!x && !x) }") | 196 | check_assist(apply_demorgan, "fn f() { (x ||$0 x) }", "fn f() { !(!x && !x) }") |
199 | } | 197 | } |
200 | } | 198 | } |
diff --git a/crates/ide_assists/src/handlers/auto_import.rs b/crates/ide_assists/src/handlers/auto_import.rs index dc38f90e9..7019039b9 100644 --- a/crates/ide_assists/src/handlers/auto_import.rs +++ b/crates/ide_assists/src/handlers/auto_import.rs | |||
@@ -90,17 +90,17 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()> | |||
90 | } | 90 | } |
91 | 91 | ||
92 | let range = ctx.sema.original_range(&syntax_under_caret).range; | 92 | let range = ctx.sema.original_range(&syntax_under_caret).range; |
93 | let group = import_group_message(import_assets.import_candidate()); | 93 | let group_label = group_label(import_assets.import_candidate()); |
94 | let scope = ImportScope::find_insert_use_container(&syntax_under_caret, &ctx.sema)?; | 94 | let scope = ImportScope::find_insert_use_container(&syntax_under_caret, &ctx.sema)?; |
95 | for (import, _) in proposed_imports { | 95 | for import in proposed_imports { |
96 | acc.add_group( | 96 | acc.add_group( |
97 | &group, | 97 | &group_label, |
98 | AssistId("auto_import", AssistKind::QuickFix), | 98 | AssistId("auto_import", AssistKind::QuickFix), |
99 | format!("Import `{}`", &import), | 99 | format!("Import `{}`", import.import_path), |
100 | range, | 100 | range, |
101 | |builder| { | 101 | |builder| { |
102 | let rewriter = | 102 | let rewriter = |
103 | insert_use(&scope, mod_path_to_ast(&import), ctx.config.insert_use.merge); | 103 | insert_use(&scope, mod_path_to_ast(&import.import_path), ctx.config.insert_use); |
104 | builder.rewrite(rewriter); | 104 | builder.rewrite(rewriter); |
105 | }, | 105 | }, |
106 | ); | 106 | ); |
@@ -122,14 +122,14 @@ pub(super) fn find_importable_node(ctx: &AssistContext) -> Option<(ImportAssets, | |||
122 | } | 122 | } |
123 | } | 123 | } |
124 | 124 | ||
125 | fn import_group_message(import_candidate: &ImportCandidate) -> GroupLabel { | 125 | fn group_label(import_candidate: &ImportCandidate) -> GroupLabel { |
126 | let name = match import_candidate { | 126 | let name = match import_candidate { |
127 | ImportCandidate::Path(candidate) => format!("Import {}", candidate.name.text()), | 127 | ImportCandidate::Path(candidate) => format!("Import {}", candidate.name.text()), |
128 | ImportCandidate::TraitAssocItem(candidate) => { | 128 | ImportCandidate::TraitAssocItem(candidate) => { |
129 | format!("Import a trait for item {}", candidate.name.text()) | 129 | format!("Import a trait for item {}", candidate.assoc_item_name.text()) |
130 | } | 130 | } |
131 | ImportCandidate::TraitMethod(candidate) => { | 131 | ImportCandidate::TraitMethod(candidate) => { |
132 | format!("Import a trait for method {}", candidate.name.text()) | 132 | format!("Import a trait for method {}", candidate.assoc_item_name.text()) |
133 | } | 133 | } |
134 | }; | 134 | }; |
135 | GroupLabel(name) | 135 | GroupLabel(name) |
@@ -222,41 +222,6 @@ mod tests { | |||
222 | } | 222 | } |
223 | 223 | ||
224 | #[test] | 224 | #[test] |
225 | fn auto_imports_are_merged() { | ||
226 | check_assist( | ||
227 | auto_import, | ||
228 | r" | ||
229 | use PubMod::PubStruct1; | ||
230 | |||
231 | struct Test { | ||
232 | test: Pub$0Struct2<u8>, | ||
233 | } | ||
234 | |||
235 | pub mod PubMod { | ||
236 | pub struct PubStruct1; | ||
237 | pub struct PubStruct2<T> { | ||
238 | _t: T, | ||
239 | } | ||
240 | } | ||
241 | ", | ||
242 | r" | ||
243 | use PubMod::{PubStruct1, PubStruct2}; | ||
244 | |||
245 | struct Test { | ||
246 | test: PubStruct2<u8>, | ||
247 | } | ||
248 | |||
249 | pub mod PubMod { | ||
250 | pub struct PubStruct1; | ||
251 | pub struct PubStruct2<T> { | ||
252 | _t: T, | ||
253 | } | ||
254 | } | ||
255 | ", | ||
256 | ); | ||
257 | } | ||
258 | |||
259 | #[test] | ||
260 | fn applicable_when_found_multiple_imports() { | 225 | fn applicable_when_found_multiple_imports() { |
261 | check_assist( | 226 | check_assist( |
262 | auto_import, | 227 | auto_import, |
diff --git a/crates/ide_assists/src/handlers/change_visibility.rs b/crates/ide_assists/src/handlers/change_visibility.rs index ac8c44124..ec99a5505 100644 --- a/crates/ide_assists/src/handlers/change_visibility.rs +++ b/crates/ide_assists/src/handlers/change_visibility.rs | |||
@@ -4,7 +4,6 @@ use syntax::{ | |||
4 | SyntaxKind::{CONST, ENUM, FN, MODULE, STATIC, STRUCT, TRAIT, TYPE_ALIAS, VISIBILITY}, | 4 | SyntaxKind::{CONST, ENUM, FN, MODULE, STATIC, STRUCT, TRAIT, TYPE_ALIAS, VISIBILITY}, |
5 | T, | 5 | T, |
6 | }; | 6 | }; |
7 | use test_utils::mark; | ||
8 | 7 | ||
9 | use crate::{utils::vis_offset, AssistContext, AssistId, AssistKind, Assists}; | 8 | use crate::{utils::vis_offset, AssistContext, AssistId, AssistKind, Assists}; |
10 | 9 | ||
@@ -56,7 +55,7 @@ fn add_vis(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | |||
56 | } else if let Some(field_name) = ctx.find_node_at_offset::<ast::Name>() { | 55 | } else if let Some(field_name) = ctx.find_node_at_offset::<ast::Name>() { |
57 | let field = field_name.syntax().ancestors().find_map(ast::RecordField::cast)?; | 56 | let field = field_name.syntax().ancestors().find_map(ast::RecordField::cast)?; |
58 | if field.name()? != field_name { | 57 | if field.name()? != field_name { |
59 | mark::hit!(change_visibility_field_false_positive); | 58 | cov_mark::hit!(change_visibility_field_false_positive); |
60 | return None; | 59 | return None; |
61 | } | 60 | } |
62 | if field.visibility().is_some() { | 61 | if field.visibility().is_some() { |
@@ -110,8 +109,6 @@ fn change_vis(acc: &mut Assists, vis: ast::Visibility) -> Option<()> { | |||
110 | 109 | ||
111 | #[cfg(test)] | 110 | #[cfg(test)] |
112 | mod tests { | 111 | mod tests { |
113 | use test_utils::mark; | ||
114 | |||
115 | use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target}; | 112 | use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target}; |
116 | 113 | ||
117 | use super::*; | 114 | use super::*; |
@@ -139,7 +136,7 @@ mod tests { | |||
139 | 136 | ||
140 | #[test] | 137 | #[test] |
141 | fn change_visibility_field_false_positive() { | 138 | fn change_visibility_field_false_positive() { |
142 | mark::check!(change_visibility_field_false_positive); | 139 | cov_mark::check!(change_visibility_field_false_positive); |
143 | check_assist_not_applicable( | 140 | check_assist_not_applicable( |
144 | change_visibility, | 141 | change_visibility, |
145 | r"struct S { field: [(); { let $0x = ();}] }", | 142 | r"struct S { field: [(); { let $0x = ();}] }", |
diff --git a/crates/ide_assists/src/handlers/convert_iter_for_each_to_for.rs b/crates/ide_assists/src/handlers/convert_iter_for_each_to_for.rs new file mode 100644 index 000000000..4e75a7b14 --- /dev/null +++ b/crates/ide_assists/src/handlers/convert_iter_for_each_to_for.rs | |||
@@ -0,0 +1,248 @@ | |||
1 | use ide_db::helpers::FamousDefs; | ||
2 | use syntax::{ | ||
3 | ast::{self, edit::AstNodeEdit, make, ArgListOwner}, | ||
4 | AstNode, | ||
5 | }; | ||
6 | |||
7 | use crate::{AssistContext, AssistId, AssistKind, Assists}; | ||
8 | |||
9 | // Assist: convert_iter_for_each_to_for | ||
10 | // | ||
11 | // Converts an Iterator::for_each function into a for loop. | ||
12 | // | ||
13 | // ``` | ||
14 | // # //- /lib.rs crate:core | ||
15 | // # pub mod iter { pub mod traits { pub mod iterator { pub trait Iterator {} } } } | ||
16 | // # pub struct SomeIter; | ||
17 | // # impl self::iter::traits::iterator::Iterator for SomeIter {} | ||
18 | // # //- /lib.rs crate:main deps:core | ||
19 | // # use core::SomeIter; | ||
20 | // fn main() { | ||
21 | // let iter = SomeIter; | ||
22 | // iter.for_each$0(|(x, y)| { | ||
23 | // println!("x: {}, y: {}", x, y); | ||
24 | // }); | ||
25 | // } | ||
26 | // ``` | ||
27 | // -> | ||
28 | // ``` | ||
29 | // # use core::SomeIter; | ||
30 | // fn main() { | ||
31 | // let iter = SomeIter; | ||
32 | // for (x, y) in iter { | ||
33 | // println!("x: {}, y: {}", x, y); | ||
34 | // } | ||
35 | // } | ||
36 | // ``` | ||
37 | |||
38 | pub(crate) fn convert_iter_for_each_to_for(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | ||
39 | let method = ctx.find_node_at_offset::<ast::MethodCallExpr>()?; | ||
40 | |||
41 | let closure = match method.arg_list()?.args().next()? { | ||
42 | ast::Expr::ClosureExpr(expr) => expr, | ||
43 | _ => return None, | ||
44 | }; | ||
45 | |||
46 | let (method, receiver) = validate_method_call_expr(ctx, method)?; | ||
47 | |||
48 | let param_list = closure.param_list()?; | ||
49 | let param = param_list.params().next()?.pat()?; | ||
50 | let body = closure.body()?; | ||
51 | |||
52 | let stmt = method.syntax().parent().and_then(ast::ExprStmt::cast); | ||
53 | let syntax = stmt.as_ref().map_or(method.syntax(), |stmt| stmt.syntax()); | ||
54 | |||
55 | acc.add( | ||
56 | AssistId("convert_iter_for_each_to_for", AssistKind::RefactorRewrite), | ||
57 | "Replace this `Iterator::for_each` with a for loop", | ||
58 | syntax.text_range(), | ||
59 | |builder| { | ||
60 | let indent = stmt.as_ref().map_or(method.indent_level(), |stmt| stmt.indent_level()); | ||
61 | |||
62 | let block = match body { | ||
63 | ast::Expr::BlockExpr(block) => block, | ||
64 | _ => make::block_expr(Vec::new(), Some(body)), | ||
65 | } | ||
66 | .reset_indent() | ||
67 | .indent(indent); | ||
68 | |||
69 | let expr_for_loop = make::expr_for_loop(param, receiver, block); | ||
70 | builder.replace(syntax.text_range(), expr_for_loop.syntax().text()) | ||
71 | }, | ||
72 | ) | ||
73 | } | ||
74 | |||
75 | fn validate_method_call_expr( | ||
76 | ctx: &AssistContext, | ||
77 | expr: ast::MethodCallExpr, | ||
78 | ) -> Option<(ast::Expr, ast::Expr)> { | ||
79 | let name_ref = expr.name_ref()?; | ||
80 | if name_ref.syntax().text_range().intersect(ctx.frange.range).is_none() | ||
81 | || name_ref.text() != "for_each" | ||
82 | { | ||
83 | return None; | ||
84 | } | ||
85 | |||
86 | let sema = &ctx.sema; | ||
87 | |||
88 | let receiver = expr.receiver()?; | ||
89 | let expr = ast::Expr::MethodCallExpr(expr); | ||
90 | |||
91 | let it_type = sema.type_of_expr(&receiver)?; | ||
92 | let module = sema.scope(receiver.syntax()).module()?; | ||
93 | let krate = module.krate(); | ||
94 | |||
95 | let iter_trait = FamousDefs(sema, Some(krate)).core_iter_Iterator()?; | ||
96 | it_type.impls_trait(sema.db, iter_trait, &[]).then(|| (expr, receiver)) | ||
97 | } | ||
98 | |||
99 | #[cfg(test)] | ||
100 | mod tests { | ||
101 | use crate::tests::{self, check_assist}; | ||
102 | |||
103 | use super::*; | ||
104 | |||
105 | const EMPTY_ITER_FIXTURE: &'static str = r" | ||
106 | //- /lib.rs deps:core crate:empty_iter | ||
107 | pub struct EmptyIter; | ||
108 | impl Iterator for EmptyIter { | ||
109 | type Item = usize; | ||
110 | fn next(&mut self) -> Option<Self::Item> { None } | ||
111 | } | ||
112 | pub struct Empty; | ||
113 | impl Empty { | ||
114 | pub fn iter(&self) -> EmptyIter { EmptyIter } | ||
115 | } | ||
116 | "; | ||
117 | |||
118 | fn check_assist_with_fixtures(before: &str, after: &str) { | ||
119 | let before = &format!( | ||
120 | "//- /main.rs crate:main deps:core,empty_iter{}{}{}", | ||
121 | before, | ||
122 | EMPTY_ITER_FIXTURE, | ||
123 | FamousDefs::FIXTURE, | ||
124 | ); | ||
125 | check_assist(convert_iter_for_each_to_for, before, after); | ||
126 | } | ||
127 | |||
128 | fn check_assist_not_applicable(before: &str) { | ||
129 | let before = &format!( | ||
130 | "//- /main.rs crate:main deps:core,empty_iter{}{}{}", | ||
131 | before, | ||
132 | EMPTY_ITER_FIXTURE, | ||
133 | FamousDefs::FIXTURE, | ||
134 | ); | ||
135 | tests::check_assist_not_applicable(convert_iter_for_each_to_for, before); | ||
136 | } | ||
137 | |||
138 | #[test] | ||
139 | fn test_for_each_in_method_stmt() { | ||
140 | check_assist_with_fixtures( | ||
141 | r#" | ||
142 | use empty_iter::*; | ||
143 | fn main() { | ||
144 | let x = Empty; | ||
145 | x.iter().$0for_each(|(x, y)| { | ||
146 | println!("x: {}, y: {}", x, y); | ||
147 | }); | ||
148 | }"#, | ||
149 | r#" | ||
150 | use empty_iter::*; | ||
151 | fn main() { | ||
152 | let x = Empty; | ||
153 | for (x, y) in x.iter() { | ||
154 | println!("x: {}, y: {}", x, y); | ||
155 | } | ||
156 | } | ||
157 | "#, | ||
158 | ) | ||
159 | } | ||
160 | |||
161 | #[test] | ||
162 | fn test_for_each_in_method() { | ||
163 | check_assist_with_fixtures( | ||
164 | r#" | ||
165 | use empty_iter::*; | ||
166 | fn main() { | ||
167 | let x = Empty; | ||
168 | x.iter().$0for_each(|(x, y)| { | ||
169 | println!("x: {}, y: {}", x, y); | ||
170 | }) | ||
171 | }"#, | ||
172 | r#" | ||
173 | use empty_iter::*; | ||
174 | fn main() { | ||
175 | let x = Empty; | ||
176 | for (x, y) in x.iter() { | ||
177 | println!("x: {}, y: {}", x, y); | ||
178 | } | ||
179 | } | ||
180 | "#, | ||
181 | ) | ||
182 | } | ||
183 | |||
184 | #[test] | ||
185 | fn test_for_each_in_iter_stmt() { | ||
186 | check_assist_with_fixtures( | ||
187 | r#" | ||
188 | use empty_iter::*; | ||
189 | fn main() { | ||
190 | let x = Empty.iter(); | ||
191 | x.$0for_each(|(x, y)| { | ||
192 | println!("x: {}, y: {}", x, y); | ||
193 | }); | ||
194 | }"#, | ||
195 | r#" | ||
196 | use empty_iter::*; | ||
197 | fn main() { | ||
198 | let x = Empty.iter(); | ||
199 | for (x, y) in x { | ||
200 | println!("x: {}, y: {}", x, y); | ||
201 | } | ||
202 | } | ||
203 | "#, | ||
204 | ) | ||
205 | } | ||
206 | |||
207 | #[test] | ||
208 | fn test_for_each_without_braces_stmt() { | ||
209 | check_assist_with_fixtures( | ||
210 | r#" | ||
211 | use empty_iter::*; | ||
212 | fn main() { | ||
213 | let x = Empty; | ||
214 | x.iter().$0for_each(|(x, y)| println!("x: {}, y: {}", x, y)); | ||
215 | }"#, | ||
216 | r#" | ||
217 | use empty_iter::*; | ||
218 | fn main() { | ||
219 | let x = Empty; | ||
220 | for (x, y) in x.iter() { | ||
221 | println!("x: {}, y: {}", x, y) | ||
222 | } | ||
223 | } | ||
224 | "#, | ||
225 | ) | ||
226 | } | ||
227 | |||
228 | #[test] | ||
229 | fn test_for_each_not_applicable() { | ||
230 | check_assist_not_applicable( | ||
231 | r#" | ||
232 | fn main() { | ||
233 | ().$0for_each(|x| println!("{}", x)); | ||
234 | }"#, | ||
235 | ) | ||
236 | } | ||
237 | |||
238 | #[test] | ||
239 | fn test_for_each_not_applicable_invalid_cursor_pos() { | ||
240 | check_assist_not_applicable( | ||
241 | r#" | ||
242 | use empty_iter::*; | ||
243 | fn main() { | ||
244 | Empty.iter().for_each(|(x, y)| $0println!("x: {}, y: {}", x, y)); | ||
245 | }"#, | ||
246 | ) | ||
247 | } | ||
248 | } | ||
diff --git a/crates/ide_assists/src/handlers/extract_function.rs b/crates/ide_assists/src/handlers/extract_function.rs index 8779d8bd1..dd4501709 100644 --- a/crates/ide_assists/src/handlers/extract_function.rs +++ b/crates/ide_assists/src/handlers/extract_function.rs | |||
@@ -20,7 +20,6 @@ use syntax::{ | |||
20 | SyntaxKind::{self, BLOCK_EXPR, BREAK_EXPR, COMMENT, PATH_EXPR, RETURN_EXPR}, | 20 | SyntaxKind::{self, BLOCK_EXPR, BREAK_EXPR, COMMENT, PATH_EXPR, RETURN_EXPR}, |
21 | SyntaxNode, SyntaxToken, TextRange, TextSize, TokenAtOffset, WalkEvent, T, | 21 | SyntaxNode, SyntaxToken, TextRange, TextSize, TokenAtOffset, WalkEvent, T, |
22 | }; | 22 | }; |
23 | use test_utils::mark; | ||
24 | 23 | ||
25 | use crate::{ | 24 | use crate::{ |
26 | assist_context::{AssistContext, Assists}, | 25 | assist_context::{AssistContext, Assists}, |
@@ -59,7 +58,7 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext) -> Option | |||
59 | 58 | ||
60 | let node = ctx.covering_element(); | 59 | let node = ctx.covering_element(); |
61 | if node.kind() == COMMENT { | 60 | if node.kind() == COMMENT { |
62 | mark::hit!(extract_function_in_comment_is_not_applicable); | 61 | cov_mark::hit!(extract_function_in_comment_is_not_applicable); |
63 | return None; | 62 | return None; |
64 | } | 63 | } |
65 | 64 | ||
@@ -197,14 +196,14 @@ fn external_control_flow(ctx: &AssistContext, body: &FunctionBody) -> Option<Con | |||
197 | if let Some(kind) = expr_err_kind(&expr, ctx) { | 196 | if let Some(kind) = expr_err_kind(&expr, ctx) { |
198 | Some(FlowKind::TryReturn { expr, kind }) | 197 | Some(FlowKind::TryReturn { expr, kind }) |
199 | } else { | 198 | } else { |
200 | mark::hit!(external_control_flow_try_and_return_non_err); | 199 | cov_mark::hit!(external_control_flow_try_and_return_non_err); |
201 | return None; | 200 | return None; |
202 | } | 201 | } |
203 | } | 202 | } |
204 | None => return None, | 203 | None => return None, |
205 | }, | 204 | }, |
206 | (Some(_), _, _, _) => { | 205 | (Some(_), _, _, _) => { |
207 | mark::hit!(external_control_flow_try_and_bc); | 206 | cov_mark::hit!(external_control_flow_try_and_bc); |
208 | return None; | 207 | return None; |
209 | } | 208 | } |
210 | (None, Some(r), None, None) => match r.expr() { | 209 | (None, Some(r), None, None) => match r.expr() { |
@@ -212,11 +211,11 @@ fn external_control_flow(ctx: &AssistContext, body: &FunctionBody) -> Option<Con | |||
212 | None => Some(FlowKind::Return), | 211 | None => Some(FlowKind::Return), |
213 | }, | 212 | }, |
214 | (None, Some(_), _, _) => { | 213 | (None, Some(_), _, _) => { |
215 | mark::hit!(external_control_flow_return_and_bc); | 214 | cov_mark::hit!(external_control_flow_return_and_bc); |
216 | return None; | 215 | return None; |
217 | } | 216 | } |
218 | (None, None, Some(_), Some(_)) => { | 217 | (None, None, Some(_), Some(_)) => { |
219 | mark::hit!(external_control_flow_break_and_continue); | 218 | cov_mark::hit!(external_control_flow_break_and_continue); |
220 | return None; | 219 | return None; |
221 | } | 220 | } |
222 | (None, None, Some(b), None) => match b.expr() { | 221 | (None, None, Some(b), None) => match b.expr() { |
@@ -1837,7 +1836,7 @@ fn $0fun_name(n: u32) -> u32 { | |||
1837 | 1836 | ||
1838 | #[test] | 1837 | #[test] |
1839 | fn in_comment_is_not_applicable() { | 1838 | fn in_comment_is_not_applicable() { |
1840 | mark::check!(extract_function_in_comment_is_not_applicable); | 1839 | cov_mark::check!(extract_function_in_comment_is_not_applicable); |
1841 | check_assist_not_applicable(extract_function, r"fn main() { 1 + /* $0comment$0 */ 1; }"); | 1840 | check_assist_not_applicable(extract_function, r"fn main() { 1 + /* $0comment$0 */ 1; }"); |
1842 | } | 1841 | } |
1843 | 1842 | ||
@@ -2822,7 +2821,7 @@ fn $0fun_name(n: i32) -> Result<i32, i64> { | |||
2822 | 2821 | ||
2823 | #[test] | 2822 | #[test] |
2824 | fn break_and_continue() { | 2823 | fn break_and_continue() { |
2825 | mark::check!(external_control_flow_break_and_continue); | 2824 | cov_mark::check!(external_control_flow_break_and_continue); |
2826 | check_assist_not_applicable( | 2825 | check_assist_not_applicable( |
2827 | extract_function, | 2826 | extract_function, |
2828 | r##" | 2827 | r##" |
@@ -2842,7 +2841,7 @@ fn foo() { | |||
2842 | 2841 | ||
2843 | #[test] | 2842 | #[test] |
2844 | fn return_and_break() { | 2843 | fn return_and_break() { |
2845 | mark::check!(external_control_flow_return_and_bc); | 2844 | cov_mark::check!(external_control_flow_return_and_bc); |
2846 | check_assist_not_applicable( | 2845 | check_assist_not_applicable( |
2847 | extract_function, | 2846 | extract_function, |
2848 | r##" | 2847 | r##" |
@@ -3341,7 +3340,7 @@ fn $0fun_name() -> Result<i32, i64> { | |||
3341 | 3340 | ||
3342 | #[test] | 3341 | #[test] |
3343 | fn try_and_break() { | 3342 | fn try_and_break() { |
3344 | mark::check!(external_control_flow_try_and_bc); | 3343 | cov_mark::check!(external_control_flow_try_and_bc); |
3345 | check_assist_not_applicable( | 3344 | check_assist_not_applicable( |
3346 | extract_function, | 3345 | extract_function, |
3347 | r##" | 3346 | r##" |
@@ -3363,7 +3362,7 @@ fn foo() -> Option<()> { | |||
3363 | 3362 | ||
3364 | #[test] | 3363 | #[test] |
3365 | fn try_and_return_ok() { | 3364 | fn try_and_return_ok() { |
3366 | mark::check!(external_control_flow_try_and_return_non_err); | 3365 | cov_mark::check!(external_control_flow_try_and_return_non_err); |
3367 | check_assist_not_applicable( | 3366 | check_assist_not_applicable( |
3368 | extract_function, | 3367 | extract_function, |
3369 | r##" | 3368 | r##" |
diff --git a/crates/ide_assists/src/handlers/extract_struct_from_enum_variant.rs b/crates/ide_assists/src/handlers/extract_struct_from_enum_variant.rs index 5c7678b53..335e0ed95 100644 --- a/crates/ide_assists/src/handlers/extract_struct_from_enum_variant.rs +++ b/crates/ide_assists/src/handlers/extract_struct_from_enum_variant.rs | |||
@@ -1,7 +1,7 @@ | |||
1 | use std::iter; | 1 | use std::iter; |
2 | 2 | ||
3 | use either::Either; | 3 | use either::Either; |
4 | use hir::{AsName, Module, ModuleDef, Name, Variant}; | 4 | use hir::{Module, ModuleDef, Name, Variant}; |
5 | use ide_db::{ | 5 | use ide_db::{ |
6 | defs::Definition, | 6 | defs::Definition, |
7 | helpers::{ | 7 | helpers::{ |
@@ -133,7 +133,7 @@ fn existing_definition(db: &RootDatabase, variant_name: &ast::Name, variant: &Va | |||
133 | ), | 133 | ), |
134 | _ => false, | 134 | _ => false, |
135 | }) | 135 | }) |
136 | .any(|(name, _)| name == variant_name.as_name()) | 136 | .any(|(name, _)| name.to_string() == variant_name.to_string()) |
137 | } | 137 | } |
138 | 138 | ||
139 | fn insert_import( | 139 | fn insert_import( |
@@ -154,7 +154,7 @@ fn insert_import( | |||
154 | mod_path.pop_segment(); | 154 | mod_path.pop_segment(); |
155 | mod_path.push_segment(variant_hir_name.clone()); | 155 | mod_path.push_segment(variant_hir_name.clone()); |
156 | let scope = ImportScope::find_insert_use_container(scope_node, &ctx.sema)?; | 156 | let scope = ImportScope::find_insert_use_container(scope_node, &ctx.sema)?; |
157 | *rewriter += insert_use(&scope, mod_path_to_ast(&mod_path), ctx.config.insert_use.merge); | 157 | *rewriter += insert_use(&scope, mod_path_to_ast(&mod_path), ctx.config.insert_use); |
158 | } | 158 | } |
159 | Some(()) | 159 | Some(()) |
160 | } | 160 | } |
diff --git a/crates/ide_assists/src/handlers/extract_variable.rs b/crates/ide_assists/src/handlers/extract_variable.rs index 312ac7ac4..7a32483dc 100644 --- a/crates/ide_assists/src/handlers/extract_variable.rs +++ b/crates/ide_assists/src/handlers/extract_variable.rs | |||
@@ -6,7 +6,6 @@ use syntax::{ | |||
6 | }, | 6 | }, |
7 | SyntaxNode, | 7 | SyntaxNode, |
8 | }; | 8 | }; |
9 | use test_utils::mark; | ||
10 | 9 | ||
11 | use crate::{utils::suggest_name, AssistContext, AssistId, AssistKind, Assists}; | 10 | use crate::{utils::suggest_name, AssistContext, AssistId, AssistKind, Assists}; |
12 | 11 | ||
@@ -32,7 +31,7 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext) -> Option | |||
32 | } | 31 | } |
33 | let node = ctx.covering_element(); | 32 | let node = ctx.covering_element(); |
34 | if node.kind() == COMMENT { | 33 | if node.kind() == COMMENT { |
35 | mark::hit!(extract_var_in_comment_is_not_applicable); | 34 | cov_mark::hit!(extract_var_in_comment_is_not_applicable); |
36 | return None; | 35 | return None; |
37 | } | 36 | } |
38 | let to_extract = node.ancestors().find_map(valid_target_expr)?; | 37 | let to_extract = node.ancestors().find_map(valid_target_expr)?; |
@@ -69,7 +68,7 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext) -> Option | |||
69 | format_to!(buf, "{}", to_extract.syntax()); | 68 | format_to!(buf, "{}", to_extract.syntax()); |
70 | 69 | ||
71 | if let Anchor::Replace(stmt) = anchor { | 70 | if let Anchor::Replace(stmt) = anchor { |
72 | mark::hit!(test_extract_var_expr_stmt); | 71 | cov_mark::hit!(test_extract_var_expr_stmt); |
73 | if stmt.semicolon_token().is_none() { | 72 | if stmt.semicolon_token().is_none() { |
74 | buf.push_str(";"); | 73 | buf.push_str(";"); |
75 | } | 74 | } |
@@ -142,7 +141,7 @@ impl Anchor { | |||
142 | node.parent().and_then(ast::BlockExpr::cast).and_then(|it| it.tail_expr()) | 141 | node.parent().and_then(ast::BlockExpr::cast).and_then(|it| it.tail_expr()) |
143 | { | 142 | { |
144 | if expr.syntax() == &node { | 143 | if expr.syntax() == &node { |
145 | mark::hit!(test_extract_var_last_expr); | 144 | cov_mark::hit!(test_extract_var_last_expr); |
146 | return Some(Anchor::Before(node)); | 145 | return Some(Anchor::Before(node)); |
147 | } | 146 | } |
148 | } | 147 | } |
@@ -175,8 +174,6 @@ impl Anchor { | |||
175 | 174 | ||
176 | #[cfg(test)] | 175 | #[cfg(test)] |
177 | mod tests { | 176 | mod tests { |
178 | use test_utils::mark; | ||
179 | |||
180 | use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target}; | 177 | use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target}; |
181 | 178 | ||
182 | use super::*; | 179 | use super::*; |
@@ -199,13 +196,13 @@ fn foo() { | |||
199 | 196 | ||
200 | #[test] | 197 | #[test] |
201 | fn extract_var_in_comment_is_not_applicable() { | 198 | fn extract_var_in_comment_is_not_applicable() { |
202 | mark::check!(extract_var_in_comment_is_not_applicable); | 199 | cov_mark::check!(extract_var_in_comment_is_not_applicable); |
203 | check_assist_not_applicable(extract_variable, "fn main() { 1 + /* $0comment$0 */ 1; }"); | 200 | check_assist_not_applicable(extract_variable, "fn main() { 1 + /* $0comment$0 */ 1; }"); |
204 | } | 201 | } |
205 | 202 | ||
206 | #[test] | 203 | #[test] |
207 | fn test_extract_var_expr_stmt() { | 204 | fn test_extract_var_expr_stmt() { |
208 | mark::check!(test_extract_var_expr_stmt); | 205 | cov_mark::check!(test_extract_var_expr_stmt); |
209 | check_assist( | 206 | check_assist( |
210 | extract_variable, | 207 | extract_variable, |
211 | r#" | 208 | r#" |
@@ -250,7 +247,7 @@ fn foo() { | |||
250 | 247 | ||
251 | #[test] | 248 | #[test] |
252 | fn test_extract_var_last_expr() { | 249 | fn test_extract_var_last_expr() { |
253 | mark::check!(test_extract_var_last_expr); | 250 | cov_mark::check!(test_extract_var_last_expr); |
254 | check_assist( | 251 | check_assist( |
255 | extract_variable, | 252 | extract_variable, |
256 | r#" | 253 | r#" |
diff --git a/crates/ide_assists/src/handlers/fill_match_arms.rs b/crates/ide_assists/src/handlers/fill_match_arms.rs index 7086e47d2..878b3a3fa 100644 --- a/crates/ide_assists/src/handlers/fill_match_arms.rs +++ b/crates/ide_assists/src/handlers/fill_match_arms.rs | |||
@@ -5,7 +5,6 @@ use ide_db::helpers::{mod_path_to_ast, FamousDefs}; | |||
5 | use ide_db::RootDatabase; | 5 | use ide_db::RootDatabase; |
6 | use itertools::Itertools; | 6 | use itertools::Itertools; |
7 | use syntax::ast::{self, make, AstNode, MatchArm, NameOwner, Pat}; | 7 | use syntax::ast::{self, make, AstNode, MatchArm, NameOwner, Pat}; |
8 | use test_utils::mark; | ||
9 | 8 | ||
10 | use crate::{ | 9 | use crate::{ |
11 | utils::{does_pat_match_variant, render_snippet, Cursor}, | 10 | utils::{does_pat_match_variant, render_snippet, Cursor}, |
@@ -62,7 +61,7 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option< | |||
62 | .collect::<Vec<_>>(); | 61 | .collect::<Vec<_>>(); |
63 | if Some(enum_def) == FamousDefs(&ctx.sema, Some(module.krate())).core_option_Option() { | 62 | if Some(enum_def) == FamousDefs(&ctx.sema, Some(module.krate())).core_option_Option() { |
64 | // Match `Some` variant first. | 63 | // Match `Some` variant first. |
65 | mark::hit!(option_order); | 64 | cov_mark::hit!(option_order); |
66 | variants.reverse() | 65 | variants.reverse() |
67 | } | 66 | } |
68 | variants | 67 | variants |
@@ -195,7 +194,6 @@ fn build_pat(db: &RootDatabase, module: hir::Module, var: hir::Variant) -> Optio | |||
195 | #[cfg(test)] | 194 | #[cfg(test)] |
196 | mod tests { | 195 | mod tests { |
197 | use ide_db::helpers::FamousDefs; | 196 | use ide_db::helpers::FamousDefs; |
198 | use test_utils::mark; | ||
199 | 197 | ||
200 | use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target}; | 198 | use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target}; |
201 | 199 | ||
@@ -730,7 +728,7 @@ fn main() { | |||
730 | 728 | ||
731 | #[test] | 729 | #[test] |
732 | fn option_order() { | 730 | fn option_order() { |
733 | mark::check!(option_order); | 731 | cov_mark::check!(option_order); |
734 | let before = r#" | 732 | let before = r#" |
735 | fn foo(opt: Option<i32>) { | 733 | fn foo(opt: Option<i32>) { |
736 | match opt$0 { | 734 | match opt$0 { |
diff --git a/crates/ide_assists/src/handlers/generate_default_from_enum_variant.rs b/crates/ide_assists/src/handlers/generate_default_from_enum_variant.rs index 6a2ab9596..588ee1350 100644 --- a/crates/ide_assists/src/handlers/generate_default_from_enum_variant.rs +++ b/crates/ide_assists/src/handlers/generate_default_from_enum_variant.rs | |||
@@ -1,7 +1,6 @@ | |||
1 | use ide_db::helpers::FamousDefs; | 1 | use ide_db::helpers::FamousDefs; |
2 | use ide_db::RootDatabase; | 2 | use ide_db::RootDatabase; |
3 | use syntax::ast::{self, AstNode, NameOwner}; | 3 | use syntax::ast::{self, AstNode, NameOwner}; |
4 | use test_utils::mark; | ||
5 | 4 | ||
6 | use crate::{AssistContext, AssistId, AssistKind, Assists}; | 5 | use crate::{AssistContext, AssistId, AssistKind, Assists}; |
7 | 6 | ||
@@ -38,12 +37,12 @@ pub(crate) fn generate_default_from_enum_variant( | |||
38 | let variant_name = variant.name()?; | 37 | let variant_name = variant.name()?; |
39 | let enum_name = variant.parent_enum().name()?; | 38 | let enum_name = variant.parent_enum().name()?; |
40 | if !matches!(variant.kind(), ast::StructKind::Unit) { | 39 | if !matches!(variant.kind(), ast::StructKind::Unit) { |
41 | mark::hit!(test_gen_default_on_non_unit_variant_not_implemented); | 40 | cov_mark::hit!(test_gen_default_on_non_unit_variant_not_implemented); |
42 | return None; | 41 | return None; |
43 | } | 42 | } |
44 | 43 | ||
45 | if existing_default_impl(&ctx.sema, &variant).is_some() { | 44 | if existing_default_impl(&ctx.sema, &variant).is_some() { |
46 | mark::hit!(test_gen_default_impl_already_exists); | 45 | cov_mark::hit!(test_gen_default_impl_already_exists); |
47 | return None; | 46 | return None; |
48 | } | 47 | } |
49 | 48 | ||
@@ -89,8 +88,6 @@ fn existing_default_impl( | |||
89 | 88 | ||
90 | #[cfg(test)] | 89 | #[cfg(test)] |
91 | mod tests { | 90 | mod tests { |
92 | use test_utils::mark; | ||
93 | |||
94 | use crate::tests::{check_assist, check_assist_not_applicable}; | 91 | use crate::tests::{check_assist, check_assist_not_applicable}; |
95 | 92 | ||
96 | use super::*; | 93 | use super::*; |
@@ -127,7 +124,7 @@ impl Default for Variant { | |||
127 | 124 | ||
128 | #[test] | 125 | #[test] |
129 | fn test_generate_default_already_implemented() { | 126 | fn test_generate_default_already_implemented() { |
130 | mark::check!(test_gen_default_impl_already_exists); | 127 | cov_mark::check!(test_gen_default_impl_already_exists); |
131 | check_not_applicable( | 128 | check_not_applicable( |
132 | r#" | 129 | r#" |
133 | enum Variant { | 130 | enum Variant { |
@@ -146,7 +143,7 @@ impl Default for Variant { | |||
146 | 143 | ||
147 | #[test] | 144 | #[test] |
148 | fn test_add_from_impl_no_element() { | 145 | fn test_add_from_impl_no_element() { |
149 | mark::check!(test_gen_default_on_non_unit_variant_not_implemented); | 146 | cov_mark::check!(test_gen_default_on_non_unit_variant_not_implemented); |
150 | check_not_applicable( | 147 | check_not_applicable( |
151 | r#" | 148 | r#" |
152 | enum Variant { | 149 | enum Variant { |
diff --git a/crates/ide_assists/src/handlers/generate_default_from_new.rs b/crates/ide_assists/src/handlers/generate_default_from_new.rs index fa1254579..81c54ba3e 100644 --- a/crates/ide_assists/src/handlers/generate_default_from_new.rs +++ b/crates/ide_assists/src/handlers/generate_default_from_new.rs | |||
@@ -7,7 +7,6 @@ use syntax::{ | |||
7 | ast::{self, Impl, NameOwner}, | 7 | ast::{self, Impl, NameOwner}, |
8 | AstNode, | 8 | AstNode, |
9 | }; | 9 | }; |
10 | use test_utils::mark; | ||
11 | 10 | ||
12 | // Assist: generate_default_from_new | 11 | // Assist: generate_default_from_new |
13 | // | 12 | // |
@@ -43,19 +42,19 @@ pub(crate) fn generate_default_from_new(acc: &mut Assists, ctx: &AssistContext) | |||
43 | let fn_name = fn_node.name()?; | 42 | let fn_name = fn_node.name()?; |
44 | 43 | ||
45 | if fn_name.text() != "new" { | 44 | if fn_name.text() != "new" { |
46 | mark::hit!(other_function_than_new); | 45 | cov_mark::hit!(other_function_than_new); |
47 | return None; | 46 | return None; |
48 | } | 47 | } |
49 | 48 | ||
50 | if fn_node.param_list()?.params().next().is_some() { | 49 | if fn_node.param_list()?.params().next().is_some() { |
51 | mark::hit!(new_function_with_parameters); | 50 | cov_mark::hit!(new_function_with_parameters); |
52 | return None; | 51 | return None; |
53 | } | 52 | } |
54 | 53 | ||
55 | let impl_ = fn_node.syntax().ancestors().into_iter().find_map(ast::Impl::cast)?; | 54 | let impl_ = fn_node.syntax().ancestors().into_iter().find_map(ast::Impl::cast)?; |
56 | if is_default_implemented(ctx, &impl_) { | 55 | if is_default_implemented(ctx, &impl_) { |
57 | mark::hit!(default_block_is_already_present); | 56 | cov_mark::hit!(default_block_is_already_present); |
58 | mark::hit!(struct_in_module_with_default); | 57 | cov_mark::hit!(struct_in_module_with_default); |
59 | return None; | 58 | return None; |
60 | } | 59 | } |
61 | 60 | ||
@@ -178,7 +177,7 @@ impl Default for Test { | |||
178 | 177 | ||
179 | #[test] | 178 | #[test] |
180 | fn new_function_with_parameters() { | 179 | fn new_function_with_parameters() { |
181 | mark::check!(new_function_with_parameters); | 180 | cov_mark::check!(new_function_with_parameters); |
182 | check_not_applicable( | 181 | check_not_applicable( |
183 | r#" | 182 | r#" |
184 | struct Example { _inner: () } | 183 | struct Example { _inner: () } |
@@ -194,7 +193,7 @@ impl Example { | |||
194 | 193 | ||
195 | #[test] | 194 | #[test] |
196 | fn other_function_than_new() { | 195 | fn other_function_than_new() { |
197 | mark::check!(other_function_than_new); | 196 | cov_mark::check!(other_function_than_new); |
198 | check_not_applicable( | 197 | check_not_applicable( |
199 | r#" | 198 | r#" |
200 | struct Example { _inner: () } | 199 | struct Example { _inner: () } |
@@ -211,7 +210,7 @@ impl Example { | |||
211 | 210 | ||
212 | #[test] | 211 | #[test] |
213 | fn default_block_is_already_present() { | 212 | fn default_block_is_already_present() { |
214 | mark::check!(default_block_is_already_present); | 213 | cov_mark::check!(default_block_is_already_present); |
215 | check_not_applicable( | 214 | check_not_applicable( |
216 | r#" | 215 | r#" |
217 | struct Example { _inner: () } | 216 | struct Example { _inner: () } |
@@ -340,7 +339,7 @@ impl Default for Example { | |||
340 | 339 | ||
341 | #[test] | 340 | #[test] |
342 | fn struct_in_module_with_default() { | 341 | fn struct_in_module_with_default() { |
343 | mark::check!(struct_in_module_with_default); | 342 | cov_mark::check!(struct_in_module_with_default); |
344 | check_not_applicable( | 343 | check_not_applicable( |
345 | r#" | 344 | r#" |
346 | mod test { | 345 | mod test { |
diff --git a/crates/ide_assists/src/handlers/generate_from_impl_for_enum.rs b/crates/ide_assists/src/handlers/generate_from_impl_for_enum.rs index d9388a737..c13c6eebe 100644 --- a/crates/ide_assists/src/handlers/generate_from_impl_for_enum.rs +++ b/crates/ide_assists/src/handlers/generate_from_impl_for_enum.rs | |||
@@ -1,7 +1,6 @@ | |||
1 | use ide_db::helpers::FamousDefs; | 1 | use ide_db::helpers::FamousDefs; |
2 | use ide_db::RootDatabase; | 2 | use ide_db::RootDatabase; |
3 | use syntax::ast::{self, AstNode, NameOwner}; | 3 | use syntax::ast::{self, AstNode, NameOwner}; |
4 | use test_utils::mark; | ||
5 | 4 | ||
6 | use crate::{utils::generate_trait_impl_text, AssistContext, AssistId, AssistKind, Assists}; | 5 | use crate::{utils::generate_trait_impl_text, AssistContext, AssistId, AssistKind, Assists}; |
7 | 6 | ||
@@ -44,7 +43,7 @@ pub(crate) fn generate_from_impl_for_enum(acc: &mut Assists, ctx: &AssistContext | |||
44 | }; | 43 | }; |
45 | 44 | ||
46 | if existing_from_impl(&ctx.sema, &variant).is_some() { | 45 | if existing_from_impl(&ctx.sema, &variant).is_some() { |
47 | mark::hit!(test_add_from_impl_already_exists); | 46 | cov_mark::hit!(test_add_from_impl_already_exists); |
48 | return None; | 47 | return None; |
49 | } | 48 | } |
50 | 49 | ||
@@ -103,8 +102,6 @@ fn existing_from_impl( | |||
103 | 102 | ||
104 | #[cfg(test)] | 103 | #[cfg(test)] |
105 | mod tests { | 104 | mod tests { |
106 | use test_utils::mark; | ||
107 | |||
108 | use crate::tests::{check_assist, check_assist_not_applicable}; | 105 | use crate::tests::{check_assist, check_assist_not_applicable}; |
109 | 106 | ||
110 | use super::*; | 107 | use super::*; |
@@ -172,7 +169,7 @@ impl From<u32> for A { | |||
172 | 169 | ||
173 | #[test] | 170 | #[test] |
174 | fn test_add_from_impl_already_exists() { | 171 | fn test_add_from_impl_already_exists() { |
175 | mark::check!(test_add_from_impl_already_exists); | 172 | cov_mark::check!(test_add_from_impl_already_exists); |
176 | check_not_applicable( | 173 | check_not_applicable( |
177 | r#" | 174 | r#" |
178 | enum A { $0One(u32), } | 175 | enum A { $0One(u32), } |
diff --git a/crates/ide_assists/src/handlers/generate_function.rs b/crates/ide_assists/src/handlers/generate_function.rs index 3870b7e75..6f95b1a07 100644 --- a/crates/ide_assists/src/handlers/generate_function.rs +++ b/crates/ide_assists/src/handlers/generate_function.rs | |||
@@ -83,17 +83,18 @@ struct FunctionTemplate { | |||
83 | leading_ws: String, | 83 | leading_ws: String, |
84 | fn_def: ast::Fn, | 84 | fn_def: ast::Fn, |
85 | ret_type: ast::RetType, | 85 | ret_type: ast::RetType, |
86 | should_render_snippet: bool, | ||
86 | trailing_ws: String, | 87 | trailing_ws: String, |
87 | file: FileId, | 88 | file: FileId, |
88 | } | 89 | } |
89 | 90 | ||
90 | impl FunctionTemplate { | 91 | impl FunctionTemplate { |
91 | fn to_string(&self, cap: Option<SnippetCap>) -> String { | 92 | fn to_string(&self, cap: Option<SnippetCap>) -> String { |
92 | let f = match cap { | 93 | let f = match (cap, self.should_render_snippet) { |
93 | Some(cap) => { | 94 | (Some(cap), true) => { |
94 | render_snippet(cap, self.fn_def.syntax(), Cursor::Replace(self.ret_type.syntax())) | 95 | render_snippet(cap, self.fn_def.syntax(), Cursor::Replace(self.ret_type.syntax())) |
95 | } | 96 | } |
96 | None => self.fn_def.to_string(), | 97 | _ => self.fn_def.to_string(), |
97 | }; | 98 | }; |
98 | format!("{}{}{}", self.leading_ws, f, self.trailing_ws) | 99 | format!("{}{}{}", self.leading_ws, f, self.trailing_ws) |
99 | } | 100 | } |
@@ -104,6 +105,8 @@ struct FunctionBuilder { | |||
104 | fn_name: ast::Name, | 105 | fn_name: ast::Name, |
105 | type_params: Option<ast::GenericParamList>, | 106 | type_params: Option<ast::GenericParamList>, |
106 | params: ast::ParamList, | 107 | params: ast::ParamList, |
108 | ret_type: ast::RetType, | ||
109 | should_render_snippet: bool, | ||
107 | file: FileId, | 110 | file: FileId, |
108 | needs_pub: bool, | 111 | needs_pub: bool, |
109 | } | 112 | } |
@@ -132,7 +135,43 @@ impl FunctionBuilder { | |||
132 | let fn_name = fn_name(&path)?; | 135 | let fn_name = fn_name(&path)?; |
133 | let (type_params, params) = fn_args(ctx, target_module, &call)?; | 136 | let (type_params, params) = fn_args(ctx, target_module, &call)?; |
134 | 137 | ||
135 | Some(Self { target, fn_name, type_params, params, file, needs_pub }) | 138 | // should_render_snippet intends to express a rough level of confidence about |
139 | // the correctness of the return type. | ||
140 | // | ||
141 | // If we are able to infer some return type, and that return type is not unit, we | ||
142 | // don't want to render the snippet. The assumption here is in this situation the | ||
143 | // return type is just as likely to be correct as any other part of the generated | ||
144 | // function. | ||
145 | // | ||
146 | // In the case where the return type is inferred as unit it is likely that the | ||
147 | // user does in fact intend for this generated function to return some non unit | ||
148 | // type, but that the current state of their code doesn't allow that return type | ||
149 | // to be accurately inferred. | ||
150 | let (ret_ty, should_render_snippet) = { | ||
151 | match ctx.sema.type_of_expr(&ast::Expr::CallExpr(call.clone())) { | ||
152 | Some(ty) if ty.is_unknown() || ty.is_unit() => (make::ty_unit(), true), | ||
153 | Some(ty) => { | ||
154 | let rendered = ty.display_source_code(ctx.db(), target_module.into()); | ||
155 | match rendered { | ||
156 | Ok(rendered) => (make::ty(&rendered), false), | ||
157 | Err(_) => (make::ty_unit(), true), | ||
158 | } | ||
159 | } | ||
160 | None => (make::ty_unit(), true), | ||
161 | } | ||
162 | }; | ||
163 | let ret_type = make::ret_type(ret_ty); | ||
164 | |||
165 | Some(Self { | ||
166 | target, | ||
167 | fn_name, | ||
168 | type_params, | ||
169 | params, | ||
170 | ret_type, | ||
171 | should_render_snippet, | ||
172 | file, | ||
173 | needs_pub, | ||
174 | }) | ||
136 | } | 175 | } |
137 | 176 | ||
138 | fn render(self) -> FunctionTemplate { | 177 | fn render(self) -> FunctionTemplate { |
@@ -145,7 +184,7 @@ impl FunctionBuilder { | |||
145 | self.type_params, | 184 | self.type_params, |
146 | self.params, | 185 | self.params, |
147 | fn_body, | 186 | fn_body, |
148 | Some(make::ret_type(make::ty_unit())), | 187 | Some(self.ret_type), |
149 | ); | 188 | ); |
150 | let leading_ws; | 189 | let leading_ws; |
151 | let trailing_ws; | 190 | let trailing_ws; |
@@ -171,6 +210,7 @@ impl FunctionBuilder { | |||
171 | insert_offset, | 210 | insert_offset, |
172 | leading_ws, | 211 | leading_ws, |
173 | ret_type: fn_def.ret_type().unwrap(), | 212 | ret_type: fn_def.ret_type().unwrap(), |
213 | should_render_snippet: self.should_render_snippet, | ||
174 | fn_def, | 214 | fn_def, |
175 | trailing_ws, | 215 | trailing_ws, |
176 | file: self.file, | 216 | file: self.file, |
@@ -546,7 +586,7 @@ impl Baz { | |||
546 | } | 586 | } |
547 | } | 587 | } |
548 | 588 | ||
549 | fn bar(baz: Baz) ${0:-> ()} { | 589 | fn bar(baz: Baz) -> Baz { |
550 | todo!() | 590 | todo!() |
551 | } | 591 | } |
552 | ", | 592 | ", |
@@ -1060,6 +1100,27 @@ pub(crate) fn bar() ${0:-> ()} { | |||
1060 | } | 1100 | } |
1061 | 1101 | ||
1062 | #[test] | 1102 | #[test] |
1103 | fn add_function_with_return_type() { | ||
1104 | check_assist( | ||
1105 | generate_function, | ||
1106 | r" | ||
1107 | fn main() { | ||
1108 | let x: u32 = foo$0(); | ||
1109 | } | ||
1110 | ", | ||
1111 | r" | ||
1112 | fn main() { | ||
1113 | let x: u32 = foo(); | ||
1114 | } | ||
1115 | |||
1116 | fn foo() -> u32 { | ||
1117 | todo!() | ||
1118 | } | ||
1119 | ", | ||
1120 | ) | ||
1121 | } | ||
1122 | |||
1123 | #[test] | ||
1063 | fn add_function_not_applicable_if_function_already_exists() { | 1124 | fn add_function_not_applicable_if_function_already_exists() { |
1064 | check_assist_not_applicable( | 1125 | check_assist_not_applicable( |
1065 | generate_function, | 1126 | generate_function, |
diff --git a/crates/ide_assists/src/handlers/infer_function_return_type.rs b/crates/ide_assists/src/handlers/infer_function_return_type.rs index 5279af1f3..66113751c 100644 --- a/crates/ide_assists/src/handlers/infer_function_return_type.rs +++ b/crates/ide_assists/src/handlers/infer_function_return_type.rs | |||
@@ -1,6 +1,5 @@ | |||
1 | use hir::HirDisplay; | 1 | use hir::HirDisplay; |
2 | use syntax::{ast, AstNode, TextRange, TextSize}; | 2 | use syntax::{ast, AstNode, TextRange, TextSize}; |
3 | use test_utils::mark; | ||
4 | 3 | ||
5 | use crate::{AssistContext, AssistId, AssistKind, Assists}; | 4 | use crate::{AssistContext, AssistId, AssistKind, Assists}; |
6 | 5 | ||
@@ -42,7 +41,7 @@ pub(crate) fn infer_function_return_type(acc: &mut Assists, ctx: &AssistContext) | |||
42 | } | 41 | } |
43 | } | 42 | } |
44 | if let FnType::Closure { wrap_expr: true } = fn_type { | 43 | if let FnType::Closure { wrap_expr: true } = fn_type { |
45 | mark::hit!(wrap_closure_non_block_expr); | 44 | cov_mark::hit!(wrap_closure_non_block_expr); |
46 | // `|x| x` becomes `|x| -> T x` which is invalid, so wrap it in a block | 45 | // `|x| x` becomes `|x| -> T x` which is invalid, so wrap it in a block |
47 | builder.replace(tail_expr.syntax().text_range(), &format!("{{{}}}", tail_expr)); | 46 | builder.replace(tail_expr.syntax().text_range(), &format!("{{{}}}", tail_expr)); |
48 | } | 47 | } |
@@ -61,13 +60,13 @@ fn ret_ty_to_action(ret_ty: Option<ast::RetType>, insert_pos: TextSize) -> Optio | |||
61 | match ret_ty { | 60 | match ret_ty { |
62 | Some(ret_ty) => match ret_ty.ty() { | 61 | Some(ret_ty) => match ret_ty.ty() { |
63 | Some(ast::Type::InferType(_)) | None => { | 62 | Some(ast::Type::InferType(_)) | None => { |
64 | mark::hit!(existing_infer_ret_type); | 63 | cov_mark::hit!(existing_infer_ret_type); |
65 | mark::hit!(existing_infer_ret_type_closure); | 64 | cov_mark::hit!(existing_infer_ret_type_closure); |
66 | Some(InsertOrReplace::Replace(ret_ty.syntax().text_range())) | 65 | Some(InsertOrReplace::Replace(ret_ty.syntax().text_range())) |
67 | } | 66 | } |
68 | _ => { | 67 | _ => { |
69 | mark::hit!(existing_ret_type); | 68 | cov_mark::hit!(existing_ret_type); |
70 | mark::hit!(existing_ret_type_closure); | 69 | cov_mark::hit!(existing_ret_type_closure); |
71 | None | 70 | None |
72 | } | 71 | } |
73 | }, | 72 | }, |
@@ -109,11 +108,11 @@ fn extract_tail(ctx: &AssistContext) -> Option<(FnType, ast::Expr, InsertOrRepla | |||
109 | }; | 108 | }; |
110 | let frange = ctx.frange.range; | 109 | let frange = ctx.frange.range; |
111 | if return_type_range.contains_range(frange) { | 110 | if return_type_range.contains_range(frange) { |
112 | mark::hit!(cursor_in_ret_position); | 111 | cov_mark::hit!(cursor_in_ret_position); |
113 | mark::hit!(cursor_in_ret_position_closure); | 112 | cov_mark::hit!(cursor_in_ret_position_closure); |
114 | } else if tail_expr.syntax().text_range().contains_range(frange) { | 113 | } else if tail_expr.syntax().text_range().contains_range(frange) { |
115 | mark::hit!(cursor_on_tail); | 114 | cov_mark::hit!(cursor_on_tail); |
116 | mark::hit!(cursor_on_tail_closure); | 115 | cov_mark::hit!(cursor_on_tail_closure); |
117 | } else { | 116 | } else { |
118 | return None; | 117 | return None; |
119 | } | 118 | } |
@@ -128,7 +127,7 @@ mod tests { | |||
128 | 127 | ||
129 | #[test] | 128 | #[test] |
130 | fn infer_return_type_specified_inferred() { | 129 | fn infer_return_type_specified_inferred() { |
131 | mark::check!(existing_infer_ret_type); | 130 | cov_mark::check!(existing_infer_ret_type); |
132 | check_assist( | 131 | check_assist( |
133 | infer_function_return_type, | 132 | infer_function_return_type, |
134 | r#"fn foo() -> $0_ { | 133 | r#"fn foo() -> $0_ { |
@@ -142,7 +141,7 @@ mod tests { | |||
142 | 141 | ||
143 | #[test] | 142 | #[test] |
144 | fn infer_return_type_specified_inferred_closure() { | 143 | fn infer_return_type_specified_inferred_closure() { |
145 | mark::check!(existing_infer_ret_type_closure); | 144 | cov_mark::check!(existing_infer_ret_type_closure); |
146 | check_assist( | 145 | check_assist( |
147 | infer_function_return_type, | 146 | infer_function_return_type, |
148 | r#"fn foo() { | 147 | r#"fn foo() { |
@@ -156,7 +155,7 @@ mod tests { | |||
156 | 155 | ||
157 | #[test] | 156 | #[test] |
158 | fn infer_return_type_cursor_at_return_type_pos() { | 157 | fn infer_return_type_cursor_at_return_type_pos() { |
159 | mark::check!(cursor_in_ret_position); | 158 | cov_mark::check!(cursor_in_ret_position); |
160 | check_assist( | 159 | check_assist( |
161 | infer_function_return_type, | 160 | infer_function_return_type, |
162 | r#"fn foo() $0{ | 161 | r#"fn foo() $0{ |
@@ -170,7 +169,7 @@ mod tests { | |||
170 | 169 | ||
171 | #[test] | 170 | #[test] |
172 | fn infer_return_type_cursor_at_return_type_pos_closure() { | 171 | fn infer_return_type_cursor_at_return_type_pos_closure() { |
173 | mark::check!(cursor_in_ret_position_closure); | 172 | cov_mark::check!(cursor_in_ret_position_closure); |
174 | check_assist( | 173 | check_assist( |
175 | infer_function_return_type, | 174 | infer_function_return_type, |
176 | r#"fn foo() { | 175 | r#"fn foo() { |
@@ -184,7 +183,7 @@ mod tests { | |||
184 | 183 | ||
185 | #[test] | 184 | #[test] |
186 | fn infer_return_type() { | 185 | fn infer_return_type() { |
187 | mark::check!(cursor_on_tail); | 186 | cov_mark::check!(cursor_on_tail); |
188 | check_assist( | 187 | check_assist( |
189 | infer_function_return_type, | 188 | infer_function_return_type, |
190 | r#"fn foo() { | 189 | r#"fn foo() { |
@@ -219,7 +218,7 @@ mod tests { | |||
219 | 218 | ||
220 | #[test] | 219 | #[test] |
221 | fn not_applicable_ret_type_specified() { | 220 | fn not_applicable_ret_type_specified() { |
222 | mark::check!(existing_ret_type); | 221 | cov_mark::check!(existing_ret_type); |
223 | check_assist_not_applicable( | 222 | check_assist_not_applicable( |
224 | infer_function_return_type, | 223 | infer_function_return_type, |
225 | r#"fn foo() -> i32 { | 224 | r#"fn foo() -> i32 { |
@@ -251,7 +250,7 @@ mod tests { | |||
251 | 250 | ||
252 | #[test] | 251 | #[test] |
253 | fn infer_return_type_closure_block() { | 252 | fn infer_return_type_closure_block() { |
254 | mark::check!(cursor_on_tail_closure); | 253 | cov_mark::check!(cursor_on_tail_closure); |
255 | check_assist( | 254 | check_assist( |
256 | infer_function_return_type, | 255 | infer_function_return_type, |
257 | r#"fn foo() { | 256 | r#"fn foo() { |
@@ -282,7 +281,7 @@ mod tests { | |||
282 | 281 | ||
283 | #[test] | 282 | #[test] |
284 | fn infer_return_type_closure_wrap() { | 283 | fn infer_return_type_closure_wrap() { |
285 | mark::check!(wrap_closure_non_block_expr); | 284 | cov_mark::check!(wrap_closure_non_block_expr); |
286 | check_assist( | 285 | check_assist( |
287 | infer_function_return_type, | 286 | infer_function_return_type, |
288 | r#"fn foo() { | 287 | r#"fn foo() { |
@@ -321,7 +320,7 @@ mod tests { | |||
321 | 320 | ||
322 | #[test] | 321 | #[test] |
323 | fn not_applicable_ret_type_specified_closure() { | 322 | fn not_applicable_ret_type_specified_closure() { |
324 | mark::check!(existing_ret_type_closure); | 323 | cov_mark::check!(existing_ret_type_closure); |
325 | check_assist_not_applicable( | 324 | check_assist_not_applicable( |
326 | infer_function_return_type, | 325 | infer_function_return_type, |
327 | r#"fn foo() { | 326 | r#"fn foo() { |
diff --git a/crates/ide_assists/src/handlers/inline_function.rs b/crates/ide_assists/src/handlers/inline_function.rs index 6ec99b09b..8e56029cb 100644 --- a/crates/ide_assists/src/handlers/inline_function.rs +++ b/crates/ide_assists/src/handlers/inline_function.rs | |||
@@ -4,7 +4,6 @@ use syntax::{ | |||
4 | ast::{self, edit::AstNodeEdit, ArgListOwner}, | 4 | ast::{self, edit::AstNodeEdit, ArgListOwner}, |
5 | AstNode, | 5 | AstNode, |
6 | }; | 6 | }; |
7 | use test_utils::mark; | ||
8 | 7 | ||
9 | use crate::{ | 8 | use crate::{ |
10 | assist_context::{AssistContext, Assists}, | 9 | assist_context::{AssistContext, Assists}, |
@@ -49,7 +48,7 @@ pub(crate) fn inline_function(acc: &mut Assists, ctx: &AssistContext) -> Option< | |||
49 | if arguments.len() != parameters.len() { | 48 | if arguments.len() != parameters.len() { |
50 | // Can't inline the function because they've passed the wrong number of | 49 | // Can't inline the function because they've passed the wrong number of |
51 | // arguments to this function | 50 | // arguments to this function |
52 | mark::hit!(inline_function_incorrect_number_of_arguments); | 51 | cov_mark::hit!(inline_function_incorrect_number_of_arguments); |
53 | return None; | 52 | return None; |
54 | } | 53 | } |
55 | 54 | ||
@@ -155,7 +154,7 @@ fn main() { Foo.bar$0(); } | |||
155 | 154 | ||
156 | #[test] | 155 | #[test] |
157 | fn not_applicable_when_incorrect_number_of_parameters_are_provided() { | 156 | fn not_applicable_when_incorrect_number_of_parameters_are_provided() { |
158 | mark::check!(inline_function_incorrect_number_of_arguments); | 157 | cov_mark::check!(inline_function_incorrect_number_of_arguments); |
159 | check_assist_not_applicable( | 158 | check_assist_not_applicable( |
160 | inline_function, | 159 | inline_function, |
161 | r#" | 160 | r#" |
diff --git a/crates/ide_assists/src/handlers/inline_local_variable.rs b/crates/ide_assists/src/handlers/inline_local_variable.rs index da5522670..ea1466dc8 100644 --- a/crates/ide_assists/src/handlers/inline_local_variable.rs +++ b/crates/ide_assists/src/handlers/inline_local_variable.rs | |||
@@ -4,7 +4,6 @@ use syntax::{ | |||
4 | ast::{self, AstNode, AstToken}, | 4 | ast::{self, AstNode, AstToken}, |
5 | TextRange, | 5 | TextRange, |
6 | }; | 6 | }; |
7 | use test_utils::mark; | ||
8 | 7 | ||
9 | use crate::{ | 8 | use crate::{ |
10 | assist_context::{AssistContext, Assists}, | 9 | assist_context::{AssistContext, Assists}, |
@@ -34,11 +33,11 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> O | |||
34 | _ => return None, | 33 | _ => return None, |
35 | }; | 34 | }; |
36 | if bind_pat.mut_token().is_some() { | 35 | if bind_pat.mut_token().is_some() { |
37 | mark::hit!(test_not_inline_mut_variable); | 36 | cov_mark::hit!(test_not_inline_mut_variable); |
38 | return None; | 37 | return None; |
39 | } | 38 | } |
40 | if !bind_pat.syntax().text_range().contains_inclusive(ctx.offset()) { | 39 | if !bind_pat.syntax().text_range().contains_inclusive(ctx.offset()) { |
41 | mark::hit!(not_applicable_outside_of_bind_pat); | 40 | cov_mark::hit!(not_applicable_outside_of_bind_pat); |
42 | return None; | 41 | return None; |
43 | } | 42 | } |
44 | let initializer_expr = let_stmt.initializer()?; | 43 | let initializer_expr = let_stmt.initializer()?; |
@@ -47,7 +46,7 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> O | |||
47 | let def = Definition::Local(def); | 46 | let def = Definition::Local(def); |
48 | let usages = def.usages(&ctx.sema).all(); | 47 | let usages = def.usages(&ctx.sema).all(); |
49 | if usages.is_empty() { | 48 | if usages.is_empty() { |
50 | mark::hit!(test_not_applicable_if_variable_unused); | 49 | cov_mark::hit!(test_not_applicable_if_variable_unused); |
51 | return None; | 50 | return None; |
52 | }; | 51 | }; |
53 | 52 | ||
@@ -130,7 +129,7 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> O | |||
130 | Some(name_ref) | 129 | Some(name_ref) |
131 | if ast::RecordExprField::for_field_name(name_ref).is_some() => | 130 | if ast::RecordExprField::for_field_name(name_ref).is_some() => |
132 | { | 131 | { |
133 | mark::hit!(inline_field_shorthand); | 132 | cov_mark::hit!(inline_field_shorthand); |
134 | builder.insert(reference.range.end(), format!(": {}", replacement)); | 133 | builder.insert(reference.range.end(), format!(": {}", replacement)); |
135 | } | 134 | } |
136 | _ => builder.replace(reference.range, replacement), | 135 | _ => builder.replace(reference.range, replacement), |
@@ -143,8 +142,6 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> O | |||
143 | 142 | ||
144 | #[cfg(test)] | 143 | #[cfg(test)] |
145 | mod tests { | 144 | mod tests { |
146 | use test_utils::mark; | ||
147 | |||
148 | use crate::tests::{check_assist, check_assist_not_applicable}; | 145 | use crate::tests::{check_assist, check_assist_not_applicable}; |
149 | 146 | ||
150 | use super::*; | 147 | use super::*; |
@@ -351,7 +348,7 @@ fn foo() { | |||
351 | 348 | ||
352 | #[test] | 349 | #[test] |
353 | fn test_not_inline_mut_variable() { | 350 | fn test_not_inline_mut_variable() { |
354 | mark::check!(test_not_inline_mut_variable); | 351 | cov_mark::check!(test_not_inline_mut_variable); |
355 | check_assist_not_applicable( | 352 | check_assist_not_applicable( |
356 | inline_local_variable, | 353 | inline_local_variable, |
357 | r" | 354 | r" |
@@ -684,7 +681,7 @@ fn foo() { | |||
684 | 681 | ||
685 | #[test] | 682 | #[test] |
686 | fn inline_field_shorthand() { | 683 | fn inline_field_shorthand() { |
687 | mark::check!(inline_field_shorthand); | 684 | cov_mark::check!(inline_field_shorthand); |
688 | check_assist( | 685 | check_assist( |
689 | inline_local_variable, | 686 | inline_local_variable, |
690 | r" | 687 | r" |
@@ -705,7 +702,7 @@ fn main() { | |||
705 | 702 | ||
706 | #[test] | 703 | #[test] |
707 | fn test_not_applicable_if_variable_unused() { | 704 | fn test_not_applicable_if_variable_unused() { |
708 | mark::check!(test_not_applicable_if_variable_unused); | 705 | cov_mark::check!(test_not_applicable_if_variable_unused); |
709 | check_assist_not_applicable( | 706 | check_assist_not_applicable( |
710 | inline_local_variable, | 707 | inline_local_variable, |
711 | r" | 708 | r" |
@@ -718,7 +715,7 @@ fn foo() { | |||
718 | 715 | ||
719 | #[test] | 716 | #[test] |
720 | fn not_applicable_outside_of_bind_pat() { | 717 | fn not_applicable_outside_of_bind_pat() { |
721 | mark::check!(not_applicable_outside_of_bind_pat); | 718 | cov_mark::check!(not_applicable_outside_of_bind_pat); |
722 | check_assist_not_applicable( | 719 | check_assist_not_applicable( |
723 | inline_local_variable, | 720 | inline_local_variable, |
724 | r" | 721 | r" |
diff --git a/crates/ide_assists/src/handlers/move_module_to_file.rs b/crates/ide_assists/src/handlers/move_module_to_file.rs index 91c395c1b..6e685b4b2 100644 --- a/crates/ide_assists/src/handlers/move_module_to_file.rs +++ b/crates/ide_assists/src/handlers/move_module_to_file.rs | |||
@@ -5,7 +5,6 @@ use syntax::{ | |||
5 | ast::{self, edit::AstNodeEdit, NameOwner}, | 5 | ast::{self, edit::AstNodeEdit, NameOwner}, |
6 | AstNode, TextRange, | 6 | AstNode, TextRange, |
7 | }; | 7 | }; |
8 | use test_utils::mark; | ||
9 | 8 | ||
10 | use crate::{AssistContext, AssistId, AssistKind, Assists}; | 9 | use crate::{AssistContext, AssistId, AssistKind, Assists}; |
11 | 10 | ||
@@ -28,7 +27,7 @@ pub(crate) fn move_module_to_file(acc: &mut Assists, ctx: &AssistContext) -> Opt | |||
28 | 27 | ||
29 | let l_curly_offset = module_items.syntax().text_range().start(); | 28 | let l_curly_offset = module_items.syntax().text_range().start(); |
30 | if l_curly_offset <= ctx.offset() { | 29 | if l_curly_offset <= ctx.offset() { |
31 | mark::hit!(available_before_curly); | 30 | cov_mark::hit!(available_before_curly); |
32 | return None; | 31 | return None; |
33 | } | 32 | } |
34 | let target = TextRange::new(module_ast.syntax().text_range().start(), l_curly_offset); | 33 | let target = TextRange::new(module_ast.syntax().text_range().start(), l_curly_offset); |
@@ -182,7 +181,7 @@ pub(crate) mod tests; | |||
182 | 181 | ||
183 | #[test] | 182 | #[test] |
184 | fn available_before_curly() { | 183 | fn available_before_curly() { |
185 | mark::check!(available_before_curly); | 184 | cov_mark::check!(available_before_curly); |
186 | check_assist_not_applicable(move_module_to_file, r#"mod m { $0 }"#); | 185 | check_assist_not_applicable(move_module_to_file, r#"mod m { $0 }"#); |
187 | } | 186 | } |
188 | } | 187 | } |
diff --git a/crates/ide_assists/src/handlers/pull_assignment_up.rs b/crates/ide_assists/src/handlers/pull_assignment_up.rs index 377ed4f2f..04bae4e58 100644 --- a/crates/ide_assists/src/handlers/pull_assignment_up.rs +++ b/crates/ide_assists/src/handlers/pull_assignment_up.rs | |||
@@ -2,7 +2,6 @@ use syntax::{ | |||
2 | ast::{self, edit::AstNodeEdit, make}, | 2 | ast::{self, edit::AstNodeEdit, make}, |
3 | AstNode, | 3 | AstNode, |
4 | }; | 4 | }; |
5 | use test_utils::mark; | ||
6 | 5 | ||
7 | use crate::{ | 6 | use crate::{ |
8 | assist_context::{AssistContext, Assists}, | 7 | assist_context::{AssistContext, Assists}, |
@@ -104,7 +103,7 @@ fn exprify_if( | |||
104 | ast::ElseBranch::Block(exprify_block(block, sema, name)?) | 103 | ast::ElseBranch::Block(exprify_block(block, sema, name)?) |
105 | } | 104 | } |
106 | ast::ElseBranch::IfExpr(expr) => { | 105 | ast::ElseBranch::IfExpr(expr) => { |
107 | mark::hit!(test_pull_assignment_up_chained_if); | 106 | cov_mark::hit!(test_pull_assignment_up_chained_if); |
108 | ast::ElseBranch::IfExpr(ast::IfExpr::cast( | 107 | ast::ElseBranch::IfExpr(ast::IfExpr::cast( |
109 | exprify_if(&expr, sema, name)?.syntax().to_owned(), | 108 | exprify_if(&expr, sema, name)?.syntax().to_owned(), |
110 | )?) | 109 | )?) |
@@ -144,7 +143,7 @@ fn is_equivalent( | |||
144 | ) -> bool { | 143 | ) -> bool { |
145 | match (expr0, expr1) { | 144 | match (expr0, expr1) { |
146 | (ast::Expr::FieldExpr(field_expr0), ast::Expr::FieldExpr(field_expr1)) => { | 145 | (ast::Expr::FieldExpr(field_expr0), ast::Expr::FieldExpr(field_expr1)) => { |
147 | mark::hit!(test_pull_assignment_up_field_assignment); | 146 | cov_mark::hit!(test_pull_assignment_up_field_assignment); |
148 | sema.resolve_field(field_expr0) == sema.resolve_field(field_expr1) | 147 | sema.resolve_field(field_expr0) == sema.resolve_field(field_expr1) |
149 | } | 148 | } |
150 | (ast::Expr::PathExpr(path0), ast::Expr::PathExpr(path1)) => { | 149 | (ast::Expr::PathExpr(path0), ast::Expr::PathExpr(path1)) => { |
@@ -160,7 +159,7 @@ fn is_equivalent( | |||
160 | if prefix0.op_kind() == Some(ast::PrefixOp::Deref) | 159 | if prefix0.op_kind() == Some(ast::PrefixOp::Deref) |
161 | && prefix1.op_kind() == Some(ast::PrefixOp::Deref) => | 160 | && prefix1.op_kind() == Some(ast::PrefixOp::Deref) => |
162 | { | 161 | { |
163 | mark::hit!(test_pull_assignment_up_deref); | 162 | cov_mark::hit!(test_pull_assignment_up_deref); |
164 | if let (Some(prefix0), Some(prefix1)) = (prefix0.expr(), prefix1.expr()) { | 163 | if let (Some(prefix0), Some(prefix1)) = (prefix0.expr(), prefix1.expr()) { |
165 | is_equivalent(sema, &prefix0, &prefix1) | 164 | is_equivalent(sema, &prefix0, &prefix1) |
166 | } else { | 165 | } else { |
@@ -263,7 +262,7 @@ fn foo() { | |||
263 | 262 | ||
264 | #[test] | 263 | #[test] |
265 | fn test_pull_assignment_up_chained_if() { | 264 | fn test_pull_assignment_up_chained_if() { |
266 | mark::check!(test_pull_assignment_up_chained_if); | 265 | cov_mark::check!(test_pull_assignment_up_chained_if); |
267 | check_assist( | 266 | check_assist( |
268 | pull_assignment_up, | 267 | pull_assignment_up, |
269 | r#" | 268 | r#" |
@@ -379,7 +378,7 @@ fn foo() { | |||
379 | 378 | ||
380 | #[test] | 379 | #[test] |
381 | fn test_pull_assignment_up_field_assignment() { | 380 | fn test_pull_assignment_up_field_assignment() { |
382 | mark::check!(test_pull_assignment_up_field_assignment); | 381 | cov_mark::check!(test_pull_assignment_up_field_assignment); |
383 | check_assist( | 382 | check_assist( |
384 | pull_assignment_up, | 383 | pull_assignment_up, |
385 | r#" | 384 | r#" |
@@ -411,7 +410,7 @@ fn foo() { | |||
411 | 410 | ||
412 | #[test] | 411 | #[test] |
413 | fn test_pull_assignment_up_deref() { | 412 | fn test_pull_assignment_up_deref() { |
414 | mark::check!(test_pull_assignment_up_deref); | 413 | cov_mark::check!(test_pull_assignment_up_deref); |
415 | check_assist( | 414 | check_assist( |
416 | pull_assignment_up, | 415 | pull_assignment_up, |
417 | r#" | 416 | r#" |
diff --git a/crates/ide_assists/src/handlers/qualify_path.rs b/crates/ide_assists/src/handlers/qualify_path.rs index b0b0d31b4..30b23da6c 100644 --- a/crates/ide_assists/src/handlers/qualify_path.rs +++ b/crates/ide_assists/src/handlers/qualify_path.rs | |||
@@ -1,14 +1,16 @@ | |||
1 | use std::iter; | 1 | use std::iter; |
2 | 2 | ||
3 | use hir::{AsAssocItem, AsName}; | 3 | use hir::AsAssocItem; |
4 | use ide_db::helpers::{import_assets::ImportCandidate, mod_path_to_ast}; | 4 | use ide_db::helpers::{ |
5 | import_assets::{ImportCandidate, LocatedImport}, | ||
6 | mod_path_to_ast, | ||
7 | }; | ||
5 | use ide_db::RootDatabase; | 8 | use ide_db::RootDatabase; |
6 | use syntax::{ | 9 | use syntax::{ |
7 | ast, | 10 | ast, |
8 | ast::{make, ArgListOwner}, | 11 | ast::{make, ArgListOwner}, |
9 | AstNode, | 12 | AstNode, |
10 | }; | 13 | }; |
11 | use test_utils::mark; | ||
12 | 14 | ||
13 | use crate::{ | 15 | use crate::{ |
14 | assist_context::{AssistContext, Assists}, | 16 | assist_context::{AssistContext, Assists}, |
@@ -47,32 +49,32 @@ pub(crate) fn qualify_path(acc: &mut Assists, ctx: &AssistContext) -> Option<()> | |||
47 | let qualify_candidate = match candidate { | 49 | let qualify_candidate = match candidate { |
48 | ImportCandidate::Path(candidate) => { | 50 | ImportCandidate::Path(candidate) => { |
49 | if candidate.qualifier.is_some() { | 51 | if candidate.qualifier.is_some() { |
50 | mark::hit!(qualify_path_qualifier_start); | 52 | cov_mark::hit!(qualify_path_qualifier_start); |
51 | let path = ast::Path::cast(syntax_under_caret)?; | 53 | let path = ast::Path::cast(syntax_under_caret)?; |
52 | let (prev_segment, segment) = (path.qualifier()?.segment()?, path.segment()?); | 54 | let (prev_segment, segment) = (path.qualifier()?.segment()?, path.segment()?); |
53 | QualifyCandidate::QualifierStart(segment, prev_segment.generic_arg_list()) | 55 | QualifyCandidate::QualifierStart(segment, prev_segment.generic_arg_list()) |
54 | } else { | 56 | } else { |
55 | mark::hit!(qualify_path_unqualified_name); | 57 | cov_mark::hit!(qualify_path_unqualified_name); |
56 | let path = ast::Path::cast(syntax_under_caret)?; | 58 | let path = ast::Path::cast(syntax_under_caret)?; |
57 | let generics = path.segment()?.generic_arg_list(); | 59 | let generics = path.segment()?.generic_arg_list(); |
58 | QualifyCandidate::UnqualifiedName(generics) | 60 | QualifyCandidate::UnqualifiedName(generics) |
59 | } | 61 | } |
60 | } | 62 | } |
61 | ImportCandidate::TraitAssocItem(_) => { | 63 | ImportCandidate::TraitAssocItem(_) => { |
62 | mark::hit!(qualify_path_trait_assoc_item); | 64 | cov_mark::hit!(qualify_path_trait_assoc_item); |
63 | let path = ast::Path::cast(syntax_under_caret)?; | 65 | let path = ast::Path::cast(syntax_under_caret)?; |
64 | let (qualifier, segment) = (path.qualifier()?, path.segment()?); | 66 | let (qualifier, segment) = (path.qualifier()?, path.segment()?); |
65 | QualifyCandidate::TraitAssocItem(qualifier, segment) | 67 | QualifyCandidate::TraitAssocItem(qualifier, segment) |
66 | } | 68 | } |
67 | ImportCandidate::TraitMethod(_) => { | 69 | ImportCandidate::TraitMethod(_) => { |
68 | mark::hit!(qualify_path_trait_method); | 70 | cov_mark::hit!(qualify_path_trait_method); |
69 | let mcall_expr = ast::MethodCallExpr::cast(syntax_under_caret)?; | 71 | let mcall_expr = ast::MethodCallExpr::cast(syntax_under_caret)?; |
70 | QualifyCandidate::TraitMethod(ctx.sema.db, mcall_expr) | 72 | QualifyCandidate::TraitMethod(ctx.sema.db, mcall_expr) |
71 | } | 73 | } |
72 | }; | 74 | }; |
73 | 75 | ||
74 | let group_label = group_label(candidate); | 76 | let group_label = group_label(candidate); |
75 | for (import, item) in proposed_imports { | 77 | for import in proposed_imports { |
76 | acc.add_group( | 78 | acc.add_group( |
77 | &group_label, | 79 | &group_label, |
78 | AssistId("qualify_path", AssistKind::QuickFix), | 80 | AssistId("qualify_path", AssistKind::QuickFix), |
@@ -81,8 +83,8 @@ pub(crate) fn qualify_path(acc: &mut Assists, ctx: &AssistContext) -> Option<()> | |||
81 | |builder| { | 83 | |builder| { |
82 | qualify_candidate.qualify( | 84 | qualify_candidate.qualify( |
83 | |replace_with: String| builder.replace(range, replace_with), | 85 | |replace_with: String| builder.replace(range, replace_with), |
84 | import, | 86 | &import.import_path, |
85 | item, | 87 | import.item_to_import, |
86 | ) | 88 | ) |
87 | }, | 89 | }, |
88 | ); | 90 | ); |
@@ -98,8 +100,13 @@ enum QualifyCandidate<'db> { | |||
98 | } | 100 | } |
99 | 101 | ||
100 | impl QualifyCandidate<'_> { | 102 | impl QualifyCandidate<'_> { |
101 | fn qualify(&self, mut replacer: impl FnMut(String), import: hir::ModPath, item: hir::ItemInNs) { | 103 | fn qualify( |
102 | let import = mod_path_to_ast(&import); | 104 | &self, |
105 | mut replacer: impl FnMut(String), | ||
106 | import: &hir::ModPath, | ||
107 | item: hir::ItemInNs, | ||
108 | ) { | ||
109 | let import = mod_path_to_ast(import); | ||
103 | match self { | 110 | match self { |
104 | QualifyCandidate::QualifierStart(segment, generics) => { | 111 | QualifyCandidate::QualifierStart(segment, generics) => { |
105 | let generics = generics.as_ref().map_or_else(String::new, ToString::to_string); | 112 | let generics = generics.as_ref().map_or_else(String::new, ToString::to_string); |
@@ -160,7 +167,9 @@ fn find_trait_method( | |||
160 | ) -> Option<hir::Function> { | 167 | ) -> Option<hir::Function> { |
161 | if let Some(hir::AssocItem::Function(method)) = | 168 | if let Some(hir::AssocItem::Function(method)) = |
162 | trait_.items(db).into_iter().find(|item: &hir::AssocItem| { | 169 | trait_.items(db).into_iter().find(|item: &hir::AssocItem| { |
163 | item.name(db).map(|name| name == trait_method_name.as_name()).unwrap_or(false) | 170 | item.name(db) |
171 | .map(|name| name.to_string() == trait_method_name.to_string()) | ||
172 | .unwrap_or(false) | ||
164 | }) | 173 | }) |
165 | { | 174 | { |
166 | Some(method) | 175 | Some(method) |
@@ -182,23 +191,25 @@ fn item_as_trait(db: &RootDatabase, item: hir::ItemInNs) -> Option<hir::Trait> { | |||
182 | fn group_label(candidate: &ImportCandidate) -> GroupLabel { | 191 | fn group_label(candidate: &ImportCandidate) -> GroupLabel { |
183 | let name = match candidate { | 192 | let name = match candidate { |
184 | ImportCandidate::Path(it) => &it.name, | 193 | ImportCandidate::Path(it) => &it.name, |
185 | ImportCandidate::TraitAssocItem(it) | ImportCandidate::TraitMethod(it) => &it.name, | 194 | ImportCandidate::TraitAssocItem(it) | ImportCandidate::TraitMethod(it) => { |
195 | &it.assoc_item_name | ||
196 | } | ||
186 | } | 197 | } |
187 | .text(); | 198 | .text(); |
188 | GroupLabel(format!("Qualify {}", name)) | 199 | GroupLabel(format!("Qualify {}", name)) |
189 | } | 200 | } |
190 | 201 | ||
191 | fn label(candidate: &ImportCandidate, import: &hir::ModPath) -> String { | 202 | fn label(candidate: &ImportCandidate, import: &LocatedImport) -> String { |
192 | match candidate { | 203 | match candidate { |
193 | ImportCandidate::Path(candidate) => { | 204 | ImportCandidate::Path(candidate) => { |
194 | if candidate.qualifier.is_some() { | 205 | if candidate.qualifier.is_some() { |
195 | format!("Qualify with `{}`", &import) | 206 | format!("Qualify with `{}`", import.import_path) |
196 | } else { | 207 | } else { |
197 | format!("Qualify as `{}`", &import) | 208 | format!("Qualify as `{}`", import.import_path) |
198 | } | 209 | } |
199 | } | 210 | } |
200 | ImportCandidate::TraitAssocItem(_) => format!("Qualify `{}`", &import), | 211 | ImportCandidate::TraitAssocItem(_) => format!("Qualify `{}`", import.import_path), |
201 | ImportCandidate::TraitMethod(_) => format!("Qualify with cast as `{}`", &import), | 212 | ImportCandidate::TraitMethod(_) => format!("Qualify with cast as `{}`", import.import_path), |
202 | } | 213 | } |
203 | } | 214 | } |
204 | 215 | ||
@@ -210,7 +221,7 @@ mod tests { | |||
210 | 221 | ||
211 | #[test] | 222 | #[test] |
212 | fn applicable_when_found_an_import_partial() { | 223 | fn applicable_when_found_an_import_partial() { |
213 | mark::check!(qualify_path_unqualified_name); | 224 | cov_mark::check!(qualify_path_unqualified_name); |
214 | check_assist( | 225 | check_assist( |
215 | qualify_path, | 226 | qualify_path, |
216 | r" | 227 | r" |
@@ -502,7 +513,7 @@ fn main() { | |||
502 | 513 | ||
503 | #[test] | 514 | #[test] |
504 | fn associated_struct_const() { | 515 | fn associated_struct_const() { |
505 | mark::check!(qualify_path_qualifier_start); | 516 | cov_mark::check!(qualify_path_qualifier_start); |
506 | check_assist( | 517 | check_assist( |
507 | qualify_path, | 518 | qualify_path, |
508 | r" | 519 | r" |
@@ -603,7 +614,7 @@ fn main() { | |||
603 | 614 | ||
604 | #[test] | 615 | #[test] |
605 | fn associated_trait_const() { | 616 | fn associated_trait_const() { |
606 | mark::check!(qualify_path_trait_assoc_item); | 617 | cov_mark::check!(qualify_path_trait_assoc_item); |
607 | check_assist( | 618 | check_assist( |
608 | qualify_path, | 619 | qualify_path, |
609 | r" | 620 | r" |
@@ -673,7 +684,7 @@ fn main() { | |||
673 | 684 | ||
674 | #[test] | 685 | #[test] |
675 | fn trait_method() { | 686 | fn trait_method() { |
676 | mark::check!(qualify_path_trait_method); | 687 | cov_mark::check!(qualify_path_trait_method); |
677 | check_assist( | 688 | check_assist( |
678 | qualify_path, | 689 | qualify_path, |
679 | r" | 690 | r" |
diff --git a/crates/ide_assists/src/handlers/raw_string.rs b/crates/ide_assists/src/handlers/raw_string.rs index d95267607..d0f1613f3 100644 --- a/crates/ide_assists/src/handlers/raw_string.rs +++ b/crates/ide_assists/src/handlers/raw_string.rs | |||
@@ -1,7 +1,6 @@ | |||
1 | use std::borrow::Cow; | 1 | use std::borrow::Cow; |
2 | 2 | ||
3 | use syntax::{ast, AstToken, TextRange, TextSize}; | 3 | use syntax::{ast, AstToken, TextRange, TextSize}; |
4 | use test_utils::mark; | ||
5 | 4 | ||
6 | use crate::{AssistContext, AssistId, AssistKind, Assists}; | 5 | use crate::{AssistContext, AssistId, AssistKind, Assists}; |
7 | 6 | ||
@@ -149,7 +148,7 @@ pub(crate) fn remove_hash(acc: &mut Assists, ctx: &AssistContext) -> Option<()> | |||
149 | let internal_text = &text[token.text_range_between_quotes()? - text_range.start()]; | 148 | let internal_text = &text[token.text_range_between_quotes()? - text_range.start()]; |
150 | 149 | ||
151 | if existing_hashes == required_hashes(internal_text) { | 150 | if existing_hashes == required_hashes(internal_text) { |
152 | mark::hit!(cant_remove_required_hash); | 151 | cov_mark::hit!(cant_remove_required_hash); |
153 | return None; | 152 | return None; |
154 | } | 153 | } |
155 | 154 | ||
@@ -182,8 +181,6 @@ fn test_required_hashes() { | |||
182 | 181 | ||
183 | #[cfg(test)] | 182 | #[cfg(test)] |
184 | mod tests { | 183 | mod tests { |
185 | use test_utils::mark; | ||
186 | |||
187 | use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target}; | 184 | use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target}; |
188 | 185 | ||
189 | use super::*; | 186 | use super::*; |
@@ -396,7 +393,7 @@ string"###; | |||
396 | 393 | ||
397 | #[test] | 394 | #[test] |
398 | fn cant_remove_required_hash() { | 395 | fn cant_remove_required_hash() { |
399 | mark::check!(cant_remove_required_hash); | 396 | cov_mark::check!(cant_remove_required_hash); |
400 | check_assist_not_applicable( | 397 | check_assist_not_applicable( |
401 | remove_hash, | 398 | remove_hash, |
402 | r##" | 399 | r##" |
diff --git a/crates/ide_assists/src/handlers/remove_unused_param.rs b/crates/ide_assists/src/handlers/remove_unused_param.rs index c961680e2..2699d2861 100644 --- a/crates/ide_assists/src/handlers/remove_unused_param.rs +++ b/crates/ide_assists/src/handlers/remove_unused_param.rs | |||
@@ -4,7 +4,7 @@ use syntax::{ | |||
4 | ast::{self, ArgListOwner}, | 4 | ast::{self, ArgListOwner}, |
5 | AstNode, SourceFile, SyntaxKind, SyntaxNode, TextRange, T, | 5 | AstNode, SourceFile, SyntaxKind, SyntaxNode, TextRange, T, |
6 | }; | 6 | }; |
7 | use test_utils::mark; | 7 | |
8 | use SyntaxKind::WHITESPACE; | 8 | use SyntaxKind::WHITESPACE; |
9 | 9 | ||
10 | use crate::{ | 10 | use crate::{ |
@@ -49,7 +49,7 @@ pub(crate) fn remove_unused_param(acc: &mut Assists, ctx: &AssistContext) -> Opt | |||
49 | Definition::Local(local) | 49 | Definition::Local(local) |
50 | }; | 50 | }; |
51 | if param_def.usages(&ctx.sema).at_least_one() { | 51 | if param_def.usages(&ctx.sema).at_least_one() { |
52 | mark::hit!(keep_used); | 52 | cov_mark::hit!(keep_used); |
53 | return None; | 53 | return None; |
54 | } | 54 | } |
55 | acc.add( | 55 | acc.add( |
@@ -243,7 +243,7 @@ fn b2() { foo(9) } | |||
243 | 243 | ||
244 | #[test] | 244 | #[test] |
245 | fn keep_used() { | 245 | fn keep_used() { |
246 | mark::check!(keep_used); | 246 | cov_mark::check!(keep_used); |
247 | check_assist_not_applicable( | 247 | check_assist_not_applicable( |
248 | remove_unused_param, | 248 | remove_unused_param, |
249 | r#" | 249 | r#" |
diff --git a/crates/ide_assists/src/handlers/reorder_fields.rs b/crates/ide_assists/src/handlers/reorder_fields.rs index fba7d6ddb..794c89323 100644 --- a/crates/ide_assists/src/handlers/reorder_fields.rs +++ b/crates/ide_assists/src/handlers/reorder_fields.rs | |||
@@ -4,7 +4,6 @@ use rustc_hash::FxHashMap; | |||
4 | use hir::{Adt, ModuleDef, PathResolution, Semantics, Struct}; | 4 | use hir::{Adt, ModuleDef, PathResolution, Semantics, Struct}; |
5 | use ide_db::RootDatabase; | 5 | use ide_db::RootDatabase; |
6 | use syntax::{algo, ast, match_ast, AstNode, SyntaxKind, SyntaxKind::*, SyntaxNode}; | 6 | use syntax::{algo, ast, match_ast, AstNode, SyntaxKind, SyntaxKind::*, SyntaxNode}; |
7 | use test_utils::mark; | ||
8 | 7 | ||
9 | use crate::{AssistContext, AssistId, AssistKind, Assists}; | 8 | use crate::{AssistContext, AssistId, AssistKind, Assists}; |
10 | 9 | ||
@@ -39,7 +38,7 @@ fn reorder<R: AstNode>(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | |||
39 | }); | 38 | }); |
40 | 39 | ||
41 | if sorted_fields == fields { | 40 | if sorted_fields == fields { |
42 | mark::hit!(reorder_sorted_fields); | 41 | cov_mark::hit!(reorder_sorted_fields); |
43 | return None; | 42 | return None; |
44 | } | 43 | } |
45 | 44 | ||
@@ -109,15 +108,13 @@ fn compute_fields_ranks(path: &ast::Path, ctx: &AssistContext) -> Option<FxHashM | |||
109 | 108 | ||
110 | #[cfg(test)] | 109 | #[cfg(test)] |
111 | mod tests { | 110 | mod tests { |
112 | use test_utils::mark; | ||
113 | |||
114 | use crate::tests::{check_assist, check_assist_not_applicable}; | 111 | use crate::tests::{check_assist, check_assist_not_applicable}; |
115 | 112 | ||
116 | use super::*; | 113 | use super::*; |
117 | 114 | ||
118 | #[test] | 115 | #[test] |
119 | fn reorder_sorted_fields() { | 116 | fn reorder_sorted_fields() { |
120 | mark::check!(reorder_sorted_fields); | 117 | cov_mark::check!(reorder_sorted_fields); |
121 | check_assist_not_applicable( | 118 | check_assist_not_applicable( |
122 | reorder_fields, | 119 | reorder_fields, |
123 | r#" | 120 | r#" |
diff --git a/crates/ide_assists/src/handlers/reorder_impl.rs b/crates/ide_assists/src/handlers/reorder_impl.rs index 309f291c8..edf4b0bfe 100644 --- a/crates/ide_assists/src/handlers/reorder_impl.rs +++ b/crates/ide_assists/src/handlers/reorder_impl.rs | |||
@@ -8,7 +8,6 @@ use syntax::{ | |||
8 | ast::{self, NameOwner}, | 8 | ast::{self, NameOwner}, |
9 | AstNode, | 9 | AstNode, |
10 | }; | 10 | }; |
11 | use test_utils::mark; | ||
12 | 11 | ||
13 | use crate::{AssistContext, AssistId, AssistKind, Assists}; | 12 | use crate::{AssistContext, AssistId, AssistKind, Assists}; |
14 | 13 | ||
@@ -71,7 +70,7 @@ pub(crate) fn reorder_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<()> | |||
71 | 70 | ||
72 | // Don't edit already sorted methods: | 71 | // Don't edit already sorted methods: |
73 | if methods == sorted { | 72 | if methods == sorted { |
74 | mark::hit!(not_applicable_if_sorted); | 73 | cov_mark::hit!(not_applicable_if_sorted); |
75 | return None; | 74 | return None; |
76 | } | 75 | } |
77 | 76 | ||
@@ -121,15 +120,13 @@ fn get_methods(items: &ast::AssocItemList) -> Vec<ast::Fn> { | |||
121 | 120 | ||
122 | #[cfg(test)] | 121 | #[cfg(test)] |
123 | mod tests { | 122 | mod tests { |
124 | use test_utils::mark; | ||
125 | |||
126 | use crate::tests::{check_assist, check_assist_not_applicable}; | 123 | use crate::tests::{check_assist, check_assist_not_applicable}; |
127 | 124 | ||
128 | use super::*; | 125 | use super::*; |
129 | 126 | ||
130 | #[test] | 127 | #[test] |
131 | fn not_applicable_if_sorted() { | 128 | fn not_applicable_if_sorted() { |
132 | mark::check!(not_applicable_if_sorted); | 129 | cov_mark::check!(not_applicable_if_sorted); |
133 | check_assist_not_applicable( | 130 | check_assist_not_applicable( |
134 | reorder_impl, | 131 | reorder_impl, |
135 | r#" | 132 | r#" |
diff --git a/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs b/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs index c69bc5cac..88fe2fe90 100644 --- a/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs +++ b/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs | |||
@@ -1,5 +1,6 @@ | |||
1 | use hir::ModuleDef; | ||
1 | use ide_db::helpers::mod_path_to_ast; | 2 | use ide_db::helpers::mod_path_to_ast; |
2 | use ide_db::imports_locator; | 3 | use ide_db::items_locator; |
3 | use itertools::Itertools; | 4 | use itertools::Itertools; |
4 | use syntax::{ | 5 | use syntax::{ |
5 | ast::{self, make, AstNode, NameOwner}, | 6 | ast::{self, make, AstNode, NameOwner}, |
@@ -64,22 +65,20 @@ pub(crate) fn replace_derive_with_manual_impl( | |||
64 | let current_module = ctx.sema.scope(annotated_name.syntax()).module()?; | 65 | let current_module = ctx.sema.scope(annotated_name.syntax()).module()?; |
65 | let current_crate = current_module.krate(); | 66 | let current_crate = current_module.krate(); |
66 | 67 | ||
67 | let found_traits = imports_locator::find_exact_imports( | 68 | let found_traits = |
68 | &ctx.sema, | 69 | items_locator::with_exact_name(&ctx.sema, current_crate, trait_token.text().to_string()) |
69 | current_crate, | 70 | .into_iter() |
70 | trait_token.text().to_string(), | 71 | .filter_map(|item| match ModuleDef::from(item.as_module_def_id()?) { |
71 | ) | 72 | ModuleDef::Trait(trait_) => Some(trait_), |
72 | .filter_map(|candidate: either::Either<hir::ModuleDef, hir::MacroDef>| match candidate { | 73 | _ => None, |
73 | either::Either::Left(hir::ModuleDef::Trait(trait_)) => Some(trait_), | 74 | }) |
74 | _ => None, | 75 | .flat_map(|trait_| { |
75 | }) | 76 | current_module |
76 | .flat_map(|trait_| { | 77 | .find_use_path(ctx.sema.db, hir::ModuleDef::Trait(trait_)) |
77 | current_module | 78 | .as_ref() |
78 | .find_use_path(ctx.sema.db, hir::ModuleDef::Trait(trait_)) | 79 | .map(mod_path_to_ast) |
79 | .as_ref() | 80 | .zip(Some(trait_)) |
80 | .map(mod_path_to_ast) | 81 | }); |
81 | .zip(Some(trait_)) | ||
82 | }); | ||
83 | 82 | ||
84 | let mut no_traits_found = true; | 83 | let mut no_traits_found = true; |
85 | for (trait_path, trait_) in found_traits.inspect(|_| no_traits_found = false) { | 84 | for (trait_path, trait_) in found_traits.inspect(|_| no_traits_found = false) { |
diff --git a/crates/ide_assists/src/handlers/replace_for_loop_with_for_each.rs b/crates/ide_assists/src/handlers/replace_for_loop_with_for_each.rs index 27da28bc0..50b05ab0b 100644 --- a/crates/ide_assists/src/handlers/replace_for_loop_with_for_each.rs +++ b/crates/ide_assists/src/handlers/replace_for_loop_with_for_each.rs | |||
@@ -3,7 +3,6 @@ use hir::known; | |||
3 | use ide_db::helpers::FamousDefs; | 3 | use ide_db::helpers::FamousDefs; |
4 | use stdx::format_to; | 4 | use stdx::format_to; |
5 | use syntax::{ast, AstNode}; | 5 | use syntax::{ast, AstNode}; |
6 | use test_utils::mark; | ||
7 | 6 | ||
8 | use crate::{AssistContext, AssistId, AssistKind, Assists}; | 7 | use crate::{AssistContext, AssistId, AssistKind, Assists}; |
9 | 8 | ||
@@ -34,7 +33,7 @@ pub(crate) fn replace_for_loop_with_for_each(acc: &mut Assists, ctx: &AssistCont | |||
34 | let pat = for_loop.pat()?; | 33 | let pat = for_loop.pat()?; |
35 | let body = for_loop.loop_body()?; | 34 | let body = for_loop.loop_body()?; |
36 | if body.syntax().text_range().start() < ctx.offset() { | 35 | if body.syntax().text_range().start() < ctx.offset() { |
37 | mark::hit!(not_available_in_body); | 36 | cov_mark::hit!(not_available_in_body); |
38 | return None; | 37 | return None; |
39 | } | 38 | } |
40 | 39 | ||
@@ -187,7 +186,7 @@ fn main() { | |||
187 | 186 | ||
188 | #[test] | 187 | #[test] |
189 | fn not_available_in_body() { | 188 | fn not_available_in_body() { |
190 | mark::check!(not_available_in_body); | 189 | cov_mark::check!(not_available_in_body); |
191 | check_assist_not_applicable( | 190 | check_assist_not_applicable( |
192 | replace_for_loop_with_for_each, | 191 | replace_for_loop_with_for_each, |
193 | r" | 192 | r" |
diff --git a/crates/ide_assists/src/handlers/replace_qualified_name_with_use.rs b/crates/ide_assists/src/handlers/replace_qualified_name_with_use.rs index f3bc6cf39..36d2e0331 100644 --- a/crates/ide_assists/src/handlers/replace_qualified_name_with_use.rs +++ b/crates/ide_assists/src/handlers/replace_qualified_name_with_use.rs | |||
@@ -1,6 +1,5 @@ | |||
1 | use ide_db::helpers::insert_use::{insert_use, ImportScope}; | 1 | use ide_db::helpers::insert_use::{insert_use, ImportScope}; |
2 | use syntax::{algo::SyntaxRewriter, ast, match_ast, AstNode, SyntaxNode}; | 2 | use syntax::{algo::SyntaxRewriter, ast, match_ast, AstNode, SyntaxNode}; |
3 | use test_utils::mark; | ||
4 | 3 | ||
5 | use crate::{AssistContext, AssistId, AssistKind, Assists}; | 4 | use crate::{AssistContext, AssistId, AssistKind, Assists}; |
6 | 5 | ||
@@ -27,7 +26,7 @@ pub(crate) fn replace_qualified_name_with_use( | |||
27 | return None; | 26 | return None; |
28 | } | 27 | } |
29 | if path.qualifier().is_none() { | 28 | if path.qualifier().is_none() { |
30 | mark::hit!(dont_import_trivial_paths); | 29 | cov_mark::hit!(dont_import_trivial_paths); |
31 | return None; | 30 | return None; |
32 | } | 31 | } |
33 | 32 | ||
@@ -44,7 +43,7 @@ pub(crate) fn replace_qualified_name_with_use( | |||
44 | let mut rewriter = SyntaxRewriter::default(); | 43 | let mut rewriter = SyntaxRewriter::default(); |
45 | shorten_paths(&mut rewriter, syntax.clone(), &path); | 44 | shorten_paths(&mut rewriter, syntax.clone(), &path); |
46 | if let Some(ref import_scope) = ImportScope::from(syntax.clone()) { | 45 | if let Some(ref import_scope) = ImportScope::from(syntax.clone()) { |
47 | rewriter += insert_use(import_scope, path, ctx.config.insert_use.merge); | 46 | rewriter += insert_use(import_scope, path, ctx.config.insert_use); |
48 | builder.rewrite(rewriter); | 47 | builder.rewrite(rewriter); |
49 | } | 48 | } |
50 | }, | 49 | }, |
@@ -458,7 +457,7 @@ impl Debug for Foo { | |||
458 | 457 | ||
459 | #[test] | 458 | #[test] |
460 | fn dont_import_trivial_paths() { | 459 | fn dont_import_trivial_paths() { |
461 | mark::check!(dont_import_trivial_paths); | 460 | cov_mark::check!(dont_import_trivial_paths); |
462 | check_assist_not_applicable( | 461 | check_assist_not_applicable( |
463 | replace_qualified_name_with_use, | 462 | replace_qualified_name_with_use, |
464 | r" | 463 | r" |
diff --git a/crates/ide_assists/src/handlers/unmerge_use.rs b/crates/ide_assists/src/handlers/unmerge_use.rs index 3dbef8e51..616af7c2e 100644 --- a/crates/ide_assists/src/handlers/unmerge_use.rs +++ b/crates/ide_assists/src/handlers/unmerge_use.rs | |||
@@ -3,7 +3,6 @@ use syntax::{ | |||
3 | ast::{self, edit::AstNodeEdit, VisibilityOwner}, | 3 | ast::{self, edit::AstNodeEdit, VisibilityOwner}, |
4 | AstNode, SyntaxKind, | 4 | AstNode, SyntaxKind, |
5 | }; | 5 | }; |
6 | use test_utils::mark; | ||
7 | 6 | ||
8 | use crate::{ | 7 | use crate::{ |
9 | assist_context::{AssistContext, Assists}, | 8 | assist_context::{AssistContext, Assists}, |
@@ -27,7 +26,7 @@ pub(crate) fn unmerge_use(acc: &mut Assists, ctx: &AssistContext) -> Option<()> | |||
27 | 26 | ||
28 | let tree_list = tree.syntax().parent().and_then(ast::UseTreeList::cast)?; | 27 | let tree_list = tree.syntax().parent().and_then(ast::UseTreeList::cast)?; |
29 | if tree_list.use_trees().count() < 2 { | 28 | if tree_list.use_trees().count() < 2 { |
30 | mark::hit!(skip_single_use_item); | 29 | cov_mark::hit!(skip_single_use_item); |
31 | return None; | 30 | return None; |
32 | } | 31 | } |
33 | 32 | ||
@@ -89,7 +88,7 @@ mod tests { | |||
89 | 88 | ||
90 | #[test] | 89 | #[test] |
91 | fn skip_single_use_item() { | 90 | fn skip_single_use_item() { |
92 | mark::check!(skip_single_use_item); | 91 | cov_mark::check!(skip_single_use_item); |
93 | check_assist_not_applicable( | 92 | check_assist_not_applicable( |
94 | unmerge_use, | 93 | unmerge_use, |
95 | r" | 94 | r" |
diff --git a/crates/ide_assists/src/handlers/wrap_return_type_in_result.rs b/crates/ide_assists/src/handlers/wrap_return_type_in_result.rs index fec16fc49..e838630ea 100644 --- a/crates/ide_assists/src/handlers/wrap_return_type_in_result.rs +++ b/crates/ide_assists/src/handlers/wrap_return_type_in_result.rs | |||
@@ -4,7 +4,6 @@ use syntax::{ | |||
4 | ast::{self, make, BlockExpr, Expr, LoopBodyOwner}, | 4 | ast::{self, make, BlockExpr, Expr, LoopBodyOwner}, |
5 | match_ast, AstNode, SyntaxNode, | 5 | match_ast, AstNode, SyntaxNode, |
6 | }; | 6 | }; |
7 | use test_utils::mark; | ||
8 | 7 | ||
9 | use crate::{AssistContext, AssistId, AssistKind, Assists}; | 8 | use crate::{AssistContext, AssistId, AssistKind, Assists}; |
10 | 9 | ||
@@ -39,7 +38,7 @@ pub(crate) fn wrap_return_type_in_result(acc: &mut Assists, ctx: &AssistContext) | |||
39 | let first_part_ret_type = ret_type_str.splitn(2, '<').next(); | 38 | let first_part_ret_type = ret_type_str.splitn(2, '<').next(); |
40 | if let Some(ret_type_first_part) = first_part_ret_type { | 39 | if let Some(ret_type_first_part) = first_part_ret_type { |
41 | if ret_type_first_part.ends_with("Result") { | 40 | if ret_type_first_part.ends_with("Result") { |
42 | mark::hit!(wrap_return_type_in_result_simple_return_type_already_result); | 41 | cov_mark::hit!(wrap_return_type_in_result_simple_return_type_already_result); |
43 | return None; | 42 | return None; |
44 | } | 43 | } |
45 | } | 44 | } |
@@ -367,7 +366,7 @@ fn foo() -> std::result::Result<i32$0, String> { | |||
367 | 366 | ||
368 | #[test] | 367 | #[test] |
369 | fn wrap_return_type_in_result_simple_return_type_already_result() { | 368 | fn wrap_return_type_in_result_simple_return_type_already_result() { |
370 | mark::check!(wrap_return_type_in_result_simple_return_type_already_result); | 369 | cov_mark::check!(wrap_return_type_in_result_simple_return_type_already_result); |
371 | check_assist_not_applicable( | 370 | check_assist_not_applicable( |
372 | wrap_return_type_in_result, | 371 | wrap_return_type_in_result, |
373 | r#" | 372 | r#" |
diff --git a/crates/ide_assists/src/lib.rs b/crates/ide_assists/src/lib.rs index ea62d5f5d..f1aab74d4 100644 --- a/crates/ide_assists/src/lib.rs +++ b/crates/ide_assists/src/lib.rs | |||
@@ -116,6 +116,7 @@ mod handlers { | |||
116 | mod change_visibility; | 116 | mod change_visibility; |
117 | mod convert_integer_literal; | 117 | mod convert_integer_literal; |
118 | mod convert_comment_block; | 118 | mod convert_comment_block; |
119 | mod convert_iter_for_each_to_for; | ||
119 | mod early_return; | 120 | mod early_return; |
120 | mod expand_glob_import; | 121 | mod expand_glob_import; |
121 | mod extract_function; | 122 | mod extract_function; |
@@ -181,6 +182,7 @@ mod handlers { | |||
181 | change_visibility::change_visibility, | 182 | change_visibility::change_visibility, |
182 | convert_integer_literal::convert_integer_literal, | 183 | convert_integer_literal::convert_integer_literal, |
183 | convert_comment_block::convert_comment_block, | 184 | convert_comment_block::convert_comment_block, |
185 | convert_iter_for_each_to_for::convert_iter_for_each_to_for, | ||
184 | early_return::convert_to_guarded_return, | 186 | early_return::convert_to_guarded_return, |
185 | expand_glob_import::expand_glob_import, | 187 | expand_glob_import::expand_glob_import, |
186 | extract_struct_from_enum_variant::extract_struct_from_enum_variant, | 188 | extract_struct_from_enum_variant::extract_struct_from_enum_variant, |
diff --git a/crates/ide_assists/src/tests.rs b/crates/ide_assists/src/tests.rs index b7f616760..a7a923beb 100644 --- a/crates/ide_assists/src/tests.rs +++ b/crates/ide_assists/src/tests.rs | |||
@@ -23,6 +23,7 @@ pub(crate) const TEST_CONFIG: AssistConfig = AssistConfig { | |||
23 | insert_use: InsertUseConfig { | 23 | insert_use: InsertUseConfig { |
24 | merge: Some(MergeBehavior::Full), | 24 | merge: Some(MergeBehavior::Full), |
25 | prefix_kind: hir::PrefixKind::Plain, | 25 | prefix_kind: hir::PrefixKind::Plain, |
26 | group: true, | ||
26 | }, | 27 | }, |
27 | }; | 28 | }; |
28 | 29 | ||
diff --git a/crates/ide_assists/src/tests/generated.rs b/crates/ide_assists/src/tests/generated.rs index 304b5798f..3f77edd8d 100644 --- a/crates/ide_assists/src/tests/generated.rs +++ b/crates/ide_assists/src/tests/generated.rs | |||
@@ -206,6 +206,36 @@ const _: i32 = 0b1010; | |||
206 | } | 206 | } |
207 | 207 | ||
208 | #[test] | 208 | #[test] |
209 | fn doctest_convert_iter_for_each_to_for() { | ||
210 | check_doc_test( | ||
211 | "convert_iter_for_each_to_for", | ||
212 | r#####" | ||
213 | //- /lib.rs crate:core | ||
214 | pub mod iter { pub mod traits { pub mod iterator { pub trait Iterator {} } } } | ||
215 | pub struct SomeIter; | ||
216 | impl self::iter::traits::iterator::Iterator for SomeIter {} | ||
217 | //- /lib.rs crate:main deps:core | ||
218 | use core::SomeIter; | ||
219 | fn main() { | ||
220 | let iter = SomeIter; | ||
221 | iter.for_each$0(|(x, y)| { | ||
222 | println!("x: {}, y: {}", x, y); | ||
223 | }); | ||
224 | } | ||
225 | "#####, | ||
226 | r#####" | ||
227 | use core::SomeIter; | ||
228 | fn main() { | ||
229 | let iter = SomeIter; | ||
230 | for (x, y) in iter { | ||
231 | println!("x: {}, y: {}", x, y); | ||
232 | } | ||
233 | } | ||
234 | "#####, | ||
235 | ) | ||
236 | } | ||
237 | |||
238 | #[test] | ||
209 | fn doctest_convert_to_guarded_return() { | 239 | fn doctest_convert_to_guarded_return() { |
210 | check_doc_test( | 240 | check_doc_test( |
211 | "convert_to_guarded_return", | 241 | "convert_to_guarded_return", |
diff --git a/crates/ide_completion/Cargo.toml b/crates/ide_completion/Cargo.toml index c09101ccb..585ecca50 100644 --- a/crates/ide_completion/Cargo.toml +++ b/crates/ide_completion/Cargo.toml | |||
@@ -10,6 +10,7 @@ edition = "2018" | |||
10 | doctest = false | 10 | doctest = false |
11 | 11 | ||
12 | [dependencies] | 12 | [dependencies] |
13 | cov-mark = "1.1" | ||
13 | itertools = "0.10.0" | 14 | itertools = "0.10.0" |
14 | log = "0.4.8" | 15 | log = "0.4.8" |
15 | rustc-hash = "1.1.0" | 16 | rustc-hash = "1.1.0" |
@@ -21,11 +22,11 @@ text_edit = { path = "../text_edit", version = "0.0.0" } | |||
21 | base_db = { path = "../base_db", version = "0.0.0" } | 22 | base_db = { path = "../base_db", version = "0.0.0" } |
22 | ide_db = { path = "../ide_db", version = "0.0.0" } | 23 | ide_db = { path = "../ide_db", version = "0.0.0" } |
23 | profile = { path = "../profile", version = "0.0.0" } | 24 | profile = { path = "../profile", version = "0.0.0" } |
24 | test_utils = { path = "../test_utils", version = "0.0.0" } | ||
25 | 25 | ||
26 | # completions crate should depend only on the top-level `hir` package. if you need | 26 | # completions crate should depend only on the top-level `hir` package. if you need |
27 | # something from some `hir_xxx` subpackage, reexport the API via `hir`. | 27 | # something from some `hir_xxx` subpackage, reexport the API via `hir`. |
28 | hir = { path = "../hir", version = "0.0.0" } | 28 | hir = { path = "../hir", version = "0.0.0" } |
29 | 29 | ||
30 | [dev-dependencies] | 30 | [dev-dependencies] |
31 | test_utils = { path = "../test_utils" } | ||
31 | expect-test = "1.1" | 32 | expect-test = "1.1" |
diff --git a/crates/ide_completion/src/completions/attribute.rs b/crates/ide_completion/src/completions/attribute.rs index cb05e85fc..e846678b4 100644 --- a/crates/ide_completion/src/completions/attribute.rs +++ b/crates/ide_completion/src/completions/attribute.rs | |||
@@ -45,15 +45,15 @@ fn complete_attribute_start(acc: &mut Completions, ctx: &CompletionContext, attr | |||
45 | CompletionKind::Attribute, | 45 | CompletionKind::Attribute, |
46 | ctx.source_range(), | 46 | ctx.source_range(), |
47 | attr_completion.label, | 47 | attr_completion.label, |
48 | ) | 48 | ); |
49 | .kind(CompletionItemKind::Attribute); | 49 | item.kind(CompletionItemKind::Attribute); |
50 | 50 | ||
51 | if let Some(lookup) = attr_completion.lookup { | 51 | if let Some(lookup) = attr_completion.lookup { |
52 | item = item.lookup_by(lookup); | 52 | item.lookup_by(lookup); |
53 | } | 53 | } |
54 | 54 | ||
55 | if let Some((snippet, cap)) = attr_completion.snippet.zip(ctx.config.snippet_cap) { | 55 | if let Some((snippet, cap)) = attr_completion.snippet.zip(ctx.config.snippet_cap) { |
56 | item = item.insert_snippet(cap, snippet); | 56 | item.insert_snippet(cap, snippet); |
57 | } | 57 | } |
58 | 58 | ||
59 | if attribute.kind() == ast::AttrKind::Inner || !attr_completion.prefer_inner { | 59 | if attribute.kind() == ast::AttrKind::Inner || !attr_completion.prefer_inner { |
@@ -168,16 +168,20 @@ fn complete_derive(acc: &mut Completions, ctx: &CompletionContext, derive_input: | |||
168 | ); | 168 | ); |
169 | let lookup = components.join(", "); | 169 | let lookup = components.join(", "); |
170 | let label = components.iter().rev().join(", "); | 170 | let label = components.iter().rev().join(", "); |
171 | CompletionItem::new(CompletionKind::Attribute, ctx.source_range(), label) | 171 | let mut item = |
172 | .lookup_by(lookup) | 172 | CompletionItem::new(CompletionKind::Attribute, ctx.source_range(), label); |
173 | .kind(CompletionItemKind::Attribute) | 173 | item.lookup_by(lookup).kind(CompletionItemKind::Attribute); |
174 | .add_to(acc) | 174 | item.add_to(acc); |
175 | } | 175 | } |
176 | 176 | ||
177 | for custom_derive_name in get_derive_names_in_scope(ctx).difference(&existing_derives) { | 177 | for custom_derive_name in get_derive_names_in_scope(ctx).difference(&existing_derives) { |
178 | CompletionItem::new(CompletionKind::Attribute, ctx.source_range(), custom_derive_name) | 178 | let mut item = CompletionItem::new( |
179 | .kind(CompletionItemKind::Attribute) | 179 | CompletionKind::Attribute, |
180 | .add_to(acc) | 180 | ctx.source_range(), |
181 | custom_derive_name, | ||
182 | ); | ||
183 | item.kind(CompletionItemKind::Attribute); | ||
184 | item.add_to(acc); | ||
181 | } | 185 | } |
182 | } | 186 | } |
183 | } | 187 | } |
@@ -193,14 +197,13 @@ fn complete_lint( | |||
193 | .into_iter() | 197 | .into_iter() |
194 | .filter(|completion| !existing_lints.contains(completion.label)) | 198 | .filter(|completion| !existing_lints.contains(completion.label)) |
195 | { | 199 | { |
196 | CompletionItem::new( | 200 | let mut item = CompletionItem::new( |
197 | CompletionKind::Attribute, | 201 | CompletionKind::Attribute, |
198 | ctx.source_range(), | 202 | ctx.source_range(), |
199 | lint_completion.label, | 203 | lint_completion.label, |
200 | ) | 204 | ); |
201 | .kind(CompletionItemKind::Attribute) | 205 | item.kind(CompletionItemKind::Attribute).detail(lint_completion.description); |
202 | .detail(lint_completion.description) | 206 | item.add_to(acc) |
203 | .add_to(acc) | ||
204 | } | 207 | } |
205 | } | 208 | } |
206 | } | 209 | } |
diff --git a/crates/ide_completion/src/completions/dot.rs b/crates/ide_completion/src/completions/dot.rs index 084d7721d..5ee9a9f07 100644 --- a/crates/ide_completion/src/completions/dot.rs +++ b/crates/ide_completion/src/completions/dot.rs | |||
@@ -2,7 +2,6 @@ | |||
2 | 2 | ||
3 | use hir::{HasVisibility, Type}; | 3 | use hir::{HasVisibility, Type}; |
4 | use rustc_hash::FxHashSet; | 4 | use rustc_hash::FxHashSet; |
5 | use test_utils::mark; | ||
6 | 5 | ||
7 | use crate::{context::CompletionContext, Completions}; | 6 | use crate::{context::CompletionContext, Completions}; |
8 | 7 | ||
@@ -19,7 +18,7 @@ pub(crate) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) { | |||
19 | }; | 18 | }; |
20 | 19 | ||
21 | if ctx.is_call { | 20 | if ctx.is_call { |
22 | mark::hit!(test_no_struct_field_completion_for_method_call); | 21 | cov_mark::hit!(test_no_struct_field_completion_for_method_call); |
23 | } else { | 22 | } else { |
24 | complete_fields(acc, ctx, &receiver_ty); | 23 | complete_fields(acc, ctx, &receiver_ty); |
25 | } | 24 | } |
@@ -62,7 +61,6 @@ fn complete_methods(acc: &mut Completions, ctx: &CompletionContext, receiver: &T | |||
62 | #[cfg(test)] | 61 | #[cfg(test)] |
63 | mod tests { | 62 | mod tests { |
64 | use expect_test::{expect, Expect}; | 63 | use expect_test::{expect, Expect}; |
65 | use test_utils::mark; | ||
66 | 64 | ||
67 | use crate::{test_utils::completion_list, CompletionKind}; | 65 | use crate::{test_utils::completion_list, CompletionKind}; |
68 | 66 | ||
@@ -122,7 +120,7 @@ impl A { | |||
122 | 120 | ||
123 | #[test] | 121 | #[test] |
124 | fn test_no_struct_field_completion_for_method_call() { | 122 | fn test_no_struct_field_completion_for_method_call() { |
125 | mark::check!(test_no_struct_field_completion_for_method_call); | 123 | cov_mark::check!(test_no_struct_field_completion_for_method_call); |
126 | check( | 124 | check( |
127 | r#" | 125 | r#" |
128 | struct A { the_field: u32 } | 126 | struct A { the_field: u32 } |
diff --git a/crates/ide_completion/src/completions/flyimport.rs b/crates/ide_completion/src/completions/flyimport.rs index da8375af9..391a11c91 100644 --- a/crates/ide_completion/src/completions/flyimport.rs +++ b/crates/ide_completion/src/completions/flyimport.rs | |||
@@ -21,6 +21,46 @@ | |||
21 | //! ``` | 21 | //! ``` |
22 | //! | 22 | //! |
23 | //! Also completes associated items, that require trait imports. | 23 | //! Also completes associated items, that require trait imports. |
24 | //! If any unresolved and/or partially-qualified path predeces the input, it will be taken into account. | ||
25 | //! Currently, only the imports with their import path ending with the whole qialifier will be proposed | ||
26 | //! (no fuzzy matching for qualifier). | ||
27 | //! | ||
28 | //! ``` | ||
29 | //! mod foo { | ||
30 | //! pub mod bar { | ||
31 | //! pub struct Item; | ||
32 | //! | ||
33 | //! impl Item { | ||
34 | //! pub const TEST_ASSOC: usize = 3; | ||
35 | //! } | ||
36 | //! } | ||
37 | //! } | ||
38 | //! | ||
39 | //! fn main() { | ||
40 | //! bar::Item::TEST_A$0 | ||
41 | //! } | ||
42 | //! ``` | ||
43 | //! -> | ||
44 | //! ``` | ||
45 | //! use foo::bar; | ||
46 | //! | ||
47 | //! mod foo { | ||
48 | //! pub mod bar { | ||
49 | //! pub struct Item; | ||
50 | //! | ||
51 | //! impl Item { | ||
52 | //! pub const TEST_ASSOC: usize = 3; | ||
53 | //! } | ||
54 | //! } | ||
55 | //! } | ||
56 | //! | ||
57 | //! fn main() { | ||
58 | //! bar::Item::TEST_ASSOC | ||
59 | //! } | ||
60 | //! ``` | ||
61 | //! | ||
62 | //! NOTE: currently, if an assoc item comes from a trait that's not currently imported and it also has an unresolved and/or partially-qualified path, | ||
63 | //! no imports will be proposed. | ||
24 | //! | 64 | //! |
25 | //! .Fuzzy search details | 65 | //! .Fuzzy search details |
26 | //! | 66 | //! |
@@ -48,14 +88,13 @@ | |||
48 | //! Note that having this flag set to `true` does not guarantee that the feature is enabled: your client needs to have the corredponding | 88 | //! Note that having this flag set to `true` does not guarantee that the feature is enabled: your client needs to have the corredponding |
49 | //! capability enabled. | 89 | //! capability enabled. |
50 | 90 | ||
51 | use hir::{AsAssocItem, ModPath, ScopeDef}; | 91 | use hir::ModPath; |
52 | use ide_db::helpers::{ | 92 | use ide_db::helpers::{ |
53 | import_assets::{ImportAssets, ImportCandidate}, | 93 | import_assets::{ImportAssets, ImportCandidate}, |
54 | insert_use::ImportScope, | 94 | insert_use::ImportScope, |
55 | }; | 95 | }; |
56 | use rustc_hash::FxHashSet; | 96 | use itertools::Itertools; |
57 | use syntax::{AstNode, SyntaxNode, T}; | 97 | use syntax::{AstNode, SyntaxNode, T}; |
58 | use test_utils::mark; | ||
59 | 98 | ||
60 | use crate::{ | 99 | use crate::{ |
61 | context::CompletionContext, | 100 | context::CompletionContext, |
@@ -93,50 +132,26 @@ pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext) | |||
93 | &ctx.sema, | 132 | &ctx.sema, |
94 | )?; | 133 | )?; |
95 | 134 | ||
96 | let scope_definitions = scope_definitions(ctx); | 135 | acc.add_all( |
97 | let mut all_mod_paths = import_assets | 136 | import_assets |
98 | .search_for_imports(&ctx.sema, ctx.config.insert_use.prefix_kind) | 137 | .search_for_imports(&ctx.sema, ctx.config.insert_use.prefix_kind) |
99 | .into_iter() | 138 | .into_iter() |
100 | .map(|(mod_path, item_in_ns)| { | 139 | .sorted_by_key(|located_import| { |
101 | let scope_item = match item_in_ns { | 140 | compute_fuzzy_completion_order_key( |
102 | hir::ItemInNs::Types(id) => ScopeDef::ModuleDef(id.into()), | 141 | &located_import.import_path, |
103 | hir::ItemInNs::Values(id) => ScopeDef::ModuleDef(id.into()), | 142 | &user_input_lowercased, |
104 | hir::ItemInNs::Macros(id) => ScopeDef::MacroDef(id.into()), | 143 | ) |
105 | }; | 144 | }) |
106 | (mod_path, scope_item) | 145 | .filter_map(|import| { |
107 | }) | 146 | render_resolution_with_import( |
108 | .filter(|(_, proposed_def)| !scope_definitions.contains(proposed_def)) | 147 | RenderContext::new(ctx), |
109 | .collect::<Vec<_>>(); | 148 | ImportEdit { import, scope: import_scope.clone() }, |
110 | all_mod_paths.sort_by_cached_key(|(mod_path, _)| { | 149 | ) |
111 | compute_fuzzy_completion_order_key(mod_path, &user_input_lowercased) | 150 | }), |
112 | }); | 151 | ); |
113 | |||
114 | acc.add_all(all_mod_paths.into_iter().filter_map(|(import_path, definition)| { | ||
115 | let import_for_trait_assoc_item = match definition { | ||
116 | ScopeDef::ModuleDef(module_def) => module_def | ||
117 | .as_assoc_item(ctx.db) | ||
118 | .and_then(|assoc| assoc.containing_trait(ctx.db)) | ||
119 | .is_some(), | ||
120 | _ => false, | ||
121 | }; | ||
122 | let import_edit = ImportEdit { | ||
123 | import_path, | ||
124 | import_scope: import_scope.clone(), | ||
125 | import_for_trait_assoc_item, | ||
126 | }; | ||
127 | render_resolution_with_import(RenderContext::new(ctx), import_edit, &definition) | ||
128 | })); | ||
129 | Some(()) | 152 | Some(()) |
130 | } | 153 | } |
131 | 154 | ||
132 | fn scope_definitions(ctx: &CompletionContext) -> FxHashSet<ScopeDef> { | ||
133 | let mut scope_definitions = FxHashSet::default(); | ||
134 | ctx.scope.process_all_names(&mut |_, scope_def| { | ||
135 | scope_definitions.insert(scope_def); | ||
136 | }); | ||
137 | scope_definitions | ||
138 | } | ||
139 | |||
140 | pub(crate) fn position_for_import<'a>( | 155 | pub(crate) fn position_for_import<'a>( |
141 | ctx: &'a CompletionContext, | 156 | ctx: &'a CompletionContext, |
142 | import_candidate: Option<&ImportCandidate>, | 157 | import_candidate: Option<&ImportCandidate>, |
@@ -161,23 +176,30 @@ fn import_assets(ctx: &CompletionContext, fuzzy_name: String) -> Option<ImportAs | |||
161 | current_module, | 176 | current_module, |
162 | ctx.sema.type_of_expr(dot_receiver)?, | 177 | ctx.sema.type_of_expr(dot_receiver)?, |
163 | fuzzy_name, | 178 | fuzzy_name, |
179 | dot_receiver.syntax().clone(), | ||
164 | ) | 180 | ) |
165 | } else { | 181 | } else { |
166 | let fuzzy_name_length = fuzzy_name.len(); | 182 | let fuzzy_name_length = fuzzy_name.len(); |
183 | let approximate_node = match current_module.definition_source(ctx.db).value { | ||
184 | hir::ModuleSource::SourceFile(s) => s.syntax().clone(), | ||
185 | hir::ModuleSource::Module(m) => m.syntax().clone(), | ||
186 | hir::ModuleSource::BlockExpr(b) => b.syntax().clone(), | ||
187 | }; | ||
167 | let assets_for_path = ImportAssets::for_fuzzy_path( | 188 | let assets_for_path = ImportAssets::for_fuzzy_path( |
168 | current_module, | 189 | current_module, |
169 | ctx.path_qual.clone(), | 190 | ctx.path_qual.clone(), |
170 | fuzzy_name, | 191 | fuzzy_name, |
171 | &ctx.sema, | 192 | &ctx.sema, |
172 | ); | 193 | approximate_node, |
194 | )?; | ||
173 | 195 | ||
174 | if matches!(assets_for_path.as_ref()?.import_candidate(), ImportCandidate::Path(_)) | 196 | if matches!(assets_for_path.import_candidate(), ImportCandidate::Path(_)) |
175 | && fuzzy_name_length < 2 | 197 | && fuzzy_name_length < 2 |
176 | { | 198 | { |
177 | mark::hit!(ignore_short_input_for_path); | 199 | cov_mark::hit!(ignore_short_input_for_path); |
178 | None | 200 | None |
179 | } else { | 201 | } else { |
180 | assets_for_path | 202 | Some(assets_for_path) |
181 | } | 203 | } |
182 | } | 204 | } |
183 | } | 205 | } |
@@ -186,12 +208,12 @@ fn compute_fuzzy_completion_order_key( | |||
186 | proposed_mod_path: &ModPath, | 208 | proposed_mod_path: &ModPath, |
187 | user_input_lowercased: &str, | 209 | user_input_lowercased: &str, |
188 | ) -> usize { | 210 | ) -> usize { |
189 | mark::hit!(certain_fuzzy_order_test); | 211 | cov_mark::hit!(certain_fuzzy_order_test); |
190 | let proposed_import_name = match proposed_mod_path.segments().last() { | 212 | let import_name = match proposed_mod_path.segments().last() { |
191 | Some(name) => name.to_string().to_lowercase(), | 213 | Some(name) => name.to_string().to_lowercase(), |
192 | None => return usize::MAX, | 214 | None => return usize::MAX, |
193 | }; | 215 | }; |
194 | match proposed_import_name.match_indices(user_input_lowercased).next() { | 216 | match import_name.match_indices(user_input_lowercased).next() { |
195 | Some((first_matching_index, _)) => first_matching_index, | 217 | Some((first_matching_index, _)) => first_matching_index, |
196 | None => usize::MAX, | 218 | None => usize::MAX, |
197 | } | 219 | } |
@@ -200,7 +222,6 @@ fn compute_fuzzy_completion_order_key( | |||
200 | #[cfg(test)] | 222 | #[cfg(test)] |
201 | mod tests { | 223 | mod tests { |
202 | use expect_test::{expect, Expect}; | 224 | use expect_test::{expect, Expect}; |
203 | use test_utils::mark; | ||
204 | 225 | ||
205 | use crate::{ | 226 | use crate::{ |
206 | item::CompletionKind, | 227 | item::CompletionKind, |
@@ -295,7 +316,7 @@ fn main() { | |||
295 | 316 | ||
296 | #[test] | 317 | #[test] |
297 | fn short_paths_are_ignored() { | 318 | fn short_paths_are_ignored() { |
298 | mark::check!(ignore_short_input_for_path); | 319 | cov_mark::check!(ignore_short_input_for_path); |
299 | 320 | ||
300 | check( | 321 | check( |
301 | r#" | 322 | r#" |
@@ -319,7 +340,7 @@ fn main() { | |||
319 | 340 | ||
320 | #[test] | 341 | #[test] |
321 | fn fuzzy_completions_come_in_specific_order() { | 342 | fn fuzzy_completions_come_in_specific_order() { |
322 | mark::check!(certain_fuzzy_order_test); | 343 | cov_mark::check!(certain_fuzzy_order_test); |
323 | check( | 344 | check( |
324 | r#" | 345 | r#" |
325 | //- /lib.rs crate:dep | 346 | //- /lib.rs crate:dep |
@@ -775,4 +796,155 @@ fn main() { | |||
775 | }"#, | 796 | }"#, |
776 | ); | 797 | ); |
777 | } | 798 | } |
799 | |||
800 | #[test] | ||
801 | fn unresolved_qualifier() { | ||
802 | let fixture = r#" | ||
803 | mod foo { | ||
804 | pub mod bar { | ||
805 | pub mod baz { | ||
806 | pub struct Item; | ||
807 | } | ||
808 | } | ||
809 | } | ||
810 | |||
811 | fn main() { | ||
812 | bar::baz::Ite$0 | ||
813 | }"#; | ||
814 | |||
815 | check( | ||
816 | fixture, | ||
817 | expect![[r#" | ||
818 | st foo::bar::baz::Item | ||
819 | "#]], | ||
820 | ); | ||
821 | |||
822 | check_edit( | ||
823 | "Item", | ||
824 | fixture, | ||
825 | r#" | ||
826 | use foo::bar; | ||
827 | |||
828 | mod foo { | ||
829 | pub mod bar { | ||
830 | pub mod baz { | ||
831 | pub struct Item; | ||
832 | } | ||
833 | } | ||
834 | } | ||
835 | |||
836 | fn main() { | ||
837 | bar::baz::Item | ||
838 | }"#, | ||
839 | ); | ||
840 | } | ||
841 | |||
842 | #[test] | ||
843 | fn unresolved_assoc_item_container() { | ||
844 | let fixture = r#" | ||
845 | mod foo { | ||
846 | pub struct Item; | ||
847 | |||
848 | impl Item { | ||
849 | pub const TEST_ASSOC: usize = 3; | ||
850 | } | ||
851 | } | ||
852 | |||
853 | fn main() { | ||
854 | Item::TEST_A$0 | ||
855 | }"#; | ||
856 | |||
857 | check( | ||
858 | fixture, | ||
859 | expect![[r#" | ||
860 | ct TEST_ASSOC (foo::Item) | ||
861 | "#]], | ||
862 | ); | ||
863 | |||
864 | check_edit( | ||
865 | "TEST_ASSOC", | ||
866 | fixture, | ||
867 | r#" | ||
868 | use foo::Item; | ||
869 | |||
870 | mod foo { | ||
871 | pub struct Item; | ||
872 | |||
873 | impl Item { | ||
874 | pub const TEST_ASSOC: usize = 3; | ||
875 | } | ||
876 | } | ||
877 | |||
878 | fn main() { | ||
879 | Item::TEST_ASSOC | ||
880 | }"#, | ||
881 | ); | ||
882 | } | ||
883 | |||
884 | #[test] | ||
885 | fn unresolved_assoc_item_container_with_path() { | ||
886 | let fixture = r#" | ||
887 | mod foo { | ||
888 | pub mod bar { | ||
889 | pub struct Item; | ||
890 | |||
891 | impl Item { | ||
892 | pub const TEST_ASSOC: usize = 3; | ||
893 | } | ||
894 | } | ||
895 | } | ||
896 | |||
897 | fn main() { | ||
898 | bar::Item::TEST_A$0 | ||
899 | }"#; | ||
900 | |||
901 | check( | ||
902 | fixture, | ||
903 | expect![[r#" | ||
904 | ct TEST_ASSOC (foo::bar::Item) | ||
905 | "#]], | ||
906 | ); | ||
907 | |||
908 | check_edit( | ||
909 | "TEST_ASSOC", | ||
910 | fixture, | ||
911 | r#" | ||
912 | use foo::bar; | ||
913 | |||
914 | mod foo { | ||
915 | pub mod bar { | ||
916 | pub struct Item; | ||
917 | |||
918 | impl Item { | ||
919 | pub const TEST_ASSOC: usize = 3; | ||
920 | } | ||
921 | } | ||
922 | } | ||
923 | |||
924 | fn main() { | ||
925 | bar::Item::TEST_ASSOC | ||
926 | }"#, | ||
927 | ); | ||
928 | } | ||
929 | |||
930 | #[test] | ||
931 | fn fuzzy_unresolved_path() { | ||
932 | check( | ||
933 | r#" | ||
934 | mod foo { | ||
935 | pub mod bar { | ||
936 | pub struct Item; | ||
937 | |||
938 | impl Item { | ||
939 | pub const TEST_ASSOC: usize = 3; | ||
940 | } | ||
941 | } | ||
942 | } | ||
943 | |||
944 | fn main() { | ||
945 | bar::Ass$0 | ||
946 | }"#, | ||
947 | expect![[]], | ||
948 | ) | ||
949 | } | ||
778 | } | 950 | } |
diff --git a/crates/ide_completion/src/completions/fn_param.rs b/crates/ide_completion/src/completions/fn_param.rs index 1bcc8727f..0243dce56 100644 --- a/crates/ide_completion/src/completions/fn_param.rs +++ b/crates/ide_completion/src/completions/fn_param.rs | |||
@@ -54,10 +54,9 @@ pub(crate) fn complete_fn_param(acc: &mut Completions, ctx: &CompletionContext) | |||
54 | } | 54 | } |
55 | 55 | ||
56 | params.into_iter().for_each(|(label, lookup)| { | 56 | params.into_iter().for_each(|(label, lookup)| { |
57 | CompletionItem::new(CompletionKind::Magic, ctx.source_range(), label) | 57 | let mut item = CompletionItem::new(CompletionKind::Magic, ctx.source_range(), label); |
58 | .kind(CompletionItemKind::Binding) | 58 | item.kind(CompletionItemKind::Binding).lookup_by(lookup); |
59 | .lookup_by(lookup) | 59 | item.add_to(acc) |
60 | .add_to(acc) | ||
61 | }); | 60 | }); |
62 | } | 61 | } |
63 | 62 | ||
diff --git a/crates/ide_completion/src/completions/keyword.rs b/crates/ide_completion/src/completions/keyword.rs index 03c6dd454..b635e0ca3 100644 --- a/crates/ide_completion/src/completions/keyword.rs +++ b/crates/ide_completion/src/completions/keyword.rs | |||
@@ -3,7 +3,6 @@ | |||
3 | use std::iter; | 3 | use std::iter; |
4 | 4 | ||
5 | use syntax::SyntaxKind; | 5 | use syntax::SyntaxKind; |
6 | use test_utils::mark; | ||
7 | 6 | ||
8 | use crate::{CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions}; | 7 | use crate::{CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions}; |
9 | 8 | ||
@@ -13,21 +12,19 @@ pub(crate) fn complete_use_tree_keyword(acc: &mut Completions, ctx: &CompletionC | |||
13 | 12 | ||
14 | if ctx.use_item_syntax.is_some() { | 13 | if ctx.use_item_syntax.is_some() { |
15 | if ctx.path_qual.is_none() { | 14 | if ctx.path_qual.is_none() { |
16 | CompletionItem::new(CompletionKind::Keyword, source_range, "crate::") | 15 | let mut item = CompletionItem::new(CompletionKind::Keyword, source_range, "crate::"); |
17 | .kind(CompletionItemKind::Keyword) | 16 | item.kind(CompletionItemKind::Keyword).insert_text("crate::"); |
18 | .insert_text("crate::") | 17 | item.add_to(acc); |
19 | .add_to(acc); | ||
20 | } | 18 | } |
21 | CompletionItem::new(CompletionKind::Keyword, source_range, "self") | 19 | let mut item = CompletionItem::new(CompletionKind::Keyword, source_range, "self"); |
22 | .kind(CompletionItemKind::Keyword) | 20 | item.kind(CompletionItemKind::Keyword); |
23 | .add_to(acc); | 21 | item.add_to(acc); |
24 | if iter::successors(ctx.path_qual.clone(), |p| p.qualifier()) | 22 | if iter::successors(ctx.path_qual.clone(), |p| p.qualifier()) |
25 | .all(|p| p.segment().and_then(|s| s.super_token()).is_some()) | 23 | .all(|p| p.segment().and_then(|s| s.super_token()).is_some()) |
26 | { | 24 | { |
27 | CompletionItem::new(CompletionKind::Keyword, source_range, "super::") | 25 | let mut item = CompletionItem::new(CompletionKind::Keyword, source_range, "super::"); |
28 | .kind(CompletionItemKind::Keyword) | 26 | item.kind(CompletionItemKind::Keyword).insert_text("super::"); |
29 | .insert_text("super::") | 27 | item.add_to(acc); |
30 | .add_to(acc); | ||
31 | } | 28 | } |
32 | } | 29 | } |
33 | 30 | ||
@@ -35,11 +32,10 @@ pub(crate) fn complete_use_tree_keyword(acc: &mut Completions, ctx: &CompletionC | |||
35 | if let Some(receiver) = &ctx.dot_receiver { | 32 | if let Some(receiver) = &ctx.dot_receiver { |
36 | if let Some(ty) = ctx.sema.type_of_expr(receiver) { | 33 | if let Some(ty) = ctx.sema.type_of_expr(receiver) { |
37 | if ty.impls_future(ctx.db) { | 34 | if ty.impls_future(ctx.db) { |
38 | CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), "await") | 35 | let mut item = |
39 | .kind(CompletionItemKind::Keyword) | 36 | CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), "await"); |
40 | .detail("expr.await") | 37 | item.kind(CompletionItemKind::Keyword).detail("expr.await").insert_text("await"); |
41 | .insert_text("await") | 38 | item.add_to(acc); |
42 | .add_to(acc); | ||
43 | } | 39 | } |
44 | }; | 40 | }; |
45 | } | 41 | } |
@@ -47,11 +43,11 @@ pub(crate) fn complete_use_tree_keyword(acc: &mut Completions, ctx: &CompletionC | |||
47 | 43 | ||
48 | pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) { | 44 | pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) { |
49 | if ctx.token.kind() == SyntaxKind::COMMENT { | 45 | if ctx.token.kind() == SyntaxKind::COMMENT { |
50 | mark::hit!(no_keyword_completion_in_comments); | 46 | cov_mark::hit!(no_keyword_completion_in_comments); |
51 | return; | 47 | return; |
52 | } | 48 | } |
53 | if ctx.record_lit_syntax.is_some() { | 49 | if ctx.record_lit_syntax.is_some() { |
54 | mark::hit!(no_keyword_completion_in_record_lit); | 50 | cov_mark::hit!(no_keyword_completion_in_record_lit); |
55 | return; | 51 | return; |
56 | } | 52 | } |
57 | 53 | ||
@@ -166,29 +162,31 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte | |||
166 | } | 162 | } |
167 | 163 | ||
168 | fn add_keyword(ctx: &CompletionContext, acc: &mut Completions, kw: &str, snippet: &str) { | 164 | fn add_keyword(ctx: &CompletionContext, acc: &mut Completions, kw: &str, snippet: &str) { |
169 | let builder = CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), kw) | 165 | let mut item = CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), kw); |
170 | .kind(CompletionItemKind::Keyword); | 166 | item.kind(CompletionItemKind::Keyword); |
171 | let builder = match ctx.config.snippet_cap { | 167 | |
168 | match ctx.config.snippet_cap { | ||
172 | Some(cap) => { | 169 | Some(cap) => { |
173 | let tmp; | 170 | let tmp; |
174 | let snippet = if snippet.ends_with('}') && ctx.incomplete_let { | 171 | let snippet = if snippet.ends_with('}') && ctx.incomplete_let { |
175 | mark::hit!(let_semi); | 172 | cov_mark::hit!(let_semi); |
176 | tmp = format!("{};", snippet); | 173 | tmp = format!("{};", snippet); |
177 | &tmp | 174 | &tmp |
178 | } else { | 175 | } else { |
179 | snippet | 176 | snippet |
180 | }; | 177 | }; |
181 | builder.insert_snippet(cap, snippet) | 178 | item.insert_snippet(cap, snippet); |
179 | } | ||
180 | None => { | ||
181 | item.insert_text(if snippet.contains('$') { kw } else { snippet }); | ||
182 | } | 182 | } |
183 | None => builder.insert_text(if snippet.contains('$') { kw } else { snippet }), | ||
184 | }; | 183 | }; |
185 | acc.add(builder.build()); | 184 | item.add_to(acc); |
186 | } | 185 | } |
187 | 186 | ||
188 | #[cfg(test)] | 187 | #[cfg(test)] |
189 | mod tests { | 188 | mod tests { |
190 | use expect_test::{expect, Expect}; | 189 | use expect_test::{expect, Expect}; |
191 | use test_utils::mark; | ||
192 | 190 | ||
193 | use crate::{ | 191 | use crate::{ |
194 | test_utils::{check_edit, completion_list}, | 192 | test_utils::{check_edit, completion_list}, |
@@ -494,7 +492,7 @@ fn quux() -> i32 { | |||
494 | 492 | ||
495 | #[test] | 493 | #[test] |
496 | fn no_keyword_completion_in_comments() { | 494 | fn no_keyword_completion_in_comments() { |
497 | mark::check!(no_keyword_completion_in_comments); | 495 | cov_mark::check!(no_keyword_completion_in_comments); |
498 | check( | 496 | check( |
499 | r#" | 497 | r#" |
500 | fn test() { | 498 | fn test() { |
@@ -599,7 +597,7 @@ struct Foo { | |||
599 | 597 | ||
600 | #[test] | 598 | #[test] |
601 | fn skip_struct_initializer() { | 599 | fn skip_struct_initializer() { |
602 | mark::check!(no_keyword_completion_in_record_lit); | 600 | cov_mark::check!(no_keyword_completion_in_record_lit); |
603 | check( | 601 | check( |
604 | r#" | 602 | r#" |
605 | struct Foo { | 603 | struct Foo { |
@@ -643,7 +641,7 @@ fn foo() { | |||
643 | 641 | ||
644 | #[test] | 642 | #[test] |
645 | fn let_semi() { | 643 | fn let_semi() { |
646 | mark::check!(let_semi); | 644 | cov_mark::check!(let_semi); |
647 | check_edit( | 645 | check_edit( |
648 | "match", | 646 | "match", |
649 | r#" | 647 | r#" |
diff --git a/crates/ide_completion/src/completions/mod_.rs b/crates/ide_completion/src/completions/mod_.rs index 352fc7c77..4f9415736 100644 --- a/crates/ide_completion/src/completions/mod_.rs +++ b/crates/ide_completion/src/completions/mod_.rs | |||
@@ -80,9 +80,9 @@ pub(crate) fn complete_mod(acc: &mut Completions, ctx: &CompletionContext) -> Op | |||
80 | if mod_under_caret.semicolon_token().is_none() { | 80 | if mod_under_caret.semicolon_token().is_none() { |
81 | label.push(';'); | 81 | label.push(';'); |
82 | } | 82 | } |
83 | CompletionItem::new(CompletionKind::Magic, ctx.source_range(), &label) | 83 | let mut item = CompletionItem::new(CompletionKind::Magic, ctx.source_range(), &label); |
84 | .kind(SymbolKind::Module) | 84 | item.kind(SymbolKind::Module); |
85 | .add_to(acc) | 85 | item.add_to(acc) |
86 | }); | 86 | }); |
87 | 87 | ||
88 | Some(()) | 88 | Some(()) |
diff --git a/crates/ide_completion/src/completions/postfix.rs b/crates/ide_completion/src/completions/postfix.rs index 9c34ed0b6..ac69b720a 100644 --- a/crates/ide_completion/src/completions/postfix.rs +++ b/crates/ide_completion/src/completions/postfix.rs | |||
@@ -187,6 +187,16 @@ pub(crate) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) { | |||
187 | ctx, | 187 | ctx, |
188 | cap, | 188 | cap, |
189 | &dot_receiver, | 189 | &dot_receiver, |
190 | "err", | ||
191 | "Err(expr)", | ||
192 | &format!("Err({})", receiver_text), | ||
193 | ) | ||
194 | .add_to(acc); | ||
195 | |||
196 | postfix_snippet( | ||
197 | ctx, | ||
198 | cap, | ||
199 | &dot_receiver, | ||
190 | "some", | 200 | "some", |
191 | "Some(expr)", | 201 | "Some(expr)", |
192 | &format!("Some({})", receiver_text), | 202 | &format!("Some({})", receiver_text), |
@@ -287,10 +297,9 @@ fn postfix_snippet( | |||
287 | let delete_range = TextRange::new(receiver_range.start(), ctx.source_range().end()); | 297 | let delete_range = TextRange::new(receiver_range.start(), ctx.source_range().end()); |
288 | TextEdit::replace(delete_range, snippet.to_string()) | 298 | TextEdit::replace(delete_range, snippet.to_string()) |
289 | }; | 299 | }; |
290 | CompletionItem::new(CompletionKind::Postfix, ctx.source_range(), label) | 300 | let mut item = CompletionItem::new(CompletionKind::Postfix, ctx.source_range(), label); |
291 | .detail(detail) | 301 | item.detail(detail).kind(CompletionItemKind::Snippet).snippet_edit(cap, edit); |
292 | .kind(CompletionItemKind::Snippet) | 302 | item |
293 | .snippet_edit(cap, edit) | ||
294 | } | 303 | } |
295 | 304 | ||
296 | #[cfg(test)] | 305 | #[cfg(test)] |
@@ -325,6 +334,7 @@ fn main() { | |||
325 | sn match match expr {} | 334 | sn match match expr {} |
326 | sn box Box::new(expr) | 335 | sn box Box::new(expr) |
327 | sn ok Ok(expr) | 336 | sn ok Ok(expr) |
337 | sn err Err(expr) | ||
328 | sn some Some(expr) | 338 | sn some Some(expr) |
329 | sn dbg dbg!(expr) | 339 | sn dbg dbg!(expr) |
330 | sn dbgr dbg!(&expr) | 340 | sn dbgr dbg!(&expr) |
@@ -357,6 +367,7 @@ fn main() { | |||
357 | sn match match expr {} | 367 | sn match match expr {} |
358 | sn box Box::new(expr) | 368 | sn box Box::new(expr) |
359 | sn ok Ok(expr) | 369 | sn ok Ok(expr) |
370 | sn err Err(expr) | ||
360 | sn some Some(expr) | 371 | sn some Some(expr) |
361 | sn dbg dbg!(expr) | 372 | sn dbg dbg!(expr) |
362 | sn dbgr dbg!(&expr) | 373 | sn dbgr dbg!(&expr) |
@@ -380,6 +391,7 @@ fn main() { | |||
380 | sn match match expr {} | 391 | sn match match expr {} |
381 | sn box Box::new(expr) | 392 | sn box Box::new(expr) |
382 | sn ok Ok(expr) | 393 | sn ok Ok(expr) |
394 | sn err Err(expr) | ||
383 | sn some Some(expr) | 395 | sn some Some(expr) |
384 | sn dbg dbg!(expr) | 396 | sn dbg dbg!(expr) |
385 | sn dbgr dbg!(&expr) | 397 | sn dbgr dbg!(&expr) |
@@ -408,6 +420,7 @@ fn main() { | |||
408 | sn match match expr {} | 420 | sn match match expr {} |
409 | sn box Box::new(expr) | 421 | sn box Box::new(expr) |
410 | sn ok Ok(expr) | 422 | sn ok Ok(expr) |
423 | sn err Err(expr) | ||
411 | sn some Some(expr) | 424 | sn some Some(expr) |
412 | sn dbg dbg!(expr) | 425 | sn dbg dbg!(expr) |
413 | sn dbgr dbg!(&expr) | 426 | sn dbgr dbg!(&expr) |
diff --git a/crates/ide_completion/src/completions/qualified_path.rs b/crates/ide_completion/src/completions/qualified_path.rs index 72fb757b1..df74b739e 100644 --- a/crates/ide_completion/src/completions/qualified_path.rs +++ b/crates/ide_completion/src/completions/qualified_path.rs | |||
@@ -3,7 +3,6 @@ | |||
3 | use hir::{Adt, HasVisibility, PathResolution, ScopeDef}; | 3 | use hir::{Adt, HasVisibility, PathResolution, ScopeDef}; |
4 | use rustc_hash::FxHashSet; | 4 | use rustc_hash::FxHashSet; |
5 | use syntax::AstNode; | 5 | use syntax::AstNode; |
6 | use test_utils::mark; | ||
7 | 6 | ||
8 | use crate::{CompletionContext, Completions}; | 7 | use crate::{CompletionContext, Completions}; |
9 | 8 | ||
@@ -39,7 +38,7 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon | |||
39 | if let Some(name_ref) = ctx.name_ref_syntax.as_ref() { | 38 | if let Some(name_ref) = ctx.name_ref_syntax.as_ref() { |
40 | if name_ref.syntax().text() == name.to_string().as_str() { | 39 | if name_ref.syntax().text() == name.to_string().as_str() { |
41 | // for `use self::foo$0`, don't suggest `foo` as a completion | 40 | // for `use self::foo$0`, don't suggest `foo` as a completion |
42 | mark::hit!(dont_complete_current_use); | 41 | cov_mark::hit!(dont_complete_current_use); |
43 | continue; | 42 | continue; |
44 | } | 43 | } |
45 | } | 44 | } |
@@ -155,7 +154,6 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon | |||
155 | #[cfg(test)] | 154 | #[cfg(test)] |
156 | mod tests { | 155 | mod tests { |
157 | use expect_test::{expect, Expect}; | 156 | use expect_test::{expect, Expect}; |
158 | use test_utils::mark; | ||
159 | 157 | ||
160 | use crate::{ | 158 | use crate::{ |
161 | test_utils::{check_edit, completion_list}, | 159 | test_utils::{check_edit, completion_list}, |
@@ -174,7 +172,7 @@ mod tests { | |||
174 | 172 | ||
175 | #[test] | 173 | #[test] |
176 | fn dont_complete_current_use() { | 174 | fn dont_complete_current_use() { |
177 | mark::check!(dont_complete_current_use); | 175 | cov_mark::check!(dont_complete_current_use); |
178 | check(r#"use self::foo$0;"#, expect![[""]]); | 176 | check(r#"use self::foo$0;"#, expect![[""]]); |
179 | } | 177 | } |
180 | 178 | ||
diff --git a/crates/ide_completion/src/completions/record.rs b/crates/ide_completion/src/completions/record.rs index 0a7927eb8..2f95b8687 100644 --- a/crates/ide_completion/src/completions/record.rs +++ b/crates/ide_completion/src/completions/record.rs | |||
@@ -22,16 +22,13 @@ pub(crate) fn complete_record(acc: &mut Completions, ctx: &CompletionContext) -> | |||
22 | let completion_text = completion_text | 22 | let completion_text = completion_text |
23 | .strip_prefix(ctx.token.to_string().as_str()) | 23 | .strip_prefix(ctx.token.to_string().as_str()) |
24 | .unwrap_or(completion_text); | 24 | .unwrap_or(completion_text); |
25 | acc.add( | 25 | let mut item = CompletionItem::new( |
26 | CompletionItem::new( | 26 | CompletionKind::Snippet, |
27 | CompletionKind::Snippet, | 27 | ctx.source_range(), |
28 | ctx.source_range(), | 28 | "..Default::default()", |
29 | "..Default::default()", | ||
30 | ) | ||
31 | .insert_text(completion_text) | ||
32 | .kind(SymbolKind::Field) | ||
33 | .build(), | ||
34 | ); | 29 | ); |
30 | item.insert_text(completion_text).kind(SymbolKind::Field); | ||
31 | item.add_to(acc); | ||
35 | } | 32 | } |
36 | 33 | ||
37 | missing_fields | 34 | missing_fields |
diff --git a/crates/ide_completion/src/completions/snippet.rs b/crates/ide_completion/src/completions/snippet.rs index df17a15c5..7f7830976 100644 --- a/crates/ide_completion/src/completions/snippet.rs +++ b/crates/ide_completion/src/completions/snippet.rs | |||
@@ -8,9 +8,9 @@ use crate::{ | |||
8 | }; | 8 | }; |
9 | 9 | ||
10 | fn snippet(ctx: &CompletionContext, cap: SnippetCap, label: &str, snippet: &str) -> Builder { | 10 | fn snippet(ctx: &CompletionContext, cap: SnippetCap, label: &str, snippet: &str) -> Builder { |
11 | CompletionItem::new(CompletionKind::Snippet, ctx.source_range(), label) | 11 | let mut item = CompletionItem::new(CompletionKind::Snippet, ctx.source_range(), label); |
12 | .insert_snippet(cap, snippet) | 12 | item.insert_snippet(cap, snippet).kind(CompletionItemKind::Snippet); |
13 | .kind(CompletionItemKind::Snippet) | 13 | item |
14 | } | 14 | } |
15 | 15 | ||
16 | pub(crate) fn complete_expr_snippet(acc: &mut Completions, ctx: &CompletionContext) { | 16 | pub(crate) fn complete_expr_snippet(acc: &mut Completions, ctx: &CompletionContext) { |
@@ -35,7 +35,7 @@ pub(crate) fn complete_item_snippet(acc: &mut Completions, ctx: &CompletionConte | |||
35 | None => return, | 35 | None => return, |
36 | }; | 36 | }; |
37 | 37 | ||
38 | snippet( | 38 | let mut item = snippet( |
39 | ctx, | 39 | ctx, |
40 | cap, | 40 | cap, |
41 | "tmod (Test module)", | 41 | "tmod (Test module)", |
@@ -49,11 +49,11 @@ mod tests { | |||
49 | $0 | 49 | $0 |
50 | } | 50 | } |
51 | }", | 51 | }", |
52 | ) | 52 | ); |
53 | .lookup_by("tmod") | 53 | item.lookup_by("tmod"); |
54 | .add_to(acc); | 54 | item.add_to(acc); |
55 | 55 | ||
56 | snippet( | 56 | let mut item = snippet( |
57 | ctx, | 57 | ctx, |
58 | cap, | 58 | cap, |
59 | "tfn (Test function)", | 59 | "tfn (Test function)", |
@@ -62,11 +62,12 @@ mod tests { | |||
62 | fn ${1:feature}() { | 62 | fn ${1:feature}() { |
63 | $0 | 63 | $0 |
64 | }", | 64 | }", |
65 | ) | 65 | ); |
66 | .lookup_by("tfn") | 66 | item.lookup_by("tfn"); |
67 | .add_to(acc); | 67 | item.add_to(acc); |
68 | 68 | ||
69 | snippet(ctx, cap, "macro_rules", "macro_rules! $1 {\n\t($2) => {\n\t\t$0\n\t};\n}").add_to(acc); | 69 | let item = snippet(ctx, cap, "macro_rules", "macro_rules! $1 {\n\t($2) => {\n\t\t$0\n\t};\n}"); |
70 | item.add_to(acc); | ||
70 | } | 71 | } |
71 | 72 | ||
72 | #[cfg(test)] | 73 | #[cfg(test)] |
diff --git a/crates/ide_completion/src/completions/trait_impl.rs b/crates/ide_completion/src/completions/trait_impl.rs index b999540b8..5a7361f8e 100644 --- a/crates/ide_completion/src/completions/trait_impl.rs +++ b/crates/ide_completion/src/completions/trait_impl.rs | |||
@@ -145,9 +145,8 @@ fn add_function_impl( | |||
145 | format!("fn {}(..)", fn_name) | 145 | format!("fn {}(..)", fn_name) |
146 | }; | 146 | }; |
147 | 147 | ||
148 | let builder = CompletionItem::new(CompletionKind::Magic, ctx.source_range(), label) | 148 | let mut item = CompletionItem::new(CompletionKind::Magic, ctx.source_range(), label); |
149 | .lookup_by(fn_name) | 149 | item.lookup_by(fn_name).set_documentation(func.docs(ctx.db)); |
150 | .set_documentation(func.docs(ctx.db)); | ||
151 | 150 | ||
152 | let completion_kind = if func.self_param(ctx.db).is_some() { | 151 | let completion_kind = if func.self_param(ctx.db).is_some() { |
153 | CompletionItemKind::Method | 152 | CompletionItemKind::Method |
@@ -161,15 +160,15 @@ fn add_function_impl( | |||
161 | match ctx.config.snippet_cap { | 160 | match ctx.config.snippet_cap { |
162 | Some(cap) => { | 161 | Some(cap) => { |
163 | let snippet = format!("{} {{\n $0\n}}", function_decl); | 162 | let snippet = format!("{} {{\n $0\n}}", function_decl); |
164 | builder.snippet_edit(cap, TextEdit::replace(range, snippet)) | 163 | item.snippet_edit(cap, TextEdit::replace(range, snippet)); |
165 | } | 164 | } |
166 | None => { | 165 | None => { |
167 | let header = format!("{} {{", function_decl); | 166 | let header = format!("{} {{", function_decl); |
168 | builder.text_edit(TextEdit::replace(range, header)) | 167 | item.text_edit(TextEdit::replace(range, header)); |
169 | } | 168 | } |
170 | } | 169 | }; |
171 | .kind(completion_kind) | 170 | item.kind(completion_kind); |
172 | .add_to(acc); | 171 | item.add_to(acc); |
173 | } | 172 | } |
174 | } | 173 | } |
175 | 174 | ||
@@ -185,12 +184,12 @@ fn add_type_alias_impl( | |||
185 | 184 | ||
186 | let range = TextRange::new(type_def_node.text_range().start(), ctx.source_range().end()); | 185 | let range = TextRange::new(type_def_node.text_range().start(), ctx.source_range().end()); |
187 | 186 | ||
188 | CompletionItem::new(CompletionKind::Magic, ctx.source_range(), snippet.clone()) | 187 | let mut item = CompletionItem::new(CompletionKind::Magic, ctx.source_range(), snippet.clone()); |
189 | .text_edit(TextEdit::replace(range, snippet)) | 188 | item.text_edit(TextEdit::replace(range, snippet)) |
190 | .lookup_by(alias_name) | 189 | .lookup_by(alias_name) |
191 | .kind(SymbolKind::TypeAlias) | 190 | .kind(SymbolKind::TypeAlias) |
192 | .set_documentation(type_alias.docs(ctx.db)) | 191 | .set_documentation(type_alias.docs(ctx.db)); |
193 | .add_to(acc); | 192 | item.add_to(acc); |
194 | } | 193 | } |
195 | 194 | ||
196 | fn add_const_impl( | 195 | fn add_const_impl( |
@@ -208,12 +207,13 @@ fn add_const_impl( | |||
208 | let range = | 207 | let range = |
209 | TextRange::new(const_def_node.text_range().start(), ctx.source_range().end()); | 208 | TextRange::new(const_def_node.text_range().start(), ctx.source_range().end()); |
210 | 209 | ||
211 | CompletionItem::new(CompletionKind::Magic, ctx.source_range(), snippet.clone()) | 210 | let mut item = |
212 | .text_edit(TextEdit::replace(range, snippet)) | 211 | CompletionItem::new(CompletionKind::Magic, ctx.source_range(), snippet.clone()); |
212 | item.text_edit(TextEdit::replace(range, snippet)) | ||
213 | .lookup_by(const_name) | 213 | .lookup_by(const_name) |
214 | .kind(SymbolKind::Const) | 214 | .kind(SymbolKind::Const) |
215 | .set_documentation(const_.docs(ctx.db)) | 215 | .set_documentation(const_.docs(ctx.db)); |
216 | .add_to(acc); | 216 | item.add_to(acc); |
217 | } | 217 | } |
218 | } | 218 | } |
219 | } | 219 | } |
diff --git a/crates/ide_completion/src/completions/unqualified_path.rs b/crates/ide_completion/src/completions/unqualified_path.rs index e9d0ff665..044dfd160 100644 --- a/crates/ide_completion/src/completions/unqualified_path.rs +++ b/crates/ide_completion/src/completions/unqualified_path.rs | |||
@@ -2,7 +2,6 @@ | |||
2 | 2 | ||
3 | use hir::ScopeDef; | 3 | use hir::ScopeDef; |
4 | use syntax::AstNode; | 4 | use syntax::AstNode; |
5 | use test_utils::mark; | ||
6 | 5 | ||
7 | use crate::{CompletionContext, Completions}; | 6 | use crate::{CompletionContext, Completions}; |
8 | 7 | ||
@@ -30,13 +29,13 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC | |||
30 | 29 | ||
31 | ctx.scope.process_all_names(&mut |name, res| { | 30 | ctx.scope.process_all_names(&mut |name, res| { |
32 | if let ScopeDef::GenericParam(hir::GenericParam::LifetimeParam(_)) = res { | 31 | if let ScopeDef::GenericParam(hir::GenericParam::LifetimeParam(_)) = res { |
33 | mark::hit!(skip_lifetime_completion); | 32 | cov_mark::hit!(skip_lifetime_completion); |
34 | return; | 33 | return; |
35 | } | 34 | } |
36 | if ctx.use_item_syntax.is_some() { | 35 | if ctx.use_item_syntax.is_some() { |
37 | if let (ScopeDef::Unknown, Some(name_ref)) = (&res, &ctx.name_ref_syntax) { | 36 | if let (ScopeDef::Unknown, Some(name_ref)) = (&res, &ctx.name_ref_syntax) { |
38 | if name_ref.syntax().text() == name.to_string().as_str() { | 37 | if name_ref.syntax().text() == name.to_string().as_str() { |
39 | mark::hit!(self_fulfilling_completion); | 38 | cov_mark::hit!(self_fulfilling_completion); |
40 | return; | 39 | return; |
41 | } | 40 | } |
42 | } | 41 | } |
@@ -48,7 +47,6 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC | |||
48 | #[cfg(test)] | 47 | #[cfg(test)] |
49 | mod tests { | 48 | mod tests { |
50 | use expect_test::{expect, Expect}; | 49 | use expect_test::{expect, Expect}; |
51 | use test_utils::mark; | ||
52 | 50 | ||
53 | use crate::{ | 51 | use crate::{ |
54 | test_utils::{check_edit, completion_list_with_config, TEST_CONFIG}, | 52 | test_utils::{check_edit, completion_list_with_config, TEST_CONFIG}, |
@@ -66,7 +64,7 @@ mod tests { | |||
66 | 64 | ||
67 | #[test] | 65 | #[test] |
68 | fn self_fulfilling_completion() { | 66 | fn self_fulfilling_completion() { |
69 | mark::check!(self_fulfilling_completion); | 67 | cov_mark::check!(self_fulfilling_completion); |
70 | check( | 68 | check( |
71 | r#" | 69 | r#" |
72 | use foo$0 | 70 | use foo$0 |
@@ -185,7 +183,7 @@ fn quux() { | |||
185 | 183 | ||
186 | #[test] | 184 | #[test] |
187 | fn completes_if_prefix_is_keyword() { | 185 | fn completes_if_prefix_is_keyword() { |
188 | mark::check!(completes_if_prefix_is_keyword); | 186 | cov_mark::check!(completes_if_prefix_is_keyword); |
189 | check_edit( | 187 | check_edit( |
190 | "wherewolf", | 188 | "wherewolf", |
191 | r#" | 189 | r#" |
@@ -223,7 +221,7 @@ fn main() { | |||
223 | 221 | ||
224 | #[test] | 222 | #[test] |
225 | fn does_not_complete_lifetimes() { | 223 | fn does_not_complete_lifetimes() { |
226 | mark::check!(skip_lifetime_completion); | 224 | cov_mark::check!(skip_lifetime_completion); |
227 | check( | 225 | check( |
228 | r#"fn quux<'a>() { $0 }"#, | 226 | r#"fn quux<'a>() { $0 }"#, |
229 | expect![[r#" | 227 | expect![[r#" |
diff --git a/crates/ide_completion/src/context.rs b/crates/ide_completion/src/context.rs index 3db357855..17d9a3adf 100644 --- a/crates/ide_completion/src/context.rs +++ b/crates/ide_completion/src/context.rs | |||
@@ -7,7 +7,7 @@ use syntax::{ | |||
7 | algo::find_node_at_offset, ast, match_ast, AstNode, NodeOrToken, SyntaxKind::*, SyntaxNode, | 7 | algo::find_node_at_offset, ast, match_ast, AstNode, NodeOrToken, SyntaxKind::*, SyntaxNode, |
8 | SyntaxToken, TextRange, TextSize, | 8 | SyntaxToken, TextRange, TextSize, |
9 | }; | 9 | }; |
10 | use test_utils::mark; | 10 | |
11 | use text_edit::Indel; | 11 | use text_edit::Indel; |
12 | 12 | ||
13 | use crate::{ | 13 | use crate::{ |
@@ -240,7 +240,7 @@ impl<'a> CompletionContext<'a> { | |||
240 | // check kind of macro-expanded token, but use range of original token | 240 | // check kind of macro-expanded token, but use range of original token |
241 | let kind = self.token.kind(); | 241 | let kind = self.token.kind(); |
242 | if kind == IDENT || kind == UNDERSCORE || kind.is_keyword() { | 242 | if kind == IDENT || kind == UNDERSCORE || kind.is_keyword() { |
243 | mark::hit!(completes_if_prefix_is_keyword); | 243 | cov_mark::hit!(completes_if_prefix_is_keyword); |
244 | self.original_token.text_range() | 244 | self.original_token.text_range() |
245 | } else { | 245 | } else { |
246 | TextRange::empty(self.position.offset) | 246 | TextRange::empty(self.position.offset) |
diff --git a/crates/ide_completion/src/item.rs b/crates/ide_completion/src/item.rs index 884711f11..3febab32b 100644 --- a/crates/ide_completion/src/item.rs +++ b/crates/ide_completion/src/item.rs | |||
@@ -2,15 +2,16 @@ | |||
2 | 2 | ||
3 | use std::fmt; | 3 | use std::fmt; |
4 | 4 | ||
5 | use hir::{Documentation, ModPath, Mutability}; | 5 | use hir::{Documentation, Mutability}; |
6 | use ide_db::{ | 6 | use ide_db::{ |
7 | helpers::{ | 7 | helpers::{ |
8 | insert_use::{self, ImportScope, MergeBehavior}, | 8 | import_assets::LocatedImport, |
9 | insert_use::{self, ImportScope, InsertUseConfig}, | ||
9 | mod_path_to_ast, SnippetCap, | 10 | mod_path_to_ast, SnippetCap, |
10 | }, | 11 | }, |
11 | SymbolKind, | 12 | SymbolKind, |
12 | }; | 13 | }; |
13 | use stdx::{impl_from, never}; | 14 | use stdx::{format_to, impl_from, never}; |
14 | use syntax::{algo, TextRange}; | 15 | use syntax::{algo, TextRange}; |
15 | use text_edit::TextEdit; | 16 | use text_edit::TextEdit; |
16 | 17 | ||
@@ -62,12 +63,18 @@ pub struct CompletionItem { | |||
62 | /// after completion. | 63 | /// after completion. |
63 | trigger_call_info: bool, | 64 | trigger_call_info: bool, |
64 | 65 | ||
65 | /// Score is useful to pre select or display in better order completion items | 66 | /// We use this to sort completion. Relevance records facts like "do the |
66 | score: Option<CompletionScore>, | 67 | /// types align precisely?". We can't sort by relevances directly, they are |
68 | /// only partially ordered. | ||
69 | /// | ||
70 | /// Note that Relevance ignores fuzzy match score. We compute Relevance for | ||
71 | /// all possible items, and then separately build an ordered completion list | ||
72 | /// based on relevance and fuzzy matching with the already typed identifier. | ||
73 | relevance: CompletionRelevance, | ||
67 | 74 | ||
68 | /// Indicates that a reference or mutable reference to this variable is a | 75 | /// Indicates that a reference or mutable reference to this variable is a |
69 | /// possible match. | 76 | /// possible match. |
70 | ref_match: Option<(Mutability, CompletionScore)>, | 77 | ref_match: Option<Mutability>, |
71 | 78 | ||
72 | /// The import data to add to completion's edits. | 79 | /// The import data to add to completion's edits. |
73 | import_to_add: Option<ImportEdit>, | 80 | import_to_add: Option<ImportEdit>, |
@@ -100,8 +107,13 @@ impl fmt::Debug for CompletionItem { | |||
100 | if self.deprecated { | 107 | if self.deprecated { |
101 | s.field("deprecated", &true); | 108 | s.field("deprecated", &true); |
102 | } | 109 | } |
103 | if let Some(score) = &self.score { | 110 | |
104 | s.field("score", score); | 111 | if self.relevance != CompletionRelevance::default() { |
112 | s.field("relevance", &self.relevance); | ||
113 | } | ||
114 | |||
115 | if let Some(mutability) = &self.ref_match { | ||
116 | s.field("ref_match", &format!("&{}", mutability.as_keyword_for_ref())); | ||
105 | } | 117 | } |
106 | if self.trigger_call_info { | 118 | if self.trigger_call_info { |
107 | s.field("trigger_call_info", &true); | 119 | s.field("trigger_call_info", &true); |
@@ -110,12 +122,59 @@ impl fmt::Debug for CompletionItem { | |||
110 | } | 122 | } |
111 | } | 123 | } |
112 | 124 | ||
113 | #[derive(Debug, Clone, Copy, Ord, PartialOrd, Eq, PartialEq)] | 125 | #[derive(Debug, Clone, Copy, Ord, PartialOrd, Eq, PartialEq, Default)] |
114 | pub enum CompletionScore { | 126 | pub struct CompletionRelevance { |
115 | /// If only type match | 127 | /// This is set in cases like these: |
116 | TypeMatch, | 128 | /// |
117 | /// If type and name match | 129 | /// ``` |
118 | TypeAndNameMatch, | 130 | /// fn f(spam: String) {} |
131 | /// fn main { | ||
132 | /// let spam = 92; | ||
133 | /// f($0) // name of local matches the name of param | ||
134 | /// } | ||
135 | /// ``` | ||
136 | pub exact_name_match: bool, | ||
137 | /// This is set in cases like these: | ||
138 | /// | ||
139 | /// ``` | ||
140 | /// fn f(spam: String) {} | ||
141 | /// fn main { | ||
142 | /// let foo = String::new(); | ||
143 | /// f($0) // type of local matches the type of param | ||
144 | /// } | ||
145 | /// ``` | ||
146 | pub exact_type_match: bool, | ||
147 | } | ||
148 | |||
149 | impl CompletionRelevance { | ||
150 | /// Provides a relevance score. Higher values are more relevant. | ||
151 | /// | ||
152 | /// The absolute value of the relevance score is not meaningful, for | ||
153 | /// example a value of 0 doesn't mean "not relevant", rather | ||
154 | /// it means "least relevant". The score value should only be used | ||
155 | /// for relative ordering. | ||
156 | /// | ||
157 | /// See is_relevant if you need to make some judgement about score | ||
158 | /// in an absolute sense. | ||
159 | pub fn score(&self) -> u32 { | ||
160 | let mut score = 0; | ||
161 | |||
162 | if self.exact_name_match { | ||
163 | score += 1; | ||
164 | } | ||
165 | if self.exact_type_match { | ||
166 | score += 1; | ||
167 | } | ||
168 | |||
169 | score | ||
170 | } | ||
171 | |||
172 | /// Returns true when the score for this threshold is above | ||
173 | /// some threshold such that we think it is especially likely | ||
174 | /// to be relevant. | ||
175 | pub fn is_relevant(&self) -> bool { | ||
176 | self.score() > 0 | ||
177 | } | ||
119 | } | 178 | } |
120 | 179 | ||
121 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | 180 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] |
@@ -207,9 +266,9 @@ impl CompletionItem { | |||
207 | lookup: None, | 266 | lookup: None, |
208 | kind: None, | 267 | kind: None, |
209 | text_edit: None, | 268 | text_edit: None, |
210 | deprecated: None, | 269 | deprecated: false, |
211 | trigger_call_info: None, | 270 | trigger_call_info: None, |
212 | score: None, | 271 | relevance: CompletionRelevance::default(), |
213 | ref_match: None, | 272 | ref_match: None, |
214 | import_to_add: None, | 273 | import_to_add: None, |
215 | } | 274 | } |
@@ -252,16 +311,22 @@ impl CompletionItem { | |||
252 | self.deprecated | 311 | self.deprecated |
253 | } | 312 | } |
254 | 313 | ||
255 | pub fn score(&self) -> Option<CompletionScore> { | 314 | pub fn relevance(&self) -> CompletionRelevance { |
256 | self.score | 315 | self.relevance |
257 | } | 316 | } |
258 | 317 | ||
259 | pub fn trigger_call_info(&self) -> bool { | 318 | pub fn trigger_call_info(&self) -> bool { |
260 | self.trigger_call_info | 319 | self.trigger_call_info |
261 | } | 320 | } |
262 | 321 | ||
263 | pub fn ref_match(&self) -> Option<(Mutability, CompletionScore)> { | 322 | pub fn ref_match(&self) -> Option<(Mutability, CompletionRelevance)> { |
264 | self.ref_match | 323 | // Relevance of the ref match should be the same as the original |
324 | // match, but with exact type match set because self.ref_match | ||
325 | // is only set if there is an exact type match. | ||
326 | let mut relevance = self.relevance; | ||
327 | relevance.exact_type_match = true; | ||
328 | |||
329 | self.ref_match.map(|mutability| (mutability, relevance)) | ||
265 | } | 330 | } |
266 | 331 | ||
267 | pub fn import_to_add(&self) -> Option<&ImportEdit> { | 332 | pub fn import_to_add(&self) -> Option<&ImportEdit> { |
@@ -272,22 +337,18 @@ impl CompletionItem { | |||
272 | /// An extra import to add after the completion is applied. | 337 | /// An extra import to add after the completion is applied. |
273 | #[derive(Debug, Clone)] | 338 | #[derive(Debug, Clone)] |
274 | pub struct ImportEdit { | 339 | pub struct ImportEdit { |
275 | pub import_path: ModPath, | 340 | pub import: LocatedImport, |
276 | pub import_scope: ImportScope, | 341 | pub scope: ImportScope, |
277 | pub import_for_trait_assoc_item: bool, | ||
278 | } | 342 | } |
279 | 343 | ||
280 | impl ImportEdit { | 344 | impl ImportEdit { |
281 | /// Attempts to insert the import to the given scope, producing a text edit. | 345 | /// Attempts to insert the import to the given scope, producing a text edit. |
282 | /// May return no edit in edge cases, such as scope already containing the import. | 346 | /// May return no edit in edge cases, such as scope already containing the import. |
283 | pub fn to_text_edit(&self, merge_behavior: Option<MergeBehavior>) -> Option<TextEdit> { | 347 | pub fn to_text_edit(&self, cfg: InsertUseConfig) -> Option<TextEdit> { |
284 | let _p = profile::span("ImportEdit::to_text_edit"); | 348 | let _p = profile::span("ImportEdit::to_text_edit"); |
285 | 349 | ||
286 | let rewriter = insert_use::insert_use( | 350 | let rewriter = |
287 | &self.import_scope, | 351 | insert_use::insert_use(&self.scope, mod_path_to_ast(&self.import.import_path), cfg); |
288 | mod_path_to_ast(&self.import_path), | ||
289 | merge_behavior, | ||
290 | ); | ||
291 | let old_ast = rewriter.rewrite_root()?; | 352 | let old_ast = rewriter.rewrite_root()?; |
292 | let mut import_insert = TextEdit::builder(); | 353 | let mut import_insert = TextEdit::builder(); |
293 | algo::diff(&old_ast, &rewriter.rewrite(&old_ast)).into_text_edit(&mut import_insert); | 354 | algo::diff(&old_ast, &rewriter.rewrite(&old_ast)).into_text_edit(&mut import_insert); |
@@ -311,10 +372,10 @@ pub(crate) struct Builder { | |||
311 | lookup: Option<String>, | 372 | lookup: Option<String>, |
312 | kind: Option<CompletionItemKind>, | 373 | kind: Option<CompletionItemKind>, |
313 | text_edit: Option<TextEdit>, | 374 | text_edit: Option<TextEdit>, |
314 | deprecated: Option<bool>, | 375 | deprecated: bool, |
315 | trigger_call_info: Option<bool>, | 376 | trigger_call_info: Option<bool>, |
316 | score: Option<CompletionScore>, | 377 | relevance: CompletionRelevance, |
317 | ref_match: Option<(Mutability, CompletionScore)>, | 378 | ref_match: Option<Mutability>, |
318 | } | 379 | } |
319 | 380 | ||
320 | impl Builder { | 381 | impl Builder { |
@@ -325,20 +386,19 @@ impl Builder { | |||
325 | let mut lookup = self.lookup; | 386 | let mut lookup = self.lookup; |
326 | let mut insert_text = self.insert_text; | 387 | let mut insert_text = self.insert_text; |
327 | 388 | ||
328 | if let Some(import_to_add) = self.import_to_add.as_ref() { | 389 | if let Some(original_path) = self |
329 | if import_to_add.import_for_trait_assoc_item { | 390 | .import_to_add |
330 | lookup = lookup.or_else(|| Some(label.clone())); | 391 | .as_ref() |
331 | insert_text = insert_text.or_else(|| Some(label.clone())); | 392 | .and_then(|import_edit| import_edit.import.original_path.as_ref()) |
332 | label = format!("{} ({})", label, import_to_add.import_path); | 393 | { |
394 | lookup = lookup.or_else(|| Some(label.clone())); | ||
395 | insert_text = insert_text.or_else(|| Some(label.clone())); | ||
396 | |||
397 | let original_path_label = original_path.to_string(); | ||
398 | if original_path_label.ends_with(&label) { | ||
399 | label = original_path_label; | ||
333 | } else { | 400 | } else { |
334 | let mut import_path_without_last_segment = import_to_add.import_path.to_owned(); | 401 | format_to!(label, " ({})", original_path) |
335 | let _ = import_path_without_last_segment.pop_segment(); | ||
336 | |||
337 | if !import_path_without_last_segment.segments().is_empty() { | ||
338 | lookup = lookup.or_else(|| Some(label.clone())); | ||
339 | insert_text = insert_text.or_else(|| Some(label.clone())); | ||
340 | label = format!("{}::{}", import_path_without_last_segment, label); | ||
341 | } | ||
342 | } | 402 | } |
343 | } | 403 | } |
344 | 404 | ||
@@ -359,49 +419,49 @@ impl Builder { | |||
359 | lookup, | 419 | lookup, |
360 | kind: self.kind, | 420 | kind: self.kind, |
361 | completion_kind: self.completion_kind, | 421 | completion_kind: self.completion_kind, |
362 | deprecated: self.deprecated.unwrap_or(false), | 422 | deprecated: self.deprecated, |
363 | trigger_call_info: self.trigger_call_info.unwrap_or(false), | 423 | trigger_call_info: self.trigger_call_info.unwrap_or(false), |
364 | score: self.score, | 424 | relevance: self.relevance, |
365 | ref_match: self.ref_match, | 425 | ref_match: self.ref_match, |
366 | import_to_add: self.import_to_add, | 426 | import_to_add: self.import_to_add, |
367 | } | 427 | } |
368 | } | 428 | } |
369 | pub(crate) fn lookup_by(mut self, lookup: impl Into<String>) -> Builder { | 429 | pub(crate) fn lookup_by(&mut self, lookup: impl Into<String>) -> &mut Builder { |
370 | self.lookup = Some(lookup.into()); | 430 | self.lookup = Some(lookup.into()); |
371 | self | 431 | self |
372 | } | 432 | } |
373 | pub(crate) fn label(mut self, label: impl Into<String>) -> Builder { | 433 | pub(crate) fn label(&mut self, label: impl Into<String>) -> &mut Builder { |
374 | self.label = label.into(); | 434 | self.label = label.into(); |
375 | self | 435 | self |
376 | } | 436 | } |
377 | pub(crate) fn insert_text(mut self, insert_text: impl Into<String>) -> Builder { | 437 | pub(crate) fn insert_text(&mut self, insert_text: impl Into<String>) -> &mut Builder { |
378 | self.insert_text = Some(insert_text.into()); | 438 | self.insert_text = Some(insert_text.into()); |
379 | self | 439 | self |
380 | } | 440 | } |
381 | pub(crate) fn insert_snippet( | 441 | pub(crate) fn insert_snippet( |
382 | mut self, | 442 | &mut self, |
383 | _cap: SnippetCap, | 443 | _cap: SnippetCap, |
384 | snippet: impl Into<String>, | 444 | snippet: impl Into<String>, |
385 | ) -> Builder { | 445 | ) -> &mut Builder { |
386 | self.insert_text_format = InsertTextFormat::Snippet; | 446 | self.insert_text_format = InsertTextFormat::Snippet; |
387 | self.insert_text(snippet) | 447 | self.insert_text(snippet) |
388 | } | 448 | } |
389 | pub(crate) fn kind(mut self, kind: impl Into<CompletionItemKind>) -> Builder { | 449 | pub(crate) fn kind(&mut self, kind: impl Into<CompletionItemKind>) -> &mut Builder { |
390 | self.kind = Some(kind.into()); | 450 | self.kind = Some(kind.into()); |
391 | self | 451 | self |
392 | } | 452 | } |
393 | pub(crate) fn text_edit(mut self, edit: TextEdit) -> Builder { | 453 | pub(crate) fn text_edit(&mut self, edit: TextEdit) -> &mut Builder { |
394 | self.text_edit = Some(edit); | 454 | self.text_edit = Some(edit); |
395 | self | 455 | self |
396 | } | 456 | } |
397 | pub(crate) fn snippet_edit(mut self, _cap: SnippetCap, edit: TextEdit) -> Builder { | 457 | pub(crate) fn snippet_edit(&mut self, _cap: SnippetCap, edit: TextEdit) -> &mut Builder { |
398 | self.insert_text_format = InsertTextFormat::Snippet; | 458 | self.insert_text_format = InsertTextFormat::Snippet; |
399 | self.text_edit(edit) | 459 | self.text_edit(edit) |
400 | } | 460 | } |
401 | pub(crate) fn detail(self, detail: impl Into<String>) -> Builder { | 461 | pub(crate) fn detail(&mut self, detail: impl Into<String>) -> &mut Builder { |
402 | self.set_detail(Some(detail)) | 462 | self.set_detail(Some(detail)) |
403 | } | 463 | } |
404 | pub(crate) fn set_detail(mut self, detail: Option<impl Into<String>>) -> Builder { | 464 | pub(crate) fn set_detail(&mut self, detail: Option<impl Into<String>>) -> &mut Builder { |
405 | self.detail = detail.map(Into::into); | 465 | self.detail = detail.map(Into::into); |
406 | if let Some(detail) = &self.detail { | 466 | if let Some(detail) = &self.detail { |
407 | if never!(detail.contains('\n'), "multiline detail:\n{}", detail) { | 467 | if never!(detail.contains('\n'), "multiline detail:\n{}", detail) { |
@@ -411,40 +471,91 @@ impl Builder { | |||
411 | self | 471 | self |
412 | } | 472 | } |
413 | #[allow(unused)] | 473 | #[allow(unused)] |
414 | pub(crate) fn documentation(self, docs: Documentation) -> Builder { | 474 | pub(crate) fn documentation(&mut self, docs: Documentation) -> &mut Builder { |
415 | self.set_documentation(Some(docs)) | 475 | self.set_documentation(Some(docs)) |
416 | } | 476 | } |
417 | pub(crate) fn set_documentation(mut self, docs: Option<Documentation>) -> Builder { | 477 | pub(crate) fn set_documentation(&mut self, docs: Option<Documentation>) -> &mut Builder { |
418 | self.documentation = docs.map(Into::into); | 478 | self.documentation = docs.map(Into::into); |
419 | self | 479 | self |
420 | } | 480 | } |
421 | pub(crate) fn set_deprecated(mut self, deprecated: bool) -> Builder { | 481 | pub(crate) fn set_deprecated(&mut self, deprecated: bool) -> &mut Builder { |
422 | self.deprecated = Some(deprecated); | 482 | self.deprecated = deprecated; |
423 | self | 483 | self |
424 | } | 484 | } |
425 | pub(crate) fn set_score(mut self, score: CompletionScore) -> Builder { | 485 | pub(crate) fn set_relevance(&mut self, relevance: CompletionRelevance) -> &mut Builder { |
426 | self.score = Some(score); | 486 | self.relevance = relevance; |
427 | self | 487 | self |
428 | } | 488 | } |
429 | pub(crate) fn trigger_call_info(mut self) -> Builder { | 489 | pub(crate) fn trigger_call_info(&mut self) -> &mut Builder { |
430 | self.trigger_call_info = Some(true); | 490 | self.trigger_call_info = Some(true); |
431 | self | 491 | self |
432 | } | 492 | } |
433 | pub(crate) fn add_import(mut self, import_to_add: Option<ImportEdit>) -> Builder { | 493 | pub(crate) fn add_import(&mut self, import_to_add: Option<ImportEdit>) -> &mut Builder { |
434 | self.import_to_add = import_to_add; | 494 | self.import_to_add = import_to_add; |
435 | self | 495 | self |
436 | } | 496 | } |
437 | pub(crate) fn set_ref_match( | 497 | pub(crate) fn ref_match(&mut self, mutability: Mutability) -> &mut Builder { |
438 | mut self, | 498 | self.ref_match = Some(mutability); |
439 | ref_match: Option<(Mutability, CompletionScore)>, | ||
440 | ) -> Builder { | ||
441 | self.ref_match = ref_match; | ||
442 | self | 499 | self |
443 | } | 500 | } |
444 | } | 501 | } |
445 | 502 | ||
446 | impl<'a> Into<CompletionItem> for Builder { | 503 | #[cfg(test)] |
447 | fn into(self) -> CompletionItem { | 504 | mod tests { |
448 | self.build() | 505 | use itertools::Itertools; |
506 | use test_utils::assert_eq_text; | ||
507 | |||
508 | use super::CompletionRelevance; | ||
509 | |||
510 | /// Check that these are CompletionRelevance are sorted in ascending order | ||
511 | /// by their relevance score. | ||
512 | /// | ||
513 | /// We want to avoid making assertions about the absolute score of any | ||
514 | /// item, but we do want to assert whether each is >, <, or == to the | ||
515 | /// others. | ||
516 | /// | ||
517 | /// If provided vec![vec![a], vec![b, c], vec![d]], then this will assert: | ||
518 | /// a.score < b.score == c.score < d.score | ||
519 | fn check_relevance_score_ordered(expected_relevance_order: Vec<Vec<CompletionRelevance>>) { | ||
520 | let expected = format!("{:#?}", &expected_relevance_order); | ||
521 | |||
522 | let actual_relevance_order = expected_relevance_order | ||
523 | .into_iter() | ||
524 | .flatten() | ||
525 | .map(|r| (r.score(), r)) | ||
526 | .sorted_by_key(|(score, _r)| *score) | ||
527 | .fold( | ||
528 | (u32::MIN, vec![vec![]]), | ||
529 | |(mut currently_collecting_score, mut out), (score, r)| { | ||
530 | if currently_collecting_score == score { | ||
531 | out.last_mut().unwrap().push(r); | ||
532 | } else { | ||
533 | currently_collecting_score = score; | ||
534 | out.push(vec![r]); | ||
535 | } | ||
536 | (currently_collecting_score, out) | ||
537 | }, | ||
538 | ) | ||
539 | .1; | ||
540 | |||
541 | let actual = format!("{:#?}", &actual_relevance_order); | ||
542 | |||
543 | assert_eq_text!(&expected, &actual); | ||
544 | } | ||
545 | |||
546 | #[test] | ||
547 | fn relevance_score() { | ||
548 | // This test asserts that the relevance score for these items is ascending, and | ||
549 | // that any items in the same vec have the same score. | ||
550 | let expected_relevance_order = vec![ | ||
551 | vec![CompletionRelevance::default()], | ||
552 | vec![ | ||
553 | CompletionRelevance { exact_name_match: true, ..CompletionRelevance::default() }, | ||
554 | CompletionRelevance { exact_type_match: true, ..CompletionRelevance::default() }, | ||
555 | ], | ||
556 | vec![CompletionRelevance { exact_name_match: true, exact_type_match: true }], | ||
557 | ]; | ||
558 | |||
559 | check_relevance_score_ordered(expected_relevance_order); | ||
449 | } | 560 | } |
450 | } | 561 | } |
diff --git a/crates/ide_completion/src/lib.rs b/crates/ide_completion/src/lib.rs index 76f31de9e..263554ecf 100644 --- a/crates/ide_completion/src/lib.rs +++ b/crates/ide_completion/src/lib.rs | |||
@@ -13,7 +13,9 @@ mod completions; | |||
13 | 13 | ||
14 | use completions::flyimport::position_for_import; | 14 | use completions::flyimport::position_for_import; |
15 | use ide_db::{ | 15 | use ide_db::{ |
16 | base_db::FilePosition, helpers::insert_use::ImportScope, imports_locator, RootDatabase, | 16 | base_db::FilePosition, |
17 | helpers::{import_assets::LocatedImport, insert_use::ImportScope}, | ||
18 | items_locator, RootDatabase, | ||
17 | }; | 19 | }; |
18 | use text_edit::TextEdit; | 20 | use text_edit::TextEdit; |
19 | 21 | ||
@@ -21,7 +23,7 @@ use crate::{completions::Completions, context::CompletionContext, item::Completi | |||
21 | 23 | ||
22 | pub use crate::{ | 24 | pub use crate::{ |
23 | config::CompletionConfig, | 25 | config::CompletionConfig, |
24 | item::{CompletionItem, CompletionItemKind, CompletionScore, ImportEdit, InsertTextFormat}, | 26 | item::{CompletionItem, CompletionItemKind, CompletionRelevance, ImportEdit, InsertTextFormat}, |
25 | }; | 27 | }; |
26 | 28 | ||
27 | //FIXME: split the following feature into fine-grained features. | 29 | //FIXME: split the following feature into fine-grained features. |
@@ -139,25 +141,27 @@ pub fn resolve_completion_edits( | |||
139 | position: FilePosition, | 141 | position: FilePosition, |
140 | full_import_path: &str, | 142 | full_import_path: &str, |
141 | imported_name: String, | 143 | imported_name: String, |
142 | import_for_trait_assoc_item: bool, | ||
143 | ) -> Option<Vec<TextEdit>> { | 144 | ) -> Option<Vec<TextEdit>> { |
144 | let ctx = CompletionContext::new(db, position, config)?; | 145 | let ctx = CompletionContext::new(db, position, config)?; |
145 | let position_for_import = position_for_import(&ctx, None)?; | 146 | let position_for_import = position_for_import(&ctx, None)?; |
146 | let import_scope = ImportScope::find_insert_use_container(position_for_import, &ctx.sema)?; | 147 | let scope = ImportScope::find_insert_use_container(position_for_import, &ctx.sema)?; |
147 | 148 | ||
148 | let current_module = ctx.sema.scope(position_for_import).module()?; | 149 | let current_module = ctx.sema.scope(position_for_import).module()?; |
149 | let current_crate = current_module.krate(); | 150 | let current_crate = current_module.krate(); |
150 | 151 | ||
151 | let import_path = imports_locator::find_exact_imports(&ctx.sema, current_crate, imported_name) | 152 | let (import_path, item_to_import) = |
152 | .filter_map(|candidate| { | 153 | items_locator::with_exact_name(&ctx.sema, current_crate, imported_name) |
153 | let item: hir::ItemInNs = candidate.either(Into::into, Into::into); | 154 | .into_iter() |
154 | current_module.find_use_path_prefixed(db, item, config.insert_use.prefix_kind) | 155 | .filter_map(|candidate| { |
155 | }) | 156 | current_module |
156 | .find(|mod_path| mod_path.to_string() == full_import_path)?; | 157 | .find_use_path_prefixed(db, candidate, config.insert_use.prefix_kind) |
158 | .zip(Some(candidate)) | ||
159 | }) | ||
160 | .find(|(mod_path, _)| mod_path.to_string() == full_import_path)?; | ||
161 | let import = | ||
162 | LocatedImport::new(import_path.clone(), item_to_import, item_to_import, Some(import_path)); | ||
157 | 163 | ||
158 | ImportEdit { import_path, import_scope, import_for_trait_assoc_item } | 164 | ImportEdit { import, scope }.to_text_edit(config.insert_use).map(|edit| vec![edit]) |
159 | .to_text_edit(config.insert_use.merge) | ||
160 | .map(|edit| vec![edit]) | ||
161 | } | 165 | } |
162 | 166 | ||
163 | #[cfg(test)] | 167 | #[cfg(test)] |
diff --git a/crates/ide_completion/src/render.rs b/crates/ide_completion/src/render.rs index eddaaa6f3..db31896e5 100644 --- a/crates/ide_completion/src/render.rs +++ b/crates/ide_completion/src/render.rs | |||
@@ -13,13 +13,15 @@ mod builder_ext; | |||
13 | use hir::{ | 13 | use hir::{ |
14 | AsAssocItem, Documentation, HasAttrs, HirDisplay, ModuleDef, Mutability, ScopeDef, Type, | 14 | AsAssocItem, Documentation, HasAttrs, HirDisplay, ModuleDef, Mutability, ScopeDef, Type, |
15 | }; | 15 | }; |
16 | use ide_db::{helpers::SnippetCap, RootDatabase, SymbolKind}; | 16 | use ide_db::{ |
17 | helpers::{item_name, SnippetCap}, | ||
18 | RootDatabase, SymbolKind, | ||
19 | }; | ||
17 | use syntax::TextRange; | 20 | use syntax::TextRange; |
18 | use test_utils::mark; | ||
19 | 21 | ||
20 | use crate::{ | 22 | use crate::{ |
21 | item::ImportEdit, CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, | 23 | item::{CompletionRelevance, ImportEdit}, |
22 | CompletionScore, | 24 | CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, |
23 | }; | 25 | }; |
24 | 26 | ||
25 | use crate::render::{enum_variant::render_variant, function::render_fn, macro_::render_macro}; | 27 | use crate::render::{enum_variant::render_variant, function::render_fn, macro_::render_macro}; |
@@ -51,18 +53,20 @@ pub(crate) fn render_resolution<'a>( | |||
51 | pub(crate) fn render_resolution_with_import<'a>( | 53 | pub(crate) fn render_resolution_with_import<'a>( |
52 | ctx: RenderContext<'a>, | 54 | ctx: RenderContext<'a>, |
53 | import_edit: ImportEdit, | 55 | import_edit: ImportEdit, |
54 | resolution: &ScopeDef, | ||
55 | ) -> Option<CompletionItem> { | 56 | ) -> Option<CompletionItem> { |
57 | let resolution = ScopeDef::from(import_edit.import.original_item); | ||
56 | let local_name = match resolution { | 58 | let local_name = match resolution { |
57 | ScopeDef::ModuleDef(ModuleDef::Function(f)) => f.name(ctx.completion.db).to_string(), | 59 | ScopeDef::ModuleDef(ModuleDef::Function(f)) => f.name(ctx.completion.db).to_string(), |
58 | ScopeDef::ModuleDef(ModuleDef::Const(c)) => c.name(ctx.completion.db)?.to_string(), | 60 | ScopeDef::ModuleDef(ModuleDef::Const(c)) => c.name(ctx.completion.db)?.to_string(), |
59 | ScopeDef::ModuleDef(ModuleDef::TypeAlias(t)) => t.name(ctx.completion.db).to_string(), | 61 | ScopeDef::ModuleDef(ModuleDef::TypeAlias(t)) => t.name(ctx.completion.db).to_string(), |
60 | _ => import_edit.import_path.segments().last()?.to_string(), | 62 | _ => item_name(ctx.db(), import_edit.import.original_item)?.to_string(), |
61 | }; | 63 | }; |
62 | Render::new(ctx).render_resolution(local_name, Some(import_edit), resolution).map(|mut item| { | 64 | Render::new(ctx).render_resolution(local_name, Some(import_edit), &resolution).map( |
63 | item.completion_kind = CompletionKind::Magic; | 65 | |mut item| { |
64 | item | 66 | item.completion_kind = CompletionKind::Magic; |
65 | }) | 67 | item |
68 | }, | ||
69 | ) | ||
66 | } | 70 | } |
67 | 71 | ||
68 | /// Interface for data and methods required for items rendering. | 72 | /// Interface for data and methods required for items rendering. |
@@ -113,13 +117,13 @@ impl<'a> RenderContext<'a> { | |||
113 | node.docs(self.db()) | 117 | node.docs(self.db()) |
114 | } | 118 | } |
115 | 119 | ||
116 | fn active_name_and_type(&self) -> Option<(String, Type)> { | 120 | fn expected_name_and_type(&self) -> Option<(String, Type)> { |
117 | if let Some(record_field) = &self.completion.record_field_syntax { | 121 | if let Some(record_field) = &self.completion.record_field_syntax { |
118 | mark::hit!(record_field_type_match); | 122 | cov_mark::hit!(record_field_type_match); |
119 | let (struct_field, _local) = self.completion.sema.resolve_record_field(record_field)?; | 123 | let (struct_field, _local) = self.completion.sema.resolve_record_field(record_field)?; |
120 | Some((struct_field.name(self.db()).to_string(), struct_field.signature_ty(self.db()))) | 124 | Some((struct_field.name(self.db()).to_string(), struct_field.signature_ty(self.db()))) |
121 | } else if let Some(active_parameter) = &self.completion.active_parameter { | 125 | } else if let Some(active_parameter) = &self.completion.active_parameter { |
122 | mark::hit!(active_param_type_match); | 126 | cov_mark::hit!(active_param_type_match); |
123 | Some((active_parameter.name.clone(), active_parameter.ty.clone())) | 127 | Some((active_parameter.name.clone(), active_parameter.ty.clone())) |
124 | } else { | 128 | } else { |
125 | None | 129 | None |
@@ -145,24 +149,29 @@ impl<'a> Render<'a> { | |||
145 | CompletionKind::Reference, | 149 | CompletionKind::Reference, |
146 | self.ctx.source_range(), | 150 | self.ctx.source_range(), |
147 | name.to_string(), | 151 | name.to_string(), |
148 | ) | 152 | ); |
149 | .kind(SymbolKind::Field) | 153 | item.kind(SymbolKind::Field) |
150 | .detail(ty.display(self.ctx.db()).to_string()) | 154 | .detail(ty.display(self.ctx.db()).to_string()) |
151 | .set_documentation(field.docs(self.ctx.db())) | 155 | .set_documentation(field.docs(self.ctx.db())) |
152 | .set_deprecated(is_deprecated); | 156 | .set_deprecated(is_deprecated); |
153 | 157 | ||
154 | if let Some(score) = compute_score(&self.ctx, &ty, &name.to_string()) { | 158 | if let Some(relevance) = compute_relevance(&self.ctx, &ty, &name.to_string()) { |
155 | item = item.set_score(score); | 159 | item.set_relevance(relevance); |
156 | } | 160 | } |
157 | 161 | ||
158 | item.build() | 162 | item.build() |
159 | } | 163 | } |
160 | 164 | ||
161 | fn add_tuple_field(&mut self, field: usize, ty: &Type) -> CompletionItem { | 165 | fn add_tuple_field(&mut self, field: usize, ty: &Type) -> CompletionItem { |
162 | CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), field.to_string()) | 166 | let mut item = CompletionItem::new( |
163 | .kind(SymbolKind::Field) | 167 | CompletionKind::Reference, |
164 | .detail(ty.display(self.ctx.db()).to_string()) | 168 | self.ctx.source_range(), |
165 | .build() | 169 | field.to_string(), |
170 | ); | ||
171 | |||
172 | item.kind(SymbolKind::Field).detail(ty.display(self.ctx.db()).to_string()); | ||
173 | |||
174 | item.build() | ||
166 | } | 175 | } |
167 | 176 | ||
168 | fn render_resolution( | 177 | fn render_resolution( |
@@ -221,15 +230,13 @@ impl<'a> Render<'a> { | |||
221 | CompletionItemKind::SymbolKind(SymbolKind::SelfParam) | 230 | CompletionItemKind::SymbolKind(SymbolKind::SelfParam) |
222 | } | 231 | } |
223 | ScopeDef::Unknown => { | 232 | ScopeDef::Unknown => { |
224 | let item = CompletionItem::new( | 233 | let mut item = CompletionItem::new( |
225 | CompletionKind::Reference, | 234 | CompletionKind::Reference, |
226 | self.ctx.source_range(), | 235 | self.ctx.source_range(), |
227 | local_name, | 236 | local_name, |
228 | ) | 237 | ); |
229 | .kind(CompletionItemKind::UnresolvedReference) | 238 | item.kind(CompletionItemKind::UnresolvedReference).add_import(import_to_add); |
230 | .add_import(import_to_add) | 239 | return Some(item.build()); |
231 | .build(); | ||
232 | return Some(item); | ||
233 | } | 240 | } |
234 | }; | 241 | }; |
235 | 242 | ||
@@ -238,20 +245,27 @@ impl<'a> Render<'a> { | |||
238 | if let ScopeDef::Local(local) = resolution { | 245 | if let ScopeDef::Local(local) = resolution { |
239 | let ty = local.ty(self.ctx.db()); | 246 | let ty = local.ty(self.ctx.db()); |
240 | if !ty.is_unknown() { | 247 | if !ty.is_unknown() { |
241 | item = item.detail(ty.display(self.ctx.db()).to_string()); | 248 | item.detail(ty.display(self.ctx.db()).to_string()); |
242 | } | 249 | } |
243 | }; | 250 | }; |
244 | 251 | ||
245 | let mut ref_match = None; | ||
246 | if let ScopeDef::Local(local) = resolution { | 252 | if let ScopeDef::Local(local) = resolution { |
247 | if let Some((active_name, active_type)) = self.ctx.active_name_and_type() { | 253 | let ty = local.ty(self.ctx.db()); |
248 | let ty = local.ty(self.ctx.db()); | 254 | if let Some(relevance) = compute_relevance(&self.ctx, &ty, &local_name) { |
249 | if let Some(score) = | 255 | item.set_relevance(relevance); |
250 | compute_score_from_active(&active_type, &active_name, &ty, &local_name) | 256 | } |
251 | { | 257 | if let Some((_expected_name, expected_type)) = self.ctx.expected_name_and_type() { |
252 | item = item.set_score(score); | 258 | if let Some(ty_without_ref) = expected_type.remove_ref() { |
259 | if ty_without_ref == ty { | ||
260 | cov_mark::hit!(suggest_ref); | ||
261 | let mutability = if expected_type.is_mutable_reference() { | ||
262 | Mutability::Mut | ||
263 | } else { | ||
264 | Mutability::Shared | ||
265 | }; | ||
266 | item.ref_match(mutability); | ||
267 | } | ||
253 | } | 268 | } |
254 | ref_match = refed_type_matches(&active_type, &active_name, &ty, &local_name); | ||
255 | } | 269 | } |
256 | } | 270 | } |
257 | 271 | ||
@@ -269,23 +283,18 @@ impl<'a> Render<'a> { | |||
269 | _ => false, | 283 | _ => false, |
270 | }; | 284 | }; |
271 | if has_non_default_type_params { | 285 | if has_non_default_type_params { |
272 | mark::hit!(inserts_angle_brackets_for_generics); | 286 | cov_mark::hit!(inserts_angle_brackets_for_generics); |
273 | item = item | 287 | item.lookup_by(local_name.clone()) |
274 | .lookup_by(local_name.clone()) | ||
275 | .label(format!("{}<…>", local_name)) | 288 | .label(format!("{}<…>", local_name)) |
276 | .insert_snippet(cap, format!("{}<$0>", local_name)); | 289 | .insert_snippet(cap, format!("{}<$0>", local_name)); |
277 | } | 290 | } |
278 | } | 291 | } |
279 | } | 292 | } |
280 | 293 | item.kind(kind) | |
281 | Some( | 294 | .add_import(import_to_add) |
282 | item.kind(kind) | 295 | .set_documentation(self.docs(resolution)) |
283 | .add_import(import_to_add) | 296 | .set_deprecated(self.is_deprecated(resolution)); |
284 | .set_ref_match(ref_match) | 297 | Some(item.build()) |
285 | .set_documentation(self.docs(resolution)) | ||
286 | .set_deprecated(self.is_deprecated(resolution)) | ||
287 | .build(), | ||
288 | ) | ||
289 | } | 298 | } |
290 | 299 | ||
291 | fn docs(&self, resolution: &ScopeDef) -> Option<Documentation> { | 300 | fn docs(&self, resolution: &ScopeDef) -> Option<Documentation> { |
@@ -313,56 +322,23 @@ impl<'a> Render<'a> { | |||
313 | } | 322 | } |
314 | } | 323 | } |
315 | 324 | ||
316 | fn compute_score_from_active( | 325 | fn compute_relevance(ctx: &RenderContext, ty: &Type, name: &str) -> Option<CompletionRelevance> { |
317 | active_type: &Type, | 326 | let (expected_name, expected_type) = ctx.expected_name_and_type()?; |
318 | active_name: &str, | 327 | let mut res = CompletionRelevance::default(); |
319 | ty: &Type, | 328 | res.exact_type_match = ty == &expected_type; |
320 | name: &str, | 329 | res.exact_name_match = name == &expected_name; |
321 | ) -> Option<CompletionScore> { | ||
322 | // Compute score | ||
323 | // For the same type | ||
324 | if active_type != ty { | ||
325 | return None; | ||
326 | } | ||
327 | |||
328 | let mut res = CompletionScore::TypeMatch; | ||
329 | |||
330 | // If same type + same name then go top position | ||
331 | if active_name == name { | ||
332 | res = CompletionScore::TypeAndNameMatch | ||
333 | } | ||
334 | |||
335 | Some(res) | 330 | Some(res) |
336 | } | 331 | } |
337 | fn refed_type_matches( | ||
338 | active_type: &Type, | ||
339 | active_name: &str, | ||
340 | ty: &Type, | ||
341 | name: &str, | ||
342 | ) -> Option<(Mutability, CompletionScore)> { | ||
343 | let derefed_active = active_type.remove_ref()?; | ||
344 | let score = compute_score_from_active(&derefed_active, &active_name, &ty, &name)?; | ||
345 | Some(( | ||
346 | if active_type.is_mutable_reference() { Mutability::Mut } else { Mutability::Shared }, | ||
347 | score, | ||
348 | )) | ||
349 | } | ||
350 | |||
351 | fn compute_score(ctx: &RenderContext, ty: &Type, name: &str) -> Option<CompletionScore> { | ||
352 | let (active_name, active_type) = ctx.active_name_and_type()?; | ||
353 | compute_score_from_active(&active_type, &active_name, ty, name) | ||
354 | } | ||
355 | 332 | ||
356 | #[cfg(test)] | 333 | #[cfg(test)] |
357 | mod tests { | 334 | mod tests { |
358 | use std::cmp::Reverse; | 335 | use std::cmp::Reverse; |
359 | 336 | ||
360 | use expect_test::{expect, Expect}; | 337 | use expect_test::{expect, Expect}; |
361 | use test_utils::mark; | ||
362 | 338 | ||
363 | use crate::{ | 339 | use crate::{ |
364 | test_utils::{check_edit, do_completion, get_all_items, TEST_CONFIG}, | 340 | test_utils::{check_edit, do_completion, get_all_items, TEST_CONFIG}, |
365 | CompletionKind, CompletionScore, | 341 | CompletionKind, CompletionRelevance, |
366 | }; | 342 | }; |
367 | 343 | ||
368 | fn check(ra_fixture: &str, expect: Expect) { | 344 | fn check(ra_fixture: &str, expect: Expect) { |
@@ -370,24 +346,27 @@ mod tests { | |||
370 | expect.assert_debug_eq(&actual); | 346 | expect.assert_debug_eq(&actual); |
371 | } | 347 | } |
372 | 348 | ||
373 | fn check_scores(ra_fixture: &str, expect: Expect) { | 349 | fn check_relevance(ra_fixture: &str, expect: Expect) { |
374 | fn display_score(score: Option<CompletionScore>) -> &'static str { | 350 | fn display_relevance(relevance: CompletionRelevance) -> &'static str { |
375 | match score { | 351 | match relevance { |
376 | Some(CompletionScore::TypeMatch) => "[type]", | 352 | CompletionRelevance { exact_type_match: true, exact_name_match: true } => { |
377 | Some(CompletionScore::TypeAndNameMatch) => "[type+name]", | 353 | "[type+name]" |
378 | None => "[]".into(), | 354 | } |
355 | CompletionRelevance { exact_type_match: true, exact_name_match: false } => "[type]", | ||
356 | CompletionRelevance { exact_type_match: false, exact_name_match: true } => "[name]", | ||
357 | CompletionRelevance { exact_type_match: false, exact_name_match: false } => "[]", | ||
379 | } | 358 | } |
380 | } | 359 | } |
381 | 360 | ||
382 | let mut completions = get_all_items(TEST_CONFIG, ra_fixture); | 361 | let mut completions = get_all_items(TEST_CONFIG, ra_fixture); |
383 | completions.sort_by_key(|it| (Reverse(it.score()), it.label().to_string())); | 362 | completions.sort_by_key(|it| (Reverse(it.relevance()), it.label().to_string())); |
384 | let actual = completions | 363 | let actual = completions |
385 | .into_iter() | 364 | .into_iter() |
386 | .filter(|it| it.completion_kind == CompletionKind::Reference) | 365 | .filter(|it| it.completion_kind == CompletionKind::Reference) |
387 | .map(|it| { | 366 | .map(|it| { |
388 | let tag = it.kind().unwrap().tag(); | 367 | let tag = it.kind().unwrap().tag(); |
389 | let score = display_score(it.score()); | 368 | let relevance = display_relevance(it.relevance()); |
390 | format!("{} {} {}\n", tag, it.label(), score) | 369 | format!("{} {} {}\n", tag, it.label(), relevance) |
391 | }) | 370 | }) |
392 | .collect::<String>(); | 371 | .collect::<String>(); |
393 | expect.assert_eq(&actual); | 372 | expect.assert_eq(&actual); |
@@ -734,7 +713,7 @@ fn foo(s: S) { s.$0 } | |||
734 | 713 | ||
735 | #[test] | 714 | #[test] |
736 | fn no_call_parens_if_fn_ptr_needed() { | 715 | fn no_call_parens_if_fn_ptr_needed() { |
737 | mark::check!(no_call_parens_if_fn_ptr_needed); | 716 | cov_mark::check!(no_call_parens_if_fn_ptr_needed); |
738 | check_edit( | 717 | check_edit( |
739 | "foo", | 718 | "foo", |
740 | r#" | 719 | r#" |
@@ -758,7 +737,7 @@ fn main() -> ManualVtable { | |||
758 | 737 | ||
759 | #[test] | 738 | #[test] |
760 | fn no_parens_in_use_item() { | 739 | fn no_parens_in_use_item() { |
761 | mark::check!(no_parens_in_use_item); | 740 | cov_mark::check!(no_parens_in_use_item); |
762 | check_edit( | 741 | check_edit( |
763 | "foo", | 742 | "foo", |
764 | r#" | 743 | r#" |
@@ -802,7 +781,7 @@ fn f(foo: &Foo) { foo.foo(); } | |||
802 | 781 | ||
803 | #[test] | 782 | #[test] |
804 | fn inserts_angle_brackets_for_generics() { | 783 | fn inserts_angle_brackets_for_generics() { |
805 | mark::check!(inserts_angle_brackets_for_generics); | 784 | cov_mark::check!(inserts_angle_brackets_for_generics); |
806 | check_edit( | 785 | check_edit( |
807 | "Vec", | 786 | "Vec", |
808 | r#" | 787 | r#" |
@@ -850,9 +829,9 @@ fn foo(xs: Vec<i128>) | |||
850 | } | 829 | } |
851 | 830 | ||
852 | #[test] | 831 | #[test] |
853 | fn active_param_score() { | 832 | fn active_param_relevance() { |
854 | mark::check!(active_param_type_match); | 833 | cov_mark::check!(active_param_type_match); |
855 | check_scores( | 834 | check_relevance( |
856 | r#" | 835 | r#" |
857 | struct S { foo: i64, bar: u32, baz: u32 } | 836 | struct S { foo: i64, bar: u32, baz: u32 } |
858 | fn test(bar: u32) { } | 837 | fn test(bar: u32) { } |
@@ -867,9 +846,9 @@ fn foo(s: S) { test(s.$0) } | |||
867 | } | 846 | } |
868 | 847 | ||
869 | #[test] | 848 | #[test] |
870 | fn record_field_scores() { | 849 | fn record_field_relevances() { |
871 | mark::check!(record_field_type_match); | 850 | cov_mark::check!(record_field_type_match); |
872 | check_scores( | 851 | check_relevance( |
873 | r#" | 852 | r#" |
874 | struct A { foo: i64, bar: u32, baz: u32 } | 853 | struct A { foo: i64, bar: u32, baz: u32 } |
875 | struct B { x: (), y: f32, bar: u32 } | 854 | struct B { x: (), y: f32, bar: u32 } |
@@ -884,8 +863,8 @@ fn foo(a: A) { B { bar: a.$0 }; } | |||
884 | } | 863 | } |
885 | 864 | ||
886 | #[test] | 865 | #[test] |
887 | fn record_field_and_call_scores() { | 866 | fn record_field_and_call_relevances() { |
888 | check_scores( | 867 | check_relevance( |
889 | r#" | 868 | r#" |
890 | struct A { foo: i64, bar: u32, baz: u32 } | 869 | struct A { foo: i64, bar: u32, baz: u32 } |
891 | struct B { x: (), y: f32, bar: u32 } | 870 | struct B { x: (), y: f32, bar: u32 } |
@@ -898,7 +877,7 @@ fn foo(a: A) { B { bar: f(a.$0) }; } | |||
898 | fd baz [] | 877 | fd baz [] |
899 | "#]], | 878 | "#]], |
900 | ); | 879 | ); |
901 | check_scores( | 880 | check_relevance( |
902 | r#" | 881 | r#" |
903 | struct A { foo: i64, bar: u32, baz: u32 } | 882 | struct A { foo: i64, bar: u32, baz: u32 } |
904 | struct B { x: (), y: f32, bar: u32 } | 883 | struct B { x: (), y: f32, bar: u32 } |
@@ -915,7 +894,7 @@ fn foo(a: A) { f(B { bar: a.$0 }); } | |||
915 | 894 | ||
916 | #[test] | 895 | #[test] |
917 | fn prioritize_exact_ref_match() { | 896 | fn prioritize_exact_ref_match() { |
918 | check_scores( | 897 | check_relevance( |
919 | r#" | 898 | r#" |
920 | struct WorldSnapshot { _f: () }; | 899 | struct WorldSnapshot { _f: () }; |
921 | fn go(world: &WorldSnapshot) { go(w$0) } | 900 | fn go(world: &WorldSnapshot) { go(w$0) } |
@@ -930,7 +909,7 @@ fn go(world: &WorldSnapshot) { go(w$0) } | |||
930 | 909 | ||
931 | #[test] | 910 | #[test] |
932 | fn too_many_arguments() { | 911 | fn too_many_arguments() { |
933 | check_scores( | 912 | check_relevance( |
934 | r#" | 913 | r#" |
935 | struct Foo; | 914 | struct Foo; |
936 | fn f(foo: &Foo) { f(foo, w$0) } | 915 | fn f(foo: &Foo) { f(foo, w$0) } |
@@ -942,4 +921,70 @@ fn f(foo: &Foo) { f(foo, w$0) } | |||
942 | "#]], | 921 | "#]], |
943 | ); | 922 | ); |
944 | } | 923 | } |
924 | |||
925 | #[test] | ||
926 | fn suggest_ref_mut() { | ||
927 | cov_mark::check!(suggest_ref); | ||
928 | check( | ||
929 | r#" | ||
930 | struct S; | ||
931 | fn foo(s: &mut S) {} | ||
932 | fn main() { | ||
933 | let mut s = S; | ||
934 | foo($0); | ||
935 | } | ||
936 | "#, | ||
937 | expect![[r#" | ||
938 | [ | ||
939 | CompletionItem { | ||
940 | label: "S", | ||
941 | source_range: 70..70, | ||
942 | delete: 70..70, | ||
943 | insert: "S", | ||
944 | kind: SymbolKind( | ||
945 | Struct, | ||
946 | ), | ||
947 | }, | ||
948 | CompletionItem { | ||
949 | label: "foo(…)", | ||
950 | source_range: 70..70, | ||
951 | delete: 70..70, | ||
952 | insert: "foo(${1:&mut s})$0", | ||
953 | kind: SymbolKind( | ||
954 | Function, | ||
955 | ), | ||
956 | lookup: "foo", | ||
957 | detail: "-> ()", | ||
958 | trigger_call_info: true, | ||
959 | }, | ||
960 | CompletionItem { | ||
961 | label: "main()", | ||
962 | source_range: 70..70, | ||
963 | delete: 70..70, | ||
964 | insert: "main()$0", | ||
965 | kind: SymbolKind( | ||
966 | Function, | ||
967 | ), | ||
968 | lookup: "main", | ||
969 | detail: "-> ()", | ||
970 | }, | ||
971 | CompletionItem { | ||
972 | label: "s", | ||
973 | source_range: 70..70, | ||
974 | delete: 70..70, | ||
975 | insert: "s", | ||
976 | kind: SymbolKind( | ||
977 | Local, | ||
978 | ), | ||
979 | detail: "S", | ||
980 | relevance: CompletionRelevance { | ||
981 | exact_name_match: true, | ||
982 | exact_type_match: false, | ||
983 | }, | ||
984 | ref_match: "&mut ", | ||
985 | }, | ||
986 | ] | ||
987 | "#]], | ||
988 | ) | ||
989 | } | ||
945 | } | 990 | } |
diff --git a/crates/ide_completion/src/render/builder_ext.rs b/crates/ide_completion/src/render/builder_ext.rs index d053a988b..6d062b3b9 100644 --- a/crates/ide_completion/src/render/builder_ext.rs +++ b/crates/ide_completion/src/render/builder_ext.rs | |||
@@ -1,7 +1,6 @@ | |||
1 | //! Extensions for `Builder` structure required for item rendering. | 1 | //! Extensions for `Builder` structure required for item rendering. |
2 | 2 | ||
3 | use itertools::Itertools; | 3 | use itertools::Itertools; |
4 | use test_utils::mark; | ||
5 | 4 | ||
6 | use crate::{item::Builder, CompletionContext}; | 5 | use crate::{item::Builder, CompletionContext}; |
7 | 6 | ||
@@ -30,7 +29,7 @@ impl Builder { | |||
30 | return false; | 29 | return false; |
31 | } | 30 | } |
32 | if ctx.use_item_syntax.is_some() { | 31 | if ctx.use_item_syntax.is_some() { |
33 | mark::hit!(no_parens_in_use_item); | 32 | cov_mark::hit!(no_parens_in_use_item); |
34 | return false; | 33 | return false; |
35 | } | 34 | } |
36 | if ctx.is_pattern_call { | 35 | if ctx.is_pattern_call { |
@@ -43,7 +42,7 @@ impl Builder { | |||
43 | // Don't add parentheses if the expected type is some function reference. | 42 | // Don't add parentheses if the expected type is some function reference. |
44 | if let Some(ty) = &ctx.expected_type { | 43 | if let Some(ty) = &ctx.expected_type { |
45 | if ty.is_fn() { | 44 | if ty.is_fn() { |
46 | mark::hit!(no_call_parens_if_fn_ptr_needed); | 45 | cov_mark::hit!(no_call_parens_if_fn_ptr_needed); |
47 | return false; | 46 | return false; |
48 | } | 47 | } |
49 | } | 48 | } |
@@ -53,11 +52,11 @@ impl Builder { | |||
53 | } | 52 | } |
54 | 53 | ||
55 | pub(super) fn add_call_parens( | 54 | pub(super) fn add_call_parens( |
56 | mut self, | 55 | &mut self, |
57 | ctx: &CompletionContext, | 56 | ctx: &CompletionContext, |
58 | name: String, | 57 | name: String, |
59 | params: Params, | 58 | params: Params, |
60 | ) -> Builder { | 59 | ) -> &mut Builder { |
61 | if !self.should_add_parens(ctx) { | 60 | if !self.should_add_parens(ctx) { |
62 | return self; | 61 | return self; |
63 | } | 62 | } |
@@ -67,12 +66,12 @@ impl Builder { | |||
67 | None => return self, | 66 | None => return self, |
68 | }; | 67 | }; |
69 | // If not an import, add parenthesis automatically. | 68 | // If not an import, add parenthesis automatically. |
70 | mark::hit!(inserts_parens_for_function_calls); | 69 | cov_mark::hit!(inserts_parens_for_function_calls); |
71 | 70 | ||
72 | let (snippet, label) = if params.is_empty() { | 71 | let (snippet, label) = if params.is_empty() { |
73 | (format!("{}()$0", name), format!("{}()", name)) | 72 | (format!("{}()$0", name), format!("{}()", name)) |
74 | } else { | 73 | } else { |
75 | self = self.trigger_call_info(); | 74 | self.trigger_call_info(); |
76 | let snippet = match (ctx.config.add_call_argument_snippets, params) { | 75 | let snippet = match (ctx.config.add_call_argument_snippets, params) { |
77 | (true, Params::Named(params)) => { | 76 | (true, Params::Named(params)) => { |
78 | let function_params_snippet = | 77 | let function_params_snippet = |
@@ -82,7 +81,7 @@ impl Builder { | |||
82 | format!("{}({})$0", name, function_params_snippet) | 81 | format!("{}({})$0", name, function_params_snippet) |
83 | } | 82 | } |
84 | _ => { | 83 | _ => { |
85 | mark::hit!(suppress_arg_snippets); | 84 | cov_mark::hit!(suppress_arg_snippets); |
86 | format!("{}($0)", name) | 85 | format!("{}($0)", name) |
87 | } | 86 | } |
88 | }; | 87 | }; |
diff --git a/crates/ide_completion/src/render/const_.rs b/crates/ide_completion/src/render/const_.rs index 5010b642a..8add369e4 100644 --- a/crates/ide_completion/src/render/const_.rs +++ b/crates/ide_completion/src/render/const_.rs | |||
@@ -36,17 +36,17 @@ impl<'a> ConstRender<'a> { | |||
36 | let name = self.name()?; | 36 | let name = self.name()?; |
37 | let detail = self.detail(); | 37 | let detail = self.detail(); |
38 | 38 | ||
39 | let item = CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), name) | 39 | let mut item = |
40 | .kind(SymbolKind::Const) | 40 | CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), name); |
41 | item.kind(SymbolKind::Const) | ||
41 | .set_documentation(self.ctx.docs(self.const_)) | 42 | .set_documentation(self.ctx.docs(self.const_)) |
42 | .set_deprecated( | 43 | .set_deprecated( |
43 | self.ctx.is_deprecated(self.const_) | 44 | self.ctx.is_deprecated(self.const_) |
44 | || self.ctx.is_deprecated_assoc_item(self.const_), | 45 | || self.ctx.is_deprecated_assoc_item(self.const_), |
45 | ) | 46 | ) |
46 | .detail(detail) | 47 | .detail(detail); |
47 | .build(); | ||
48 | 48 | ||
49 | Some(item) | 49 | Some(item.build()) |
50 | } | 50 | } |
51 | 51 | ||
52 | fn name(&self) -> Option<String> { | 52 | fn name(&self) -> Option<String> { |
diff --git a/crates/ide_completion/src/render/enum_variant.rs b/crates/ide_completion/src/render/enum_variant.rs index 9214193b4..e8cfcc0c7 100644 --- a/crates/ide_completion/src/render/enum_variant.rs +++ b/crates/ide_completion/src/render/enum_variant.rs | |||
@@ -3,7 +3,6 @@ | |||
3 | use hir::{HasAttrs, HirDisplay, ModPath, StructKind}; | 3 | use hir::{HasAttrs, HirDisplay, ModPath, StructKind}; |
4 | use ide_db::SymbolKind; | 4 | use ide_db::SymbolKind; |
5 | use itertools::Itertools; | 5 | use itertools::Itertools; |
6 | use test_utils::mark; | ||
7 | 6 | ||
8 | use crate::{ | 7 | use crate::{ |
9 | item::{CompletionItem, CompletionKind, ImportEdit}, | 8 | item::{CompletionItem, CompletionKind, ImportEdit}, |
@@ -56,27 +55,26 @@ impl<'a> EnumRender<'a> { | |||
56 | } | 55 | } |
57 | 56 | ||
58 | fn render(self, import_to_add: Option<ImportEdit>) -> CompletionItem { | 57 | fn render(self, import_to_add: Option<ImportEdit>) -> CompletionItem { |
59 | let mut builder = CompletionItem::new( | 58 | let mut item = CompletionItem::new( |
60 | CompletionKind::Reference, | 59 | CompletionKind::Reference, |
61 | self.ctx.source_range(), | 60 | self.ctx.source_range(), |
62 | self.qualified_name.clone(), | 61 | self.qualified_name.clone(), |
63 | ) | 62 | ); |
64 | .kind(SymbolKind::Variant) | 63 | item.kind(SymbolKind::Variant) |
65 | .set_documentation(self.variant.docs(self.ctx.db())) | 64 | .set_documentation(self.variant.docs(self.ctx.db())) |
66 | .set_deprecated(self.ctx.is_deprecated(self.variant)) | 65 | .set_deprecated(self.ctx.is_deprecated(self.variant)) |
67 | .add_import(import_to_add) | 66 | .add_import(import_to_add) |
68 | .detail(self.detail()); | 67 | .detail(self.detail()); |
69 | 68 | ||
70 | if self.variant_kind == StructKind::Tuple { | 69 | if self.variant_kind == StructKind::Tuple { |
71 | mark::hit!(inserts_parens_for_tuple_enums); | 70 | cov_mark::hit!(inserts_parens_for_tuple_enums); |
72 | let params = Params::Anonymous(self.variant.fields(self.ctx.db()).len()); | 71 | let params = Params::Anonymous(self.variant.fields(self.ctx.db()).len()); |
73 | builder = | 72 | item.add_call_parens(self.ctx.completion, self.short_qualified_name, params); |
74 | builder.add_call_parens(self.ctx.completion, self.short_qualified_name, params); | ||
75 | } else if self.path.is_some() { | 73 | } else if self.path.is_some() { |
76 | builder = builder.lookup_by(self.short_qualified_name); | 74 | item.lookup_by(self.short_qualified_name); |
77 | } | 75 | } |
78 | 76 | ||
79 | builder.build() | 77 | item.build() |
80 | } | 78 | } |
81 | 79 | ||
82 | fn detail(&self) -> String { | 80 | fn detail(&self) -> String { |
@@ -103,13 +101,11 @@ impl<'a> EnumRender<'a> { | |||
103 | 101 | ||
104 | #[cfg(test)] | 102 | #[cfg(test)] |
105 | mod tests { | 103 | mod tests { |
106 | use test_utils::mark; | ||
107 | |||
108 | use crate::test_utils::check_edit; | 104 | use crate::test_utils::check_edit; |
109 | 105 | ||
110 | #[test] | 106 | #[test] |
111 | fn inserts_parens_for_tuple_enums() { | 107 | fn inserts_parens_for_tuple_enums() { |
112 | mark::check!(inserts_parens_for_tuple_enums); | 108 | cov_mark::check!(inserts_parens_for_tuple_enums); |
113 | check_edit( | 109 | check_edit( |
114 | "Some", | 110 | "Some", |
115 | r#" | 111 | r#" |
diff --git a/crates/ide_completion/src/render/function.rs b/crates/ide_completion/src/render/function.rs index e46e21d24..f4dabe3d1 100644 --- a/crates/ide_completion/src/render/function.rs +++ b/crates/ide_completion/src/render/function.rs | |||
@@ -3,7 +3,6 @@ | |||
3 | use hir::{HasSource, HirDisplay, Type}; | 3 | use hir::{HasSource, HirDisplay, Type}; |
4 | use ide_db::SymbolKind; | 4 | use ide_db::SymbolKind; |
5 | use syntax::ast::Fn; | 5 | use syntax::ast::Fn; |
6 | use test_utils::mark; | ||
7 | 6 | ||
8 | use crate::{ | 7 | use crate::{ |
9 | item::{CompletionItem, CompletionItemKind, CompletionKind, ImportEdit}, | 8 | item::{CompletionItem, CompletionItemKind, CompletionKind, ImportEdit}, |
@@ -42,16 +41,21 @@ impl<'a> FunctionRender<'a> { | |||
42 | 41 | ||
43 | fn render(self, import_to_add: Option<ImportEdit>) -> CompletionItem { | 42 | fn render(self, import_to_add: Option<ImportEdit>) -> CompletionItem { |
44 | let params = self.params(); | 43 | let params = self.params(); |
45 | CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), self.name.clone()) | 44 | let mut item = CompletionItem::new( |
46 | .kind(self.kind()) | 45 | CompletionKind::Reference, |
46 | self.ctx.source_range(), | ||
47 | self.name.clone(), | ||
48 | ); | ||
49 | item.kind(self.kind()) | ||
47 | .set_documentation(self.ctx.docs(self.func)) | 50 | .set_documentation(self.ctx.docs(self.func)) |
48 | .set_deprecated( | 51 | .set_deprecated( |
49 | self.ctx.is_deprecated(self.func) || self.ctx.is_deprecated_assoc_item(self.func), | 52 | self.ctx.is_deprecated(self.func) || self.ctx.is_deprecated_assoc_item(self.func), |
50 | ) | 53 | ) |
51 | .detail(self.detail()) | 54 | .detail(self.detail()) |
52 | .add_call_parens(self.ctx.completion, self.name, params) | 55 | .add_call_parens(self.ctx.completion, self.name, params) |
53 | .add_import(import_to_add) | 56 | .add_import(import_to_add); |
54 | .build() | 57 | |
58 | item.build() | ||
55 | } | 59 | } |
56 | 60 | ||
57 | fn detail(&self) -> String { | 61 | fn detail(&self) -> String { |
@@ -82,7 +86,7 @@ impl<'a> FunctionRender<'a> { | |||
82 | self.func.method_params(self.ctx.db()).unwrap_or_default() | 86 | self.func.method_params(self.ctx.db()).unwrap_or_default() |
83 | } else { | 87 | } else { |
84 | if let Some(s) = ast_params.self_param() { | 88 | if let Some(s) = ast_params.self_param() { |
85 | mark::hit!(parens_for_method_call_as_assoc_fn); | 89 | cov_mark::hit!(parens_for_method_call_as_assoc_fn); |
86 | params_pats.push(Some(s.to_string())); | 90 | params_pats.push(Some(s.to_string())); |
87 | } | 91 | } |
88 | self.func.assoc_fn_params(self.ctx.db()) | 92 | self.func.assoc_fn_params(self.ctx.db()) |
@@ -114,8 +118,6 @@ impl<'a> FunctionRender<'a> { | |||
114 | 118 | ||
115 | #[cfg(test)] | 119 | #[cfg(test)] |
116 | mod tests { | 120 | mod tests { |
117 | use test_utils::mark; | ||
118 | |||
119 | use crate::{ | 121 | use crate::{ |
120 | test_utils::{check_edit, check_edit_with_config, TEST_CONFIG}, | 122 | test_utils::{check_edit, check_edit_with_config, TEST_CONFIG}, |
121 | CompletionConfig, | 123 | CompletionConfig, |
@@ -123,7 +125,7 @@ mod tests { | |||
123 | 125 | ||
124 | #[test] | 126 | #[test] |
125 | fn inserts_parens_for_function_calls() { | 127 | fn inserts_parens_for_function_calls() { |
126 | mark::check!(inserts_parens_for_function_calls); | 128 | cov_mark::check!(inserts_parens_for_function_calls); |
127 | check_edit( | 129 | check_edit( |
128 | "no_args", | 130 | "no_args", |
129 | r#" | 131 | r#" |
@@ -191,7 +193,7 @@ fn bar(s: &S) { | |||
191 | 193 | ||
192 | #[test] | 194 | #[test] |
193 | fn parens_for_method_call_as_assoc_fn() { | 195 | fn parens_for_method_call_as_assoc_fn() { |
194 | mark::check!(parens_for_method_call_as_assoc_fn); | 196 | cov_mark::check!(parens_for_method_call_as_assoc_fn); |
195 | check_edit( | 197 | check_edit( |
196 | "foo", | 198 | "foo", |
197 | r#" | 199 | r#" |
@@ -213,7 +215,7 @@ fn main() { S::foo(${1:&self})$0 } | |||
213 | 215 | ||
214 | #[test] | 216 | #[test] |
215 | fn suppress_arg_snippets() { | 217 | fn suppress_arg_snippets() { |
216 | mark::check!(suppress_arg_snippets); | 218 | cov_mark::check!(suppress_arg_snippets); |
217 | check_edit_with_config( | 219 | check_edit_with_config( |
218 | CompletionConfig { add_call_argument_snippets: false, ..TEST_CONFIG }, | 220 | CompletionConfig { add_call_argument_snippets: false, ..TEST_CONFIG }, |
219 | "with_args", | 221 | "with_args", |
diff --git a/crates/ide_completion/src/render/macro_.rs b/crates/ide_completion/src/render/macro_.rs index a4535786f..3fa21ba7c 100644 --- a/crates/ide_completion/src/render/macro_.rs +++ b/crates/ide_completion/src/render/macro_.rs | |||
@@ -3,7 +3,6 @@ | |||
3 | use hir::{Documentation, HasSource}; | 3 | use hir::{Documentation, HasSource}; |
4 | use ide_db::SymbolKind; | 4 | use ide_db::SymbolKind; |
5 | use syntax::display::macro_label; | 5 | use syntax::display::macro_label; |
6 | use test_utils::mark; | ||
7 | 6 | ||
8 | use crate::{ | 7 | use crate::{ |
9 | item::{CompletionItem, CompletionKind, ImportEdit}, | 8 | item::{CompletionItem, CompletionKind, ImportEdit}, |
@@ -40,29 +39,31 @@ impl<'a> MacroRender<'a> { | |||
40 | } | 39 | } |
41 | 40 | ||
42 | fn render(&self, import_to_add: Option<ImportEdit>) -> Option<CompletionItem> { | 41 | fn render(&self, import_to_add: Option<ImportEdit>) -> Option<CompletionItem> { |
43 | let mut builder = | 42 | let mut item = |
44 | CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), &self.label()) | 43 | CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), &self.label()); |
45 | .kind(SymbolKind::Macro) | 44 | item.kind(SymbolKind::Macro) |
46 | .set_documentation(self.docs.clone()) | 45 | .set_documentation(self.docs.clone()) |
47 | .set_deprecated(self.ctx.is_deprecated(self.macro_)) | 46 | .set_deprecated(self.ctx.is_deprecated(self.macro_)) |
48 | .add_import(import_to_add) | 47 | .add_import(import_to_add) |
49 | .set_detail(self.detail()); | 48 | .set_detail(self.detail()); |
50 | 49 | ||
51 | let needs_bang = self.needs_bang(); | 50 | let needs_bang = self.needs_bang(); |
52 | builder = match self.ctx.snippet_cap() { | 51 | match self.ctx.snippet_cap() { |
53 | Some(cap) if needs_bang => { | 52 | Some(cap) if needs_bang => { |
54 | let snippet = self.snippet(); | 53 | let snippet = self.snippet(); |
55 | let lookup = self.lookup(); | 54 | let lookup = self.lookup(); |
56 | builder.insert_snippet(cap, snippet).lookup_by(lookup) | 55 | item.insert_snippet(cap, snippet).lookup_by(lookup); |
56 | } | ||
57 | None if needs_bang => { | ||
58 | item.insert_text(self.banged_name()); | ||
57 | } | 59 | } |
58 | None if needs_bang => builder.insert_text(self.banged_name()), | ||
59 | _ => { | 60 | _ => { |
60 | mark::hit!(dont_insert_macro_call_parens_unncessary); | 61 | cov_mark::hit!(dont_insert_macro_call_parens_unncessary); |
61 | builder.insert_text(&self.name) | 62 | item.insert_text(&self.name); |
62 | } | 63 | } |
63 | }; | 64 | }; |
64 | 65 | ||
65 | Some(builder.build()) | 66 | Some(item.build()) |
66 | } | 67 | } |
67 | 68 | ||
68 | fn needs_bang(&self) -> bool { | 69 | fn needs_bang(&self) -> bool { |
@@ -125,13 +126,11 @@ fn guess_macro_braces(macro_name: &str, docs: &str) -> (&'static str, &'static s | |||
125 | 126 | ||
126 | #[cfg(test)] | 127 | #[cfg(test)] |
127 | mod tests { | 128 | mod tests { |
128 | use test_utils::mark; | ||
129 | |||
130 | use crate::test_utils::check_edit; | 129 | use crate::test_utils::check_edit; |
131 | 130 | ||
132 | #[test] | 131 | #[test] |
133 | fn dont_insert_macro_call_parens_unncessary() { | 132 | fn dont_insert_macro_call_parens_unncessary() { |
134 | mark::check!(dont_insert_macro_call_parens_unncessary); | 133 | cov_mark::check!(dont_insert_macro_call_parens_unncessary); |
135 | check_edit( | 134 | check_edit( |
136 | "frobnicate!", | 135 | "frobnicate!", |
137 | r#" | 136 | r#" |
diff --git a/crates/ide_completion/src/render/pattern.rs b/crates/ide_completion/src/render/pattern.rs index 465dfe00c..ca2926125 100644 --- a/crates/ide_completion/src/render/pattern.rs +++ b/crates/ide_completion/src/render/pattern.rs | |||
@@ -69,19 +69,19 @@ fn build_completion( | |||
69 | ctx: RenderContext<'_>, | 69 | ctx: RenderContext<'_>, |
70 | name: String, | 70 | name: String, |
71 | pat: String, | 71 | pat: String, |
72 | item: impl HasAttrs + Copy, | 72 | def: impl HasAttrs + Copy, |
73 | ) -> CompletionItem { | 73 | ) -> CompletionItem { |
74 | let completion = CompletionItem::new(CompletionKind::Snippet, ctx.source_range(), name) | 74 | let mut item = CompletionItem::new(CompletionKind::Snippet, ctx.source_range(), name); |
75 | .kind(CompletionItemKind::Binding) | 75 | item.kind(CompletionItemKind::Binding) |
76 | .set_documentation(ctx.docs(item)) | 76 | .set_documentation(ctx.docs(def)) |
77 | .set_deprecated(ctx.is_deprecated(item)) | 77 | .set_deprecated(ctx.is_deprecated(def)) |
78 | .detail(&pat); | 78 | .detail(&pat); |
79 | let completion = if let Some(snippet_cap) = ctx.snippet_cap() { | 79 | if let Some(snippet_cap) = ctx.snippet_cap() { |
80 | completion.insert_snippet(snippet_cap, pat) | 80 | item.insert_snippet(snippet_cap, pat); |
81 | } else { | 81 | } else { |
82 | completion.insert_text(pat) | 82 | item.insert_text(pat); |
83 | }; | 83 | }; |
84 | completion.build() | 84 | item.build() |
85 | } | 85 | } |
86 | 86 | ||
87 | fn render_pat( | 87 | fn render_pat( |
diff --git a/crates/ide_completion/src/render/type_alias.rs b/crates/ide_completion/src/render/type_alias.rs index bd97c3692..e47b4c745 100644 --- a/crates/ide_completion/src/render/type_alias.rs +++ b/crates/ide_completion/src/render/type_alias.rs | |||
@@ -36,17 +36,17 @@ impl<'a> TypeAliasRender<'a> { | |||
36 | let name = self.name()?; | 36 | let name = self.name()?; |
37 | let detail = self.detail(); | 37 | let detail = self.detail(); |
38 | 38 | ||
39 | let item = CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), name) | 39 | let mut item = |
40 | .kind(SymbolKind::TypeAlias) | 40 | CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), name); |
41 | item.kind(SymbolKind::TypeAlias) | ||
41 | .set_documentation(self.ctx.docs(self.type_alias)) | 42 | .set_documentation(self.ctx.docs(self.type_alias)) |
42 | .set_deprecated( | 43 | .set_deprecated( |
43 | self.ctx.is_deprecated(self.type_alias) | 44 | self.ctx.is_deprecated(self.type_alias) |
44 | || self.ctx.is_deprecated_assoc_item(self.type_alias), | 45 | || self.ctx.is_deprecated_assoc_item(self.type_alias), |
45 | ) | 46 | ) |
46 | .detail(detail) | 47 | .detail(detail); |
47 | .build(); | ||
48 | 48 | ||
49 | Some(item) | 49 | Some(item.build()) |
50 | } | 50 | } |
51 | 51 | ||
52 | fn name(&self) -> Option<String> { | 52 | fn name(&self) -> Option<String> { |
diff --git a/crates/ide_completion/src/test_utils.rs b/crates/ide_completion/src/test_utils.rs index baff83305..9da844031 100644 --- a/crates/ide_completion/src/test_utils.rs +++ b/crates/ide_completion/src/test_utils.rs | |||
@@ -25,6 +25,7 @@ pub(crate) const TEST_CONFIG: CompletionConfig = CompletionConfig { | |||
25 | insert_use: InsertUseConfig { | 25 | insert_use: InsertUseConfig { |
26 | merge: Some(MergeBehavior::Full), | 26 | merge: Some(MergeBehavior::Full), |
27 | prefix_kind: PrefixKind::Plain, | 27 | prefix_kind: PrefixKind::Plain, |
28 | group: true, | ||
28 | }, | 29 | }, |
29 | }; | 30 | }; |
30 | 31 | ||
@@ -119,7 +120,7 @@ pub(crate) fn check_edit_with_config( | |||
119 | 120 | ||
120 | let mut combined_edit = completion.text_edit().to_owned(); | 121 | let mut combined_edit = completion.text_edit().to_owned(); |
121 | if let Some(import_text_edit) = | 122 | if let Some(import_text_edit) = |
122 | completion.import_to_add().and_then(|edit| edit.to_text_edit(config.insert_use.merge)) | 123 | completion.import_to_add().and_then(|edit| edit.to_text_edit(config.insert_use)) |
123 | { | 124 | { |
124 | combined_edit.union(import_text_edit).expect( | 125 | combined_edit.union(import_text_edit).expect( |
125 | "Failed to apply completion resolve changes: change ranges overlap, but should not", | 126 | "Failed to apply completion resolve changes: change ranges overlap, but should not", |
diff --git a/crates/ide_db/Cargo.toml b/crates/ide_db/Cargo.toml index d4612a75e..1f7a90d20 100644 --- a/crates/ide_db/Cargo.toml +++ b/crates/ide_db/Cargo.toml | |||
@@ -10,6 +10,7 @@ edition = "2018" | |||
10 | doctest = false | 10 | doctest = false |
11 | 11 | ||
12 | [dependencies] | 12 | [dependencies] |
13 | cov-mark = "1.1" | ||
13 | log = "0.4.8" | 14 | log = "0.4.8" |
14 | rayon = "1.5.0" | 15 | rayon = "1.5.0" |
15 | fst = { version = "0.4", default-features = false } | 16 | fst = { version = "0.4", default-features = false } |
@@ -23,10 +24,10 @@ syntax = { path = "../syntax", version = "0.0.0" } | |||
23 | text_edit = { path = "../text_edit", version = "0.0.0" } | 24 | text_edit = { path = "../text_edit", version = "0.0.0" } |
24 | base_db = { path = "../base_db", version = "0.0.0" } | 25 | base_db = { path = "../base_db", version = "0.0.0" } |
25 | profile = { path = "../profile", version = "0.0.0" } | 26 | profile = { path = "../profile", version = "0.0.0" } |
26 | test_utils = { path = "../test_utils", version = "0.0.0" } | ||
27 | # ide should depend only on the top-level `hir` package. if you need | 27 | # ide should depend only on the top-level `hir` package. if you need |
28 | # something from some `hir_xxx` subpackage, reexport the API via `hir`. | 28 | # something from some `hir_xxx` subpackage, reexport the API via `hir`. |
29 | hir = { path = "../hir", version = "0.0.0" } | 29 | hir = { path = "../hir", version = "0.0.0" } |
30 | 30 | ||
31 | [dev-dependencies] | 31 | [dev-dependencies] |
32 | test_utils = { path = "../test_utils" } | ||
32 | expect-test = "1.1" | 33 | expect-test = "1.1" |
diff --git a/crates/ide_db/src/call_info.rs b/crates/ide_db/src/call_info.rs index 615fa7b0e..d8878aa91 100644 --- a/crates/ide_db/src/call_info.rs +++ b/crates/ide_db/src/call_info.rs | |||
@@ -7,7 +7,6 @@ use syntax::{ | |||
7 | ast::{self, ArgListOwner}, | 7 | ast::{self, ArgListOwner}, |
8 | match_ast, AstNode, SyntaxNode, SyntaxToken, TextRange, TextSize, | 8 | match_ast, AstNode, SyntaxNode, SyntaxToken, TextRange, TextSize, |
9 | }; | 9 | }; |
10 | use test_utils::mark; | ||
11 | 10 | ||
12 | use crate::RootDatabase; | 11 | use crate::RootDatabase; |
13 | 12 | ||
@@ -122,7 +121,7 @@ fn call_info_impl( | |||
122 | 121 | ||
123 | let arg_list_range = arg_list.syntax().text_range(); | 122 | let arg_list_range = arg_list.syntax().text_range(); |
124 | if !arg_list_range.contains_inclusive(token.text_range().start()) { | 123 | if !arg_list_range.contains_inclusive(token.text_range().start()) { |
125 | mark::hit!(call_info_bad_offset); | 124 | cov_mark::hit!(call_info_bad_offset); |
126 | return None; | 125 | return None; |
127 | } | 126 | } |
128 | let param = std::cmp::min( | 127 | let param = std::cmp::min( |
@@ -162,7 +161,7 @@ impl ActiveParameter { | |||
162 | let idx = active_parameter?; | 161 | let idx = active_parameter?; |
163 | let mut params = signature.params(sema.db); | 162 | let mut params = signature.params(sema.db); |
164 | if !(idx < params.len()) { | 163 | if !(idx < params.len()) { |
165 | mark::hit!(too_many_arguments); | 164 | cov_mark::hit!(too_many_arguments); |
166 | return None; | 165 | return None; |
167 | } | 166 | } |
168 | let (pat, ty) = params.swap_remove(idx); | 167 | let (pat, ty) = params.swap_remove(idx); |
diff --git a/crates/ide_db/src/call_info/tests.rs b/crates/ide_db/src/call_info/tests.rs index c714cf280..9f84c253c 100644 --- a/crates/ide_db/src/call_info/tests.rs +++ b/crates/ide_db/src/call_info/tests.rs | |||
@@ -1,7 +1,7 @@ | |||
1 | use crate::RootDatabase; | 1 | use crate::RootDatabase; |
2 | use base_db::{fixture::ChangeFixture, FilePosition}; | 2 | use base_db::{fixture::ChangeFixture, FilePosition}; |
3 | use expect_test::{expect, Expect}; | 3 | use expect_test::{expect, Expect}; |
4 | use test_utils::{mark, RangeOrOffset}; | 4 | use test_utils::RangeOrOffset; |
5 | 5 | ||
6 | /// Creates analysis from a multi-file fixture, returns positions marked with $0. | 6 | /// Creates analysis from a multi-file fixture, returns positions marked with $0. |
7 | pub(crate) fn position(ra_fixture: &str) -> (RootDatabase, FilePosition) { | 7 | pub(crate) fn position(ra_fixture: &str) -> (RootDatabase, FilePosition) { |
@@ -347,7 +347,7 @@ pub fn foo(mut r: WriteHandler<()>) { | |||
347 | 347 | ||
348 | #[test] | 348 | #[test] |
349 | fn call_info_bad_offset() { | 349 | fn call_info_bad_offset() { |
350 | mark::check!(call_info_bad_offset); | 350 | cov_mark::check!(call_info_bad_offset); |
351 | check( | 351 | check( |
352 | r#" | 352 | r#" |
353 | fn foo(x: u32, y: u32) -> u32 {x + y} | 353 | fn foo(x: u32, y: u32) -> u32 {x + y} |
diff --git a/crates/ide_db/src/helpers.rs b/crates/ide_db/src/helpers.rs index 3ff77400b..3c95d3cff 100644 --- a/crates/ide_db/src/helpers.rs +++ b/crates/ide_db/src/helpers.rs | |||
@@ -2,11 +2,19 @@ | |||
2 | pub mod insert_use; | 2 | pub mod insert_use; |
3 | pub mod import_assets; | 3 | pub mod import_assets; |
4 | 4 | ||
5 | use hir::{Crate, Enum, Module, ScopeDef, Semantics, Trait}; | 5 | use hir::{Crate, Enum, ItemInNs, MacroDef, Module, ModuleDef, Name, ScopeDef, Semantics, Trait}; |
6 | use syntax::ast::{self, make}; | 6 | use syntax::ast::{self, make}; |
7 | 7 | ||
8 | use crate::RootDatabase; | 8 | use crate::RootDatabase; |
9 | 9 | ||
10 | pub fn item_name(db: &RootDatabase, item: ItemInNs) -> Option<Name> { | ||
11 | match item { | ||
12 | ItemInNs::Types(module_def_id) => ModuleDef::from(module_def_id).name(db), | ||
13 | ItemInNs::Values(module_def_id) => ModuleDef::from(module_def_id).name(db), | ||
14 | ItemInNs::Macros(macro_def_id) => MacroDef::from(macro_def_id).name(db), | ||
15 | } | ||
16 | } | ||
17 | |||
10 | /// Converts the mod path struct into its ast representation. | 18 | /// Converts the mod path struct into its ast representation. |
11 | pub fn mod_path_to_ast(path: &hir::ModPath) -> ast::Path { | 19 | pub fn mod_path_to_ast(path: &hir::ModPath) -> ast::Path { |
12 | let _p = profile::span("mod_path_to_ast"); | 20 | let _p = profile::span("mod_path_to_ast"); |
diff --git a/crates/ide_db/src/helpers/import_assets.rs b/crates/ide_db/src/helpers/import_assets.rs index 517abbb4b..e03ccd351 100644 --- a/crates/ide_db/src/helpers/import_assets.rs +++ b/crates/ide_db/src/helpers/import_assets.rs | |||
@@ -1,19 +1,29 @@ | |||
1 | //! Look up accessible paths for items. | 1 | //! Look up accessible paths for items. |
2 | use either::Either; | 2 | use hir::{ |
3 | use hir::{AsAssocItem, AssocItem, Crate, MacroDef, Module, ModuleDef, PrefixKind, Semantics}; | 3 | AsAssocItem, AssocItem, AssocItemContainer, Crate, ItemInNs, MacroDef, ModPath, Module, |
4 | ModuleDef, PathResolution, PrefixKind, ScopeDef, Semantics, Type, | ||
5 | }; | ||
6 | use itertools::Itertools; | ||
4 | use rustc_hash::FxHashSet; | 7 | use rustc_hash::FxHashSet; |
5 | use syntax::{ast, AstNode}; | 8 | use syntax::{ast, utils::path_to_string_stripping_turbo_fish, AstNode, SyntaxNode}; |
6 | 9 | ||
7 | use crate::{ | 10 | use crate::{ |
8 | imports_locator::{self, AssocItemSearch, DEFAULT_QUERY_SEARCH_LIMIT}, | 11 | items_locator::{self, AssocItemSearch, DEFAULT_QUERY_SEARCH_LIMIT}, |
9 | RootDatabase, | 12 | RootDatabase, |
10 | }; | 13 | }; |
11 | 14 | ||
15 | use super::item_name; | ||
16 | |||
17 | /// A candidate for import, derived during various IDE activities: | ||
18 | /// * completion with imports on the fly proposals | ||
19 | /// * completion edit resolve requests | ||
20 | /// * assists | ||
21 | /// * etc. | ||
12 | #[derive(Debug)] | 22 | #[derive(Debug)] |
13 | pub enum ImportCandidate { | 23 | pub enum ImportCandidate { |
14 | // A path, qualified (`std::collections::HashMap`) or not (`HashMap`). | 24 | /// A path, qualified (`std::collections::HashMap`) or not (`HashMap`). |
15 | Path(PathImportCandidate), | 25 | Path(PathImportCandidate), |
16 | /// A trait associated function (with no self parameter) or associated constant. | 26 | /// A trait associated function (with no self parameter) or an associated constant. |
17 | /// For 'test_mod::TestEnum::test_function', `ty` is the `test_mod::TestEnum` expression type | 27 | /// For 'test_mod::TestEnum::test_function', `ty` is the `test_mod::TestEnum` expression type |
18 | /// and `name` is the `test_function` | 28 | /// and `name` is the `test_function` |
19 | TraitAssocItem(TraitImportCandidate), | 29 | TraitAssocItem(TraitImportCandidate), |
@@ -23,21 +33,40 @@ pub enum ImportCandidate { | |||
23 | TraitMethod(TraitImportCandidate), | 33 | TraitMethod(TraitImportCandidate), |
24 | } | 34 | } |
25 | 35 | ||
36 | /// A trait import needed for a given associated item access. | ||
37 | /// For `some::path::SomeStruct::ASSOC_`, contains the | ||
38 | /// type of `some::path::SomeStruct` and `ASSOC_` as the item name. | ||
26 | #[derive(Debug)] | 39 | #[derive(Debug)] |
27 | pub struct TraitImportCandidate { | 40 | pub struct TraitImportCandidate { |
28 | pub receiver_ty: hir::Type, | 41 | /// A type of the item that has the associated item accessed at. |
29 | pub name: NameToImport, | 42 | pub receiver_ty: Type, |
43 | /// The associated item name that the trait to import should contain. | ||
44 | pub assoc_item_name: NameToImport, | ||
30 | } | 45 | } |
31 | 46 | ||
47 | /// Path import for a given name, qualified or not. | ||
32 | #[derive(Debug)] | 48 | #[derive(Debug)] |
33 | pub struct PathImportCandidate { | 49 | pub struct PathImportCandidate { |
34 | pub qualifier: Option<ast::Path>, | 50 | /// Optional qualifier before name. |
51 | pub qualifier: Option<FirstSegmentUnresolved>, | ||
52 | /// The name the item (struct, trait, enum, etc.) should have. | ||
35 | pub name: NameToImport, | 53 | pub name: NameToImport, |
36 | } | 54 | } |
37 | 55 | ||
56 | /// A qualifier that has a first segment and it's unresolved. | ||
57 | #[derive(Debug)] | ||
58 | pub struct FirstSegmentUnresolved { | ||
59 | fist_segment: ast::NameRef, | ||
60 | full_qualifier: ast::Path, | ||
61 | } | ||
62 | |||
63 | /// A name that will be used during item lookups. | ||
38 | #[derive(Debug)] | 64 | #[derive(Debug)] |
39 | pub enum NameToImport { | 65 | pub enum NameToImport { |
66 | /// Requires items with names that exactly match the given string, case-sensitive. | ||
40 | Exact(String), | 67 | Exact(String), |
68 | /// Requires items with names that case-insensitively contain all letters from the string, | ||
69 | /// in the same order, but not necessary adjacent. | ||
41 | Fuzzy(String), | 70 | Fuzzy(String), |
42 | } | 71 | } |
43 | 72 | ||
@@ -50,10 +79,12 @@ impl NameToImport { | |||
50 | } | 79 | } |
51 | } | 80 | } |
52 | 81 | ||
82 | /// A struct to find imports in the project, given a certain name (or its part) and the context. | ||
53 | #[derive(Debug)] | 83 | #[derive(Debug)] |
54 | pub struct ImportAssets { | 84 | pub struct ImportAssets { |
55 | import_candidate: ImportCandidate, | 85 | import_candidate: ImportCandidate, |
56 | module_with_candidate: hir::Module, | 86 | candidate_node: SyntaxNode, |
87 | module_with_candidate: Module, | ||
57 | } | 88 | } |
58 | 89 | ||
59 | impl ImportAssets { | 90 | impl ImportAssets { |
@@ -61,9 +92,11 @@ impl ImportAssets { | |||
61 | method_call: &ast::MethodCallExpr, | 92 | method_call: &ast::MethodCallExpr, |
62 | sema: &Semantics<RootDatabase>, | 93 | sema: &Semantics<RootDatabase>, |
63 | ) -> Option<Self> { | 94 | ) -> Option<Self> { |
95 | let candidate_node = method_call.syntax().clone(); | ||
64 | Some(Self { | 96 | Some(Self { |
65 | import_candidate: ImportCandidate::for_method_call(sema, method_call)?, | 97 | import_candidate: ImportCandidate::for_method_call(sema, method_call)?, |
66 | module_with_candidate: sema.scope(method_call.syntax()).module()?, | 98 | module_with_candidate: sema.scope(&candidate_node).module()?, |
99 | candidate_node, | ||
67 | }) | 100 | }) |
68 | } | 101 | } |
69 | 102 | ||
@@ -71,94 +104,94 @@ impl ImportAssets { | |||
71 | fully_qualified_path: &ast::Path, | 104 | fully_qualified_path: &ast::Path, |
72 | sema: &Semantics<RootDatabase>, | 105 | sema: &Semantics<RootDatabase>, |
73 | ) -> Option<Self> { | 106 | ) -> Option<Self> { |
74 | let syntax_under_caret = fully_qualified_path.syntax(); | 107 | let candidate_node = fully_qualified_path.syntax().clone(); |
75 | if syntax_under_caret.ancestors().find_map(ast::Use::cast).is_some() { | 108 | if candidate_node.ancestors().find_map(ast::Use::cast).is_some() { |
76 | return None; | 109 | return None; |
77 | } | 110 | } |
78 | Some(Self { | 111 | Some(Self { |
79 | import_candidate: ImportCandidate::for_regular_path(sema, fully_qualified_path)?, | 112 | import_candidate: ImportCandidate::for_regular_path(sema, fully_qualified_path)?, |
80 | module_with_candidate: sema.scope(syntax_under_caret).module()?, | 113 | module_with_candidate: sema.scope(&candidate_node).module()?, |
114 | candidate_node, | ||
81 | }) | 115 | }) |
82 | } | 116 | } |
83 | 117 | ||
84 | pub fn for_fuzzy_path( | 118 | pub fn for_fuzzy_path( |
85 | module_with_path: Module, | 119 | module_with_candidate: Module, |
86 | qualifier: Option<ast::Path>, | 120 | qualifier: Option<ast::Path>, |
87 | fuzzy_name: String, | 121 | fuzzy_name: String, |
88 | sema: &Semantics<RootDatabase>, | 122 | sema: &Semantics<RootDatabase>, |
123 | candidate_node: SyntaxNode, | ||
89 | ) -> Option<Self> { | 124 | ) -> Option<Self> { |
90 | Some(match qualifier { | 125 | Some(Self { |
91 | Some(qualifier) => { | 126 | import_candidate: ImportCandidate::for_fuzzy_path(qualifier, fuzzy_name, sema)?, |
92 | let qualifier_resolution = sema.resolve_path(&qualifier)?; | 127 | module_with_candidate, |
93 | match qualifier_resolution { | 128 | candidate_node, |
94 | hir::PathResolution::Def(hir::ModuleDef::Adt(assoc_item_path)) => Self { | ||
95 | import_candidate: ImportCandidate::TraitAssocItem(TraitImportCandidate { | ||
96 | receiver_ty: assoc_item_path.ty(sema.db), | ||
97 | name: NameToImport::Fuzzy(fuzzy_name), | ||
98 | }), | ||
99 | module_with_candidate: module_with_path, | ||
100 | }, | ||
101 | _ => Self { | ||
102 | import_candidate: ImportCandidate::Path(PathImportCandidate { | ||
103 | qualifier: Some(qualifier), | ||
104 | name: NameToImport::Fuzzy(fuzzy_name), | ||
105 | }), | ||
106 | module_with_candidate: module_with_path, | ||
107 | }, | ||
108 | } | ||
109 | } | ||
110 | None => Self { | ||
111 | import_candidate: ImportCandidate::Path(PathImportCandidate { | ||
112 | qualifier: None, | ||
113 | name: NameToImport::Fuzzy(fuzzy_name), | ||
114 | }), | ||
115 | module_with_candidate: module_with_path, | ||
116 | }, | ||
117 | }) | 129 | }) |
118 | } | 130 | } |
119 | 131 | ||
120 | pub fn for_fuzzy_method_call( | 132 | pub fn for_fuzzy_method_call( |
121 | module_with_method_call: Module, | 133 | module_with_method_call: Module, |
122 | receiver_ty: hir::Type, | 134 | receiver_ty: Type, |
123 | fuzzy_method_name: String, | 135 | fuzzy_method_name: String, |
136 | candidate_node: SyntaxNode, | ||
124 | ) -> Option<Self> { | 137 | ) -> Option<Self> { |
125 | Some(Self { | 138 | Some(Self { |
126 | import_candidate: ImportCandidate::TraitMethod(TraitImportCandidate { | 139 | import_candidate: ImportCandidate::TraitMethod(TraitImportCandidate { |
127 | receiver_ty, | 140 | receiver_ty, |
128 | name: NameToImport::Fuzzy(fuzzy_method_name), | 141 | assoc_item_name: NameToImport::Fuzzy(fuzzy_method_name), |
129 | }), | 142 | }), |
130 | module_with_candidate: module_with_method_call, | 143 | module_with_candidate: module_with_method_call, |
144 | candidate_node, | ||
131 | }) | 145 | }) |
132 | } | 146 | } |
133 | } | 147 | } |
134 | 148 | ||
149 | /// An import (not necessary the only one) that corresponds a certain given [`PathImportCandidate`]. | ||
150 | /// (the structure is not entirely correct, since there can be situations requiring two imports, see FIXME below for the details) | ||
151 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | ||
152 | pub struct LocatedImport { | ||
153 | /// The path to use in the `use` statement for a given candidate to be imported. | ||
154 | pub import_path: ModPath, | ||
155 | /// An item that will be imported with the import path given. | ||
156 | pub item_to_import: ItemInNs, | ||
157 | /// The path import candidate, resolved. | ||
158 | /// | ||
159 | /// Not necessary matches the import: | ||
160 | /// For any associated constant from the trait, we try to access as `some::path::SomeStruct::ASSOC_` | ||
161 | /// the original item is the associated constant, but the import has to be a trait that | ||
162 | /// defines this constant. | ||
163 | pub original_item: ItemInNs, | ||
164 | /// A path of the original item. | ||
165 | pub original_path: Option<ModPath>, | ||
166 | } | ||
167 | |||
168 | impl LocatedImport { | ||
169 | pub fn new( | ||
170 | import_path: ModPath, | ||
171 | item_to_import: ItemInNs, | ||
172 | original_item: ItemInNs, | ||
173 | original_path: Option<ModPath>, | ||
174 | ) -> Self { | ||
175 | Self { import_path, item_to_import, original_item, original_path } | ||
176 | } | ||
177 | } | ||
178 | |||
135 | impl ImportAssets { | 179 | impl ImportAssets { |
136 | pub fn import_candidate(&self) -> &ImportCandidate { | 180 | pub fn import_candidate(&self) -> &ImportCandidate { |
137 | &self.import_candidate | 181 | &self.import_candidate |
138 | } | 182 | } |
139 | 183 | ||
140 | fn name_to_import(&self) -> &NameToImport { | ||
141 | match &self.import_candidate { | ||
142 | ImportCandidate::Path(candidate) => &candidate.name, | ||
143 | ImportCandidate::TraitAssocItem(candidate) | ||
144 | | ImportCandidate::TraitMethod(candidate) => &candidate.name, | ||
145 | } | ||
146 | } | ||
147 | |||
148 | pub fn search_for_imports( | 184 | pub fn search_for_imports( |
149 | &self, | 185 | &self, |
150 | sema: &Semantics<RootDatabase>, | 186 | sema: &Semantics<RootDatabase>, |
151 | prefix_kind: PrefixKind, | 187 | prefix_kind: PrefixKind, |
152 | ) -> Vec<(hir::ModPath, hir::ItemInNs)> { | 188 | ) -> Vec<LocatedImport> { |
153 | let _p = profile::span("import_assets::search_for_imports"); | 189 | let _p = profile::span("import_assets::search_for_imports"); |
154 | self.search_for(sema, Some(prefix_kind)) | 190 | self.search_for(sema, Some(prefix_kind)) |
155 | } | 191 | } |
156 | 192 | ||
157 | /// This may return non-absolute paths if a part of the returned path is already imported into scope. | 193 | /// This may return non-absolute paths if a part of the returned path is already imported into scope. |
158 | pub fn search_for_relative_paths( | 194 | pub fn search_for_relative_paths(&self, sema: &Semantics<RootDatabase>) -> Vec<LocatedImport> { |
159 | &self, | ||
160 | sema: &Semantics<RootDatabase>, | ||
161 | ) -> Vec<(hir::ModPath, hir::ItemInNs)> { | ||
162 | let _p = profile::span("import_assets::search_for_relative_paths"); | 195 | let _p = profile::span("import_assets::search_for_relative_paths"); |
163 | self.search_for(sema, None) | 196 | self.search_for(sema, None) |
164 | } | 197 | } |
@@ -166,29 +199,29 @@ impl ImportAssets { | |||
166 | fn search_for( | 199 | fn search_for( |
167 | &self, | 200 | &self, |
168 | sema: &Semantics<RootDatabase>, | 201 | sema: &Semantics<RootDatabase>, |
169 | prefixed: Option<hir::PrefixKind>, | 202 | prefixed: Option<PrefixKind>, |
170 | ) -> Vec<(hir::ModPath, hir::ItemInNs)> { | 203 | ) -> Vec<LocatedImport> { |
171 | let current_crate = self.module_with_candidate.krate(); | 204 | let items_with_candidate_name = match self.name_to_import() { |
172 | 205 | NameToImport::Exact(exact_name) => items_locator::with_exact_name( | |
173 | let unfiltered_imports = match self.name_to_import() { | 206 | sema, |
174 | NameToImport::Exact(exact_name) => { | 207 | self.module_with_candidate.krate(), |
175 | imports_locator::find_exact_imports(sema, current_crate, exact_name.clone()) | 208 | exact_name.clone(), |
176 | } | 209 | ), |
177 | // FIXME: ideally, we should avoid using `fst` for seacrhing trait imports for assoc items: | 210 | // FIXME: ideally, we should avoid using `fst` for seacrhing trait imports for assoc items: |
178 | // instead, we need to look up all trait impls for a certain struct and search through them only | 211 | // instead, we need to look up all trait impls for a certain struct and search through them only |
179 | // see https://github.com/rust-analyzer/rust-analyzer/pull/7293#issuecomment-761585032 | 212 | // see https://github.com/rust-analyzer/rust-analyzer/pull/7293#issuecomment-761585032 |
180 | // and https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Fwg-rls-2.2E0/topic/Blanket.20trait.20impls.20lookup | 213 | // and https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Fwg-rls-2.2E0/topic/Blanket.20trait.20impls.20lookup |
181 | // for the details | 214 | // for the details |
182 | NameToImport::Fuzzy(fuzzy_name) => { | 215 | NameToImport::Fuzzy(fuzzy_name) => { |
183 | let (assoc_item_search, limit) = match self.import_candidate { | 216 | let (assoc_item_search, limit) = if self.import_candidate.is_trait_candidate() { |
184 | ImportCandidate::TraitAssocItem(_) | ImportCandidate::TraitMethod(_) => { | 217 | (AssocItemSearch::AssocItemsOnly, None) |
185 | (AssocItemSearch::AssocItemsOnly, None) | 218 | } else { |
186 | } | 219 | (AssocItemSearch::Include, Some(DEFAULT_QUERY_SEARCH_LIMIT)) |
187 | _ => (AssocItemSearch::Exclude, Some(DEFAULT_QUERY_SEARCH_LIMIT)), | ||
188 | }; | 220 | }; |
189 | imports_locator::find_similar_imports( | 221 | |
222 | items_locator::with_similar_name( | ||
190 | sema, | 223 | sema, |
191 | current_crate, | 224 | self.module_with_candidate.krate(), |
192 | fuzzy_name.clone(), | 225 | fuzzy_name.clone(), |
193 | assoc_item_search, | 226 | assoc_item_search, |
194 | limit, | 227 | limit, |
@@ -196,63 +229,224 @@ impl ImportAssets { | |||
196 | } | 229 | } |
197 | }; | 230 | }; |
198 | 231 | ||
199 | let db = sema.db; | 232 | let scope_definitions = self.scope_definitions(sema); |
200 | let mut res = | 233 | self.applicable_defs(sema.db, prefixed, items_with_candidate_name) |
201 | applicable_defs(self.import_candidate(), current_crate, db, unfiltered_imports) | 234 | .into_iter() |
202 | .filter_map(|candidate| { | 235 | .filter(|import| import.import_path.len() > 1) |
203 | let item: hir::ItemInNs = candidate.clone().either(Into::into, Into::into); | 236 | .filter(|import| !scope_definitions.contains(&ScopeDef::from(import.item_to_import))) |
204 | 237 | .sorted_by_key(|import| import.import_path.clone()) | |
205 | let item_to_search = match self.import_candidate { | 238 | .collect() |
206 | ImportCandidate::TraitAssocItem(_) | ImportCandidate::TraitMethod(_) => { | 239 | } |
207 | let canidate_trait = match candidate { | 240 | |
208 | Either::Left(module_def) => { | 241 | fn scope_definitions(&self, sema: &Semantics<RootDatabase>) -> FxHashSet<ScopeDef> { |
209 | module_def.as_assoc_item(db)?.containing_trait(db) | 242 | let mut scope_definitions = FxHashSet::default(); |
210 | } | 243 | sema.scope(&self.candidate_node).process_all_names(&mut |_, scope_def| { |
211 | _ => None, | 244 | scope_definitions.insert(scope_def); |
212 | }?; | 245 | }); |
213 | ModuleDef::from(canidate_trait).into() | 246 | scope_definitions |
214 | } | 247 | } |
215 | _ => item, | 248 | |
216 | }; | 249 | fn name_to_import(&self) -> &NameToImport { |
217 | 250 | match &self.import_candidate { | |
218 | if let Some(prefix_kind) = prefixed { | 251 | ImportCandidate::Path(candidate) => &candidate.name, |
219 | self.module_with_candidate.find_use_path_prefixed( | 252 | ImportCandidate::TraitAssocItem(candidate) |
220 | db, | 253 | | ImportCandidate::TraitMethod(candidate) => &candidate.assoc_item_name, |
221 | item_to_search, | 254 | } |
222 | prefix_kind, | 255 | } |
223 | ) | 256 | |
224 | } else { | 257 | fn applicable_defs( |
225 | self.module_with_candidate.find_use_path(db, item_to_search) | 258 | &self, |
226 | } | 259 | db: &RootDatabase, |
227 | .map(|path| (path, item)) | 260 | prefixed: Option<PrefixKind>, |
228 | }) | 261 | items_with_candidate_name: FxHashSet<ItemInNs>, |
229 | .filter(|(use_path, _)| use_path.len() > 1) | 262 | ) -> FxHashSet<LocatedImport> { |
230 | .collect::<Vec<_>>(); | 263 | let _p = profile::span("import_assets::applicable_defs"); |
231 | res.sort_by_cached_key(|(path, _)| path.clone()); | 264 | let current_crate = self.module_with_candidate.krate(); |
232 | res | 265 | |
266 | let mod_path = |item| { | ||
267 | get_mod_path(db, item_for_path_search(db, item)?, &self.module_with_candidate, prefixed) | ||
268 | }; | ||
269 | |||
270 | match &self.import_candidate { | ||
271 | ImportCandidate::Path(path_candidate) => { | ||
272 | path_applicable_imports(db, path_candidate, mod_path, items_with_candidate_name) | ||
273 | } | ||
274 | ImportCandidate::TraitAssocItem(trait_candidate) => trait_applicable_items( | ||
275 | db, | ||
276 | current_crate, | ||
277 | trait_candidate, | ||
278 | true, | ||
279 | mod_path, | ||
280 | items_with_candidate_name, | ||
281 | ), | ||
282 | ImportCandidate::TraitMethod(trait_candidate) => trait_applicable_items( | ||
283 | db, | ||
284 | current_crate, | ||
285 | trait_candidate, | ||
286 | false, | ||
287 | mod_path, | ||
288 | items_with_candidate_name, | ||
289 | ), | ||
290 | } | ||
233 | } | 291 | } |
234 | } | 292 | } |
235 | 293 | ||
236 | fn applicable_defs<'a>( | 294 | fn path_applicable_imports( |
237 | import_candidate: &ImportCandidate, | ||
238 | current_crate: Crate, | ||
239 | db: &RootDatabase, | 295 | db: &RootDatabase, |
240 | unfiltered_imports: Box<dyn Iterator<Item = Either<ModuleDef, MacroDef>> + 'a>, | 296 | path_candidate: &PathImportCandidate, |
241 | ) -> Box<dyn Iterator<Item = Either<ModuleDef, MacroDef>> + 'a> { | 297 | mod_path: impl Fn(ItemInNs) -> Option<ModPath> + Copy, |
242 | let receiver_ty = match import_candidate { | 298 | items_with_candidate_name: FxHashSet<ItemInNs>, |
243 | ImportCandidate::Path(_) => return unfiltered_imports, | 299 | ) -> FxHashSet<LocatedImport> { |
244 | ImportCandidate::TraitAssocItem(candidate) | ImportCandidate::TraitMethod(candidate) => { | 300 | let _p = profile::span("import_assets::path_applicable_imports"); |
245 | &candidate.receiver_ty | 301 | |
302 | let (unresolved_first_segment, unresolved_qualifier) = match &path_candidate.qualifier { | ||
303 | None => { | ||
304 | return items_with_candidate_name | ||
305 | .into_iter() | ||
306 | .filter_map(|item| { | ||
307 | Some(LocatedImport::new(mod_path(item)?, item, item, mod_path(item))) | ||
308 | }) | ||
309 | .collect(); | ||
246 | } | 310 | } |
311 | Some(first_segment_unresolved) => ( | ||
312 | first_segment_unresolved.fist_segment.to_string(), | ||
313 | path_to_string_stripping_turbo_fish(&first_segment_unresolved.full_qualifier), | ||
314 | ), | ||
247 | }; | 315 | }; |
248 | 316 | ||
317 | items_with_candidate_name | ||
318 | .into_iter() | ||
319 | .filter_map(|item| { | ||
320 | import_for_item(db, mod_path, &unresolved_first_segment, &unresolved_qualifier, item) | ||
321 | }) | ||
322 | .collect() | ||
323 | } | ||
324 | |||
325 | fn import_for_item( | ||
326 | db: &RootDatabase, | ||
327 | mod_path: impl Fn(ItemInNs) -> Option<ModPath>, | ||
328 | unresolved_first_segment: &str, | ||
329 | unresolved_qualifier: &str, | ||
330 | original_item: ItemInNs, | ||
331 | ) -> Option<LocatedImport> { | ||
332 | let _p = profile::span("import_assets::import_for_item"); | ||
333 | |||
334 | let original_item_candidate = item_for_path_search(db, original_item)?; | ||
335 | let import_path_candidate = mod_path(original_item_candidate)?; | ||
336 | let import_path_string = import_path_candidate.to_string(); | ||
337 | |||
338 | let expected_import_end = if item_as_assoc(db, original_item).is_some() { | ||
339 | unresolved_qualifier.to_string() | ||
340 | } else { | ||
341 | format!("{}::{}", unresolved_qualifier, item_name(db, original_item)?) | ||
342 | }; | ||
343 | if !import_path_string.contains(unresolved_first_segment) | ||
344 | || !import_path_string.ends_with(&expected_import_end) | ||
345 | { | ||
346 | return None; | ||
347 | } | ||
348 | |||
349 | let segment_import = | ||
350 | find_import_for_segment(db, original_item_candidate, &unresolved_first_segment)?; | ||
351 | let trait_item_to_import = item_as_assoc(db, original_item) | ||
352 | .and_then(|assoc| assoc.containing_trait(db)) | ||
353 | .map(|trait_| ItemInNs::from(ModuleDef::from(trait_))); | ||
354 | Some(match (segment_import == original_item_candidate, trait_item_to_import) { | ||
355 | (true, Some(_)) => { | ||
356 | // FIXME we should be able to import both the trait and the segment, | ||
357 | // but it's unclear what to do with overlapping edits (merge imports?) | ||
358 | // especially in case of lazy completion edit resolutions. | ||
359 | return None; | ||
360 | } | ||
361 | (false, Some(trait_to_import)) => LocatedImport::new( | ||
362 | mod_path(trait_to_import)?, | ||
363 | trait_to_import, | ||
364 | original_item, | ||
365 | mod_path(original_item), | ||
366 | ), | ||
367 | (true, None) => LocatedImport::new( | ||
368 | import_path_candidate, | ||
369 | original_item_candidate, | ||
370 | original_item, | ||
371 | mod_path(original_item), | ||
372 | ), | ||
373 | (false, None) => LocatedImport::new( | ||
374 | mod_path(segment_import)?, | ||
375 | segment_import, | ||
376 | original_item, | ||
377 | mod_path(original_item), | ||
378 | ), | ||
379 | }) | ||
380 | } | ||
381 | |||
382 | fn item_for_path_search(db: &RootDatabase, item: ItemInNs) -> Option<ItemInNs> { | ||
383 | Some(match item { | ||
384 | ItemInNs::Types(_) | ItemInNs::Values(_) => match item_as_assoc(db, item) { | ||
385 | Some(assoc_item) => match assoc_item.container(db) { | ||
386 | AssocItemContainer::Trait(trait_) => ItemInNs::from(ModuleDef::from(trait_)), | ||
387 | AssocItemContainer::Impl(impl_) => { | ||
388 | ItemInNs::from(ModuleDef::from(impl_.target_ty(db).as_adt()?)) | ||
389 | } | ||
390 | }, | ||
391 | None => item, | ||
392 | }, | ||
393 | ItemInNs::Macros(_) => item, | ||
394 | }) | ||
395 | } | ||
396 | |||
397 | fn find_import_for_segment( | ||
398 | db: &RootDatabase, | ||
399 | original_item: ItemInNs, | ||
400 | unresolved_first_segment: &str, | ||
401 | ) -> Option<ItemInNs> { | ||
402 | let segment_is_name = item_name(db, original_item) | ||
403 | .map(|name| name.to_string() == unresolved_first_segment) | ||
404 | .unwrap_or(false); | ||
405 | |||
406 | Some(if segment_is_name { | ||
407 | original_item | ||
408 | } else { | ||
409 | let matching_module = | ||
410 | module_with_segment_name(db, &unresolved_first_segment, original_item)?; | ||
411 | ItemInNs::from(ModuleDef::from(matching_module)) | ||
412 | }) | ||
413 | } | ||
414 | |||
415 | fn module_with_segment_name( | ||
416 | db: &RootDatabase, | ||
417 | segment_name: &str, | ||
418 | candidate: ItemInNs, | ||
419 | ) -> Option<Module> { | ||
420 | let mut current_module = match candidate { | ||
421 | ItemInNs::Types(module_def_id) => ModuleDef::from(module_def_id).module(db), | ||
422 | ItemInNs::Values(module_def_id) => ModuleDef::from(module_def_id).module(db), | ||
423 | ItemInNs::Macros(macro_def_id) => MacroDef::from(macro_def_id).module(db), | ||
424 | }; | ||
425 | while let Some(module) = current_module { | ||
426 | if let Some(module_name) = module.name(db) { | ||
427 | if module_name.to_string() == segment_name { | ||
428 | return Some(module); | ||
429 | } | ||
430 | } | ||
431 | current_module = module.parent(db); | ||
432 | } | ||
433 | None | ||
434 | } | ||
435 | |||
436 | fn trait_applicable_items( | ||
437 | db: &RootDatabase, | ||
438 | current_crate: Crate, | ||
439 | trait_candidate: &TraitImportCandidate, | ||
440 | trait_assoc_item: bool, | ||
441 | mod_path: impl Fn(ItemInNs) -> Option<ModPath>, | ||
442 | items_with_candidate_name: FxHashSet<ItemInNs>, | ||
443 | ) -> FxHashSet<LocatedImport> { | ||
444 | let _p = profile::span("import_assets::trait_applicable_items"); | ||
249 | let mut required_assoc_items = FxHashSet::default(); | 445 | let mut required_assoc_items = FxHashSet::default(); |
250 | 446 | ||
251 | let trait_candidates = unfiltered_imports | 447 | let trait_candidates = items_with_candidate_name |
252 | .filter_map(|input| match input { | 448 | .into_iter() |
253 | Either::Left(module_def) => module_def.as_assoc_item(db), | 449 | .filter_map(|input| item_as_assoc(db, input)) |
254 | _ => None, | ||
255 | }) | ||
256 | .filter_map(|assoc| { | 450 | .filter_map(|assoc| { |
257 | let assoc_item_trait = assoc.containing_trait(db)?; | 451 | let assoc_item_trait = assoc.containing_trait(db)?; |
258 | required_assoc_items.insert(assoc); | 452 | required_assoc_items.insert(assoc); |
@@ -260,11 +454,10 @@ fn applicable_defs<'a>( | |||
260 | }) | 454 | }) |
261 | .collect(); | 455 | .collect(); |
262 | 456 | ||
263 | let mut applicable_defs = FxHashSet::default(); | 457 | let mut located_imports = FxHashSet::default(); |
264 | 458 | ||
265 | match import_candidate { | 459 | if trait_assoc_item { |
266 | ImportCandidate::Path(_) => unreachable!(), | 460 | trait_candidate.receiver_ty.iterate_path_candidates( |
267 | ImportCandidate::TraitAssocItem(_) => receiver_ty.iterate_path_candidates( | ||
268 | db, | 461 | db, |
269 | current_crate, | 462 | current_crate, |
270 | &trait_candidates, | 463 | &trait_candidates, |
@@ -276,12 +469,21 @@ fn applicable_defs<'a>( | |||
276 | return None; | 469 | return None; |
277 | } | 470 | } |
278 | } | 471 | } |
279 | applicable_defs.insert(Either::Left(assoc_to_module_def(assoc))); | 472 | |
473 | let item = ItemInNs::from(ModuleDef::from(assoc.containing_trait(db)?)); | ||
474 | let original_item = assoc_to_item(assoc); | ||
475 | located_imports.insert(LocatedImport::new( | ||
476 | mod_path(item)?, | ||
477 | item, | ||
478 | original_item, | ||
479 | mod_path(original_item), | ||
480 | )); | ||
280 | } | 481 | } |
281 | None::<()> | 482 | None::<()> |
282 | }, | 483 | }, |
283 | ), | 484 | ) |
284 | ImportCandidate::TraitMethod(_) => receiver_ty.iterate_method_candidates( | 485 | } else { |
486 | trait_candidate.receiver_ty.iterate_method_candidates( | ||
285 | db, | 487 | db, |
286 | current_crate, | 488 | current_crate, |
287 | &trait_candidates, | 489 | &trait_candidates, |
@@ -289,21 +491,41 @@ fn applicable_defs<'a>( | |||
289 | |_, function| { | 491 | |_, function| { |
290 | let assoc = function.as_assoc_item(db)?; | 492 | let assoc = function.as_assoc_item(db)?; |
291 | if required_assoc_items.contains(&assoc) { | 493 | if required_assoc_items.contains(&assoc) { |
292 | applicable_defs.insert(Either::Left(assoc_to_module_def(assoc))); | 494 | let item = ItemInNs::from(ModuleDef::from(assoc.containing_trait(db)?)); |
495 | let original_item = assoc_to_item(assoc); | ||
496 | located_imports.insert(LocatedImport::new( | ||
497 | mod_path(item)?, | ||
498 | item, | ||
499 | original_item, | ||
500 | mod_path(original_item), | ||
501 | )); | ||
293 | } | 502 | } |
294 | None::<()> | 503 | None::<()> |
295 | }, | 504 | }, |
296 | ), | 505 | ) |
297 | }; | 506 | }; |
298 | 507 | ||
299 | Box::new(applicable_defs.into_iter()) | 508 | located_imports |
300 | } | 509 | } |
301 | 510 | ||
302 | fn assoc_to_module_def(assoc: AssocItem) -> ModuleDef { | 511 | fn assoc_to_item(assoc: AssocItem) -> ItemInNs { |
303 | match assoc { | 512 | match assoc { |
304 | AssocItem::Function(f) => f.into(), | 513 | AssocItem::Function(f) => ItemInNs::from(ModuleDef::from(f)), |
305 | AssocItem::Const(c) => c.into(), | 514 | AssocItem::Const(c) => ItemInNs::from(ModuleDef::from(c)), |
306 | AssocItem::TypeAlias(t) => t.into(), | 515 | AssocItem::TypeAlias(t) => ItemInNs::from(ModuleDef::from(t)), |
516 | } | ||
517 | } | ||
518 | |||
519 | fn get_mod_path( | ||
520 | db: &RootDatabase, | ||
521 | item_to_search: ItemInNs, | ||
522 | module_with_candidate: &Module, | ||
523 | prefixed: Option<PrefixKind>, | ||
524 | ) -> Option<ModPath> { | ||
525 | if let Some(prefix_kind) = prefixed { | ||
526 | module_with_candidate.find_use_path_prefixed(db, item_to_search, prefix_kind) | ||
527 | } else { | ||
528 | module_with_candidate.find_use_path(db, item_to_search) | ||
307 | } | 529 | } |
308 | } | 530 | } |
309 | 531 | ||
@@ -316,7 +538,7 @@ impl ImportCandidate { | |||
316 | Some(_) => None, | 538 | Some(_) => None, |
317 | None => Some(Self::TraitMethod(TraitImportCandidate { | 539 | None => Some(Self::TraitMethod(TraitImportCandidate { |
318 | receiver_ty: sema.type_of_expr(&method_call.receiver()?)?, | 540 | receiver_ty: sema.type_of_expr(&method_call.receiver()?)?, |
319 | name: NameToImport::Exact(method_call.name_ref()?.to_string()), | 541 | assoc_item_name: NameToImport::Exact(method_call.name_ref()?.to_string()), |
320 | })), | 542 | })), |
321 | } | 543 | } |
322 | } | 544 | } |
@@ -325,41 +547,63 @@ impl ImportCandidate { | |||
325 | if sema.resolve_path(path).is_some() { | 547 | if sema.resolve_path(path).is_some() { |
326 | return None; | 548 | return None; |
327 | } | 549 | } |
550 | path_import_candidate( | ||
551 | sema, | ||
552 | path.qualifier(), | ||
553 | NameToImport::Exact(path.segment()?.name_ref()?.to_string()), | ||
554 | ) | ||
555 | } | ||
328 | 556 | ||
329 | let segment = path.segment()?; | 557 | fn for_fuzzy_path( |
330 | let candidate = if let Some(qualifier) = path.qualifier() { | 558 | qualifier: Option<ast::Path>, |
331 | let qualifier_start = qualifier.syntax().descendants().find_map(ast::NameRef::cast)?; | 559 | fuzzy_name: String, |
332 | let qualifier_start_path = | 560 | sema: &Semantics<RootDatabase>, |
333 | qualifier_start.syntax().ancestors().find_map(ast::Path::cast)?; | 561 | ) -> Option<Self> { |
334 | if let Some(qualifier_start_resolution) = sema.resolve_path(&qualifier_start_path) { | 562 | path_import_candidate(sema, qualifier, NameToImport::Fuzzy(fuzzy_name)) |
335 | let qualifier_resolution = if qualifier_start_path == qualifier { | 563 | } |
336 | qualifier_start_resolution | 564 | |
565 | fn is_trait_candidate(&self) -> bool { | ||
566 | matches!(self, ImportCandidate::TraitAssocItem(_) | ImportCandidate::TraitMethod(_)) | ||
567 | } | ||
568 | } | ||
569 | |||
570 | fn path_import_candidate( | ||
571 | sema: &Semantics<RootDatabase>, | ||
572 | qualifier: Option<ast::Path>, | ||
573 | name: NameToImport, | ||
574 | ) -> Option<ImportCandidate> { | ||
575 | Some(match qualifier { | ||
576 | Some(qualifier) => match sema.resolve_path(&qualifier) { | ||
577 | None => { | ||
578 | let qualifier_start = | ||
579 | qualifier.syntax().descendants().find_map(ast::NameRef::cast)?; | ||
580 | let qualifier_start_path = | ||
581 | qualifier_start.syntax().ancestors().find_map(ast::Path::cast)?; | ||
582 | if sema.resolve_path(&qualifier_start_path).is_none() { | ||
583 | ImportCandidate::Path(PathImportCandidate { | ||
584 | qualifier: Some(FirstSegmentUnresolved { | ||
585 | fist_segment: qualifier_start, | ||
586 | full_qualifier: qualifier, | ||
587 | }), | ||
588 | name, | ||
589 | }) | ||
337 | } else { | 590 | } else { |
338 | sema.resolve_path(&qualifier)? | 591 | return None; |
339 | }; | ||
340 | match qualifier_resolution { | ||
341 | hir::PathResolution::Def(hir::ModuleDef::Adt(assoc_item_path)) => { | ||
342 | ImportCandidate::TraitAssocItem(TraitImportCandidate { | ||
343 | receiver_ty: assoc_item_path.ty(sema.db), | ||
344 | name: NameToImport::Exact(segment.name_ref()?.to_string()), | ||
345 | }) | ||
346 | } | ||
347 | _ => return None, | ||
348 | } | 592 | } |
349 | } else { | 593 | } |
350 | ImportCandidate::Path(PathImportCandidate { | 594 | Some(PathResolution::Def(ModuleDef::Adt(assoc_item_path))) => { |
351 | qualifier: Some(qualifier), | 595 | ImportCandidate::TraitAssocItem(TraitImportCandidate { |
352 | name: NameToImport::Exact(qualifier_start.to_string()), | 596 | receiver_ty: assoc_item_path.ty(sema.db), |
597 | assoc_item_name: name, | ||
353 | }) | 598 | }) |
354 | } | 599 | } |
355 | } else { | 600 | Some(_) => return None, |
356 | ImportCandidate::Path(PathImportCandidate { | 601 | }, |
357 | qualifier: None, | 602 | None => ImportCandidate::Path(PathImportCandidate { qualifier: None, name }), |
358 | name: NameToImport::Exact( | 603 | }) |
359 | segment.syntax().descendants().find_map(ast::NameRef::cast)?.to_string(), | 604 | } |
360 | ), | 605 | |
361 | }) | 606 | fn item_as_assoc(db: &RootDatabase, item: ItemInNs) -> Option<AssocItem> { |
362 | }; | 607 | item.as_module_def_id() |
363 | Some(candidate) | 608 | .and_then(|module_def_id| ModuleDef::from(module_def_id).as_assoc_item(db)) |
364 | } | ||
365 | } | 609 | } |
diff --git a/crates/ide_db/src/helpers/insert_use.rs b/crates/ide_db/src/helpers/insert_use.rs index fd4035198..df66d8ea0 100644 --- a/crates/ide_db/src/helpers/insert_use.rs +++ b/crates/ide_db/src/helpers/insert_use.rs | |||
@@ -13,12 +13,12 @@ use syntax::{ | |||
13 | }, | 13 | }, |
14 | AstToken, InsertPosition, NodeOrToken, SyntaxElement, SyntaxNode, SyntaxToken, | 14 | AstToken, InsertPosition, NodeOrToken, SyntaxElement, SyntaxNode, SyntaxToken, |
15 | }; | 15 | }; |
16 | use test_utils::mark; | ||
17 | 16 | ||
18 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] | 17 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] |
19 | pub struct InsertUseConfig { | 18 | pub struct InsertUseConfig { |
20 | pub merge: Option<MergeBehavior>, | 19 | pub merge: Option<MergeBehavior>, |
21 | pub prefix_kind: hir::PrefixKind, | 20 | pub prefix_kind: hir::PrefixKind, |
21 | pub group: bool, | ||
22 | } | 22 | } |
23 | 23 | ||
24 | #[derive(Debug, Clone)] | 24 | #[derive(Debug, Clone)] |
@@ -99,13 +99,13 @@ fn is_inner_comment(token: SyntaxToken) -> bool { | |||
99 | pub fn insert_use<'a>( | 99 | pub fn insert_use<'a>( |
100 | scope: &ImportScope, | 100 | scope: &ImportScope, |
101 | path: ast::Path, | 101 | path: ast::Path, |
102 | merge: Option<MergeBehavior>, | 102 | cfg: InsertUseConfig, |
103 | ) -> SyntaxRewriter<'a> { | 103 | ) -> SyntaxRewriter<'a> { |
104 | let _p = profile::span("insert_use"); | 104 | let _p = profile::span("insert_use"); |
105 | let mut rewriter = SyntaxRewriter::default(); | 105 | let mut rewriter = SyntaxRewriter::default(); |
106 | let use_item = make::use_(None, make::use_tree(path.clone(), None, None, false)); | 106 | let use_item = make::use_(None, make::use_tree(path.clone(), None, None, false)); |
107 | // merge into existing imports if possible | 107 | // merge into existing imports if possible |
108 | if let Some(mb) = merge { | 108 | if let Some(mb) = cfg.merge { |
109 | for existing_use in scope.as_syntax_node().children().filter_map(ast::Use::cast) { | 109 | for existing_use in scope.as_syntax_node().children().filter_map(ast::Use::cast) { |
110 | if let Some(merged) = try_merge_imports(&existing_use, &use_item, mb) { | 110 | if let Some(merged) = try_merge_imports(&existing_use, &use_item, mb) { |
111 | rewriter.replace(existing_use.syntax(), merged.syntax()); | 111 | rewriter.replace(existing_use.syntax(), merged.syntax()); |
@@ -116,7 +116,7 @@ pub fn insert_use<'a>( | |||
116 | 116 | ||
117 | // either we weren't allowed to merge or there is no import that fits the merge conditions | 117 | // either we weren't allowed to merge or there is no import that fits the merge conditions |
118 | // so look for the place we have to insert to | 118 | // so look for the place we have to insert to |
119 | let (insert_position, add_blank) = find_insert_position(scope, path); | 119 | let (insert_position, add_blank) = find_insert_position(scope, path, cfg.group); |
120 | 120 | ||
121 | let indent = if let ident_level @ 1..=usize::MAX = scope.indent_level().0 as usize { | 121 | let indent = if let ident_level @ 1..=usize::MAX = scope.indent_level().0 as usize { |
122 | Some(make::tokens::whitespace(&" ".repeat(4 * ident_level)).into()) | 122 | Some(make::tokens::whitespace(&" ".repeat(4 * ident_level)).into()) |
@@ -137,7 +137,7 @@ pub fn insert_use<'a>( | |||
137 | 137 | ||
138 | if add_blank.has_before() { | 138 | if add_blank.has_before() { |
139 | if let Some(indent) = indent.clone() { | 139 | if let Some(indent) = indent.clone() { |
140 | mark::hit!(insert_use_indent_before); | 140 | cov_mark::hit!(insert_use_indent_before); |
141 | buf.push(indent); | 141 | buf.push(indent); |
142 | } | 142 | } |
143 | } | 143 | } |
@@ -155,11 +155,11 @@ pub fn insert_use<'a>( | |||
155 | // only add indentation *after* our stuff if there's another node directly after it | 155 | // only add indentation *after* our stuff if there's another node directly after it |
156 | if add_blank.has_after() && matches!(insert_position, InsertPosition::Before(_)) { | 156 | if add_blank.has_after() && matches!(insert_position, InsertPosition::Before(_)) { |
157 | if let Some(indent) = indent { | 157 | if let Some(indent) = indent { |
158 | mark::hit!(insert_use_indent_after); | 158 | cov_mark::hit!(insert_use_indent_after); |
159 | buf.push(indent); | 159 | buf.push(indent); |
160 | } | 160 | } |
161 | } else if add_blank.has_after() && matches!(insert_position, InsertPosition::After(_)) { | 161 | } else if add_blank.has_after() && matches!(insert_position, InsertPosition::After(_)) { |
162 | mark::hit!(insert_use_no_indent_after); | 162 | cov_mark::hit!(insert_use_no_indent_after); |
163 | } | 163 | } |
164 | 164 | ||
165 | buf | 165 | buf |
@@ -538,6 +538,7 @@ impl AddBlankLine { | |||
538 | fn find_insert_position( | 538 | fn find_insert_position( |
539 | scope: &ImportScope, | 539 | scope: &ImportScope, |
540 | insert_path: ast::Path, | 540 | insert_path: ast::Path, |
541 | group_imports: bool, | ||
541 | ) -> (InsertPosition<SyntaxElement>, AddBlankLine) { | 542 | ) -> (InsertPosition<SyntaxElement>, AddBlankLine) { |
542 | let group = ImportGroup::new(&insert_path); | 543 | let group = ImportGroup::new(&insert_path); |
543 | let path_node_iter = scope | 544 | let path_node_iter = scope |
@@ -550,6 +551,14 @@ fn find_insert_position( | |||
550 | let has_tl = tree.use_tree_list().is_some(); | 551 | let has_tl = tree.use_tree_list().is_some(); |
551 | Some((path, has_tl, node)) | 552 | Some((path, has_tl, node)) |
552 | }); | 553 | }); |
554 | |||
555 | if !group_imports { | ||
556 | if let Some((_, _, node)) = path_node_iter.last() { | ||
557 | return (InsertPosition::After(node.into()), AddBlankLine::Before); | ||
558 | } | ||
559 | return (InsertPosition::First, AddBlankLine::AfterTwice); | ||
560 | } | ||
561 | |||
553 | // Iterator that discards anything thats not in the required grouping | 562 | // Iterator that discards anything thats not in the required grouping |
554 | // This implementation allows the user to rearrange their import groups as this only takes the first group that fits | 563 | // This implementation allows the user to rearrange their import groups as this only takes the first group that fits |
555 | let group_iter = path_node_iter | 564 | let group_iter = path_node_iter |
@@ -565,6 +574,7 @@ fn find_insert_position( | |||
565 | use_tree_path_cmp(&insert_path, false, path, has_tl) != Ordering::Greater | 574 | use_tree_path_cmp(&insert_path, false, path, has_tl) != Ordering::Greater |
566 | }, | 575 | }, |
567 | ); | 576 | ); |
577 | |||
568 | match post_insert { | 578 | match post_insert { |
569 | // insert our import before that element | 579 | // insert our import before that element |
570 | Some((.., node)) => (InsertPosition::Before(node.into()), AddBlankLine::After), | 580 | Some((.., node)) => (InsertPosition::Before(node.into()), AddBlankLine::After), |
diff --git a/crates/ide_db/src/helpers/insert_use/tests.rs b/crates/ide_db/src/helpers/insert_use/tests.rs index 4bbe66f1f..3d151e629 100644 --- a/crates/ide_db/src/helpers/insert_use/tests.rs +++ b/crates/ide_db/src/helpers/insert_use/tests.rs | |||
@@ -1,8 +1,32 @@ | |||
1 | use super::*; | 1 | use super::*; |
2 | 2 | ||
3 | use hir::PrefixKind; | ||
3 | use test_utils::assert_eq_text; | 4 | use test_utils::assert_eq_text; |
4 | 5 | ||
5 | #[test] | 6 | #[test] |
7 | fn insert_not_group() { | ||
8 | check( | ||
9 | "use external_crate2::bar::A", | ||
10 | r" | ||
11 | use std::bar::B; | ||
12 | use external_crate::bar::A; | ||
13 | use crate::bar::A; | ||
14 | use self::bar::A; | ||
15 | use super::bar::A;", | ||
16 | r" | ||
17 | use std::bar::B; | ||
18 | use external_crate::bar::A; | ||
19 | use crate::bar::A; | ||
20 | use self::bar::A; | ||
21 | use super::bar::A; | ||
22 | use external_crate2::bar::A;", | ||
23 | None, | ||
24 | false, | ||
25 | false, | ||
26 | ); | ||
27 | } | ||
28 | |||
29 | #[test] | ||
6 | fn insert_existing() { | 30 | fn insert_existing() { |
7 | check_full("std::fs", "use std::fs;", "use std::fs;") | 31 | check_full("std::fs", "use std::fs;", "use std::fs;") |
8 | } | 32 | } |
@@ -27,7 +51,7 @@ use std::bar::G;", | |||
27 | 51 | ||
28 | #[test] | 52 | #[test] |
29 | fn insert_start_indent() { | 53 | fn insert_start_indent() { |
30 | mark::check!(insert_use_indent_after); | 54 | cov_mark::check!(insert_use_indent_after); |
31 | check_none( | 55 | check_none( |
32 | "std::bar::AA", | 56 | "std::bar::AA", |
33 | r" | 57 | r" |
@@ -96,7 +120,7 @@ use std::bar::ZZ;", | |||
96 | 120 | ||
97 | #[test] | 121 | #[test] |
98 | fn insert_end_indent() { | 122 | fn insert_end_indent() { |
99 | mark::check!(insert_use_indent_before); | 123 | cov_mark::check!(insert_use_indent_before); |
100 | check_none( | 124 | check_none( |
101 | "std::bar::ZZ", | 125 | "std::bar::ZZ", |
102 | r" | 126 | r" |
@@ -231,7 +255,7 @@ fn insert_empty_file() { | |||
231 | 255 | ||
232 | #[test] | 256 | #[test] |
233 | fn insert_empty_module() { | 257 | fn insert_empty_module() { |
234 | mark::check!(insert_use_no_indent_after); | 258 | cov_mark::check!(insert_use_no_indent_after); |
235 | check( | 259 | check( |
236 | "foo::bar", | 260 | "foo::bar", |
237 | "mod x {}", | 261 | "mod x {}", |
@@ -240,6 +264,7 @@ fn insert_empty_module() { | |||
240 | }", | 264 | }", |
241 | None, | 265 | None, |
242 | true, | 266 | true, |
267 | true, | ||
243 | ) | 268 | ) |
244 | } | 269 | } |
245 | 270 | ||
@@ -584,6 +609,7 @@ fn check( | |||
584 | ra_fixture_after: &str, | 609 | ra_fixture_after: &str, |
585 | mb: Option<MergeBehavior>, | 610 | mb: Option<MergeBehavior>, |
586 | module: bool, | 611 | module: bool, |
612 | group: bool, | ||
587 | ) { | 613 | ) { |
588 | let mut syntax = ast::SourceFile::parse(ra_fixture_before).tree().syntax().clone(); | 614 | let mut syntax = ast::SourceFile::parse(ra_fixture_before).tree().syntax().clone(); |
589 | if module { | 615 | if module { |
@@ -597,21 +623,25 @@ fn check( | |||
597 | .find_map(ast::Path::cast) | 623 | .find_map(ast::Path::cast) |
598 | .unwrap(); | 624 | .unwrap(); |
599 | 625 | ||
600 | let rewriter = insert_use(&file, path, mb); | 626 | let rewriter = insert_use( |
627 | &file, | ||
628 | path, | ||
629 | InsertUseConfig { merge: mb, prefix_kind: PrefixKind::Plain, group }, | ||
630 | ); | ||
601 | let result = rewriter.rewrite(file.as_syntax_node()).to_string(); | 631 | let result = rewriter.rewrite(file.as_syntax_node()).to_string(); |
602 | assert_eq_text!(ra_fixture_after, &result); | 632 | assert_eq_text!(ra_fixture_after, &result); |
603 | } | 633 | } |
604 | 634 | ||
605 | fn check_full(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) { | 635 | fn check_full(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) { |
606 | check(path, ra_fixture_before, ra_fixture_after, Some(MergeBehavior::Full), false) | 636 | check(path, ra_fixture_before, ra_fixture_after, Some(MergeBehavior::Full), false, true) |
607 | } | 637 | } |
608 | 638 | ||
609 | fn check_last(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) { | 639 | fn check_last(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) { |
610 | check(path, ra_fixture_before, ra_fixture_after, Some(MergeBehavior::Last), false) | 640 | check(path, ra_fixture_before, ra_fixture_after, Some(MergeBehavior::Last), false, true) |
611 | } | 641 | } |
612 | 642 | ||
613 | fn check_none(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) { | 643 | fn check_none(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) { |
614 | check(path, ra_fixture_before, ra_fixture_after, None, false) | 644 | check(path, ra_fixture_before, ra_fixture_after, None, false, true) |
615 | } | 645 | } |
616 | 646 | ||
617 | fn check_merge_only_fail(ra_fixture0: &str, ra_fixture1: &str, mb: MergeBehavior) { | 647 | fn check_merge_only_fail(ra_fixture0: &str, ra_fixture1: &str, mb: MergeBehavior) { |
diff --git a/crates/ide_db/src/imports_locator.rs b/crates/ide_db/src/items_locator.rs index 502e8281a..8a7f02935 100644 --- a/crates/ide_db/src/imports_locator.rs +++ b/crates/ide_db/src/items_locator.rs | |||
@@ -1,9 +1,10 @@ | |||
1 | //! This module contains an import search functionality that is provided to the assists module. | 1 | //! This module contains an import search functionality that is provided to the assists module. |
2 | //! Later, this should be moved away to a separate crate that is accessible from the assists module. | 2 | //! Later, this should be moved away to a separate crate that is accessible from the assists module. |
3 | 3 | ||
4 | use either::Either; | ||
4 | use hir::{ | 5 | use hir::{ |
5 | import_map::{self, ImportKind}, | 6 | import_map::{self, ImportKind}, |
6 | AsAssocItem, Crate, MacroDef, ModuleDef, Semantics, | 7 | AsAssocItem, Crate, ItemInNs, ModuleDef, Semantics, |
7 | }; | 8 | }; |
8 | use syntax::{ast, AstNode, SyntaxKind::NAME}; | 9 | use syntax::{ast, AstNode, SyntaxKind::NAME}; |
9 | 10 | ||
@@ -12,47 +13,47 @@ use crate::{ | |||
12 | symbol_index::{self, FileSymbol}, | 13 | symbol_index::{self, FileSymbol}, |
13 | RootDatabase, | 14 | RootDatabase, |
14 | }; | 15 | }; |
15 | use either::Either; | ||
16 | use rustc_hash::FxHashSet; | 16 | use rustc_hash::FxHashSet; |
17 | 17 | ||
18 | pub(crate) const DEFAULT_QUERY_SEARCH_LIMIT: usize = 40; | 18 | pub(crate) const DEFAULT_QUERY_SEARCH_LIMIT: usize = 40; |
19 | 19 | ||
20 | pub fn find_exact_imports<'a>( | 20 | pub fn with_exact_name( |
21 | sema: &Semantics<'a, RootDatabase>, | 21 | sema: &Semantics<'_, RootDatabase>, |
22 | krate: Crate, | 22 | krate: Crate, |
23 | name_to_import: String, | 23 | exact_name: String, |
24 | ) -> Box<dyn Iterator<Item = Either<ModuleDef, MacroDef>>> { | 24 | ) -> FxHashSet<ItemInNs> { |
25 | let _p = profile::span("find_exact_imports"); | 25 | let _p = profile::span("find_exact_imports"); |
26 | Box::new(find_imports( | 26 | find_items( |
27 | sema, | 27 | sema, |
28 | krate, | 28 | krate, |
29 | { | 29 | { |
30 | let mut local_query = symbol_index::Query::new(name_to_import.clone()); | 30 | let mut local_query = symbol_index::Query::new(exact_name.clone()); |
31 | local_query.exact(); | 31 | local_query.exact(); |
32 | local_query.limit(DEFAULT_QUERY_SEARCH_LIMIT); | 32 | local_query.limit(DEFAULT_QUERY_SEARCH_LIMIT); |
33 | local_query | 33 | local_query |
34 | }, | 34 | }, |
35 | import_map::Query::new(name_to_import) | 35 | import_map::Query::new(exact_name) |
36 | .limit(DEFAULT_QUERY_SEARCH_LIMIT) | 36 | .limit(DEFAULT_QUERY_SEARCH_LIMIT) |
37 | .name_only() | 37 | .name_only() |
38 | .search_mode(import_map::SearchMode::Equals) | 38 | .search_mode(import_map::SearchMode::Equals) |
39 | .case_sensitive(), | 39 | .case_sensitive(), |
40 | )) | 40 | ) |
41 | } | 41 | } |
42 | 42 | ||
43 | #[derive(Debug)] | ||
43 | pub enum AssocItemSearch { | 44 | pub enum AssocItemSearch { |
44 | Include, | 45 | Include, |
45 | Exclude, | 46 | Exclude, |
46 | AssocItemsOnly, | 47 | AssocItemsOnly, |
47 | } | 48 | } |
48 | 49 | ||
49 | pub fn find_similar_imports<'a>( | 50 | pub fn with_similar_name( |
50 | sema: &Semantics<'a, RootDatabase>, | 51 | sema: &Semantics<'_, RootDatabase>, |
51 | krate: Crate, | 52 | krate: Crate, |
52 | fuzzy_search_string: String, | 53 | fuzzy_search_string: String, |
53 | assoc_item_search: AssocItemSearch, | 54 | assoc_item_search: AssocItemSearch, |
54 | limit: Option<usize>, | 55 | limit: Option<usize>, |
55 | ) -> Box<dyn Iterator<Item = Either<ModuleDef, MacroDef>> + 'a> { | 56 | ) -> FxHashSet<ItemInNs> { |
56 | let _p = profile::span("find_similar_imports"); | 57 | let _p = profile::span("find_similar_imports"); |
57 | 58 | ||
58 | let mut external_query = import_map::Query::new(fuzzy_search_string.clone()) | 59 | let mut external_query = import_map::Query::new(fuzzy_search_string.clone()) |
@@ -76,37 +77,39 @@ pub fn find_similar_imports<'a>( | |||
76 | local_query.limit(limit); | 77 | local_query.limit(limit); |
77 | } | 78 | } |
78 | 79 | ||
79 | let db = sema.db; | 80 | find_items(sema, krate, local_query, external_query) |
80 | Box::new(find_imports(sema, krate, local_query, external_query).filter( | 81 | .into_iter() |
81 | move |import_candidate| match assoc_item_search { | 82 | .filter(move |&item| match assoc_item_search { |
82 | AssocItemSearch::Include => true, | 83 | AssocItemSearch::Include => true, |
83 | AssocItemSearch::Exclude => !is_assoc_item(import_candidate, db), | 84 | AssocItemSearch::Exclude => !is_assoc_item(item, sema.db), |
84 | AssocItemSearch::AssocItemsOnly => is_assoc_item(import_candidate, db), | 85 | AssocItemSearch::AssocItemsOnly => is_assoc_item(item, sema.db), |
85 | }, | 86 | }) |
86 | )) | 87 | .collect() |
87 | } | 88 | } |
88 | 89 | ||
89 | fn is_assoc_item(import_candidate: &Either<ModuleDef, MacroDef>, db: &RootDatabase) -> bool { | 90 | fn is_assoc_item(item: ItemInNs, db: &RootDatabase) -> bool { |
90 | match import_candidate { | 91 | item.as_module_def_id() |
91 | Either::Left(ModuleDef::Function(function)) => function.as_assoc_item(db).is_some(), | 92 | .and_then(|module_def_id| ModuleDef::from(module_def_id).as_assoc_item(db)) |
92 | Either::Left(ModuleDef::Const(const_)) => const_.as_assoc_item(db).is_some(), | 93 | .is_some() |
93 | Either::Left(ModuleDef::TypeAlias(type_alias)) => type_alias.as_assoc_item(db).is_some(), | ||
94 | _ => false, | ||
95 | } | ||
96 | } | 94 | } |
97 | 95 | ||
98 | fn find_imports<'a>( | 96 | fn find_items( |
99 | sema: &Semantics<'a, RootDatabase>, | 97 | sema: &Semantics<'_, RootDatabase>, |
100 | krate: Crate, | 98 | krate: Crate, |
101 | local_query: symbol_index::Query, | 99 | local_query: symbol_index::Query, |
102 | external_query: import_map::Query, | 100 | external_query: import_map::Query, |
103 | ) -> impl Iterator<Item = Either<ModuleDef, MacroDef>> { | 101 | ) -> FxHashSet<ItemInNs> { |
104 | let _p = profile::span("find_similar_imports"); | 102 | let _p = profile::span("find_similar_imports"); |
105 | let db = sema.db; | 103 | let db = sema.db; |
106 | 104 | ||
107 | // Query dependencies first. | 105 | // Query dependencies first. |
108 | let mut candidates: FxHashSet<_> = | 106 | let mut candidates = krate |
109 | krate.query_external_importables(db, external_query).collect(); | 107 | .query_external_importables(db, external_query) |
108 | .map(|external_importable| match external_importable { | ||
109 | Either::Left(module_def) => ItemInNs::from(module_def), | ||
110 | Either::Right(macro_def) => ItemInNs::from(macro_def), | ||
111 | }) | ||
112 | .collect::<FxHashSet<_>>(); | ||
110 | 113 | ||
111 | // Query the local crate using the symbol index. | 114 | // Query the local crate using the symbol index. |
112 | let local_results = symbol_index::crate_symbols(db, krate.into(), local_query); | 115 | let local_results = symbol_index::crate_symbols(db, krate.into(), local_query); |
@@ -114,19 +117,19 @@ fn find_imports<'a>( | |||
114 | candidates.extend( | 117 | candidates.extend( |
115 | local_results | 118 | local_results |
116 | .into_iter() | 119 | .into_iter() |
117 | .filter_map(|import_candidate| get_name_definition(sema, &import_candidate)) | 120 | .filter_map(|local_candidate| get_name_definition(sema, &local_candidate)) |
118 | .filter_map(|name_definition_to_import| match name_definition_to_import { | 121 | .filter_map(|name_definition_to_import| match name_definition_to_import { |
119 | Definition::ModuleDef(module_def) => Some(Either::Left(module_def)), | 122 | Definition::ModuleDef(module_def) => Some(ItemInNs::from(module_def)), |
120 | Definition::Macro(macro_def) => Some(Either::Right(macro_def)), | 123 | Definition::Macro(macro_def) => Some(ItemInNs::from(macro_def)), |
121 | _ => None, | 124 | _ => None, |
122 | }), | 125 | }), |
123 | ); | 126 | ); |
124 | 127 | ||
125 | candidates.into_iter() | 128 | candidates |
126 | } | 129 | } |
127 | 130 | ||
128 | fn get_name_definition<'a>( | 131 | fn get_name_definition( |
129 | sema: &Semantics<'a, RootDatabase>, | 132 | sema: &Semantics<'_, RootDatabase>, |
130 | import_candidate: &FileSymbol, | 133 | import_candidate: &FileSymbol, |
131 | ) -> Option<Definition> { | 134 | ) -> Option<Definition> { |
132 | let _p = profile::span("get_name_definition"); | 135 | let _p = profile::span("get_name_definition"); |
diff --git a/crates/ide_db/src/lib.rs b/crates/ide_db/src/lib.rs index 6eb34b06b..88ee4a87d 100644 --- a/crates/ide_db/src/lib.rs +++ b/crates/ide_db/src/lib.rs | |||
@@ -8,7 +8,7 @@ pub mod line_index; | |||
8 | pub mod symbol_index; | 8 | pub mod symbol_index; |
9 | pub mod defs; | 9 | pub mod defs; |
10 | pub mod search; | 10 | pub mod search; |
11 | pub mod imports_locator; | 11 | pub mod items_locator; |
12 | pub mod source_change; | 12 | pub mod source_change; |
13 | pub mod ty_filter; | 13 | pub mod ty_filter; |
14 | pub mod traits; | 14 | pub mod traits; |
diff --git a/crates/ide_ssr/Cargo.toml b/crates/ide_ssr/Cargo.toml index edbc1846b..8c31df13a 100644 --- a/crates/ide_ssr/Cargo.toml +++ b/crates/ide_ssr/Cargo.toml | |||
@@ -11,6 +11,7 @@ edition = "2018" | |||
11 | doctest = false | 11 | doctest = false |
12 | 12 | ||
13 | [dependencies] | 13 | [dependencies] |
14 | cov-mark = "1.1" | ||
14 | rustc-hash = "1.1.0" | 15 | rustc-hash = "1.1.0" |
15 | itertools = "0.10.0" | 16 | itertools = "0.10.0" |
16 | 17 | ||
@@ -18,7 +19,7 @@ text_edit = { path = "../text_edit", version = "0.0.0" } | |||
18 | syntax = { path = "../syntax", version = "0.0.0" } | 19 | syntax = { path = "../syntax", version = "0.0.0" } |
19 | ide_db = { path = "../ide_db", version = "0.0.0" } | 20 | ide_db = { path = "../ide_db", version = "0.0.0" } |
20 | hir = { path = "../hir", version = "0.0.0" } | 21 | hir = { path = "../hir", version = "0.0.0" } |
21 | test_utils = { path = "../test_utils", version = "0.0.0" } | ||
22 | 22 | ||
23 | [dev-dependencies] | 23 | [dev-dependencies] |
24 | test_utils = { path = "../test_utils" } | ||
24 | expect-test = "1.1" | 25 | expect-test = "1.1" |
diff --git a/crates/ide_ssr/src/from_comment.rs b/crates/ide_ssr/src/from_comment.rs new file mode 100644 index 000000000..f1b312284 --- /dev/null +++ b/crates/ide_ssr/src/from_comment.rs | |||
@@ -0,0 +1,32 @@ | |||
1 | //! This module allows building an SSR MatchFinder by parsing the SSR rule | ||
2 | //! from a comment. | ||
3 | |||
4 | use ide_db::{ | ||
5 | base_db::{FilePosition, FileRange, SourceDatabase}, | ||
6 | RootDatabase, | ||
7 | }; | ||
8 | use syntax::{ | ||
9 | ast::{self, AstNode, AstToken}, | ||
10 | TextRange, | ||
11 | }; | ||
12 | |||
13 | use crate::MatchFinder; | ||
14 | |||
15 | /// Attempts to build an SSR MatchFinder from a comment at the given file | ||
16 | /// range. If successful, returns the MatchFinder and a TextRange covering | ||
17 | /// comment. | ||
18 | pub fn ssr_from_comment(db: &RootDatabase, frange: FileRange) -> Option<(MatchFinder, TextRange)> { | ||
19 | let comment = { | ||
20 | let file = db.parse(frange.file_id); | ||
21 | file.tree().syntax().token_at_offset(frange.range.start()).find_map(ast::Comment::cast) | ||
22 | }?; | ||
23 | let comment_text_without_prefix = comment.text().strip_prefix(comment.prefix()).unwrap(); | ||
24 | let ssr_rule = comment_text_without_prefix.parse().ok()?; | ||
25 | |||
26 | let lookup_context = FilePosition { file_id: frange.file_id, offset: frange.range.start() }; | ||
27 | |||
28 | let mut match_finder = MatchFinder::in_context(db, lookup_context, vec![]); | ||
29 | match_finder.add_rule(ssr_rule).ok()?; | ||
30 | |||
31 | Some((match_finder, comment.syntax().text_range())) | ||
32 | } | ||
diff --git a/crates/ide_ssr/src/lib.rs b/crates/ide_ssr/src/lib.rs index a97fc8bca..00585f448 100644 --- a/crates/ide_ssr/src/lib.rs +++ b/crates/ide_ssr/src/lib.rs | |||
@@ -57,7 +57,17 @@ | |||
57 | // | 57 | // |
58 | // | VS Code | **Rust Analyzer: Structural Search Replace** | 58 | // | VS Code | **Rust Analyzer: Structural Search Replace** |
59 | // |=== | 59 | // |=== |
60 | // | ||
61 | // Also available as an assist, by writing a comment containing the structural | ||
62 | // search and replace rule. You will only see the assist if the comment can | ||
63 | // be parsed as a valid structural search and replace rule. | ||
64 | // | ||
65 | // ```rust | ||
66 | // // Place the cursor on the line below to see the assist 💡. | ||
67 | // // foo($a, $b) ==>> ($a).foo($b) | ||
68 | // ``` | ||
60 | 69 | ||
70 | mod from_comment; | ||
61 | mod matching; | 71 | mod matching; |
62 | mod nester; | 72 | mod nester; |
63 | mod parsing; | 73 | mod parsing; |
@@ -71,6 +81,7 @@ mod tests; | |||
71 | 81 | ||
72 | use crate::errors::bail; | 82 | use crate::errors::bail; |
73 | pub use crate::errors::SsrError; | 83 | pub use crate::errors::SsrError; |
84 | pub use crate::from_comment::ssr_from_comment; | ||
74 | pub use crate::matching::Match; | 85 | pub use crate::matching::Match; |
75 | use crate::matching::MatchFailureReason; | 86 | use crate::matching::MatchFailureReason; |
76 | use hir::Semantics; | 87 | use hir::Semantics; |
diff --git a/crates/ide_ssr/src/matching.rs b/crates/ide_ssr/src/matching.rs index df013bae9..e1adb381e 100644 --- a/crates/ide_ssr/src/matching.rs +++ b/crates/ide_ssr/src/matching.rs | |||
@@ -15,7 +15,6 @@ use syntax::{ | |||
15 | ast::{AstNode, AstToken}, | 15 | ast::{AstNode, AstToken}, |
16 | SmolStr, | 16 | SmolStr, |
17 | }; | 17 | }; |
18 | use test_utils::mark; | ||
19 | 18 | ||
20 | // Creates a match error. If we're currently attempting to match some code that we thought we were | 19 | // Creates a match error. If we're currently attempting to match some code that we thought we were |
21 | // going to match, as indicated by the --debug-snippet flag, then populate the reason field. | 20 | // going to match, as indicated by the --debug-snippet flag, then populate the reason field. |
@@ -731,7 +730,7 @@ impl NodeKind { | |||
731 | fn matches(&self, node: &SyntaxNode) -> Result<(), MatchFailed> { | 730 | fn matches(&self, node: &SyntaxNode) -> Result<(), MatchFailed> { |
732 | let ok = match self { | 731 | let ok = match self { |
733 | Self::Literal => { | 732 | Self::Literal => { |
734 | mark::hit!(literal_constraint); | 733 | cov_mark::hit!(literal_constraint); |
735 | ast::Literal::can_cast(node.kind()) | 734 | ast::Literal::can_cast(node.kind()) |
736 | } | 735 | } |
737 | }; | 736 | }; |
diff --git a/crates/ide_ssr/src/parsing.rs b/crates/ide_ssr/src/parsing.rs index 3d5e4feb7..5ff25cb6d 100644 --- a/crates/ide_ssr/src/parsing.rs +++ b/crates/ide_ssr/src/parsing.rs | |||
@@ -10,7 +10,6 @@ use crate::{SsrError, SsrPattern, SsrRule}; | |||
10 | use rustc_hash::{FxHashMap, FxHashSet}; | 10 | use rustc_hash::{FxHashMap, FxHashSet}; |
11 | use std::{fmt::Display, str::FromStr}; | 11 | use std::{fmt::Display, str::FromStr}; |
12 | use syntax::{ast, AstNode, SmolStr, SyntaxKind, SyntaxNode, T}; | 12 | use syntax::{ast, AstNode, SmolStr, SyntaxKind, SyntaxNode, T}; |
13 | use test_utils::mark; | ||
14 | 13 | ||
15 | #[derive(Debug)] | 14 | #[derive(Debug)] |
16 | pub(crate) struct ParsedRule { | 15 | pub(crate) struct ParsedRule { |
@@ -131,7 +130,7 @@ impl RuleBuilder { | |||
131 | let old_len = self.rules.len(); | 130 | let old_len = self.rules.len(); |
132 | self.rules.retain(|rule| contains_path(&rule.pattern)); | 131 | self.rules.retain(|rule| contains_path(&rule.pattern)); |
133 | if self.rules.len() < old_len { | 132 | if self.rules.len() < old_len { |
134 | mark::hit!(pattern_is_a_single_segment_path); | 133 | cov_mark::hit!(pattern_is_a_single_segment_path); |
135 | } | 134 | } |
136 | } | 135 | } |
137 | Ok(self.rules) | 136 | Ok(self.rules) |
diff --git a/crates/ide_ssr/src/replacing.rs b/crates/ide_ssr/src/replacing.rs index 06a94a46c..c9ccc1961 100644 --- a/crates/ide_ssr/src/replacing.rs +++ b/crates/ide_ssr/src/replacing.rs | |||
@@ -5,7 +5,7 @@ use itertools::Itertools; | |||
5 | use rustc_hash::{FxHashMap, FxHashSet}; | 5 | use rustc_hash::{FxHashMap, FxHashSet}; |
6 | use syntax::ast::{self, AstNode, AstToken}; | 6 | use syntax::ast::{self, AstNode, AstToken}; |
7 | use syntax::{SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken, TextRange, TextSize}; | 7 | use syntax::{SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken, TextRange, TextSize}; |
8 | use test_utils::mark; | 8 | |
9 | use text_edit::TextEdit; | 9 | use text_edit::TextEdit; |
10 | 10 | ||
11 | /// Returns a text edit that will replace each match in `matches` with its corresponding replacement | 11 | /// Returns a text edit that will replace each match in `matches` with its corresponding replacement |
@@ -128,7 +128,7 @@ impl ReplacementRenderer<'_> { | |||
128 | && (placeholder_value.autoderef_count > 0 | 128 | && (placeholder_value.autoderef_count > 0 |
129 | || placeholder_value.autoref_kind != ast::SelfParamKind::Owned) | 129 | || placeholder_value.autoref_kind != ast::SelfParamKind::Owned) |
130 | { | 130 | { |
131 | mark::hit!(replace_autoref_autoderef_capture); | 131 | cov_mark::hit!(replace_autoref_autoderef_capture); |
132 | let ref_kind = match placeholder_value.autoref_kind { | 132 | let ref_kind = match placeholder_value.autoref_kind { |
133 | ast::SelfParamKind::Owned => "", | 133 | ast::SelfParamKind::Owned => "", |
134 | ast::SelfParamKind::Ref => "&", | 134 | ast::SelfParamKind::Ref => "&", |
diff --git a/crates/ide_ssr/src/resolving.rs b/crates/ide_ssr/src/resolving.rs index 14e5a3b69..af94c7bb1 100644 --- a/crates/ide_ssr/src/resolving.rs +++ b/crates/ide_ssr/src/resolving.rs | |||
@@ -6,7 +6,6 @@ use ide_db::base_db::FilePosition; | |||
6 | use parsing::Placeholder; | 6 | use parsing::Placeholder; |
7 | use rustc_hash::FxHashMap; | 7 | use rustc_hash::FxHashMap; |
8 | use syntax::{ast, SmolStr, SyntaxKind, SyntaxNode, SyntaxToken}; | 8 | use syntax::{ast, SmolStr, SyntaxKind, SyntaxNode, SyntaxToken}; |
9 | use test_utils::mark; | ||
10 | 9 | ||
11 | pub(crate) struct ResolutionScope<'db> { | 10 | pub(crate) struct ResolutionScope<'db> { |
12 | scope: hir::SemanticsScope<'db>, | 11 | scope: hir::SemanticsScope<'db>, |
@@ -170,13 +169,13 @@ impl Resolver<'_, '_> { | |||
170 | // calls. e.g. `Foo::bar($s)` should match `x.bar()`. | 169 | // calls. e.g. `Foo::bar($s)` should match `x.bar()`. |
171 | true | 170 | true |
172 | } else { | 171 | } else { |
173 | mark::hit!(replace_associated_trait_default_function_call); | 172 | cov_mark::hit!(replace_associated_trait_default_function_call); |
174 | false | 173 | false |
175 | } | 174 | } |
176 | } | 175 | } |
177 | hir::PathResolution::AssocItem(_) => { | 176 | hir::PathResolution::AssocItem(_) => { |
178 | // Not a function. Could be a constant or an associated type. | 177 | // Not a function. Could be a constant or an associated type. |
179 | mark::hit!(replace_associated_trait_constant); | 178 | cov_mark::hit!(replace_associated_trait_constant); |
180 | false | 179 | false |
181 | } | 180 | } |
182 | _ => true, | 181 | _ => true, |
@@ -267,7 +266,7 @@ fn pick_node_for_resolution(node: SyntaxNode) -> SyntaxNode { | |||
267 | match node.kind() { | 266 | match node.kind() { |
268 | SyntaxKind::EXPR_STMT => { | 267 | SyntaxKind::EXPR_STMT => { |
269 | if let Some(n) = node.first_child() { | 268 | if let Some(n) = node.first_child() { |
270 | mark::hit!(cursor_after_semicolon); | 269 | cov_mark::hit!(cursor_after_semicolon); |
271 | return n; | 270 | return n; |
272 | } | 271 | } |
273 | } | 272 | } |
@@ -291,7 +290,7 @@ fn path_contains_type_arguments(path: Option<ast::Path>) -> bool { | |||
291 | if let Some(path) = path { | 290 | if let Some(path) = path { |
292 | if let Some(segment) = path.segment() { | 291 | if let Some(segment) = path.segment() { |
293 | if segment.generic_arg_list().is_some() { | 292 | if segment.generic_arg_list().is_some() { |
294 | mark::hit!(type_arguments_within_path); | 293 | cov_mark::hit!(type_arguments_within_path); |
295 | return true; | 294 | return true; |
296 | } | 295 | } |
297 | } | 296 | } |
diff --git a/crates/ide_ssr/src/search.rs b/crates/ide_ssr/src/search.rs index 836eb94b2..28cef742c 100644 --- a/crates/ide_ssr/src/search.rs +++ b/crates/ide_ssr/src/search.rs | |||
@@ -12,7 +12,6 @@ use ide_db::{ | |||
12 | }; | 12 | }; |
13 | use rustc_hash::FxHashSet; | 13 | use rustc_hash::FxHashSet; |
14 | use syntax::{ast, AstNode, SyntaxKind, SyntaxNode}; | 14 | use syntax::{ast, AstNode, SyntaxKind, SyntaxNode}; |
15 | use test_utils::mark; | ||
16 | 15 | ||
17 | /// A cache for the results of find_usages. This is for when we have multiple patterns that have the | 16 | /// A cache for the results of find_usages. This is for when we have multiple patterns that have the |
18 | /// same path. e.g. if the pattern was `foo::Bar` that can parse as a path, an expression, a type | 17 | /// same path. e.g. if the pattern was `foo::Bar` that can parse as a path, an expression, a type |
@@ -61,7 +60,7 @@ impl<'db> MatchFinder<'db> { | |||
61 | for file_range in self.find_usages(usage_cache, definition).file_ranges() { | 60 | for file_range in self.find_usages(usage_cache, definition).file_ranges() { |
62 | if let Some(node_to_match) = self.find_node_to_match(resolved_path, file_range) { | 61 | if let Some(node_to_match) = self.find_node_to_match(resolved_path, file_range) { |
63 | if !is_search_permitted_ancestors(&node_to_match) { | 62 | if !is_search_permitted_ancestors(&node_to_match) { |
64 | mark::hit!(use_declaration_with_braces); | 63 | cov_mark::hit!(use_declaration_with_braces); |
65 | continue; | 64 | continue; |
66 | } | 65 | } |
67 | self.try_add_match(rule, &node_to_match, &None, matches_out); | 66 | self.try_add_match(rule, &node_to_match, &None, matches_out); |
@@ -205,7 +204,7 @@ impl<'db> MatchFinder<'db> { | |||
205 | matches_out: &mut Vec<Match>, | 204 | matches_out: &mut Vec<Match>, |
206 | ) { | 205 | ) { |
207 | if !self.within_range_restrictions(code) { | 206 | if !self.within_range_restrictions(code) { |
208 | mark::hit!(replace_nonpath_within_selection); | 207 | cov_mark::hit!(replace_nonpath_within_selection); |
209 | return; | 208 | return; |
210 | } | 209 | } |
211 | if let Ok(m) = matching::get_match(false, rule, code, restrict_range, &self.sema) { | 210 | if let Ok(m) = matching::get_match(false, rule, code, restrict_range, &self.sema) { |
diff --git a/crates/ide_ssr/src/tests.rs b/crates/ide_ssr/src/tests.rs index a3ea44f23..1d8565dc0 100644 --- a/crates/ide_ssr/src/tests.rs +++ b/crates/ide_ssr/src/tests.rs | |||
@@ -3,7 +3,7 @@ use expect_test::{expect, Expect}; | |||
3 | use ide_db::base_db::{salsa::Durability, FileId, FilePosition, FileRange, SourceDatabaseExt}; | 3 | use ide_db::base_db::{salsa::Durability, FileId, FilePosition, FileRange, SourceDatabaseExt}; |
4 | use rustc_hash::FxHashSet; | 4 | use rustc_hash::FxHashSet; |
5 | use std::sync::Arc; | 5 | use std::sync::Arc; |
6 | use test_utils::{mark, RangeOrOffset}; | 6 | use test_utils::RangeOrOffset; |
7 | 7 | ||
8 | fn parse_error_text(query: &str) -> String { | 8 | fn parse_error_text(query: &str) -> String { |
9 | format!("{}", query.parse::<SsrRule>().unwrap_err()) | 9 | format!("{}", query.parse::<SsrRule>().unwrap_err()) |
@@ -492,7 +492,7 @@ fn match_resolved_type_name() { | |||
492 | 492 | ||
493 | #[test] | 493 | #[test] |
494 | fn type_arguments_within_path() { | 494 | fn type_arguments_within_path() { |
495 | mark::check!(type_arguments_within_path); | 495 | cov_mark::check!(type_arguments_within_path); |
496 | let code = r#" | 496 | let code = r#" |
497 | mod foo { | 497 | mod foo { |
498 | pub struct Bar<T> {t: T} | 498 | pub struct Bar<T> {t: T} |
@@ -508,7 +508,7 @@ fn type_arguments_within_path() { | |||
508 | 508 | ||
509 | #[test] | 509 | #[test] |
510 | fn literal_constraint() { | 510 | fn literal_constraint() { |
511 | mark::check!(literal_constraint); | 511 | cov_mark::check!(literal_constraint); |
512 | let code = r#" | 512 | let code = r#" |
513 | enum Option<T> { Some(T), None } | 513 | enum Option<T> { Some(T), None } |
514 | use Option::Some; | 514 | use Option::Some; |
@@ -641,7 +641,7 @@ fn replace_associated_function_call() { | |||
641 | 641 | ||
642 | #[test] | 642 | #[test] |
643 | fn replace_associated_trait_default_function_call() { | 643 | fn replace_associated_trait_default_function_call() { |
644 | mark::check!(replace_associated_trait_default_function_call); | 644 | cov_mark::check!(replace_associated_trait_default_function_call); |
645 | assert_ssr_transform( | 645 | assert_ssr_transform( |
646 | "Bar2::foo() ==>> Bar2::foo2()", | 646 | "Bar2::foo() ==>> Bar2::foo2()", |
647 | r#" | 647 | r#" |
@@ -673,7 +673,7 @@ fn replace_associated_trait_default_function_call() { | |||
673 | 673 | ||
674 | #[test] | 674 | #[test] |
675 | fn replace_associated_trait_constant() { | 675 | fn replace_associated_trait_constant() { |
676 | mark::check!(replace_associated_trait_constant); | 676 | cov_mark::check!(replace_associated_trait_constant); |
677 | assert_ssr_transform( | 677 | assert_ssr_transform( |
678 | "Bar2::VALUE ==>> Bar2::VALUE_2222", | 678 | "Bar2::VALUE ==>> Bar2::VALUE_2222", |
679 | r#" | 679 | r#" |
@@ -998,7 +998,7 @@ fn use_declaration_with_braces() { | |||
998 | // It would be OK for a path rule to match and alter a use declaration. We shouldn't mess it up | 998 | // It would be OK for a path rule to match and alter a use declaration. We shouldn't mess it up |
999 | // though. In particular, we must not change `use foo::{baz, bar}` to `use foo::{baz, | 999 | // though. In particular, we must not change `use foo::{baz, bar}` to `use foo::{baz, |
1000 | // foo2::bar2}`. | 1000 | // foo2::bar2}`. |
1001 | mark::check!(use_declaration_with_braces); | 1001 | cov_mark::check!(use_declaration_with_braces); |
1002 | assert_ssr_transform( | 1002 | assert_ssr_transform( |
1003 | "foo::bar ==>> foo2::bar2", | 1003 | "foo::bar ==>> foo2::bar2", |
1004 | r#" | 1004 | r#" |
@@ -1076,7 +1076,7 @@ fn ufcs_matches_method_call() { | |||
1076 | 1076 | ||
1077 | #[test] | 1077 | #[test] |
1078 | fn pattern_is_a_single_segment_path() { | 1078 | fn pattern_is_a_single_segment_path() { |
1079 | mark::check!(pattern_is_a_single_segment_path); | 1079 | cov_mark::check!(pattern_is_a_single_segment_path); |
1080 | // The first function should not be altered because the `foo` in scope at the cursor position is | 1080 | // The first function should not be altered because the `foo` in scope at the cursor position is |
1081 | // a different `foo`. This case is special because "foo" can be parsed as a pattern (IDENT_PAT -> | 1081 | // a different `foo`. This case is special because "foo" can be parsed as a pattern (IDENT_PAT -> |
1082 | // NAME -> IDENT), which contains no path. If we're not careful we'll end up matching the `foo` | 1082 | // NAME -> IDENT), which contains no path. If we're not careful we'll end up matching the `foo` |
@@ -1118,7 +1118,7 @@ fn replace_local_variable_reference() { | |||
1118 | // The pattern references a local variable `foo` in the block containing the cursor. We should | 1118 | // The pattern references a local variable `foo` in the block containing the cursor. We should |
1119 | // only replace references to this variable `foo`, not other variables that just happen to have | 1119 | // only replace references to this variable `foo`, not other variables that just happen to have |
1120 | // the same name. | 1120 | // the same name. |
1121 | mark::check!(cursor_after_semicolon); | 1121 | cov_mark::check!(cursor_after_semicolon); |
1122 | assert_ssr_transform( | 1122 | assert_ssr_transform( |
1123 | "foo + $a ==>> $a - foo", | 1123 | "foo + $a ==>> $a - foo", |
1124 | r#" | 1124 | r#" |
@@ -1179,7 +1179,7 @@ fn replace_path_within_selection() { | |||
1179 | 1179 | ||
1180 | #[test] | 1180 | #[test] |
1181 | fn replace_nonpath_within_selection() { | 1181 | fn replace_nonpath_within_selection() { |
1182 | mark::check!(replace_nonpath_within_selection); | 1182 | cov_mark::check!(replace_nonpath_within_selection); |
1183 | assert_ssr_transform( | 1183 | assert_ssr_transform( |
1184 | "$a + $b ==>> $b * $a", | 1184 | "$a + $b ==>> $b * $a", |
1185 | r#" | 1185 | r#" |
@@ -1269,7 +1269,7 @@ fn replace_autoref_autoderef_capture() { | |||
1269 | // second, we already have a reference, so it isn't. When $a is used in a context where autoref | 1269 | // second, we already have a reference, so it isn't. When $a is used in a context where autoref |
1270 | // doesn't apply, we need to prefix it with `&`. Finally, we have some cases where autoderef | 1270 | // doesn't apply, we need to prefix it with `&`. Finally, we have some cases where autoderef |
1271 | // needs to be applied. | 1271 | // needs to be applied. |
1272 | mark::check!(replace_autoref_autoderef_capture); | 1272 | cov_mark::check!(replace_autoref_autoderef_capture); |
1273 | let code = r#" | 1273 | let code = r#" |
1274 | struct Foo {} | 1274 | struct Foo {} |
1275 | impl Foo { | 1275 | impl Foo { |
diff --git a/crates/mbe/Cargo.toml b/crates/mbe/Cargo.toml index bb2656a80..139214207 100644 --- a/crates/mbe/Cargo.toml +++ b/crates/mbe/Cargo.toml | |||
@@ -10,6 +10,7 @@ edition = "2018" | |||
10 | doctest = false | 10 | doctest = false |
11 | 11 | ||
12 | [dependencies] | 12 | [dependencies] |
13 | cov-mark = "1.1" | ||
13 | rustc-hash = "1.1.0" | 14 | rustc-hash = "1.1.0" |
14 | smallvec = "1.2.0" | 15 | smallvec = "1.2.0" |
15 | log = "0.4.8" | 16 | log = "0.4.8" |
@@ -17,8 +18,8 @@ log = "0.4.8" | |||
17 | syntax = { path = "../syntax", version = "0.0.0" } | 18 | syntax = { path = "../syntax", version = "0.0.0" } |
18 | parser = { path = "../parser", version = "0.0.0" } | 19 | parser = { path = "../parser", version = "0.0.0" } |
19 | tt = { path = "../tt", version = "0.0.0" } | 20 | tt = { path = "../tt", version = "0.0.0" } |
20 | test_utils = { path = "../test_utils", version = "0.0.0" } | ||
21 | stdx = { path = "../stdx", version = "0.0.0" } | 21 | stdx = { path = "../stdx", version = "0.0.0" } |
22 | 22 | ||
23 | [dev-dependencies] | 23 | [dev-dependencies] |
24 | profile = { path = "../profile" } | 24 | profile = { path = "../profile" } |
25 | test_utils = { path = "../test_utils" } | ||
diff --git a/crates/mbe/src/lib.rs b/crates/mbe/src/lib.rs index f3d2da55a..33b85e23d 100644 --- a/crates/mbe/src/lib.rs +++ b/crates/mbe/src/lib.rs | |||
@@ -17,7 +17,6 @@ mod benchmark; | |||
17 | 17 | ||
18 | use std::fmt; | 18 | use std::fmt; |
19 | 19 | ||
20 | use test_utils::mark; | ||
21 | pub use tt::{Delimiter, DelimiterKind, Punct}; | 20 | pub use tt::{Delimiter, DelimiterKind, Punct}; |
22 | 21 | ||
23 | use crate::{ | 22 | use crate::{ |
@@ -217,7 +216,7 @@ impl MacroDef { | |||
217 | let mut rules = Vec::new(); | 216 | let mut rules = Vec::new(); |
218 | 217 | ||
219 | if Some(tt::DelimiterKind::Brace) == tt.delimiter_kind() { | 218 | if Some(tt::DelimiterKind::Brace) == tt.delimiter_kind() { |
220 | mark::hit!(parse_macro_def_rules); | 219 | cov_mark::hit!(parse_macro_def_rules); |
221 | while src.len() > 0 { | 220 | while src.len() > 0 { |
222 | let rule = Rule::parse(&mut src, true)?; | 221 | let rule = Rule::parse(&mut src, true)?; |
223 | rules.push(rule); | 222 | rules.push(rule); |
@@ -229,7 +228,7 @@ impl MacroDef { | |||
229 | } | 228 | } |
230 | } | 229 | } |
231 | } else { | 230 | } else { |
232 | mark::hit!(parse_macro_def_simple); | 231 | cov_mark::hit!(parse_macro_def_simple); |
233 | let rule = Rule::parse(&mut src, false)?; | 232 | let rule = Rule::parse(&mut src, false)?; |
234 | if src.len() != 0 { | 233 | if src.len() != 0 { |
235 | return Err(ParseError::Expected("remain tokens in macro def".to_string())); | 234 | return Err(ParseError::Expected("remain tokens in macro def".to_string())); |
diff --git a/crates/mbe/src/tests.rs b/crates/mbe/src/tests.rs index 08acd4ac2..3a168bb4b 100644 --- a/crates/mbe/src/tests.rs +++ b/crates/mbe/src/tests.rs | |||
@@ -6,7 +6,7 @@ use syntax::{ | |||
6 | SyntaxKind::{ERROR, IDENT}, | 6 | SyntaxKind::{ERROR, IDENT}, |
7 | SyntaxNode, WalkEvent, T, | 7 | SyntaxNode, WalkEvent, T, |
8 | }; | 8 | }; |
9 | use test_utils::{assert_eq_text, mark}; | 9 | use test_utils::assert_eq_text; |
10 | 10 | ||
11 | use super::*; | 11 | use super::*; |
12 | 12 | ||
@@ -687,7 +687,7 @@ fn test_match_literal() { | |||
687 | 687 | ||
688 | #[test] | 688 | #[test] |
689 | fn test_parse_macro_def_simple() { | 689 | fn test_parse_macro_def_simple() { |
690 | mark::check!(parse_macro_def_simple); | 690 | cov_mark::check!(parse_macro_def_simple); |
691 | 691 | ||
692 | parse_macro2( | 692 | parse_macro2( |
693 | r#" | 693 | r#" |
@@ -701,7 +701,7 @@ macro foo($id:ident) { | |||
701 | 701 | ||
702 | #[test] | 702 | #[test] |
703 | fn test_parse_macro_def_rules() { | 703 | fn test_parse_macro_def_rules() { |
704 | mark::check!(parse_macro_def_rules); | 704 | cov_mark::check!(parse_macro_def_rules); |
705 | 705 | ||
706 | parse_macro2( | 706 | parse_macro2( |
707 | r#" | 707 | r#" |
diff --git a/crates/parser/src/grammar.rs b/crates/parser/src/grammar.rs index 6159d064c..6c0e22722 100644 --- a/crates/parser/src/grammar.rs +++ b/crates/parser/src/grammar.rs | |||
@@ -18,7 +18,7 @@ | |||
18 | //! // fn foo() {} | 18 | //! // fn foo() {} |
19 | //! ``` | 19 | //! ``` |
20 | //! | 20 | //! |
21 | //! After adding a new inline-test, run `cargo xtask codegen` to | 21 | //! After adding a new inline-test, run `cargo test -p xtask` to |
22 | //! extract it as a standalone text-fixture into | 22 | //! extract it as a standalone text-fixture into |
23 | //! `crates/syntax/test_data/parser/`, and run `cargo test` once to | 23 | //! `crates/syntax/test_data/parser/`, and run `cargo test` once to |
24 | //! create the "gold" value. | 24 | //! create the "gold" value. |
diff --git a/crates/proc_macro_api/Cargo.toml b/crates/proc_macro_api/Cargo.toml index f09726223..16fd56c7e 100644 --- a/crates/proc_macro_api/Cargo.toml +++ b/crates/proc_macro_api/Cargo.toml | |||
@@ -19,3 +19,6 @@ jod-thread = "0.1.1" | |||
19 | tt = { path = "../tt", version = "0.0.0" } | 19 | tt = { path = "../tt", version = "0.0.0" } |
20 | base_db = { path = "../base_db", version = "0.0.0" } | 20 | base_db = { path = "../base_db", version = "0.0.0" } |
21 | stdx = { path = "../stdx", version = "0.0.0" } | 21 | stdx = { path = "../stdx", version = "0.0.0" } |
22 | snap = "1" | ||
23 | object = { version = "0.23.0", default-features = false, features = ["std", "read_core", "elf", "macho", "pe", "unaligned"] } | ||
24 | memmap = "0.7.0" | ||
diff --git a/crates/proc_macro_api/src/lib.rs b/crates/proc_macro_api/src/lib.rs index 2ea456fb0..941d0fe9e 100644 --- a/crates/proc_macro_api/src/lib.rs +++ b/crates/proc_macro_api/src/lib.rs | |||
@@ -5,10 +5,12 @@ | |||
5 | //! is used to provide basic infrastructure for communication between two | 5 | //! is used to provide basic infrastructure for communication between two |
6 | //! processes: Client (RA itself), Server (the external program) | 6 | //! processes: Client (RA itself), Server (the external program) |
7 | 7 | ||
8 | mod rpc; | ||
9 | mod process; | ||
10 | pub mod msg; | 8 | pub mod msg; |
9 | mod process; | ||
10 | mod rpc; | ||
11 | mod version; | ||
11 | 12 | ||
13 | use base_db::{Env, ProcMacro}; | ||
12 | use std::{ | 14 | use std::{ |
13 | ffi::OsStr, | 15 | ffi::OsStr, |
14 | io, | 16 | io, |
@@ -16,7 +18,6 @@ use std::{ | |||
16 | sync::Arc, | 18 | sync::Arc, |
17 | }; | 19 | }; |
18 | 20 | ||
19 | use base_db::{Env, ProcMacro}; | ||
20 | use tt::{SmolStr, Subtree}; | 21 | use tt::{SmolStr, Subtree}; |
21 | 22 | ||
22 | use crate::process::{ProcMacroProcessSrv, ProcMacroProcessThread}; | 23 | use crate::process::{ProcMacroProcessSrv, ProcMacroProcessThread}; |
@@ -75,6 +76,21 @@ impl ProcMacroClient { | |||
75 | } | 76 | } |
76 | 77 | ||
77 | pub fn by_dylib_path(&self, dylib_path: &Path) -> Vec<ProcMacro> { | 78 | pub fn by_dylib_path(&self, dylib_path: &Path) -> Vec<ProcMacro> { |
79 | match version::read_info(dylib_path) { | ||
80 | Ok(info) => { | ||
81 | if info.version.0 < 1 || info.version.1 < 47 { | ||
82 | eprintln!("proc-macro {} built by {:#?} is not supported by Rust Analyzer, please update your rust version.", dylib_path.to_string_lossy(), info); | ||
83 | } | ||
84 | } | ||
85 | Err(err) => { | ||
86 | eprintln!( | ||
87 | "proc-macro {} failed to find the given version. Reason: {}", | ||
88 | dylib_path.to_string_lossy(), | ||
89 | err | ||
90 | ); | ||
91 | } | ||
92 | } | ||
93 | |||
78 | let macros = match self.process.find_proc_macros(dylib_path) { | 94 | let macros = match self.process.find_proc_macros(dylib_path) { |
79 | Err(err) => { | 95 | Err(err) => { |
80 | eprintln!("Failed to find proc macros. Error: {:#?}", err); | 96 | eprintln!("Failed to find proc macros. Error: {:#?}", err); |
diff --git a/crates/proc_macro_api/src/version.rs b/crates/proc_macro_api/src/version.rs new file mode 100644 index 000000000..11a7fb59a --- /dev/null +++ b/crates/proc_macro_api/src/version.rs | |||
@@ -0,0 +1,132 @@ | |||
1 | //! Reading proc-macro rustc version information from binary data | ||
2 | |||
3 | use std::{ | ||
4 | fs::File, | ||
5 | io::{self, Read}, | ||
6 | path::Path, | ||
7 | }; | ||
8 | |||
9 | use memmap::Mmap; | ||
10 | use object::read::{File as BinaryFile, Object, ObjectSection}; | ||
11 | use snap::read::FrameDecoder as SnapDecoder; | ||
12 | |||
13 | #[derive(Debug)] | ||
14 | pub(crate) struct RustCInfo { | ||
15 | pub(crate) version: (usize, usize, usize), | ||
16 | pub(crate) channel: String, | ||
17 | pub(crate) commit: String, | ||
18 | pub(crate) date: String, | ||
19 | } | ||
20 | |||
21 | pub(crate) fn read_info(dylib_path: &Path) -> io::Result<RustCInfo> { | ||
22 | macro_rules! err { | ||
23 | ($e:literal) => { | ||
24 | io::Error::new(io::ErrorKind::InvalidData, $e) | ||
25 | }; | ||
26 | } | ||
27 | |||
28 | let ver_str = read_version(dylib_path)?; | ||
29 | let mut items = ver_str.split_whitespace(); | ||
30 | let tag = items.next().ok_or(err!("version format error"))?; | ||
31 | if tag != "rustc" { | ||
32 | return Err(err!("version format error (No rustc tag)")); | ||
33 | } | ||
34 | |||
35 | let version_part = items.next().ok_or(err!("no version string"))?; | ||
36 | let mut version_parts = version_part.split("-"); | ||
37 | let version = version_parts.next().ok_or(err!("no version"))?; | ||
38 | let channel = version_parts.next().unwrap_or_default().to_string(); | ||
39 | |||
40 | let commit = items.next().ok_or(err!("no commit info"))?; | ||
41 | // remove ( | ||
42 | if commit.len() == 0 { | ||
43 | return Err(err!("commit format error")); | ||
44 | } | ||
45 | let commit = commit[1..].to_string(); | ||
46 | let date = items.next().ok_or(err!("no date info"))?; | ||
47 | // remove ) | ||
48 | if date.len() == 0 { | ||
49 | return Err(err!("date format error")); | ||
50 | } | ||
51 | let date = date[0..date.len() - 2].to_string(); | ||
52 | |||
53 | let version_numbers = version | ||
54 | .split(".") | ||
55 | .map(|it| it.parse::<usize>()) | ||
56 | .collect::<Result<Vec<_>, _>>() | ||
57 | .map_err(|_| err!("version number error"))?; | ||
58 | |||
59 | if version_numbers.len() != 3 { | ||
60 | return Err(err!("version number format error")); | ||
61 | } | ||
62 | let version = (version_numbers[0], version_numbers[1], version_numbers[2]); | ||
63 | |||
64 | Ok(RustCInfo { version, channel, commit, date }) | ||
65 | } | ||
66 | |||
67 | /// This is used inside read_version() to locate the ".rustc" section | ||
68 | /// from a proc macro crate's binary file. | ||
69 | fn read_section<'a>(dylib_binary: &'a [u8], section_name: &str) -> io::Result<&'a [u8]> { | ||
70 | BinaryFile::parse(dylib_binary) | ||
71 | .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))? | ||
72 | .section_by_name(section_name) | ||
73 | .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "section read error"))? | ||
74 | .data() | ||
75 | .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) | ||
76 | } | ||
77 | |||
78 | /// Check the version of rustc that was used to compile a proc macro crate's | ||
79 | /// | ||
80 | /// binary file. | ||
81 | /// A proc macro crate binary's ".rustc" section has following byte layout: | ||
82 | /// * [b'r',b'u',b's',b't',0,0,0,5] is the first 8 bytes | ||
83 | /// * ff060000 734e6150 is followed, it's the snappy format magic bytes, | ||
84 | /// means bytes from here(including this sequence) are compressed in | ||
85 | /// snappy compression format. Version info is inside here, so decompress | ||
86 | /// this. | ||
87 | /// The bytes you get after decompressing the snappy format portion has | ||
88 | /// following layout: | ||
89 | /// * [b'r',b'u',b's',b't',0,0,0,5] is the first 8 bytes(again) | ||
90 | /// * [crate root bytes] next 4 bytes is to store crate root position, | ||
91 | /// according to rustc's source code comment | ||
92 | /// * [length byte] next 1 byte tells us how many bytes we should read next | ||
93 | /// for the version string's utf8 bytes | ||
94 | /// * [version string bytes encoded in utf8] <- GET THIS BOI | ||
95 | /// * [some more bytes that we don really care but still there] :-) | ||
96 | /// Check this issue for more about the bytes layout: | ||
97 | /// https://github.com/rust-analyzer/rust-analyzer/issues/6174 | ||
98 | fn read_version(dylib_path: &Path) -> io::Result<String> { | ||
99 | let dylib_file = File::open(dylib_path)?; | ||
100 | let dylib_mmaped = unsafe { Mmap::map(&dylib_file) }?; | ||
101 | |||
102 | let dot_rustc = read_section(&dylib_mmaped, ".rustc")?; | ||
103 | |||
104 | let header = &dot_rustc[..8]; | ||
105 | const EXPECTED_HEADER: [u8; 8] = [b'r', b'u', b's', b't', 0, 0, 0, 5]; | ||
106 | // check if header is valid | ||
107 | if header != EXPECTED_HEADER { | ||
108 | return Err(io::Error::new( | ||
109 | io::ErrorKind::InvalidData, | ||
110 | format!("only metadata version 5 is supported, section header was: {:?}", header), | ||
111 | )); | ||
112 | } | ||
113 | |||
114 | let snappy_portion = &dot_rustc[8..]; | ||
115 | |||
116 | let mut snappy_decoder = SnapDecoder::new(snappy_portion); | ||
117 | |||
118 | // the bytes before version string bytes, so this basically is: | ||
119 | // 8 bytes for [b'r',b'u',b's',b't',0,0,0,5] | ||
120 | // 4 bytes for [crate root bytes] | ||
121 | // 1 byte for length of version string | ||
122 | // so 13 bytes in total, and we should check the 13th byte | ||
123 | // to know the length | ||
124 | let mut bytes_before_version = [0u8; 13]; | ||
125 | snappy_decoder.read_exact(&mut bytes_before_version)?; | ||
126 | let length = bytes_before_version[12]; // what? can't use -1 indexing? | ||
127 | |||
128 | let mut version_string_utf8 = vec![0u8; length as usize]; | ||
129 | snappy_decoder.read_exact(&mut version_string_utf8)?; | ||
130 | let version_string = String::from_utf8(version_string_utf8); | ||
131 | version_string.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) | ||
132 | } | ||
diff --git a/crates/proc_macro_srv/Cargo.toml b/crates/proc_macro_srv/Cargo.toml index 4c1b3036a..63b3f1532 100644 --- a/crates/proc_macro_srv/Cargo.toml +++ b/crates/proc_macro_srv/Cargo.toml | |||
@@ -17,9 +17,9 @@ memmap2 = "0.2.0" | |||
17 | tt = { path = "../tt", version = "0.0.0" } | 17 | tt = { path = "../tt", version = "0.0.0" } |
18 | mbe = { path = "../mbe", version = "0.0.0" } | 18 | mbe = { path = "../mbe", version = "0.0.0" } |
19 | proc_macro_api = { path = "../proc_macro_api", version = "0.0.0" } | 19 | proc_macro_api = { path = "../proc_macro_api", version = "0.0.0" } |
20 | test_utils = { path = "../test_utils", version = "0.0.0" } | ||
21 | 20 | ||
22 | [dev-dependencies] | 21 | [dev-dependencies] |
22 | test_utils = { path = "../test_utils" } | ||
23 | cargo_metadata = "0.13" | 23 | cargo_metadata = "0.13" |
24 | 24 | ||
25 | # used as proc macro test targets | 25 | # used as proc macro test targets |
diff --git a/crates/proc_macro_srv/src/proc_macro/bridge/mod.rs b/crates/proc_macro_srv/src/proc_macro/bridge/mod.rs index e4006a5ab..e67902682 100644 --- a/crates/proc_macro_srv/src/proc_macro/bridge/mod.rs +++ b/crates/proc_macro_srv/src/proc_macro/bridge/mod.rs | |||
@@ -268,7 +268,7 @@ trait Mark { | |||
268 | fn mark(unmarked: Self::Unmarked) -> Self; | 268 | fn mark(unmarked: Self::Unmarked) -> Self; |
269 | } | 269 | } |
270 | 270 | ||
271 | /// Unwrap types wrapped by `Mark::mark` (see `Mark` for details). | 271 | /// Unwrap types wrapped by `cov_mark::mark` (see `Mark` for details). |
272 | trait Unmark { | 272 | trait Unmark { |
273 | type Unmarked; | 273 | type Unmarked; |
274 | fn unmark(self) -> Self::Unmarked; | 274 | fn unmark(self) -> Self::Unmarked; |
diff --git a/crates/project_model/src/cargo_workspace.rs b/crates/project_model/src/cargo_workspace.rs index f7241b711..bc6e20341 100644 --- a/crates/project_model/src/cargo_workspace.rs +++ b/crates/project_model/src/cargo_workspace.rs | |||
@@ -9,6 +9,8 @@ use cargo_metadata::{CargoOpt, MetadataCommand}; | |||
9 | use la_arena::{Arena, Idx}; | 9 | use la_arena::{Arena, Idx}; |
10 | use paths::{AbsPath, AbsPathBuf}; | 10 | use paths::{AbsPath, AbsPathBuf}; |
11 | use rustc_hash::FxHashMap; | 11 | use rustc_hash::FxHashMap; |
12 | use serde::Deserialize; | ||
13 | use serde_json::from_value; | ||
12 | 14 | ||
13 | use crate::build_data::BuildDataConfig; | 15 | use crate::build_data::BuildDataConfig; |
14 | use crate::utf8_stdout; | 16 | use crate::utf8_stdout; |
@@ -104,6 +106,13 @@ pub struct PackageData { | |||
104 | pub active_features: Vec<String>, | 106 | pub active_features: Vec<String>, |
105 | // String representation of package id | 107 | // String representation of package id |
106 | pub id: String, | 108 | pub id: String, |
109 | // The contents of [package.metadata.rust-analyzer] | ||
110 | pub metadata: RustAnalyzerPackageMetaData, | ||
111 | } | ||
112 | |||
113 | #[derive(Deserialize, Default, Debug, Clone, Eq, PartialEq)] | ||
114 | pub struct RustAnalyzerPackageMetaData { | ||
115 | pub rustc_private: bool, | ||
107 | } | 116 | } |
108 | 117 | ||
109 | #[derive(Debug, Clone, Eq, PartialEq)] | 118 | #[derive(Debug, Clone, Eq, PartialEq)] |
@@ -161,6 +170,13 @@ impl PackageData { | |||
161 | } | 170 | } |
162 | } | 171 | } |
163 | 172 | ||
173 | #[derive(Deserialize, Default)] | ||
174 | // Deserialise helper for the cargo metadata | ||
175 | struct PackageMetadata { | ||
176 | #[serde(rename = "rust-analyzer")] | ||
177 | rust_analyzer: Option<RustAnalyzerPackageMetaData>, | ||
178 | } | ||
179 | |||
164 | impl CargoWorkspace { | 180 | impl CargoWorkspace { |
165 | pub fn from_cargo_metadata( | 181 | pub fn from_cargo_metadata( |
166 | cargo_toml: &AbsPath, | 182 | cargo_toml: &AbsPath, |
@@ -244,8 +260,10 @@ impl CargoWorkspace { | |||
244 | 260 | ||
245 | meta.packages.sort_by(|a, b| a.id.cmp(&b.id)); | 261 | meta.packages.sort_by(|a, b| a.id.cmp(&b.id)); |
246 | for meta_pkg in &meta.packages { | 262 | for meta_pkg in &meta.packages { |
247 | let cargo_metadata::Package { id, edition, name, manifest_path, version, .. } = | 263 | let cargo_metadata::Package { |
248 | meta_pkg; | 264 | id, edition, name, manifest_path, version, metadata, .. |
265 | } = meta_pkg; | ||
266 | let meta = from_value::<PackageMetadata>(metadata.clone()).unwrap_or_default(); | ||
249 | let is_member = ws_members.contains(&id); | 267 | let is_member = ws_members.contains(&id); |
250 | let edition = edition | 268 | let edition = edition |
251 | .parse::<Edition>() | 269 | .parse::<Edition>() |
@@ -262,6 +280,7 @@ impl CargoWorkspace { | |||
262 | dependencies: Vec::new(), | 280 | dependencies: Vec::new(), |
263 | features: meta_pkg.features.clone().into_iter().collect(), | 281 | features: meta_pkg.features.clone().into_iter().collect(), |
264 | active_features: Vec::new(), | 282 | active_features: Vec::new(), |
283 | metadata: meta.rust_analyzer.unwrap_or_default(), | ||
265 | }); | 284 | }); |
266 | let pkg_data = &mut packages[pkg]; | 285 | let pkg_data = &mut packages[pkg]; |
267 | pkg_by_id.insert(id, pkg); | 286 | pkg_by_id.insert(id, pkg); |
diff --git a/crates/project_model/src/workspace.rs b/crates/project_model/src/workspace.rs index 0220efdb4..1b53fcc30 100644 --- a/crates/project_model/src/workspace.rs +++ b/crates/project_model/src/workspace.rs | |||
@@ -2,11 +2,7 @@ | |||
2 | //! metadata` or `rust-project.json`) into representation stored in the salsa | 2 | //! metadata` or `rust-project.json`) into representation stored in the salsa |
3 | //! database -- `CrateGraph`. | 3 | //! database -- `CrateGraph`. |
4 | 4 | ||
5 | use std::{ | 5 | use std::{collections::VecDeque, fmt, fs, path::Path, process::Command}; |
6 | fmt, fs, | ||
7 | path::{Component, Path}, | ||
8 | process::Command, | ||
9 | }; | ||
10 | 6 | ||
11 | use anyhow::{Context, Result}; | 7 | use anyhow::{Context, Result}; |
12 | use base_db::{CrateDisplayName, CrateGraph, CrateId, CrateName, Edition, Env, FileId, ProcMacro}; | 8 | use base_db::{CrateDisplayName, CrateGraph, CrateId, CrateName, Edition, Env, FileId, ProcMacro}; |
@@ -60,6 +56,7 @@ impl fmt::Debug for ProjectWorkspace { | |||
60 | match self { | 56 | match self { |
61 | ProjectWorkspace::Cargo { cargo, sysroot, rustc, rustc_cfg } => f | 57 | ProjectWorkspace::Cargo { cargo, sysroot, rustc, rustc_cfg } => f |
62 | .debug_struct("Cargo") | 58 | .debug_struct("Cargo") |
59 | .field("root", &cargo.workspace_root().file_name()) | ||
63 | .field("n_packages", &cargo.packages().len()) | 60 | .field("n_packages", &cargo.packages().len()) |
64 | .field("n_sysroot_crates", &sysroot.crates().len()) | 61 | .field("n_sysroot_crates", &sysroot.crates().len()) |
65 | .field( | 62 | .field( |
@@ -279,11 +276,8 @@ impl ProjectWorkspace { | |||
279 | 276 | ||
280 | pub fn collect_build_data_configs(&self, collector: &mut BuildDataCollector) { | 277 | pub fn collect_build_data_configs(&self, collector: &mut BuildDataCollector) { |
281 | match self { | 278 | match self { |
282 | ProjectWorkspace::Cargo { cargo, rustc, .. } => { | 279 | ProjectWorkspace::Cargo { cargo, .. } => { |
283 | collector.add_config(&cargo.workspace_root(), cargo.build_data_config().clone()); | 280 | collector.add_config(&cargo.workspace_root(), cargo.build_data_config().clone()); |
284 | if let Some(rustc) = rustc { | ||
285 | collector.add_config(rustc.workspace_root(), rustc.build_data_config().clone()); | ||
286 | } | ||
287 | } | 281 | } |
288 | _ => {} | 282 | _ => {} |
289 | } | 283 | } |
@@ -380,9 +374,11 @@ fn cargo_to_crate_graph( | |||
380 | cfg_options.insert_atom("debug_assertions".into()); | 374 | cfg_options.insert_atom("debug_assertions".into()); |
381 | 375 | ||
382 | let mut pkg_crates = FxHashMap::default(); | 376 | let mut pkg_crates = FxHashMap::default(); |
383 | 377 | // Does any crate signal to rust-analyzer that they need the rustc_private crates? | |
378 | let mut has_private = false; | ||
384 | // Next, create crates for each package, target pair | 379 | // Next, create crates for each package, target pair |
385 | for pkg in cargo.packages() { | 380 | for pkg in cargo.packages() { |
381 | has_private |= cargo[pkg].metadata.rustc_private; | ||
386 | let mut lib_tgt = None; | 382 | let mut lib_tgt = None; |
387 | for &tgt in cargo[pkg].targets.iter() { | 383 | for &tgt in cargo[pkg].targets.iter() { |
388 | if let Some(file_id) = load(&cargo[tgt].root) { | 384 | if let Some(file_id) = load(&cargo[tgt].root) { |
@@ -443,28 +439,66 @@ fn cargo_to_crate_graph( | |||
443 | } | 439 | } |
444 | } | 440 | } |
445 | 441 | ||
446 | let mut rustc_pkg_crates = FxHashMap::default(); | 442 | if has_private { |
443 | // If the user provided a path to rustc sources, we add all the rustc_private crates | ||
444 | // and create dependencies on them for the crates which opt-in to that | ||
445 | if let Some(rustc_workspace) = rustc { | ||
446 | handle_rustc_crates( | ||
447 | rustc_workspace, | ||
448 | load, | ||
449 | &mut crate_graph, | ||
450 | rustc_build_data_map, | ||
451 | &cfg_options, | ||
452 | proc_macro_loader, | ||
453 | &mut pkg_to_lib_crate, | ||
454 | &public_deps, | ||
455 | cargo, | ||
456 | &pkg_crates, | ||
457 | ); | ||
458 | } | ||
459 | } | ||
460 | crate_graph | ||
461 | } | ||
447 | 462 | ||
448 | // If the user provided a path to rustc sources, we add all the rustc_private crates | 463 | fn handle_rustc_crates( |
449 | // and create dependencies on them for the crates in the current workspace | 464 | rustc_workspace: &CargoWorkspace, |
450 | if let Some(rustc_workspace) = rustc { | 465 | load: &mut dyn FnMut(&AbsPath) -> Option<FileId>, |
451 | for pkg in rustc_workspace.packages() { | 466 | crate_graph: &mut CrateGraph, |
467 | rustc_build_data_map: Option<&FxHashMap<String, BuildData>>, | ||
468 | cfg_options: &CfgOptions, | ||
469 | proc_macro_loader: &dyn Fn(&Path) -> Vec<ProcMacro>, | ||
470 | pkg_to_lib_crate: &mut FxHashMap<la_arena::Idx<crate::PackageData>, CrateId>, | ||
471 | public_deps: &[(CrateName, CrateId)], | ||
472 | cargo: &CargoWorkspace, | ||
473 | pkg_crates: &FxHashMap<la_arena::Idx<crate::PackageData>, Vec<CrateId>>, | ||
474 | ) { | ||
475 | let mut rustc_pkg_crates = FxHashMap::default(); | ||
476 | // The root package of the rustc-dev component is rustc_driver, so we match that | ||
477 | let root_pkg = | ||
478 | rustc_workspace.packages().find(|package| rustc_workspace[*package].name == "rustc_driver"); | ||
479 | // The rustc workspace might be incomplete (such as if rustc-dev is not | ||
480 | // installed for the current toolchain) and `rustcSource` is set to discover. | ||
481 | if let Some(root_pkg) = root_pkg { | ||
482 | // Iterate through every crate in the dependency subtree of rustc_driver using BFS | ||
483 | let mut queue = VecDeque::new(); | ||
484 | queue.push_back(root_pkg); | ||
485 | while let Some(pkg) = queue.pop_front() { | ||
486 | // Don't duplicate packages if they are dependended on a diamond pattern | ||
487 | // N.B. if this line is ommitted, we try to analyse over 4_800_000 crates | ||
488 | // which is not ideal | ||
489 | if rustc_pkg_crates.contains_key(&pkg) { | ||
490 | continue; | ||
491 | } | ||
492 | for dep in &rustc_workspace[pkg].dependencies { | ||
493 | queue.push_back(dep.pkg); | ||
494 | } | ||
452 | for &tgt in rustc_workspace[pkg].targets.iter() { | 495 | for &tgt in rustc_workspace[pkg].targets.iter() { |
453 | if rustc_workspace[tgt].kind != TargetKind::Lib { | 496 | if rustc_workspace[tgt].kind != TargetKind::Lib { |
454 | continue; | 497 | continue; |
455 | } | 498 | } |
456 | // Exclude alloc / core / std | ||
457 | if rustc_workspace[tgt] | ||
458 | .root | ||
459 | .components() | ||
460 | .any(|c| c == Component::Normal("library".as_ref())) | ||
461 | { | ||
462 | continue; | ||
463 | } | ||
464 | |||
465 | if let Some(file_id) = load(&rustc_workspace[tgt].root) { | 499 | if let Some(file_id) = load(&rustc_workspace[tgt].root) { |
466 | let crate_id = add_target_crate_root( | 500 | let crate_id = add_target_crate_root( |
467 | &mut crate_graph, | 501 | crate_graph, |
468 | &rustc_workspace[pkg], | 502 | &rustc_workspace[pkg], |
469 | rustc_build_data_map.and_then(|it| it.get(&rustc_workspace[pkg].id)), | 503 | rustc_build_data_map.and_then(|it| it.get(&rustc_workspace[pkg].id)), |
470 | &cfg_options, | 504 | &cfg_options, |
@@ -472,44 +506,50 @@ fn cargo_to_crate_graph( | |||
472 | file_id, | 506 | file_id, |
473 | ); | 507 | ); |
474 | pkg_to_lib_crate.insert(pkg, crate_id); | 508 | pkg_to_lib_crate.insert(pkg, crate_id); |
475 | // Add dependencies on the core / std / alloc for rustc | 509 | // Add dependencies on core / std / alloc for this crate |
476 | for (name, krate) in public_deps.iter() { | 510 | for (name, krate) in public_deps.iter() { |
477 | add_dep(&mut crate_graph, crate_id, name.clone(), *krate); | 511 | add_dep(crate_graph, crate_id, name.clone(), *krate); |
478 | } | 512 | } |
479 | rustc_pkg_crates.entry(pkg).or_insert_with(Vec::new).push(crate_id); | 513 | rustc_pkg_crates.entry(pkg).or_insert_with(Vec::new).push(crate_id); |
480 | } | 514 | } |
481 | } | 515 | } |
482 | } | 516 | } |
483 | // Now add a dep edge from all targets of upstream to the lib | 517 | } |
484 | // target of downstream. | 518 | // Now add a dep edge from all targets of upstream to the lib |
485 | for pkg in rustc_workspace.packages() { | 519 | // target of downstream. |
486 | for dep in rustc_workspace[pkg].dependencies.iter() { | 520 | for pkg in rustc_pkg_crates.keys().copied() { |
487 | let name = CrateName::new(&dep.name).unwrap(); | 521 | for dep in rustc_workspace[pkg].dependencies.iter() { |
488 | if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) { | 522 | let name = CrateName::new(&dep.name).unwrap(); |
489 | for &from in rustc_pkg_crates.get(&pkg).into_iter().flatten() { | 523 | if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) { |
490 | add_dep(&mut crate_graph, from, name.clone(), to); | 524 | for &from in rustc_pkg_crates.get(&pkg).into_iter().flatten() { |
491 | } | 525 | add_dep(crate_graph, from, name.clone(), to); |
492 | } | 526 | } |
493 | } | 527 | } |
494 | } | 528 | } |
495 | 529 | } | |
496 | // Add dependencies for all the crates of the current workspace to rustc_private libraries | 530 | // Add a dependency on the rustc_private crates for all targets of each package |
497 | for dep in rustc_workspace.packages() { | 531 | // which opts in |
498 | let name = CrateName::normalize_dashes(&rustc_workspace[dep].name); | 532 | for dep in rustc_workspace.packages() { |
499 | 533 | let name = CrateName::normalize_dashes(&rustc_workspace[dep].name); | |
500 | if let Some(&to) = pkg_to_lib_crate.get(&dep) { | 534 | |
501 | for pkg in cargo.packages() { | 535 | if let Some(&to) = pkg_to_lib_crate.get(&dep) { |
502 | if !cargo[pkg].is_member { | 536 | for pkg in cargo.packages() { |
503 | continue; | 537 | let package = &cargo[pkg]; |
504 | } | 538 | if !package.metadata.rustc_private { |
505 | for &from in pkg_crates.get(&pkg).into_iter().flatten() { | 539 | continue; |
506 | add_dep(&mut crate_graph, from, name.clone(), to); | 540 | } |
541 | for &from in pkg_crates.get(&pkg).into_iter().flatten() { | ||
542 | // Avoid creating duplicate dependencies | ||
543 | // This avoids the situation where `from` depends on e.g. `arrayvec`, but | ||
544 | // `rust_analyzer` thinks that it should use the one from the `rustcSource` | ||
545 | // instead of the one from `crates.io` | ||
546 | if !crate_graph[from].dependencies.iter().any(|d| d.name == name) { | ||
547 | add_dep(crate_graph, from, name.clone(), to); | ||
507 | } | 548 | } |
508 | } | 549 | } |
509 | } | 550 | } |
510 | } | 551 | } |
511 | } | 552 | } |
512 | crate_graph | ||
513 | } | 553 | } |
514 | 554 | ||
515 | fn add_target_crate_root( | 555 | fn add_target_crate_root( |
diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml index 8789f0852..3130785cc 100644 --- a/crates/rust-analyzer/Cargo.toml +++ b/crates/rust-analyzer/Cargo.toml | |||
@@ -24,7 +24,7 @@ jod-thread = "0.1.0" | |||
24 | log = "0.4.8" | 24 | log = "0.4.8" |
25 | lsp-types = { version = "0.88.0", features = ["proposed"] } | 25 | lsp-types = { version = "0.88.0", features = ["proposed"] } |
26 | parking_lot = "0.11.0" | 26 | parking_lot = "0.11.0" |
27 | xflags = "0.1.2" | 27 | xflags = "0.2.1" |
28 | oorandom = "11.1.2" | 28 | oorandom = "11.1.2" |
29 | rustc-hash = "1.1.0" | 29 | rustc-hash = "1.1.0" |
30 | serde = { version = "1.0.106", features = ["derive"] } | 30 | serde = { version = "1.0.106", features = ["derive"] } |
diff --git a/crates/rust-analyzer/src/bin/flags.rs b/crates/rust-analyzer/src/bin/flags.rs index 244912d26..3a7caaf3f 100644 --- a/crates/rust-analyzer/src/bin/flags.rs +++ b/crates/rust-analyzer/src/bin/flags.rs | |||
@@ -6,7 +6,9 @@ use ide_ssr::{SsrPattern, SsrRule}; | |||
6 | use rust_analyzer::cli::{BenchWhat, Position, Verbosity}; | 6 | use rust_analyzer::cli::{BenchWhat, Position, Verbosity}; |
7 | use vfs::AbsPathBuf; | 7 | use vfs::AbsPathBuf; |
8 | 8 | ||
9 | xflags::args_parser! { | 9 | xflags::xflags! { |
10 | src "./src/bin/flags.rs" | ||
11 | |||
10 | /// LSP server for the Rust programming language. | 12 | /// LSP server for the Rust programming language. |
11 | cmd rust-analyzer { | 13 | cmd rust-analyzer { |
12 | /// Verbosity level, can be repeated multiple times. | 14 | /// Verbosity level, can be repeated multiple times. |
@@ -120,7 +122,7 @@ xflags::args_parser! { | |||
120 | 122 | ||
121 | // generated start | 123 | // generated start |
122 | // The following code is generated by `xflags` macro. | 124 | // The following code is generated by `xflags` macro. |
123 | // Run `env XFLAGS_DUMP= cargo build` to regenerate. | 125 | // Run `env UPDATE_XFLAGS=1 cargo build` to regenerate. |
124 | #[derive(Debug)] | 126 | #[derive(Debug)] |
125 | pub struct RustAnalyzer { | 127 | pub struct RustAnalyzer { |
126 | pub verbose: u32, | 128 | pub verbose: u32, |
@@ -158,7 +160,7 @@ pub struct Parse { | |||
158 | } | 160 | } |
159 | 161 | ||
160 | #[derive(Debug)] | 162 | #[derive(Debug)] |
161 | pub struct Symbols {} | 163 | pub struct Symbols; |
162 | 164 | ||
163 | #[derive(Debug)] | 165 | #[derive(Debug)] |
164 | pub struct Highlight { | 166 | pub struct Highlight { |
@@ -211,14 +213,13 @@ pub struct Search { | |||
211 | } | 213 | } |
212 | 214 | ||
213 | #[derive(Debug)] | 215 | #[derive(Debug)] |
214 | pub struct ProcMacro {} | 216 | pub struct ProcMacro; |
215 | 217 | ||
216 | impl RustAnalyzer { | 218 | impl RustAnalyzer { |
217 | pub const HELP: &'static str = Self::_HELP; | 219 | pub const HELP: &'static str = Self::HELP_; |
218 | 220 | ||
219 | pub fn from_env() -> xflags::Result<Self> { | 221 | pub fn from_env() -> xflags::Result<Self> { |
220 | let mut p = xflags::rt::Parser::new_from_env(); | 222 | Self::from_env_() |
221 | Self::_parse(&mut p) | ||
222 | } | 223 | } |
223 | } | 224 | } |
224 | // generated end | 225 | // generated end |
diff --git a/crates/rust-analyzer/src/cli/analysis_bench.rs b/crates/rust-analyzer/src/cli/analysis_bench.rs index 3bd7e678d..49994824f 100644 --- a/crates/rust-analyzer/src/cli/analysis_bench.rs +++ b/crates/rust-analyzer/src/cli/analysis_bench.rs | |||
@@ -108,7 +108,11 @@ impl BenchCmd { | |||
108 | add_call_parenthesis: true, | 108 | add_call_parenthesis: true, |
109 | add_call_argument_snippets: true, | 109 | add_call_argument_snippets: true, |
110 | snippet_cap: SnippetCap::new(true), | 110 | snippet_cap: SnippetCap::new(true), |
111 | insert_use: InsertUseConfig { merge: None, prefix_kind: PrefixKind::Plain }, | 111 | insert_use: InsertUseConfig { |
112 | merge: None, | ||
113 | prefix_kind: PrefixKind::Plain, | ||
114 | group: true, | ||
115 | }, | ||
112 | }; | 116 | }; |
113 | let res = do_work(&mut host, file_id, |analysis| { | 117 | let res = do_work(&mut host, file_id, |analysis| { |
114 | analysis.completions(&options, file_position) | 118 | analysis.completions(&options, file_position) |
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 367136702..8af7871ac 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs | |||
@@ -16,7 +16,6 @@ use ide_db::helpers::{ | |||
16 | insert_use::{InsertUseConfig, MergeBehavior}, | 16 | insert_use::{InsertUseConfig, MergeBehavior}, |
17 | SnippetCap, | 17 | SnippetCap, |
18 | }; | 18 | }; |
19 | use itertools::Itertools; | ||
20 | use lsp_types::{ClientCapabilities, MarkupKind}; | 19 | use lsp_types::{ClientCapabilities, MarkupKind}; |
21 | use project_model::{CargoConfig, ProjectJson, ProjectJsonData, ProjectManifest, RustcSource}; | 20 | use project_model::{CargoConfig, ProjectJson, ProjectJsonData, ProjectManifest, RustcSource}; |
22 | use rustc_hash::FxHashSet; | 21 | use rustc_hash::FxHashSet; |
@@ -35,7 +34,8 @@ config_data! { | |||
35 | assist_importMergeBehaviour: MergeBehaviorDef = "\"full\"", | 34 | assist_importMergeBehaviour: MergeBehaviorDef = "\"full\"", |
36 | /// The path structure for newly inserted paths to use. | 35 | /// The path structure for newly inserted paths to use. |
37 | assist_importPrefix: ImportPrefixDef = "\"plain\"", | 36 | assist_importPrefix: ImportPrefixDef = "\"plain\"", |
38 | 37 | /// Group inserted imports by the [following order](https://rust-analyzer.github.io/manual.html#auto-import). Groups are separated by newlines. | |
38 | assist_importGroup: bool = "true", | ||
39 | /// Show function name and docs in parameter hints. | 39 | /// Show function name and docs in parameter hints. |
40 | callInfo_full: bool = "true", | 40 | callInfo_full: bool = "true", |
41 | 41 | ||
@@ -48,7 +48,7 @@ config_data! { | |||
48 | cargo_features: Vec<String> = "[]", | 48 | cargo_features: Vec<String> = "[]", |
49 | /// Run build scripts (`build.rs`) for more precise code analysis. | 49 | /// Run build scripts (`build.rs`) for more precise code analysis. |
50 | cargo_runBuildScripts | | 50 | cargo_runBuildScripts | |
51 | cargo_loadOutDirsFromCheck: bool = "false", | 51 | cargo_loadOutDirsFromCheck: bool = "true", |
52 | /// Do not activate the `default` feature. | 52 | /// Do not activate the `default` feature. |
53 | cargo_noDefaultFeatures: bool = "false", | 53 | cargo_noDefaultFeatures: bool = "false", |
54 | /// Compilation target (target triple). | 54 | /// Compilation target (target triple). |
@@ -97,13 +97,15 @@ config_data! { | |||
97 | diagnostics_enableExperimental: bool = "true", | 97 | diagnostics_enableExperimental: bool = "true", |
98 | /// List of rust-analyzer diagnostics to disable. | 98 | /// List of rust-analyzer diagnostics to disable. |
99 | diagnostics_disabled: FxHashSet<String> = "[]", | 99 | diagnostics_disabled: FxHashSet<String> = "[]", |
100 | /// List of warnings that should be displayed with info severity.\n\nThe | 100 | /// List of warnings that should be displayed with info severity. |
101 | /// warnings will be indicated by a blue squiggly underline in code and | 101 | /// |
102 | /// a blue icon in the `Problems Panel`. | 102 | /// The warnings will be indicated by a blue squiggly underline in code |
103 | /// and a blue icon in the `Problems Panel`. | ||
103 | diagnostics_warningsAsHint: Vec<String> = "[]", | 104 | diagnostics_warningsAsHint: Vec<String> = "[]", |
104 | /// List of warnings that should be displayed with hint severity.\n\nThe | 105 | /// List of warnings that should be displayed with hint severity. |
105 | /// warnings will be indicated by faded text or three dots in code and | 106 | /// |
106 | /// will not show up in the `Problems Panel`. | 107 | /// The warnings will be indicated by faded text or three dots in code |
108 | /// and will not show up in the `Problems Panel`. | ||
107 | diagnostics_warningsAsInfo: Vec<String> = "[]", | 109 | diagnostics_warningsAsInfo: Vec<String> = "[]", |
108 | 110 | ||
109 | /// Controls file watching implementation. | 111 | /// Controls file watching implementation. |
@@ -157,7 +159,9 @@ config_data! { | |||
157 | lens_references: bool = "false", | 159 | lens_references: bool = "false", |
158 | 160 | ||
159 | /// Disable project auto-discovery in favor of explicitly specified set | 161 | /// Disable project auto-discovery in favor of explicitly specified set |
160 | /// of projects.\n\nElements must be paths pointing to `Cargo.toml`, | 162 | /// of projects. |
163 | /// | ||
164 | /// Elements must be paths pointing to `Cargo.toml`, | ||
161 | /// `rust-project.json`, or JSON objects in `rust-project.json` format. | 165 | /// `rust-project.json`, or JSON objects in `rust-project.json` format. |
162 | linkedProjects: Vec<ManifestOrProjectJson> = "[]", | 166 | linkedProjects: Vec<ManifestOrProjectJson> = "[]", |
163 | 167 | ||
@@ -176,12 +180,17 @@ config_data! { | |||
176 | /// Command to be executed instead of 'cargo' for runnables. | 180 | /// Command to be executed instead of 'cargo' for runnables. |
177 | runnables_overrideCargo: Option<String> = "null", | 181 | runnables_overrideCargo: Option<String> = "null", |
178 | /// Additional arguments to be passed to cargo for runnables such as | 182 | /// Additional arguments to be passed to cargo for runnables such as |
179 | /// tests or binaries.\nFor example, it may be `--release`. | 183 | /// tests or binaries. For example, it may be `--release`. |
180 | runnables_cargoExtraArgs: Vec<String> = "[]", | 184 | runnables_cargoExtraArgs: Vec<String> = "[]", |
181 | 185 | ||
182 | /// Path to the rust compiler sources, for usage in rustc_private projects, or "discover" | 186 | /// Path to the Cargo.toml of the rust compiler workspace, for usage in rustc_private |
183 | /// to try to automatically find it. | 187 | /// projects, or "discover" to try to automatically find it. |
184 | rustcSource : Option<String> = "null", | 188 | /// |
189 | /// Any project which uses rust-analyzer with the rustcPrivate | ||
190 | /// crates must set `[package.metadata.rust-analyzer] rustc_private=true` to use it. | ||
191 | /// | ||
192 | /// This option is not reloaded automatically; you must restart rust-analyzer for it to take effect. | ||
193 | rustcSource: Option<String> = "null", | ||
185 | 194 | ||
186 | /// Additional arguments to `rustfmt`. | 195 | /// Additional arguments to `rustfmt`. |
187 | rustfmt_extraArgs: Vec<String> = "[]", | 196 | rustfmt_extraArgs: Vec<String> = "[]", |
@@ -386,6 +395,9 @@ impl Config { | |||
386 | pub fn work_done_progress(&self) -> bool { | 395 | pub fn work_done_progress(&self) -> bool { |
387 | try_or!(self.caps.window.as_ref()?.work_done_progress?, false) | 396 | try_or!(self.caps.window.as_ref()?.work_done_progress?, false) |
388 | } | 397 | } |
398 | pub fn will_rename(&self) -> bool { | ||
399 | try_or!(self.caps.workspace.as_ref()?.file_operations.as_ref()?.will_rename?, false) | ||
400 | } | ||
389 | pub fn code_action_resolve(&self) -> bool { | 401 | pub fn code_action_resolve(&self) -> bool { |
390 | try_or!( | 402 | try_or!( |
391 | self.caps | 403 | self.caps |
@@ -574,6 +586,7 @@ impl Config { | |||
574 | ImportPrefixDef::ByCrate => PrefixKind::ByCrate, | 586 | ImportPrefixDef::ByCrate => PrefixKind::ByCrate, |
575 | ImportPrefixDef::BySelf => PrefixKind::BySelf, | 587 | ImportPrefixDef::BySelf => PrefixKind::BySelf, |
576 | }, | 588 | }, |
589 | group: self.data.assist_importGroup, | ||
577 | } | 590 | } |
578 | } | 591 | } |
579 | pub fn completion(&self) -> CompletionConfig { | 592 | pub fn completion(&self) -> CompletionConfig { |
@@ -758,7 +771,8 @@ fn schema(fields: &[(&'static str, &'static str, &[&str], &str)]) -> serde_json: | |||
758 | } | 771 | } |
759 | 772 | ||
760 | fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json::Value { | 773 | fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json::Value { |
761 | let doc = doc.iter().map(|it| it.trim()).join(" "); | 774 | let doc = doc_comment_to_string(doc); |
775 | let doc = doc.trim_end_matches('\n'); | ||
762 | assert!( | 776 | assert!( |
763 | doc.ends_with('.') && doc.starts_with(char::is_uppercase), | 777 | doc.ends_with('.') && doc.starts_with(char::is_uppercase), |
764 | "bad docs for {}: {:?}", | 778 | "bad docs for {}: {:?}", |
@@ -847,21 +861,26 @@ fn manual(fields: &[(&'static str, &'static str, &[&str], &str)]) -> String { | |||
847 | .iter() | 861 | .iter() |
848 | .map(|(field, _ty, doc, default)| { | 862 | .map(|(field, _ty, doc, default)| { |
849 | let name = format!("rust-analyzer.{}", field.replace("_", ".")); | 863 | let name = format!("rust-analyzer.{}", field.replace("_", ".")); |
850 | format!("[[{}]]{} (default: `{}`)::\n{}\n", name, name, default, doc.join(" ")) | 864 | let doc = doc_comment_to_string(*doc); |
865 | format!("[[{}]]{} (default: `{}`)::\n+\n--\n{}--\n", name, name, default, doc) | ||
851 | }) | 866 | }) |
852 | .collect::<String>() | 867 | .collect::<String>() |
853 | } | 868 | } |
854 | 869 | ||
870 | fn doc_comment_to_string(doc: &[&str]) -> String { | ||
871 | doc.iter().map(|it| it.strip_prefix(' ').unwrap_or(it)).map(|it| format!("{}\n", it)).collect() | ||
872 | } | ||
873 | |||
855 | #[cfg(test)] | 874 | #[cfg(test)] |
856 | mod tests { | 875 | mod tests { |
857 | use std::fs; | 876 | use std::fs; |
858 | 877 | ||
859 | use test_utils::project_dir; | 878 | use test_utils::{ensure_file_contents, project_root}; |
860 | 879 | ||
861 | use super::*; | 880 | use super::*; |
862 | 881 | ||
863 | #[test] | 882 | #[test] |
864 | fn schema_in_sync_with_package_json() { | 883 | fn generate_package_json_config() { |
865 | let s = Config::json_schema(); | 884 | let s = Config::json_schema(); |
866 | let schema = format!("{:#}", s); | 885 | let schema = format!("{:#}", s); |
867 | let mut schema = schema | 886 | let mut schema = schema |
@@ -874,7 +893,7 @@ mod tests { | |||
874 | .to_string(); | 893 | .to_string(); |
875 | schema.push_str(",\n"); | 894 | schema.push_str(",\n"); |
876 | 895 | ||
877 | let package_json_path = project_dir().join("editors/code/package.json"); | 896 | let package_json_path = project_root().join("editors/code/package.json"); |
878 | let mut package_json = fs::read_to_string(&package_json_path).unwrap(); | 897 | let mut package_json = fs::read_to_string(&package_json_path).unwrap(); |
879 | 898 | ||
880 | let start_marker = " \"$generated-start\": false,\n"; | 899 | let start_marker = " \"$generated-start\": false,\n"; |
@@ -882,26 +901,20 @@ mod tests { | |||
882 | 901 | ||
883 | let start = package_json.find(start_marker).unwrap() + start_marker.len(); | 902 | let start = package_json.find(start_marker).unwrap() + start_marker.len(); |
884 | let end = package_json.find(end_marker).unwrap(); | 903 | let end = package_json.find(end_marker).unwrap(); |
904 | |||
885 | let p = remove_ws(&package_json[start..end]); | 905 | let p = remove_ws(&package_json[start..end]); |
886 | let s = remove_ws(&schema); | 906 | let s = remove_ws(&schema); |
887 | |||
888 | if !p.contains(&s) { | 907 | if !p.contains(&s) { |
889 | package_json.replace_range(start..end, &schema); | 908 | package_json.replace_range(start..end, &schema); |
890 | fs::write(&package_json_path, &mut package_json).unwrap(); | 909 | ensure_file_contents(&package_json_path, &package_json) |
891 | panic!("new config, updating package.json") | ||
892 | } | 910 | } |
893 | } | 911 | } |
894 | 912 | ||
895 | #[test] | 913 | #[test] |
896 | fn schema_in_sync_with_docs() { | 914 | fn generate_config_documentation() { |
897 | let docs_path = project_dir().join("docs/user/generated_config.adoc"); | 915 | let docs_path = project_root().join("docs/user/generated_config.adoc"); |
898 | let current = fs::read_to_string(&docs_path).unwrap(); | ||
899 | let expected = ConfigData::manual(); | 916 | let expected = ConfigData::manual(); |
900 | 917 | ensure_file_contents(&docs_path, &expected); | |
901 | if remove_ws(¤t) != remove_ws(&expected) { | ||
902 | fs::write(&docs_path, expected).unwrap(); | ||
903 | panic!("updated config manual"); | ||
904 | } | ||
905 | } | 918 | } |
906 | 919 | ||
907 | fn remove_ws(text: &str) -> String { | 920 | fn remove_ws(text: &str) -> String { |
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs index 4f6f250d6..6cc433cb8 100644 --- a/crates/rust-analyzer/src/handlers.rs +++ b/crates/rust-analyzer/src/handlers.rs | |||
@@ -697,7 +697,6 @@ pub(crate) fn handle_completion_resolve( | |||
697 | FilePosition { file_id, offset }, | 697 | FilePosition { file_id, offset }, |
698 | &resolve_data.full_import_path, | 698 | &resolve_data.full_import_path, |
699 | resolve_data.imported_name, | 699 | resolve_data.imported_name, |
700 | resolve_data.import_for_trait_assoc_item, | ||
701 | )? | 700 | )? |
702 | .into_iter() | 701 | .into_iter() |
703 | .flat_map(|edit| edit.into_iter().map(|indel| to_proto::text_edit(&line_index, indel))) | 702 | .flat_map(|edit| edit.into_iter().map(|indel| to_proto::text_edit(&line_index, indel))) |
@@ -800,8 +799,18 @@ pub(crate) fn handle_rename( | |||
800 | let _p = profile::span("handle_rename"); | 799 | let _p = profile::span("handle_rename"); |
801 | let position = from_proto::file_position(&snap, params.text_document_position)?; | 800 | let position = from_proto::file_position(&snap, params.text_document_position)?; |
802 | 801 | ||
803 | let change = | 802 | let mut change = |
804 | snap.analysis.rename(position, &*params.new_name)?.map_err(to_proto::rename_error)?; | 803 | snap.analysis.rename(position, &*params.new_name)?.map_err(to_proto::rename_error)?; |
804 | |||
805 | // this is kind of a hack to prevent double edits from happening when moving files | ||
806 | // When a module gets renamed by renaming the mod declaration this causes the file to move | ||
807 | // which in turn will trigger a WillRenameFiles request to the server for which we reply with a | ||
808 | // a second identical set of renames, the client will then apply both edits causing incorrect edits | ||
809 | // with this we only emit source_file_edits in the WillRenameFiles response which will do the rename instead | ||
810 | // See https://github.com/microsoft/vscode-languageserver-node/issues/752 for more info | ||
811 | if !change.file_system_edits.is_empty() && snap.config.will_rename() { | ||
812 | change.source_file_edits.clear(); | ||
813 | } | ||
805 | let workspace_edit = to_proto::workspace_edit(&snap, change)?; | 814 | let workspace_edit = to_proto::workspace_edit(&snap, change)?; |
806 | Ok(Some(workspace_edit)) | 815 | Ok(Some(workspace_edit)) |
807 | } | 816 | } |
@@ -1135,20 +1144,13 @@ pub(crate) fn handle_document_highlight( | |||
1135 | None | 1144 | None |
1136 | }; | 1145 | }; |
1137 | 1146 | ||
1138 | let res = refs | 1147 | let file_refs = refs.references.get(&position.file_id).map_or(&[][..], Vec::as_slice); |
1139 | .references | 1148 | let mut res = Vec::with_capacity(file_refs.len() + 1); |
1140 | .get(&position.file_id) | 1149 | res.extend(decl); |
1141 | .map(|file_refs| { | 1150 | res.extend(file_refs.iter().map(|&(range, access)| DocumentHighlight { |
1142 | file_refs | 1151 | range: to_proto::range(&line_index, range), |
1143 | .into_iter() | 1152 | kind: access.map(to_proto::document_highlight_kind), |
1144 | .map(|&(range, access)| DocumentHighlight { | 1153 | })); |
1145 | range: to_proto::range(&line_index, range), | ||
1146 | kind: access.map(to_proto::document_highlight_kind), | ||
1147 | }) | ||
1148 | .chain(decl) | ||
1149 | .collect() | ||
1150 | }) | ||
1151 | .unwrap_or_default(); | ||
1152 | Ok(Some(res)) | 1154 | Ok(Some(res)) |
1153 | } | 1155 | } |
1154 | 1156 | ||
@@ -1525,7 +1527,6 @@ struct CompletionResolveData { | |||
1525 | position: lsp_types::TextDocumentPositionParams, | 1527 | position: lsp_types::TextDocumentPositionParams, |
1526 | full_import_path: String, | 1528 | full_import_path: String, |
1527 | imported_name: String, | 1529 | imported_name: String, |
1528 | import_for_trait_assoc_item: bool, | ||
1529 | } | 1530 | } |
1530 | 1531 | ||
1531 | fn fill_resolve_data( | 1532 | fn fill_resolve_data( |
@@ -1534,15 +1535,13 @@ fn fill_resolve_data( | |||
1534 | position: &TextDocumentPositionParams, | 1535 | position: &TextDocumentPositionParams, |
1535 | ) -> Option<()> { | 1536 | ) -> Option<()> { |
1536 | let import_edit = item.import_to_add()?; | 1537 | let import_edit = item.import_to_add()?; |
1537 | let full_import_path = import_edit.import_path.to_string(); | 1538 | let import_path = &import_edit.import.import_path; |
1538 | let imported_name = import_edit.import_path.segments().last()?.to_string(); | ||
1539 | 1539 | ||
1540 | *resolve_data = Some( | 1540 | *resolve_data = Some( |
1541 | to_value(CompletionResolveData { | 1541 | to_value(CompletionResolveData { |
1542 | position: position.to_owned(), | 1542 | position: position.to_owned(), |
1543 | full_import_path, | 1543 | full_import_path: import_path.to_string(), |
1544 | imported_name, | 1544 | imported_name: import_path.segments().last()?.to_string(), |
1545 | import_for_trait_assoc_item: import_edit.import_for_trait_assoc_item, | ||
1546 | }) | 1545 | }) |
1547 | .unwrap(), | 1546 | .unwrap(), |
1548 | ); | 1547 | ); |
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs index c1ca88df6..1a8cdadad 100644 --- a/crates/rust-analyzer/src/to_proto.rs +++ b/crates/rust-analyzer/src/to_proto.rs | |||
@@ -6,9 +6,10 @@ use std::{ | |||
6 | 6 | ||
7 | use ide::{ | 7 | use ide::{ |
8 | Annotation, AnnotationKind, Assist, AssistKind, CallInfo, CompletionItem, CompletionItemKind, | 8 | Annotation, AnnotationKind, Assist, AssistKind, CallInfo, CompletionItem, CompletionItemKind, |
9 | Documentation, FileId, FileRange, FileSystemEdit, Fold, FoldKind, Highlight, HlMod, HlPunct, | 9 | CompletionRelevance, Documentation, FileId, FileRange, FileSystemEdit, Fold, FoldKind, |
10 | HlRange, HlTag, Indel, InlayHint, InlayKind, InsertTextFormat, Markup, NavigationTarget, | 10 | Highlight, HlMod, HlPunct, HlRange, HlTag, Indel, InlayHint, InlayKind, InsertTextFormat, |
11 | ReferenceAccess, RenameError, Runnable, Severity, SourceChange, TextEdit, TextRange, TextSize, | 11 | Markup, NavigationTarget, ReferenceAccess, RenameError, Runnable, Severity, SourceChange, |
12 | TextEdit, TextRange, TextSize, | ||
12 | }; | 13 | }; |
13 | use ide_db::SymbolKind; | 14 | use ide_db::SymbolKind; |
14 | use itertools::Itertools; | 15 | use itertools::Itertools; |
@@ -173,20 +174,14 @@ pub(crate) fn snippet_text_edit_vec( | |||
173 | 174 | ||
174 | pub(crate) fn completion_item( | 175 | pub(crate) fn completion_item( |
175 | line_index: &LineIndex, | 176 | line_index: &LineIndex, |
176 | completion_item: CompletionItem, | 177 | item: CompletionItem, |
177 | ) -> Vec<lsp_types::CompletionItem> { | 178 | ) -> Vec<lsp_types::CompletionItem> { |
178 | fn set_score(res: &mut lsp_types::CompletionItem, label: &str) { | ||
179 | res.preselect = Some(true); | ||
180 | // HACK: sort preselect items first | ||
181 | res.sort_text = Some(format!(" {}", label)); | ||
182 | } | ||
183 | |||
184 | let mut additional_text_edits = Vec::new(); | 179 | let mut additional_text_edits = Vec::new(); |
185 | let mut text_edit = None; | 180 | let mut text_edit = None; |
186 | // LSP does not allow arbitrary edits in completion, so we have to do a | 181 | // LSP does not allow arbitrary edits in completion, so we have to do a |
187 | // non-trivial mapping here. | 182 | // non-trivial mapping here. |
188 | let source_range = completion_item.source_range(); | 183 | let source_range = item.source_range(); |
189 | for indel in completion_item.text_edit().iter() { | 184 | for indel in item.text_edit().iter() { |
190 | if indel.delete.contains_range(source_range) { | 185 | if indel.delete.contains_range(source_range) { |
191 | text_edit = Some(if indel.delete == source_range { | 186 | text_edit = Some(if indel.delete == source_range { |
192 | self::text_edit(line_index, indel.clone()) | 187 | self::text_edit(line_index, indel.clone()) |
@@ -207,46 +202,61 @@ pub(crate) fn completion_item( | |||
207 | } | 202 | } |
208 | let text_edit = text_edit.unwrap(); | 203 | let text_edit = text_edit.unwrap(); |
209 | 204 | ||
210 | let mut res = lsp_types::CompletionItem { | 205 | let mut lsp_item = lsp_types::CompletionItem { |
211 | label: completion_item.label().to_string(), | 206 | label: item.label().to_string(), |
212 | detail: completion_item.detail().map(|it| it.to_string()), | 207 | detail: item.detail().map(|it| it.to_string()), |
213 | filter_text: Some(completion_item.lookup().to_string()), | 208 | filter_text: Some(item.lookup().to_string()), |
214 | kind: completion_item.kind().map(completion_item_kind), | 209 | kind: item.kind().map(completion_item_kind), |
215 | text_edit: Some(text_edit.into()), | 210 | text_edit: Some(text_edit.into()), |
216 | additional_text_edits: Some(additional_text_edits), | 211 | additional_text_edits: Some(additional_text_edits), |
217 | documentation: completion_item.documentation().map(documentation), | 212 | documentation: item.documentation().map(documentation), |
218 | deprecated: Some(completion_item.deprecated()), | 213 | deprecated: Some(item.deprecated()), |
219 | ..Default::default() | 214 | ..Default::default() |
220 | }; | 215 | }; |
221 | 216 | ||
222 | if completion_item.score().is_some() { | 217 | fn set_score(res: &mut lsp_types::CompletionItem, relevance: CompletionRelevance) { |
223 | set_score(&mut res, completion_item.label()); | 218 | if relevance.is_relevant() { |
219 | res.preselect = Some(true); | ||
220 | } | ||
221 | // The relevance needs to be inverted to come up with a sort score | ||
222 | // because the client will sort ascending. | ||
223 | let sort_score = relevance.score() ^ 0xFF_FF_FF_FF; | ||
224 | // Zero pad the string to ensure values can be properly sorted | ||
225 | // by the client. Hex format is used because it is easier to | ||
226 | // visually compare very large values, which the sort text | ||
227 | // tends to be since it is the opposite of the score. | ||
228 | res.sort_text = Some(format!("{:08x}", sort_score)); | ||
224 | } | 229 | } |
225 | 230 | ||
226 | if completion_item.deprecated() { | 231 | set_score(&mut lsp_item, item.relevance()); |
227 | res.tags = Some(vec![lsp_types::CompletionItemTag::Deprecated]) | 232 | |
233 | if item.deprecated() { | ||
234 | lsp_item.tags = Some(vec![lsp_types::CompletionItemTag::Deprecated]) | ||
228 | } | 235 | } |
229 | 236 | ||
230 | if completion_item.trigger_call_info() { | 237 | if item.trigger_call_info() { |
231 | res.command = Some(command::trigger_parameter_hints()); | 238 | lsp_item.command = Some(command::trigger_parameter_hints()); |
232 | } | 239 | } |
233 | 240 | ||
234 | let mut all_results = match completion_item.ref_match() { | 241 | let mut res = match item.ref_match() { |
235 | Some(ref_match) => { | 242 | Some((mutability, relevance)) => { |
236 | let mut refed = res.clone(); | 243 | let mut lsp_item_with_ref = lsp_item.clone(); |
237 | let (mutability, _score) = ref_match; | 244 | set_score(&mut lsp_item_with_ref, relevance); |
238 | let label = format!("&{}{}", mutability.as_keyword_for_ref(), refed.label); | 245 | lsp_item_with_ref.label = |
239 | set_score(&mut refed, &label); | 246 | format!("&{}{}", mutability.as_keyword_for_ref(), lsp_item_with_ref.label); |
240 | refed.label = label; | 247 | if let Some(lsp_types::CompletionTextEdit::Edit(it)) = &mut lsp_item_with_ref.text_edit |
241 | vec![res, refed] | 248 | { |
249 | it.new_text = format!("&{}{}", mutability.as_keyword_for_ref(), it.new_text); | ||
250 | } | ||
251 | vec![lsp_item_with_ref, lsp_item] | ||
242 | } | 252 | } |
243 | None => vec![res], | 253 | None => vec![lsp_item], |
244 | }; | 254 | }; |
245 | 255 | ||
246 | for mut r in all_results.iter_mut() { | 256 | for lsp_item in res.iter_mut() { |
247 | r.insert_text_format = Some(insert_text_format(completion_item.insert_text_format())); | 257 | lsp_item.insert_text_format = Some(insert_text_format(item.insert_text_format())); |
248 | } | 258 | } |
249 | all_results | 259 | res |
250 | } | 260 | } |
251 | 261 | ||
252 | pub(crate) fn signature_help( | 262 | pub(crate) fn signature_help( |
@@ -1087,7 +1097,11 @@ mod tests { | |||
1087 | add_call_parenthesis: true, | 1097 | add_call_parenthesis: true, |
1088 | add_call_argument_snippets: true, | 1098 | add_call_argument_snippets: true, |
1089 | snippet_cap: SnippetCap::new(true), | 1099 | snippet_cap: SnippetCap::new(true), |
1090 | insert_use: InsertUseConfig { merge: None, prefix_kind: PrefixKind::Plain }, | 1100 | insert_use: InsertUseConfig { |
1101 | merge: None, | ||
1102 | prefix_kind: PrefixKind::Plain, | ||
1103 | group: true, | ||
1104 | }, | ||
1091 | }, | 1105 | }, |
1092 | ide_db::base_db::FilePosition { file_id, offset }, | 1106 | ide_db::base_db::FilePosition { file_id, offset }, |
1093 | ) | 1107 | ) |
@@ -1101,13 +1115,15 @@ mod tests { | |||
1101 | expect_test::expect![[r#" | 1115 | expect_test::expect![[r#" |
1102 | [ | 1116 | [ |
1103 | ( | 1117 | ( |
1104 | "arg", | 1118 | "&arg", |
1105 | None, | 1119 | Some( |
1120 | "fffffffd", | ||
1121 | ), | ||
1106 | ), | 1122 | ), |
1107 | ( | 1123 | ( |
1108 | "&arg", | 1124 | "arg", |
1109 | Some( | 1125 | Some( |
1110 | " &arg", | 1126 | "fffffffe", |
1111 | ), | 1127 | ), |
1112 | ), | 1128 | ), |
1113 | ] | 1129 | ] |
diff --git a/crates/rust-analyzer/tests/rust-analyzer/support.rs b/crates/rust-analyzer/tests/rust-analyzer/support.rs index 6b774073d..cd0c91481 100644 --- a/crates/rust-analyzer/tests/rust-analyzer/support.rs +++ b/crates/rust-analyzer/tests/rust-analyzer/support.rs | |||
@@ -13,7 +13,7 @@ use project_model::ProjectManifest; | |||
13 | use rust_analyzer::{config::Config, lsp_ext, main_loop}; | 13 | use rust_analyzer::{config::Config, lsp_ext, main_loop}; |
14 | use serde::Serialize; | 14 | use serde::Serialize; |
15 | use serde_json::{json, to_string_pretty, Value}; | 15 | use serde_json::{json, to_string_pretty, Value}; |
16 | use test_utils::{find_mismatch, Fixture}; | 16 | use test_utils::Fixture; |
17 | use vfs::AbsPathBuf; | 17 | use vfs::AbsPathBuf; |
18 | 18 | ||
19 | use crate::testdir::TestDir; | 19 | use crate::testdir::TestDir; |
@@ -279,3 +279,98 @@ fn recv_timeout(receiver: &Receiver<Message>) -> Result<Option<Message>, Timeout | |||
279 | recv(after(timeout)) -> _ => Err(Timeout), | 279 | recv(after(timeout)) -> _ => Err(Timeout), |
280 | } | 280 | } |
281 | } | 281 | } |
282 | |||
283 | // Comparison functionality borrowed from cargo: | ||
284 | |||
285 | /// Compares JSON object for approximate equality. | ||
286 | /// You can use `[..]` wildcard in strings (useful for OS dependent things such | ||
287 | /// as paths). You can use a `"{...}"` string literal as a wildcard for | ||
288 | /// arbitrary nested JSON. Arrays are sorted before comparison. | ||
289 | fn find_mismatch<'a>(expected: &'a Value, actual: &'a Value) -> Option<(&'a Value, &'a Value)> { | ||
290 | match (expected, actual) { | ||
291 | (Value::Number(l), Value::Number(r)) if l == r => None, | ||
292 | (Value::Bool(l), Value::Bool(r)) if l == r => None, | ||
293 | (Value::String(l), Value::String(r)) if lines_match(l, r) => None, | ||
294 | (Value::Array(l), Value::Array(r)) => { | ||
295 | if l.len() != r.len() { | ||
296 | return Some((expected, actual)); | ||
297 | } | ||
298 | |||
299 | let mut l = l.iter().collect::<Vec<_>>(); | ||
300 | let mut r = r.iter().collect::<Vec<_>>(); | ||
301 | |||
302 | l.retain(|l| match r.iter().position(|r| find_mismatch(l, r).is_none()) { | ||
303 | Some(i) => { | ||
304 | r.remove(i); | ||
305 | false | ||
306 | } | ||
307 | None => true, | ||
308 | }); | ||
309 | |||
310 | if !l.is_empty() { | ||
311 | assert!(!r.is_empty()); | ||
312 | Some((&l[0], &r[0])) | ||
313 | } else { | ||
314 | assert_eq!(r.len(), 0); | ||
315 | None | ||
316 | } | ||
317 | } | ||
318 | (Value::Object(l), Value::Object(r)) => { | ||
319 | fn sorted_values(obj: &serde_json::Map<String, Value>) -> Vec<&Value> { | ||
320 | let mut entries = obj.iter().collect::<Vec<_>>(); | ||
321 | entries.sort_by_key(|it| it.0); | ||
322 | entries.into_iter().map(|(_k, v)| v).collect::<Vec<_>>() | ||
323 | } | ||
324 | |||
325 | let same_keys = l.len() == r.len() && l.keys().all(|k| r.contains_key(k)); | ||
326 | if !same_keys { | ||
327 | return Some((expected, actual)); | ||
328 | } | ||
329 | |||
330 | let l = sorted_values(l); | ||
331 | let r = sorted_values(r); | ||
332 | |||
333 | l.into_iter().zip(r).filter_map(|(l, r)| find_mismatch(l, r)).next() | ||
334 | } | ||
335 | (Value::Null, Value::Null) => None, | ||
336 | // magic string literal "{...}" acts as wildcard for any sub-JSON | ||
337 | (Value::String(l), _) if l == "{...}" => None, | ||
338 | _ => Some((expected, actual)), | ||
339 | } | ||
340 | } | ||
341 | |||
342 | /// Compare a line with an expected pattern. | ||
343 | /// - Use `[..]` as a wildcard to match 0 or more characters on the same line | ||
344 | /// (similar to `.*` in a regex). | ||
345 | fn lines_match(expected: &str, actual: &str) -> bool { | ||
346 | // Let's not deal with / vs \ (windows...) | ||
347 | // First replace backslash-escaped backslashes with forward slashes | ||
348 | // which can occur in, for example, JSON output | ||
349 | let expected = expected.replace(r"\\", "/").replace(r"\", "/"); | ||
350 | let mut actual: &str = &actual.replace(r"\\", "/").replace(r"\", "/"); | ||
351 | for (i, part) in expected.split("[..]").enumerate() { | ||
352 | match actual.find(part) { | ||
353 | Some(j) => { | ||
354 | if i == 0 && j != 0 { | ||
355 | return false; | ||
356 | } | ||
357 | actual = &actual[j + part.len()..]; | ||
358 | } | ||
359 | None => return false, | ||
360 | } | ||
361 | } | ||
362 | actual.is_empty() || expected.ends_with("[..]") | ||
363 | } | ||
364 | |||
365 | #[test] | ||
366 | fn lines_match_works() { | ||
367 | assert!(lines_match("a b", "a b")); | ||
368 | assert!(lines_match("a[..]b", "a b")); | ||
369 | assert!(lines_match("a[..]", "a b")); | ||
370 | assert!(lines_match("[..]", "a b")); | ||
371 | assert!(lines_match("[..]b", "a b")); | ||
372 | |||
373 | assert!(!lines_match("[..]b", "c")); | ||
374 | assert!(!lines_match("b", "c")); | ||
375 | assert!(!lines_match("b", "cb")); | ||
376 | } | ||
diff --git a/crates/syntax/Cargo.toml b/crates/syntax/Cargo.toml index 9ee3a8586..05fca5dc4 100644 --- a/crates/syntax/Cargo.toml +++ b/crates/syntax/Cargo.toml | |||
@@ -11,9 +11,10 @@ edition = "2018" | |||
11 | doctest = false | 11 | doctest = false |
12 | 12 | ||
13 | [dependencies] | 13 | [dependencies] |
14 | cov-mark = "1.1" | ||
14 | itertools = "0.10.0" | 15 | itertools = "0.10.0" |
15 | rowan = "0.12.2" | 16 | rowan = "0.12.2" |
16 | rustc_lexer = { version = "709.0.0", package = "rustc-ap-rustc_lexer" } | 17 | rustc_lexer = { version = "710.0.0", package = "rustc-ap-rustc_lexer" } |
17 | rustc-hash = "1.1.0" | 18 | rustc-hash = "1.1.0" |
18 | arrayvec = "0.5.1" | 19 | arrayvec = "0.5.1" |
19 | once_cell = "1.3.1" | 20 | once_cell = "1.3.1" |
@@ -24,10 +25,10 @@ serde = { version = "1.0.106", features = ["derive"] } | |||
24 | stdx = { path = "../stdx", version = "0.0.0" } | 25 | stdx = { path = "../stdx", version = "0.0.0" } |
25 | text_edit = { path = "../text_edit", version = "0.0.0" } | 26 | text_edit = { path = "../text_edit", version = "0.0.0" } |
26 | parser = { path = "../parser", version = "0.0.0" } | 27 | parser = { path = "../parser", version = "0.0.0" } |
27 | test_utils = { path = "../test_utils", version = "0.0.0" } | ||
28 | profile = { path = "../profile", version = "0.0.0" } | 28 | profile = { path = "../profile", version = "0.0.0" } |
29 | 29 | ||
30 | [dev-dependencies] | 30 | [dev-dependencies] |
31 | test_utils = { path = "../test_utils" } | ||
31 | walkdir = "2.3.1" | 32 | walkdir = "2.3.1" |
32 | rayon = "1" | 33 | rayon = "1" |
33 | expect-test = "1.1" | 34 | expect-test = "1.1" |
diff --git a/crates/syntax/src/algo.rs b/crates/syntax/src/algo.rs index 2ff92f9f6..b13252eec 100644 --- a/crates/syntax/src/algo.rs +++ b/crates/syntax/src/algo.rs | |||
@@ -10,7 +10,6 @@ use std::{ | |||
10 | use indexmap::IndexMap; | 10 | use indexmap::IndexMap; |
11 | use itertools::Itertools; | 11 | use itertools::Itertools; |
12 | use rustc_hash::FxHashMap; | 12 | use rustc_hash::FxHashMap; |
13 | use test_utils::mark; | ||
14 | use text_edit::TextEditBuilder; | 13 | use text_edit::TextEditBuilder; |
15 | 14 | ||
16 | use crate::{ | 15 | use crate::{ |
@@ -184,7 +183,7 @@ pub fn diff(from: &SyntaxNode, to: &SyntaxNode) -> TreeDiff { | |||
184 | let (lhs, rhs) = match lhs.as_node().zip(rhs.as_node()) { | 183 | let (lhs, rhs) = match lhs.as_node().zip(rhs.as_node()) { |
185 | Some((lhs, rhs)) => (lhs, rhs), | 184 | Some((lhs, rhs)) => (lhs, rhs), |
186 | _ => { | 185 | _ => { |
187 | mark::hit!(diff_node_token_replace); | 186 | cov_mark::hit!(diff_node_token_replace); |
188 | diff.replacements.insert(lhs, rhs); | 187 | diff.replacements.insert(lhs, rhs); |
189 | return; | 188 | return; |
190 | } | 189 | } |
@@ -202,19 +201,19 @@ pub fn diff(from: &SyntaxNode, to: &SyntaxNode) -> TreeDiff { | |||
202 | (None, Some(element)) => { | 201 | (None, Some(element)) => { |
203 | let insert_pos = match last_lhs.clone() { | 202 | let insert_pos = match last_lhs.clone() { |
204 | Some(prev) => { | 203 | Some(prev) => { |
205 | mark::hit!(diff_insert); | 204 | cov_mark::hit!(diff_insert); |
206 | TreeDiffInsertPos::After(prev) | 205 | TreeDiffInsertPos::After(prev) |
207 | } | 206 | } |
208 | // first iteration, insert into out parent as the first child | 207 | // first iteration, insert into out parent as the first child |
209 | None => { | 208 | None => { |
210 | mark::hit!(diff_insert_as_first_child); | 209 | cov_mark::hit!(diff_insert_as_first_child); |
211 | TreeDiffInsertPos::AsFirstChild(lhs.clone().into()) | 210 | TreeDiffInsertPos::AsFirstChild(lhs.clone().into()) |
212 | } | 211 | } |
213 | }; | 212 | }; |
214 | diff.insertions.entry(insert_pos).or_insert_with(Vec::new).push(element); | 213 | diff.insertions.entry(insert_pos).or_insert_with(Vec::new).push(element); |
215 | } | 214 | } |
216 | (Some(element), None) => { | 215 | (Some(element), None) => { |
217 | mark::hit!(diff_delete); | 216 | cov_mark::hit!(diff_delete); |
218 | diff.deletions.push(element); | 217 | diff.deletions.push(element); |
219 | } | 218 | } |
220 | (Some(ref lhs_ele), Some(ref rhs_ele)) if syntax_element_eq(lhs_ele, rhs_ele) => {} | 219 | (Some(ref lhs_ele), Some(ref rhs_ele)) if syntax_element_eq(lhs_ele, rhs_ele) => {} |
@@ -228,7 +227,7 @@ pub fn diff(from: &SyntaxNode, to: &SyntaxNode) -> TreeDiff { | |||
228 | let mut insert = false; | 227 | let mut insert = false; |
229 | while let Some(rhs_child) = rhs_children_clone.next() { | 228 | while let Some(rhs_child) = rhs_children_clone.next() { |
230 | if syntax_element_eq(&lhs_ele, &rhs_child) { | 229 | if syntax_element_eq(&lhs_ele, &rhs_child) { |
231 | mark::hit!(diff_insertions); | 230 | cov_mark::hit!(diff_insertions); |
232 | insert = true; | 231 | insert = true; |
233 | break; | 232 | break; |
234 | } else { | 233 | } else { |
@@ -240,7 +239,7 @@ pub fn diff(from: &SyntaxNode, to: &SyntaxNode) -> TreeDiff { | |||
240 | let insert_pos = if let Some(prev) = last_lhs.clone().filter(|_| insert) { | 239 | let insert_pos = if let Some(prev) = last_lhs.clone().filter(|_| insert) { |
241 | TreeDiffInsertPos::After(prev) | 240 | TreeDiffInsertPos::After(prev) |
242 | } else { | 241 | } else { |
243 | mark::hit!(insert_first_child); | 242 | cov_mark::hit!(insert_first_child); |
244 | TreeDiffInsertPos::AsFirstChild(lhs.clone().into()) | 243 | TreeDiffInsertPos::AsFirstChild(lhs.clone().into()) |
245 | }; | 244 | }; |
246 | 245 | ||
@@ -635,14 +634,13 @@ mod tests { | |||
635 | use expect_test::{expect, Expect}; | 634 | use expect_test::{expect, Expect}; |
636 | use itertools::Itertools; | 635 | use itertools::Itertools; |
637 | use parser::SyntaxKind; | 636 | use parser::SyntaxKind; |
638 | use test_utils::mark; | ||
639 | use text_edit::TextEdit; | 637 | use text_edit::TextEdit; |
640 | 638 | ||
641 | use crate::{AstNode, SyntaxElement}; | 639 | use crate::{AstNode, SyntaxElement}; |
642 | 640 | ||
643 | #[test] | 641 | #[test] |
644 | fn replace_node_token() { | 642 | fn replace_node_token() { |
645 | mark::check!(diff_node_token_replace); | 643 | cov_mark::check!(diff_node_token_replace); |
646 | check_diff( | 644 | check_diff( |
647 | r#"use node;"#, | 645 | r#"use node;"#, |
648 | r#"ident"#, | 646 | r#"ident"#, |
@@ -666,7 +664,7 @@ mod tests { | |||
666 | 664 | ||
667 | #[test] | 665 | #[test] |
668 | fn replace_parent() { | 666 | fn replace_parent() { |
669 | mark::check!(diff_insert_as_first_child); | 667 | cov_mark::check!(diff_insert_as_first_child); |
670 | check_diff( | 668 | check_diff( |
671 | r#""#, | 669 | r#""#, |
672 | r#"use foo::bar;"#, | 670 | r#"use foo::bar;"#, |
@@ -689,7 +687,7 @@ mod tests { | |||
689 | 687 | ||
690 | #[test] | 688 | #[test] |
691 | fn insert_last() { | 689 | fn insert_last() { |
692 | mark::check!(diff_insert); | 690 | cov_mark::check!(diff_insert); |
693 | check_diff( | 691 | check_diff( |
694 | r#" | 692 | r#" |
695 | use foo; | 693 | use foo; |
@@ -774,7 +772,7 @@ use baz;"#, | |||
774 | 772 | ||
775 | #[test] | 773 | #[test] |
776 | fn first_child_insertion() { | 774 | fn first_child_insertion() { |
777 | mark::check!(insert_first_child); | 775 | cov_mark::check!(insert_first_child); |
778 | check_diff( | 776 | check_diff( |
779 | r#"fn main() { | 777 | r#"fn main() { |
780 | stdi | 778 | stdi |
@@ -804,7 +802,7 @@ use baz;"#, | |||
804 | 802 | ||
805 | #[test] | 803 | #[test] |
806 | fn delete_last() { | 804 | fn delete_last() { |
807 | mark::check!(diff_delete); | 805 | cov_mark::check!(diff_delete); |
808 | check_diff( | 806 | check_diff( |
809 | r#"use foo; | 807 | r#"use foo; |
810 | use bar;"#, | 808 | use bar;"#, |
@@ -828,7 +826,7 @@ use baz;"#, | |||
828 | 826 | ||
829 | #[test] | 827 | #[test] |
830 | fn delete_middle() { | 828 | fn delete_middle() { |
831 | mark::check!(diff_insertions); | 829 | cov_mark::check!(diff_insertions); |
832 | check_diff( | 830 | check_diff( |
833 | r#" | 831 | r#" |
834 | use expect_test::{expect, Expect}; | 832 | use expect_test::{expect, Expect}; |
diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs index b6c5de658..05a6b0b25 100644 --- a/crates/syntax/src/ast/make.rs +++ b/crates/syntax/src/ast/make.rs | |||
@@ -91,6 +91,10 @@ pub fn path_from_segments( | |||
91 | }) | 91 | }) |
92 | } | 92 | } |
93 | 93 | ||
94 | pub fn path_from_text(text: &str) -> ast::Path { | ||
95 | ast_from_text(&format!("fn main() {{ let test = {}; }}", text)) | ||
96 | } | ||
97 | |||
94 | pub fn glob_use_tree() -> ast::UseTree { | 98 | pub fn glob_use_tree() -> ast::UseTree { |
95 | ast_from_text("use *;") | 99 | ast_from_text("use *;") |
96 | } | 100 | } |
@@ -218,6 +222,9 @@ pub fn expr_if( | |||
218 | }; | 222 | }; |
219 | expr_from_text(&format!("if {} {} {}", condition, then_branch, else_branch)) | 223 | expr_from_text(&format!("if {} {} {}", condition, then_branch, else_branch)) |
220 | } | 224 | } |
225 | pub fn expr_for_loop(pat: ast::Pat, expr: ast::Expr, block: ast::BlockExpr) -> ast::Expr { | ||
226 | expr_from_text(&format!("for {} in {} {}", pat, expr, block)) | ||
227 | } | ||
221 | pub fn expr_prefix(op: SyntaxKind, expr: ast::Expr) -> ast::Expr { | 228 | pub fn expr_prefix(op: SyntaxKind, expr: ast::Expr) -> ast::Expr { |
222 | let token = token(op); | 229 | let token = token(op); |
223 | expr_from_text(&format!("{}{}", token, expr)) | 230 | expr_from_text(&format!("{}{}", token, expr)) |
diff --git a/crates/syntax/src/lib.rs b/crates/syntax/src/lib.rs index 11294c5b2..09e212e8c 100644 --- a/crates/syntax/src/lib.rs +++ b/crates/syntax/src/lib.rs | |||
@@ -37,6 +37,7 @@ pub mod algo; | |||
37 | pub mod ast; | 37 | pub mod ast; |
38 | #[doc(hidden)] | 38 | #[doc(hidden)] |
39 | pub mod fuzz; | 39 | pub mod fuzz; |
40 | pub mod utils; | ||
40 | 41 | ||
41 | use std::{marker::PhantomData, sync::Arc}; | 42 | use std::{marker::PhantomData, sync::Arc}; |
42 | 43 | ||
diff --git a/crates/syntax/src/tests.rs b/crates/syntax/src/tests.rs index b2c06e24f..ba0ccfaed 100644 --- a/crates/syntax/src/tests.rs +++ b/crates/syntax/src/tests.rs | |||
@@ -7,7 +7,7 @@ use std::{ | |||
7 | use ast::NameOwner; | 7 | use ast::NameOwner; |
8 | use expect_test::expect_file; | 8 | use expect_test::expect_file; |
9 | use rayon::prelude::*; | 9 | use rayon::prelude::*; |
10 | use test_utils::{bench, bench_fixture, project_dir, skip_slow_tests}; | 10 | use test_utils::{bench, bench_fixture, project_root, skip_slow_tests}; |
11 | 11 | ||
12 | use crate::{ast, fuzz, tokenize, AstNode, SourceFile, SyntaxError, TextRange, TextSize, Token}; | 12 | use crate::{ast, fuzz, tokenize, AstNode, SourceFile, SyntaxError, TextRange, TextSize, Token}; |
13 | 13 | ||
@@ -153,7 +153,7 @@ fn reparse_fuzz_tests() { | |||
153 | /// Test that Rust-analyzer can parse and validate the rust-analyzer | 153 | /// Test that Rust-analyzer can parse and validate the rust-analyzer |
154 | #[test] | 154 | #[test] |
155 | fn self_hosting_parsing() { | 155 | fn self_hosting_parsing() { |
156 | let dir = project_dir().join("crates"); | 156 | let dir = project_root().join("crates"); |
157 | let files = walkdir::WalkDir::new(dir) | 157 | let files = walkdir::WalkDir::new(dir) |
158 | .into_iter() | 158 | .into_iter() |
159 | .filter_entry(|entry| { | 159 | .filter_entry(|entry| { |
@@ -193,7 +193,7 @@ fn self_hosting_parsing() { | |||
193 | } | 193 | } |
194 | 194 | ||
195 | fn test_data_dir() -> PathBuf { | 195 | fn test_data_dir() -> PathBuf { |
196 | project_dir().join("crates/syntax/test_data") | 196 | project_root().join("crates/syntax/test_data") |
197 | } | 197 | } |
198 | 198 | ||
199 | fn assert_errors_are_present(errors: &[SyntaxError], path: &Path) { | 199 | fn assert_errors_are_present(errors: &[SyntaxError], path: &Path) { |
diff --git a/crates/syntax/src/utils.rs b/crates/syntax/src/utils.rs new file mode 100644 index 000000000..f4c02518b --- /dev/null +++ b/crates/syntax/src/utils.rs | |||
@@ -0,0 +1,43 @@ | |||
1 | //! A set of utils methods to reuse on other abstraction levels | ||
2 | |||
3 | use itertools::Itertools; | ||
4 | |||
5 | use crate::{ast, match_ast, AstNode}; | ||
6 | |||
7 | pub fn path_to_string_stripping_turbo_fish(path: &ast::Path) -> String { | ||
8 | path.syntax() | ||
9 | .children() | ||
10 | .filter_map(|node| { | ||
11 | match_ast! { | ||
12 | match node { | ||
13 | ast::PathSegment(it) => { | ||
14 | Some(it.name_ref()?.to_string()) | ||
15 | }, | ||
16 | ast::Path(it) => { | ||
17 | Some(path_to_string_stripping_turbo_fish(&it)) | ||
18 | }, | ||
19 | _ => None, | ||
20 | } | ||
21 | } | ||
22 | }) | ||
23 | .join("::") | ||
24 | } | ||
25 | |||
26 | #[cfg(test)] | ||
27 | mod tests { | ||
28 | use super::path_to_string_stripping_turbo_fish; | ||
29 | use crate::ast::make; | ||
30 | |||
31 | #[test] | ||
32 | fn turbofishes_are_stripped() { | ||
33 | assert_eq!("Vec", path_to_string_stripping_turbo_fish(&make::path_from_text("Vec::<i32>")),); | ||
34 | assert_eq!( | ||
35 | "Vec::new", | ||
36 | path_to_string_stripping_turbo_fish(&make::path_from_text("Vec::<i32>::new")), | ||
37 | ); | ||
38 | assert_eq!( | ||
39 | "Vec::new", | ||
40 | path_to_string_stripping_turbo_fish(&make::path_from_text("Vec::new()")), | ||
41 | ); | ||
42 | } | ||
43 | } | ||
diff --git a/crates/test_utils/Cargo.toml b/crates/test_utils/Cargo.toml index 2a65000b8..87bab7a08 100644 --- a/crates/test_utils/Cargo.toml +++ b/crates/test_utils/Cargo.toml | |||
@@ -13,7 +13,6 @@ doctest = false | |||
13 | # Avoid adding deps here, this crate is widely used in tests it should compile fast! | 13 | # Avoid adding deps here, this crate is widely used in tests it should compile fast! |
14 | dissimilar = "1.0.2" | 14 | dissimilar = "1.0.2" |
15 | text-size = "1.0.0" | 15 | text-size = "1.0.0" |
16 | serde_json = "1.0.48" | ||
17 | rustc-hash = "1.1.0" | 16 | rustc-hash = "1.1.0" |
18 | 17 | ||
19 | stdx = { path = "../stdx", version = "0.0.0" } | 18 | stdx = { path = "../stdx", version = "0.0.0" } |
diff --git a/crates/test_utils/src/bench_fixture.rs b/crates/test_utils/src/bench_fixture.rs index d775e2cc9..3a37c4473 100644 --- a/crates/test_utils/src/bench_fixture.rs +++ b/crates/test_utils/src/bench_fixture.rs | |||
@@ -4,7 +4,7 @@ use std::fs; | |||
4 | 4 | ||
5 | use stdx::format_to; | 5 | use stdx::format_to; |
6 | 6 | ||
7 | use crate::project_dir; | 7 | use crate::project_root; |
8 | 8 | ||
9 | pub fn big_struct() -> String { | 9 | pub fn big_struct() -> String { |
10 | let n = 1_000; | 10 | let n = 1_000; |
@@ -32,11 +32,11 @@ struct S{} {{ | |||
32 | } | 32 | } |
33 | 33 | ||
34 | pub fn glorious_old_parser() -> String { | 34 | pub fn glorious_old_parser() -> String { |
35 | let path = project_dir().join("bench_data/glorious_old_parser"); | 35 | let path = project_root().join("bench_data/glorious_old_parser"); |
36 | fs::read_to_string(&path).unwrap() | 36 | fs::read_to_string(&path).unwrap() |
37 | } | 37 | } |
38 | 38 | ||
39 | pub fn numerous_macro_rules() -> String { | 39 | pub fn numerous_macro_rules() -> String { |
40 | let path = project_dir().join("bench_data/numerous_macro_rules"); | 40 | let path = project_root().join("bench_data/numerous_macro_rules"); |
41 | fs::read_to_string(&path).unwrap() | 41 | fs::read_to_string(&path).unwrap() |
42 | } | 42 | } |
diff --git a/crates/test_utils/src/lib.rs b/crates/test_utils/src/lib.rs index 5be4a64fc..c5f859790 100644 --- a/crates/test_utils/src/lib.rs +++ b/crates/test_utils/src/lib.rs | |||
@@ -6,20 +6,17 @@ | |||
6 | //! * Extracting markup (mainly, `$0` markers) out of fixture strings. | 6 | //! * Extracting markup (mainly, `$0` markers) out of fixture strings. |
7 | //! * marks (see the eponymous module). | 7 | //! * marks (see the eponymous module). |
8 | 8 | ||
9 | #[macro_use] | ||
10 | pub mod mark; | ||
11 | pub mod bench_fixture; | 9 | pub mod bench_fixture; |
12 | mod fixture; | 10 | mod fixture; |
13 | 11 | ||
14 | use std::{ | 12 | use std::{ |
15 | convert::{TryFrom, TryInto}, | 13 | convert::{TryFrom, TryInto}, |
16 | env, fs, | 14 | env, fs, |
17 | path::PathBuf, | 15 | path::{Path, PathBuf}, |
18 | }; | 16 | }; |
19 | 17 | ||
20 | use profile::StopWatch; | 18 | use profile::StopWatch; |
21 | use serde_json::Value; | 19 | use stdx::{is_ci, lines_with_ends}; |
22 | use stdx::lines_with_ends; | ||
23 | use text_size::{TextRange, TextSize}; | 20 | use text_size::{TextRange, TextSize}; |
24 | 21 | ||
25 | pub use dissimilar::diff as __diff; | 22 | pub use dissimilar::diff as __diff; |
@@ -281,101 +278,6 @@ fn main() { | |||
281 | ); | 278 | ); |
282 | } | 279 | } |
283 | 280 | ||
284 | // Comparison functionality borrowed from cargo: | ||
285 | |||
286 | /// Compare a line with an expected pattern. | ||
287 | /// - Use `[..]` as a wildcard to match 0 or more characters on the same line | ||
288 | /// (similar to `.*` in a regex). | ||
289 | pub fn lines_match(expected: &str, actual: &str) -> bool { | ||
290 | // Let's not deal with / vs \ (windows...) | ||
291 | // First replace backslash-escaped backslashes with forward slashes | ||
292 | // which can occur in, for example, JSON output | ||
293 | let expected = expected.replace(r"\\", "/").replace(r"\", "/"); | ||
294 | let mut actual: &str = &actual.replace(r"\\", "/").replace(r"\", "/"); | ||
295 | for (i, part) in expected.split("[..]").enumerate() { | ||
296 | match actual.find(part) { | ||
297 | Some(j) => { | ||
298 | if i == 0 && j != 0 { | ||
299 | return false; | ||
300 | } | ||
301 | actual = &actual[j + part.len()..]; | ||
302 | } | ||
303 | None => return false, | ||
304 | } | ||
305 | } | ||
306 | actual.is_empty() || expected.ends_with("[..]") | ||
307 | } | ||
308 | |||
309 | #[test] | ||
310 | fn lines_match_works() { | ||
311 | assert!(lines_match("a b", "a b")); | ||
312 | assert!(lines_match("a[..]b", "a b")); | ||
313 | assert!(lines_match("a[..]", "a b")); | ||
314 | assert!(lines_match("[..]", "a b")); | ||
315 | assert!(lines_match("[..]b", "a b")); | ||
316 | |||
317 | assert!(!lines_match("[..]b", "c")); | ||
318 | assert!(!lines_match("b", "c")); | ||
319 | assert!(!lines_match("b", "cb")); | ||
320 | } | ||
321 | |||
322 | /// Compares JSON object for approximate equality. | ||
323 | /// You can use `[..]` wildcard in strings (useful for OS dependent things such | ||
324 | /// as paths). You can use a `"{...}"` string literal as a wildcard for | ||
325 | /// arbitrary nested JSON. Arrays are sorted before comparison. | ||
326 | pub fn find_mismatch<'a>(expected: &'a Value, actual: &'a Value) -> Option<(&'a Value, &'a Value)> { | ||
327 | match (expected, actual) { | ||
328 | (Value::Number(l), Value::Number(r)) if l == r => None, | ||
329 | (Value::Bool(l), Value::Bool(r)) if l == r => None, | ||
330 | (Value::String(l), Value::String(r)) if lines_match(l, r) => None, | ||
331 | (Value::Array(l), Value::Array(r)) => { | ||
332 | if l.len() != r.len() { | ||
333 | return Some((expected, actual)); | ||
334 | } | ||
335 | |||
336 | let mut l = l.iter().collect::<Vec<_>>(); | ||
337 | let mut r = r.iter().collect::<Vec<_>>(); | ||
338 | |||
339 | l.retain(|l| match r.iter().position(|r| find_mismatch(l, r).is_none()) { | ||
340 | Some(i) => { | ||
341 | r.remove(i); | ||
342 | false | ||
343 | } | ||
344 | None => true, | ||
345 | }); | ||
346 | |||
347 | if !l.is_empty() { | ||
348 | assert!(!r.is_empty()); | ||
349 | Some((&l[0], &r[0])) | ||
350 | } else { | ||
351 | assert_eq!(r.len(), 0); | ||
352 | None | ||
353 | } | ||
354 | } | ||
355 | (Value::Object(l), Value::Object(r)) => { | ||
356 | fn sorted_values(obj: &serde_json::Map<String, Value>) -> Vec<&Value> { | ||
357 | let mut entries = obj.iter().collect::<Vec<_>>(); | ||
358 | entries.sort_by_key(|it| it.0); | ||
359 | entries.into_iter().map(|(_k, v)| v).collect::<Vec<_>>() | ||
360 | } | ||
361 | |||
362 | let same_keys = l.len() == r.len() && l.keys().all(|k| r.contains_key(k)); | ||
363 | if !same_keys { | ||
364 | return Some((expected, actual)); | ||
365 | } | ||
366 | |||
367 | let l = sorted_values(l); | ||
368 | let r = sorted_values(r); | ||
369 | |||
370 | l.into_iter().zip(r).filter_map(|(l, r)| find_mismatch(l, r)).next() | ||
371 | } | ||
372 | (Value::Null, Value::Null) => None, | ||
373 | // magic string literal "{...}" acts as wildcard for any sub-JSON | ||
374 | (Value::String(l), _) if l == "{...}" => None, | ||
375 | _ => Some((expected, actual)), | ||
376 | } | ||
377 | } | ||
378 | |||
379 | /// Returns `false` if slow tests should not run, otherwise returns `true` and | 281 | /// Returns `false` if slow tests should not run, otherwise returns `true` and |
380 | /// also creates a file at `./target/.slow_tests_cookie` which serves as a flag | 282 | /// also creates a file at `./target/.slow_tests_cookie` which serves as a flag |
381 | /// that slow tests did run. | 283 | /// that slow tests did run. |
@@ -384,14 +286,14 @@ pub fn skip_slow_tests() -> bool { | |||
384 | if should_skip { | 286 | if should_skip { |
385 | eprintln!("ignoring slow test") | 287 | eprintln!("ignoring slow test") |
386 | } else { | 288 | } else { |
387 | let path = project_dir().join("./target/.slow_tests_cookie"); | 289 | let path = project_root().join("./target/.slow_tests_cookie"); |
388 | fs::write(&path, ".").unwrap(); | 290 | fs::write(&path, ".").unwrap(); |
389 | } | 291 | } |
390 | should_skip | 292 | should_skip |
391 | } | 293 | } |
392 | 294 | ||
393 | /// Returns the path to the root directory of `rust-analyzer` project. | 295 | /// Returns the path to the root directory of `rust-analyzer` project. |
394 | pub fn project_dir() -> PathBuf { | 296 | pub fn project_root() -> PathBuf { |
395 | let dir = env!("CARGO_MANIFEST_DIR"); | 297 | let dir = env!("CARGO_MANIFEST_DIR"); |
396 | PathBuf::from(dir).parent().unwrap().parent().unwrap().to_owned() | 298 | PathBuf::from(dir).parent().unwrap().parent().unwrap().to_owned() |
397 | } | 299 | } |
@@ -449,3 +351,39 @@ pub fn bench(label: &'static str) -> impl Drop { | |||
449 | 351 | ||
450 | Bencher { sw: StopWatch::start(), label } | 352 | Bencher { sw: StopWatch::start(), label } |
451 | } | 353 | } |
354 | |||
355 | /// Checks that the `file` has the specified `contents`. If that is not the | ||
356 | /// case, updates the file and then fails the test. | ||
357 | pub fn ensure_file_contents(file: &Path, contents: &str) { | ||
358 | if let Err(()) = try_ensure_file_contents(file, contents) { | ||
359 | panic!("Some files were not up-to-date"); | ||
360 | } | ||
361 | } | ||
362 | |||
363 | /// Checks that the `file` has the specified `contents`. If that is not the | ||
364 | /// case, updates the file and return an Error. | ||
365 | pub fn try_ensure_file_contents(file: &Path, contents: &str) -> Result<(), ()> { | ||
366 | match std::fs::read_to_string(file) { | ||
367 | Ok(old_contents) if normalize_newlines(&old_contents) == normalize_newlines(contents) => { | ||
368 | return Ok(()) | ||
369 | } | ||
370 | _ => (), | ||
371 | } | ||
372 | let display_path = file.strip_prefix(&project_root()).unwrap_or(file); | ||
373 | eprintln!( | ||
374 | "\n\x1b[31;1merror\x1b[0m: {} was not up-to-date, updating\n", | ||
375 | display_path.display() | ||
376 | ); | ||
377 | if is_ci() { | ||
378 | eprintln!(" NOTE: run `cargo test` locally and commit the updated files\n"); | ||
379 | } | ||
380 | if let Some(parent) = file.parent() { | ||
381 | let _ = std::fs::create_dir_all(parent); | ||
382 | } | ||
383 | std::fs::write(file, contents).unwrap(); | ||
384 | Err(()) | ||
385 | } | ||
386 | |||
387 | fn normalize_newlines(s: &str) -> String { | ||
388 | s.replace("\r\n", "\n") | ||
389 | } | ||
diff --git a/crates/test_utils/src/mark.rs b/crates/test_utils/src/mark.rs deleted file mode 100644 index 97f5a93ad..000000000 --- a/crates/test_utils/src/mark.rs +++ /dev/null | |||
@@ -1,78 +0,0 @@ | |||
1 | //! This module implements manually tracked test coverage, which is useful for | ||
2 | //! quickly finding a test responsible for testing a particular bit of code. | ||
3 | //! | ||
4 | //! See <https://matklad.github.io/2018/06/18/a-trick-for-test-maintenance.html> | ||
5 | //! for details, but the TL;DR is that you write your test as | ||
6 | //! | ||
7 | //! ``` | ||
8 | //! #[test] | ||
9 | //! fn test_foo() { | ||
10 | //! mark::check!(test_foo); | ||
11 | //! } | ||
12 | //! ``` | ||
13 | //! | ||
14 | //! and in the code under test you write | ||
15 | //! | ||
16 | //! ``` | ||
17 | //! # use test_utils::mark; | ||
18 | //! # fn some_condition() -> bool { true } | ||
19 | //! fn foo() { | ||
20 | //! if some_condition() { | ||
21 | //! mark::hit!(test_foo); | ||
22 | //! } | ||
23 | //! } | ||
24 | //! ``` | ||
25 | //! | ||
26 | //! This module then checks that executing the test indeed covers the specified | ||
27 | //! function. This is useful if you come back to the `foo` function ten years | ||
28 | //! later and wonder where the test are: now you can grep for `test_foo`. | ||
29 | use std::sync::atomic::{AtomicUsize, Ordering}; | ||
30 | |||
31 | #[macro_export] | ||
32 | macro_rules! _hit { | ||
33 | ($ident:ident) => {{ | ||
34 | #[cfg(test)] | ||
35 | { | ||
36 | extern "C" { | ||
37 | #[no_mangle] | ||
38 | static $ident: std::sync::atomic::AtomicUsize; | ||
39 | } | ||
40 | unsafe { | ||
41 | $ident.fetch_add(1, std::sync::atomic::Ordering::SeqCst); | ||
42 | } | ||
43 | } | ||
44 | }}; | ||
45 | } | ||
46 | pub use _hit as hit; | ||
47 | |||
48 | #[macro_export] | ||
49 | macro_rules! _check { | ||
50 | ($ident:ident) => { | ||
51 | #[no_mangle] | ||
52 | static $ident: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0); | ||
53 | let _checker = $crate::mark::MarkChecker::new(&$ident); | ||
54 | }; | ||
55 | } | ||
56 | pub use _check as check; | ||
57 | |||
58 | pub struct MarkChecker { | ||
59 | mark: &'static AtomicUsize, | ||
60 | value_on_entry: usize, | ||
61 | } | ||
62 | |||
63 | impl MarkChecker { | ||
64 | pub fn new(mark: &'static AtomicUsize) -> MarkChecker { | ||
65 | let value_on_entry = mark.load(Ordering::Relaxed); | ||
66 | MarkChecker { mark, value_on_entry } | ||
67 | } | ||
68 | } | ||
69 | |||
70 | impl Drop for MarkChecker { | ||
71 | fn drop(&mut self) { | ||
72 | if std::thread::panicking() { | ||
73 | return; | ||
74 | } | ||
75 | let value_on_exit = self.mark.load(Ordering::Relaxed); | ||
76 | assert!(value_on_exit > self.value_on_entry, "mark was not hit") | ||
77 | } | ||
78 | } | ||
diff --git a/docs/dev/architecture.md b/docs/dev/architecture.md index ead12616e..fb991133a 100644 --- a/docs/dev/architecture.md +++ b/docs/dev/architecture.md | |||
@@ -46,7 +46,7 @@ This is *the* entry point, but it front-loads a lot of complexity, so its fine t | |||
46 | 46 | ||
47 | `crates/rust-analyzer/src/handlers.rs` implements all LSP requests and is a great place to start if you are already familiar with LSP. | 47 | `crates/rust-analyzer/src/handlers.rs` implements all LSP requests and is a great place to start if you are already familiar with LSP. |
48 | 48 | ||
49 | `Analysis` and `AnalysisHost` types define the main API. | 49 | `Analysis` and `AnalysisHost` types define the main API for consumers of IDE services. |
50 | 50 | ||
51 | ## Code Map | 51 | ## Code Map |
52 | 52 | ||
@@ -97,13 +97,13 @@ See [RFC](https://github.com/rust-lang/rfcs/pull/2256) and [./syntax.md](./synta | |||
97 | 97 | ||
98 | - [rowan](https://github.com/rust-analyzer/rowan) library is used for constructing syntax trees. | 98 | - [rowan](https://github.com/rust-analyzer/rowan) library is used for constructing syntax trees. |
99 | - `ast` provides a type safe API on top of the raw `rowan` tree. | 99 | - `ast` provides a type safe API on top of the raw `rowan` tree. |
100 | - `ungrammar` description of the grammar, which is used to generate `syntax_kinds` and `ast` modules, using `cargo xtask codegen` command. | 100 | - `ungrammar` description of the grammar, which is used to generate `syntax_kinds` and `ast` modules, using `cargo test -p xtask` command. |
101 | 101 | ||
102 | Tests for ra_syntax are mostly data-driven. | 102 | Tests for ra_syntax are mostly data-driven. |
103 | `test_data/parser` contains subdirectories with a bunch of `.rs` (test vectors) and `.txt` files with corresponding syntax trees. | 103 | `test_data/parser` contains subdirectories with a bunch of `.rs` (test vectors) and `.txt` files with corresponding syntax trees. |
104 | During testing, we check `.rs` against `.txt`. | 104 | During testing, we check `.rs` against `.txt`. |
105 | If the `.txt` file is missing, it is created (this is how you update tests). | 105 | If the `.txt` file is missing, it is created (this is how you update tests). |
106 | Additionally, running `cargo xtask codegen` will walk the grammar module and collect all `// test test_name` comments into files inside `test_data/parser/inline` directory. | 106 | Additionally, running the xtask test suite with `cargo test -p xtask` will walk the grammar module and collect all `// test test_name` comments into files inside `test_data/parser/inline` directory. |
107 | 107 | ||
108 | To update test data, run with `UPDATE_EXPECT` variable: | 108 | To update test data, run with `UPDATE_EXPECT` variable: |
109 | 109 | ||
@@ -111,7 +111,7 @@ To update test data, run with `UPDATE_EXPECT` variable: | |||
111 | env UPDATE_EXPECT=1 cargo qt | 111 | env UPDATE_EXPECT=1 cargo qt |
112 | ``` | 112 | ``` |
113 | 113 | ||
114 | After adding a new inline test you need to run `cargo xtest codegen` and also update the test data as described above. | 114 | After adding a new inline test you need to run `cargo test -p xtask` and also update the test data as described above. |
115 | 115 | ||
116 | Note [`api_walkthrough`](https://github.com/rust-analyzer/rust-analyzer/blob/2fb6af89eb794f775de60b82afe56b6f986c2a40/crates/ra_syntax/src/lib.rs#L190-L348) | 116 | Note [`api_walkthrough`](https://github.com/rust-analyzer/rust-analyzer/blob/2fb6af89eb794f775de60b82afe56b6f986c2a40/crates/ra_syntax/src/lib.rs#L190-L348) |
117 | in particular: it shows off various methods of working with syntax tree. | 117 | in particular: it shows off various methods of working with syntax tree. |
@@ -308,9 +308,8 @@ This sections talks about the things which are everywhere and nowhere in particu | |||
308 | ### Code generation | 308 | ### Code generation |
309 | 309 | ||
310 | Some of the components of this repository are generated through automatic processes. | 310 | Some of the components of this repository are generated through automatic processes. |
311 | `cargo xtask codegen` runs all generation tasks. | 311 | Generated code is updated automatically on `cargo test`. |
312 | Generated code is generally committed to the git repository. | 312 | Generated code is generally committed to the git repository. |
313 | There are tests to check that the generated code is fresh. | ||
314 | 313 | ||
315 | In particular, we generate: | 314 | In particular, we generate: |
316 | 315 | ||
diff --git a/docs/dev/lsp-extensions.md b/docs/dev/lsp-extensions.md index 164c8482e..dd3ecc18d 100644 --- a/docs/dev/lsp-extensions.md +++ b/docs/dev/lsp-extensions.md | |||
@@ -39,7 +39,7 @@ If a language client does not know about `rust-analyzer`'s configuration options | |||
39 | 39 | ||
40 | **Issue:** https://github.com/microsoft/language-server-protocol/issues/724 | 40 | **Issue:** https://github.com/microsoft/language-server-protocol/issues/724 |
41 | 41 | ||
42 | **Client Capability:** `{ "snippetTextEdit": boolean }` | 42 | **Experimental Client Capability:** `{ "snippetTextEdit": boolean }` |
43 | 43 | ||
44 | If this capability is set, `WorkspaceEdit`s returned from `codeAction` requests might contain `SnippetTextEdit`s instead of usual `TextEdit`s: | 44 | If this capability is set, `WorkspaceEdit`s returned from `codeAction` requests might contain `SnippetTextEdit`s instead of usual `TextEdit`s: |
45 | 45 | ||
@@ -72,7 +72,7 @@ At the moment, rust-analyzer guarantees that only a single edit will have `Inser | |||
72 | 72 | ||
73 | **Issue:** https://github.com/microsoft/language-server-protocol/issues/994 | 73 | **Issue:** https://github.com/microsoft/language-server-protocol/issues/994 |
74 | 74 | ||
75 | **Client Capability:** `{ "codeActionGroup": boolean }` | 75 | **Experimental Client Capability:** `{ "codeActionGroup": boolean }` |
76 | 76 | ||
77 | If this capability is set, `CodeAction` returned from the server contain an additional field, `group`: | 77 | If this capability is set, `CodeAction` returned from the server contain an additional field, `group`: |
78 | 78 | ||
@@ -119,7 +119,7 @@ Invoking code action at this position will yield two code actions for importing | |||
119 | 119 | ||
120 | **Issue:** https://github.com/microsoft/language-server-protocol/issues/1002 | 120 | **Issue:** https://github.com/microsoft/language-server-protocol/issues/1002 |
121 | 121 | ||
122 | **Server Capability:** `{ "parentModule": boolean }` | 122 | **Experimental Server Capability:** `{ "parentModule": boolean }` |
123 | 123 | ||
124 | This request is sent from client to server to handle "Goto Parent Module" editor action. | 124 | This request is sent from client to server to handle "Goto Parent Module" editor action. |
125 | 125 | ||
@@ -153,7 +153,7 @@ mod foo; | |||
153 | 153 | ||
154 | **Issue:** https://github.com/microsoft/language-server-protocol/issues/992 | 154 | **Issue:** https://github.com/microsoft/language-server-protocol/issues/992 |
155 | 155 | ||
156 | **Server Capability:** `{ "joinLines": boolean }` | 156 | **Experimental Server Capability:** `{ "joinLines": boolean }` |
157 | 157 | ||
158 | This request is sent from client to server to handle "Join Lines" editor action. | 158 | This request is sent from client to server to handle "Join Lines" editor action. |
159 | 159 | ||
@@ -200,7 +200,7 @@ fn main() { | |||
200 | 200 | ||
201 | **Issue:** https://github.com/microsoft/language-server-protocol/issues/1001 | 201 | **Issue:** https://github.com/microsoft/language-server-protocol/issues/1001 |
202 | 202 | ||
203 | **Server Capability:** `{ "onEnter": boolean }` | 203 | **Experimental Server Capability:** `{ "onEnter": boolean }` |
204 | 204 | ||
205 | This request is sent from client to server to handle <kbd>Enter</kbd> keypress. | 205 | This request is sent from client to server to handle <kbd>Enter</kbd> keypress. |
206 | 206 | ||
@@ -251,7 +251,7 @@ As proper cursor positioning is raison-d'etat for `onEnter`, it uses `SnippetTex | |||
251 | 251 | ||
252 | ## Structural Search Replace (SSR) | 252 | ## Structural Search Replace (SSR) |
253 | 253 | ||
254 | **Server Capability:** `{ "ssr": boolean }` | 254 | **Experimental Server Capability:** `{ "ssr": boolean }` |
255 | 255 | ||
256 | This request is sent from client to server to handle structural search replace -- automated syntax tree based transformation of the source. | 256 | This request is sent from client to server to handle structural search replace -- automated syntax tree based transformation of the source. |
257 | 257 | ||
@@ -293,7 +293,7 @@ SSR with query `foo($a, $b) ==>> ($a).foo($b)` will transform, eg `foo(y + 5, z) | |||
293 | 293 | ||
294 | **Issue:** https://github.com/microsoft/language-server-protocol/issues/999 | 294 | **Issue:** https://github.com/microsoft/language-server-protocol/issues/999 |
295 | 295 | ||
296 | **Server Capability:** `{ "matchingBrace": boolean }` | 296 | **Experimental Server Capability:** `{ "matchingBrace": boolean }` |
297 | 297 | ||
298 | This request is sent from client to server to handle "Matching Brace" editor action. | 298 | This request is sent from client to server to handle "Matching Brace" editor action. |
299 | 299 | ||
@@ -338,7 +338,7 @@ Moreover, it would be cool if editors didn't need to implement even basic langua | |||
338 | 338 | ||
339 | **Issue:** https://github.com/microsoft/language-server-protocol/issues/944 | 339 | **Issue:** https://github.com/microsoft/language-server-protocol/issues/944 |
340 | 340 | ||
341 | **Server Capability:** `{ "runnables": { "kinds": string[] } }` | 341 | **Experimental Server Capability:** `{ "runnables": { "kinds": string[] } }` |
342 | 342 | ||
343 | This request is sent from client to server to get the list of things that can be run (tests, binaries, `cargo check -p`). | 343 | This request is sent from client to server to get the list of things that can be run (tests, binaries, `cargo check -p`). |
344 | 344 | ||
@@ -421,7 +421,7 @@ Reloads project information (that is, re-executes `cargo metadata`). | |||
421 | 421 | ||
422 | ## Status Notification | 422 | ## Status Notification |
423 | 423 | ||
424 | **Client Capability:** `{ "statusNotification": boolean }` | 424 | **Experimental Client Capability:** `{ "statusNotification": boolean }` |
425 | 425 | ||
426 | **Method:** `rust-analyzer/status` | 426 | **Method:** `rust-analyzer/status` |
427 | 427 | ||
@@ -519,7 +519,7 @@ interface InlayHint { | |||
519 | 519 | ||
520 | ## Hover Actions | 520 | ## Hover Actions |
521 | 521 | ||
522 | **Client Capability:** `{ "hoverActions": boolean }` | 522 | **Experimental Client Capability:** `{ "hoverActions": boolean }` |
523 | 523 | ||
524 | If this capability is set, `Hover` request returned from the server might contain an additional field, `actions`: | 524 | If this capability is set, `Hover` request returned from the server might contain an additional field, `actions`: |
525 | 525 | ||
diff --git a/docs/dev/style.md b/docs/dev/style.md index 93ad98f20..46bd8b9b2 100644 --- a/docs/dev/style.md +++ b/docs/dev/style.md | |||
@@ -145,7 +145,7 @@ Formatting ensures that you can use your editor's "number of selected characters | |||
145 | ## Marked Tests | 145 | ## Marked Tests |
146 | 146 | ||
147 | Use | 147 | Use |
148 | [`mark::hit! / mark::check!`](https://github.com/rust-analyzer/rust-analyzer/blob/71fe719dd5247ed8615641d9303d7ca1aa201c2f/crates/test_utils/src/mark.rs) | 148 | [`cov_mark::hit! / cov_mark::check!`](https://github.com/matklad/cov-mark) |
149 | when testing specific conditions. | 149 | when testing specific conditions. |
150 | Do not place several marks into a single test or condition. | 150 | Do not place several marks into a single test or condition. |
151 | Do not reuse marks between several tests. | 151 | Do not reuse marks between several tests. |
diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc index 1dbf2a611..042ba2d54 100644 --- a/docs/user/generated_config.adoc +++ b/docs/user/generated_config.adoc | |||
@@ -1,112 +1,322 @@ | |||
1 | [[rust-analyzer.assist.importMergeBehavior]]rust-analyzer.assist.importMergeBehavior (default: `"full"`):: | 1 | [[rust-analyzer.assist.importMergeBehavior]]rust-analyzer.assist.importMergeBehavior (default: `"full"`):: |
2 | The strategy to use when inserting new imports or merging imports. | 2 | + |
3 | -- | ||
4 | The strategy to use when inserting new imports or merging imports. | ||
5 | -- | ||
3 | [[rust-analyzer.assist.importPrefix]]rust-analyzer.assist.importPrefix (default: `"plain"`):: | 6 | [[rust-analyzer.assist.importPrefix]]rust-analyzer.assist.importPrefix (default: `"plain"`):: |
4 | The path structure for newly inserted paths to use. | 7 | + |
8 | -- | ||
9 | The path structure for newly inserted paths to use. | ||
10 | -- | ||
11 | [[rust-analyzer.assist.importGroup]]rust-analyzer.assist.importGroup (default: `true`):: | ||
12 | + | ||
13 | -- | ||
14 | Group inserted imports by the [following order](https://rust-analyzer.github.io/manual.html#auto-import). Groups are separated by newlines. | ||
15 | -- | ||
5 | [[rust-analyzer.callInfo.full]]rust-analyzer.callInfo.full (default: `true`):: | 16 | [[rust-analyzer.callInfo.full]]rust-analyzer.callInfo.full (default: `true`):: |
6 | Show function name and docs in parameter hints. | 17 | + |
18 | -- | ||
19 | Show function name and docs in parameter hints. | ||
20 | -- | ||
7 | [[rust-analyzer.cargo.autoreload]]rust-analyzer.cargo.autoreload (default: `true`):: | 21 | [[rust-analyzer.cargo.autoreload]]rust-analyzer.cargo.autoreload (default: `true`):: |
8 | Automatically refresh project info via `cargo metadata` on `Cargo.toml` changes. | 22 | + |
23 | -- | ||
24 | Automatically refresh project info via `cargo metadata` on | ||
25 | `Cargo.toml` changes. | ||
26 | -- | ||
9 | [[rust-analyzer.cargo.allFeatures]]rust-analyzer.cargo.allFeatures (default: `false`):: | 27 | [[rust-analyzer.cargo.allFeatures]]rust-analyzer.cargo.allFeatures (default: `false`):: |
10 | Activate all available features (`--all-features`). | 28 | + |
29 | -- | ||
30 | Activate all available features (`--all-features`). | ||
31 | -- | ||
11 | [[rust-analyzer.cargo.features]]rust-analyzer.cargo.features (default: `[]`):: | 32 | [[rust-analyzer.cargo.features]]rust-analyzer.cargo.features (default: `[]`):: |
12 | List of features to activate. | 33 | + |
13 | [[rust-analyzer.cargo.runBuildScripts]]rust-analyzer.cargo.runBuildScripts (default: `false`):: | 34 | -- |
14 | Run build scripts (`build.rs`) for more precise code analysis. | 35 | List of features to activate. |
36 | -- | ||
37 | [[rust-analyzer.cargo.runBuildScripts]]rust-analyzer.cargo.runBuildScripts (default: `true`):: | ||
38 | + | ||
39 | -- | ||
40 | Run build scripts (`build.rs`) for more precise code analysis. | ||
41 | -- | ||
15 | [[rust-analyzer.cargo.noDefaultFeatures]]rust-analyzer.cargo.noDefaultFeatures (default: `false`):: | 42 | [[rust-analyzer.cargo.noDefaultFeatures]]rust-analyzer.cargo.noDefaultFeatures (default: `false`):: |
16 | Do not activate the `default` feature. | 43 | + |
44 | -- | ||
45 | Do not activate the `default` feature. | ||
46 | -- | ||
17 | [[rust-analyzer.cargo.target]]rust-analyzer.cargo.target (default: `null`):: | 47 | [[rust-analyzer.cargo.target]]rust-analyzer.cargo.target (default: `null`):: |
18 | Compilation target (target triple). | 48 | + |
49 | -- | ||
50 | Compilation target (target triple). | ||
51 | -- | ||
19 | [[rust-analyzer.cargo.noSysroot]]rust-analyzer.cargo.noSysroot (default: `false`):: | 52 | [[rust-analyzer.cargo.noSysroot]]rust-analyzer.cargo.noSysroot (default: `false`):: |
20 | Internal config for debugging, disables loading of sysroot crates. | 53 | + |
54 | -- | ||
55 | Internal config for debugging, disables loading of sysroot crates. | ||
56 | -- | ||
21 | [[rust-analyzer.checkOnSave.enable]]rust-analyzer.checkOnSave.enable (default: `true`):: | 57 | [[rust-analyzer.checkOnSave.enable]]rust-analyzer.checkOnSave.enable (default: `true`):: |
22 | Run specified `cargo check` command for diagnostics on save. | 58 | + |
59 | -- | ||
60 | Run specified `cargo check` command for diagnostics on save. | ||
61 | -- | ||
23 | [[rust-analyzer.checkOnSave.allFeatures]]rust-analyzer.checkOnSave.allFeatures (default: `null`):: | 62 | [[rust-analyzer.checkOnSave.allFeatures]]rust-analyzer.checkOnSave.allFeatures (default: `null`):: |
24 | Check with all features (`--all-features`). Defaults to `#rust-analyzer.cargo.allFeatures#`. | 63 | + |
64 | -- | ||
65 | Check with all features (`--all-features`). | ||
66 | Defaults to `#rust-analyzer.cargo.allFeatures#`. | ||
67 | -- | ||
25 | [[rust-analyzer.checkOnSave.allTargets]]rust-analyzer.checkOnSave.allTargets (default: `true`):: | 68 | [[rust-analyzer.checkOnSave.allTargets]]rust-analyzer.checkOnSave.allTargets (default: `true`):: |
26 | Check all targets and tests (`--all-targets`). | 69 | + |
70 | -- | ||
71 | Check all targets and tests (`--all-targets`). | ||
72 | -- | ||
27 | [[rust-analyzer.checkOnSave.command]]rust-analyzer.checkOnSave.command (default: `"check"`):: | 73 | [[rust-analyzer.checkOnSave.command]]rust-analyzer.checkOnSave.command (default: `"check"`):: |
28 | Cargo command to use for `cargo check`. | 74 | + |
75 | -- | ||
76 | Cargo command to use for `cargo check`. | ||
77 | -- | ||
29 | [[rust-analyzer.checkOnSave.noDefaultFeatures]]rust-analyzer.checkOnSave.noDefaultFeatures (default: `null`):: | 78 | [[rust-analyzer.checkOnSave.noDefaultFeatures]]rust-analyzer.checkOnSave.noDefaultFeatures (default: `null`):: |
30 | Do not activate the `default` feature. | 79 | + |
80 | -- | ||
81 | Do not activate the `default` feature. | ||
82 | -- | ||
31 | [[rust-analyzer.checkOnSave.target]]rust-analyzer.checkOnSave.target (default: `null`):: | 83 | [[rust-analyzer.checkOnSave.target]]rust-analyzer.checkOnSave.target (default: `null`):: |
32 | Check for a specific target. Defaults to `#rust-analyzer.cargo.target#`. | 84 | + |
85 | -- | ||
86 | Check for a specific target. Defaults to | ||
87 | `#rust-analyzer.cargo.target#`. | ||
88 | -- | ||
33 | [[rust-analyzer.checkOnSave.extraArgs]]rust-analyzer.checkOnSave.extraArgs (default: `[]`):: | 89 | [[rust-analyzer.checkOnSave.extraArgs]]rust-analyzer.checkOnSave.extraArgs (default: `[]`):: |
34 | Extra arguments for `cargo check`. | 90 | + |
91 | -- | ||
92 | Extra arguments for `cargo check`. | ||
93 | -- | ||
35 | [[rust-analyzer.checkOnSave.features]]rust-analyzer.checkOnSave.features (default: `null`):: | 94 | [[rust-analyzer.checkOnSave.features]]rust-analyzer.checkOnSave.features (default: `null`):: |
36 | List of features to activate. Defaults to `#rust-analyzer.cargo.features#`. | 95 | + |
96 | -- | ||
97 | List of features to activate. Defaults to | ||
98 | `#rust-analyzer.cargo.features#`. | ||
99 | -- | ||
37 | [[rust-analyzer.checkOnSave.overrideCommand]]rust-analyzer.checkOnSave.overrideCommand (default: `null`):: | 100 | [[rust-analyzer.checkOnSave.overrideCommand]]rust-analyzer.checkOnSave.overrideCommand (default: `null`):: |
38 | Advanced option, fully override the command rust-analyzer uses for checking. The command should include `--message-format=json` or similar option. | 101 | + |
102 | -- | ||
103 | Advanced option, fully override the command rust-analyzer uses for | ||
104 | checking. The command should include `--message-format=json` or | ||
105 | similar option. | ||
106 | -- | ||
39 | [[rust-analyzer.completion.addCallArgumentSnippets]]rust-analyzer.completion.addCallArgumentSnippets (default: `true`):: | 107 | [[rust-analyzer.completion.addCallArgumentSnippets]]rust-analyzer.completion.addCallArgumentSnippets (default: `true`):: |
40 | Whether to add argument snippets when completing functions. | 108 | + |
109 | -- | ||
110 | Whether to add argument snippets when completing functions. | ||
111 | -- | ||
41 | [[rust-analyzer.completion.addCallParenthesis]]rust-analyzer.completion.addCallParenthesis (default: `true`):: | 112 | [[rust-analyzer.completion.addCallParenthesis]]rust-analyzer.completion.addCallParenthesis (default: `true`):: |
42 | Whether to add parenthesis when completing functions. | 113 | + |
114 | -- | ||
115 | Whether to add parenthesis when completing functions. | ||
116 | -- | ||
43 | [[rust-analyzer.completion.postfix.enable]]rust-analyzer.completion.postfix.enable (default: `true`):: | 117 | [[rust-analyzer.completion.postfix.enable]]rust-analyzer.completion.postfix.enable (default: `true`):: |
44 | Whether to show postfix snippets like `dbg`, `if`, `not`, etc. | 118 | + |
119 | -- | ||
120 | Whether to show postfix snippets like `dbg`, `if`, `not`, etc. | ||
121 | -- | ||
45 | [[rust-analyzer.completion.autoimport.enable]]rust-analyzer.completion.autoimport.enable (default: `true`):: | 122 | [[rust-analyzer.completion.autoimport.enable]]rust-analyzer.completion.autoimport.enable (default: `true`):: |
46 | Toggles the additional completions that automatically add imports when completed. Note that your client must specify the `additionalTextEdits` LSP client capability to truly have this feature enabled. | 123 | + |
124 | -- | ||
125 | Toggles the additional completions that automatically add imports when completed. | ||
126 | Note that your client must specify the `additionalTextEdits` LSP client capability to truly have this feature enabled. | ||
127 | -- | ||
47 | [[rust-analyzer.diagnostics.enable]]rust-analyzer.diagnostics.enable (default: `true`):: | 128 | [[rust-analyzer.diagnostics.enable]]rust-analyzer.diagnostics.enable (default: `true`):: |
48 | Whether to show native rust-analyzer diagnostics. | 129 | + |
130 | -- | ||
131 | Whether to show native rust-analyzer diagnostics. | ||
132 | -- | ||
49 | [[rust-analyzer.diagnostics.enableExperimental]]rust-analyzer.diagnostics.enableExperimental (default: `true`):: | 133 | [[rust-analyzer.diagnostics.enableExperimental]]rust-analyzer.diagnostics.enableExperimental (default: `true`):: |
50 | Whether to show experimental rust-analyzer diagnostics that might have more false positives than usual. | 134 | + |
135 | -- | ||
136 | Whether to show experimental rust-analyzer diagnostics that might | ||
137 | have more false positives than usual. | ||
138 | -- | ||
51 | [[rust-analyzer.diagnostics.disabled]]rust-analyzer.diagnostics.disabled (default: `[]`):: | 139 | [[rust-analyzer.diagnostics.disabled]]rust-analyzer.diagnostics.disabled (default: `[]`):: |
52 | List of rust-analyzer diagnostics to disable. | 140 | + |
141 | -- | ||
142 | List of rust-analyzer diagnostics to disable. | ||
143 | -- | ||
53 | [[rust-analyzer.diagnostics.warningsAsHint]]rust-analyzer.diagnostics.warningsAsHint (default: `[]`):: | 144 | [[rust-analyzer.diagnostics.warningsAsHint]]rust-analyzer.diagnostics.warningsAsHint (default: `[]`):: |
54 | List of warnings that should be displayed with info severity.\n\nThe warnings will be indicated by a blue squiggly underline in code and a blue icon in the `Problems Panel`. | 145 | + |
146 | -- | ||
147 | List of warnings that should be displayed with info severity. | ||
148 | |||
149 | The warnings will be indicated by a blue squiggly underline in code | ||
150 | and a blue icon in the `Problems Panel`. | ||
151 | -- | ||
55 | [[rust-analyzer.diagnostics.warningsAsInfo]]rust-analyzer.diagnostics.warningsAsInfo (default: `[]`):: | 152 | [[rust-analyzer.diagnostics.warningsAsInfo]]rust-analyzer.diagnostics.warningsAsInfo (default: `[]`):: |
56 | List of warnings that should be displayed with hint severity.\n\nThe warnings will be indicated by faded text or three dots in code and will not show up in the `Problems Panel`. | 153 | + |
154 | -- | ||
155 | List of warnings that should be displayed with hint severity. | ||
156 | |||
157 | The warnings will be indicated by faded text or three dots in code | ||
158 | and will not show up in the `Problems Panel`. | ||
159 | -- | ||
57 | [[rust-analyzer.files.watcher]]rust-analyzer.files.watcher (default: `"client"`):: | 160 | [[rust-analyzer.files.watcher]]rust-analyzer.files.watcher (default: `"client"`):: |
58 | Controls file watching implementation. | 161 | + |
162 | -- | ||
163 | Controls file watching implementation. | ||
164 | -- | ||
59 | [[rust-analyzer.files.excludeDirs]]rust-analyzer.files.excludeDirs (default: `[]`):: | 165 | [[rust-analyzer.files.excludeDirs]]rust-analyzer.files.excludeDirs (default: `[]`):: |
60 | These directories will be ignored by rust-analyzer. | 166 | + |
167 | -- | ||
168 | These directories will be ignored by rust-analyzer. | ||
169 | -- | ||
61 | [[rust-analyzer.hoverActions.debug]]rust-analyzer.hoverActions.debug (default: `true`):: | 170 | [[rust-analyzer.hoverActions.debug]]rust-analyzer.hoverActions.debug (default: `true`):: |
62 | Whether to show `Debug` action. Only applies when `#rust-analyzer.hoverActions.enable#` is set. | 171 | + |
172 | -- | ||
173 | Whether to show `Debug` action. Only applies when | ||
174 | `#rust-analyzer.hoverActions.enable#` is set. | ||
175 | -- | ||
63 | [[rust-analyzer.hoverActions.enable]]rust-analyzer.hoverActions.enable (default: `true`):: | 176 | [[rust-analyzer.hoverActions.enable]]rust-analyzer.hoverActions.enable (default: `true`):: |
64 | Whether to show HoverActions in Rust files. | 177 | + |
178 | -- | ||
179 | Whether to show HoverActions in Rust files. | ||
180 | -- | ||
65 | [[rust-analyzer.hoverActions.gotoTypeDef]]rust-analyzer.hoverActions.gotoTypeDef (default: `true`):: | 181 | [[rust-analyzer.hoverActions.gotoTypeDef]]rust-analyzer.hoverActions.gotoTypeDef (default: `true`):: |
66 | Whether to show `Go to Type Definition` action. Only applies when `#rust-analyzer.hoverActions.enable#` is set. | 182 | + |
183 | -- | ||
184 | Whether to show `Go to Type Definition` action. Only applies when | ||
185 | `#rust-analyzer.hoverActions.enable#` is set. | ||
186 | -- | ||
67 | [[rust-analyzer.hoverActions.implementations]]rust-analyzer.hoverActions.implementations (default: `true`):: | 187 | [[rust-analyzer.hoverActions.implementations]]rust-analyzer.hoverActions.implementations (default: `true`):: |
68 | Whether to show `Implementations` action. Only applies when `#rust-analyzer.hoverActions.enable#` is set. | 188 | + |
189 | -- | ||
190 | Whether to show `Implementations` action. Only applies when | ||
191 | `#rust-analyzer.hoverActions.enable#` is set. | ||
192 | -- | ||
69 | [[rust-analyzer.hoverActions.run]]rust-analyzer.hoverActions.run (default: `true`):: | 193 | [[rust-analyzer.hoverActions.run]]rust-analyzer.hoverActions.run (default: `true`):: |
70 | Whether to show `Run` action. Only applies when `#rust-analyzer.hoverActions.enable#` is set. | 194 | + |
195 | -- | ||
196 | Whether to show `Run` action. Only applies when | ||
197 | `#rust-analyzer.hoverActions.enable#` is set. | ||
198 | -- | ||
71 | [[rust-analyzer.hoverActions.linksInHover]]rust-analyzer.hoverActions.linksInHover (default: `true`):: | 199 | [[rust-analyzer.hoverActions.linksInHover]]rust-analyzer.hoverActions.linksInHover (default: `true`):: |
72 | Use markdown syntax for links in hover. | 200 | + |
201 | -- | ||
202 | Use markdown syntax for links in hover. | ||
203 | -- | ||
73 | [[rust-analyzer.inlayHints.chainingHints]]rust-analyzer.inlayHints.chainingHints (default: `true`):: | 204 | [[rust-analyzer.inlayHints.chainingHints]]rust-analyzer.inlayHints.chainingHints (default: `true`):: |
74 | Whether to show inlay type hints for method chains. | 205 | + |
206 | -- | ||
207 | Whether to show inlay type hints for method chains. | ||
208 | -- | ||
75 | [[rust-analyzer.inlayHints.maxLength]]rust-analyzer.inlayHints.maxLength (default: `null`):: | 209 | [[rust-analyzer.inlayHints.maxLength]]rust-analyzer.inlayHints.maxLength (default: `null`):: |
76 | Maximum length for inlay hints. Default is unlimited. | 210 | + |
211 | -- | ||
212 | Maximum length for inlay hints. Default is unlimited. | ||
213 | -- | ||
77 | [[rust-analyzer.inlayHints.parameterHints]]rust-analyzer.inlayHints.parameterHints (default: `true`):: | 214 | [[rust-analyzer.inlayHints.parameterHints]]rust-analyzer.inlayHints.parameterHints (default: `true`):: |
78 | Whether to show function parameter name inlay hints at the call site. | 215 | + |
216 | -- | ||
217 | Whether to show function parameter name inlay hints at the call | ||
218 | site. | ||
219 | -- | ||
79 | [[rust-analyzer.inlayHints.typeHints]]rust-analyzer.inlayHints.typeHints (default: `true`):: | 220 | [[rust-analyzer.inlayHints.typeHints]]rust-analyzer.inlayHints.typeHints (default: `true`):: |
80 | Whether to show inlay type hints for variables. | 221 | + |
222 | -- | ||
223 | Whether to show inlay type hints for variables. | ||
224 | -- | ||
81 | [[rust-analyzer.lens.debug]]rust-analyzer.lens.debug (default: `true`):: | 225 | [[rust-analyzer.lens.debug]]rust-analyzer.lens.debug (default: `true`):: |
82 | Whether to show `Debug` lens. Only applies when `#rust-analyzer.lens.enable#` is set. | 226 | + |
227 | -- | ||
228 | Whether to show `Debug` lens. Only applies when | ||
229 | `#rust-analyzer.lens.enable#` is set. | ||
230 | -- | ||
83 | [[rust-analyzer.lens.enable]]rust-analyzer.lens.enable (default: `true`):: | 231 | [[rust-analyzer.lens.enable]]rust-analyzer.lens.enable (default: `true`):: |
84 | Whether to show CodeLens in Rust files. | 232 | + |
233 | -- | ||
234 | Whether to show CodeLens in Rust files. | ||
235 | -- | ||
85 | [[rust-analyzer.lens.implementations]]rust-analyzer.lens.implementations (default: `true`):: | 236 | [[rust-analyzer.lens.implementations]]rust-analyzer.lens.implementations (default: `true`):: |
86 | Whether to show `Implementations` lens. Only applies when `#rust-analyzer.lens.enable#` is set. | 237 | + |
238 | -- | ||
239 | Whether to show `Implementations` lens. Only applies when | ||
240 | `#rust-analyzer.lens.enable#` is set. | ||
241 | -- | ||
87 | [[rust-analyzer.lens.run]]rust-analyzer.lens.run (default: `true`):: | 242 | [[rust-analyzer.lens.run]]rust-analyzer.lens.run (default: `true`):: |
88 | Whether to show `Run` lens. Only applies when `#rust-analyzer.lens.enable#` is set. | 243 | + |
244 | -- | ||
245 | Whether to show `Run` lens. Only applies when | ||
246 | `#rust-analyzer.lens.enable#` is set. | ||
247 | -- | ||
89 | [[rust-analyzer.lens.methodReferences]]rust-analyzer.lens.methodReferences (default: `false`):: | 248 | [[rust-analyzer.lens.methodReferences]]rust-analyzer.lens.methodReferences (default: `false`):: |
90 | Whether to show `Method References` lens. Only applies when `#rust-analyzer.lens.enable#` is set. | 249 | + |
250 | -- | ||
251 | Whether to show `Method References` lens. Only applies when | ||
252 | `#rust-analyzer.lens.enable#` is set. | ||
253 | -- | ||
91 | [[rust-analyzer.lens.references]]rust-analyzer.lens.references (default: `false`):: | 254 | [[rust-analyzer.lens.references]]rust-analyzer.lens.references (default: `false`):: |
92 | Whether to show `References` lens. Only applies when `#rust-analyzer.lens.enable#` is set. | 255 | + |
256 | -- | ||
257 | Whether to show `References` lens. Only applies when | ||
258 | `#rust-analyzer.lens.enable#` is set. | ||
259 | -- | ||
93 | [[rust-analyzer.linkedProjects]]rust-analyzer.linkedProjects (default: `[]`):: | 260 | [[rust-analyzer.linkedProjects]]rust-analyzer.linkedProjects (default: `[]`):: |
94 | Disable project auto-discovery in favor of explicitly specified set of projects.\n\nElements must be paths pointing to `Cargo.toml`, `rust-project.json`, or JSON objects in `rust-project.json` format. | 261 | + |
262 | -- | ||
263 | Disable project auto-discovery in favor of explicitly specified set | ||
264 | of projects. | ||
265 | |||
266 | Elements must be paths pointing to `Cargo.toml`, | ||
267 | `rust-project.json`, or JSON objects in `rust-project.json` format. | ||
268 | -- | ||
95 | [[rust-analyzer.lruCapacity]]rust-analyzer.lruCapacity (default: `null`):: | 269 | [[rust-analyzer.lruCapacity]]rust-analyzer.lruCapacity (default: `null`):: |
96 | Number of syntax trees rust-analyzer keeps in memory. Defaults to 128. | 270 | + |
271 | -- | ||
272 | Number of syntax trees rust-analyzer keeps in memory. Defaults to 128. | ||
273 | -- | ||
97 | [[rust-analyzer.notifications.cargoTomlNotFound]]rust-analyzer.notifications.cargoTomlNotFound (default: `true`):: | 274 | [[rust-analyzer.notifications.cargoTomlNotFound]]rust-analyzer.notifications.cargoTomlNotFound (default: `true`):: |
98 | Whether to show `can't find Cargo.toml` error message. | 275 | + |
276 | -- | ||
277 | Whether to show `can't find Cargo.toml` error message. | ||
278 | -- | ||
99 | [[rust-analyzer.procMacro.enable]]rust-analyzer.procMacro.enable (default: `false`):: | 279 | [[rust-analyzer.procMacro.enable]]rust-analyzer.procMacro.enable (default: `false`):: |
100 | Enable support for procedural macros, implies `#rust-analyzer.cargo.runBuildScripts#`. | 280 | + |
281 | -- | ||
282 | Enable support for procedural macros, implies `#rust-analyzer.cargo.runBuildScripts#`. | ||
283 | -- | ||
101 | [[rust-analyzer.procMacro.server]]rust-analyzer.procMacro.server (default: `null`):: | 284 | [[rust-analyzer.procMacro.server]]rust-analyzer.procMacro.server (default: `null`):: |
102 | Internal config, path to proc-macro server executable (typically, this is rust-analyzer itself, but we override this in tests). | 285 | + |
286 | -- | ||
287 | Internal config, path to proc-macro server executable (typically, | ||
288 | this is rust-analyzer itself, but we override this in tests). | ||
289 | -- | ||
103 | [[rust-analyzer.runnables.overrideCargo]]rust-analyzer.runnables.overrideCargo (default: `null`):: | 290 | [[rust-analyzer.runnables.overrideCargo]]rust-analyzer.runnables.overrideCargo (default: `null`):: |
104 | Command to be executed instead of 'cargo' for runnables. | 291 | + |
292 | -- | ||
293 | Command to be executed instead of 'cargo' for runnables. | ||
294 | -- | ||
105 | [[rust-analyzer.runnables.cargoExtraArgs]]rust-analyzer.runnables.cargoExtraArgs (default: `[]`):: | 295 | [[rust-analyzer.runnables.cargoExtraArgs]]rust-analyzer.runnables.cargoExtraArgs (default: `[]`):: |
106 | Additional arguments to be passed to cargo for runnables such as tests or binaries.\nFor example, it may be `--release`. | 296 | + |
297 | -- | ||
298 | Additional arguments to be passed to cargo for runnables such as | ||
299 | tests or binaries. For example, it may be `--release`. | ||
300 | -- | ||
107 | [[rust-analyzer.rustcSource]]rust-analyzer.rustcSource (default: `null`):: | 301 | [[rust-analyzer.rustcSource]]rust-analyzer.rustcSource (default: `null`):: |
108 | Path to the rust compiler sources, for usage in rustc_private projects, or "discover" to try to automatically find it. | 302 | + |
303 | -- | ||
304 | Path to the Cargo.toml of the rust compiler workspace, for usage in rustc_private | ||
305 | projects, or "discover" to try to automatically find it. | ||
306 | |||
307 | Any project which uses rust-analyzer with the rustcPrivate | ||
308 | crates must set `[package.metadata.rust-analyzer] rustc_private=true` to use it. | ||
309 | |||
310 | This option is not reloaded automatically; you must restart rust-analyzer for it to take effect. | ||
311 | -- | ||
109 | [[rust-analyzer.rustfmt.extraArgs]]rust-analyzer.rustfmt.extraArgs (default: `[]`):: | 312 | [[rust-analyzer.rustfmt.extraArgs]]rust-analyzer.rustfmt.extraArgs (default: `[]`):: |
110 | Additional arguments to `rustfmt`. | 313 | + |
314 | -- | ||
315 | Additional arguments to `rustfmt`. | ||
316 | -- | ||
111 | [[rust-analyzer.rustfmt.overrideCommand]]rust-analyzer.rustfmt.overrideCommand (default: `null`):: | 317 | [[rust-analyzer.rustfmt.overrideCommand]]rust-analyzer.rustfmt.overrideCommand (default: `null`):: |
112 | Advanced option, fully override the command rust-analyzer uses for formatting. | 318 | + |
319 | -- | ||
320 | Advanced option, fully override the command rust-analyzer uses for | ||
321 | formatting. | ||
322 | -- | ||
diff --git a/docs/user/manual.adoc b/docs/user/manual.adoc index 4f2217546..f4c37353c 100644 --- a/docs/user/manual.adoc +++ b/docs/user/manual.adoc | |||
@@ -19,7 +19,7 @@ The LSP allows various code editors, like VS Code, Emacs or Vim, to implement se | |||
19 | To improve this document, send a pull request: + | 19 | To improve this document, send a pull request: + |
20 | https://github.com/rust-analyzer/rust-analyzer/blob/master/docs/user/manual.adoc[https://github.com/rust-analyzer/.../manual.adoc] | 20 | https://github.com/rust-analyzer/rust-analyzer/blob/master/docs/user/manual.adoc[https://github.com/rust-analyzer/.../manual.adoc] |
21 | 21 | ||
22 | The manual is written in https://asciidoc.org[AsciiDoc] and includes some extra files which are generated from the source code. Run `cargo test` and `cargo xtask codegen` to create these and then `asciidoctor manual.adoc` to create an HTML copy. | 22 | The manual is written in https://asciidoc.org[AsciiDoc] and includes some extra files which are generated from the source code. Run `cargo test` and `cargo test -p xtask` to create these and then `asciidoctor manual.adoc` to create an HTML copy. |
23 | 23 | ||
24 | ==== | 24 | ==== |
25 | 25 | ||
diff --git a/editors/code/package-lock.json b/editors/code/package-lock.json index 9d0d1d4ec..198c17556 100644 --- a/editors/code/package-lock.json +++ b/editors/code/package-lock.json | |||
@@ -9,6 +9,7 @@ | |||
9 | "version": "0.4.0-dev", | 9 | "version": "0.4.0-dev", |
10 | "license": "MIT OR Apache-2.0", | 10 | "license": "MIT OR Apache-2.0", |
11 | "dependencies": { | 11 | "dependencies": { |
12 | "https-proxy-agent": "^5.0.0", | ||
12 | "node-fetch": "^2.6.1", | 13 | "node-fetch": "^2.6.1", |
13 | "vscode-languageclient": "^7.1.0-next.4" | 14 | "vscode-languageclient": "^7.1.0-next.4" |
14 | }, | 15 | }, |
@@ -515,7 +516,6 @@ | |||
515 | "version": "6.0.2", | 516 | "version": "6.0.2", |
516 | "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", | 517 | "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", |
517 | "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", | 518 | "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", |
518 | "dev": true, | ||
519 | "dependencies": { | 519 | "dependencies": { |
520 | "debug": "4" | 520 | "debug": "4" |
521 | }, | 521 | }, |
@@ -830,6 +830,7 @@ | |||
830 | "dependencies": { | 830 | "dependencies": { |
831 | "anymatch": "~3.1.1", | 831 | "anymatch": "~3.1.1", |
832 | "braces": "~3.0.2", | 832 | "braces": "~3.0.2", |
833 | "fsevents": "~2.3.1", | ||
833 | "glob-parent": "~5.1.0", | 834 | "glob-parent": "~5.1.0", |
834 | "is-binary-path": "~2.1.0", | 835 | "is-binary-path": "~2.1.0", |
835 | "is-glob": "~4.0.1", | 836 | "is-glob": "~4.0.1", |
@@ -959,7 +960,6 @@ | |||
959 | "version": "4.3.1", | 960 | "version": "4.3.1", |
960 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", | 961 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", |
961 | "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", | 962 | "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", |
962 | "dev": true, | ||
963 | "dependencies": { | 963 | "dependencies": { |
964 | "ms": "2.1.2" | 964 | "ms": "2.1.2" |
965 | }, | 965 | }, |
@@ -1759,7 +1759,6 @@ | |||
1759 | "version": "5.0.0", | 1759 | "version": "5.0.0", |
1760 | "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", | 1760 | "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", |
1761 | "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", | 1761 | "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", |
1762 | "dev": true, | ||
1763 | "dependencies": { | 1762 | "dependencies": { |
1764 | "agent-base": "6", | 1763 | "agent-base": "6", |
1765 | "debug": "4" | 1764 | "debug": "4" |
@@ -2236,8 +2235,7 @@ | |||
2236 | "node_modules/ms": { | 2235 | "node_modules/ms": { |
2237 | "version": "2.1.2", | 2236 | "version": "2.1.2", |
2238 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", | 2237 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", |
2239 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", | 2238 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" |
2240 | "dev": true | ||
2241 | }, | 2239 | }, |
2242 | "node_modules/mute-stream": { | 2240 | "node_modules/mute-stream": { |
2243 | "version": "0.0.8", | 2241 | "version": "0.0.8", |
@@ -2682,6 +2680,9 @@ | |||
2682 | "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.39.1.tgz", | 2680 | "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.39.1.tgz", |
2683 | "integrity": "sha512-9rfr0Z6j+vE+eayfNVFr1KZ+k+jiUl2+0e4quZafy1x6SFCjzFspfRSO2ZZQeWeX9noeDTUDgg6eCENiEPFvQg==", | 2681 | "integrity": "sha512-9rfr0Z6j+vE+eayfNVFr1KZ+k+jiUl2+0e4quZafy1x6SFCjzFspfRSO2ZZQeWeX9noeDTUDgg6eCENiEPFvQg==", |
2684 | "dev": true, | 2682 | "dev": true, |
2683 | "dependencies": { | ||
2684 | "fsevents": "~2.3.1" | ||
2685 | }, | ||
2685 | "bin": { | 2686 | "bin": { |
2686 | "rollup": "dist/bin/rollup" | 2687 | "rollup": "dist/bin/rollup" |
2687 | }, | 2688 | }, |
@@ -3843,7 +3844,6 @@ | |||
3843 | "version": "6.0.2", | 3844 | "version": "6.0.2", |
3844 | "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", | 3845 | "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", |
3845 | "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", | 3846 | "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", |
3846 | "dev": true, | ||
3847 | "requires": { | 3847 | "requires": { |
3848 | "debug": "4" | 3848 | "debug": "4" |
3849 | } | 3849 | } |
@@ -4190,7 +4190,6 @@ | |||
4190 | "version": "4.3.1", | 4190 | "version": "4.3.1", |
4191 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", | 4191 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", |
4192 | "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", | 4192 | "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", |
4193 | "dev": true, | ||
4194 | "requires": { | 4193 | "requires": { |
4195 | "ms": "2.1.2" | 4194 | "ms": "2.1.2" |
4196 | } | 4195 | } |
@@ -4798,7 +4797,6 @@ | |||
4798 | "version": "5.0.0", | 4797 | "version": "5.0.0", |
4799 | "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", | 4798 | "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", |
4800 | "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", | 4799 | "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", |
4801 | "dev": true, | ||
4802 | "requires": { | 4800 | "requires": { |
4803 | "agent-base": "6", | 4801 | "agent-base": "6", |
4804 | "debug": "4" | 4802 | "debug": "4" |
@@ -5175,8 +5173,7 @@ | |||
5175 | "ms": { | 5173 | "ms": { |
5176 | "version": "2.1.2", | 5174 | "version": "2.1.2", |
5177 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", | 5175 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", |
5178 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", | 5176 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" |
5179 | "dev": true | ||
5180 | }, | 5177 | }, |
5181 | "mute-stream": { | 5178 | "mute-stream": { |
5182 | "version": "0.0.8", | 5179 | "version": "0.0.8", |
diff --git a/editors/code/package.json b/editors/code/package.json index 1987364bc..b29f006f0 100644 --- a/editors/code/package.json +++ b/editors/code/package.json | |||
@@ -35,6 +35,7 @@ | |||
35 | "test": "node ./out/tests/runTests.js" | 35 | "test": "node ./out/tests/runTests.js" |
36 | }, | 36 | }, |
37 | "dependencies": { | 37 | "dependencies": { |
38 | "https-proxy-agent": "^5.0.0", | ||
38 | "node-fetch": "^2.6.1", | 39 | "node-fetch": "^2.6.1", |
39 | "vscode-languageclient": "^7.1.0-next.4" | 40 | "vscode-languageclient": "^7.1.0-next.4" |
40 | }, | 41 | }, |
@@ -385,13 +386,18 @@ | |||
385 | "Force import paths to be absolute by always starting them with `crate` or the crate name they refer to." | 386 | "Force import paths to be absolute by always starting them with `crate` or the crate name they refer to." |
386 | ] | 387 | ] |
387 | }, | 388 | }, |
389 | "rust-analyzer.assist.importGroup": { | ||
390 | "markdownDescription": "Group inserted imports by the [following order](https://rust-analyzer.github.io/manual.html#auto-import). Groups are separated by newlines.", | ||
391 | "default": true, | ||
392 | "type": "boolean" | ||
393 | }, | ||
388 | "rust-analyzer.callInfo.full": { | 394 | "rust-analyzer.callInfo.full": { |
389 | "markdownDescription": "Show function name and docs in parameter hints.", | 395 | "markdownDescription": "Show function name and docs in parameter hints.", |
390 | "default": true, | 396 | "default": true, |
391 | "type": "boolean" | 397 | "type": "boolean" |
392 | }, | 398 | }, |
393 | "rust-analyzer.cargo.autoreload": { | 399 | "rust-analyzer.cargo.autoreload": { |
394 | "markdownDescription": "Automatically refresh project info via `cargo metadata` on `Cargo.toml` changes.", | 400 | "markdownDescription": "Automatically refresh project info via `cargo metadata` on\n`Cargo.toml` changes.", |
395 | "default": true, | 401 | "default": true, |
396 | "type": "boolean" | 402 | "type": "boolean" |
397 | }, | 403 | }, |
@@ -410,7 +416,7 @@ | |||
410 | }, | 416 | }, |
411 | "rust-analyzer.cargo.runBuildScripts": { | 417 | "rust-analyzer.cargo.runBuildScripts": { |
412 | "markdownDescription": "Run build scripts (`build.rs`) for more precise code analysis.", | 418 | "markdownDescription": "Run build scripts (`build.rs`) for more precise code analysis.", |
413 | "default": false, | 419 | "default": true, |
414 | "type": "boolean" | 420 | "type": "boolean" |
415 | }, | 421 | }, |
416 | "rust-analyzer.cargo.noDefaultFeatures": { | 422 | "rust-analyzer.cargo.noDefaultFeatures": { |
@@ -437,7 +443,7 @@ | |||
437 | "type": "boolean" | 443 | "type": "boolean" |
438 | }, | 444 | }, |
439 | "rust-analyzer.checkOnSave.allFeatures": { | 445 | "rust-analyzer.checkOnSave.allFeatures": { |
440 | "markdownDescription": "Check with all features (`--all-features`). Defaults to `#rust-analyzer.cargo.allFeatures#`.", | 446 | "markdownDescription": "Check with all features (`--all-features`).\nDefaults to `#rust-analyzer.cargo.allFeatures#`.", |
441 | "default": null, | 447 | "default": null, |
442 | "type": [ | 448 | "type": [ |
443 | "null", | 449 | "null", |
@@ -463,7 +469,7 @@ | |||
463 | ] | 469 | ] |
464 | }, | 470 | }, |
465 | "rust-analyzer.checkOnSave.target": { | 471 | "rust-analyzer.checkOnSave.target": { |
466 | "markdownDescription": "Check for a specific target. Defaults to `#rust-analyzer.cargo.target#`.", | 472 | "markdownDescription": "Check for a specific target. Defaults to\n`#rust-analyzer.cargo.target#`.", |
467 | "default": null, | 473 | "default": null, |
468 | "type": [ | 474 | "type": [ |
469 | "null", | 475 | "null", |
@@ -479,7 +485,7 @@ | |||
479 | } | 485 | } |
480 | }, | 486 | }, |
481 | "rust-analyzer.checkOnSave.features": { | 487 | "rust-analyzer.checkOnSave.features": { |
482 | "markdownDescription": "List of features to activate. Defaults to `#rust-analyzer.cargo.features#`.", | 488 | "markdownDescription": "List of features to activate. Defaults to\n`#rust-analyzer.cargo.features#`.", |
483 | "default": null, | 489 | "default": null, |
484 | "type": [ | 490 | "type": [ |
485 | "null", | 491 | "null", |
@@ -490,7 +496,7 @@ | |||
490 | } | 496 | } |
491 | }, | 497 | }, |
492 | "rust-analyzer.checkOnSave.overrideCommand": { | 498 | "rust-analyzer.checkOnSave.overrideCommand": { |
493 | "markdownDescription": "Advanced option, fully override the command rust-analyzer uses for checking. The command should include `--message-format=json` or similar option.", | 499 | "markdownDescription": "Advanced option, fully override the command rust-analyzer uses for\nchecking. The command should include `--message-format=json` or\nsimilar option.", |
494 | "default": null, | 500 | "default": null, |
495 | "type": [ | 501 | "type": [ |
496 | "null", | 502 | "null", |
@@ -516,7 +522,7 @@ | |||
516 | "type": "boolean" | 522 | "type": "boolean" |
517 | }, | 523 | }, |
518 | "rust-analyzer.completion.autoimport.enable": { | 524 | "rust-analyzer.completion.autoimport.enable": { |
519 | "markdownDescription": "Toggles the additional completions that automatically add imports when completed. Note that your client must specify the `additionalTextEdits` LSP client capability to truly have this feature enabled.", | 525 | "markdownDescription": "Toggles the additional completions that automatically add imports when completed.\nNote that your client must specify the `additionalTextEdits` LSP client capability to truly have this feature enabled.", |
520 | "default": true, | 526 | "default": true, |
521 | "type": "boolean" | 527 | "type": "boolean" |
522 | }, | 528 | }, |
@@ -526,7 +532,7 @@ | |||
526 | "type": "boolean" | 532 | "type": "boolean" |
527 | }, | 533 | }, |
528 | "rust-analyzer.diagnostics.enableExperimental": { | 534 | "rust-analyzer.diagnostics.enableExperimental": { |
529 | "markdownDescription": "Whether to show experimental rust-analyzer diagnostics that might have more false positives than usual.", | 535 | "markdownDescription": "Whether to show experimental rust-analyzer diagnostics that might\nhave more false positives than usual.", |
530 | "default": true, | 536 | "default": true, |
531 | "type": "boolean" | 537 | "type": "boolean" |
532 | }, | 538 | }, |
@@ -540,7 +546,7 @@ | |||
540 | "uniqueItems": true | 546 | "uniqueItems": true |
541 | }, | 547 | }, |
542 | "rust-analyzer.diagnostics.warningsAsHint": { | 548 | "rust-analyzer.diagnostics.warningsAsHint": { |
543 | "markdownDescription": "List of warnings that should be displayed with info severity.\\n\\nThe warnings will be indicated by a blue squiggly underline in code and a blue icon in the `Problems Panel`.", | 549 | "markdownDescription": "List of warnings that should be displayed with info severity.\n\nThe warnings will be indicated by a blue squiggly underline in code\nand a blue icon in the `Problems Panel`.", |
544 | "default": [], | 550 | "default": [], |
545 | "type": "array", | 551 | "type": "array", |
546 | "items": { | 552 | "items": { |
@@ -548,7 +554,7 @@ | |||
548 | } | 554 | } |
549 | }, | 555 | }, |
550 | "rust-analyzer.diagnostics.warningsAsInfo": { | 556 | "rust-analyzer.diagnostics.warningsAsInfo": { |
551 | "markdownDescription": "List of warnings that should be displayed with hint severity.\\n\\nThe warnings will be indicated by faded text or three dots in code and will not show up in the `Problems Panel`.", | 557 | "markdownDescription": "List of warnings that should be displayed with hint severity.\n\nThe warnings will be indicated by faded text or three dots in code\nand will not show up in the `Problems Panel`.", |
552 | "default": [], | 558 | "default": [], |
553 | "type": "array", | 559 | "type": "array", |
554 | "items": { | 560 | "items": { |
@@ -569,7 +575,7 @@ | |||
569 | } | 575 | } |
570 | }, | 576 | }, |
571 | "rust-analyzer.hoverActions.debug": { | 577 | "rust-analyzer.hoverActions.debug": { |
572 | "markdownDescription": "Whether to show `Debug` action. Only applies when `#rust-analyzer.hoverActions.enable#` is set.", | 578 | "markdownDescription": "Whether to show `Debug` action. Only applies when\n`#rust-analyzer.hoverActions.enable#` is set.", |
573 | "default": true, | 579 | "default": true, |
574 | "type": "boolean" | 580 | "type": "boolean" |
575 | }, | 581 | }, |
@@ -579,17 +585,17 @@ | |||
579 | "type": "boolean" | 585 | "type": "boolean" |
580 | }, | 586 | }, |
581 | "rust-analyzer.hoverActions.gotoTypeDef": { | 587 | "rust-analyzer.hoverActions.gotoTypeDef": { |
582 | "markdownDescription": "Whether to show `Go to Type Definition` action. Only applies when `#rust-analyzer.hoverActions.enable#` is set.", | 588 | "markdownDescription": "Whether to show `Go to Type Definition` action. Only applies when\n`#rust-analyzer.hoverActions.enable#` is set.", |
583 | "default": true, | 589 | "default": true, |
584 | "type": "boolean" | 590 | "type": "boolean" |
585 | }, | 591 | }, |
586 | "rust-analyzer.hoverActions.implementations": { | 592 | "rust-analyzer.hoverActions.implementations": { |
587 | "markdownDescription": "Whether to show `Implementations` action. Only applies when `#rust-analyzer.hoverActions.enable#` is set.", | 593 | "markdownDescription": "Whether to show `Implementations` action. Only applies when\n`#rust-analyzer.hoverActions.enable#` is set.", |
588 | "default": true, | 594 | "default": true, |
589 | "type": "boolean" | 595 | "type": "boolean" |
590 | }, | 596 | }, |
591 | "rust-analyzer.hoverActions.run": { | 597 | "rust-analyzer.hoverActions.run": { |
592 | "markdownDescription": "Whether to show `Run` action. Only applies when `#rust-analyzer.hoverActions.enable#` is set.", | 598 | "markdownDescription": "Whether to show `Run` action. Only applies when\n`#rust-analyzer.hoverActions.enable#` is set.", |
593 | "default": true, | 599 | "default": true, |
594 | "type": "boolean" | 600 | "type": "boolean" |
595 | }, | 601 | }, |
@@ -613,7 +619,7 @@ | |||
613 | "minimum": 0 | 619 | "minimum": 0 |
614 | }, | 620 | }, |
615 | "rust-analyzer.inlayHints.parameterHints": { | 621 | "rust-analyzer.inlayHints.parameterHints": { |
616 | "markdownDescription": "Whether to show function parameter name inlay hints at the call site.", | 622 | "markdownDescription": "Whether to show function parameter name inlay hints at the call\nsite.", |
617 | "default": true, | 623 | "default": true, |
618 | "type": "boolean" | 624 | "type": "boolean" |
619 | }, | 625 | }, |
@@ -623,7 +629,7 @@ | |||
623 | "type": "boolean" | 629 | "type": "boolean" |
624 | }, | 630 | }, |
625 | "rust-analyzer.lens.debug": { | 631 | "rust-analyzer.lens.debug": { |
626 | "markdownDescription": "Whether to show `Debug` lens. Only applies when `#rust-analyzer.lens.enable#` is set.", | 632 | "markdownDescription": "Whether to show `Debug` lens. Only applies when\n`#rust-analyzer.lens.enable#` is set.", |
627 | "default": true, | 633 | "default": true, |
628 | "type": "boolean" | 634 | "type": "boolean" |
629 | }, | 635 | }, |
@@ -633,27 +639,27 @@ | |||
633 | "type": "boolean" | 639 | "type": "boolean" |
634 | }, | 640 | }, |
635 | "rust-analyzer.lens.implementations": { | 641 | "rust-analyzer.lens.implementations": { |
636 | "markdownDescription": "Whether to show `Implementations` lens. Only applies when `#rust-analyzer.lens.enable#` is set.", | 642 | "markdownDescription": "Whether to show `Implementations` lens. Only applies when\n`#rust-analyzer.lens.enable#` is set.", |
637 | "default": true, | 643 | "default": true, |
638 | "type": "boolean" | 644 | "type": "boolean" |
639 | }, | 645 | }, |
640 | "rust-analyzer.lens.run": { | 646 | "rust-analyzer.lens.run": { |
641 | "markdownDescription": "Whether to show `Run` lens. Only applies when `#rust-analyzer.lens.enable#` is set.", | 647 | "markdownDescription": "Whether to show `Run` lens. Only applies when\n`#rust-analyzer.lens.enable#` is set.", |
642 | "default": true, | 648 | "default": true, |
643 | "type": "boolean" | 649 | "type": "boolean" |
644 | }, | 650 | }, |
645 | "rust-analyzer.lens.methodReferences": { | 651 | "rust-analyzer.lens.methodReferences": { |
646 | "markdownDescription": "Whether to show `Method References` lens. Only applies when `#rust-analyzer.lens.enable#` is set.", | 652 | "markdownDescription": "Whether to show `Method References` lens. Only applies when\n`#rust-analyzer.lens.enable#` is set.", |
647 | "default": false, | 653 | "default": false, |
648 | "type": "boolean" | 654 | "type": "boolean" |
649 | }, | 655 | }, |
650 | "rust-analyzer.lens.references": { | 656 | "rust-analyzer.lens.references": { |
651 | "markdownDescription": "Whether to show `References` lens. Only applies when `#rust-analyzer.lens.enable#` is set.", | 657 | "markdownDescription": "Whether to show `References` lens. Only applies when\n`#rust-analyzer.lens.enable#` is set.", |
652 | "default": false, | 658 | "default": false, |
653 | "type": "boolean" | 659 | "type": "boolean" |
654 | }, | 660 | }, |
655 | "rust-analyzer.linkedProjects": { | 661 | "rust-analyzer.linkedProjects": { |
656 | "markdownDescription": "Disable project auto-discovery in favor of explicitly specified set of projects.\\n\\nElements must be paths pointing to `Cargo.toml`, `rust-project.json`, or JSON objects in `rust-project.json` format.", | 662 | "markdownDescription": "Disable project auto-discovery in favor of explicitly specified set\nof projects.\n\nElements must be paths pointing to `Cargo.toml`,\n`rust-project.json`, or JSON objects in `rust-project.json` format.", |
657 | "default": [], | 663 | "default": [], |
658 | "type": "array", | 664 | "type": "array", |
659 | "items": { | 665 | "items": { |
@@ -683,7 +689,7 @@ | |||
683 | "type": "boolean" | 689 | "type": "boolean" |
684 | }, | 690 | }, |
685 | "rust-analyzer.procMacro.server": { | 691 | "rust-analyzer.procMacro.server": { |
686 | "markdownDescription": "Internal config, path to proc-macro server executable (typically, this is rust-analyzer itself, but we override this in tests).", | 692 | "markdownDescription": "Internal config, path to proc-macro server executable (typically,\nthis is rust-analyzer itself, but we override this in tests).", |
687 | "default": null, | 693 | "default": null, |
688 | "type": [ | 694 | "type": [ |
689 | "null", | 695 | "null", |
@@ -699,7 +705,7 @@ | |||
699 | ] | 705 | ] |
700 | }, | 706 | }, |
701 | "rust-analyzer.runnables.cargoExtraArgs": { | 707 | "rust-analyzer.runnables.cargoExtraArgs": { |
702 | "markdownDescription": "Additional arguments to be passed to cargo for runnables such as tests or binaries.\\nFor example, it may be `--release`.", | 708 | "markdownDescription": "Additional arguments to be passed to cargo for runnables such as\ntests or binaries. For example, it may be `--release`.", |
703 | "default": [], | 709 | "default": [], |
704 | "type": "array", | 710 | "type": "array", |
705 | "items": { | 711 | "items": { |
@@ -707,7 +713,7 @@ | |||
707 | } | 713 | } |
708 | }, | 714 | }, |
709 | "rust-analyzer.rustcSource": { | 715 | "rust-analyzer.rustcSource": { |
710 | "markdownDescription": "Path to the rust compiler sources, for usage in rustc_private projects, or \"discover\" to try to automatically find it.", | 716 | "markdownDescription": "Path to the Cargo.toml of the rust compiler workspace, for usage in rustc_private\nprojects, or \"discover\" to try to automatically find it.\n\nAny project which uses rust-analyzer with the rustcPrivate\ncrates must set `[package.metadata.rust-analyzer] rustc_private=true` to use it.\n\nThis option is not reloaded automatically; you must restart rust-analyzer for it to take effect.", |
711 | "default": null, | 717 | "default": null, |
712 | "type": [ | 718 | "type": [ |
713 | "null", | 719 | "null", |
@@ -723,7 +729,7 @@ | |||
723 | } | 729 | } |
724 | }, | 730 | }, |
725 | "rust-analyzer.rustfmt.overrideCommand": { | 731 | "rust-analyzer.rustfmt.overrideCommand": { |
726 | "markdownDescription": "Advanced option, fully override the command rust-analyzer uses for formatting.", | 732 | "markdownDescription": "Advanced option, fully override the command rust-analyzer uses for\nformatting.", |
727 | "default": null, | 733 | "default": null, |
728 | "type": [ | 734 | "type": [ |
729 | "null", | 735 | "null", |
diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts index ddb5cfbd3..82f0a0566 100644 --- a/editors/code/src/config.ts +++ b/editors/code/src/config.ts | |||
@@ -100,6 +100,14 @@ export class Config { | |||
100 | get channel() { return this.get<UpdatesChannel>("updates.channel"); } | 100 | get channel() { return this.get<UpdatesChannel>("updates.channel"); } |
101 | get askBeforeDownload() { return this.get<boolean>("updates.askBeforeDownload"); } | 101 | get askBeforeDownload() { return this.get<boolean>("updates.askBeforeDownload"); } |
102 | get traceExtension() { return this.get<boolean>("trace.extension"); } | 102 | get traceExtension() { return this.get<boolean>("trace.extension"); } |
103 | get httpProxy() { | ||
104 | const httpProxy = vscode | ||
105 | .workspace | ||
106 | .getConfiguration('http') | ||
107 | .get<null | string>("proxy")!; | ||
108 | |||
109 | return httpProxy || process.env["https_proxy"] || process.env["HTTPS_PROXY"]; | ||
110 | } | ||
103 | 111 | ||
104 | get inlayHints() { | 112 | get inlayHints() { |
105 | return { | 113 | return { |
diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts index 00393d6e8..1be4f1758 100644 --- a/editors/code/src/main.ts +++ b/editors/code/src/main.ts | |||
@@ -183,7 +183,7 @@ async function bootstrapExtension(config: Config, state: PersistentState): Promi | |||
183 | } | 183 | } |
184 | 184 | ||
185 | const release = await downloadWithRetryDialog(state, async () => { | 185 | const release = await downloadWithRetryDialog(state, async () => { |
186 | return await fetchRelease("nightly", state.githubToken); | 186 | return await fetchRelease("nightly", state.githubToken, config.httpProxy); |
187 | }).catch(async (e) => { | 187 | }).catch(async (e) => { |
188 | log.error(e); | 188 | log.error(e); |
189 | if (state.releaseId === undefined) { // Show error only for the initial download | 189 | if (state.releaseId === undefined) { // Show error only for the initial download |
@@ -209,6 +209,7 @@ async function bootstrapExtension(config: Config, state: PersistentState): Promi | |||
209 | url: artifact.browser_download_url, | 209 | url: artifact.browser_download_url, |
210 | dest, | 210 | dest, |
211 | progressTitle: "Downloading rust-analyzer extension", | 211 | progressTitle: "Downloading rust-analyzer extension", |
212 | httpProxy: config.httpProxy, | ||
212 | }); | 213 | }); |
213 | }); | 214 | }); |
214 | 215 | ||
@@ -331,7 +332,7 @@ async function getServer(config: Config, state: PersistentState): Promise<string | |||
331 | 332 | ||
332 | const releaseTag = config.package.releaseTag; | 333 | const releaseTag = config.package.releaseTag; |
333 | const release = await downloadWithRetryDialog(state, async () => { | 334 | const release = await downloadWithRetryDialog(state, async () => { |
334 | return await fetchRelease(releaseTag, state.githubToken); | 335 | return await fetchRelease(releaseTag, state.githubToken, config.httpProxy); |
335 | }); | 336 | }); |
336 | const artifact = release.assets.find(artifact => artifact.name === `rust-analyzer-${platform}.gz`); | 337 | const artifact = release.assets.find(artifact => artifact.name === `rust-analyzer-${platform}.gz`); |
337 | assert(!!artifact, `Bad release: ${JSON.stringify(release)}`); | 338 | assert(!!artifact, `Bad release: ${JSON.stringify(release)}`); |
@@ -343,6 +344,7 @@ async function getServer(config: Config, state: PersistentState): Promise<string | |||
343 | progressTitle: "Downloading rust-analyzer server", | 344 | progressTitle: "Downloading rust-analyzer server", |
344 | gunzip: true, | 345 | gunzip: true, |
345 | mode: 0o755, | 346 | mode: 0o755, |
347 | httpProxy: config.httpProxy, | ||
346 | }); | 348 | }); |
347 | }); | 349 | }); |
348 | 350 | ||
diff --git a/editors/code/src/net.ts b/editors/code/src/net.ts index d39dc1baf..07ebc615c 100644 --- a/editors/code/src/net.ts +++ b/editors/code/src/net.ts | |||
@@ -1,4 +1,6 @@ | |||
1 | import fetch from "node-fetch"; | 1 | import fetch from "node-fetch"; |
2 | var HttpsProxyAgent = require('https-proxy-agent'); | ||
3 | |||
2 | import * as vscode from "vscode"; | 4 | import * as vscode from "vscode"; |
3 | import * as stream from "stream"; | 5 | import * as stream from "stream"; |
4 | import * as crypto from "crypto"; | 6 | import * as crypto from "crypto"; |
@@ -17,6 +19,7 @@ const REPO = "rust-analyzer"; | |||
17 | export async function fetchRelease( | 19 | export async function fetchRelease( |
18 | releaseTag: string, | 20 | releaseTag: string, |
19 | githubToken: string | null | undefined, | 21 | githubToken: string | null | undefined, |
22 | httpProxy: string | null | undefined, | ||
20 | ): Promise<GithubRelease> { | 23 | ): Promise<GithubRelease> { |
21 | 24 | ||
22 | const apiEndpointPath = `/repos/${OWNER}/${REPO}/releases/tags/${releaseTag}`; | 25 | const apiEndpointPath = `/repos/${OWNER}/${REPO}/releases/tags/${releaseTag}`; |
@@ -30,7 +33,14 @@ export async function fetchRelease( | |||
30 | headers.Authorization = "token " + githubToken; | 33 | headers.Authorization = "token " + githubToken; |
31 | } | 34 | } |
32 | 35 | ||
33 | const response = await fetch(requestUrl, { headers: headers }); | 36 | const response = await (() => { |
37 | if (httpProxy) { | ||
38 | log.debug(`Fetching release metadata via proxy: ${httpProxy}`); | ||
39 | return fetch(requestUrl, { headers: headers, agent: new HttpsProxyAgent(httpProxy) }); | ||
40 | } | ||
41 | |||
42 | return fetch(requestUrl, { headers: headers }); | ||
43 | })(); | ||
34 | 44 | ||
35 | if (!response.ok) { | 45 | if (!response.ok) { |
36 | log.error("Error fetching artifact release info", { | 46 | log.error("Error fetching artifact release info", { |
@@ -73,6 +83,7 @@ interface DownloadOpts { | |||
73 | dest: string; | 83 | dest: string; |
74 | mode?: number; | 84 | mode?: number; |
75 | gunzip?: boolean; | 85 | gunzip?: boolean; |
86 | httpProxy?: string; | ||
76 | } | 87 | } |
77 | 88 | ||
78 | export async function download(opts: DownloadOpts) { | 89 | export async function download(opts: DownloadOpts) { |
@@ -91,7 +102,7 @@ export async function download(opts: DownloadOpts) { | |||
91 | }, | 102 | }, |
92 | async (progress, _cancellationToken) => { | 103 | async (progress, _cancellationToken) => { |
93 | let lastPercentage = 0; | 104 | let lastPercentage = 0; |
94 | await downloadFile(opts.url, tempFile, opts.mode, !!opts.gunzip, (readBytes, totalBytes) => { | 105 | await downloadFile(opts.url, tempFile, opts.mode, !!opts.gunzip, opts.httpProxy, (readBytes, totalBytes) => { |
95 | const newPercentage = Math.round((readBytes / totalBytes) * 100); | 106 | const newPercentage = Math.round((readBytes / totalBytes) * 100); |
96 | if (newPercentage !== lastPercentage) { | 107 | if (newPercentage !== lastPercentage) { |
97 | progress.report({ | 108 | progress.report({ |
@@ -113,9 +124,17 @@ async function downloadFile( | |||
113 | destFilePath: fs.PathLike, | 124 | destFilePath: fs.PathLike, |
114 | mode: number | undefined, | 125 | mode: number | undefined, |
115 | gunzip: boolean, | 126 | gunzip: boolean, |
127 | httpProxy: string | null | undefined, | ||
116 | onProgress: (readBytes: number, totalBytes: number) => void | 128 | onProgress: (readBytes: number, totalBytes: number) => void |
117 | ): Promise<void> { | 129 | ): Promise<void> { |
118 | const res = await fetch(url); | 130 | const res = await (() => { |
131 | if (httpProxy) { | ||
132 | log.debug(`Downloading ${url} via proxy: ${httpProxy}`); | ||
133 | return fetch(url, { agent: new HttpsProxyAgent(httpProxy) }); | ||
134 | } | ||
135 | |||
136 | return fetch(url); | ||
137 | })(); | ||
119 | 138 | ||
120 | if (!res.ok) { | 139 | if (!res.ok) { |
121 | log.error("Error", res.status, "while downloading file from", url); | 140 | log.error("Error", res.status, "while downloading file from", url); |
diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml index b17dde598..e084f0df6 100644 --- a/xtask/Cargo.toml +++ b/xtask/Cargo.toml | |||
@@ -15,5 +15,5 @@ ungrammar = "=1.11" | |||
15 | walkdir = "2.3.1" | 15 | walkdir = "2.3.1" |
16 | write-json = "0.1.0" | 16 | write-json = "0.1.0" |
17 | xshell = "0.1" | 17 | xshell = "0.1" |
18 | xflags = "0.1.2" | 18 | xflags = "0.2.1" |
19 | # Avoid adding more dependencies to this crate | 19 | # Avoid adding more dependencies to this crate |
diff --git a/xtask/src/codegen.rs b/xtask/src/codegen.rs index 2f56c5ad0..518e17e38 100644 --- a/xtask/src/codegen.rs +++ b/xtask/src/codegen.rs | |||
@@ -7,68 +7,66 @@ | |||
7 | 7 | ||
8 | mod gen_syntax; | 8 | mod gen_syntax; |
9 | mod gen_parser_tests; | 9 | mod gen_parser_tests; |
10 | mod gen_lint_completions; | ||
10 | mod gen_assists_docs; | 11 | mod gen_assists_docs; |
11 | mod gen_feature_docs; | 12 | mod gen_feature_docs; |
12 | mod gen_lint_completions; | ||
13 | mod gen_diagnostic_docs; | 13 | mod gen_diagnostic_docs; |
14 | 14 | ||
15 | use std::{ | 15 | use std::{ |
16 | fmt, mem, | 16 | fmt, mem, |
17 | path::{Path, PathBuf}, | 17 | path::{Path, PathBuf}, |
18 | }; | 18 | }; |
19 | use xshell::{cmd, pushenv, read_file, write_file}; | 19 | use xshell::{cmd, pushenv}; |
20 | 20 | ||
21 | use crate::{ensure_rustfmt, flags, project_root, Result}; | 21 | use crate::{ensure_rustfmt, project_root, Result}; |
22 | 22 | ||
23 | pub(crate) use self::{ | 23 | pub(crate) use self::{ |
24 | gen_assists_docs::{generate_assists_docs, generate_assists_tests}, | 24 | gen_assists_docs::generate_assists_tests, gen_lint_completions::generate_lint_completions, |
25 | gen_diagnostic_docs::generate_diagnostic_docs, | 25 | gen_parser_tests::generate_parser_tests, gen_syntax::generate_syntax, |
26 | gen_feature_docs::generate_feature_docs, | ||
27 | gen_lint_completions::generate_lint_completions, | ||
28 | gen_parser_tests::generate_parser_tests, | ||
29 | gen_syntax::generate_syntax, | ||
30 | }; | 26 | }; |
31 | 27 | ||
32 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] | 28 | pub(crate) fn docs() -> Result<()> { |
33 | pub(crate) enum Mode { | 29 | // We don't commit docs to the repo, so we can just overwrite them. |
34 | Overwrite, | 30 | gen_assists_docs::generate_assists_docs()?; |
35 | Verify, | 31 | gen_feature_docs::generate_feature_docs()?; |
32 | gen_diagnostic_docs::generate_diagnostic_docs()?; | ||
33 | Ok(()) | ||
36 | } | 34 | } |
37 | 35 | ||
38 | impl flags::Codegen { | 36 | #[allow(unused)] |
39 | pub(crate) fn run(self) -> Result<()> { | 37 | fn used() { |
40 | if self.features { | 38 | generate_parser_tests(); |
41 | generate_lint_completions(Mode::Overwrite)?; | 39 | generate_assists_tests(); |
42 | } | 40 | generate_syntax(); |
43 | generate_syntax(Mode::Overwrite)?; | 41 | generate_lint_completions(); |
44 | generate_parser_tests(Mode::Overwrite)?; | ||
45 | generate_assists_tests(Mode::Overwrite)?; | ||
46 | generate_assists_docs(Mode::Overwrite)?; | ||
47 | generate_feature_docs(Mode::Overwrite)?; | ||
48 | generate_diagnostic_docs(Mode::Overwrite)?; | ||
49 | Ok(()) | ||
50 | } | ||
51 | } | 42 | } |
52 | 43 | ||
53 | /// A helper to update file on disk if it has changed. | 44 | /// Checks that the `file` has the specified `contents`. If that is not the |
54 | /// With verify = false, | 45 | /// case, updates the file and then fails the test. |
55 | fn update(path: &Path, contents: &str, mode: Mode) -> Result<()> { | 46 | pub(crate) fn ensure_file_contents(file: &Path, contents: &str) -> Result<()> { |
56 | match read_file(path) { | 47 | match std::fs::read_to_string(file) { |
57 | Ok(old_contents) if normalize(&old_contents) == normalize(contents) => { | 48 | Ok(old_contents) if normalize_newlines(&old_contents) == normalize_newlines(contents) => { |
58 | return Ok(()); | 49 | return Ok(()) |
59 | } | 50 | } |
60 | _ => (), | 51 | _ => (), |
61 | } | 52 | } |
62 | if mode == Mode::Verify { | 53 | let display_path = file.strip_prefix(&project_root()).unwrap_or(file); |
63 | anyhow::bail!("`{}` is not up-to-date", path.display()); | 54 | eprintln!( |
55 | "\n\x1b[31;1merror\x1b[0m: {} was not up-to-date, updating\n", | ||
56 | display_path.display() | ||
57 | ); | ||
58 | if std::env::var("CI").is_ok() { | ||
59 | eprintln!(" NOTE: run `cargo test` locally and commit the updated files\n"); | ||
64 | } | 60 | } |
65 | eprintln!("updating {}", path.display()); | 61 | if let Some(parent) = file.parent() { |
66 | write_file(path, contents)?; | 62 | let _ = std::fs::create_dir_all(parent); |
67 | return Ok(()); | ||
68 | |||
69 | fn normalize(s: &str) -> String { | ||
70 | s.replace("\r\n", "\n") | ||
71 | } | 63 | } |
64 | std::fs::write(file, contents).unwrap(); | ||
65 | anyhow::bail!("some file was not up to date and has been updated, simply re-run the tests") | ||
66 | } | ||
67 | |||
68 | fn normalize_newlines(s: &str) -> String { | ||
69 | s.replace("\r\n", "\n") | ||
72 | } | 70 | } |
73 | 71 | ||
74 | const PREAMBLE: &str = "Generated file, do not edit by hand, see `xtask/src/codegen`"; | 72 | const PREAMBLE: &str = "Generated file, do not edit by hand, see `xtask/src/codegen`"; |
diff --git a/xtask/src/codegen/gen_assists_docs.rs b/xtask/src/codegen/gen_assists_docs.rs index c469b388d..158680993 100644 --- a/xtask/src/codegen/gen_assists_docs.rs +++ b/xtask/src/codegen/gen_assists_docs.rs | |||
@@ -2,22 +2,25 @@ | |||
2 | 2 | ||
3 | use std::{fmt, path::Path}; | 3 | use std::{fmt, path::Path}; |
4 | 4 | ||
5 | use xshell::write_file; | ||
6 | |||
5 | use crate::{ | 7 | use crate::{ |
6 | codegen::{self, extract_comment_blocks_with_empty_lines, reformat, Location, Mode, PREAMBLE}, | 8 | codegen::{self, extract_comment_blocks_with_empty_lines, reformat, Location, PREAMBLE}, |
7 | project_root, rust_files_in, Result, | 9 | project_root, rust_files_in, Result, |
8 | }; | 10 | }; |
9 | 11 | ||
10 | pub(crate) fn generate_assists_tests(mode: Mode) -> Result<()> { | 12 | pub(crate) fn generate_assists_tests() -> Result<()> { |
11 | let assists = Assist::collect()?; | 13 | let assists = Assist::collect()?; |
12 | generate_tests(&assists, mode) | 14 | generate_tests(&assists) |
13 | } | 15 | } |
14 | 16 | ||
15 | pub(crate) fn generate_assists_docs(mode: Mode) -> Result<()> { | 17 | pub(crate) fn generate_assists_docs() -> Result<()> { |
16 | let assists = Assist::collect()?; | 18 | let assists = Assist::collect()?; |
17 | let contents = assists.into_iter().map(|it| it.to_string()).collect::<Vec<_>>().join("\n\n"); | 19 | let contents = assists.into_iter().map(|it| it.to_string()).collect::<Vec<_>>().join("\n\n"); |
18 | let contents = format!("//{}\n{}\n", PREAMBLE, contents.trim()); | 20 | let contents = format!("//{}\n{}\n", PREAMBLE, contents.trim()); |
19 | let dst = project_root().join("docs/user/generated_assists.adoc"); | 21 | let dst = project_root().join("docs/user/generated_assists.adoc"); |
20 | codegen::update(&dst, &contents, mode) | 22 | write_file(dst, &contents)?; |
23 | Ok(()) | ||
21 | } | 24 | } |
22 | 25 | ||
23 | #[derive(Debug)] | 26 | #[derive(Debug)] |
@@ -111,7 +114,7 @@ impl fmt::Display for Assist { | |||
111 | } | 114 | } |
112 | } | 115 | } |
113 | 116 | ||
114 | fn generate_tests(assists: &[Assist], mode: Mode) -> Result<()> { | 117 | fn generate_tests(assists: &[Assist]) -> Result<()> { |
115 | let mut buf = String::from("use super::check_doc_test;\n"); | 118 | let mut buf = String::from("use super::check_doc_test;\n"); |
116 | 119 | ||
117 | for assist in assists.iter() { | 120 | for assist in assists.iter() { |
@@ -135,7 +138,10 @@ r#####" | |||
135 | buf.push_str(&test) | 138 | buf.push_str(&test) |
136 | } | 139 | } |
137 | let buf = reformat(&buf)?; | 140 | let buf = reformat(&buf)?; |
138 | codegen::update(&project_root().join("crates/ide_assists/src/tests/generated.rs"), &buf, mode) | 141 | codegen::ensure_file_contents( |
142 | &project_root().join("crates/ide_assists/src/tests/generated.rs"), | ||
143 | &buf, | ||
144 | ) | ||
139 | } | 145 | } |
140 | 146 | ||
141 | fn hide_hash_comments(text: &str) -> String { | 147 | fn hide_hash_comments(text: &str) -> String { |
diff --git a/xtask/src/codegen/gen_diagnostic_docs.rs b/xtask/src/codegen/gen_diagnostic_docs.rs index a2561817b..9cf4d0a88 100644 --- a/xtask/src/codegen/gen_diagnostic_docs.rs +++ b/xtask/src/codegen/gen_diagnostic_docs.rs | |||
@@ -2,18 +2,20 @@ | |||
2 | 2 | ||
3 | use std::{fmt, path::PathBuf}; | 3 | use std::{fmt, path::PathBuf}; |
4 | 4 | ||
5 | use xshell::write_file; | ||
6 | |||
5 | use crate::{ | 7 | use crate::{ |
6 | codegen::{self, extract_comment_blocks_with_empty_lines, Location, Mode, PREAMBLE}, | 8 | codegen::{extract_comment_blocks_with_empty_lines, Location, PREAMBLE}, |
7 | project_root, rust_files, Result, | 9 | project_root, rust_files, Result, |
8 | }; | 10 | }; |
9 | 11 | ||
10 | pub(crate) fn generate_diagnostic_docs(mode: Mode) -> Result<()> { | 12 | pub(crate) fn generate_diagnostic_docs() -> Result<()> { |
11 | let diagnostics = Diagnostic::collect()?; | 13 | let diagnostics = Diagnostic::collect()?; |
12 | let contents = | 14 | let contents = |
13 | diagnostics.into_iter().map(|it| it.to_string()).collect::<Vec<_>>().join("\n\n"); | 15 | diagnostics.into_iter().map(|it| it.to_string()).collect::<Vec<_>>().join("\n\n"); |
14 | let contents = format!("//{}\n{}\n", PREAMBLE, contents.trim()); | 16 | let contents = format!("//{}\n{}\n", PREAMBLE, contents.trim()); |
15 | let dst = project_root().join("docs/user/generated_diagnostic.adoc"); | 17 | let dst = project_root().join("docs/user/generated_diagnostic.adoc"); |
16 | codegen::update(&dst, &contents, mode)?; | 18 | write_file(&dst, &contents)?; |
17 | Ok(()) | 19 | Ok(()) |
18 | } | 20 | } |
19 | 21 | ||
diff --git a/xtask/src/codegen/gen_feature_docs.rs b/xtask/src/codegen/gen_feature_docs.rs index cad7ff477..c373d7d70 100644 --- a/xtask/src/codegen/gen_feature_docs.rs +++ b/xtask/src/codegen/gen_feature_docs.rs | |||
@@ -2,17 +2,19 @@ | |||
2 | 2 | ||
3 | use std::{fmt, path::PathBuf}; | 3 | use std::{fmt, path::PathBuf}; |
4 | 4 | ||
5 | use xshell::write_file; | ||
6 | |||
5 | use crate::{ | 7 | use crate::{ |
6 | codegen::{self, extract_comment_blocks_with_empty_lines, Location, Mode, PREAMBLE}, | 8 | codegen::{extract_comment_blocks_with_empty_lines, Location, PREAMBLE}, |
7 | project_root, rust_files, Result, | 9 | project_root, rust_files, Result, |
8 | }; | 10 | }; |
9 | 11 | ||
10 | pub(crate) fn generate_feature_docs(mode: Mode) -> Result<()> { | 12 | pub(crate) fn generate_feature_docs() -> Result<()> { |
11 | let features = Feature::collect()?; | 13 | let features = Feature::collect()?; |
12 | let contents = features.into_iter().map(|it| it.to_string()).collect::<Vec<_>>().join("\n\n"); | 14 | let contents = features.into_iter().map(|it| it.to_string()).collect::<Vec<_>>().join("\n\n"); |
13 | let contents = format!("//{}\n{}\n", PREAMBLE, contents.trim()); | 15 | let contents = format!("//{}\n{}\n", PREAMBLE, contents.trim()); |
14 | let dst = project_root().join("docs/user/generated_features.adoc"); | 16 | let dst = project_root().join("docs/user/generated_features.adoc"); |
15 | codegen::update(&dst, &contents, mode)?; | 17 | write_file(&dst, &contents)?; |
16 | Ok(()) | 18 | Ok(()) |
17 | } | 19 | } |
18 | 20 | ||
diff --git a/xtask/src/codegen/gen_lint_completions.rs b/xtask/src/codegen/gen_lint_completions.rs index b1c057037..24dbc6a39 100644 --- a/xtask/src/codegen/gen_lint_completions.rs +++ b/xtask/src/codegen/gen_lint_completions.rs | |||
@@ -5,13 +5,10 @@ use std::path::{Path, PathBuf}; | |||
5 | use walkdir::WalkDir; | 5 | use walkdir::WalkDir; |
6 | use xshell::{cmd, read_file}; | 6 | use xshell::{cmd, read_file}; |
7 | 7 | ||
8 | use crate::{ | 8 | use crate::codegen::{ensure_file_contents, project_root, reformat, Result}; |
9 | codegen::{project_root, reformat, update, Mode, Result}, | ||
10 | run_rustfmt, | ||
11 | }; | ||
12 | 9 | ||
13 | pub(crate) fn generate_lint_completions(mode: Mode) -> Result<()> { | 10 | pub(crate) fn generate_lint_completions() -> Result<()> { |
14 | if !Path::new("./target/rust").exists() { | 11 | if !project_root().join("./target/rust").exists() { |
15 | cmd!("git clone --depth=1 https://github.com/rust-lang/rust ./target/rust").run()?; | 12 | cmd!("git clone --depth=1 https://github.com/rust-lang/rust ./target/rust").run()?; |
16 | } | 13 | } |
17 | 14 | ||
@@ -25,8 +22,7 @@ pub(crate) fn generate_lint_completions(mode: Mode) -> Result<()> { | |||
25 | 22 | ||
26 | let destination = | 23 | let destination = |
27 | project_root().join("crates/ide_completion/src/generated_lint_completions.rs"); | 24 | project_root().join("crates/ide_completion/src/generated_lint_completions.rs"); |
28 | update(destination.as_path(), &contents, mode)?; | 25 | ensure_file_contents(destination.as_path(), &contents)?; |
29 | run_rustfmt(mode)?; | ||
30 | 26 | ||
31 | Ok(()) | 27 | Ok(()) |
32 | } | 28 | } |
diff --git a/xtask/src/codegen/gen_parser_tests.rs b/xtask/src/codegen/gen_parser_tests.rs index cb8939063..096590653 100644 --- a/xtask/src/codegen/gen_parser_tests.rs +++ b/xtask/src/codegen/gen_parser_tests.rs | |||
@@ -8,13 +8,13 @@ use std::{ | |||
8 | }; | 8 | }; |
9 | 9 | ||
10 | use crate::{ | 10 | use crate::{ |
11 | codegen::{extract_comment_blocks, update, Mode}, | 11 | codegen::{ensure_file_contents, extract_comment_blocks}, |
12 | project_root, Result, | 12 | project_root, Result, |
13 | }; | 13 | }; |
14 | 14 | ||
15 | pub(crate) fn generate_parser_tests(mode: Mode) -> Result<()> { | 15 | pub(crate) fn generate_parser_tests() -> Result<()> { |
16 | let tests = tests_from_dir(&project_root().join(Path::new("crates/parser/src/grammar")))?; | 16 | let tests = tests_from_dir(&project_root().join(Path::new("crates/parser/src/grammar")))?; |
17 | fn install_tests(tests: &HashMap<String, Test>, into: &str, mode: Mode) -> Result<()> { | 17 | fn install_tests(tests: &HashMap<String, Test>, into: &str) -> Result<()> { |
18 | let tests_dir = project_root().join(into); | 18 | let tests_dir = project_root().join(into); |
19 | if !tests_dir.is_dir() { | 19 | if !tests_dir.is_dir() { |
20 | fs::create_dir_all(&tests_dir)?; | 20 | fs::create_dir_all(&tests_dir)?; |
@@ -35,12 +35,12 @@ pub(crate) fn generate_parser_tests(mode: Mode) -> Result<()> { | |||
35 | tests_dir.join(file_name) | 35 | tests_dir.join(file_name) |
36 | } | 36 | } |
37 | }; | 37 | }; |
38 | update(&path, &test.text, mode)?; | 38 | ensure_file_contents(&path, &test.text)?; |
39 | } | 39 | } |
40 | Ok(()) | 40 | Ok(()) |
41 | } | 41 | } |
42 | install_tests(&tests.ok, "crates/syntax/test_data/parser/inline/ok", mode)?; | 42 | install_tests(&tests.ok, "crates/syntax/test_data/parser/inline/ok")?; |
43 | install_tests(&tests.err, "crates/syntax/test_data/parser/inline/err", mode) | 43 | install_tests(&tests.err, "crates/syntax/test_data/parser/inline/err") |
44 | } | 44 | } |
45 | 45 | ||
46 | #[derive(Debug)] | 46 | #[derive(Debug)] |
diff --git a/xtask/src/codegen/gen_syntax.rs b/xtask/src/codegen/gen_syntax.rs index 191bc0e9d..80f26e8f5 100644 --- a/xtask/src/codegen/gen_syntax.rs +++ b/xtask/src/codegen/gen_syntax.rs | |||
@@ -14,25 +14,25 @@ use ungrammar::{rust_grammar, Grammar, Rule}; | |||
14 | 14 | ||
15 | use crate::{ | 15 | use crate::{ |
16 | ast_src::{AstEnumSrc, AstNodeSrc, AstSrc, Cardinality, Field, KindsSrc, KINDS_SRC}, | 16 | ast_src::{AstEnumSrc, AstNodeSrc, AstSrc, Cardinality, Field, KindsSrc, KINDS_SRC}, |
17 | codegen::{reformat, update, Mode}, | 17 | codegen::{ensure_file_contents, reformat}, |
18 | project_root, Result, | 18 | project_root, Result, |
19 | }; | 19 | }; |
20 | 20 | ||
21 | pub(crate) fn generate_syntax(mode: Mode) -> Result<()> { | 21 | pub(crate) fn generate_syntax() -> Result<()> { |
22 | let grammar = rust_grammar(); | 22 | let grammar = rust_grammar(); |
23 | let ast = lower(&grammar); | 23 | let ast = lower(&grammar); |
24 | 24 | ||
25 | let syntax_kinds_file = project_root().join("crates/parser/src/syntax_kind/generated.rs"); | 25 | let syntax_kinds_file = project_root().join("crates/parser/src/syntax_kind/generated.rs"); |
26 | let syntax_kinds = generate_syntax_kinds(KINDS_SRC)?; | 26 | let syntax_kinds = generate_syntax_kinds(KINDS_SRC)?; |
27 | update(syntax_kinds_file.as_path(), &syntax_kinds, mode)?; | 27 | ensure_file_contents(syntax_kinds_file.as_path(), &syntax_kinds)?; |
28 | 28 | ||
29 | let ast_tokens_file = project_root().join("crates/syntax/src/ast/generated/tokens.rs"); | 29 | let ast_tokens_file = project_root().join("crates/syntax/src/ast/generated/tokens.rs"); |
30 | let contents = generate_tokens(&ast)?; | 30 | let contents = generate_tokens(&ast)?; |
31 | update(ast_tokens_file.as_path(), &contents, mode)?; | 31 | ensure_file_contents(ast_tokens_file.as_path(), &contents)?; |
32 | 32 | ||
33 | let ast_nodes_file = project_root().join("crates/syntax/src/ast/generated/nodes.rs"); | 33 | let ast_nodes_file = project_root().join("crates/syntax/src/ast/generated/nodes.rs"); |
34 | let contents = generate_nodes(KINDS_SRC, &ast)?; | 34 | let contents = generate_nodes(KINDS_SRC, &ast)?; |
35 | update(ast_nodes_file.as_path(), &contents, mode)?; | 35 | ensure_file_contents(ast_nodes_file.as_path(), &contents)?; |
36 | 36 | ||
37 | Ok(()) | 37 | Ok(()) |
38 | } | 38 | } |
diff --git a/xtask/src/flags.rs b/xtask/src/flags.rs index 56eda5b1e..48d1ad45e 100644 --- a/xtask/src/flags.rs +++ b/xtask/src/flags.rs | |||
@@ -2,7 +2,9 @@ | |||
2 | 2 | ||
3 | use crate::install::{ClientOpt, Malloc, ServerOpt}; | 3 | use crate::install::{ClientOpt, Malloc, ServerOpt}; |
4 | 4 | ||
5 | xflags::args_parser! { | 5 | xflags::xflags! { |
6 | src "./src/flags.rs" | ||
7 | |||
6 | /// Run custom build command. | 8 | /// Run custom build command. |
7 | cmd xtask { | 9 | cmd xtask { |
8 | default cmd help { | 10 | default cmd help { |
@@ -25,10 +27,6 @@ xflags::args_parser! { | |||
25 | optional --jemalloc | 27 | optional --jemalloc |
26 | } | 28 | } |
27 | 29 | ||
28 | cmd codegen { | ||
29 | optional --features | ||
30 | } | ||
31 | |||
32 | cmd lint {} | 30 | cmd lint {} |
33 | cmd fuzz-tests {} | 31 | cmd fuzz-tests {} |
34 | cmd pre-cache {} | 32 | cmd pre-cache {} |
@@ -55,7 +53,7 @@ xflags::args_parser! { | |||
55 | 53 | ||
56 | // generated start | 54 | // generated start |
57 | // The following code is generated by `xflags` macro. | 55 | // The following code is generated by `xflags` macro. |
58 | // Run `env XFLAGS_DUMP= cargo build` to regenerate. | 56 | // Run `env UPDATE_XFLAGS=1 cargo build` to regenerate. |
59 | #[derive(Debug)] | 57 | #[derive(Debug)] |
60 | pub struct Xtask { | 58 | pub struct Xtask { |
61 | pub subcommand: XtaskCmd, | 59 | pub subcommand: XtaskCmd, |
@@ -65,7 +63,6 @@ pub struct Xtask { | |||
65 | pub enum XtaskCmd { | 63 | pub enum XtaskCmd { |
66 | Help(Help), | 64 | Help(Help), |
67 | Install(Install), | 65 | Install(Install), |
68 | Codegen(Codegen), | ||
69 | Lint(Lint), | 66 | Lint(Lint), |
70 | FuzzTests(FuzzTests), | 67 | FuzzTests(FuzzTests), |
71 | PreCache(PreCache), | 68 | PreCache(PreCache), |
@@ -91,18 +88,13 @@ pub struct Install { | |||
91 | } | 88 | } |
92 | 89 | ||
93 | #[derive(Debug)] | 90 | #[derive(Debug)] |
94 | pub struct Codegen { | 91 | pub struct Lint; |
95 | pub features: bool, | ||
96 | } | ||
97 | |||
98 | #[derive(Debug)] | ||
99 | pub struct Lint {} | ||
100 | 92 | ||
101 | #[derive(Debug)] | 93 | #[derive(Debug)] |
102 | pub struct FuzzTests {} | 94 | pub struct FuzzTests; |
103 | 95 | ||
104 | #[derive(Debug)] | 96 | #[derive(Debug)] |
105 | pub struct PreCache {} | 97 | pub struct PreCache; |
106 | 98 | ||
107 | #[derive(Debug)] | 99 | #[derive(Debug)] |
108 | pub struct Release { | 100 | pub struct Release { |
@@ -131,11 +123,10 @@ pub struct Bb { | |||
131 | } | 123 | } |
132 | 124 | ||
133 | impl Xtask { | 125 | impl Xtask { |
134 | pub const HELP: &'static str = Self::_HELP; | 126 | pub const HELP: &'static str = Self::HELP_; |
135 | 127 | ||
136 | pub fn from_env() -> xflags::Result<Self> { | 128 | pub fn from_env() -> xflags::Result<Self> { |
137 | let mut p = xflags::rt::Parser::new_from_env(); | 129 | Self::from_env_() |
138 | Self::_parse(&mut p) | ||
139 | } | 130 | } |
140 | } | 131 | } |
141 | // generated end | 132 | // generated end |
diff --git a/xtask/src/main.rs b/xtask/src/main.rs index 3c4332f75..057cd57ae 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs | |||
@@ -1,7 +1,7 @@ | |||
1 | //! See https://github.com/matklad/cargo-xtask/. | 1 | //! See https://github.com/matklad/cargo-xtask/. |
2 | //! | 2 | //! |
3 | //! This binary defines various auxiliary build commands, which are not | 3 | //! This binary defines various auxiliary build commands, which are not |
4 | //! expressible with just `cargo`. Notably, it provides `cargo xtask codegen` | 4 | //! expressible with just `cargo`. Notably, it provides tests via `cargo test -p xtask` |
5 | //! for code generation and `cargo xtask install` for installation of | 5 | //! for code generation and `cargo xtask install` for installation of |
6 | //! rust-analyzer server and client. | 6 | //! rust-analyzer server and client. |
7 | //! | 7 | //! |
@@ -28,7 +28,7 @@ use std::{ | |||
28 | use walkdir::{DirEntry, WalkDir}; | 28 | use walkdir::{DirEntry, WalkDir}; |
29 | use xshell::{cmd, cp, pushd, pushenv}; | 29 | use xshell::{cmd, cp, pushd, pushenv}; |
30 | 30 | ||
31 | use crate::{codegen::Mode, dist::DistCmd}; | 31 | use crate::dist::DistCmd; |
32 | 32 | ||
33 | fn main() -> Result<()> { | 33 | fn main() -> Result<()> { |
34 | let _d = pushd(project_root())?; | 34 | let _d = pushd(project_root())?; |
@@ -40,7 +40,6 @@ fn main() -> Result<()> { | |||
40 | return Ok(()); | 40 | return Ok(()); |
41 | } | 41 | } |
42 | flags::XtaskCmd::Install(cmd) => cmd.run(), | 42 | flags::XtaskCmd::Install(cmd) => cmd.run(), |
43 | flags::XtaskCmd::Codegen(cmd) => cmd.run(), | ||
44 | flags::XtaskCmd::Lint(_) => run_clippy(), | 43 | flags::XtaskCmd::Lint(_) => run_clippy(), |
45 | flags::XtaskCmd::FuzzTests(_) => run_fuzzer(), | 44 | flags::XtaskCmd::FuzzTests(_) => run_fuzzer(), |
46 | flags::XtaskCmd::PreCache(cmd) => cmd.run(), | 45 | flags::XtaskCmd::PreCache(cmd) => cmd.run(), |
@@ -85,18 +84,6 @@ fn rust_files_in(path: &Path) -> impl Iterator<Item = PathBuf> { | |||
85 | files_in(path, "rs") | 84 | files_in(path, "rs") |
86 | } | 85 | } |
87 | 86 | ||
88 | fn run_rustfmt(mode: Mode) -> Result<()> { | ||
89 | let _dir = pushd(project_root())?; | ||
90 | let _e = pushenv("RUSTUP_TOOLCHAIN", "stable"); | ||
91 | ensure_rustfmt()?; | ||
92 | let check = match mode { | ||
93 | Mode::Overwrite => &[][..], | ||
94 | Mode::Verify => &["--", "--check"], | ||
95 | }; | ||
96 | cmd!("cargo fmt {check...}").run()?; | ||
97 | Ok(()) | ||
98 | } | ||
99 | |||
100 | fn ensure_rustfmt() -> Result<()> { | 87 | fn ensure_rustfmt() -> Result<()> { |
101 | let out = cmd!("rustfmt --version").read()?; | 88 | let out = cmd!("rustfmt --version").read()?; |
102 | if !out.contains("stable") { | 89 | if !out.contains("stable") { |
diff --git a/xtask/src/release.rs b/xtask/src/release.rs index d8d86fd63..dde5d14ee 100644 --- a/xtask/src/release.rs +++ b/xtask/src/release.rs | |||
@@ -2,7 +2,7 @@ use std::fmt::Write; | |||
2 | 2 | ||
3 | use xshell::{cmd, cp, pushd, read_dir, write_file}; | 3 | use xshell::{cmd, cp, pushd, read_dir, write_file}; |
4 | 4 | ||
5 | use crate::{codegen, date_iso, flags, is_release_tag, project_root, Mode, Result}; | 5 | use crate::{codegen, date_iso, flags, is_release_tag, project_root, Result}; |
6 | 6 | ||
7 | impl flags::Release { | 7 | impl flags::Release { |
8 | pub(crate) fn run(self) -> Result<()> { | 8 | pub(crate) fn run(self) -> Result<()> { |
@@ -12,8 +12,7 @@ impl flags::Release { | |||
12 | cmd!("git reset --hard tags/nightly").run()?; | 12 | cmd!("git reset --hard tags/nightly").run()?; |
13 | cmd!("git push").run()?; | 13 | cmd!("git push").run()?; |
14 | } | 14 | } |
15 | codegen::generate_assists_docs(Mode::Overwrite)?; | 15 | codegen::docs()?; |
16 | codegen::generate_feature_docs(Mode::Overwrite)?; | ||
17 | 16 | ||
18 | let website_root = project_root().join("../rust-analyzer.github.io"); | 17 | let website_root = project_root().join("../rust-analyzer.github.io"); |
19 | let changelog_dir = website_root.join("./thisweek/_posts"); | 18 | let changelog_dir = website_root.join("./thisweek/_posts"); |
diff --git a/xtask/src/tidy.rs b/xtask/src/tidy.rs index 349ca14d0..1352d1218 100644 --- a/xtask/src/tidy.rs +++ b/xtask/src/tidy.rs | |||
@@ -3,48 +3,48 @@ use std::{ | |||
3 | path::{Path, PathBuf}, | 3 | path::{Path, PathBuf}, |
4 | }; | 4 | }; |
5 | 5 | ||
6 | use xshell::{cmd, read_file}; | 6 | use xshell::{cmd, pushd, pushenv, read_file}; |
7 | 7 | ||
8 | use crate::{ | 8 | use crate::{cargo_files, codegen, project_root, rust_files}; |
9 | cargo_files, | ||
10 | codegen::{self, Mode}, | ||
11 | project_root, run_rustfmt, rust_files, | ||
12 | }; | ||
13 | 9 | ||
14 | #[test] | 10 | #[test] |
15 | fn generated_grammar_is_fresh() { | 11 | fn generate_grammar() { |
16 | if let Err(error) = codegen::generate_syntax(Mode::Verify) { | 12 | codegen::generate_syntax().unwrap() |
17 | panic!("{}. Please update it by running `cargo xtask codegen`", error); | ||
18 | } | ||
19 | } | 13 | } |
20 | 14 | ||
21 | #[test] | 15 | #[test] |
22 | fn generated_tests_are_fresh() { | 16 | fn generate_parser_tests() { |
23 | if let Err(error) = codegen::generate_parser_tests(Mode::Verify) { | 17 | codegen::generate_parser_tests().unwrap() |
24 | panic!("{}. Please update tests by running `cargo xtask codegen`", error); | ||
25 | } | ||
26 | } | 18 | } |
27 | 19 | ||
28 | #[test] | 20 | #[test] |
29 | fn generated_assists_are_fresh() { | 21 | fn generate_assists_tests() { |
30 | if let Err(error) = codegen::generate_assists_tests(Mode::Verify) { | 22 | codegen::generate_assists_tests().unwrap(); |
31 | panic!("{}. Please update assists by running `cargo xtask codegen`", error); | 23 | } |
32 | } | 24 | |
25 | /// This clones rustc repo, and so is not worth to keep up-to-date. We update | ||
26 | /// manually by un-ignoring the test from time to time. | ||
27 | #[test] | ||
28 | #[ignore] | ||
29 | fn generate_lint_completions() { | ||
30 | codegen::generate_lint_completions().unwrap() | ||
33 | } | 31 | } |
34 | 32 | ||
35 | #[test] | 33 | #[test] |
36 | fn check_code_formatting() { | 34 | fn check_code_formatting() { |
37 | if let Err(error) = run_rustfmt(Mode::Verify) { | 35 | let _dir = pushd(project_root()).unwrap(); |
38 | panic!("{}. Please format the code by running `cargo format`", error); | 36 | let _e = pushenv("RUSTUP_TOOLCHAIN", "stable"); |
37 | crate::ensure_rustfmt().unwrap(); | ||
38 | let res = cmd!("cargo fmt -- --check").run(); | ||
39 | if !res.is_ok() { | ||
40 | let _ = cmd!("cargo fmt").run(); | ||
39 | } | 41 | } |
42 | res.unwrap() | ||
40 | } | 43 | } |
41 | 44 | ||
42 | #[test] | 45 | #[test] |
43 | fn smoke_test_docs_generation() { | 46 | fn smoke_test_generate_documentation() { |
44 | // We don't commit docs to the repo, so we can just overwrite in tests. | 47 | codegen::docs().unwrap() |
45 | codegen::generate_assists_docs(Mode::Overwrite).unwrap(); | ||
46 | codegen::generate_feature_docs(Mode::Overwrite).unwrap(); | ||
47 | codegen::generate_diagnostic_docs(Mode::Overwrite).unwrap(); | ||
48 | } | 48 | } |
49 | 49 | ||
50 | #[test] | 50 | #[test] |