diff options
122 files changed, 5511 insertions, 3062 deletions
diff --git a/.gitignore b/.gitignore index b205bf3fb..7e097c015 100644 --- a/.gitignore +++ b/.gitignore | |||
@@ -10,3 +10,4 @@ crates/*/target | |||
10 | generated_assists.adoc | 10 | generated_assists.adoc |
11 | generated_features.adoc | 11 | generated_features.adoc |
12 | generated_diagnostic.adoc | 12 | generated_diagnostic.adoc |
13 | .DS_Store | ||
diff --git a/Cargo.lock b/Cargo.lock index 1a4a63550..051d9e734 100644 --- a/Cargo.lock +++ b/Cargo.lock | |||
@@ -81,9 +81,9 @@ checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" | |||
81 | 81 | ||
82 | [[package]] | 82 | [[package]] |
83 | name = "backtrace" | 83 | name = "backtrace" |
84 | version = "0.3.54" | 84 | version = "0.3.55" |
85 | source = "registry+https://github.com/rust-lang/crates.io-index" | 85 | source = "registry+https://github.com/rust-lang/crates.io-index" |
86 | checksum = "2baad346b2d4e94a24347adeee9c7a93f412ee94b9cc26e5b59dea23848e9f28" | 86 | checksum = "ef5140344c85b01f9bbb4d4b7288a8aa4b3287ccef913a14bcc78a1063623598" |
87 | dependencies = [ | 87 | dependencies = [ |
88 | "addr2line", | 88 | "addr2line", |
89 | "cfg-if 1.0.0", | 89 | "cfg-if 1.0.0", |
@@ -139,9 +139,9 @@ dependencies = [ | |||
139 | 139 | ||
140 | [[package]] | 140 | [[package]] |
141 | name = "cc" | 141 | name = "cc" |
142 | version = "1.0.61" | 142 | version = "1.0.65" |
143 | source = "registry+https://github.com/rust-lang/crates.io-index" | 143 | source = "registry+https://github.com/rust-lang/crates.io-index" |
144 | checksum = "ed67cbde08356238e75fc4656be4749481eeffb09e19f320a25237d5221c985d" | 144 | checksum = "95752358c8f7552394baf48cd82695b345628ad3f170d607de3ca03b8dacca15" |
145 | 145 | ||
146 | [[package]] | 146 | [[package]] |
147 | name = "cfg" | 147 | name = "cfg" |
@@ -242,9 +242,9 @@ dependencies = [ | |||
242 | 242 | ||
243 | [[package]] | 243 | [[package]] |
244 | name = "cmake" | 244 | name = "cmake" |
245 | version = "0.1.44" | 245 | version = "0.1.45" |
246 | source = "registry+https://github.com/rust-lang/crates.io-index" | 246 | source = "registry+https://github.com/rust-lang/crates.io-index" |
247 | checksum = "0e56268c17a6248366d66d4a47a3381369d068cce8409bb1716ed77ea32163bb" | 247 | checksum = "eb6210b637171dfba4cda12e579ac6dc73f5165ad56133e5d72ef3131f320855" |
248 | dependencies = [ | 248 | dependencies = [ |
249 | "cc", | 249 | "cc", |
250 | ] | 250 | ] |
@@ -253,7 +253,9 @@ dependencies = [ | |||
253 | name = "completion" | 253 | name = "completion" |
254 | version = "0.0.0" | 254 | version = "0.0.0" |
255 | dependencies = [ | 255 | dependencies = [ |
256 | "assists", | ||
256 | "base_db", | 257 | "base_db", |
258 | "either", | ||
257 | "expect-test", | 259 | "expect-test", |
258 | "hir", | 260 | "hir", |
259 | "ide_db", | 261 | "ide_db", |
@@ -379,9 +381,9 @@ dependencies = [ | |||
379 | 381 | ||
380 | [[package]] | 382 | [[package]] |
381 | name = "env_logger" | 383 | name = "env_logger" |
382 | version = "0.8.1" | 384 | version = "0.8.2" |
383 | source = "registry+https://github.com/rust-lang/crates.io-index" | 385 | source = "registry+https://github.com/rust-lang/crates.io-index" |
384 | checksum = "54532e3223c5af90a6a757c90b5c5521564b07e5e7a958681bcd2afad421cdcd" | 386 | checksum = "f26ecb66b4bdca6c1409b40fb255eefc2bd4f6d135dab3c3124f80ffa2a9661e" |
385 | dependencies = [ | 387 | dependencies = [ |
386 | "log", | 388 | "log", |
387 | ] | 389 | ] |
@@ -398,11 +400,11 @@ dependencies = [ | |||
398 | 400 | ||
399 | [[package]] | 401 | [[package]] |
400 | name = "filetime" | 402 | name = "filetime" |
401 | version = "0.2.12" | 403 | version = "0.2.13" |
402 | source = "registry+https://github.com/rust-lang/crates.io-index" | 404 | source = "registry+https://github.com/rust-lang/crates.io-index" |
403 | checksum = "3ed85775dcc68644b5c950ac06a2b23768d3bc9390464151aaf27136998dcf9e" | 405 | checksum = "0c122a393ea57648015bf06fbd3d372378992e86b9ff5a7a497b076a28c79efe" |
404 | dependencies = [ | 406 | dependencies = [ |
405 | "cfg-if 0.1.10", | 407 | "cfg-if 1.0.0", |
406 | "libc", | 408 | "libc", |
407 | "redox_syscall", | 409 | "redox_syscall", |
408 | "winapi 0.3.9", | 410 | "winapi 0.3.9", |
@@ -439,6 +441,16 @@ dependencies = [ | |||
439 | ] | 441 | ] |
440 | 442 | ||
441 | [[package]] | 443 | [[package]] |
444 | name = "form_urlencoded" | ||
445 | version = "1.0.0" | ||
446 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
447 | checksum = "ece68d15c92e84fa4f19d3780f1294e5ca82a78a6d515f1efaabcc144688be00" | ||
448 | dependencies = [ | ||
449 | "matches", | ||
450 | "percent-encoding", | ||
451 | ] | ||
452 | |||
453 | [[package]] | ||
442 | name = "fsevent" | 454 | name = "fsevent" |
443 | version = "2.0.2" | 455 | version = "2.0.2" |
444 | source = "registry+https://github.com/rust-lang/crates.io-index" | 456 | source = "registry+https://github.com/rust-lang/crates.io-index" |
@@ -612,6 +624,7 @@ dependencies = [ | |||
612 | "hir_expand", | 624 | "hir_expand", |
613 | "itertools", | 625 | "itertools", |
614 | "log", | 626 | "log", |
627 | "once_cell", | ||
615 | "profile", | 628 | "profile", |
616 | "rustc-hash", | 629 | "rustc-hash", |
617 | "scoped-tls", | 630 | "scoped-tls", |
@@ -714,18 +727,18 @@ dependencies = [ | |||
714 | 727 | ||
715 | [[package]] | 728 | [[package]] |
716 | name = "inotify-sys" | 729 | name = "inotify-sys" |
717 | version = "0.1.3" | 730 | version = "0.1.4" |
718 | source = "registry+https://github.com/rust-lang/crates.io-index" | 731 | source = "registry+https://github.com/rust-lang/crates.io-index" |
719 | checksum = "e74a1aa87c59aeff6ef2cc2fa62d41bc43f54952f55652656b18a02fd5e356c0" | 732 | checksum = "c4563555856585ab3180a5bf0b2f9f8d301a728462afffc8195b3f5394229c55" |
720 | dependencies = [ | 733 | dependencies = [ |
721 | "libc", | 734 | "libc", |
722 | ] | 735 | ] |
723 | 736 | ||
724 | [[package]] | 737 | [[package]] |
725 | name = "instant" | 738 | name = "instant" |
726 | version = "0.1.8" | 739 | version = "0.1.9" |
727 | source = "registry+https://github.com/rust-lang/crates.io-index" | 740 | source = "registry+https://github.com/rust-lang/crates.io-index" |
728 | checksum = "cb1fc4429a33e1f80d41dc9fea4d108a88bec1de8053878898ae448a0b52f613" | 741 | checksum = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec" |
729 | dependencies = [ | 742 | dependencies = [ |
730 | "cfg-if 1.0.0", | 743 | "cfg-if 1.0.0", |
731 | ] | 744 | ] |
@@ -809,9 +822,9 @@ dependencies = [ | |||
809 | 822 | ||
810 | [[package]] | 823 | [[package]] |
811 | name = "lock_api" | 824 | name = "lock_api" |
812 | version = "0.4.1" | 825 | version = "0.4.2" |
813 | source = "registry+https://github.com/rust-lang/crates.io-index" | 826 | source = "registry+https://github.com/rust-lang/crates.io-index" |
814 | checksum = "28247cc5a5be2f05fbcd76dd0cf2c7d3b5400cb978a28042abcd4fa0b3f8261c" | 827 | checksum = "dd96ffd135b2fd7b973ac026d28085defbe8983df057ced3eb4f2130b0831312" |
815 | dependencies = [ | 828 | dependencies = [ |
816 | "scopeguard", | 829 | "scopeguard", |
817 | ] | 830 | ] |
@@ -840,9 +853,9 @@ dependencies = [ | |||
840 | 853 | ||
841 | [[package]] | 854 | [[package]] |
842 | name = "lsp-server" | 855 | name = "lsp-server" |
843 | version = "0.4.1" | 856 | version = "0.5.0" |
844 | source = "registry+https://github.com/rust-lang/crates.io-index" | 857 | source = "registry+https://github.com/rust-lang/crates.io-index" |
845 | checksum = "9c85acaf36c53bf15da2b8b35afeea56747707261f59eb0b77229081dd72b04e" | 858 | checksum = "69b18dfe0e4a380b872aa79d8e0ee6c3d7a9682466e84b83ad807c88b3545f79" |
846 | dependencies = [ | 859 | dependencies = [ |
847 | "crossbeam-channel 0.5.0", | 860 | "crossbeam-channel 0.5.0", |
848 | "log", | 861 | "log", |
@@ -852,9 +865,9 @@ dependencies = [ | |||
852 | 865 | ||
853 | [[package]] | 866 | [[package]] |
854 | name = "lsp-types" | 867 | name = "lsp-types" |
855 | version = "0.83.0" | 868 | version = "0.84.0" |
856 | source = "registry+https://github.com/rust-lang/crates.io-index" | 869 | source = "registry+https://github.com/rust-lang/crates.io-index" |
857 | checksum = "25e0bd4b95038f2c23bda332ba0ca684e8dda765db1f9bdb63dc4c3e01f3b456" | 870 | checksum = "3b95be71fe205e44de754185bcf86447b65813ce1ceb298f8d3793ade5fff08d" |
858 | dependencies = [ | 871 | dependencies = [ |
859 | "base64", | 872 | "base64", |
860 | "bitflags", | 873 | "bitflags", |
@@ -1053,21 +1066,21 @@ checksum = "8d3b63360ec3cb337817c2dbd47ab4a0f170d285d8e5a2064600f3def1402397" | |||
1053 | 1066 | ||
1054 | [[package]] | 1067 | [[package]] |
1055 | name = "once_cell" | 1068 | name = "once_cell" |
1056 | version = "1.4.1" | 1069 | version = "1.5.2" |
1057 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1070 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1058 | checksum = "260e51e7efe62b592207e9e13a68e43692a7a279171d6ba57abd208bf23645ad" | 1071 | checksum = "13bd41f508810a131401606d54ac32a467c97172d74ba7662562ebba5ad07fa0" |
1059 | 1072 | ||
1060 | [[package]] | 1073 | [[package]] |
1061 | name = "oorandom" | 1074 | name = "oorandom" |
1062 | version = "11.1.2" | 1075 | version = "11.1.3" |
1063 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1076 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1064 | checksum = "a170cebd8021a008ea92e4db85a72f80b35df514ec664b296fdcbb654eac0b2c" | 1077 | checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" |
1065 | 1078 | ||
1066 | [[package]] | 1079 | [[package]] |
1067 | name = "parking_lot" | 1080 | name = "parking_lot" |
1068 | version = "0.11.0" | 1081 | version = "0.11.1" |
1069 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1082 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1070 | checksum = "a4893845fa2ca272e647da5d0e46660a314ead9c2fdd9a883aabc32e481a8733" | 1083 | checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb" |
1071 | dependencies = [ | 1084 | dependencies = [ |
1072 | "instant", | 1085 | "instant", |
1073 | "lock_api", | 1086 | "lock_api", |
@@ -1152,9 +1165,9 @@ checksum = "28b9b4df73455c861d7cbf8be42f01d3b373ed7f02e378d55fa84eafc6f638b1" | |||
1152 | 1165 | ||
1153 | [[package]] | 1166 | [[package]] |
1154 | name = "pin-project-lite" | 1167 | name = "pin-project-lite" |
1155 | version = "0.1.11" | 1168 | version = "0.2.0" |
1156 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1169 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1157 | checksum = "c917123afa01924fc84bb20c4c03f004d9c38e5127e3c039bbf7f4b9c76a2f6b" | 1170 | checksum = "6b063f57ec186e6140e2b8b6921e5f1bd89c7356dda5b33acc5401203ca6131c" |
1158 | 1171 | ||
1159 | [[package]] | 1172 | [[package]] |
1160 | name = "plain" | 1173 | name = "plain" |
@@ -1383,9 +1396,9 @@ dependencies = [ | |||
1383 | 1396 | ||
1384 | [[package]] | 1397 | [[package]] |
1385 | name = "rustc-ap-rustc_lexer" | 1398 | name = "rustc-ap-rustc_lexer" |
1386 | version = "686.0.0" | 1399 | version = "688.0.0" |
1387 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1400 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1388 | checksum = "a5b04cd2159495584d976d501c5394498470c2e94e4f0cebb8186562d407a678" | 1401 | checksum = "ebbdcc99bd015349093fcbae4780fda21416fec5d8843acfb3d1733e130cd4db" |
1389 | dependencies = [ | 1402 | dependencies = [ |
1390 | "unicode-xid", | 1403 | "unicode-xid", |
1391 | ] | 1404 | ] |
@@ -1581,9 +1594,9 @@ checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" | |||
1581 | 1594 | ||
1582 | [[package]] | 1595 | [[package]] |
1583 | name = "smallvec" | 1596 | name = "smallvec" |
1584 | version = "1.4.2" | 1597 | version = "1.5.0" |
1585 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1598 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1586 | checksum = "fbee7696b84bbf3d89a1c2eccff0850e3047ed46bfcd2e92c29a2d074d57e252" | 1599 | checksum = "7acad6f34eb9e8a259d3283d1e8c1d34d7415943d4895f65cc73813c7396fc85" |
1587 | 1600 | ||
1588 | [[package]] | 1601 | [[package]] |
1589 | name = "smol_str" | 1602 | name = "smol_str" |
@@ -1614,9 +1627,9 @@ version = "0.0.0" | |||
1614 | 1627 | ||
1615 | [[package]] | 1628 | [[package]] |
1616 | name = "syn" | 1629 | name = "syn" |
1617 | version = "1.0.48" | 1630 | version = "1.0.51" |
1618 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1631 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1619 | checksum = "cc371affeffc477f42a221a1e4297aedcea33d47d19b61455588bd9d8f6b19ac" | 1632 | checksum = "3b4f34193997d92804d359ed09953e25d5138df6bcc055a71bf68ee89fdf9223" |
1620 | dependencies = [ | 1633 | dependencies = [ |
1621 | "proc-macro2", | 1634 | "proc-macro2", |
1622 | "quote", | 1635 | "quote", |
@@ -1659,9 +1672,9 @@ dependencies = [ | |||
1659 | 1672 | ||
1660 | [[package]] | 1673 | [[package]] |
1661 | name = "termcolor" | 1674 | name = "termcolor" |
1662 | version = "1.1.0" | 1675 | version = "1.1.2" |
1663 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1676 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1664 | checksum = "bb6bfa289a4d7c5766392812c0a1f4c1ba45afa1ad47803c11e1f407d846d75f" | 1677 | checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" |
1665 | dependencies = [ | 1678 | dependencies = [ |
1666 | "winapi-util", | 1679 | "winapi-util", |
1667 | ] | 1680 | ] |
@@ -1727,9 +1740,18 @@ dependencies = [ | |||
1727 | 1740 | ||
1728 | [[package]] | 1741 | [[package]] |
1729 | name = "tinyvec" | 1742 | name = "tinyvec" |
1730 | version = "0.3.4" | 1743 | version = "1.1.0" |
1731 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1744 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1732 | checksum = "238ce071d267c5710f9d31451efec16c5ee22de34df17cc05e56cbc92e967117" | 1745 | checksum = "ccf8dbc19eb42fba10e8feaaec282fb50e2c14b2726d6301dbfeed0f73306a6f" |
1746 | dependencies = [ | ||
1747 | "tinyvec_macros", | ||
1748 | ] | ||
1749 | |||
1750 | [[package]] | ||
1751 | name = "tinyvec_macros" | ||
1752 | version = "0.1.0" | ||
1753 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1754 | checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" | ||
1733 | 1755 | ||
1734 | [[package]] | 1756 | [[package]] |
1735 | name = "toolchain" | 1757 | name = "toolchain" |
@@ -1740,11 +1762,11 @@ dependencies = [ | |||
1740 | 1762 | ||
1741 | [[package]] | 1763 | [[package]] |
1742 | name = "tracing" | 1764 | name = "tracing" |
1743 | version = "0.1.21" | 1765 | version = "0.1.22" |
1744 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1766 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1745 | checksum = "b0987850db3733619253fe60e17cb59b82d37c7e6c0236bb81e4d6b87c879f27" | 1767 | checksum = "9f47026cdc4080c07e49b37087de021820269d996f581aac150ef9e5583eefe3" |
1746 | dependencies = [ | 1768 | dependencies = [ |
1747 | "cfg-if 0.1.10", | 1769 | "cfg-if 1.0.0", |
1748 | "pin-project-lite", | 1770 | "pin-project-lite", |
1749 | "tracing-attributes", | 1771 | "tracing-attributes", |
1750 | "tracing-core", | 1772 | "tracing-core", |
@@ -1867,18 +1889,18 @@ dependencies = [ | |||
1867 | 1889 | ||
1868 | [[package]] | 1890 | [[package]] |
1869 | name = "unicode-normalization" | 1891 | name = "unicode-normalization" |
1870 | version = "0.1.13" | 1892 | version = "0.1.16" |
1871 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1893 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1872 | checksum = "6fb19cf769fa8c6a80a162df694621ebeb4dafb606470b2b2fce0be40a98a977" | 1894 | checksum = "a13e63ab62dbe32aeee58d1c5408d35c36c392bba5d9d3142287219721afe606" |
1873 | dependencies = [ | 1895 | dependencies = [ |
1874 | "tinyvec", | 1896 | "tinyvec", |
1875 | ] | 1897 | ] |
1876 | 1898 | ||
1877 | [[package]] | 1899 | [[package]] |
1878 | name = "unicode-segmentation" | 1900 | name = "unicode-segmentation" |
1879 | version = "1.6.0" | 1901 | version = "1.7.0" |
1880 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1902 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1881 | checksum = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0" | 1903 | checksum = "db8716a166f290ff49dabc18b44aa407cb7c6dbe1aa0971b44b8a24b0ca35aae" |
1882 | 1904 | ||
1883 | [[package]] | 1905 | [[package]] |
1884 | name = "unicode-xid" | 1906 | name = "unicode-xid" |
@@ -1888,10 +1910,11 @@ checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" | |||
1888 | 1910 | ||
1889 | [[package]] | 1911 | [[package]] |
1890 | name = "url" | 1912 | name = "url" |
1891 | version = "2.1.1" | 1913 | version = "2.2.0" |
1892 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1914 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1893 | checksum = "829d4a8476c35c9bf0bbce5a3b23f4106f79728039b726d292bb93bc106787cb" | 1915 | checksum = "5909f2b0817350449ed73e8bcd81c8c3c8d9a7a5d8acba4b27db277f1868976e" |
1894 | dependencies = [ | 1916 | dependencies = [ |
1917 | "form_urlencoded", | ||
1895 | "idna", | 1918 | "idna", |
1896 | "matches", | 1919 | "matches", |
1897 | "percent-encoding", | 1920 | "percent-encoding", |
diff --git a/crates/assists/src/assist_context.rs b/crates/assists/src/assist_context.rs index fcfe2d6ee..69499ea32 100644 --- a/crates/assists/src/assist_context.rs +++ b/crates/assists/src/assist_context.rs | |||
@@ -208,7 +208,7 @@ pub(crate) struct AssistBuilder { | |||
208 | edit: TextEditBuilder, | 208 | edit: TextEditBuilder, |
209 | file_id: FileId, | 209 | file_id: FileId, |
210 | is_snippet: bool, | 210 | is_snippet: bool, |
211 | change: SourceChange, | 211 | source_file_edits: Vec<SourceFileEdit>, |
212 | } | 212 | } |
213 | 213 | ||
214 | impl AssistBuilder { | 214 | impl AssistBuilder { |
@@ -217,20 +217,27 @@ impl AssistBuilder { | |||
217 | edit: TextEdit::builder(), | 217 | edit: TextEdit::builder(), |
218 | file_id, | 218 | file_id, |
219 | is_snippet: false, | 219 | is_snippet: false, |
220 | change: SourceChange::default(), | 220 | source_file_edits: Vec::default(), |
221 | } | 221 | } |
222 | } | 222 | } |
223 | 223 | ||
224 | pub(crate) fn edit_file(&mut self, file_id: FileId) { | 224 | pub(crate) fn edit_file(&mut self, file_id: FileId) { |
225 | self.commit(); | ||
225 | self.file_id = file_id; | 226 | self.file_id = file_id; |
226 | } | 227 | } |
227 | 228 | ||
228 | fn commit(&mut self) { | 229 | fn commit(&mut self) { |
229 | let edit = mem::take(&mut self.edit).finish(); | 230 | let edit = mem::take(&mut self.edit).finish(); |
230 | if !edit.is_empty() { | 231 | if !edit.is_empty() { |
231 | let new_edit = SourceFileEdit { file_id: self.file_id, edit }; | 232 | match self.source_file_edits.binary_search_by_key(&self.file_id, |edit| edit.file_id) { |
232 | assert!(!self.change.source_file_edits.iter().any(|it| it.file_id == new_edit.file_id)); | 233 | Ok(idx) => self.source_file_edits[idx] |
233 | self.change.source_file_edits.push(new_edit); | 234 | .edit |
235 | .union(edit) | ||
236 | .expect("overlapping edits for same file"), | ||
237 | Err(idx) => self | ||
238 | .source_file_edits | ||
239 | .insert(idx, SourceFileEdit { file_id: self.file_id, edit }), | ||
240 | } | ||
234 | } | 241 | } |
235 | } | 242 | } |
236 | 243 | ||
@@ -270,23 +277,18 @@ impl AssistBuilder { | |||
270 | algo::diff(old.syntax(), new.syntax()).into_text_edit(&mut self.edit) | 277 | algo::diff(old.syntax(), new.syntax()).into_text_edit(&mut self.edit) |
271 | } | 278 | } |
272 | pub(crate) fn rewrite(&mut self, rewriter: SyntaxRewriter) { | 279 | pub(crate) fn rewrite(&mut self, rewriter: SyntaxRewriter) { |
273 | let node = rewriter.rewrite_root().unwrap(); | 280 | if let Some(node) = rewriter.rewrite_root() { |
274 | let new = rewriter.rewrite(&node); | 281 | let new = rewriter.rewrite(&node); |
275 | algo::diff(&node, &new).into_text_edit(&mut self.edit); | 282 | algo::diff(&node, &new).into_text_edit(&mut self.edit); |
276 | } | 283 | } |
277 | |||
278 | // FIXME: kill this API | ||
279 | /// Get access to the raw `TextEditBuilder`. | ||
280 | pub(crate) fn text_edit_builder(&mut self) -> &mut TextEditBuilder { | ||
281 | &mut self.edit | ||
282 | } | 284 | } |
283 | 285 | ||
284 | fn finish(mut self) -> SourceChange { | 286 | fn finish(mut self) -> SourceChange { |
285 | self.commit(); | 287 | self.commit(); |
286 | let mut change = mem::take(&mut self.change); | 288 | SourceChange { |
287 | if self.is_snippet { | 289 | source_file_edits: mem::take(&mut self.source_file_edits), |
288 | change.is_snippet = true; | 290 | file_system_edits: Default::default(), |
291 | is_snippet: self.is_snippet, | ||
289 | } | 292 | } |
290 | change | ||
291 | } | 293 | } |
292 | } | 294 | } |
diff --git a/crates/assists/src/handlers/add_custom_impl.rs b/crates/assists/src/handlers/add_custom_impl.rs deleted file mode 100644 index 669dd9b21..000000000 --- a/crates/assists/src/handlers/add_custom_impl.rs +++ /dev/null | |||
@@ -1,284 +0,0 @@ | |||
1 | use ide_db::imports_locator; | ||
2 | use itertools::Itertools; | ||
3 | use syntax::{ | ||
4 | ast::{self, make, AstNode}, | ||
5 | Direction, SmolStr, | ||
6 | SyntaxKind::{IDENT, WHITESPACE}, | ||
7 | TextRange, TextSize, | ||
8 | }; | ||
9 | |||
10 | use crate::{ | ||
11 | assist_config::SnippetCap, | ||
12 | assist_context::{AssistBuilder, AssistContext, Assists}, | ||
13 | utils::mod_path_to_ast, | ||
14 | AssistId, AssistKind, | ||
15 | }; | ||
16 | |||
17 | // Assist: add_custom_impl | ||
18 | // | ||
19 | // Adds impl block for derived trait. | ||
20 | // | ||
21 | // ``` | ||
22 | // #[derive(Deb<|>ug, Display)] | ||
23 | // struct S; | ||
24 | // ``` | ||
25 | // -> | ||
26 | // ``` | ||
27 | // #[derive(Display)] | ||
28 | // struct S; | ||
29 | // | ||
30 | // impl Debug for S { | ||
31 | // $0 | ||
32 | // } | ||
33 | // ``` | ||
34 | pub(crate) fn add_custom_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | ||
35 | let attr = ctx.find_node_at_offset::<ast::Attr>()?; | ||
36 | |||
37 | let attr_name = attr | ||
38 | .syntax() | ||
39 | .descendants_with_tokens() | ||
40 | .filter(|t| t.kind() == IDENT) | ||
41 | .find_map(syntax::NodeOrToken::into_token) | ||
42 | .filter(|t| t.text() == "derive")? | ||
43 | .text() | ||
44 | .clone(); | ||
45 | |||
46 | let trait_token = | ||
47 | ctx.token_at_offset().find(|t| t.kind() == IDENT && *t.text() != attr_name)?; | ||
48 | let trait_path = make::path_unqualified(make::path_segment(make::name_ref(trait_token.text()))); | ||
49 | |||
50 | let annotated = attr.syntax().siblings(Direction::Next).find_map(ast::Name::cast)?; | ||
51 | let annotated_name = annotated.syntax().text().to_string(); | ||
52 | let insert_pos = annotated.syntax().parent()?.text_range().end(); | ||
53 | |||
54 | let current_module = ctx.sema.scope(annotated.syntax()).module()?; | ||
55 | let current_crate = current_module.krate(); | ||
56 | |||
57 | let found_traits = imports_locator::find_imports(&ctx.sema, current_crate, trait_token.text()) | ||
58 | .into_iter() | ||
59 | .filter_map(|candidate: either::Either<hir::ModuleDef, hir::MacroDef>| match candidate { | ||
60 | either::Either::Left(hir::ModuleDef::Trait(trait_)) => Some(trait_), | ||
61 | _ => None, | ||
62 | }) | ||
63 | .flat_map(|trait_| { | ||
64 | current_module | ||
65 | .find_use_path(ctx.sema.db, hir::ModuleDef::Trait(trait_)) | ||
66 | .as_ref() | ||
67 | .map(mod_path_to_ast) | ||
68 | .zip(Some(trait_)) | ||
69 | }); | ||
70 | |||
71 | let mut no_traits_found = true; | ||
72 | for (trait_path, _trait) in found_traits.inspect(|_| no_traits_found = false) { | ||
73 | add_assist(acc, ctx.config.snippet_cap, &attr, &trait_path, &annotated_name, insert_pos)?; | ||
74 | } | ||
75 | if no_traits_found { | ||
76 | add_assist(acc, ctx.config.snippet_cap, &attr, &trait_path, &annotated_name, insert_pos)?; | ||
77 | } | ||
78 | Some(()) | ||
79 | } | ||
80 | |||
81 | fn add_assist( | ||
82 | acc: &mut Assists, | ||
83 | snippet_cap: Option<SnippetCap>, | ||
84 | attr: &ast::Attr, | ||
85 | trait_path: &ast::Path, | ||
86 | annotated_name: &str, | ||
87 | insert_pos: TextSize, | ||
88 | ) -> Option<()> { | ||
89 | let target = attr.syntax().text_range(); | ||
90 | let input = attr.token_tree()?; | ||
91 | let label = format!("Add custom impl `{}` for `{}`", trait_path, annotated_name); | ||
92 | let trait_name = trait_path.segment().and_then(|seg| seg.name_ref())?; | ||
93 | |||
94 | acc.add(AssistId("add_custom_impl", AssistKind::Refactor), label, target, |builder| { | ||
95 | update_attribute(builder, &input, &trait_name, &attr); | ||
96 | match snippet_cap { | ||
97 | Some(cap) => { | ||
98 | builder.insert_snippet( | ||
99 | cap, | ||
100 | insert_pos, | ||
101 | format!("\n\nimpl {} for {} {{\n $0\n}}", trait_path, annotated_name), | ||
102 | ); | ||
103 | } | ||
104 | None => { | ||
105 | builder.insert( | ||
106 | insert_pos, | ||
107 | format!("\n\nimpl {} for {} {{\n\n}}", trait_path, annotated_name), | ||
108 | ); | ||
109 | } | ||
110 | } | ||
111 | }) | ||
112 | } | ||
113 | |||
114 | fn update_attribute( | ||
115 | builder: &mut AssistBuilder, | ||
116 | input: &ast::TokenTree, | ||
117 | trait_name: &ast::NameRef, | ||
118 | attr: &ast::Attr, | ||
119 | ) { | ||
120 | let new_attr_input = input | ||
121 | .syntax() | ||
122 | .descendants_with_tokens() | ||
123 | .filter(|t| t.kind() == IDENT) | ||
124 | .filter_map(|t| t.into_token().map(|t| t.text().clone())) | ||
125 | .filter(|t| t != trait_name.text()) | ||
126 | .collect::<Vec<SmolStr>>(); | ||
127 | let has_more_derives = !new_attr_input.is_empty(); | ||
128 | |||
129 | if has_more_derives { | ||
130 | let new_attr_input = format!("({})", new_attr_input.iter().format(", ")); | ||
131 | builder.replace(input.syntax().text_range(), new_attr_input); | ||
132 | } else { | ||
133 | let attr_range = attr.syntax().text_range(); | ||
134 | builder.delete(attr_range); | ||
135 | |||
136 | let line_break_range = attr | ||
137 | .syntax() | ||
138 | .next_sibling_or_token() | ||
139 | .filter(|t| t.kind() == WHITESPACE) | ||
140 | .map(|t| t.text_range()) | ||
141 | .unwrap_or_else(|| TextRange::new(TextSize::from(0), TextSize::from(0))); | ||
142 | builder.delete(line_break_range); | ||
143 | } | ||
144 | } | ||
145 | |||
146 | #[cfg(test)] | ||
147 | mod tests { | ||
148 | use crate::tests::{check_assist, check_assist_not_applicable}; | ||
149 | |||
150 | use super::*; | ||
151 | |||
152 | #[test] | ||
153 | fn add_custom_impl_qualified() { | ||
154 | check_assist( | ||
155 | add_custom_impl, | ||
156 | " | ||
157 | mod fmt { | ||
158 | pub trait Debug {} | ||
159 | } | ||
160 | |||
161 | #[derive(Debu<|>g)] | ||
162 | struct Foo { | ||
163 | bar: String, | ||
164 | } | ||
165 | ", | ||
166 | " | ||
167 | mod fmt { | ||
168 | pub trait Debug {} | ||
169 | } | ||
170 | |||
171 | struct Foo { | ||
172 | bar: String, | ||
173 | } | ||
174 | |||
175 | impl fmt::Debug for Foo { | ||
176 | $0 | ||
177 | } | ||
178 | ", | ||
179 | ) | ||
180 | } | ||
181 | #[test] | ||
182 | fn add_custom_impl_for_unique_input() { | ||
183 | check_assist( | ||
184 | add_custom_impl, | ||
185 | " | ||
186 | #[derive(Debu<|>g)] | ||
187 | struct Foo { | ||
188 | bar: String, | ||
189 | } | ||
190 | ", | ||
191 | " | ||
192 | struct Foo { | ||
193 | bar: String, | ||
194 | } | ||
195 | |||
196 | impl Debug for Foo { | ||
197 | $0 | ||
198 | } | ||
199 | ", | ||
200 | ) | ||
201 | } | ||
202 | |||
203 | #[test] | ||
204 | fn add_custom_impl_for_with_visibility_modifier() { | ||
205 | check_assist( | ||
206 | add_custom_impl, | ||
207 | " | ||
208 | #[derive(Debug<|>)] | ||
209 | pub struct Foo { | ||
210 | bar: String, | ||
211 | } | ||
212 | ", | ||
213 | " | ||
214 | pub struct Foo { | ||
215 | bar: String, | ||
216 | } | ||
217 | |||
218 | impl Debug for Foo { | ||
219 | $0 | ||
220 | } | ||
221 | ", | ||
222 | ) | ||
223 | } | ||
224 | |||
225 | #[test] | ||
226 | fn add_custom_impl_when_multiple_inputs() { | ||
227 | check_assist( | ||
228 | add_custom_impl, | ||
229 | " | ||
230 | #[derive(Display, Debug<|>, Serialize)] | ||
231 | struct Foo {} | ||
232 | ", | ||
233 | " | ||
234 | #[derive(Display, Serialize)] | ||
235 | struct Foo {} | ||
236 | |||
237 | impl Debug for Foo { | ||
238 | $0 | ||
239 | } | ||
240 | ", | ||
241 | ) | ||
242 | } | ||
243 | |||
244 | #[test] | ||
245 | fn test_ignore_derive_macro_without_input() { | ||
246 | check_assist_not_applicable( | ||
247 | add_custom_impl, | ||
248 | " | ||
249 | #[derive(<|>)] | ||
250 | struct Foo {} | ||
251 | ", | ||
252 | ) | ||
253 | } | ||
254 | |||
255 | #[test] | ||
256 | fn test_ignore_if_cursor_on_param() { | ||
257 | check_assist_not_applicable( | ||
258 | add_custom_impl, | ||
259 | " | ||
260 | #[derive<|>(Debug)] | ||
261 | struct Foo {} | ||
262 | ", | ||
263 | ); | ||
264 | |||
265 | check_assist_not_applicable( | ||
266 | add_custom_impl, | ||
267 | " | ||
268 | #[derive(Debug)<|>] | ||
269 | struct Foo {} | ||
270 | ", | ||
271 | ) | ||
272 | } | ||
273 | |||
274 | #[test] | ||
275 | fn test_ignore_if_not_derive() { | ||
276 | check_assist_not_applicable( | ||
277 | add_custom_impl, | ||
278 | " | ||
279 | #[allow(non_camel_<|>case_types)] | ||
280 | struct Foo {} | ||
281 | ", | ||
282 | ) | ||
283 | } | ||
284 | } | ||
diff --git a/crates/assists/src/handlers/add_missing_impl_members.rs b/crates/assists/src/handlers/add_missing_impl_members.rs index b82fb30ad..bbb71e261 100644 --- a/crates/assists/src/handlers/add_missing_impl_members.rs +++ b/crates/assists/src/handlers/add_missing_impl_members.rs | |||
@@ -1,27 +1,14 @@ | |||
1 | use hir::HasSource; | 1 | use ide_db::traits::resolve_target_trait; |
2 | use ide_db::traits::{get_missing_assoc_items, resolve_target_trait}; | 2 | use syntax::ast::{self, AstNode}; |
3 | use syntax::{ | ||
4 | ast::{ | ||
5 | self, | ||
6 | edit::{self, AstNodeEdit, IndentLevel}, | ||
7 | make, AstNode, NameOwner, | ||
8 | }, | ||
9 | SmolStr, | ||
10 | }; | ||
11 | 3 | ||
12 | use crate::{ | 4 | use crate::{ |
13 | assist_context::{AssistContext, Assists}, | 5 | assist_context::{AssistContext, Assists}, |
14 | ast_transform::{self, AstTransform, QualifyPaths, SubstituteTypeParams}, | 6 | utils::add_trait_assoc_items_to_impl, |
15 | utils::{render_snippet, Cursor}, | 7 | utils::DefaultMethods, |
8 | utils::{filter_assoc_items, render_snippet, Cursor}, | ||
16 | AssistId, AssistKind, | 9 | AssistId, AssistKind, |
17 | }; | 10 | }; |
18 | 11 | ||
19 | #[derive(PartialEq)] | ||
20 | enum AddMissingImplMembersMode { | ||
21 | DefaultMethodsOnly, | ||
22 | NoDefaultMethods, | ||
23 | } | ||
24 | |||
25 | // Assist: add_impl_missing_members | 12 | // Assist: add_impl_missing_members |
26 | // | 13 | // |
27 | // Adds scaffold for required impl members. | 14 | // Adds scaffold for required impl members. |
@@ -55,7 +42,7 @@ pub(crate) fn add_missing_impl_members(acc: &mut Assists, ctx: &AssistContext) - | |||
55 | add_missing_impl_members_inner( | 42 | add_missing_impl_members_inner( |
56 | acc, | 43 | acc, |
57 | ctx, | 44 | ctx, |
58 | AddMissingImplMembersMode::NoDefaultMethods, | 45 | DefaultMethods::No, |
59 | "add_impl_missing_members", | 46 | "add_impl_missing_members", |
60 | "Implement missing members", | 47 | "Implement missing members", |
61 | ) | 48 | ) |
@@ -97,7 +84,7 @@ pub(crate) fn add_missing_default_members(acc: &mut Assists, ctx: &AssistContext | |||
97 | add_missing_impl_members_inner( | 84 | add_missing_impl_members_inner( |
98 | acc, | 85 | acc, |
99 | ctx, | 86 | ctx, |
100 | AddMissingImplMembersMode::DefaultMethodsOnly, | 87 | DefaultMethods::Only, |
101 | "add_impl_default_members", | 88 | "add_impl_default_members", |
102 | "Implement default members", | 89 | "Implement default members", |
103 | ) | 90 | ) |
@@ -106,7 +93,7 @@ pub(crate) fn add_missing_default_members(acc: &mut Assists, ctx: &AssistContext | |||
106 | fn add_missing_impl_members_inner( | 93 | fn add_missing_impl_members_inner( |
107 | acc: &mut Assists, | 94 | acc: &mut Assists, |
108 | ctx: &AssistContext, | 95 | ctx: &AssistContext, |
109 | mode: AddMissingImplMembersMode, | 96 | mode: DefaultMethods, |
110 | assist_id: &'static str, | 97 | assist_id: &'static str, |
111 | label: &'static str, | 98 | label: &'static str, |
112 | ) -> Option<()> { | 99 | ) -> Option<()> { |
@@ -114,32 +101,11 @@ fn add_missing_impl_members_inner( | |||
114 | let impl_def = ctx.find_node_at_offset::<ast::Impl>()?; | 101 | let impl_def = ctx.find_node_at_offset::<ast::Impl>()?; |
115 | let trait_ = resolve_target_trait(&ctx.sema, &impl_def)?; | 102 | let trait_ = resolve_target_trait(&ctx.sema, &impl_def)?; |
116 | 103 | ||
117 | let def_name = |item: &ast::AssocItem| -> Option<SmolStr> { | 104 | let missing_items = filter_assoc_items( |
118 | match item { | 105 | ctx.db(), |
119 | ast::AssocItem::Fn(def) => def.name(), | 106 | &ide_db::traits::get_missing_assoc_items(&ctx.sema, &impl_def), |
120 | ast::AssocItem::TypeAlias(def) => def.name(), | 107 | mode, |
121 | ast::AssocItem::Const(def) => def.name(), | 108 | ); |
122 | ast::AssocItem::MacroCall(_) => None, | ||
123 | } | ||
124 | .map(|it| it.text().clone()) | ||
125 | }; | ||
126 | |||
127 | let missing_items = get_missing_assoc_items(&ctx.sema, &impl_def) | ||
128 | .iter() | ||
129 | .map(|i| match i { | ||
130 | hir::AssocItem::Function(i) => ast::AssocItem::Fn(i.source(ctx.db()).value), | ||
131 | hir::AssocItem::TypeAlias(i) => ast::AssocItem::TypeAlias(i.source(ctx.db()).value), | ||
132 | hir::AssocItem::Const(i) => ast::AssocItem::Const(i.source(ctx.db()).value), | ||
133 | }) | ||
134 | .filter(|t| def_name(&t).is_some()) | ||
135 | .filter(|t| match t { | ||
136 | ast::AssocItem::Fn(def) => match mode { | ||
137 | AddMissingImplMembersMode::DefaultMethodsOnly => def.body().is_some(), | ||
138 | AddMissingImplMembersMode::NoDefaultMethods => def.body().is_none(), | ||
139 | }, | ||
140 | _ => mode == AddMissingImplMembersMode::NoDefaultMethods, | ||
141 | }) | ||
142 | .collect::<Vec<_>>(); | ||
143 | 109 | ||
144 | if missing_items.is_empty() { | 110 | if missing_items.is_empty() { |
145 | return None; | 111 | return None; |
@@ -147,29 +113,9 @@ fn add_missing_impl_members_inner( | |||
147 | 113 | ||
148 | let target = impl_def.syntax().text_range(); | 114 | let target = impl_def.syntax().text_range(); |
149 | acc.add(AssistId(assist_id, AssistKind::QuickFix), label, target, |builder| { | 115 | acc.add(AssistId(assist_id, AssistKind::QuickFix), label, target, |builder| { |
150 | let impl_item_list = impl_def.assoc_item_list().unwrap_or_else(make::assoc_item_list); | ||
151 | |||
152 | let n_existing_items = impl_item_list.assoc_items().count(); | ||
153 | let source_scope = ctx.sema.scope_for_def(trait_); | ||
154 | let target_scope = ctx.sema.scope(impl_def.syntax()); | 116 | let target_scope = ctx.sema.scope(impl_def.syntax()); |
155 | let ast_transform = QualifyPaths::new(&target_scope, &source_scope) | 117 | let (new_impl_def, first_new_item) = |
156 | .or(SubstituteTypeParams::for_trait_impl(&source_scope, trait_, impl_def.clone())); | 118 | add_trait_assoc_items_to_impl(&ctx.sema, missing_items, trait_, impl_def, target_scope); |
157 | |||
158 | let items = missing_items | ||
159 | .into_iter() | ||
160 | .map(|it| ast_transform::apply(&*ast_transform, it)) | ||
161 | .map(|it| match it { | ||
162 | ast::AssocItem::Fn(def) => ast::AssocItem::Fn(add_body(def)), | ||
163 | ast::AssocItem::TypeAlias(def) => ast::AssocItem::TypeAlias(def.remove_bounds()), | ||
164 | _ => it, | ||
165 | }) | ||
166 | .map(|it| edit::remove_attrs_and_docs(&it)); | ||
167 | |||
168 | let new_impl_item_list = impl_item_list.append_items(items); | ||
169 | let new_impl_def = impl_def.with_assoc_item_list(new_impl_item_list); | ||
170 | let first_new_item = | ||
171 | new_impl_def.assoc_item_list().unwrap().assoc_items().nth(n_existing_items).unwrap(); | ||
172 | |||
173 | match ctx.config.snippet_cap { | 119 | match ctx.config.snippet_cap { |
174 | None => builder.replace(target, new_impl_def.to_string()), | 120 | None => builder.replace(target, new_impl_def.to_string()), |
175 | Some(cap) => { | 121 | Some(cap) => { |
@@ -193,14 +139,6 @@ fn add_missing_impl_members_inner( | |||
193 | }) | 139 | }) |
194 | } | 140 | } |
195 | 141 | ||
196 | fn add_body(fn_def: ast::Fn) -> ast::Fn { | ||
197 | if fn_def.body().is_some() { | ||
198 | return fn_def; | ||
199 | } | ||
200 | let body = make::block_expr(None, Some(make::expr_todo())).indent(IndentLevel(1)); | ||
201 | fn_def.with_body(body) | ||
202 | } | ||
203 | |||
204 | #[cfg(test)] | 142 | #[cfg(test)] |
205 | mod tests { | 143 | mod tests { |
206 | use crate::tests::{check_assist, check_assist_not_applicable}; | 144 | use crate::tests::{check_assist, check_assist_not_applicable}; |
diff --git a/crates/assists/src/handlers/auto_import.rs b/crates/assists/src/handlers/auto_import.rs index 37dd61266..d665837a2 100644 --- a/crates/assists/src/handlers/auto_import.rs +++ b/crates/assists/src/handlers/auto_import.rs | |||
@@ -98,7 +98,8 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()> | |||
98 | 98 | ||
99 | let range = ctx.sema.original_range(import_assets.syntax_under_caret()).range; | 99 | let range = ctx.sema.original_range(import_assets.syntax_under_caret()).range; |
100 | let group = import_group_message(import_assets.import_candidate()); | 100 | let group = import_group_message(import_assets.import_candidate()); |
101 | let scope = ImportScope::find_insert_use_container(import_assets.syntax_under_caret(), ctx)?; | 101 | let scope = |
102 | ImportScope::find_insert_use_container(import_assets.syntax_under_caret(), &ctx.sema)?; | ||
102 | for (import, _) in proposed_imports { | 103 | for (import, _) in proposed_imports { |
103 | acc.add_group( | 104 | acc.add_group( |
104 | &group, | 105 | &group, |
diff --git a/crates/assists/src/handlers/change_return_type_to_result.rs b/crates/assists/src/handlers/change_return_type_to_result.rs deleted file mode 100644 index 76f33a5b6..000000000 --- a/crates/assists/src/handlers/change_return_type_to_result.rs +++ /dev/null | |||
@@ -1,1091 +0,0 @@ | |||
1 | use std::iter; | ||
2 | |||
3 | use syntax::{ | ||
4 | ast::{self, make, BlockExpr, Expr, LoopBodyOwner}, | ||
5 | match_ast, AstNode, SyntaxNode, | ||
6 | }; | ||
7 | use test_utils::mark; | ||
8 | |||
9 | use crate::{AssistContext, AssistId, AssistKind, Assists}; | ||
10 | |||
11 | // Assist: change_return_type_to_result | ||
12 | // | ||
13 | // Change the function's return type to Result. | ||
14 | // | ||
15 | // ``` | ||
16 | // fn foo() -> i32<|> { 42i32 } | ||
17 | // ``` | ||
18 | // -> | ||
19 | // ``` | ||
20 | // fn foo() -> Result<i32, ${0:_}> { Ok(42i32) } | ||
21 | // ``` | ||
22 | pub(crate) fn change_return_type_to_result(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | ||
23 | let ret_type = ctx.find_node_at_offset::<ast::RetType>()?; | ||
24 | let parent = ret_type.syntax().parent()?; | ||
25 | let block_expr = match_ast! { | ||
26 | match parent { | ||
27 | ast::Fn(func) => func.body()?, | ||
28 | ast::ClosureExpr(closure) => match closure.body()? { | ||
29 | Expr::BlockExpr(block) => block, | ||
30 | // closures require a block when a return type is specified | ||
31 | _ => return None, | ||
32 | }, | ||
33 | _ => return None, | ||
34 | } | ||
35 | }; | ||
36 | |||
37 | let type_ref = &ret_type.ty()?; | ||
38 | let ret_type_str = type_ref.syntax().text().to_string(); | ||
39 | let first_part_ret_type = ret_type_str.splitn(2, '<').next(); | ||
40 | if let Some(ret_type_first_part) = first_part_ret_type { | ||
41 | if ret_type_first_part.ends_with("Result") { | ||
42 | mark::hit!(change_return_type_to_result_simple_return_type_already_result); | ||
43 | return None; | ||
44 | } | ||
45 | } | ||
46 | |||
47 | acc.add( | ||
48 | AssistId("change_return_type_to_result", AssistKind::RefactorRewrite), | ||
49 | "Wrap return type in Result", | ||
50 | type_ref.syntax().text_range(), | ||
51 | |builder| { | ||
52 | let mut tail_return_expr_collector = TailReturnCollector::new(); | ||
53 | tail_return_expr_collector.collect_jump_exprs(&block_expr, false); | ||
54 | tail_return_expr_collector.collect_tail_exprs(&block_expr); | ||
55 | |||
56 | for ret_expr_arg in tail_return_expr_collector.exprs_to_wrap { | ||
57 | let ok_wrapped = make::expr_call( | ||
58 | make::expr_path(make::path_unqualified(make::path_segment(make::name_ref( | ||
59 | "Ok", | ||
60 | )))), | ||
61 | make::arg_list(iter::once(ret_expr_arg.clone())), | ||
62 | ); | ||
63 | builder.replace_ast(ret_expr_arg, ok_wrapped); | ||
64 | } | ||
65 | |||
66 | match ctx.config.snippet_cap { | ||
67 | Some(cap) => { | ||
68 | let snippet = format!("Result<{}, ${{0:_}}>", type_ref); | ||
69 | builder.replace_snippet(cap, type_ref.syntax().text_range(), snippet) | ||
70 | } | ||
71 | None => builder | ||
72 | .replace(type_ref.syntax().text_range(), format!("Result<{}, _>", type_ref)), | ||
73 | } | ||
74 | }, | ||
75 | ) | ||
76 | } | ||
77 | |||
78 | struct TailReturnCollector { | ||
79 | exprs_to_wrap: Vec<ast::Expr>, | ||
80 | } | ||
81 | |||
82 | impl TailReturnCollector { | ||
83 | fn new() -> Self { | ||
84 | Self { exprs_to_wrap: vec![] } | ||
85 | } | ||
86 | /// Collect all`return` expression | ||
87 | fn collect_jump_exprs(&mut self, block_expr: &BlockExpr, collect_break: bool) { | ||
88 | let statements = block_expr.statements(); | ||
89 | for stmt in statements { | ||
90 | let expr = match &stmt { | ||
91 | ast::Stmt::ExprStmt(stmt) => stmt.expr(), | ||
92 | ast::Stmt::LetStmt(stmt) => stmt.initializer(), | ||
93 | ast::Stmt::Item(_) => continue, | ||
94 | }; | ||
95 | if let Some(expr) = &expr { | ||
96 | self.handle_exprs(expr, collect_break); | ||
97 | } | ||
98 | } | ||
99 | |||
100 | // Browse tail expressions for each block | ||
101 | if let Some(expr) = block_expr.expr() { | ||
102 | if let Some(last_exprs) = get_tail_expr_from_block(&expr) { | ||
103 | for last_expr in last_exprs { | ||
104 | let last_expr = match last_expr { | ||
105 | NodeType::Node(expr) => expr, | ||
106 | NodeType::Leaf(expr) => expr.syntax().clone(), | ||
107 | }; | ||
108 | |||
109 | if let Some(last_expr) = Expr::cast(last_expr.clone()) { | ||
110 | self.handle_exprs(&last_expr, collect_break); | ||
111 | } else if let Some(expr_stmt) = ast::Stmt::cast(last_expr) { | ||
112 | let expr_stmt = match &expr_stmt { | ||
113 | ast::Stmt::ExprStmt(stmt) => stmt.expr(), | ||
114 | ast::Stmt::LetStmt(stmt) => stmt.initializer(), | ||
115 | ast::Stmt::Item(_) => None, | ||
116 | }; | ||
117 | if let Some(expr) = &expr_stmt { | ||
118 | self.handle_exprs(expr, collect_break); | ||
119 | } | ||
120 | } | ||
121 | } | ||
122 | } | ||
123 | } | ||
124 | } | ||
125 | |||
126 | fn handle_exprs(&mut self, expr: &Expr, collect_break: bool) { | ||
127 | match expr { | ||
128 | Expr::BlockExpr(block_expr) => { | ||
129 | self.collect_jump_exprs(&block_expr, collect_break); | ||
130 | } | ||
131 | Expr::ReturnExpr(ret_expr) => { | ||
132 | if let Some(ret_expr_arg) = &ret_expr.expr() { | ||
133 | self.exprs_to_wrap.push(ret_expr_arg.clone()); | ||
134 | } | ||
135 | } | ||
136 | Expr::BreakExpr(break_expr) if collect_break => { | ||
137 | if let Some(break_expr_arg) = &break_expr.expr() { | ||
138 | self.exprs_to_wrap.push(break_expr_arg.clone()); | ||
139 | } | ||
140 | } | ||
141 | Expr::IfExpr(if_expr) => { | ||
142 | for block in if_expr.blocks() { | ||
143 | self.collect_jump_exprs(&block, collect_break); | ||
144 | } | ||
145 | } | ||
146 | Expr::LoopExpr(loop_expr) => { | ||
147 | if let Some(block_expr) = loop_expr.loop_body() { | ||
148 | self.collect_jump_exprs(&block_expr, collect_break); | ||
149 | } | ||
150 | } | ||
151 | Expr::ForExpr(for_expr) => { | ||
152 | if let Some(block_expr) = for_expr.loop_body() { | ||
153 | self.collect_jump_exprs(&block_expr, collect_break); | ||
154 | } | ||
155 | } | ||
156 | Expr::WhileExpr(while_expr) => { | ||
157 | if let Some(block_expr) = while_expr.loop_body() { | ||
158 | self.collect_jump_exprs(&block_expr, collect_break); | ||
159 | } | ||
160 | } | ||
161 | Expr::MatchExpr(match_expr) => { | ||
162 | if let Some(arm_list) = match_expr.match_arm_list() { | ||
163 | arm_list.arms().filter_map(|match_arm| match_arm.expr()).for_each(|expr| { | ||
164 | self.handle_exprs(&expr, collect_break); | ||
165 | }); | ||
166 | } | ||
167 | } | ||
168 | _ => {} | ||
169 | } | ||
170 | } | ||
171 | |||
172 | fn collect_tail_exprs(&mut self, block: &BlockExpr) { | ||
173 | if let Some(expr) = block.expr() { | ||
174 | self.handle_exprs(&expr, true); | ||
175 | self.fetch_tail_exprs(&expr); | ||
176 | } | ||
177 | } | ||
178 | |||
179 | fn fetch_tail_exprs(&mut self, expr: &Expr) { | ||
180 | if let Some(exprs) = get_tail_expr_from_block(expr) { | ||
181 | for node_type in &exprs { | ||
182 | match node_type { | ||
183 | NodeType::Leaf(expr) => { | ||
184 | self.exprs_to_wrap.push(expr.clone()); | ||
185 | } | ||
186 | NodeType::Node(expr) => { | ||
187 | if let Some(last_expr) = Expr::cast(expr.clone()) { | ||
188 | self.fetch_tail_exprs(&last_expr); | ||
189 | } | ||
190 | } | ||
191 | } | ||
192 | } | ||
193 | } | ||
194 | } | ||
195 | } | ||
196 | |||
197 | #[derive(Debug)] | ||
198 | enum NodeType { | ||
199 | Leaf(ast::Expr), | ||
200 | Node(SyntaxNode), | ||
201 | } | ||
202 | |||
203 | /// Get a tail expression inside a block | ||
204 | fn get_tail_expr_from_block(expr: &Expr) -> Option<Vec<NodeType>> { | ||
205 | match expr { | ||
206 | Expr::IfExpr(if_expr) => { | ||
207 | let mut nodes = vec![]; | ||
208 | for block in if_expr.blocks() { | ||
209 | if let Some(block_expr) = block.expr() { | ||
210 | if let Some(tail_exprs) = get_tail_expr_from_block(&block_expr) { | ||
211 | nodes.extend(tail_exprs); | ||
212 | } | ||
213 | } else if let Some(last_expr) = block.syntax().last_child() { | ||
214 | nodes.push(NodeType::Node(last_expr)); | ||
215 | } else { | ||
216 | nodes.push(NodeType::Node(block.syntax().clone())); | ||
217 | } | ||
218 | } | ||
219 | Some(nodes) | ||
220 | } | ||
221 | Expr::LoopExpr(loop_expr) => { | ||
222 | loop_expr.syntax().last_child().map(|lc| vec![NodeType::Node(lc)]) | ||
223 | } | ||
224 | Expr::ForExpr(for_expr) => { | ||
225 | for_expr.syntax().last_child().map(|lc| vec![NodeType::Node(lc)]) | ||
226 | } | ||
227 | Expr::WhileExpr(while_expr) => { | ||
228 | while_expr.syntax().last_child().map(|lc| vec![NodeType::Node(lc)]) | ||
229 | } | ||
230 | Expr::BlockExpr(block_expr) => { | ||
231 | block_expr.expr().map(|lc| vec![NodeType::Node(lc.syntax().clone())]) | ||
232 | } | ||
233 | Expr::MatchExpr(match_expr) => { | ||
234 | let arm_list = match_expr.match_arm_list()?; | ||
235 | let arms: Vec<NodeType> = arm_list | ||
236 | .arms() | ||
237 | .filter_map(|match_arm| match_arm.expr()) | ||
238 | .map(|expr| match expr { | ||
239 | Expr::ReturnExpr(ret_expr) => NodeType::Node(ret_expr.syntax().clone()), | ||
240 | Expr::BreakExpr(break_expr) => NodeType::Node(break_expr.syntax().clone()), | ||
241 | _ => match expr.syntax().last_child() { | ||
242 | Some(last_expr) => NodeType::Node(last_expr), | ||
243 | None => NodeType::Node(expr.syntax().clone()), | ||
244 | }, | ||
245 | }) | ||
246 | .collect(); | ||
247 | |||
248 | Some(arms) | ||
249 | } | ||
250 | Expr::BreakExpr(expr) => expr.expr().map(|e| vec![NodeType::Leaf(e)]), | ||
251 | Expr::ReturnExpr(ret_expr) => Some(vec![NodeType::Node(ret_expr.syntax().clone())]), | ||
252 | |||
253 | Expr::CallExpr(_) | ||
254 | | Expr::Literal(_) | ||
255 | | Expr::TupleExpr(_) | ||
256 | | Expr::ArrayExpr(_) | ||
257 | | Expr::ParenExpr(_) | ||
258 | | Expr::PathExpr(_) | ||
259 | | Expr::RecordExpr(_) | ||
260 | | Expr::IndexExpr(_) | ||
261 | | Expr::MethodCallExpr(_) | ||
262 | | Expr::AwaitExpr(_) | ||
263 | | Expr::CastExpr(_) | ||
264 | | Expr::RefExpr(_) | ||
265 | | Expr::PrefixExpr(_) | ||
266 | | Expr::RangeExpr(_) | ||
267 | | Expr::BinExpr(_) | ||
268 | | Expr::MacroCall(_) | ||
269 | | Expr::BoxExpr(_) => Some(vec![NodeType::Leaf(expr.clone())]), | ||
270 | _ => None, | ||
271 | } | ||
272 | } | ||
273 | |||
274 | #[cfg(test)] | ||
275 | mod tests { | ||
276 | use crate::tests::{check_assist, check_assist_not_applicable}; | ||
277 | |||
278 | use super::*; | ||
279 | |||
280 | #[test] | ||
281 | fn change_return_type_to_result_simple() { | ||
282 | check_assist( | ||
283 | change_return_type_to_result, | ||
284 | r#"fn foo() -> i3<|>2 { | ||
285 | let test = "test"; | ||
286 | return 42i32; | ||
287 | }"#, | ||
288 | r#"fn foo() -> Result<i32, ${0:_}> { | ||
289 | let test = "test"; | ||
290 | return Ok(42i32); | ||
291 | }"#, | ||
292 | ); | ||
293 | } | ||
294 | |||
295 | #[test] | ||
296 | fn change_return_type_to_result_simple_closure() { | ||
297 | check_assist( | ||
298 | change_return_type_to_result, | ||
299 | r#"fn foo() { | ||
300 | || -> i32<|> { | ||
301 | let test = "test"; | ||
302 | return 42i32; | ||
303 | }; | ||
304 | }"#, | ||
305 | r#"fn foo() { | ||
306 | || -> Result<i32, ${0:_}> { | ||
307 | let test = "test"; | ||
308 | return Ok(42i32); | ||
309 | }; | ||
310 | }"#, | ||
311 | ); | ||
312 | } | ||
313 | |||
314 | #[test] | ||
315 | fn change_return_type_to_result_simple_return_type_bad_cursor() { | ||
316 | check_assist_not_applicable( | ||
317 | change_return_type_to_result, | ||
318 | r#"fn foo() -> i32 { | ||
319 | let test = "test";<|> | ||
320 | return 42i32; | ||
321 | }"#, | ||
322 | ); | ||
323 | } | ||
324 | |||
325 | #[test] | ||
326 | fn change_return_type_to_result_simple_return_type_bad_cursor_closure() { | ||
327 | check_assist_not_applicable( | ||
328 | change_return_type_to_result, | ||
329 | r#"fn foo() { | ||
330 | || -> i32 { | ||
331 | let test = "test";<|> | ||
332 | return 42i32; | ||
333 | }; | ||
334 | }"#, | ||
335 | ); | ||
336 | } | ||
337 | |||
338 | #[test] | ||
339 | fn change_return_type_to_result_closure_non_block() { | ||
340 | check_assist_not_applicable( | ||
341 | change_return_type_to_result, | ||
342 | r#"fn foo() { | ||
343 | || -> i<|>32 3; | ||
344 | }"#, | ||
345 | ); | ||
346 | } | ||
347 | |||
348 | #[test] | ||
349 | fn change_return_type_to_result_simple_return_type_already_result_std() { | ||
350 | check_assist_not_applicable( | ||
351 | change_return_type_to_result, | ||
352 | r#"fn foo() -> std::result::Result<i32<|>, String> { | ||
353 | let test = "test"; | ||
354 | return 42i32; | ||
355 | }"#, | ||
356 | ); | ||
357 | } | ||
358 | |||
359 | #[test] | ||
360 | fn change_return_type_to_result_simple_return_type_already_result() { | ||
361 | mark::check!(change_return_type_to_result_simple_return_type_already_result); | ||
362 | check_assist_not_applicable( | ||
363 | change_return_type_to_result, | ||
364 | r#"fn foo() -> Result<i32<|>, String> { | ||
365 | let test = "test"; | ||
366 | return 42i32; | ||
367 | }"#, | ||
368 | ); | ||
369 | } | ||
370 | |||
371 | #[test] | ||
372 | fn change_return_type_to_result_simple_return_type_already_result_closure() { | ||
373 | check_assist_not_applicable( | ||
374 | change_return_type_to_result, | ||
375 | r#"fn foo() { | ||
376 | || -> Result<i32<|>, String> { | ||
377 | let test = "test"; | ||
378 | return 42i32; | ||
379 | }; | ||
380 | }"#, | ||
381 | ); | ||
382 | } | ||
383 | |||
384 | #[test] | ||
385 | fn change_return_type_to_result_simple_with_cursor() { | ||
386 | check_assist( | ||
387 | change_return_type_to_result, | ||
388 | r#"fn foo() -> <|>i32 { | ||
389 | let test = "test"; | ||
390 | return 42i32; | ||
391 | }"#, | ||
392 | r#"fn foo() -> Result<i32, ${0:_}> { | ||
393 | let test = "test"; | ||
394 | return Ok(42i32); | ||
395 | }"#, | ||
396 | ); | ||
397 | } | ||
398 | |||
399 | #[test] | ||
400 | fn change_return_type_to_result_simple_with_tail() { | ||
401 | check_assist( | ||
402 | change_return_type_to_result, | ||
403 | r#"fn foo() -><|> i32 { | ||
404 | let test = "test"; | ||
405 | 42i32 | ||
406 | }"#, | ||
407 | r#"fn foo() -> Result<i32, ${0:_}> { | ||
408 | let test = "test"; | ||
409 | Ok(42i32) | ||
410 | }"#, | ||
411 | ); | ||
412 | } | ||
413 | |||
414 | #[test] | ||
415 | fn change_return_type_to_result_simple_with_tail_closure() { | ||
416 | check_assist( | ||
417 | change_return_type_to_result, | ||
418 | r#"fn foo() { | ||
419 | || -><|> i32 { | ||
420 | let test = "test"; | ||
421 | 42i32 | ||
422 | }; | ||
423 | }"#, | ||
424 | r#"fn foo() { | ||
425 | || -> Result<i32, ${0:_}> { | ||
426 | let test = "test"; | ||
427 | Ok(42i32) | ||
428 | }; | ||
429 | }"#, | ||
430 | ); | ||
431 | } | ||
432 | |||
433 | #[test] | ||
434 | fn change_return_type_to_result_simple_with_tail_only() { | ||
435 | check_assist( | ||
436 | change_return_type_to_result, | ||
437 | r#"fn foo() -> i32<|> { | ||
438 | 42i32 | ||
439 | }"#, | ||
440 | r#"fn foo() -> Result<i32, ${0:_}> { | ||
441 | Ok(42i32) | ||
442 | }"#, | ||
443 | ); | ||
444 | } | ||
445 | |||
446 | #[test] | ||
447 | fn change_return_type_to_result_simple_with_tail_block_like() { | ||
448 | check_assist( | ||
449 | change_return_type_to_result, | ||
450 | r#"fn foo() -> i32<|> { | ||
451 | if true { | ||
452 | 42i32 | ||
453 | } else { | ||
454 | 24i32 | ||
455 | } | ||
456 | }"#, | ||
457 | r#"fn foo() -> Result<i32, ${0:_}> { | ||
458 | if true { | ||
459 | Ok(42i32) | ||
460 | } else { | ||
461 | Ok(24i32) | ||
462 | } | ||
463 | }"#, | ||
464 | ); | ||
465 | } | ||
466 | |||
467 | #[test] | ||
468 | fn change_return_type_to_result_simple_without_block_closure() { | ||
469 | check_assist( | ||
470 | change_return_type_to_result, | ||
471 | r#"fn foo() { | ||
472 | || -> i32<|> { | ||
473 | if true { | ||
474 | 42i32 | ||
475 | } else { | ||
476 | 24i32 | ||
477 | } | ||
478 | }; | ||
479 | }"#, | ||
480 | r#"fn foo() { | ||
481 | || -> Result<i32, ${0:_}> { | ||
482 | if true { | ||
483 | Ok(42i32) | ||
484 | } else { | ||
485 | Ok(24i32) | ||
486 | } | ||
487 | }; | ||
488 | }"#, | ||
489 | ); | ||
490 | } | ||
491 | |||
492 | #[test] | ||
493 | fn change_return_type_to_result_simple_with_nested_if() { | ||
494 | check_assist( | ||
495 | change_return_type_to_result, | ||
496 | r#"fn foo() -> i32<|> { | ||
497 | if true { | ||
498 | if false { | ||
499 | 1 | ||
500 | } else { | ||
501 | 2 | ||
502 | } | ||
503 | } else { | ||
504 | 24i32 | ||
505 | } | ||
506 | }"#, | ||
507 | r#"fn foo() -> Result<i32, ${0:_}> { | ||
508 | if true { | ||
509 | if false { | ||
510 | Ok(1) | ||
511 | } else { | ||
512 | Ok(2) | ||
513 | } | ||
514 | } else { | ||
515 | Ok(24i32) | ||
516 | } | ||
517 | }"#, | ||
518 | ); | ||
519 | } | ||
520 | |||
521 | #[test] | ||
522 | fn change_return_type_to_result_simple_with_await() { | ||
523 | check_assist( | ||
524 | change_return_type_to_result, | ||
525 | r#"async fn foo() -> i<|>32 { | ||
526 | if true { | ||
527 | if false { | ||
528 | 1.await | ||
529 | } else { | ||
530 | 2.await | ||
531 | } | ||
532 | } else { | ||
533 | 24i32.await | ||
534 | } | ||
535 | }"#, | ||
536 | r#"async fn foo() -> Result<i32, ${0:_}> { | ||
537 | if true { | ||
538 | if false { | ||
539 | Ok(1.await) | ||
540 | } else { | ||
541 | Ok(2.await) | ||
542 | } | ||
543 | } else { | ||
544 | Ok(24i32.await) | ||
545 | } | ||
546 | }"#, | ||
547 | ); | ||
548 | } | ||
549 | |||
550 | #[test] | ||
551 | fn change_return_type_to_result_simple_with_array() { | ||
552 | check_assist( | ||
553 | change_return_type_to_result, | ||
554 | r#"fn foo() -> [i32;<|> 3] { | ||
555 | [1, 2, 3] | ||
556 | }"#, | ||
557 | r#"fn foo() -> Result<[i32; 3], ${0:_}> { | ||
558 | Ok([1, 2, 3]) | ||
559 | }"#, | ||
560 | ); | ||
561 | } | ||
562 | |||
563 | #[test] | ||
564 | fn change_return_type_to_result_simple_with_cast() { | ||
565 | check_assist( | ||
566 | change_return_type_to_result, | ||
567 | r#"fn foo() -<|>> i32 { | ||
568 | if true { | ||
569 | if false { | ||
570 | 1 as i32 | ||
571 | } else { | ||
572 | 2 as i32 | ||
573 | } | ||
574 | } else { | ||
575 | 24 as i32 | ||
576 | } | ||
577 | }"#, | ||
578 | r#"fn foo() -> Result<i32, ${0:_}> { | ||
579 | if true { | ||
580 | if false { | ||
581 | Ok(1 as i32) | ||
582 | } else { | ||
583 | Ok(2 as i32) | ||
584 | } | ||
585 | } else { | ||
586 | Ok(24 as i32) | ||
587 | } | ||
588 | }"#, | ||
589 | ); | ||
590 | } | ||
591 | |||
592 | #[test] | ||
593 | fn change_return_type_to_result_simple_with_tail_block_like_match() { | ||
594 | check_assist( | ||
595 | change_return_type_to_result, | ||
596 | r#"fn foo() -> i32<|> { | ||
597 | let my_var = 5; | ||
598 | match my_var { | ||
599 | 5 => 42i32, | ||
600 | _ => 24i32, | ||
601 | } | ||
602 | }"#, | ||
603 | r#"fn foo() -> Result<i32, ${0:_}> { | ||
604 | let my_var = 5; | ||
605 | match my_var { | ||
606 | 5 => Ok(42i32), | ||
607 | _ => Ok(24i32), | ||
608 | } | ||
609 | }"#, | ||
610 | ); | ||
611 | } | ||
612 | |||
613 | #[test] | ||
614 | fn change_return_type_to_result_simple_with_loop_with_tail() { | ||
615 | check_assist( | ||
616 | change_return_type_to_result, | ||
617 | r#"fn foo() -> i32<|> { | ||
618 | let my_var = 5; | ||
619 | loop { | ||
620 | println!("test"); | ||
621 | 5 | ||
622 | } | ||
623 | |||
624 | my_var | ||
625 | }"#, | ||
626 | r#"fn foo() -> Result<i32, ${0:_}> { | ||
627 | let my_var = 5; | ||
628 | loop { | ||
629 | println!("test"); | ||
630 | 5 | ||
631 | } | ||
632 | |||
633 | Ok(my_var) | ||
634 | }"#, | ||
635 | ); | ||
636 | } | ||
637 | |||
638 | #[test] | ||
639 | fn change_return_type_to_result_simple_with_loop_in_let_stmt() { | ||
640 | check_assist( | ||
641 | change_return_type_to_result, | ||
642 | r#"fn foo() -> i32<|> { | ||
643 | let my_var = let x = loop { | ||
644 | break 1; | ||
645 | }; | ||
646 | |||
647 | my_var | ||
648 | }"#, | ||
649 | r#"fn foo() -> Result<i32, ${0:_}> { | ||
650 | let my_var = let x = loop { | ||
651 | break 1; | ||
652 | }; | ||
653 | |||
654 | Ok(my_var) | ||
655 | }"#, | ||
656 | ); | ||
657 | } | ||
658 | |||
659 | #[test] | ||
660 | fn change_return_type_to_result_simple_with_tail_block_like_match_return_expr() { | ||
661 | check_assist( | ||
662 | change_return_type_to_result, | ||
663 | r#"fn foo() -> i32<|> { | ||
664 | let my_var = 5; | ||
665 | let res = match my_var { | ||
666 | 5 => 42i32, | ||
667 | _ => return 24i32, | ||
668 | }; | ||
669 | |||
670 | res | ||
671 | }"#, | ||
672 | r#"fn foo() -> Result<i32, ${0:_}> { | ||
673 | let my_var = 5; | ||
674 | let res = match my_var { | ||
675 | 5 => 42i32, | ||
676 | _ => return Ok(24i32), | ||
677 | }; | ||
678 | |||
679 | Ok(res) | ||
680 | }"#, | ||
681 | ); | ||
682 | |||
683 | check_assist( | ||
684 | change_return_type_to_result, | ||
685 | r#"fn foo() -> i32<|> { | ||
686 | let my_var = 5; | ||
687 | let res = if my_var == 5 { | ||
688 | 42i32 | ||
689 | } else { | ||
690 | return 24i32; | ||
691 | }; | ||
692 | |||
693 | res | ||
694 | }"#, | ||
695 | r#"fn foo() -> Result<i32, ${0:_}> { | ||
696 | let my_var = 5; | ||
697 | let res = if my_var == 5 { | ||
698 | 42i32 | ||
699 | } else { | ||
700 | return Ok(24i32); | ||
701 | }; | ||
702 | |||
703 | Ok(res) | ||
704 | }"#, | ||
705 | ); | ||
706 | } | ||
707 | |||
708 | #[test] | ||
709 | fn change_return_type_to_result_simple_with_tail_block_like_match_deeper() { | ||
710 | check_assist( | ||
711 | change_return_type_to_result, | ||
712 | r#"fn foo() -> i32<|> { | ||
713 | let my_var = 5; | ||
714 | match my_var { | ||
715 | 5 => { | ||
716 | if true { | ||
717 | 42i32 | ||
718 | } else { | ||
719 | 25i32 | ||
720 | } | ||
721 | }, | ||
722 | _ => { | ||
723 | let test = "test"; | ||
724 | if test == "test" { | ||
725 | return bar(); | ||
726 | } | ||
727 | 53i32 | ||
728 | }, | ||
729 | } | ||
730 | }"#, | ||
731 | r#"fn foo() -> Result<i32, ${0:_}> { | ||
732 | let my_var = 5; | ||
733 | match my_var { | ||
734 | 5 => { | ||
735 | if true { | ||
736 | Ok(42i32) | ||
737 | } else { | ||
738 | Ok(25i32) | ||
739 | } | ||
740 | }, | ||
741 | _ => { | ||
742 | let test = "test"; | ||
743 | if test == "test" { | ||
744 | return Ok(bar()); | ||
745 | } | ||
746 | Ok(53i32) | ||
747 | }, | ||
748 | } | ||
749 | }"#, | ||
750 | ); | ||
751 | } | ||
752 | |||
753 | #[test] | ||
754 | fn change_return_type_to_result_simple_with_tail_block_like_early_return() { | ||
755 | check_assist( | ||
756 | change_return_type_to_result, | ||
757 | r#"fn foo() -> i<|>32 { | ||
758 | let test = "test"; | ||
759 | if test == "test" { | ||
760 | return 24i32; | ||
761 | } | ||
762 | 53i32 | ||
763 | }"#, | ||
764 | r#"fn foo() -> Result<i32, ${0:_}> { | ||
765 | let test = "test"; | ||
766 | if test == "test" { | ||
767 | return Ok(24i32); | ||
768 | } | ||
769 | Ok(53i32) | ||
770 | }"#, | ||
771 | ); | ||
772 | } | ||
773 | |||
774 | #[test] | ||
775 | fn change_return_type_to_result_simple_with_closure() { | ||
776 | check_assist( | ||
777 | change_return_type_to_result, | ||
778 | r#"fn foo(the_field: u32) -><|> u32 { | ||
779 | let true_closure = || { | ||
780 | return true; | ||
781 | }; | ||
782 | if the_field < 5 { | ||
783 | let mut i = 0; | ||
784 | |||
785 | |||
786 | if true_closure() { | ||
787 | return 99; | ||
788 | } else { | ||
789 | return 0; | ||
790 | } | ||
791 | } | ||
792 | |||
793 | the_field | ||
794 | }"#, | ||
795 | r#"fn foo(the_field: u32) -> Result<u32, ${0:_}> { | ||
796 | let true_closure = || { | ||
797 | return true; | ||
798 | }; | ||
799 | if the_field < 5 { | ||
800 | let mut i = 0; | ||
801 | |||
802 | |||
803 | if true_closure() { | ||
804 | return Ok(99); | ||
805 | } else { | ||
806 | return Ok(0); | ||
807 | } | ||
808 | } | ||
809 | |||
810 | Ok(the_field) | ||
811 | }"#, | ||
812 | ); | ||
813 | |||
814 | check_assist( | ||
815 | change_return_type_to_result, | ||
816 | r#"fn foo(the_field: u32) -> u32<|> { | ||
817 | let true_closure = || { | ||
818 | return true; | ||
819 | }; | ||
820 | if the_field < 5 { | ||
821 | let mut i = 0; | ||
822 | |||
823 | |||
824 | if true_closure() { | ||
825 | return 99; | ||
826 | } else { | ||
827 | return 0; | ||
828 | } | ||
829 | } | ||
830 | let t = None; | ||
831 | |||
832 | t.unwrap_or_else(|| the_field) | ||
833 | }"#, | ||
834 | r#"fn foo(the_field: u32) -> Result<u32, ${0:_}> { | ||
835 | let true_closure = || { | ||
836 | return true; | ||
837 | }; | ||
838 | if the_field < 5 { | ||
839 | let mut i = 0; | ||
840 | |||
841 | |||
842 | if true_closure() { | ||
843 | return Ok(99); | ||
844 | } else { | ||
845 | return Ok(0); | ||
846 | } | ||
847 | } | ||
848 | let t = None; | ||
849 | |||
850 | Ok(t.unwrap_or_else(|| the_field)) | ||
851 | }"#, | ||
852 | ); | ||
853 | } | ||
854 | |||
855 | #[test] | ||
856 | fn change_return_type_to_result_simple_with_weird_forms() { | ||
857 | check_assist( | ||
858 | change_return_type_to_result, | ||
859 | r#"fn foo() -> i32<|> { | ||
860 | let test = "test"; | ||
861 | if test == "test" { | ||
862 | return 24i32; | ||
863 | } | ||
864 | let mut i = 0; | ||
865 | loop { | ||
866 | if i == 1 { | ||
867 | break 55; | ||
868 | } | ||
869 | i += 1; | ||
870 | } | ||
871 | }"#, | ||
872 | r#"fn foo() -> Result<i32, ${0:_}> { | ||
873 | let test = "test"; | ||
874 | if test == "test" { | ||
875 | return Ok(24i32); | ||
876 | } | ||
877 | let mut i = 0; | ||
878 | loop { | ||
879 | if i == 1 { | ||
880 | break Ok(55); | ||
881 | } | ||
882 | i += 1; | ||
883 | } | ||
884 | }"#, | ||
885 | ); | ||
886 | |||
887 | check_assist( | ||
888 | change_return_type_to_result, | ||
889 | r#"fn foo() -> i32<|> { | ||
890 | let test = "test"; | ||
891 | if test == "test" { | ||
892 | return 24i32; | ||
893 | } | ||
894 | let mut i = 0; | ||
895 | loop { | ||
896 | loop { | ||
897 | if i == 1 { | ||
898 | break 55; | ||
899 | } | ||
900 | i += 1; | ||
901 | } | ||
902 | } | ||
903 | }"#, | ||
904 | r#"fn foo() -> Result<i32, ${0:_}> { | ||
905 | let test = "test"; | ||
906 | if test == "test" { | ||
907 | return Ok(24i32); | ||
908 | } | ||
909 | let mut i = 0; | ||
910 | loop { | ||
911 | loop { | ||
912 | if i == 1 { | ||
913 | break Ok(55); | ||
914 | } | ||
915 | i += 1; | ||
916 | } | ||
917 | } | ||
918 | }"#, | ||
919 | ); | ||
920 | |||
921 | check_assist( | ||
922 | change_return_type_to_result, | ||
923 | r#"fn foo() -> i3<|>2 { | ||
924 | let test = "test"; | ||
925 | let other = 5; | ||
926 | if test == "test" { | ||
927 | let res = match other { | ||
928 | 5 => 43, | ||
929 | _ => return 56, | ||
930 | }; | ||
931 | } | ||
932 | let mut i = 0; | ||
933 | loop { | ||
934 | loop { | ||
935 | if i == 1 { | ||
936 | break 55; | ||
937 | } | ||
938 | i += 1; | ||
939 | } | ||
940 | } | ||
941 | }"#, | ||
942 | r#"fn foo() -> Result<i32, ${0:_}> { | ||
943 | let test = "test"; | ||
944 | let other = 5; | ||
945 | if test == "test" { | ||
946 | let res = match other { | ||
947 | 5 => 43, | ||
948 | _ => return Ok(56), | ||
949 | }; | ||
950 | } | ||
951 | let mut i = 0; | ||
952 | loop { | ||
953 | loop { | ||
954 | if i == 1 { | ||
955 | break Ok(55); | ||
956 | } | ||
957 | i += 1; | ||
958 | } | ||
959 | } | ||
960 | }"#, | ||
961 | ); | ||
962 | |||
963 | check_assist( | ||
964 | change_return_type_to_result, | ||
965 | r#"fn foo(the_field: u32) -> u32<|> { | ||
966 | if the_field < 5 { | ||
967 | let mut i = 0; | ||
968 | loop { | ||
969 | if i > 5 { | ||
970 | return 55u32; | ||
971 | } | ||
972 | i += 3; | ||
973 | } | ||
974 | |||
975 | match i { | ||
976 | 5 => return 99, | ||
977 | _ => return 0, | ||
978 | }; | ||
979 | } | ||
980 | |||
981 | the_field | ||
982 | }"#, | ||
983 | r#"fn foo(the_field: u32) -> Result<u32, ${0:_}> { | ||
984 | if the_field < 5 { | ||
985 | let mut i = 0; | ||
986 | loop { | ||
987 | if i > 5 { | ||
988 | return Ok(55u32); | ||
989 | } | ||
990 | i += 3; | ||
991 | } | ||
992 | |||
993 | match i { | ||
994 | 5 => return Ok(99), | ||
995 | _ => return Ok(0), | ||
996 | }; | ||
997 | } | ||
998 | |||
999 | Ok(the_field) | ||
1000 | }"#, | ||
1001 | ); | ||
1002 | |||
1003 | check_assist( | ||
1004 | change_return_type_to_result, | ||
1005 | r#"fn foo(the_field: u32) -> u3<|>2 { | ||
1006 | if the_field < 5 { | ||
1007 | let mut i = 0; | ||
1008 | |||
1009 | match i { | ||
1010 | 5 => return 99, | ||
1011 | _ => return 0, | ||
1012 | } | ||
1013 | } | ||
1014 | |||
1015 | the_field | ||
1016 | }"#, | ||
1017 | r#"fn foo(the_field: u32) -> Result<u32, ${0:_}> { | ||
1018 | if the_field < 5 { | ||
1019 | let mut i = 0; | ||
1020 | |||
1021 | match i { | ||
1022 | 5 => return Ok(99), | ||
1023 | _ => return Ok(0), | ||
1024 | } | ||
1025 | } | ||
1026 | |||
1027 | Ok(the_field) | ||
1028 | }"#, | ||
1029 | ); | ||
1030 | |||
1031 | check_assist( | ||
1032 | change_return_type_to_result, | ||
1033 | r#"fn foo(the_field: u32) -> u32<|> { | ||
1034 | if the_field < 5 { | ||
1035 | let mut i = 0; | ||
1036 | |||
1037 | if i == 5 { | ||
1038 | return 99 | ||
1039 | } else { | ||
1040 | return 0 | ||
1041 | } | ||
1042 | } | ||
1043 | |||
1044 | the_field | ||
1045 | }"#, | ||
1046 | r#"fn foo(the_field: u32) -> Result<u32, ${0:_}> { | ||
1047 | if the_field < 5 { | ||
1048 | let mut i = 0; | ||
1049 | |||
1050 | if i == 5 { | ||
1051 | return Ok(99) | ||
1052 | } else { | ||
1053 | return Ok(0) | ||
1054 | } | ||
1055 | } | ||
1056 | |||
1057 | Ok(the_field) | ||
1058 | }"#, | ||
1059 | ); | ||
1060 | |||
1061 | check_assist( | ||
1062 | change_return_type_to_result, | ||
1063 | r#"fn foo(the_field: u32) -> <|>u32 { | ||
1064 | if the_field < 5 { | ||
1065 | let mut i = 0; | ||
1066 | |||
1067 | if i == 5 { | ||
1068 | return 99; | ||
1069 | } else { | ||
1070 | return 0; | ||
1071 | } | ||
1072 | } | ||
1073 | |||
1074 | the_field | ||
1075 | }"#, | ||
1076 | r#"fn foo(the_field: u32) -> Result<u32, ${0:_}> { | ||
1077 | if the_field < 5 { | ||
1078 | let mut i = 0; | ||
1079 | |||
1080 | if i == 5 { | ||
1081 | return Ok(99); | ||
1082 | } else { | ||
1083 | return Ok(0); | ||
1084 | } | ||
1085 | } | ||
1086 | |||
1087 | Ok(the_field) | ||
1088 | }"#, | ||
1089 | ); | ||
1090 | } | ||
1091 | } | ||
diff --git a/crates/assists/src/handlers/expand_glob_import.rs b/crates/assists/src/handlers/expand_glob_import.rs index 853266395..f51a9a4ad 100644 --- a/crates/assists/src/handlers/expand_glob_import.rs +++ b/crates/assists/src/handlers/expand_glob_import.rs | |||
@@ -5,13 +5,13 @@ use ide_db::{ | |||
5 | search::SearchScope, | 5 | search::SearchScope, |
6 | }; | 6 | }; |
7 | use syntax::{ | 7 | use syntax::{ |
8 | algo, | 8 | algo::SyntaxRewriter, |
9 | ast::{self, make}, | 9 | ast::{self, make}, |
10 | AstNode, Direction, SyntaxNode, SyntaxToken, T, | 10 | AstNode, Direction, SyntaxNode, SyntaxToken, T, |
11 | }; | 11 | }; |
12 | 12 | ||
13 | use crate::{ | 13 | use crate::{ |
14 | assist_context::{AssistBuilder, AssistContext, Assists}, | 14 | assist_context::{AssistContext, Assists}, |
15 | AssistId, AssistKind, | 15 | AssistId, AssistKind, |
16 | }; | 16 | }; |
17 | 17 | ||
@@ -61,7 +61,9 @@ pub(crate) fn expand_glob_import(acc: &mut Assists, ctx: &AssistContext) -> Opti | |||
61 | "Expand glob import", | 61 | "Expand glob import", |
62 | target.text_range(), | 62 | target.text_range(), |
63 | |builder| { | 63 | |builder| { |
64 | replace_ast(builder, parent, mod_path, names_to_import); | 64 | let mut rewriter = SyntaxRewriter::default(); |
65 | replace_ast(&mut rewriter, parent, mod_path, names_to_import); | ||
66 | builder.rewrite(rewriter); | ||
65 | }, | 67 | }, |
66 | ) | 68 | ) |
67 | } | 69 | } |
@@ -236,7 +238,7 @@ fn find_names_to_import( | |||
236 | } | 238 | } |
237 | 239 | ||
238 | fn replace_ast( | 240 | fn replace_ast( |
239 | builder: &mut AssistBuilder, | 241 | rewriter: &mut SyntaxRewriter, |
240 | parent: Either<ast::UseTree, ast::UseTreeList>, | 242 | parent: Either<ast::UseTree, ast::UseTreeList>, |
241 | path: ast::Path, | 243 | path: ast::Path, |
242 | names_to_import: Vec<Name>, | 244 | names_to_import: Vec<Name>, |
@@ -264,32 +266,21 @@ fn replace_ast( | |||
264 | match use_trees.as_slice() { | 266 | match use_trees.as_slice() { |
265 | [name] => { | 267 | [name] => { |
266 | if let Some(end_path) = name.path() { | 268 | if let Some(end_path) = name.path() { |
267 | let replacement = | 269 | rewriter.replace_ast( |
268 | make::use_tree(make::path_concat(path, end_path), None, None, false); | 270 | &parent.left_or_else(|tl| tl.parent_use_tree()), |
269 | 271 | &make::use_tree(make::path_concat(path, end_path), None, None, false), | |
270 | algo::diff( | 272 | ); |
271 | &parent.either(|n| n.syntax().clone(), |n| n.syntax().clone()), | ||
272 | replacement.syntax(), | ||
273 | ) | ||
274 | .into_text_edit(builder.text_edit_builder()); | ||
275 | } | 273 | } |
276 | } | 274 | } |
277 | names => { | 275 | names => match &parent { |
278 | let replacement = match parent { | 276 | Either::Left(parent) => rewriter.replace_ast( |
279 | Either::Left(_) => { | 277 | parent, |
280 | make::use_tree(path, Some(make::use_tree_list(names.to_owned())), None, false) | 278 | &make::use_tree(path, Some(make::use_tree_list(names.to_owned())), None, false), |
281 | .syntax() | 279 | ), |
282 | .clone() | 280 | Either::Right(parent) => { |
283 | } | 281 | rewriter.replace_ast(parent, &make::use_tree_list(names.to_owned())) |
284 | Either::Right(_) => make::use_tree_list(names.to_owned()).syntax().clone(), | 282 | } |
285 | }; | 283 | }, |
286 | |||
287 | algo::diff( | ||
288 | &parent.either(|n| n.syntax().clone(), |n| n.syntax().clone()), | ||
289 | &replacement, | ||
290 | ) | ||
291 | .into_text_edit(builder.text_edit_builder()); | ||
292 | } | ||
293 | }; | 284 | }; |
294 | } | 285 | } |
295 | 286 | ||
@@ -884,4 +875,33 @@ fn qux(baz: Baz) {} | |||
884 | ", | 875 | ", |
885 | ) | 876 | ) |
886 | } | 877 | } |
878 | |||
879 | #[test] | ||
880 | fn expanding_glob_import_single_nested_glob_only() { | ||
881 | check_assist( | ||
882 | expand_glob_import, | ||
883 | r" | ||
884 | mod foo { | ||
885 | pub struct Bar; | ||
886 | } | ||
887 | |||
888 | use foo::{*<|>}; | ||
889 | |||
890 | struct Baz { | ||
891 | bar: Bar | ||
892 | } | ||
893 | ", | ||
894 | r" | ||
895 | mod foo { | ||
896 | pub struct Bar; | ||
897 | } | ||
898 | |||
899 | use foo::Bar; | ||
900 | |||
901 | struct Baz { | ||
902 | bar: Bar | ||
903 | } | ||
904 | ", | ||
905 | ); | ||
906 | } | ||
887 | } | 907 | } |
diff --git a/crates/assists/src/handlers/extract_struct_from_enum_variant.rs b/crates/assists/src/handlers/extract_struct_from_enum_variant.rs index 14209b771..cac77c49b 100644 --- a/crates/assists/src/handlers/extract_struct_from_enum_variant.rs +++ b/crates/assists/src/handlers/extract_struct_from_enum_variant.rs | |||
@@ -5,10 +5,9 @@ use hir::{AsName, EnumVariant, Module, ModuleDef, Name}; | |||
5 | use ide_db::{defs::Definition, search::Reference, RootDatabase}; | 5 | use ide_db::{defs::Definition, search::Reference, RootDatabase}; |
6 | use rustc_hash::{FxHashMap, FxHashSet}; | 6 | use rustc_hash::{FxHashMap, FxHashSet}; |
7 | use syntax::{ | 7 | use syntax::{ |
8 | algo::find_node_at_offset, | 8 | algo::{find_node_at_offset, SyntaxRewriter}, |
9 | algo::SyntaxRewriter, | 9 | ast::{self, edit::IndentLevel, make, AstNode, NameOwner, VisibilityOwner}, |
10 | ast::{self, edit::IndentLevel, make, ArgListOwner, AstNode, NameOwner, VisibilityOwner}, | 10 | SourceFile, SyntaxElement, SyntaxNode, T, |
11 | SourceFile, SyntaxElement, | ||
12 | }; | 11 | }; |
13 | 12 | ||
14 | use crate::{ | 13 | use crate::{ |
@@ -130,18 +129,21 @@ fn existing_definition(db: &RootDatabase, variant_name: &ast::Name, variant: &En | |||
130 | fn insert_import( | 129 | fn insert_import( |
131 | ctx: &AssistContext, | 130 | ctx: &AssistContext, |
132 | rewriter: &mut SyntaxRewriter, | 131 | rewriter: &mut SyntaxRewriter, |
133 | path: &ast::PathExpr, | 132 | scope_node: &SyntaxNode, |
134 | module: &Module, | 133 | module: &Module, |
135 | enum_module_def: &ModuleDef, | 134 | enum_module_def: &ModuleDef, |
136 | variant_hir_name: &Name, | 135 | variant_hir_name: &Name, |
137 | ) -> Option<()> { | 136 | ) -> Option<()> { |
138 | let db = ctx.db(); | 137 | let db = ctx.db(); |
139 | let mod_path = module.find_use_path(db, enum_module_def.clone()); | 138 | let mod_path = module.find_use_path_prefixed( |
139 | db, | ||
140 | enum_module_def.clone(), | ||
141 | ctx.config.insert_use.prefix_kind, | ||
142 | ); | ||
140 | if let Some(mut mod_path) = mod_path { | 143 | if let Some(mut mod_path) = mod_path { |
141 | mod_path.segments.pop(); | 144 | mod_path.segments.pop(); |
142 | mod_path.segments.push(variant_hir_name.clone()); | 145 | mod_path.segments.push(variant_hir_name.clone()); |
143 | let scope = ImportScope::find_insert_use_container(path.syntax(), ctx)?; | 146 | let scope = ImportScope::find_insert_use_container(scope_node, &ctx.sema)?; |
144 | |||
145 | *rewriter += insert_use(&scope, mod_path_to_ast(&mod_path), ctx.config.insert_use.merge); | 147 | *rewriter += insert_use(&scope, mod_path_to_ast(&mod_path), ctx.config.insert_use.merge); |
146 | } | 148 | } |
147 | Some(()) | 149 | Some(()) |
@@ -204,27 +206,31 @@ fn update_reference( | |||
204 | variant_hir_name: &Name, | 206 | variant_hir_name: &Name, |
205 | visited_modules_set: &mut FxHashSet<Module>, | 207 | visited_modules_set: &mut FxHashSet<Module>, |
206 | ) -> Option<()> { | 208 | ) -> Option<()> { |
207 | let path_expr: ast::PathExpr = find_node_at_offset::<ast::PathExpr>( | 209 | let offset = reference.file_range.range.start(); |
208 | source_file.syntax(), | 210 | let (segment, expr) = if let Some(path_expr) = |
209 | reference.file_range.range.start(), | 211 | find_node_at_offset::<ast::PathExpr>(source_file.syntax(), offset) |
210 | )?; | 212 | { |
211 | let call = path_expr.syntax().parent().and_then(ast::CallExpr::cast)?; | 213 | // tuple variant |
212 | let list = call.arg_list()?; | 214 | (path_expr.path()?.segment()?, path_expr.syntax().parent()?.clone()) |
213 | let segment = path_expr.path()?.segment()?; | 215 | } else if let Some(record_expr) = |
214 | let module = ctx.sema.scope(&path_expr.syntax()).module()?; | 216 | find_node_at_offset::<ast::RecordExpr>(source_file.syntax(), offset) |
217 | { | ||
218 | // record variant | ||
219 | (record_expr.path()?.segment()?, record_expr.syntax().clone()) | ||
220 | } else { | ||
221 | return None; | ||
222 | }; | ||
223 | |||
224 | let module = ctx.sema.scope(&expr).module()?; | ||
215 | if !visited_modules_set.contains(&module) { | 225 | if !visited_modules_set.contains(&module) { |
216 | if insert_import(ctx, rewriter, &path_expr, &module, enum_module_def, variant_hir_name) | 226 | if insert_import(ctx, rewriter, &expr, &module, enum_module_def, variant_hir_name).is_some() |
217 | .is_some() | ||
218 | { | 227 | { |
219 | visited_modules_set.insert(module); | 228 | visited_modules_set.insert(module); |
220 | } | 229 | } |
221 | } | 230 | } |
222 | 231 | rewriter.insert_after(segment.syntax(), &make::token(T!['('])); | |
223 | let lparen = syntax::SyntaxElement::from(list.l_paren_token()?); | 232 | rewriter.insert_after(segment.syntax(), segment.syntax()); |
224 | let rparen = syntax::SyntaxElement::from(list.r_paren_token()?); | 233 | rewriter.insert_after(&expr, &make::token(T![')'])); |
225 | rewriter.insert_after(&lparen, segment.syntax()); | ||
226 | rewriter.insert_after(&lparen, &lparen); | ||
227 | rewriter.insert_before(&rparen, &rparen); | ||
228 | Some(()) | 234 | Some(()) |
229 | } | 235 | } |
230 | 236 | ||
@@ -320,7 +326,7 @@ fn another_fn() { | |||
320 | r#"use my_mod::my_other_mod::MyField; | 326 | r#"use my_mod::my_other_mod::MyField; |
321 | 327 | ||
322 | mod my_mod { | 328 | mod my_mod { |
323 | use my_other_mod::MyField; | 329 | use self::my_other_mod::MyField; |
324 | 330 | ||
325 | fn another_fn() { | 331 | fn another_fn() { |
326 | let m = my_other_mod::MyEnum::MyField(MyField(1, 1)); | 332 | let m = my_other_mod::MyEnum::MyField(MyField(1, 1)); |
@@ -345,6 +351,130 @@ fn another_fn() { | |||
345 | ); | 351 | ); |
346 | } | 352 | } |
347 | 353 | ||
354 | #[test] | ||
355 | fn extract_record_fix_references() { | ||
356 | check_assist( | ||
357 | extract_struct_from_enum_variant, | ||
358 | r#" | ||
359 | enum E { | ||
360 | <|>V { i: i32, j: i32 } | ||
361 | } | ||
362 | |||
363 | fn f() { | ||
364 | let e = E::V { i: 9, j: 2 }; | ||
365 | } | ||
366 | "#, | ||
367 | r#" | ||
368 | struct V{ pub i: i32, pub j: i32 } | ||
369 | |||
370 | enum E { | ||
371 | V(V) | ||
372 | } | ||
373 | |||
374 | fn f() { | ||
375 | let e = E::V(V { i: 9, j: 2 }); | ||
376 | } | ||
377 | "#, | ||
378 | ) | ||
379 | } | ||
380 | |||
381 | #[test] | ||
382 | fn test_several_files() { | ||
383 | check_assist( | ||
384 | extract_struct_from_enum_variant, | ||
385 | r#" | ||
386 | //- /main.rs | ||
387 | enum E { | ||
388 | <|>V(i32, i32) | ||
389 | } | ||
390 | mod foo; | ||
391 | |||
392 | //- /foo.rs | ||
393 | use crate::E; | ||
394 | fn f() { | ||
395 | let e = E::V(9, 2); | ||
396 | } | ||
397 | "#, | ||
398 | r#" | ||
399 | //- /main.rs | ||
400 | struct V(pub i32, pub i32); | ||
401 | |||
402 | enum E { | ||
403 | V(V) | ||
404 | } | ||
405 | mod foo; | ||
406 | |||
407 | //- /foo.rs | ||
408 | use crate::{E, V}; | ||
409 | fn f() { | ||
410 | let e = E::V(V(9, 2)); | ||
411 | } | ||
412 | "#, | ||
413 | ) | ||
414 | } | ||
415 | |||
416 | #[test] | ||
417 | fn test_several_files_record() { | ||
418 | check_assist( | ||
419 | extract_struct_from_enum_variant, | ||
420 | r#" | ||
421 | //- /main.rs | ||
422 | enum E { | ||
423 | <|>V { i: i32, j: i32 } | ||
424 | } | ||
425 | mod foo; | ||
426 | |||
427 | //- /foo.rs | ||
428 | use crate::E; | ||
429 | fn f() { | ||
430 | let e = E::V { i: 9, j: 2 }; | ||
431 | } | ||
432 | "#, | ||
433 | r#" | ||
434 | //- /main.rs | ||
435 | struct V{ pub i: i32, pub j: i32 } | ||
436 | |||
437 | enum E { | ||
438 | V(V) | ||
439 | } | ||
440 | mod foo; | ||
441 | |||
442 | //- /foo.rs | ||
443 | use crate::{E, V}; | ||
444 | fn f() { | ||
445 | let e = E::V(V { i: 9, j: 2 }); | ||
446 | } | ||
447 | "#, | ||
448 | ) | ||
449 | } | ||
450 | |||
451 | #[test] | ||
452 | fn test_extract_struct_record_nested_call_exp() { | ||
453 | check_assist( | ||
454 | extract_struct_from_enum_variant, | ||
455 | r#" | ||
456 | enum A { <|>One { a: u32, b: u32 } } | ||
457 | |||
458 | struct B(A); | ||
459 | |||
460 | fn foo() { | ||
461 | let _ = B(A::One { a: 1, b: 2 }); | ||
462 | } | ||
463 | "#, | ||
464 | r#" | ||
465 | struct One{ pub a: u32, pub b: u32 } | ||
466 | |||
467 | enum A { One(One) } | ||
468 | |||
469 | struct B(A); | ||
470 | |||
471 | fn foo() { | ||
472 | let _ = B(A::One(One { a: 1, b: 2 })); | ||
473 | } | ||
474 | "#, | ||
475 | ); | ||
476 | } | ||
477 | |||
348 | fn check_not_applicable(ra_fixture: &str) { | 478 | fn check_not_applicable(ra_fixture: &str) { |
349 | let fixture = | 479 | let fixture = |
350 | format!("//- /main.rs crate:main deps:core\n{}\n{}", ra_fixture, FamousDefs::FIXTURE); | 480 | format!("//- /main.rs crate:main deps:core\n{}\n{}", ra_fixture, FamousDefs::FIXTURE); |
diff --git a/crates/assists/src/handlers/ignore_test.rs b/crates/assists/src/handlers/ignore_test.rs new file mode 100644 index 000000000..5096a0005 --- /dev/null +++ b/crates/assists/src/handlers/ignore_test.rs | |||
@@ -0,0 +1,103 @@ | |||
1 | use syntax::{ | ||
2 | ast::{self, AttrsOwner}, | ||
3 | AstNode, AstToken, | ||
4 | }; | ||
5 | |||
6 | use crate::{utils::test_related_attribute, AssistContext, AssistId, AssistKind, Assists}; | ||
7 | |||
8 | // Assist: ignore_test | ||
9 | // | ||
10 | // Adds `#[ignore]` attribute to the test. | ||
11 | // | ||
12 | // ``` | ||
13 | // <|>#[test] | ||
14 | // fn arithmetics { | ||
15 | // assert_eq!(2 + 2, 5); | ||
16 | // } | ||
17 | // ``` | ||
18 | // -> | ||
19 | // ``` | ||
20 | // #[test] | ||
21 | // #[ignore] | ||
22 | // fn arithmetics { | ||
23 | // assert_eq!(2 + 2, 5); | ||
24 | // } | ||
25 | // ``` | ||
26 | pub(crate) fn ignore_test(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | ||
27 | let attr: ast::Attr = ctx.find_node_at_offset()?; | ||
28 | let func = attr.syntax().parent().and_then(ast::Fn::cast)?; | ||
29 | let attr = test_related_attribute(&func)?; | ||
30 | |||
31 | match has_ignore_attribute(&func) { | ||
32 | None => acc.add( | ||
33 | AssistId("ignore_test", AssistKind::None), | ||
34 | "Ignore this test", | ||
35 | attr.syntax().text_range(), | ||
36 | |builder| builder.insert(attr.syntax().text_range().end(), &format!("\n#[ignore]")), | ||
37 | ), | ||
38 | Some(ignore_attr) => acc.add( | ||
39 | AssistId("unignore_test", AssistKind::None), | ||
40 | "Re-enable this test", | ||
41 | ignore_attr.syntax().text_range(), | ||
42 | |builder| { | ||
43 | builder.delete(ignore_attr.syntax().text_range()); | ||
44 | let whitespace = ignore_attr | ||
45 | .syntax() | ||
46 | .next_sibling_or_token() | ||
47 | .and_then(|x| x.into_token()) | ||
48 | .and_then(ast::Whitespace::cast); | ||
49 | if let Some(whitespace) = whitespace { | ||
50 | builder.delete(whitespace.syntax().text_range()); | ||
51 | } | ||
52 | }, | ||
53 | ), | ||
54 | } | ||
55 | } | ||
56 | |||
57 | fn has_ignore_attribute(fn_def: &ast::Fn) -> Option<ast::Attr> { | ||
58 | fn_def.attrs().find_map(|attr| { | ||
59 | if attr.path()?.syntax().text() == "ignore" { | ||
60 | Some(attr) | ||
61 | } else { | ||
62 | None | ||
63 | } | ||
64 | }) | ||
65 | } | ||
66 | |||
67 | #[cfg(test)] | ||
68 | mod tests { | ||
69 | use super::ignore_test; | ||
70 | use crate::tests::check_assist; | ||
71 | |||
72 | #[test] | ||
73 | fn test_base_case() { | ||
74 | check_assist( | ||
75 | ignore_test, | ||
76 | r#" | ||
77 | #[test<|>] | ||
78 | fn test() {} | ||
79 | "#, | ||
80 | r#" | ||
81 | #[test] | ||
82 | #[ignore] | ||
83 | fn test() {} | ||
84 | "#, | ||
85 | ) | ||
86 | } | ||
87 | |||
88 | #[test] | ||
89 | fn test_unignore() { | ||
90 | check_assist( | ||
91 | ignore_test, | ||
92 | r#" | ||
93 | #[test<|>] | ||
94 | #[ignore] | ||
95 | fn test() {} | ||
96 | "#, | ||
97 | r#" | ||
98 | #[test] | ||
99 | fn test() {} | ||
100 | "#, | ||
101 | ) | ||
102 | } | ||
103 | } | ||
diff --git a/crates/assists/src/handlers/remove_dbg.rs b/crates/assists/src/handlers/remove_dbg.rs index 9731344b8..eae6367c1 100644 --- a/crates/assists/src/handlers/remove_dbg.rs +++ b/crates/assists/src/handlers/remove_dbg.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | use syntax::{ | 1 | use syntax::{ |
2 | ast::{self, AstNode}, | 2 | ast::{self, AstNode}, |
3 | SyntaxElement, SyntaxKind, TextRange, TextSize, T, | 3 | match_ast, SyntaxElement, SyntaxKind, TextRange, TextSize, T, |
4 | }; | 4 | }; |
5 | 5 | ||
6 | use crate::{AssistContext, AssistId, AssistKind, Assists}; | 6 | use crate::{AssistContext, AssistId, AssistKind, Assists}; |
@@ -49,12 +49,29 @@ fn adjusted_macro_contents(macro_call: &ast::MacroCall) -> Option<String> { | |||
49 | macro_text_with_brackets.len() - TextSize::of(')'), | 49 | macro_text_with_brackets.len() - TextSize::of(')'), |
50 | )); | 50 | )); |
51 | 51 | ||
52 | let is_leaf = macro_call.syntax().next_sibling().is_none(); | 52 | Some( |
53 | Some(if !is_leaf && needs_parentheses_around_macro_contents(contents) { | 53 | if !is_leaf_or_control_flow_expr(macro_call) |
54 | format!("({})", macro_text_in_brackets) | 54 | && needs_parentheses_around_macro_contents(contents) |
55 | } else { | 55 | { |
56 | macro_text_in_brackets.to_string() | 56 | format!("({})", macro_text_in_brackets) |
57 | }) | 57 | } else { |
58 | macro_text_in_brackets.to_string() | ||
59 | }, | ||
60 | ) | ||
61 | } | ||
62 | |||
63 | fn is_leaf_or_control_flow_expr(macro_call: &ast::MacroCall) -> bool { | ||
64 | macro_call.syntax().next_sibling().is_none() | ||
65 | || match macro_call.syntax().parent() { | ||
66 | Some(parent) => match_ast! { | ||
67 | match parent { | ||
68 | ast::Condition(_it) => true, | ||
69 | ast::MatchExpr(_it) => true, | ||
70 | _ => false, | ||
71 | } | ||
72 | }, | ||
73 | None => false, | ||
74 | } | ||
58 | } | 75 | } |
59 | 76 | ||
60 | /// Verifies that the given macro_call actually matches the given name | 77 | /// Verifies that the given macro_call actually matches the given name |
@@ -361,4 +378,44 @@ fn main() { | |||
361 | r#"let res = (foo..=bar).foo();"#, | 378 | r#"let res = (foo..=bar).foo();"#, |
362 | ); | 379 | ); |
363 | } | 380 | } |
381 | |||
382 | #[test] | ||
383 | fn test_remove_dbg_followed_by_block() { | ||
384 | check_assist( | ||
385 | remove_dbg, | ||
386 | r#"fn foo() { | ||
387 | if <|>dbg!(x || y) {} | ||
388 | }"#, | ||
389 | r#"fn foo() { | ||
390 | if x || y {} | ||
391 | }"#, | ||
392 | ); | ||
393 | check_assist( | ||
394 | remove_dbg, | ||
395 | r#"fn foo() { | ||
396 | while let foo = <|>dbg!(&x) {} | ||
397 | }"#, | ||
398 | r#"fn foo() { | ||
399 | while let foo = &x {} | ||
400 | }"#, | ||
401 | ); | ||
402 | check_assist( | ||
403 | remove_dbg, | ||
404 | r#"fn foo() { | ||
405 | if let foo = <|>dbg!(&x) {} | ||
406 | }"#, | ||
407 | r#"fn foo() { | ||
408 | if let foo = &x {} | ||
409 | }"#, | ||
410 | ); | ||
411 | check_assist( | ||
412 | remove_dbg, | ||
413 | r#"fn foo() { | ||
414 | match <|>dbg!(&x) {} | ||
415 | }"#, | ||
416 | r#"fn foo() { | ||
417 | match &x {} | ||
418 | }"#, | ||
419 | ); | ||
420 | } | ||
364 | } | 421 | } |
diff --git a/crates/assists/src/handlers/remove_unused_param.rs b/crates/assists/src/handlers/remove_unused_param.rs index 5fccca54b..1ff5e92b0 100644 --- a/crates/assists/src/handlers/remove_unused_param.rs +++ b/crates/assists/src/handlers/remove_unused_param.rs | |||
@@ -73,7 +73,8 @@ fn process_usage( | |||
73 | let source_file = ctx.sema.parse(usage.file_range.file_id); | 73 | let source_file = ctx.sema.parse(usage.file_range.file_id); |
74 | let call_expr: ast::CallExpr = | 74 | let call_expr: ast::CallExpr = |
75 | find_node_at_range(source_file.syntax(), usage.file_range.range)?; | 75 | find_node_at_range(source_file.syntax(), usage.file_range.range)?; |
76 | if call_expr.expr()?.syntax().text_range() != usage.file_range.range { | 76 | let call_expr_range = call_expr.expr()?.syntax().text_range(); |
77 | if !call_expr_range.contains_range(usage.file_range.range) { | ||
77 | return None; | 78 | return None; |
78 | } | 79 | } |
79 | let arg = call_expr.arg_list()?.args().nth(arg_to_remove)?; | 80 | let arg = call_expr.arg_list()?.args().nth(arg_to_remove)?; |
@@ -118,6 +119,53 @@ fn b() { foo(9, ) } | |||
118 | } | 119 | } |
119 | 120 | ||
120 | #[test] | 121 | #[test] |
122 | fn remove_unused_qualified_call() { | ||
123 | check_assist( | ||
124 | remove_unused_param, | ||
125 | r#" | ||
126 | mod bar { pub fn foo(x: i32, <|>y: i32) { x; } } | ||
127 | fn b() { bar::foo(9, 2) } | ||
128 | "#, | ||
129 | r#" | ||
130 | mod bar { pub fn foo(x: i32) { x; } } | ||
131 | fn b() { bar::foo(9) } | ||
132 | "#, | ||
133 | ); | ||
134 | } | ||
135 | |||
136 | #[test] | ||
137 | fn remove_unused_turbofished_func() { | ||
138 | check_assist( | ||
139 | remove_unused_param, | ||
140 | r#" | ||
141 | pub fn foo<T>(x: T, <|>y: i32) { x; } | ||
142 | fn b() { foo::<i32>(9, 2) } | ||
143 | "#, | ||
144 | r#" | ||
145 | pub fn foo<T>(x: T) { x; } | ||
146 | fn b() { foo::<i32>(9) } | ||
147 | "#, | ||
148 | ); | ||
149 | } | ||
150 | |||
151 | #[test] | ||
152 | fn remove_unused_generic_unused_param_func() { | ||
153 | check_assist( | ||
154 | remove_unused_param, | ||
155 | r#" | ||
156 | pub fn foo<T>(x: i32, <|>y: T) { x; } | ||
157 | fn b() { foo::<i32>(9, 2) } | ||
158 | fn b2() { foo(9, 2) } | ||
159 | "#, | ||
160 | r#" | ||
161 | pub fn foo<T>(x: i32) { x; } | ||
162 | fn b() { foo::<i32>(9) } | ||
163 | fn b2() { foo(9) } | ||
164 | "#, | ||
165 | ); | ||
166 | } | ||
167 | |||
168 | #[test] | ||
121 | fn keep_used() { | 169 | fn keep_used() { |
122 | mark::check!(keep_used); | 170 | mark::check!(keep_used); |
123 | check_assist_not_applicable( | 171 | check_assist_not_applicable( |
@@ -128,4 +176,37 @@ fn main() { foo(9, 2) } | |||
128 | "#, | 176 | "#, |
129 | ); | 177 | ); |
130 | } | 178 | } |
179 | |||
180 | #[test] | ||
181 | fn remove_across_files() { | ||
182 | check_assist( | ||
183 | remove_unused_param, | ||
184 | r#" | ||
185 | //- /main.rs | ||
186 | fn foo(x: i32, <|>y: i32) { x; } | ||
187 | |||
188 | mod foo; | ||
189 | |||
190 | //- /foo.rs | ||
191 | use super::foo; | ||
192 | |||
193 | fn bar() { | ||
194 | let _ = foo(1, 2); | ||
195 | } | ||
196 | "#, | ||
197 | r#" | ||
198 | //- /main.rs | ||
199 | fn foo(x: i32) { x; } | ||
200 | |||
201 | mod foo; | ||
202 | |||
203 | //- /foo.rs | ||
204 | use super::foo; | ||
205 | |||
206 | fn bar() { | ||
207 | let _ = foo(1); | ||
208 | } | ||
209 | "#, | ||
210 | ) | ||
211 | } | ||
131 | } | 212 | } |
diff --git a/crates/assists/src/handlers/reorder_fields.rs b/crates/assists/src/handlers/reorder_fields.rs index 527f457a7..7c0f0f44e 100644 --- a/crates/assists/src/handlers/reorder_fields.rs +++ b/crates/assists/src/handlers/reorder_fields.rs | |||
@@ -47,9 +47,11 @@ fn reorder<R: AstNode>(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | |||
47 | "Reorder record fields", | 47 | "Reorder record fields", |
48 | target, | 48 | target, |
49 | |edit| { | 49 | |edit| { |
50 | let mut rewriter = algo::SyntaxRewriter::default(); | ||
50 | for (old, new) in fields.iter().zip(&sorted_fields) { | 51 | for (old, new) in fields.iter().zip(&sorted_fields) { |
51 | algo::diff(old, new).into_text_edit(edit.text_edit_builder()); | 52 | rewriter.replace(old, new); |
52 | } | 53 | } |
54 | edit.rewrite(rewriter); | ||
53 | }, | 55 | }, |
54 | ) | 56 | ) |
55 | } | 57 | } |
diff --git a/crates/assists/src/handlers/replace_derive_with_manual_impl.rs b/crates/assists/src/handlers/replace_derive_with_manual_impl.rs new file mode 100644 index 000000000..453a6cebf --- /dev/null +++ b/crates/assists/src/handlers/replace_derive_with_manual_impl.rs | |||
@@ -0,0 +1,400 @@ | |||
1 | use ide_db::imports_locator; | ||
2 | use itertools::Itertools; | ||
3 | use syntax::{ | ||
4 | ast::{self, make, AstNode}, | ||
5 | Direction, SmolStr, | ||
6 | SyntaxKind::{IDENT, WHITESPACE}, | ||
7 | TextSize, | ||
8 | }; | ||
9 | |||
10 | use crate::{ | ||
11 | assist_context::{AssistBuilder, AssistContext, Assists}, | ||
12 | utils::{ | ||
13 | add_trait_assoc_items_to_impl, filter_assoc_items, mod_path_to_ast, render_snippet, Cursor, | ||
14 | DefaultMethods, | ||
15 | }, | ||
16 | AssistId, AssistKind, | ||
17 | }; | ||
18 | |||
19 | // Assist: replace_derive_with_manual_impl | ||
20 | // | ||
21 | // Converts a `derive` impl into a manual one. | ||
22 | // | ||
23 | // ``` | ||
24 | // # trait Debug { fn fmt(&self, f: &mut Formatter) -> Result<()>; } | ||
25 | // #[derive(Deb<|>ug, Display)] | ||
26 | // struct S; | ||
27 | // ``` | ||
28 | // -> | ||
29 | // ``` | ||
30 | // # trait Debug { fn fmt(&self, f: &mut Formatter) -> Result<()>; } | ||
31 | // #[derive(Display)] | ||
32 | // struct S; | ||
33 | // | ||
34 | // impl Debug for S { | ||
35 | // fn fmt(&self, f: &mut Formatter) -> Result<()> { | ||
36 | // ${0:todo!()} | ||
37 | // } | ||
38 | // } | ||
39 | // ``` | ||
40 | pub(crate) fn replace_derive_with_manual_impl( | ||
41 | acc: &mut Assists, | ||
42 | ctx: &AssistContext, | ||
43 | ) -> Option<()> { | ||
44 | let attr = ctx.find_node_at_offset::<ast::Attr>()?; | ||
45 | |||
46 | let attr_name = attr | ||
47 | .syntax() | ||
48 | .descendants_with_tokens() | ||
49 | .filter(|t| t.kind() == IDENT) | ||
50 | .find_map(syntax::NodeOrToken::into_token) | ||
51 | .filter(|t| t.text() == "derive")? | ||
52 | .text() | ||
53 | .clone(); | ||
54 | |||
55 | let trait_token = | ||
56 | ctx.token_at_offset().find(|t| t.kind() == IDENT && *t.text() != attr_name)?; | ||
57 | let trait_path = make::path_unqualified(make::path_segment(make::name_ref(trait_token.text()))); | ||
58 | |||
59 | let annotated_name = attr.syntax().siblings(Direction::Next).find_map(ast::Name::cast)?; | ||
60 | let insert_pos = annotated_name.syntax().parent()?.text_range().end(); | ||
61 | |||
62 | let current_module = ctx.sema.scope(annotated_name.syntax()).module()?; | ||
63 | let current_crate = current_module.krate(); | ||
64 | |||
65 | let found_traits = | ||
66 | imports_locator::find_exact_imports(&ctx.sema, current_crate, trait_token.text()) | ||
67 | .filter_map( | ||
68 | |candidate: either::Either<hir::ModuleDef, hir::MacroDef>| match candidate { | ||
69 | either::Either::Left(hir::ModuleDef::Trait(trait_)) => Some(trait_), | ||
70 | _ => None, | ||
71 | }, | ||
72 | ) | ||
73 | .flat_map(|trait_| { | ||
74 | current_module | ||
75 | .find_use_path(ctx.sema.db, hir::ModuleDef::Trait(trait_)) | ||
76 | .as_ref() | ||
77 | .map(mod_path_to_ast) | ||
78 | .zip(Some(trait_)) | ||
79 | }); | ||
80 | |||
81 | let mut no_traits_found = true; | ||
82 | for (trait_path, trait_) in found_traits.inspect(|_| no_traits_found = false) { | ||
83 | add_assist(acc, ctx, &attr, &trait_path, Some(trait_), &annotated_name, insert_pos)?; | ||
84 | } | ||
85 | if no_traits_found { | ||
86 | add_assist(acc, ctx, &attr, &trait_path, None, &annotated_name, insert_pos)?; | ||
87 | } | ||
88 | Some(()) | ||
89 | } | ||
90 | |||
91 | fn add_assist( | ||
92 | acc: &mut Assists, | ||
93 | ctx: &AssistContext, | ||
94 | attr: &ast::Attr, | ||
95 | trait_path: &ast::Path, | ||
96 | trait_: Option<hir::Trait>, | ||
97 | annotated_name: &ast::Name, | ||
98 | insert_pos: TextSize, | ||
99 | ) -> Option<()> { | ||
100 | let target = attr.syntax().text_range(); | ||
101 | let input = attr.token_tree()?; | ||
102 | let label = format!("Convert to manual `impl {} for {}`", trait_path, annotated_name); | ||
103 | let trait_name = trait_path.segment().and_then(|seg| seg.name_ref())?; | ||
104 | |||
105 | acc.add( | ||
106 | AssistId("replace_derive_with_manual_impl", AssistKind::Refactor), | ||
107 | label, | ||
108 | target, | ||
109 | |builder| { | ||
110 | let impl_def_with_items = | ||
111 | impl_def_from_trait(&ctx.sema, annotated_name, trait_, trait_path); | ||
112 | update_attribute(builder, &input, &trait_name, &attr); | ||
113 | match (ctx.config.snippet_cap, impl_def_with_items) { | ||
114 | (None, _) => builder.insert( | ||
115 | insert_pos, | ||
116 | format!("\n\nimpl {} for {} {{\n\n}}", trait_path, annotated_name), | ||
117 | ), | ||
118 | (Some(cap), None) => builder.insert_snippet( | ||
119 | cap, | ||
120 | insert_pos, | ||
121 | format!("\n\nimpl {} for {} {{\n $0\n}}", trait_path, annotated_name), | ||
122 | ), | ||
123 | (Some(cap), Some((impl_def, first_assoc_item))) => { | ||
124 | let mut cursor = Cursor::Before(first_assoc_item.syntax()); | ||
125 | let placeholder; | ||
126 | if let ast::AssocItem::Fn(ref func) = first_assoc_item { | ||
127 | if let Some(m) = func.syntax().descendants().find_map(ast::MacroCall::cast) | ||
128 | { | ||
129 | if m.syntax().text() == "todo!()" { | ||
130 | placeholder = m; | ||
131 | cursor = Cursor::Replace(placeholder.syntax()); | ||
132 | } | ||
133 | } | ||
134 | } | ||
135 | |||
136 | builder.insert_snippet( | ||
137 | cap, | ||
138 | insert_pos, | ||
139 | format!("\n\n{}", render_snippet(cap, impl_def.syntax(), cursor)), | ||
140 | ) | ||
141 | } | ||
142 | }; | ||
143 | }, | ||
144 | ) | ||
145 | } | ||
146 | |||
147 | fn impl_def_from_trait( | ||
148 | sema: &hir::Semantics<ide_db::RootDatabase>, | ||
149 | annotated_name: &ast::Name, | ||
150 | trait_: Option<hir::Trait>, | ||
151 | trait_path: &ast::Path, | ||
152 | ) -> Option<(ast::Impl, ast::AssocItem)> { | ||
153 | let trait_ = trait_?; | ||
154 | let target_scope = sema.scope(annotated_name.syntax()); | ||
155 | let trait_items = filter_assoc_items(sema.db, &trait_.items(sema.db), DefaultMethods::No); | ||
156 | if trait_items.is_empty() { | ||
157 | return None; | ||
158 | } | ||
159 | let impl_def = make::impl_trait( | ||
160 | trait_path.clone(), | ||
161 | make::path_unqualified(make::path_segment(make::name_ref(annotated_name.text()))), | ||
162 | ); | ||
163 | let (impl_def, first_assoc_item) = | ||
164 | add_trait_assoc_items_to_impl(sema, trait_items, trait_, impl_def, target_scope); | ||
165 | Some((impl_def, first_assoc_item)) | ||
166 | } | ||
167 | |||
168 | fn update_attribute( | ||
169 | builder: &mut AssistBuilder, | ||
170 | input: &ast::TokenTree, | ||
171 | trait_name: &ast::NameRef, | ||
172 | attr: &ast::Attr, | ||
173 | ) { | ||
174 | let new_attr_input = input | ||
175 | .syntax() | ||
176 | .descendants_with_tokens() | ||
177 | .filter(|t| t.kind() == IDENT) | ||
178 | .filter_map(|t| t.into_token().map(|t| t.text().clone())) | ||
179 | .filter(|t| t != trait_name.text()) | ||
180 | .collect::<Vec<SmolStr>>(); | ||
181 | let has_more_derives = !new_attr_input.is_empty(); | ||
182 | |||
183 | if has_more_derives { | ||
184 | let new_attr_input = format!("({})", new_attr_input.iter().format(", ")); | ||
185 | builder.replace(input.syntax().text_range(), new_attr_input); | ||
186 | } else { | ||
187 | let attr_range = attr.syntax().text_range(); | ||
188 | builder.delete(attr_range); | ||
189 | |||
190 | if let Some(line_break_range) = attr | ||
191 | .syntax() | ||
192 | .next_sibling_or_token() | ||
193 | .filter(|t| t.kind() == WHITESPACE) | ||
194 | .map(|t| t.text_range()) | ||
195 | { | ||
196 | builder.delete(line_break_range); | ||
197 | } | ||
198 | } | ||
199 | } | ||
200 | |||
201 | #[cfg(test)] | ||
202 | mod tests { | ||
203 | use crate::tests::{check_assist, check_assist_not_applicable}; | ||
204 | |||
205 | use super::*; | ||
206 | |||
207 | #[test] | ||
208 | fn add_custom_impl_debug() { | ||
209 | check_assist( | ||
210 | replace_derive_with_manual_impl, | ||
211 | " | ||
212 | mod fmt { | ||
213 | pub struct Error; | ||
214 | pub type Result = Result<(), Error>; | ||
215 | pub struct Formatter<'a>; | ||
216 | pub trait Debug { | ||
217 | fn fmt(&self, f: &mut Formatter<'_>) -> Result; | ||
218 | } | ||
219 | } | ||
220 | |||
221 | #[derive(Debu<|>g)] | ||
222 | struct Foo { | ||
223 | bar: String, | ||
224 | } | ||
225 | ", | ||
226 | " | ||
227 | mod fmt { | ||
228 | pub struct Error; | ||
229 | pub type Result = Result<(), Error>; | ||
230 | pub struct Formatter<'a>; | ||
231 | pub trait Debug { | ||
232 | fn fmt(&self, f: &mut Formatter<'_>) -> Result; | ||
233 | } | ||
234 | } | ||
235 | |||
236 | struct Foo { | ||
237 | bar: String, | ||
238 | } | ||
239 | |||
240 | impl fmt::Debug for Foo { | ||
241 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
242 | ${0:todo!()} | ||
243 | } | ||
244 | } | ||
245 | ", | ||
246 | ) | ||
247 | } | ||
248 | #[test] | ||
249 | fn add_custom_impl_all() { | ||
250 | check_assist( | ||
251 | replace_derive_with_manual_impl, | ||
252 | " | ||
253 | mod foo { | ||
254 | pub trait Bar { | ||
255 | type Qux; | ||
256 | const Baz: usize = 42; | ||
257 | const Fez: usize; | ||
258 | fn foo(); | ||
259 | fn bar() {} | ||
260 | } | ||
261 | } | ||
262 | |||
263 | #[derive(<|>Bar)] | ||
264 | struct Foo { | ||
265 | bar: String, | ||
266 | } | ||
267 | ", | ||
268 | " | ||
269 | mod foo { | ||
270 | pub trait Bar { | ||
271 | type Qux; | ||
272 | const Baz: usize = 42; | ||
273 | const Fez: usize; | ||
274 | fn foo(); | ||
275 | fn bar() {} | ||
276 | } | ||
277 | } | ||
278 | |||
279 | struct Foo { | ||
280 | bar: String, | ||
281 | } | ||
282 | |||
283 | impl foo::Bar for Foo { | ||
284 | $0type Qux; | ||
285 | |||
286 | const Baz: usize = 42; | ||
287 | |||
288 | const Fez: usize; | ||
289 | |||
290 | fn foo() { | ||
291 | todo!() | ||
292 | } | ||
293 | } | ||
294 | ", | ||
295 | ) | ||
296 | } | ||
297 | #[test] | ||
298 | fn add_custom_impl_for_unique_input() { | ||
299 | check_assist( | ||
300 | replace_derive_with_manual_impl, | ||
301 | " | ||
302 | #[derive(Debu<|>g)] | ||
303 | struct Foo { | ||
304 | bar: String, | ||
305 | } | ||
306 | ", | ||
307 | " | ||
308 | struct Foo { | ||
309 | bar: String, | ||
310 | } | ||
311 | |||
312 | impl Debug for Foo { | ||
313 | $0 | ||
314 | } | ||
315 | ", | ||
316 | ) | ||
317 | } | ||
318 | |||
319 | #[test] | ||
320 | fn add_custom_impl_for_with_visibility_modifier() { | ||
321 | check_assist( | ||
322 | replace_derive_with_manual_impl, | ||
323 | " | ||
324 | #[derive(Debug<|>)] | ||
325 | pub struct Foo { | ||
326 | bar: String, | ||
327 | } | ||
328 | ", | ||
329 | " | ||
330 | pub struct Foo { | ||
331 | bar: String, | ||
332 | } | ||
333 | |||
334 | impl Debug for Foo { | ||
335 | $0 | ||
336 | } | ||
337 | ", | ||
338 | ) | ||
339 | } | ||
340 | |||
341 | #[test] | ||
342 | fn add_custom_impl_when_multiple_inputs() { | ||
343 | check_assist( | ||
344 | replace_derive_with_manual_impl, | ||
345 | " | ||
346 | #[derive(Display, Debug<|>, Serialize)] | ||
347 | struct Foo {} | ||
348 | ", | ||
349 | " | ||
350 | #[derive(Display, Serialize)] | ||
351 | struct Foo {} | ||
352 | |||
353 | impl Debug for Foo { | ||
354 | $0 | ||
355 | } | ||
356 | ", | ||
357 | ) | ||
358 | } | ||
359 | |||
360 | #[test] | ||
361 | fn test_ignore_derive_macro_without_input() { | ||
362 | check_assist_not_applicable( | ||
363 | replace_derive_with_manual_impl, | ||
364 | " | ||
365 | #[derive(<|>)] | ||
366 | struct Foo {} | ||
367 | ", | ||
368 | ) | ||
369 | } | ||
370 | |||
371 | #[test] | ||
372 | fn test_ignore_if_cursor_on_param() { | ||
373 | check_assist_not_applicable( | ||
374 | replace_derive_with_manual_impl, | ||
375 | " | ||
376 | #[derive<|>(Debug)] | ||
377 | struct Foo {} | ||
378 | ", | ||
379 | ); | ||
380 | |||
381 | check_assist_not_applicable( | ||
382 | replace_derive_with_manual_impl, | ||
383 | " | ||
384 | #[derive(Debug)<|>] | ||
385 | struct Foo {} | ||
386 | ", | ||
387 | ) | ||
388 | } | ||
389 | |||
390 | #[test] | ||
391 | fn test_ignore_if_not_derive() { | ||
392 | check_assist_not_applicable( | ||
393 | replace_derive_with_manual_impl, | ||
394 | " | ||
395 | #[allow(non_camel_<|>case_types)] | ||
396 | struct Foo {} | ||
397 | ", | ||
398 | ) | ||
399 | } | ||
400 | } | ||
diff --git a/crates/assists/src/handlers/replace_qualified_name_with_use.rs b/crates/assists/src/handlers/replace_qualified_name_with_use.rs index d7e1d9580..a66db9ae3 100644 --- a/crates/assists/src/handlers/replace_qualified_name_with_use.rs +++ b/crates/assists/src/handlers/replace_qualified_name_with_use.rs | |||
@@ -34,7 +34,7 @@ pub(crate) fn replace_qualified_name_with_use( | |||
34 | } | 34 | } |
35 | 35 | ||
36 | let target = path.syntax().text_range(); | 36 | let target = path.syntax().text_range(); |
37 | let scope = ImportScope::find_insert_use_container(path.syntax(), ctx)?; | 37 | let scope = ImportScope::find_insert_use_container(path.syntax(), &ctx.sema)?; |
38 | let syntax = scope.as_syntax_node(); | 38 | let syntax = scope.as_syntax_node(); |
39 | acc.add( | 39 | acc.add( |
40 | AssistId("replace_qualified_name_with_use", AssistKind::RefactorRewrite), | 40 | AssistId("replace_qualified_name_with_use", AssistKind::RefactorRewrite), |
diff --git a/crates/assists/src/handlers/unwrap_block.rs b/crates/assists/src/handlers/unwrap_block.rs index 36ef871b9..676db7137 100644 --- a/crates/assists/src/handlers/unwrap_block.rs +++ b/crates/assists/src/handlers/unwrap_block.rs | |||
@@ -3,7 +3,7 @@ use syntax::{ | |||
3 | self, | 3 | self, |
4 | edit::{AstNodeEdit, IndentLevel}, | 4 | edit::{AstNodeEdit, IndentLevel}, |
5 | }, | 5 | }, |
6 | AstNode, TextRange, T, | 6 | AstNode, SyntaxKind, TextRange, T, |
7 | }; | 7 | }; |
8 | 8 | ||
9 | use crate::{utils::unwrap_trivial_block, AssistContext, AssistId, AssistKind, Assists}; | 9 | use crate::{utils::unwrap_trivial_block, AssistContext, AssistId, AssistKind, Assists}; |
@@ -31,11 +31,21 @@ pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext) -> Option<()> | |||
31 | 31 | ||
32 | let l_curly_token = ctx.find_token_syntax_at_offset(T!['{'])?; | 32 | let l_curly_token = ctx.find_token_syntax_at_offset(T!['{'])?; |
33 | let mut block = ast::BlockExpr::cast(l_curly_token.parent())?; | 33 | let mut block = ast::BlockExpr::cast(l_curly_token.parent())?; |
34 | let target = block.syntax().text_range(); | ||
34 | let mut parent = block.syntax().parent()?; | 35 | let mut parent = block.syntax().parent()?; |
35 | if ast::MatchArm::can_cast(parent.kind()) { | 36 | if ast::MatchArm::can_cast(parent.kind()) { |
36 | parent = parent.ancestors().find(|it| ast::MatchExpr::can_cast(it.kind()))? | 37 | parent = parent.ancestors().find(|it| ast::MatchExpr::can_cast(it.kind()))? |
37 | } | 38 | } |
38 | 39 | ||
40 | if matches!(parent.kind(), SyntaxKind::BLOCK_EXPR | SyntaxKind::EXPR_STMT) { | ||
41 | return acc.add(assist_id, assist_label, target, |builder| { | ||
42 | builder.replace( | ||
43 | block.syntax().text_range(), | ||
44 | update_expr_string(block.to_string(), &[' ', '{', '\n']), | ||
45 | ); | ||
46 | }); | ||
47 | } | ||
48 | |||
39 | let parent = ast::Expr::cast(parent)?; | 49 | let parent = ast::Expr::cast(parent)?; |
40 | 50 | ||
41 | match parent.clone() { | 51 | match parent.clone() { |
@@ -48,7 +58,6 @@ pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext) -> Option<()> | |||
48 | // For `else if` blocks | 58 | // For `else if` blocks |
49 | let ancestor_then_branch = ancestor.then_branch()?; | 59 | let ancestor_then_branch = ancestor.then_branch()?; |
50 | 60 | ||
51 | let target = then_branch.syntax().text_range(); | ||
52 | return acc.add(assist_id, assist_label, target, |edit| { | 61 | return acc.add(assist_id, assist_label, target, |edit| { |
53 | let range_to_del_else_if = TextRange::new( | 62 | let range_to_del_else_if = TextRange::new( |
54 | ancestor_then_branch.syntax().text_range().end(), | 63 | ancestor_then_branch.syntax().text_range().end(), |
@@ -68,7 +77,6 @@ pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext) -> Option<()> | |||
68 | }); | 77 | }); |
69 | } | 78 | } |
70 | } else { | 79 | } else { |
71 | let target = block.syntax().text_range(); | ||
72 | return acc.add(assist_id, assist_label, target, |edit| { | 80 | return acc.add(assist_id, assist_label, target, |edit| { |
73 | let range_to_del = TextRange::new( | 81 | let range_to_del = TextRange::new( |
74 | then_branch.syntax().text_range().end(), | 82 | then_branch.syntax().text_range().end(), |
@@ -84,7 +92,6 @@ pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext) -> Option<()> | |||
84 | }; | 92 | }; |
85 | 93 | ||
86 | let unwrapped = unwrap_trivial_block(block); | 94 | let unwrapped = unwrap_trivial_block(block); |
87 | let target = unwrapped.syntax().text_range(); | ||
88 | acc.add(assist_id, assist_label, target, |builder| { | 95 | acc.add(assist_id, assist_label, target, |builder| { |
89 | builder.replace( | 96 | builder.replace( |
90 | parent.syntax().text_range(), | 97 | parent.syntax().text_range(), |
@@ -112,31 +119,89 @@ mod tests { | |||
112 | use super::*; | 119 | use super::*; |
113 | 120 | ||
114 | #[test] | 121 | #[test] |
122 | fn unwrap_tail_expr_block() { | ||
123 | check_assist( | ||
124 | unwrap_block, | ||
125 | r#" | ||
126 | fn main() { | ||
127 | <|>{ | ||
128 | 92 | ||
129 | } | ||
130 | } | ||
131 | "#, | ||
132 | r#" | ||
133 | fn main() { | ||
134 | 92 | ||
135 | } | ||
136 | "#, | ||
137 | ) | ||
138 | } | ||
139 | |||
140 | #[test] | ||
141 | fn unwrap_stmt_expr_block() { | ||
142 | check_assist( | ||
143 | unwrap_block, | ||
144 | r#" | ||
145 | fn main() { | ||
146 | <|>{ | ||
147 | 92; | ||
148 | } | ||
149 | () | ||
150 | } | ||
151 | "#, | ||
152 | r#" | ||
153 | fn main() { | ||
154 | 92; | ||
155 | () | ||
156 | } | ||
157 | "#, | ||
158 | ); | ||
159 | // Pedantically, we should add an `;` here... | ||
160 | check_assist( | ||
161 | unwrap_block, | ||
162 | r#" | ||
163 | fn main() { | ||
164 | <|>{ | ||
165 | 92 | ||
166 | } | ||
167 | () | ||
168 | } | ||
169 | "#, | ||
170 | r#" | ||
171 | fn main() { | ||
172 | 92 | ||
173 | () | ||
174 | } | ||
175 | "#, | ||
176 | ); | ||
177 | } | ||
178 | |||
179 | #[test] | ||
115 | fn simple_if() { | 180 | fn simple_if() { |
116 | check_assist( | 181 | check_assist( |
117 | unwrap_block, | 182 | unwrap_block, |
118 | r#" | 183 | r#" |
119 | fn main() { | 184 | fn main() { |
120 | bar(); | 185 | bar(); |
121 | if true {<|> | 186 | if true {<|> |
122 | foo(); | 187 | foo(); |
123 | 188 | ||
124 | //comment | 189 | //comment |
125 | bar(); | 190 | bar(); |
126 | } else { | 191 | } else { |
127 | println!("bar"); | 192 | println!("bar"); |
128 | } | 193 | } |
129 | } | 194 | } |
130 | "#, | 195 | "#, |
131 | r#" | 196 | r#" |
132 | fn main() { | 197 | fn main() { |
133 | bar(); | 198 | bar(); |
134 | foo(); | 199 | foo(); |
135 | 200 | ||
136 | //comment | 201 | //comment |
137 | bar(); | 202 | bar(); |
138 | } | 203 | } |
139 | "#, | 204 | "#, |
140 | ); | 205 | ); |
141 | } | 206 | } |
142 | 207 | ||
@@ -145,30 +210,30 @@ mod tests { | |||
145 | check_assist( | 210 | check_assist( |
146 | unwrap_block, | 211 | unwrap_block, |
147 | r#" | 212 | r#" |
148 | fn main() { | 213 | fn main() { |
149 | bar(); | 214 | bar(); |
150 | if true { | 215 | if true { |
151 | foo(); | 216 | foo(); |
152 | 217 | ||
153 | //comment | 218 | //comment |
154 | bar(); | 219 | bar(); |
155 | } else {<|> | 220 | } else {<|> |
156 | println!("bar"); | 221 | println!("bar"); |
157 | } | 222 | } |
158 | } | 223 | } |
159 | "#, | 224 | "#, |
160 | r#" | 225 | r#" |
161 | fn main() { | 226 | fn main() { |
162 | bar(); | 227 | bar(); |
163 | if true { | 228 | if true { |
164 | foo(); | 229 | foo(); |
165 | 230 | ||
166 | //comment | 231 | //comment |
167 | bar(); | 232 | bar(); |
168 | } | 233 | } |
169 | println!("bar"); | 234 | println!("bar"); |
170 | } | 235 | } |
171 | "#, | 236 | "#, |
172 | ); | 237 | ); |
173 | } | 238 | } |
174 | 239 | ||
@@ -177,32 +242,32 @@ mod tests { | |||
177 | check_assist( | 242 | check_assist( |
178 | unwrap_block, | 243 | unwrap_block, |
179 | r#" | 244 | r#" |
180 | fn main() { | 245 | fn main() { |
181 | //bar(); | 246 | //bar(); |
182 | if true { | 247 | if true { |
183 | println!("true"); | 248 | println!("true"); |
184 | 249 | ||
185 | //comment | 250 | //comment |
186 | //bar(); | 251 | //bar(); |
187 | } else if false {<|> | 252 | } else if false {<|> |
188 | println!("bar"); | 253 | println!("bar"); |
189 | } else { | 254 | } else { |
190 | println!("foo"); | 255 | println!("foo"); |
191 | } | 256 | } |
192 | } | 257 | } |
193 | "#, | 258 | "#, |
194 | r#" | 259 | r#" |
195 | fn main() { | 260 | fn main() { |
196 | //bar(); | 261 | //bar(); |
197 | if true { | 262 | if true { |
198 | println!("true"); | 263 | println!("true"); |
199 | 264 | ||
200 | //comment | 265 | //comment |
201 | //bar(); | 266 | //bar(); |
202 | } | 267 | } |
203 | println!("bar"); | 268 | println!("bar"); |
204 | } | 269 | } |
205 | "#, | 270 | "#, |
206 | ); | 271 | ); |
207 | } | 272 | } |
208 | 273 | ||
@@ -211,34 +276,34 @@ mod tests { | |||
211 | check_assist( | 276 | check_assist( |
212 | unwrap_block, | 277 | unwrap_block, |
213 | r#" | 278 | r#" |
214 | fn main() { | 279 | fn main() { |
215 | //bar(); | 280 | //bar(); |
216 | if true { | 281 | if true { |
217 | println!("true"); | 282 | println!("true"); |
218 | 283 | ||
219 | //comment | 284 | //comment |
220 | //bar(); | 285 | //bar(); |
221 | } else if false { | 286 | } else if false { |
222 | println!("bar"); | 287 | println!("bar"); |
223 | } else if true {<|> | 288 | } else if true {<|> |
224 | println!("foo"); | 289 | println!("foo"); |
225 | } | 290 | } |
226 | } | 291 | } |
227 | "#, | 292 | "#, |
228 | r#" | 293 | r#" |
229 | fn main() { | 294 | fn main() { |
230 | //bar(); | 295 | //bar(); |
231 | if true { | 296 | if true { |
232 | println!("true"); | 297 | println!("true"); |
233 | 298 | ||
234 | //comment | 299 | //comment |
235 | //bar(); | 300 | //bar(); |
236 | } else if false { | 301 | } else if false { |
237 | println!("bar"); | 302 | println!("bar"); |
238 | } | 303 | } |
239 | println!("foo"); | 304 | println!("foo"); |
240 | } | 305 | } |
241 | "#, | 306 | "#, |
242 | ); | 307 | ); |
243 | } | 308 | } |
244 | 309 | ||
@@ -247,38 +312,38 @@ mod tests { | |||
247 | check_assist( | 312 | check_assist( |
248 | unwrap_block, | 313 | unwrap_block, |
249 | r#" | 314 | r#" |
250 | fn main() { | 315 | fn main() { |
251 | //bar(); | 316 | //bar(); |
252 | if true { | 317 | if true { |
253 | println!("true"); | 318 | println!("true"); |
254 | 319 | ||
255 | //comment | 320 | //comment |
256 | //bar(); | 321 | //bar(); |
257 | } else if false { | 322 | } else if false { |
258 | println!("bar"); | 323 | println!("bar"); |
259 | } else if true { | 324 | } else if true { |
260 | println!("foo"); | 325 | println!("foo"); |
261 | } else {<|> | 326 | } else {<|> |
262 | println!("else"); | 327 | println!("else"); |
263 | } | 328 | } |
264 | } | 329 | } |
265 | "#, | 330 | "#, |
266 | r#" | 331 | r#" |
267 | fn main() { | 332 | fn main() { |
268 | //bar(); | 333 | //bar(); |
269 | if true { | 334 | if true { |
270 | println!("true"); | 335 | println!("true"); |
271 | 336 | ||
272 | //comment | 337 | //comment |
273 | //bar(); | 338 | //bar(); |
274 | } else if false { | 339 | } else if false { |
275 | println!("bar"); | 340 | println!("bar"); |
276 | } else if true { | 341 | } else if true { |
277 | println!("foo"); | 342 | println!("foo"); |
278 | } | 343 | } |
279 | println!("else"); | 344 | println!("else"); |
280 | } | 345 | } |
281 | "#, | 346 | "#, |
282 | ); | 347 | ); |
283 | } | 348 | } |
284 | 349 | ||
@@ -287,36 +352,36 @@ mod tests { | |||
287 | check_assist( | 352 | check_assist( |
288 | unwrap_block, | 353 | unwrap_block, |
289 | r#" | 354 | r#" |
290 | fn main() { | 355 | fn main() { |
291 | //bar(); | 356 | //bar(); |
292 | if true { | 357 | if true { |
293 | println!("true"); | 358 | println!("true"); |
294 | 359 | ||
295 | //comment | 360 | //comment |
296 | //bar(); | 361 | //bar(); |
297 | } else if false { | 362 | } else if false { |
298 | println!("bar"); | 363 | println!("bar"); |
299 | } else if true {<|> | 364 | } else if true {<|> |
300 | println!("foo"); | 365 | println!("foo"); |
301 | } else { | 366 | } else { |
302 | println!("else"); | 367 | println!("else"); |
303 | } | 368 | } |
304 | } | 369 | } |
305 | "#, | 370 | "#, |
306 | r#" | 371 | r#" |
307 | fn main() { | 372 | fn main() { |
308 | //bar(); | 373 | //bar(); |
309 | if true { | 374 | if true { |
310 | println!("true"); | 375 | println!("true"); |
311 | 376 | ||
312 | //comment | 377 | //comment |
313 | //bar(); | 378 | //bar(); |
314 | } else if false { | 379 | } else if false { |
315 | println!("bar"); | 380 | println!("bar"); |
316 | } | 381 | } |
317 | println!("foo"); | 382 | println!("foo"); |
318 | } | 383 | } |
319 | "#, | 384 | "#, |
320 | ); | 385 | ); |
321 | } | 386 | } |
322 | 387 | ||
@@ -325,18 +390,18 @@ mod tests { | |||
325 | check_assist_not_applicable( | 390 | check_assist_not_applicable( |
326 | unwrap_block, | 391 | unwrap_block, |
327 | r#" | 392 | r#" |
328 | fn main() { | 393 | fn main() { |
329 | bar();<|> | 394 | bar();<|> |
330 | if true { | 395 | if true { |
331 | foo(); | 396 | foo(); |
332 | 397 | ||
333 | //comment | 398 | //comment |
334 | bar(); | 399 | bar(); |
335 | } else { | 400 | } else { |
336 | println!("bar"); | 401 | println!("bar"); |
337 | } | 402 | } |
338 | } | 403 | } |
339 | "#, | 404 | "#, |
340 | ); | 405 | ); |
341 | } | 406 | } |
342 | 407 | ||
@@ -345,31 +410,31 @@ mod tests { | |||
345 | check_assist( | 410 | check_assist( |
346 | unwrap_block, | 411 | unwrap_block, |
347 | r#" | 412 | r#" |
348 | fn main() { | 413 | fn main() { |
349 | for i in 0..5 {<|> | 414 | for i in 0..5 {<|> |
350 | if true { | 415 | if true { |
351 | foo(); | 416 | foo(); |
352 | 417 | ||
353 | //comment | 418 | //comment |
354 | bar(); | 419 | bar(); |
355 | } else { | 420 | } else { |
356 | println!("bar"); | 421 | println!("bar"); |
357 | } | 422 | } |
358 | } | 423 | } |
359 | } | 424 | } |
360 | "#, | 425 | "#, |
361 | r#" | 426 | r#" |
362 | fn main() { | 427 | fn main() { |
363 | if true { | 428 | if true { |
364 | foo(); | 429 | foo(); |
365 | 430 | ||
366 | //comment | 431 | //comment |
367 | bar(); | 432 | bar(); |
368 | } else { | 433 | } else { |
369 | println!("bar"); | 434 | println!("bar"); |
370 | } | 435 | } |
371 | } | 436 | } |
372 | "#, | 437 | "#, |
373 | ); | 438 | ); |
374 | } | 439 | } |
375 | 440 | ||
@@ -378,29 +443,29 @@ mod tests { | |||
378 | check_assist( | 443 | check_assist( |
379 | unwrap_block, | 444 | unwrap_block, |
380 | r#" | 445 | r#" |
381 | fn main() { | 446 | fn main() { |
382 | for i in 0..5 { | 447 | for i in 0..5 { |
383 | if true {<|> | 448 | if true {<|> |
384 | foo(); | 449 | foo(); |
385 | 450 | ||
386 | //comment | 451 | //comment |
387 | bar(); | 452 | bar(); |
388 | } else { | 453 | } else { |
389 | println!("bar"); | 454 | println!("bar"); |
390 | } | 455 | } |
391 | } | 456 | } |
392 | } | 457 | } |
393 | "#, | 458 | "#, |
394 | r#" | 459 | r#" |
395 | fn main() { | 460 | fn main() { |
396 | for i in 0..5 { | 461 | for i in 0..5 { |
397 | foo(); | 462 | foo(); |
398 | 463 | ||
399 | //comment | 464 | //comment |
400 | bar(); | 465 | bar(); |
401 | } | 466 | } |
402 | } | 467 | } |
403 | "#, | 468 | "#, |
404 | ); | 469 | ); |
405 | } | 470 | } |
406 | 471 | ||
@@ -409,31 +474,31 @@ mod tests { | |||
409 | check_assist( | 474 | check_assist( |
410 | unwrap_block, | 475 | unwrap_block, |
411 | r#" | 476 | r#" |
412 | fn main() { | 477 | fn main() { |
413 | loop {<|> | 478 | loop {<|> |
414 | if true { | 479 | if true { |
415 | foo(); | 480 | foo(); |
416 | 481 | ||
417 | //comment | 482 | //comment |
418 | bar(); | 483 | bar(); |
419 | } else { | 484 | } else { |
420 | println!("bar"); | 485 | println!("bar"); |
421 | } | 486 | } |
422 | } | 487 | } |
423 | } | 488 | } |
424 | "#, | 489 | "#, |
425 | r#" | 490 | r#" |
426 | fn main() { | 491 | fn main() { |
427 | if true { | 492 | if true { |
428 | foo(); | 493 | foo(); |
429 | 494 | ||
430 | //comment | 495 | //comment |
431 | bar(); | 496 | bar(); |
432 | } else { | 497 | } else { |
433 | println!("bar"); | 498 | println!("bar"); |
434 | } | 499 | } |
435 | } | 500 | } |
436 | "#, | 501 | "#, |
437 | ); | 502 | ); |
438 | } | 503 | } |
439 | 504 | ||
@@ -442,31 +507,31 @@ mod tests { | |||
442 | check_assist( | 507 | check_assist( |
443 | unwrap_block, | 508 | unwrap_block, |
444 | r#" | 509 | r#" |
445 | fn main() { | 510 | fn main() { |
446 | while true {<|> | 511 | while true {<|> |
447 | if true { | 512 | if true { |
448 | foo(); | 513 | foo(); |
449 | 514 | ||
450 | //comment | 515 | //comment |
451 | bar(); | 516 | bar(); |
452 | } else { | 517 | } else { |
453 | println!("bar"); | 518 | println!("bar"); |
454 | } | 519 | } |
455 | } | 520 | } |
456 | } | 521 | } |
457 | "#, | 522 | "#, |
458 | r#" | 523 | r#" |
459 | fn main() { | 524 | fn main() { |
460 | if true { | 525 | if true { |
461 | foo(); | 526 | foo(); |
462 | 527 | ||
463 | //comment | 528 | //comment |
464 | bar(); | 529 | bar(); |
465 | } else { | 530 | } else { |
466 | println!("bar"); | 531 | println!("bar"); |
467 | } | 532 | } |
468 | } | 533 | } |
469 | "#, | 534 | "#, |
470 | ); | 535 | ); |
471 | } | 536 | } |
472 | 537 | ||
@@ -499,19 +564,19 @@ fn main() { | |||
499 | check_assist_not_applicable( | 564 | check_assist_not_applicable( |
500 | unwrap_block, | 565 | unwrap_block, |
501 | r#" | 566 | r#" |
502 | fn main() { | 567 | fn main() { |
503 | while true { | 568 | while true { |
504 | if true { | 569 | if true { |
505 | foo();<|> | 570 | foo();<|> |
506 | 571 | ||
507 | //comment | 572 | //comment |
508 | bar(); | 573 | bar(); |
509 | } else { | 574 | } else { |
510 | println!("bar"); | 575 | println!("bar"); |
511 | } | 576 | } |
512 | } | 577 | } |
513 | } | 578 | } |
514 | "#, | 579 | "#, |
515 | ); | 580 | ); |
516 | } | 581 | } |
517 | } | 582 | } |
diff --git a/crates/assists/src/handlers/wrap_return_type_in_result.rs b/crates/assists/src/handlers/wrap_return_type_in_result.rs new file mode 100644 index 000000000..59e5debb1 --- /dev/null +++ b/crates/assists/src/handlers/wrap_return_type_in_result.rs | |||
@@ -0,0 +1,1158 @@ | |||
1 | use std::iter; | ||
2 | |||
3 | use syntax::{ | ||
4 | ast::{self, make, BlockExpr, Expr, LoopBodyOwner}, | ||
5 | match_ast, AstNode, SyntaxNode, | ||
6 | }; | ||
7 | use test_utils::mark; | ||
8 | |||
9 | use crate::{AssistContext, AssistId, AssistKind, Assists}; | ||
10 | |||
11 | // Assist: wrap_return_type_in_result | ||
12 | // | ||
13 | // Wrap the function's return type into Result. | ||
14 | // | ||
15 | // ``` | ||
16 | // fn foo() -> i32<|> { 42i32 } | ||
17 | // ``` | ||
18 | // -> | ||
19 | // ``` | ||
20 | // fn foo() -> Result<i32, ${0:_}> { Ok(42i32) } | ||
21 | // ``` | ||
22 | pub(crate) fn wrap_return_type_in_result(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | ||
23 | let ret_type = ctx.find_node_at_offset::<ast::RetType>()?; | ||
24 | let parent = ret_type.syntax().parent()?; | ||
25 | let block_expr = match_ast! { | ||
26 | match parent { | ||
27 | ast::Fn(func) => func.body()?, | ||
28 | ast::ClosureExpr(closure) => match closure.body()? { | ||
29 | Expr::BlockExpr(block) => block, | ||
30 | // closures require a block when a return type is specified | ||
31 | _ => return None, | ||
32 | }, | ||
33 | _ => return None, | ||
34 | } | ||
35 | }; | ||
36 | |||
37 | let type_ref = &ret_type.ty()?; | ||
38 | let ret_type_str = type_ref.syntax().text().to_string(); | ||
39 | let first_part_ret_type = ret_type_str.splitn(2, '<').next(); | ||
40 | if let Some(ret_type_first_part) = first_part_ret_type { | ||
41 | if ret_type_first_part.ends_with("Result") { | ||
42 | mark::hit!(wrap_return_type_in_result_simple_return_type_already_result); | ||
43 | return None; | ||
44 | } | ||
45 | } | ||
46 | |||
47 | acc.add( | ||
48 | AssistId("wrap_return_type_in_result", AssistKind::RefactorRewrite), | ||
49 | "Wrap return type in Result", | ||
50 | type_ref.syntax().text_range(), | ||
51 | |builder| { | ||
52 | let mut tail_return_expr_collector = TailReturnCollector::new(); | ||
53 | tail_return_expr_collector.collect_jump_exprs(&block_expr, false); | ||
54 | tail_return_expr_collector.collect_tail_exprs(&block_expr); | ||
55 | |||
56 | for ret_expr_arg in tail_return_expr_collector.exprs_to_wrap { | ||
57 | let ok_wrapped = make::expr_call( | ||
58 | make::expr_path(make::path_unqualified(make::path_segment(make::name_ref( | ||
59 | "Ok", | ||
60 | )))), | ||
61 | make::arg_list(iter::once(ret_expr_arg.clone())), | ||
62 | ); | ||
63 | builder.replace_ast(ret_expr_arg, ok_wrapped); | ||
64 | } | ||
65 | |||
66 | match ctx.config.snippet_cap { | ||
67 | Some(cap) => { | ||
68 | let snippet = format!("Result<{}, ${{0:_}}>", type_ref); | ||
69 | builder.replace_snippet(cap, type_ref.syntax().text_range(), snippet) | ||
70 | } | ||
71 | None => builder | ||
72 | .replace(type_ref.syntax().text_range(), format!("Result<{}, _>", type_ref)), | ||
73 | } | ||
74 | }, | ||
75 | ) | ||
76 | } | ||
77 | |||
78 | struct TailReturnCollector { | ||
79 | exprs_to_wrap: Vec<ast::Expr>, | ||
80 | } | ||
81 | |||
82 | impl TailReturnCollector { | ||
83 | fn new() -> Self { | ||
84 | Self { exprs_to_wrap: vec![] } | ||
85 | } | ||
86 | /// Collect all`return` expression | ||
87 | fn collect_jump_exprs(&mut self, block_expr: &BlockExpr, collect_break: bool) { | ||
88 | let statements = block_expr.statements(); | ||
89 | for stmt in statements { | ||
90 | let expr = match &stmt { | ||
91 | ast::Stmt::ExprStmt(stmt) => stmt.expr(), | ||
92 | ast::Stmt::LetStmt(stmt) => stmt.initializer(), | ||
93 | ast::Stmt::Item(_) => continue, | ||
94 | }; | ||
95 | if let Some(expr) = &expr { | ||
96 | self.handle_exprs(expr, collect_break); | ||
97 | } | ||
98 | } | ||
99 | |||
100 | // Browse tail expressions for each block | ||
101 | if let Some(expr) = block_expr.expr() { | ||
102 | if let Some(last_exprs) = get_tail_expr_from_block(&expr) { | ||
103 | for last_expr in last_exprs { | ||
104 | let last_expr = match last_expr { | ||
105 | NodeType::Node(expr) => expr, | ||
106 | NodeType::Leaf(expr) => expr.syntax().clone(), | ||
107 | }; | ||
108 | |||
109 | if let Some(last_expr) = Expr::cast(last_expr.clone()) { | ||
110 | self.handle_exprs(&last_expr, collect_break); | ||
111 | } else if let Some(expr_stmt) = ast::Stmt::cast(last_expr) { | ||
112 | let expr_stmt = match &expr_stmt { | ||
113 | ast::Stmt::ExprStmt(stmt) => stmt.expr(), | ||
114 | ast::Stmt::LetStmt(stmt) => stmt.initializer(), | ||
115 | ast::Stmt::Item(_) => None, | ||
116 | }; | ||
117 | if let Some(expr) = &expr_stmt { | ||
118 | self.handle_exprs(expr, collect_break); | ||
119 | } | ||
120 | } | ||
121 | } | ||
122 | } | ||
123 | } | ||
124 | } | ||
125 | |||
126 | fn handle_exprs(&mut self, expr: &Expr, collect_break: bool) { | ||
127 | match expr { | ||
128 | Expr::BlockExpr(block_expr) => { | ||
129 | self.collect_jump_exprs(&block_expr, collect_break); | ||
130 | } | ||
131 | Expr::ReturnExpr(ret_expr) => { | ||
132 | if let Some(ret_expr_arg) = &ret_expr.expr() { | ||
133 | self.exprs_to_wrap.push(ret_expr_arg.clone()); | ||
134 | } | ||
135 | } | ||
136 | Expr::BreakExpr(break_expr) if collect_break => { | ||
137 | if let Some(break_expr_arg) = &break_expr.expr() { | ||
138 | self.exprs_to_wrap.push(break_expr_arg.clone()); | ||
139 | } | ||
140 | } | ||
141 | Expr::IfExpr(if_expr) => { | ||
142 | for block in if_expr.blocks() { | ||
143 | self.collect_jump_exprs(&block, collect_break); | ||
144 | } | ||
145 | } | ||
146 | Expr::LoopExpr(loop_expr) => { | ||
147 | if let Some(block_expr) = loop_expr.loop_body() { | ||
148 | self.collect_jump_exprs(&block_expr, collect_break); | ||
149 | } | ||
150 | } | ||
151 | Expr::ForExpr(for_expr) => { | ||
152 | if let Some(block_expr) = for_expr.loop_body() { | ||
153 | self.collect_jump_exprs(&block_expr, collect_break); | ||
154 | } | ||
155 | } | ||
156 | Expr::WhileExpr(while_expr) => { | ||
157 | if let Some(block_expr) = while_expr.loop_body() { | ||
158 | self.collect_jump_exprs(&block_expr, collect_break); | ||
159 | } | ||
160 | } | ||
161 | Expr::MatchExpr(match_expr) => { | ||
162 | if let Some(arm_list) = match_expr.match_arm_list() { | ||
163 | arm_list.arms().filter_map(|match_arm| match_arm.expr()).for_each(|expr| { | ||
164 | self.handle_exprs(&expr, collect_break); | ||
165 | }); | ||
166 | } | ||
167 | } | ||
168 | _ => {} | ||
169 | } | ||
170 | } | ||
171 | |||
172 | fn collect_tail_exprs(&mut self, block: &BlockExpr) { | ||
173 | if let Some(expr) = block.expr() { | ||
174 | self.handle_exprs(&expr, true); | ||
175 | self.fetch_tail_exprs(&expr); | ||
176 | } | ||
177 | } | ||
178 | |||
179 | fn fetch_tail_exprs(&mut self, expr: &Expr) { | ||
180 | if let Some(exprs) = get_tail_expr_from_block(expr) { | ||
181 | for node_type in &exprs { | ||
182 | match node_type { | ||
183 | NodeType::Leaf(expr) => { | ||
184 | self.exprs_to_wrap.push(expr.clone()); | ||
185 | } | ||
186 | NodeType::Node(expr) => { | ||
187 | if let Some(last_expr) = Expr::cast(expr.clone()) { | ||
188 | self.fetch_tail_exprs(&last_expr); | ||
189 | } | ||
190 | } | ||
191 | } | ||
192 | } | ||
193 | } | ||
194 | } | ||
195 | } | ||
196 | |||
197 | #[derive(Debug)] | ||
198 | enum NodeType { | ||
199 | Leaf(ast::Expr), | ||
200 | Node(SyntaxNode), | ||
201 | } | ||
202 | |||
203 | /// Get a tail expression inside a block | ||
204 | fn get_tail_expr_from_block(expr: &Expr) -> Option<Vec<NodeType>> { | ||
205 | match expr { | ||
206 | Expr::IfExpr(if_expr) => { | ||
207 | let mut nodes = vec![]; | ||
208 | for block in if_expr.blocks() { | ||
209 | if let Some(block_expr) = block.expr() { | ||
210 | if let Some(tail_exprs) = get_tail_expr_from_block(&block_expr) { | ||
211 | nodes.extend(tail_exprs); | ||
212 | } | ||
213 | } else if let Some(last_expr) = block.syntax().last_child() { | ||
214 | nodes.push(NodeType::Node(last_expr)); | ||
215 | } else { | ||
216 | nodes.push(NodeType::Node(block.syntax().clone())); | ||
217 | } | ||
218 | } | ||
219 | Some(nodes) | ||
220 | } | ||
221 | Expr::LoopExpr(loop_expr) => { | ||
222 | loop_expr.syntax().last_child().map(|lc| vec![NodeType::Node(lc)]) | ||
223 | } | ||
224 | Expr::ForExpr(for_expr) => { | ||
225 | for_expr.syntax().last_child().map(|lc| vec![NodeType::Node(lc)]) | ||
226 | } | ||
227 | Expr::WhileExpr(while_expr) => { | ||
228 | while_expr.syntax().last_child().map(|lc| vec![NodeType::Node(lc)]) | ||
229 | } | ||
230 | Expr::BlockExpr(block_expr) => { | ||
231 | block_expr.expr().map(|lc| vec![NodeType::Node(lc.syntax().clone())]) | ||
232 | } | ||
233 | Expr::MatchExpr(match_expr) => { | ||
234 | let arm_list = match_expr.match_arm_list()?; | ||
235 | let arms: Vec<NodeType> = arm_list | ||
236 | .arms() | ||
237 | .filter_map(|match_arm| match_arm.expr()) | ||
238 | .map(|expr| match expr { | ||
239 | Expr::ReturnExpr(ret_expr) => NodeType::Node(ret_expr.syntax().clone()), | ||
240 | Expr::BreakExpr(break_expr) => NodeType::Node(break_expr.syntax().clone()), | ||
241 | _ => match expr.syntax().last_child() { | ||
242 | Some(last_expr) => NodeType::Node(last_expr), | ||
243 | None => NodeType::Node(expr.syntax().clone()), | ||
244 | }, | ||
245 | }) | ||
246 | .collect(); | ||
247 | |||
248 | Some(arms) | ||
249 | } | ||
250 | Expr::BreakExpr(expr) => expr.expr().map(|e| vec![NodeType::Leaf(e)]), | ||
251 | Expr::ReturnExpr(ret_expr) => Some(vec![NodeType::Node(ret_expr.syntax().clone())]), | ||
252 | |||
253 | Expr::CallExpr(_) | ||
254 | | Expr::Literal(_) | ||
255 | | Expr::TupleExpr(_) | ||
256 | | Expr::ArrayExpr(_) | ||
257 | | Expr::ParenExpr(_) | ||
258 | | Expr::PathExpr(_) | ||
259 | | Expr::RecordExpr(_) | ||
260 | | Expr::IndexExpr(_) | ||
261 | | Expr::MethodCallExpr(_) | ||
262 | | Expr::AwaitExpr(_) | ||
263 | | Expr::CastExpr(_) | ||
264 | | Expr::RefExpr(_) | ||
265 | | Expr::PrefixExpr(_) | ||
266 | | Expr::RangeExpr(_) | ||
267 | | Expr::BinExpr(_) | ||
268 | | Expr::MacroCall(_) | ||
269 | | Expr::BoxExpr(_) => Some(vec![NodeType::Leaf(expr.clone())]), | ||
270 | _ => None, | ||
271 | } | ||
272 | } | ||
273 | |||
274 | #[cfg(test)] | ||
275 | mod tests { | ||
276 | use crate::tests::{check_assist, check_assist_not_applicable}; | ||
277 | |||
278 | use super::*; | ||
279 | |||
280 | #[test] | ||
281 | fn wrap_return_type_in_result_simple() { | ||
282 | check_assist( | ||
283 | wrap_return_type_in_result, | ||
284 | r#" | ||
285 | fn foo() -> i3<|>2 { | ||
286 | let test = "test"; | ||
287 | return 42i32; | ||
288 | } | ||
289 | "#, | ||
290 | r#" | ||
291 | fn foo() -> Result<i32, ${0:_}> { | ||
292 | let test = "test"; | ||
293 | return Ok(42i32); | ||
294 | } | ||
295 | "#, | ||
296 | ); | ||
297 | } | ||
298 | |||
299 | #[test] | ||
300 | fn wrap_return_type_in_result_simple_closure() { | ||
301 | check_assist( | ||
302 | wrap_return_type_in_result, | ||
303 | r#" | ||
304 | fn foo() { | ||
305 | || -> i32<|> { | ||
306 | let test = "test"; | ||
307 | return 42i32; | ||
308 | }; | ||
309 | } | ||
310 | "#, | ||
311 | r#" | ||
312 | fn foo() { | ||
313 | || -> Result<i32, ${0:_}> { | ||
314 | let test = "test"; | ||
315 | return Ok(42i32); | ||
316 | }; | ||
317 | } | ||
318 | "#, | ||
319 | ); | ||
320 | } | ||
321 | |||
322 | #[test] | ||
323 | fn wrap_return_type_in_result_simple_return_type_bad_cursor() { | ||
324 | check_assist_not_applicable( | ||
325 | wrap_return_type_in_result, | ||
326 | r#" | ||
327 | fn foo() -> i32 { | ||
328 | let test = "test";<|> | ||
329 | return 42i32; | ||
330 | } | ||
331 | "#, | ||
332 | ); | ||
333 | } | ||
334 | |||
335 | #[test] | ||
336 | fn wrap_return_type_in_result_simple_return_type_bad_cursor_closure() { | ||
337 | check_assist_not_applicable( | ||
338 | wrap_return_type_in_result, | ||
339 | r#" | ||
340 | fn foo() { | ||
341 | || -> i32 { | ||
342 | let test = "test";<|> | ||
343 | return 42i32; | ||
344 | }; | ||
345 | } | ||
346 | "#, | ||
347 | ); | ||
348 | } | ||
349 | |||
350 | #[test] | ||
351 | fn wrap_return_type_in_result_closure_non_block() { | ||
352 | check_assist_not_applicable(wrap_return_type_in_result, r#"fn foo() { || -> i<|>32 3; }"#); | ||
353 | } | ||
354 | |||
355 | #[test] | ||
356 | fn wrap_return_type_in_result_simple_return_type_already_result_std() { | ||
357 | check_assist_not_applicable( | ||
358 | wrap_return_type_in_result, | ||
359 | r#" | ||
360 | fn foo() -> std::result::Result<i32<|>, String> { | ||
361 | let test = "test"; | ||
362 | return 42i32; | ||
363 | } | ||
364 | "#, | ||
365 | ); | ||
366 | } | ||
367 | |||
368 | #[test] | ||
369 | fn wrap_return_type_in_result_simple_return_type_already_result() { | ||
370 | mark::check!(wrap_return_type_in_result_simple_return_type_already_result); | ||
371 | check_assist_not_applicable( | ||
372 | wrap_return_type_in_result, | ||
373 | r#" | ||
374 | fn foo() -> Result<i32<|>, String> { | ||
375 | let test = "test"; | ||
376 | return 42i32; | ||
377 | } | ||
378 | "#, | ||
379 | ); | ||
380 | } | ||
381 | |||
382 | #[test] | ||
383 | fn wrap_return_type_in_result_simple_return_type_already_result_closure() { | ||
384 | check_assist_not_applicable( | ||
385 | wrap_return_type_in_result, | ||
386 | r#" | ||
387 | fn foo() { | ||
388 | || -> Result<i32<|>, String> { | ||
389 | let test = "test"; | ||
390 | return 42i32; | ||
391 | }; | ||
392 | } | ||
393 | "#, | ||
394 | ); | ||
395 | } | ||
396 | |||
397 | #[test] | ||
398 | fn wrap_return_type_in_result_simple_with_cursor() { | ||
399 | check_assist( | ||
400 | wrap_return_type_in_result, | ||
401 | r#" | ||
402 | fn foo() -> <|>i32 { | ||
403 | let test = "test"; | ||
404 | return 42i32; | ||
405 | } | ||
406 | "#, | ||
407 | r#" | ||
408 | fn foo() -> Result<i32, ${0:_}> { | ||
409 | let test = "test"; | ||
410 | return Ok(42i32); | ||
411 | } | ||
412 | "#, | ||
413 | ); | ||
414 | } | ||
415 | |||
416 | #[test] | ||
417 | fn wrap_return_type_in_result_simple_with_tail() { | ||
418 | check_assist( | ||
419 | wrap_return_type_in_result, | ||
420 | r#" | ||
421 | fn foo() -><|> i32 { | ||
422 | let test = "test"; | ||
423 | 42i32 | ||
424 | } | ||
425 | "#, | ||
426 | r#" | ||
427 | fn foo() -> Result<i32, ${0:_}> { | ||
428 | let test = "test"; | ||
429 | Ok(42i32) | ||
430 | } | ||
431 | "#, | ||
432 | ); | ||
433 | } | ||
434 | |||
435 | #[test] | ||
436 | fn wrap_return_type_in_result_simple_with_tail_closure() { | ||
437 | check_assist( | ||
438 | wrap_return_type_in_result, | ||
439 | r#" | ||
440 | fn foo() { | ||
441 | || -><|> i32 { | ||
442 | let test = "test"; | ||
443 | 42i32 | ||
444 | }; | ||
445 | } | ||
446 | "#, | ||
447 | r#" | ||
448 | fn foo() { | ||
449 | || -> Result<i32, ${0:_}> { | ||
450 | let test = "test"; | ||
451 | Ok(42i32) | ||
452 | }; | ||
453 | } | ||
454 | "#, | ||
455 | ); | ||
456 | } | ||
457 | |||
458 | #[test] | ||
459 | fn wrap_return_type_in_result_simple_with_tail_only() { | ||
460 | check_assist( | ||
461 | wrap_return_type_in_result, | ||
462 | r#"fn foo() -> i32<|> { 42i32 }"#, | ||
463 | r#"fn foo() -> Result<i32, ${0:_}> { Ok(42i32) }"#, | ||
464 | ); | ||
465 | } | ||
466 | |||
467 | #[test] | ||
468 | fn wrap_return_type_in_result_simple_with_tail_block_like() { | ||
469 | check_assist( | ||
470 | wrap_return_type_in_result, | ||
471 | r#" | ||
472 | fn foo() -> i32<|> { | ||
473 | if true { | ||
474 | 42i32 | ||
475 | } else { | ||
476 | 24i32 | ||
477 | } | ||
478 | } | ||
479 | "#, | ||
480 | r#" | ||
481 | fn foo() -> Result<i32, ${0:_}> { | ||
482 | if true { | ||
483 | Ok(42i32) | ||
484 | } else { | ||
485 | Ok(24i32) | ||
486 | } | ||
487 | } | ||
488 | "#, | ||
489 | ); | ||
490 | } | ||
491 | |||
492 | #[test] | ||
493 | fn wrap_return_type_in_result_simple_without_block_closure() { | ||
494 | check_assist( | ||
495 | wrap_return_type_in_result, | ||
496 | r#" | ||
497 | fn foo() { | ||
498 | || -> i32<|> { | ||
499 | if true { | ||
500 | 42i32 | ||
501 | } else { | ||
502 | 24i32 | ||
503 | } | ||
504 | }; | ||
505 | } | ||
506 | "#, | ||
507 | r#" | ||
508 | fn foo() { | ||
509 | || -> Result<i32, ${0:_}> { | ||
510 | if true { | ||
511 | Ok(42i32) | ||
512 | } else { | ||
513 | Ok(24i32) | ||
514 | } | ||
515 | }; | ||
516 | } | ||
517 | "#, | ||
518 | ); | ||
519 | } | ||
520 | |||
521 | #[test] | ||
522 | fn wrap_return_type_in_result_simple_with_nested_if() { | ||
523 | check_assist( | ||
524 | wrap_return_type_in_result, | ||
525 | r#" | ||
526 | fn foo() -> i32<|> { | ||
527 | if true { | ||
528 | if false { | ||
529 | 1 | ||
530 | } else { | ||
531 | 2 | ||
532 | } | ||
533 | } else { | ||
534 | 24i32 | ||
535 | } | ||
536 | } | ||
537 | "#, | ||
538 | r#" | ||
539 | fn foo() -> Result<i32, ${0:_}> { | ||
540 | if true { | ||
541 | if false { | ||
542 | Ok(1) | ||
543 | } else { | ||
544 | Ok(2) | ||
545 | } | ||
546 | } else { | ||
547 | Ok(24i32) | ||
548 | } | ||
549 | } | ||
550 | "#, | ||
551 | ); | ||
552 | } | ||
553 | |||
554 | #[test] | ||
555 | fn wrap_return_type_in_result_simple_with_await() { | ||
556 | check_assist( | ||
557 | wrap_return_type_in_result, | ||
558 | r#" | ||
559 | async fn foo() -> i<|>32 { | ||
560 | if true { | ||
561 | if false { | ||
562 | 1.await | ||
563 | } else { | ||
564 | 2.await | ||
565 | } | ||
566 | } else { | ||
567 | 24i32.await | ||
568 | } | ||
569 | } | ||
570 | "#, | ||
571 | r#" | ||
572 | async fn foo() -> Result<i32, ${0:_}> { | ||
573 | if true { | ||
574 | if false { | ||
575 | Ok(1.await) | ||
576 | } else { | ||
577 | Ok(2.await) | ||
578 | } | ||
579 | } else { | ||
580 | Ok(24i32.await) | ||
581 | } | ||
582 | } | ||
583 | "#, | ||
584 | ); | ||
585 | } | ||
586 | |||
587 | #[test] | ||
588 | fn wrap_return_type_in_result_simple_with_array() { | ||
589 | check_assist( | ||
590 | wrap_return_type_in_result, | ||
591 | r#"fn foo() -> [i32;<|> 3] { [1, 2, 3] }"#, | ||
592 | r#"fn foo() -> Result<[i32; 3], ${0:_}> { Ok([1, 2, 3]) }"#, | ||
593 | ); | ||
594 | } | ||
595 | |||
596 | #[test] | ||
597 | fn wrap_return_type_in_result_simple_with_cast() { | ||
598 | check_assist( | ||
599 | wrap_return_type_in_result, | ||
600 | r#" | ||
601 | fn foo() -<|>> i32 { | ||
602 | if true { | ||
603 | if false { | ||
604 | 1 as i32 | ||
605 | } else { | ||
606 | 2 as i32 | ||
607 | } | ||
608 | } else { | ||
609 | 24 as i32 | ||
610 | } | ||
611 | } | ||
612 | "#, | ||
613 | r#" | ||
614 | fn foo() -> Result<i32, ${0:_}> { | ||
615 | if true { | ||
616 | if false { | ||
617 | Ok(1 as i32) | ||
618 | } else { | ||
619 | Ok(2 as i32) | ||
620 | } | ||
621 | } else { | ||
622 | Ok(24 as i32) | ||
623 | } | ||
624 | } | ||
625 | "#, | ||
626 | ); | ||
627 | } | ||
628 | |||
629 | #[test] | ||
630 | fn wrap_return_type_in_result_simple_with_tail_block_like_match() { | ||
631 | check_assist( | ||
632 | wrap_return_type_in_result, | ||
633 | r#" | ||
634 | fn foo() -> i32<|> { | ||
635 | let my_var = 5; | ||
636 | match my_var { | ||
637 | 5 => 42i32, | ||
638 | _ => 24i32, | ||
639 | } | ||
640 | } | ||
641 | "#, | ||
642 | r#" | ||
643 | fn foo() -> Result<i32, ${0:_}> { | ||
644 | let my_var = 5; | ||
645 | match my_var { | ||
646 | 5 => Ok(42i32), | ||
647 | _ => Ok(24i32), | ||
648 | } | ||
649 | } | ||
650 | "#, | ||
651 | ); | ||
652 | } | ||
653 | |||
654 | #[test] | ||
655 | fn wrap_return_type_in_result_simple_with_loop_with_tail() { | ||
656 | check_assist( | ||
657 | wrap_return_type_in_result, | ||
658 | r#" | ||
659 | fn foo() -> i32<|> { | ||
660 | let my_var = 5; | ||
661 | loop { | ||
662 | println!("test"); | ||
663 | 5 | ||
664 | } | ||
665 | my_var | ||
666 | } | ||
667 | "#, | ||
668 | r#" | ||
669 | fn foo() -> Result<i32, ${0:_}> { | ||
670 | let my_var = 5; | ||
671 | loop { | ||
672 | println!("test"); | ||
673 | 5 | ||
674 | } | ||
675 | Ok(my_var) | ||
676 | } | ||
677 | "#, | ||
678 | ); | ||
679 | } | ||
680 | |||
681 | #[test] | ||
682 | fn wrap_return_type_in_result_simple_with_loop_in_let_stmt() { | ||
683 | check_assist( | ||
684 | wrap_return_type_in_result, | ||
685 | r#" | ||
686 | fn foo() -> i32<|> { | ||
687 | let my_var = let x = loop { | ||
688 | break 1; | ||
689 | }; | ||
690 | my_var | ||
691 | } | ||
692 | "#, | ||
693 | r#" | ||
694 | fn foo() -> Result<i32, ${0:_}> { | ||
695 | let my_var = let x = loop { | ||
696 | break 1; | ||
697 | }; | ||
698 | Ok(my_var) | ||
699 | } | ||
700 | "#, | ||
701 | ); | ||
702 | } | ||
703 | |||
704 | #[test] | ||
705 | fn wrap_return_type_in_result_simple_with_tail_block_like_match_return_expr() { | ||
706 | check_assist( | ||
707 | wrap_return_type_in_result, | ||
708 | r#" | ||
709 | fn foo() -> i32<|> { | ||
710 | let my_var = 5; | ||
711 | let res = match my_var { | ||
712 | 5 => 42i32, | ||
713 | _ => return 24i32, | ||
714 | }; | ||
715 | res | ||
716 | } | ||
717 | "#, | ||
718 | r#" | ||
719 | fn foo() -> Result<i32, ${0:_}> { | ||
720 | let my_var = 5; | ||
721 | let res = match my_var { | ||
722 | 5 => 42i32, | ||
723 | _ => return Ok(24i32), | ||
724 | }; | ||
725 | Ok(res) | ||
726 | } | ||
727 | "#, | ||
728 | ); | ||
729 | |||
730 | check_assist( | ||
731 | wrap_return_type_in_result, | ||
732 | r#" | ||
733 | fn foo() -> i32<|> { | ||
734 | let my_var = 5; | ||
735 | let res = if my_var == 5 { | ||
736 | 42i32 | ||
737 | } else { | ||
738 | return 24i32; | ||
739 | }; | ||
740 | res | ||
741 | } | ||
742 | "#, | ||
743 | r#" | ||
744 | fn foo() -> Result<i32, ${0:_}> { | ||
745 | let my_var = 5; | ||
746 | let res = if my_var == 5 { | ||
747 | 42i32 | ||
748 | } else { | ||
749 | return Ok(24i32); | ||
750 | }; | ||
751 | Ok(res) | ||
752 | } | ||
753 | "#, | ||
754 | ); | ||
755 | } | ||
756 | |||
757 | #[test] | ||
758 | fn wrap_return_type_in_result_simple_with_tail_block_like_match_deeper() { | ||
759 | check_assist( | ||
760 | wrap_return_type_in_result, | ||
761 | r#" | ||
762 | fn foo() -> i32<|> { | ||
763 | let my_var = 5; | ||
764 | match my_var { | ||
765 | 5 => { | ||
766 | if true { | ||
767 | 42i32 | ||
768 | } else { | ||
769 | 25i32 | ||
770 | } | ||
771 | }, | ||
772 | _ => { | ||
773 | let test = "test"; | ||
774 | if test == "test" { | ||
775 | return bar(); | ||
776 | } | ||
777 | 53i32 | ||
778 | }, | ||
779 | } | ||
780 | } | ||
781 | "#, | ||
782 | r#" | ||
783 | fn foo() -> Result<i32, ${0:_}> { | ||
784 | let my_var = 5; | ||
785 | match my_var { | ||
786 | 5 => { | ||
787 | if true { | ||
788 | Ok(42i32) | ||
789 | } else { | ||
790 | Ok(25i32) | ||
791 | } | ||
792 | }, | ||
793 | _ => { | ||
794 | let test = "test"; | ||
795 | if test == "test" { | ||
796 | return Ok(bar()); | ||
797 | } | ||
798 | Ok(53i32) | ||
799 | }, | ||
800 | } | ||
801 | } | ||
802 | "#, | ||
803 | ); | ||
804 | } | ||
805 | |||
806 | #[test] | ||
807 | fn wrap_return_type_in_result_simple_with_tail_block_like_early_return() { | ||
808 | check_assist( | ||
809 | wrap_return_type_in_result, | ||
810 | r#" | ||
811 | fn foo() -> i<|>32 { | ||
812 | let test = "test"; | ||
813 | if test == "test" { | ||
814 | return 24i32; | ||
815 | } | ||
816 | 53i32 | ||
817 | } | ||
818 | "#, | ||
819 | r#" | ||
820 | fn foo() -> Result<i32, ${0:_}> { | ||
821 | let test = "test"; | ||
822 | if test == "test" { | ||
823 | return Ok(24i32); | ||
824 | } | ||
825 | Ok(53i32) | ||
826 | } | ||
827 | "#, | ||
828 | ); | ||
829 | } | ||
830 | |||
831 | #[test] | ||
832 | fn wrap_return_type_in_result_simple_with_closure() { | ||
833 | check_assist( | ||
834 | wrap_return_type_in_result, | ||
835 | r#" | ||
836 | fn foo(the_field: u32) -><|> u32 { | ||
837 | let true_closure = || { return true; }; | ||
838 | if the_field < 5 { | ||
839 | let mut i = 0; | ||
840 | if true_closure() { | ||
841 | return 99; | ||
842 | } else { | ||
843 | return 0; | ||
844 | } | ||
845 | } | ||
846 | the_field | ||
847 | } | ||
848 | "#, | ||
849 | r#" | ||
850 | fn foo(the_field: u32) -> Result<u32, ${0:_}> { | ||
851 | let true_closure = || { return true; }; | ||
852 | if the_field < 5 { | ||
853 | let mut i = 0; | ||
854 | if true_closure() { | ||
855 | return Ok(99); | ||
856 | } else { | ||
857 | return Ok(0); | ||
858 | } | ||
859 | } | ||
860 | Ok(the_field) | ||
861 | } | ||
862 | "#, | ||
863 | ); | ||
864 | |||
865 | check_assist( | ||
866 | wrap_return_type_in_result, | ||
867 | r#" | ||
868 | fn foo(the_field: u32) -> u32<|> { | ||
869 | let true_closure = || { | ||
870 | return true; | ||
871 | }; | ||
872 | if the_field < 5 { | ||
873 | let mut i = 0; | ||
874 | |||
875 | |||
876 | if true_closure() { | ||
877 | return 99; | ||
878 | } else { | ||
879 | return 0; | ||
880 | } | ||
881 | } | ||
882 | let t = None; | ||
883 | |||
884 | t.unwrap_or_else(|| the_field) | ||
885 | } | ||
886 | "#, | ||
887 | r#" | ||
888 | fn foo(the_field: u32) -> Result<u32, ${0:_}> { | ||
889 | let true_closure = || { | ||
890 | return true; | ||
891 | }; | ||
892 | if the_field < 5 { | ||
893 | let mut i = 0; | ||
894 | |||
895 | |||
896 | if true_closure() { | ||
897 | return Ok(99); | ||
898 | } else { | ||
899 | return Ok(0); | ||
900 | } | ||
901 | } | ||
902 | let t = None; | ||
903 | |||
904 | Ok(t.unwrap_or_else(|| the_field)) | ||
905 | } | ||
906 | "#, | ||
907 | ); | ||
908 | } | ||
909 | |||
910 | #[test] | ||
911 | fn wrap_return_type_in_result_simple_with_weird_forms() { | ||
912 | check_assist( | ||
913 | wrap_return_type_in_result, | ||
914 | r#" | ||
915 | fn foo() -> i32<|> { | ||
916 | let test = "test"; | ||
917 | if test == "test" { | ||
918 | return 24i32; | ||
919 | } | ||
920 | let mut i = 0; | ||
921 | loop { | ||
922 | if i == 1 { | ||
923 | break 55; | ||
924 | } | ||
925 | i += 1; | ||
926 | } | ||
927 | } | ||
928 | "#, | ||
929 | r#" | ||
930 | fn foo() -> Result<i32, ${0:_}> { | ||
931 | let test = "test"; | ||
932 | if test == "test" { | ||
933 | return Ok(24i32); | ||
934 | } | ||
935 | let mut i = 0; | ||
936 | loop { | ||
937 | if i == 1 { | ||
938 | break Ok(55); | ||
939 | } | ||
940 | i += 1; | ||
941 | } | ||
942 | } | ||
943 | "#, | ||
944 | ); | ||
945 | |||
946 | check_assist( | ||
947 | wrap_return_type_in_result, | ||
948 | r#" | ||
949 | fn foo() -> i32<|> { | ||
950 | let test = "test"; | ||
951 | if test == "test" { | ||
952 | return 24i32; | ||
953 | } | ||
954 | let mut i = 0; | ||
955 | loop { | ||
956 | loop { | ||
957 | if i == 1 { | ||
958 | break 55; | ||
959 | } | ||
960 | i += 1; | ||
961 | } | ||
962 | } | ||
963 | } | ||
964 | "#, | ||
965 | r#" | ||
966 | fn foo() -> Result<i32, ${0:_}> { | ||
967 | let test = "test"; | ||
968 | if test == "test" { | ||
969 | return Ok(24i32); | ||
970 | } | ||
971 | let mut i = 0; | ||
972 | loop { | ||
973 | loop { | ||
974 | if i == 1 { | ||
975 | break Ok(55); | ||
976 | } | ||
977 | i += 1; | ||
978 | } | ||
979 | } | ||
980 | } | ||
981 | "#, | ||
982 | ); | ||
983 | |||
984 | check_assist( | ||
985 | wrap_return_type_in_result, | ||
986 | r#" | ||
987 | fn foo() -> i3<|>2 { | ||
988 | let test = "test"; | ||
989 | let other = 5; | ||
990 | if test == "test" { | ||
991 | let res = match other { | ||
992 | 5 => 43, | ||
993 | _ => return 56, | ||
994 | }; | ||
995 | } | ||
996 | let mut i = 0; | ||
997 | loop { | ||
998 | loop { | ||
999 | if i == 1 { | ||
1000 | break 55; | ||
1001 | } | ||
1002 | i += 1; | ||
1003 | } | ||
1004 | } | ||
1005 | } | ||
1006 | "#, | ||
1007 | r#" | ||
1008 | fn foo() -> Result<i32, ${0:_}> { | ||
1009 | let test = "test"; | ||
1010 | let other = 5; | ||
1011 | if test == "test" { | ||
1012 | let res = match other { | ||
1013 | 5 => 43, | ||
1014 | _ => return Ok(56), | ||
1015 | }; | ||
1016 | } | ||
1017 | let mut i = 0; | ||
1018 | loop { | ||
1019 | loop { | ||
1020 | if i == 1 { | ||
1021 | break Ok(55); | ||
1022 | } | ||
1023 | i += 1; | ||
1024 | } | ||
1025 | } | ||
1026 | } | ||
1027 | "#, | ||
1028 | ); | ||
1029 | |||
1030 | check_assist( | ||
1031 | wrap_return_type_in_result, | ||
1032 | r#" | ||
1033 | fn foo(the_field: u32) -> u32<|> { | ||
1034 | if the_field < 5 { | ||
1035 | let mut i = 0; | ||
1036 | loop { | ||
1037 | if i > 5 { | ||
1038 | return 55u32; | ||
1039 | } | ||
1040 | i += 3; | ||
1041 | } | ||
1042 | match i { | ||
1043 | 5 => return 99, | ||
1044 | _ => return 0, | ||
1045 | }; | ||
1046 | } | ||
1047 | the_field | ||
1048 | } | ||
1049 | "#, | ||
1050 | r#" | ||
1051 | fn foo(the_field: u32) -> Result<u32, ${0:_}> { | ||
1052 | if the_field < 5 { | ||
1053 | let mut i = 0; | ||
1054 | loop { | ||
1055 | if i > 5 { | ||
1056 | return Ok(55u32); | ||
1057 | } | ||
1058 | i += 3; | ||
1059 | } | ||
1060 | match i { | ||
1061 | 5 => return Ok(99), | ||
1062 | _ => return Ok(0), | ||
1063 | }; | ||
1064 | } | ||
1065 | Ok(the_field) | ||
1066 | } | ||
1067 | "#, | ||
1068 | ); | ||
1069 | |||
1070 | check_assist( | ||
1071 | wrap_return_type_in_result, | ||
1072 | r#" | ||
1073 | fn foo(the_field: u32) -> u3<|>2 { | ||
1074 | if the_field < 5 { | ||
1075 | let mut i = 0; | ||
1076 | match i { | ||
1077 | 5 => return 99, | ||
1078 | _ => return 0, | ||
1079 | } | ||
1080 | } | ||
1081 | the_field | ||
1082 | } | ||
1083 | "#, | ||
1084 | r#" | ||
1085 | fn foo(the_field: u32) -> Result<u32, ${0:_}> { | ||
1086 | if the_field < 5 { | ||
1087 | let mut i = 0; | ||
1088 | match i { | ||
1089 | 5 => return Ok(99), | ||
1090 | _ => return Ok(0), | ||
1091 | } | ||
1092 | } | ||
1093 | Ok(the_field) | ||
1094 | } | ||
1095 | "#, | ||
1096 | ); | ||
1097 | |||
1098 | check_assist( | ||
1099 | wrap_return_type_in_result, | ||
1100 | r#" | ||
1101 | fn foo(the_field: u32) -> u32<|> { | ||
1102 | if the_field < 5 { | ||
1103 | let mut i = 0; | ||
1104 | if i == 5 { | ||
1105 | return 99 | ||
1106 | } else { | ||
1107 | return 0 | ||
1108 | } | ||
1109 | } | ||
1110 | the_field | ||
1111 | } | ||
1112 | "#, | ||
1113 | r#" | ||
1114 | fn foo(the_field: u32) -> Result<u32, ${0:_}> { | ||
1115 | if the_field < 5 { | ||
1116 | let mut i = 0; | ||
1117 | if i == 5 { | ||
1118 | return Ok(99) | ||
1119 | } else { | ||
1120 | return Ok(0) | ||
1121 | } | ||
1122 | } | ||
1123 | Ok(the_field) | ||
1124 | } | ||
1125 | "#, | ||
1126 | ); | ||
1127 | |||
1128 | check_assist( | ||
1129 | wrap_return_type_in_result, | ||
1130 | r#" | ||
1131 | fn foo(the_field: u32) -> <|>u32 { | ||
1132 | if the_field < 5 { | ||
1133 | let mut i = 0; | ||
1134 | if i == 5 { | ||
1135 | return 99; | ||
1136 | } else { | ||
1137 | return 0; | ||
1138 | } | ||
1139 | } | ||
1140 | the_field | ||
1141 | } | ||
1142 | "#, | ||
1143 | r#" | ||
1144 | fn foo(the_field: u32) -> Result<u32, ${0:_}> { | ||
1145 | if the_field < 5 { | ||
1146 | let mut i = 0; | ||
1147 | if i == 5 { | ||
1148 | return Ok(99); | ||
1149 | } else { | ||
1150 | return Ok(0); | ||
1151 | } | ||
1152 | } | ||
1153 | Ok(the_field) | ||
1154 | } | ||
1155 | "#, | ||
1156 | ); | ||
1157 | } | ||
1158 | } | ||
diff --git a/crates/assists/src/lib.rs b/crates/assists/src/lib.rs index af88b3437..17e9312db 100644 --- a/crates/assists/src/lib.rs +++ b/crates/assists/src/lib.rs | |||
@@ -120,13 +120,11 @@ mod handlers { | |||
120 | 120 | ||
121 | pub(crate) type Handler = fn(&mut Assists, &AssistContext) -> Option<()>; | 121 | pub(crate) type Handler = fn(&mut Assists, &AssistContext) -> Option<()>; |
122 | 122 | ||
123 | mod add_custom_impl; | ||
124 | mod add_explicit_type; | 123 | mod add_explicit_type; |
125 | mod add_missing_impl_members; | 124 | mod add_missing_impl_members; |
126 | mod add_turbo_fish; | 125 | mod add_turbo_fish; |
127 | mod apply_demorgan; | 126 | mod apply_demorgan; |
128 | mod auto_import; | 127 | mod auto_import; |
129 | mod change_return_type_to_result; | ||
130 | mod change_visibility; | 128 | mod change_visibility; |
131 | mod convert_integer_literal; | 129 | mod convert_integer_literal; |
132 | mod early_return; | 130 | mod early_return; |
@@ -143,6 +141,7 @@ mod handlers { | |||
143 | mod generate_function; | 141 | mod generate_function; |
144 | mod generate_impl; | 142 | mod generate_impl; |
145 | mod generate_new; | 143 | mod generate_new; |
144 | mod ignore_test; | ||
146 | mod infer_function_return_type; | 145 | mod infer_function_return_type; |
147 | mod inline_local_variable; | 146 | mod inline_local_variable; |
148 | mod introduce_named_lifetime; | 147 | mod introduce_named_lifetime; |
@@ -157,6 +156,7 @@ mod handlers { | |||
157 | mod remove_mut; | 156 | mod remove_mut; |
158 | mod remove_unused_param; | 157 | mod remove_unused_param; |
159 | mod reorder_fields; | 158 | mod reorder_fields; |
159 | mod replace_derive_with_manual_impl; | ||
160 | mod replace_if_let_with_match; | 160 | mod replace_if_let_with_match; |
161 | mod replace_impl_trait_with_generic; | 161 | mod replace_impl_trait_with_generic; |
162 | mod replace_let_with_if_let; | 162 | mod replace_let_with_if_let; |
@@ -165,16 +165,15 @@ mod handlers { | |||
165 | mod replace_unwrap_with_match; | 165 | mod replace_unwrap_with_match; |
166 | mod split_import; | 166 | mod split_import; |
167 | mod unwrap_block; | 167 | mod unwrap_block; |
168 | mod wrap_return_type_in_result; | ||
168 | 169 | ||
169 | pub(crate) fn all() -> &'static [Handler] { | 170 | pub(crate) fn all() -> &'static [Handler] { |
170 | &[ | 171 | &[ |
171 | // These are alphabetic for the foolish consistency | 172 | // These are alphabetic for the foolish consistency |
172 | add_custom_impl::add_custom_impl, | ||
173 | add_explicit_type::add_explicit_type, | 173 | add_explicit_type::add_explicit_type, |
174 | add_turbo_fish::add_turbo_fish, | 174 | add_turbo_fish::add_turbo_fish, |
175 | apply_demorgan::apply_demorgan, | 175 | apply_demorgan::apply_demorgan, |
176 | auto_import::auto_import, | 176 | auto_import::auto_import, |
177 | change_return_type_to_result::change_return_type_to_result, | ||
178 | change_visibility::change_visibility, | 177 | change_visibility::change_visibility, |
179 | convert_integer_literal::convert_integer_literal, | 178 | convert_integer_literal::convert_integer_literal, |
180 | early_return::convert_to_guarded_return, | 179 | early_return::convert_to_guarded_return, |
@@ -191,6 +190,7 @@ mod handlers { | |||
191 | generate_function::generate_function, | 190 | generate_function::generate_function, |
192 | generate_impl::generate_impl, | 191 | generate_impl::generate_impl, |
193 | generate_new::generate_new, | 192 | generate_new::generate_new, |
193 | ignore_test::ignore_test, | ||
194 | infer_function_return_type::infer_function_return_type, | 194 | infer_function_return_type::infer_function_return_type, |
195 | inline_local_variable::inline_local_variable, | 195 | inline_local_variable::inline_local_variable, |
196 | introduce_named_lifetime::introduce_named_lifetime, | 196 | introduce_named_lifetime::introduce_named_lifetime, |
@@ -208,6 +208,7 @@ mod handlers { | |||
208 | remove_mut::remove_mut, | 208 | remove_mut::remove_mut, |
209 | remove_unused_param::remove_unused_param, | 209 | remove_unused_param::remove_unused_param, |
210 | reorder_fields::reorder_fields, | 210 | reorder_fields::reorder_fields, |
211 | replace_derive_with_manual_impl::replace_derive_with_manual_impl, | ||
211 | replace_if_let_with_match::replace_if_let_with_match, | 212 | replace_if_let_with_match::replace_if_let_with_match, |
212 | replace_impl_trait_with_generic::replace_impl_trait_with_generic, | 213 | replace_impl_trait_with_generic::replace_impl_trait_with_generic, |
213 | replace_let_with_if_let::replace_let_with_if_let, | 214 | replace_let_with_if_let::replace_let_with_if_let, |
@@ -215,6 +216,7 @@ mod handlers { | |||
215 | replace_unwrap_with_match::replace_unwrap_with_match, | 216 | replace_unwrap_with_match::replace_unwrap_with_match, |
216 | split_import::split_import, | 217 | split_import::split_import, |
217 | unwrap_block::unwrap_block, | 218 | unwrap_block::unwrap_block, |
219 | wrap_return_type_in_result::wrap_return_type_in_result, | ||
218 | // These are manually sorted for better priorities | 220 | // These are manually sorted for better priorities |
219 | add_missing_impl_members::add_missing_impl_members, | 221 | add_missing_impl_members::add_missing_impl_members, |
220 | add_missing_impl_members::add_missing_default_members, | 222 | add_missing_impl_members::add_missing_default_members, |
diff --git a/crates/assists/src/tests.rs b/crates/assists/src/tests.rs index 849d85e76..709a34d03 100644 --- a/crates/assists/src/tests.rs +++ b/crates/assists/src/tests.rs | |||
@@ -7,7 +7,7 @@ use syntax::TextRange; | |||
7 | use test_utils::{assert_eq_text, extract_offset, extract_range}; | 7 | use test_utils::{assert_eq_text, extract_offset, extract_range}; |
8 | 8 | ||
9 | use crate::{handlers::Handler, Assist, AssistConfig, AssistContext, AssistKind, Assists}; | 9 | use crate::{handlers::Handler, Assist, AssistConfig, AssistContext, AssistKind, Assists}; |
10 | use stdx::trim_indent; | 10 | use stdx::{format_to, trim_indent}; |
11 | 11 | ||
12 | pub(crate) fn with_single_file(text: &str) -> (RootDatabase, FileId) { | 12 | pub(crate) fn with_single_file(text: &str) -> (RootDatabase, FileId) { |
13 | RootDatabase::with_single_file(text) | 13 | RootDatabase::with_single_file(text) |
@@ -98,11 +98,24 @@ fn check(handler: Handler, before: &str, expected: ExpectedResult, assist_label: | |||
98 | match (assist, expected) { | 98 | match (assist, expected) { |
99 | (Some(assist), ExpectedResult::After(after)) => { | 99 | (Some(assist), ExpectedResult::After(after)) => { |
100 | let mut source_change = assist.source_change; | 100 | let mut source_change = assist.source_change; |
101 | let change = source_change.source_file_edits.pop().unwrap(); | 101 | assert!(!source_change.source_file_edits.is_empty()); |
102 | 102 | let skip_header = source_change.source_file_edits.len() == 1; | |
103 | let mut actual = db.file_text(change.file_id).as_ref().to_owned(); | 103 | source_change.source_file_edits.sort_by_key(|it| it.file_id); |
104 | change.edit.apply(&mut actual); | 104 | |
105 | assert_eq_text!(after, &actual); | 105 | let mut buf = String::new(); |
106 | for source_file_edit in source_change.source_file_edits { | ||
107 | let mut text = db.file_text(source_file_edit.file_id).as_ref().to_owned(); | ||
108 | source_file_edit.edit.apply(&mut text); | ||
109 | if !skip_header { | ||
110 | let sr = db.file_source_root(source_file_edit.file_id); | ||
111 | let sr = db.source_root(sr); | ||
112 | let path = sr.path_for_file(&source_file_edit.file_id).unwrap(); | ||
113 | format_to!(buf, "//- {}\n", path) | ||
114 | } | ||
115 | buf.push_str(&text); | ||
116 | } | ||
117 | |||
118 | assert_eq_text!(after, &buf); | ||
106 | } | 119 | } |
107 | (Some(assist), ExpectedResult::Target(target)) => { | 120 | (Some(assist), ExpectedResult::Target(target)) => { |
108 | let range = assist.assist.target; | 121 | let range = assist.assist.target; |
diff --git a/crates/assists/src/tests/generated.rs b/crates/assists/src/tests/generated.rs index 168e1626a..5a9d1a01b 100644 --- a/crates/assists/src/tests/generated.rs +++ b/crates/assists/src/tests/generated.rs | |||
@@ -3,25 +3,6 @@ | |||
3 | use super::check_doc_test; | 3 | use super::check_doc_test; |
4 | 4 | ||
5 | #[test] | 5 | #[test] |
6 | fn doctest_add_custom_impl() { | ||
7 | check_doc_test( | ||
8 | "add_custom_impl", | ||
9 | r#####" | ||
10 | #[derive(Deb<|>ug, Display)] | ||
11 | struct S; | ||
12 | "#####, | ||
13 | r#####" | ||
14 | #[derive(Display)] | ||
15 | struct S; | ||
16 | |||
17 | impl Debug for S { | ||
18 | $0 | ||
19 | } | ||
20 | "#####, | ||
21 | ) | ||
22 | } | ||
23 | |||
24 | #[test] | ||
25 | fn doctest_add_explicit_type() { | 6 | fn doctest_add_explicit_type() { |
26 | check_doc_test( | 7 | check_doc_test( |
27 | "add_explicit_type", | 8 | "add_explicit_type", |
@@ -178,19 +159,6 @@ pub mod std { pub mod collections { pub struct HashMap { } } } | |||
178 | } | 159 | } |
179 | 160 | ||
180 | #[test] | 161 | #[test] |
181 | fn doctest_change_return_type_to_result() { | ||
182 | check_doc_test( | ||
183 | "change_return_type_to_result", | ||
184 | r#####" | ||
185 | fn foo() -> i32<|> { 42i32 } | ||
186 | "#####, | ||
187 | r#####" | ||
188 | fn foo() -> Result<i32, ${0:_}> { Ok(42i32) } | ||
189 | "#####, | ||
190 | ) | ||
191 | } | ||
192 | |||
193 | #[test] | ||
194 | fn doctest_change_visibility() { | 162 | fn doctest_change_visibility() { |
195 | check_doc_test( | 163 | check_doc_test( |
196 | "change_visibility", | 164 | "change_visibility", |
@@ -506,6 +474,26 @@ impl<T: Clone> Ctx<T> { | |||
506 | } | 474 | } |
507 | 475 | ||
508 | #[test] | 476 | #[test] |
477 | fn doctest_ignore_test() { | ||
478 | check_doc_test( | ||
479 | "ignore_test", | ||
480 | r#####" | ||
481 | <|>#[test] | ||
482 | fn arithmetics { | ||
483 | assert_eq!(2 + 2, 5); | ||
484 | } | ||
485 | "#####, | ||
486 | r#####" | ||
487 | #[test] | ||
488 | #[ignore] | ||
489 | fn arithmetics { | ||
490 | assert_eq!(2 + 2, 5); | ||
491 | } | ||
492 | "#####, | ||
493 | ) | ||
494 | } | ||
495 | |||
496 | #[test] | ||
509 | fn doctest_infer_function_return_type() { | 497 | fn doctest_infer_function_return_type() { |
510 | check_doc_test( | 498 | check_doc_test( |
511 | "infer_function_return_type", | 499 | "infer_function_return_type", |
@@ -832,6 +820,29 @@ const test: Foo = Foo {foo: 1, bar: 0} | |||
832 | } | 820 | } |
833 | 821 | ||
834 | #[test] | 822 | #[test] |
823 | fn doctest_replace_derive_with_manual_impl() { | ||
824 | check_doc_test( | ||
825 | "replace_derive_with_manual_impl", | ||
826 | r#####" | ||
827 | trait Debug { fn fmt(&self, f: &mut Formatter) -> Result<()>; } | ||
828 | #[derive(Deb<|>ug, Display)] | ||
829 | struct S; | ||
830 | "#####, | ||
831 | r#####" | ||
832 | trait Debug { fn fmt(&self, f: &mut Formatter) -> Result<()>; } | ||
833 | #[derive(Display)] | ||
834 | struct S; | ||
835 | |||
836 | impl Debug for S { | ||
837 | fn fmt(&self, f: &mut Formatter) -> Result<()> { | ||
838 | ${0:todo!()} | ||
839 | } | ||
840 | } | ||
841 | "#####, | ||
842 | ) | ||
843 | } | ||
844 | |||
845 | #[test] | ||
835 | fn doctest_replace_if_let_with_match() { | 846 | fn doctest_replace_if_let_with_match() { |
836 | check_doc_test( | 847 | check_doc_test( |
837 | "replace_if_let_with_match", | 848 | "replace_if_let_with_match", |
@@ -985,3 +996,16 @@ fn foo() { | |||
985 | "#####, | 996 | "#####, |
986 | ) | 997 | ) |
987 | } | 998 | } |
999 | |||
1000 | #[test] | ||
1001 | fn doctest_wrap_return_type_in_result() { | ||
1002 | check_doc_test( | ||
1003 | "wrap_return_type_in_result", | ||
1004 | r#####" | ||
1005 | fn foo() -> i32<|> { 42i32 } | ||
1006 | "#####, | ||
1007 | r#####" | ||
1008 | fn foo() -> Result<i32, ${0:_}> { Ok(42i32) } | ||
1009 | "#####, | ||
1010 | ) | ||
1011 | } | ||
diff --git a/crates/assists/src/utils.rs b/crates/assists/src/utils.rs index 56f925ee6..66c0cdd5f 100644 --- a/crates/assists/src/utils.rs +++ b/crates/assists/src/utils.rs | |||
@@ -4,20 +4,25 @@ pub(crate) mod import_assets; | |||
4 | 4 | ||
5 | use std::ops; | 5 | use std::ops; |
6 | 6 | ||
7 | use hir::{Crate, Enum, Module, ScopeDef, Semantics, Trait}; | 7 | use hir::{Crate, Enum, HasSource, Module, ScopeDef, Semantics, Trait}; |
8 | use ide_db::RootDatabase; | 8 | use ide_db::RootDatabase; |
9 | use itertools::Itertools; | 9 | use itertools::Itertools; |
10 | use syntax::{ | 10 | use syntax::{ |
11 | ast::{self, make, ArgListOwner}, | 11 | ast::edit::AstNodeEdit, |
12 | ast::AttrsOwner, | ||
13 | ast::NameOwner, | ||
14 | ast::{self, edit, make, ArgListOwner}, | ||
12 | AstNode, Direction, | 15 | AstNode, Direction, |
13 | SyntaxKind::*, | 16 | SyntaxKind::*, |
14 | SyntaxNode, TextSize, T, | 17 | SyntaxNode, TextSize, T, |
15 | }; | 18 | }; |
16 | 19 | ||
17 | use crate::assist_config::SnippetCap; | 20 | use crate::{ |
21 | assist_config::SnippetCap, | ||
22 | ast_transform::{self, AstTransform, QualifyPaths, SubstituteTypeParams}, | ||
23 | }; | ||
18 | 24 | ||
19 | pub use insert_use::MergeBehaviour; | 25 | pub use insert_use::{insert_use, ImportScope, MergeBehaviour}; |
20 | pub(crate) use insert_use::{insert_use, ImportScope}; | ||
21 | 26 | ||
22 | pub fn mod_path_to_ast(path: &hir::ModPath) -> ast::Path { | 27 | pub fn mod_path_to_ast(path: &hir::ModPath) -> ast::Path { |
23 | let mut segments = Vec::new(); | 28 | let mut segments = Vec::new(); |
@@ -77,6 +82,104 @@ pub fn extract_trivial_expression(block: &ast::BlockExpr) -> Option<ast::Expr> { | |||
77 | None | 82 | None |
78 | } | 83 | } |
79 | 84 | ||
85 | /// This is a method with a heuristics to support test methods annotated with custom test annotations, such as | ||
86 | /// `#[test_case(...)]`, `#[tokio::test]` and similar. | ||
87 | /// Also a regular `#[test]` annotation is supported. | ||
88 | /// | ||
89 | /// It may produce false positives, for example, `#[wasm_bindgen_test]` requires a different command to run the test, | ||
90 | /// but it's better than not to have the runnables for the tests at all. | ||
91 | pub fn test_related_attribute(fn_def: &ast::Fn) -> Option<ast::Attr> { | ||
92 | fn_def.attrs().find_map(|attr| { | ||
93 | let path = attr.path()?; | ||
94 | if path.syntax().text().to_string().contains("test") { | ||
95 | Some(attr) | ||
96 | } else { | ||
97 | None | ||
98 | } | ||
99 | }) | ||
100 | } | ||
101 | |||
102 | #[derive(Copy, Clone, PartialEq)] | ||
103 | pub enum DefaultMethods { | ||
104 | Only, | ||
105 | No, | ||
106 | } | ||
107 | |||
108 | pub fn filter_assoc_items( | ||
109 | db: &RootDatabase, | ||
110 | items: &[hir::AssocItem], | ||
111 | default_methods: DefaultMethods, | ||
112 | ) -> Vec<ast::AssocItem> { | ||
113 | fn has_def_name(item: &ast::AssocItem) -> bool { | ||
114 | match item { | ||
115 | ast::AssocItem::Fn(def) => def.name(), | ||
116 | ast::AssocItem::TypeAlias(def) => def.name(), | ||
117 | ast::AssocItem::Const(def) => def.name(), | ||
118 | ast::AssocItem::MacroCall(_) => None, | ||
119 | } | ||
120 | .is_some() | ||
121 | }; | ||
122 | |||
123 | items | ||
124 | .iter() | ||
125 | .map(|i| match i { | ||
126 | hir::AssocItem::Function(i) => ast::AssocItem::Fn(i.source(db).value), | ||
127 | hir::AssocItem::TypeAlias(i) => ast::AssocItem::TypeAlias(i.source(db).value), | ||
128 | hir::AssocItem::Const(i) => ast::AssocItem::Const(i.source(db).value), | ||
129 | }) | ||
130 | .filter(has_def_name) | ||
131 | .filter(|it| match it { | ||
132 | ast::AssocItem::Fn(def) => matches!( | ||
133 | (default_methods, def.body()), | ||
134 | (DefaultMethods::Only, Some(_)) | (DefaultMethods::No, None) | ||
135 | ), | ||
136 | _ => default_methods == DefaultMethods::No, | ||
137 | }) | ||
138 | .collect::<Vec<_>>() | ||
139 | } | ||
140 | |||
141 | pub fn add_trait_assoc_items_to_impl( | ||
142 | sema: &hir::Semantics<ide_db::RootDatabase>, | ||
143 | items: Vec<ast::AssocItem>, | ||
144 | trait_: hir::Trait, | ||
145 | impl_def: ast::Impl, | ||
146 | target_scope: hir::SemanticsScope, | ||
147 | ) -> (ast::Impl, ast::AssocItem) { | ||
148 | let impl_item_list = impl_def.assoc_item_list().unwrap_or_else(make::assoc_item_list); | ||
149 | |||
150 | let n_existing_items = impl_item_list.assoc_items().count(); | ||
151 | let source_scope = sema.scope_for_def(trait_); | ||
152 | let ast_transform = QualifyPaths::new(&target_scope, &source_scope) | ||
153 | .or(SubstituteTypeParams::for_trait_impl(&source_scope, trait_, impl_def.clone())); | ||
154 | |||
155 | let items = items | ||
156 | .into_iter() | ||
157 | .map(|it| ast_transform::apply(&*ast_transform, it)) | ||
158 | .map(|it| match it { | ||
159 | ast::AssocItem::Fn(def) => ast::AssocItem::Fn(add_body(def)), | ||
160 | ast::AssocItem::TypeAlias(def) => ast::AssocItem::TypeAlias(def.remove_bounds()), | ||
161 | _ => it, | ||
162 | }) | ||
163 | .map(|it| edit::remove_attrs_and_docs(&it)); | ||
164 | |||
165 | let new_impl_item_list = impl_item_list.append_items(items); | ||
166 | let new_impl_def = impl_def.with_assoc_item_list(new_impl_item_list); | ||
167 | let first_new_item = | ||
168 | new_impl_def.assoc_item_list().unwrap().assoc_items().nth(n_existing_items).unwrap(); | ||
169 | return (new_impl_def, first_new_item); | ||
170 | |||
171 | fn add_body(fn_def: ast::Fn) -> ast::Fn { | ||
172 | match fn_def.body() { | ||
173 | Some(_) => fn_def, | ||
174 | None => { | ||
175 | let body = | ||
176 | make::block_expr(None, Some(make::expr_todo())).indent(edit::IndentLevel(1)); | ||
177 | fn_def.with_body(body) | ||
178 | } | ||
179 | } | ||
180 | } | ||
181 | } | ||
182 | |||
80 | #[derive(Clone, Copy, Debug)] | 183 | #[derive(Clone, Copy, Debug)] |
81 | pub(crate) enum Cursor<'a> { | 184 | pub(crate) enum Cursor<'a> { |
82 | Replace(&'a SyntaxNode), | 185 | Replace(&'a SyntaxNode), |
@@ -171,6 +274,12 @@ pub mod convert { | |||
171 | } | 274 | } |
172 | } | 275 | } |
173 | 276 | ||
277 | pub mod default { | ||
278 | pub trait Default { | ||
279 | fn default() -> Self; | ||
280 | } | ||
281 | } | ||
282 | |||
174 | pub mod iter { | 283 | pub mod iter { |
175 | pub use self::traits::{collect::IntoIterator, iterator::Iterator}; | 284 | pub use self::traits::{collect::IntoIterator, iterator::Iterator}; |
176 | mod traits { | 285 | mod traits { |
@@ -241,7 +350,7 @@ pub mod option { | |||
241 | } | 350 | } |
242 | 351 | ||
243 | pub mod prelude { | 352 | pub mod prelude { |
244 | pub use crate::{convert::From, iter::{IntoIterator, Iterator}, option::Option::{self, *}}; | 353 | pub use crate::{convert::From, iter::{IntoIterator, Iterator}, option::Option::{self, *}, default::Default}; |
245 | } | 354 | } |
246 | #[prelude_import] | 355 | #[prelude_import] |
247 | pub use prelude::*; | 356 | pub use prelude::*; |
@@ -259,6 +368,10 @@ pub use prelude::*; | |||
259 | self.find_enum("core:option:Option") | 368 | self.find_enum("core:option:Option") |
260 | } | 369 | } |
261 | 370 | ||
371 | pub fn core_default_Default(&self) -> Option<Trait> { | ||
372 | self.find_trait("core:default:Default") | ||
373 | } | ||
374 | |||
262 | pub fn core_iter_Iterator(&self) -> Option<Trait> { | 375 | pub fn core_iter_Iterator(&self) -> Option<Trait> { |
263 | self.find_trait("core:iter:traits:iterator:Iterator") | 376 | self.find_trait("core:iter:traits:iterator:Iterator") |
264 | } | 377 | } |
diff --git a/crates/assists/src/utils/import_assets.rs b/crates/assists/src/utils/import_assets.rs index f47edbb76..ff5c0e78e 100644 --- a/crates/assists/src/utils/import_assets.rs +++ b/crates/assists/src/utils/import_assets.rs | |||
@@ -179,21 +179,25 @@ impl ImportAssets { | |||
179 | } | 179 | } |
180 | }; | 180 | }; |
181 | 181 | ||
182 | let mut res = imports_locator::find_imports(sema, current_crate, &self.get_search_query()) | 182 | let mut res = |
183 | .into_iter() | 183 | imports_locator::find_exact_imports(sema, current_crate, &self.get_search_query()) |
184 | .filter_map(filter) | 184 | .filter_map(filter) |
185 | .filter_map(|candidate| { | 185 | .filter_map(|candidate| { |
186 | let item: hir::ItemInNs = candidate.either(Into::into, Into::into); | 186 | let item: hir::ItemInNs = candidate.either(Into::into, Into::into); |
187 | if let Some(prefix_kind) = prefixed { | 187 | if let Some(prefix_kind) = prefixed { |
188 | self.module_with_name_to_import.find_use_path_prefixed(db, item, prefix_kind) | 188 | self.module_with_name_to_import.find_use_path_prefixed( |
189 | } else { | 189 | db, |
190 | self.module_with_name_to_import.find_use_path(db, item) | 190 | item, |
191 | } | 191 | prefix_kind, |
192 | .map(|path| (path, item)) | 192 | ) |
193 | }) | 193 | } else { |
194 | .filter(|(use_path, _)| !use_path.segments.is_empty()) | 194 | self.module_with_name_to_import.find_use_path(db, item) |
195 | .take(20) | 195 | } |
196 | .collect::<Vec<_>>(); | 196 | .map(|path| (path, item)) |
197 | }) | ||
198 | .filter(|(use_path, _)| use_path.len() > 1) | ||
199 | .take(20) | ||
200 | .collect::<Vec<_>>(); | ||
197 | res.sort_by_key(|(path, _)| path.clone()); | 201 | res.sort_by_key(|(path, _)| path.clone()); |
198 | res | 202 | res |
199 | } | 203 | } |
diff --git a/crates/assists/src/utils/insert_use.rs b/crates/assists/src/utils/insert_use.rs index 84a0dffdd..423782a0e 100644 --- a/crates/assists/src/utils/insert_use.rs +++ b/crates/assists/src/utils/insert_use.rs | |||
@@ -1,6 +1,8 @@ | |||
1 | //! Handle syntactic aspects of inserting a new `use`. | 1 | //! Handle syntactic aspects of inserting a new `use`. |
2 | use std::{cmp::Ordering, iter::successors}; | 2 | use std::{cmp::Ordering, iter::successors}; |
3 | 3 | ||
4 | use hir::Semantics; | ||
5 | use ide_db::RootDatabase; | ||
4 | use itertools::{EitherOrBoth, Itertools}; | 6 | use itertools::{EitherOrBoth, Itertools}; |
5 | use syntax::{ | 7 | use syntax::{ |
6 | algo::SyntaxRewriter, | 8 | algo::SyntaxRewriter, |
@@ -9,12 +11,12 @@ use syntax::{ | |||
9 | edit::{AstNodeEdit, IndentLevel}, | 11 | edit::{AstNodeEdit, IndentLevel}, |
10 | make, AstNode, PathSegmentKind, VisibilityOwner, | 12 | make, AstNode, PathSegmentKind, VisibilityOwner, |
11 | }, | 13 | }, |
12 | InsertPosition, SyntaxElement, SyntaxNode, | 14 | AstToken, InsertPosition, NodeOrToken, SyntaxElement, SyntaxNode, SyntaxToken, |
13 | }; | 15 | }; |
14 | use test_utils::mark; | 16 | use test_utils::mark; |
15 | 17 | ||
16 | #[derive(Debug)] | 18 | #[derive(Debug, Clone)] |
17 | pub(crate) enum ImportScope { | 19 | pub enum ImportScope { |
18 | File(ast::SourceFile), | 20 | File(ast::SourceFile), |
19 | Module(ast::ItemList), | 21 | Module(ast::ItemList), |
20 | } | 22 | } |
@@ -31,14 +33,14 @@ impl ImportScope { | |||
31 | } | 33 | } |
32 | 34 | ||
33 | /// Determines the containing syntax node in which to insert a `use` statement affecting `position`. | 35 | /// Determines the containing syntax node in which to insert a `use` statement affecting `position`. |
34 | pub(crate) fn find_insert_use_container( | 36 | pub fn find_insert_use_container( |
35 | position: &SyntaxNode, | 37 | position: &SyntaxNode, |
36 | ctx: &crate::assist_context::AssistContext, | 38 | sema: &Semantics<'_, RootDatabase>, |
37 | ) -> Option<Self> { | 39 | ) -> Option<Self> { |
38 | ctx.sema.ancestors_with_macros(position.clone()).find_map(Self::from) | 40 | sema.ancestors_with_macros(position.clone()).find_map(Self::from) |
39 | } | 41 | } |
40 | 42 | ||
41 | pub(crate) fn as_syntax_node(&self) -> &SyntaxNode { | 43 | pub fn as_syntax_node(&self) -> &SyntaxNode { |
42 | match self { | 44 | match self { |
43 | ImportScope::File(file) => file.syntax(), | 45 | ImportScope::File(file) => file.syntax(), |
44 | ImportScope::Module(item_list) => item_list.syntax(), | 46 | ImportScope::Module(item_list) => item_list.syntax(), |
@@ -63,29 +65,32 @@ impl ImportScope { | |||
63 | } | 65 | } |
64 | } | 66 | } |
65 | 67 | ||
66 | fn insert_pos_after_inner_attribute(&self) -> (InsertPosition<SyntaxElement>, AddBlankLine) { | 68 | fn insert_pos_after_last_inner_element(&self) -> (InsertPosition<SyntaxElement>, AddBlankLine) { |
67 | // check if the scope has inner attributes, we dont want to insert in front of them | 69 | self.as_syntax_node() |
68 | match self | 70 | .children_with_tokens() |
69 | .as_syntax_node() | 71 | .filter(|child| match child { |
70 | .children() | 72 | NodeOrToken::Node(node) => is_inner_attribute(node.clone()), |
71 | // no flat_map here cause we want to short circuit the iterator | 73 | NodeOrToken::Token(token) => is_inner_comment(token.clone()), |
72 | .map(ast::Attr::cast) | ||
73 | .take_while(|attr| { | ||
74 | attr.as_ref().map(|attr| attr.kind() == ast::AttrKind::Inner).unwrap_or(false) | ||
75 | }) | 74 | }) |
76 | .last() | 75 | .last() |
77 | .flatten() | 76 | .map(|last_inner_element| { |
78 | { | 77 | (InsertPosition::After(last_inner_element.into()), AddBlankLine::BeforeTwice) |
79 | Some(attr) => { | 78 | }) |
80 | (InsertPosition::After(attr.syntax().clone().into()), AddBlankLine::BeforeTwice) | 79 | .unwrap_or_else(|| self.first_insert_pos()) |
81 | } | ||
82 | None => self.first_insert_pos(), | ||
83 | } | ||
84 | } | 80 | } |
85 | } | 81 | } |
86 | 82 | ||
83 | fn is_inner_attribute(node: SyntaxNode) -> bool { | ||
84 | ast::Attr::cast(node).map(|attr| attr.kind()) == Some(ast::AttrKind::Inner) | ||
85 | } | ||
86 | |||
87 | fn is_inner_comment(token: SyntaxToken) -> bool { | ||
88 | ast::Comment::cast(token).and_then(|comment| comment.kind().doc) | ||
89 | == Some(ast::CommentPlacement::Inner) | ||
90 | } | ||
91 | |||
87 | /// Insert an import path into the given file/node. A `merge` value of none indicates that no import merging is allowed to occur. | 92 | /// Insert an import path into the given file/node. A `merge` value of none indicates that no import merging is allowed to occur. |
88 | pub(crate) fn insert_use<'a>( | 93 | pub fn insert_use<'a>( |
89 | scope: &ImportScope, | 94 | scope: &ImportScope, |
90 | path: ast::Path, | 95 | path: ast::Path, |
91 | merge: Option<MergeBehaviour>, | 96 | merge: Option<MergeBehaviour>, |
@@ -558,7 +563,7 @@ fn find_insert_position( | |||
558 | (InsertPosition::After(node.into()), AddBlankLine::BeforeTwice) | 563 | (InsertPosition::After(node.into()), AddBlankLine::BeforeTwice) |
559 | } | 564 | } |
560 | // there are no imports in this file at all | 565 | // there are no imports in this file at all |
561 | None => scope.insert_pos_after_inner_attribute(), | 566 | None => scope.insert_pos_after_last_inner_element(), |
562 | }, | 567 | }, |
563 | } | 568 | } |
564 | } | 569 | } |
@@ -830,12 +835,67 @@ use foo::bar;", | |||
830 | "foo::bar", | 835 | "foo::bar", |
831 | r"#![allow(unused_imports)] | 836 | r"#![allow(unused_imports)] |
832 | 837 | ||
838 | #![no_std] | ||
833 | fn main() {}", | 839 | fn main() {}", |
834 | r"#![allow(unused_imports)] | 840 | r"#![allow(unused_imports)] |
835 | 841 | ||
836 | use foo::bar; | 842 | #![no_std] |
837 | 843 | ||
844 | use foo::bar; | ||
838 | fn main() {}", | 845 | fn main() {}", |
846 | ); | ||
847 | } | ||
848 | |||
849 | #[test] | ||
850 | fn inserts_after_single_line_inner_comments() { | ||
851 | check_none( | ||
852 | "foo::bar::Baz", | ||
853 | "//! Single line inner comments do not allow any code before them.", | ||
854 | r#"//! Single line inner comments do not allow any code before them. | ||
855 | |||
856 | use foo::bar::Baz;"#, | ||
857 | ); | ||
858 | } | ||
859 | |||
860 | #[test] | ||
861 | fn inserts_after_multiline_inner_comments() { | ||
862 | check_none( | ||
863 | "foo::bar::Baz", | ||
864 | r#"/*! Multiline inner comments do not allow any code before them. */ | ||
865 | |||
866 | /*! Still an inner comment, cannot place any code before. */ | ||
867 | fn main() {}"#, | ||
868 | r#"/*! Multiline inner comments do not allow any code before them. */ | ||
869 | |||
870 | /*! Still an inner comment, cannot place any code before. */ | ||
871 | |||
872 | use foo::bar::Baz; | ||
873 | fn main() {}"#, | ||
874 | ) | ||
875 | } | ||
876 | |||
877 | #[test] | ||
878 | fn inserts_after_all_inner_items() { | ||
879 | check_none( | ||
880 | "foo::bar::Baz", | ||
881 | r#"#![allow(unused_imports)] | ||
882 | /*! Multiline line comment 2 */ | ||
883 | |||
884 | |||
885 | //! Single line comment 1 | ||
886 | #![no_std] | ||
887 | //! Single line comment 2 | ||
888 | fn main() {}"#, | ||
889 | r#"#![allow(unused_imports)] | ||
890 | /*! Multiline line comment 2 */ | ||
891 | |||
892 | |||
893 | //! Single line comment 1 | ||
894 | #![no_std] | ||
895 | //! Single line comment 2 | ||
896 | |||
897 | use foo::bar::Baz; | ||
898 | fn main() {}"#, | ||
839 | ) | 899 | ) |
840 | } | 900 | } |
841 | 901 | ||
diff --git a/crates/base_db/src/input.rs b/crates/base_db/src/input.rs index 31907ed98..98ba372ad 100644 --- a/crates/base_db/src/input.rs +++ b/crates/base_db/src/input.rs | |||
@@ -225,7 +225,10 @@ impl CrateGraph { | |||
225 | to: CrateId, | 225 | to: CrateId, |
226 | ) -> Result<(), CyclicDependenciesError> { | 226 | ) -> Result<(), CyclicDependenciesError> { |
227 | if self.dfs_find(from, to, &mut FxHashSet::default()) { | 227 | if self.dfs_find(from, to, &mut FxHashSet::default()) { |
228 | return Err(CyclicDependenciesError); | 228 | return Err(CyclicDependenciesError { |
229 | from: (from, self[from].display_name.clone()), | ||
230 | to: (to, self[to].display_name.clone()), | ||
231 | }); | ||
229 | } | 232 | } |
230 | self.arena.get_mut(&from).unwrap().add_dep(name, to); | 233 | self.arena.get_mut(&from).unwrap().add_dep(name, to); |
231 | Ok(()) | 234 | Ok(()) |
@@ -421,7 +424,20 @@ impl fmt::Display for ParseEditionError { | |||
421 | impl std::error::Error for ParseEditionError {} | 424 | impl std::error::Error for ParseEditionError {} |
422 | 425 | ||
423 | #[derive(Debug)] | 426 | #[derive(Debug)] |
424 | pub struct CyclicDependenciesError; | 427 | pub struct CyclicDependenciesError { |
428 | from: (CrateId, Option<CrateDisplayName>), | ||
429 | to: (CrateId, Option<CrateDisplayName>), | ||
430 | } | ||
431 | |||
432 | impl fmt::Display for CyclicDependenciesError { | ||
433 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
434 | let render = |(id, name): &(CrateId, Option<CrateDisplayName>)| match name { | ||
435 | Some(it) => format!("{}({:?})", it, id), | ||
436 | None => format!("{:?}", id), | ||
437 | }; | ||
438 | write!(f, "cyclic deps: {} -> {}", render(&self.from), render(&self.to)) | ||
439 | } | ||
440 | } | ||
425 | 441 | ||
426 | #[cfg(test)] | 442 | #[cfg(test)] |
427 | mod tests { | 443 | mod tests { |
diff --git a/crates/cfg/src/lib.rs b/crates/cfg/src/lib.rs index d0e08cf5f..d88ecf8b0 100644 --- a/crates/cfg/src/lib.rs +++ b/crates/cfg/src/lib.rs | |||
@@ -41,12 +41,6 @@ impl CfgOptions { | |||
41 | self.enabled.insert(CfgAtom::KeyValue { key, value }); | 41 | self.enabled.insert(CfgAtom::KeyValue { key, value }); |
42 | } | 42 | } |
43 | 43 | ||
44 | pub fn append(&mut self, other: &CfgOptions) { | ||
45 | for atom in &other.enabled { | ||
46 | self.enabled.insert(atom.clone()); | ||
47 | } | ||
48 | } | ||
49 | |||
50 | pub fn apply_diff(&mut self, diff: CfgDiff) { | 44 | pub fn apply_diff(&mut self, diff: CfgDiff) { |
51 | for atom in diff.enable { | 45 | for atom in diff.enable { |
52 | self.enabled.insert(atom); | 46 | self.enabled.insert(atom); |
diff --git a/crates/completion/Cargo.toml b/crates/completion/Cargo.toml index b79ee33f7..e7df9d955 100644 --- a/crates/completion/Cargo.toml +++ b/crates/completion/Cargo.toml | |||
@@ -13,7 +13,9 @@ doctest = false | |||
13 | itertools = "0.9.0" | 13 | itertools = "0.9.0" |
14 | log = "0.4.8" | 14 | log = "0.4.8" |
15 | rustc-hash = "1.1.0" | 15 | rustc-hash = "1.1.0" |
16 | either = "1.6.1" | ||
16 | 17 | ||
18 | assists = { path = "../assists", version = "0.0.0" } | ||
17 | stdx = { path = "../stdx", version = "0.0.0" } | 19 | stdx = { path = "../stdx", version = "0.0.0" } |
18 | syntax = { path = "../syntax", version = "0.0.0" } | 20 | syntax = { path = "../syntax", version = "0.0.0" } |
19 | text_edit = { path = "../text_edit", version = "0.0.0" } | 21 | text_edit = { path = "../text_edit", version = "0.0.0" } |
diff --git a/crates/completion/src/completions.rs b/crates/completion/src/completions.rs index 75dbb1a23..9b7d6c580 100644 --- a/crates/completion/src/completions.rs +++ b/crates/completion/src/completions.rs | |||
@@ -90,7 +90,7 @@ impl Completions { | |||
90 | Some(it) => it, | 90 | Some(it) => it, |
91 | None => return, | 91 | None => return, |
92 | }; | 92 | }; |
93 | if let Some(item) = render_macro(RenderContext::new(ctx), name, macro_) { | 93 | if let Some(item) = render_macro(RenderContext::new(ctx), None, name, macro_) { |
94 | self.add(item); | 94 | self.add(item); |
95 | } | 95 | } |
96 | } | 96 | } |
@@ -101,7 +101,7 @@ impl Completions { | |||
101 | func: hir::Function, | 101 | func: hir::Function, |
102 | local_name: Option<String>, | 102 | local_name: Option<String>, |
103 | ) { | 103 | ) { |
104 | let item = render_fn(RenderContext::new(ctx), local_name, func); | 104 | let item = render_fn(RenderContext::new(ctx), None, local_name, func); |
105 | self.add(item) | 105 | self.add(item) |
106 | } | 106 | } |
107 | 107 | ||
@@ -123,7 +123,7 @@ impl Completions { | |||
123 | variant: hir::EnumVariant, | 123 | variant: hir::EnumVariant, |
124 | path: ModPath, | 124 | path: ModPath, |
125 | ) { | 125 | ) { |
126 | let item = render_enum_variant(RenderContext::new(ctx), None, variant, Some(path)); | 126 | let item = render_enum_variant(RenderContext::new(ctx), None, None, variant, Some(path)); |
127 | self.add(item); | 127 | self.add(item); |
128 | } | 128 | } |
129 | 129 | ||
@@ -133,7 +133,7 @@ impl Completions { | |||
133 | variant: hir::EnumVariant, | 133 | variant: hir::EnumVariant, |
134 | local_name: Option<String>, | 134 | local_name: Option<String>, |
135 | ) { | 135 | ) { |
136 | let item = render_enum_variant(RenderContext::new(ctx), local_name, variant, None); | 136 | let item = render_enum_variant(RenderContext::new(ctx), None, local_name, variant, None); |
137 | self.add(item); | 137 | self.add(item); |
138 | } | 138 | } |
139 | } | 139 | } |
diff --git a/crates/completion/src/completions/keyword.rs b/crates/completion/src/completions/keyword.rs index c7df15900..720349b9d 100644 --- a/crates/completion/src/completions/keyword.rs +++ b/crates/completion/src/completions/keyword.rs | |||
@@ -44,6 +44,10 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte | |||
44 | mark::hit!(no_keyword_completion_in_comments); | 44 | mark::hit!(no_keyword_completion_in_comments); |
45 | return; | 45 | return; |
46 | } | 46 | } |
47 | if ctx.record_lit_syntax.is_some() { | ||
48 | mark::hit!(no_keyword_completion_in_record_lit); | ||
49 | return; | ||
50 | } | ||
47 | 51 | ||
48 | let has_trait_or_impl_parent = ctx.has_impl_parent || ctx.has_trait_parent; | 52 | let has_trait_or_impl_parent = ctx.has_impl_parent || ctx.has_trait_parent; |
49 | if ctx.trait_as_prev_sibling || ctx.impl_as_prev_sibling { | 53 | if ctx.trait_as_prev_sibling || ctx.impl_as_prev_sibling { |
@@ -563,4 +567,46 @@ struct Foo { | |||
563 | "#]], | 567 | "#]], |
564 | ) | 568 | ) |
565 | } | 569 | } |
570 | |||
571 | #[test] | ||
572 | fn skip_struct_initializer() { | ||
573 | mark::check!(no_keyword_completion_in_record_lit); | ||
574 | check( | ||
575 | r#" | ||
576 | struct Foo { | ||
577 | pub f: i32, | ||
578 | } | ||
579 | fn foo() { | ||
580 | Foo { | ||
581 | <|> | ||
582 | } | ||
583 | } | ||
584 | "#, | ||
585 | expect![[r#""#]], | ||
586 | ); | ||
587 | } | ||
588 | |||
589 | #[test] | ||
590 | fn struct_initializer_field_expr() { | ||
591 | check( | ||
592 | r#" | ||
593 | struct Foo { | ||
594 | pub f: i32, | ||
595 | } | ||
596 | fn foo() { | ||
597 | Foo { | ||
598 | f: <|> | ||
599 | } | ||
600 | } | ||
601 | "#, | ||
602 | expect![[r#" | ||
603 | kw if | ||
604 | kw if let | ||
605 | kw loop | ||
606 | kw match | ||
607 | kw return | ||
608 | kw while | ||
609 | "#]], | ||
610 | ); | ||
611 | } | ||
566 | } | 612 | } |
diff --git a/crates/completion/src/completions/postfix.rs b/crates/completion/src/completions/postfix.rs index 348f017bd..7fbda7a6b 100644 --- a/crates/completion/src/completions/postfix.rs +++ b/crates/completion/src/completions/postfix.rs | |||
@@ -184,6 +184,16 @@ pub(crate) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) { | |||
184 | ctx, | 184 | ctx, |
185 | cap, | 185 | cap, |
186 | &dot_receiver, | 186 | &dot_receiver, |
187 | "some", | ||
188 | "Some(expr)", | ||
189 | &format!("Some({})", receiver_text), | ||
190 | ) | ||
191 | .add_to(acc); | ||
192 | |||
193 | postfix_snippet( | ||
194 | ctx, | ||
195 | cap, | ||
196 | &dot_receiver, | ||
187 | "dbg", | 197 | "dbg", |
188 | "dbg!(expr)", | 198 | "dbg!(expr)", |
189 | &format!("dbg!({})", receiver_text), | 199 | &format!("dbg!({})", receiver_text), |
@@ -291,6 +301,7 @@ fn main() { | |||
291 | sn ok Ok(expr) | 301 | sn ok Ok(expr) |
292 | sn ref &expr | 302 | sn ref &expr |
293 | sn refm &mut expr | 303 | sn refm &mut expr |
304 | sn some Some(expr) | ||
294 | sn while while expr {} | 305 | sn while while expr {} |
295 | "#]], | 306 | "#]], |
296 | ); | 307 | ); |
@@ -314,6 +325,7 @@ fn main() { | |||
314 | sn ok Ok(expr) | 325 | sn ok Ok(expr) |
315 | sn ref &expr | 326 | sn ref &expr |
316 | sn refm &mut expr | 327 | sn refm &mut expr |
328 | sn some Some(expr) | ||
317 | "#]], | 329 | "#]], |
318 | ) | 330 | ) |
319 | } | 331 | } |
diff --git a/crates/completion/src/completions/record.rs b/crates/completion/src/completions/record.rs index 0f611084b..2049b9d09 100644 --- a/crates/completion/src/completions/record.rs +++ b/crates/completion/src/completions/record.rs | |||
@@ -1,16 +1,43 @@ | |||
1 | //! Complete fields in record literals and patterns. | 1 | //! Complete fields in record literals and patterns. |
2 | use crate::{CompletionContext, Completions}; | 2 | use assists::utils::FamousDefs; |
3 | use syntax::ast::Expr; | ||
4 | |||
5 | use crate::{ | ||
6 | item::CompletionKind, CompletionContext, CompletionItem, CompletionItemKind, Completions, | ||
7 | }; | ||
3 | 8 | ||
4 | pub(crate) fn complete_record(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> { | 9 | pub(crate) fn complete_record(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> { |
5 | let missing_fields = match (ctx.record_pat_syntax.as_ref(), ctx.record_lit_syntax.as_ref()) { | 10 | let missing_fields = match (ctx.record_pat_syntax.as_ref(), ctx.record_lit_syntax.as_ref()) { |
6 | (None, None) => return None, | 11 | (None, None) => return None, |
7 | (Some(_), Some(_)) => unreachable!("A record cannot be both a literal and a pattern"), | 12 | (Some(_), Some(_)) => unreachable!("A record cannot be both a literal and a pattern"), |
8 | (Some(record_pat), _) => ctx.sema.record_pattern_missing_fields(record_pat), | 13 | (Some(record_pat), _) => ctx.sema.record_pattern_missing_fields(record_pat), |
9 | (_, Some(record_lit)) => ctx.sema.record_literal_missing_fields(record_lit), | 14 | (_, Some(record_lit)) => { |
15 | let ty = ctx.sema.type_of_expr(&Expr::RecordExpr(record_lit.clone())); | ||
16 | let default_trait = FamousDefs(&ctx.sema, ctx.krate).core_default_Default(); | ||
17 | let impl_default_trait = default_trait | ||
18 | .and_then(|default_trait| ty.map(|ty| ty.impls_trait(ctx.db, default_trait, &[]))) | ||
19 | .unwrap_or(false); | ||
20 | |||
21 | let missing_fields = ctx.sema.record_literal_missing_fields(record_lit); | ||
22 | if impl_default_trait && !missing_fields.is_empty() { | ||
23 | acc.add( | ||
24 | CompletionItem::new( | ||
25 | CompletionKind::Snippet, | ||
26 | ctx.source_range(), | ||
27 | "..Default::default()", | ||
28 | ) | ||
29 | .insert_text("..Default::default()") | ||
30 | .kind(CompletionItemKind::Field) | ||
31 | .build(), | ||
32 | ); | ||
33 | } | ||
34 | |||
35 | missing_fields | ||
36 | } | ||
10 | }; | 37 | }; |
11 | 38 | ||
12 | for (field, ty) in missing_fields { | 39 | for (field, ty) in missing_fields { |
13 | acc.add_field(ctx, field, &ty) | 40 | acc.add_field(ctx, field, &ty); |
14 | } | 41 | } |
15 | 42 | ||
16 | Some(()) | 43 | Some(()) |
@@ -18,6 +45,7 @@ pub(crate) fn complete_record(acc: &mut Completions, ctx: &CompletionContext) -> | |||
18 | 45 | ||
19 | #[cfg(test)] | 46 | #[cfg(test)] |
20 | mod tests { | 47 | mod tests { |
48 | use assists::utils::FamousDefs; | ||
21 | use expect_test::{expect, Expect}; | 49 | use expect_test::{expect, Expect}; |
22 | 50 | ||
23 | use crate::{test_utils::completion_list, CompletionKind}; | 51 | use crate::{test_utils::completion_list, CompletionKind}; |
@@ -27,6 +55,80 @@ mod tests { | |||
27 | expect.assert_eq(&actual); | 55 | expect.assert_eq(&actual); |
28 | } | 56 | } |
29 | 57 | ||
58 | fn check_snippet(ra_fixture: &str, expect: Expect) { | ||
59 | let actual = completion_list( | ||
60 | &format!("//- /main.rs crate:main deps:core\n{}\n{}", ra_fixture, FamousDefs::FIXTURE), | ||
61 | CompletionKind::Snippet, | ||
62 | ); | ||
63 | expect.assert_eq(&actual); | ||
64 | } | ||
65 | |||
66 | #[test] | ||
67 | fn test_record_literal_field_default() { | ||
68 | let test_code = r#" | ||
69 | struct S { foo: u32, bar: usize } | ||
70 | |||
71 | impl core::default::Default for S { | ||
72 | fn default() -> Self { | ||
73 | S { | ||
74 | foo: 0, | ||
75 | bar: 0, | ||
76 | } | ||
77 | } | ||
78 | } | ||
79 | |||
80 | fn process(f: S) { | ||
81 | let other = S { | ||
82 | foo: 5, | ||
83 | .<|> | ||
84 | }; | ||
85 | } | ||
86 | "#; | ||
87 | check( | ||
88 | test_code, | ||
89 | expect![[r#" | ||
90 | fd bar usize | ||
91 | "#]], | ||
92 | ); | ||
93 | |||
94 | check_snippet( | ||
95 | test_code, | ||
96 | expect![[r#" | ||
97 | fd ..Default::default() | ||
98 | sn pd | ||
99 | sn ppd | ||
100 | "#]], | ||
101 | ); | ||
102 | } | ||
103 | |||
104 | #[test] | ||
105 | fn test_record_literal_field_without_default() { | ||
106 | let test_code = r#" | ||
107 | struct S { foo: u32, bar: usize } | ||
108 | |||
109 | fn process(f: S) { | ||
110 | let other = S { | ||
111 | foo: 5, | ||
112 | .<|> | ||
113 | }; | ||
114 | } | ||
115 | "#; | ||
116 | check( | ||
117 | test_code, | ||
118 | expect![[r#" | ||
119 | fd bar usize | ||
120 | "#]], | ||
121 | ); | ||
122 | |||
123 | check_snippet( | ||
124 | test_code, | ||
125 | expect![[r#" | ||
126 | sn pd | ||
127 | sn ppd | ||
128 | "#]], | ||
129 | ); | ||
130 | } | ||
131 | |||
30 | #[test] | 132 | #[test] |
31 | fn test_record_pattern_field() { | 133 | fn test_record_pattern_field() { |
32 | check( | 134 | check( |
diff --git a/crates/completion/src/completions/unqualified_path.rs b/crates/completion/src/completions/unqualified_path.rs index 7df58e1da..4f1c9faa0 100644 --- a/crates/completion/src/completions/unqualified_path.rs +++ b/crates/completion/src/completions/unqualified_path.rs | |||
@@ -1,10 +1,16 @@ | |||
1 | //! Completion of names from the current scope, e.g. locals and imported items. | 1 | //! Completion of names from the current scope, e.g. locals and imported items. |
2 | 2 | ||
3 | use assists::utils::ImportScope; | ||
4 | use either::Either; | ||
3 | use hir::{Adt, ModuleDef, ScopeDef, Type}; | 5 | use hir::{Adt, ModuleDef, ScopeDef, Type}; |
6 | use ide_db::imports_locator; | ||
4 | use syntax::AstNode; | 7 | use syntax::AstNode; |
5 | use test_utils::mark; | 8 | use test_utils::mark; |
6 | 9 | ||
7 | use crate::{CompletionContext, Completions}; | 10 | use crate::{ |
11 | render::{render_resolution_with_import, RenderContext}, | ||
12 | CompletionContext, Completions, | ||
13 | }; | ||
8 | 14 | ||
9 | pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) { | 15 | pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) { |
10 | if !(ctx.is_trivial_path || ctx.is_pat_binding_or_const) { | 16 | if !(ctx.is_trivial_path || ctx.is_pat_binding_or_const) { |
@@ -37,6 +43,10 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC | |||
37 | } | 43 | } |
38 | acc.add_resolution(ctx, name.to_string(), &res) | 44 | acc.add_resolution(ctx, name.to_string(), &res) |
39 | }); | 45 | }); |
46 | |||
47 | if ctx.config.enable_experimental_completions { | ||
48 | fuzzy_completion(acc, ctx).unwrap_or_default() | ||
49 | } | ||
40 | } | 50 | } |
41 | 51 | ||
42 | fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext, ty: &Type) { | 52 | fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext, ty: &Type) { |
@@ -63,6 +73,45 @@ fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext, ty: &T | |||
63 | } | 73 | } |
64 | } | 74 | } |
65 | 75 | ||
76 | fn fuzzy_completion(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> { | ||
77 | let _p = profile::span("fuzzy_completion"); | ||
78 | let current_module = ctx.scope.module()?; | ||
79 | let anchor = ctx.name_ref_syntax.as_ref()?; | ||
80 | let import_scope = ImportScope::find_insert_use_container(anchor.syntax(), &ctx.sema)?; | ||
81 | |||
82 | let potential_import_name = ctx.token.to_string(); | ||
83 | |||
84 | let possible_imports = | ||
85 | imports_locator::find_similar_imports(&ctx.sema, ctx.krate?, &potential_import_name, 400) | ||
86 | .filter_map(|import_candidate| match import_candidate { | ||
87 | // when completing outside the use declaration, modules are pretty useless | ||
88 | // and tend to bloat the completion suggestions a lot | ||
89 | Either::Left(ModuleDef::Module(_)) => None, | ||
90 | Either::Left(module_def) => Some(( | ||
91 | current_module.find_use_path(ctx.db, module_def)?, | ||
92 | ScopeDef::ModuleDef(module_def), | ||
93 | )), | ||
94 | Either::Right(macro_def) => Some(( | ||
95 | current_module.find_use_path(ctx.db, macro_def)?, | ||
96 | ScopeDef::MacroDef(macro_def), | ||
97 | )), | ||
98 | }) | ||
99 | .filter(|(mod_path, _)| mod_path.len() > 1) | ||
100 | .filter_map(|(import_path, definition)| { | ||
101 | render_resolution_with_import( | ||
102 | RenderContext::new(ctx), | ||
103 | import_path.clone(), | ||
104 | import_scope.clone(), | ||
105 | ctx.config.merge, | ||
106 | &definition, | ||
107 | ) | ||
108 | }) | ||
109 | .take(20); | ||
110 | |||
111 | acc.add_all(possible_imports); | ||
112 | Some(()) | ||
113 | } | ||
114 | |||
66 | #[cfg(test)] | 115 | #[cfg(test)] |
67 | mod tests { | 116 | mod tests { |
68 | use expect_test::{expect, Expect}; | 117 | use expect_test::{expect, Expect}; |
@@ -676,4 +725,85 @@ impl My<|> | |||
676 | "#]], | 725 | "#]], |
677 | ) | 726 | ) |
678 | } | 727 | } |
728 | |||
729 | #[test] | ||
730 | fn function_fuzzy_completion() { | ||
731 | check_edit( | ||
732 | "stdin", | ||
733 | r#" | ||
734 | //- /lib.rs crate:dep | ||
735 | pub mod io { | ||
736 | pub fn stdin() {} | ||
737 | }; | ||
738 | |||
739 | //- /main.rs crate:main deps:dep | ||
740 | fn main() { | ||
741 | stdi<|> | ||
742 | } | ||
743 | "#, | ||
744 | r#" | ||
745 | use dep::io::stdin; | ||
746 | |||
747 | fn main() { | ||
748 | stdin()$0 | ||
749 | } | ||
750 | "#, | ||
751 | ); | ||
752 | } | ||
753 | |||
754 | #[test] | ||
755 | fn macro_fuzzy_completion() { | ||
756 | check_edit( | ||
757 | "macro_with_curlies!", | ||
758 | r#" | ||
759 | //- /lib.rs crate:dep | ||
760 | /// Please call me as macro_with_curlies! {} | ||
761 | #[macro_export] | ||
762 | macro_rules! macro_with_curlies { | ||
763 | () => {} | ||
764 | } | ||
765 | |||
766 | //- /main.rs crate:main deps:dep | ||
767 | fn main() { | ||
768 | curli<|> | ||
769 | } | ||
770 | "#, | ||
771 | r#" | ||
772 | use dep::macro_with_curlies; | ||
773 | |||
774 | fn main() { | ||
775 | macro_with_curlies! {$0} | ||
776 | } | ||
777 | "#, | ||
778 | ); | ||
779 | } | ||
780 | |||
781 | #[test] | ||
782 | fn struct_fuzzy_completion() { | ||
783 | check_edit( | ||
784 | "ThirdStruct", | ||
785 | r#" | ||
786 | //- /lib.rs crate:dep | ||
787 | pub struct FirstStruct; | ||
788 | pub mod some_module { | ||
789 | pub struct SecondStruct; | ||
790 | pub struct ThirdStruct; | ||
791 | } | ||
792 | |||
793 | //- /main.rs crate:main deps:dep | ||
794 | use dep::{FirstStruct, some_module::SecondStruct}; | ||
795 | |||
796 | fn main() { | ||
797 | this<|> | ||
798 | } | ||
799 | "#, | ||
800 | r#" | ||
801 | use dep::{FirstStruct, some_module::{SecondStruct, ThirdStruct}}; | ||
802 | |||
803 | fn main() { | ||
804 | ThirdStruct | ||
805 | } | ||
806 | "#, | ||
807 | ); | ||
808 | } | ||
679 | } | 809 | } |
diff --git a/crates/completion/src/config.rs b/crates/completion/src/config.rs index 71b49ace8..f50735372 100644 --- a/crates/completion/src/config.rs +++ b/crates/completion/src/config.rs | |||
@@ -4,12 +4,16 @@ | |||
4 | //! module, and we use to statically check that we only produce snippet | 4 | //! module, and we use to statically check that we only produce snippet |
5 | //! completions if we are allowed to. | 5 | //! completions if we are allowed to. |
6 | 6 | ||
7 | use assists::utils::MergeBehaviour; | ||
8 | |||
7 | #[derive(Clone, Debug, PartialEq, Eq)] | 9 | #[derive(Clone, Debug, PartialEq, Eq)] |
8 | pub struct CompletionConfig { | 10 | pub struct CompletionConfig { |
9 | pub enable_postfix_completions: bool, | 11 | pub enable_postfix_completions: bool, |
12 | pub enable_experimental_completions: bool, | ||
10 | pub add_call_parenthesis: bool, | 13 | pub add_call_parenthesis: bool, |
11 | pub add_call_argument_snippets: bool, | 14 | pub add_call_argument_snippets: bool, |
12 | pub snippet_cap: Option<SnippetCap>, | 15 | pub snippet_cap: Option<SnippetCap>, |
16 | pub merge: Option<MergeBehaviour>, | ||
13 | } | 17 | } |
14 | 18 | ||
15 | impl CompletionConfig { | 19 | impl CompletionConfig { |
@@ -27,9 +31,11 @@ impl Default for CompletionConfig { | |||
27 | fn default() -> Self { | 31 | fn default() -> Self { |
28 | CompletionConfig { | 32 | CompletionConfig { |
29 | enable_postfix_completions: true, | 33 | enable_postfix_completions: true, |
34 | enable_experimental_completions: true, | ||
30 | add_call_parenthesis: true, | 35 | add_call_parenthesis: true, |
31 | add_call_argument_snippets: true, | 36 | add_call_argument_snippets: true, |
32 | snippet_cap: Some(SnippetCap { _private: () }), | 37 | snippet_cap: Some(SnippetCap { _private: () }), |
38 | merge: Some(MergeBehaviour::Full), | ||
33 | } | 39 | } |
34 | } | 40 | } |
35 | } | 41 | } |
diff --git a/crates/completion/src/item.rs b/crates/completion/src/item.rs index 6d1d085f4..b13c3f376 100644 --- a/crates/completion/src/item.rs +++ b/crates/completion/src/item.rs | |||
@@ -2,8 +2,9 @@ | |||
2 | 2 | ||
3 | use std::fmt; | 3 | use std::fmt; |
4 | 4 | ||
5 | use hir::{Documentation, Mutability}; | 5 | use assists::utils::{insert_use, mod_path_to_ast, ImportScope, MergeBehaviour}; |
6 | use syntax::TextRange; | 6 | use hir::{Documentation, ModPath, Mutability}; |
7 | use syntax::{algo, TextRange}; | ||
7 | use text_edit::TextEdit; | 8 | use text_edit::TextEdit; |
8 | 9 | ||
9 | use crate::config::SnippetCap; | 10 | use crate::config::SnippetCap; |
@@ -31,6 +32,7 @@ pub struct CompletionItem { | |||
31 | /// | 32 | /// |
32 | /// Typically, replaces `source_range` with new identifier. | 33 | /// Typically, replaces `source_range` with new identifier. |
33 | text_edit: TextEdit, | 34 | text_edit: TextEdit, |
35 | |||
34 | insert_text_format: InsertTextFormat, | 36 | insert_text_format: InsertTextFormat, |
35 | 37 | ||
36 | /// What item (struct, function, etc) are we completing. | 38 | /// What item (struct, function, etc) are we completing. |
@@ -199,8 +201,10 @@ impl CompletionItem { | |||
199 | trigger_call_info: None, | 201 | trigger_call_info: None, |
200 | score: None, | 202 | score: None, |
201 | ref_match: None, | 203 | ref_match: None, |
204 | import_data: None, | ||
202 | } | 205 | } |
203 | } | 206 | } |
207 | |||
204 | /// What user sees in pop-up in the UI. | 208 | /// What user sees in pop-up in the UI. |
205 | pub fn label(&self) -> &str { | 209 | pub fn label(&self) -> &str { |
206 | &self.label | 210 | &self.label |
@@ -257,6 +261,7 @@ impl CompletionItem { | |||
257 | pub(crate) struct Builder { | 261 | pub(crate) struct Builder { |
258 | source_range: TextRange, | 262 | source_range: TextRange, |
259 | completion_kind: CompletionKind, | 263 | completion_kind: CompletionKind, |
264 | import_data: Option<(ModPath, ImportScope, Option<MergeBehaviour>)>, | ||
260 | label: String, | 265 | label: String, |
261 | insert_text: Option<String>, | 266 | insert_text: Option<String>, |
262 | insert_text_format: InsertTextFormat, | 267 | insert_text_format: InsertTextFormat, |
@@ -273,23 +278,50 @@ pub(crate) struct Builder { | |||
273 | 278 | ||
274 | impl Builder { | 279 | impl Builder { |
275 | pub(crate) fn build(self) -> CompletionItem { | 280 | pub(crate) fn build(self) -> CompletionItem { |
276 | let label = self.label; | 281 | let mut label = self.label; |
277 | let text_edit = match self.text_edit { | 282 | let mut lookup = self.lookup; |
283 | let mut insert_text = self.insert_text; | ||
284 | let mut text_edits = TextEdit::builder(); | ||
285 | |||
286 | if let Some((import_path, import_scope, merge_behaviour)) = self.import_data { | ||
287 | let import = mod_path_to_ast(&import_path); | ||
288 | let mut import_path_without_last_segment = import_path; | ||
289 | let _ = import_path_without_last_segment.segments.pop(); | ||
290 | |||
291 | if !import_path_without_last_segment.segments.is_empty() { | ||
292 | if lookup.is_none() { | ||
293 | lookup = Some(label.clone()); | ||
294 | } | ||
295 | if insert_text.is_none() { | ||
296 | insert_text = Some(label.clone()); | ||
297 | } | ||
298 | label = format!("{}::{}", import_path_without_last_segment, label); | ||
299 | } | ||
300 | |||
301 | let rewriter = insert_use(&import_scope, import, merge_behaviour); | ||
302 | if let Some(old_ast) = rewriter.rewrite_root() { | ||
303 | algo::diff(&old_ast, &rewriter.rewrite(&old_ast)).into_text_edit(&mut text_edits); | ||
304 | } | ||
305 | } | ||
306 | |||
307 | let original_edit = match self.text_edit { | ||
278 | Some(it) => it, | 308 | Some(it) => it, |
279 | None => TextEdit::replace( | 309 | None => { |
280 | self.source_range, | 310 | TextEdit::replace(self.source_range, insert_text.unwrap_or_else(|| label.clone())) |
281 | self.insert_text.unwrap_or_else(|| label.clone()), | 311 | } |
282 | ), | ||
283 | }; | 312 | }; |
284 | 313 | ||
314 | let mut resulting_edit = text_edits.finish(); | ||
315 | resulting_edit.union(original_edit).expect("Failed to unite text edits"); | ||
316 | |||
285 | CompletionItem { | 317 | CompletionItem { |
286 | source_range: self.source_range, | 318 | source_range: self.source_range, |
287 | label, | 319 | label, |
288 | insert_text_format: self.insert_text_format, | 320 | insert_text_format: self.insert_text_format, |
289 | text_edit, | 321 | text_edit: resulting_edit, |
290 | detail: self.detail, | 322 | detail: self.detail, |
291 | documentation: self.documentation, | 323 | documentation: self.documentation, |
292 | lookup: self.lookup, | 324 | lookup, |
293 | kind: self.kind, | 325 | kind: self.kind, |
294 | completion_kind: self.completion_kind, | 326 | completion_kind: self.completion_kind, |
295 | deprecated: self.deprecated.unwrap_or(false), | 327 | deprecated: self.deprecated.unwrap_or(false), |
@@ -358,6 +390,13 @@ impl Builder { | |||
358 | self.trigger_call_info = Some(true); | 390 | self.trigger_call_info = Some(true); |
359 | self | 391 | self |
360 | } | 392 | } |
393 | pub(crate) fn import_data( | ||
394 | mut self, | ||
395 | import_data: Option<(ModPath, ImportScope, Option<MergeBehaviour>)>, | ||
396 | ) -> Builder { | ||
397 | self.import_data = import_data; | ||
398 | self | ||
399 | } | ||
361 | pub(crate) fn set_ref_match( | 400 | pub(crate) fn set_ref_match( |
362 | mut self, | 401 | mut self, |
363 | ref_match: Option<(Mutability, CompletionScore)>, | 402 | ref_match: Option<(Mutability, CompletionScore)>, |
diff --git a/crates/completion/src/lib.rs b/crates/completion/src/lib.rs index cb6e0554e..aecc1378b 100644 --- a/crates/completion/src/lib.rs +++ b/crates/completion/src/lib.rs | |||
@@ -67,6 +67,13 @@ pub use crate::{ | |||
67 | // fn test_name() {} | 67 | // fn test_name() {} |
68 | // } | 68 | // } |
69 | // ``` | 69 | // ``` |
70 | // | ||
71 | // And experimental completions, enabled with the `rust-analyzer.completion.enableExperimental` setting. | ||
72 | // This flag enables or disables: | ||
73 | // | ||
74 | // - Auto import: additional completion options with automatic `use` import and options from all project importable items, matched for the input | ||
75 | // | ||
76 | // Experimental completions might cause issues with performance and completion list look. | ||
70 | 77 | ||
71 | /// Main entry point for completion. We run completion as a two-phase process. | 78 | /// Main entry point for completion. We run completion as a two-phase process. |
72 | /// | 79 | /// |
diff --git a/crates/completion/src/render.rs b/crates/completion/src/render.rs index 1fa02c375..e892d4de8 100644 --- a/crates/completion/src/render.rs +++ b/crates/completion/src/render.rs | |||
@@ -9,7 +9,8 @@ pub(crate) mod type_alias; | |||
9 | 9 | ||
10 | mod builder_ext; | 10 | mod builder_ext; |
11 | 11 | ||
12 | use hir::{Documentation, HasAttrs, HirDisplay, Mutability, ScopeDef, Type}; | 12 | use assists::utils::{ImportScope, MergeBehaviour}; |
13 | use hir::{Documentation, HasAttrs, HirDisplay, ModPath, Mutability, ScopeDef, Type}; | ||
13 | use ide_db::RootDatabase; | 14 | use ide_db::RootDatabase; |
14 | use syntax::TextRange; | 15 | use syntax::TextRange; |
15 | use test_utils::mark; | 16 | use test_utils::mark; |
@@ -42,7 +43,22 @@ pub(crate) fn render_resolution<'a>( | |||
42 | local_name: String, | 43 | local_name: String, |
43 | resolution: &ScopeDef, | 44 | resolution: &ScopeDef, |
44 | ) -> Option<CompletionItem> { | 45 | ) -> Option<CompletionItem> { |
45 | Render::new(ctx).render_resolution(local_name, resolution) | 46 | Render::new(ctx).render_resolution(local_name, None, resolution) |
47 | } | ||
48 | |||
49 | pub(crate) fn render_resolution_with_import<'a>( | ||
50 | ctx: RenderContext<'a>, | ||
51 | import: ModPath, | ||
52 | import_scope: ImportScope, | ||
53 | merge_behaviour: Option<MergeBehaviour>, | ||
54 | resolution: &ScopeDef, | ||
55 | ) -> Option<CompletionItem> { | ||
56 | let local_name = import.segments.last()?.to_string(); | ||
57 | Render::new(ctx).render_resolution( | ||
58 | local_name, | ||
59 | Some((import, import_scope, merge_behaviour)), | ||
60 | resolution, | ||
61 | ) | ||
46 | } | 62 | } |
47 | 63 | ||
48 | /// Interface for data and methods required for items rendering. | 64 | /// Interface for data and methods required for items rendering. |
@@ -131,6 +147,7 @@ impl<'a> Render<'a> { | |||
131 | fn render_resolution( | 147 | fn render_resolution( |
132 | self, | 148 | self, |
133 | local_name: String, | 149 | local_name: String, |
150 | import_data: Option<(ModPath, ImportScope, Option<MergeBehaviour>)>, | ||
134 | resolution: &ScopeDef, | 151 | resolution: &ScopeDef, |
135 | ) -> Option<CompletionItem> { | 152 | ) -> Option<CompletionItem> { |
136 | use hir::ModuleDef::*; | 153 | use hir::ModuleDef::*; |
@@ -142,15 +159,15 @@ impl<'a> Render<'a> { | |||
142 | 159 | ||
143 | let kind = match resolution { | 160 | let kind = match resolution { |
144 | ScopeDef::ModuleDef(Function(func)) => { | 161 | ScopeDef::ModuleDef(Function(func)) => { |
145 | let item = render_fn(self.ctx, Some(local_name), *func); | 162 | let item = render_fn(self.ctx, import_data, Some(local_name), *func); |
146 | return Some(item); | 163 | return Some(item); |
147 | } | 164 | } |
148 | ScopeDef::ModuleDef(EnumVariant(var)) => { | 165 | ScopeDef::ModuleDef(EnumVariant(var)) => { |
149 | let item = render_enum_variant(self.ctx, Some(local_name), *var, None); | 166 | let item = render_enum_variant(self.ctx, import_data, Some(local_name), *var, None); |
150 | return Some(item); | 167 | return Some(item); |
151 | } | 168 | } |
152 | ScopeDef::MacroDef(mac) => { | 169 | ScopeDef::MacroDef(mac) => { |
153 | let item = render_macro(self.ctx, local_name, *mac); | 170 | let item = render_macro(self.ctx, import_data, local_name, *mac); |
154 | return item; | 171 | return item; |
155 | } | 172 | } |
156 | 173 | ||
@@ -175,6 +192,7 @@ impl<'a> Render<'a> { | |||
175 | local_name, | 192 | local_name, |
176 | ) | 193 | ) |
177 | .kind(CompletionItemKind::UnresolvedReference) | 194 | .kind(CompletionItemKind::UnresolvedReference) |
195 | .import_data(import_data) | ||
178 | .build(); | 196 | .build(); |
179 | return Some(item); | 197 | return Some(item); |
180 | } | 198 | } |
@@ -227,7 +245,12 @@ impl<'a> Render<'a> { | |||
227 | } | 245 | } |
228 | } | 246 | } |
229 | 247 | ||
230 | let item = item.kind(kind).set_documentation(docs).set_ref_match(ref_match).build(); | 248 | let item = item |
249 | .kind(kind) | ||
250 | .import_data(import_data) | ||
251 | .set_documentation(docs) | ||
252 | .set_ref_match(ref_match) | ||
253 | .build(); | ||
231 | Some(item) | 254 | Some(item) |
232 | } | 255 | } |
233 | 256 | ||
@@ -426,6 +449,28 @@ fn main() { let _: m::Spam = S<|> } | |||
426 | kind: Module, | 449 | kind: Module, |
427 | }, | 450 | }, |
428 | CompletionItem { | 451 | CompletionItem { |
452 | label: "m::Spam", | ||
453 | source_range: 75..76, | ||
454 | text_edit: TextEdit { | ||
455 | indels: [ | ||
456 | Indel { | ||
457 | insert: "use m::Spam;", | ||
458 | delete: 0..0, | ||
459 | }, | ||
460 | Indel { | ||
461 | insert: "\n\n", | ||
462 | delete: 0..0, | ||
463 | }, | ||
464 | Indel { | ||
465 | insert: "Spam", | ||
466 | delete: 75..76, | ||
467 | }, | ||
468 | ], | ||
469 | }, | ||
470 | kind: Enum, | ||
471 | lookup: "Spam", | ||
472 | }, | ||
473 | CompletionItem { | ||
429 | label: "m::Spam::Foo", | 474 | label: "m::Spam::Foo", |
430 | source_range: 75..76, | 475 | source_range: 75..76, |
431 | delete: 75..76, | 476 | delete: 75..76, |
diff --git a/crates/completion/src/render/enum_variant.rs b/crates/completion/src/render/enum_variant.rs index fd412ed0e..6070e9b1d 100644 --- a/crates/completion/src/render/enum_variant.rs +++ b/crates/completion/src/render/enum_variant.rs | |||
@@ -1,5 +1,6 @@ | |||
1 | //! Renderer for `enum` variants. | 1 | //! Renderer for `enum` variants. |
2 | 2 | ||
3 | use assists::utils::{ImportScope, MergeBehaviour}; | ||
3 | use hir::{HasAttrs, HirDisplay, ModPath, StructKind}; | 4 | use hir::{HasAttrs, HirDisplay, ModPath, StructKind}; |
4 | use itertools::Itertools; | 5 | use itertools::Itertools; |
5 | use test_utils::mark; | 6 | use test_utils::mark; |
@@ -11,11 +12,12 @@ use crate::{ | |||
11 | 12 | ||
12 | pub(crate) fn render_enum_variant<'a>( | 13 | pub(crate) fn render_enum_variant<'a>( |
13 | ctx: RenderContext<'a>, | 14 | ctx: RenderContext<'a>, |
15 | import_data: Option<(ModPath, ImportScope, Option<MergeBehaviour>)>, | ||
14 | local_name: Option<String>, | 16 | local_name: Option<String>, |
15 | variant: hir::EnumVariant, | 17 | variant: hir::EnumVariant, |
16 | path: Option<ModPath>, | 18 | path: Option<ModPath>, |
17 | ) -> CompletionItem { | 19 | ) -> CompletionItem { |
18 | EnumVariantRender::new(ctx, local_name, variant, path).render() | 20 | EnumVariantRender::new(ctx, local_name, variant, path).render(import_data) |
19 | } | 21 | } |
20 | 22 | ||
21 | #[derive(Debug)] | 23 | #[derive(Debug)] |
@@ -60,7 +62,10 @@ impl<'a> EnumVariantRender<'a> { | |||
60 | } | 62 | } |
61 | } | 63 | } |
62 | 64 | ||
63 | fn render(self) -> CompletionItem { | 65 | fn render( |
66 | self, | ||
67 | import_data: Option<(ModPath, ImportScope, Option<MergeBehaviour>)>, | ||
68 | ) -> CompletionItem { | ||
64 | let mut builder = CompletionItem::new( | 69 | let mut builder = CompletionItem::new( |
65 | CompletionKind::Reference, | 70 | CompletionKind::Reference, |
66 | self.ctx.source_range(), | 71 | self.ctx.source_range(), |
@@ -69,6 +74,7 @@ impl<'a> EnumVariantRender<'a> { | |||
69 | .kind(CompletionItemKind::EnumVariant) | 74 | .kind(CompletionItemKind::EnumVariant) |
70 | .set_documentation(self.variant.docs(self.ctx.db())) | 75 | .set_documentation(self.variant.docs(self.ctx.db())) |
71 | .set_deprecated(self.ctx.is_deprecated(self.variant)) | 76 | .set_deprecated(self.ctx.is_deprecated(self.variant)) |
77 | .import_data(import_data) | ||
72 | .detail(self.detail()); | 78 | .detail(self.detail()); |
73 | 79 | ||
74 | if self.variant_kind == StructKind::Tuple { | 80 | if self.variant_kind == StructKind::Tuple { |
diff --git a/crates/completion/src/render/function.rs b/crates/completion/src/render/function.rs index 4fa6eafd7..9dd5cd18c 100644 --- a/crates/completion/src/render/function.rs +++ b/crates/completion/src/render/function.rs | |||
@@ -1,6 +1,7 @@ | |||
1 | //! Renderer for function calls. | 1 | //! Renderer for function calls. |
2 | 2 | ||
3 | use hir::{HasSource, Type}; | 3 | use assists::utils::{ImportScope, MergeBehaviour}; |
4 | use hir::{HasSource, ModPath, Type}; | ||
4 | use syntax::{ast::Fn, display::function_declaration}; | 5 | use syntax::{ast::Fn, display::function_declaration}; |
5 | 6 | ||
6 | use crate::{ | 7 | use crate::{ |
@@ -10,10 +11,11 @@ use crate::{ | |||
10 | 11 | ||
11 | pub(crate) fn render_fn<'a>( | 12 | pub(crate) fn render_fn<'a>( |
12 | ctx: RenderContext<'a>, | 13 | ctx: RenderContext<'a>, |
14 | import_data: Option<(ModPath, ImportScope, Option<MergeBehaviour>)>, | ||
13 | local_name: Option<String>, | 15 | local_name: Option<String>, |
14 | fn_: hir::Function, | 16 | fn_: hir::Function, |
15 | ) -> CompletionItem { | 17 | ) -> CompletionItem { |
16 | FunctionRender::new(ctx, local_name, fn_).render() | 18 | FunctionRender::new(ctx, local_name, fn_).render(import_data) |
17 | } | 19 | } |
18 | 20 | ||
19 | #[derive(Debug)] | 21 | #[derive(Debug)] |
@@ -36,7 +38,10 @@ impl<'a> FunctionRender<'a> { | |||
36 | FunctionRender { ctx, name, fn_, ast_node } | 38 | FunctionRender { ctx, name, fn_, ast_node } |
37 | } | 39 | } |
38 | 40 | ||
39 | fn render(self) -> CompletionItem { | 41 | fn render( |
42 | self, | ||
43 | import_data: Option<(ModPath, ImportScope, Option<MergeBehaviour>)>, | ||
44 | ) -> CompletionItem { | ||
40 | let params = self.params(); | 45 | let params = self.params(); |
41 | CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), self.name.clone()) | 46 | CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), self.name.clone()) |
42 | .kind(self.kind()) | 47 | .kind(self.kind()) |
@@ -44,6 +49,7 @@ impl<'a> FunctionRender<'a> { | |||
44 | .set_deprecated(self.ctx.is_deprecated(self.fn_)) | 49 | .set_deprecated(self.ctx.is_deprecated(self.fn_)) |
45 | .detail(self.detail()) | 50 | .detail(self.detail()) |
46 | .add_call_parens(self.ctx.completion, self.name, params) | 51 | .add_call_parens(self.ctx.completion, self.name, params) |
52 | .import_data(import_data) | ||
47 | .build() | 53 | .build() |
48 | } | 54 | } |
49 | 55 | ||
diff --git a/crates/completion/src/render/macro_.rs b/crates/completion/src/render/macro_.rs index 96be59cc3..fead59e41 100644 --- a/crates/completion/src/render/macro_.rs +++ b/crates/completion/src/render/macro_.rs | |||
@@ -1,6 +1,7 @@ | |||
1 | //! Renderer for macro invocations. | 1 | //! Renderer for macro invocations. |
2 | 2 | ||
3 | use hir::{Documentation, HasSource}; | 3 | use assists::utils::{ImportScope, MergeBehaviour}; |
4 | use hir::{Documentation, HasSource, ModPath}; | ||
4 | use syntax::display::macro_label; | 5 | use syntax::display::macro_label; |
5 | use test_utils::mark; | 6 | use test_utils::mark; |
6 | 7 | ||
@@ -11,10 +12,11 @@ use crate::{ | |||
11 | 12 | ||
12 | pub(crate) fn render_macro<'a>( | 13 | pub(crate) fn render_macro<'a>( |
13 | ctx: RenderContext<'a>, | 14 | ctx: RenderContext<'a>, |
15 | import_data: Option<(ModPath, ImportScope, Option<MergeBehaviour>)>, | ||
14 | name: String, | 16 | name: String, |
15 | macro_: hir::MacroDef, | 17 | macro_: hir::MacroDef, |
16 | ) -> Option<CompletionItem> { | 18 | ) -> Option<CompletionItem> { |
17 | MacroRender::new(ctx, name, macro_).render() | 19 | MacroRender::new(ctx, name, macro_).render(import_data) |
18 | } | 20 | } |
19 | 21 | ||
20 | #[derive(Debug)] | 22 | #[derive(Debug)] |
@@ -36,7 +38,10 @@ impl<'a> MacroRender<'a> { | |||
36 | MacroRender { ctx, name, macro_, docs, bra, ket } | 38 | MacroRender { ctx, name, macro_, docs, bra, ket } |
37 | } | 39 | } |
38 | 40 | ||
39 | fn render(&self) -> Option<CompletionItem> { | 41 | fn render( |
42 | &self, | ||
43 | import_data: Option<(ModPath, ImportScope, Option<MergeBehaviour>)>, | ||
44 | ) -> Option<CompletionItem> { | ||
40 | // FIXME: Currently proc-macro do not have ast-node, | 45 | // FIXME: Currently proc-macro do not have ast-node, |
41 | // such that it does not have source | 46 | // such that it does not have source |
42 | if self.macro_.is_proc_macro() { | 47 | if self.macro_.is_proc_macro() { |
@@ -48,6 +53,7 @@ impl<'a> MacroRender<'a> { | |||
48 | .kind(CompletionItemKind::Macro) | 53 | .kind(CompletionItemKind::Macro) |
49 | .set_documentation(self.docs.clone()) | 54 | .set_documentation(self.docs.clone()) |
50 | .set_deprecated(self.ctx.is_deprecated(self.macro_)) | 55 | .set_deprecated(self.ctx.is_deprecated(self.macro_)) |
56 | .import_data(import_data) | ||
51 | .detail(self.detail()); | 57 | .detail(self.detail()); |
52 | 58 | ||
53 | let needs_bang = self.needs_bang(); | 59 | let needs_bang = self.needs_bang(); |
diff --git a/crates/hir/src/code_model.rs b/crates/hir/src/code_model.rs index 30a5e4580..37ed092ad 100644 --- a/crates/hir/src/code_model.rs +++ b/crates/hir/src/code_model.rs | |||
@@ -110,15 +110,9 @@ impl Crate { | |||
110 | pub fn query_external_importables( | 110 | pub fn query_external_importables( |
111 | self, | 111 | self, |
112 | db: &dyn DefDatabase, | 112 | db: &dyn DefDatabase, |
113 | query: &str, | 113 | query: import_map::Query, |
114 | ) -> impl Iterator<Item = Either<ModuleDef, MacroDef>> { | 114 | ) -> impl Iterator<Item = Either<ModuleDef, MacroDef>> { |
115 | import_map::search_dependencies( | 115 | import_map::search_dependencies(db, self.into(), query).into_iter().map(|item| match item { |
116 | db, | ||
117 | self.into(), | ||
118 | import_map::Query::new(query).anchor_end().case_sensitive().limit(40), | ||
119 | ) | ||
120 | .into_iter() | ||
121 | .map(|item| match item { | ||
122 | ItemInNs::Types(mod_id) | ItemInNs::Values(mod_id) => Either::Left(mod_id.into()), | 116 | ItemInNs::Types(mod_id) | ItemInNs::Values(mod_id) => Either::Left(mod_id.into()), |
123 | ItemInNs::Macros(mac_id) => Either::Right(mac_id.into()), | 117 | ItemInNs::Macros(mac_id) => Either::Right(mac_id.into()), |
124 | }) | 118 | }) |
diff --git a/crates/hir/src/db.rs b/crates/hir/src/db.rs index 07333c453..8c767b249 100644 --- a/crates/hir/src/db.rs +++ b/crates/hir/src/db.rs | |||
@@ -11,7 +11,7 @@ pub use hir_def::db::{ | |||
11 | }; | 11 | }; |
12 | pub use hir_expand::db::{ | 12 | pub use hir_expand::db::{ |
13 | AstDatabase, AstDatabaseStorage, AstIdMapQuery, InternEagerExpansionQuery, InternMacroQuery, | 13 | AstDatabase, AstDatabaseStorage, AstIdMapQuery, InternEagerExpansionQuery, InternMacroQuery, |
14 | MacroArgTextQuery, MacroDefQuery, MacroExpandQuery, ParseMacroQuery, | 14 | MacroArgTextQuery, MacroDefQuery, MacroExpandQuery, ParseMacroExpansionQuery, |
15 | }; | 15 | }; |
16 | pub use hir_ty::db::*; | 16 | pub use hir_ty::db::*; |
17 | 17 | ||
diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs index c18c1c587..d9ad8db6f 100644 --- a/crates/hir/src/diagnostics.rs +++ b/crates/hir/src/diagnostics.rs | |||
@@ -1,6 +1,8 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | pub use hir_def::diagnostics::{InactiveCode, UnresolvedModule}; | 2 | pub use hir_def::diagnostics::{InactiveCode, UnresolvedModule}; |
3 | pub use hir_expand::diagnostics::{Diagnostic, DiagnosticSink, DiagnosticSinkBuilder}; | 3 | pub use hir_expand::diagnostics::{ |
4 | Diagnostic, DiagnosticCode, DiagnosticSink, DiagnosticSinkBuilder, | ||
5 | }; | ||
4 | pub use hir_ty::diagnostics::{ | 6 | pub use hir_ty::diagnostics::{ |
5 | IncorrectCase, MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkInTailExpr, | 7 | IncorrectCase, MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkInTailExpr, |
6 | NoSuchField, | 8 | NoSuchField, |
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 0d184379f..ed110329d 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs | |||
@@ -49,6 +49,7 @@ pub use hir_def::{ | |||
49 | builtin_type::BuiltinType, | 49 | builtin_type::BuiltinType, |
50 | docs::Documentation, | 50 | docs::Documentation, |
51 | find_path::PrefixKind, | 51 | find_path::PrefixKind, |
52 | import_map, | ||
52 | item_scope::ItemInNs, | 53 | item_scope::ItemInNs, |
53 | nameres::ModuleSource, | 54 | nameres::ModuleSource, |
54 | path::{ModPath, PathKind}, | 55 | path::{ModPath, PathKind}, |
@@ -56,8 +57,8 @@ pub use hir_def::{ | |||
56 | visibility::Visibility, | 57 | visibility::Visibility, |
57 | }; | 58 | }; |
58 | pub use hir_expand::{ | 59 | pub use hir_expand::{ |
59 | name::known, name::AsName, name::Name, HirFileId, InFile, MacroCallId, MacroCallLoc, | 60 | db::MacroResult, name::known, name::AsName, name::Name, HirFileId, InFile, MacroCallId, |
60 | /* FIXME */ MacroDefId, MacroFile, Origin, | 61 | MacroCallLoc, /* FIXME */ MacroDefId, MacroFile, Origin, |
61 | }; | 62 | }; |
62 | pub use hir_ty::display::HirDisplay; | 63 | pub use hir_ty::display::HirDisplay; |
63 | 64 | ||
diff --git a/crates/hir_expand/src/db.rs b/crates/hir_expand/src/db.rs index ade57ac1b..a9099eb22 100644 --- a/crates/hir_expand/src/db.rs +++ b/crates/hir_expand/src/db.rs | |||
@@ -13,6 +13,19 @@ use crate::{ | |||
13 | MacroFile, ProcMacroExpander, | 13 | MacroFile, ProcMacroExpander, |
14 | }; | 14 | }; |
15 | 15 | ||
16 | /// A result of some macro expansion. | ||
17 | #[derive(Debug, Clone, Eq, PartialEq)] | ||
18 | pub struct MacroResult<T> { | ||
19 | /// The result of the expansion. Might be `None` when error recovery was impossible and no | ||
20 | /// usable result was produced. | ||
21 | pub value: Option<T>, | ||
22 | |||
23 | /// The error that occurred during expansion or processing. | ||
24 | /// | ||
25 | /// Since we do error recovery, getting an error here does not mean that `value` will be absent. | ||
26 | pub error: Option<String>, | ||
27 | } | ||
28 | |||
16 | #[derive(Debug, Clone, Eq, PartialEq)] | 29 | #[derive(Debug, Clone, Eq, PartialEq)] |
17 | pub enum TokenExpander { | 30 | pub enum TokenExpander { |
18 | MacroRules(mbe::MacroRules), | 31 | MacroRules(mbe::MacroRules), |
@@ -75,9 +88,11 @@ pub trait AstDatabase: SourceDatabase { | |||
75 | #[salsa::transparent] | 88 | #[salsa::transparent] |
76 | fn macro_arg(&self, id: MacroCallId) -> Option<Arc<(tt::Subtree, mbe::TokenMap)>>; | 89 | fn macro_arg(&self, id: MacroCallId) -> Option<Arc<(tt::Subtree, mbe::TokenMap)>>; |
77 | fn macro_def(&self, id: MacroDefId) -> Option<Arc<(TokenExpander, mbe::TokenMap)>>; | 90 | fn macro_def(&self, id: MacroDefId) -> Option<Arc<(TokenExpander, mbe::TokenMap)>>; |
78 | fn parse_macro(&self, macro_file: MacroFile) | 91 | fn parse_macro_expansion( |
79 | -> Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)>; | 92 | &self, |
80 | fn macro_expand(&self, macro_call: MacroCallId) -> (Option<Arc<tt::Subtree>>, Option<String>); | 93 | macro_file: MacroFile, |
94 | ) -> MacroResult<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)>; | ||
95 | fn macro_expand(&self, macro_call: MacroCallId) -> MacroResult<Arc<tt::Subtree>>; | ||
81 | 96 | ||
82 | #[salsa::interned] | 97 | #[salsa::interned] |
83 | fn intern_eager_expansion(&self, eager: EagerCallLoc) -> EagerMacroId; | 98 | fn intern_eager_expansion(&self, eager: EagerCallLoc) -> EagerMacroId; |
@@ -85,6 +100,20 @@ pub trait AstDatabase: SourceDatabase { | |||
85 | fn expand_proc_macro(&self, call: MacroCallId) -> Result<tt::Subtree, mbe::ExpandError>; | 100 | fn expand_proc_macro(&self, call: MacroCallId) -> Result<tt::Subtree, mbe::ExpandError>; |
86 | } | 101 | } |
87 | 102 | ||
103 | impl<T> MacroResult<T> { | ||
104 | fn error(message: String) -> Self { | ||
105 | Self { value: None, error: Some(message) } | ||
106 | } | ||
107 | |||
108 | fn map<U>(self, f: impl FnOnce(T) -> U) -> MacroResult<U> { | ||
109 | MacroResult { value: self.value.map(f), error: self.error } | ||
110 | } | ||
111 | |||
112 | fn drop_value<U>(self) -> MacroResult<U> { | ||
113 | MacroResult { value: None, error: self.error } | ||
114 | } | ||
115 | } | ||
116 | |||
88 | /// This expands the given macro call, but with different arguments. This is | 117 | /// This expands the given macro call, but with different arguments. This is |
89 | /// used for completion, where we want to see what 'would happen' if we insert a | 118 | /// used for completion, where we want to see what 'would happen' if we insert a |
90 | /// token. The `token_to_map` mapped down into the expansion, with the mapped | 119 | /// token. The `token_to_map` mapped down into the expansion, with the mapped |
@@ -102,23 +131,20 @@ pub fn expand_hypothetical( | |||
102 | let token_id = tmap_1.token_by_range(range)?; | 131 | let token_id = tmap_1.token_by_range(range)?; |
103 | let macro_def = expander(db, actual_macro_call)?; | 132 | let macro_def = expander(db, actual_macro_call)?; |
104 | let (node, tmap_2) = | 133 | let (node, tmap_2) = |
105 | parse_macro_with_arg(db, macro_file, Some(std::sync::Arc::new((tt, tmap_1))))?; | 134 | parse_macro_with_arg(db, macro_file, Some(std::sync::Arc::new((tt, tmap_1)))).value?; |
106 | let token_id = macro_def.0.map_id_down(token_id); | 135 | let token_id = macro_def.0.map_id_down(token_id); |
107 | let range = tmap_2.range_by_token(token_id)?.by_kind(token_to_map.kind())?; | 136 | let range = tmap_2.range_by_token(token_id)?.by_kind(token_to_map.kind())?; |
108 | let token = syntax::algo::find_covering_element(&node.syntax_node(), range).into_token()?; | 137 | let token = syntax::algo::find_covering_element(&node.syntax_node(), range).into_token()?; |
109 | Some((node.syntax_node(), token)) | 138 | Some((node.syntax_node(), token)) |
110 | } | 139 | } |
111 | 140 | ||
112 | pub(crate) fn ast_id_map(db: &dyn AstDatabase, file_id: HirFileId) -> Arc<AstIdMap> { | 141 | fn ast_id_map(db: &dyn AstDatabase, file_id: HirFileId) -> Arc<AstIdMap> { |
113 | let map = | 142 | let map = |
114 | db.parse_or_expand(file_id).map_or_else(AstIdMap::default, |it| AstIdMap::from_source(&it)); | 143 | db.parse_or_expand(file_id).map_or_else(AstIdMap::default, |it| AstIdMap::from_source(&it)); |
115 | Arc::new(map) | 144 | Arc::new(map) |
116 | } | 145 | } |
117 | 146 | ||
118 | pub(crate) fn macro_def( | 147 | fn macro_def(db: &dyn AstDatabase, id: MacroDefId) -> Option<Arc<(TokenExpander, mbe::TokenMap)>> { |
119 | db: &dyn AstDatabase, | ||
120 | id: MacroDefId, | ||
121 | ) -> Option<Arc<(TokenExpander, mbe::TokenMap)>> { | ||
122 | match id.kind { | 148 | match id.kind { |
123 | MacroDefKind::Declarative => { | 149 | MacroDefKind::Declarative => { |
124 | let macro_call = id.ast_id?.to_node(db); | 150 | let macro_call = id.ast_id?.to_node(db); |
@@ -149,7 +175,7 @@ pub(crate) fn macro_def( | |||
149 | } | 175 | } |
150 | } | 176 | } |
151 | 177 | ||
152 | pub(crate) fn macro_arg_text(db: &dyn AstDatabase, id: MacroCallId) -> Option<GreenNode> { | 178 | fn macro_arg_text(db: &dyn AstDatabase, id: MacroCallId) -> Option<GreenNode> { |
153 | let id = match id { | 179 | let id = match id { |
154 | MacroCallId::LazyMacro(id) => id, | 180 | MacroCallId::LazyMacro(id) => id, |
155 | MacroCallId::EagerMacro(_id) => { | 181 | MacroCallId::EagerMacro(_id) => { |
@@ -162,19 +188,13 @@ pub(crate) fn macro_arg_text(db: &dyn AstDatabase, id: MacroCallId) -> Option<Gr | |||
162 | Some(arg.green().clone()) | 188 | Some(arg.green().clone()) |
163 | } | 189 | } |
164 | 190 | ||
165 | pub(crate) fn macro_arg( | 191 | fn macro_arg(db: &dyn AstDatabase, id: MacroCallId) -> Option<Arc<(tt::Subtree, mbe::TokenMap)>> { |
166 | db: &dyn AstDatabase, | ||
167 | id: MacroCallId, | ||
168 | ) -> Option<Arc<(tt::Subtree, mbe::TokenMap)>> { | ||
169 | let arg = db.macro_arg_text(id)?; | 192 | let arg = db.macro_arg_text(id)?; |
170 | let (tt, tmap) = mbe::syntax_node_to_token_tree(&SyntaxNode::new_root(arg))?; | 193 | let (tt, tmap) = mbe::syntax_node_to_token_tree(&SyntaxNode::new_root(arg))?; |
171 | Some(Arc::new((tt, tmap))) | 194 | Some(Arc::new((tt, tmap))) |
172 | } | 195 | } |
173 | 196 | ||
174 | pub(crate) fn macro_expand( | 197 | fn macro_expand(db: &dyn AstDatabase, id: MacroCallId) -> MacroResult<Arc<tt::Subtree>> { |
175 | db: &dyn AstDatabase, | ||
176 | id: MacroCallId, | ||
177 | ) -> (Option<Arc<tt::Subtree>>, Option<String>) { | ||
178 | macro_expand_with_arg(db, id, None) | 198 | macro_expand_with_arg(db, id, None) |
179 | } | 199 | } |
180 | 200 | ||
@@ -195,17 +215,19 @@ fn macro_expand_with_arg( | |||
195 | db: &dyn AstDatabase, | 215 | db: &dyn AstDatabase, |
196 | id: MacroCallId, | 216 | id: MacroCallId, |
197 | arg: Option<Arc<(tt::Subtree, mbe::TokenMap)>>, | 217 | arg: Option<Arc<(tt::Subtree, mbe::TokenMap)>>, |
198 | ) -> (Option<Arc<tt::Subtree>>, Option<String>) { | 218 | ) -> MacroResult<Arc<tt::Subtree>> { |
199 | let lazy_id = match id { | 219 | let lazy_id = match id { |
200 | MacroCallId::LazyMacro(id) => id, | 220 | MacroCallId::LazyMacro(id) => id, |
201 | MacroCallId::EagerMacro(id) => { | 221 | MacroCallId::EagerMacro(id) => { |
202 | if arg.is_some() { | 222 | if arg.is_some() { |
203 | return ( | 223 | return MacroResult::error( |
204 | None, | 224 | "hypothetical macro expansion not implemented for eager macro".to_owned(), |
205 | Some("hypothetical macro expansion not implemented for eager macro".to_owned()), | ||
206 | ); | 225 | ); |
207 | } else { | 226 | } else { |
208 | return (Some(db.lookup_intern_eager_expansion(id).subtree), None); | 227 | return MacroResult { |
228 | value: Some(db.lookup_intern_eager_expansion(id).subtree), | ||
229 | error: None, | ||
230 | }; | ||
209 | } | 231 | } |
210 | } | 232 | } |
211 | }; | 233 | }; |
@@ -213,23 +235,24 @@ fn macro_expand_with_arg( | |||
213 | let loc = db.lookup_intern_macro(lazy_id); | 235 | let loc = db.lookup_intern_macro(lazy_id); |
214 | let macro_arg = match arg.or_else(|| db.macro_arg(id)) { | 236 | let macro_arg = match arg.or_else(|| db.macro_arg(id)) { |
215 | Some(it) => it, | 237 | Some(it) => it, |
216 | None => return (None, Some("Fail to args in to tt::TokenTree".into())), | 238 | None => return MacroResult::error("Fail to args in to tt::TokenTree".into()), |
217 | }; | 239 | }; |
218 | 240 | ||
219 | let macro_rules = match db.macro_def(loc.def) { | 241 | let macro_rules = match db.macro_def(loc.def) { |
220 | Some(it) => it, | 242 | Some(it) => it, |
221 | None => return (None, Some("Fail to find macro definition".into())), | 243 | None => return MacroResult::error("Fail to find macro definition".into()), |
222 | }; | 244 | }; |
223 | let ExpandResult(tt, err) = macro_rules.0.expand(db, lazy_id, ¯o_arg.0); | 245 | let ExpandResult(tt, err) = macro_rules.0.expand(db, lazy_id, ¯o_arg.0); |
224 | // Set a hard limit for the expanded tt | 246 | // Set a hard limit for the expanded tt |
225 | let count = tt.count(); | 247 | let count = tt.count(); |
226 | if count > 262144 { | 248 | if count > 262144 { |
227 | return (None, Some(format!("Total tokens count exceed limit : count = {}", count))); | 249 | return MacroResult::error(format!("Total tokens count exceed limit : count = {}", count)); |
228 | } | 250 | } |
229 | (Some(Arc::new(tt)), err.map(|e| format!("{:?}", e))) | 251 | |
252 | MacroResult { value: Some(Arc::new(tt)), error: err.map(|e| format!("{:?}", e)) } | ||
230 | } | 253 | } |
231 | 254 | ||
232 | pub(crate) fn expand_proc_macro( | 255 | fn expand_proc_macro( |
233 | db: &dyn AstDatabase, | 256 | db: &dyn AstDatabase, |
234 | id: MacroCallId, | 257 | id: MacroCallId, |
235 | ) -> Result<tt::Subtree, mbe::ExpandError> { | 258 | ) -> Result<tt::Subtree, mbe::ExpandError> { |
@@ -256,36 +279,36 @@ pub(crate) fn expand_proc_macro( | |||
256 | expander.expand(db, lazy_id, ¯o_arg.0) | 279 | expander.expand(db, lazy_id, ¯o_arg.0) |
257 | } | 280 | } |
258 | 281 | ||
259 | pub(crate) fn parse_or_expand(db: &dyn AstDatabase, file_id: HirFileId) -> Option<SyntaxNode> { | 282 | fn parse_or_expand(db: &dyn AstDatabase, file_id: HirFileId) -> Option<SyntaxNode> { |
260 | match file_id.0 { | 283 | match file_id.0 { |
261 | HirFileIdRepr::FileId(file_id) => Some(db.parse(file_id).tree().syntax().clone()), | 284 | HirFileIdRepr::FileId(file_id) => Some(db.parse(file_id).tree().syntax().clone()), |
262 | HirFileIdRepr::MacroFile(macro_file) => { | 285 | HirFileIdRepr::MacroFile(macro_file) => { |
263 | db.parse_macro(macro_file).map(|(it, _)| it.syntax_node()) | 286 | db.parse_macro_expansion(macro_file).map(|(it, _)| it.syntax_node()).value |
264 | } | 287 | } |
265 | } | 288 | } |
266 | } | 289 | } |
267 | 290 | ||
268 | pub(crate) fn parse_macro( | 291 | fn parse_macro_expansion( |
269 | db: &dyn AstDatabase, | 292 | db: &dyn AstDatabase, |
270 | macro_file: MacroFile, | 293 | macro_file: MacroFile, |
271 | ) -> Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)> { | 294 | ) -> MacroResult<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)> { |
272 | parse_macro_with_arg(db, macro_file, None) | 295 | parse_macro_with_arg(db, macro_file, None) |
273 | } | 296 | } |
274 | 297 | ||
275 | pub fn parse_macro_with_arg( | 298 | fn parse_macro_with_arg( |
276 | db: &dyn AstDatabase, | 299 | db: &dyn AstDatabase, |
277 | macro_file: MacroFile, | 300 | macro_file: MacroFile, |
278 | arg: Option<Arc<(tt::Subtree, mbe::TokenMap)>>, | 301 | arg: Option<Arc<(tt::Subtree, mbe::TokenMap)>>, |
279 | ) -> Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)> { | 302 | ) -> MacroResult<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)> { |
280 | let _p = profile::span("parse_macro_query"); | 303 | let _p = profile::span("parse_macro_query"); |
281 | 304 | ||
282 | let macro_call_id = macro_file.macro_call_id; | 305 | let macro_call_id = macro_file.macro_call_id; |
283 | let (tt, err) = if let Some(arg) = arg { | 306 | let result = if let Some(arg) = arg { |
284 | macro_expand_with_arg(db, macro_call_id, Some(arg)) | 307 | macro_expand_with_arg(db, macro_call_id, Some(arg)) |
285 | } else { | 308 | } else { |
286 | db.macro_expand(macro_call_id) | 309 | db.macro_expand(macro_call_id) |
287 | }; | 310 | }; |
288 | if let Some(err) = &err { | 311 | if let Some(err) = &result.error { |
289 | // Note: | 312 | // Note: |
290 | // The final goal we would like to make all parse_macro success, | 313 | // The final goal we would like to make all parse_macro success, |
291 | // such that the following log will not call anyway. | 314 | // such that the following log will not call anyway. |
@@ -313,30 +336,40 @@ pub fn parse_macro_with_arg( | |||
313 | log::warn!("fail on macro_parse: (reason: {})", err); | 336 | log::warn!("fail on macro_parse: (reason: {})", err); |
314 | } | 337 | } |
315 | } | 338 | } |
339 | } | ||
340 | let tt = match result.value { | ||
341 | Some(tt) => tt, | ||
342 | None => return result.drop_value(), | ||
316 | }; | 343 | }; |
317 | let tt = tt?; | ||
318 | 344 | ||
319 | let fragment_kind = to_fragment_kind(db, macro_call_id); | 345 | let fragment_kind = to_fragment_kind(db, macro_call_id); |
320 | 346 | ||
321 | let (parse, rev_token_map) = mbe::token_tree_to_syntax_node(&tt, fragment_kind).ok()?; | 347 | let (parse, rev_token_map) = match mbe::token_tree_to_syntax_node(&tt, fragment_kind) { |
348 | Ok(it) => it, | ||
349 | Err(err) => { | ||
350 | return MacroResult::error(format!("{:?}", err)); | ||
351 | } | ||
352 | }; | ||
322 | 353 | ||
323 | if err.is_none() { | 354 | match result.error { |
324 | Some((parse, Arc::new(rev_token_map))) | 355 | Some(error) => { |
325 | } else { | 356 | // Safety check for recursive identity macro. |
326 | // FIXME: | 357 | let node = parse.syntax_node(); |
327 | // In future, we should propagate the actual error with recovery information | 358 | let file: HirFileId = macro_file.into(); |
328 | // instead of ignore the error here. | 359 | let call_node = match file.call_node(db) { |
329 | 360 | Some(it) => it, | |
330 | // Safe check for recurisve identity macro | 361 | None => { |
331 | let node = parse.syntax_node(); | 362 | return MacroResult::error(error); |
332 | let file: HirFileId = macro_file.into(); | 363 | } |
333 | let call_node = file.call_node(db)?; | 364 | }; |
334 | 365 | ||
335 | if !diff(&node, &call_node.value).is_empty() { | 366 | if !diff(&node, &call_node.value).is_empty() { |
336 | Some((parse, Arc::new(rev_token_map))) | 367 | MacroResult { value: Some((parse, Arc::new(rev_token_map))), error: Some(error) } |
337 | } else { | 368 | } else { |
338 | None | 369 | return MacroResult::error(error); |
370 | } | ||
339 | } | 371 | } |
372 | None => MacroResult { value: Some((parse, Arc::new(rev_token_map))), error: None }, | ||
340 | } | 373 | } |
341 | } | 374 | } |
342 | 375 | ||
diff --git a/crates/hir_expand/src/diagnostics.rs b/crates/hir_expand/src/diagnostics.rs index 78ccc212c..1043c6aeb 100644 --- a/crates/hir_expand/src/diagnostics.rs +++ b/crates/hir_expand/src/diagnostics.rs | |||
@@ -20,7 +20,7 @@ use syntax::SyntaxNodePtr; | |||
20 | 20 | ||
21 | use crate::InFile; | 21 | use crate::InFile; |
22 | 22 | ||
23 | #[derive(Copy, Clone, PartialEq)] | 23 | #[derive(Copy, Clone, Debug, PartialEq)] |
24 | pub struct DiagnosticCode(pub &'static str); | 24 | pub struct DiagnosticCode(pub &'static str); |
25 | 25 | ||
26 | impl DiagnosticCode { | 26 | impl DiagnosticCode { |
diff --git a/crates/hir_expand/src/lib.rs b/crates/hir_expand/src/lib.rs index 17f1178ed..83e09738b 100644 --- a/crates/hir_expand/src/lib.rs +++ b/crates/hir_expand/src/lib.rs | |||
@@ -144,7 +144,7 @@ impl HirFileId { | |||
144 | let def_tt = loc.def.ast_id?.to_node(db).token_tree()?; | 144 | let def_tt = loc.def.ast_id?.to_node(db).token_tree()?; |
145 | 145 | ||
146 | let macro_def = db.macro_def(loc.def)?; | 146 | let macro_def = db.macro_def(loc.def)?; |
147 | let (parse, exp_map) = db.parse_macro(macro_file)?; | 147 | let (parse, exp_map) = db.parse_macro_expansion(macro_file).value?; |
148 | let macro_arg = db.macro_arg(macro_file.macro_call_id)?; | 148 | let macro_arg = db.macro_arg(macro_file.macro_call_id)?; |
149 | 149 | ||
150 | Some(ExpansionInfo { | 150 | Some(ExpansionInfo { |
diff --git a/crates/hir_ty/Cargo.toml b/crates/hir_ty/Cargo.toml index fdc65a5c3..cf5c38a23 100644 --- a/crates/hir_ty/Cargo.toml +++ b/crates/hir_ty/Cargo.toml | |||
@@ -35,3 +35,4 @@ expect-test = "1.0" | |||
35 | tracing = "0.1" | 35 | tracing = "0.1" |
36 | tracing-subscriber = { version = "0.2", default-features = false, features = ["env-filter", "registry"] } | 36 | tracing-subscriber = { version = "0.2", default-features = false, features = ["env-filter", "registry"] } |
37 | tracing-tree = { version = "0.1.4" } | 37 | tracing-tree = { version = "0.1.4" } |
38 | once_cell = { version = "1.5.0", features = ["unstable"] } | ||
diff --git a/crates/hir_ty/src/diagnostics/match_check.rs b/crates/hir_ty/src/diagnostics/match_check.rs index 5bd03f2ac..62c329731 100644 --- a/crates/hir_ty/src/diagnostics/match_check.rs +++ b/crates/hir_ty/src/diagnostics/match_check.rs | |||
@@ -216,14 +216,14 @@ | |||
216 | //! U(P, p) := U(P, (r_1, p_2, .., p_n)) | 216 | //! U(P, p) := U(P, (r_1, p_2, .., p_n)) |
217 | //! || U(P, (r_2, p_2, .., p_n)) | 217 | //! || U(P, (r_2, p_2, .., p_n)) |
218 | //! ``` | 218 | //! ``` |
219 | use std::sync::Arc; | 219 | use std::{iter, sync::Arc}; |
220 | 220 | ||
221 | use arena::Idx; | 221 | use arena::Idx; |
222 | use hir_def::{ | 222 | use hir_def::{ |
223 | adt::VariantData, | 223 | adt::VariantData, |
224 | body::Body, | 224 | body::Body, |
225 | expr::{Expr, Literal, Pat, PatId}, | 225 | expr::{Expr, Literal, Pat, PatId}, |
226 | AdtId, EnumVariantId, VariantId, | 226 | AdtId, EnumVariantId, StructId, VariantId, |
227 | }; | 227 | }; |
228 | use smallvec::{smallvec, SmallVec}; | 228 | use smallvec::{smallvec, SmallVec}; |
229 | 229 | ||
@@ -366,16 +366,17 @@ impl PatStack { | |||
366 | 366 | ||
367 | let head_pat = head.as_pat(cx); | 367 | let head_pat = head.as_pat(cx); |
368 | let result = match (head_pat, constructor) { | 368 | let result = match (head_pat, constructor) { |
369 | (Pat::Tuple { args: ref pat_ids, ellipsis }, Constructor::Tuple { arity: _ }) => { | 369 | (Pat::Tuple { args: pat_ids, ellipsis }, &Constructor::Tuple { arity }) => { |
370 | if ellipsis.is_some() { | 370 | if let Some(ellipsis) = ellipsis { |
371 | // If there are ellipsis here, we should add the correct number of | 371 | let (pre, post) = pat_ids.split_at(ellipsis); |
372 | // Pat::Wild patterns to `pat_ids`. We should be able to use the | 372 | let n_wild_pats = arity.saturating_sub(pat_ids.len()); |
373 | // constructors arity for this, but at the time of writing we aren't | 373 | let pre_iter = pre.iter().map(Into::into); |
374 | // correctly calculating this arity when ellipsis are present. | 374 | let wildcards = iter::repeat(PatIdOrWild::Wild).take(n_wild_pats); |
375 | return Err(MatchCheckErr::NotImplemented); | 375 | let post_iter = post.iter().map(Into::into); |
376 | Some(self.replace_head_with(pre_iter.chain(wildcards).chain(post_iter))) | ||
377 | } else { | ||
378 | Some(self.replace_head_with(pat_ids.iter())) | ||
376 | } | 379 | } |
377 | |||
378 | Some(self.replace_head_with(pat_ids.iter())) | ||
379 | } | 380 | } |
380 | (Pat::Lit(lit_expr), Constructor::Bool(constructor_val)) => { | 381 | (Pat::Lit(lit_expr), Constructor::Bool(constructor_val)) => { |
381 | match cx.body.exprs[lit_expr] { | 382 | match cx.body.exprs[lit_expr] { |
@@ -390,21 +391,28 @@ impl PatStack { | |||
390 | } | 391 | } |
391 | } | 392 | } |
392 | (Pat::Wild, constructor) => Some(self.expand_wildcard(cx, constructor)?), | 393 | (Pat::Wild, constructor) => Some(self.expand_wildcard(cx, constructor)?), |
393 | (Pat::Path(_), Constructor::Enum(constructor)) => { | 394 | (Pat::Path(_), constructor) => { |
394 | // unit enum variants become `Pat::Path` | 395 | // unit enum variants become `Pat::Path` |
395 | let pat_id = head.as_id().expect("we know this isn't a wild"); | 396 | let pat_id = head.as_id().expect("we know this isn't a wild"); |
396 | if !enum_variant_matches(cx, pat_id, *constructor) { | 397 | let variant_id: VariantId = match constructor { |
398 | &Constructor::Enum(e) => e.into(), | ||
399 | &Constructor::Struct(s) => s.into(), | ||
400 | _ => return Err(MatchCheckErr::NotImplemented), | ||
401 | }; | ||
402 | if Some(variant_id) != cx.infer.variant_resolution_for_pat(pat_id) { | ||
397 | None | 403 | None |
398 | } else { | 404 | } else { |
399 | Some(self.to_tail()) | 405 | Some(self.to_tail()) |
400 | } | 406 | } |
401 | } | 407 | } |
402 | ( | 408 | (Pat::TupleStruct { args: ref pat_ids, ellipsis, .. }, constructor) => { |
403 | Pat::TupleStruct { args: ref pat_ids, ellipsis, .. }, | ||
404 | Constructor::Enum(enum_constructor), | ||
405 | ) => { | ||
406 | let pat_id = head.as_id().expect("we know this isn't a wild"); | 409 | let pat_id = head.as_id().expect("we know this isn't a wild"); |
407 | if !enum_variant_matches(cx, pat_id, *enum_constructor) { | 410 | let variant_id: VariantId = match constructor { |
411 | &Constructor::Enum(e) => e.into(), | ||
412 | &Constructor::Struct(s) => s.into(), | ||
413 | _ => return Err(MatchCheckErr::MalformedMatchArm), | ||
414 | }; | ||
415 | if Some(variant_id) != cx.infer.variant_resolution_for_pat(pat_id) { | ||
408 | None | 416 | None |
409 | } else { | 417 | } else { |
410 | let constructor_arity = constructor.arity(cx)?; | 418 | let constructor_arity = constructor.arity(cx)?; |
@@ -442,12 +450,22 @@ impl PatStack { | |||
442 | } | 450 | } |
443 | } | 451 | } |
444 | } | 452 | } |
445 | (Pat::Record { args: ref arg_patterns, .. }, Constructor::Enum(e)) => { | 453 | (Pat::Record { args: ref arg_patterns, .. }, constructor) => { |
446 | let pat_id = head.as_id().expect("we know this isn't a wild"); | 454 | let pat_id = head.as_id().expect("we know this isn't a wild"); |
447 | if !enum_variant_matches(cx, pat_id, *e) { | 455 | let (variant_id, variant_data) = match constructor { |
456 | &Constructor::Enum(e) => ( | ||
457 | e.into(), | ||
458 | cx.db.enum_data(e.parent).variants[e.local_id].variant_data.clone(), | ||
459 | ), | ||
460 | &Constructor::Struct(s) => { | ||
461 | (s.into(), cx.db.struct_data(s).variant_data.clone()) | ||
462 | } | ||
463 | _ => return Err(MatchCheckErr::MalformedMatchArm), | ||
464 | }; | ||
465 | if Some(variant_id) != cx.infer.variant_resolution_for_pat(pat_id) { | ||
448 | None | 466 | None |
449 | } else { | 467 | } else { |
450 | match cx.db.enum_data(e.parent).variants[e.local_id].variant_data.as_ref() { | 468 | match variant_data.as_ref() { |
451 | VariantData::Record(struct_field_arena) => { | 469 | VariantData::Record(struct_field_arena) => { |
452 | // Here we treat any missing fields in the record as the wild pattern, as | 470 | // Here we treat any missing fields in the record as the wild pattern, as |
453 | // if the record has ellipsis. We want to do this here even if the | 471 | // if the record has ellipsis. We want to do this here even if the |
@@ -726,6 +744,7 @@ enum Constructor { | |||
726 | Bool(bool), | 744 | Bool(bool), |
727 | Tuple { arity: usize }, | 745 | Tuple { arity: usize }, |
728 | Enum(EnumVariantId), | 746 | Enum(EnumVariantId), |
747 | Struct(StructId), | ||
729 | } | 748 | } |
730 | 749 | ||
731 | impl Constructor { | 750 | impl Constructor { |
@@ -740,6 +759,11 @@ impl Constructor { | |||
740 | VariantData::Unit => 0, | 759 | VariantData::Unit => 0, |
741 | } | 760 | } |
742 | } | 761 | } |
762 | &Constructor::Struct(s) => match cx.db.struct_data(s).variant_data.as_ref() { | ||
763 | VariantData::Tuple(struct_field_data) => struct_field_data.len(), | ||
764 | VariantData::Record(struct_field_data) => struct_field_data.len(), | ||
765 | VariantData::Unit => 0, | ||
766 | }, | ||
743 | }; | 767 | }; |
744 | 768 | ||
745 | Ok(arity) | 769 | Ok(arity) |
@@ -748,7 +772,7 @@ impl Constructor { | |||
748 | fn all_constructors(&self, cx: &MatchCheckCtx) -> Vec<Constructor> { | 772 | fn all_constructors(&self, cx: &MatchCheckCtx) -> Vec<Constructor> { |
749 | match self { | 773 | match self { |
750 | Constructor::Bool(_) => vec![Constructor::Bool(true), Constructor::Bool(false)], | 774 | Constructor::Bool(_) => vec![Constructor::Bool(true), Constructor::Bool(false)], |
751 | Constructor::Tuple { .. } => vec![*self], | 775 | Constructor::Tuple { .. } | Constructor::Struct(_) => vec![*self], |
752 | Constructor::Enum(e) => cx | 776 | Constructor::Enum(e) => cx |
753 | .db | 777 | .db |
754 | .enum_data(e.parent) | 778 | .enum_data(e.parent) |
@@ -767,10 +791,11 @@ impl Constructor { | |||
767 | fn pat_constructor(cx: &MatchCheckCtx, pat: PatIdOrWild) -> MatchCheckResult<Option<Constructor>> { | 791 | fn pat_constructor(cx: &MatchCheckCtx, pat: PatIdOrWild) -> MatchCheckResult<Option<Constructor>> { |
768 | let res = match pat.as_pat(cx) { | 792 | let res = match pat.as_pat(cx) { |
769 | Pat::Wild => None, | 793 | Pat::Wild => None, |
770 | // FIXME somehow create the Tuple constructor with the proper arity. If there are | 794 | Pat::Tuple { .. } => { |
771 | // ellipsis, the arity is not equal to the number of patterns. | 795 | let pat_id = pat.as_id().expect("we already know this pattern is not a wild"); |
772 | Pat::Tuple { args: pats, ellipsis } if ellipsis.is_none() => { | 796 | Some(Constructor::Tuple { |
773 | Some(Constructor::Tuple { arity: pats.len() }) | 797 | arity: cx.infer.type_of_pat[pat_id].as_tuple().ok_or(MatchCheckErr::Unknown)?.len(), |
798 | }) | ||
774 | } | 799 | } |
775 | Pat::Lit(lit_expr) => match cx.body.exprs[lit_expr] { | 800 | Pat::Lit(lit_expr) => match cx.body.exprs[lit_expr] { |
776 | Expr::Literal(Literal::Bool(val)) => Some(Constructor::Bool(val)), | 801 | Expr::Literal(Literal::Bool(val)) => Some(Constructor::Bool(val)), |
@@ -784,6 +809,7 @@ fn pat_constructor(cx: &MatchCheckCtx, pat: PatIdOrWild) -> MatchCheckResult<Opt | |||
784 | VariantId::EnumVariantId(enum_variant_id) => { | 809 | VariantId::EnumVariantId(enum_variant_id) => { |
785 | Some(Constructor::Enum(enum_variant_id)) | 810 | Some(Constructor::Enum(enum_variant_id)) |
786 | } | 811 | } |
812 | VariantId::StructId(struct_id) => Some(Constructor::Struct(struct_id)), | ||
787 | _ => return Err(MatchCheckErr::NotImplemented), | 813 | _ => return Err(MatchCheckErr::NotImplemented), |
788 | } | 814 | } |
789 | } | 815 | } |
@@ -828,13 +854,13 @@ fn all_constructors_covered( | |||
828 | 854 | ||
829 | false | 855 | false |
830 | }), | 856 | }), |
857 | &Constructor::Struct(s) => used_constructors.iter().any(|constructor| match constructor { | ||
858 | &Constructor::Struct(sid) => sid == s, | ||
859 | _ => false, | ||
860 | }), | ||
831 | } | 861 | } |
832 | } | 862 | } |
833 | 863 | ||
834 | fn enum_variant_matches(cx: &MatchCheckCtx, pat_id: PatId, enum_variant_id: EnumVariantId) -> bool { | ||
835 | Some(enum_variant_id.into()) == cx.infer.variant_resolution_for_pat(pat_id) | ||
836 | } | ||
837 | |||
838 | #[cfg(test)] | 864 | #[cfg(test)] |
839 | mod tests { | 865 | mod tests { |
840 | use crate::diagnostics::tests::check_diagnostics; | 866 | use crate::diagnostics::tests::check_diagnostics; |
@@ -846,8 +872,8 @@ mod tests { | |||
846 | fn main() { | 872 | fn main() { |
847 | match () { } | 873 | match () { } |
848 | //^^ Missing match arm | 874 | //^^ Missing match arm |
849 | match (()) { } | 875 | match (()) { } |
850 | //^^^^ Missing match arm | 876 | //^^^^ Missing match arm |
851 | 877 | ||
852 | match () { _ => (), } | 878 | match () { _ => (), } |
853 | match () { () => (), } | 879 | match () { () => (), } |
@@ -1352,6 +1378,123 @@ fn main() { | |||
1352 | ); | 1378 | ); |
1353 | } | 1379 | } |
1354 | 1380 | ||
1381 | #[test] | ||
1382 | fn tuple_of_bools_with_ellipsis_at_end_missing_arm() { | ||
1383 | check_diagnostics( | ||
1384 | r#" | ||
1385 | fn main() { | ||
1386 | match (false, true, false) { | ||
1387 | //^^^^^^^^^^^^^^^^^^^^ Missing match arm | ||
1388 | (false, ..) => (), | ||
1389 | } | ||
1390 | }"#, | ||
1391 | ); | ||
1392 | } | ||
1393 | |||
1394 | #[test] | ||
1395 | fn tuple_of_bools_with_ellipsis_at_beginning_missing_arm() { | ||
1396 | check_diagnostics( | ||
1397 | r#" | ||
1398 | fn main() { | ||
1399 | match (false, true, false) { | ||
1400 | //^^^^^^^^^^^^^^^^^^^^ Missing match arm | ||
1401 | (.., false) => (), | ||
1402 | } | ||
1403 | }"#, | ||
1404 | ); | ||
1405 | } | ||
1406 | |||
1407 | #[test] | ||
1408 | fn tuple_of_bools_with_ellipsis_in_middle_missing_arm() { | ||
1409 | check_diagnostics( | ||
1410 | r#" | ||
1411 | fn main() { | ||
1412 | match (false, true, false) { | ||
1413 | //^^^^^^^^^^^^^^^^^^^^ Missing match arm | ||
1414 | (true, .., false) => (), | ||
1415 | } | ||
1416 | }"#, | ||
1417 | ); | ||
1418 | } | ||
1419 | |||
1420 | #[test] | ||
1421 | fn record_struct() { | ||
1422 | check_diagnostics( | ||
1423 | r#"struct Foo { a: bool } | ||
1424 | fn main(f: Foo) { | ||
1425 | match f {} | ||
1426 | //^ Missing match arm | ||
1427 | match f { Foo { a: true } => () } | ||
1428 | //^ Missing match arm | ||
1429 | match &f { Foo { a: true } => () } | ||
1430 | //^^ Missing match arm | ||
1431 | match f { Foo { a: _ } => () } | ||
1432 | match f { | ||
1433 | Foo { a: true } => (), | ||
1434 | Foo { a: false } => (), | ||
1435 | } | ||
1436 | match &f { | ||
1437 | Foo { a: true } => (), | ||
1438 | Foo { a: false } => (), | ||
1439 | } | ||
1440 | } | ||
1441 | "#, | ||
1442 | ); | ||
1443 | } | ||
1444 | |||
1445 | #[test] | ||
1446 | fn tuple_struct() { | ||
1447 | check_diagnostics( | ||
1448 | r#"struct Foo(bool); | ||
1449 | fn main(f: Foo) { | ||
1450 | match f {} | ||
1451 | //^ Missing match arm | ||
1452 | match f { Foo(true) => () } | ||
1453 | //^ Missing match arm | ||
1454 | match f { | ||
1455 | Foo(true) => (), | ||
1456 | Foo(false) => (), | ||
1457 | } | ||
1458 | } | ||
1459 | "#, | ||
1460 | ); | ||
1461 | } | ||
1462 | |||
1463 | #[test] | ||
1464 | fn unit_struct() { | ||
1465 | check_diagnostics( | ||
1466 | r#"struct Foo; | ||
1467 | fn main(f: Foo) { | ||
1468 | match f {} | ||
1469 | //^ Missing match arm | ||
1470 | match f { Foo => () } | ||
1471 | } | ||
1472 | "#, | ||
1473 | ); | ||
1474 | } | ||
1475 | |||
1476 | #[test] | ||
1477 | fn record_struct_ellipsis() { | ||
1478 | check_diagnostics( | ||
1479 | r#"struct Foo { foo: bool, bar: bool } | ||
1480 | fn main(f: Foo) { | ||
1481 | match f { Foo { foo: true, .. } => () } | ||
1482 | //^ Missing match arm | ||
1483 | match f { | ||
1484 | //^ Missing match arm | ||
1485 | Foo { foo: true, .. } => (), | ||
1486 | Foo { bar: false, .. } => () | ||
1487 | } | ||
1488 | match f { Foo { .. } => () } | ||
1489 | match f { | ||
1490 | Foo { foo: true, .. } => (), | ||
1491 | Foo { foo: false, .. } => () | ||
1492 | } | ||
1493 | } | ||
1494 | "#, | ||
1495 | ); | ||
1496 | } | ||
1497 | |||
1355 | mod false_negatives { | 1498 | mod false_negatives { |
1356 | //! The implementation of match checking here is a work in progress. As we roll this out, we | 1499 | //! The implementation of match checking here is a work in progress. As we roll this out, we |
1357 | //! prefer false negatives to false positives (ideally there would be no false positives). This | 1500 | //! prefer false negatives to false positives (ideally there would be no false positives). This |
@@ -1393,46 +1536,5 @@ fn main() { | |||
1393 | "#, | 1536 | "#, |
1394 | ); | 1537 | ); |
1395 | } | 1538 | } |
1396 | |||
1397 | #[test] | ||
1398 | fn tuple_of_bools_with_ellipsis_at_end_missing_arm() { | ||
1399 | // We don't currently handle tuple patterns with ellipsis. | ||
1400 | check_diagnostics( | ||
1401 | r#" | ||
1402 | fn main() { | ||
1403 | match (false, true, false) { | ||
1404 | (false, ..) => (), | ||
1405 | } | ||
1406 | } | ||
1407 | "#, | ||
1408 | ); | ||
1409 | } | ||
1410 | |||
1411 | #[test] | ||
1412 | fn tuple_of_bools_with_ellipsis_at_beginning_missing_arm() { | ||
1413 | // We don't currently handle tuple patterns with ellipsis. | ||
1414 | check_diagnostics( | ||
1415 | r#" | ||
1416 | fn main() { | ||
1417 | match (false, true, false) { | ||
1418 | (.., false) => (), | ||
1419 | } | ||
1420 | } | ||
1421 | "#, | ||
1422 | ); | ||
1423 | } | ||
1424 | |||
1425 | #[test] | ||
1426 | fn struct_missing_arm() { | ||
1427 | // We don't currently handle structs. | ||
1428 | check_diagnostics( | ||
1429 | r#" | ||
1430 | struct Foo { a: bool } | ||
1431 | fn main(f: Foo) { | ||
1432 | match f { Foo { a: true } => () } | ||
1433 | } | ||
1434 | "#, | ||
1435 | ); | ||
1436 | } | ||
1437 | } | 1539 | } |
1438 | } | 1540 | } |
diff --git a/crates/hir_ty/src/infer/pat.rs b/crates/hir_ty/src/infer/pat.rs index cde2ab82b..b70ec55eb 100644 --- a/crates/hir_ty/src/infer/pat.rs +++ b/crates/hir_ty/src/infer/pat.rs | |||
@@ -23,6 +23,7 @@ impl<'a> InferenceContext<'a> { | |||
23 | expected: &Ty, | 23 | expected: &Ty, |
24 | default_bm: BindingMode, | 24 | default_bm: BindingMode, |
25 | id: PatId, | 25 | id: PatId, |
26 | ellipsis: Option<usize>, | ||
26 | ) -> Ty { | 27 | ) -> Ty { |
27 | let (ty, def) = self.resolve_variant(path); | 28 | let (ty, def) = self.resolve_variant(path); |
28 | let var_data = def.map(|it| variant_data(self.db.upcast(), it)); | 29 | let var_data = def.map(|it| variant_data(self.db.upcast(), it)); |
@@ -34,8 +35,15 @@ impl<'a> InferenceContext<'a> { | |||
34 | let substs = ty.substs().unwrap_or_else(Substs::empty); | 35 | let substs = ty.substs().unwrap_or_else(Substs::empty); |
35 | 36 | ||
36 | let field_tys = def.map(|it| self.db.field_types(it)).unwrap_or_default(); | 37 | let field_tys = def.map(|it| self.db.field_types(it)).unwrap_or_default(); |
38 | let (pre, post) = match ellipsis { | ||
39 | Some(idx) => subpats.split_at(idx), | ||
40 | None => (&subpats[..], &[][..]), | ||
41 | }; | ||
42 | let post_idx_offset = field_tys.iter().count() - post.len(); | ||
37 | 43 | ||
38 | for (i, &subpat) in subpats.iter().enumerate() { | 44 | let pre_iter = pre.iter().enumerate(); |
45 | let post_iter = (post_idx_offset..).zip(post.iter()); | ||
46 | for (i, &subpat) in pre_iter.chain(post_iter) { | ||
39 | let expected_ty = var_data | 47 | let expected_ty = var_data |
40 | .as_ref() | 48 | .as_ref() |
41 | .and_then(|d| d.field(&Name::new_tuple_field(i))) | 49 | .and_then(|d| d.field(&Name::new_tuple_field(i))) |
@@ -111,20 +119,29 @@ impl<'a> InferenceContext<'a> { | |||
111 | let expected = expected; | 119 | let expected = expected; |
112 | 120 | ||
113 | let ty = match &body[pat] { | 121 | let ty = match &body[pat] { |
114 | Pat::Tuple { ref args, .. } => { | 122 | &Pat::Tuple { ref args, ellipsis } => { |
115 | let expectations = match expected.as_tuple() { | 123 | let expectations = match expected.as_tuple() { |
116 | Some(parameters) => &*parameters.0, | 124 | Some(parameters) => &*parameters.0, |
117 | _ => &[], | 125 | _ => &[], |
118 | }; | 126 | }; |
119 | let expectations_iter = expectations.iter().chain(repeat(&Ty::Unknown)); | ||
120 | 127 | ||
121 | let inner_tys = args | 128 | let (pre, post) = match ellipsis { |
122 | .iter() | 129 | Some(idx) => args.split_at(idx), |
123 | .zip(expectations_iter) | 130 | None => (&args[..], &[][..]), |
124 | .map(|(&pat, ty)| self.infer_pat(pat, ty, default_bm)) | 131 | }; |
125 | .collect(); | 132 | let n_uncovered_patterns = expectations.len().saturating_sub(args.len()); |
133 | let mut expectations_iter = expectations.iter().chain(repeat(&Ty::Unknown)); | ||
134 | let mut infer_pat = |(&pat, ty)| self.infer_pat(pat, ty, default_bm); | ||
135 | |||
136 | let mut inner_tys = Vec::with_capacity(n_uncovered_patterns + args.len()); | ||
137 | inner_tys.extend(pre.iter().zip(expectations_iter.by_ref()).map(&mut infer_pat)); | ||
138 | inner_tys.extend(expectations_iter.by_ref().take(n_uncovered_patterns).cloned()); | ||
139 | inner_tys.extend(post.iter().zip(expectations_iter).map(infer_pat)); | ||
126 | 140 | ||
127 | Ty::apply(TypeCtor::Tuple { cardinality: args.len() as u16 }, Substs(inner_tys)) | 141 | Ty::apply( |
142 | TypeCtor::Tuple { cardinality: inner_tys.len() as u16 }, | ||
143 | Substs(inner_tys.into()), | ||
144 | ) | ||
128 | } | 145 | } |
129 | Pat::Or(ref pats) => { | 146 | Pat::Or(ref pats) => { |
130 | if let Some((first_pat, rest)) = pats.split_first() { | 147 | if let Some((first_pat, rest)) = pats.split_first() { |
@@ -150,9 +167,14 @@ impl<'a> InferenceContext<'a> { | |||
150 | let subty = self.infer_pat(*pat, expectation, default_bm); | 167 | let subty = self.infer_pat(*pat, expectation, default_bm); |
151 | Ty::apply_one(TypeCtor::Ref(*mutability), subty) | 168 | Ty::apply_one(TypeCtor::Ref(*mutability), subty) |
152 | } | 169 | } |
153 | Pat::TupleStruct { path: p, args: subpats, .. } => { | 170 | Pat::TupleStruct { path: p, args: subpats, ellipsis } => self.infer_tuple_struct_pat( |
154 | self.infer_tuple_struct_pat(p.as_ref(), subpats, expected, default_bm, pat) | 171 | p.as_ref(), |
155 | } | 172 | subpats, |
173 | expected, | ||
174 | default_bm, | ||
175 | pat, | ||
176 | *ellipsis, | ||
177 | ), | ||
156 | Pat::Record { path: p, args: fields, ellipsis: _ } => { | 178 | Pat::Record { path: p, args: fields, ellipsis: _ } => { |
157 | self.infer_record_pat(p.as_ref(), fields, expected, default_bm, pat) | 179 | self.infer_record_pat(p.as_ref(), fields, expected, default_bm, pat) |
158 | } | 180 | } |
diff --git a/crates/hir_ty/src/tests.rs b/crates/hir_ty/src/tests.rs index 104ef334c..0a400cb70 100644 --- a/crates/hir_ty/src/tests.rs +++ b/crates/hir_ty/src/tests.rs | |||
@@ -22,7 +22,8 @@ use hir_def::{ | |||
22 | AssocItemId, DefWithBodyId, LocalModuleId, Lookup, ModuleDefId, | 22 | AssocItemId, DefWithBodyId, LocalModuleId, Lookup, ModuleDefId, |
23 | }; | 23 | }; |
24 | use hir_expand::{db::AstDatabase, InFile}; | 24 | use hir_expand::{db::AstDatabase, InFile}; |
25 | use stdx::{format_to, RacyFlag}; | 25 | use once_cell::race::OnceBool; |
26 | use stdx::format_to; | ||
26 | use syntax::{ | 27 | use syntax::{ |
27 | algo, | 28 | algo, |
28 | ast::{self, AstNode}, | 29 | ast::{self, AstNode}, |
@@ -40,8 +41,8 @@ use crate::{ | |||
40 | // `env UPDATE_EXPECT=1 cargo test -p hir_ty` to update the snapshots. | 41 | // `env UPDATE_EXPECT=1 cargo test -p hir_ty` to update the snapshots. |
41 | 42 | ||
42 | fn setup_tracing() -> Option<tracing::subscriber::DefaultGuard> { | 43 | fn setup_tracing() -> Option<tracing::subscriber::DefaultGuard> { |
43 | static ENABLE: RacyFlag = RacyFlag::new(); | 44 | static ENABLE: OnceBool = OnceBool::new(); |
44 | if !ENABLE.get(|| env::var("CHALK_DEBUG").is_ok()) { | 45 | if !ENABLE.get_or_init(|| env::var("CHALK_DEBUG").is_ok()) { |
45 | return None; | 46 | return None; |
46 | } | 47 | } |
47 | 48 | ||
diff --git a/crates/hir_ty/src/tests/patterns.rs b/crates/hir_ty/src/tests/patterns.rs index 6a965ac4f..5a5f48fd0 100644 --- a/crates/hir_ty/src/tests/patterns.rs +++ b/crates/hir_ty/src/tests/patterns.rs | |||
@@ -679,3 +679,98 @@ fn box_pattern() { | |||
679 | "#]], | 679 | "#]], |
680 | ); | 680 | ); |
681 | } | 681 | } |
682 | |||
683 | #[test] | ||
684 | fn tuple_ellipsis_pattern() { | ||
685 | check_infer( | ||
686 | r#" | ||
687 | fn foo(tuple: (u8, i16, f32)) { | ||
688 | match tuple { | ||
689 | (.., b, c) => {}, | ||
690 | (a, .., c) => {}, | ||
691 | (a, b, ..) => {}, | ||
692 | (a, b) => {/*too short*/} | ||
693 | (a, b, c, d) => {/*too long*/} | ||
694 | _ => {} | ||
695 | } | ||
696 | }"#, | ||
697 | expect![[r#" | ||
698 | 7..12 'tuple': (u8, i16, f32) | ||
699 | 30..224 '{ ... } }': () | ||
700 | 36..222 'match ... }': () | ||
701 | 42..47 'tuple': (u8, i16, f32) | ||
702 | 58..68 '(.., b, c)': (u8, i16, f32) | ||
703 | 63..64 'b': i16 | ||
704 | 66..67 'c': f32 | ||
705 | 72..74 '{}': () | ||
706 | 84..94 '(a, .., c)': (u8, i16, f32) | ||
707 | 85..86 'a': u8 | ||
708 | 92..93 'c': f32 | ||
709 | 98..100 '{}': () | ||
710 | 110..120 '(a, b, ..)': (u8, i16, f32) | ||
711 | 111..112 'a': u8 | ||
712 | 114..115 'b': i16 | ||
713 | 124..126 '{}': () | ||
714 | 136..142 '(a, b)': (u8, i16, f32) | ||
715 | 137..138 'a': u8 | ||
716 | 140..141 'b': i16 | ||
717 | 146..161 '{/*too short*/}': () | ||
718 | 170..182 '(a, b, c, d)': (u8, i16, f32, {unknown}) | ||
719 | 171..172 'a': u8 | ||
720 | 174..175 'b': i16 | ||
721 | 177..178 'c': f32 | ||
722 | 180..181 'd': {unknown} | ||
723 | 186..200 '{/*too long*/}': () | ||
724 | 209..210 '_': (u8, i16, f32) | ||
725 | 214..216 '{}': () | ||
726 | "#]], | ||
727 | ); | ||
728 | } | ||
729 | |||
730 | #[test] | ||
731 | fn tuple_struct_ellipsis_pattern() { | ||
732 | check_infer( | ||
733 | r#" | ||
734 | struct Tuple(u8, i16, f32); | ||
735 | fn foo(tuple: Tuple) { | ||
736 | match tuple { | ||
737 | Tuple(.., b, c) => {}, | ||
738 | Tuple(a, .., c) => {}, | ||
739 | Tuple(a, b, ..) => {}, | ||
740 | Tuple(a, b) => {/*too short*/} | ||
741 | Tuple(a, b, c, d) => {/*too long*/} | ||
742 | _ => {} | ||
743 | } | ||
744 | }"#, | ||
745 | expect![[r#" | ||
746 | 35..40 'tuple': Tuple | ||
747 | 49..268 '{ ... } }': () | ||
748 | 55..266 'match ... }': () | ||
749 | 61..66 'tuple': Tuple | ||
750 | 77..92 'Tuple(.., b, c)': Tuple | ||
751 | 87..88 'b': i16 | ||
752 | 90..91 'c': f32 | ||
753 | 96..98 '{}': () | ||
754 | 108..123 'Tuple(a, .., c)': Tuple | ||
755 | 114..115 'a': u8 | ||
756 | 121..122 'c': f32 | ||
757 | 127..129 '{}': () | ||
758 | 139..154 'Tuple(a, b, ..)': Tuple | ||
759 | 145..146 'a': u8 | ||
760 | 148..149 'b': i16 | ||
761 | 158..160 '{}': () | ||
762 | 170..181 'Tuple(a, b)': Tuple | ||
763 | 176..177 'a': u8 | ||
764 | 179..180 'b': i16 | ||
765 | 185..200 '{/*too short*/}': () | ||
766 | 209..226 'Tuple(... c, d)': Tuple | ||
767 | 215..216 'a': u8 | ||
768 | 218..219 'b': i16 | ||
769 | 221..222 'c': f32 | ||
770 | 224..225 'd': {unknown} | ||
771 | 230..244 '{/*too long*/}': () | ||
772 | 253..254 '_': Tuple | ||
773 | 258..260 '{}': () | ||
774 | "#]], | ||
775 | ); | ||
776 | } | ||
diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs index 1c7f02763..3df73ed4f 100644 --- a/crates/ide/src/diagnostics.rs +++ b/crates/ide/src/diagnostics.rs | |||
@@ -10,7 +10,7 @@ mod field_shorthand; | |||
10 | use std::cell::RefCell; | 10 | use std::cell::RefCell; |
11 | 11 | ||
12 | use hir::{ | 12 | use hir::{ |
13 | diagnostics::{Diagnostic as _, DiagnosticSinkBuilder}, | 13 | diagnostics::{Diagnostic as _, DiagnosticCode, DiagnosticSinkBuilder}, |
14 | Semantics, | 14 | Semantics, |
15 | }; | 15 | }; |
16 | use ide_db::base_db::SourceDatabase; | 16 | use ide_db::base_db::SourceDatabase; |
@@ -35,15 +35,23 @@ pub struct Diagnostic { | |||
35 | pub severity: Severity, | 35 | pub severity: Severity, |
36 | pub fix: Option<Fix>, | 36 | pub fix: Option<Fix>, |
37 | pub unused: bool, | 37 | pub unused: bool, |
38 | pub code: Option<DiagnosticCode>, | ||
38 | } | 39 | } |
39 | 40 | ||
40 | impl Diagnostic { | 41 | impl Diagnostic { |
41 | fn error(range: TextRange, message: String) -> Self { | 42 | fn error(range: TextRange, message: String) -> Self { |
42 | Self { message, range, severity: Severity::Error, fix: None, unused: false } | 43 | Self { message, range, severity: Severity::Error, fix: None, unused: false, code: None } |
43 | } | 44 | } |
44 | 45 | ||
45 | fn hint(range: TextRange, message: String) -> Self { | 46 | fn hint(range: TextRange, message: String) -> Self { |
46 | Self { message, range, severity: Severity::WeakWarning, fix: None, unused: false } | 47 | Self { |
48 | message, | ||
49 | range, | ||
50 | severity: Severity::WeakWarning, | ||
51 | fix: None, | ||
52 | unused: false, | ||
53 | code: None, | ||
54 | } | ||
47 | } | 55 | } |
48 | 56 | ||
49 | fn with_fix(self, fix: Option<Fix>) -> Self { | 57 | fn with_fix(self, fix: Option<Fix>) -> Self { |
@@ -53,6 +61,10 @@ impl Diagnostic { | |||
53 | fn with_unused(self, unused: bool) -> Self { | 61 | fn with_unused(self, unused: bool) -> Self { |
54 | Self { unused, ..self } | 62 | Self { unused, ..self } |
55 | } | 63 | } |
64 | |||
65 | fn with_code(self, code: Option<DiagnosticCode>) -> Self { | ||
66 | Self { code, ..self } | ||
67 | } | ||
56 | } | 68 | } |
57 | 69 | ||
58 | #[derive(Debug)] | 70 | #[derive(Debug)] |
@@ -126,7 +138,8 @@ pub(crate) fn diagnostics( | |||
126 | // Override severity and mark as unused. | 138 | // Override severity and mark as unused. |
127 | res.borrow_mut().push( | 139 | res.borrow_mut().push( |
128 | Diagnostic::hint(sema.diagnostics_display_range(d).range, d.message()) | 140 | Diagnostic::hint(sema.diagnostics_display_range(d).range, d.message()) |
129 | .with_unused(true), | 141 | .with_unused(true) |
142 | .with_code(Some(d.code())), | ||
130 | ); | 143 | ); |
131 | }) | 144 | }) |
132 | // Only collect experimental diagnostics when they're enabled. | 145 | // Only collect experimental diagnostics when they're enabled. |
@@ -137,8 +150,10 @@ pub(crate) fn diagnostics( | |||
137 | let mut sink = sink_builder | 150 | let mut sink = sink_builder |
138 | // Diagnostics not handled above get no fix and default treatment. | 151 | // Diagnostics not handled above get no fix and default treatment. |
139 | .build(|d| { | 152 | .build(|d| { |
140 | res.borrow_mut() | 153 | res.borrow_mut().push( |
141 | .push(Diagnostic::error(sema.diagnostics_display_range(d).range, d.message())); | 154 | Diagnostic::error(sema.diagnostics_display_range(d).range, d.message()) |
155 | .with_code(Some(d.code())), | ||
156 | ); | ||
142 | }); | 157 | }); |
143 | 158 | ||
144 | if let Some(m) = sema.to_module_def(file_id) { | 159 | if let Some(m) = sema.to_module_def(file_id) { |
@@ -149,11 +164,15 @@ pub(crate) fn diagnostics( | |||
149 | } | 164 | } |
150 | 165 | ||
151 | fn diagnostic_with_fix<D: DiagnosticWithFix>(d: &D, sema: &Semantics<RootDatabase>) -> Diagnostic { | 166 | fn diagnostic_with_fix<D: DiagnosticWithFix>(d: &D, sema: &Semantics<RootDatabase>) -> Diagnostic { |
152 | Diagnostic::error(sema.diagnostics_display_range(d).range, d.message()).with_fix(d.fix(&sema)) | 167 | Diagnostic::error(sema.diagnostics_display_range(d).range, d.message()) |
168 | .with_fix(d.fix(&sema)) | ||
169 | .with_code(Some(d.code())) | ||
153 | } | 170 | } |
154 | 171 | ||
155 | fn warning_with_fix<D: DiagnosticWithFix>(d: &D, sema: &Semantics<RootDatabase>) -> Diagnostic { | 172 | fn warning_with_fix<D: DiagnosticWithFix>(d: &D, sema: &Semantics<RootDatabase>) -> Diagnostic { |
156 | Diagnostic::hint(sema.diagnostics_display_range(d).range, d.message()).with_fix(d.fix(&sema)) | 173 | Diagnostic::hint(sema.diagnostics_display_range(d).range, d.message()) |
174 | .with_fix(d.fix(&sema)) | ||
175 | .with_code(Some(d.code())) | ||
157 | } | 176 | } |
158 | 177 | ||
159 | fn check_unnecessary_braces_in_use_statement( | 178 | fn check_unnecessary_braces_in_use_statement( |
@@ -589,6 +608,11 @@ fn test_fn() { | |||
589 | }, | 608 | }, |
590 | ), | 609 | ), |
591 | unused: false, | 610 | unused: false, |
611 | code: Some( | ||
612 | DiagnosticCode( | ||
613 | "unresolved-module", | ||
614 | ), | ||
615 | ), | ||
592 | }, | 616 | }, |
593 | ] | 617 | ] |
594 | "#]], | 618 | "#]], |
diff --git a/crates/ide/src/fn_references.rs b/crates/ide/src/fn_references.rs index 459f201ed..5cbbe306e 100644 --- a/crates/ide/src/fn_references.rs +++ b/crates/ide/src/fn_references.rs | |||
@@ -1,11 +1,12 @@ | |||
1 | //! This module implements a methods and free functions search in the specified file. | 1 | //! This module implements a methods and free functions search in the specified file. |
2 | //! We have to skip tests, so cannot reuse file_structure module. | 2 | //! We have to skip tests, so cannot reuse file_structure module. |
3 | 3 | ||
4 | use assists::utils::test_related_attribute; | ||
4 | use hir::Semantics; | 5 | use hir::Semantics; |
5 | use ide_db::RootDatabase; | 6 | use ide_db::RootDatabase; |
6 | use syntax::{ast, ast::NameOwner, AstNode, SyntaxNode}; | 7 | use syntax::{ast, ast::NameOwner, AstNode, SyntaxNode}; |
7 | 8 | ||
8 | use crate::{runnables::has_test_related_attribute, FileId, FileRange}; | 9 | use crate::{FileId, FileRange}; |
9 | 10 | ||
10 | pub(crate) fn find_all_methods(db: &RootDatabase, file_id: FileId) -> Vec<FileRange> { | 11 | pub(crate) fn find_all_methods(db: &RootDatabase, file_id: FileId) -> Vec<FileRange> { |
11 | let sema = Semantics::new(db); | 12 | let sema = Semantics::new(db); |
@@ -15,7 +16,7 @@ pub(crate) fn find_all_methods(db: &RootDatabase, file_id: FileId) -> Vec<FileRa | |||
15 | 16 | ||
16 | fn method_range(item: SyntaxNode, file_id: FileId) -> Option<FileRange> { | 17 | fn method_range(item: SyntaxNode, file_id: FileId) -> Option<FileRange> { |
17 | ast::Fn::cast(item).and_then(|fn_def| { | 18 | ast::Fn::cast(item).and_then(|fn_def| { |
18 | if has_test_related_attribute(&fn_def) { | 19 | if test_related_attribute(&fn_def).is_some() { |
19 | None | 20 | None |
20 | } else { | 21 | } else { |
21 | fn_def.name().map(|name| FileRange { file_id, range: name.syntax().text_range() }) | 22 | fn_def.name().map(|name| FileRange { file_id, range: name.syntax().text_range() }) |
diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs index e05465b32..5693dd400 100644 --- a/crates/ide/src/references.rs +++ b/crates/ide/src/references.rs | |||
@@ -110,14 +110,23 @@ pub(crate) fn find_all_refs( | |||
110 | .filter(|r| search_kind == ReferenceKind::Other || search_kind == r.kind) | 110 | .filter(|r| search_kind == ReferenceKind::Other || search_kind == r.kind) |
111 | .collect(); | 111 | .collect(); |
112 | 112 | ||
113 | let decl_range = def.try_to_nav(sema.db)?.focus_or_full_range(); | 113 | let nav = def.try_to_nav(sema.db)?; |
114 | 114 | let decl_range = nav.focus_or_full_range(); | |
115 | let declaration = Declaration { | 115 | |
116 | nav: def.try_to_nav(sema.db)?, | 116 | let mut kind = ReferenceKind::Other; |
117 | kind: ReferenceKind::Other, | 117 | if let Definition::Local(local) = def { |
118 | access: decl_access(&def, &syntax, decl_range), | 118 | if let either::Either::Left(pat) = local.source(sema.db).value { |
119 | if matches!( | ||
120 | pat.syntax().parent().and_then(ast::RecordPatField::cast), | ||
121 | Some(pat_field) if pat_field.name_ref().is_none() | ||
122 | ) { | ||
123 | kind = ReferenceKind::FieldShorthandForLocal; | ||
124 | } | ||
125 | } | ||
119 | }; | 126 | }; |
120 | 127 | ||
128 | let declaration = Declaration { nav, kind, access: decl_access(&def, &syntax, decl_range) }; | ||
129 | |||
121 | Some(RangeInfo::new(range, ReferenceSearchResult { declaration, references })) | 130 | Some(RangeInfo::new(range, ReferenceSearchResult { declaration, references })) |
122 | } | 131 | } |
123 | 132 | ||
@@ -613,7 +622,7 @@ fn foo() { | |||
613 | expect![[r#" | 622 | expect![[r#" |
614 | f RECORD_FIELD FileId(0) 15..21 15..16 Other | 623 | f RECORD_FIELD FileId(0) 15..21 15..16 Other |
615 | 624 | ||
616 | FileId(0) 55..56 Other Read | 625 | FileId(0) 55..56 RecordFieldExprOrPat Read |
617 | FileId(0) 68..69 Other Write | 626 | FileId(0) 68..69 Other Write |
618 | "#]], | 627 | "#]], |
619 | ); | 628 | ); |
@@ -748,7 +757,7 @@ fn f() -> m::En { | |||
748 | expect![[r#" | 757 | expect![[r#" |
749 | field RECORD_FIELD FileId(0) 56..65 56..61 Other | 758 | field RECORD_FIELD FileId(0) 56..65 56..61 Other |
750 | 759 | ||
751 | FileId(0) 125..130 Other Read | 760 | FileId(0) 125..130 RecordFieldExprOrPat Read |
752 | "#]], | 761 | "#]], |
753 | ); | 762 | ); |
754 | } | 763 | } |
diff --git a/crates/ide/src/references/rename.rs b/crates/ide/src/references/rename.rs index 26ac2371a..91c64bd4a 100644 --- a/crates/ide/src/references/rename.rs +++ b/crates/ide/src/references/rename.rs | |||
@@ -1,17 +1,16 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | use std::{ | ||
3 | convert::TryInto, | ||
4 | error::Error, | ||
5 | fmt::{self, Display}, | ||
6 | }; | ||
2 | 7 | ||
3 | use hir::{Module, ModuleDef, ModuleSource, Semantics}; | 8 | use hir::{Module, ModuleDef, ModuleSource, Semantics}; |
4 | use ide_db::base_db::SourceDatabaseExt; | 9 | use ide_db::base_db::{FileRange, SourceDatabaseExt}; |
5 | use ide_db::{ | 10 | use ide_db::{ |
6 | defs::{Definition, NameClass, NameRefClass}, | 11 | defs::{Definition, NameClass, NameRefClass}, |
7 | RootDatabase, | 12 | RootDatabase, |
8 | }; | 13 | }; |
9 | |||
10 | use std::{ | ||
11 | convert::TryInto, | ||
12 | error::Error, | ||
13 | fmt::{self, Display}, | ||
14 | }; | ||
15 | use syntax::{ | 14 | use syntax::{ |
16 | algo::find_node_at_offset, | 15 | algo::find_node_at_offset, |
17 | ast::{self, NameOwner}, | 16 | ast::{self, NameOwner}, |
@@ -106,9 +105,12 @@ fn find_module_at_offset( | |||
106 | Some(module) | 105 | Some(module) |
107 | } | 106 | } |
108 | 107 | ||
109 | fn source_edit_from_reference(reference: Reference, new_name: &str) -> SourceFileEdit { | 108 | fn source_edit_from_reference( |
109 | sema: &Semantics<RootDatabase>, | ||
110 | reference: Reference, | ||
111 | new_name: &str, | ||
112 | ) -> SourceFileEdit { | ||
110 | let mut replacement_text = String::new(); | 113 | let mut replacement_text = String::new(); |
111 | let file_id = reference.file_range.file_id; | ||
112 | let range = match reference.kind { | 114 | let range = match reference.kind { |
113 | ReferenceKind::FieldShorthandForField => { | 115 | ReferenceKind::FieldShorthandForField => { |
114 | mark::hit!(test_rename_struct_field_for_shorthand); | 116 | mark::hit!(test_rename_struct_field_for_shorthand); |
@@ -122,12 +124,48 @@ fn source_edit_from_reference(reference: Reference, new_name: &str) -> SourceFil | |||
122 | replacement_text.push_str(new_name); | 124 | replacement_text.push_str(new_name); |
123 | TextRange::new(reference.file_range.range.end(), reference.file_range.range.end()) | 125 | TextRange::new(reference.file_range.range.end(), reference.file_range.range.end()) |
124 | } | 126 | } |
127 | ReferenceKind::RecordFieldExprOrPat => { | ||
128 | mark::hit!(test_rename_field_expr_pat); | ||
129 | replacement_text.push_str(new_name); | ||
130 | edit_text_range_for_record_field_expr_or_pat(sema, reference.file_range, new_name) | ||
131 | } | ||
125 | _ => { | 132 | _ => { |
126 | replacement_text.push_str(new_name); | 133 | replacement_text.push_str(new_name); |
127 | reference.file_range.range | 134 | reference.file_range.range |
128 | } | 135 | } |
129 | }; | 136 | }; |
130 | SourceFileEdit { file_id, edit: TextEdit::replace(range, replacement_text) } | 137 | SourceFileEdit { |
138 | file_id: reference.file_range.file_id, | ||
139 | edit: TextEdit::replace(range, replacement_text), | ||
140 | } | ||
141 | } | ||
142 | |||
143 | fn edit_text_range_for_record_field_expr_or_pat( | ||
144 | sema: &Semantics<RootDatabase>, | ||
145 | file_range: FileRange, | ||
146 | new_name: &str, | ||
147 | ) -> TextRange { | ||
148 | let source_file = sema.parse(file_range.file_id); | ||
149 | let file_syntax = source_file.syntax(); | ||
150 | let original_range = file_range.range; | ||
151 | |||
152 | syntax::algo::find_node_at_range::<ast::RecordExprField>(file_syntax, original_range) | ||
153 | .and_then(|field_expr| match field_expr.expr().and_then(|e| e.name_ref()) { | ||
154 | Some(name) if &name.to_string() == new_name => Some(field_expr.syntax().text_range()), | ||
155 | _ => None, | ||
156 | }) | ||
157 | .or_else(|| { | ||
158 | syntax::algo::find_node_at_range::<ast::RecordPatField>(file_syntax, original_range) | ||
159 | .and_then(|field_pat| match field_pat.pat() { | ||
160 | Some(ast::Pat::IdentPat(pat)) | ||
161 | if pat.name().map(|n| n.to_string()).as_deref() == Some(new_name) => | ||
162 | { | ||
163 | Some(field_pat.syntax().text_range()) | ||
164 | } | ||
165 | _ => None, | ||
166 | }) | ||
167 | }) | ||
168 | .unwrap_or(original_range) | ||
131 | } | 169 | } |
132 | 170 | ||
133 | fn rename_mod( | 171 | fn rename_mod( |
@@ -170,7 +208,7 @@ fn rename_mod( | |||
170 | let ref_edits = refs | 208 | let ref_edits = refs |
171 | .references | 209 | .references |
172 | .into_iter() | 210 | .into_iter() |
173 | .map(|reference| source_edit_from_reference(reference, new_name)); | 211 | .map(|reference| source_edit_from_reference(sema, reference, new_name)); |
174 | source_file_edits.extend(ref_edits); | 212 | source_file_edits.extend(ref_edits); |
175 | 213 | ||
176 | Ok(RangeInfo::new(range, SourceChange::from_edits(source_file_edits, file_system_edits))) | 214 | Ok(RangeInfo::new(range, SourceChange::from_edits(source_file_edits, file_system_edits))) |
@@ -211,7 +249,7 @@ fn rename_to_self( | |||
211 | 249 | ||
212 | let mut edits = usages | 250 | let mut edits = usages |
213 | .into_iter() | 251 | .into_iter() |
214 | .map(|reference| source_edit_from_reference(reference, "self")) | 252 | .map(|reference| source_edit_from_reference(sema, reference, "self")) |
215 | .collect::<Vec<_>>(); | 253 | .collect::<Vec<_>>(); |
216 | 254 | ||
217 | edits.push(SourceFileEdit { | 255 | edits.push(SourceFileEdit { |
@@ -300,7 +338,7 @@ fn rename_reference( | |||
300 | 338 | ||
301 | let edit = refs | 339 | let edit = refs |
302 | .into_iter() | 340 | .into_iter() |
303 | .map(|reference| source_edit_from_reference(reference, new_name)) | 341 | .map(|reference| source_edit_from_reference(sema, reference, new_name)) |
304 | .collect::<Vec<_>>(); | 342 | .collect::<Vec<_>>(); |
305 | 343 | ||
306 | if edit.is_empty() { | 344 | if edit.is_empty() { |
@@ -1097,4 +1135,116 @@ impl Foo { | |||
1097 | "#, | 1135 | "#, |
1098 | ); | 1136 | ); |
1099 | } | 1137 | } |
1138 | |||
1139 | #[test] | ||
1140 | fn test_initializer_use_field_init_shorthand() { | ||
1141 | mark::check!(test_rename_field_expr_pat); | ||
1142 | check( | ||
1143 | "bar", | ||
1144 | r#" | ||
1145 | struct Foo { i<|>: i32 } | ||
1146 | |||
1147 | fn foo(bar: i32) -> Foo { | ||
1148 | Foo { i: bar } | ||
1149 | } | ||
1150 | "#, | ||
1151 | r#" | ||
1152 | struct Foo { bar: i32 } | ||
1153 | |||
1154 | fn foo(bar: i32) -> Foo { | ||
1155 | Foo { bar } | ||
1156 | } | ||
1157 | "#, | ||
1158 | ); | ||
1159 | } | ||
1160 | |||
1161 | #[test] | ||
1162 | fn test_struct_field_destructure_into_shorthand() { | ||
1163 | check( | ||
1164 | "baz", | ||
1165 | r#" | ||
1166 | struct Foo { i<|>: i32 } | ||
1167 | |||
1168 | fn foo(foo: Foo) { | ||
1169 | let Foo { i: baz } = foo; | ||
1170 | let _ = baz; | ||
1171 | } | ||
1172 | "#, | ||
1173 | r#" | ||
1174 | struct Foo { baz: i32 } | ||
1175 | |||
1176 | fn foo(foo: Foo) { | ||
1177 | let Foo { baz } = foo; | ||
1178 | let _ = baz; | ||
1179 | } | ||
1180 | "#, | ||
1181 | ); | ||
1182 | } | ||
1183 | |||
1184 | #[test] | ||
1185 | fn test_rename_binding_in_destructure_pat() { | ||
1186 | let expected_fixture = r#" | ||
1187 | struct Foo { | ||
1188 | i: i32, | ||
1189 | } | ||
1190 | |||
1191 | fn foo(foo: Foo) { | ||
1192 | let Foo { i: bar } = foo; | ||
1193 | let _ = bar; | ||
1194 | } | ||
1195 | "#; | ||
1196 | check( | ||
1197 | "bar", | ||
1198 | r#" | ||
1199 | struct Foo { | ||
1200 | i: i32, | ||
1201 | } | ||
1202 | |||
1203 | fn foo(foo: Foo) { | ||
1204 | let Foo { i: b } = foo; | ||
1205 | let _ = b<|>; | ||
1206 | } | ||
1207 | "#, | ||
1208 | expected_fixture, | ||
1209 | ); | ||
1210 | check( | ||
1211 | "bar", | ||
1212 | r#" | ||
1213 | struct Foo { | ||
1214 | i: i32, | ||
1215 | } | ||
1216 | |||
1217 | fn foo(foo: Foo) { | ||
1218 | let Foo { i } = foo; | ||
1219 | let _ = i<|>; | ||
1220 | } | ||
1221 | "#, | ||
1222 | expected_fixture, | ||
1223 | ); | ||
1224 | } | ||
1225 | |||
1226 | #[test] | ||
1227 | fn test_rename_binding_in_destructure_param_pat() { | ||
1228 | check( | ||
1229 | "bar", | ||
1230 | r#" | ||
1231 | struct Foo { | ||
1232 | i: i32 | ||
1233 | } | ||
1234 | |||
1235 | fn foo(Foo { i }: foo) -> i32 { | ||
1236 | i<|> | ||
1237 | } | ||
1238 | "#, | ||
1239 | r#" | ||
1240 | struct Foo { | ||
1241 | i: i32 | ||
1242 | } | ||
1243 | |||
1244 | fn foo(Foo { i: bar }: foo) -> i32 { | ||
1245 | bar | ||
1246 | } | ||
1247 | "#, | ||
1248 | ) | ||
1249 | } | ||
1100 | } | 1250 | } |
diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs index 2bd0e86e5..e15411777 100644 --- a/crates/ide/src/runnables.rs +++ b/crates/ide/src/runnables.rs | |||
@@ -1,5 +1,6 @@ | |||
1 | use std::fmt; | 1 | use std::fmt; |
2 | 2 | ||
3 | use assists::utils::test_related_attribute; | ||
3 | use cfg::CfgExpr; | 4 | use cfg::CfgExpr; |
4 | use hir::{AsAssocItem, Attrs, HirFileId, InFile, Semantics}; | 5 | use hir::{AsAssocItem, Attrs, HirFileId, InFile, Semantics}; |
5 | use ide_db::RootDatabase; | 6 | use ide_db::RootDatabase; |
@@ -156,7 +157,7 @@ fn runnable_fn( | |||
156 | None => TestId::Name(name_string), | 157 | None => TestId::Name(name_string), |
157 | }; | 158 | }; |
158 | 159 | ||
159 | if has_test_related_attribute(&fn_def) { | 160 | if test_related_attribute(&fn_def).is_some() { |
160 | let attr = TestAttr::from_fn(&fn_def); | 161 | let attr = TestAttr::from_fn(&fn_def); |
161 | RunnableKind::Test { test_id, attr } | 162 | RunnableKind::Test { test_id, attr } |
162 | } else if fn_def.has_atom_attr("bench") { | 163 | } else if fn_def.has_atom_attr("bench") { |
@@ -235,20 +236,6 @@ impl TestAttr { | |||
235 | } | 236 | } |
236 | } | 237 | } |
237 | 238 | ||
238 | /// This is a method with a heuristics to support test methods annotated with custom test annotations, such as | ||
239 | /// `#[test_case(...)]`, `#[tokio::test]` and similar. | ||
240 | /// Also a regular `#[test]` annotation is supported. | ||
241 | /// | ||
242 | /// It may produce false positives, for example, `#[wasm_bindgen_test]` requires a different command to run the test, | ||
243 | /// but it's better than not to have the runnables for the tests at all. | ||
244 | pub(crate) fn has_test_related_attribute(fn_def: &ast::Fn) -> bool { | ||
245 | fn_def | ||
246 | .attrs() | ||
247 | .filter_map(|attr| attr.path()) | ||
248 | .map(|path| path.syntax().to_string().to_lowercase()) | ||
249 | .any(|attribute_text| attribute_text.contains("test")) | ||
250 | } | ||
251 | |||
252 | const RUSTDOC_FENCE: &str = "```"; | 239 | const RUSTDOC_FENCE: &str = "```"; |
253 | const RUSTDOC_CODE_BLOCK_ATTRIBUTES_RUNNABLE: &[&str] = | 240 | const RUSTDOC_CODE_BLOCK_ATTRIBUTES_RUNNABLE: &[&str] = |
254 | &["", "rust", "should_panic", "edition2015", "edition2018"]; | 241 | &["", "rust", "should_panic", "edition2015", "edition2018"]; |
@@ -307,7 +294,7 @@ fn has_test_function_or_multiple_test_submodules(module: &ast::Module) -> bool { | |||
307 | for item in item_list.items() { | 294 | for item in item_list.items() { |
308 | match item { | 295 | match item { |
309 | ast::Item::Fn(f) => { | 296 | ast::Item::Fn(f) => { |
310 | if has_test_related_attribute(&f) { | 297 | if test_related_attribute(&f).is_some() { |
311 | return true; | 298 | return true; |
312 | } | 299 | } |
313 | } | 300 | } |
diff --git a/crates/ide/src/status.rs b/crates/ide/src/status.rs index 8e91c99d7..b75f88ed9 100644 --- a/crates/ide/src/status.rs +++ b/crates/ide/src/status.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | use std::{fmt, iter::FromIterator, sync::Arc}; | 1 | use std::{fmt, iter::FromIterator, sync::Arc}; |
2 | 2 | ||
3 | use hir::MacroFile; | 3 | use hir::{MacroFile, MacroResult}; |
4 | use ide_db::base_db::{ | 4 | use ide_db::base_db::{ |
5 | salsa::debug::{DebugQueryTable, TableEntry}, | 5 | salsa::debug::{DebugQueryTable, TableEntry}, |
6 | CrateId, FileId, FileTextQuery, SourceDatabase, SourceRootId, | 6 | CrateId, FileId, FileTextQuery, SourceDatabase, SourceRootId, |
@@ -19,7 +19,7 @@ fn syntax_tree_stats(db: &RootDatabase) -> SyntaxTreeStats { | |||
19 | ide_db::base_db::ParseQuery.in_db(db).entries::<SyntaxTreeStats>() | 19 | ide_db::base_db::ParseQuery.in_db(db).entries::<SyntaxTreeStats>() |
20 | } | 20 | } |
21 | fn macro_syntax_tree_stats(db: &RootDatabase) -> SyntaxTreeStats { | 21 | fn macro_syntax_tree_stats(db: &RootDatabase) -> SyntaxTreeStats { |
22 | hir::db::ParseMacroQuery.in_db(db).entries::<SyntaxTreeStats>() | 22 | hir::db::ParseMacroExpansionQuery.in_db(db).entries::<SyntaxTreeStats>() |
23 | } | 23 | } |
24 | 24 | ||
25 | // Feature: Status | 25 | // Feature: Status |
@@ -115,10 +115,12 @@ impl FromIterator<TableEntry<FileId, Parse<ast::SourceFile>>> for SyntaxTreeStat | |||
115 | } | 115 | } |
116 | } | 116 | } |
117 | 117 | ||
118 | impl<M> FromIterator<TableEntry<MacroFile, Option<(Parse<SyntaxNode>, M)>>> for SyntaxTreeStats { | 118 | impl<M> FromIterator<TableEntry<MacroFile, MacroResult<(Parse<SyntaxNode>, M)>>> |
119 | for SyntaxTreeStats | ||
120 | { | ||
119 | fn from_iter<T>(iter: T) -> SyntaxTreeStats | 121 | fn from_iter<T>(iter: T) -> SyntaxTreeStats |
120 | where | 122 | where |
121 | T: IntoIterator<Item = TableEntry<MacroFile, Option<(Parse<SyntaxNode>, M)>>>, | 123 | T: IntoIterator<Item = TableEntry<MacroFile, MacroResult<(Parse<SyntaxNode>, M)>>>, |
122 | { | 124 | { |
123 | let mut res = SyntaxTreeStats::default(); | 125 | let mut res = SyntaxTreeStats::default(); |
124 | for entry in iter { | 126 | for entry in iter { |
diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs index 05bafe9c8..1ed77b40b 100644 --- a/crates/ide/src/syntax_highlighting.rs +++ b/crates/ide/src/syntax_highlighting.rs | |||
@@ -6,7 +6,7 @@ pub(crate) mod tags; | |||
6 | #[cfg(test)] | 6 | #[cfg(test)] |
7 | mod tests; | 7 | mod tests; |
8 | 8 | ||
9 | use hir::{Local, Name, Semantics, VariantDef}; | 9 | use hir::{AsAssocItem, Local, Name, Semantics, VariantDef}; |
10 | use ide_db::{ | 10 | use ide_db::{ |
11 | defs::{Definition, NameClass, NameRefClass}, | 11 | defs::{Definition, NameClass, NameRefClass}, |
12 | RootDatabase, | 12 | RootDatabase, |
@@ -557,7 +557,9 @@ fn highlight_element( | |||
557 | h | 557 | h |
558 | } | 558 | } |
559 | } | 559 | } |
560 | T![::] | T![->] | T![=>] | T![..] | T![=] | T![@] => HighlightTag::Operator.into(), | 560 | T![::] | T![->] | T![=>] | T![..] | T![=] | T![@] | T![.] => { |
561 | HighlightTag::Operator.into() | ||
562 | } | ||
561 | T![!] if element.parent().and_then(ast::MacroCall::cast).is_some() => { | 563 | T![!] if element.parent().and_then(ast::MacroCall::cast).is_some() => { |
562 | HighlightTag::Macro.into() | 564 | HighlightTag::Macro.into() |
563 | } | 565 | } |
@@ -744,6 +746,9 @@ fn highlight_def(db: &RootDatabase, def: Definition) -> Highlight { | |||
744 | if func.is_unsafe(db) { | 746 | if func.is_unsafe(db) { |
745 | h |= HighlightModifier::Unsafe; | 747 | h |= HighlightModifier::Unsafe; |
746 | } | 748 | } |
749 | if func.as_assoc_item(db).is_some() && func.self_param(db).is_none() { | ||
750 | h |= HighlightModifier::Static; | ||
751 | } | ||
747 | return h; | 752 | return h; |
748 | } | 753 | } |
749 | hir::ModuleDef::Adt(hir::Adt::Struct(_)) => HighlightTag::Struct, | 754 | hir::ModuleDef::Adt(hir::Adt::Struct(_)) => HighlightTag::Struct, |
diff --git a/crates/ide/src/syntax_highlighting/tags.rs b/crates/ide/src/syntax_highlighting/tags.rs index e8f78ad52..65e0671a5 100644 --- a/crates/ide/src/syntax_highlighting/tags.rs +++ b/crates/ide/src/syntax_highlighting/tags.rs | |||
@@ -65,6 +65,8 @@ pub enum HighlightModifier { | |||
65 | Consuming, | 65 | Consuming, |
66 | Unsafe, | 66 | Unsafe, |
67 | Callable, | 67 | Callable, |
68 | /// Used for associated functions | ||
69 | Static, | ||
68 | } | 70 | } |
69 | 71 | ||
70 | impl HighlightTag { | 72 | impl HighlightTag { |
@@ -124,6 +126,7 @@ impl HighlightModifier { | |||
124 | HighlightModifier::Consuming, | 126 | HighlightModifier::Consuming, |
125 | HighlightModifier::Unsafe, | 127 | HighlightModifier::Unsafe, |
126 | HighlightModifier::Callable, | 128 | HighlightModifier::Callable, |
129 | HighlightModifier::Static, | ||
127 | ]; | 130 | ]; |
128 | 131 | ||
129 | fn as_str(self) -> &'static str { | 132 | fn as_str(self) -> &'static str { |
@@ -137,6 +140,7 @@ impl HighlightModifier { | |||
137 | HighlightModifier::Consuming => "consuming", | 140 | HighlightModifier::Consuming => "consuming", |
138 | HighlightModifier::Unsafe => "unsafe", | 141 | HighlightModifier::Unsafe => "unsafe", |
139 | HighlightModifier::Callable => "callable", | 142 | HighlightModifier::Callable => "callable", |
143 | HighlightModifier::Static => "static", | ||
140 | } | 144 | } |
141 | } | 145 | } |
142 | 146 | ||
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html b/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html new file mode 100644 index 000000000..cd80d72b7 --- /dev/null +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html | |||
@@ -0,0 +1,56 @@ | |||
1 | |||
2 | <style> | ||
3 | body { margin: 0; } | ||
4 | pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; } | ||
5 | |||
6 | .lifetime { color: #DFAF8F; font-style: italic; } | ||
7 | .comment { color: #7F9F7F; } | ||
8 | .documentation { color: #629755; } | ||
9 | .injected { opacity: 0.65 ; } | ||
10 | .struct, .enum { color: #7CB8BB; } | ||
11 | .enum_variant { color: #BDE0F3; } | ||
12 | .string_literal { color: #CC9393; } | ||
13 | .field { color: #94BFF3; } | ||
14 | .function { color: #93E0E3; } | ||
15 | .function.unsafe { color: #BC8383; } | ||
16 | .operator.unsafe { color: #BC8383; } | ||
17 | .parameter { color: #94BFF3; } | ||
18 | .text { color: #DCDCCC; } | ||
19 | .type { color: #7CB8BB; } | ||
20 | .builtin_type { color: #8CD0D3; } | ||
21 | .type_param { color: #DFAF8F; } | ||
22 | .attribute { color: #94BFF3; } | ||
23 | .numeric_literal { color: #BFEBBF; } | ||
24 | .bool_literal { color: #BFE6EB; } | ||
25 | .macro { color: #94BFF3; } | ||
26 | .module { color: #AFD8AF; } | ||
27 | .value_param { color: #DCDCCC; } | ||
28 | .variable { color: #DCDCCC; } | ||
29 | .format_specifier { color: #CC696B; } | ||
30 | .mutable { text-decoration: underline; } | ||
31 | .escape_sequence { color: #94BFF3; } | ||
32 | .keyword { color: #F0DFAF; font-weight: bold; } | ||
33 | .keyword.unsafe { color: #BC8383; font-weight: bold; } | ||
34 | .control { font-style: italic; } | ||
35 | |||
36 | .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } | ||
37 | </style> | ||
38 | <pre><code><span class="keyword">fn</span> <span class="function declaration">not_static</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span> | ||
39 | |||
40 | <span class="keyword">struct</span> <span class="struct declaration">foo</span> <span class="punctuation">{</span><span class="punctuation">}</span> | ||
41 | |||
42 | <span class="keyword">impl</span> <span class="struct">foo</span> <span class="punctuation">{</span> | ||
43 | <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration static">is_static</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span> | ||
44 | <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration">is_not_static</span><span class="punctuation">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span> | ||
45 | <span class="punctuation">}</span> | ||
46 | |||
47 | <span class="keyword">trait</span> <span class="trait declaration">t</span> <span class="punctuation">{</span> | ||
48 | <span class="keyword">fn</span> <span class="function declaration static">t_is_static</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span> | ||
49 | <span class="keyword">fn</span> <span class="function declaration">t_is_not_static</span><span class="punctuation">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span> | ||
50 | <span class="punctuation">}</span> | ||
51 | |||
52 | <span class="keyword">impl</span> <span class="trait">t</span> <span class="keyword">for</span> <span class="struct">foo</span> <span class="punctuation">{</span> | ||
53 | <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration static">is_static</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span> | ||
54 | <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration">is_not_static</span><span class="punctuation">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span> | ||
55 | <span class="punctuation">}</span> | ||
56 | </code></pre> \ No newline at end of file | ||
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html index 6322d404f..6be88f856 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html | |||
@@ -53,7 +53,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
53 | <span class="comment documentation">/// #</span><span class="generic injected"> </span><span class="attribute injected">#</span><span class="attribute injected">!</span><span class="attribute injected">[</span><span class="function attribute injected">allow</span><span class="punctuation injected">(</span><span class="attribute injected">unused_mut</span><span class="punctuation injected">)</span><span class="attribute injected">]</span> | 53 | <span class="comment documentation">/// #</span><span class="generic injected"> </span><span class="attribute injected">#</span><span class="attribute injected">!</span><span class="attribute injected">[</span><span class="function attribute injected">allow</span><span class="punctuation injected">(</span><span class="attribute injected">unused_mut</span><span class="punctuation injected">)</span><span class="attribute injected">]</span> |
54 | <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="generic injected"> </span><span class="keyword injected">mut</span><span class="generic injected"> </span><span class="variable declaration injected mutable">foo</span><span class="punctuation injected">:</span><span class="generic injected"> </span><span class="struct injected">Foo</span><span class="generic injected"> </span><span class="operator injected">=</span><span class="generic injected"> </span><span class="struct injected">Foo</span><span class="operator injected">::</span><span class="function injected">new</span><span class="punctuation injected">(</span><span class="punctuation injected">)</span><span class="punctuation injected">;</span><span class="punctuation injected"> | 54 | <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="generic injected"> </span><span class="keyword injected">mut</span><span class="generic injected"> </span><span class="variable declaration injected mutable">foo</span><span class="punctuation injected">:</span><span class="generic injected"> </span><span class="struct injected">Foo</span><span class="generic injected"> </span><span class="operator injected">=</span><span class="generic injected"> </span><span class="struct injected">Foo</span><span class="operator injected">::</span><span class="function injected">new</span><span class="punctuation injected">(</span><span class="punctuation injected">)</span><span class="punctuation injected">;</span><span class="punctuation injected"> |
55 | </span> <span class="comment documentation">/// ```</span> | 55 | </span> <span class="comment documentation">/// ```</span> |
56 | <span class="keyword">pub</span> <span class="keyword">const</span> <span class="keyword">fn</span> <span class="function declaration">new</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="operator">-></span> <span class="struct">Foo</span> <span class="punctuation">{</span> | 56 | <span class="keyword">pub</span> <span class="keyword">const</span> <span class="keyword">fn</span> <span class="function declaration static">new</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="operator">-></span> <span class="struct">Foo</span> <span class="punctuation">{</span> |
57 | <span class="struct">Foo</span> <span class="punctuation">{</span> <span class="field">bar</span><span class="punctuation">:</span> <span class="bool_literal">true</span> <span class="punctuation">}</span> | 57 | <span class="struct">Foo</span> <span class="punctuation">{</span> <span class="field">bar</span><span class="punctuation">:</span> <span class="bool_literal">true</span> <span class="punctuation">}</span> |
58 | <span class="punctuation">}</span> | 58 | <span class="punctuation">}</span> |
59 | 59 | ||
@@ -67,9 +67,9 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
67 | <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="generic injected"> </span><span class="variable declaration injected">foo</span><span class="generic injected"> </span><span class="operator injected">=</span><span class="generic injected"> </span><span class="struct injected">Foo</span><span class="operator injected">::</span><span class="function injected">new</span><span class="punctuation injected">(</span><span class="punctuation injected">)</span><span class="punctuation injected">;</span> | 67 | <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="generic injected"> </span><span class="variable declaration injected">foo</span><span class="generic injected"> </span><span class="operator injected">=</span><span class="generic injected"> </span><span class="struct injected">Foo</span><span class="operator injected">::</span><span class="function injected">new</span><span class="punctuation injected">(</span><span class="punctuation injected">)</span><span class="punctuation injected">;</span> |
68 | <span class="comment documentation">///</span> | 68 | <span class="comment documentation">///</span> |
69 | <span class="comment documentation">/// </span><span class="comment injected">// calls bar on foo</span> | 69 | <span class="comment documentation">/// </span><span class="comment injected">// calls bar on foo</span> |
70 | <span class="comment documentation">/// </span><span class="macro injected">assert!</span><span class="punctuation injected">(</span><span class="generic injected">foo</span><span class="punctuation injected">.</span><span class="generic injected">bar</span><span class="punctuation injected">(</span><span class="punctuation injected">)</span><span class="punctuation injected">)</span><span class="punctuation injected">;</span> | 70 | <span class="comment documentation">/// </span><span class="macro injected">assert!</span><span class="punctuation injected">(</span><span class="generic injected">foo</span><span class="operator injected">.</span><span class="generic injected">bar</span><span class="punctuation injected">(</span><span class="punctuation injected">)</span><span class="punctuation injected">)</span><span class="punctuation injected">;</span> |
71 | <span class="comment documentation">///</span> | 71 | <span class="comment documentation">///</span> |
72 | <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="generic injected"> </span><span class="variable declaration injected">bar</span><span class="generic injected"> </span><span class="operator injected">=</span><span class="generic injected"> </span><span class="variable injected">foo</span><span class="punctuation injected">.</span><span class="field injected">bar</span><span class="generic injected"> </span><span class="operator injected">||</span><span class="generic injected"> </span><span class="struct injected">Foo</span><span class="operator injected">::</span><span class="constant injected">bar</span><span class="punctuation injected">;</span> | 72 | <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="generic injected"> </span><span class="variable declaration injected">bar</span><span class="generic injected"> </span><span class="operator injected">=</span><span class="generic injected"> </span><span class="variable injected">foo</span><span class="operator injected">.</span><span class="field injected">bar</span><span class="generic injected"> </span><span class="operator injected">||</span><span class="generic injected"> </span><span class="struct injected">Foo</span><span class="operator injected">::</span><span class="constant injected">bar</span><span class="punctuation injected">;</span> |
73 | <span class="comment documentation">///</span> | 73 | <span class="comment documentation">///</span> |
74 | <span class="comment documentation">/// </span><span class="comment injected">/* multi-line | 74 | <span class="comment documentation">/// </span><span class="comment injected">/* multi-line |
75 | </span><span class="comment documentation">/// </span><span class="comment injected"> comment */</span> | 75 | </span><span class="comment documentation">/// </span><span class="comment injected"> comment */</span> |
@@ -81,7 +81,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
81 | <span class="comment documentation">/// ```</span> | 81 | <span class="comment documentation">/// ```</span> |
82 | <span class="comment documentation">///</span> | 82 | <span class="comment documentation">///</span> |
83 | <span class="comment documentation">/// ```rust,no_run</span> | 83 | <span class="comment documentation">/// ```rust,no_run</span> |
84 | <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="generic injected"> </span><span class="variable declaration injected">foobar</span><span class="generic injected"> </span><span class="operator injected">=</span><span class="generic injected"> </span><span class="struct injected">Foo</span><span class="operator injected">::</span><span class="function injected">new</span><span class="punctuation injected">(</span><span class="punctuation injected">)</span><span class="punctuation injected">.</span><span class="function injected">bar</span><span class="punctuation injected">(</span><span class="punctuation injected">)</span><span class="punctuation injected">;</span><span class="punctuation injected"> | 84 | <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="generic injected"> </span><span class="variable declaration injected">foobar</span><span class="generic injected"> </span><span class="operator injected">=</span><span class="generic injected"> </span><span class="struct injected">Foo</span><span class="operator injected">::</span><span class="function injected">new</span><span class="punctuation injected">(</span><span class="punctuation injected">)</span><span class="operator injected">.</span><span class="function injected">bar</span><span class="punctuation injected">(</span><span class="punctuation injected">)</span><span class="punctuation injected">;</span><span class="punctuation injected"> |
85 | </span> <span class="comment documentation">/// ```</span> | 85 | </span> <span class="comment documentation">/// ```</span> |
86 | <span class="comment documentation">///</span> | 86 | <span class="comment documentation">///</span> |
87 | <span class="comment documentation">/// ```sh</span> | 87 | <span class="comment documentation">/// ```sh</span> |
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html b/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html index 18addd00d..57c178916 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html | |||
@@ -40,7 +40,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
40 | <span class="keyword">fn</span> <span class="function declaration">main</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span> | 40 | <span class="keyword">fn</span> <span class="function declaration">main</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span> |
41 | <span class="function">fixture</span><span class="punctuation">(</span><span class="string_literal">r#"</span> | 41 | <span class="function">fixture</span><span class="punctuation">(</span><span class="string_literal">r#"</span> |
42 | <span class="keyword">trait</span> <span class="trait declaration">Foo</span> <span class="punctuation">{</span> | 42 | <span class="keyword">trait</span> <span class="trait declaration">Foo</span> <span class="punctuation">{</span> |
43 | <span class="keyword">fn</span> <span class="function declaration">foo</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span> | 43 | <span class="keyword">fn</span> <span class="function declaration static">foo</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span> |
44 | <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"2 + 2 = {}"</span><span class="punctuation">,</span> <span class="numeric_literal">4</span><span class="punctuation">)</span><span class="punctuation">;</span> | 44 | <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"2 + 2 = {}"</span><span class="punctuation">,</span> <span class="numeric_literal">4</span><span class="punctuation">)</span><span class="punctuation">;</span> |
45 | <span class="punctuation">}</span> | 45 | <span class="punctuation">}</span> |
46 | <span class="punctuation">}</span><span class="string_literal">"#</span> | 46 | <span class="punctuation">}</span><span class="string_literal">"#</span> |
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html b/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html index 43f1b32fd..d398e1ec8 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html | |||
@@ -93,4 +93,6 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
93 | 93 | ||
94 | <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="escape_sequence">\x41</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="punctuation">,</span> A <span class="operator">=</span> <span class="numeric_literal">92</span><span class="punctuation">)</span><span class="punctuation">;</span> | 94 | <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="escape_sequence">\x41</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="punctuation">,</span> A <span class="operator">=</span> <span class="numeric_literal">92</span><span class="punctuation">)</span><span class="punctuation">;</span> |
95 | <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">ничоси</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="punctuation">,</span> ничоси <span class="operator">=</span> <span class="numeric_literal">92</span><span class="punctuation">)</span><span class="punctuation">;</span> | 95 | <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">ничоси</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="punctuation">,</span> ничоси <span class="operator">=</span> <span class="numeric_literal">92</span><span class="punctuation">)</span><span class="punctuation">;</span> |
96 | |||
97 | <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="variable">x</span><span class="format_specifier">?</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> "</span><span class="punctuation">,</span> thingy<span class="punctuation">,</span> n2<span class="punctuation">)</span><span class="punctuation">;</span> | ||
96 | <span class="punctuation">}</span></code></pre> \ No newline at end of file | 98 | <span class="punctuation">}</span></code></pre> \ No newline at end of file |
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html b/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html index 552fea668..4b6d6adc9 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html | |||
@@ -73,27 +73,27 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
73 | <span class="keyword unsafe">unsafe</span> <span class="punctuation">{</span> | 73 | <span class="keyword unsafe">unsafe</span> <span class="punctuation">{</span> |
74 | <span class="comment">// unsafe fn and method calls</span> | 74 | <span class="comment">// unsafe fn and method calls</span> |
75 | <span class="function unsafe">unsafe_fn</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> | 75 | <span class="function unsafe">unsafe_fn</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> |
76 | <span class="keyword">let</span> <span class="variable declaration">b</span> <span class="operator">=</span> <span class="variable">u</span><span class="punctuation">.</span><span class="field unsafe">b</span><span class="punctuation">;</span> | 76 | <span class="keyword">let</span> <span class="variable declaration">b</span> <span class="operator">=</span> <span class="variable">u</span><span class="operator">.</span><span class="field unsafe">b</span><span class="punctuation">;</span> |
77 | <span class="keyword control">match</span> <span class="variable">u</span> <span class="punctuation">{</span> | 77 | <span class="keyword control">match</span> <span class="variable">u</span> <span class="punctuation">{</span> |
78 | <span class="union">Union</span> <span class="punctuation">{</span> <span class="field unsafe">b</span><span class="punctuation">:</span> <span class="numeric_literal">0</span> <span class="punctuation">}</span> <span class="operator">=></span> <span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">,</span> | 78 | <span class="union">Union</span> <span class="punctuation">{</span> <span class="field unsafe">b</span><span class="punctuation">:</span> <span class="numeric_literal">0</span> <span class="punctuation">}</span> <span class="operator">=></span> <span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">,</span> |
79 | <span class="union">Union</span> <span class="punctuation">{</span> <span class="field unsafe">a</span> <span class="punctuation">}</span> <span class="operator">=></span> <span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">,</span> | 79 | <span class="union">Union</span> <span class="punctuation">{</span> <span class="field unsafe">a</span> <span class="punctuation">}</span> <span class="operator">=></span> <span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">,</span> |
80 | <span class="punctuation">}</span> | 80 | <span class="punctuation">}</span> |
81 | <span class="struct">HasUnsafeFn</span><span class="punctuation">.</span><span class="function unsafe">unsafe_method</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> | 81 | <span class="struct">HasUnsafeFn</span><span class="operator">.</span><span class="function unsafe">unsafe_method</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> |
82 | 82 | ||
83 | <span class="comment">// unsafe deref</span> | 83 | <span class="comment">// unsafe deref</span> |
84 | <span class="keyword">let</span> <span class="variable declaration">y</span> <span class="operator">=</span> <span class="operator unsafe">*</span><span class="variable">x</span><span class="punctuation">;</span> | 84 | <span class="keyword">let</span> <span class="variable declaration">y</span> <span class="operator">=</span> <span class="operator unsafe">*</span><span class="variable">x</span><span class="punctuation">;</span> |
85 | 85 | ||
86 | <span class="comment">// unsafe access to a static mut</span> | 86 | <span class="comment">// unsafe access to a static mut</span> |
87 | <span class="keyword">let</span> <span class="variable declaration">a</span> <span class="operator">=</span> <span class="static mutable unsafe">global_mut</span><span class="punctuation">.</span><span class="field">a</span><span class="punctuation">;</span> | 87 | <span class="keyword">let</span> <span class="variable declaration">a</span> <span class="operator">=</span> <span class="static mutable unsafe">global_mut</span><span class="operator">.</span><span class="field">a</span><span class="punctuation">;</span> |
88 | 88 | ||
89 | <span class="comment">// unsafe ref of packed fields</span> | 89 | <span class="comment">// unsafe ref of packed fields</span> |
90 | <span class="keyword">let</span> <span class="variable declaration">packed</span> <span class="operator">=</span> <span class="struct">Packed</span> <span class="punctuation">{</span> <span class="field">a</span><span class="punctuation">:</span> <span class="numeric_literal">0</span> <span class="punctuation">}</span><span class="punctuation">;</span> | 90 | <span class="keyword">let</span> <span class="variable declaration">packed</span> <span class="operator">=</span> <span class="struct">Packed</span> <span class="punctuation">{</span> <span class="field">a</span><span class="punctuation">:</span> <span class="numeric_literal">0</span> <span class="punctuation">}</span><span class="punctuation">;</span> |
91 | <span class="keyword">let</span> <span class="variable declaration">a</span> <span class="operator">=</span> <span class="operator unsafe">&</span><span class="variable">packed</span><span class="punctuation">.</span><span class="field">a</span><span class="punctuation">;</span> | 91 | <span class="keyword">let</span> <span class="variable declaration">a</span> <span class="operator">=</span> <span class="operator unsafe">&</span><span class="variable">packed</span><span class="operator">.</span><span class="field">a</span><span class="punctuation">;</span> |
92 | <span class="keyword">let</span> <span class="keyword unsafe">ref</span> <span class="variable declaration">a</span> <span class="operator">=</span> <span class="variable">packed</span><span class="punctuation">.</span><span class="field">a</span><span class="punctuation">;</span> | 92 | <span class="keyword">let</span> <span class="keyword unsafe">ref</span> <span class="variable declaration">a</span> <span class="operator">=</span> <span class="variable">packed</span><span class="operator">.</span><span class="field">a</span><span class="punctuation">;</span> |
93 | <span class="keyword">let</span> <span class="struct">Packed</span> <span class="punctuation">{</span> <span class="keyword unsafe">ref</span> <span class="field">a</span> <span class="punctuation">}</span> <span class="operator">=</span> <span class="variable">packed</span><span class="punctuation">;</span> | 93 | <span class="keyword">let</span> <span class="struct">Packed</span> <span class="punctuation">{</span> <span class="keyword unsafe">ref</span> <span class="field">a</span> <span class="punctuation">}</span> <span class="operator">=</span> <span class="variable">packed</span><span class="punctuation">;</span> |
94 | <span class="keyword">let</span> <span class="struct">Packed</span> <span class="punctuation">{</span> <span class="field">a</span><span class="punctuation">:</span> <span class="keyword unsafe">ref</span> <span class="variable declaration">_a</span> <span class="punctuation">}</span> <span class="operator">=</span> <span class="variable">packed</span><span class="punctuation">;</span> | 94 | <span class="keyword">let</span> <span class="struct">Packed</span> <span class="punctuation">{</span> <span class="field">a</span><span class="punctuation">:</span> <span class="keyword unsafe">ref</span> <span class="variable declaration">_a</span> <span class="punctuation">}</span> <span class="operator">=</span> <span class="variable">packed</span><span class="punctuation">;</span> |
95 | 95 | ||
96 | <span class="comment">// unsafe auto ref of packed field</span> | 96 | <span class="comment">// unsafe auto ref of packed field</span> |
97 | <span class="variable">packed</span><span class="punctuation">.</span><span class="field">a</span><span class="punctuation">.</span><span class="function unsafe">calls_autoref</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> | 97 | <span class="variable">packed</span><span class="operator">.</span><span class="field">a</span><span class="operator">.</span><span class="function unsafe">calls_autoref</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> |
98 | <span class="punctuation">}</span> | 98 | <span class="punctuation">}</span> |
99 | <span class="punctuation">}</span></code></pre> \ No newline at end of file | 99 | <span class="punctuation">}</span></code></pre> \ No newline at end of file |
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlighting.html b/crates/ide/src/syntax_highlighting/test_data/highlighting.html index 5eb222ee2..6a10a9dcd 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlighting.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlighting.html | |||
@@ -67,21 +67,21 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
67 | 67 | ||
68 | <span class="keyword">impl</span> <span class="trait">Bar</span> <span class="keyword">for</span> <span class="struct">Foo</span> <span class="punctuation">{</span> | 68 | <span class="keyword">impl</span> <span class="trait">Bar</span> <span class="keyword">for</span> <span class="struct">Foo</span> <span class="punctuation">{</span> |
69 | <span class="keyword">fn</span> <span class="function declaration">bar</span><span class="punctuation">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="punctuation">)</span> <span class="operator">-></span> <span class="builtin_type">i32</span> <span class="punctuation">{</span> | 69 | <span class="keyword">fn</span> <span class="function declaration">bar</span><span class="punctuation">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="punctuation">)</span> <span class="operator">-></span> <span class="builtin_type">i32</span> <span class="punctuation">{</span> |
70 | <span class="self_keyword">self</span><span class="punctuation">.</span><span class="field">x</span> | 70 | <span class="self_keyword">self</span><span class="operator">.</span><span class="field">x</span> |
71 | <span class="punctuation">}</span> | 71 | <span class="punctuation">}</span> |
72 | <span class="punctuation">}</span> | 72 | <span class="punctuation">}</span> |
73 | 73 | ||
74 | <span class="keyword">impl</span> <span class="struct">Foo</span> <span class="punctuation">{</span> | 74 | <span class="keyword">impl</span> <span class="struct">Foo</span> <span class="punctuation">{</span> |
75 | <span class="keyword">fn</span> <span class="function declaration">baz</span><span class="punctuation">(</span><span class="keyword">mut</span> <span class="self_keyword mutable">self</span><span class="punctuation">,</span> <span class="value_param declaration">f</span><span class="punctuation">:</span> <span class="struct">Foo</span><span class="punctuation">)</span> <span class="operator">-></span> <span class="builtin_type">i32</span> <span class="punctuation">{</span> | 75 | <span class="keyword">fn</span> <span class="function declaration">baz</span><span class="punctuation">(</span><span class="keyword">mut</span> <span class="self_keyword mutable">self</span><span class="punctuation">,</span> <span class="value_param declaration">f</span><span class="punctuation">:</span> <span class="struct">Foo</span><span class="punctuation">)</span> <span class="operator">-></span> <span class="builtin_type">i32</span> <span class="punctuation">{</span> |
76 | <span class="value_param">f</span><span class="punctuation">.</span><span class="function consuming">baz</span><span class="punctuation">(</span><span class="self_keyword mutable consuming">self</span><span class="punctuation">)</span> | 76 | <span class="value_param">f</span><span class="operator">.</span><span class="function consuming">baz</span><span class="punctuation">(</span><span class="self_keyword mutable consuming">self</span><span class="punctuation">)</span> |
77 | <span class="punctuation">}</span> | 77 | <span class="punctuation">}</span> |
78 | 78 | ||
79 | <span class="keyword">fn</span> <span class="function declaration">qux</span><span class="punctuation">(</span><span class="operator">&</span><span class="keyword">mut</span> <span class="self_keyword mutable">self</span><span class="punctuation">)</span> <span class="punctuation">{</span> | 79 | <span class="keyword">fn</span> <span class="function declaration">qux</span><span class="punctuation">(</span><span class="operator">&</span><span class="keyword">mut</span> <span class="self_keyword mutable">self</span><span class="punctuation">)</span> <span class="punctuation">{</span> |
80 | <span class="self_keyword mutable">self</span><span class="punctuation">.</span><span class="field">x</span> <span class="operator">=</span> <span class="numeric_literal">0</span><span class="punctuation">;</span> | 80 | <span class="self_keyword mutable">self</span><span class="operator">.</span><span class="field">x</span> <span class="operator">=</span> <span class="numeric_literal">0</span><span class="punctuation">;</span> |
81 | <span class="punctuation">}</span> | 81 | <span class="punctuation">}</span> |
82 | 82 | ||
83 | <span class="keyword">fn</span> <span class="function declaration">quop</span><span class="punctuation">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="punctuation">)</span> <span class="operator">-></span> <span class="builtin_type">i32</span> <span class="punctuation">{</span> | 83 | <span class="keyword">fn</span> <span class="function declaration">quop</span><span class="punctuation">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="punctuation">)</span> <span class="operator">-></span> <span class="builtin_type">i32</span> <span class="punctuation">{</span> |
84 | <span class="self_keyword">self</span><span class="punctuation">.</span><span class="field">x</span> | 84 | <span class="self_keyword">self</span><span class="operator">.</span><span class="field">x</span> |
85 | <span class="punctuation">}</span> | 85 | <span class="punctuation">}</span> |
86 | <span class="punctuation">}</span> | 86 | <span class="punctuation">}</span> |
87 | 87 | ||
@@ -92,15 +92,15 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
92 | 92 | ||
93 | <span class="keyword">impl</span> <span class="struct">FooCopy</span> <span class="punctuation">{</span> | 93 | <span class="keyword">impl</span> <span class="struct">FooCopy</span> <span class="punctuation">{</span> |
94 | <span class="keyword">fn</span> <span class="function declaration">baz</span><span class="punctuation">(</span><span class="self_keyword">self</span><span class="punctuation">,</span> <span class="value_param declaration">f</span><span class="punctuation">:</span> <span class="struct">FooCopy</span><span class="punctuation">)</span> <span class="operator">-></span> <span class="builtin_type">u32</span> <span class="punctuation">{</span> | 94 | <span class="keyword">fn</span> <span class="function declaration">baz</span><span class="punctuation">(</span><span class="self_keyword">self</span><span class="punctuation">,</span> <span class="value_param declaration">f</span><span class="punctuation">:</span> <span class="struct">FooCopy</span><span class="punctuation">)</span> <span class="operator">-></span> <span class="builtin_type">u32</span> <span class="punctuation">{</span> |
95 | <span class="value_param">f</span><span class="punctuation">.</span><span class="function">baz</span><span class="punctuation">(</span><span class="self_keyword">self</span><span class="punctuation">)</span> | 95 | <span class="value_param">f</span><span class="operator">.</span><span class="function">baz</span><span class="punctuation">(</span><span class="self_keyword">self</span><span class="punctuation">)</span> |
96 | <span class="punctuation">}</span> | 96 | <span class="punctuation">}</span> |
97 | 97 | ||
98 | <span class="keyword">fn</span> <span class="function declaration">qux</span><span class="punctuation">(</span><span class="operator">&</span><span class="keyword">mut</span> <span class="self_keyword mutable">self</span><span class="punctuation">)</span> <span class="punctuation">{</span> | 98 | <span class="keyword">fn</span> <span class="function declaration">qux</span><span class="punctuation">(</span><span class="operator">&</span><span class="keyword">mut</span> <span class="self_keyword mutable">self</span><span class="punctuation">)</span> <span class="punctuation">{</span> |
99 | <span class="self_keyword mutable">self</span><span class="punctuation">.</span><span class="field">x</span> <span class="operator">=</span> <span class="numeric_literal">0</span><span class="punctuation">;</span> | 99 | <span class="self_keyword mutable">self</span><span class="operator">.</span><span class="field">x</span> <span class="operator">=</span> <span class="numeric_literal">0</span><span class="punctuation">;</span> |
100 | <span class="punctuation">}</span> | 100 | <span class="punctuation">}</span> |
101 | 101 | ||
102 | <span class="keyword">fn</span> <span class="function declaration">quop</span><span class="punctuation">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="punctuation">)</span> <span class="operator">-></span> <span class="builtin_type">u32</span> <span class="punctuation">{</span> | 102 | <span class="keyword">fn</span> <span class="function declaration">quop</span><span class="punctuation">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="punctuation">)</span> <span class="operator">-></span> <span class="builtin_type">u32</span> <span class="punctuation">{</span> |
103 | <span class="self_keyword">self</span><span class="punctuation">.</span><span class="field">x</span> | 103 | <span class="self_keyword">self</span><span class="operator">.</span><span class="field">x</span> |
104 | <span class="punctuation">}</span> | 104 | <span class="punctuation">}</span> |
105 | <span class="punctuation">}</span> | 105 | <span class="punctuation">}</span> |
106 | 106 | ||
@@ -152,10 +152,10 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
152 | <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">vec</span> <span class="operator">=</span> <span class="unresolved_reference">Vec</span><span class="operator">::</span><span class="unresolved_reference">new</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> | 152 | <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">vec</span> <span class="operator">=</span> <span class="unresolved_reference">Vec</span><span class="operator">::</span><span class="unresolved_reference">new</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> |
153 | <span class="keyword control">if</span> <span class="bool_literal">true</span> <span class="punctuation">{</span> | 153 | <span class="keyword control">if</span> <span class="bool_literal">true</span> <span class="punctuation">{</span> |
154 | <span class="keyword">let</span> <span class="variable declaration">x</span> <span class="operator">=</span> <span class="numeric_literal">92</span><span class="punctuation">;</span> | 154 | <span class="keyword">let</span> <span class="variable declaration">x</span> <span class="operator">=</span> <span class="numeric_literal">92</span><span class="punctuation">;</span> |
155 | <span class="variable mutable">vec</span><span class="punctuation">.</span><span class="unresolved_reference">push</span><span class="punctuation">(</span><span class="struct">Foo</span> <span class="punctuation">{</span> <span class="field">x</span><span class="punctuation">,</span> <span class="field">y</span><span class="punctuation">:</span> <span class="numeric_literal">1</span> <span class="punctuation">}</span><span class="punctuation">)</span><span class="punctuation">;</span> | 155 | <span class="variable mutable">vec</span><span class="operator">.</span><span class="unresolved_reference">push</span><span class="punctuation">(</span><span class="struct">Foo</span> <span class="punctuation">{</span> <span class="field">x</span><span class="punctuation">,</span> <span class="field">y</span><span class="punctuation">:</span> <span class="numeric_literal">1</span> <span class="punctuation">}</span><span class="punctuation">)</span><span class="punctuation">;</span> |
156 | <span class="punctuation">}</span> | 156 | <span class="punctuation">}</span> |
157 | <span class="keyword unsafe">unsafe</span> <span class="punctuation">{</span> | 157 | <span class="keyword unsafe">unsafe</span> <span class="punctuation">{</span> |
158 | <span class="variable mutable">vec</span><span class="punctuation">.</span><span class="unresolved_reference">set_len</span><span class="punctuation">(</span><span class="numeric_literal">0</span><span class="punctuation">)</span><span class="punctuation">;</span> | 158 | <span class="variable mutable">vec</span><span class="operator">.</span><span class="unresolved_reference">set_len</span><span class="punctuation">(</span><span class="numeric_literal">0</span><span class="punctuation">)</span><span class="punctuation">;</span> |
159 | <span class="static mutable unsafe">STATIC_MUT</span> <span class="operator">=</span> <span class="numeric_literal">1</span><span class="punctuation">;</span> | 159 | <span class="static mutable unsafe">STATIC_MUT</span> <span class="operator">=</span> <span class="numeric_literal">1</span><span class="punctuation">;</span> |
160 | <span class="punctuation">}</span> | 160 | <span class="punctuation">}</span> |
161 | 161 | ||
@@ -175,14 +175,14 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
175 | 175 | ||
176 | <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">foo</span> <span class="operator">=</span> <span class="struct">Foo</span> <span class="punctuation">{</span> <span class="field">x</span><span class="punctuation">,</span> <span class="field">y</span><span class="punctuation">:</span> <span class="variable mutable">x</span> <span class="punctuation">}</span><span class="punctuation">;</span> | 176 | <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">foo</span> <span class="operator">=</span> <span class="struct">Foo</span> <span class="punctuation">{</span> <span class="field">x</span><span class="punctuation">,</span> <span class="field">y</span><span class="punctuation">:</span> <span class="variable mutable">x</span> <span class="punctuation">}</span><span class="punctuation">;</span> |
177 | <span class="keyword">let</span> <span class="variable declaration">foo2</span> <span class="operator">=</span> <span class="struct">Foo</span> <span class="punctuation">{</span> <span class="field">x</span><span class="punctuation">,</span> <span class="field">y</span><span class="punctuation">:</span> <span class="variable mutable">x</span> <span class="punctuation">}</span><span class="punctuation">;</span> | 177 | <span class="keyword">let</span> <span class="variable declaration">foo2</span> <span class="operator">=</span> <span class="struct">Foo</span> <span class="punctuation">{</span> <span class="field">x</span><span class="punctuation">,</span> <span class="field">y</span><span class="punctuation">:</span> <span class="variable mutable">x</span> <span class="punctuation">}</span><span class="punctuation">;</span> |
178 | <span class="variable mutable">foo</span><span class="punctuation">.</span><span class="function">quop</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> | 178 | <span class="variable mutable">foo</span><span class="operator">.</span><span class="function">quop</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> |
179 | <span class="variable mutable">foo</span><span class="punctuation">.</span><span class="function mutable">qux</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> | 179 | <span class="variable mutable">foo</span><span class="operator">.</span><span class="function mutable">qux</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> |
180 | <span class="variable mutable">foo</span><span class="punctuation">.</span><span class="function consuming">baz</span><span class="punctuation">(</span><span class="variable consuming">foo2</span><span class="punctuation">)</span><span class="punctuation">;</span> | 180 | <span class="variable mutable">foo</span><span class="operator">.</span><span class="function consuming">baz</span><span class="punctuation">(</span><span class="variable consuming">foo2</span><span class="punctuation">)</span><span class="punctuation">;</span> |
181 | 181 | ||
182 | <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">copy</span> <span class="operator">=</span> <span class="struct">FooCopy</span> <span class="punctuation">{</span> <span class="field">x</span> <span class="punctuation">}</span><span class="punctuation">;</span> | 182 | <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">copy</span> <span class="operator">=</span> <span class="struct">FooCopy</span> <span class="punctuation">{</span> <span class="field">x</span> <span class="punctuation">}</span><span class="punctuation">;</span> |
183 | <span class="variable mutable">copy</span><span class="punctuation">.</span><span class="function">quop</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> | 183 | <span class="variable mutable">copy</span><span class="operator">.</span><span class="function">quop</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> |
184 | <span class="variable mutable">copy</span><span class="punctuation">.</span><span class="function mutable">qux</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> | 184 | <span class="variable mutable">copy</span><span class="operator">.</span><span class="function mutable">qux</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> |
185 | <span class="variable mutable">copy</span><span class="punctuation">.</span><span class="function">baz</span><span class="punctuation">(</span><span class="variable mutable">copy</span><span class="punctuation">)</span><span class="punctuation">;</span> | 185 | <span class="variable mutable">copy</span><span class="operator">.</span><span class="function">baz</span><span class="punctuation">(</span><span class="variable mutable">copy</span><span class="punctuation">)</span><span class="punctuation">;</span> |
186 | 186 | ||
187 | <span class="keyword">let</span> <span class="variable declaration callable">a</span> <span class="operator">=</span> <span class="punctuation">|</span><span class="value_param declaration">x</span><span class="punctuation">|</span> <span class="value_param">x</span><span class="punctuation">;</span> | 187 | <span class="keyword">let</span> <span class="variable declaration callable">a</span> <span class="operator">=</span> <span class="punctuation">|</span><span class="value_param declaration">x</span><span class="punctuation">|</span> <span class="value_param">x</span><span class="punctuation">;</span> |
188 | <span class="keyword">let</span> <span class="variable declaration callable">bar</span> <span class="operator">=</span> <span class="struct">Foo</span><span class="operator">::</span><span class="function">baz</span><span class="punctuation">;</span> | 188 | <span class="keyword">let</span> <span class="variable declaration callable">bar</span> <span class="operator">=</span> <span class="struct">Foo</span><span class="operator">::</span><span class="function">baz</span><span class="punctuation">;</span> |
diff --git a/crates/ide/src/syntax_highlighting/test_data/rainbow_highlighting.html b/crates/ide/src/syntax_highlighting/test_data/rainbow_highlighting.html index 401e87a73..c7589605f 100644 --- a/crates/ide/src/syntax_highlighting/test_data/rainbow_highlighting.html +++ b/crates/ide/src/syntax_highlighting/test_data/rainbow_highlighting.html | |||
@@ -37,11 +37,11 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
37 | </style> | 37 | </style> |
38 | <pre><code><span class="keyword">fn</span> <span class="function declaration">main</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span> | 38 | <pre><code><span class="keyword">fn</span> <span class="function declaration">main</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span> |
39 | <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="8121853618659664005" style="color: hsl(273,88%,88%);">hello</span> <span class="operator">=</span> <span class="string_literal">"hello"</span><span class="punctuation">;</span> | 39 | <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="8121853618659664005" style="color: hsl(273,88%,88%);">hello</span> <span class="operator">=</span> <span class="string_literal">"hello"</span><span class="punctuation">;</span> |
40 | <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="2705725358298919760" style="color: hsl(76,47%,83%);">x</span> <span class="operator">=</span> <span class="variable" data-binding-hash="8121853618659664005" style="color: hsl(273,88%,88%);">hello</span><span class="punctuation">.</span><span class="unresolved_reference">to_string</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> | 40 | <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="2705725358298919760" style="color: hsl(76,47%,83%);">x</span> <span class="operator">=</span> <span class="variable" data-binding-hash="8121853618659664005" style="color: hsl(273,88%,88%);">hello</span><span class="operator">.</span><span class="unresolved_reference">to_string</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> |
41 | <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="3365759661443752373" style="color: hsl(15,86%,51%);">y</span> <span class="operator">=</span> <span class="variable" data-binding-hash="8121853618659664005" style="color: hsl(273,88%,88%);">hello</span><span class="punctuation">.</span><span class="unresolved_reference">to_string</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> | 41 | <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="3365759661443752373" style="color: hsl(15,86%,51%);">y</span> <span class="operator">=</span> <span class="variable" data-binding-hash="8121853618659664005" style="color: hsl(273,88%,88%);">hello</span><span class="operator">.</span><span class="unresolved_reference">to_string</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> |
42 | 42 | ||
43 | <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="794745962933817518" style="color: hsl(127,71%,87%);">x</span> <span class="operator">=</span> <span class="string_literal">"other color please!"</span><span class="punctuation">;</span> | 43 | <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="794745962933817518" style="color: hsl(127,71%,87%);">x</span> <span class="operator">=</span> <span class="string_literal">"other color please!"</span><span class="punctuation">;</span> |
44 | <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="6717528807933952652" style="color: hsl(90,74%,79%);">y</span> <span class="operator">=</span> <span class="variable" data-binding-hash="794745962933817518" style="color: hsl(127,71%,87%);">x</span><span class="punctuation">.</span><span class="unresolved_reference">to_string</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> | 44 | <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="6717528807933952652" style="color: hsl(90,74%,79%);">y</span> <span class="operator">=</span> <span class="variable" data-binding-hash="794745962933817518" style="color: hsl(127,71%,87%);">x</span><span class="operator">.</span><span class="unresolved_reference">to_string</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> |
45 | <span class="punctuation">}</span> | 45 | <span class="punctuation">}</span> |
46 | 46 | ||
47 | <span class="keyword">fn</span> <span class="function declaration">bar</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span> | 47 | <span class="keyword">fn</span> <span class="function declaration">bar</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span> |
diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs index 2b667b0d4..1dc018a16 100644 --- a/crates/ide/src/syntax_highlighting/tests.rs +++ b/crates/ide/src/syntax_highlighting/tests.rs | |||
@@ -340,6 +340,8 @@ fn main() { | |||
340 | 340 | ||
341 | println!("{\x41}", A = 92); | 341 | println!("{\x41}", A = 92); |
342 | println!("{ничоси}", ничоси = 92); | 342 | println!("{ничоси}", ничоси = 92); |
343 | |||
344 | println!("{:x?} {} ", thingy, n2); | ||
343 | }"# | 345 | }"# |
344 | .trim(), | 346 | .trim(), |
345 | expect_file!["./test_data/highlight_strings.html"], | 347 | expect_file!["./test_data/highlight_strings.html"], |
@@ -513,6 +515,34 @@ fn test_extern_crate() { | |||
513 | ); | 515 | ); |
514 | } | 516 | } |
515 | 517 | ||
518 | #[test] | ||
519 | fn test_associated_function() { | ||
520 | check_highlighting( | ||
521 | r#" | ||
522 | fn not_static() {} | ||
523 | |||
524 | struct foo {} | ||
525 | |||
526 | impl foo { | ||
527 | pub fn is_static() {} | ||
528 | pub fn is_not_static(&self) {} | ||
529 | } | ||
530 | |||
531 | trait t { | ||
532 | fn t_is_static() {} | ||
533 | fn t_is_not_static(&self) {} | ||
534 | } | ||
535 | |||
536 | impl t for foo { | ||
537 | pub fn is_static() {} | ||
538 | pub fn is_not_static(&self) {} | ||
539 | } | ||
540 | "#, | ||
541 | expect_file!["./test_data/highlight_assoc_functions.html"], | ||
542 | false, | ||
543 | ) | ||
544 | } | ||
545 | |||
516 | /// Highlights the code given by the `ra_fixture` argument, renders the | 546 | /// Highlights the code given by the `ra_fixture` argument, renders the |
517 | /// result as HTML, and compares it with the HTML file given as `snapshot`. | 547 | /// result as HTML, and compares it with the HTML file given as `snapshot`. |
518 | /// Note that the `snapshot` file is overwritten by the rendered HTML. | 548 | /// Note that the `snapshot` file is overwritten by the rendered HTML. |
diff --git a/crates/ide_db/src/apply_change.rs b/crates/ide_db/src/apply_change.rs index da16fa21d..987191fe3 100644 --- a/crates/ide_db/src/apply_change.rs +++ b/crates/ide_db/src/apply_change.rs | |||
@@ -76,7 +76,7 @@ impl RootDatabase { | |||
76 | let sweep = SweepStrategy::default().discard_values().sweep_all_revisions(); | 76 | let sweep = SweepStrategy::default().discard_values().sweep_all_revisions(); |
77 | 77 | ||
78 | base_db::ParseQuery.in_db(self).sweep(sweep); | 78 | base_db::ParseQuery.in_db(self).sweep(sweep); |
79 | hir::db::ParseMacroQuery.in_db(self).sweep(sweep); | 79 | hir::db::ParseMacroExpansionQuery.in_db(self).sweep(sweep); |
80 | 80 | ||
81 | // Macros do take significant space, but less then the syntax trees | 81 | // Macros do take significant space, but less then the syntax trees |
82 | // self.query(hir::db::MacroDefQuery).sweep(sweep); | 82 | // self.query(hir::db::MacroDefQuery).sweep(sweep); |
@@ -143,7 +143,7 @@ impl RootDatabase { | |||
143 | hir::db::AstIdMapQuery | 143 | hir::db::AstIdMapQuery |
144 | hir::db::MacroArgTextQuery | 144 | hir::db::MacroArgTextQuery |
145 | hir::db::MacroDefQuery | 145 | hir::db::MacroDefQuery |
146 | hir::db::ParseMacroQuery | 146 | hir::db::ParseMacroExpansionQuery |
147 | hir::db::MacroExpandQuery | 147 | hir::db::MacroExpandQuery |
148 | 148 | ||
149 | // DefDatabase | 149 | // DefDatabase |
diff --git a/crates/ide_db/src/imports_locator.rs b/crates/ide_db/src/imports_locator.rs index df74be00b..9d8ea7368 100644 --- a/crates/ide_db/src/imports_locator.rs +++ b/crates/ide_db/src/imports_locator.rs | |||
@@ -1,36 +1,70 @@ | |||
1 | //! This module contains an import search funcionality that is provided to the assists module. | 1 | //! This module contains an import search funcionality 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 hir::{Crate, MacroDef, ModuleDef, Semantics}; | 4 | use hir::{import_map, Crate, MacroDef, ModuleDef, Semantics}; |
5 | use syntax::{ast, AstNode, SyntaxKind::NAME}; | 5 | use syntax::{ast, AstNode, SyntaxKind::NAME}; |
6 | 6 | ||
7 | use crate::{ | 7 | use crate::{ |
8 | defs::{Definition, NameClass}, | 8 | defs::{Definition, NameClass}, |
9 | symbol_index::{self, FileSymbol, Query}, | 9 | symbol_index::{self, FileSymbol}, |
10 | RootDatabase, | 10 | RootDatabase, |
11 | }; | 11 | }; |
12 | use either::Either; | 12 | use either::Either; |
13 | use rustc_hash::FxHashSet; | 13 | use rustc_hash::FxHashSet; |
14 | 14 | ||
15 | pub fn find_imports<'a>( | 15 | pub fn find_exact_imports<'a>( |
16 | sema: &Semantics<'a, RootDatabase>, | 16 | sema: &Semantics<'a, RootDatabase>, |
17 | krate: Crate, | 17 | krate: Crate, |
18 | name_to_import: &str, | 18 | name_to_import: &str, |
19 | ) -> Vec<Either<ModuleDef, MacroDef>> { | 19 | ) -> impl Iterator<Item = Either<ModuleDef, MacroDef>> { |
20 | let _p = profile::span("search_for_imports"); | 20 | let _p = profile::span("find_exact_imports"); |
21 | find_imports( | ||
22 | sema, | ||
23 | krate, | ||
24 | { | ||
25 | let mut local_query = symbol_index::Query::new(name_to_import.to_string()); | ||
26 | local_query.exact(); | ||
27 | local_query.limit(40); | ||
28 | local_query | ||
29 | }, | ||
30 | import_map::Query::new(name_to_import).anchor_end().case_sensitive().limit(40), | ||
31 | ) | ||
32 | } | ||
33 | |||
34 | pub fn find_similar_imports<'a>( | ||
35 | sema: &Semantics<'a, RootDatabase>, | ||
36 | krate: Crate, | ||
37 | name_to_import: &str, | ||
38 | limit: usize, | ||
39 | ) -> impl Iterator<Item = Either<ModuleDef, MacroDef>> { | ||
40 | let _p = profile::span("find_similar_imports"); | ||
41 | find_imports( | ||
42 | sema, | ||
43 | krate, | ||
44 | { | ||
45 | let mut local_query = symbol_index::Query::new(name_to_import.to_string()); | ||
46 | local_query.limit(limit); | ||
47 | local_query | ||
48 | }, | ||
49 | import_map::Query::new(name_to_import).limit(limit), | ||
50 | ) | ||
51 | } | ||
52 | |||
53 | fn find_imports<'a>( | ||
54 | sema: &Semantics<'a, RootDatabase>, | ||
55 | krate: Crate, | ||
56 | local_query: symbol_index::Query, | ||
57 | external_query: import_map::Query, | ||
58 | ) -> impl Iterator<Item = Either<ModuleDef, MacroDef>> { | ||
59 | let _p = profile::span("find_similar_imports"); | ||
21 | let db = sema.db; | 60 | let db = sema.db; |
22 | 61 | ||
23 | // Query dependencies first. | 62 | // Query dependencies first. |
24 | let mut candidates: FxHashSet<_> = | 63 | let mut candidates: FxHashSet<_> = |
25 | krate.query_external_importables(db, name_to_import).collect(); | 64 | krate.query_external_importables(db, external_query).collect(); |
26 | 65 | ||
27 | // Query the local crate using the symbol index. | 66 | // Query the local crate using the symbol index. |
28 | let local_results = { | 67 | let local_results = symbol_index::crate_symbols(db, krate.into(), local_query); |
29 | let mut query = Query::new(name_to_import.to_string()); | ||
30 | query.exact(); | ||
31 | query.limit(40); | ||
32 | symbol_index::crate_symbols(db, krate.into(), query) | ||
33 | }; | ||
34 | 68 | ||
35 | candidates.extend( | 69 | candidates.extend( |
36 | local_results | 70 | local_results |
@@ -43,7 +77,7 @@ pub fn find_imports<'a>( | |||
43 | }), | 77 | }), |
44 | ); | 78 | ); |
45 | 79 | ||
46 | candidates.into_iter().collect() | 80 | candidates.into_iter() |
47 | } | 81 | } |
48 | 82 | ||
49 | fn get_name_definition<'a>( | 83 | fn get_name_definition<'a>( |
diff --git a/crates/ide_db/src/lib.rs b/crates/ide_db/src/lib.rs index 38ebdbf79..05139a651 100644 --- a/crates/ide_db/src/lib.rs +++ b/crates/ide_db/src/lib.rs | |||
@@ -113,7 +113,7 @@ impl RootDatabase { | |||
113 | pub fn update_lru_capacity(&mut self, lru_capacity: Option<usize>) { | 113 | pub fn update_lru_capacity(&mut self, lru_capacity: Option<usize>) { |
114 | let lru_capacity = lru_capacity.unwrap_or(base_db::DEFAULT_LRU_CAP); | 114 | let lru_capacity = lru_capacity.unwrap_or(base_db::DEFAULT_LRU_CAP); |
115 | base_db::ParseQuery.in_db_mut(self).set_lru_capacity(lru_capacity); | 115 | base_db::ParseQuery.in_db_mut(self).set_lru_capacity(lru_capacity); |
116 | hir::db::ParseMacroQuery.in_db_mut(self).set_lru_capacity(lru_capacity); | 116 | hir::db::ParseMacroExpansionQuery.in_db_mut(self).set_lru_capacity(lru_capacity); |
117 | hir::db::MacroExpandQuery.in_db_mut(self).set_lru_capacity(lru_capacity); | 117 | hir::db::MacroExpandQuery.in_db_mut(self).set_lru_capacity(lru_capacity); |
118 | } | 118 | } |
119 | } | 119 | } |
diff --git a/crates/ide_db/src/search.rs b/crates/ide_db/src/search.rs index a24335240..a3e765d05 100644 --- a/crates/ide_db/src/search.rs +++ b/crates/ide_db/src/search.rs | |||
@@ -30,6 +30,7 @@ pub enum ReferenceKind { | |||
30 | FieldShorthandForField, | 30 | FieldShorthandForField, |
31 | FieldShorthandForLocal, | 31 | FieldShorthandForLocal, |
32 | StructLiteral, | 32 | StructLiteral, |
33 | RecordFieldExprOrPat, | ||
33 | Other, | 34 | Other, |
34 | } | 35 | } |
35 | 36 | ||
@@ -278,8 +279,9 @@ impl<'a> FindUsages<'a> { | |||
278 | ) -> bool { | 279 | ) -> bool { |
279 | match NameRefClass::classify(self.sema, &name_ref) { | 280 | match NameRefClass::classify(self.sema, &name_ref) { |
280 | Some(NameRefClass::Definition(def)) if &def == self.def => { | 281 | Some(NameRefClass::Definition(def)) if &def == self.def => { |
281 | let kind = if is_record_lit_name_ref(&name_ref) || is_call_expr_name_ref(&name_ref) | 282 | let kind = if is_record_field_expr_or_pat(&name_ref) { |
282 | { | 283 | ReferenceKind::RecordFieldExprOrPat |
284 | } else if is_record_lit_name_ref(&name_ref) || is_call_expr_name_ref(&name_ref) { | ||
283 | ReferenceKind::StructLiteral | 285 | ReferenceKind::StructLiteral |
284 | } else { | 286 | } else { |
285 | ReferenceKind::Other | 287 | ReferenceKind::Other |
@@ -385,3 +387,17 @@ fn is_record_lit_name_ref(name_ref: &ast::NameRef) -> bool { | |||
385 | .map(|p| p.name_ref().as_ref() == Some(name_ref)) | 387 | .map(|p| p.name_ref().as_ref() == Some(name_ref)) |
386 | .unwrap_or(false) | 388 | .unwrap_or(false) |
387 | } | 389 | } |
390 | |||
391 | fn is_record_field_expr_or_pat(name_ref: &ast::NameRef) -> bool { | ||
392 | if let Some(parent) = name_ref.syntax().parent() { | ||
393 | match_ast! { | ||
394 | match parent { | ||
395 | ast::RecordExprField(it) => true, | ||
396 | ast::RecordPatField(_it) => true, | ||
397 | _ => false, | ||
398 | } | ||
399 | } | ||
400 | } else { | ||
401 | false | ||
402 | } | ||
403 | } | ||
diff --git a/crates/parser/src/grammar/items.rs b/crates/parser/src/grammar/items.rs index 780bc470a..ad29b82f7 100644 --- a/crates/parser/src/grammar/items.rs +++ b/crates/parser/src/grammar/items.rs | |||
@@ -112,7 +112,7 @@ pub(super) fn maybe_item(p: &mut Parser, m: Marker) -> Result<(), Marker> { | |||
112 | has_mods = true; | 112 | has_mods = true; |
113 | } | 113 | } |
114 | 114 | ||
115 | if p.at(T![extern]) { | 115 | if p.at(T![extern]) && p.nth(1) != T!['{'] && (p.nth(1) != STRING || p.nth(2) != T!['{']) { |
116 | has_mods = true; | 116 | has_mods = true; |
117 | abi(p); | 117 | abi(p); |
118 | } | 118 | } |
@@ -181,6 +181,14 @@ pub(super) fn maybe_item(p: &mut Parser, m: Marker) -> Result<(), Marker> { | |||
181 | T![type] => { | 181 | T![type] => { |
182 | type_alias(p, m); | 182 | type_alias(p, m); |
183 | } | 183 | } |
184 | |||
185 | // unsafe extern "C" {} | ||
186 | T![extern] => { | ||
187 | abi(p); | ||
188 | extern_item_list(p); | ||
189 | m.complete(p, EXTERN_BLOCK); | ||
190 | } | ||
191 | |||
184 | _ => { | 192 | _ => { |
185 | if !has_visibility && !has_mods { | 193 | if !has_visibility && !has_mods { |
186 | return Err(m); | 194 | return Err(m); |
diff --git a/crates/project_model/src/cargo_workspace.rs b/crates/project_model/src/cargo_workspace.rs index d5f6a4025..540b57ae4 100644 --- a/crates/project_model/src/cargo_workspace.rs +++ b/crates/project_model/src/cargo_workspace.rs | |||
@@ -64,6 +64,13 @@ pub struct CargoConfig { | |||
64 | 64 | ||
65 | /// rustc target | 65 | /// rustc target |
66 | pub target: Option<String>, | 66 | pub target: Option<String>, |
67 | |||
68 | /// Don't load sysroot crates (`std`, `core` & friends). Might be useful | ||
69 | /// when debugging isolated issues. | ||
70 | pub no_sysroot: bool, | ||
71 | |||
72 | /// rustc private crate source | ||
73 | pub rustc_source: Option<AbsPathBuf>, | ||
67 | } | 74 | } |
68 | 75 | ||
69 | pub type Package = Idx<PackageData>; | 76 | pub type Package = Idx<PackageData>; |
@@ -137,27 +144,27 @@ impl PackageData { | |||
137 | impl CargoWorkspace { | 144 | impl CargoWorkspace { |
138 | pub fn from_cargo_metadata( | 145 | pub fn from_cargo_metadata( |
139 | cargo_toml: &AbsPath, | 146 | cargo_toml: &AbsPath, |
140 | cargo_features: &CargoConfig, | 147 | config: &CargoConfig, |
141 | ) -> Result<CargoWorkspace> { | 148 | ) -> Result<CargoWorkspace> { |
142 | let mut meta = MetadataCommand::new(); | 149 | let mut meta = MetadataCommand::new(); |
143 | meta.cargo_path(toolchain::cargo()); | 150 | meta.cargo_path(toolchain::cargo()); |
144 | meta.manifest_path(cargo_toml.to_path_buf()); | 151 | meta.manifest_path(cargo_toml.to_path_buf()); |
145 | if cargo_features.all_features { | 152 | if config.all_features { |
146 | meta.features(CargoOpt::AllFeatures); | 153 | meta.features(CargoOpt::AllFeatures); |
147 | } else { | 154 | } else { |
148 | if cargo_features.no_default_features { | 155 | if config.no_default_features { |
149 | // FIXME: `NoDefaultFeatures` is mutual exclusive with `SomeFeatures` | 156 | // FIXME: `NoDefaultFeatures` is mutual exclusive with `SomeFeatures` |
150 | // https://github.com/oli-obk/cargo_metadata/issues/79 | 157 | // https://github.com/oli-obk/cargo_metadata/issues/79 |
151 | meta.features(CargoOpt::NoDefaultFeatures); | 158 | meta.features(CargoOpt::NoDefaultFeatures); |
152 | } | 159 | } |
153 | if !cargo_features.features.is_empty() { | 160 | if !config.features.is_empty() { |
154 | meta.features(CargoOpt::SomeFeatures(cargo_features.features.clone())); | 161 | meta.features(CargoOpt::SomeFeatures(config.features.clone())); |
155 | } | 162 | } |
156 | } | 163 | } |
157 | if let Some(parent) = cargo_toml.parent() { | 164 | if let Some(parent) = cargo_toml.parent() { |
158 | meta.current_dir(parent.to_path_buf()); | 165 | meta.current_dir(parent.to_path_buf()); |
159 | } | 166 | } |
160 | if let Some(target) = cargo_features.target.as_ref() { | 167 | if let Some(target) = config.target.as_ref() { |
161 | meta.other_options(vec![String::from("--filter-platform"), target.clone()]); | 168 | meta.other_options(vec![String::from("--filter-platform"), target.clone()]); |
162 | } | 169 | } |
163 | let mut meta = meta.exec().with_context(|| { | 170 | let mut meta = meta.exec().with_context(|| { |
@@ -167,8 +174,8 @@ impl CargoWorkspace { | |||
167 | let mut out_dir_by_id = FxHashMap::default(); | 174 | let mut out_dir_by_id = FxHashMap::default(); |
168 | let mut cfgs = FxHashMap::default(); | 175 | let mut cfgs = FxHashMap::default(); |
169 | let mut proc_macro_dylib_paths = FxHashMap::default(); | 176 | let mut proc_macro_dylib_paths = FxHashMap::default(); |
170 | if cargo_features.load_out_dirs_from_check { | 177 | if config.load_out_dirs_from_check { |
171 | let resources = load_extern_resources(cargo_toml, cargo_features)?; | 178 | let resources = load_extern_resources(cargo_toml, config)?; |
172 | out_dir_by_id = resources.out_dirs; | 179 | out_dir_by_id = resources.out_dirs; |
173 | cfgs = resources.cfgs; | 180 | cfgs = resources.cfgs; |
174 | proc_macro_dylib_paths = resources.proc_dylib_paths; | 181 | proc_macro_dylib_paths = resources.proc_dylib_paths; |
diff --git a/crates/project_model/src/lib.rs b/crates/project_model/src/lib.rs index e92cfea59..24aa9b8fa 100644 --- a/crates/project_model/src/lib.rs +++ b/crates/project_model/src/lib.rs | |||
@@ -4,69 +4,27 @@ mod cargo_workspace; | |||
4 | mod project_json; | 4 | mod project_json; |
5 | mod sysroot; | 5 | mod sysroot; |
6 | mod cfg_flag; | 6 | mod cfg_flag; |
7 | mod workspace; | ||
7 | 8 | ||
8 | use std::{ | 9 | use std::{ |
9 | fmt, | 10 | fs::{read_dir, ReadDir}, |
10 | fs::{self, read_dir, ReadDir}, | ||
11 | io, | 11 | io, |
12 | process::Command, | 12 | process::Command, |
13 | }; | 13 | }; |
14 | 14 | ||
15 | use anyhow::{bail, Context, Result}; | 15 | use anyhow::{bail, Context, Result}; |
16 | use base_db::{CrateDisplayName, CrateGraph, CrateId, CrateName, Edition, Env, FileId}; | ||
17 | use cfg::CfgOptions; | ||
18 | use paths::{AbsPath, AbsPathBuf}; | 16 | use paths::{AbsPath, AbsPathBuf}; |
19 | use rustc_hash::{FxHashMap, FxHashSet}; | 17 | use rustc_hash::FxHashSet; |
20 | |||
21 | use crate::cfg_flag::CfgFlag; | ||
22 | 18 | ||
23 | pub use crate::{ | 19 | pub use crate::{ |
24 | cargo_workspace::{CargoConfig, CargoWorkspace, Package, Target, TargetKind}, | 20 | cargo_workspace::{CargoConfig, CargoWorkspace, Package, Target, TargetKind}, |
25 | project_json::{ProjectJson, ProjectJsonData}, | 21 | project_json::{ProjectJson, ProjectJsonData}, |
26 | sysroot::Sysroot, | 22 | sysroot::Sysroot, |
23 | workspace::{PackageRoot, ProjectWorkspace}, | ||
27 | }; | 24 | }; |
28 | 25 | ||
29 | pub use proc_macro_api::ProcMacroClient; | 26 | pub use proc_macro_api::ProcMacroClient; |
30 | 27 | ||
31 | #[derive(Clone, Eq, PartialEq)] | ||
32 | pub enum ProjectWorkspace { | ||
33 | /// Project workspace was discovered by running `cargo metadata` and `rustc --print sysroot`. | ||
34 | Cargo { cargo: CargoWorkspace, sysroot: Sysroot }, | ||
35 | /// Project workspace was manually specified using a `rust-project.json` file. | ||
36 | Json { project: ProjectJson, sysroot: Option<Sysroot> }, | ||
37 | } | ||
38 | |||
39 | impl fmt::Debug for ProjectWorkspace { | ||
40 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
41 | match self { | ||
42 | ProjectWorkspace::Cargo { cargo, sysroot } => f | ||
43 | .debug_struct("Cargo") | ||
44 | .field("n_packages", &cargo.packages().len()) | ||
45 | .field("n_sysroot_crates", &sysroot.crates().len()) | ||
46 | .finish(), | ||
47 | ProjectWorkspace::Json { project, sysroot } => { | ||
48 | let mut debug_struct = f.debug_struct("Json"); | ||
49 | debug_struct.field("n_crates", &project.n_crates()); | ||
50 | if let Some(sysroot) = sysroot { | ||
51 | debug_struct.field("n_sysroot_crates", &sysroot.crates().len()); | ||
52 | } | ||
53 | debug_struct.finish() | ||
54 | } | ||
55 | } | ||
56 | } | ||
57 | } | ||
58 | |||
59 | /// `PackageRoot` describes a package root folder. | ||
60 | /// Which may be an external dependency, or a member of | ||
61 | /// the current workspace. | ||
62 | #[derive(Debug, Clone, Eq, PartialEq, Hash)] | ||
63 | pub struct PackageRoot { | ||
64 | /// Is a member of the current workspace | ||
65 | pub is_member: bool, | ||
66 | pub include: Vec<AbsPathBuf>, | ||
67 | pub exclude: Vec<AbsPathBuf>, | ||
68 | } | ||
69 | |||
70 | #[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)] | 28 | #[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)] |
71 | pub enum ProjectManifest { | 29 | pub enum ProjectManifest { |
72 | ProjectJson(AbsPathBuf), | 30 | ProjectJson(AbsPathBuf), |
@@ -153,376 +111,6 @@ impl ProjectManifest { | |||
153 | } | 111 | } |
154 | } | 112 | } |
155 | 113 | ||
156 | impl ProjectWorkspace { | ||
157 | pub fn load( | ||
158 | manifest: ProjectManifest, | ||
159 | cargo_config: &CargoConfig, | ||
160 | with_sysroot: bool, | ||
161 | ) -> Result<ProjectWorkspace> { | ||
162 | let res = match manifest { | ||
163 | ProjectManifest::ProjectJson(project_json) => { | ||
164 | let file = fs::read_to_string(&project_json).with_context(|| { | ||
165 | format!("Failed to read json file {}", project_json.display()) | ||
166 | })?; | ||
167 | let data = serde_json::from_str(&file).with_context(|| { | ||
168 | format!("Failed to deserialize json file {}", project_json.display()) | ||
169 | })?; | ||
170 | let project_location = project_json.parent().unwrap().to_path_buf(); | ||
171 | let project = ProjectJson::new(&project_location, data); | ||
172 | let sysroot = match &project.sysroot_src { | ||
173 | Some(path) => Some(Sysroot::load(path)?), | ||
174 | None => None, | ||
175 | }; | ||
176 | ProjectWorkspace::Json { project, sysroot } | ||
177 | } | ||
178 | ProjectManifest::CargoToml(cargo_toml) => { | ||
179 | let cargo_version = utf8_stdout({ | ||
180 | let mut cmd = Command::new(toolchain::cargo()); | ||
181 | cmd.arg("--version"); | ||
182 | cmd | ||
183 | })?; | ||
184 | |||
185 | let cargo = CargoWorkspace::from_cargo_metadata(&cargo_toml, cargo_config) | ||
186 | .with_context(|| { | ||
187 | format!( | ||
188 | "Failed to read Cargo metadata from Cargo.toml file {}, {}", | ||
189 | cargo_toml.display(), | ||
190 | cargo_version | ||
191 | ) | ||
192 | })?; | ||
193 | let sysroot = if with_sysroot { | ||
194 | Sysroot::discover(&cargo_toml).with_context(|| { | ||
195 | format!( | ||
196 | "Failed to find sysroot for Cargo.toml file {}. Is rust-src installed?", | ||
197 | cargo_toml.display() | ||
198 | ) | ||
199 | })? | ||
200 | } else { | ||
201 | Sysroot::default() | ||
202 | }; | ||
203 | ProjectWorkspace::Cargo { cargo, sysroot } | ||
204 | } | ||
205 | }; | ||
206 | |||
207 | Ok(res) | ||
208 | } | ||
209 | |||
210 | pub fn load_inline(project_json: ProjectJson) -> Result<ProjectWorkspace> { | ||
211 | let sysroot = match &project_json.sysroot_src { | ||
212 | Some(path) => Some(Sysroot::load(path)?), | ||
213 | None => None, | ||
214 | }; | ||
215 | |||
216 | Ok(ProjectWorkspace::Json { project: project_json, sysroot }) | ||
217 | } | ||
218 | |||
219 | /// Returns the roots for the current `ProjectWorkspace` | ||
220 | /// The return type contains the path and whether or not | ||
221 | /// the root is a member of the current workspace | ||
222 | pub fn to_roots(&self) -> Vec<PackageRoot> { | ||
223 | match self { | ||
224 | ProjectWorkspace::Json { project, sysroot } => project | ||
225 | .crates() | ||
226 | .map(|(_, krate)| PackageRoot { | ||
227 | is_member: krate.is_workspace_member, | ||
228 | include: krate.include.clone(), | ||
229 | exclude: krate.exclude.clone(), | ||
230 | }) | ||
231 | .collect::<FxHashSet<_>>() | ||
232 | .into_iter() | ||
233 | .chain(sysroot.as_ref().into_iter().flat_map(|sysroot| { | ||
234 | sysroot.crates().map(move |krate| PackageRoot { | ||
235 | is_member: false, | ||
236 | include: vec![sysroot[krate].root_dir().to_path_buf()], | ||
237 | exclude: Vec::new(), | ||
238 | }) | ||
239 | })) | ||
240 | .collect::<Vec<_>>(), | ||
241 | ProjectWorkspace::Cargo { cargo, sysroot } => cargo | ||
242 | .packages() | ||
243 | .map(|pkg| { | ||
244 | let is_member = cargo[pkg].is_member; | ||
245 | let pkg_root = cargo[pkg].root().to_path_buf(); | ||
246 | |||
247 | let mut include = vec![pkg_root.clone()]; | ||
248 | include.extend(cargo[pkg].out_dir.clone()); | ||
249 | |||
250 | let mut exclude = vec![pkg_root.join(".git")]; | ||
251 | if is_member { | ||
252 | exclude.push(pkg_root.join("target")); | ||
253 | } else { | ||
254 | exclude.push(pkg_root.join("tests")); | ||
255 | exclude.push(pkg_root.join("examples")); | ||
256 | exclude.push(pkg_root.join("benches")); | ||
257 | } | ||
258 | PackageRoot { is_member, include, exclude } | ||
259 | }) | ||
260 | .chain(sysroot.crates().map(|krate| PackageRoot { | ||
261 | is_member: false, | ||
262 | include: vec![sysroot[krate].root_dir().to_path_buf()], | ||
263 | exclude: Vec::new(), | ||
264 | })) | ||
265 | .collect(), | ||
266 | } | ||
267 | } | ||
268 | |||
269 | pub fn proc_macro_dylib_paths(&self) -> Vec<AbsPathBuf> { | ||
270 | match self { | ||
271 | ProjectWorkspace::Json { project, sysroot: _ } => project | ||
272 | .crates() | ||
273 | .filter_map(|(_, krate)| krate.proc_macro_dylib_path.as_ref()) | ||
274 | .cloned() | ||
275 | .collect(), | ||
276 | ProjectWorkspace::Cargo { cargo, sysroot: _sysroot } => cargo | ||
277 | .packages() | ||
278 | .filter_map(|pkg| cargo[pkg].proc_macro_dylib_path.as_ref()) | ||
279 | .cloned() | ||
280 | .collect(), | ||
281 | } | ||
282 | } | ||
283 | |||
284 | pub fn n_packages(&self) -> usize { | ||
285 | match self { | ||
286 | ProjectWorkspace::Json { project, .. } => project.n_crates(), | ||
287 | ProjectWorkspace::Cargo { cargo, sysroot } => { | ||
288 | cargo.packages().len() + sysroot.crates().len() | ||
289 | } | ||
290 | } | ||
291 | } | ||
292 | |||
293 | pub fn to_crate_graph( | ||
294 | &self, | ||
295 | target: Option<&str>, | ||
296 | proc_macro_client: &ProcMacroClient, | ||
297 | load: &mut dyn FnMut(&AbsPath) -> Option<FileId>, | ||
298 | ) -> CrateGraph { | ||
299 | let mut crate_graph = CrateGraph::default(); | ||
300 | match self { | ||
301 | ProjectWorkspace::Json { project, sysroot } => { | ||
302 | let sysroot_dps = sysroot | ||
303 | .as_ref() | ||
304 | .map(|sysroot| sysroot_to_crate_graph(&mut crate_graph, sysroot, target, load)); | ||
305 | |||
306 | let mut cfg_cache: FxHashMap<Option<&str>, Vec<CfgFlag>> = FxHashMap::default(); | ||
307 | let crates: FxHashMap<_, _> = project | ||
308 | .crates() | ||
309 | .filter_map(|(crate_id, krate)| { | ||
310 | let file_path = &krate.root_module; | ||
311 | let file_id = match load(&file_path) { | ||
312 | Some(id) => id, | ||
313 | None => { | ||
314 | log::error!("failed to load crate root {}", file_path.display()); | ||
315 | return None; | ||
316 | } | ||
317 | }; | ||
318 | |||
319 | let env = krate.env.clone().into_iter().collect(); | ||
320 | let proc_macro = krate | ||
321 | .proc_macro_dylib_path | ||
322 | .clone() | ||
323 | .map(|it| proc_macro_client.by_dylib_path(&it)); | ||
324 | |||
325 | let target = krate.target.as_deref().or(target); | ||
326 | let target_cfgs = cfg_cache | ||
327 | .entry(target) | ||
328 | .or_insert_with(|| get_rustc_cfg_options(target)); | ||
329 | |||
330 | let mut cfg_options = CfgOptions::default(); | ||
331 | cfg_options.extend(target_cfgs.iter().chain(krate.cfg.iter()).cloned()); | ||
332 | |||
333 | Some(( | ||
334 | crate_id, | ||
335 | crate_graph.add_crate_root( | ||
336 | file_id, | ||
337 | krate.edition, | ||
338 | krate.display_name.clone(), | ||
339 | cfg_options, | ||
340 | env, | ||
341 | proc_macro.unwrap_or_default(), | ||
342 | ), | ||
343 | )) | ||
344 | }) | ||
345 | .collect(); | ||
346 | |||
347 | for (from, krate) in project.crates() { | ||
348 | if let Some(&from) = crates.get(&from) { | ||
349 | if let Some((public_deps, _proc_macro)) = &sysroot_dps { | ||
350 | for (name, to) in public_deps.iter() { | ||
351 | if let Err(_) = crate_graph.add_dep(from, name.clone(), *to) { | ||
352 | log::error!("cyclic dependency on {} for {:?}", name, from) | ||
353 | } | ||
354 | } | ||
355 | } | ||
356 | |||
357 | for dep in &krate.deps { | ||
358 | let to_crate_id = dep.crate_id; | ||
359 | if let Some(&to) = crates.get(&to_crate_id) { | ||
360 | if let Err(_) = crate_graph.add_dep(from, dep.name.clone(), to) { | ||
361 | log::error!("cyclic dependency {:?} -> {:?}", from, to); | ||
362 | } | ||
363 | } | ||
364 | } | ||
365 | } | ||
366 | } | ||
367 | } | ||
368 | ProjectWorkspace::Cargo { cargo, sysroot } => { | ||
369 | let (public_deps, libproc_macro) = | ||
370 | sysroot_to_crate_graph(&mut crate_graph, sysroot, target, load); | ||
371 | |||
372 | let mut cfg_options = CfgOptions::default(); | ||
373 | cfg_options.extend(get_rustc_cfg_options(target)); | ||
374 | |||
375 | let mut pkg_to_lib_crate = FxHashMap::default(); | ||
376 | let mut pkg_crates = FxHashMap::default(); | ||
377 | |||
378 | // Add test cfg for non-sysroot crates | ||
379 | cfg_options.insert_atom("test".into()); | ||
380 | cfg_options.insert_atom("debug_assertions".into()); | ||
381 | |||
382 | // Next, create crates for each package, target pair | ||
383 | for pkg in cargo.packages() { | ||
384 | let mut lib_tgt = None; | ||
385 | for &tgt in cargo[pkg].targets.iter() { | ||
386 | let root = cargo[tgt].root.as_path(); | ||
387 | if let Some(file_id) = load(root) { | ||
388 | let edition = cargo[pkg].edition; | ||
389 | let cfg_options = { | ||
390 | let mut opts = cfg_options.clone(); | ||
391 | for feature in cargo[pkg].features.iter() { | ||
392 | opts.insert_key_value("feature".into(), feature.into()); | ||
393 | } | ||
394 | opts.extend(cargo[pkg].cfgs.iter().cloned()); | ||
395 | opts | ||
396 | }; | ||
397 | let mut env = Env::default(); | ||
398 | if let Some(out_dir) = &cargo[pkg].out_dir { | ||
399 | // NOTE: cargo and rustc seem to hide non-UTF-8 strings from env! and option_env!() | ||
400 | if let Some(out_dir) = out_dir.to_str().map(|s| s.to_owned()) { | ||
401 | env.set("OUT_DIR", out_dir); | ||
402 | } | ||
403 | } | ||
404 | let proc_macro = cargo[pkg] | ||
405 | .proc_macro_dylib_path | ||
406 | .as_ref() | ||
407 | .map(|it| proc_macro_client.by_dylib_path(&it)) | ||
408 | .unwrap_or_default(); | ||
409 | |||
410 | let display_name = | ||
411 | CrateDisplayName::from_canonical_name(cargo[pkg].name.clone()); | ||
412 | let crate_id = crate_graph.add_crate_root( | ||
413 | file_id, | ||
414 | edition, | ||
415 | Some(display_name), | ||
416 | cfg_options, | ||
417 | env, | ||
418 | proc_macro.clone(), | ||
419 | ); | ||
420 | if cargo[tgt].kind == TargetKind::Lib { | ||
421 | lib_tgt = Some((crate_id, cargo[tgt].name.clone())); | ||
422 | pkg_to_lib_crate.insert(pkg, crate_id); | ||
423 | } | ||
424 | if cargo[tgt].is_proc_macro { | ||
425 | if let Some(proc_macro) = libproc_macro { | ||
426 | if let Err(_) = crate_graph.add_dep( | ||
427 | crate_id, | ||
428 | CrateName::new("proc_macro").unwrap(), | ||
429 | proc_macro, | ||
430 | ) { | ||
431 | log::error!( | ||
432 | "cyclic dependency on proc_macro for {}", | ||
433 | &cargo[pkg].name | ||
434 | ) | ||
435 | } | ||
436 | } | ||
437 | } | ||
438 | |||
439 | pkg_crates.entry(pkg).or_insert_with(Vec::new).push(crate_id); | ||
440 | } | ||
441 | } | ||
442 | |||
443 | // Set deps to the core, std and to the lib target of the current package | ||
444 | for &from in pkg_crates.get(&pkg).into_iter().flatten() { | ||
445 | if let Some((to, name)) = lib_tgt.clone() { | ||
446 | // For root projects with dashes in their name, | ||
447 | // cargo metadata does not do any normalization, | ||
448 | // so we do it ourselves currently | ||
449 | let name = CrateName::normalize_dashes(&name); | ||
450 | if to != from && crate_graph.add_dep(from, name, to).is_err() { | ||
451 | log::error!( | ||
452 | "cyclic dependency between targets of {}", | ||
453 | &cargo[pkg].name | ||
454 | ) | ||
455 | } | ||
456 | } | ||
457 | for (name, krate) in public_deps.iter() { | ||
458 | if let Err(_) = crate_graph.add_dep(from, name.clone(), *krate) { | ||
459 | log::error!( | ||
460 | "cyclic dependency on {} for {}", | ||
461 | name, | ||
462 | &cargo[pkg].name | ||
463 | ) | ||
464 | } | ||
465 | } | ||
466 | } | ||
467 | } | ||
468 | |||
469 | // Now add a dep edge from all targets of upstream to the lib | ||
470 | // target of downstream. | ||
471 | for pkg in cargo.packages() { | ||
472 | for dep in cargo[pkg].dependencies.iter() { | ||
473 | let name = CrateName::new(&dep.name).unwrap(); | ||
474 | if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) { | ||
475 | for &from in pkg_crates.get(&pkg).into_iter().flatten() { | ||
476 | if let Err(_) = crate_graph.add_dep(from, name.clone(), to) { | ||
477 | log::error!( | ||
478 | "cyclic dependency {} -> {}", | ||
479 | &cargo[pkg].name, | ||
480 | &cargo[dep.pkg].name | ||
481 | ) | ||
482 | } | ||
483 | } | ||
484 | } | ||
485 | } | ||
486 | } | ||
487 | } | ||
488 | } | ||
489 | if crate_graph.patch_cfg_if() { | ||
490 | log::debug!("Patched std to depend on cfg-if") | ||
491 | } else { | ||
492 | log::debug!("Did not patch std to depend on cfg-if") | ||
493 | } | ||
494 | crate_graph | ||
495 | } | ||
496 | } | ||
497 | |||
498 | fn get_rustc_cfg_options(target: Option<&str>) -> Vec<CfgFlag> { | ||
499 | let mut res = Vec::new(); | ||
500 | |||
501 | // Some nightly-only cfgs, which are required for stdlib | ||
502 | res.push(CfgFlag::Atom("target_thread_local".into())); | ||
503 | for &ty in ["8", "16", "32", "64", "cas", "ptr"].iter() { | ||
504 | for &key in ["target_has_atomic", "target_has_atomic_load_store"].iter() { | ||
505 | res.push(CfgFlag::KeyValue { key: key.to_string(), value: ty.into() }); | ||
506 | } | ||
507 | } | ||
508 | |||
509 | let rustc_cfgs = { | ||
510 | let mut cmd = Command::new(toolchain::rustc()); | ||
511 | cmd.args(&["--print", "cfg", "-O"]); | ||
512 | if let Some(target) = target { | ||
513 | cmd.args(&["--target", target]); | ||
514 | } | ||
515 | utf8_stdout(cmd) | ||
516 | }; | ||
517 | |||
518 | match rustc_cfgs { | ||
519 | Ok(rustc_cfgs) => res.extend(rustc_cfgs.lines().map(|it| it.parse().unwrap())), | ||
520 | Err(e) => log::error!("failed to get rustc cfgs: {:#}", e), | ||
521 | } | ||
522 | |||
523 | res | ||
524 | } | ||
525 | |||
526 | fn utf8_stdout(mut cmd: Command) -> Result<String> { | 114 | fn utf8_stdout(mut cmd: Command) -> Result<String> { |
527 | let output = cmd.output().with_context(|| format!("{:?} failed", cmd))?; | 115 | let output = cmd.output().with_context(|| format!("{:?} failed", cmd))?; |
528 | if !output.status.success() { | 116 | if !output.status.success() { |
@@ -536,52 +124,3 @@ fn utf8_stdout(mut cmd: Command) -> Result<String> { | |||
536 | let stdout = String::from_utf8(output.stdout)?; | 124 | let stdout = String::from_utf8(output.stdout)?; |
537 | Ok(stdout.trim().to_string()) | 125 | Ok(stdout.trim().to_string()) |
538 | } | 126 | } |
539 | |||
540 | fn sysroot_to_crate_graph( | ||
541 | crate_graph: &mut CrateGraph, | ||
542 | sysroot: &Sysroot, | ||
543 | target: Option<&str>, | ||
544 | load: &mut dyn FnMut(&AbsPath) -> Option<FileId>, | ||
545 | ) -> (Vec<(CrateName, CrateId)>, Option<CrateId>) { | ||
546 | let mut cfg_options = CfgOptions::default(); | ||
547 | cfg_options.extend(get_rustc_cfg_options(target)); | ||
548 | let sysroot_crates: FxHashMap<_, _> = sysroot | ||
549 | .crates() | ||
550 | .filter_map(|krate| { | ||
551 | let file_id = load(&sysroot[krate].root)?; | ||
552 | |||
553 | let env = Env::default(); | ||
554 | let proc_macro = vec![]; | ||
555 | let name = CrateName::new(&sysroot[krate].name) | ||
556 | .expect("Sysroot crates' names do not contain dashes"); | ||
557 | let crate_id = crate_graph.add_crate_root( | ||
558 | file_id, | ||
559 | Edition::Edition2018, | ||
560 | Some(name.into()), | ||
561 | cfg_options.clone(), | ||
562 | env, | ||
563 | proc_macro, | ||
564 | ); | ||
565 | Some((krate, crate_id)) | ||
566 | }) | ||
567 | .collect(); | ||
568 | |||
569 | for from in sysroot.crates() { | ||
570 | for &to in sysroot[from].deps.iter() { | ||
571 | let name = CrateName::new(&sysroot[to].name).unwrap(); | ||
572 | if let (Some(&from), Some(&to)) = (sysroot_crates.get(&from), sysroot_crates.get(&to)) { | ||
573 | if let Err(_) = crate_graph.add_dep(from, name, to) { | ||
574 | log::error!("cyclic dependency between sysroot crates") | ||
575 | } | ||
576 | } | ||
577 | } | ||
578 | } | ||
579 | |||
580 | let public_deps = sysroot | ||
581 | .public_deps() | ||
582 | .map(|(name, idx)| (CrateName::new(name).unwrap(), sysroot_crates[&idx])) | ||
583 | .collect::<Vec<_>>(); | ||
584 | |||
585 | let libproc_macro = sysroot.proc_macro().and_then(|it| sysroot_crates.get(&it).copied()); | ||
586 | (public_deps, libproc_macro) | ||
587 | } | ||
diff --git a/crates/project_model/src/sysroot.rs b/crates/project_model/src/sysroot.rs index b0e8863f6..f0a43eaf6 100644 --- a/crates/project_model/src/sysroot.rs +++ b/crates/project_model/src/sysroot.rs | |||
@@ -37,7 +37,7 @@ impl Sysroot { | |||
37 | pub fn public_deps(&self) -> impl Iterator<Item = (&'static str, SysrootCrate)> + '_ { | 37 | pub fn public_deps(&self) -> impl Iterator<Item = (&'static str, SysrootCrate)> + '_ { |
38 | // core is added as a dependency before std in order to | 38 | // core is added as a dependency before std in order to |
39 | // mimic rustcs dependency order | 39 | // mimic rustcs dependency order |
40 | vec!["core", "alloc", "std"].into_iter().filter_map(move |it| Some((it, self.by_name(it)?))) | 40 | ["core", "alloc", "std"].iter().filter_map(move |&it| Some((it, self.by_name(it)?))) |
41 | } | 41 | } |
42 | 42 | ||
43 | pub fn proc_macro(&self) -> Option<SysrootCrate> { | 43 | pub fn proc_macro(&self) -> Option<SysrootCrate> { |
diff --git a/crates/project_model/src/workspace.rs b/crates/project_model/src/workspace.rs new file mode 100644 index 000000000..a71f96164 --- /dev/null +++ b/crates/project_model/src/workspace.rs | |||
@@ -0,0 +1,552 @@ | |||
1 | //! Handles lowering of build-system specific workspace information (`cargo | ||
2 | //! metadata` or `rust-project.json`) into representation stored in the salsa | ||
3 | //! database -- `CrateGraph`. | ||
4 | |||
5 | use std::{fmt, fs, path::Component, process::Command}; | ||
6 | |||
7 | use anyhow::{Context, Result}; | ||
8 | use base_db::{CrateDisplayName, CrateGraph, CrateId, CrateName, Edition, Env, FileId}; | ||
9 | use cfg::CfgOptions; | ||
10 | use paths::{AbsPath, AbsPathBuf}; | ||
11 | use proc_macro_api::ProcMacroClient; | ||
12 | use rustc_hash::{FxHashMap, FxHashSet}; | ||
13 | |||
14 | use crate::{ | ||
15 | cargo_workspace, cfg_flag::CfgFlag, sysroot::SysrootCrate, utf8_stdout, CargoConfig, | ||
16 | CargoWorkspace, ProjectJson, ProjectManifest, Sysroot, TargetKind, | ||
17 | }; | ||
18 | |||
19 | /// `PackageRoot` describes a package root folder. | ||
20 | /// Which may be an external dependency, or a member of | ||
21 | /// the current workspace. | ||
22 | #[derive(Debug, Clone, Eq, PartialEq, Hash)] | ||
23 | pub struct PackageRoot { | ||
24 | /// Is a member of the current workspace | ||
25 | pub is_member: bool, | ||
26 | pub include: Vec<AbsPathBuf>, | ||
27 | pub exclude: Vec<AbsPathBuf>, | ||
28 | } | ||
29 | |||
30 | #[derive(Clone, Eq, PartialEq)] | ||
31 | pub enum ProjectWorkspace { | ||
32 | /// Project workspace was discovered by running `cargo metadata` and `rustc --print sysroot`. | ||
33 | Cargo { cargo: CargoWorkspace, sysroot: Sysroot, rustc: Option<CargoWorkspace> }, | ||
34 | /// Project workspace was manually specified using a `rust-project.json` file. | ||
35 | Json { project: ProjectJson, sysroot: Option<Sysroot> }, | ||
36 | } | ||
37 | |||
38 | impl fmt::Debug for ProjectWorkspace { | ||
39 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
40 | match self { | ||
41 | ProjectWorkspace::Cargo { cargo, sysroot, rustc } => f | ||
42 | .debug_struct("Cargo") | ||
43 | .field("n_packages", &cargo.packages().len()) | ||
44 | .field("n_sysroot_crates", &sysroot.crates().len()) | ||
45 | .field( | ||
46 | "n_rustc_compiler_crates", | ||
47 | &rustc.as_ref().map_or(0, |rc| rc.packages().len()), | ||
48 | ) | ||
49 | .finish(), | ||
50 | ProjectWorkspace::Json { project, sysroot } => { | ||
51 | let mut debug_struct = f.debug_struct("Json"); | ||
52 | debug_struct.field("n_crates", &project.n_crates()); | ||
53 | if let Some(sysroot) = sysroot { | ||
54 | debug_struct.field("n_sysroot_crates", &sysroot.crates().len()); | ||
55 | } | ||
56 | debug_struct.finish() | ||
57 | } | ||
58 | } | ||
59 | } | ||
60 | } | ||
61 | |||
62 | impl ProjectWorkspace { | ||
63 | pub fn load(manifest: ProjectManifest, config: &CargoConfig) -> Result<ProjectWorkspace> { | ||
64 | let res = match manifest { | ||
65 | ProjectManifest::ProjectJson(project_json) => { | ||
66 | let file = fs::read_to_string(&project_json).with_context(|| { | ||
67 | format!("Failed to read json file {}", project_json.display()) | ||
68 | })?; | ||
69 | let data = serde_json::from_str(&file).with_context(|| { | ||
70 | format!("Failed to deserialize json file {}", project_json.display()) | ||
71 | })?; | ||
72 | let project_location = project_json.parent().unwrap().to_path_buf(); | ||
73 | let project_json = ProjectJson::new(&project_location, data); | ||
74 | ProjectWorkspace::load_inline(project_json)? | ||
75 | } | ||
76 | ProjectManifest::CargoToml(cargo_toml) => { | ||
77 | let cargo_version = utf8_stdout({ | ||
78 | let mut cmd = Command::new(toolchain::cargo()); | ||
79 | cmd.arg("--version"); | ||
80 | cmd | ||
81 | })?; | ||
82 | |||
83 | let cargo = CargoWorkspace::from_cargo_metadata(&cargo_toml, config).with_context( | ||
84 | || { | ||
85 | format!( | ||
86 | "Failed to read Cargo metadata from Cargo.toml file {}, {}", | ||
87 | cargo_toml.display(), | ||
88 | cargo_version | ||
89 | ) | ||
90 | }, | ||
91 | )?; | ||
92 | let sysroot = if config.no_sysroot { | ||
93 | Sysroot::default() | ||
94 | } else { | ||
95 | Sysroot::discover(&cargo_toml).with_context(|| { | ||
96 | format!( | ||
97 | "Failed to find sysroot for Cargo.toml file {}. Is rust-src installed?", | ||
98 | cargo_toml.display() | ||
99 | ) | ||
100 | })? | ||
101 | }; | ||
102 | |||
103 | let rustc = if let Some(rustc_dir) = &config.rustc_source { | ||
104 | Some(CargoWorkspace::from_cargo_metadata(&rustc_dir, config).with_context( | ||
105 | || format!("Failed to read Cargo metadata for Rust sources"), | ||
106 | )?) | ||
107 | } else { | ||
108 | None | ||
109 | }; | ||
110 | |||
111 | ProjectWorkspace::Cargo { cargo, sysroot, rustc } | ||
112 | } | ||
113 | }; | ||
114 | |||
115 | Ok(res) | ||
116 | } | ||
117 | |||
118 | pub fn load_inline(project_json: ProjectJson) -> Result<ProjectWorkspace> { | ||
119 | let sysroot = match &project_json.sysroot_src { | ||
120 | Some(path) => Some(Sysroot::load(path)?), | ||
121 | None => None, | ||
122 | }; | ||
123 | |||
124 | Ok(ProjectWorkspace::Json { project: project_json, sysroot }) | ||
125 | } | ||
126 | |||
127 | /// Returns the roots for the current `ProjectWorkspace` | ||
128 | /// The return type contains the path and whether or not | ||
129 | /// the root is a member of the current workspace | ||
130 | pub fn to_roots(&self) -> Vec<PackageRoot> { | ||
131 | match self { | ||
132 | ProjectWorkspace::Json { project, sysroot } => project | ||
133 | .crates() | ||
134 | .map(|(_, krate)| PackageRoot { | ||
135 | is_member: krate.is_workspace_member, | ||
136 | include: krate.include.clone(), | ||
137 | exclude: krate.exclude.clone(), | ||
138 | }) | ||
139 | .collect::<FxHashSet<_>>() | ||
140 | .into_iter() | ||
141 | .chain(sysroot.as_ref().into_iter().flat_map(|sysroot| { | ||
142 | sysroot.crates().map(move |krate| PackageRoot { | ||
143 | is_member: false, | ||
144 | include: vec![sysroot[krate].root_dir().to_path_buf()], | ||
145 | exclude: Vec::new(), | ||
146 | }) | ||
147 | })) | ||
148 | .collect::<Vec<_>>(), | ||
149 | ProjectWorkspace::Cargo { cargo, sysroot, rustc } => cargo | ||
150 | .packages() | ||
151 | .map(|pkg| { | ||
152 | let is_member = cargo[pkg].is_member; | ||
153 | let pkg_root = cargo[pkg].root().to_path_buf(); | ||
154 | |||
155 | let mut include = vec![pkg_root.clone()]; | ||
156 | include.extend(cargo[pkg].out_dir.clone()); | ||
157 | |||
158 | let mut exclude = vec![pkg_root.join(".git")]; | ||
159 | if is_member { | ||
160 | exclude.push(pkg_root.join("target")); | ||
161 | } else { | ||
162 | exclude.push(pkg_root.join("tests")); | ||
163 | exclude.push(pkg_root.join("examples")); | ||
164 | exclude.push(pkg_root.join("benches")); | ||
165 | } | ||
166 | PackageRoot { is_member, include, exclude } | ||
167 | }) | ||
168 | .chain(sysroot.crates().map(|krate| PackageRoot { | ||
169 | is_member: false, | ||
170 | include: vec![sysroot[krate].root_dir().to_path_buf()], | ||
171 | exclude: Vec::new(), | ||
172 | })) | ||
173 | .chain(rustc.into_iter().flat_map(|rustc| { | ||
174 | rustc.packages().map(move |krate| PackageRoot { | ||
175 | is_member: false, | ||
176 | include: vec![rustc[krate].root().to_path_buf()], | ||
177 | exclude: Vec::new(), | ||
178 | }) | ||
179 | })) | ||
180 | .collect(), | ||
181 | } | ||
182 | } | ||
183 | |||
184 | pub fn n_packages(&self) -> usize { | ||
185 | match self { | ||
186 | ProjectWorkspace::Json { project, .. } => project.n_crates(), | ||
187 | ProjectWorkspace::Cargo { cargo, sysroot, rustc } => { | ||
188 | let rustc_package_len = rustc.as_ref().map_or(0, |rc| rc.packages().len()); | ||
189 | cargo.packages().len() + sysroot.crates().len() + rustc_package_len | ||
190 | } | ||
191 | } | ||
192 | } | ||
193 | |||
194 | pub fn to_crate_graph( | ||
195 | &self, | ||
196 | target: Option<&str>, | ||
197 | proc_macro_client: &ProcMacroClient, | ||
198 | load: &mut dyn FnMut(&AbsPath) -> Option<FileId>, | ||
199 | ) -> CrateGraph { | ||
200 | let mut crate_graph = match self { | ||
201 | ProjectWorkspace::Json { project, sysroot } => { | ||
202 | project_json_to_crate_graph(target, proc_macro_client, load, project, sysroot) | ||
203 | } | ||
204 | ProjectWorkspace::Cargo { cargo, sysroot, rustc } => { | ||
205 | cargo_to_crate_graph(target, proc_macro_client, load, cargo, sysroot, rustc) | ||
206 | } | ||
207 | }; | ||
208 | if crate_graph.patch_cfg_if() { | ||
209 | log::debug!("Patched std to depend on cfg-if") | ||
210 | } else { | ||
211 | log::debug!("Did not patch std to depend on cfg-if") | ||
212 | } | ||
213 | crate_graph | ||
214 | } | ||
215 | } | ||
216 | |||
217 | fn project_json_to_crate_graph( | ||
218 | target: Option<&str>, | ||
219 | proc_macro_client: &ProcMacroClient, | ||
220 | load: &mut dyn FnMut(&AbsPath) -> Option<FileId>, | ||
221 | project: &ProjectJson, | ||
222 | sysroot: &Option<Sysroot>, | ||
223 | ) -> CrateGraph { | ||
224 | let mut crate_graph = CrateGraph::default(); | ||
225 | let sysroot_deps = sysroot | ||
226 | .as_ref() | ||
227 | .map(|sysroot| sysroot_to_crate_graph(&mut crate_graph, sysroot, target, load)); | ||
228 | |||
229 | let mut cfg_cache: FxHashMap<Option<&str>, Vec<CfgFlag>> = FxHashMap::default(); | ||
230 | let crates: FxHashMap<CrateId, CrateId> = project | ||
231 | .crates() | ||
232 | .filter_map(|(crate_id, krate)| { | ||
233 | let file_path = &krate.root_module; | ||
234 | let file_id = load(&file_path)?; | ||
235 | Some((crate_id, krate, file_id)) | ||
236 | }) | ||
237 | .map(|(crate_id, krate, file_id)| { | ||
238 | let env = krate.env.clone().into_iter().collect(); | ||
239 | let proc_macro = | ||
240 | krate.proc_macro_dylib_path.clone().map(|it| proc_macro_client.by_dylib_path(&it)); | ||
241 | |||
242 | let target = krate.target.as_deref().or(target); | ||
243 | let target_cfgs = | ||
244 | cfg_cache.entry(target).or_insert_with(|| get_rustc_cfg_options(target)); | ||
245 | |||
246 | let mut cfg_options = CfgOptions::default(); | ||
247 | cfg_options.extend(target_cfgs.iter().chain(krate.cfg.iter()).cloned()); | ||
248 | ( | ||
249 | crate_id, | ||
250 | crate_graph.add_crate_root( | ||
251 | file_id, | ||
252 | krate.edition, | ||
253 | krate.display_name.clone(), | ||
254 | cfg_options, | ||
255 | env, | ||
256 | proc_macro.unwrap_or_default(), | ||
257 | ), | ||
258 | ) | ||
259 | }) | ||
260 | .collect(); | ||
261 | |||
262 | for (from, krate) in project.crates() { | ||
263 | if let Some(&from) = crates.get(&from) { | ||
264 | if let Some((public_deps, _proc_macro)) = &sysroot_deps { | ||
265 | for (name, to) in public_deps.iter() { | ||
266 | add_dep(&mut crate_graph, from, name.clone(), *to) | ||
267 | } | ||
268 | } | ||
269 | |||
270 | for dep in &krate.deps { | ||
271 | if let Some(&to) = crates.get(&dep.crate_id) { | ||
272 | add_dep(&mut crate_graph, from, dep.name.clone(), to) | ||
273 | } | ||
274 | } | ||
275 | } | ||
276 | } | ||
277 | crate_graph | ||
278 | } | ||
279 | |||
280 | fn cargo_to_crate_graph( | ||
281 | target: Option<&str>, | ||
282 | proc_macro_client: &ProcMacroClient, | ||
283 | load: &mut dyn FnMut(&AbsPath) -> Option<FileId>, | ||
284 | cargo: &CargoWorkspace, | ||
285 | sysroot: &Sysroot, | ||
286 | rustc: &Option<CargoWorkspace>, | ||
287 | ) -> CrateGraph { | ||
288 | let mut crate_graph = CrateGraph::default(); | ||
289 | let (public_deps, libproc_macro) = | ||
290 | sysroot_to_crate_graph(&mut crate_graph, sysroot, target, load); | ||
291 | |||
292 | let mut cfg_options = CfgOptions::default(); | ||
293 | cfg_options.extend(get_rustc_cfg_options(target)); | ||
294 | |||
295 | let mut pkg_to_lib_crate = FxHashMap::default(); | ||
296 | |||
297 | // Add test cfg for non-sysroot crates | ||
298 | cfg_options.insert_atom("test".into()); | ||
299 | cfg_options.insert_atom("debug_assertions".into()); | ||
300 | |||
301 | let mut pkg_crates = FxHashMap::default(); | ||
302 | |||
303 | // Next, create crates for each package, target pair | ||
304 | for pkg in cargo.packages() { | ||
305 | let mut lib_tgt = None; | ||
306 | for &tgt in cargo[pkg].targets.iter() { | ||
307 | if let Some(file_id) = load(&cargo[tgt].root) { | ||
308 | let crate_id = add_target_crate_root( | ||
309 | &mut crate_graph, | ||
310 | &cargo[pkg], | ||
311 | &cfg_options, | ||
312 | proc_macro_client, | ||
313 | file_id, | ||
314 | ); | ||
315 | if cargo[tgt].kind == TargetKind::Lib { | ||
316 | lib_tgt = Some((crate_id, cargo[tgt].name.clone())); | ||
317 | pkg_to_lib_crate.insert(pkg, crate_id); | ||
318 | } | ||
319 | if cargo[tgt].is_proc_macro { | ||
320 | if let Some(proc_macro) = libproc_macro { | ||
321 | add_dep( | ||
322 | &mut crate_graph, | ||
323 | crate_id, | ||
324 | CrateName::new("proc_macro").unwrap(), | ||
325 | proc_macro, | ||
326 | ); | ||
327 | } | ||
328 | } | ||
329 | |||
330 | pkg_crates.entry(pkg).or_insert_with(Vec::new).push(crate_id); | ||
331 | } | ||
332 | } | ||
333 | |||
334 | // Set deps to the core, std and to the lib target of the current package | ||
335 | for &from in pkg_crates.get(&pkg).into_iter().flatten() { | ||
336 | if let Some((to, name)) = lib_tgt.clone() { | ||
337 | if to != from { | ||
338 | // For root projects with dashes in their name, | ||
339 | // cargo metadata does not do any normalization, | ||
340 | // so we do it ourselves currently | ||
341 | let name = CrateName::normalize_dashes(&name); | ||
342 | add_dep(&mut crate_graph, from, name, to); | ||
343 | } | ||
344 | } | ||
345 | for (name, krate) in public_deps.iter() { | ||
346 | add_dep(&mut crate_graph, from, name.clone(), *krate); | ||
347 | } | ||
348 | } | ||
349 | } | ||
350 | |||
351 | // Now add a dep edge from all targets of upstream to the lib | ||
352 | // target of downstream. | ||
353 | for pkg in cargo.packages() { | ||
354 | for dep in cargo[pkg].dependencies.iter() { | ||
355 | let name = CrateName::new(&dep.name).unwrap(); | ||
356 | if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) { | ||
357 | for &from in pkg_crates.get(&pkg).into_iter().flatten() { | ||
358 | add_dep(&mut crate_graph, from, name.clone(), to) | ||
359 | } | ||
360 | } | ||
361 | } | ||
362 | } | ||
363 | |||
364 | let mut rustc_pkg_crates = FxHashMap::default(); | ||
365 | |||
366 | // If the user provided a path to rustc sources, we add all the rustc_private crates | ||
367 | // and create dependencies on them for the crates in the current workspace | ||
368 | if let Some(rustc_workspace) = rustc { | ||
369 | for pkg in rustc_workspace.packages() { | ||
370 | for &tgt in rustc_workspace[pkg].targets.iter() { | ||
371 | if rustc_workspace[tgt].kind != TargetKind::Lib { | ||
372 | continue; | ||
373 | } | ||
374 | // Exclude alloc / core / std | ||
375 | if rustc_workspace[tgt] | ||
376 | .root | ||
377 | .components() | ||
378 | .any(|c| c == Component::Normal("library".as_ref())) | ||
379 | { | ||
380 | continue; | ||
381 | } | ||
382 | |||
383 | if let Some(file_id) = load(&rustc_workspace[tgt].root) { | ||
384 | let crate_id = add_target_crate_root( | ||
385 | &mut crate_graph, | ||
386 | &rustc_workspace[pkg], | ||
387 | &cfg_options, | ||
388 | proc_macro_client, | ||
389 | file_id, | ||
390 | ); | ||
391 | pkg_to_lib_crate.insert(pkg, crate_id); | ||
392 | // Add dependencies on the core / std / alloc for rustc | ||
393 | for (name, krate) in public_deps.iter() { | ||
394 | add_dep(&mut crate_graph, crate_id, name.clone(), *krate); | ||
395 | } | ||
396 | rustc_pkg_crates.entry(pkg).or_insert_with(Vec::new).push(crate_id); | ||
397 | } | ||
398 | } | ||
399 | } | ||
400 | // Now add a dep edge from all targets of upstream to the lib | ||
401 | // target of downstream. | ||
402 | for pkg in rustc_workspace.packages() { | ||
403 | for dep in rustc_workspace[pkg].dependencies.iter() { | ||
404 | let name = CrateName::new(&dep.name).unwrap(); | ||
405 | if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) { | ||
406 | for &from in rustc_pkg_crates.get(&pkg).into_iter().flatten() { | ||
407 | add_dep(&mut crate_graph, from, name.clone(), to); | ||
408 | } | ||
409 | } | ||
410 | } | ||
411 | } | ||
412 | |||
413 | // Add dependencies for all the crates of the current workspace to rustc_private libraries | ||
414 | for dep in rustc_workspace.packages() { | ||
415 | let name = CrateName::normalize_dashes(&rustc_workspace[dep].name); | ||
416 | |||
417 | if let Some(&to) = pkg_to_lib_crate.get(&dep) { | ||
418 | for pkg in cargo.packages() { | ||
419 | if !cargo[pkg].is_member { | ||
420 | continue; | ||
421 | } | ||
422 | for &from in pkg_crates.get(&pkg).into_iter().flatten() { | ||
423 | add_dep(&mut crate_graph, from, name.clone(), to); | ||
424 | } | ||
425 | } | ||
426 | } | ||
427 | } | ||
428 | } | ||
429 | crate_graph | ||
430 | } | ||
431 | |||
432 | fn add_target_crate_root( | ||
433 | crate_graph: &mut CrateGraph, | ||
434 | pkg: &cargo_workspace::PackageData, | ||
435 | cfg_options: &CfgOptions, | ||
436 | proc_macro_client: &ProcMacroClient, | ||
437 | file_id: FileId, | ||
438 | ) -> CrateId { | ||
439 | let edition = pkg.edition; | ||
440 | let cfg_options = { | ||
441 | let mut opts = cfg_options.clone(); | ||
442 | for feature in pkg.features.iter() { | ||
443 | opts.insert_key_value("feature".into(), feature.into()); | ||
444 | } | ||
445 | opts.extend(pkg.cfgs.iter().cloned()); | ||
446 | opts | ||
447 | }; | ||
448 | let mut env = Env::default(); | ||
449 | if let Some(out_dir) = &pkg.out_dir { | ||
450 | // NOTE: cargo and rustc seem to hide non-UTF-8 strings from env! and option_env!() | ||
451 | if let Some(out_dir) = out_dir.to_str().map(|s| s.to_owned()) { | ||
452 | env.set("OUT_DIR", out_dir); | ||
453 | } | ||
454 | } | ||
455 | let proc_macro = pkg | ||
456 | .proc_macro_dylib_path | ||
457 | .as_ref() | ||
458 | .map(|it| proc_macro_client.by_dylib_path(&it)) | ||
459 | .unwrap_or_default(); | ||
460 | |||
461 | let display_name = CrateDisplayName::from_canonical_name(pkg.name.clone()); | ||
462 | let crate_id = crate_graph.add_crate_root( | ||
463 | file_id, | ||
464 | edition, | ||
465 | Some(display_name), | ||
466 | cfg_options, | ||
467 | env, | ||
468 | proc_macro.clone(), | ||
469 | ); | ||
470 | |||
471 | crate_id | ||
472 | } | ||
473 | |||
474 | fn sysroot_to_crate_graph( | ||
475 | crate_graph: &mut CrateGraph, | ||
476 | sysroot: &Sysroot, | ||
477 | target: Option<&str>, | ||
478 | load: &mut dyn FnMut(&AbsPath) -> Option<FileId>, | ||
479 | ) -> (Vec<(CrateName, CrateId)>, Option<CrateId>) { | ||
480 | let mut cfg_options = CfgOptions::default(); | ||
481 | cfg_options.extend(get_rustc_cfg_options(target)); | ||
482 | let sysroot_crates: FxHashMap<SysrootCrate, CrateId> = sysroot | ||
483 | .crates() | ||
484 | .filter_map(|krate| { | ||
485 | let file_id = load(&sysroot[krate].root)?; | ||
486 | |||
487 | let env = Env::default(); | ||
488 | let proc_macro = vec![]; | ||
489 | let display_name = CrateDisplayName::from_canonical_name(sysroot[krate].name.clone()); | ||
490 | let crate_id = crate_graph.add_crate_root( | ||
491 | file_id, | ||
492 | Edition::Edition2018, | ||
493 | Some(display_name), | ||
494 | cfg_options.clone(), | ||
495 | env, | ||
496 | proc_macro, | ||
497 | ); | ||
498 | Some((krate, crate_id)) | ||
499 | }) | ||
500 | .collect(); | ||
501 | |||
502 | for from in sysroot.crates() { | ||
503 | for &to in sysroot[from].deps.iter() { | ||
504 | let name = CrateName::new(&sysroot[to].name).unwrap(); | ||
505 | if let (Some(&from), Some(&to)) = (sysroot_crates.get(&from), sysroot_crates.get(&to)) { | ||
506 | add_dep(crate_graph, from, name, to); | ||
507 | } | ||
508 | } | ||
509 | } | ||
510 | |||
511 | let public_deps = sysroot | ||
512 | .public_deps() | ||
513 | .map(|(name, idx)| (CrateName::new(name).unwrap(), sysroot_crates[&idx])) | ||
514 | .collect::<Vec<_>>(); | ||
515 | |||
516 | let libproc_macro = sysroot.proc_macro().and_then(|it| sysroot_crates.get(&it).copied()); | ||
517 | (public_deps, libproc_macro) | ||
518 | } | ||
519 | |||
520 | fn get_rustc_cfg_options(target: Option<&str>) -> Vec<CfgFlag> { | ||
521 | let mut res = Vec::new(); | ||
522 | |||
523 | // Some nightly-only cfgs, which are required for stdlib | ||
524 | res.push(CfgFlag::Atom("target_thread_local".into())); | ||
525 | for &ty in ["8", "16", "32", "64", "cas", "ptr"].iter() { | ||
526 | for &key in ["target_has_atomic", "target_has_atomic_load_store"].iter() { | ||
527 | res.push(CfgFlag::KeyValue { key: key.to_string(), value: ty.into() }); | ||
528 | } | ||
529 | } | ||
530 | |||
531 | let rustc_cfgs = { | ||
532 | let mut cmd = Command::new(toolchain::rustc()); | ||
533 | cmd.args(&["--print", "cfg", "-O"]); | ||
534 | if let Some(target) = target { | ||
535 | cmd.args(&["--target", target]); | ||
536 | } | ||
537 | utf8_stdout(cmd) | ||
538 | }; | ||
539 | |||
540 | match rustc_cfgs { | ||
541 | Ok(rustc_cfgs) => res.extend(rustc_cfgs.lines().map(|it| it.parse().unwrap())), | ||
542 | Err(e) => log::error!("failed to get rustc cfgs: {:#}", e), | ||
543 | } | ||
544 | |||
545 | res | ||
546 | } | ||
547 | |||
548 | fn add_dep(graph: &mut CrateGraph, from: CrateId, name: CrateName, to: CrateId) { | ||
549 | if let Err(err) = graph.add_dep(from, name, to) { | ||
550 | log::error!("{}", err) | ||
551 | } | ||
552 | } | ||
diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml index d25c4bf83..436f5041b 100644 --- a/crates/rust-analyzer/Cargo.toml +++ b/crates/rust-analyzer/Cargo.toml | |||
@@ -21,7 +21,7 @@ env_logger = { version = "0.8.1", default-features = false } | |||
21 | itertools = "0.9.0" | 21 | itertools = "0.9.0" |
22 | jod-thread = "0.1.0" | 22 | jod-thread = "0.1.0" |
23 | log = "0.4.8" | 23 | log = "0.4.8" |
24 | lsp-types = { version = "0.83.0", features = ["proposed"] } | 24 | lsp-types = { version = "0.84.0", features = ["proposed"] } |
25 | parking_lot = "0.11.0" | 25 | parking_lot = "0.11.0" |
26 | pico-args = "0.3.1" | 26 | pico-args = "0.3.1" |
27 | oorandom = "11.1.2" | 27 | oorandom = "11.1.2" |
@@ -31,7 +31,7 @@ serde_json = "1.0.48" | |||
31 | threadpool = "1.7.1" | 31 | threadpool = "1.7.1" |
32 | rayon = "1.5" | 32 | rayon = "1.5" |
33 | mimalloc = { version = "0.1.19", default-features = false, optional = true } | 33 | mimalloc = { version = "0.1.19", default-features = false, optional = true } |
34 | lsp-server = "0.4.0" | 34 | lsp-server = "0.5.0" |
35 | tracing = "0.1" | 35 | tracing = "0.1" |
36 | tracing-subscriber = { version = "0.2", default-features = false, features = ["env-filter", "registry"] } | 36 | tracing-subscriber = { version = "0.2", default-features = false, features = ["env-filter", "registry"] } |
37 | tracing-tree = { version = "0.1.4" } | 37 | tracing-tree = { version = "0.1.4" } |
diff --git a/crates/rust-analyzer/src/caps.rs b/crates/rust-analyzer/src/caps.rs index ff1ae9575..c7203451c 100644 --- a/crates/rust-analyzer/src/caps.rs +++ b/crates/rust-analyzer/src/caps.rs | |||
@@ -16,8 +16,6 @@ use serde_json::json; | |||
16 | use crate::semantic_tokens; | 16 | use crate::semantic_tokens; |
17 | 17 | ||
18 | pub fn server_capabilities(client_caps: &ClientCapabilities) -> ServerCapabilities { | 18 | pub fn server_capabilities(client_caps: &ClientCapabilities) -> ServerCapabilities { |
19 | let code_action_provider = code_action_capabilities(client_caps); | ||
20 | |||
21 | ServerCapabilities { | 19 | ServerCapabilities { |
22 | text_document_sync: Some(TextDocumentSyncCapability::Options(TextDocumentSyncOptions { | 20 | text_document_sync: Some(TextDocumentSyncCapability::Options(TextDocumentSyncOptions { |
23 | open_close: Some(true), | 21 | open_close: Some(true), |
@@ -49,7 +47,7 @@ pub fn server_capabilities(client_caps: &ClientCapabilities) -> ServerCapabiliti | |||
49 | document_highlight_provider: Some(OneOf::Left(true)), | 47 | document_highlight_provider: Some(OneOf::Left(true)), |
50 | document_symbol_provider: Some(OneOf::Left(true)), | 48 | document_symbol_provider: Some(OneOf::Left(true)), |
51 | workspace_symbol_provider: Some(OneOf::Left(true)), | 49 | workspace_symbol_provider: Some(OneOf::Left(true)), |
52 | code_action_provider: Some(code_action_provider), | 50 | code_action_provider: Some(code_action_capabilities(client_caps)), |
53 | code_lens_provider: Some(CodeLensOptions { resolve_provider: Some(true) }), | 51 | code_lens_provider: Some(CodeLensOptions { resolve_provider: Some(true) }), |
54 | document_formatting_provider: Some(OneOf::Left(true)), | 52 | document_formatting_provider: Some(OneOf::Left(true)), |
55 | document_range_formatting_provider: None, | 53 | document_range_formatting_provider: None, |
@@ -64,6 +62,7 @@ pub fn server_capabilities(client_caps: &ClientCapabilities) -> ServerCapabiliti | |||
64 | prepare_provider: Some(true), | 62 | prepare_provider: Some(true), |
65 | work_done_progress_options: WorkDoneProgressOptions { work_done_progress: None }, | 63 | work_done_progress_options: WorkDoneProgressOptions { work_done_progress: None }, |
66 | })), | 64 | })), |
65 | on_type_rename_provider: None, | ||
67 | document_link_provider: None, | 66 | document_link_provider: None, |
68 | color_provider: None, | 67 | color_provider: None, |
69 | execute_command_provider: None, | 68 | execute_command_provider: None, |
@@ -113,7 +112,7 @@ fn code_action_capabilities(client_caps: &ClientCapabilities) -> CodeActionProvi | |||
113 | CodeActionKind::REFACTOR_INLINE, | 112 | CodeActionKind::REFACTOR_INLINE, |
114 | CodeActionKind::REFACTOR_REWRITE, | 113 | CodeActionKind::REFACTOR_REWRITE, |
115 | ]), | 114 | ]), |
116 | resolve_provider: None, | 115 | resolve_provider: Some(true), |
117 | work_done_progress_options: Default::default(), | 116 | work_done_progress_options: Default::default(), |
118 | }) | 117 | }) |
119 | }) | 118 | }) |
diff --git a/crates/rust-analyzer/src/cli/load_cargo.rs b/crates/rust-analyzer/src/cli/load_cargo.rs index ab1e2ab92..76526c66c 100644 --- a/crates/rust-analyzer/src/cli/load_cargo.rs +++ b/crates/rust-analyzer/src/cli/load_cargo.rs | |||
@@ -21,7 +21,6 @@ pub fn load_cargo( | |||
21 | let ws = ProjectWorkspace::load( | 21 | let ws = ProjectWorkspace::load( |
22 | root, | 22 | root, |
23 | &CargoConfig { load_out_dirs_from_check, ..Default::default() }, | 23 | &CargoConfig { load_out_dirs_from_check, ..Default::default() }, |
24 | true, | ||
25 | )?; | 24 | )?; |
26 | 25 | ||
27 | let (sender, receiver) = unbounded(); | 26 | let (sender, receiver) = unbounded(); |
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 2ed6a0d82..a334cdb11 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs | |||
@@ -7,7 +7,7 @@ | |||
7 | //! configure the server itself, feature flags are passed into analysis, and | 7 | //! configure the server itself, feature flags are passed into analysis, and |
8 | //! tweak things like automatic insertion of `()` in completions. | 8 | //! tweak things like automatic insertion of `()` in completions. |
9 | 9 | ||
10 | use std::{ffi::OsString, path::PathBuf}; | 10 | use std::{convert::TryFrom, ffi::OsString, path::PathBuf}; |
11 | 11 | ||
12 | use flycheck::FlycheckConfig; | 12 | use flycheck::FlycheckConfig; |
13 | use hir::PrefixKind; | 13 | use hir::PrefixKind; |
@@ -49,7 +49,6 @@ pub struct Config { | |||
49 | pub hover: HoverConfig, | 49 | pub hover: HoverConfig, |
50 | pub semantic_tokens_refresh: bool, | 50 | pub semantic_tokens_refresh: bool, |
51 | 51 | ||
52 | pub with_sysroot: bool, | ||
53 | pub linked_projects: Vec<LinkedProject>, | 52 | pub linked_projects: Vec<LinkedProject>, |
54 | pub root_path: AbsPathBuf, | 53 | pub root_path: AbsPathBuf, |
55 | } | 54 | } |
@@ -144,7 +143,7 @@ pub struct ClientCapsConfig { | |||
144 | pub code_action_literals: bool, | 143 | pub code_action_literals: bool, |
145 | pub work_done_progress: bool, | 144 | pub work_done_progress: bool, |
146 | pub code_action_group: bool, | 145 | pub code_action_group: bool, |
147 | pub resolve_code_action: bool, | 146 | pub code_action_resolve: bool, |
148 | pub hover_actions: bool, | 147 | pub hover_actions: bool, |
149 | pub status_notification: bool, | 148 | pub status_notification: bool, |
150 | pub signature_help_label_offsets: bool, | 149 | pub signature_help_label_offsets: bool, |
@@ -155,7 +154,6 @@ impl Config { | |||
155 | Config { | 154 | Config { |
156 | client_caps: ClientCapsConfig::default(), | 155 | client_caps: ClientCapsConfig::default(), |
157 | 156 | ||
158 | with_sysroot: true, | ||
159 | publish_diagnostics: true, | 157 | publish_diagnostics: true, |
160 | diagnostics: DiagnosticsConfig::default(), | 158 | diagnostics: DiagnosticsConfig::default(), |
161 | diagnostics_map: DiagnosticsMapConfig::default(), | 159 | diagnostics_map: DiagnosticsMapConfig::default(), |
@@ -186,6 +184,7 @@ impl Config { | |||
186 | }, | 184 | }, |
187 | completion: CompletionConfig { | 185 | completion: CompletionConfig { |
188 | enable_postfix_completions: true, | 186 | enable_postfix_completions: true, |
187 | enable_experimental_completions: true, | ||
189 | add_call_parenthesis: true, | 188 | add_call_parenthesis: true, |
190 | add_call_argument_snippets: true, | 189 | add_call_argument_snippets: true, |
191 | ..CompletionConfig::default() | 190 | ..CompletionConfig::default() |
@@ -209,7 +208,6 @@ impl Config { | |||
209 | 208 | ||
210 | let data = ConfigData::from_json(json); | 209 | let data = ConfigData::from_json(json); |
211 | 210 | ||
212 | self.with_sysroot = data.withSysroot; | ||
213 | self.publish_diagnostics = data.diagnostics_enable; | 211 | self.publish_diagnostics = data.diagnostics_enable; |
214 | self.diagnostics = DiagnosticsConfig { | 212 | self.diagnostics = DiagnosticsConfig { |
215 | disable_experimental: !data.diagnostics_enableExperimental, | 213 | disable_experimental: !data.diagnostics_enableExperimental, |
@@ -227,12 +225,26 @@ impl Config { | |||
227 | self.notifications = | 225 | self.notifications = |
228 | NotificationsConfig { cargo_toml_not_found: data.notifications_cargoTomlNotFound }; | 226 | NotificationsConfig { cargo_toml_not_found: data.notifications_cargoTomlNotFound }; |
229 | self.cargo_autoreload = data.cargo_autoreload; | 227 | self.cargo_autoreload = data.cargo_autoreload; |
228 | |||
229 | let rustc_source = if let Some(rustc_source) = data.rustcSource { | ||
230 | let rustpath: PathBuf = rustc_source.into(); | ||
231 | AbsPathBuf::try_from(rustpath) | ||
232 | .map_err(|_| { | ||
233 | log::error!("rustc source directory must be an absolute path"); | ||
234 | }) | ||
235 | .ok() | ||
236 | } else { | ||
237 | None | ||
238 | }; | ||
239 | |||
230 | self.cargo = CargoConfig { | 240 | self.cargo = CargoConfig { |
231 | no_default_features: data.cargo_noDefaultFeatures, | 241 | no_default_features: data.cargo_noDefaultFeatures, |
232 | all_features: data.cargo_allFeatures, | 242 | all_features: data.cargo_allFeatures, |
233 | features: data.cargo_features.clone(), | 243 | features: data.cargo_features.clone(), |
234 | load_out_dirs_from_check: data.cargo_loadOutDirsFromCheck, | 244 | load_out_dirs_from_check: data.cargo_loadOutDirsFromCheck, |
235 | target: data.cargo_target.clone(), | 245 | target: data.cargo_target.clone(), |
246 | rustc_source: rustc_source, | ||
247 | no_sysroot: data.cargo_noSysroot, | ||
236 | }; | 248 | }; |
237 | self.runnables = RunnablesConfig { | 249 | self.runnables = RunnablesConfig { |
238 | override_cargo: data.runnables_overrideCargo, | 250 | override_cargo: data.runnables_overrideCargo, |
@@ -283,10 +295,6 @@ impl Config { | |||
283 | max_length: data.inlayHints_maxLength, | 295 | max_length: data.inlayHints_maxLength, |
284 | }; | 296 | }; |
285 | 297 | ||
286 | self.completion.enable_postfix_completions = data.completion_postfix_enable; | ||
287 | self.completion.add_call_parenthesis = data.completion_addCallParenthesis; | ||
288 | self.completion.add_call_argument_snippets = data.completion_addCallArgumentSnippets; | ||
289 | |||
290 | self.assist.insert_use.merge = match data.assist_importMergeBehaviour { | 298 | self.assist.insert_use.merge = match data.assist_importMergeBehaviour { |
291 | MergeBehaviourDef::None => None, | 299 | MergeBehaviourDef::None => None, |
292 | MergeBehaviourDef::Full => Some(MergeBehaviour::Full), | 300 | MergeBehaviourDef::Full => Some(MergeBehaviour::Full), |
@@ -298,6 +306,12 @@ impl Config { | |||
298 | ImportPrefixDef::BySelf => PrefixKind::BySelf, | 306 | ImportPrefixDef::BySelf => PrefixKind::BySelf, |
299 | }; | 307 | }; |
300 | 308 | ||
309 | self.completion.enable_postfix_completions = data.completion_postfix_enable; | ||
310 | self.completion.enable_experimental_completions = data.completion_enableExperimental; | ||
311 | self.completion.add_call_parenthesis = data.completion_addCallParenthesis; | ||
312 | self.completion.add_call_argument_snippets = data.completion_addCallArgumentSnippets; | ||
313 | self.completion.merge = self.assist.insert_use.merge; | ||
314 | |||
301 | self.call_info_full = data.callInfo_full; | 315 | self.call_info_full = data.callInfo_full; |
302 | 316 | ||
303 | self.lens = LensConfig { | 317 | self.lens = LensConfig { |
@@ -383,6 +397,14 @@ impl Config { | |||
383 | } | 397 | } |
384 | } | 398 | } |
385 | } | 399 | } |
400 | |||
401 | if let Some(code_action) = &doc_caps.code_action { | ||
402 | if let Some(resolve_support) = &code_action.resolve_support { | ||
403 | if resolve_support.properties.iter().any(|it| it == "edit") { | ||
404 | self.client_caps.code_action_resolve = true; | ||
405 | } | ||
406 | } | ||
407 | } | ||
386 | } | 408 | } |
387 | 409 | ||
388 | if let Some(window_caps) = caps.window.as_ref() { | 410 | if let Some(window_caps) = caps.window.as_ref() { |
@@ -400,7 +422,6 @@ impl Config { | |||
400 | self.assist.allow_snippets(snippet_text_edit); | 422 | self.assist.allow_snippets(snippet_text_edit); |
401 | 423 | ||
402 | self.client_caps.code_action_group = get_bool("codeActionGroup"); | 424 | self.client_caps.code_action_group = get_bool("codeActionGroup"); |
403 | self.client_caps.resolve_code_action = get_bool("resolveCodeAction"); | ||
404 | self.client_caps.hover_actions = get_bool("hoverActions"); | 425 | self.client_caps.hover_actions = get_bool("hoverActions"); |
405 | self.client_caps.status_notification = get_bool("statusNotification"); | 426 | self.client_caps.status_notification = get_bool("statusNotification"); |
406 | } | 427 | } |
@@ -472,6 +493,7 @@ config_data! { | |||
472 | cargo_loadOutDirsFromCheck: bool = false, | 493 | cargo_loadOutDirsFromCheck: bool = false, |
473 | cargo_noDefaultFeatures: bool = false, | 494 | cargo_noDefaultFeatures: bool = false, |
474 | cargo_target: Option<String> = None, | 495 | cargo_target: Option<String> = None, |
496 | cargo_noSysroot: bool = false, | ||
475 | 497 | ||
476 | checkOnSave_enable: bool = true, | 498 | checkOnSave_enable: bool = true, |
477 | checkOnSave_allFeatures: Option<bool> = None, | 499 | checkOnSave_allFeatures: Option<bool> = None, |
@@ -486,6 +508,7 @@ config_data! { | |||
486 | completion_addCallArgumentSnippets: bool = true, | 508 | completion_addCallArgumentSnippets: bool = true, |
487 | completion_addCallParenthesis: bool = true, | 509 | completion_addCallParenthesis: bool = true, |
488 | completion_postfix_enable: bool = true, | 510 | completion_postfix_enable: bool = true, |
511 | completion_enableExperimental: bool = true, | ||
489 | 512 | ||
490 | diagnostics_enable: bool = true, | 513 | diagnostics_enable: bool = true, |
491 | diagnostics_enableExperimental: bool = true, | 514 | diagnostics_enableExperimental: bool = true, |
@@ -524,6 +547,6 @@ config_data! { | |||
524 | rustfmt_extraArgs: Vec<String> = Vec::new(), | 547 | rustfmt_extraArgs: Vec<String> = Vec::new(), |
525 | rustfmt_overrideCommand: Option<Vec<String>> = None, | 548 | rustfmt_overrideCommand: Option<Vec<String>> = None, |
526 | 549 | ||
527 | withSysroot: bool = true, | 550 | rustcSource : Option<String> = None, |
528 | } | 551 | } |
529 | } | 552 | } |
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/clippy_pass_by_ref.txt b/crates/rust-analyzer/src/diagnostics/test_data/clippy_pass_by_ref.txt index 58d47d32a..72f6c5725 100644 --- a/crates/rust-analyzer/src/diagnostics/test_data/clippy_pass_by_ref.txt +++ b/crates/rust-analyzer/src/diagnostics/test_data/clippy_pass_by_ref.txt | |||
@@ -1,6 +1,13 @@ | |||
1 | [ | 1 | [ |
2 | MappedRustDiagnostic { | 2 | MappedRustDiagnostic { |
3 | url: "file:///test/compiler/mir/tagset.rs", | 3 | url: Url { |
4 | scheme: "file", | ||
5 | host: None, | ||
6 | port: None, | ||
7 | path: "/test/compiler/mir/tagset.rs", | ||
8 | query: None, | ||
9 | fragment: None, | ||
10 | }, | ||
4 | diagnostic: Diagnostic { | 11 | diagnostic: Diagnostic { |
5 | range: Range { | 12 | range: Range { |
6 | start: Position { | 13 | start: Position { |
@@ -20,7 +27,24 @@ | |||
20 | "trivially_copy_pass_by_ref", | 27 | "trivially_copy_pass_by_ref", |
21 | ), | 28 | ), |
22 | ), | 29 | ), |
23 | code_description: None, | 30 | code_description: Some( |
31 | CodeDescription { | ||
32 | href: Url { | ||
33 | scheme: "https", | ||
34 | host: Some( | ||
35 | Domain( | ||
36 | "rust-lang.github.io", | ||
37 | ), | ||
38 | ), | ||
39 | port: None, | ||
40 | path: "/rust-clippy/master/index.html", | ||
41 | query: None, | ||
42 | fragment: Some( | ||
43 | "trivially_copy_pass_by_ref", | ||
44 | ), | ||
45 | }, | ||
46 | }, | ||
47 | ), | ||
24 | source: Some( | 48 | source: Some( |
25 | "clippy", | 49 | "clippy", |
26 | ), | 50 | ), |
@@ -29,7 +53,14 @@ | |||
29 | [ | 53 | [ |
30 | DiagnosticRelatedInformation { | 54 | DiagnosticRelatedInformation { |
31 | location: Location { | 55 | location: Location { |
32 | uri: "file:///test/compiler/lib.rs", | 56 | uri: Url { |
57 | scheme: "file", | ||
58 | host: None, | ||
59 | port: None, | ||
60 | path: "/test/compiler/lib.rs", | ||
61 | query: None, | ||
62 | fragment: None, | ||
63 | }, | ||
33 | range: Range { | 64 | range: Range { |
34 | start: Position { | 65 | start: Position { |
35 | line: 0, | 66 | line: 0, |
@@ -45,7 +76,14 @@ | |||
45 | }, | 76 | }, |
46 | DiagnosticRelatedInformation { | 77 | DiagnosticRelatedInformation { |
47 | location: Location { | 78 | location: Location { |
48 | uri: "file:///test/compiler/mir/tagset.rs", | 79 | uri: Url { |
80 | scheme: "file", | ||
81 | host: None, | ||
82 | port: None, | ||
83 | path: "/test/compiler/mir/tagset.rs", | ||
84 | query: None, | ||
85 | fragment: None, | ||
86 | }, | ||
49 | range: Range { | 87 | range: Range { |
50 | start: Position { | 88 | start: Position { |
51 | line: 41, | 89 | line: 41, |
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/handles_macro_location.txt b/crates/rust-analyzer/src/diagnostics/test_data/handles_macro_location.txt index 6aa26bf63..eb4a6b597 100644 --- a/crates/rust-analyzer/src/diagnostics/test_data/handles_macro_location.txt +++ b/crates/rust-analyzer/src/diagnostics/test_data/handles_macro_location.txt | |||
@@ -1,6 +1,13 @@ | |||
1 | [ | 1 | [ |
2 | MappedRustDiagnostic { | 2 | MappedRustDiagnostic { |
3 | url: "file:///test/src/main.rs", | 3 | url: Url { |
4 | scheme: "file", | ||
5 | host: None, | ||
6 | port: None, | ||
7 | path: "/test/src/main.rs", | ||
8 | query: None, | ||
9 | fragment: None, | ||
10 | }, | ||
4 | diagnostic: Diagnostic { | 11 | diagnostic: Diagnostic { |
5 | range: Range { | 12 | range: Range { |
6 | start: Position { | 13 | start: Position { |
@@ -20,7 +27,24 @@ | |||
20 | "E0277", | 27 | "E0277", |
21 | ), | 28 | ), |
22 | ), | 29 | ), |
23 | code_description: None, | 30 | code_description: Some( |
31 | CodeDescription { | ||
32 | href: Url { | ||
33 | scheme: "https", | ||
34 | host: Some( | ||
35 | Domain( | ||
36 | "doc.rust-lang.org", | ||
37 | ), | ||
38 | ), | ||
39 | port: None, | ||
40 | path: "/error-index.html", | ||
41 | query: None, | ||
42 | fragment: Some( | ||
43 | "E0277", | ||
44 | ), | ||
45 | }, | ||
46 | }, | ||
47 | ), | ||
24 | source: Some( | 48 | source: Some( |
25 | "rustc", | 49 | "rustc", |
26 | ), | 50 | ), |
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/macro_compiler_error.txt b/crates/rust-analyzer/src/diagnostics/test_data/macro_compiler_error.txt index 7aaffaba2..bbec6a796 100644 --- a/crates/rust-analyzer/src/diagnostics/test_data/macro_compiler_error.txt +++ b/crates/rust-analyzer/src/diagnostics/test_data/macro_compiler_error.txt | |||
@@ -1,6 +1,13 @@ | |||
1 | [ | 1 | [ |
2 | MappedRustDiagnostic { | 2 | MappedRustDiagnostic { |
3 | url: "file:///test/crates/hir_def/src/data.rs", | 3 | url: Url { |
4 | scheme: "file", | ||
5 | host: None, | ||
6 | port: None, | ||
7 | path: "/test/crates/hir_def/src/data.rs", | ||
8 | query: None, | ||
9 | fragment: None, | ||
10 | }, | ||
4 | diagnostic: Diagnostic { | 11 | diagnostic: Diagnostic { |
5 | range: Range { | 12 | range: Range { |
6 | start: Position { | 13 | start: Position { |
@@ -25,7 +32,14 @@ | |||
25 | [ | 32 | [ |
26 | DiagnosticRelatedInformation { | 33 | DiagnosticRelatedInformation { |
27 | location: Location { | 34 | location: Location { |
28 | uri: "file:///test/crates/hir_def/src/path.rs", | 35 | uri: Url { |
36 | scheme: "file", | ||
37 | host: None, | ||
38 | port: None, | ||
39 | path: "/test/crates/hir_def/src/path.rs", | ||
40 | query: None, | ||
41 | fragment: None, | ||
42 | }, | ||
29 | range: Range { | 43 | range: Range { |
30 | start: Position { | 44 | start: Position { |
31 | line: 264, | 45 | line: 264, |
@@ -47,7 +61,14 @@ | |||
47 | fixes: [], | 61 | fixes: [], |
48 | }, | 62 | }, |
49 | MappedRustDiagnostic { | 63 | MappedRustDiagnostic { |
50 | url: "file:///test/crates/hir_def/src/path.rs", | 64 | url: Url { |
65 | scheme: "file", | ||
66 | host: None, | ||
67 | port: None, | ||
68 | path: "/test/crates/hir_def/src/path.rs", | ||
69 | query: None, | ||
70 | fragment: None, | ||
71 | }, | ||
51 | diagnostic: Diagnostic { | 72 | diagnostic: Diagnostic { |
52 | range: Range { | 73 | range: Range { |
53 | start: Position { | 74 | start: Position { |
@@ -72,7 +93,14 @@ | |||
72 | [ | 93 | [ |
73 | DiagnosticRelatedInformation { | 94 | DiagnosticRelatedInformation { |
74 | location: Location { | 95 | location: Location { |
75 | uri: "file:///test/crates/hir_def/src/data.rs", | 96 | uri: Url { |
97 | scheme: "file", | ||
98 | host: None, | ||
99 | port: None, | ||
100 | path: "/test/crates/hir_def/src/data.rs", | ||
101 | query: None, | ||
102 | fragment: None, | ||
103 | }, | ||
76 | range: Range { | 104 | range: Range { |
77 | start: Position { | 105 | start: Position { |
78 | line: 79, | 106 | line: 79, |
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/rustc_incompatible_type_for_trait.txt b/crates/rust-analyzer/src/diagnostics/test_data/rustc_incompatible_type_for_trait.txt index 584213420..19f72196d 100644 --- a/crates/rust-analyzer/src/diagnostics/test_data/rustc_incompatible_type_for_trait.txt +++ b/crates/rust-analyzer/src/diagnostics/test_data/rustc_incompatible_type_for_trait.txt | |||
@@ -1,6 +1,13 @@ | |||
1 | [ | 1 | [ |
2 | MappedRustDiagnostic { | 2 | MappedRustDiagnostic { |
3 | url: "file:///test/compiler/ty/list_iter.rs", | 3 | url: Url { |
4 | scheme: "file", | ||
5 | host: None, | ||
6 | port: None, | ||
7 | path: "/test/compiler/ty/list_iter.rs", | ||
8 | query: None, | ||
9 | fragment: None, | ||
10 | }, | ||
4 | diagnostic: Diagnostic { | 11 | diagnostic: Diagnostic { |
5 | range: Range { | 12 | range: Range { |
6 | start: Position { | 13 | start: Position { |
@@ -20,7 +27,24 @@ | |||
20 | "E0053", | 27 | "E0053", |
21 | ), | 28 | ), |
22 | ), | 29 | ), |
23 | code_description: None, | 30 | code_description: Some( |
31 | CodeDescription { | ||
32 | href: Url { | ||
33 | scheme: "https", | ||
34 | host: Some( | ||
35 | Domain( | ||
36 | "doc.rust-lang.org", | ||
37 | ), | ||
38 | ), | ||
39 | port: None, | ||
40 | path: "/error-index.html", | ||
41 | query: None, | ||
42 | fragment: Some( | ||
43 | "E0053", | ||
44 | ), | ||
45 | }, | ||
46 | }, | ||
47 | ), | ||
24 | source: Some( | 48 | source: Some( |
25 | "rustc", | 49 | "rustc", |
26 | ), | 50 | ), |
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/rustc_mismatched_type.txt b/crates/rust-analyzer/src/diagnostics/test_data/rustc_mismatched_type.txt index 2610e4e20..15ac95d72 100644 --- a/crates/rust-analyzer/src/diagnostics/test_data/rustc_mismatched_type.txt +++ b/crates/rust-analyzer/src/diagnostics/test_data/rustc_mismatched_type.txt | |||
@@ -1,6 +1,13 @@ | |||
1 | [ | 1 | [ |
2 | MappedRustDiagnostic { | 2 | MappedRustDiagnostic { |
3 | url: "file:///test/runtime/compiler_support.rs", | 3 | url: Url { |
4 | scheme: "file", | ||
5 | host: None, | ||
6 | port: None, | ||
7 | path: "/test/runtime/compiler_support.rs", | ||
8 | query: None, | ||
9 | fragment: None, | ||
10 | }, | ||
4 | diagnostic: Diagnostic { | 11 | diagnostic: Diagnostic { |
5 | range: Range { | 12 | range: Range { |
6 | start: Position { | 13 | start: Position { |
@@ -20,7 +27,24 @@ | |||
20 | "E0308", | 27 | "E0308", |
21 | ), | 28 | ), |
22 | ), | 29 | ), |
23 | code_description: None, | 30 | code_description: Some( |
31 | CodeDescription { | ||
32 | href: Url { | ||
33 | scheme: "https", | ||
34 | host: Some( | ||
35 | Domain( | ||
36 | "doc.rust-lang.org", | ||
37 | ), | ||
38 | ), | ||
39 | port: None, | ||
40 | path: "/error-index.html", | ||
41 | query: None, | ||
42 | fragment: Some( | ||
43 | "E0308", | ||
44 | ), | ||
45 | }, | ||
46 | }, | ||
47 | ), | ||
24 | source: Some( | 48 | source: Some( |
25 | "rustc", | 49 | "rustc", |
26 | ), | 50 | ), |
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable.txt b/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable.txt index 8dc53391e..c709de95f 100644 --- a/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable.txt +++ b/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable.txt | |||
@@ -1,6 +1,13 @@ | |||
1 | [ | 1 | [ |
2 | MappedRustDiagnostic { | 2 | MappedRustDiagnostic { |
3 | url: "file:///test/driver/subcommand/repl.rs", | 3 | url: Url { |
4 | scheme: "file", | ||
5 | host: None, | ||
6 | port: None, | ||
7 | path: "/test/driver/subcommand/repl.rs", | ||
8 | query: None, | ||
9 | fragment: None, | ||
10 | }, | ||
4 | diagnostic: Diagnostic { | 11 | diagnostic: Diagnostic { |
5 | range: Range { | 12 | range: Range { |
6 | start: Position { | 13 | start: Position { |
@@ -36,7 +43,6 @@ | |||
36 | fixes: [ | 43 | fixes: [ |
37 | CodeAction { | 44 | CodeAction { |
38 | title: "consider prefixing with an underscore", | 45 | title: "consider prefixing with an underscore", |
39 | id: None, | ||
40 | group: None, | 46 | group: None, |
41 | kind: Some( | 47 | kind: Some( |
42 | CodeActionKind( | 48 | CodeActionKind( |
@@ -47,7 +53,14 @@ | |||
47 | SnippetWorkspaceEdit { | 53 | SnippetWorkspaceEdit { |
48 | changes: Some( | 54 | changes: Some( |
49 | { | 55 | { |
50 | "file:///test/driver/subcommand/repl.rs": [ | 56 | Url { |
57 | scheme: "file", | ||
58 | host: None, | ||
59 | port: None, | ||
60 | path: "/test/driver/subcommand/repl.rs", | ||
61 | query: None, | ||
62 | fragment: None, | ||
63 | }: [ | ||
51 | TextEdit { | 64 | TextEdit { |
52 | range: Range { | 65 | range: Range { |
53 | start: Position { | 66 | start: Position { |
@@ -70,6 +83,7 @@ | |||
70 | is_preferred: Some( | 83 | is_preferred: Some( |
71 | true, | 84 | true, |
72 | ), | 85 | ), |
86 | data: None, | ||
73 | }, | 87 | }, |
74 | ], | 88 | ], |
75 | }, | 89 | }, |
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_hint.txt b/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_hint.txt index c8703194c..632f438d7 100644 --- a/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_hint.txt +++ b/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_hint.txt | |||
@@ -1,6 +1,13 @@ | |||
1 | [ | 1 | [ |
2 | MappedRustDiagnostic { | 2 | MappedRustDiagnostic { |
3 | url: "file:///test/driver/subcommand/repl.rs", | 3 | url: Url { |
4 | scheme: "file", | ||
5 | host: None, | ||
6 | port: None, | ||
7 | path: "/test/driver/subcommand/repl.rs", | ||
8 | query: None, | ||
9 | fragment: None, | ||
10 | }, | ||
4 | diagnostic: Diagnostic { | 11 | diagnostic: Diagnostic { |
5 | range: Range { | 12 | range: Range { |
6 | start: Position { | 13 | start: Position { |
@@ -36,7 +43,6 @@ | |||
36 | fixes: [ | 43 | fixes: [ |
37 | CodeAction { | 44 | CodeAction { |
38 | title: "consider prefixing with an underscore", | 45 | title: "consider prefixing with an underscore", |
39 | id: None, | ||
40 | group: None, | 46 | group: None, |
41 | kind: Some( | 47 | kind: Some( |
42 | CodeActionKind( | 48 | CodeActionKind( |
@@ -47,7 +53,14 @@ | |||
47 | SnippetWorkspaceEdit { | 53 | SnippetWorkspaceEdit { |
48 | changes: Some( | 54 | changes: Some( |
49 | { | 55 | { |
50 | "file:///test/driver/subcommand/repl.rs": [ | 56 | Url { |
57 | scheme: "file", | ||
58 | host: None, | ||
59 | port: None, | ||
60 | path: "/test/driver/subcommand/repl.rs", | ||
61 | query: None, | ||
62 | fragment: None, | ||
63 | }: [ | ||
51 | TextEdit { | 64 | TextEdit { |
52 | range: Range { | 65 | range: Range { |
53 | start: Position { | 66 | start: Position { |
@@ -70,6 +83,7 @@ | |||
70 | is_preferred: Some( | 83 | is_preferred: Some( |
71 | true, | 84 | true, |
72 | ), | 85 | ), |
86 | data: None, | ||
73 | }, | 87 | }, |
74 | ], | 88 | ], |
75 | }, | 89 | }, |
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_info.txt b/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_info.txt index dc93227ad..c0b79428d 100644 --- a/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_info.txt +++ b/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_info.txt | |||
@@ -1,6 +1,13 @@ | |||
1 | [ | 1 | [ |
2 | MappedRustDiagnostic { | 2 | MappedRustDiagnostic { |
3 | url: "file:///test/driver/subcommand/repl.rs", | 3 | url: Url { |
4 | scheme: "file", | ||
5 | host: None, | ||
6 | port: None, | ||
7 | path: "/test/driver/subcommand/repl.rs", | ||
8 | query: None, | ||
9 | fragment: None, | ||
10 | }, | ||
4 | diagnostic: Diagnostic { | 11 | diagnostic: Diagnostic { |
5 | range: Range { | 12 | range: Range { |
6 | start: Position { | 13 | start: Position { |
@@ -36,7 +43,6 @@ | |||
36 | fixes: [ | 43 | fixes: [ |
37 | CodeAction { | 44 | CodeAction { |
38 | title: "consider prefixing with an underscore", | 45 | title: "consider prefixing with an underscore", |
39 | id: None, | ||
40 | group: None, | 46 | group: None, |
41 | kind: Some( | 47 | kind: Some( |
42 | CodeActionKind( | 48 | CodeActionKind( |
@@ -47,7 +53,14 @@ | |||
47 | SnippetWorkspaceEdit { | 53 | SnippetWorkspaceEdit { |
48 | changes: Some( | 54 | changes: Some( |
49 | { | 55 | { |
50 | "file:///test/driver/subcommand/repl.rs": [ | 56 | Url { |
57 | scheme: "file", | ||
58 | host: None, | ||
59 | port: None, | ||
60 | path: "/test/driver/subcommand/repl.rs", | ||
61 | query: None, | ||
62 | fragment: None, | ||
63 | }: [ | ||
51 | TextEdit { | 64 | TextEdit { |
52 | range: Range { | 65 | range: Range { |
53 | start: Position { | 66 | start: Position { |
@@ -70,6 +83,7 @@ | |||
70 | is_preferred: Some( | 83 | is_preferred: Some( |
71 | true, | 84 | true, |
72 | ), | 85 | ), |
86 | data: None, | ||
73 | }, | 87 | }, |
74 | ], | 88 | ], |
75 | }, | 89 | }, |
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/rustc_wrong_number_of_parameters.txt b/crates/rust-analyzer/src/diagnostics/test_data/rustc_wrong_number_of_parameters.txt index ba1b98b33..b9650f3e4 100644 --- a/crates/rust-analyzer/src/diagnostics/test_data/rustc_wrong_number_of_parameters.txt +++ b/crates/rust-analyzer/src/diagnostics/test_data/rustc_wrong_number_of_parameters.txt | |||
@@ -1,6 +1,13 @@ | |||
1 | [ | 1 | [ |
2 | MappedRustDiagnostic { | 2 | MappedRustDiagnostic { |
3 | url: "file:///test/compiler/ty/select.rs", | 3 | url: Url { |
4 | scheme: "file", | ||
5 | host: None, | ||
6 | port: None, | ||
7 | path: "/test/compiler/ty/select.rs", | ||
8 | query: None, | ||
9 | fragment: None, | ||
10 | }, | ||
4 | diagnostic: Diagnostic { | 11 | diagnostic: Diagnostic { |
5 | range: Range { | 12 | range: Range { |
6 | start: Position { | 13 | start: Position { |
@@ -20,7 +27,24 @@ | |||
20 | "E0061", | 27 | "E0061", |
21 | ), | 28 | ), |
22 | ), | 29 | ), |
23 | code_description: None, | 30 | code_description: Some( |
31 | CodeDescription { | ||
32 | href: Url { | ||
33 | scheme: "https", | ||
34 | host: Some( | ||
35 | Domain( | ||
36 | "doc.rust-lang.org", | ||
37 | ), | ||
38 | ), | ||
39 | port: None, | ||
40 | path: "/error-index.html", | ||
41 | query: None, | ||
42 | fragment: Some( | ||
43 | "E0061", | ||
44 | ), | ||
45 | }, | ||
46 | }, | ||
47 | ), | ||
24 | source: Some( | 48 | source: Some( |
25 | "rustc", | 49 | "rustc", |
26 | ), | 50 | ), |
@@ -29,7 +53,14 @@ | |||
29 | [ | 53 | [ |
30 | DiagnosticRelatedInformation { | 54 | DiagnosticRelatedInformation { |
31 | location: Location { | 55 | location: Location { |
32 | uri: "file:///test/compiler/ty/select.rs", | 56 | uri: Url { |
57 | scheme: "file", | ||
58 | host: None, | ||
59 | port: None, | ||
60 | path: "/test/compiler/ty/select.rs", | ||
61 | query: None, | ||
62 | fragment: None, | ||
63 | }, | ||
33 | range: Range { | 64 | range: Range { |
34 | start: Position { | 65 | start: Position { |
35 | line: 218, | 66 | line: 218, |
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/snap_multi_line_fix.txt b/crates/rust-analyzer/src/diagnostics/test_data/snap_multi_line_fix.txt index 81f752672..c45f68a91 100644 --- a/crates/rust-analyzer/src/diagnostics/test_data/snap_multi_line_fix.txt +++ b/crates/rust-analyzer/src/diagnostics/test_data/snap_multi_line_fix.txt | |||
@@ -1,6 +1,13 @@ | |||
1 | [ | 1 | [ |
2 | MappedRustDiagnostic { | 2 | MappedRustDiagnostic { |
3 | url: "file:///test/src/main.rs", | 3 | url: Url { |
4 | scheme: "file", | ||
5 | host: None, | ||
6 | port: None, | ||
7 | path: "/test/src/main.rs", | ||
8 | query: None, | ||
9 | fragment: None, | ||
10 | }, | ||
4 | diagnostic: Diagnostic { | 11 | diagnostic: Diagnostic { |
5 | range: Range { | 12 | range: Range { |
6 | start: Position { | 13 | start: Position { |
@@ -20,7 +27,24 @@ | |||
20 | "let_and_return", | 27 | "let_and_return", |
21 | ), | 28 | ), |
22 | ), | 29 | ), |
23 | code_description: None, | 30 | code_description: Some( |
31 | CodeDescription { | ||
32 | href: Url { | ||
33 | scheme: "https", | ||
34 | host: Some( | ||
35 | Domain( | ||
36 | "rust-lang.github.io", | ||
37 | ), | ||
38 | ), | ||
39 | port: None, | ||
40 | path: "/rust-clippy/master/index.html", | ||
41 | query: None, | ||
42 | fragment: Some( | ||
43 | "let_and_return", | ||
44 | ), | ||
45 | }, | ||
46 | }, | ||
47 | ), | ||
24 | source: Some( | 48 | source: Some( |
25 | "clippy", | 49 | "clippy", |
26 | ), | 50 | ), |
@@ -29,7 +53,14 @@ | |||
29 | [ | 53 | [ |
30 | DiagnosticRelatedInformation { | 54 | DiagnosticRelatedInformation { |
31 | location: Location { | 55 | location: Location { |
32 | uri: "file:///test/src/main.rs", | 56 | uri: Url { |
57 | scheme: "file", | ||
58 | host: None, | ||
59 | port: None, | ||
60 | path: "/test/src/main.rs", | ||
61 | query: None, | ||
62 | fragment: None, | ||
63 | }, | ||
33 | range: Range { | 64 | range: Range { |
34 | start: Position { | 65 | start: Position { |
35 | line: 2, | 66 | line: 2, |
@@ -51,7 +82,6 @@ | |||
51 | fixes: [ | 82 | fixes: [ |
52 | CodeAction { | 83 | CodeAction { |
53 | title: "return the expression directly", | 84 | title: "return the expression directly", |
54 | id: None, | ||
55 | group: None, | 85 | group: None, |
56 | kind: Some( | 86 | kind: Some( |
57 | CodeActionKind( | 87 | CodeActionKind( |
@@ -62,7 +92,14 @@ | |||
62 | SnippetWorkspaceEdit { | 92 | SnippetWorkspaceEdit { |
63 | changes: Some( | 93 | changes: Some( |
64 | { | 94 | { |
65 | "file:///test/src/main.rs": [ | 95 | Url { |
96 | scheme: "file", | ||
97 | host: None, | ||
98 | port: None, | ||
99 | path: "/test/src/main.rs", | ||
100 | query: None, | ||
101 | fragment: None, | ||
102 | }: [ | ||
66 | TextEdit { | 103 | TextEdit { |
67 | range: Range { | 104 | range: Range { |
68 | start: Position { | 105 | start: Position { |
@@ -98,6 +135,7 @@ | |||
98 | is_preferred: Some( | 135 | is_preferred: Some( |
99 | true, | 136 | true, |
100 | ), | 137 | ), |
138 | data: None, | ||
101 | }, | 139 | }, |
102 | ], | 140 | ], |
103 | }, | 141 | }, |
diff --git a/crates/rust-analyzer/src/diagnostics/to_proto.rs b/crates/rust-analyzer/src/diagnostics/to_proto.rs index b949577c1..324019614 100644 --- a/crates/rust-analyzer/src/diagnostics/to_proto.rs +++ b/crates/rust-analyzer/src/diagnostics/to_proto.rs | |||
@@ -55,8 +55,8 @@ fn location_naive(workspace_root: &Path, span: &DiagnosticSpan) -> lsp_types::Lo | |||
55 | 55 | ||
56 | // FIXME: this doesn't handle UTF16 offsets correctly | 56 | // FIXME: this doesn't handle UTF16 offsets correctly |
57 | let range = lsp_types::Range::new( | 57 | let range = lsp_types::Range::new( |
58 | lsp_types::Position::new(span.line_start as u64 - 1, span.column_start as u64 - 1), | 58 | lsp_types::Position::new(span.line_start as u32 - 1, span.column_start as u32 - 1), |
59 | lsp_types::Position::new(span.line_end as u64 - 1, span.column_end as u64 - 1), | 59 | lsp_types::Position::new(span.line_end as u32 - 1, span.column_end as u32 - 1), |
60 | ); | 60 | ); |
61 | 61 | ||
62 | lsp_types::Location { uri, range } | 62 | lsp_types::Location { uri, range } |
@@ -110,7 +110,6 @@ fn map_rust_child_diagnostic( | |||
110 | } else { | 110 | } else { |
111 | MappedRustChildDiagnostic::SuggestedFix(lsp_ext::CodeAction { | 111 | MappedRustChildDiagnostic::SuggestedFix(lsp_ext::CodeAction { |
112 | title: rd.message.clone(), | 112 | title: rd.message.clone(), |
113 | id: None, | ||
114 | group: None, | 113 | group: None, |
115 | kind: Some(lsp_types::CodeActionKind::QUICKFIX), | 114 | kind: Some(lsp_types::CodeActionKind::QUICKFIX), |
116 | edit: Some(lsp_ext::SnippetWorkspaceEdit { | 115 | edit: Some(lsp_ext::SnippetWorkspaceEdit { |
@@ -119,6 +118,7 @@ fn map_rust_child_diagnostic( | |||
119 | document_changes: None, | 118 | document_changes: None, |
120 | }), | 119 | }), |
121 | is_preferred: Some(true), | 120 | is_preferred: Some(true), |
121 | data: None, | ||
122 | }) | 122 | }) |
123 | } | 123 | } |
124 | } | 124 | } |
@@ -211,6 +211,12 @@ pub(crate) fn map_rust_diagnostic_to_lsp( | |||
211 | } | 211 | } |
212 | } | 212 | } |
213 | 213 | ||
214 | let code_description = match source.as_str() { | ||
215 | "rustc" => rustc_code_description(code.as_deref()), | ||
216 | "clippy" => clippy_code_description(code.as_deref()), | ||
217 | _ => None, | ||
218 | }; | ||
219 | |||
214 | primary_spans | 220 | primary_spans |
215 | .iter() | 221 | .iter() |
216 | .map(|primary_span| { | 222 | .map(|primary_span| { |
@@ -248,7 +254,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp( | |||
248 | range: in_macro_location.range, | 254 | range: in_macro_location.range, |
249 | severity, | 255 | severity, |
250 | code: code.clone().map(lsp_types::NumberOrString::String), | 256 | code: code.clone().map(lsp_types::NumberOrString::String), |
251 | code_description: None, | 257 | code_description: code_description.clone(), |
252 | source: Some(source.clone()), | 258 | source: Some(source.clone()), |
253 | message: message.clone(), | 259 | message: message.clone(), |
254 | related_information: Some(information_for_additional_diagnostic), | 260 | related_information: Some(information_for_additional_diagnostic), |
@@ -269,7 +275,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp( | |||
269 | range: location.range, | 275 | range: location.range, |
270 | severity, | 276 | severity, |
271 | code: code.clone().map(lsp_types::NumberOrString::String), | 277 | code: code.clone().map(lsp_types::NumberOrString::String), |
272 | code_description: None, | 278 | code_description: code_description.clone(), |
273 | source: Some(source.clone()), | 279 | source: Some(source.clone()), |
274 | message, | 280 | message, |
275 | related_information: if related_information.is_empty() { | 281 | related_information: if related_information.is_empty() { |
@@ -292,6 +298,31 @@ pub(crate) fn map_rust_diagnostic_to_lsp( | |||
292 | .collect() | 298 | .collect() |
293 | } | 299 | } |
294 | 300 | ||
301 | fn rustc_code_description(code: Option<&str>) -> Option<lsp_types::CodeDescription> { | ||
302 | code.filter(|code| { | ||
303 | let mut chars = code.chars(); | ||
304 | chars.next().map_or(false, |c| c == 'E') | ||
305 | && chars.by_ref().take(4).all(|c| c.is_ascii_digit()) | ||
306 | && chars.next().is_none() | ||
307 | }) | ||
308 | .and_then(|code| { | ||
309 | lsp_types::Url::parse(&format!("https://doc.rust-lang.org/error-index.html#{}", code)) | ||
310 | .ok() | ||
311 | .map(|href| lsp_types::CodeDescription { href }) | ||
312 | }) | ||
313 | } | ||
314 | |||
315 | fn clippy_code_description(code: Option<&str>) -> Option<lsp_types::CodeDescription> { | ||
316 | code.and_then(|code| { | ||
317 | lsp_types::Url::parse(&format!( | ||
318 | "https://rust-lang.github.io/rust-clippy/master/index.html#{}", | ||
319 | code | ||
320 | )) | ||
321 | .ok() | ||
322 | .map(|href| lsp_types::CodeDescription { href }) | ||
323 | }) | ||
324 | } | ||
325 | |||
295 | #[cfg(test)] | 326 | #[cfg(test)] |
296 | #[cfg(not(windows))] | 327 | #[cfg(not(windows))] |
297 | mod tests { | 328 | mod tests { |
diff --git a/crates/rust-analyzer/src/document.rs b/crates/rust-analyzer/src/document.rs index 04c7ee150..cf091510f 100644 --- a/crates/rust-analyzer/src/document.rs +++ b/crates/rust-analyzer/src/document.rs | |||
@@ -6,11 +6,11 @@ | |||
6 | /// client notifications. | 6 | /// client notifications. |
7 | #[derive(Debug, Clone)] | 7 | #[derive(Debug, Clone)] |
8 | pub(crate) struct DocumentData { | 8 | pub(crate) struct DocumentData { |
9 | pub(crate) version: Option<i64>, | 9 | pub(crate) version: i32, |
10 | } | 10 | } |
11 | 11 | ||
12 | impl DocumentData { | 12 | impl DocumentData { |
13 | pub(crate) fn new(version: i64) -> Self { | 13 | pub(crate) fn new(version: i32) -> Self { |
14 | DocumentData { version: Some(version) } | 14 | DocumentData { version } |
15 | } | 15 | } |
16 | } | 16 | } |
diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs index 63c70a09d..defe11c55 100644 --- a/crates/rust-analyzer/src/global_state.rs +++ b/crates/rust-analyzer/src/global_state.rs | |||
@@ -263,9 +263,9 @@ impl GlobalStateSnapshot { | |||
263 | self.vfs.read().1[&id] | 263 | self.vfs.read().1[&id] |
264 | } | 264 | } |
265 | 265 | ||
266 | pub(crate) fn url_file_version(&self, url: &Url) -> Option<i64> { | 266 | pub(crate) fn url_file_version(&self, url: &Url) -> Option<i32> { |
267 | let path = from_proto::vfs_path(&url).ok()?; | 267 | let path = from_proto::vfs_path(&url).ok()?; |
268 | self.mem_docs.get(&path)?.version | 268 | Some(self.mem_docs.get(&path)?.version) |
269 | } | 269 | } |
270 | 270 | ||
271 | pub(crate) fn anchored_path(&self, file_id: FileId, path: &str) -> Url { | 271 | pub(crate) fn anchored_path(&self, file_id: FileId, path: &str) -> Url { |
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs index 049c583a4..1cf4139d2 100644 --- a/crates/rust-analyzer/src/handlers.rs +++ b/crates/rust-analyzer/src/handlers.rs | |||
@@ -18,7 +18,7 @@ use lsp_types::{ | |||
18 | CallHierarchyOutgoingCall, CallHierarchyOutgoingCallsParams, CallHierarchyPrepareParams, | 18 | CallHierarchyOutgoingCall, CallHierarchyOutgoingCallsParams, CallHierarchyPrepareParams, |
19 | CodeActionKind, CodeLens, Command, CompletionItem, Diagnostic, DiagnosticTag, | 19 | CodeActionKind, CodeLens, Command, CompletionItem, Diagnostic, DiagnosticTag, |
20 | DocumentFormattingParams, DocumentHighlight, DocumentSymbol, FoldingRange, FoldingRangeParams, | 20 | DocumentFormattingParams, DocumentHighlight, DocumentSymbol, FoldingRange, FoldingRangeParams, |
21 | HoverContents, Location, Position, PrepareRenameResponse, Range, RenameParams, | 21 | HoverContents, Location, NumberOrString, Position, PrepareRenameResponse, Range, RenameParams, |
22 | SemanticTokensDeltaParams, SemanticTokensFullDeltaResult, SemanticTokensParams, | 22 | SemanticTokensDeltaParams, SemanticTokensFullDeltaResult, SemanticTokensParams, |
23 | SemanticTokensRangeParams, SemanticTokensRangeResult, SemanticTokensResult, SymbolInformation, | 23 | SemanticTokensRangeParams, SemanticTokensRangeResult, SemanticTokensResult, SymbolInformation, |
24 | SymbolTag, TextDocumentIdentifier, Url, WorkspaceEdit, | 24 | SymbolTag, TextDocumentIdentifier, Url, WorkspaceEdit, |
@@ -573,7 +573,8 @@ pub(crate) fn handle_completion( | |||
573 | .flat_map(|item| to_proto::completion_item(&line_index, line_endings, item)) | 573 | .flat_map(|item| to_proto::completion_item(&line_index, line_endings, item)) |
574 | .collect(); | 574 | .collect(); |
575 | 575 | ||
576 | Ok(Some(items.into())) | 576 | let completion_list = lsp_types::CompletionList { is_incomplete: true, items }; |
577 | Ok(Some(completion_list.into())) | ||
577 | } | 578 | } |
578 | 579 | ||
579 | pub(crate) fn handle_folding_range( | 580 | pub(crate) fn handle_folding_range( |
@@ -806,11 +807,11 @@ fn handle_fixes( | |||
806 | let edit = to_proto::snippet_workspace_edit(&snap, fix.source_change)?; | 807 | let edit = to_proto::snippet_workspace_edit(&snap, fix.source_change)?; |
807 | let action = lsp_ext::CodeAction { | 808 | let action = lsp_ext::CodeAction { |
808 | title: fix.label.to_string(), | 809 | title: fix.label.to_string(), |
809 | id: None, | ||
810 | group: None, | 810 | group: None, |
811 | kind: Some(CodeActionKind::QUICKFIX), | 811 | kind: Some(CodeActionKind::QUICKFIX), |
812 | edit: Some(edit), | 812 | edit: Some(edit), |
813 | is_preferred: Some(false), | 813 | is_preferred: Some(false), |
814 | data: None, | ||
814 | }; | 815 | }; |
815 | res.push(action); | 816 | res.push(action); |
816 | } | 817 | } |
@@ -852,11 +853,11 @@ pub(crate) fn handle_code_action( | |||
852 | 853 | ||
853 | handle_fixes(&snap, ¶ms, &mut res)?; | 854 | handle_fixes(&snap, ¶ms, &mut res)?; |
854 | 855 | ||
855 | if snap.config.client_caps.resolve_code_action { | 856 | if snap.config.client_caps.code_action_resolve { |
856 | for (index, assist) in | 857 | for (index, assist) in |
857 | snap.analysis.unresolved_assists(&snap.config.assist, frange)?.into_iter().enumerate() | 858 | snap.analysis.unresolved_assists(&snap.config.assist, frange)?.into_iter().enumerate() |
858 | { | 859 | { |
859 | res.push(to_proto::unresolved_code_action(&snap, assist, index)?); | 860 | res.push(to_proto::unresolved_code_action(&snap, params.clone(), assist, index)?); |
860 | } | 861 | } |
861 | } else { | 862 | } else { |
862 | for assist in snap.analysis.resolved_assists(&snap.config.assist, frange)?.into_iter() { | 863 | for assist in snap.analysis.resolved_assists(&snap.config.assist, frange)?.into_iter() { |
@@ -867,11 +868,16 @@ pub(crate) fn handle_code_action( | |||
867 | Ok(Some(res)) | 868 | Ok(Some(res)) |
868 | } | 869 | } |
869 | 870 | ||
870 | pub(crate) fn handle_resolve_code_action( | 871 | pub(crate) fn handle_code_action_resolve( |
871 | mut snap: GlobalStateSnapshot, | 872 | mut snap: GlobalStateSnapshot, |
872 | params: lsp_ext::ResolveCodeActionParams, | 873 | mut code_action: lsp_ext::CodeAction, |
873 | ) -> Result<Option<lsp_ext::SnippetWorkspaceEdit>> { | 874 | ) -> Result<lsp_ext::CodeAction> { |
874 | let _p = profile::span("handle_resolve_code_action"); | 875 | let _p = profile::span("handle_code_action_resolve"); |
876 | let params = match code_action.data.take() { | ||
877 | Some(it) => it, | ||
878 | None => Err("can't resolve code action without data")?, | ||
879 | }; | ||
880 | |||
875 | let file_id = from_proto::file_id(&snap, ¶ms.code_action_params.text_document.uri)?; | 881 | let file_id = from_proto::file_id(&snap, ¶ms.code_action_params.text_document.uri)?; |
876 | let line_index = snap.analysis.file_line_index(file_id)?; | 882 | let line_index = snap.analysis.file_line_index(file_id)?; |
877 | let range = from_proto::text_range(&line_index, params.code_action_params.range); | 883 | let range = from_proto::text_range(&line_index, params.code_action_params.range); |
@@ -888,7 +894,9 @@ pub(crate) fn handle_resolve_code_action( | |||
888 | let index = index.parse::<usize>().unwrap(); | 894 | let index = index.parse::<usize>().unwrap(); |
889 | let assist = &assists[index]; | 895 | let assist = &assists[index]; |
890 | assert!(assist.assist.id.0 == id); | 896 | assert!(assist.assist.id.0 == id); |
891 | Ok(to_proto::resolved_code_action(&snap, assist.clone())?.edit) | 897 | let edit = to_proto::resolved_code_action(&snap, assist.clone())?.edit; |
898 | code_action.edit = edit; | ||
899 | Ok(code_action) | ||
892 | } | 900 | } |
893 | 901 | ||
894 | pub(crate) fn handle_code_lens( | 902 | pub(crate) fn handle_code_lens( |
@@ -1120,8 +1128,15 @@ pub(crate) fn publish_diagnostics( | |||
1120 | .map(|d| Diagnostic { | 1128 | .map(|d| Diagnostic { |
1121 | range: to_proto::range(&line_index, d.range), | 1129 | range: to_proto::range(&line_index, d.range), |
1122 | severity: Some(to_proto::diagnostic_severity(d.severity)), | 1130 | severity: Some(to_proto::diagnostic_severity(d.severity)), |
1123 | code: None, | 1131 | code: d.code.map(|d| d.as_str().to_owned()).map(NumberOrString::String), |
1124 | code_description: None, | 1132 | code_description: d.code.and_then(|code| { |
1133 | lsp_types::Url::parse(&format!( | ||
1134 | "https://rust-analyzer.github.io/manual.html#{}", | ||
1135 | code.as_str() | ||
1136 | )) | ||
1137 | .ok() | ||
1138 | .map(|href| lsp_types::CodeDescription { href }) | ||
1139 | }), | ||
1125 | source: Some("rust-analyzer".to_string()), | 1140 | source: Some("rust-analyzer".to_string()), |
1126 | message: d.message, | 1141 | message: d.message, |
1127 | related_information: None, | 1142 | related_information: None, |
@@ -1315,6 +1330,28 @@ pub(crate) fn handle_open_docs( | |||
1315 | Ok(remote.and_then(|remote| Url::parse(&remote).ok())) | 1330 | Ok(remote.and_then(|remote| Url::parse(&remote).ok())) |
1316 | } | 1331 | } |
1317 | 1332 | ||
1333 | pub(crate) fn handle_open_cargo_toml( | ||
1334 | snap: GlobalStateSnapshot, | ||
1335 | params: lsp_ext::OpenCargoTomlParams, | ||
1336 | ) -> Result<Option<lsp_types::GotoDefinitionResponse>> { | ||
1337 | let _p = profile::span("handle_open_cargo_toml"); | ||
1338 | let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; | ||
1339 | let maybe_cargo_spec = CargoTargetSpec::for_file(&snap, file_id)?; | ||
1340 | if maybe_cargo_spec.is_none() { | ||
1341 | return Ok(None); | ||
1342 | } | ||
1343 | |||
1344 | let cargo_spec = maybe_cargo_spec.unwrap(); | ||
1345 | let cargo_toml_path = cargo_spec.workspace_root.join("Cargo.toml"); | ||
1346 | if !cargo_toml_path.exists() { | ||
1347 | return Ok(None); | ||
1348 | } | ||
1349 | let cargo_toml_url = to_proto::url_from_abs_path(&cargo_toml_path); | ||
1350 | let cargo_toml_location = Location::new(cargo_toml_url, Range::default()); | ||
1351 | let res = lsp_types::GotoDefinitionResponse::from(cargo_toml_location); | ||
1352 | Ok(Some(res)) | ||
1353 | } | ||
1354 | |||
1318 | fn implementation_title(count: usize) -> String { | 1355 | fn implementation_title(count: usize) -> String { |
1319 | if count == 1 { | 1356 | if count == 1 { |
1320 | "1 implementation".into() | 1357 | "1 implementation".into() |
diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs index f31f8d900..93ac45415 100644 --- a/crates/rust-analyzer/src/lsp_ext.rs +++ b/crates/rust-analyzer/src/lsp_ext.rs | |||
@@ -113,22 +113,6 @@ pub struct JoinLinesParams { | |||
113 | pub ranges: Vec<Range>, | 113 | pub ranges: Vec<Range>, |
114 | } | 114 | } |
115 | 115 | ||
116 | pub enum ResolveCodeActionRequest {} | ||
117 | |||
118 | impl Request for ResolveCodeActionRequest { | ||
119 | type Params = ResolveCodeActionParams; | ||
120 | type Result = Option<SnippetWorkspaceEdit>; | ||
121 | const METHOD: &'static str = "experimental/resolveCodeAction"; | ||
122 | } | ||
123 | |||
124 | /// Params for the ResolveCodeActionRequest | ||
125 | #[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)] | ||
126 | #[serde(rename_all = "camelCase")] | ||
127 | pub struct ResolveCodeActionParams { | ||
128 | pub code_action_params: lsp_types::CodeActionParams, | ||
129 | pub id: String, | ||
130 | } | ||
131 | |||
132 | pub enum OnEnter {} | 116 | pub enum OnEnter {} |
133 | 117 | ||
134 | impl Request for OnEnter { | 118 | impl Request for OnEnter { |
@@ -265,13 +249,18 @@ impl Request for CodeActionRequest { | |||
265 | const METHOD: &'static str = "textDocument/codeAction"; | 249 | const METHOD: &'static str = "textDocument/codeAction"; |
266 | } | 250 | } |
267 | 251 | ||
252 | pub enum CodeActionResolveRequest {} | ||
253 | impl Request for CodeActionResolveRequest { | ||
254 | type Params = CodeAction; | ||
255 | type Result = CodeAction; | ||
256 | const METHOD: &'static str = "codeAction/resolve"; | ||
257 | } | ||
258 | |||
268 | #[derive(Debug, PartialEq, Clone, Default, Deserialize, Serialize)] | 259 | #[derive(Debug, PartialEq, Clone, Default, Deserialize, Serialize)] |
269 | #[serde(rename_all = "camelCase")] | 260 | #[serde(rename_all = "camelCase")] |
270 | pub struct CodeAction { | 261 | pub struct CodeAction { |
271 | pub title: String, | 262 | pub title: String, |
272 | #[serde(skip_serializing_if = "Option::is_none")] | 263 | #[serde(skip_serializing_if = "Option::is_none")] |
273 | pub id: Option<String>, | ||
274 | #[serde(skip_serializing_if = "Option::is_none")] | ||
275 | pub group: Option<String>, | 264 | pub group: Option<String>, |
276 | #[serde(skip_serializing_if = "Option::is_none")] | 265 | #[serde(skip_serializing_if = "Option::is_none")] |
277 | pub kind: Option<CodeActionKind>, | 266 | pub kind: Option<CodeActionKind>, |
@@ -282,6 +271,16 @@ pub struct CodeAction { | |||
282 | pub edit: Option<SnippetWorkspaceEdit>, | 271 | pub edit: Option<SnippetWorkspaceEdit>, |
283 | #[serde(skip_serializing_if = "Option::is_none")] | 272 | #[serde(skip_serializing_if = "Option::is_none")] |
284 | pub is_preferred: Option<bool>, | 273 | pub is_preferred: Option<bool>, |
274 | |||
275 | #[serde(skip_serializing_if = "Option::is_none")] | ||
276 | pub data: Option<CodeActionData>, | ||
277 | } | ||
278 | |||
279 | #[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)] | ||
280 | #[serde(rename_all = "camelCase")] | ||
281 | pub struct CodeActionData { | ||
282 | pub code_action_params: lsp_types::CodeActionParams, | ||
283 | pub id: String, | ||
285 | } | 284 | } |
286 | 285 | ||
287 | #[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)] | 286 | #[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)] |
@@ -303,7 +302,7 @@ pub enum SnippetDocumentChangeOperation { | |||
303 | #[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)] | 302 | #[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)] |
304 | #[serde(rename_all = "camelCase")] | 303 | #[serde(rename_all = "camelCase")] |
305 | pub struct SnippetTextDocumentEdit { | 304 | pub struct SnippetTextDocumentEdit { |
306 | pub text_document: lsp_types::VersionedTextDocumentIdentifier, | 305 | pub text_document: lsp_types::OptionalVersionedTextDocumentIdentifier, |
307 | pub edits: Vec<SnippetTextEdit>, | 306 | pub edits: Vec<SnippetTextEdit>, |
308 | } | 307 | } |
309 | 308 | ||
@@ -355,3 +354,17 @@ impl Request for ExternalDocs { | |||
355 | type Result = Option<lsp_types::Url>; | 354 | type Result = Option<lsp_types::Url>; |
356 | const METHOD: &'static str = "experimental/externalDocs"; | 355 | const METHOD: &'static str = "experimental/externalDocs"; |
357 | } | 356 | } |
357 | |||
358 | pub enum OpenCargoToml {} | ||
359 | |||
360 | impl Request for OpenCargoToml { | ||
361 | type Params = OpenCargoTomlParams; | ||
362 | type Result = Option<lsp_types::GotoDefinitionResponse>; | ||
363 | const METHOD: &'static str = "experimental/openCargoToml"; | ||
364 | } | ||
365 | |||
366 | #[derive(Serialize, Deserialize, Debug)] | ||
367 | #[serde(rename_all = "camelCase")] | ||
368 | pub struct OpenCargoTomlParams { | ||
369 | pub text_document: TextDocumentIdentifier, | ||
370 | } | ||
diff --git a/crates/rust-analyzer/src/lsp_utils.rs b/crates/rust-analyzer/src/lsp_utils.rs index 1d271a9d8..6427c7367 100644 --- a/crates/rust-analyzer/src/lsp_utils.rs +++ b/crates/rust-analyzer/src/lsp_utils.rs | |||
@@ -51,7 +51,7 @@ impl GlobalState { | |||
51 | } | 51 | } |
52 | let percentage = fraction.map(|f| { | 52 | let percentage = fraction.map(|f| { |
53 | assert!(0.0 <= f && f <= 1.0); | 53 | assert!(0.0 <= f && f <= 1.0); |
54 | f * 100.0 | 54 | (f * 100.0) as u32 |
55 | }); | 55 | }); |
56 | let token = lsp_types::ProgressToken::String(format!("rustAnalyzer/{}", title)); | 56 | let token = lsp_types::ProgressToken::String(format!("rustAnalyzer/{}", title)); |
57 | let work_done_progress = match state { | 57 | let work_done_progress = match state { |
@@ -98,11 +98,11 @@ pub(crate) fn apply_document_changes( | |||
98 | // The VFS will normalize the end of lines to `\n`. | 98 | // The VFS will normalize the end of lines to `\n`. |
99 | enum IndexValid { | 99 | enum IndexValid { |
100 | All, | 100 | All, |
101 | UpToLineExclusive(u64), | 101 | UpToLineExclusive(u32), |
102 | } | 102 | } |
103 | 103 | ||
104 | impl IndexValid { | 104 | impl IndexValid { |
105 | fn covers(&self, line: u64) -> bool { | 105 | fn covers(&self, line: u32) -> bool { |
106 | match *self { | 106 | match *self { |
107 | IndexValid::UpToLineExclusive(to) => to > line, | 107 | IndexValid::UpToLineExclusive(to) => to > line, |
108 | _ => true, | 108 | _ => true, |
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index d504572a8..b34ff092d 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs | |||
@@ -368,7 +368,7 @@ impl GlobalState { | |||
368 | let url = file_id_to_url(&self.vfs.read().0, file_id); | 368 | let url = file_id_to_url(&self.vfs.read().0, file_id); |
369 | let diagnostics = self.diagnostics.diagnostics_for(file_id).cloned().collect(); | 369 | let diagnostics = self.diagnostics.diagnostics_for(file_id).cloned().collect(); |
370 | let version = from_proto::vfs_path(&url) | 370 | let version = from_proto::vfs_path(&url) |
371 | .map(|path| self.mem_docs.get(&path)?.version) | 371 | .map(|path| self.mem_docs.get(&path).map(|it| it.version)) |
372 | .unwrap_or_default(); | 372 | .unwrap_or_default(); |
373 | 373 | ||
374 | self.send_notification::<lsp_types::notification::PublishDiagnostics>( | 374 | self.send_notification::<lsp_types::notification::PublishDiagnostics>( |
@@ -435,9 +435,10 @@ impl GlobalState { | |||
435 | .on::<lsp_ext::Runnables>(handlers::handle_runnables) | 435 | .on::<lsp_ext::Runnables>(handlers::handle_runnables) |
436 | .on::<lsp_ext::InlayHints>(handlers::handle_inlay_hints) | 436 | .on::<lsp_ext::InlayHints>(handlers::handle_inlay_hints) |
437 | .on::<lsp_ext::CodeActionRequest>(handlers::handle_code_action) | 437 | .on::<lsp_ext::CodeActionRequest>(handlers::handle_code_action) |
438 | .on::<lsp_ext::ResolveCodeActionRequest>(handlers::handle_resolve_code_action) | 438 | .on::<lsp_ext::CodeActionResolveRequest>(handlers::handle_code_action_resolve) |
439 | .on::<lsp_ext::HoverRequest>(handlers::handle_hover) | 439 | .on::<lsp_ext::HoverRequest>(handlers::handle_hover) |
440 | .on::<lsp_ext::ExternalDocs>(handlers::handle_open_docs) | 440 | .on::<lsp_ext::ExternalDocs>(handlers::handle_open_docs) |
441 | .on::<lsp_ext::OpenCargoToml>(handlers::handle_open_cargo_toml) | ||
441 | .on::<lsp_types::request::OnTypeFormatting>(handlers::handle_on_type_formatting) | 442 | .on::<lsp_types::request::OnTypeFormatting>(handlers::handle_on_type_formatting) |
442 | .on::<lsp_types::request::DocumentSymbolRequest>(handlers::handle_document_symbol) | 443 | .on::<lsp_types::request::DocumentSymbolRequest>(handlers::handle_document_symbol) |
443 | .on::<lsp_types::request::WorkspaceSymbol>(handlers::handle_workspace_symbol) | 444 | .on::<lsp_types::request::WorkspaceSymbol>(handlers::handle_workspace_symbol) |
@@ -502,7 +503,13 @@ impl GlobalState { | |||
502 | })? | 503 | })? |
503 | .on::<lsp_types::notification::DidChangeTextDocument>(|this, params| { | 504 | .on::<lsp_types::notification::DidChangeTextDocument>(|this, params| { |
504 | if let Ok(path) = from_proto::vfs_path(¶ms.text_document.uri) { | 505 | if let Ok(path) = from_proto::vfs_path(¶ms.text_document.uri) { |
505 | let doc = this.mem_docs.get_mut(&path).unwrap(); | 506 | let doc = match this.mem_docs.get_mut(&path) { |
507 | Some(doc) => doc, | ||
508 | None => { | ||
509 | log::error!("expected DidChangeTextDocument: {}", path); | ||
510 | return Ok(()); | ||
511 | } | ||
512 | }; | ||
506 | let vfs = &mut this.vfs.write().0; | 513 | let vfs = &mut this.vfs.write().0; |
507 | let file_id = vfs.file_id(&path).unwrap(); | 514 | let file_id = vfs.file_id(&path).unwrap(); |
508 | let mut text = String::from_utf8(vfs.file_contents(file_id).to_vec()).unwrap(); | 515 | let mut text = String::from_utf8(vfs.file_contents(file_id).to_vec()).unwrap(); |
@@ -520,7 +527,7 @@ impl GlobalState { | |||
520 | let mut version = None; | 527 | let mut version = None; |
521 | if let Ok(path) = from_proto::vfs_path(¶ms.text_document.uri) { | 528 | if let Ok(path) = from_proto::vfs_path(¶ms.text_document.uri) { |
522 | match this.mem_docs.remove(&path) { | 529 | match this.mem_docs.remove(&path) { |
523 | Some(doc) => version = doc.version, | 530 | Some(doc) => version = Some(doc.version), |
524 | None => log::error!("orphan DidCloseTextDocument: {}", path), | 531 | None => log::error!("orphan DidCloseTextDocument: {}", path), |
525 | } | 532 | } |
526 | 533 | ||
diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs index 0eabd51bd..001bf5949 100644 --- a/crates/rust-analyzer/src/reload.rs +++ b/crates/rust-analyzer/src/reload.rs | |||
@@ -96,17 +96,12 @@ impl GlobalState { | |||
96 | self.task_pool.handle.spawn({ | 96 | self.task_pool.handle.spawn({ |
97 | let linked_projects = self.config.linked_projects.clone(); | 97 | let linked_projects = self.config.linked_projects.clone(); |
98 | let cargo_config = self.config.cargo.clone(); | 98 | let cargo_config = self.config.cargo.clone(); |
99 | let with_sysroot = self.config.with_sysroot.clone(); | ||
100 | move || { | 99 | move || { |
101 | let workspaces = linked_projects | 100 | let workspaces = linked_projects |
102 | .iter() | 101 | .iter() |
103 | .map(|project| match project { | 102 | .map(|project| match project { |
104 | LinkedProject::ProjectManifest(manifest) => { | 103 | LinkedProject::ProjectManifest(manifest) => { |
105 | project_model::ProjectWorkspace::load( | 104 | project_model::ProjectWorkspace::load(manifest.clone(), &cargo_config) |
106 | manifest.clone(), | ||
107 | &cargo_config, | ||
108 | with_sysroot, | ||
109 | ) | ||
110 | } | 105 | } |
111 | LinkedProject::InlineJsonProject(it) => { | 106 | LinkedProject::InlineJsonProject(it) => { |
112 | project_model::ProjectWorkspace::load_inline(it.clone()) | 107 | project_model::ProjectWorkspace::load_inline(it.clone()) |
@@ -208,7 +203,11 @@ impl GlobalState { | |||
208 | let contents = loader.handle.load_sync(path); | 203 | let contents = loader.handle.load_sync(path); |
209 | vfs.set_file_contents(vfs_path.clone(), contents); | 204 | vfs.set_file_contents(vfs_path.clone(), contents); |
210 | } | 205 | } |
211 | vfs.file_id(&vfs_path) | 206 | let res = vfs.file_id(&vfs_path); |
207 | if res.is_none() { | ||
208 | log::error!("failed to load {}", path.display()) | ||
209 | } | ||
210 | res | ||
212 | }; | 211 | }; |
213 | for ws in workspaces.iter() { | 212 | for ws in workspaces.iter() { |
214 | crate_graph.extend(ws.to_crate_graph( | 213 | crate_graph.extend(ws.to_crate_graph( |
@@ -246,7 +245,9 @@ impl GlobalState { | |||
246 | .iter() | 245 | .iter() |
247 | .enumerate() | 246 | .enumerate() |
248 | .filter_map(|(id, w)| match w { | 247 | .filter_map(|(id, w)| match w { |
249 | ProjectWorkspace::Cargo { cargo, sysroot: _ } => Some((id, cargo.workspace_root())), | 248 | ProjectWorkspace::Cargo { cargo, sysroot: _, rustc: _ } => { |
249 | Some((id, cargo.workspace_root())) | ||
250 | } | ||
250 | ProjectWorkspace::Json { project, .. } => { | 251 | ProjectWorkspace::Json { project, .. } => { |
251 | // Enable flychecks for json projects if a custom flycheck command was supplied | 252 | // Enable flychecks for json projects if a custom flycheck command was supplied |
252 | // in the workspace configuration. | 253 | // in the workspace configuration. |
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs index 92b7c7b68..2052b800c 100644 --- a/crates/rust-analyzer/src/to_proto.rs +++ b/crates/rust-analyzer/src/to_proto.rs | |||
@@ -21,9 +21,7 @@ use crate::{ | |||
21 | 21 | ||
22 | pub(crate) fn position(line_index: &LineIndex, offset: TextSize) -> lsp_types::Position { | 22 | pub(crate) fn position(line_index: &LineIndex, offset: TextSize) -> lsp_types::Position { |
23 | let line_col = line_index.line_col(offset); | 23 | let line_col = line_index.line_col(offset); |
24 | let line = u64::from(line_col.line); | 24 | lsp_types::Position::new(line_col.line, line_col.col_utf16) |
25 | let character = u64::from(line_col.col_utf16); | ||
26 | lsp_types::Position::new(line, character) | ||
27 | } | 25 | } |
28 | 26 | ||
29 | pub(crate) fn range(line_index: &LineIndex, range: TextRange) -> lsp_types::Range { | 27 | pub(crate) fn range(line_index: &LineIndex, range: TextRange) -> lsp_types::Range { |
@@ -278,9 +276,9 @@ pub(crate) fn signature_help( | |||
278 | label.push_str(", "); | 276 | label.push_str(", "); |
279 | } | 277 | } |
280 | first = false; | 278 | first = false; |
281 | let start = label.len() as u64; | 279 | let start = label.len() as u32; |
282 | label.push_str(param); | 280 | label.push_str(param); |
283 | let end = label.len() as u64; | 281 | let end = label.len() as u32; |
284 | params.push(lsp_types::ParameterInformation { | 282 | params.push(lsp_types::ParameterInformation { |
285 | label: lsp_types::ParameterLabel::LabelOffsets([start, end]), | 283 | label: lsp_types::ParameterLabel::LabelOffsets([start, end]), |
286 | documentation: None, | 284 | documentation: None, |
@@ -302,7 +300,7 @@ pub(crate) fn signature_help( | |||
302 | }) | 300 | }) |
303 | }; | 301 | }; |
304 | 302 | ||
305 | let active_parameter = call_info.active_parameter.map(|it| it as i64); | 303 | let active_parameter = call_info.active_parameter.map(|it| it as u32); |
306 | 304 | ||
307 | let signature = lsp_types::SignatureInformation { | 305 | let signature = lsp_types::SignatureInformation { |
308 | label, | 306 | label, |
@@ -426,6 +424,7 @@ fn semantic_token_type_and_modifiers( | |||
426 | HighlightModifier::Consuming => semantic_tokens::CONSUMING, | 424 | HighlightModifier::Consuming => semantic_tokens::CONSUMING, |
427 | HighlightModifier::Unsafe => semantic_tokens::UNSAFE, | 425 | HighlightModifier::Unsafe => semantic_tokens::UNSAFE, |
428 | HighlightModifier::Callable => semantic_tokens::CALLABLE, | 426 | HighlightModifier::Callable => semantic_tokens::CALLABLE, |
427 | HighlightModifier::Static => lsp_types::SemanticTokenModifier::STATIC, | ||
429 | }; | 428 | }; |
430 | mods |= modifier; | 429 | mods |= modifier; |
431 | } | 430 | } |
@@ -517,13 +516,13 @@ pub(crate) fn url_from_abs_path(path: &Path) -> lsp_types::Url { | |||
517 | lsp_types::Url::parse(&url).unwrap() | 516 | lsp_types::Url::parse(&url).unwrap() |
518 | } | 517 | } |
519 | 518 | ||
520 | pub(crate) fn versioned_text_document_identifier( | 519 | pub(crate) fn optional_versioned_text_document_identifier( |
521 | snap: &GlobalStateSnapshot, | 520 | snap: &GlobalStateSnapshot, |
522 | file_id: FileId, | 521 | file_id: FileId, |
523 | ) -> lsp_types::VersionedTextDocumentIdentifier { | 522 | ) -> lsp_types::OptionalVersionedTextDocumentIdentifier { |
524 | let url = url(snap, file_id); | 523 | let url = url(snap, file_id); |
525 | let version = snap.url_file_version(&url); | 524 | let version = snap.url_file_version(&url); |
526 | lsp_types::VersionedTextDocumentIdentifier { uri: url, version } | 525 | lsp_types::OptionalVersionedTextDocumentIdentifier { uri: url, version } |
527 | } | 526 | } |
528 | 527 | ||
529 | pub(crate) fn location( | 528 | pub(crate) fn location( |
@@ -612,7 +611,7 @@ pub(crate) fn snippet_text_document_edit( | |||
612 | is_snippet: bool, | 611 | is_snippet: bool, |
613 | source_file_edit: SourceFileEdit, | 612 | source_file_edit: SourceFileEdit, |
614 | ) -> Result<lsp_ext::SnippetTextDocumentEdit> { | 613 | ) -> Result<lsp_ext::SnippetTextDocumentEdit> { |
615 | let text_document = versioned_text_document_identifier(snap, source_file_edit.file_id); | 614 | let text_document = optional_versioned_text_document_identifier(snap, source_file_edit.file_id); |
616 | let line_index = snap.analysis.file_line_index(source_file_edit.file_id)?; | 615 | let line_index = snap.analysis.file_line_index(source_file_edit.file_id)?; |
617 | let line_endings = snap.file_line_endings(source_file_edit.file_id); | 616 | let line_endings = snap.file_line_endings(source_file_edit.file_id); |
618 | let edits = source_file_edit | 617 | let edits = source_file_edit |
@@ -630,12 +629,21 @@ pub(crate) fn resource_op( | |||
630 | match file_system_edit { | 629 | match file_system_edit { |
631 | FileSystemEdit::CreateFile { anchor, dst } => { | 630 | FileSystemEdit::CreateFile { anchor, dst } => { |
632 | let uri = snap.anchored_path(anchor, &dst); | 631 | let uri = snap.anchored_path(anchor, &dst); |
633 | lsp_types::ResourceOp::Create(lsp_types::CreateFile { uri, options: None }) | 632 | lsp_types::ResourceOp::Create(lsp_types::CreateFile { |
633 | uri, | ||
634 | options: None, | ||
635 | annotation: None, | ||
636 | }) | ||
634 | } | 637 | } |
635 | FileSystemEdit::MoveFile { src, anchor, dst } => { | 638 | FileSystemEdit::MoveFile { src, anchor, dst } => { |
636 | let old_uri = snap.file_id_to_url(src); | 639 | let old_uri = snap.file_id_to_url(src); |
637 | let new_uri = snap.anchored_path(anchor, &dst); | 640 | let new_uri = snap.anchored_path(anchor, &dst); |
638 | lsp_types::ResourceOp::Rename(lsp_types::RenameFile { old_uri, new_uri, options: None }) | 641 | lsp_types::ResourceOp::Rename(lsp_types::RenameFile { |
642 | old_uri, | ||
643 | new_uri, | ||
644 | options: None, | ||
645 | annotation: None, | ||
646 | }) | ||
639 | } | 647 | } |
640 | } | 648 | } |
641 | } | 649 | } |
@@ -685,9 +693,11 @@ impl From<lsp_ext::SnippetWorkspaceEdit> for lsp_types::WorkspaceEdit { | |||
685 | edits: edit | 693 | edits: edit |
686 | .edits | 694 | .edits |
687 | .into_iter() | 695 | .into_iter() |
688 | .map(|edit| lsp_types::TextEdit { | 696 | .map(|edit| { |
689 | range: edit.range, | 697 | lsp_types::OneOf::Left(lsp_types::TextEdit { |
690 | new_text: edit.new_text, | 698 | range: edit.range, |
699 | new_text: edit.new_text, | ||
700 | }) | ||
691 | }) | 701 | }) |
692 | .collect(), | 702 | .collect(), |
693 | }, | 703 | }, |
@@ -734,16 +744,20 @@ pub(crate) fn code_action_kind(kind: AssistKind) -> lsp_types::CodeActionKind { | |||
734 | 744 | ||
735 | pub(crate) fn unresolved_code_action( | 745 | pub(crate) fn unresolved_code_action( |
736 | snap: &GlobalStateSnapshot, | 746 | snap: &GlobalStateSnapshot, |
747 | code_action_params: lsp_types::CodeActionParams, | ||
737 | assist: Assist, | 748 | assist: Assist, |
738 | index: usize, | 749 | index: usize, |
739 | ) -> Result<lsp_ext::CodeAction> { | 750 | ) -> Result<lsp_ext::CodeAction> { |
740 | let res = lsp_ext::CodeAction { | 751 | let res = lsp_ext::CodeAction { |
741 | title: assist.label.to_string(), | 752 | title: assist.label.to_string(), |
742 | id: Some(format!("{}:{}", assist.id.0, index.to_string())), | ||
743 | group: assist.group.filter(|_| snap.config.client_caps.code_action_group).map(|gr| gr.0), | 753 | group: assist.group.filter(|_| snap.config.client_caps.code_action_group).map(|gr| gr.0), |
744 | kind: Some(code_action_kind(assist.id.1)), | 754 | kind: Some(code_action_kind(assist.id.1)), |
745 | edit: None, | 755 | edit: None, |
746 | is_preferred: None, | 756 | is_preferred: None, |
757 | data: Some(lsp_ext::CodeActionData { | ||
758 | id: format!("{}:{}", assist.id.0, index.to_string()), | ||
759 | code_action_params, | ||
760 | }), | ||
747 | }; | 761 | }; |
748 | Ok(res) | 762 | Ok(res) |
749 | } | 763 | } |
@@ -753,13 +767,19 @@ pub(crate) fn resolved_code_action( | |||
753 | assist: ResolvedAssist, | 767 | assist: ResolvedAssist, |
754 | ) -> Result<lsp_ext::CodeAction> { | 768 | ) -> Result<lsp_ext::CodeAction> { |
755 | let change = assist.source_change; | 769 | let change = assist.source_change; |
756 | unresolved_code_action(snap, assist.assist, 0).and_then(|it| { | 770 | let res = lsp_ext::CodeAction { |
757 | Ok(lsp_ext::CodeAction { | 771 | edit: Some(snippet_workspace_edit(snap, change)?), |
758 | id: None, | 772 | title: assist.assist.label.to_string(), |
759 | edit: Some(snippet_workspace_edit(snap, change)?), | 773 | group: assist |
760 | ..it | 774 | .assist |
761 | }) | 775 | .group |
762 | }) | 776 | .filter(|_| snap.config.client_caps.code_action_group) |
777 | .map(|gr| gr.0), | ||
778 | kind: Some(code_action_kind(assist.assist.id.1)), | ||
779 | is_preferred: None, | ||
780 | data: None, | ||
781 | }; | ||
782 | Ok(res) | ||
763 | } | 783 | } |
764 | 784 | ||
765 | pub(crate) fn runnable( | 785 | pub(crate) fn runnable( |
diff --git a/crates/rust-analyzer/tests/rust-analyzer/support.rs b/crates/rust-analyzer/tests/rust-analyzer/support.rs index fe9362bc0..456125789 100644 --- a/crates/rust-analyzer/tests/rust-analyzer/support.rs +++ b/crates/rust-analyzer/tests/rust-analyzer/support.rs | |||
@@ -12,7 +12,7 @@ use lsp_types::{ | |||
12 | notification::Exit, request::Shutdown, TextDocumentIdentifier, Url, WorkDoneProgress, | 12 | notification::Exit, request::Shutdown, TextDocumentIdentifier, Url, WorkDoneProgress, |
13 | }; | 13 | }; |
14 | use lsp_types::{ProgressParams, ProgressParamsValue}; | 14 | use lsp_types::{ProgressParams, ProgressParamsValue}; |
15 | use project_model::ProjectManifest; | 15 | use project_model::{CargoConfig, ProjectManifest}; |
16 | use rust_analyzer::{ | 16 | use rust_analyzer::{ |
17 | config::{ClientCapsConfig, Config, FilesConfig, FilesWatcher, LinkedProject}, | 17 | config::{ClientCapsConfig, Config, FilesConfig, FilesWatcher, LinkedProject}, |
18 | main_loop, | 18 | main_loop, |
@@ -47,8 +47,8 @@ impl<'a> Project<'a> { | |||
47 | self | 47 | self |
48 | } | 48 | } |
49 | 49 | ||
50 | pub(crate) fn with_sysroot(mut self, sysroot: bool) -> Project<'a> { | 50 | pub(crate) fn with_sysroot(mut self, yes: bool) -> Project<'a> { |
51 | self.with_sysroot = sysroot; | 51 | self.with_sysroot = yes; |
52 | self | 52 | self |
53 | } | 53 | } |
54 | 54 | ||
@@ -90,7 +90,7 @@ impl<'a> Project<'a> { | |||
90 | work_done_progress: true, | 90 | work_done_progress: true, |
91 | ..Default::default() | 91 | ..Default::default() |
92 | }, | 92 | }, |
93 | with_sysroot: self.with_sysroot, | 93 | cargo: CargoConfig { no_sysroot: !self.with_sysroot, ..Default::default() }, |
94 | linked_projects, | 94 | linked_projects, |
95 | files: FilesConfig { watcher: FilesWatcher::Client, exclude: Vec::new() }, | 95 | files: FilesConfig { watcher: FilesWatcher::Client, exclude: Vec::new() }, |
96 | ..Config::new(tmp_dir_path) | 96 | ..Config::new(tmp_dir_path) |
@@ -108,7 +108,7 @@ pub(crate) fn project(fixture: &str) -> Server { | |||
108 | } | 108 | } |
109 | 109 | ||
110 | pub(crate) struct Server { | 110 | pub(crate) struct Server { |
111 | req_id: Cell<u64>, | 111 | req_id: Cell<i32>, |
112 | messages: RefCell<Vec<Message>>, | 112 | messages: RefCell<Vec<Message>>, |
113 | _thread: jod_thread::JoinHandle<()>, | 113 | _thread: jod_thread::JoinHandle<()>, |
114 | client: Connection, | 114 | client: Connection, |
@@ -165,7 +165,7 @@ impl Server { | |||
165 | R::Params: Serialize, | 165 | R::Params: Serialize, |
166 | { | 166 | { |
167 | let id = self.req_id.get(); | 167 | let id = self.req_id.get(); |
168 | self.req_id.set(id + 1); | 168 | self.req_id.set(id.wrapping_add(1)); |
169 | 169 | ||
170 | let r = Request::new(id.into(), R::METHOD.to_string(), params); | 170 | let r = Request::new(id.into(), R::METHOD.to_string(), params); |
171 | self.send_request_(r) | 171 | self.send_request_(r) |
diff --git a/crates/stdx/src/lib.rs b/crates/stdx/src/lib.rs index 59d89f47d..374ed5910 100644 --- a/crates/stdx/src/lib.rs +++ b/crates/stdx/src/lib.rs | |||
@@ -1,8 +1,5 @@ | |||
1 | //! Missing batteries for standard libraries. | 1 | //! Missing batteries for standard libraries. |
2 | use std::{ | 2 | use std::time::Instant; |
3 | sync::atomic::{AtomicUsize, Ordering}, | ||
4 | time::Instant, | ||
5 | }; | ||
6 | 3 | ||
7 | mod macros; | 4 | mod macros; |
8 | pub mod panic_context; | 5 | pub mod panic_context; |
@@ -150,31 +147,6 @@ where | |||
150 | left | 147 | left |
151 | } | 148 | } |
152 | 149 | ||
153 | pub struct RacyFlag(AtomicUsize); | ||
154 | |||
155 | impl RacyFlag { | ||
156 | pub const fn new() -> RacyFlag { | ||
157 | RacyFlag(AtomicUsize::new(!0)) | ||
158 | } | ||
159 | |||
160 | pub fn get(&self, init: impl FnMut() -> bool) -> bool { | ||
161 | let mut init = Some(init); | ||
162 | self.get_impl(&mut || init.take().map_or(false, |mut f| f())) | ||
163 | } | ||
164 | |||
165 | fn get_impl(&self, init: &mut dyn FnMut() -> bool) -> bool { | ||
166 | match self.0.load(Ordering::Relaxed) { | ||
167 | 0 => false, | ||
168 | 1 => true, | ||
169 | _ => { | ||
170 | let res = init(); | ||
171 | self.0.store(if res { 1 } else { 0 }, Ordering::Relaxed); | ||
172 | res | ||
173 | } | ||
174 | } | ||
175 | } | ||
176 | } | ||
177 | |||
178 | #[cfg(test)] | 150 | #[cfg(test)] |
179 | mod tests { | 151 | mod tests { |
180 | use super::*; | 152 | use super::*; |
diff --git a/crates/syntax/Cargo.toml b/crates/syntax/Cargo.toml index 61d2acb49..1fe907753 100644 --- a/crates/syntax/Cargo.toml +++ b/crates/syntax/Cargo.toml | |||
@@ -13,7 +13,7 @@ doctest = false | |||
13 | [dependencies] | 13 | [dependencies] |
14 | itertools = "0.9.0" | 14 | itertools = "0.9.0" |
15 | rowan = "0.10.0" | 15 | rowan = "0.10.0" |
16 | rustc_lexer = { version = "686.0.0", package = "rustc-ap-rustc_lexer" } | 16 | rustc_lexer = { version = "688.0.0", package = "rustc-ap-rustc_lexer" } |
17 | rustc-hash = "1.1.0" | 17 | rustc-hash = "1.1.0" |
18 | arrayvec = "0.5.1" | 18 | arrayvec = "0.5.1" |
19 | once_cell = "1.3.1" | 19 | once_cell = "1.3.1" |
diff --git a/crates/syntax/src/algo.rs b/crates/syntax/src/algo.rs index 9dc7182bd..320c430c9 100644 --- a/crates/syntax/src/algo.rs +++ b/crates/syntax/src/algo.rs | |||
@@ -111,18 +111,28 @@ pub enum InsertPosition<T> { | |||
111 | 111 | ||
112 | type FxIndexMap<K, V> = IndexMap<K, V, BuildHasherDefault<rustc_hash::FxHasher>>; | 112 | type FxIndexMap<K, V> = IndexMap<K, V, BuildHasherDefault<rustc_hash::FxHasher>>; |
113 | 113 | ||
114 | #[derive(Debug, Hash, PartialEq, Eq)] | ||
115 | enum TreeDiffInsertPos { | ||
116 | After(SyntaxElement), | ||
117 | AsFirstChild(SyntaxElement), | ||
118 | } | ||
119 | |||
114 | #[derive(Debug)] | 120 | #[derive(Debug)] |
115 | pub struct TreeDiff { | 121 | pub struct TreeDiff { |
116 | replacements: FxHashMap<SyntaxElement, SyntaxElement>, | 122 | replacements: FxHashMap<SyntaxElement, SyntaxElement>, |
117 | deletions: Vec<SyntaxElement>, | 123 | deletions: Vec<SyntaxElement>, |
118 | // the vec as well as the indexmap are both here to preserve order | 124 | // the vec as well as the indexmap are both here to preserve order |
119 | insertions: FxIndexMap<SyntaxElement, Vec<SyntaxElement>>, | 125 | insertions: FxIndexMap<TreeDiffInsertPos, Vec<SyntaxElement>>, |
120 | } | 126 | } |
121 | 127 | ||
122 | impl TreeDiff { | 128 | impl TreeDiff { |
123 | pub fn into_text_edit(&self, builder: &mut TextEditBuilder) { | 129 | pub fn into_text_edit(&self, builder: &mut TextEditBuilder) { |
124 | for (anchor, to) in self.insertions.iter() { | 130 | for (anchor, to) in self.insertions.iter() { |
125 | to.iter().for_each(|to| builder.insert(anchor.text_range().end(), to.to_string())); | 131 | let offset = match anchor { |
132 | TreeDiffInsertPos::After(it) => it.text_range().end(), | ||
133 | TreeDiffInsertPos::AsFirstChild(it) => it.text_range().start(), | ||
134 | }; | ||
135 | to.iter().for_each(|to| builder.insert(offset, to.to_string())); | ||
126 | } | 136 | } |
127 | for (from, to) in self.replacements.iter() { | 137 | for (from, to) in self.replacements.iter() { |
128 | builder.replace(from.text_range(), to.to_string()) | 138 | builder.replace(from.text_range(), to.to_string()) |
@@ -188,19 +198,20 @@ pub fn diff(from: &SyntaxNode, to: &SyntaxNode) -> TreeDiff { | |||
188 | let lhs_child = lhs_children.next(); | 198 | let lhs_child = lhs_children.next(); |
189 | match (lhs_child.clone(), rhs_children.next()) { | 199 | match (lhs_child.clone(), rhs_children.next()) { |
190 | (None, None) => break, | 200 | (None, None) => break, |
191 | (None, Some(element)) => match last_lhs.clone() { | 201 | (None, Some(element)) => { |
192 | Some(prev) => { | 202 | let insert_pos = match last_lhs.clone() { |
193 | mark::hit!(diff_insert); | 203 | Some(prev) => { |
194 | diff.insertions.entry(prev).or_insert_with(Vec::new).push(element); | 204 | mark::hit!(diff_insert); |
195 | } | 205 | TreeDiffInsertPos::After(prev) |
196 | // first iteration, this means we got no anchor element to insert after | 206 | } |
197 | // therefor replace the parent node instead | 207 | // first iteration, insert into out parent as the first child |
198 | None => { | 208 | None => { |
199 | mark::hit!(diff_replace_parent); | 209 | mark::hit!(diff_insert_as_first_child); |
200 | diff.replacements.insert(lhs.clone().into(), rhs.clone().into()); | 210 | TreeDiffInsertPos::AsFirstChild(lhs.clone().into()) |
201 | break; | 211 | } |
202 | } | 212 | }; |
203 | }, | 213 | diff.insertions.entry(insert_pos).or_insert_with(Vec::new).push(element); |
214 | } | ||
204 | (Some(element), None) => { | 215 | (Some(element), None) => { |
205 | mark::hit!(diff_delete); | 216 | mark::hit!(diff_delete); |
206 | diff.deletions.push(element); | 217 | diff.deletions.push(element); |
@@ -224,8 +235,15 @@ pub fn diff(from: &SyntaxNode, to: &SyntaxNode) -> TreeDiff { | |||
224 | } | 235 | } |
225 | } | 236 | } |
226 | let drain = look_ahead_scratch.drain(..); | 237 | let drain = look_ahead_scratch.drain(..); |
227 | if let Some(prev) = last_lhs.clone().filter(|_| insert) { | 238 | if insert { |
228 | diff.insertions.entry(prev).or_insert_with(Vec::new).extend(drain); | 239 | let insert_pos = if let Some(prev) = last_lhs.clone().filter(|_| insert) { |
240 | TreeDiffInsertPos::After(prev) | ||
241 | } else { | ||
242 | mark::hit!(insert_first_child); | ||
243 | TreeDiffInsertPos::AsFirstChild(lhs.clone().into()) | ||
244 | }; | ||
245 | |||
246 | diff.insertions.entry(insert_pos).or_insert_with(Vec::new).extend(drain); | ||
229 | rhs_children = rhs_children_clone; | 247 | rhs_children = rhs_children_clone; |
230 | } else { | 248 | } else { |
231 | go(diff, lhs_ele, rhs_ele) | 249 | go(diff, lhs_ele, rhs_ele) |
@@ -331,7 +349,10 @@ pub struct SyntaxRewriter<'a> { | |||
331 | 349 | ||
332 | impl fmt::Debug for SyntaxRewriter<'_> { | 350 | impl fmt::Debug for SyntaxRewriter<'_> { |
333 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | 351 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
334 | f.debug_struct("SyntaxRewriter").field("replacements", &self.replacements).finish() | 352 | f.debug_struct("SyntaxRewriter") |
353 | .field("replacements", &self.replacements) | ||
354 | .field("insertions", &self.insertions) | ||
355 | .finish() | ||
335 | } | 356 | } |
336 | } | 357 | } |
337 | 358 | ||
@@ -629,18 +650,19 @@ mod tests { | |||
629 | 650 | ||
630 | #[test] | 651 | #[test] |
631 | fn replace_parent() { | 652 | fn replace_parent() { |
632 | mark::check!(diff_replace_parent); | 653 | mark::check!(diff_insert_as_first_child); |
633 | check_diff( | 654 | check_diff( |
634 | r#""#, | 655 | r#""#, |
635 | r#"use foo::bar;"#, | 656 | r#"use foo::bar;"#, |
636 | expect![[r#" | 657 | expect![[r#" |
637 | insertions: | 658 | insertions: |
638 | 659 | ||
639 | 660 | Line 0: AsFirstChild(Node([email protected])) | |
661 | -> use foo::bar; | ||
640 | 662 | ||
641 | replacements: | 663 | replacements: |
642 | 664 | ||
643 | Line 0: Node([email protected]) -> use foo::bar; | 665 | |
644 | 666 | ||
645 | deletions: | 667 | deletions: |
646 | 668 | ||
@@ -663,7 +685,7 @@ use baz;"#, | |||
663 | expect![[r#" | 685 | expect![[r#" |
664 | insertions: | 686 | insertions: |
665 | 687 | ||
666 | Line 2: Node([email protected]) | 688 | Line 2: After(Node([email protected])) |
667 | -> "\n" | 689 | -> "\n" |
668 | -> use baz; | 690 | -> use baz; |
669 | 691 | ||
@@ -691,7 +713,7 @@ use baz;"#, | |||
691 | expect![[r#" | 713 | expect![[r#" |
692 | insertions: | 714 | insertions: |
693 | 715 | ||
694 | Line 2: Token([email protected] "\n") | 716 | Line 2: After(Token([email protected] "\n")) |
695 | -> use bar; | 717 | -> use bar; |
696 | -> "\n" | 718 | -> "\n" |
697 | 719 | ||
@@ -719,7 +741,7 @@ use baz;"#, | |||
719 | expect![[r#" | 741 | expect![[r#" |
720 | insertions: | 742 | insertions: |
721 | 743 | ||
722 | Line 0: Token([email protected] "\n") | 744 | Line 0: After(Token([email protected] "\n")) |
723 | -> use foo; | 745 | -> use foo; |
724 | -> "\n" | 746 | -> "\n" |
725 | 747 | ||
@@ -735,6 +757,36 @@ use baz;"#, | |||
735 | } | 757 | } |
736 | 758 | ||
737 | #[test] | 759 | #[test] |
760 | fn first_child_insertion() { | ||
761 | mark::check!(insert_first_child); | ||
762 | check_diff( | ||
763 | r#"fn main() { | ||
764 | stdi | ||
765 | }"#, | ||
766 | r#"use foo::bar; | ||
767 | |||
768 | fn main() { | ||
769 | stdi | ||
770 | }"#, | ||
771 | expect![[r#" | ||
772 | insertions: | ||
773 | |||
774 | Line 0: AsFirstChild(Node([email protected])) | ||
775 | -> use foo::bar; | ||
776 | -> "\n\n " | ||
777 | |||
778 | replacements: | ||
779 | |||
780 | |||
781 | |||
782 | deletions: | ||
783 | |||
784 | |||
785 | "#]], | ||
786 | ); | ||
787 | } | ||
788 | |||
789 | #[test] | ||
738 | fn delete_last() { | 790 | fn delete_last() { |
739 | mark::check!(diff_delete); | 791 | mark::check!(diff_delete); |
740 | check_diff( | 792 | check_diff( |
@@ -776,7 +828,7 @@ use crate::AstNode; | |||
776 | expect![[r#" | 828 | expect![[r#" |
777 | insertions: | 829 | insertions: |
778 | 830 | ||
779 | Line 1: Node([email protected]) | 831 | Line 1: After(Node([email protected])) |
780 | -> "\n\n" | 832 | -> "\n\n" |
781 | -> use crate::AstNode; | 833 | -> use crate::AstNode; |
782 | 834 | ||
@@ -842,10 +894,10 @@ use std::ops::{self, RangeInclusive}; | |||
842 | expect![[r#" | 894 | expect![[r#" |
843 | insertions: | 895 | insertions: |
844 | 896 | ||
845 | Line 2: Node([email protected]) | 897 | Line 2: After(Node([email protected])) |
846 | -> :: | 898 | -> :: |
847 | -> fmt | 899 | -> fmt |
848 | Line 6: Token([email protected] "\n") | 900 | Line 6: After(Token([email protected] "\n")) |
849 | -> use std::hash::BuildHasherDefault; | 901 | -> use std::hash::BuildHasherDefault; |
850 | -> "\n" | 902 | -> "\n" |
851 | -> use std::ops::{self, RangeInclusive}; | 903 | -> use std::ops::{self, RangeInclusive}; |
@@ -889,14 +941,14 @@ fn main() { | |||
889 | expect![[r#" | 941 | expect![[r#" |
890 | insertions: | 942 | insertions: |
891 | 943 | ||
892 | Line 3: Node([email protected]) | 944 | Line 3: After(Node([email protected])) |
893 | -> " " | 945 | -> " " |
894 | -> match Err(92) { | 946 | -> match Err(92) { |
895 | Ok(it) => it, | 947 | Ok(it) => it, |
896 | _ => return, | 948 | _ => return, |
897 | } | 949 | } |
898 | -> ; | 950 | -> ; |
899 | Line 3: Node([email protected]) | 951 | Line 3: After(Node([email protected])) |
900 | -> "\n " | 952 | -> "\n " |
901 | -> foo(x); | 953 | -> foo(x); |
902 | 954 | ||
@@ -931,14 +983,18 @@ fn main() { | |||
931 | _ => format!("{}", syn), | 983 | _ => format!("{}", syn), |
932 | }; | 984 | }; |
933 | 985 | ||
934 | let insertions = diff.insertions.iter().format_with("\n", |(k, v), f| { | 986 | let insertions = |
935 | f(&format!( | 987 | diff.insertions.iter().format_with("\n", |(k, v), f| -> Result<(), std::fmt::Error> { |
936 | "Line {}: {:?}\n-> {}", | 988 | f(&format!( |
937 | line_number(k), | 989 | "Line {}: {:?}\n-> {}", |
938 | k, | 990 | line_number(match k { |
939 | v.iter().format_with("\n-> ", |v, f| f(&fmt_syntax(v))) | 991 | super::TreeDiffInsertPos::After(syn) => syn, |
940 | )) | 992 | super::TreeDiffInsertPos::AsFirstChild(syn) => syn, |
941 | }); | 993 | }), |
994 | k, | ||
995 | v.iter().format_with("\n-> ", |v, f| f(&fmt_syntax(v))) | ||
996 | )) | ||
997 | }); | ||
942 | 998 | ||
943 | let replacements = diff | 999 | let replacements = diff |
944 | .replacements | 1000 | .replacements |
diff --git a/crates/syntax/src/ast.rs b/crates/syntax/src/ast.rs index 8a0e3d27b..7844f9ed6 100644 --- a/crates/syntax/src/ast.rs +++ b/crates/syntax/src/ast.rs | |||
@@ -115,10 +115,10 @@ fn test_doc_comment_none() { | |||
115 | } | 115 | } |
116 | 116 | ||
117 | #[test] | 117 | #[test] |
118 | fn test_doc_comment_of_items() { | 118 | fn test_outer_doc_comment_of_items() { |
119 | let file = SourceFile::parse( | 119 | let file = SourceFile::parse( |
120 | r#" | 120 | r#" |
121 | //! doc | 121 | /// doc |
122 | // non-doc | 122 | // non-doc |
123 | mod foo {} | 123 | mod foo {} |
124 | "#, | 124 | "#, |
@@ -130,6 +130,21 @@ fn test_doc_comment_of_items() { | |||
130 | } | 130 | } |
131 | 131 | ||
132 | #[test] | 132 | #[test] |
133 | fn test_inner_doc_comment_of_items() { | ||
134 | let file = SourceFile::parse( | ||
135 | r#" | ||
136 | //! doc | ||
137 | // non-doc | ||
138 | mod foo {} | ||
139 | "#, | ||
140 | ) | ||
141 | .ok() | ||
142 | .unwrap(); | ||
143 | let module = file.syntax().descendants().find_map(Module::cast).unwrap(); | ||
144 | assert!(module.doc_comment_text().is_none()); | ||
145 | } | ||
146 | |||
147 | #[test] | ||
133 | fn test_doc_comment_of_statics() { | 148 | fn test_doc_comment_of_statics() { |
134 | let file = SourceFile::parse( | 149 | let file = SourceFile::parse( |
135 | r#" | 150 | r#" |
diff --git a/crates/syntax/src/ast/expr_ext.rs b/crates/syntax/src/ast/expr_ext.rs index 9253c97d0..e4a9b945c 100644 --- a/crates/syntax/src/ast/expr_ext.rs +++ b/crates/syntax/src/ast/expr_ext.rs | |||
@@ -22,6 +22,18 @@ impl ast::Expr { | |||
22 | _ => false, | 22 | _ => false, |
23 | } | 23 | } |
24 | } | 24 | } |
25 | |||
26 | pub fn name_ref(&self) -> Option<ast::NameRef> { | ||
27 | if let ast::Expr::PathExpr(expr) = self { | ||
28 | let path = expr.path()?; | ||
29 | let segment = path.segment()?; | ||
30 | let name_ref = segment.name_ref()?; | ||
31 | if path.qualifier().is_none() { | ||
32 | return Some(name_ref); | ||
33 | } | ||
34 | } | ||
35 | None | ||
36 | } | ||
25 | } | 37 | } |
26 | 38 | ||
27 | #[derive(Debug, Clone, PartialEq, Eq)] | 39 | #[derive(Debug, Clone, PartialEq, Eq)] |
diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs index b1578820f..876659a2b 100644 --- a/crates/syntax/src/ast/make.rs +++ b/crates/syntax/src/ast/make.rs | |||
@@ -25,6 +25,10 @@ pub fn assoc_item_list() -> ast::AssocItemList { | |||
25 | ast_from_text("impl C for D {};") | 25 | ast_from_text("impl C for D {};") |
26 | } | 26 | } |
27 | 27 | ||
28 | pub fn impl_trait(trait_: ast::Path, ty: ast::Path) -> ast::Impl { | ||
29 | ast_from_text(&format!("impl {} for {} {{}}", trait_, ty)) | ||
30 | } | ||
31 | |||
28 | pub fn path_segment(name_ref: ast::NameRef) -> ast::PathSegment { | 32 | pub fn path_segment(name_ref: ast::NameRef) -> ast::PathSegment { |
29 | ast_from_text(&format!("use {};", name_ref)) | 33 | ast_from_text(&format!("use {};", name_ref)) |
30 | } | 34 | } |
diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs index ce35ac01a..b70b840b8 100644 --- a/crates/syntax/src/ast/node_ext.rs +++ b/crates/syntax/src/ast/node_ext.rs | |||
@@ -203,15 +203,7 @@ impl ast::RecordExprField { | |||
203 | if let Some(name_ref) = self.name_ref() { | 203 | if let Some(name_ref) = self.name_ref() { |
204 | return Some(name_ref); | 204 | return Some(name_ref); |
205 | } | 205 | } |
206 | if let Some(ast::Expr::PathExpr(expr)) = self.expr() { | 206 | self.expr()?.name_ref() |
207 | let path = expr.path()?; | ||
208 | let segment = path.segment()?; | ||
209 | let name_ref = segment.name_ref()?; | ||
210 | if path.qualifier().is_none() { | ||
211 | return Some(name_ref); | ||
212 | } | ||
213 | } | ||
214 | None | ||
215 | } | 207 | } |
216 | } | 208 | } |
217 | 209 | ||
diff --git a/crates/syntax/src/ast/token_ext.rs b/crates/syntax/src/ast/token_ext.rs index e4e512f2e..ac0326420 100644 --- a/crates/syntax/src/ast/token_ext.rs +++ b/crates/syntax/src/ast/token_ext.rs | |||
@@ -14,16 +14,15 @@ use crate::{ | |||
14 | 14 | ||
15 | impl ast::Comment { | 15 | impl ast::Comment { |
16 | pub fn kind(&self) -> CommentKind { | 16 | pub fn kind(&self) -> CommentKind { |
17 | kind_by_prefix(self.text()) | 17 | CommentKind::from_text(self.text()) |
18 | } | 18 | } |
19 | 19 | ||
20 | pub fn prefix(&self) -> &'static str { | 20 | pub fn prefix(&self) -> &'static str { |
21 | for (prefix, k) in COMMENT_PREFIX_TO_KIND.iter() { | 21 | let &(prefix, _kind) = CommentKind::BY_PREFIX |
22 | if *k == self.kind() && self.text().starts_with(prefix) { | 22 | .iter() |
23 | return prefix; | 23 | .find(|&(prefix, kind)| self.kind() == *kind && self.text().starts_with(prefix)) |
24 | } | 24 | .unwrap(); |
25 | } | 25 | prefix |
26 | unreachable!() | ||
27 | } | 26 | } |
28 | } | 27 | } |
29 | 28 | ||
@@ -55,29 +54,25 @@ pub enum CommentPlacement { | |||
55 | Outer, | 54 | Outer, |
56 | } | 55 | } |
57 | 56 | ||
58 | const COMMENT_PREFIX_TO_KIND: &[(&str, CommentKind)] = { | 57 | impl CommentKind { |
59 | use {CommentPlacement::*, CommentShape::*}; | 58 | const BY_PREFIX: [(&'static str, CommentKind); 8] = [ |
60 | &[ | 59 | ("/**/", CommentKind { shape: CommentShape::Block, doc: None }), |
61 | ("////", CommentKind { shape: Line, doc: None }), | 60 | ("////", CommentKind { shape: CommentShape::Line, doc: None }), |
62 | ("///", CommentKind { shape: Line, doc: Some(Outer) }), | 61 | ("///", CommentKind { shape: CommentShape::Line, doc: Some(CommentPlacement::Outer) }), |
63 | ("//!", CommentKind { shape: Line, doc: Some(Inner) }), | 62 | ("//!", CommentKind { shape: CommentShape::Line, doc: Some(CommentPlacement::Inner) }), |
64 | ("/**", CommentKind { shape: Block, doc: Some(Outer) }), | 63 | ("/**", CommentKind { shape: CommentShape::Block, doc: Some(CommentPlacement::Outer) }), |
65 | ("/*!", CommentKind { shape: Block, doc: Some(Inner) }), | 64 | ("/*!", CommentKind { shape: CommentShape::Block, doc: Some(CommentPlacement::Inner) }), |
66 | ("//", CommentKind { shape: Line, doc: None }), | 65 | ("//", CommentKind { shape: CommentShape::Line, doc: None }), |
67 | ("/*", CommentKind { shape: Block, doc: None }), | 66 | ("/*", CommentKind { shape: CommentShape::Block, doc: None }), |
68 | ] | 67 | ]; |
69 | }; | ||
70 | 68 | ||
71 | fn kind_by_prefix(text: &str) -> CommentKind { | 69 | pub(crate) fn from_text(text: &str) -> CommentKind { |
72 | if text == "/**/" { | 70 | let &(_prefix, kind) = CommentKind::BY_PREFIX |
73 | return CommentKind { shape: CommentShape::Block, doc: None }; | 71 | .iter() |
74 | } | 72 | .find(|&(prefix, _kind)| text.starts_with(prefix)) |
75 | for (prefix, kind) in COMMENT_PREFIX_TO_KIND.iter() { | 73 | .unwrap(); |
76 | if text.starts_with(prefix) { | 74 | kind |
77 | return *kind; | ||
78 | } | ||
79 | } | 75 | } |
80 | panic!("bad comment text: {:?}", text) | ||
81 | } | 76 | } |
82 | 77 | ||
83 | impl ast::Whitespace { | 78 | impl ast::Whitespace { |
@@ -336,10 +331,22 @@ pub trait HasFormatSpecifier: AstToken { | |||
336 | } | 331 | } |
337 | c if c == '_' || c.is_alphabetic() => { | 332 | c if c == '_' || c.is_alphabetic() => { |
338 | read_identifier(&mut chars, &mut callback); | 333 | read_identifier(&mut chars, &mut callback); |
334 | |||
335 | if chars.peek().and_then(|next| next.1.as_ref().ok()).copied() | ||
336 | == Some('?') | ||
337 | { | ||
338 | skip_char_and_emit( | ||
339 | &mut chars, | ||
340 | FormatSpecifier::QuestionMark, | ||
341 | &mut callback, | ||
342 | ); | ||
343 | } | ||
344 | |||
339 | // can be either width (indicated by dollar sign, or type in which case | 345 | // can be either width (indicated by dollar sign, or type in which case |
340 | // the next sign has to be `}`) | 346 | // the next sign has to be `}`) |
341 | let next = | 347 | let next = |
342 | chars.peek().and_then(|next| next.1.as_ref().ok()).copied(); | 348 | chars.peek().and_then(|next| next.1.as_ref().ok()).copied(); |
349 | |||
343 | match next { | 350 | match next { |
344 | Some('$') => skip_char_and_emit( | 351 | Some('$') => skip_char_and_emit( |
345 | &mut chars, | 352 | &mut chars, |
@@ -422,6 +429,16 @@ pub trait HasFormatSpecifier: AstToken { | |||
422 | } | 429 | } |
423 | c if c == '_' || c.is_alphabetic() => { | 430 | c if c == '_' || c.is_alphabetic() => { |
424 | read_identifier(&mut chars, &mut callback); | 431 | read_identifier(&mut chars, &mut callback); |
432 | |||
433 | if chars.peek().and_then(|next| next.1.as_ref().ok()).copied() | ||
434 | == Some('?') | ||
435 | { | ||
436 | skip_char_and_emit( | ||
437 | &mut chars, | ||
438 | FormatSpecifier::QuestionMark, | ||
439 | &mut callback, | ||
440 | ); | ||
441 | } | ||
425 | } | 442 | } |
426 | _ => {} | 443 | _ => {} |
427 | } | 444 | } |
diff --git a/crates/syntax/src/parsing/text_tree_sink.rs b/crates/syntax/src/parsing/text_tree_sink.rs index c1b5f246d..997bc5d28 100644 --- a/crates/syntax/src/parsing/text_tree_sink.rs +++ b/crates/syntax/src/parsing/text_tree_sink.rs | |||
@@ -5,6 +5,7 @@ use std::mem; | |||
5 | use parser::{ParseError, TreeSink}; | 5 | use parser::{ParseError, TreeSink}; |
6 | 6 | ||
7 | use crate::{ | 7 | use crate::{ |
8 | ast, | ||
8 | parsing::Token, | 9 | parsing::Token, |
9 | syntax_node::GreenNode, | 10 | syntax_node::GreenNode, |
10 | SmolStr, SyntaxError, | 11 | SmolStr, SyntaxError, |
@@ -153,24 +154,22 @@ fn n_attached_trivias<'a>( | |||
153 | 154 | ||
154 | while let Some((i, (kind, text))) = trivias.next() { | 155 | while let Some((i, (kind, text))) = trivias.next() { |
155 | match kind { | 156 | match kind { |
156 | WHITESPACE => { | 157 | WHITESPACE if text.contains("\n\n") => { |
157 | if text.contains("\n\n") { | 158 | // we check whether the next token is a doc-comment |
158 | // we check whether the next token is a doc-comment | 159 | // and skip the whitespace in this case |
159 | // and skip the whitespace in this case | 160 | if let Some((COMMENT, peek_text)) = trivias.peek().map(|(_, pair)| pair) { |
160 | if let Some((peek_kind, peek_text)) = | 161 | let comment_kind = ast::CommentKind::from_text(peek_text); |
161 | trivias.peek().map(|(_, pair)| pair) | 162 | if comment_kind.doc == Some(ast::CommentPlacement::Outer) { |
162 | { | 163 | continue; |
163 | if *peek_kind == COMMENT | ||
164 | && peek_text.starts_with("///") | ||
165 | && !peek_text.starts_with("////") | ||
166 | { | ||
167 | continue; | ||
168 | } | ||
169 | } | 164 | } |
170 | break; | ||
171 | } | 165 | } |
166 | break; | ||
172 | } | 167 | } |
173 | COMMENT => { | 168 | COMMENT => { |
169 | let comment_kind = ast::CommentKind::from_text(text); | ||
170 | if comment_kind.doc == Some(ast::CommentPlacement::Inner) { | ||
171 | break; | ||
172 | } | ||
174 | res = i + 1; | 173 | res = i + 1; |
175 | } | 174 | } |
176 | _ => (), | 175 | _ => (), |
diff --git a/crates/syntax/test_data/parser/ok/0037_mod.rast b/crates/syntax/test_data/parser/ok/0037_mod.rast index 1d5d94bde..35577272e 100644 --- a/crates/syntax/test_data/parser/ok/0037_mod.rast +++ b/crates/syntax/test_data/parser/ok/0037_mod.rast | |||
@@ -1,9 +1,9 @@ | |||
1 | [email protected] | 1 | [email protected] |
2 | [email protected] "// https://github.com ..." | 2 | [email protected] "// https://github.com ..." |
3 | [email protected] "\n\n" | 3 | [email protected] "\n\n" |
4 | MODULE@62..93 | 4 | COMMENT@62..70 "//! docs" |
5 | COMMENT@62..70 "//! docs" | 5 | WHITESPACE@70..71 "\n" |
6 | WHITESPACE@70..71 "\n" | 6 | MODULE@71..93 |
7 | [email protected] "// non-docs" | 7 | [email protected] "// non-docs" |
8 | [email protected] "\n" | 8 | [email protected] "\n" |
9 | [email protected] "mod" | 9 | [email protected] "mod" |
diff --git a/crates/syntax/test_data/parser/ok/0068_item_modifiers.rast b/crates/syntax/test_data/parser/ok/0068_item_modifiers.rast index 50a6d8ee9..87eebf185 100644 --- a/crates/syntax/test_data/parser/ok/0068_item_modifiers.rast +++ b/crates/syntax/test_data/parser/ok/0068_item_modifiers.rast | |||
@@ -1,4 +1,4 @@ | |||
1 | [email protected]04 | 1 | [email protected]28 |
2 | [email protected] | 2 | [email protected] |
3 | [email protected] "async" | 3 | [email protected] "async" |
4 | [email protected] " " | 4 | [email protected] " " |
@@ -215,4 +215,16 @@ [email protected] | |||
215 | [email protected] | 215 | [email protected] |
216 | [email protected] "{" | 216 | [email protected] "{" |
217 | [email protected] "}" | 217 | [email protected] "}" |
218 | [email protected] "\n" | 218 | [email protected] "\n\n" |
219 | [email protected] | ||
220 | [email protected] "unsafe" | ||
221 | [email protected] " " | ||
222 | [email protected] | ||
223 | [email protected] "extern" | ||
224 | [email protected] " " | ||
225 | [email protected] "\"C++\"" | ||
226 | [email protected] " " | ||
227 | [email protected] | ||
228 | [email protected] "{" | ||
229 | [email protected] "}" | ||
230 | [email protected] "\n" | ||
diff --git a/crates/syntax/test_data/parser/ok/0068_item_modifiers.rs b/crates/syntax/test_data/parser/ok/0068_item_modifiers.rs index 8d697c04b..6d27a082c 100644 --- a/crates/syntax/test_data/parser/ok/0068_item_modifiers.rs +++ b/crates/syntax/test_data/parser/ok/0068_item_modifiers.rs | |||
@@ -14,3 +14,5 @@ unsafe auto trait T {} | |||
14 | unsafe impl Foo {} | 14 | unsafe impl Foo {} |
15 | default impl Foo {} | 15 | default impl Foo {} |
16 | unsafe default impl Foo {} | 16 | unsafe default impl Foo {} |
17 | |||
18 | unsafe extern "C++" {} | ||
diff --git a/docs/dev/lsp-extensions.md b/docs/dev/lsp-extensions.md index 780f5cb91..8c01db07c 100644 --- a/docs/dev/lsp-extensions.md +++ b/docs/dev/lsp-extensions.md | |||
@@ -1,8 +1,8 @@ | |||
1 | <!--- | 1 | <!--- |
2 | lsp_ext.rs hash: 286f8bbac885531a | 2 | lsp_ext.rs hash: 203fdf79b21b5987 |
3 | 3 | ||
4 | If you need to change the above hash to make the test pass, please check if you | 4 | If you need to change the above hash to make the test pass, please check if you |
5 | need to adjust this doc as well and ping this issue: | 5 | need to adjust this doc as well and ping this issue: |
6 | 6 | ||
7 | https://github.com/rust-analyzer/rust-analyzer/issues/4604 | 7 | https://github.com/rust-analyzer/rust-analyzer/issues/4604 |
8 | 8 | ||
@@ -45,7 +45,7 @@ interface SnippetTextEdit extends TextEdit { | |||
45 | 45 | ||
46 | ```typescript | 46 | ```typescript |
47 | export interface TextDocumentEdit { | 47 | export interface TextDocumentEdit { |
48 | textDocument: VersionedTextDocumentIdentifier; | 48 | textDocument: OptionalVersionedTextDocumentIdentifier; |
49 | edits: (TextEdit | SnippetTextEdit)[]; | 49 | edits: (TextEdit | SnippetTextEdit)[]; |
50 | } | 50 | } |
51 | ``` | 51 | ``` |
@@ -109,30 +109,6 @@ Invoking code action at this position will yield two code actions for importing | |||
109 | * Is a fixed two-level structure enough? | 109 | * Is a fixed two-level structure enough? |
110 | * Should we devise a general way to encode custom interaction protocols for GUI refactorings? | 110 | * Should we devise a general way to encode custom interaction protocols for GUI refactorings? |
111 | 111 | ||
112 | ## Lazy assists with `ResolveCodeAction` | ||
113 | |||
114 | **Issue:** https://github.com/microsoft/language-server-protocol/issues/787 | ||
115 | |||
116 | **Client Capability** `{ "resolveCodeAction": boolean }` | ||
117 | |||
118 | If this capability is set, the assists will be computed lazily. Thus `CodeAction` returned from the server will only contain `id` but not `edit` or `command` fields. The only exclusion from the rule is the diagnostic edits. | ||
119 | |||
120 | After the client got the id, it should then call `experimental/resolveCodeAction` command on the server and provide the following payload: | ||
121 | |||
122 | ```typescript | ||
123 | interface ResolveCodeActionParams { | ||
124 | id: string; | ||
125 | codeActionParams: lc.CodeActionParams; | ||
126 | } | ||
127 | ``` | ||
128 | |||
129 | As a result of the command call the client will get the respective workspace edit (`lc.WorkspaceEdit`). | ||
130 | |||
131 | ### Unresolved Questions | ||
132 | |||
133 | * Apply smarter filtering for ids? | ||
134 | * Upon `resolveCodeAction` command only call the assits which should be resolved and not all of them? | ||
135 | |||
136 | ## Parent Module | 112 | ## Parent Module |
137 | 113 | ||
138 | **Issue:** https://github.com/microsoft/language-server-protocol/issues/1002 | 114 | **Issue:** https://github.com/microsoft/language-server-protocol/issues/1002 |
@@ -561,3 +537,28 @@ Such actions on the client side are appended to a hover bottom as command links: | |||
561 | +-----------------------------+ | 537 | +-----------------------------+ |
562 | ... | 538 | ... |
563 | ``` | 539 | ``` |
540 | |||
541 | ## Open Cargo.toml | ||
542 | |||
543 | **Issue:** https://github.com/rust-analyzer/rust-analyzer/issues/6462 | ||
544 | |||
545 | This request is sent from client to server to open the current project's Cargo.toml | ||
546 | |||
547 | **Method:** `experimental/openCargoToml` | ||
548 | |||
549 | **Request:** `OpenCargoTomlParams` | ||
550 | |||
551 | **Response:** `Location | null` | ||
552 | |||
553 | |||
554 | ### Example | ||
555 | |||
556 | ```rust | ||
557 | // Cargo.toml | ||
558 | [package] | ||
559 | // src/main.rs | ||
560 | |||
561 | /* cursor here*/ | ||
562 | ``` | ||
563 | |||
564 | `experimental/openCargoToml` returns a single `Link` to the start of the `[package]` keyword. | ||
diff --git a/docs/dev/syntax.md b/docs/dev/syntax.md index 2eb08b7ca..1edafab68 100644 --- a/docs/dev/syntax.md +++ b/docs/dev/syntax.md | |||
@@ -195,7 +195,7 @@ Modeling this with immutable trees is possible, but annoying. | |||
195 | A function green tree is not super-convenient to use. | 195 | A function green tree is not super-convenient to use. |
196 | The biggest problem is accessing parents (there are no parent pointers!). | 196 | The biggest problem is accessing parents (there are no parent pointers!). |
197 | But there are also "identify" issues. | 197 | But there are also "identify" issues. |
198 | Let's say you want to write a code which builds a list of expressions in a file: `fn collect_exrepssions(file: GreenNode) -> HashSet<GreenNode>`. | 198 | Let's say you want to write a code which builds a list of expressions in a file: `fn collect_expressions(file: GreenNode) -> HashSet<GreenNode>`. |
199 | For the input like | 199 | For the input like |
200 | 200 | ||
201 | ```rust | 201 | ```rust |
@@ -236,7 +236,7 @@ impl SyntaxNode { | |||
236 | self.parent.clone() | 236 | self.parent.clone() |
237 | } | 237 | } |
238 | fn children(&self) -> impl Iterator<Item = SyntaxNode> { | 238 | fn children(&self) -> impl Iterator<Item = SyntaxNode> { |
239 | let mut offset = self.offset | 239 | let mut offset = self.offset; |
240 | self.green.children().map(|green_child| { | 240 | self.green.children().map(|green_child| { |
241 | let child_offset = offset; | 241 | let child_offset = offset; |
242 | offset += green_child.text_len; | 242 | offset += green_child.text_len; |
diff --git a/editors/code/package-lock.json b/editors/code/package-lock.json index 83ef00058..a60d3668b 100644 --- a/editors/code/package-lock.json +++ b/editors/code/package-lock.json | |||
@@ -2414,27 +2414,27 @@ | |||
2414 | "integrity": "sha512-1nG+6cuTtpzmXe7yYfO9GCkYlyV6Ai+jDnwidHiT2T7zhc+bJM+VTtc0T/CdTlDyTNTqIcCj0V1nD4TcVjJ7Ug==" | 2414 | "integrity": "sha512-1nG+6cuTtpzmXe7yYfO9GCkYlyV6Ai+jDnwidHiT2T7zhc+bJM+VTtc0T/CdTlDyTNTqIcCj0V1nD4TcVjJ7Ug==" |
2415 | }, | 2415 | }, |
2416 | "vscode-languageclient": { | 2416 | "vscode-languageclient": { |
2417 | "version": "7.0.0-next.12", | 2417 | "version": "7.0.0-next.14", |
2418 | "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-7.0.0-next.12.tgz", | 2418 | "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-7.0.0-next.14.tgz", |
2419 | "integrity": "sha512-OrzvOvhS5o26C0KctTJC7hkwh3avCwkVhllzy42AqwpIUZ3p2aVqkSG2uVxaeodq8ThBb3TLgtg50vxyWs6FEg==", | 2419 | "integrity": "sha512-QUccfXK2F6AXXRFR8QJCaIz7N2BhJK6ok8E1aO8LHq2IBU33+5hTSJBXs7nEqrqZ/cY2VlDDbMWtMvCxz+/y1w==", |
2420 | "requires": { | 2420 | "requires": { |
2421 | "semver": "^6.3.0", | 2421 | "semver": "^6.3.0", |
2422 | "vscode-languageserver-protocol": "3.16.0-next.10" | 2422 | "vscode-languageserver-protocol": "3.16.0-next.11" |
2423 | } | 2423 | } |
2424 | }, | 2424 | }, |
2425 | "vscode-languageserver-protocol": { | 2425 | "vscode-languageserver-protocol": { |
2426 | "version": "3.16.0-next.10", | 2426 | "version": "3.16.0-next.11", |
2427 | "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.16.0-next.10.tgz", | 2427 | "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.16.0-next.11.tgz", |
2428 | "integrity": "sha512-YRTctHUZvts0Z1xXKNYU0ha0o+Tlgtwr+6O8OmDquM086N8exiSKBMwMC+Ra1QtIE+1mfW43Wxsme2FnMkAS9A==", | 2428 | "integrity": "sha512-31FmupmSmfznuMuGp7qN6h3d/hKUbexbvcwTvrUE/igqRlzFU542s8MtGICx1ERbVuDOLGp96W2Z92qbUbmBPA==", |
2429 | "requires": { | 2429 | "requires": { |
2430 | "vscode-jsonrpc": "6.0.0-next.7", | 2430 | "vscode-jsonrpc": "6.0.0-next.7", |
2431 | "vscode-languageserver-types": "3.16.0-next.4" | 2431 | "vscode-languageserver-types": "3.16.0-next.5" |
2432 | } | 2432 | } |
2433 | }, | 2433 | }, |
2434 | "vscode-languageserver-types": { | 2434 | "vscode-languageserver-types": { |
2435 | "version": "3.16.0-next.4", | 2435 | "version": "3.16.0-next.5", |
2436 | "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.16.0-next.4.tgz", | 2436 | "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.16.0-next.5.tgz", |
2437 | "integrity": "sha512-NlKJyGcET/ZBCCLBYIPaGo2c37R03bPYeWXozUtnjyye7+9dhlbMSODyoG2INcQf8zFmB4qhm2UOJjgYEgPCNA==" | 2437 | "integrity": "sha512-lf8Y1XXMtF1r2oDDAmJe+drizNXkybSRXAQQk5dPy2rYJsY9SPXYNO074L3THu9zNYepzV5fRJZUPo/V/TLBRQ==" |
2438 | }, | 2438 | }, |
2439 | "vscode-test": { | 2439 | "vscode-test": { |
2440 | "version": "1.4.0", | 2440 | "version": "1.4.0", |
diff --git a/editors/code/package.json b/editors/code/package.json index fa8cf90f8..c3f1a0d8d 100644 --- a/editors/code/package.json +++ b/editors/code/package.json | |||
@@ -21,7 +21,7 @@ | |||
21 | "Programming Languages" | 21 | "Programming Languages" |
22 | ], | 22 | ], |
23 | "engines": { | 23 | "engines": { |
24 | "vscode": "^1.47.1" | 24 | "vscode": "^1.51.0" |
25 | }, | 25 | }, |
26 | "enableProposedApi": true, | 26 | "enableProposedApi": true, |
27 | "scripts": { | 27 | "scripts": { |
@@ -36,7 +36,7 @@ | |||
36 | }, | 36 | }, |
37 | "dependencies": { | 37 | "dependencies": { |
38 | "node-fetch": "^2.6.1", | 38 | "node-fetch": "^2.6.1", |
39 | "vscode-languageclient": "7.0.0-next.12" | 39 | "vscode-languageclient": "7.0.0-next.14" |
40 | }, | 40 | }, |
41 | "devDependencies": { | 41 | "devDependencies": { |
42 | "@rollup/plugin-commonjs": "^13.0.2", | 42 | "@rollup/plugin-commonjs": "^13.0.2", |
@@ -187,6 +187,11 @@ | |||
187 | "command": "rust-analyzer.openDocs", | 187 | "command": "rust-analyzer.openDocs", |
188 | "title": "Open docs under cursor", | 188 | "title": "Open docs under cursor", |
189 | "category": "Rust Analyzer" | 189 | "category": "Rust Analyzer" |
190 | }, | ||
191 | { | ||
192 | "command": "rust-analyzer.openCargoToml", | ||
193 | "title": "Open Cargo.toml", | ||
194 | "category": "Rust Analyzer" | ||
190 | } | 195 | } |
191 | ], | 196 | ], |
192 | "keybindings": [ | 197 | "keybindings": [ |
@@ -278,6 +283,11 @@ | |||
278 | "default": null, | 283 | "default": null, |
279 | "description": "Specify the compilation target" | 284 | "description": "Specify the compilation target" |
280 | }, | 285 | }, |
286 | "rust-analyzer.noSysroot": { | ||
287 | "markdownDescription": "Internal config for debugging, disables loading of sysroot crates", | ||
288 | "type": "boolean", | ||
289 | "default": false | ||
290 | }, | ||
281 | "rust-analyzer.rustfmt.extraArgs": { | 291 | "rust-analyzer.rustfmt.extraArgs": { |
282 | "type": "array", | 292 | "type": "array", |
283 | "items": { | 293 | "items": { |
@@ -450,6 +460,11 @@ | |||
450 | "default": true, | 460 | "default": true, |
451 | "markdownDescription": "Whether to show postfix snippets like `dbg`, `if`, `not`, etc." | 461 | "markdownDescription": "Whether to show postfix snippets like `dbg`, `if`, `not`, etc." |
452 | }, | 462 | }, |
463 | "rust-analyzer.completion.enableExperimental": { | ||
464 | "type": "boolean", | ||
465 | "default": true, | ||
466 | "markdownDescription": "Display additional completions with potential false positives and performance issues" | ||
467 | }, | ||
453 | "rust-analyzer.callInfo.full": { | 468 | "rust-analyzer.callInfo.full": { |
454 | "type": "boolean", | 469 | "type": "boolean", |
455 | "default": true, | 470 | "default": true, |
@@ -600,11 +615,6 @@ | |||
600 | }, | 615 | }, |
601 | "default": null | 616 | "default": null |
602 | }, | 617 | }, |
603 | "rust-analyzer.withSysroot": { | ||
604 | "markdownDescription": "Internal config for debugging, disables loading of sysroot crates", | ||
605 | "type": "boolean", | ||
606 | "default": true | ||
607 | }, | ||
608 | "rust-analyzer.diagnostics.enable": { | 618 | "rust-analyzer.diagnostics.enable": { |
609 | "type": "boolean", | 619 | "type": "boolean", |
610 | "default": true, | 620 | "default": true, |
@@ -687,6 +697,14 @@ | |||
687 | }, | 697 | }, |
688 | "default": [], | 698 | "default": [], |
689 | "description": "Additional arguments to be passed to cargo for runnables such as tests or binaries.\nFor example, it may be '--release'" | 699 | "description": "Additional arguments to be passed to cargo for runnables such as tests or binaries.\nFor example, it may be '--release'" |
700 | }, | ||
701 | "rust-analyzer.rustcSource": { | ||
702 | "type": [ | ||
703 | "null", | ||
704 | "string" | ||
705 | ], | ||
706 | "default": null, | ||
707 | "description": "Path to the rust compiler sources, for usage in rustc_private projects." | ||
690 | } | 708 | } |
691 | } | 709 | } |
692 | }, | 710 | }, |
@@ -1054,6 +1072,10 @@ | |||
1054 | { | 1072 | { |
1055 | "command": "rust-analyzer.openDocs", | 1073 | "command": "rust-analyzer.openDocs", |
1056 | "when": "inRustProject" | 1074 | "when": "inRustProject" |
1075 | }, | ||
1076 | { | ||
1077 | "command": "rust-analyzer.openCargoToml", | ||
1078 | "when": "inRustProject" | ||
1057 | } | 1079 | } |
1058 | ] | 1080 | ] |
1059 | } | 1081 | } |
diff --git a/editors/code/rust.tmGrammar.json b/editors/code/rust.tmGrammar.json index 608a3354e..4759bb116 100644 --- a/editors/code/rust.tmGrammar.json +++ b/editors/code/rust.tmGrammar.json | |||
@@ -167,7 +167,7 @@ | |||
167 | "match": "(mod)\\s+((?:r#(?!crate|[Ss]elf|super))?[a-z][A-Za-z0-9_]*)", | 167 | "match": "(mod)\\s+((?:r#(?!crate|[Ss]elf|super))?[a-z][A-Za-z0-9_]*)", |
168 | "captures": { | 168 | "captures": { |
169 | "1": { | 169 | "1": { |
170 | "name": "keyword.control.rust" | 170 | "name": "storage.type.rust" |
171 | }, | 171 | }, |
172 | "2": { | 172 | "2": { |
173 | "name": "entity.name.module.rust" | 173 | "name": "entity.name.module.rust" |
@@ -180,7 +180,7 @@ | |||
180 | "begin": "\\b(extern)\\s+(crate)", | 180 | "begin": "\\b(extern)\\s+(crate)", |
181 | "beginCaptures": { | 181 | "beginCaptures": { |
182 | "1": { | 182 | "1": { |
183 | "name": "keyword.control.rust" | 183 | "name": "storage.type.rust" |
184 | }, | 184 | }, |
185 | "2": { | 185 | "2": { |
186 | "name": "keyword.other.crate.rust" | 186 | "name": "keyword.other.crate.rust" |
@@ -213,7 +213,7 @@ | |||
213 | "begin": "\\b(use)\\s", | 213 | "begin": "\\b(use)\\s", |
214 | "beginCaptures": { | 214 | "beginCaptures": { |
215 | "1": { | 215 | "1": { |
216 | "name": "keyword.control.rust" | 216 | "name": "keyword.other.rust" |
217 | } | 217 | } |
218 | }, | 218 | }, |
219 | "end": ";", | 219 | "end": ";", |
@@ -307,9 +307,14 @@ | |||
307 | "block-comments": { | 307 | "block-comments": { |
308 | "patterns": [ | 308 | "patterns": [ |
309 | { | 309 | { |
310 | "comment": "block comments", | 310 | "comment": "empty block comments", |
311 | "name": "comment.block.rust", | 311 | "name": "comment.block.rust", |
312 | "begin": "/\\*(?!\\*)", | 312 | "match": "/\\*\\*/" |
313 | }, | ||
314 | { | ||
315 | "comment": "block documentation comments", | ||
316 | "name": "comment.block.documentation.rust", | ||
317 | "begin": "/\\*\\*", | ||
313 | "end": "\\*/", | 318 | "end": "\\*/", |
314 | "patterns": [ | 319 | "patterns": [ |
315 | { | 320 | { |
@@ -318,9 +323,9 @@ | |||
318 | ] | 323 | ] |
319 | }, | 324 | }, |
320 | { | 325 | { |
321 | "comment": "block documentation comments", | 326 | "comment": "block comments", |
322 | "name": "comment.block.documentation.rust", | 327 | "name": "comment.block.rust", |
323 | "begin": "/\\*\\*", | 328 | "begin": "/\\*(?!\\*)", |
324 | "end": "\\*/", | 329 | "end": "\\*/", |
325 | "patterns": [ | 330 | "patterns": [ |
326 | { | 331 | { |
@@ -342,7 +347,7 @@ | |||
342 | "match": "\\b(const)\\s+([A-Z][A-Za-z0-9_]*)\\b", | 347 | "match": "\\b(const)\\s+([A-Z][A-Za-z0-9_]*)\\b", |
343 | "captures": { | 348 | "captures": { |
344 | "1": { | 349 | "1": { |
345 | "name": "keyword.control.rust" | 350 | "name": "storage.type.rust" |
346 | }, | 351 | }, |
347 | "2": { | 352 | "2": { |
348 | "name": "constant.other.caps.rust" | 353 | "name": "constant.other.caps.rust" |
@@ -404,7 +409,7 @@ | |||
404 | { | 409 | { |
405 | "comment": "booleans", | 410 | "comment": "booleans", |
406 | "name": "constant.language.bool.rust", | 411 | "name": "constant.language.bool.rust", |
407 | "match": "\\btrue|false\\b" | 412 | "match": "\\b(true|false)\\b" |
408 | } | 413 | } |
409 | ] | 414 | ] |
410 | }, | 415 | }, |
@@ -450,7 +455,7 @@ | |||
450 | "begin": "\\b(fn)\\s+((?:r#(?!crate|[Ss]elf|super))?[A-Za-z0-9_]+)((\\()|(<))", | 455 | "begin": "\\b(fn)\\s+((?:r#(?!crate|[Ss]elf|super))?[A-Za-z0-9_]+)((\\()|(<))", |
451 | "beginCaptures": { | 456 | "beginCaptures": { |
452 | "1": { | 457 | "1": { |
453 | "name": "keyword.control.fn.rust" | 458 | "name": "keyword.other.fn.rust" |
454 | }, | 459 | }, |
455 | "2": { | 460 | "2": { |
456 | "name": "entity.name.function.rust" | 461 | "name": "entity.name.function.rust" |
@@ -643,7 +648,7 @@ | |||
643 | { | 648 | { |
644 | "comment": "control flow keywords", | 649 | "comment": "control flow keywords", |
645 | "name": "keyword.control.rust", | 650 | "name": "keyword.control.rust", |
646 | "match": "\\b(async|await|break|continue|do|else|for|if|loop|match|move|return|try|where|while|yield)\\b" | 651 | "match": "\\b(await|break|continue|do|else|for|if|loop|match|return|try|while|yield)\\b" |
647 | }, | 652 | }, |
648 | { | 653 | { |
649 | "comment": "storage keywords", | 654 | "comment": "storage keywords", |
@@ -658,7 +663,7 @@ | |||
658 | { | 663 | { |
659 | "comment": "other keywords", | 664 | "comment": "other keywords", |
660 | "name": "keyword.other.rust", | 665 | "name": "keyword.other.rust", |
661 | "match": "\\b(as|become|box|dyn|final|impl|in|override|priv|pub|ref|typeof|union|unsafe|unsized|use|virtual)\\b" | 666 | "match": "\\b(as|async|become|box|dyn|move|final|impl|in|override|priv|pub|ref|typeof|union|unsafe|unsized|use|virtual|where)\\b" |
662 | }, | 667 | }, |
663 | { | 668 | { |
664 | "comment": "fn", | 669 | "comment": "fn", |
diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts index d032b45b7..63ab82dde 100644 --- a/editors/code/src/client.ts +++ b/editors/code/src/client.ts | |||
@@ -4,6 +4,7 @@ import * as ra from '../src/lsp_ext'; | |||
4 | import * as Is from 'vscode-languageclient/lib/common/utils/is'; | 4 | import * as Is from 'vscode-languageclient/lib/common/utils/is'; |
5 | import { DocumentSemanticsTokensSignature, DocumentSemanticsTokensEditsSignature, DocumentRangeSemanticTokensSignature } from 'vscode-languageclient/lib/common/semanticTokens'; | 5 | import { DocumentSemanticsTokensSignature, DocumentSemanticsTokensEditsSignature, DocumentRangeSemanticTokensSignature } from 'vscode-languageclient/lib/common/semanticTokens'; |
6 | import { assert } from './util'; | 6 | import { assert } from './util'; |
7 | import { WorkspaceEdit } from 'vscode'; | ||
7 | 8 | ||
8 | function renderCommand(cmd: ra.CommandLink) { | 9 | function renderCommand(cmd: ra.CommandLink) { |
9 | return `[${cmd.title}](command:${cmd.command}?${encodeURIComponent(JSON.stringify(cmd.arguments))} '${cmd.tooltip!}')`; | 10 | return `[${cmd.title}](command:${cmd.command}?${encodeURIComponent(JSON.stringify(cmd.arguments))} '${cmd.tooltip!}')`; |
@@ -75,8 +76,8 @@ export function createClient(serverPath: string, cwd: string): lc.LanguageClient | |||
75 | return Promise.resolve(null); | 76 | return Promise.resolve(null); |
76 | }); | 77 | }); |
77 | }, | 78 | }, |
78 | // Using custom handling of CodeActions where each code action is resolved lazily | 79 | // Using custom handling of CodeActions to support action groups and snippet edits. |
79 | // That's why we are not waiting for any command or edits | 80 | // Note that this means we have to re-implement lazy edit resolving ourselves as well. |
80 | async provideCodeActions(document: vscode.TextDocument, range: vscode.Range, context: vscode.CodeActionContext, token: vscode.CancellationToken, _next: lc.ProvideCodeActionsSignature) { | 81 | async provideCodeActions(document: vscode.TextDocument, range: vscode.Range, context: vscode.CodeActionContext, token: vscode.CancellationToken, _next: lc.ProvideCodeActionsSignature) { |
81 | const params: lc.CodeActionParams = { | 82 | const params: lc.CodeActionParams = { |
82 | textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(document), | 83 | textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(document), |
@@ -99,16 +100,15 @@ export function createClient(serverPath: string, cwd: string): lc.LanguageClient | |||
99 | const kind = client.protocol2CodeConverter.asCodeActionKind((item as any).kind); | 100 | const kind = client.protocol2CodeConverter.asCodeActionKind((item as any).kind); |
100 | const action = new vscode.CodeAction(item.title, kind); | 101 | const action = new vscode.CodeAction(item.title, kind); |
101 | const group = (item as any).group; | 102 | const group = (item as any).group; |
102 | const id = (item as any).id; | ||
103 | const resolveParams: ra.ResolveCodeActionParams = { | ||
104 | id: id, | ||
105 | codeActionParams: params | ||
106 | }; | ||
107 | action.command = { | 103 | action.command = { |
108 | command: "rust-analyzer.resolveCodeAction", | 104 | command: "rust-analyzer.resolveCodeAction", |
109 | title: item.title, | 105 | title: item.title, |
110 | arguments: [resolveParams], | 106 | arguments: [item], |
111 | }; | 107 | }; |
108 | |||
109 | // Set a dummy edit, so that VS Code doesn't try to resolve this. | ||
110 | action.edit = new WorkspaceEdit(); | ||
111 | |||
112 | if (group) { | 112 | if (group) { |
113 | let entry = groups.get(group); | 113 | let entry = groups.get(group); |
114 | if (!entry) { | 114 | if (!entry) { |
@@ -134,6 +134,10 @@ export function createClient(serverPath: string, cwd: string): lc.LanguageClient | |||
134 | return { label: item.title, arguments: item.command!!.arguments!![0] }; | 134 | return { label: item.title, arguments: item.command!!.arguments!![0] }; |
135 | })], | 135 | })], |
136 | }; | 136 | }; |
137 | |||
138 | // Set a dummy edit, so that VS Code doesn't try to resolve this. | ||
139 | action.edit = new WorkspaceEdit(); | ||
140 | |||
137 | result[index] = action; | 141 | result[index] = action; |
138 | } | 142 | } |
139 | } | 143 | } |
@@ -164,13 +168,14 @@ class ExperimentalFeatures implements lc.StaticFeature { | |||
164 | const caps: any = capabilities.experimental ?? {}; | 168 | const caps: any = capabilities.experimental ?? {}; |
165 | caps.snippetTextEdit = true; | 169 | caps.snippetTextEdit = true; |
166 | caps.codeActionGroup = true; | 170 | caps.codeActionGroup = true; |
167 | caps.resolveCodeAction = true; | ||
168 | caps.hoverActions = true; | 171 | caps.hoverActions = true; |
169 | caps.statusNotification = true; | 172 | caps.statusNotification = true; |
170 | capabilities.experimental = caps; | 173 | capabilities.experimental = caps; |
171 | } | 174 | } |
172 | initialize(_capabilities: lc.ServerCapabilities<any>, _documentSelector: lc.DocumentSelector | undefined): void { | 175 | initialize(_capabilities: lc.ServerCapabilities<any>, _documentSelector: lc.DocumentSelector | undefined): void { |
173 | } | 176 | } |
177 | dispose(): void { | ||
178 | } | ||
174 | } | 179 | } |
175 | 180 | ||
176 | function isCodeActionWithoutEditsAndCommands(value: any): boolean { | 181 | function isCodeActionWithoutEditsAndCommands(value: any): boolean { |
diff --git a/editors/code/src/commands.ts b/editors/code/src/commands.ts index 22509e874..92bc4d7f7 100644 --- a/editors/code/src/commands.ts +++ b/editors/code/src/commands.ts | |||
@@ -188,6 +188,27 @@ export function parentModule(ctx: Ctx): Cmd { | |||
188 | }; | 188 | }; |
189 | } | 189 | } |
190 | 190 | ||
191 | export function openCargoToml(ctx: Ctx): Cmd { | ||
192 | return async () => { | ||
193 | const editor = ctx.activeRustEditor; | ||
194 | const client = ctx.client; | ||
195 | if (!editor || !client) return; | ||
196 | |||
197 | const response = await client.sendRequest(ra.openCargoToml, { | ||
198 | textDocument: ctx.client.code2ProtocolConverter.asTextDocumentIdentifier(editor.document), | ||
199 | }); | ||
200 | if (!response) return; | ||
201 | |||
202 | const uri = client.protocol2CodeConverter.asUri(response.uri); | ||
203 | const range = client.protocol2CodeConverter.asRange(response.range); | ||
204 | |||
205 | const doc = await vscode.workspace.openTextDocument(uri); | ||
206 | const e = await vscode.window.showTextDocument(doc); | ||
207 | e.selection = new vscode.Selection(range.start, range.start); | ||
208 | e.revealRange(range, vscode.TextEditorRevealType.InCenter); | ||
209 | }; | ||
210 | } | ||
211 | |||
191 | export function ssr(ctx: Ctx): Cmd { | 212 | export function ssr(ctx: Ctx): Cmd { |
192 | return async () => { | 213 | return async () => { |
193 | const editor = vscode.window.activeTextEditor; | 214 | const editor = vscode.window.activeTextEditor; |
@@ -395,7 +416,7 @@ export function showReferences(ctx: Ctx): Cmd { | |||
395 | } | 416 | } |
396 | 417 | ||
397 | export function applyActionGroup(_ctx: Ctx): Cmd { | 418 | export function applyActionGroup(_ctx: Ctx): Cmd { |
398 | return async (actions: { label: string; arguments: ra.ResolveCodeActionParams }[]) => { | 419 | return async (actions: { label: string; arguments: lc.CodeAction }[]) => { |
399 | const selectedAction = await vscode.window.showQuickPick(actions); | 420 | const selectedAction = await vscode.window.showQuickPick(actions); |
400 | if (!selectedAction) return; | 421 | if (!selectedAction) return; |
401 | vscode.commands.executeCommand( | 422 | vscode.commands.executeCommand( |
@@ -442,12 +463,13 @@ export function openDocs(ctx: Ctx): Cmd { | |||
442 | 463 | ||
443 | export function resolveCodeAction(ctx: Ctx): Cmd { | 464 | export function resolveCodeAction(ctx: Ctx): Cmd { |
444 | const client = ctx.client; | 465 | const client = ctx.client; |
445 | return async (params: ra.ResolveCodeActionParams) => { | 466 | return async (params: lc.CodeAction) => { |
446 | const item: lc.WorkspaceEdit = await client.sendRequest(ra.resolveCodeAction, params); | 467 | params.command = undefined; |
447 | if (!item) { | 468 | const item = await client.sendRequest(lc.CodeActionResolveRequest.type, params); |
469 | if (!item.edit) { | ||
448 | return; | 470 | return; |
449 | } | 471 | } |
450 | const edit = client.protocol2CodeConverter.asWorkspaceEdit(item); | 472 | const edit = client.protocol2CodeConverter.asWorkspaceEdit(item.edit); |
451 | await applySnippetWorkspaceEdit(edit); | 473 | await applySnippetWorkspaceEdit(edit); |
452 | }; | 474 | }; |
453 | } | 475 | } |
diff --git a/editors/code/src/lsp_ext.ts b/editors/code/src/lsp_ext.ts index fc8e120b3..5e877ce65 100644 --- a/editors/code/src/lsp_ext.ts +++ b/editors/code/src/lsp_ext.ts | |||
@@ -43,12 +43,6 @@ export const matchingBrace = new lc.RequestType<MatchingBraceParams, lc.Position | |||
43 | 43 | ||
44 | export const parentModule = new lc.RequestType<lc.TextDocumentPositionParams, lc.LocationLink[], void>("experimental/parentModule"); | 44 | export const parentModule = new lc.RequestType<lc.TextDocumentPositionParams, lc.LocationLink[], void>("experimental/parentModule"); |
45 | 45 | ||
46 | export interface ResolveCodeActionParams { | ||
47 | id: string; | ||
48 | codeActionParams: lc.CodeActionParams; | ||
49 | } | ||
50 | export const resolveCodeAction = new lc.RequestType<ResolveCodeActionParams, lc.WorkspaceEdit, unknown>('experimental/resolveCodeAction'); | ||
51 | |||
52 | export interface JoinLinesParams { | 46 | export interface JoinLinesParams { |
53 | textDocument: lc.TextDocumentIdentifier; | 47 | textDocument: lc.TextDocumentIdentifier; |
54 | ranges: lc.Range[]; | 48 | ranges: lc.Range[]; |
@@ -120,3 +114,9 @@ export interface CommandLinkGroup { | |||
120 | } | 114 | } |
121 | 115 | ||
122 | export const openDocs = new lc.RequestType<lc.TextDocumentPositionParams, string | void, void>('experimental/externalDocs'); | 116 | export const openDocs = new lc.RequestType<lc.TextDocumentPositionParams, string | void, void>('experimental/externalDocs'); |
117 | |||
118 | export const openCargoToml = new lc.RequestType<OpenCargoTomlParams, lc.Location, void>("experimental/openCargoToml"); | ||
119 | |||
120 | export interface OpenCargoTomlParams { | ||
121 | textDocument: lc.TextDocumentIdentifier; | ||
122 | } | ||
diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts index 09543e348..2f3dde8ac 100644 --- a/editors/code/src/main.ts +++ b/editors/code/src/main.ts | |||
@@ -111,6 +111,7 @@ async function tryActivate(context: vscode.ExtensionContext) { | |||
111 | ctx.registerCommand('debug', commands.debug); | 111 | ctx.registerCommand('debug', commands.debug); |
112 | ctx.registerCommand('newDebugConfig', commands.newDebugConfig); | 112 | ctx.registerCommand('newDebugConfig', commands.newDebugConfig); |
113 | ctx.registerCommand('openDocs', commands.openDocs); | 113 | ctx.registerCommand('openDocs', commands.openDocs); |
114 | ctx.registerCommand('openCargoToml', commands.openCargoToml); | ||
114 | 115 | ||
115 | defaultOnEnter.dispose(); | 116 | defaultOnEnter.dispose(); |
116 | ctx.registerCommand('onEnter', commands.onEnter); | 117 | ctx.registerCommand('onEnter', commands.onEnter); |
diff --git a/editors/code/src/snippets.ts b/editors/code/src/snippets.ts index 258b49982..fee736e7d 100644 --- a/editors/code/src/snippets.ts +++ b/editors/code/src/snippets.ts | |||
@@ -3,16 +3,29 @@ import * as vscode from 'vscode'; | |||
3 | import { assert } from './util'; | 3 | import { assert } from './util'; |
4 | 4 | ||
5 | export async function applySnippetWorkspaceEdit(edit: vscode.WorkspaceEdit) { | 5 | export async function applySnippetWorkspaceEdit(edit: vscode.WorkspaceEdit) { |
6 | assert(edit.entries().length === 1, `bad ws edit: ${JSON.stringify(edit)}`); | 6 | if (edit.entries().length === 1) { |
7 | const [uri, edits] = edit.entries()[0]; | 7 | const [uri, edits] = edit.entries()[0]; |
8 | const editor = await editorFromUri(uri); | ||
9 | if (editor) await applySnippetTextEdits(editor, edits); | ||
10 | return; | ||
11 | } | ||
12 | for (const [uri, edits] of edit.entries()) { | ||
13 | const editor = await editorFromUri(uri); | ||
14 | if (editor) await editor.edit((builder) => { | ||
15 | for (const indel of edits) { | ||
16 | assert(!parseSnippet(indel.newText), `bad ws edit: snippet received with multiple edits: ${JSON.stringify(edit)}`); | ||
17 | builder.replace(indel.range, indel.newText); | ||
18 | } | ||
19 | }); | ||
20 | } | ||
21 | } | ||
8 | 22 | ||
23 | async function editorFromUri(uri: vscode.Uri): Promise<vscode.TextEditor | undefined> { | ||
9 | if (vscode.window.activeTextEditor?.document.uri !== uri) { | 24 | if (vscode.window.activeTextEditor?.document.uri !== uri) { |
10 | // `vscode.window.visibleTextEditors` only contains editors whose contents are being displayed | 25 | // `vscode.window.visibleTextEditors` only contains editors whose contents are being displayed |
11 | await vscode.window.showTextDocument(uri, {}); | 26 | await vscode.window.showTextDocument(uri, {}); |
12 | } | 27 | } |
13 | const editor = vscode.window.visibleTextEditors.find((it) => it.document.uri.toString() === uri.toString()); | 28 | return vscode.window.visibleTextEditors.find((it) => it.document.uri.toString() === uri.toString()); |
14 | if (!editor) return; | ||
15 | await applySnippetTextEdits(editor, edits); | ||
16 | } | 29 | } |
17 | 30 | ||
18 | export async function applySnippetTextEdits(editor: vscode.TextEditor, edits: vscode.TextEdit[]) { | 31 | export async function applySnippetTextEdits(editor: vscode.TextEditor, edits: vscode.TextEdit[]) { |
diff --git a/xtask/tests/tidy.rs b/xtask/tests/tidy.rs index 9de60c76c..4c58aed59 100644 --- a/xtask/tests/tidy.rs +++ b/xtask/tests/tidy.rs | |||
@@ -168,6 +168,7 @@ ISC | |||
168 | MIT | 168 | MIT |
169 | MIT / Apache-2.0 | 169 | MIT / Apache-2.0 |
170 | MIT OR Apache-2.0 | 170 | MIT OR Apache-2.0 |
171 | MIT OR Apache-2.0 OR Zlib | ||
171 | MIT OR Zlib OR Apache-2.0 | 172 | MIT OR Zlib OR Apache-2.0 |
172 | MIT/Apache-2.0 | 173 | MIT/Apache-2.0 |
173 | Unlicense OR MIT | 174 | Unlicense OR MIT |
@@ -214,8 +215,6 @@ fn check_todo(path: &Path, text: &str) { | |||
214 | // This file itself obviously needs to use todo (<- like this!). | 215 | // This file itself obviously needs to use todo (<- like this!). |
215 | "tests/cli.rs", | 216 | "tests/cli.rs", |
216 | // Some of our assists generate `todo!()`. | 217 | // Some of our assists generate `todo!()`. |
217 | "tests/generated.rs", | ||
218 | "handlers/add_missing_impl_members.rs", | ||
219 | "handlers/add_turbo_fish.rs", | 218 | "handlers/add_turbo_fish.rs", |
220 | "handlers/generate_function.rs", | 219 | "handlers/generate_function.rs", |
221 | // To support generating `todo!()` in assists, we have `expr_todo()` in | 220 | // To support generating `todo!()` in assists, we have `expr_todo()` in |
@@ -228,6 +227,11 @@ fn check_todo(path: &Path, text: &str) { | |||
228 | return; | 227 | return; |
229 | } | 228 | } |
230 | if text.contains("TODO") || text.contains("TOOD") || text.contains("todo!") { | 229 | if text.contains("TODO") || text.contains("TOOD") || text.contains("todo!") { |
230 | // Generated by an assist | ||
231 | if text.contains("${0:todo!()}") { | ||
232 | return; | ||
233 | } | ||
234 | |||
231 | panic!( | 235 | panic!( |
232 | "\nTODO markers or todo! macros should not be committed to the master branch,\n\ | 236 | "\nTODO markers or todo! macros should not be committed to the master branch,\n\ |
233 | use FIXME instead\n\ | 237 | use FIXME instead\n\ |