diff options
81 files changed, 3051 insertions, 2433 deletions
diff --git a/Cargo.lock b/Cargo.lock index e724c1a76..b63b5ae13 100644 --- a/Cargo.lock +++ b/Cargo.lock | |||
@@ -10,7 +10,7 @@ dependencies = [ | |||
10 | 10 | ||
11 | [[package]] | 11 | [[package]] |
12 | name = "anyhow" | 12 | name = "anyhow" |
13 | version = "1.0.25" | 13 | version = "1.0.26" |
14 | source = "registry+https://github.com/rust-lang/crates.io-index" | 14 | source = "registry+https://github.com/rust-lang/crates.io-index" |
15 | 15 | ||
16 | [[package]] | 16 | [[package]] |
@@ -66,6 +66,11 @@ dependencies = [ | |||
66 | ] | 66 | ] |
67 | 67 | ||
68 | [[package]] | 68 | [[package]] |
69 | name = "base64" | ||
70 | version = "0.11.0" | ||
71 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
72 | |||
73 | [[package]] | ||
69 | name = "bit-set" | 74 | name = "bit-set" |
70 | version = "0.5.1" | 75 | version = "0.5.1" |
71 | source = "registry+https://github.com/rust-lang/crates.io-index" | 76 | source = "registry+https://github.com/rust-lang/crates.io-index" |
@@ -232,7 +237,7 @@ dependencies = [ | |||
232 | "crossbeam-channel 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", | 237 | "crossbeam-channel 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", |
233 | "crossbeam-deque 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", | 238 | "crossbeam-deque 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", |
234 | "crossbeam-epoch 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", | 239 | "crossbeam-epoch 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", |
235 | "crossbeam-queue 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", | 240 | "crossbeam-queue 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", |
236 | "crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", | 241 | "crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", |
237 | ] | 242 | ] |
238 | 243 | ||
@@ -268,9 +273,10 @@ dependencies = [ | |||
268 | 273 | ||
269 | [[package]] | 274 | [[package]] |
270 | name = "crossbeam-queue" | 275 | name = "crossbeam-queue" |
271 | version = "0.2.0" | 276 | version = "0.2.1" |
272 | source = "registry+https://github.com/rust-lang/crates.io-index" | 277 | source = "registry+https://github.com/rust-lang/crates.io-index" |
273 | dependencies = [ | 278 | dependencies = [ |
279 | "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", | ||
274 | "crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", | 280 | "crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", |
275 | ] | 281 | ] |
276 | 282 | ||
@@ -620,9 +626,10 @@ dependencies = [ | |||
620 | 626 | ||
621 | [[package]] | 627 | [[package]] |
622 | name = "lsp-types" | 628 | name = "lsp-types" |
623 | version = "0.66.0" | 629 | version = "0.67.1" |
624 | source = "registry+https://github.com/rust-lang/crates.io-index" | 630 | source = "registry+https://github.com/rust-lang/crates.io-index" |
625 | dependencies = [ | 631 | dependencies = [ |
632 | "base64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||
626 | "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", | 633 | "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", |
627 | "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", | 634 | "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", |
628 | "serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)", | 635 | "serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)", |
@@ -892,6 +899,20 @@ dependencies = [ | |||
892 | ] | 899 | ] |
893 | 900 | ||
894 | [[package]] | 901 | [[package]] |
902 | name = "ra_cargo_watch" | ||
903 | version = "0.1.0" | ||
904 | dependencies = [ | ||
905 | "cargo_metadata 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", | ||
906 | "crossbeam-channel 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||
907 | "insta 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||
908 | "jod-thread 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||
909 | "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", | ||
910 | "lsp-types 0.67.1 (registry+https://github.com/rust-lang/crates.io-index)", | ||
911 | "parking_lot 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||
912 | "serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)", | ||
913 | ] | ||
914 | |||
915 | [[package]] | ||
895 | name = "ra_cfg" | 916 | name = "ra_cfg" |
896 | version = "0.1.0" | 917 | version = "0.1.0" |
897 | dependencies = [ | 918 | dependencies = [ |
@@ -1034,7 +1055,7 @@ dependencies = [ | |||
1034 | "ra_syntax 0.1.0", | 1055 | "ra_syntax 0.1.0", |
1035 | "ra_text_edit 0.1.0", | 1056 | "ra_text_edit 0.1.0", |
1036 | "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", | 1057 | "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", |
1037 | "rayon 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", | 1058 | "rayon 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", |
1038 | "rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", | 1059 | "rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", |
1039 | "superslice 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", | 1060 | "superslice 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", |
1040 | "test_utils 0.1.0", | 1061 | "test_utils 0.1.0", |
@@ -1050,8 +1071,9 @@ dependencies = [ | |||
1050 | "jod-thread 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", | 1071 | "jod-thread 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", |
1051 | "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", | 1072 | "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", |
1052 | "lsp-server 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", | 1073 | "lsp-server 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", |
1053 | "lsp-types 0.66.0 (registry+https://github.com/rust-lang/crates.io-index)", | 1074 | "lsp-types 0.67.1 (registry+https://github.com/rust-lang/crates.io-index)", |
1054 | "parking_lot 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", | 1075 | "parking_lot 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", |
1076 | "ra_cargo_watch 0.1.0", | ||
1055 | "ra_ide 0.1.0", | 1077 | "ra_ide 0.1.0", |
1056 | "ra_prof 0.1.0", | 1078 | "ra_prof 0.1.0", |
1057 | "ra_project_model 0.1.0", | 1079 | "ra_project_model 0.1.0", |
@@ -1314,21 +1336,21 @@ dependencies = [ | |||
1314 | 1336 | ||
1315 | [[package]] | 1337 | [[package]] |
1316 | name = "rayon" | 1338 | name = "rayon" |
1317 | version = "1.2.1" | 1339 | version = "1.3.0" |
1318 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1340 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1319 | dependencies = [ | 1341 | dependencies = [ |
1320 | "crossbeam-deque 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", | 1342 | "crossbeam-deque 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", |
1321 | "either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)", | 1343 | "either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)", |
1322 | "rayon-core 1.6.1 (registry+https://github.com/rust-lang/crates.io-index)", | 1344 | "rayon-core 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", |
1323 | ] | 1345 | ] |
1324 | 1346 | ||
1325 | [[package]] | 1347 | [[package]] |
1326 | name = "rayon-core" | 1348 | name = "rayon-core" |
1327 | version = "1.6.1" | 1349 | version = "1.7.0" |
1328 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1350 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1329 | dependencies = [ | 1351 | dependencies = [ |
1330 | "crossbeam-deque 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", | 1352 | "crossbeam-deque 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", |
1331 | "crossbeam-queue 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", | 1353 | "crossbeam-queue 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", |
1332 | "crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", | 1354 | "crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", |
1333 | "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", | 1355 | "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", |
1334 | "num_cpus 1.11.1 (registry+https://github.com/rust-lang/crates.io-index)", | 1356 | "num_cpus 1.11.1 (registry+https://github.com/rust-lang/crates.io-index)", |
@@ -1746,7 +1768,7 @@ dependencies = [ | |||
1746 | name = "xtask" | 1768 | name = "xtask" |
1747 | version = "0.1.0" | 1769 | version = "0.1.0" |
1748 | dependencies = [ | 1770 | dependencies = [ |
1749 | "anyhow 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)", | 1771 | "anyhow 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", |
1750 | "pico-args 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", | 1772 | "pico-args 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", |
1751 | "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", | 1773 | "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", |
1752 | "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", | 1774 | "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", |
@@ -1765,7 +1787,7 @@ dependencies = [ | |||
1765 | 1787 | ||
1766 | [metadata] | 1788 | [metadata] |
1767 | "checksum aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "58fb5e95d83b38284460a5fda7d6470aa0b8844d283a0b614b8535e880800d2d" | 1789 | "checksum aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "58fb5e95d83b38284460a5fda7d6470aa0b8844d283a0b614b8535e880800d2d" |
1768 | "checksum anyhow 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)" = "9267dff192e68f3399525901e709a48c1d3982c9c072fa32f2127a0cb0babf14" | 1790 | "checksum anyhow 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)" = "7825f6833612eb2414095684fcf6c635becf3ce97fe48cf6421321e93bfbd53c" |
1769 | "checksum anymap 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "33954243bd79057c2de7338850b85983a44588021f8a5fee574a8888c6de4344" | 1791 | "checksum anymap 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "33954243bd79057c2de7338850b85983a44588021f8a5fee574a8888c6de4344" |
1770 | "checksum arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8" | 1792 | "checksum arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8" |
1771 | "checksum atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "1803c647a3ec87095e7ae7acfca019e98de5ec9a7d01343f611cf3152ed71a90" | 1793 | "checksum atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "1803c647a3ec87095e7ae7acfca019e98de5ec9a7d01343f611cf3152ed71a90" |
@@ -1773,6 +1795,7 @@ dependencies = [ | |||
1773 | "checksum backtrace 0.3.40 (registry+https://github.com/rust-lang/crates.io-index)" = "924c76597f0d9ca25d762c25a4d369d51267536465dc5064bdf0eb073ed477ea" | 1795 | "checksum backtrace 0.3.40 (registry+https://github.com/rust-lang/crates.io-index)" = "924c76597f0d9ca25d762c25a4d369d51267536465dc5064bdf0eb073ed477ea" |
1774 | "checksum backtrace-sys 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)" = "5d6575f128516de27e3ce99689419835fce9643a9b215a14d2b5b685be018491" | 1796 | "checksum backtrace-sys 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)" = "5d6575f128516de27e3ce99689419835fce9643a9b215a14d2b5b685be018491" |
1775 | "checksum base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e" | 1797 | "checksum base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e" |
1798 | "checksum base64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7" | ||
1776 | "checksum bit-set 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e84c238982c4b1e1ee668d136c510c67a13465279c0cb367ea6baf6310620a80" | 1799 | "checksum bit-set 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e84c238982c4b1e1ee668d136c510c67a13465279c0cb367ea6baf6310620a80" |
1777 | "checksum bit-vec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f59bbe95d4e52a6398ec21238d31577f2b28a9d86807f06ca59d191d8440d0bb" | 1800 | "checksum bit-vec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f59bbe95d4e52a6398ec21238d31577f2b28a9d86807f06ca59d191d8440d0bb" |
1778 | "checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" | 1801 | "checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" |
@@ -1795,7 +1818,7 @@ dependencies = [ | |||
1795 | "checksum crossbeam-channel 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "acec9a3b0b3559f15aee4f90746c4e5e293b701c0f7d3925d24e01645267b68c" | 1818 | "checksum crossbeam-channel 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "acec9a3b0b3559f15aee4f90746c4e5e293b701c0f7d3925d24e01645267b68c" |
1796 | "checksum crossbeam-deque 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c3aa945d63861bfe624b55d153a39684da1e8c0bc8fba932f7ee3a3c16cea3ca" | 1819 | "checksum crossbeam-deque 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c3aa945d63861bfe624b55d153a39684da1e8c0bc8fba932f7ee3a3c16cea3ca" |
1797 | "checksum crossbeam-epoch 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5064ebdbf05ce3cb95e45c8b086f72263f4166b29b97f6baff7ef7fe047b55ac" | 1820 | "checksum crossbeam-epoch 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5064ebdbf05ce3cb95e45c8b086f72263f4166b29b97f6baff7ef7fe047b55ac" |
1798 | "checksum crossbeam-queue 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "dfd6515864a82d2f877b42813d4553292c6659498c9a2aa31bab5a15243c2700" | 1821 | "checksum crossbeam-queue 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c695eeca1e7173472a32221542ae469b3e9aac3a4fc81f7696bcad82029493db" |
1799 | "checksum crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ce446db02cdc3165b94ae73111e570793400d0794e46125cc4056c81cbb039f4" | 1822 | "checksum crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ce446db02cdc3165b94ae73111e570793400d0794e46125cc4056c81cbb039f4" |
1800 | "checksum difference 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198" | 1823 | "checksum difference 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198" |
1801 | "checksum drop_bomb 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "69b26e475fd29098530e709294e94e661974c851aed42512793f120fed4e199f" | 1824 | "checksum drop_bomb 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "69b26e475fd29098530e709294e94e661974c851aed42512793f120fed4e199f" |
@@ -1842,7 +1865,7 @@ dependencies = [ | |||
1842 | "checksum lock_api 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e57b3997725d2b60dbec1297f6c2e2957cc383db1cebd6be812163f969c7d586" | 1865 | "checksum lock_api 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e57b3997725d2b60dbec1297f6c2e2957cc383db1cebd6be812163f969c7d586" |
1843 | "checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" | 1866 | "checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" |
1844 | "checksum lsp-server 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0ba36405bd742139ab79c246ca5adb7fde2fe1a0f495e2c8e2f607b607dedb12" | 1867 | "checksum lsp-server 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0ba36405bd742139ab79c246ca5adb7fde2fe1a0f495e2c8e2f607b607dedb12" |
1845 | "checksum lsp-types 0.66.0 (registry+https://github.com/rust-lang/crates.io-index)" = "49a2dddfe2791cbf4b5eff5a581e45becf47a24b128a62de80e7cc135bf50064" | 1868 | "checksum lsp-types 0.67.1 (registry+https://github.com/rust-lang/crates.io-index)" = "aea9639ebf210bd5de66931cbdb2d4a8bcc1fa1e5b2dece7daa6b387ab42e803" |
1846 | "checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" | 1869 | "checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" |
1847 | "checksum memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e" | 1870 | "checksum memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e" |
1848 | "checksum memoffset 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "75189eb85871ea5c2e2c15abbdd541185f63b408415e5051f5cac122d8c774b9" | 1871 | "checksum memoffset 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "75189eb85871ea5c2e2c15abbdd541185f63b408415e5051f5cac122d8c774b9" |
@@ -1884,8 +1907,8 @@ dependencies = [ | |||
1884 | "checksum rand_pcg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44" | 1907 | "checksum rand_pcg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44" |
1885 | "checksum rand_pcg 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429" | 1908 | "checksum rand_pcg 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429" |
1886 | "checksum rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" | 1909 | "checksum rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" |
1887 | "checksum rayon 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "43739f8831493b276363637423d3622d4bd6394ab6f0a9c4a552e208aeb7fddd" | 1910 | "checksum rayon 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "db6ce3297f9c85e16621bb8cca38a06779ffc31bb8184e1be4bed2be4678a098" |
1888 | "checksum rayon-core 1.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f8bf17de6f23b05473c437eb958b9c850bfc8af0961fe17b4cc92d5a627b4791" | 1911 | "checksum rayon-core 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "08a89b46efaf957e52b18062fb2f4660f8b8a4dde1807ca002690868ef2c85a9" |
1889 | "checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" | 1912 | "checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" |
1890 | "checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" | 1913 | "checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" |
1891 | "checksum regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dc220bd33bdce8f093101afe22a037b8eb0e5af33592e6a9caafff0d4cb81cbd" | 1914 | "checksum regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dc220bd33bdce8f093101afe22a037b8eb0e5af33592e6a9caafff0d4cb81cbd" |
diff --git a/crates/ra_assists/src/assist_ctx.rs b/crates/ra_assists/src/assist_ctx.rs index 993aebc47..28152f724 100644 --- a/crates/ra_assists/src/assist_ctx.rs +++ b/crates/ra_assists/src/assist_ctx.rs | |||
@@ -46,7 +46,7 @@ pub(crate) enum Assist { | |||
46 | /// | 46 | /// |
47 | /// Note, however, that we don't actually use such two-phase logic at the | 47 | /// Note, however, that we don't actually use such two-phase logic at the |
48 | /// moment, because the LSP API is pretty awkward in this place, and it's much | 48 | /// moment, because the LSP API is pretty awkward in this place, and it's much |
49 | /// easier to just compute the edit eagerly :-)#[derive(Debug, Clone)] | 49 | /// easier to just compute the edit eagerly :-) |
50 | #[derive(Debug)] | 50 | #[derive(Debug)] |
51 | pub(crate) struct AssistCtx<'a, DB> { | 51 | pub(crate) struct AssistCtx<'a, DB> { |
52 | pub(crate) db: &'a DB, | 52 | pub(crate) db: &'a DB, |
diff --git a/crates/ra_assists/src/assists/add_explicit_type.rs b/crates/ra_assists/src/assists/add_explicit_type.rs index eeb4ff39f..2c602a79e 100644 --- a/crates/ra_assists/src/assists/add_explicit_type.rs +++ b/crates/ra_assists/src/assists/add_explicit_type.rs | |||
@@ -74,6 +74,24 @@ mod tests { | |||
74 | } | 74 | } |
75 | 75 | ||
76 | #[test] | 76 | #[test] |
77 | fn add_explicit_type_works_for_macro_call() { | ||
78 | check_assist( | ||
79 | add_explicit_type, | ||
80 | "macro_rules! v { () => {0u64} } fn f() { let a<|> = v!(); }", | ||
81 | "macro_rules! v { () => {0u64} } fn f() { let a<|>: u64 = v!(); }", | ||
82 | ); | ||
83 | } | ||
84 | |||
85 | #[test] | ||
86 | fn add_explicit_type_works_for_macro_call_recursive() { | ||
87 | check_assist( | ||
88 | add_explicit_type, | ||
89 | "macro_rules! u { () => {0u64} } macro_rules! v { () => {u!()} } fn f() { let a<|> = v!(); }", | ||
90 | "macro_rules! u { () => {0u64} } macro_rules! v { () => {u!()} } fn f() { let a<|>: u64 = v!(); }", | ||
91 | ); | ||
92 | } | ||
93 | |||
94 | #[test] | ||
77 | fn add_explicit_type_not_applicable_if_ty_not_inferred() { | 95 | fn add_explicit_type_not_applicable_if_ty_not_inferred() { |
78 | check_assist_not_applicable(add_explicit_type, "fn f() { let a<|> = None; }"); | 96 | check_assist_not_applicable(add_explicit_type, "fn f() { let a<|> = None; }"); |
79 | } | 97 | } |
diff --git a/crates/ra_assists/src/assists/add_missing_impl_members.rs b/crates/ra_assists/src/assists/add_missing_impl_members.rs index cef669cb5..bc49e71fe 100644 --- a/crates/ra_assists/src/assists/add_missing_impl_members.rs +++ b/crates/ra_assists/src/assists/add_missing_impl_members.rs | |||
@@ -1,3 +1,5 @@ | |||
1 | use std::collections::HashMap; | ||
2 | |||
1 | use hir::{db::HirDatabase, HasSource}; | 3 | use hir::{db::HirDatabase, HasSource}; |
2 | use ra_syntax::{ | 4 | use ra_syntax::{ |
3 | ast::{self, edit, make, AstNode, NameOwner}, | 5 | ast::{self, edit, make, AstNode, NameOwner}, |
@@ -17,26 +19,26 @@ enum AddMissingImplMembersMode { | |||
17 | // Adds scaffold for required impl members. | 19 | // Adds scaffold for required impl members. |
18 | // | 20 | // |
19 | // ``` | 21 | // ``` |
20 | // trait T { | 22 | // trait Trait<T> { |
21 | // Type X; | 23 | // Type X; |
22 | // fn foo(&self); | 24 | // fn foo(&self) -> T; |
23 | // fn bar(&self) {} | 25 | // fn bar(&self) {} |
24 | // } | 26 | // } |
25 | // | 27 | // |
26 | // impl T for () {<|> | 28 | // impl Trait<u32> for () {<|> |
27 | // | 29 | // |
28 | // } | 30 | // } |
29 | // ``` | 31 | // ``` |
30 | // -> | 32 | // -> |
31 | // ``` | 33 | // ``` |
32 | // trait T { | 34 | // trait Trait<T> { |
33 | // Type X; | 35 | // Type X; |
34 | // fn foo(&self); | 36 | // fn foo(&self) -> T; |
35 | // fn bar(&self) {} | 37 | // fn bar(&self) {} |
36 | // } | 38 | // } |
37 | // | 39 | // |
38 | // impl T for () { | 40 | // impl Trait<u32> for () { |
39 | // fn foo(&self) { unimplemented!() } | 41 | // fn foo(&self) -> u32 { unimplemented!() } |
40 | // | 42 | // |
41 | // } | 43 | // } |
42 | // ``` | 44 | // ``` |
@@ -54,13 +56,13 @@ pub(crate) fn add_missing_impl_members(ctx: AssistCtx<impl HirDatabase>) -> Opti | |||
54 | // Adds scaffold for overriding default impl members. | 56 | // Adds scaffold for overriding default impl members. |
55 | // | 57 | // |
56 | // ``` | 58 | // ``` |
57 | // trait T { | 59 | // trait Trait { |
58 | // Type X; | 60 | // Type X; |
59 | // fn foo(&self); | 61 | // fn foo(&self); |
60 | // fn bar(&self) {} | 62 | // fn bar(&self) {} |
61 | // } | 63 | // } |
62 | // | 64 | // |
63 | // impl T for () { | 65 | // impl Trait for () { |
64 | // Type X = (); | 66 | // Type X = (); |
65 | // fn foo(&self) {}<|> | 67 | // fn foo(&self) {}<|> |
66 | // | 68 | // |
@@ -68,13 +70,13 @@ pub(crate) fn add_missing_impl_members(ctx: AssistCtx<impl HirDatabase>) -> Opti | |||
68 | // ``` | 70 | // ``` |
69 | // -> | 71 | // -> |
70 | // ``` | 72 | // ``` |
71 | // trait T { | 73 | // trait Trait { |
72 | // Type X; | 74 | // Type X; |
73 | // fn foo(&self); | 75 | // fn foo(&self); |
74 | // fn bar(&self) {} | 76 | // fn bar(&self) {} |
75 | // } | 77 | // } |
76 | // | 78 | // |
77 | // impl T for () { | 79 | // impl Trait for () { |
78 | // Type X = (); | 80 | // Type X = (); |
79 | // fn foo(&self) {} | 81 | // fn foo(&self) {} |
80 | // fn bar(&self) {} | 82 | // fn bar(&self) {} |
@@ -99,7 +101,7 @@ fn add_missing_impl_members_inner( | |||
99 | let impl_node = ctx.find_node_at_offset::<ast::ImplBlock>()?; | 101 | let impl_node = ctx.find_node_at_offset::<ast::ImplBlock>()?; |
100 | let impl_item_list = impl_node.item_list()?; | 102 | let impl_item_list = impl_node.item_list()?; |
101 | 103 | ||
102 | let trait_def = { | 104 | let (trait_, trait_def) = { |
103 | let analyzer = ctx.source_analyzer(impl_node.syntax(), None); | 105 | let analyzer = ctx.source_analyzer(impl_node.syntax(), None); |
104 | 106 | ||
105 | resolve_target_trait_def(ctx.db, &analyzer, &impl_node)? | 107 | resolve_target_trait_def(ctx.db, &analyzer, &impl_node)? |
@@ -132,10 +134,25 @@ fn add_missing_impl_members_inner( | |||
132 | return None; | 134 | return None; |
133 | } | 135 | } |
134 | 136 | ||
137 | let file_id = ctx.frange.file_id; | ||
138 | let db = ctx.db; | ||
139 | |||
135 | ctx.add_assist(AssistId(assist_id), label, |edit| { | 140 | ctx.add_assist(AssistId(assist_id), label, |edit| { |
136 | let n_existing_items = impl_item_list.impl_items().count(); | 141 | let n_existing_items = impl_item_list.impl_items().count(); |
142 | let substs = get_syntactic_substs(impl_node).unwrap_or_default(); | ||
143 | let generic_def: hir::GenericDef = trait_.into(); | ||
144 | let substs_by_param: HashMap<_, _> = generic_def | ||
145 | .params(db) | ||
146 | .into_iter() | ||
147 | // this is a trait impl, so we need to skip the first type parameter -- this is a bit hacky | ||
148 | .skip(1) | ||
149 | .zip(substs.into_iter()) | ||
150 | .collect(); | ||
137 | let items = missing_items | 151 | let items = missing_items |
138 | .into_iter() | 152 | .into_iter() |
153 | .map(|it| { | ||
154 | substitute_type_params(db, hir::InFile::new(file_id.into(), it), &substs_by_param) | ||
155 | }) | ||
139 | .map(|it| match it { | 156 | .map(|it| match it { |
140 | ast::ImplItem::FnDef(def) => ast::ImplItem::FnDef(add_body(def)), | 157 | ast::ImplItem::FnDef(def) => ast::ImplItem::FnDef(add_body(def)), |
141 | _ => it, | 158 | _ => it, |
@@ -160,13 +177,63 @@ fn add_body(fn_def: ast::FnDef) -> ast::FnDef { | |||
160 | } | 177 | } |
161 | } | 178 | } |
162 | 179 | ||
180 | // FIXME: It would probably be nicer if we could get this via HIR (i.e. get the | ||
181 | // trait ref, and then go from the types in the substs back to the syntax) | ||
182 | // FIXME: This should be a general utility (not even just for assists) | ||
183 | fn get_syntactic_substs(impl_block: ast::ImplBlock) -> Option<Vec<ast::TypeRef>> { | ||
184 | let target_trait = impl_block.target_trait()?; | ||
185 | let path_type = match target_trait { | ||
186 | ast::TypeRef::PathType(path) => path, | ||
187 | _ => return None, | ||
188 | }; | ||
189 | let type_arg_list = path_type.path()?.segment()?.type_arg_list()?; | ||
190 | let mut result = Vec::new(); | ||
191 | for type_arg in type_arg_list.type_args() { | ||
192 | let type_arg: ast::TypeArg = type_arg; | ||
193 | result.push(type_arg.type_ref()?); | ||
194 | } | ||
195 | Some(result) | ||
196 | } | ||
197 | |||
198 | // FIXME: This should be a general utility (not even just for assists) | ||
199 | fn substitute_type_params<N: AstNode>( | ||
200 | db: &impl HirDatabase, | ||
201 | node: hir::InFile<N>, | ||
202 | substs: &HashMap<hir::TypeParam, ast::TypeRef>, | ||
203 | ) -> N { | ||
204 | let type_param_replacements = node | ||
205 | .value | ||
206 | .syntax() | ||
207 | .descendants() | ||
208 | .filter_map(ast::TypeRef::cast) | ||
209 | .filter_map(|n| { | ||
210 | let path = match &n { | ||
211 | ast::TypeRef::PathType(path_type) => path_type.path()?, | ||
212 | _ => return None, | ||
213 | }; | ||
214 | let analyzer = hir::SourceAnalyzer::new(db, node.with_value(n.syntax()), None); | ||
215 | let resolution = analyzer.resolve_path(db, &path)?; | ||
216 | match resolution { | ||
217 | hir::PathResolution::TypeParam(tp) => Some((n, substs.get(&tp)?.clone())), | ||
218 | _ => None, | ||
219 | } | ||
220 | }) | ||
221 | .collect::<Vec<_>>(); | ||
222 | |||
223 | if type_param_replacements.is_empty() { | ||
224 | node.value | ||
225 | } else { | ||
226 | edit::replace_descendants(&node.value, type_param_replacements.into_iter()) | ||
227 | } | ||
228 | } | ||
229 | |||
163 | /// Given an `ast::ImplBlock`, resolves the target trait (the one being | 230 | /// Given an `ast::ImplBlock`, resolves the target trait (the one being |
164 | /// implemented) to a `ast::TraitDef`. | 231 | /// implemented) to a `ast::TraitDef`. |
165 | fn resolve_target_trait_def( | 232 | fn resolve_target_trait_def( |
166 | db: &impl HirDatabase, | 233 | db: &impl HirDatabase, |
167 | analyzer: &hir::SourceAnalyzer, | 234 | analyzer: &hir::SourceAnalyzer, |
168 | impl_block: &ast::ImplBlock, | 235 | impl_block: &ast::ImplBlock, |
169 | ) -> Option<ast::TraitDef> { | 236 | ) -> Option<(hir::Trait, ast::TraitDef)> { |
170 | let ast_path = impl_block | 237 | let ast_path = impl_block |
171 | .target_trait() | 238 | .target_trait() |
172 | .map(|it| it.syntax().clone()) | 239 | .map(|it| it.syntax().clone()) |
@@ -174,7 +241,9 @@ fn resolve_target_trait_def( | |||
174 | .path()?; | 241 | .path()?; |
175 | 242 | ||
176 | match analyzer.resolve_path(db, &ast_path) { | 243 | match analyzer.resolve_path(db, &ast_path) { |
177 | Some(hir::PathResolution::Def(hir::ModuleDef::Trait(def))) => Some(def.source(db).value), | 244 | Some(hir::PathResolution::Def(hir::ModuleDef::Trait(def))) => { |
245 | Some((def, def.source(db).value)) | ||
246 | } | ||
178 | _ => None, | 247 | _ => None, |
179 | } | 248 | } |
180 | } | 249 | } |
@@ -281,6 +350,40 @@ impl Foo for S { | |||
281 | } | 350 | } |
282 | 351 | ||
283 | #[test] | 352 | #[test] |
353 | fn fill_in_type_params_1() { | ||
354 | check_assist( | ||
355 | add_missing_impl_members, | ||
356 | " | ||
357 | trait Foo<T> { fn foo(&self, t: T) -> &T; } | ||
358 | struct S; | ||
359 | impl Foo<u32> for S { <|> }", | ||
360 | " | ||
361 | trait Foo<T> { fn foo(&self, t: T) -> &T; } | ||
362 | struct S; | ||
363 | impl Foo<u32> for S { | ||
364 | <|>fn foo(&self, t: u32) -> &u32 { unimplemented!() } | ||
365 | }", | ||
366 | ); | ||
367 | } | ||
368 | |||
369 | #[test] | ||
370 | fn fill_in_type_params_2() { | ||
371 | check_assist( | ||
372 | add_missing_impl_members, | ||
373 | " | ||
374 | trait Foo<T> { fn foo(&self, t: T) -> &T; } | ||
375 | struct S; | ||
376 | impl<U> Foo<U> for S { <|> }", | ||
377 | " | ||
378 | trait Foo<T> { fn foo(&self, t: T) -> &T; } | ||
379 | struct S; | ||
380 | impl<U> Foo<U> for S { | ||
381 | <|>fn foo(&self, t: U) -> &U { unimplemented!() } | ||
382 | }", | ||
383 | ); | ||
384 | } | ||
385 | |||
386 | #[test] | ||
284 | fn test_cursor_after_empty_impl_block() { | 387 | fn test_cursor_after_empty_impl_block() { |
285 | check_assist( | 388 | check_assist( |
286 | add_missing_impl_members, | 389 | add_missing_impl_members, |
diff --git a/crates/ra_assists/src/doc_tests/generated.rs b/crates/ra_assists/src/doc_tests/generated.rs index 4586eeb59..7d84dc8fb 100644 --- a/crates/ra_assists/src/doc_tests/generated.rs +++ b/crates/ra_assists/src/doc_tests/generated.rs | |||
@@ -101,26 +101,26 @@ fn doctest_add_impl_default_members() { | |||
101 | check( | 101 | check( |
102 | "add_impl_default_members", | 102 | "add_impl_default_members", |
103 | r#####" | 103 | r#####" |
104 | trait T { | 104 | trait Trait { |
105 | Type X; | 105 | Type X; |
106 | fn foo(&self); | 106 | fn foo(&self); |
107 | fn bar(&self) {} | 107 | fn bar(&self) {} |
108 | } | 108 | } |
109 | 109 | ||
110 | impl T for () { | 110 | impl Trait for () { |
111 | Type X = (); | 111 | Type X = (); |
112 | fn foo(&self) {}<|> | 112 | fn foo(&self) {}<|> |
113 | 113 | ||
114 | } | 114 | } |
115 | "#####, | 115 | "#####, |
116 | r#####" | 116 | r#####" |
117 | trait T { | 117 | trait Trait { |
118 | Type X; | 118 | Type X; |
119 | fn foo(&self); | 119 | fn foo(&self); |
120 | fn bar(&self) {} | 120 | fn bar(&self) {} |
121 | } | 121 | } |
122 | 122 | ||
123 | impl T for () { | 123 | impl Trait for () { |
124 | Type X = (); | 124 | Type X = (); |
125 | fn foo(&self) {} | 125 | fn foo(&self) {} |
126 | fn bar(&self) {} | 126 | fn bar(&self) {} |
@@ -135,25 +135,25 @@ fn doctest_add_impl_missing_members() { | |||
135 | check( | 135 | check( |
136 | "add_impl_missing_members", | 136 | "add_impl_missing_members", |
137 | r#####" | 137 | r#####" |
138 | trait T { | 138 | trait Trait<T> { |
139 | Type X; | 139 | Type X; |
140 | fn foo(&self); | 140 | fn foo(&self) -> T; |
141 | fn bar(&self) {} | 141 | fn bar(&self) {} |
142 | } | 142 | } |
143 | 143 | ||
144 | impl T for () {<|> | 144 | impl Trait<u32> for () {<|> |
145 | 145 | ||
146 | } | 146 | } |
147 | "#####, | 147 | "#####, |
148 | r#####" | 148 | r#####" |
149 | trait T { | 149 | trait Trait<T> { |
150 | Type X; | 150 | Type X; |
151 | fn foo(&self); | 151 | fn foo(&self) -> T; |
152 | fn bar(&self) {} | 152 | fn bar(&self) {} |
153 | } | 153 | } |
154 | 154 | ||
155 | impl T for () { | 155 | impl Trait<u32> for () { |
156 | fn foo(&self) { unimplemented!() } | 156 | fn foo(&self) -> u32 { unimplemented!() } |
157 | 157 | ||
158 | } | 158 | } |
159 | "#####, | 159 | "#####, |
diff --git a/crates/ra_cargo_watch/Cargo.toml b/crates/ra_cargo_watch/Cargo.toml new file mode 100644 index 000000000..bcc4648ff --- /dev/null +++ b/crates/ra_cargo_watch/Cargo.toml | |||
@@ -0,0 +1,17 @@ | |||
1 | [package] | ||
2 | edition = "2018" | ||
3 | name = "ra_cargo_watch" | ||
4 | version = "0.1.0" | ||
5 | authors = ["rust-analyzer developers"] | ||
6 | |||
7 | [dependencies] | ||
8 | crossbeam-channel = "0.4" | ||
9 | lsp-types = { version = "0.67.0", features = ["proposed"] } | ||
10 | log = "0.4.3" | ||
11 | cargo_metadata = "0.9.1" | ||
12 | jod-thread = "0.1.0" | ||
13 | parking_lot = "0.10.0" | ||
14 | |||
15 | [dev-dependencies] | ||
16 | insta = "0.12.0" | ||
17 | serde_json = "1.0" \ No newline at end of file | ||
diff --git a/crates/ra_cargo_watch/src/conv.rs b/crates/ra_cargo_watch/src/conv.rs new file mode 100644 index 000000000..3bd4bf7a5 --- /dev/null +++ b/crates/ra_cargo_watch/src/conv.rs | |||
@@ -0,0 +1,280 @@ | |||
1 | //! This module provides the functionality needed to convert diagnostics from | ||
2 | //! `cargo check` json format to the LSP diagnostic format. | ||
3 | use cargo_metadata::diagnostic::{ | ||
4 | Applicability, Diagnostic as RustDiagnostic, DiagnosticLevel, DiagnosticSpan, | ||
5 | DiagnosticSpanMacroExpansion, | ||
6 | }; | ||
7 | use lsp_types::{ | ||
8 | Diagnostic, DiagnosticRelatedInformation, DiagnosticSeverity, DiagnosticTag, Location, | ||
9 | NumberOrString, Position, Range, Url, | ||
10 | }; | ||
11 | use std::{fmt::Write, path::PathBuf}; | ||
12 | |||
13 | #[cfg(test)] | ||
14 | mod test; | ||
15 | |||
16 | /// Converts a Rust level string to a LSP severity | ||
17 | fn map_level_to_severity(val: DiagnosticLevel) -> Option<DiagnosticSeverity> { | ||
18 | match val { | ||
19 | DiagnosticLevel::Ice => Some(DiagnosticSeverity::Error), | ||
20 | DiagnosticLevel::Error => Some(DiagnosticSeverity::Error), | ||
21 | DiagnosticLevel::Warning => Some(DiagnosticSeverity::Warning), | ||
22 | DiagnosticLevel::Note => Some(DiagnosticSeverity::Information), | ||
23 | DiagnosticLevel::Help => Some(DiagnosticSeverity::Hint), | ||
24 | DiagnosticLevel::Unknown => None, | ||
25 | } | ||
26 | } | ||
27 | |||
28 | /// Check whether a file name is from macro invocation | ||
29 | fn is_from_macro(file_name: &str) -> bool { | ||
30 | file_name.starts_with('<') && file_name.ends_with('>') | ||
31 | } | ||
32 | |||
33 | /// Converts a Rust macro span to a LSP location recursively | ||
34 | fn map_macro_span_to_location( | ||
35 | span_macro: &DiagnosticSpanMacroExpansion, | ||
36 | workspace_root: &PathBuf, | ||
37 | ) -> Option<Location> { | ||
38 | if !is_from_macro(&span_macro.span.file_name) { | ||
39 | return Some(map_span_to_location(&span_macro.span, workspace_root)); | ||
40 | } | ||
41 | |||
42 | if let Some(expansion) = &span_macro.span.expansion { | ||
43 | return map_macro_span_to_location(&expansion, workspace_root); | ||
44 | } | ||
45 | |||
46 | None | ||
47 | } | ||
48 | |||
49 | /// Converts a Rust span to a LSP location | ||
50 | fn map_span_to_location(span: &DiagnosticSpan, workspace_root: &PathBuf) -> Location { | ||
51 | if is_from_macro(&span.file_name) && span.expansion.is_some() { | ||
52 | let expansion = span.expansion.as_ref().unwrap(); | ||
53 | if let Some(macro_range) = map_macro_span_to_location(&expansion, workspace_root) { | ||
54 | return macro_range; | ||
55 | } | ||
56 | } | ||
57 | |||
58 | let mut file_name = workspace_root.clone(); | ||
59 | file_name.push(&span.file_name); | ||
60 | let uri = Url::from_file_path(file_name).unwrap(); | ||
61 | |||
62 | let range = Range::new( | ||
63 | Position::new(span.line_start as u64 - 1, span.column_start as u64 - 1), | ||
64 | Position::new(span.line_end as u64 - 1, span.column_end as u64 - 1), | ||
65 | ); | ||
66 | |||
67 | Location { uri, range } | ||
68 | } | ||
69 | |||
70 | /// Converts a secondary Rust span to a LSP related information | ||
71 | /// | ||
72 | /// If the span is unlabelled this will return `None`. | ||
73 | fn map_secondary_span_to_related( | ||
74 | span: &DiagnosticSpan, | ||
75 | workspace_root: &PathBuf, | ||
76 | ) -> Option<DiagnosticRelatedInformation> { | ||
77 | if let Some(label) = &span.label { | ||
78 | let location = map_span_to_location(span, workspace_root); | ||
79 | Some(DiagnosticRelatedInformation { location, message: label.clone() }) | ||
80 | } else { | ||
81 | // Nothing to label this with | ||
82 | None | ||
83 | } | ||
84 | } | ||
85 | |||
86 | /// Determines if diagnostic is related to unused code | ||
87 | fn is_unused_or_unnecessary(rd: &RustDiagnostic) -> bool { | ||
88 | if let Some(code) = &rd.code { | ||
89 | match code.code.as_str() { | ||
90 | "dead_code" | "unknown_lints" | "unreachable_code" | "unused_attributes" | ||
91 | | "unused_imports" | "unused_macros" | "unused_variables" => true, | ||
92 | _ => false, | ||
93 | } | ||
94 | } else { | ||
95 | false | ||
96 | } | ||
97 | } | ||
98 | |||
99 | /// Determines if diagnostic is related to deprecated code | ||
100 | fn is_deprecated(rd: &RustDiagnostic) -> bool { | ||
101 | if let Some(code) = &rd.code { | ||
102 | match code.code.as_str() { | ||
103 | "deprecated" => true, | ||
104 | _ => false, | ||
105 | } | ||
106 | } else { | ||
107 | false | ||
108 | } | ||
109 | } | ||
110 | |||
111 | #[derive(Debug)] | ||
112 | pub struct SuggestedFix { | ||
113 | pub title: String, | ||
114 | pub location: Location, | ||
115 | pub replacement: String, | ||
116 | pub applicability: Applicability, | ||
117 | pub diagnostics: Vec<Diagnostic>, | ||
118 | } | ||
119 | |||
120 | impl std::cmp::PartialEq<SuggestedFix> for SuggestedFix { | ||
121 | fn eq(&self, other: &SuggestedFix) -> bool { | ||
122 | if self.title == other.title | ||
123 | && self.location == other.location | ||
124 | && self.replacement == other.replacement | ||
125 | { | ||
126 | // Applicability doesn't impl PartialEq... | ||
127 | match (&self.applicability, &other.applicability) { | ||
128 | (Applicability::MachineApplicable, Applicability::MachineApplicable) => true, | ||
129 | (Applicability::HasPlaceholders, Applicability::HasPlaceholders) => true, | ||
130 | (Applicability::MaybeIncorrect, Applicability::MaybeIncorrect) => true, | ||
131 | (Applicability::Unspecified, Applicability::Unspecified) => true, | ||
132 | _ => false, | ||
133 | } | ||
134 | } else { | ||
135 | false | ||
136 | } | ||
137 | } | ||
138 | } | ||
139 | |||
140 | enum MappedRustChildDiagnostic { | ||
141 | Related(DiagnosticRelatedInformation), | ||
142 | SuggestedFix(SuggestedFix), | ||
143 | MessageLine(String), | ||
144 | } | ||
145 | |||
146 | fn map_rust_child_diagnostic( | ||
147 | rd: &RustDiagnostic, | ||
148 | workspace_root: &PathBuf, | ||
149 | ) -> MappedRustChildDiagnostic { | ||
150 | let span: &DiagnosticSpan = match rd.spans.iter().find(|s| s.is_primary) { | ||
151 | Some(span) => span, | ||
152 | None => { | ||
153 | // `rustc` uses these spanless children as a way to print multi-line | ||
154 | // messages | ||
155 | return MappedRustChildDiagnostic::MessageLine(rd.message.clone()); | ||
156 | } | ||
157 | }; | ||
158 | |||
159 | // If we have a primary span use its location, otherwise use the parent | ||
160 | let location = map_span_to_location(&span, workspace_root); | ||
161 | |||
162 | if let Some(suggested_replacement) = &span.suggested_replacement { | ||
163 | // Include our replacement in the title unless it's empty | ||
164 | let title = if !suggested_replacement.is_empty() { | ||
165 | format!("{}: '{}'", rd.message, suggested_replacement) | ||
166 | } else { | ||
167 | rd.message.clone() | ||
168 | }; | ||
169 | |||
170 | MappedRustChildDiagnostic::SuggestedFix(SuggestedFix { | ||
171 | title, | ||
172 | location, | ||
173 | replacement: suggested_replacement.clone(), | ||
174 | applicability: span.suggestion_applicability.clone().unwrap_or(Applicability::Unknown), | ||
175 | diagnostics: vec![], | ||
176 | }) | ||
177 | } else { | ||
178 | MappedRustChildDiagnostic::Related(DiagnosticRelatedInformation { | ||
179 | location, | ||
180 | message: rd.message.clone(), | ||
181 | }) | ||
182 | } | ||
183 | } | ||
184 | |||
185 | #[derive(Debug)] | ||
186 | pub(crate) struct MappedRustDiagnostic { | ||
187 | pub location: Location, | ||
188 | pub diagnostic: Diagnostic, | ||
189 | pub suggested_fixes: Vec<SuggestedFix>, | ||
190 | } | ||
191 | |||
192 | /// Converts a Rust root diagnostic to LSP form | ||
193 | /// | ||
194 | /// This flattens the Rust diagnostic by: | ||
195 | /// | ||
196 | /// 1. Creating a LSP diagnostic with the root message and primary span. | ||
197 | /// 2. Adding any labelled secondary spans to `relatedInformation` | ||
198 | /// 3. Categorising child diagnostics as either `SuggestedFix`es, | ||
199 | /// `relatedInformation` or additional message lines. | ||
200 | /// | ||
201 | /// If the diagnostic has no primary span this will return `None` | ||
202 | pub(crate) fn map_rust_diagnostic_to_lsp( | ||
203 | rd: &RustDiagnostic, | ||
204 | workspace_root: &PathBuf, | ||
205 | ) -> Option<MappedRustDiagnostic> { | ||
206 | let primary_span = rd.spans.iter().find(|s| s.is_primary)?; | ||
207 | |||
208 | let location = map_span_to_location(&primary_span, workspace_root); | ||
209 | |||
210 | let severity = map_level_to_severity(rd.level); | ||
211 | let mut primary_span_label = primary_span.label.as_ref(); | ||
212 | |||
213 | let mut source = String::from("rustc"); | ||
214 | let mut code = rd.code.as_ref().map(|c| c.code.clone()); | ||
215 | if let Some(code_val) = &code { | ||
216 | // See if this is an RFC #2103 scoped lint (e.g. from Clippy) | ||
217 | let scoped_code: Vec<&str> = code_val.split("::").collect(); | ||
218 | if scoped_code.len() == 2 { | ||
219 | source = String::from(scoped_code[0]); | ||
220 | code = Some(String::from(scoped_code[1])); | ||
221 | } | ||
222 | } | ||
223 | |||
224 | let mut related_information = vec![]; | ||
225 | let mut tags = vec![]; | ||
226 | |||
227 | for secondary_span in rd.spans.iter().filter(|s| !s.is_primary) { | ||
228 | let related = map_secondary_span_to_related(secondary_span, workspace_root); | ||
229 | if let Some(related) = related { | ||
230 | related_information.push(related); | ||
231 | } | ||
232 | } | ||
233 | |||
234 | let mut suggested_fixes = vec![]; | ||
235 | let mut message = rd.message.clone(); | ||
236 | for child in &rd.children { | ||
237 | let child = map_rust_child_diagnostic(&child, workspace_root); | ||
238 | match child { | ||
239 | MappedRustChildDiagnostic::Related(related) => related_information.push(related), | ||
240 | MappedRustChildDiagnostic::SuggestedFix(suggested_fix) => { | ||
241 | suggested_fixes.push(suggested_fix) | ||
242 | } | ||
243 | MappedRustChildDiagnostic::MessageLine(message_line) => { | ||
244 | write!(&mut message, "\n{}", message_line).unwrap(); | ||
245 | |||
246 | // These secondary messages usually duplicate the content of the | ||
247 | // primary span label. | ||
248 | primary_span_label = None; | ||
249 | } | ||
250 | } | ||
251 | } | ||
252 | |||
253 | if let Some(primary_span_label) = primary_span_label { | ||
254 | write!(&mut message, "\n{}", primary_span_label).unwrap(); | ||
255 | } | ||
256 | |||
257 | if is_unused_or_unnecessary(rd) { | ||
258 | tags.push(DiagnosticTag::Unnecessary); | ||
259 | } | ||
260 | |||
261 | if is_deprecated(rd) { | ||
262 | tags.push(DiagnosticTag::Deprecated); | ||
263 | } | ||
264 | |||
265 | let diagnostic = Diagnostic { | ||
266 | range: location.range, | ||
267 | severity, | ||
268 | code: code.map(NumberOrString::String), | ||
269 | source: Some(source), | ||
270 | message, | ||
271 | related_information: if !related_information.is_empty() { | ||
272 | Some(related_information) | ||
273 | } else { | ||
274 | None | ||
275 | }, | ||
276 | tags: if !tags.is_empty() { Some(tags) } else { None }, | ||
277 | }; | ||
278 | |||
279 | Some(MappedRustDiagnostic { location, diagnostic, suggested_fixes }) | ||
280 | } | ||
diff --git a/crates/ra_cargo_watch/src/conv/snapshots/test__snap_clippy_pass_by_ref.snap b/crates/ra_cargo_watch/src/conv/snapshots/test__snap_clippy_pass_by_ref.snap new file mode 100644 index 000000000..cb0920914 --- /dev/null +++ b/crates/ra_cargo_watch/src/conv/snapshots/test__snap_clippy_pass_by_ref.snap | |||
@@ -0,0 +1,85 @@ | |||
1 | --- | ||
2 | source: crates/ra_cargo_watch/src/conv/test.rs | ||
3 | expression: diag | ||
4 | --- | ||
5 | MappedRustDiagnostic { | ||
6 | location: Location { | ||
7 | uri: "file:///test/compiler/mir/tagset.rs", | ||
8 | range: Range { | ||
9 | start: Position { | ||
10 | line: 41, | ||
11 | character: 23, | ||
12 | }, | ||
13 | end: Position { | ||
14 | line: 41, | ||
15 | character: 28, | ||
16 | }, | ||
17 | }, | ||
18 | }, | ||
19 | diagnostic: Diagnostic { | ||
20 | range: Range { | ||
21 | start: Position { | ||
22 | line: 41, | ||
23 | character: 23, | ||
24 | }, | ||
25 | end: Position { | ||
26 | line: 41, | ||
27 | character: 28, | ||
28 | }, | ||
29 | }, | ||
30 | severity: Some( | ||
31 | Warning, | ||
32 | ), | ||
33 | code: Some( | ||
34 | String( | ||
35 | "trivially_copy_pass_by_ref", | ||
36 | ), | ||
37 | ), | ||
38 | source: Some( | ||
39 | "clippy", | ||
40 | ), | ||
41 | message: "this argument is passed by reference, but would be more efficient if passed by value\n#[warn(clippy::trivially_copy_pass_by_ref)] implied by #[warn(clippy::all)]\nfor further information visit https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref", | ||
42 | related_information: Some( | ||
43 | [ | ||
44 | DiagnosticRelatedInformation { | ||
45 | location: Location { | ||
46 | uri: "file:///test/compiler/lib.rs", | ||
47 | range: Range { | ||
48 | start: Position { | ||
49 | line: 0, | ||
50 | character: 8, | ||
51 | }, | ||
52 | end: Position { | ||
53 | line: 0, | ||
54 | character: 19, | ||
55 | }, | ||
56 | }, | ||
57 | }, | ||
58 | message: "lint level defined here", | ||
59 | }, | ||
60 | ], | ||
61 | ), | ||
62 | tags: None, | ||
63 | }, | ||
64 | suggested_fixes: [ | ||
65 | SuggestedFix { | ||
66 | title: "consider passing by value instead: \'self\'", | ||
67 | location: Location { | ||
68 | uri: "file:///test/compiler/mir/tagset.rs", | ||
69 | range: Range { | ||
70 | start: Position { | ||
71 | line: 41, | ||
72 | character: 23, | ||
73 | }, | ||
74 | end: Position { | ||
75 | line: 41, | ||
76 | character: 28, | ||
77 | }, | ||
78 | }, | ||
79 | }, | ||
80 | replacement: "self", | ||
81 | applicability: Unspecified, | ||
82 | diagnostics: [], | ||
83 | }, | ||
84 | ], | ||
85 | } | ||
diff --git a/crates/ra_cargo_watch/src/conv/snapshots/test__snap_handles_macro_location.snap b/crates/ra_cargo_watch/src/conv/snapshots/test__snap_handles_macro_location.snap new file mode 100644 index 000000000..19510ecc1 --- /dev/null +++ b/crates/ra_cargo_watch/src/conv/snapshots/test__snap_handles_macro_location.snap | |||
@@ -0,0 +1,46 @@ | |||
1 | --- | ||
2 | source: crates/ra_cargo_watch/src/conv/test.rs | ||
3 | expression: diag | ||
4 | --- | ||
5 | MappedRustDiagnostic { | ||
6 | location: Location { | ||
7 | uri: "file:///test/src/main.rs", | ||
8 | range: Range { | ||
9 | start: Position { | ||
10 | line: 1, | ||
11 | character: 4, | ||
12 | }, | ||
13 | end: Position { | ||
14 | line: 1, | ||
15 | character: 26, | ||
16 | }, | ||
17 | }, | ||
18 | }, | ||
19 | diagnostic: Diagnostic { | ||
20 | range: Range { | ||
21 | start: Position { | ||
22 | line: 1, | ||
23 | character: 4, | ||
24 | }, | ||
25 | end: Position { | ||
26 | line: 1, | ||
27 | character: 26, | ||
28 | }, | ||
29 | }, | ||
30 | severity: Some( | ||
31 | Error, | ||
32 | ), | ||
33 | code: Some( | ||
34 | String( | ||
35 | "E0277", | ||
36 | ), | ||
37 | ), | ||
38 | source: Some( | ||
39 | "rustc", | ||
40 | ), | ||
41 | message: "can\'t compare `{integer}` with `&str`\nthe trait `std::cmp::PartialEq<&str>` is not implemented for `{integer}`", | ||
42 | related_information: None, | ||
43 | tags: None, | ||
44 | }, | ||
45 | suggested_fixes: [], | ||
46 | } | ||
diff --git a/crates/ra_cargo_watch/src/conv/snapshots/test__snap_rustc_incompatible_type_for_trait.snap b/crates/ra_cargo_watch/src/conv/snapshots/test__snap_rustc_incompatible_type_for_trait.snap new file mode 100644 index 000000000..cf683e4b6 --- /dev/null +++ b/crates/ra_cargo_watch/src/conv/snapshots/test__snap_rustc_incompatible_type_for_trait.snap | |||
@@ -0,0 +1,46 @@ | |||
1 | --- | ||
2 | source: crates/ra_cargo_watch/src/conv/test.rs | ||
3 | expression: diag | ||
4 | --- | ||
5 | MappedRustDiagnostic { | ||
6 | location: Location { | ||
7 | uri: "file:///test/compiler/ty/list_iter.rs", | ||
8 | range: Range { | ||
9 | start: Position { | ||
10 | line: 51, | ||
11 | character: 4, | ||
12 | }, | ||
13 | end: Position { | ||
14 | line: 51, | ||
15 | character: 47, | ||
16 | }, | ||
17 | }, | ||
18 | }, | ||
19 | diagnostic: Diagnostic { | ||
20 | range: Range { | ||
21 | start: Position { | ||
22 | line: 51, | ||
23 | character: 4, | ||
24 | }, | ||
25 | end: Position { | ||
26 | line: 51, | ||
27 | character: 47, | ||
28 | }, | ||
29 | }, | ||
30 | severity: Some( | ||
31 | Error, | ||
32 | ), | ||
33 | code: Some( | ||
34 | String( | ||
35 | "E0053", | ||
36 | ), | ||
37 | ), | ||
38 | source: Some( | ||
39 | "rustc", | ||
40 | ), | ||
41 | message: "method `next` has an incompatible type for trait\nexpected type `fn(&mut ty::list_iter::ListIterator<\'list, M>) -> std::option::Option<&ty::Ref<M>>`\n found type `fn(&ty::list_iter::ListIterator<\'list, M>) -> std::option::Option<&\'list ty::Ref<M>>`", | ||
42 | related_information: None, | ||
43 | tags: None, | ||
44 | }, | ||
45 | suggested_fixes: [], | ||
46 | } | ||
diff --git a/crates/ra_cargo_watch/src/conv/snapshots/test__snap_rustc_mismatched_type.snap b/crates/ra_cargo_watch/src/conv/snapshots/test__snap_rustc_mismatched_type.snap new file mode 100644 index 000000000..8c1483c74 --- /dev/null +++ b/crates/ra_cargo_watch/src/conv/snapshots/test__snap_rustc_mismatched_type.snap | |||
@@ -0,0 +1,46 @@ | |||
1 | --- | ||
2 | source: crates/ra_cargo_watch/src/conv/test.rs | ||
3 | expression: diag | ||
4 | --- | ||
5 | MappedRustDiagnostic { | ||
6 | location: Location { | ||
7 | uri: "file:///test/runtime/compiler_support.rs", | ||
8 | range: Range { | ||
9 | start: Position { | ||
10 | line: 47, | ||
11 | character: 64, | ||
12 | }, | ||
13 | end: Position { | ||
14 | line: 47, | ||
15 | character: 69, | ||
16 | }, | ||
17 | }, | ||
18 | }, | ||
19 | diagnostic: Diagnostic { | ||
20 | range: Range { | ||
21 | start: Position { | ||
22 | line: 47, | ||
23 | character: 64, | ||
24 | }, | ||
25 | end: Position { | ||
26 | line: 47, | ||
27 | character: 69, | ||
28 | }, | ||
29 | }, | ||
30 | severity: Some( | ||
31 | Error, | ||
32 | ), | ||
33 | code: Some( | ||
34 | String( | ||
35 | "E0308", | ||
36 | ), | ||
37 | ), | ||
38 | source: Some( | ||
39 | "rustc", | ||
40 | ), | ||
41 | message: "mismatched types\nexpected usize, found u32", | ||
42 | related_information: None, | ||
43 | tags: None, | ||
44 | }, | ||
45 | suggested_fixes: [], | ||
46 | } | ||
diff --git a/crates/ra_cargo_watch/src/conv/snapshots/test__snap_rustc_unused_variable.snap b/crates/ra_cargo_watch/src/conv/snapshots/test__snap_rustc_unused_variable.snap new file mode 100644 index 000000000..eb5a2247b --- /dev/null +++ b/crates/ra_cargo_watch/src/conv/snapshots/test__snap_rustc_unused_variable.snap | |||
@@ -0,0 +1,70 @@ | |||
1 | --- | ||
2 | source: crates/ra_cargo_watch/src/conv/test.rs | ||
3 | expression: diag | ||
4 | --- | ||
5 | MappedRustDiagnostic { | ||
6 | location: Location { | ||
7 | uri: "file:///test/driver/subcommand/repl.rs", | ||
8 | range: Range { | ||
9 | start: Position { | ||
10 | line: 290, | ||
11 | character: 8, | ||
12 | }, | ||
13 | end: Position { | ||
14 | line: 290, | ||
15 | character: 11, | ||
16 | }, | ||
17 | }, | ||
18 | }, | ||
19 | diagnostic: Diagnostic { | ||
20 | range: Range { | ||
21 | start: Position { | ||
22 | line: 290, | ||
23 | character: 8, | ||
24 | }, | ||
25 | end: Position { | ||
26 | line: 290, | ||
27 | character: 11, | ||
28 | }, | ||
29 | }, | ||
30 | severity: Some( | ||
31 | Warning, | ||
32 | ), | ||
33 | code: Some( | ||
34 | String( | ||
35 | "unused_variables", | ||
36 | ), | ||
37 | ), | ||
38 | source: Some( | ||
39 | "rustc", | ||
40 | ), | ||
41 | message: "unused variable: `foo`\n#[warn(unused_variables)] on by default", | ||
42 | related_information: None, | ||
43 | tags: Some( | ||
44 | [ | ||
45 | Unnecessary, | ||
46 | ], | ||
47 | ), | ||
48 | }, | ||
49 | suggested_fixes: [ | ||
50 | SuggestedFix { | ||
51 | title: "consider prefixing with an underscore: \'_foo\'", | ||
52 | location: Location { | ||
53 | uri: "file:///test/driver/subcommand/repl.rs", | ||
54 | range: Range { | ||
55 | start: Position { | ||
56 | line: 290, | ||
57 | character: 8, | ||
58 | }, | ||
59 | end: Position { | ||
60 | line: 290, | ||
61 | character: 11, | ||
62 | }, | ||
63 | }, | ||
64 | }, | ||
65 | replacement: "_foo", | ||
66 | applicability: MachineApplicable, | ||
67 | diagnostics: [], | ||
68 | }, | ||
69 | ], | ||
70 | } | ||
diff --git a/crates/ra_cargo_watch/src/conv/snapshots/test__snap_rustc_wrong_number_of_parameters.snap b/crates/ra_cargo_watch/src/conv/snapshots/test__snap_rustc_wrong_number_of_parameters.snap new file mode 100644 index 000000000..2f4518931 --- /dev/null +++ b/crates/ra_cargo_watch/src/conv/snapshots/test__snap_rustc_wrong_number_of_parameters.snap | |||
@@ -0,0 +1,65 @@ | |||
1 | --- | ||
2 | source: crates/ra_cargo_watch/src/conv/test.rs | ||
3 | expression: diag | ||
4 | --- | ||
5 | MappedRustDiagnostic { | ||
6 | location: Location { | ||
7 | uri: "file:///test/compiler/ty/select.rs", | ||
8 | range: Range { | ||
9 | start: Position { | ||
10 | line: 103, | ||
11 | character: 17, | ||
12 | }, | ||
13 | end: Position { | ||
14 | line: 103, | ||
15 | character: 29, | ||
16 | }, | ||
17 | }, | ||
18 | }, | ||
19 | diagnostic: Diagnostic { | ||
20 | range: Range { | ||
21 | start: Position { | ||
22 | line: 103, | ||
23 | character: 17, | ||
24 | }, | ||
25 | end: Position { | ||
26 | line: 103, | ||
27 | character: 29, | ||
28 | }, | ||
29 | }, | ||
30 | severity: Some( | ||
31 | Error, | ||
32 | ), | ||
33 | code: Some( | ||
34 | String( | ||
35 | "E0061", | ||
36 | ), | ||
37 | ), | ||
38 | source: Some( | ||
39 | "rustc", | ||
40 | ), | ||
41 | message: "this function takes 2 parameters but 3 parameters were supplied\nexpected 2 parameters", | ||
42 | related_information: Some( | ||
43 | [ | ||
44 | DiagnosticRelatedInformation { | ||
45 | location: Location { | ||
46 | uri: "file:///test/compiler/ty/select.rs", | ||
47 | range: Range { | ||
48 | start: Position { | ||
49 | line: 218, | ||
50 | character: 4, | ||
51 | }, | ||
52 | end: Position { | ||
53 | line: 230, | ||
54 | character: 5, | ||
55 | }, | ||
56 | }, | ||
57 | }, | ||
58 | message: "defined here", | ||
59 | }, | ||
60 | ], | ||
61 | ), | ||
62 | tags: None, | ||
63 | }, | ||
64 | suggested_fixes: [], | ||
65 | } | ||
diff --git a/crates/ra_cargo_watch/src/conv/test.rs b/crates/ra_cargo_watch/src/conv/test.rs new file mode 100644 index 000000000..6817245c2 --- /dev/null +++ b/crates/ra_cargo_watch/src/conv/test.rs | |||
@@ -0,0 +1,700 @@ | |||
1 | //! This module contains the large and verbose snapshot tests for the | ||
2 | //! conversions between `cargo check` json and LSP diagnostics. | ||
3 | use crate::*; | ||
4 | |||
5 | fn parse_diagnostic(val: &str) -> cargo_metadata::diagnostic::Diagnostic { | ||
6 | serde_json::from_str::<cargo_metadata::diagnostic::Diagnostic>(val).unwrap() | ||
7 | } | ||
8 | |||
9 | #[test] | ||
10 | fn snap_rustc_incompatible_type_for_trait() { | ||
11 | let diag = parse_diagnostic( | ||
12 | r##"{ | ||
13 | "message": "method `next` has an incompatible type for trait", | ||
14 | "code": { | ||
15 | "code": "E0053", | ||
16 | "explanation": "\nThe parameters of any trait method must match between a trait implementation\nand the trait definition.\n\nHere are a couple examples of this error:\n\n```compile_fail,E0053\ntrait Foo {\n fn foo(x: u16);\n fn bar(&self);\n}\n\nstruct Bar;\n\nimpl Foo for Bar {\n // error, expected u16, found i16\n fn foo(x: i16) { }\n\n // error, types differ in mutability\n fn bar(&mut self) { }\n}\n```\n" | ||
17 | }, | ||
18 | "level": "error", | ||
19 | "spans": [ | ||
20 | { | ||
21 | "file_name": "compiler/ty/list_iter.rs", | ||
22 | "byte_start": 1307, | ||
23 | "byte_end": 1350, | ||
24 | "line_start": 52, | ||
25 | "line_end": 52, | ||
26 | "column_start": 5, | ||
27 | "column_end": 48, | ||
28 | "is_primary": true, | ||
29 | "text": [ | ||
30 | { | ||
31 | "text": " fn next(&self) -> Option<&'list ty::Ref<M>> {", | ||
32 | "highlight_start": 5, | ||
33 | "highlight_end": 48 | ||
34 | } | ||
35 | ], | ||
36 | "label": "types differ in mutability", | ||
37 | "suggested_replacement": null, | ||
38 | "suggestion_applicability": null, | ||
39 | "expansion": null | ||
40 | } | ||
41 | ], | ||
42 | "children": [ | ||
43 | { | ||
44 | "message": "expected type `fn(&mut ty::list_iter::ListIterator<'list, M>) -> std::option::Option<&ty::Ref<M>>`\n found type `fn(&ty::list_iter::ListIterator<'list, M>) -> std::option::Option<&'list ty::Ref<M>>`", | ||
45 | "code": null, | ||
46 | "level": "note", | ||
47 | "spans": [], | ||
48 | "children": [], | ||
49 | "rendered": null | ||
50 | } | ||
51 | ], | ||
52 | "rendered": "error[E0053]: method `next` has an incompatible type for trait\n --> compiler/ty/list_iter.rs:52:5\n |\n52 | fn next(&self) -> Option<&'list ty::Ref<M>> {\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ types differ in mutability\n |\n = note: expected type `fn(&mut ty::list_iter::ListIterator<'list, M>) -> std::option::Option<&ty::Ref<M>>`\n found type `fn(&ty::list_iter::ListIterator<'list, M>) -> std::option::Option<&'list ty::Ref<M>>`\n\n" | ||
53 | } | ||
54 | "##, | ||
55 | ); | ||
56 | |||
57 | let workspace_root = PathBuf::from("/test/"); | ||
58 | let diag = map_rust_diagnostic_to_lsp(&diag, &workspace_root).expect("couldn't map diagnostic"); | ||
59 | insta::assert_debug_snapshot!(diag); | ||
60 | } | ||
61 | |||
62 | #[test] | ||
63 | fn snap_rustc_unused_variable() { | ||
64 | let diag = parse_diagnostic( | ||
65 | r##"{ | ||
66 | "message": "unused variable: `foo`", | ||
67 | "code": { | ||
68 | "code": "unused_variables", | ||
69 | "explanation": null | ||
70 | }, | ||
71 | "level": "warning", | ||
72 | "spans": [ | ||
73 | { | ||
74 | "file_name": "driver/subcommand/repl.rs", | ||
75 | "byte_start": 9228, | ||
76 | "byte_end": 9231, | ||
77 | "line_start": 291, | ||
78 | "line_end": 291, | ||
79 | "column_start": 9, | ||
80 | "column_end": 12, | ||
81 | "is_primary": true, | ||
82 | "text": [ | ||
83 | { | ||
84 | "text": " let foo = 42;", | ||
85 | "highlight_start": 9, | ||
86 | "highlight_end": 12 | ||
87 | } | ||
88 | ], | ||
89 | "label": null, | ||
90 | "suggested_replacement": null, | ||
91 | "suggestion_applicability": null, | ||
92 | "expansion": null | ||
93 | } | ||
94 | ], | ||
95 | "children": [ | ||
96 | { | ||
97 | "message": "#[warn(unused_variables)] on by default", | ||
98 | "code": null, | ||
99 | "level": "note", | ||
100 | "spans": [], | ||
101 | "children": [], | ||
102 | "rendered": null | ||
103 | }, | ||
104 | { | ||
105 | "message": "consider prefixing with an underscore", | ||
106 | "code": null, | ||
107 | "level": "help", | ||
108 | "spans": [ | ||
109 | { | ||
110 | "file_name": "driver/subcommand/repl.rs", | ||
111 | "byte_start": 9228, | ||
112 | "byte_end": 9231, | ||
113 | "line_start": 291, | ||
114 | "line_end": 291, | ||
115 | "column_start": 9, | ||
116 | "column_end": 12, | ||
117 | "is_primary": true, | ||
118 | "text": [ | ||
119 | { | ||
120 | "text": " let foo = 42;", | ||
121 | "highlight_start": 9, | ||
122 | "highlight_end": 12 | ||
123 | } | ||
124 | ], | ||
125 | "label": null, | ||
126 | "suggested_replacement": "_foo", | ||
127 | "suggestion_applicability": "MachineApplicable", | ||
128 | "expansion": null | ||
129 | } | ||
130 | ], | ||
131 | "children": [], | ||
132 | "rendered": null | ||
133 | } | ||
134 | ], | ||
135 | "rendered": "warning: unused variable: `foo`\n --> driver/subcommand/repl.rs:291:9\n |\n291 | let foo = 42;\n | ^^^ help: consider prefixing with an underscore: `_foo`\n |\n = note: #[warn(unused_variables)] on by default\n\n" | ||
136 | }"##, | ||
137 | ); | ||
138 | |||
139 | let workspace_root = PathBuf::from("/test/"); | ||
140 | let diag = map_rust_diagnostic_to_lsp(&diag, &workspace_root).expect("couldn't map diagnostic"); | ||
141 | insta::assert_debug_snapshot!(diag); | ||
142 | } | ||
143 | |||
144 | #[test] | ||
145 | fn snap_rustc_wrong_number_of_parameters() { | ||
146 | let diag = parse_diagnostic( | ||
147 | r##"{ | ||
148 | "message": "this function takes 2 parameters but 3 parameters were supplied", | ||
149 | "code": { | ||
150 | "code": "E0061", | ||
151 | "explanation": "\nThe number of arguments passed to a function must match the number of arguments\nspecified in the function signature.\n\nFor example, a function like:\n\n```\nfn f(a: u16, b: &str) {}\n```\n\nMust always be called with exactly two arguments, e.g., `f(2, \"test\")`.\n\nNote that Rust does not have a notion of optional function arguments or\nvariadic functions (except for its C-FFI).\n" | ||
152 | }, | ||
153 | "level": "error", | ||
154 | "spans": [ | ||
155 | { | ||
156 | "file_name": "compiler/ty/select.rs", | ||
157 | "byte_start": 8787, | ||
158 | "byte_end": 9241, | ||
159 | "line_start": 219, | ||
160 | "line_end": 231, | ||
161 | "column_start": 5, | ||
162 | "column_end": 6, | ||
163 | "is_primary": false, | ||
164 | "text": [ | ||
165 | { | ||
166 | "text": " pub fn add_evidence(", | ||
167 | "highlight_start": 5, | ||
168 | "highlight_end": 25 | ||
169 | }, | ||
170 | { | ||
171 | "text": " &mut self,", | ||
172 | "highlight_start": 1, | ||
173 | "highlight_end": 19 | ||
174 | }, | ||
175 | { | ||
176 | "text": " target_poly: &ty::Ref<ty::Poly>,", | ||
177 | "highlight_start": 1, | ||
178 | "highlight_end": 41 | ||
179 | }, | ||
180 | { | ||
181 | "text": " evidence_poly: &ty::Ref<ty::Poly>,", | ||
182 | "highlight_start": 1, | ||
183 | "highlight_end": 43 | ||
184 | }, | ||
185 | { | ||
186 | "text": " ) {", | ||
187 | "highlight_start": 1, | ||
188 | "highlight_end": 8 | ||
189 | }, | ||
190 | { | ||
191 | "text": " match target_poly {", | ||
192 | "highlight_start": 1, | ||
193 | "highlight_end": 28 | ||
194 | }, | ||
195 | { | ||
196 | "text": " ty::Ref::Var(tvar, _) => self.add_var_evidence(tvar, evidence_poly),", | ||
197 | "highlight_start": 1, | ||
198 | "highlight_end": 81 | ||
199 | }, | ||
200 | { | ||
201 | "text": " ty::Ref::Fixed(target_ty) => {", | ||
202 | "highlight_start": 1, | ||
203 | "highlight_end": 43 | ||
204 | }, | ||
205 | { | ||
206 | "text": " let evidence_ty = evidence_poly.resolve_to_ty();", | ||
207 | "highlight_start": 1, | ||
208 | "highlight_end": 65 | ||
209 | }, | ||
210 | { | ||
211 | "text": " self.add_evidence_ty(target_ty, evidence_poly, evidence_ty)", | ||
212 | "highlight_start": 1, | ||
213 | "highlight_end": 76 | ||
214 | }, | ||
215 | { | ||
216 | "text": " }", | ||
217 | "highlight_start": 1, | ||
218 | "highlight_end": 14 | ||
219 | }, | ||
220 | { | ||
221 | "text": " }", | ||
222 | "highlight_start": 1, | ||
223 | "highlight_end": 10 | ||
224 | }, | ||
225 | { | ||
226 | "text": " }", | ||
227 | "highlight_start": 1, | ||
228 | "highlight_end": 6 | ||
229 | } | ||
230 | ], | ||
231 | "label": "defined here", | ||
232 | "suggested_replacement": null, | ||
233 | "suggestion_applicability": null, | ||
234 | "expansion": null | ||
235 | }, | ||
236 | { | ||
237 | "file_name": "compiler/ty/select.rs", | ||
238 | "byte_start": 4045, | ||
239 | "byte_end": 4057, | ||
240 | "line_start": 104, | ||
241 | "line_end": 104, | ||
242 | "column_start": 18, | ||
243 | "column_end": 30, | ||
244 | "is_primary": true, | ||
245 | "text": [ | ||
246 | { | ||
247 | "text": " self.add_evidence(target_fixed, evidence_fixed, false);", | ||
248 | "highlight_start": 18, | ||
249 | "highlight_end": 30 | ||
250 | } | ||
251 | ], | ||
252 | "label": "expected 2 parameters", | ||
253 | "suggested_replacement": null, | ||
254 | "suggestion_applicability": null, | ||
255 | "expansion": null | ||
256 | } | ||
257 | ], | ||
258 | "children": [], | ||
259 | "rendered": "error[E0061]: this function takes 2 parameters but 3 parameters were supplied\n --> compiler/ty/select.rs:104:18\n |\n104 | self.add_evidence(target_fixed, evidence_fixed, false);\n | ^^^^^^^^^^^^ expected 2 parameters\n...\n219 | / pub fn add_evidence(\n220 | | &mut self,\n221 | | target_poly: &ty::Ref<ty::Poly>,\n222 | | evidence_poly: &ty::Ref<ty::Poly>,\n... |\n230 | | }\n231 | | }\n | |_____- defined here\n\n" | ||
260 | }"##, | ||
261 | ); | ||
262 | |||
263 | let workspace_root = PathBuf::from("/test/"); | ||
264 | let diag = map_rust_diagnostic_to_lsp(&diag, &workspace_root).expect("couldn't map diagnostic"); | ||
265 | insta::assert_debug_snapshot!(diag); | ||
266 | } | ||
267 | |||
268 | #[test] | ||
269 | fn snap_clippy_pass_by_ref() { | ||
270 | let diag = parse_diagnostic( | ||
271 | r##"{ | ||
272 | "message": "this argument is passed by reference, but would be more efficient if passed by value", | ||
273 | "code": { | ||
274 | "code": "clippy::trivially_copy_pass_by_ref", | ||
275 | "explanation": null | ||
276 | }, | ||
277 | "level": "warning", | ||
278 | "spans": [ | ||
279 | { | ||
280 | "file_name": "compiler/mir/tagset.rs", | ||
281 | "byte_start": 941, | ||
282 | "byte_end": 946, | ||
283 | "line_start": 42, | ||
284 | "line_end": 42, | ||
285 | "column_start": 24, | ||
286 | "column_end": 29, | ||
287 | "is_primary": true, | ||
288 | "text": [ | ||
289 | { | ||
290 | "text": " pub fn is_disjoint(&self, other: Self) -> bool {", | ||
291 | "highlight_start": 24, | ||
292 | "highlight_end": 29 | ||
293 | } | ||
294 | ], | ||
295 | "label": null, | ||
296 | "suggested_replacement": null, | ||
297 | "suggestion_applicability": null, | ||
298 | "expansion": null | ||
299 | } | ||
300 | ], | ||
301 | "children": [ | ||
302 | { | ||
303 | "message": "lint level defined here", | ||
304 | "code": null, | ||
305 | "level": "note", | ||
306 | "spans": [ | ||
307 | { | ||
308 | "file_name": "compiler/lib.rs", | ||
309 | "byte_start": 8, | ||
310 | "byte_end": 19, | ||
311 | "line_start": 1, | ||
312 | "line_end": 1, | ||
313 | "column_start": 9, | ||
314 | "column_end": 20, | ||
315 | "is_primary": true, | ||
316 | "text": [ | ||
317 | { | ||
318 | "text": "#![warn(clippy::all)]", | ||
319 | "highlight_start": 9, | ||
320 | "highlight_end": 20 | ||
321 | } | ||
322 | ], | ||
323 | "label": null, | ||
324 | "suggested_replacement": null, | ||
325 | "suggestion_applicability": null, | ||
326 | "expansion": null | ||
327 | } | ||
328 | ], | ||
329 | "children": [], | ||
330 | "rendered": null | ||
331 | }, | ||
332 | { | ||
333 | "message": "#[warn(clippy::trivially_copy_pass_by_ref)] implied by #[warn(clippy::all)]", | ||
334 | "code": null, | ||
335 | "level": "note", | ||
336 | "spans": [], | ||
337 | "children": [], | ||
338 | "rendered": null | ||
339 | }, | ||
340 | { | ||
341 | "message": "for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref", | ||
342 | "code": null, | ||
343 | "level": "help", | ||
344 | "spans": [], | ||
345 | "children": [], | ||
346 | "rendered": null | ||
347 | }, | ||
348 | { | ||
349 | "message": "consider passing by value instead", | ||
350 | "code": null, | ||
351 | "level": "help", | ||
352 | "spans": [ | ||
353 | { | ||
354 | "file_name": "compiler/mir/tagset.rs", | ||
355 | "byte_start": 941, | ||
356 | "byte_end": 946, | ||
357 | "line_start": 42, | ||
358 | "line_end": 42, | ||
359 | "column_start": 24, | ||
360 | "column_end": 29, | ||
361 | "is_primary": true, | ||
362 | "text": [ | ||
363 | { | ||
364 | "text": " pub fn is_disjoint(&self, other: Self) -> bool {", | ||
365 | "highlight_start": 24, | ||
366 | "highlight_end": 29 | ||
367 | } | ||
368 | ], | ||
369 | "label": null, | ||
370 | "suggested_replacement": "self", | ||
371 | "suggestion_applicability": "Unspecified", | ||
372 | "expansion": null | ||
373 | } | ||
374 | ], | ||
375 | "children": [], | ||
376 | "rendered": null | ||
377 | } | ||
378 | ], | ||
379 | "rendered": "warning: this argument is passed by reference, but would be more efficient if passed by value\n --> compiler/mir/tagset.rs:42:24\n |\n42 | pub fn is_disjoint(&self, other: Self) -> bool {\n | ^^^^^ help: consider passing by value instead: `self`\n |\nnote: lint level defined here\n --> compiler/lib.rs:1:9\n |\n1 | #![warn(clippy::all)]\n | ^^^^^^^^^^^\n = note: #[warn(clippy::trivially_copy_pass_by_ref)] implied by #[warn(clippy::all)]\n = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref\n\n" | ||
380 | }"##, | ||
381 | ); | ||
382 | |||
383 | let workspace_root = PathBuf::from("/test/"); | ||
384 | let diag = map_rust_diagnostic_to_lsp(&diag, &workspace_root).expect("couldn't map diagnostic"); | ||
385 | insta::assert_debug_snapshot!(diag); | ||
386 | } | ||
387 | |||
388 | #[test] | ||
389 | fn snap_rustc_mismatched_type() { | ||
390 | let diag = parse_diagnostic( | ||
391 | r##"{ | ||
392 | "message": "mismatched types", | ||
393 | "code": { | ||
394 | "code": "E0308", | ||
395 | "explanation": "\nThis error occurs when the compiler was unable to infer the concrete type of a\nvariable. It can occur for several cases, the most common of which is a\nmismatch in the expected type that the compiler inferred for a variable's\ninitializing expression, and the actual type explicitly assigned to the\nvariable.\n\nFor example:\n\n```compile_fail,E0308\nlet x: i32 = \"I am not a number!\";\n// ~~~ ~~~~~~~~~~~~~~~~~~~~\n// | |\n// | initializing expression;\n// | compiler infers type `&str`\n// |\n// type `i32` assigned to variable `x`\n```\n" | ||
396 | }, | ||
397 | "level": "error", | ||
398 | "spans": [ | ||
399 | { | ||
400 | "file_name": "runtime/compiler_support.rs", | ||
401 | "byte_start": 1589, | ||
402 | "byte_end": 1594, | ||
403 | "line_start": 48, | ||
404 | "line_end": 48, | ||
405 | "column_start": 65, | ||
406 | "column_end": 70, | ||
407 | "is_primary": true, | ||
408 | "text": [ | ||
409 | { | ||
410 | "text": " let layout = alloc::Layout::from_size_align_unchecked(size, align);", | ||
411 | "highlight_start": 65, | ||
412 | "highlight_end": 70 | ||
413 | } | ||
414 | ], | ||
415 | "label": "expected usize, found u32", | ||
416 | "suggested_replacement": null, | ||
417 | "suggestion_applicability": null, | ||
418 | "expansion": null | ||
419 | } | ||
420 | ], | ||
421 | "children": [], | ||
422 | "rendered": "error[E0308]: mismatched types\n --> runtime/compiler_support.rs:48:65\n |\n48 | let layout = alloc::Layout::from_size_align_unchecked(size, align);\n | ^^^^^ expected usize, found u32\n\n" | ||
423 | }"##, | ||
424 | ); | ||
425 | |||
426 | let workspace_root = PathBuf::from("/test/"); | ||
427 | let diag = map_rust_diagnostic_to_lsp(&diag, &workspace_root).expect("couldn't map diagnostic"); | ||
428 | insta::assert_debug_snapshot!(diag); | ||
429 | } | ||
430 | |||
431 | #[test] | ||
432 | fn snap_handles_macro_location() { | ||
433 | let diag = parse_diagnostic( | ||
434 | r##"{ | ||
435 | "rendered": "error[E0277]: can't compare `{integer}` with `&str`\n --> src/main.rs:2:5\n |\n2 | assert_eq!(1, \"love\");\n | ^^^^^^^^^^^^^^^^^^^^^^ no implementation for `{integer} == &str`\n |\n = help: the trait `std::cmp::PartialEq<&str>` is not implemented for `{integer}`\n = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)\n\n", | ||
436 | "children": [ | ||
437 | { | ||
438 | "children": [], | ||
439 | "code": null, | ||
440 | "level": "help", | ||
441 | "message": "the trait `std::cmp::PartialEq<&str>` is not implemented for `{integer}`", | ||
442 | "rendered": null, | ||
443 | "spans": [] | ||
444 | } | ||
445 | ], | ||
446 | "code": { | ||
447 | "code": "E0277", | ||
448 | "explanation": "\nYou tried to use a type which doesn't implement some trait in a place which\nexpected that trait. Erroneous code example:\n\n```compile_fail,E0277\n// here we declare the Foo trait with a bar method\ntrait Foo {\n fn bar(&self);\n}\n\n// we now declare a function which takes an object implementing the Foo trait\nfn some_func<T: Foo>(foo: T) {\n foo.bar();\n}\n\nfn main() {\n // we now call the method with the i32 type, which doesn't implement\n // the Foo trait\n some_func(5i32); // error: the trait bound `i32 : Foo` is not satisfied\n}\n```\n\nIn order to fix this error, verify that the type you're using does implement\nthe trait. Example:\n\n```\ntrait Foo {\n fn bar(&self);\n}\n\nfn some_func<T: Foo>(foo: T) {\n foo.bar(); // we can now use this method since i32 implements the\n // Foo trait\n}\n\n// we implement the trait on the i32 type\nimpl Foo for i32 {\n fn bar(&self) {}\n}\n\nfn main() {\n some_func(5i32); // ok!\n}\n```\n\nOr in a generic context, an erroneous code example would look like:\n\n```compile_fail,E0277\nfn some_func<T>(foo: T) {\n println!(\"{:?}\", foo); // error: the trait `core::fmt::Debug` is not\n // implemented for the type `T`\n}\n\nfn main() {\n // We now call the method with the i32 type,\n // which *does* implement the Debug trait.\n some_func(5i32);\n}\n```\n\nNote that the error here is in the definition of the generic function: Although\nwe only call it with a parameter that does implement `Debug`, the compiler\nstill rejects the function: It must work with all possible input types. In\norder to make this example compile, we need to restrict the generic type we're\naccepting:\n\n```\nuse std::fmt;\n\n// Restrict the input type to types that implement Debug.\nfn some_func<T: fmt::Debug>(foo: T) {\n println!(\"{:?}\", foo);\n}\n\nfn main() {\n // Calling the method is still fine, as i32 implements Debug.\n some_func(5i32);\n\n // This would fail to compile now:\n // struct WithoutDebug;\n // some_func(WithoutDebug);\n}\n```\n\nRust only looks at the signature of the called function, as such it must\nalready specify all requirements that will be used for every type parameter.\n" | ||
449 | }, | ||
450 | "level": "error", | ||
451 | "message": "can't compare `{integer}` with `&str`", | ||
452 | "spans": [ | ||
453 | { | ||
454 | "byte_end": 155, | ||
455 | "byte_start": 153, | ||
456 | "column_end": 33, | ||
457 | "column_start": 31, | ||
458 | "expansion": { | ||
459 | "def_site_span": { | ||
460 | "byte_end": 940, | ||
461 | "byte_start": 0, | ||
462 | "column_end": 6, | ||
463 | "column_start": 1, | ||
464 | "expansion": null, | ||
465 | "file_name": "<::core::macros::assert_eq macros>", | ||
466 | "is_primary": false, | ||
467 | "label": null, | ||
468 | "line_end": 36, | ||
469 | "line_start": 1, | ||
470 | "suggested_replacement": null, | ||
471 | "suggestion_applicability": null, | ||
472 | "text": [ | ||
473 | { | ||
474 | "highlight_end": 35, | ||
475 | "highlight_start": 1, | ||
476 | "text": "($ left : expr, $ right : expr) =>" | ||
477 | }, | ||
478 | { | ||
479 | "highlight_end": 3, | ||
480 | "highlight_start": 1, | ||
481 | "text": "({" | ||
482 | }, | ||
483 | { | ||
484 | "highlight_end": 33, | ||
485 | "highlight_start": 1, | ||
486 | "text": " match (& $ left, & $ right)" | ||
487 | }, | ||
488 | { | ||
489 | "highlight_end": 7, | ||
490 | "highlight_start": 1, | ||
491 | "text": " {" | ||
492 | }, | ||
493 | { | ||
494 | "highlight_end": 34, | ||
495 | "highlight_start": 1, | ||
496 | "text": " (left_val, right_val) =>" | ||
497 | }, | ||
498 | { | ||
499 | "highlight_end": 11, | ||
500 | "highlight_start": 1, | ||
501 | "text": " {" | ||
502 | }, | ||
503 | { | ||
504 | "highlight_end": 46, | ||
505 | "highlight_start": 1, | ||
506 | "text": " if ! (* left_val == * right_val)" | ||
507 | }, | ||
508 | { | ||
509 | "highlight_end": 15, | ||
510 | "highlight_start": 1, | ||
511 | "text": " {" | ||
512 | }, | ||
513 | { | ||
514 | "highlight_end": 25, | ||
515 | "highlight_start": 1, | ||
516 | "text": " panic !" | ||
517 | }, | ||
518 | { | ||
519 | "highlight_end": 57, | ||
520 | "highlight_start": 1, | ||
521 | "text": " (r#\"assertion failed: `(left == right)`" | ||
522 | }, | ||
523 | { | ||
524 | "highlight_end": 16, | ||
525 | "highlight_start": 1, | ||
526 | "text": " left: `{:?}`," | ||
527 | }, | ||
528 | { | ||
529 | "highlight_end": 18, | ||
530 | "highlight_start": 1, | ||
531 | "text": " right: `{:?}`\"#," | ||
532 | }, | ||
533 | { | ||
534 | "highlight_end": 47, | ||
535 | "highlight_start": 1, | ||
536 | "text": " & * left_val, & * right_val)" | ||
537 | }, | ||
538 | { | ||
539 | "highlight_end": 15, | ||
540 | "highlight_start": 1, | ||
541 | "text": " }" | ||
542 | }, | ||
543 | { | ||
544 | "highlight_end": 11, | ||
545 | "highlight_start": 1, | ||
546 | "text": " }" | ||
547 | }, | ||
548 | { | ||
549 | "highlight_end": 7, | ||
550 | "highlight_start": 1, | ||
551 | "text": " }" | ||
552 | }, | ||
553 | { | ||
554 | "highlight_end": 42, | ||
555 | "highlight_start": 1, | ||
556 | "text": " }) ; ($ left : expr, $ right : expr,) =>" | ||
557 | }, | ||
558 | { | ||
559 | "highlight_end": 49, | ||
560 | "highlight_start": 1, | ||
561 | "text": "({ $ crate :: assert_eq ! ($ left, $ right) }) ;" | ||
562 | }, | ||
563 | { | ||
564 | "highlight_end": 53, | ||
565 | "highlight_start": 1, | ||
566 | "text": "($ left : expr, $ right : expr, $ ($ arg : tt) +) =>" | ||
567 | }, | ||
568 | { | ||
569 | "highlight_end": 3, | ||
570 | "highlight_start": 1, | ||
571 | "text": "({" | ||
572 | }, | ||
573 | { | ||
574 | "highlight_end": 37, | ||
575 | "highlight_start": 1, | ||
576 | "text": " match (& ($ left), & ($ right))" | ||
577 | }, | ||
578 | { | ||
579 | "highlight_end": 7, | ||
580 | "highlight_start": 1, | ||
581 | "text": " {" | ||
582 | }, | ||
583 | { | ||
584 | "highlight_end": 34, | ||
585 | "highlight_start": 1, | ||
586 | "text": " (left_val, right_val) =>" | ||
587 | }, | ||
588 | { | ||
589 | "highlight_end": 11, | ||
590 | "highlight_start": 1, | ||
591 | "text": " {" | ||
592 | }, | ||
593 | { | ||
594 | "highlight_end": 46, | ||
595 | "highlight_start": 1, | ||
596 | "text": " if ! (* left_val == * right_val)" | ||
597 | }, | ||
598 | { | ||
599 | "highlight_end": 15, | ||
600 | "highlight_start": 1, | ||
601 | "text": " {" | ||
602 | }, | ||
603 | { | ||
604 | "highlight_end": 25, | ||
605 | "highlight_start": 1, | ||
606 | "text": " panic !" | ||
607 | }, | ||
608 | { | ||
609 | "highlight_end": 57, | ||
610 | "highlight_start": 1, | ||
611 | "text": " (r#\"assertion failed: `(left == right)`" | ||
612 | }, | ||
613 | { | ||
614 | "highlight_end": 16, | ||
615 | "highlight_start": 1, | ||
616 | "text": " left: `{:?}`," | ||
617 | }, | ||
618 | { | ||
619 | "highlight_end": 22, | ||
620 | "highlight_start": 1, | ||
621 | "text": " right: `{:?}`: {}\"#," | ||
622 | }, | ||
623 | { | ||
624 | "highlight_end": 72, | ||
625 | "highlight_start": 1, | ||
626 | "text": " & * left_val, & * right_val, $ crate :: format_args !" | ||
627 | }, | ||
628 | { | ||
629 | "highlight_end": 33, | ||
630 | "highlight_start": 1, | ||
631 | "text": " ($ ($ arg) +))" | ||
632 | }, | ||
633 | { | ||
634 | "highlight_end": 15, | ||
635 | "highlight_start": 1, | ||
636 | "text": " }" | ||
637 | }, | ||
638 | { | ||
639 | "highlight_end": 11, | ||
640 | "highlight_start": 1, | ||
641 | "text": " }" | ||
642 | }, | ||
643 | { | ||
644 | "highlight_end": 7, | ||
645 | "highlight_start": 1, | ||
646 | "text": " }" | ||
647 | }, | ||
648 | { | ||
649 | "highlight_end": 6, | ||
650 | "highlight_start": 1, | ||
651 | "text": " }) ;" | ||
652 | } | ||
653 | ] | ||
654 | }, | ||
655 | "macro_decl_name": "assert_eq!", | ||
656 | "span": { | ||
657 | "byte_end": 38, | ||
658 | "byte_start": 16, | ||
659 | "column_end": 27, | ||
660 | "column_start": 5, | ||
661 | "expansion": null, | ||
662 | "file_name": "src/main.rs", | ||
663 | "is_primary": false, | ||
664 | "label": null, | ||
665 | "line_end": 2, | ||
666 | "line_start": 2, | ||
667 | "suggested_replacement": null, | ||
668 | "suggestion_applicability": null, | ||
669 | "text": [ | ||
670 | { | ||
671 | "highlight_end": 27, | ||
672 | "highlight_start": 5, | ||
673 | "text": " assert_eq!(1, \"love\");" | ||
674 | } | ||
675 | ] | ||
676 | } | ||
677 | }, | ||
678 | "file_name": "<::core::macros::assert_eq macros>", | ||
679 | "is_primary": true, | ||
680 | "label": "no implementation for `{integer} == &str`", | ||
681 | "line_end": 7, | ||
682 | "line_start": 7, | ||
683 | "suggested_replacement": null, | ||
684 | "suggestion_applicability": null, | ||
685 | "text": [ | ||
686 | { | ||
687 | "highlight_end": 33, | ||
688 | "highlight_start": 31, | ||
689 | "text": " if ! (* left_val == * right_val)" | ||
690 | } | ||
691 | ] | ||
692 | } | ||
693 | ] | ||
694 | }"##, | ||
695 | ); | ||
696 | |||
697 | let workspace_root = PathBuf::from("/test/"); | ||
698 | let diag = map_rust_diagnostic_to_lsp(&diag, &workspace_root).expect("couldn't map diagnostic"); | ||
699 | insta::assert_debug_snapshot!(diag); | ||
700 | } | ||
diff --git a/crates/ra_cargo_watch/src/lib.rs b/crates/ra_cargo_watch/src/lib.rs new file mode 100644 index 000000000..e5c22e599 --- /dev/null +++ b/crates/ra_cargo_watch/src/lib.rs | |||
@@ -0,0 +1,392 @@ | |||
1 | //! cargo_check provides the functionality needed to run `cargo check` or | ||
2 | //! another compatible command (f.x. clippy) in a background thread and provide | ||
3 | //! LSP diagnostics based on the output of the command. | ||
4 | use cargo_metadata::Message; | ||
5 | use crossbeam_channel::{never, select, unbounded, Receiver, RecvError, Sender}; | ||
6 | use lsp_types::{ | ||
7 | Diagnostic, Url, WorkDoneProgress, WorkDoneProgressBegin, WorkDoneProgressEnd, | ||
8 | WorkDoneProgressReport, | ||
9 | }; | ||
10 | use parking_lot::RwLock; | ||
11 | use std::{ | ||
12 | collections::HashMap, | ||
13 | path::PathBuf, | ||
14 | process::{Command, Stdio}, | ||
15 | sync::Arc, | ||
16 | thread::JoinHandle, | ||
17 | time::Instant, | ||
18 | }; | ||
19 | |||
20 | mod conv; | ||
21 | |||
22 | use crate::conv::{map_rust_diagnostic_to_lsp, MappedRustDiagnostic, SuggestedFix}; | ||
23 | |||
24 | #[derive(Clone, Debug)] | ||
25 | pub struct CheckOptions { | ||
26 | pub enable: bool, | ||
27 | pub args: Vec<String>, | ||
28 | pub command: String, | ||
29 | pub all_targets: bool, | ||
30 | } | ||
31 | |||
32 | /// CheckWatcher wraps the shared state and communication machinery used for | ||
33 | /// running `cargo check` (or other compatible command) and providing | ||
34 | /// diagnostics based on the output. | ||
35 | /// The spawned thread is shut down when this struct is dropped. | ||
36 | #[derive(Debug)] | ||
37 | pub struct CheckWatcher { | ||
38 | pub task_recv: Receiver<CheckTask>, | ||
39 | pub shared: Arc<RwLock<CheckWatcherSharedState>>, | ||
40 | cmd_send: Option<Sender<CheckCommand>>, | ||
41 | handle: Option<JoinHandle<()>>, | ||
42 | } | ||
43 | |||
44 | impl CheckWatcher { | ||
45 | pub fn new(options: &CheckOptions, workspace_root: PathBuf) -> CheckWatcher { | ||
46 | let options = options.clone(); | ||
47 | let shared = Arc::new(RwLock::new(CheckWatcherSharedState::new())); | ||
48 | |||
49 | let (task_send, task_recv) = unbounded::<CheckTask>(); | ||
50 | let (cmd_send, cmd_recv) = unbounded::<CheckCommand>(); | ||
51 | let shared_ = shared.clone(); | ||
52 | let handle = std::thread::spawn(move || { | ||
53 | let mut check = CheckWatcherState::new(options, workspace_root, shared_); | ||
54 | check.run(&task_send, &cmd_recv); | ||
55 | }); | ||
56 | CheckWatcher { task_recv, cmd_send: Some(cmd_send), handle: Some(handle), shared } | ||
57 | } | ||
58 | |||
59 | /// Schedule a re-start of the cargo check worker. | ||
60 | pub fn update(&self) { | ||
61 | if let Some(cmd_send) = &self.cmd_send { | ||
62 | cmd_send.send(CheckCommand::Update).unwrap(); | ||
63 | } | ||
64 | } | ||
65 | } | ||
66 | |||
67 | impl std::ops::Drop for CheckWatcher { | ||
68 | fn drop(&mut self) { | ||
69 | if let Some(handle) = self.handle.take() { | ||
70 | // Take the sender out of the option | ||
71 | let recv = self.cmd_send.take(); | ||
72 | |||
73 | // Dropping the sender finishes the thread loop | ||
74 | drop(recv); | ||
75 | |||
76 | // Join the thread, it should finish shortly. We don't really care | ||
77 | // whether it panicked, so it is safe to ignore the result | ||
78 | let _ = handle.join(); | ||
79 | } | ||
80 | } | ||
81 | } | ||
82 | |||
83 | #[derive(Debug)] | ||
84 | pub struct CheckWatcherSharedState { | ||
85 | diagnostic_collection: HashMap<Url, Vec<Diagnostic>>, | ||
86 | suggested_fix_collection: HashMap<Url, Vec<SuggestedFix>>, | ||
87 | } | ||
88 | |||
89 | impl CheckWatcherSharedState { | ||
90 | fn new() -> CheckWatcherSharedState { | ||
91 | CheckWatcherSharedState { | ||
92 | diagnostic_collection: HashMap::new(), | ||
93 | suggested_fix_collection: HashMap::new(), | ||
94 | } | ||
95 | } | ||
96 | |||
97 | /// Clear the cached diagnostics, and schedule updating diagnostics by the | ||
98 | /// server, to clear stale results. | ||
99 | pub fn clear(&mut self, task_send: &Sender<CheckTask>) { | ||
100 | let cleared_files: Vec<Url> = self.diagnostic_collection.keys().cloned().collect(); | ||
101 | |||
102 | self.diagnostic_collection.clear(); | ||
103 | self.suggested_fix_collection.clear(); | ||
104 | |||
105 | for uri in cleared_files { | ||
106 | task_send.send(CheckTask::Update(uri.clone())).unwrap(); | ||
107 | } | ||
108 | } | ||
109 | |||
110 | pub fn diagnostics_for(&self, uri: &Url) -> Option<&[Diagnostic]> { | ||
111 | self.diagnostic_collection.get(uri).map(|d| d.as_slice()) | ||
112 | } | ||
113 | |||
114 | pub fn fixes_for(&self, uri: &Url) -> Option<&[SuggestedFix]> { | ||
115 | self.suggested_fix_collection.get(uri).map(|d| d.as_slice()) | ||
116 | } | ||
117 | |||
118 | fn add_diagnostic(&mut self, file_uri: Url, diagnostic: Diagnostic) { | ||
119 | let diagnostics = self.diagnostic_collection.entry(file_uri).or_default(); | ||
120 | |||
121 | // If we're building multiple targets it's possible we've already seen this diagnostic | ||
122 | let is_duplicate = diagnostics.iter().any(|d| are_diagnostics_equal(d, &diagnostic)); | ||
123 | if is_duplicate { | ||
124 | return; | ||
125 | } | ||
126 | |||
127 | diagnostics.push(diagnostic); | ||
128 | } | ||
129 | |||
130 | fn add_suggested_fix_for_diagnostic( | ||
131 | &mut self, | ||
132 | mut suggested_fix: SuggestedFix, | ||
133 | diagnostic: &Diagnostic, | ||
134 | ) { | ||
135 | let file_uri = suggested_fix.location.uri.clone(); | ||
136 | let file_suggestions = self.suggested_fix_collection.entry(file_uri).or_default(); | ||
137 | |||
138 | let existing_suggestion: Option<&mut SuggestedFix> = | ||
139 | file_suggestions.iter_mut().find(|s| s == &&suggested_fix); | ||
140 | if let Some(existing_suggestion) = existing_suggestion { | ||
141 | // The existing suggestion also applies to this new diagnostic | ||
142 | existing_suggestion.diagnostics.push(diagnostic.clone()); | ||
143 | } else { | ||
144 | // We haven't seen this suggestion before | ||
145 | suggested_fix.diagnostics.push(diagnostic.clone()); | ||
146 | file_suggestions.push(suggested_fix); | ||
147 | } | ||
148 | } | ||
149 | } | ||
150 | |||
151 | #[derive(Debug)] | ||
152 | pub enum CheckTask { | ||
153 | /// Request a update of the given files diagnostics | ||
154 | Update(Url), | ||
155 | |||
156 | /// Request check progress notification to client | ||
157 | Status(WorkDoneProgress), | ||
158 | } | ||
159 | |||
160 | pub enum CheckCommand { | ||
161 | /// Request re-start of check thread | ||
162 | Update, | ||
163 | } | ||
164 | |||
165 | struct CheckWatcherState { | ||
166 | options: CheckOptions, | ||
167 | workspace_root: PathBuf, | ||
168 | watcher: WatchThread, | ||
169 | last_update_req: Option<Instant>, | ||
170 | shared: Arc<RwLock<CheckWatcherSharedState>>, | ||
171 | } | ||
172 | |||
173 | impl CheckWatcherState { | ||
174 | fn new( | ||
175 | options: CheckOptions, | ||
176 | workspace_root: PathBuf, | ||
177 | shared: Arc<RwLock<CheckWatcherSharedState>>, | ||
178 | ) -> CheckWatcherState { | ||
179 | let watcher = WatchThread::new(&options, &workspace_root); | ||
180 | CheckWatcherState { options, workspace_root, watcher, last_update_req: None, shared } | ||
181 | } | ||
182 | |||
183 | fn run(&mut self, task_send: &Sender<CheckTask>, cmd_recv: &Receiver<CheckCommand>) { | ||
184 | loop { | ||
185 | select! { | ||
186 | recv(&cmd_recv) -> cmd => match cmd { | ||
187 | Ok(cmd) => self.handle_command(cmd), | ||
188 | Err(RecvError) => { | ||
189 | // Command channel has closed, so shut down | ||
190 | break; | ||
191 | }, | ||
192 | }, | ||
193 | recv(self.watcher.message_recv) -> msg => match msg { | ||
194 | Ok(msg) => self.handle_message(msg, task_send), | ||
195 | Err(RecvError) => { | ||
196 | // Watcher finished, replace it with a never channel to | ||
197 | // avoid busy-waiting. | ||
198 | std::mem::replace(&mut self.watcher.message_recv, never()); | ||
199 | }, | ||
200 | } | ||
201 | }; | ||
202 | |||
203 | if self.should_recheck() { | ||
204 | self.last_update_req.take(); | ||
205 | self.shared.write().clear(task_send); | ||
206 | |||
207 | // By replacing the watcher, we drop the previous one which | ||
208 | // causes it to shut down automatically. | ||
209 | self.watcher = WatchThread::new(&self.options, &self.workspace_root); | ||
210 | } | ||
211 | } | ||
212 | } | ||
213 | |||
214 | fn should_recheck(&mut self) -> bool { | ||
215 | if let Some(_last_update_req) = &self.last_update_req { | ||
216 | // We currently only request an update on save, as we need up to | ||
217 | // date source on disk for cargo check to do it's magic, so we | ||
218 | // don't really need to debounce the requests at this point. | ||
219 | return true; | ||
220 | } | ||
221 | false | ||
222 | } | ||
223 | |||
224 | fn handle_command(&mut self, cmd: CheckCommand) { | ||
225 | match cmd { | ||
226 | CheckCommand::Update => self.last_update_req = Some(Instant::now()), | ||
227 | } | ||
228 | } | ||
229 | |||
230 | fn handle_message(&mut self, msg: CheckEvent, task_send: &Sender<CheckTask>) { | ||
231 | match msg { | ||
232 | CheckEvent::Begin => { | ||
233 | task_send | ||
234 | .send(CheckTask::Status(WorkDoneProgress::Begin(WorkDoneProgressBegin { | ||
235 | title: "Running 'cargo check'".to_string(), | ||
236 | cancellable: Some(false), | ||
237 | message: None, | ||
238 | percentage: None, | ||
239 | }))) | ||
240 | .unwrap(); | ||
241 | } | ||
242 | |||
243 | CheckEvent::End => { | ||
244 | task_send | ||
245 | .send(CheckTask::Status(WorkDoneProgress::End(WorkDoneProgressEnd { | ||
246 | message: None, | ||
247 | }))) | ||
248 | .unwrap(); | ||
249 | } | ||
250 | |||
251 | CheckEvent::Msg(Message::CompilerArtifact(msg)) => { | ||
252 | task_send | ||
253 | .send(CheckTask::Status(WorkDoneProgress::Report(WorkDoneProgressReport { | ||
254 | cancellable: Some(false), | ||
255 | message: Some(msg.target.name), | ||
256 | percentage: None, | ||
257 | }))) | ||
258 | .unwrap(); | ||
259 | } | ||
260 | |||
261 | CheckEvent::Msg(Message::CompilerMessage(msg)) => { | ||
262 | let map_result = | ||
263 | match map_rust_diagnostic_to_lsp(&msg.message, &self.workspace_root) { | ||
264 | Some(map_result) => map_result, | ||
265 | None => return, | ||
266 | }; | ||
267 | |||
268 | let MappedRustDiagnostic { location, diagnostic, suggested_fixes } = map_result; | ||
269 | let file_uri = location.uri.clone(); | ||
270 | |||
271 | if !suggested_fixes.is_empty() { | ||
272 | for suggested_fix in suggested_fixes { | ||
273 | self.shared | ||
274 | .write() | ||
275 | .add_suggested_fix_for_diagnostic(suggested_fix, &diagnostic); | ||
276 | } | ||
277 | } | ||
278 | self.shared.write().add_diagnostic(file_uri, diagnostic); | ||
279 | |||
280 | task_send.send(CheckTask::Update(location.uri)).unwrap(); | ||
281 | } | ||
282 | |||
283 | CheckEvent::Msg(Message::BuildScriptExecuted(_msg)) => {} | ||
284 | CheckEvent::Msg(Message::Unknown) => {} | ||
285 | } | ||
286 | } | ||
287 | } | ||
288 | |||
289 | /// WatchThread exists to wrap around the communication needed to be able to | ||
290 | /// run `cargo check` without blocking. Currently the Rust standard library | ||
291 | /// doesn't provide a way to read sub-process output without blocking, so we | ||
292 | /// have to wrap sub-processes output handling in a thread and pass messages | ||
293 | /// back over a channel. | ||
294 | /// The correct way to dispose of the thread is to drop it, on which the | ||
295 | /// sub-process will be killed, and the thread will be joined. | ||
296 | struct WatchThread { | ||
297 | handle: Option<JoinHandle<()>>, | ||
298 | message_recv: Receiver<CheckEvent>, | ||
299 | } | ||
300 | |||
301 | enum CheckEvent { | ||
302 | Begin, | ||
303 | Msg(cargo_metadata::Message), | ||
304 | End, | ||
305 | } | ||
306 | |||
307 | impl WatchThread { | ||
308 | fn new(options: &CheckOptions, workspace_root: &PathBuf) -> WatchThread { | ||
309 | let mut args: Vec<String> = vec![ | ||
310 | options.command.clone(), | ||
311 | "--message-format=json".to_string(), | ||
312 | "--manifest-path".to_string(), | ||
313 | format!("{}/Cargo.toml", workspace_root.to_string_lossy()), | ||
314 | ]; | ||
315 | if options.all_targets { | ||
316 | args.push("--all-targets".to_string()); | ||
317 | } | ||
318 | args.extend(options.args.iter().cloned()); | ||
319 | |||
320 | let (message_send, message_recv) = unbounded(); | ||
321 | let enabled = options.enable; | ||
322 | let handle = std::thread::spawn(move || { | ||
323 | if !enabled { | ||
324 | return; | ||
325 | } | ||
326 | |||
327 | let mut command = Command::new("cargo") | ||
328 | .args(&args) | ||
329 | .stdout(Stdio::piped()) | ||
330 | .stderr(Stdio::null()) | ||
331 | .spawn() | ||
332 | .expect("couldn't launch cargo"); | ||
333 | |||
334 | // If we trigger an error here, we will do so in the loop instead, | ||
335 | // which will break out of the loop, and continue the shutdown | ||
336 | let _ = message_send.send(CheckEvent::Begin); | ||
337 | |||
338 | for message in cargo_metadata::parse_messages(command.stdout.take().unwrap()) { | ||
339 | let message = match message { | ||
340 | Ok(message) => message, | ||
341 | Err(err) => { | ||
342 | log::error!("Invalid json from cargo check, ignoring: {}", err); | ||
343 | continue; | ||
344 | } | ||
345 | }; | ||
346 | |||
347 | match message_send.send(CheckEvent::Msg(message)) { | ||
348 | Ok(()) => {} | ||
349 | Err(_err) => { | ||
350 | // The send channel was closed, so we want to shutdown | ||
351 | break; | ||
352 | } | ||
353 | } | ||
354 | } | ||
355 | |||
356 | // We can ignore any error here, as we are already in the progress | ||
357 | // of shutting down. | ||
358 | let _ = message_send.send(CheckEvent::End); | ||
359 | |||
360 | // It is okay to ignore the result, as it only errors if the process is already dead | ||
361 | let _ = command.kill(); | ||
362 | |||
363 | // Again, we don't care about the exit status so just ignore the result | ||
364 | let _ = command.wait(); | ||
365 | }); | ||
366 | WatchThread { handle: Some(handle), message_recv } | ||
367 | } | ||
368 | } | ||
369 | |||
370 | impl std::ops::Drop for WatchThread { | ||
371 | fn drop(&mut self) { | ||
372 | if let Some(handle) = self.handle.take() { | ||
373 | // Replace our reciever with dummy one, so we can drop and close the | ||
374 | // one actually communicating with the thread | ||
375 | let recv = std::mem::replace(&mut self.message_recv, never()); | ||
376 | |||
377 | // Dropping the original reciever initiates thread sub-process shutdown | ||
378 | drop(recv); | ||
379 | |||
380 | // Join the thread, it should finish shortly. We don't really care | ||
381 | // whether it panicked, so it is safe to ignore the result | ||
382 | let _ = handle.join(); | ||
383 | } | ||
384 | } | ||
385 | } | ||
386 | |||
387 | fn are_diagnostics_equal(left: &Diagnostic, right: &Diagnostic) -> bool { | ||
388 | left.source == right.source | ||
389 | && left.severity == right.severity | ||
390 | && left.range == right.range | ||
391 | && left.message == right.message | ||
392 | } | ||
diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs index bcfc0d03e..488f74cfb 100644 --- a/crates/ra_hir/src/code_model.rs +++ b/crates/ra_hir/src/code_model.rs | |||
@@ -118,7 +118,7 @@ impl_froms!( | |||
118 | BuiltinType | 118 | BuiltinType |
119 | ); | 119 | ); |
120 | 120 | ||
121 | pub use hir_def::attr::Attrs; | 121 | pub use hir_def::{attr::Attrs, visibility::Visibility}; |
122 | 122 | ||
123 | impl Module { | 123 | impl Module { |
124 | pub(crate) fn new(krate: Crate, crate_module_id: LocalModuleId) -> Module { | 124 | pub(crate) fn new(krate: Crate, crate_module_id: LocalModuleId) -> Module { |
@@ -255,6 +255,15 @@ impl StructField { | |||
255 | } | 255 | } |
256 | } | 256 | } |
257 | 257 | ||
258 | impl HasVisibility for StructField { | ||
259 | fn visibility(&self, db: &impl HirDatabase) -> Visibility { | ||
260 | let variant_data = self.parent.variant_data(db); | ||
261 | let visibility = &variant_data.fields()[self.id].visibility; | ||
262 | let parent_id: hir_def::VariantId = self.parent.into(); | ||
263 | visibility.resolve(db, &parent_id.resolver(db)) | ||
264 | } | ||
265 | } | ||
266 | |||
258 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | 267 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
259 | pub struct Struct { | 268 | pub struct Struct { |
260 | pub(crate) id: StructId, | 269 | pub(crate) id: StructId, |
@@ -644,6 +653,17 @@ impl_froms!( | |||
644 | Const | 653 | Const |
645 | ); | 654 | ); |
646 | 655 | ||
656 | impl GenericDef { | ||
657 | pub fn params(self, db: &impl HirDatabase) -> Vec<TypeParam> { | ||
658 | let generics: Arc<hir_def::generics::GenericParams> = db.generic_params(self.into()); | ||
659 | generics | ||
660 | .types | ||
661 | .iter() | ||
662 | .map(|(local_id, _)| TypeParam { id: TypeParamId { parent: self.into(), local_id } }) | ||
663 | .collect() | ||
664 | } | ||
665 | } | ||
666 | |||
647 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] | 667 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] |
648 | pub struct Local { | 668 | pub struct Local { |
649 | pub(crate) parent: DefWithBody, | 669 | pub(crate) parent: DefWithBody, |
@@ -1030,3 +1050,11 @@ impl<T: Into<AttrDef> + Copy> Docs for T { | |||
1030 | db.documentation(def.into()) | 1050 | db.documentation(def.into()) |
1031 | } | 1051 | } |
1032 | } | 1052 | } |
1053 | |||
1054 | pub trait HasVisibility { | ||
1055 | fn visibility(&self, db: &impl HirDatabase) -> Visibility; | ||
1056 | fn is_visible_from(&self, db: &impl HirDatabase, module: Module) -> bool { | ||
1057 | let vis = self.visibility(db); | ||
1058 | vis.is_visible_from(db, module.id) | ||
1059 | } | ||
1060 | } | ||
diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs index 0008a8858..3d13978d4 100644 --- a/crates/ra_hir/src/lib.rs +++ b/crates/ra_hir/src/lib.rs | |||
@@ -40,8 +40,8 @@ mod from_source; | |||
40 | pub use crate::{ | 40 | pub use crate::{ |
41 | code_model::{ | 41 | code_model::{ |
42 | Adt, AssocItem, AttrDef, Const, Crate, CrateDependency, DefWithBody, Docs, Enum, | 42 | Adt, AssocItem, AttrDef, Const, Crate, CrateDependency, DefWithBody, Docs, Enum, |
43 | EnumVariant, FieldSource, Function, GenericDef, HasAttrs, ImplBlock, Local, MacroDef, | 43 | EnumVariant, FieldSource, Function, GenericDef, HasAttrs, HasVisibility, ImplBlock, Local, |
44 | Module, ModuleDef, ScopeDef, Static, Struct, StructField, Trait, Type, TypeAlias, | 44 | MacroDef, Module, ModuleDef, ScopeDef, Static, Struct, StructField, Trait, Type, TypeAlias, |
45 | TypeParam, Union, VariantDef, | 45 | TypeParam, Union, VariantDef, |
46 | }, | 46 | }, |
47 | from_source::FromSource, | 47 | from_source::FromSource, |
diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs index 85b378483..2c422af8b 100644 --- a/crates/ra_hir/src/source_binder.rs +++ b/crates/ra_hir/src/source_binder.rs | |||
@@ -215,8 +215,32 @@ impl SourceAnalyzer { | |||
215 | self.body_source_map.as_ref()?.node_pat(src) | 215 | self.body_source_map.as_ref()?.node_pat(src) |
216 | } | 216 | } |
217 | 217 | ||
218 | fn expand_expr( | ||
219 | &self, | ||
220 | db: &impl HirDatabase, | ||
221 | expr: InFile<&ast::Expr>, | ||
222 | ) -> Option<InFile<ast::Expr>> { | ||
223 | let macro_call = ast::MacroCall::cast(expr.value.syntax().clone())?; | ||
224 | let macro_file = | ||
225 | self.body_source_map.as_ref()?.node_macro_file(expr.with_value(¯o_call))?; | ||
226 | let expanded = db.parse_or_expand(macro_file)?; | ||
227 | let kind = expanded.kind(); | ||
228 | let expr = InFile::new(macro_file, ast::Expr::cast(expanded)?); | ||
229 | |||
230 | if ast::MacroCall::can_cast(kind) { | ||
231 | self.expand_expr(db, expr.as_ref()) | ||
232 | } else { | ||
233 | Some(expr) | ||
234 | } | ||
235 | } | ||
236 | |||
218 | pub fn type_of(&self, db: &impl HirDatabase, expr: &ast::Expr) -> Option<Type> { | 237 | pub fn type_of(&self, db: &impl HirDatabase, expr: &ast::Expr) -> Option<Type> { |
219 | let expr_id = self.expr_id(expr)?; | 238 | let expr_id = if let Some(expr) = self.expand_expr(db, InFile::new(self.file_id, expr)) { |
239 | self.body_source_map.as_ref()?.node_expr(expr.as_ref())? | ||
240 | } else { | ||
241 | self.expr_id(expr)? | ||
242 | }; | ||
243 | |||
220 | let ty = self.infer.as_ref()?[expr_id].clone(); | 244 | let ty = self.infer.as_ref()?[expr_id].clone(); |
221 | let environment = TraitEnvironment::lower(db, &self.resolver); | 245 | let environment = TraitEnvironment::lower(db, &self.resolver); |
222 | Some(Type { krate: self.resolver.krate()?, ty: InEnvironment { value: ty, environment } }) | 246 | Some(Type { krate: self.resolver.krate()?, ty: InEnvironment { value: ty, environment } }) |
diff --git a/crates/ra_hir_def/src/adt.rs b/crates/ra_hir_def/src/adt.rs index d9ea693e3..aac5f3e15 100644 --- a/crates/ra_hir_def/src/adt.rs +++ b/crates/ra_hir_def/src/adt.rs | |||
@@ -9,11 +9,12 @@ use hir_expand::{ | |||
9 | }; | 9 | }; |
10 | use ra_arena::{map::ArenaMap, Arena}; | 10 | use ra_arena::{map::ArenaMap, Arena}; |
11 | use ra_prof::profile; | 11 | use ra_prof::profile; |
12 | use ra_syntax::ast::{self, NameOwner, TypeAscriptionOwner}; | 12 | use ra_syntax::ast::{self, NameOwner, TypeAscriptionOwner, VisibilityOwner}; |
13 | 13 | ||
14 | use crate::{ | 14 | use crate::{ |
15 | db::DefDatabase, src::HasChildSource, src::HasSource, trace::Trace, type_ref::TypeRef, EnumId, | 15 | db::DefDatabase, src::HasChildSource, src::HasSource, trace::Trace, type_ref::TypeRef, |
16 | LocalEnumVariantId, LocalStructFieldId, Lookup, StructId, UnionId, VariantId, | 16 | visibility::RawVisibility, EnumId, LocalEnumVariantId, LocalStructFieldId, Lookup, StructId, |
17 | UnionId, VariantId, | ||
17 | }; | 18 | }; |
18 | 19 | ||
19 | /// Note that we use `StructData` for unions as well! | 20 | /// Note that we use `StructData` for unions as well! |
@@ -47,13 +48,14 @@ pub enum VariantData { | |||
47 | pub struct StructFieldData { | 48 | pub struct StructFieldData { |
48 | pub name: Name, | 49 | pub name: Name, |
49 | pub type_ref: TypeRef, | 50 | pub type_ref: TypeRef, |
51 | pub visibility: RawVisibility, | ||
50 | } | 52 | } |
51 | 53 | ||
52 | impl StructData { | 54 | impl StructData { |
53 | pub(crate) fn struct_data_query(db: &impl DefDatabase, id: StructId) -> Arc<StructData> { | 55 | pub(crate) fn struct_data_query(db: &impl DefDatabase, id: StructId) -> Arc<StructData> { |
54 | let src = id.lookup(db).source(db); | 56 | let src = id.lookup(db).source(db); |
55 | let name = src.value.name().map_or_else(Name::missing, |n| n.as_name()); | 57 | let name = src.value.name().map_or_else(Name::missing, |n| n.as_name()); |
56 | let variant_data = VariantData::new(src.value.kind()); | 58 | let variant_data = VariantData::new(db, src.map(|s| s.kind())); |
57 | let variant_data = Arc::new(variant_data); | 59 | let variant_data = Arc::new(variant_data); |
58 | Arc::new(StructData { name, variant_data }) | 60 | Arc::new(StructData { name, variant_data }) |
59 | } | 61 | } |
@@ -61,10 +63,12 @@ impl StructData { | |||
61 | let src = id.lookup(db).source(db); | 63 | let src = id.lookup(db).source(db); |
62 | let name = src.value.name().map_or_else(Name::missing, |n| n.as_name()); | 64 | let name = src.value.name().map_or_else(Name::missing, |n| n.as_name()); |
63 | let variant_data = VariantData::new( | 65 | let variant_data = VariantData::new( |
64 | src.value | 66 | db, |
65 | .record_field_def_list() | 67 | src.map(|s| { |
66 | .map(ast::StructKind::Record) | 68 | s.record_field_def_list() |
67 | .unwrap_or(ast::StructKind::Unit), | 69 | .map(ast::StructKind::Record) |
70 | .unwrap_or(ast::StructKind::Unit) | ||
71 | }), | ||
68 | ); | 72 | ); |
69 | let variant_data = Arc::new(variant_data); | 73 | let variant_data = Arc::new(variant_data); |
70 | Arc::new(StructData { name, variant_data }) | 74 | Arc::new(StructData { name, variant_data }) |
@@ -77,7 +81,7 @@ impl EnumData { | |||
77 | let src = e.lookup(db).source(db); | 81 | let src = e.lookup(db).source(db); |
78 | let name = src.value.name().map_or_else(Name::missing, |n| n.as_name()); | 82 | let name = src.value.name().map_or_else(Name::missing, |n| n.as_name()); |
79 | let mut trace = Trace::new_for_arena(); | 83 | let mut trace = Trace::new_for_arena(); |
80 | lower_enum(&mut trace, &src.value); | 84 | lower_enum(db, &mut trace, &src); |
81 | Arc::new(EnumData { name, variants: trace.into_arena() }) | 85 | Arc::new(EnumData { name, variants: trace.into_arena() }) |
82 | } | 86 | } |
83 | 87 | ||
@@ -93,30 +97,31 @@ impl HasChildSource for EnumId { | |||
93 | fn child_source(&self, db: &impl DefDatabase) -> InFile<ArenaMap<Self::ChildId, Self::Value>> { | 97 | fn child_source(&self, db: &impl DefDatabase) -> InFile<ArenaMap<Self::ChildId, Self::Value>> { |
94 | let src = self.lookup(db).source(db); | 98 | let src = self.lookup(db).source(db); |
95 | let mut trace = Trace::new_for_map(); | 99 | let mut trace = Trace::new_for_map(); |
96 | lower_enum(&mut trace, &src.value); | 100 | lower_enum(db, &mut trace, &src); |
97 | src.with_value(trace.into_map()) | 101 | src.with_value(trace.into_map()) |
98 | } | 102 | } |
99 | } | 103 | } |
100 | 104 | ||
101 | fn lower_enum( | 105 | fn lower_enum( |
106 | db: &impl DefDatabase, | ||
102 | trace: &mut Trace<LocalEnumVariantId, EnumVariantData, ast::EnumVariant>, | 107 | trace: &mut Trace<LocalEnumVariantId, EnumVariantData, ast::EnumVariant>, |
103 | ast: &ast::EnumDef, | 108 | ast: &InFile<ast::EnumDef>, |
104 | ) { | 109 | ) { |
105 | for var in ast.variant_list().into_iter().flat_map(|it| it.variants()) { | 110 | for var in ast.value.variant_list().into_iter().flat_map(|it| it.variants()) { |
106 | trace.alloc( | 111 | trace.alloc( |
107 | || var.clone(), | 112 | || var.clone(), |
108 | || EnumVariantData { | 113 | || EnumVariantData { |
109 | name: var.name().map_or_else(Name::missing, |it| it.as_name()), | 114 | name: var.name().map_or_else(Name::missing, |it| it.as_name()), |
110 | variant_data: Arc::new(VariantData::new(var.kind())), | 115 | variant_data: Arc::new(VariantData::new(db, ast.with_value(var.kind()))), |
111 | }, | 116 | }, |
112 | ); | 117 | ); |
113 | } | 118 | } |
114 | } | 119 | } |
115 | 120 | ||
116 | impl VariantData { | 121 | impl VariantData { |
117 | fn new(flavor: ast::StructKind) -> Self { | 122 | fn new(db: &impl DefDatabase, flavor: InFile<ast::StructKind>) -> Self { |
118 | let mut trace = Trace::new_for_arena(); | 123 | let mut trace = Trace::new_for_arena(); |
119 | match lower_struct(&mut trace, &flavor) { | 124 | match lower_struct(db, &mut trace, &flavor) { |
120 | StructKind::Tuple => VariantData::Tuple(trace.into_arena()), | 125 | StructKind::Tuple => VariantData::Tuple(trace.into_arena()), |
121 | StructKind::Record => VariantData::Record(trace.into_arena()), | 126 | StructKind::Record => VariantData::Record(trace.into_arena()), |
122 | StructKind::Unit => VariantData::Unit, | 127 | StructKind::Unit => VariantData::Unit, |
@@ -163,7 +168,7 @@ impl HasChildSource for VariantId { | |||
163 | }), | 168 | }), |
164 | }; | 169 | }; |
165 | let mut trace = Trace::new_for_map(); | 170 | let mut trace = Trace::new_for_map(); |
166 | lower_struct(&mut trace, &src.value); | 171 | lower_struct(db, &mut trace, &src); |
167 | src.with_value(trace.into_map()) | 172 | src.with_value(trace.into_map()) |
168 | } | 173 | } |
169 | } | 174 | } |
@@ -175,14 +180,15 @@ enum StructKind { | |||
175 | } | 180 | } |
176 | 181 | ||
177 | fn lower_struct( | 182 | fn lower_struct( |
183 | db: &impl DefDatabase, | ||
178 | trace: &mut Trace< | 184 | trace: &mut Trace< |
179 | LocalStructFieldId, | 185 | LocalStructFieldId, |
180 | StructFieldData, | 186 | StructFieldData, |
181 | Either<ast::TupleFieldDef, ast::RecordFieldDef>, | 187 | Either<ast::TupleFieldDef, ast::RecordFieldDef>, |
182 | >, | 188 | >, |
183 | ast: &ast::StructKind, | 189 | ast: &InFile<ast::StructKind>, |
184 | ) -> StructKind { | 190 | ) -> StructKind { |
185 | match ast { | 191 | match &ast.value { |
186 | ast::StructKind::Tuple(fl) => { | 192 | ast::StructKind::Tuple(fl) => { |
187 | for (i, fd) in fl.fields().enumerate() { | 193 | for (i, fd) in fl.fields().enumerate() { |
188 | trace.alloc( | 194 | trace.alloc( |
@@ -190,6 +196,7 @@ fn lower_struct( | |||
190 | || StructFieldData { | 196 | || StructFieldData { |
191 | name: Name::new_tuple_field(i), | 197 | name: Name::new_tuple_field(i), |
192 | type_ref: TypeRef::from_ast_opt(fd.type_ref()), | 198 | type_ref: TypeRef::from_ast_opt(fd.type_ref()), |
199 | visibility: RawVisibility::from_ast(db, ast.with_value(fd.visibility())), | ||
193 | }, | 200 | }, |
194 | ); | 201 | ); |
195 | } | 202 | } |
@@ -202,6 +209,7 @@ fn lower_struct( | |||
202 | || StructFieldData { | 209 | || StructFieldData { |
203 | name: fd.name().map(|n| n.as_name()).unwrap_or_else(Name::missing), | 210 | name: fd.name().map(|n| n.as_name()).unwrap_or_else(Name::missing), |
204 | type_ref: TypeRef::from_ast_opt(fd.ascribed_type()), | 211 | type_ref: TypeRef::from_ast_opt(fd.ascribed_type()), |
212 | visibility: RawVisibility::from_ast(db, ast.with_value(fd.visibility())), | ||
205 | }, | 213 | }, |
206 | ); | 214 | ); |
207 | } | 215 | } |
diff --git a/crates/ra_hir_def/src/body.rs b/crates/ra_hir_def/src/body.rs index d3e4c50ae..142c52d35 100644 --- a/crates/ra_hir_def/src/body.rs +++ b/crates/ra_hir_def/src/body.rs | |||
@@ -163,6 +163,7 @@ pub struct BodySourceMap { | |||
163 | pat_map: FxHashMap<PatSource, PatId>, | 163 | pat_map: FxHashMap<PatSource, PatId>, |
164 | pat_map_back: ArenaMap<PatId, PatSource>, | 164 | pat_map_back: ArenaMap<PatId, PatSource>, |
165 | field_map: FxHashMap<(ExprId, usize), AstPtr<ast::RecordField>>, | 165 | field_map: FxHashMap<(ExprId, usize), AstPtr<ast::RecordField>>, |
166 | expansions: FxHashMap<InFile<AstPtr<ast::MacroCall>>, HirFileId>, | ||
166 | } | 167 | } |
167 | 168 | ||
168 | impl Body { | 169 | impl Body { |
@@ -237,6 +238,11 @@ impl BodySourceMap { | |||
237 | self.expr_map.get(&src).cloned() | 238 | self.expr_map.get(&src).cloned() |
238 | } | 239 | } |
239 | 240 | ||
241 | pub fn node_macro_file(&self, node: InFile<&ast::MacroCall>) -> Option<HirFileId> { | ||
242 | let src = node.map(|it| AstPtr::new(it)); | ||
243 | self.expansions.get(&src).cloned() | ||
244 | } | ||
245 | |||
240 | pub fn field_init_shorthand_expr(&self, node: InFile<&ast::RecordField>) -> Option<ExprId> { | 246 | pub fn field_init_shorthand_expr(&self, node: InFile<&ast::RecordField>) -> Option<ExprId> { |
241 | let src = node.map(|it| Either::Right(AstPtr::new(it))); | 247 | let src = node.map(|it| Either::Right(AstPtr::new(it))); |
242 | self.expr_map.get(&src).cloned() | 248 | self.expr_map.get(&src).cloned() |
diff --git a/crates/ra_hir_def/src/body/lower.rs b/crates/ra_hir_def/src/body/lower.rs index 5323af097..e656f9a41 100644 --- a/crates/ra_hir_def/src/body/lower.rs +++ b/crates/ra_hir_def/src/body/lower.rs | |||
@@ -446,14 +446,20 @@ where | |||
446 | } | 446 | } |
447 | } | 447 | } |
448 | // FIXME expand to statements in statement position | 448 | // FIXME expand to statements in statement position |
449 | ast::Expr::MacroCall(e) => match self.expander.enter_expand(self.db, e) { | 449 | ast::Expr::MacroCall(e) => { |
450 | Some((mark, expansion)) => { | 450 | let macro_call = self.expander.to_source(AstPtr::new(&e)); |
451 | let id = self.collect_expr(expansion); | 451 | match self.expander.enter_expand(self.db, e.clone()) { |
452 | self.expander.exit(self.db, mark); | 452 | Some((mark, expansion)) => { |
453 | id | 453 | self.source_map |
454 | .expansions | ||
455 | .insert(macro_call, self.expander.current_file_id); | ||
456 | let id = self.collect_expr(expansion); | ||
457 | self.expander.exit(self.db, mark); | ||
458 | id | ||
459 | } | ||
460 | None => self.alloc_expr(Expr::Missing, syntax_ptr), | ||
454 | } | 461 | } |
455 | None => self.alloc_expr(Expr::Missing, syntax_ptr), | 462 | } |
456 | }, | ||
457 | 463 | ||
458 | // FIXME implement HIR for these: | 464 | // FIXME implement HIR for these: |
459 | ast::Expr::Label(_e) => self.alloc_expr(Expr::Missing, syntax_ptr), | 465 | ast::Expr::Label(_e) => self.alloc_expr(Expr::Missing, syntax_ptr), |
@@ -543,7 +549,10 @@ where | |||
543 | }; | 549 | }; |
544 | self.body.item_scope.define_def(def); | 550 | self.body.item_scope.define_def(def); |
545 | if let Some(name) = name { | 551 | if let Some(name) = name { |
546 | self.body.item_scope.push_res(name.as_name(), def.into()); | 552 | let vis = crate::visibility::Visibility::Public; // FIXME determine correctly |
553 | self.body | ||
554 | .item_scope | ||
555 | .push_res(name.as_name(), crate::per_ns::PerNs::from_def(def, vis)); | ||
547 | } | 556 | } |
548 | } | 557 | } |
549 | } | 558 | } |
diff --git a/crates/ra_hir_def/src/data.rs b/crates/ra_hir_def/src/data.rs index 1aa9a9b7d..c900a6a18 100644 --- a/crates/ra_hir_def/src/data.rs +++ b/crates/ra_hir_def/src/data.rs | |||
@@ -10,8 +10,9 @@ use ra_syntax::ast::{self, AstNode, ImplItem, ModuleItemOwner, NameOwner, TypeAs | |||
10 | 10 | ||
11 | use crate::{ | 11 | use crate::{ |
12 | db::DefDatabase, | 12 | db::DefDatabase, |
13 | path::{path, GenericArgs, Path}, | ||
13 | src::HasSource, | 14 | src::HasSource, |
14 | type_ref::{Mutability, TypeRef}, | 15 | type_ref::{Mutability, TypeBound, TypeRef}, |
15 | AssocContainerId, AssocItemId, ConstId, ConstLoc, Expander, FunctionId, FunctionLoc, HasModule, | 16 | AssocContainerId, AssocItemId, ConstId, ConstLoc, Expander, FunctionId, FunctionLoc, HasModule, |
16 | ImplId, Intern, Lookup, ModuleId, StaticId, TraitId, TypeAliasId, TypeAliasLoc, | 17 | ImplId, Intern, Lookup, ModuleId, StaticId, TraitId, TypeAliasId, TypeAliasLoc, |
17 | }; | 18 | }; |
@@ -62,11 +63,29 @@ impl FunctionData { | |||
62 | TypeRef::unit() | 63 | TypeRef::unit() |
63 | }; | 64 | }; |
64 | 65 | ||
66 | let ret_type = if src.value.is_async() { | ||
67 | let future_impl = desugar_future_path(ret_type); | ||
68 | let ty_bound = TypeBound::Path(future_impl); | ||
69 | TypeRef::ImplTrait(vec![ty_bound]) | ||
70 | } else { | ||
71 | ret_type | ||
72 | }; | ||
73 | |||
65 | let sig = FunctionData { name, params, ret_type, has_self_param }; | 74 | let sig = FunctionData { name, params, ret_type, has_self_param }; |
66 | Arc::new(sig) | 75 | Arc::new(sig) |
67 | } | 76 | } |
68 | } | 77 | } |
69 | 78 | ||
79 | fn desugar_future_path(orig: TypeRef) -> Path { | ||
80 | let path = path![std::future::Future]; | ||
81 | let mut generic_args: Vec<_> = std::iter::repeat(None).take(path.segments.len() - 1).collect(); | ||
82 | let mut last = GenericArgs::empty(); | ||
83 | last.bindings.push((name![Output], orig)); | ||
84 | generic_args.push(Some(Arc::new(last))); | ||
85 | |||
86 | Path::from_known_path(path, generic_args) | ||
87 | } | ||
88 | |||
70 | #[derive(Debug, Clone, PartialEq, Eq)] | 89 | #[derive(Debug, Clone, PartialEq, Eq)] |
71 | pub struct TypeAliasData { | 90 | pub struct TypeAliasData { |
72 | pub name: Name, | 91 | pub name: Name, |
diff --git a/crates/ra_hir_def/src/item_scope.rs b/crates/ra_hir_def/src/item_scope.rs index b0288ee8d..fe7bb9779 100644 --- a/crates/ra_hir_def/src/item_scope.rs +++ b/crates/ra_hir_def/src/item_scope.rs | |||
@@ -5,7 +5,10 @@ use hir_expand::name::Name; | |||
5 | use once_cell::sync::Lazy; | 5 | use once_cell::sync::Lazy; |
6 | use rustc_hash::FxHashMap; | 6 | use rustc_hash::FxHashMap; |
7 | 7 | ||
8 | use crate::{per_ns::PerNs, AdtId, BuiltinType, ImplId, MacroDefId, ModuleDefId, TraitId}; | 8 | use crate::{ |
9 | per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType, ImplId, MacroDefId, ModuleDefId, | ||
10 | TraitId, | ||
11 | }; | ||
9 | 12 | ||
10 | #[derive(Debug, Default, PartialEq, Eq)] | 13 | #[derive(Debug, Default, PartialEq, Eq)] |
11 | pub struct ItemScope { | 14 | pub struct ItemScope { |
@@ -30,7 +33,7 @@ pub struct ItemScope { | |||
30 | static BUILTIN_SCOPE: Lazy<FxHashMap<Name, PerNs>> = Lazy::new(|| { | 33 | static BUILTIN_SCOPE: Lazy<FxHashMap<Name, PerNs>> = Lazy::new(|| { |
31 | BuiltinType::ALL | 34 | BuiltinType::ALL |
32 | .iter() | 35 | .iter() |
33 | .map(|(name, ty)| (name.clone(), PerNs::types(ty.clone().into()))) | 36 | .map(|(name, ty)| (name.clone(), PerNs::types(ty.clone().into(), Visibility::Public))) |
34 | .collect() | 37 | .collect() |
35 | }); | 38 | }); |
36 | 39 | ||
@@ -144,8 +147,8 @@ impl ItemScope { | |||
144 | changed | 147 | changed |
145 | } | 148 | } |
146 | 149 | ||
147 | pub(crate) fn collect_resolutions(&self) -> Vec<(Name, PerNs)> { | 150 | pub(crate) fn resolutions<'a>(&'a self) -> impl Iterator<Item = (Name, PerNs)> + 'a { |
148 | self.visible.iter().map(|(name, res)| (name.clone(), res.clone())).collect() | 151 | self.visible.iter().map(|(name, res)| (name.clone(), res.clone())) |
149 | } | 152 | } |
150 | 153 | ||
151 | pub(crate) fn collect_legacy_macros(&self) -> FxHashMap<Name, MacroDefId> { | 154 | pub(crate) fn collect_legacy_macros(&self) -> FxHashMap<Name, MacroDefId> { |
@@ -153,20 +156,20 @@ impl ItemScope { | |||
153 | } | 156 | } |
154 | } | 157 | } |
155 | 158 | ||
156 | impl From<ModuleDefId> for PerNs { | 159 | impl PerNs { |
157 | fn from(def: ModuleDefId) -> PerNs { | 160 | pub(crate) fn from_def(def: ModuleDefId, v: Visibility) -> PerNs { |
158 | match def { | 161 | match def { |
159 | ModuleDefId::ModuleId(_) => PerNs::types(def), | 162 | ModuleDefId::ModuleId(_) => PerNs::types(def, v), |
160 | ModuleDefId::FunctionId(_) => PerNs::values(def), | 163 | ModuleDefId::FunctionId(_) => PerNs::values(def, v), |
161 | ModuleDefId::AdtId(adt) => match adt { | 164 | ModuleDefId::AdtId(adt) => match adt { |
162 | AdtId::StructId(_) | AdtId::UnionId(_) => PerNs::both(def, def), | 165 | AdtId::StructId(_) | AdtId::UnionId(_) => PerNs::both(def, def, v), |
163 | AdtId::EnumId(_) => PerNs::types(def), | 166 | AdtId::EnumId(_) => PerNs::types(def, v), |
164 | }, | 167 | }, |
165 | ModuleDefId::EnumVariantId(_) => PerNs::both(def, def), | 168 | ModuleDefId::EnumVariantId(_) => PerNs::both(def, def, v), |
166 | ModuleDefId::ConstId(_) | ModuleDefId::StaticId(_) => PerNs::values(def), | 169 | ModuleDefId::ConstId(_) | ModuleDefId::StaticId(_) => PerNs::values(def, v), |
167 | ModuleDefId::TraitId(_) => PerNs::types(def), | 170 | ModuleDefId::TraitId(_) => PerNs::types(def, v), |
168 | ModuleDefId::TypeAliasId(_) => PerNs::types(def), | 171 | ModuleDefId::TypeAliasId(_) => PerNs::types(def, v), |
169 | ModuleDefId::BuiltinType(_) => PerNs::types(def), | 172 | ModuleDefId::BuiltinType(_) => PerNs::types(def, v), |
170 | } | 173 | } |
171 | } | 174 | } |
172 | } | 175 | } |
diff --git a/crates/ra_hir_def/src/lib.rs b/crates/ra_hir_def/src/lib.rs index f6c7f38d1..61f044ecf 100644 --- a/crates/ra_hir_def/src/lib.rs +++ b/crates/ra_hir_def/src/lib.rs | |||
@@ -36,6 +36,8 @@ pub mod nameres; | |||
36 | pub mod src; | 36 | pub mod src; |
37 | pub mod child_by_source; | 37 | pub mod child_by_source; |
38 | 38 | ||
39 | pub mod visibility; | ||
40 | |||
39 | #[cfg(test)] | 41 | #[cfg(test)] |
40 | mod test_db; | 42 | mod test_db; |
41 | #[cfg(test)] | 43 | #[cfg(test)] |
diff --git a/crates/ra_hir_def/src/nameres/collector.rs b/crates/ra_hir_def/src/nameres/collector.rs index b9f40d3dd..8a22b0585 100644 --- a/crates/ra_hir_def/src/nameres/collector.rs +++ b/crates/ra_hir_def/src/nameres/collector.rs | |||
@@ -24,6 +24,7 @@ use crate::{ | |||
24 | }, | 24 | }, |
25 | path::{ModPath, PathKind}, | 25 | path::{ModPath, PathKind}, |
26 | per_ns::PerNs, | 26 | per_ns::PerNs, |
27 | visibility::Visibility, | ||
27 | AdtId, AstId, ConstLoc, ContainerId, EnumLoc, EnumVariantId, FunctionLoc, ImplLoc, Intern, | 28 | AdtId, AstId, ConstLoc, ContainerId, EnumLoc, EnumVariantId, FunctionLoc, ImplLoc, Intern, |
28 | LocalModuleId, ModuleDefId, ModuleId, StaticLoc, StructLoc, TraitLoc, TypeAliasLoc, UnionLoc, | 29 | LocalModuleId, ModuleDefId, ModuleId, StaticLoc, StructLoc, TraitLoc, TypeAliasLoc, UnionLoc, |
29 | }; | 30 | }; |
@@ -108,7 +109,7 @@ struct MacroDirective { | |||
108 | struct DefCollector<'a, DB> { | 109 | struct DefCollector<'a, DB> { |
109 | db: &'a DB, | 110 | db: &'a DB, |
110 | def_map: CrateDefMap, | 111 | def_map: CrateDefMap, |
111 | glob_imports: FxHashMap<LocalModuleId, Vec<(LocalModuleId, raw::Import)>>, | 112 | glob_imports: FxHashMap<LocalModuleId, Vec<(LocalModuleId, Visibility)>>, |
112 | unresolved_imports: Vec<ImportDirective>, | 113 | unresolved_imports: Vec<ImportDirective>, |
113 | resolved_imports: Vec<ImportDirective>, | 114 | resolved_imports: Vec<ImportDirective>, |
114 | unexpanded_macros: Vec<MacroDirective>, | 115 | unexpanded_macros: Vec<MacroDirective>, |
@@ -214,7 +215,11 @@ where | |||
214 | // In Rust, `#[macro_export]` macros are unconditionally visible at the | 215 | // In Rust, `#[macro_export]` macros are unconditionally visible at the |
215 | // crate root, even if the parent modules is **not** visible. | 216 | // crate root, even if the parent modules is **not** visible. |
216 | if export { | 217 | if export { |
217 | self.update(self.def_map.root, &[(name, PerNs::macros(macro_))]); | 218 | self.update( |
219 | self.def_map.root, | ||
220 | &[(name, PerNs::macros(macro_, Visibility::Public))], | ||
221 | Visibility::Public, | ||
222 | ); | ||
218 | } | 223 | } |
219 | } | 224 | } |
220 | 225 | ||
@@ -348,9 +353,12 @@ where | |||
348 | 353 | ||
349 | fn record_resolved_import(&mut self, directive: &ImportDirective) { | 354 | fn record_resolved_import(&mut self, directive: &ImportDirective) { |
350 | let module_id = directive.module_id; | 355 | let module_id = directive.module_id; |
351 | let import_id = directive.import_id; | ||
352 | let import = &directive.import; | 356 | let import = &directive.import; |
353 | let def = directive.status.namespaces(); | 357 | let def = directive.status.namespaces(); |
358 | let vis = self | ||
359 | .def_map | ||
360 | .resolve_visibility(self.db, module_id, &directive.import.visibility) | ||
361 | .unwrap_or(Visibility::Public); | ||
354 | 362 | ||
355 | if import.is_glob { | 363 | if import.is_glob { |
356 | log::debug!("glob import: {:?}", import); | 364 | log::debug!("glob import: {:?}", import); |
@@ -366,9 +374,16 @@ where | |||
366 | let scope = &item_map[m.local_id].scope; | 374 | let scope = &item_map[m.local_id].scope; |
367 | 375 | ||
368 | // Module scoped macros is included | 376 | // Module scoped macros is included |
369 | let items = scope.collect_resolutions(); | 377 | let items = scope |
370 | 378 | .resolutions() | |
371 | self.update(module_id, &items); | 379 | // only keep visible names... |
380 | .map(|(n, res)| { | ||
381 | (n, res.filter_visibility(|v| v.is_visible_from_other_crate())) | ||
382 | }) | ||
383 | .filter(|(_, res)| !res.is_none()) | ||
384 | .collect::<Vec<_>>(); | ||
385 | |||
386 | self.update(module_id, &items, vis); | ||
372 | } else { | 387 | } else { |
373 | // glob import from same crate => we do an initial | 388 | // glob import from same crate => we do an initial |
374 | // import, and then need to propagate any further | 389 | // import, and then need to propagate any further |
@@ -376,13 +391,25 @@ where | |||
376 | let scope = &self.def_map[m.local_id].scope; | 391 | let scope = &self.def_map[m.local_id].scope; |
377 | 392 | ||
378 | // Module scoped macros is included | 393 | // Module scoped macros is included |
379 | let items = scope.collect_resolutions(); | 394 | let items = scope |
380 | 395 | .resolutions() | |
381 | self.update(module_id, &items); | 396 | // only keep visible names... |
397 | .map(|(n, res)| { | ||
398 | ( | ||
399 | n, | ||
400 | res.filter_visibility(|v| { | ||
401 | v.is_visible_from_def_map(&self.def_map, module_id) | ||
402 | }), | ||
403 | ) | ||
404 | }) | ||
405 | .filter(|(_, res)| !res.is_none()) | ||
406 | .collect::<Vec<_>>(); | ||
407 | |||
408 | self.update(module_id, &items, vis); | ||
382 | // record the glob import in case we add further items | 409 | // record the glob import in case we add further items |
383 | let glob = self.glob_imports.entry(m.local_id).or_default(); | 410 | let glob = self.glob_imports.entry(m.local_id).or_default(); |
384 | if !glob.iter().any(|it| *it == (module_id, import_id)) { | 411 | if !glob.iter().any(|(mid, _)| *mid == module_id) { |
385 | glob.push((module_id, import_id)); | 412 | glob.push((module_id, vis)); |
386 | } | 413 | } |
387 | } | 414 | } |
388 | } | 415 | } |
@@ -396,11 +423,11 @@ where | |||
396 | .map(|(local_id, variant_data)| { | 423 | .map(|(local_id, variant_data)| { |
397 | let name = variant_data.name.clone(); | 424 | let name = variant_data.name.clone(); |
398 | let variant = EnumVariantId { parent: e, local_id }; | 425 | let variant = EnumVariantId { parent: e, local_id }; |
399 | let res = PerNs::both(variant.into(), variant.into()); | 426 | let res = PerNs::both(variant.into(), variant.into(), vis); |
400 | (name, res) | 427 | (name, res) |
401 | }) | 428 | }) |
402 | .collect::<Vec<_>>(); | 429 | .collect::<Vec<_>>(); |
403 | self.update(module_id, &resolutions); | 430 | self.update(module_id, &resolutions, vis); |
404 | } | 431 | } |
405 | Some(d) => { | 432 | Some(d) => { |
406 | log::debug!("glob import {:?} from non-module/enum {:?}", import, d); | 433 | log::debug!("glob import {:?} from non-module/enum {:?}", import, d); |
@@ -422,21 +449,24 @@ where | |||
422 | } | 449 | } |
423 | } | 450 | } |
424 | 451 | ||
425 | self.update(module_id, &[(name, def)]); | 452 | self.update(module_id, &[(name, def)], vis); |
426 | } | 453 | } |
427 | None => tested_by!(bogus_paths), | 454 | None => tested_by!(bogus_paths), |
428 | } | 455 | } |
429 | } | 456 | } |
430 | } | 457 | } |
431 | 458 | ||
432 | fn update(&mut self, module_id: LocalModuleId, resolutions: &[(Name, PerNs)]) { | 459 | fn update(&mut self, module_id: LocalModuleId, resolutions: &[(Name, PerNs)], vis: Visibility) { |
433 | self.update_recursive(module_id, resolutions, 0) | 460 | self.update_recursive(module_id, resolutions, vis, 0) |
434 | } | 461 | } |
435 | 462 | ||
436 | fn update_recursive( | 463 | fn update_recursive( |
437 | &mut self, | 464 | &mut self, |
438 | module_id: LocalModuleId, | 465 | module_id: LocalModuleId, |
439 | resolutions: &[(Name, PerNs)], | 466 | resolutions: &[(Name, PerNs)], |
467 | // All resolutions are imported with this visibility; the visibilies in | ||
468 | // the `PerNs` values are ignored and overwritten | ||
469 | vis: Visibility, | ||
440 | depth: usize, | 470 | depth: usize, |
441 | ) { | 471 | ) { |
442 | if depth > 100 { | 472 | if depth > 100 { |
@@ -446,7 +476,7 @@ where | |||
446 | let scope = &mut self.def_map.modules[module_id].scope; | 476 | let scope = &mut self.def_map.modules[module_id].scope; |
447 | let mut changed = false; | 477 | let mut changed = false; |
448 | for (name, res) in resolutions { | 478 | for (name, res) in resolutions { |
449 | changed |= scope.push_res(name.clone(), *res); | 479 | changed |= scope.push_res(name.clone(), res.with_visibility(vis)); |
450 | } | 480 | } |
451 | 481 | ||
452 | if !changed { | 482 | if !changed { |
@@ -459,9 +489,13 @@ where | |||
459 | .flat_map(|v| v.iter()) | 489 | .flat_map(|v| v.iter()) |
460 | .cloned() | 490 | .cloned() |
461 | .collect::<Vec<_>>(); | 491 | .collect::<Vec<_>>(); |
462 | for (glob_importing_module, _glob_import) in glob_imports { | 492 | for (glob_importing_module, glob_import_vis) in glob_imports { |
463 | // We pass the glob import so that the tracked import in those modules is that glob import | 493 | // we know all resolutions have the same visibility (`vis`), so we |
464 | self.update_recursive(glob_importing_module, resolutions, depth + 1); | 494 | // just need to check that once |
495 | if !vis.is_visible_from_def_map(&self.def_map, glob_importing_module) { | ||
496 | continue; | ||
497 | } | ||
498 | self.update_recursive(glob_importing_module, resolutions, glob_import_vis, depth + 1); | ||
465 | } | 499 | } |
466 | } | 500 | } |
467 | 501 | ||
@@ -633,9 +667,13 @@ where | |||
633 | let is_macro_use = attrs.by_key("macro_use").exists(); | 667 | let is_macro_use = attrs.by_key("macro_use").exists(); |
634 | match module { | 668 | match module { |
635 | // inline module, just recurse | 669 | // inline module, just recurse |
636 | raw::ModuleData::Definition { name, items, ast_id } => { | 670 | raw::ModuleData::Definition { name, visibility, items, ast_id } => { |
637 | let module_id = | 671 | let module_id = self.push_child_module( |
638 | self.push_child_module(name.clone(), AstId::new(self.file_id, *ast_id), None); | 672 | name.clone(), |
673 | AstId::new(self.file_id, *ast_id), | ||
674 | None, | ||
675 | &visibility, | ||
676 | ); | ||
639 | 677 | ||
640 | ModCollector { | 678 | ModCollector { |
641 | def_collector: &mut *self.def_collector, | 679 | def_collector: &mut *self.def_collector, |
@@ -650,7 +688,7 @@ where | |||
650 | } | 688 | } |
651 | } | 689 | } |
652 | // out of line module, resolve, parse and recurse | 690 | // out of line module, resolve, parse and recurse |
653 | raw::ModuleData::Declaration { name, ast_id } => { | 691 | raw::ModuleData::Declaration { name, visibility, ast_id } => { |
654 | let ast_id = AstId::new(self.file_id, *ast_id); | 692 | let ast_id = AstId::new(self.file_id, *ast_id); |
655 | match self.mod_dir.resolve_declaration( | 693 | match self.mod_dir.resolve_declaration( |
656 | self.def_collector.db, | 694 | self.def_collector.db, |
@@ -659,7 +697,12 @@ where | |||
659 | path_attr, | 697 | path_attr, |
660 | ) { | 698 | ) { |
661 | Ok((file_id, mod_dir)) => { | 699 | Ok((file_id, mod_dir)) => { |
662 | let module_id = self.push_child_module(name.clone(), ast_id, Some(file_id)); | 700 | let module_id = self.push_child_module( |
701 | name.clone(), | ||
702 | ast_id, | ||
703 | Some(file_id), | ||
704 | &visibility, | ||
705 | ); | ||
663 | let raw_items = self.def_collector.db.raw_items(file_id.into()); | 706 | let raw_items = self.def_collector.db.raw_items(file_id.into()); |
664 | ModCollector { | 707 | ModCollector { |
665 | def_collector: &mut *self.def_collector, | 708 | def_collector: &mut *self.def_collector, |
@@ -690,7 +733,13 @@ where | |||
690 | name: Name, | 733 | name: Name, |
691 | declaration: AstId<ast::Module>, | 734 | declaration: AstId<ast::Module>, |
692 | definition: Option<FileId>, | 735 | definition: Option<FileId>, |
736 | visibility: &crate::visibility::RawVisibility, | ||
693 | ) -> LocalModuleId { | 737 | ) -> LocalModuleId { |
738 | let vis = self | ||
739 | .def_collector | ||
740 | .def_map | ||
741 | .resolve_visibility(self.def_collector.db, self.module_id, visibility) | ||
742 | .unwrap_or(Visibility::Public); | ||
694 | let modules = &mut self.def_collector.def_map.modules; | 743 | let modules = &mut self.def_collector.def_map.modules; |
695 | let res = modules.alloc(ModuleData::default()); | 744 | let res = modules.alloc(ModuleData::default()); |
696 | modules[res].parent = Some(self.module_id); | 745 | modules[res].parent = Some(self.module_id); |
@@ -702,7 +751,7 @@ where | |||
702 | let module = ModuleId { krate: self.def_collector.def_map.krate, local_id: res }; | 751 | let module = ModuleId { krate: self.def_collector.def_map.krate, local_id: res }; |
703 | let def: ModuleDefId = module.into(); | 752 | let def: ModuleDefId = module.into(); |
704 | self.def_collector.def_map.modules[self.module_id].scope.define_def(def); | 753 | self.def_collector.def_map.modules[self.module_id].scope.define_def(def); |
705 | self.def_collector.update(self.module_id, &[(name, def.into())]); | 754 | self.def_collector.update(self.module_id, &[(name, PerNs::from_def(def, vis))], vis); |
706 | res | 755 | res |
707 | } | 756 | } |
708 | 757 | ||
@@ -716,6 +765,7 @@ where | |||
716 | 765 | ||
717 | let name = def.name.clone(); | 766 | let name = def.name.clone(); |
718 | let container = ContainerId::ModuleId(module); | 767 | let container = ContainerId::ModuleId(module); |
768 | let vis = &def.visibility; | ||
719 | let def: ModuleDefId = match def.kind { | 769 | let def: ModuleDefId = match def.kind { |
720 | raw::DefKind::Function(ast_id) => FunctionLoc { | 770 | raw::DefKind::Function(ast_id) => FunctionLoc { |
721 | container: container.into(), | 771 | container: container.into(), |
@@ -761,7 +811,12 @@ where | |||
761 | .into(), | 811 | .into(), |
762 | }; | 812 | }; |
763 | self.def_collector.def_map.modules[self.module_id].scope.define_def(def); | 813 | self.def_collector.def_map.modules[self.module_id].scope.define_def(def); |
764 | self.def_collector.update(self.module_id, &[(name, def.into())]) | 814 | let vis = self |
815 | .def_collector | ||
816 | .def_map | ||
817 | .resolve_visibility(self.def_collector.db, self.module_id, vis) | ||
818 | .unwrap_or(Visibility::Public); | ||
819 | self.def_collector.update(self.module_id, &[(name, PerNs::from_def(def, vis))], vis) | ||
765 | } | 820 | } |
766 | 821 | ||
767 | fn collect_derives(&mut self, attrs: &Attrs, def: &raw::DefData) { | 822 | fn collect_derives(&mut self, attrs: &Attrs, def: &raw::DefData) { |
diff --git a/crates/ra_hir_def/src/nameres/path_resolution.rs b/crates/ra_hir_def/src/nameres/path_resolution.rs index 695014c7b..fd6422d60 100644 --- a/crates/ra_hir_def/src/nameres/path_resolution.rs +++ b/crates/ra_hir_def/src/nameres/path_resolution.rs | |||
@@ -21,6 +21,7 @@ use crate::{ | |||
21 | nameres::{BuiltinShadowMode, CrateDefMap}, | 21 | nameres::{BuiltinShadowMode, CrateDefMap}, |
22 | path::{ModPath, PathKind}, | 22 | path::{ModPath, PathKind}, |
23 | per_ns::PerNs, | 23 | per_ns::PerNs, |
24 | visibility::{RawVisibility, Visibility}, | ||
24 | AdtId, CrateId, EnumVariantId, LocalModuleId, ModuleDefId, ModuleId, | 25 | AdtId, CrateId, EnumVariantId, LocalModuleId, ModuleDefId, ModuleId, |
25 | }; | 26 | }; |
26 | 27 | ||
@@ -61,7 +62,35 @@ impl ResolvePathResult { | |||
61 | 62 | ||
62 | impl CrateDefMap { | 63 | impl CrateDefMap { |
63 | pub(super) fn resolve_name_in_extern_prelude(&self, name: &Name) -> PerNs { | 64 | pub(super) fn resolve_name_in_extern_prelude(&self, name: &Name) -> PerNs { |
64 | self.extern_prelude.get(name).map_or(PerNs::none(), |&it| PerNs::types(it)) | 65 | self.extern_prelude |
66 | .get(name) | ||
67 | .map_or(PerNs::none(), |&it| PerNs::types(it, Visibility::Public)) | ||
68 | } | ||
69 | |||
70 | pub(crate) fn resolve_visibility( | ||
71 | &self, | ||
72 | db: &impl DefDatabase, | ||
73 | original_module: LocalModuleId, | ||
74 | visibility: &RawVisibility, | ||
75 | ) -> Option<Visibility> { | ||
76 | match visibility { | ||
77 | RawVisibility::Module(path) => { | ||
78 | let (result, remaining) = | ||
79 | self.resolve_path(db, original_module, &path, BuiltinShadowMode::Module); | ||
80 | if remaining.is_some() { | ||
81 | return None; | ||
82 | } | ||
83 | let types = result.take_types()?; | ||
84 | match types { | ||
85 | ModuleDefId::ModuleId(m) => Some(Visibility::Module(m)), | ||
86 | _ => { | ||
87 | // error: visibility needs to refer to module | ||
88 | None | ||
89 | } | ||
90 | } | ||
91 | } | ||
92 | RawVisibility::Public => Some(Visibility::Public), | ||
93 | } | ||
65 | } | 94 | } |
66 | 95 | ||
67 | // Returns Yes if we are sure that additions to `ItemMap` wouldn't change | 96 | // Returns Yes if we are sure that additions to `ItemMap` wouldn't change |
@@ -88,17 +117,21 @@ impl CrateDefMap { | |||
88 | PathKind::DollarCrate(krate) => { | 117 | PathKind::DollarCrate(krate) => { |
89 | if krate == self.krate { | 118 | if krate == self.krate { |
90 | tested_by!(macro_dollar_crate_self); | 119 | tested_by!(macro_dollar_crate_self); |
91 | PerNs::types(ModuleId { krate: self.krate, local_id: self.root }.into()) | 120 | PerNs::types( |
121 | ModuleId { krate: self.krate, local_id: self.root }.into(), | ||
122 | Visibility::Public, | ||
123 | ) | ||
92 | } else { | 124 | } else { |
93 | let def_map = db.crate_def_map(krate); | 125 | let def_map = db.crate_def_map(krate); |
94 | let module = ModuleId { krate, local_id: def_map.root }; | 126 | let module = ModuleId { krate, local_id: def_map.root }; |
95 | tested_by!(macro_dollar_crate_other); | 127 | tested_by!(macro_dollar_crate_other); |
96 | PerNs::types(module.into()) | 128 | PerNs::types(module.into(), Visibility::Public) |
97 | } | 129 | } |
98 | } | 130 | } |
99 | PathKind::Crate => { | 131 | PathKind::Crate => PerNs::types( |
100 | PerNs::types(ModuleId { krate: self.krate, local_id: self.root }.into()) | 132 | ModuleId { krate: self.krate, local_id: self.root }.into(), |
101 | } | 133 | Visibility::Public, |
134 | ), | ||
102 | // plain import or absolute path in 2015: crate-relative with | 135 | // plain import or absolute path in 2015: crate-relative with |
103 | // fallback to extern prelude (with the simplification in | 136 | // fallback to extern prelude (with the simplification in |
104 | // rust-lang/rust#57745) | 137 | // rust-lang/rust#57745) |
@@ -126,7 +159,10 @@ impl CrateDefMap { | |||
126 | let m = successors(Some(original_module), |m| self.modules[*m].parent) | 159 | let m = successors(Some(original_module), |m| self.modules[*m].parent) |
127 | .nth(lvl as usize); | 160 | .nth(lvl as usize); |
128 | if let Some(local_id) = m { | 161 | if let Some(local_id) = m { |
129 | PerNs::types(ModuleId { krate: self.krate, local_id }.into()) | 162 | PerNs::types( |
163 | ModuleId { krate: self.krate, local_id }.into(), | ||
164 | Visibility::Public, | ||
165 | ) | ||
130 | } else { | 166 | } else { |
131 | log::debug!("super path in root module"); | 167 | log::debug!("super path in root module"); |
132 | return ResolvePathResult::empty(ReachedFixedPoint::Yes); | 168 | return ResolvePathResult::empty(ReachedFixedPoint::Yes); |
@@ -140,7 +176,7 @@ impl CrateDefMap { | |||
140 | }; | 176 | }; |
141 | if let Some(def) = self.extern_prelude.get(&segment) { | 177 | if let Some(def) = self.extern_prelude.get(&segment) { |
142 | log::debug!("absolute path {:?} resolved to crate {:?}", path, def); | 178 | log::debug!("absolute path {:?} resolved to crate {:?}", path, def); |
143 | PerNs::types(*def) | 179 | PerNs::types(*def, Visibility::Public) |
144 | } else { | 180 | } else { |
145 | return ResolvePathResult::empty(ReachedFixedPoint::No); // extern crate declarations can add to the extern prelude | 181 | return ResolvePathResult::empty(ReachedFixedPoint::No); // extern crate declarations can add to the extern prelude |
146 | } | 182 | } |
@@ -148,7 +184,7 @@ impl CrateDefMap { | |||
148 | }; | 184 | }; |
149 | 185 | ||
150 | for (i, segment) in segments { | 186 | for (i, segment) in segments { |
151 | let curr = match curr_per_ns.take_types() { | 187 | let (curr, vis) = match curr_per_ns.take_types_vis() { |
152 | Some(r) => r, | 188 | Some(r) => r, |
153 | None => { | 189 | None => { |
154 | // we still have path segments left, but the path so far | 190 | // we still have path segments left, but the path so far |
@@ -189,11 +225,11 @@ impl CrateDefMap { | |||
189 | match enum_data.variant(&segment) { | 225 | match enum_data.variant(&segment) { |
190 | Some(local_id) => { | 226 | Some(local_id) => { |
191 | let variant = EnumVariantId { parent: e, local_id }; | 227 | let variant = EnumVariantId { parent: e, local_id }; |
192 | PerNs::both(variant.into(), variant.into()) | 228 | PerNs::both(variant.into(), variant.into(), Visibility::Public) |
193 | } | 229 | } |
194 | None => { | 230 | None => { |
195 | return ResolvePathResult::with( | 231 | return ResolvePathResult::with( |
196 | PerNs::types(e.into()), | 232 | PerNs::types(e.into(), vis), |
197 | ReachedFixedPoint::Yes, | 233 | ReachedFixedPoint::Yes, |
198 | Some(i), | 234 | Some(i), |
199 | Some(self.krate), | 235 | Some(self.krate), |
@@ -211,7 +247,7 @@ impl CrateDefMap { | |||
211 | ); | 247 | ); |
212 | 248 | ||
213 | return ResolvePathResult::with( | 249 | return ResolvePathResult::with( |
214 | PerNs::types(s), | 250 | PerNs::types(s, vis), |
215 | ReachedFixedPoint::Yes, | 251 | ReachedFixedPoint::Yes, |
216 | Some(i), | 252 | Some(i), |
217 | Some(self.krate), | 253 | Some(self.krate), |
@@ -235,11 +271,15 @@ impl CrateDefMap { | |||
235 | // - current module / scope | 271 | // - current module / scope |
236 | // - extern prelude | 272 | // - extern prelude |
237 | // - std prelude | 273 | // - std prelude |
238 | let from_legacy_macro = | 274 | let from_legacy_macro = self[module] |
239 | self[module].scope.get_legacy_macro(name).map_or_else(PerNs::none, PerNs::macros); | 275 | .scope |
276 | .get_legacy_macro(name) | ||
277 | .map_or_else(PerNs::none, |m| PerNs::macros(m, Visibility::Public)); | ||
240 | let from_scope = self[module].scope.get(name, shadow); | 278 | let from_scope = self[module].scope.get(name, shadow); |
241 | let from_extern_prelude = | 279 | let from_extern_prelude = self |
242 | self.extern_prelude.get(name).map_or(PerNs::none(), |&it| PerNs::types(it)); | 280 | .extern_prelude |
281 | .get(name) | ||
282 | .map_or(PerNs::none(), |&it| PerNs::types(it, Visibility::Public)); | ||
243 | let from_prelude = self.resolve_in_prelude(db, name, shadow); | 283 | let from_prelude = self.resolve_in_prelude(db, name, shadow); |
244 | 284 | ||
245 | from_legacy_macro.or(from_scope).or(from_extern_prelude).or(from_prelude) | 285 | from_legacy_macro.or(from_scope).or(from_extern_prelude).or(from_prelude) |
diff --git a/crates/ra_hir_def/src/nameres/raw.rs b/crates/ra_hir_def/src/nameres/raw.rs index 73dc08745..fac1169ef 100644 --- a/crates/ra_hir_def/src/nameres/raw.rs +++ b/crates/ra_hir_def/src/nameres/raw.rs | |||
@@ -16,12 +16,15 @@ use hir_expand::{ | |||
16 | use ra_arena::{impl_arena_id, Arena, RawId}; | 16 | use ra_arena::{impl_arena_id, Arena, RawId}; |
17 | use ra_prof::profile; | 17 | use ra_prof::profile; |
18 | use ra_syntax::{ | 18 | use ra_syntax::{ |
19 | ast::{self, AttrsOwner, NameOwner}, | 19 | ast::{self, AttrsOwner, NameOwner, VisibilityOwner}, |
20 | AstNode, | 20 | AstNode, |
21 | }; | 21 | }; |
22 | use test_utils::tested_by; | 22 | use test_utils::tested_by; |
23 | 23 | ||
24 | use crate::{attr::Attrs, db::DefDatabase, path::ModPath, FileAstId, HirFileId, InFile}; | 24 | use crate::{ |
25 | attr::Attrs, db::DefDatabase, path::ModPath, visibility::RawVisibility, FileAstId, HirFileId, | ||
26 | InFile, | ||
27 | }; | ||
25 | 28 | ||
26 | /// `RawItems` is a set of top-level items in a file (except for impls). | 29 | /// `RawItems` is a set of top-level items in a file (except for impls). |
27 | /// | 30 | /// |
@@ -122,8 +125,17 @@ impl_arena_id!(Module); | |||
122 | 125 | ||
123 | #[derive(Debug, PartialEq, Eq)] | 126 | #[derive(Debug, PartialEq, Eq)] |
124 | pub(super) enum ModuleData { | 127 | pub(super) enum ModuleData { |
125 | Declaration { name: Name, ast_id: FileAstId<ast::Module> }, | 128 | Declaration { |
126 | Definition { name: Name, ast_id: FileAstId<ast::Module>, items: Vec<RawItem> }, | 129 | name: Name, |
130 | visibility: RawVisibility, | ||
131 | ast_id: FileAstId<ast::Module>, | ||
132 | }, | ||
133 | Definition { | ||
134 | name: Name, | ||
135 | visibility: RawVisibility, | ||
136 | ast_id: FileAstId<ast::Module>, | ||
137 | items: Vec<RawItem>, | ||
138 | }, | ||
127 | } | 139 | } |
128 | 140 | ||
129 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | 141 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
@@ -138,6 +150,7 @@ pub struct ImportData { | |||
138 | pub(super) is_prelude: bool, | 150 | pub(super) is_prelude: bool, |
139 | pub(super) is_extern_crate: bool, | 151 | pub(super) is_extern_crate: bool, |
140 | pub(super) is_macro_use: bool, | 152 | pub(super) is_macro_use: bool, |
153 | pub(super) visibility: RawVisibility, | ||
141 | } | 154 | } |
142 | 155 | ||
143 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | 156 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
@@ -148,6 +161,7 @@ impl_arena_id!(Def); | |||
148 | pub(super) struct DefData { | 161 | pub(super) struct DefData { |
149 | pub(super) name: Name, | 162 | pub(super) name: Name, |
150 | pub(super) kind: DefKind, | 163 | pub(super) kind: DefKind, |
164 | pub(super) visibility: RawVisibility, | ||
151 | } | 165 | } |
152 | 166 | ||
153 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] | 167 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] |
@@ -218,6 +232,7 @@ impl RawItemsCollector { | |||
218 | 232 | ||
219 | fn add_item(&mut self, current_module: Option<Module>, item: ast::ModuleItem) { | 233 | fn add_item(&mut self, current_module: Option<Module>, item: ast::ModuleItem) { |
220 | let attrs = self.parse_attrs(&item); | 234 | let attrs = self.parse_attrs(&item); |
235 | let visibility = RawVisibility::from_ast_with_hygiene(item.visibility(), &self.hygiene); | ||
221 | let (kind, name) = match item { | 236 | let (kind, name) = match item { |
222 | ast::ModuleItem::Module(module) => { | 237 | ast::ModuleItem::Module(module) => { |
223 | self.add_module(current_module, module); | 238 | self.add_module(current_module, module); |
@@ -266,7 +281,7 @@ impl RawItemsCollector { | |||
266 | }; | 281 | }; |
267 | if let Some(name) = name { | 282 | if let Some(name) = name { |
268 | let name = name.as_name(); | 283 | let name = name.as_name(); |
269 | let def = self.raw_items.defs.alloc(DefData { name, kind }); | 284 | let def = self.raw_items.defs.alloc(DefData { name, kind, visibility }); |
270 | self.push_item(current_module, attrs, RawItemKind::Def(def)); | 285 | self.push_item(current_module, attrs, RawItemKind::Def(def)); |
271 | } | 286 | } |
272 | } | 287 | } |
@@ -277,10 +292,12 @@ impl RawItemsCollector { | |||
277 | None => return, | 292 | None => return, |
278 | }; | 293 | }; |
279 | let attrs = self.parse_attrs(&module); | 294 | let attrs = self.parse_attrs(&module); |
295 | let visibility = RawVisibility::from_ast_with_hygiene(module.visibility(), &self.hygiene); | ||
280 | 296 | ||
281 | let ast_id = self.source_ast_id_map.ast_id(&module); | 297 | let ast_id = self.source_ast_id_map.ast_id(&module); |
282 | if module.has_semi() { | 298 | if module.has_semi() { |
283 | let item = self.raw_items.modules.alloc(ModuleData::Declaration { name, ast_id }); | 299 | let item = |
300 | self.raw_items.modules.alloc(ModuleData::Declaration { name, visibility, ast_id }); | ||
284 | self.push_item(current_module, attrs, RawItemKind::Module(item)); | 301 | self.push_item(current_module, attrs, RawItemKind::Module(item)); |
285 | return; | 302 | return; |
286 | } | 303 | } |
@@ -288,6 +305,7 @@ impl RawItemsCollector { | |||
288 | if let Some(item_list) = module.item_list() { | 305 | if let Some(item_list) = module.item_list() { |
289 | let item = self.raw_items.modules.alloc(ModuleData::Definition { | 306 | let item = self.raw_items.modules.alloc(ModuleData::Definition { |
290 | name, | 307 | name, |
308 | visibility, | ||
291 | ast_id, | 309 | ast_id, |
292 | items: Vec::new(), | 310 | items: Vec::new(), |
293 | }); | 311 | }); |
@@ -302,6 +320,7 @@ impl RawItemsCollector { | |||
302 | // FIXME: cfg_attr | 320 | // FIXME: cfg_attr |
303 | let is_prelude = use_item.has_atom_attr("prelude_import"); | 321 | let is_prelude = use_item.has_atom_attr("prelude_import"); |
304 | let attrs = self.parse_attrs(&use_item); | 322 | let attrs = self.parse_attrs(&use_item); |
323 | let visibility = RawVisibility::from_ast_with_hygiene(use_item.visibility(), &self.hygiene); | ||
305 | 324 | ||
306 | let mut buf = Vec::new(); | 325 | let mut buf = Vec::new(); |
307 | ModPath::expand_use_item( | 326 | ModPath::expand_use_item( |
@@ -315,6 +334,7 @@ impl RawItemsCollector { | |||
315 | is_prelude, | 334 | is_prelude, |
316 | is_extern_crate: false, | 335 | is_extern_crate: false, |
317 | is_macro_use: false, | 336 | is_macro_use: false, |
337 | visibility: visibility.clone(), | ||
318 | }; | 338 | }; |
319 | buf.push(import_data); | 339 | buf.push(import_data); |
320 | }, | 340 | }, |
@@ -331,6 +351,8 @@ impl RawItemsCollector { | |||
331 | ) { | 351 | ) { |
332 | if let Some(name_ref) = extern_crate.name_ref() { | 352 | if let Some(name_ref) = extern_crate.name_ref() { |
333 | let path = ModPath::from_name_ref(&name_ref); | 353 | let path = ModPath::from_name_ref(&name_ref); |
354 | let visibility = | ||
355 | RawVisibility::from_ast_with_hygiene(extern_crate.visibility(), &self.hygiene); | ||
334 | let alias = extern_crate.alias().and_then(|a| a.name()).map(|it| it.as_name()); | 356 | let alias = extern_crate.alias().and_then(|a| a.name()).map(|it| it.as_name()); |
335 | let attrs = self.parse_attrs(&extern_crate); | 357 | let attrs = self.parse_attrs(&extern_crate); |
336 | // FIXME: cfg_attr | 358 | // FIXME: cfg_attr |
@@ -342,6 +364,7 @@ impl RawItemsCollector { | |||
342 | is_prelude: false, | 364 | is_prelude: false, |
343 | is_extern_crate: true, | 365 | is_extern_crate: true, |
344 | is_macro_use, | 366 | is_macro_use, |
367 | visibility, | ||
345 | }; | 368 | }; |
346 | self.push_import(current_module, attrs, import_data); | 369 | self.push_import(current_module, attrs, import_data); |
347 | } | 370 | } |
diff --git a/crates/ra_hir_def/src/nameres/tests.rs b/crates/ra_hir_def/src/nameres/tests.rs index ff474b53b..78bcdc850 100644 --- a/crates/ra_hir_def/src/nameres/tests.rs +++ b/crates/ra_hir_def/src/nameres/tests.rs | |||
@@ -12,8 +12,8 @@ use test_utils::covers; | |||
12 | 12 | ||
13 | use crate::{db::DefDatabase, nameres::*, test_db::TestDB, LocalModuleId}; | 13 | use crate::{db::DefDatabase, nameres::*, test_db::TestDB, LocalModuleId}; |
14 | 14 | ||
15 | fn def_map(fixtute: &str) -> String { | 15 | fn def_map(fixture: &str) -> String { |
16 | let dm = compute_crate_def_map(fixtute); | 16 | let dm = compute_crate_def_map(fixture); |
17 | render_crate_def_map(&dm) | 17 | render_crate_def_map(&dm) |
18 | } | 18 | } |
19 | 19 | ||
@@ -32,7 +32,7 @@ fn render_crate_def_map(map: &CrateDefMap) -> String { | |||
32 | *buf += path; | 32 | *buf += path; |
33 | *buf += "\n"; | 33 | *buf += "\n"; |
34 | 34 | ||
35 | let mut entries = map.modules[module].scope.collect_resolutions(); | 35 | let mut entries: Vec<_> = map.modules[module].scope.resolutions().collect(); |
36 | entries.sort_by_key(|(name, _)| name.clone()); | 36 | entries.sort_by_key(|(name, _)| name.clone()); |
37 | 37 | ||
38 | for (name, def) in entries { | 38 | for (name, def) in entries { |
diff --git a/crates/ra_hir_def/src/nameres/tests/globs.rs b/crates/ra_hir_def/src/nameres/tests/globs.rs index 5e24cb94d..71fa0abe8 100644 --- a/crates/ra_hir_def/src/nameres/tests/globs.rs +++ b/crates/ra_hir_def/src/nameres/tests/globs.rs | |||
@@ -74,6 +74,83 @@ fn glob_2() { | |||
74 | } | 74 | } |
75 | 75 | ||
76 | #[test] | 76 | #[test] |
77 | fn glob_privacy_1() { | ||
78 | let map = def_map( | ||
79 | " | ||
80 | //- /lib.rs | ||
81 | mod foo; | ||
82 | use foo::*; | ||
83 | |||
84 | //- /foo/mod.rs | ||
85 | pub mod bar; | ||
86 | pub use self::bar::*; | ||
87 | struct PrivateStructFoo; | ||
88 | |||
89 | //- /foo/bar.rs | ||
90 | pub struct Baz; | ||
91 | struct PrivateStructBar; | ||
92 | pub use super::*; | ||
93 | ", | ||
94 | ); | ||
95 | assert_snapshot!(map, @r###" | ||
96 | crate | ||
97 | Baz: t v | ||
98 | bar: t | ||
99 | foo: t | ||
100 | |||
101 | crate::foo | ||
102 | Baz: t v | ||
103 | PrivateStructFoo: t v | ||
104 | bar: t | ||
105 | |||
106 | crate::foo::bar | ||
107 | Baz: t v | ||
108 | PrivateStructBar: t v | ||
109 | PrivateStructFoo: t v | ||
110 | bar: t | ||
111 | "### | ||
112 | ); | ||
113 | } | ||
114 | |||
115 | #[test] | ||
116 | fn glob_privacy_2() { | ||
117 | let map = def_map( | ||
118 | " | ||
119 | //- /lib.rs | ||
120 | mod foo; | ||
121 | use foo::*; | ||
122 | use foo::bar::*; | ||
123 | |||
124 | //- /foo/mod.rs | ||
125 | mod bar; | ||
126 | fn Foo() {}; | ||
127 | pub struct Foo {}; | ||
128 | |||
129 | //- /foo/bar.rs | ||
130 | pub(super) struct PrivateBaz; | ||
131 | struct PrivateBar; | ||
132 | pub(crate) struct PubCrateStruct; | ||
133 | ", | ||
134 | ); | ||
135 | assert_snapshot!(map, @r###" | ||
136 | crate | ||
137 | Foo: t | ||
138 | PubCrateStruct: t v | ||
139 | foo: t | ||
140 | |||
141 | crate::foo | ||
142 | Foo: t v | ||
143 | bar: t | ||
144 | |||
145 | crate::foo::bar | ||
146 | PrivateBar: t v | ||
147 | PrivateBaz: t v | ||
148 | PubCrateStruct: t v | ||
149 | "### | ||
150 | ); | ||
151 | } | ||
152 | |||
153 | #[test] | ||
77 | fn glob_across_crates() { | 154 | fn glob_across_crates() { |
78 | covers!(glob_across_crates); | 155 | covers!(glob_across_crates); |
79 | let map = def_map( | 156 | let map = def_map( |
@@ -93,6 +170,26 @@ fn glob_across_crates() { | |||
93 | } | 170 | } |
94 | 171 | ||
95 | #[test] | 172 | #[test] |
173 | fn glob_privacy_across_crates() { | ||
174 | covers!(glob_across_crates); | ||
175 | let map = def_map( | ||
176 | " | ||
177 | //- /main.rs crate:main deps:test_crate | ||
178 | use test_crate::*; | ||
179 | |||
180 | //- /lib.rs crate:test_crate | ||
181 | pub struct Baz; | ||
182 | struct Foo; | ||
183 | ", | ||
184 | ); | ||
185 | assert_snapshot!(map, @r###" | ||
186 | â‹®crate | ||
187 | â‹®Baz: t v | ||
188 | "### | ||
189 | ); | ||
190 | } | ||
191 | |||
192 | #[test] | ||
96 | fn glob_enum() { | 193 | fn glob_enum() { |
97 | covers!(glob_enum); | 194 | covers!(glob_enum); |
98 | let map = def_map( | 195 | let map = def_map( |
diff --git a/crates/ra_hir_def/src/nameres/tests/incremental.rs b/crates/ra_hir_def/src/nameres/tests/incremental.rs index ef2e9435c..faeb7aa4d 100644 --- a/crates/ra_hir_def/src/nameres/tests/incremental.rs +++ b/crates/ra_hir_def/src/nameres/tests/incremental.rs | |||
@@ -116,7 +116,7 @@ fn typing_inside_a_macro_should_not_invalidate_def_map() { | |||
116 | let events = db.log_executed(|| { | 116 | let events = db.log_executed(|| { |
117 | let crate_def_map = db.crate_def_map(krate); | 117 | let crate_def_map = db.crate_def_map(krate); |
118 | let (_, module_data) = crate_def_map.modules.iter().last().unwrap(); | 118 | let (_, module_data) = crate_def_map.modules.iter().last().unwrap(); |
119 | assert_eq!(module_data.scope.collect_resolutions().len(), 1); | 119 | assert_eq!(module_data.scope.resolutions().collect::<Vec<_>>().len(), 1); |
120 | }); | 120 | }); |
121 | assert!(format!("{:?}", events).contains("crate_def_map"), "{:#?}", events) | 121 | assert!(format!("{:?}", events).contains("crate_def_map"), "{:#?}", events) |
122 | } | 122 | } |
@@ -126,7 +126,7 @@ fn typing_inside_a_macro_should_not_invalidate_def_map() { | |||
126 | let events = db.log_executed(|| { | 126 | let events = db.log_executed(|| { |
127 | let crate_def_map = db.crate_def_map(krate); | 127 | let crate_def_map = db.crate_def_map(krate); |
128 | let (_, module_data) = crate_def_map.modules.iter().last().unwrap(); | 128 | let (_, module_data) = crate_def_map.modules.iter().last().unwrap(); |
129 | assert_eq!(module_data.scope.collect_resolutions().len(), 1); | 129 | assert_eq!(module_data.scope.resolutions().collect::<Vec<_>>().len(), 1); |
130 | }); | 130 | }); |
131 | assert!(!format!("{:?}", events).contains("crate_def_map"), "{:#?}", events) | 131 | assert!(!format!("{:?}", events).contains("crate_def_map"), "{:#?}", events) |
132 | } | 132 | } |
diff --git a/crates/ra_hir_def/src/path.rs b/crates/ra_hir_def/src/path.rs index 8e1294201..107d2d799 100644 --- a/crates/ra_hir_def/src/path.rs +++ b/crates/ra_hir_def/src/path.rs | |||
@@ -130,6 +130,14 @@ impl Path { | |||
130 | Path { type_anchor: None, mod_path: name_ref.as_name().into(), generic_args: vec![None] } | 130 | Path { type_anchor: None, mod_path: name_ref.as_name().into(), generic_args: vec![None] } |
131 | } | 131 | } |
132 | 132 | ||
133 | /// Converts a known mod path to `Path`. | ||
134 | pub(crate) fn from_known_path( | ||
135 | path: ModPath, | ||
136 | generic_args: Vec<Option<Arc<GenericArgs>>>, | ||
137 | ) -> Path { | ||
138 | Path { type_anchor: None, mod_path: path, generic_args } | ||
139 | } | ||
140 | |||
133 | pub fn kind(&self) -> &PathKind { | 141 | pub fn kind(&self) -> &PathKind { |
134 | &self.mod_path.kind | 142 | &self.mod_path.kind |
135 | } | 143 | } |
diff --git a/crates/ra_hir_def/src/per_ns.rs b/crates/ra_hir_def/src/per_ns.rs index 3a5105028..6e435c8c1 100644 --- a/crates/ra_hir_def/src/per_ns.rs +++ b/crates/ra_hir_def/src/per_ns.rs | |||
@@ -5,13 +5,13 @@ | |||
5 | 5 | ||
6 | use hir_expand::MacroDefId; | 6 | use hir_expand::MacroDefId; |
7 | 7 | ||
8 | use crate::ModuleDefId; | 8 | use crate::{visibility::Visibility, ModuleDefId}; |
9 | 9 | ||
10 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] | 10 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] |
11 | pub struct PerNs { | 11 | pub struct PerNs { |
12 | pub types: Option<ModuleDefId>, | 12 | pub types: Option<(ModuleDefId, Visibility)>, |
13 | pub values: Option<ModuleDefId>, | 13 | pub values: Option<(ModuleDefId, Visibility)>, |
14 | pub macros: Option<MacroDefId>, | 14 | pub macros: Option<(MacroDefId, Visibility)>, |
15 | } | 15 | } |
16 | 16 | ||
17 | impl Default for PerNs { | 17 | impl Default for PerNs { |
@@ -25,20 +25,20 @@ impl PerNs { | |||
25 | PerNs { types: None, values: None, macros: None } | 25 | PerNs { types: None, values: None, macros: None } |
26 | } | 26 | } |
27 | 27 | ||
28 | pub fn values(t: ModuleDefId) -> PerNs { | 28 | pub fn values(t: ModuleDefId, v: Visibility) -> PerNs { |
29 | PerNs { types: None, values: Some(t), macros: None } | 29 | PerNs { types: None, values: Some((t, v)), macros: None } |
30 | } | 30 | } |
31 | 31 | ||
32 | pub fn types(t: ModuleDefId) -> PerNs { | 32 | pub fn types(t: ModuleDefId, v: Visibility) -> PerNs { |
33 | PerNs { types: Some(t), values: None, macros: None } | 33 | PerNs { types: Some((t, v)), values: None, macros: None } |
34 | } | 34 | } |
35 | 35 | ||
36 | pub fn both(types: ModuleDefId, values: ModuleDefId) -> PerNs { | 36 | pub fn both(types: ModuleDefId, values: ModuleDefId, v: Visibility) -> PerNs { |
37 | PerNs { types: Some(types), values: Some(values), macros: None } | 37 | PerNs { types: Some((types, v)), values: Some((values, v)), macros: None } |
38 | } | 38 | } |
39 | 39 | ||
40 | pub fn macros(macro_: MacroDefId) -> PerNs { | 40 | pub fn macros(macro_: MacroDefId, v: Visibility) -> PerNs { |
41 | PerNs { types: None, values: None, macros: Some(macro_) } | 41 | PerNs { types: None, values: None, macros: Some((macro_, v)) } |
42 | } | 42 | } |
43 | 43 | ||
44 | pub fn is_none(&self) -> bool { | 44 | pub fn is_none(&self) -> bool { |
@@ -46,15 +46,35 @@ impl PerNs { | |||
46 | } | 46 | } |
47 | 47 | ||
48 | pub fn take_types(self) -> Option<ModuleDefId> { | 48 | pub fn take_types(self) -> Option<ModuleDefId> { |
49 | self.types.map(|it| it.0) | ||
50 | } | ||
51 | |||
52 | pub fn take_types_vis(self) -> Option<(ModuleDefId, Visibility)> { | ||
49 | self.types | 53 | self.types |
50 | } | 54 | } |
51 | 55 | ||
52 | pub fn take_values(self) -> Option<ModuleDefId> { | 56 | pub fn take_values(self) -> Option<ModuleDefId> { |
53 | self.values | 57 | self.values.map(|it| it.0) |
54 | } | 58 | } |
55 | 59 | ||
56 | pub fn take_macros(self) -> Option<MacroDefId> { | 60 | pub fn take_macros(self) -> Option<MacroDefId> { |
57 | self.macros | 61 | self.macros.map(|it| it.0) |
62 | } | ||
63 | |||
64 | pub fn filter_visibility(self, mut f: impl FnMut(Visibility) -> bool) -> PerNs { | ||
65 | PerNs { | ||
66 | types: self.types.filter(|(_, v)| f(*v)), | ||
67 | values: self.values.filter(|(_, v)| f(*v)), | ||
68 | macros: self.macros.filter(|(_, v)| f(*v)), | ||
69 | } | ||
70 | } | ||
71 | |||
72 | pub fn with_visibility(self, vis: Visibility) -> PerNs { | ||
73 | PerNs { | ||
74 | types: self.types.map(|(it, _)| (it, vis)), | ||
75 | values: self.values.map(|(it, _)| (it, vis)), | ||
76 | macros: self.macros.map(|(it, _)| (it, vis)), | ||
77 | } | ||
58 | } | 78 | } |
59 | 79 | ||
60 | pub fn or(self, other: PerNs) -> PerNs { | 80 | pub fn or(self, other: PerNs) -> PerNs { |
diff --git a/crates/ra_hir_def/src/resolver.rs b/crates/ra_hir_def/src/resolver.rs index cf3c33d78..5d16dd087 100644 --- a/crates/ra_hir_def/src/resolver.rs +++ b/crates/ra_hir_def/src/resolver.rs | |||
@@ -19,6 +19,7 @@ use crate::{ | |||
19 | nameres::CrateDefMap, | 19 | nameres::CrateDefMap, |
20 | path::{ModPath, PathKind}, | 20 | path::{ModPath, PathKind}, |
21 | per_ns::PerNs, | 21 | per_ns::PerNs, |
22 | visibility::{RawVisibility, Visibility}, | ||
22 | AdtId, AssocContainerId, ConstId, ContainerId, DefWithBodyId, EnumId, EnumVariantId, | 23 | AdtId, AssocContainerId, ConstId, ContainerId, DefWithBodyId, EnumId, EnumVariantId, |
23 | FunctionId, GenericDefId, HasModule, ImplId, LocalModuleId, Lookup, ModuleDefId, ModuleId, | 24 | FunctionId, GenericDefId, HasModule, ImplId, LocalModuleId, Lookup, ModuleDefId, ModuleId, |
24 | StaticId, StructId, TraitId, TypeAliasId, TypeParamId, VariantId, | 25 | StaticId, StructId, TraitId, TypeAliasId, TypeParamId, VariantId, |
@@ -231,6 +232,23 @@ impl Resolver { | |||
231 | Some(res) | 232 | Some(res) |
232 | } | 233 | } |
233 | 234 | ||
235 | pub fn resolve_visibility( | ||
236 | &self, | ||
237 | db: &impl DefDatabase, | ||
238 | visibility: &RawVisibility, | ||
239 | ) -> Option<Visibility> { | ||
240 | match visibility { | ||
241 | RawVisibility::Module(_) => { | ||
242 | let (item_map, module) = match self.module() { | ||
243 | Some(it) => it, | ||
244 | None => return None, | ||
245 | }; | ||
246 | item_map.resolve_visibility(db, module, visibility) | ||
247 | } | ||
248 | RawVisibility::Public => Some(Visibility::Public), | ||
249 | } | ||
250 | } | ||
251 | |||
234 | pub fn resolve_path_in_value_ns( | 252 | pub fn resolve_path_in_value_ns( |
235 | &self, | 253 | &self, |
236 | db: &impl DefDatabase, | 254 | db: &impl DefDatabase, |
@@ -448,10 +466,10 @@ impl Scope { | |||
448 | f(name.clone(), ScopeDef::PerNs(def)); | 466 | f(name.clone(), ScopeDef::PerNs(def)); |
449 | }); | 467 | }); |
450 | m.crate_def_map[m.module_id].scope.legacy_macros().for_each(|(name, macro_)| { | 468 | m.crate_def_map[m.module_id].scope.legacy_macros().for_each(|(name, macro_)| { |
451 | f(name.clone(), ScopeDef::PerNs(PerNs::macros(macro_))); | 469 | f(name.clone(), ScopeDef::PerNs(PerNs::macros(macro_, Visibility::Public))); |
452 | }); | 470 | }); |
453 | m.crate_def_map.extern_prelude.iter().for_each(|(name, &def)| { | 471 | m.crate_def_map.extern_prelude.iter().for_each(|(name, &def)| { |
454 | f(name.clone(), ScopeDef::PerNs(PerNs::types(def.into()))); | 472 | f(name.clone(), ScopeDef::PerNs(PerNs::types(def.into(), Visibility::Public))); |
455 | }); | 473 | }); |
456 | if let Some(prelude) = m.crate_def_map.prelude { | 474 | if let Some(prelude) = m.crate_def_map.prelude { |
457 | let prelude_def_map = db.crate_def_map(prelude.krate); | 475 | let prelude_def_map = db.crate_def_map(prelude.krate); |
@@ -626,7 +644,7 @@ impl HasResolver for ContainerId { | |||
626 | fn resolver(self, db: &impl DefDatabase) -> Resolver { | 644 | fn resolver(self, db: &impl DefDatabase) -> Resolver { |
627 | match self { | 645 | match self { |
628 | ContainerId::ModuleId(it) => it.resolver(db), | 646 | ContainerId::ModuleId(it) => it.resolver(db), |
629 | ContainerId::DefWithBodyId(it) => it.resolver(db), | 647 | ContainerId::DefWithBodyId(it) => it.module(db).resolver(db), |
630 | } | 648 | } |
631 | } | 649 | } |
632 | } | 650 | } |
diff --git a/crates/ra_hir_def/src/visibility.rs b/crates/ra_hir_def/src/visibility.rs new file mode 100644 index 000000000..d8296da4b --- /dev/null +++ b/crates/ra_hir_def/src/visibility.rs | |||
@@ -0,0 +1,120 @@ | |||
1 | //! Defines hir-level representation of visibility (e.g. `pub` and `pub(crate)`). | ||
2 | |||
3 | use hir_expand::{hygiene::Hygiene, InFile}; | ||
4 | use ra_syntax::ast; | ||
5 | |||
6 | use crate::{ | ||
7 | db::DefDatabase, | ||
8 | path::{ModPath, PathKind}, | ||
9 | ModuleId, | ||
10 | }; | ||
11 | |||
12 | /// Visibility of an item, not yet resolved. | ||
13 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
14 | pub enum RawVisibility { | ||
15 | /// `pub(in module)`, `pub(crate)` or `pub(super)`. Also private, which is | ||
16 | /// equivalent to `pub(self)`. | ||
17 | Module(ModPath), | ||
18 | /// `pub`. | ||
19 | Public, | ||
20 | } | ||
21 | |||
22 | impl RawVisibility { | ||
23 | const fn private() -> RawVisibility { | ||
24 | let path = ModPath { kind: PathKind::Super(0), segments: Vec::new() }; | ||
25 | RawVisibility::Module(path) | ||
26 | } | ||
27 | |||
28 | pub(crate) fn from_ast( | ||
29 | db: &impl DefDatabase, | ||
30 | node: InFile<Option<ast::Visibility>>, | ||
31 | ) -> RawVisibility { | ||
32 | Self::from_ast_with_hygiene(node.value, &Hygiene::new(db, node.file_id)) | ||
33 | } | ||
34 | |||
35 | pub(crate) fn from_ast_with_hygiene( | ||
36 | node: Option<ast::Visibility>, | ||
37 | hygiene: &Hygiene, | ||
38 | ) -> RawVisibility { | ||
39 | let node = match node { | ||
40 | None => return RawVisibility::private(), | ||
41 | Some(node) => node, | ||
42 | }; | ||
43 | match node.kind() { | ||
44 | ast::VisibilityKind::In(path) => { | ||
45 | let path = ModPath::from_src(path, hygiene); | ||
46 | let path = match path { | ||
47 | None => return RawVisibility::private(), | ||
48 | Some(path) => path, | ||
49 | }; | ||
50 | RawVisibility::Module(path) | ||
51 | } | ||
52 | ast::VisibilityKind::PubCrate => { | ||
53 | let path = ModPath { kind: PathKind::Crate, segments: Vec::new() }; | ||
54 | RawVisibility::Module(path) | ||
55 | } | ||
56 | ast::VisibilityKind::PubSuper => { | ||
57 | let path = ModPath { kind: PathKind::Super(1), segments: Vec::new() }; | ||
58 | RawVisibility::Module(path) | ||
59 | } | ||
60 | ast::VisibilityKind::Pub => RawVisibility::Public, | ||
61 | } | ||
62 | } | ||
63 | |||
64 | pub fn resolve( | ||
65 | &self, | ||
66 | db: &impl DefDatabase, | ||
67 | resolver: &crate::resolver::Resolver, | ||
68 | ) -> Visibility { | ||
69 | // we fall back to public visibility (i.e. fail open) if the path can't be resolved | ||
70 | resolver.resolve_visibility(db, self).unwrap_or(Visibility::Public) | ||
71 | } | ||
72 | } | ||
73 | |||
74 | /// Visibility of an item, with the path resolved. | ||
75 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] | ||
76 | pub enum Visibility { | ||
77 | /// Visibility is restricted to a certain module. | ||
78 | Module(ModuleId), | ||
79 | /// Visibility is unrestricted. | ||
80 | Public, | ||
81 | } | ||
82 | |||
83 | impl Visibility { | ||
84 | pub fn is_visible_from(self, db: &impl DefDatabase, from_module: ModuleId) -> bool { | ||
85 | let to_module = match self { | ||
86 | Visibility::Module(m) => m, | ||
87 | Visibility::Public => return true, | ||
88 | }; | ||
89 | // if they're not in the same crate, it can't be visible | ||
90 | if from_module.krate != to_module.krate { | ||
91 | return false; | ||
92 | } | ||
93 | let def_map = db.crate_def_map(from_module.krate); | ||
94 | self.is_visible_from_def_map(&def_map, from_module.local_id) | ||
95 | } | ||
96 | |||
97 | pub(crate) fn is_visible_from_other_crate(self) -> bool { | ||
98 | match self { | ||
99 | Visibility::Module(_) => false, | ||
100 | Visibility::Public => true, | ||
101 | } | ||
102 | } | ||
103 | |||
104 | pub(crate) fn is_visible_from_def_map( | ||
105 | self, | ||
106 | def_map: &crate::nameres::CrateDefMap, | ||
107 | from_module: crate::LocalModuleId, | ||
108 | ) -> bool { | ||
109 | let to_module = match self { | ||
110 | Visibility::Module(m) => m, | ||
111 | Visibility::Public => return true, | ||
112 | }; | ||
113 | // from_module needs to be a descendant of to_module | ||
114 | let mut ancestors = std::iter::successors(Some(from_module), |m| { | ||
115 | let parent_id = def_map[*m].parent?; | ||
116 | Some(parent_id) | ||
117 | }); | ||
118 | ancestors.any(|m| m == to_module.local_id) | ||
119 | } | ||
120 | } | ||
diff --git a/crates/ra_hir_ty/src/display.rs b/crates/ra_hir_ty/src/display.rs index dcca1bace..37def7c03 100644 --- a/crates/ra_hir_ty/src/display.rs +++ b/crates/ra_hir_ty/src/display.rs | |||
@@ -10,7 +10,7 @@ pub struct HirFormatter<'a, 'b, DB> { | |||
10 | buf: String, | 10 | buf: String, |
11 | curr_size: usize, | 11 | curr_size: usize, |
12 | max_size: Option<usize>, | 12 | max_size: Option<usize>, |
13 | should_display_default_types: bool, | 13 | omit_verbose_types: bool, |
14 | } | 14 | } |
15 | 15 | ||
16 | pub trait HirDisplay { | 16 | pub trait HirDisplay { |
@@ -20,7 +20,7 @@ pub trait HirDisplay { | |||
20 | where | 20 | where |
21 | Self: Sized, | 21 | Self: Sized, |
22 | { | 22 | { |
23 | HirDisplayWrapper(db, self, None, true) | 23 | HirDisplayWrapper(db, self, None, false) |
24 | } | 24 | } |
25 | 25 | ||
26 | fn display_truncated<'a, DB>( | 26 | fn display_truncated<'a, DB>( |
@@ -31,7 +31,7 @@ pub trait HirDisplay { | |||
31 | where | 31 | where |
32 | Self: Sized, | 32 | Self: Sized, |
33 | { | 33 | { |
34 | HirDisplayWrapper(db, self, max_size, false) | 34 | HirDisplayWrapper(db, self, max_size, true) |
35 | } | 35 | } |
36 | } | 36 | } |
37 | 37 | ||
@@ -74,8 +74,8 @@ where | |||
74 | } | 74 | } |
75 | } | 75 | } |
76 | 76 | ||
77 | pub fn should_display_default_types(&self) -> bool { | 77 | pub fn omit_verbose_types(&self) -> bool { |
78 | self.should_display_default_types | 78 | self.omit_verbose_types |
79 | } | 79 | } |
80 | } | 80 | } |
81 | 81 | ||
@@ -93,7 +93,7 @@ where | |||
93 | buf: String::with_capacity(20), | 93 | buf: String::with_capacity(20), |
94 | curr_size: 0, | 94 | curr_size: 0, |
95 | max_size: self.2, | 95 | max_size: self.2, |
96 | should_display_default_types: self.3, | 96 | omit_verbose_types: self.3, |
97 | }) | 97 | }) |
98 | } | 98 | } |
99 | } | 99 | } |
diff --git a/crates/ra_hir_ty/src/infer.rs b/crates/ra_hir_ty/src/infer.rs index e97b81473..32c0d07a5 100644 --- a/crates/ra_hir_ty/src/infer.rs +++ b/crates/ra_hir_ty/src/infer.rs | |||
@@ -37,8 +37,8 @@ use test_utils::tested_by; | |||
37 | use super::{ | 37 | use super::{ |
38 | primitive::{FloatTy, IntTy}, | 38 | primitive::{FloatTy, IntTy}, |
39 | traits::{Guidance, Obligation, ProjectionPredicate, Solution}, | 39 | traits::{Guidance, Obligation, ProjectionPredicate, Solution}, |
40 | ApplicationTy, InEnvironment, ProjectionTy, Substs, TraitEnvironment, TraitRef, Ty, TypeCtor, | 40 | ApplicationTy, GenericPredicate, InEnvironment, ProjectionTy, Substs, TraitEnvironment, |
41 | TypeWalk, Uncertain, | 41 | TraitRef, Ty, TypeCtor, TypeWalk, Uncertain, |
42 | }; | 42 | }; |
43 | use crate::{db::HirDatabase, infer::diagnostics::InferenceDiagnostic}; | 43 | use crate::{db::HirDatabase, infer::diagnostics::InferenceDiagnostic}; |
44 | 44 | ||
@@ -379,6 +379,25 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
379 | ) -> Ty { | 379 | ) -> Ty { |
380 | match assoc_ty { | 380 | match assoc_ty { |
381 | Some(res_assoc_ty) => { | 381 | Some(res_assoc_ty) => { |
382 | // FIXME: | ||
383 | // Check if inner_ty is is `impl Trait` and contained input TypeAlias id | ||
384 | // this is a workaround while Chalk assoc type projection doesn't always work yet, | ||
385 | // but once that is fixed I don't think we should keep this | ||
386 | // (we'll probably change how associated types are resolved anyway) | ||
387 | if let Ty::Opaque(ref predicates) = inner_ty { | ||
388 | for p in predicates.iter() { | ||
389 | if let GenericPredicate::Projection(projection) = p { | ||
390 | if projection.projection_ty.associated_ty == res_assoc_ty { | ||
391 | if let ty_app!(_, params) = &projection.ty { | ||
392 | if params.len() == 0 { | ||
393 | return projection.ty.clone(); | ||
394 | } | ||
395 | } | ||
396 | } | ||
397 | } | ||
398 | } | ||
399 | } | ||
400 | |||
382 | let ty = self.table.new_type_var(); | 401 | let ty = self.table.new_type_var(); |
383 | let builder = Substs::build_for_def(self.db, res_assoc_ty) | 402 | let builder = Substs::build_for_def(self.db, res_assoc_ty) |
384 | .push(inner_ty) | 403 | .push(inner_ty) |
diff --git a/crates/ra_hir_ty/src/lib.rs b/crates/ra_hir_ty/src/lib.rs index 48abf97c9..55b6dd836 100644 --- a/crates/ra_hir_ty/src/lib.rs +++ b/crates/ra_hir_ty/src/lib.rs | |||
@@ -821,6 +821,8 @@ impl TypeWalk for Ty { | |||
821 | } | 821 | } |
822 | } | 822 | } |
823 | 823 | ||
824 | const TYPE_HINT_TRUNCATION: &str = "…"; | ||
825 | |||
824 | impl HirDisplay for &Ty { | 826 | impl HirDisplay for &Ty { |
825 | fn hir_fmt(&self, f: &mut HirFormatter<impl HirDatabase>) -> fmt::Result { | 827 | fn hir_fmt(&self, f: &mut HirFormatter<impl HirDatabase>) -> fmt::Result { |
826 | HirDisplay::hir_fmt(*self, f) | 828 | HirDisplay::hir_fmt(*self, f) |
@@ -830,7 +832,7 @@ impl HirDisplay for &Ty { | |||
830 | impl HirDisplay for ApplicationTy { | 832 | impl HirDisplay for ApplicationTy { |
831 | fn hir_fmt(&self, f: &mut HirFormatter<impl HirDatabase>) -> fmt::Result { | 833 | fn hir_fmt(&self, f: &mut HirFormatter<impl HirDatabase>) -> fmt::Result { |
832 | if f.should_truncate() { | 834 | if f.should_truncate() { |
833 | return write!(f, "…"); | 835 | return write!(f, "{}", TYPE_HINT_TRUNCATION); |
834 | } | 836 | } |
835 | 837 | ||
836 | match self.ctor { | 838 | match self.ctor { |
@@ -908,9 +910,7 @@ impl HirDisplay for ApplicationTy { | |||
908 | write!(f, "<")?; | 910 | write!(f, "<")?; |
909 | 911 | ||
910 | let mut non_default_parameters = Vec::with_capacity(self.parameters.len()); | 912 | let mut non_default_parameters = Vec::with_capacity(self.parameters.len()); |
911 | let parameters_to_write = if f.should_display_default_types() { | 913 | let parameters_to_write = if f.omit_verbose_types() { |
912 | self.parameters.0.as_ref() | ||
913 | } else { | ||
914 | match self | 914 | match self |
915 | .ctor | 915 | .ctor |
916 | .as_generic_def() | 916 | .as_generic_def() |
@@ -935,6 +935,8 @@ impl HirDisplay for ApplicationTy { | |||
935 | &non_default_parameters | 935 | &non_default_parameters |
936 | } | 936 | } |
937 | } | 937 | } |
938 | } else { | ||
939 | self.parameters.0.as_ref() | ||
938 | }; | 940 | }; |
939 | 941 | ||
940 | f.write_joined(parameters_to_write, ", ")?; | 942 | f.write_joined(parameters_to_write, ", ")?; |
@@ -959,9 +961,16 @@ impl HirDisplay for ApplicationTy { | |||
959 | let sig = self.parameters[0] | 961 | let sig = self.parameters[0] |
960 | .callable_sig(f.db) | 962 | .callable_sig(f.db) |
961 | .expect("first closure parameter should contain signature"); | 963 | .expect("first closure parameter should contain signature"); |
962 | write!(f, "|")?; | 964 | let return_type_hint = sig.ret().display(f.db); |
963 | f.write_joined(sig.params(), ", ")?; | 965 | if sig.params().is_empty() { |
964 | write!(f, "| -> {}", sig.ret().display(f.db))?; | 966 | write!(f, "|| -> {}", return_type_hint)?; |
967 | } else if f.omit_verbose_types() { | ||
968 | write!(f, "|{}| -> {}", TYPE_HINT_TRUNCATION, return_type_hint)?; | ||
969 | } else { | ||
970 | write!(f, "|")?; | ||
971 | f.write_joined(sig.params(), ", ")?; | ||
972 | write!(f, "| -> {}", return_type_hint)?; | ||
973 | }; | ||
965 | } | 974 | } |
966 | } | 975 | } |
967 | Ok(()) | 976 | Ok(()) |
@@ -971,7 +980,7 @@ impl HirDisplay for ApplicationTy { | |||
971 | impl HirDisplay for ProjectionTy { | 980 | impl HirDisplay for ProjectionTy { |
972 | fn hir_fmt(&self, f: &mut HirFormatter<impl HirDatabase>) -> fmt::Result { | 981 | fn hir_fmt(&self, f: &mut HirFormatter<impl HirDatabase>) -> fmt::Result { |
973 | if f.should_truncate() { | 982 | if f.should_truncate() { |
974 | return write!(f, "…"); | 983 | return write!(f, "{}", TYPE_HINT_TRUNCATION); |
975 | } | 984 | } |
976 | 985 | ||
977 | let trait_name = f.db.trait_data(self.trait_(f.db)).name.clone(); | 986 | let trait_name = f.db.trait_data(self.trait_(f.db)).name.clone(); |
@@ -989,7 +998,7 @@ impl HirDisplay for ProjectionTy { | |||
989 | impl HirDisplay for Ty { | 998 | impl HirDisplay for Ty { |
990 | fn hir_fmt(&self, f: &mut HirFormatter<impl HirDatabase>) -> fmt::Result { | 999 | fn hir_fmt(&self, f: &mut HirFormatter<impl HirDatabase>) -> fmt::Result { |
991 | if f.should_truncate() { | 1000 | if f.should_truncate() { |
992 | return write!(f, "…"); | 1001 | return write!(f, "{}", TYPE_HINT_TRUNCATION); |
993 | } | 1002 | } |
994 | 1003 | ||
995 | match self { | 1004 | match self { |
@@ -1074,7 +1083,7 @@ impl HirDisplay for Ty { | |||
1074 | impl TraitRef { | 1083 | impl TraitRef { |
1075 | fn hir_fmt_ext(&self, f: &mut HirFormatter<impl HirDatabase>, use_as: bool) -> fmt::Result { | 1084 | fn hir_fmt_ext(&self, f: &mut HirFormatter<impl HirDatabase>, use_as: bool) -> fmt::Result { |
1076 | if f.should_truncate() { | 1085 | if f.should_truncate() { |
1077 | return write!(f, "…"); | 1086 | return write!(f, "{}", TYPE_HINT_TRUNCATION); |
1078 | } | 1087 | } |
1079 | 1088 | ||
1080 | self.substs[0].hir_fmt(f)?; | 1089 | self.substs[0].hir_fmt(f)?; |
@@ -1108,7 +1117,7 @@ impl HirDisplay for &GenericPredicate { | |||
1108 | impl HirDisplay for GenericPredicate { | 1117 | impl HirDisplay for GenericPredicate { |
1109 | fn hir_fmt(&self, f: &mut HirFormatter<impl HirDatabase>) -> fmt::Result { | 1118 | fn hir_fmt(&self, f: &mut HirFormatter<impl HirDatabase>) -> fmt::Result { |
1110 | if f.should_truncate() { | 1119 | if f.should_truncate() { |
1111 | return write!(f, "…"); | 1120 | return write!(f, "{}", TYPE_HINT_TRUNCATION); |
1112 | } | 1121 | } |
1113 | 1122 | ||
1114 | match self { | 1123 | match self { |
diff --git a/crates/ra_hir_ty/src/tests.rs b/crates/ra_hir_ty/src/tests.rs index d447b4571..d1f10e675 100644 --- a/crates/ra_hir_ty/src/tests.rs +++ b/crates/ra_hir_ty/src/tests.rs | |||
@@ -11,8 +11,8 @@ use std::fmt::Write; | |||
11 | use std::sync::Arc; | 11 | use std::sync::Arc; |
12 | 12 | ||
13 | use hir_def::{ | 13 | use hir_def::{ |
14 | body::BodySourceMap, child_by_source::ChildBySource, db::DefDatabase, keys, | 14 | body::BodySourceMap, child_by_source::ChildBySource, db::DefDatabase, item_scope::ItemScope, |
15 | nameres::CrateDefMap, AssocItemId, DefWithBodyId, LocalModuleId, Lookup, ModuleDefId, | 15 | keys, nameres::CrateDefMap, AssocItemId, DefWithBodyId, LocalModuleId, Lookup, ModuleDefId, |
16 | }; | 16 | }; |
17 | use hir_expand::InFile; | 17 | use hir_expand::InFile; |
18 | use insta::assert_snapshot; | 18 | use insta::assert_snapshot; |
@@ -163,35 +163,69 @@ fn visit_module( | |||
163 | module_id: LocalModuleId, | 163 | module_id: LocalModuleId, |
164 | cb: &mut dyn FnMut(DefWithBodyId), | 164 | cb: &mut dyn FnMut(DefWithBodyId), |
165 | ) { | 165 | ) { |
166 | for decl in crate_def_map[module_id].scope.declarations() { | 166 | visit_scope(db, crate_def_map, &crate_def_map[module_id].scope, cb); |
167 | match decl { | ||
168 | ModuleDefId::FunctionId(it) => cb(it.into()), | ||
169 | ModuleDefId::ConstId(it) => cb(it.into()), | ||
170 | ModuleDefId::StaticId(it) => cb(it.into()), | ||
171 | ModuleDefId::TraitId(it) => { | ||
172 | let trait_data = db.trait_data(it); | ||
173 | for &(_, item) in trait_data.items.iter() { | ||
174 | match item { | ||
175 | AssocItemId::FunctionId(it) => cb(it.into()), | ||
176 | AssocItemId::ConstId(it) => cb(it.into()), | ||
177 | AssocItemId::TypeAliasId(_) => (), | ||
178 | } | ||
179 | } | ||
180 | } | ||
181 | ModuleDefId::ModuleId(it) => visit_module(db, crate_def_map, it.local_id, cb), | ||
182 | _ => (), | ||
183 | } | ||
184 | } | ||
185 | for impl_id in crate_def_map[module_id].scope.impls() { | 167 | for impl_id in crate_def_map[module_id].scope.impls() { |
186 | let impl_data = db.impl_data(impl_id); | 168 | let impl_data = db.impl_data(impl_id); |
187 | for &item in impl_data.items.iter() { | 169 | for &item in impl_data.items.iter() { |
188 | match item { | 170 | match item { |
189 | AssocItemId::FunctionId(it) => cb(it.into()), | 171 | AssocItemId::FunctionId(it) => { |
190 | AssocItemId::ConstId(it) => cb(it.into()), | 172 | let def = it.into(); |
173 | cb(def); | ||
174 | let body = db.body(def); | ||
175 | visit_scope(db, crate_def_map, &body.item_scope, cb); | ||
176 | } | ||
177 | AssocItemId::ConstId(it) => { | ||
178 | let def = it.into(); | ||
179 | cb(def); | ||
180 | let body = db.body(def); | ||
181 | visit_scope(db, crate_def_map, &body.item_scope, cb); | ||
182 | } | ||
191 | AssocItemId::TypeAliasId(_) => (), | 183 | AssocItemId::TypeAliasId(_) => (), |
192 | } | 184 | } |
193 | } | 185 | } |
194 | } | 186 | } |
187 | |||
188 | fn visit_scope( | ||
189 | db: &TestDB, | ||
190 | crate_def_map: &CrateDefMap, | ||
191 | scope: &ItemScope, | ||
192 | cb: &mut dyn FnMut(DefWithBodyId), | ||
193 | ) { | ||
194 | for decl in scope.declarations() { | ||
195 | match decl { | ||
196 | ModuleDefId::FunctionId(it) => { | ||
197 | let def = it.into(); | ||
198 | cb(def); | ||
199 | let body = db.body(def); | ||
200 | visit_scope(db, crate_def_map, &body.item_scope, cb); | ||
201 | } | ||
202 | ModuleDefId::ConstId(it) => { | ||
203 | let def = it.into(); | ||
204 | cb(def); | ||
205 | let body = db.body(def); | ||
206 | visit_scope(db, crate_def_map, &body.item_scope, cb); | ||
207 | } | ||
208 | ModuleDefId::StaticId(it) => { | ||
209 | let def = it.into(); | ||
210 | cb(def); | ||
211 | let body = db.body(def); | ||
212 | visit_scope(db, crate_def_map, &body.item_scope, cb); | ||
213 | } | ||
214 | ModuleDefId::TraitId(it) => { | ||
215 | let trait_data = db.trait_data(it); | ||
216 | for &(_, item) in trait_data.items.iter() { | ||
217 | match item { | ||
218 | AssocItemId::FunctionId(it) => cb(it.into()), | ||
219 | AssocItemId::ConstId(it) => cb(it.into()), | ||
220 | AssocItemId::TypeAliasId(_) => (), | ||
221 | } | ||
222 | } | ||
223 | } | ||
224 | ModuleDefId::ModuleId(it) => visit_module(db, crate_def_map, it.local_id, cb), | ||
225 | _ => (), | ||
226 | } | ||
227 | } | ||
228 | } | ||
195 | } | 229 | } |
196 | 230 | ||
197 | fn ellipsize(mut text: String, max_len: usize) -> String { | 231 | fn ellipsize(mut text: String, max_len: usize) -> String { |
diff --git a/crates/ra_hir_ty/src/tests/regression.rs b/crates/ra_hir_ty/src/tests/regression.rs index 09d684ac2..8b3aa8564 100644 --- a/crates/ra_hir_ty/src/tests/regression.rs +++ b/crates/ra_hir_ty/src/tests/regression.rs | |||
@@ -1,7 +1,8 @@ | |||
1 | use super::infer; | ||
2 | use insta::assert_snapshot; | 1 | use insta::assert_snapshot; |
3 | use test_utils::covers; | 2 | use test_utils::covers; |
4 | 3 | ||
4 | use super::infer; | ||
5 | |||
5 | #[test] | 6 | #[test] |
6 | fn bug_484() { | 7 | fn bug_484() { |
7 | assert_snapshot!( | 8 | assert_snapshot!( |
@@ -331,3 +332,36 @@ pub fn main_loop() { | |||
331 | "### | 332 | "### |
332 | ); | 333 | ); |
333 | } | 334 | } |
335 | |||
336 | #[test] | ||
337 | fn issue_2669() { | ||
338 | assert_snapshot!( | ||
339 | infer( | ||
340 | r#"trait A {} | ||
341 | trait Write {} | ||
342 | struct Response<T> {} | ||
343 | |||
344 | trait D { | ||
345 | fn foo(); | ||
346 | } | ||
347 | |||
348 | impl<T:A> D for Response<T> { | ||
349 | fn foo() { | ||
350 | end(); | ||
351 | fn end<W: Write>() { | ||
352 | let _x: T = loop {}; | ||
353 | } | ||
354 | } | ||
355 | }"# | ||
356 | ), | ||
357 | @r###" | ||
358 | [147; 262) '{ ... }': () | ||
359 | [161; 164) 'end': fn end<{unknown}>() -> () | ||
360 | [161; 166) 'end()': () | ||
361 | [199; 252) '{ ... }': () | ||
362 | [221; 223) '_x': ! | ||
363 | [230; 237) 'loop {}': ! | ||
364 | [235; 237) '{}': () | ||
365 | "### | ||
366 | ) | ||
367 | } | ||
diff --git a/crates/ra_hir_ty/src/tests/simple.rs b/crates/ra_hir_ty/src/tests/simple.rs index 3e5e163e3..00134c99b 100644 --- a/crates/ra_hir_ty/src/tests/simple.rs +++ b/crates/ra_hir_ty/src/tests/simple.rs | |||
@@ -1518,6 +1518,7 @@ fn test() { | |||
1518 | [167; 179) 'GLOBAL_CONST': u32 | 1518 | [167; 179) 'GLOBAL_CONST': u32 |
1519 | [189; 191) 'id': u32 | 1519 | [189; 191) 'id': u32 |
1520 | [194; 210) 'Foo::A..._CONST': u32 | 1520 | [194; 210) 'Foo::A..._CONST': u32 |
1521 | [126; 128) '99': u32 | ||
1521 | "### | 1522 | "### |
1522 | ); | 1523 | ); |
1523 | } | 1524 | } |
@@ -1549,6 +1550,8 @@ fn test() { | |||
1549 | [233; 246) 'GLOBAL_STATIC': u32 | 1550 | [233; 246) 'GLOBAL_STATIC': u32 |
1550 | [256; 257) 'w': u32 | 1551 | [256; 257) 'w': u32 |
1551 | [260; 277) 'GLOBAL...IC_MUT': u32 | 1552 | [260; 277) 'GLOBAL...IC_MUT': u32 |
1553 | [118; 120) '99': u32 | ||
1554 | [161; 163) '99': u32 | ||
1552 | "### | 1555 | "### |
1553 | ); | 1556 | ); |
1554 | } | 1557 | } |
diff --git a/crates/ra_hir_ty/src/tests/traits.rs b/crates/ra_hir_ty/src/tests/traits.rs index ae316922b..0bc72644a 100644 --- a/crates/ra_hir_ty/src/tests/traits.rs +++ b/crates/ra_hir_ty/src/tests/traits.rs | |||
@@ -38,6 +38,63 @@ mod future { | |||
38 | } | 38 | } |
39 | 39 | ||
40 | #[test] | 40 | #[test] |
41 | fn infer_async() { | ||
42 | let (db, pos) = TestDB::with_position( | ||
43 | r#" | ||
44 | //- /main.rs crate:main deps:std | ||
45 | |||
46 | async fn foo() -> u64 { | ||
47 | 128 | ||
48 | } | ||
49 | |||
50 | fn test() { | ||
51 | let r = foo(); | ||
52 | let v = r.await; | ||
53 | v<|>; | ||
54 | } | ||
55 | |||
56 | //- /std.rs crate:std | ||
57 | #[prelude_import] use future::*; | ||
58 | mod future { | ||
59 | trait Future { | ||
60 | type Output; | ||
61 | } | ||
62 | } | ||
63 | |||
64 | "#, | ||
65 | ); | ||
66 | assert_eq!("u64", type_at_pos(&db, pos)); | ||
67 | } | ||
68 | |||
69 | #[test] | ||
70 | fn infer_desugar_async() { | ||
71 | let (db, pos) = TestDB::with_position( | ||
72 | r#" | ||
73 | //- /main.rs crate:main deps:std | ||
74 | |||
75 | async fn foo() -> u64 { | ||
76 | 128 | ||
77 | } | ||
78 | |||
79 | fn test() { | ||
80 | let r = foo(); | ||
81 | r<|>; | ||
82 | } | ||
83 | |||
84 | //- /std.rs crate:std | ||
85 | #[prelude_import] use future::*; | ||
86 | mod future { | ||
87 | trait Future { | ||
88 | type Output; | ||
89 | } | ||
90 | } | ||
91 | |||
92 | "#, | ||
93 | ); | ||
94 | assert_eq!("impl Future<Output = u64>", type_at_pos(&db, pos)); | ||
95 | } | ||
96 | |||
97 | #[test] | ||
41 | fn infer_try() { | 98 | fn infer_try() { |
42 | let (db, pos) = TestDB::with_position( | 99 | let (db, pos) = TestDB::with_position( |
43 | r#" | 100 | r#" |
diff --git a/crates/ra_ide/src/completion/complete_dot.rs b/crates/ra_ide/src/completion/complete_dot.rs index 294964887..210a685e4 100644 --- a/crates/ra_ide/src/completion/complete_dot.rs +++ b/crates/ra_ide/src/completion/complete_dot.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | 2 | ||
3 | use hir::Type; | 3 | use hir::{HasVisibility, Type}; |
4 | 4 | ||
5 | use crate::completion::completion_item::CompletionKind; | 5 | use crate::completion::completion_item::CompletionKind; |
6 | use crate::{ | 6 | use crate::{ |
@@ -38,9 +38,15 @@ pub(super) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) { | |||
38 | fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: &Type) { | 38 | fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: &Type) { |
39 | for receiver in receiver.autoderef(ctx.db) { | 39 | for receiver in receiver.autoderef(ctx.db) { |
40 | for (field, ty) in receiver.fields(ctx.db) { | 40 | for (field, ty) in receiver.fields(ctx.db) { |
41 | if ctx.module.map_or(false, |m| !field.is_visible_from(ctx.db, m)) { | ||
42 | // Skip private field. FIXME: If the definition location of the | ||
43 | // field is editable, we should show the completion | ||
44 | continue; | ||
45 | } | ||
41 | acc.add_field(ctx, field, &ty); | 46 | acc.add_field(ctx, field, &ty); |
42 | } | 47 | } |
43 | for (i, ty) in receiver.tuple_fields(ctx.db).into_iter().enumerate() { | 48 | for (i, ty) in receiver.tuple_fields(ctx.db).into_iter().enumerate() { |
49 | // FIXME: Handle visibility | ||
44 | acc.add_tuple_field(ctx, i, &ty); | 50 | acc.add_tuple_field(ctx, i, &ty); |
45 | } | 51 | } |
46 | } | 52 | } |
@@ -187,6 +193,55 @@ mod tests { | |||
187 | } | 193 | } |
188 | 194 | ||
189 | #[test] | 195 | #[test] |
196 | fn test_struct_field_visibility_private() { | ||
197 | assert_debug_snapshot!( | ||
198 | do_ref_completion( | ||
199 | r" | ||
200 | mod inner { | ||
201 | struct A { | ||
202 | private_field: u32, | ||
203 | pub pub_field: u32, | ||
204 | pub(crate) crate_field: u32, | ||
205 | pub(super) super_field: u32, | ||
206 | } | ||
207 | } | ||
208 | fn foo(a: inner::A) { | ||
209 | a.<|> | ||
210 | } | ||
211 | ", | ||
212 | ), | ||
213 | @r###" | ||
214 | [ | ||
215 | CompletionItem { | ||
216 | label: "crate_field", | ||
217 | source_range: [313; 313), | ||
218 | delete: [313; 313), | ||
219 | insert: "crate_field", | ||
220 | kind: Field, | ||
221 | detail: "u32", | ||
222 | }, | ||
223 | CompletionItem { | ||
224 | label: "pub_field", | ||
225 | source_range: [313; 313), | ||
226 | delete: [313; 313), | ||
227 | insert: "pub_field", | ||
228 | kind: Field, | ||
229 | detail: "u32", | ||
230 | }, | ||
231 | CompletionItem { | ||
232 | label: "super_field", | ||
233 | source_range: [313; 313), | ||
234 | delete: [313; 313), | ||
235 | insert: "super_field", | ||
236 | kind: Field, | ||
237 | detail: "u32", | ||
238 | }, | ||
239 | ] | ||
240 | "### | ||
241 | ); | ||
242 | } | ||
243 | |||
244 | #[test] | ||
190 | fn test_method_completion() { | 245 | fn test_method_completion() { |
191 | assert_debug_snapshot!( | 246 | assert_debug_snapshot!( |
192 | do_ref_completion( | 247 | do_ref_completion( |
diff --git a/crates/ra_ide/src/inlay_hints.rs b/crates/ra_ide/src/inlay_hints.rs index c5e406977..977aafc51 100644 --- a/crates/ra_ide/src/inlay_hints.rs +++ b/crates/ra_ide/src/inlay_hints.rs | |||
@@ -293,7 +293,7 @@ fn main() { | |||
293 | } | 293 | } |
294 | 294 | ||
295 | #[test] | 295 | #[test] |
296 | fn closure_parameter() { | 296 | fn closure_parameters() { |
297 | let (analysis, file_id) = single_file( | 297 | let (analysis, file_id) = single_file( |
298 | r#" | 298 | r#" |
299 | fn main() { | 299 | fn main() { |
@@ -301,6 +301,11 @@ fn main() { | |||
301 | (0..2).for_each(|increment| { | 301 | (0..2).for_each(|increment| { |
302 | start += increment; | 302 | start += increment; |
303 | }) | 303 | }) |
304 | |||
305 | let multiply = |a, b, c, d| a * b * c * d; | ||
306 | let _: i32 = multiply(1, 2, 3, 4); | ||
307 | |||
308 | let return_42 = || 42; | ||
304 | }"#, | 309 | }"#, |
305 | ); | 310 | ); |
306 | 311 | ||
@@ -316,6 +321,36 @@ fn main() { | |||
316 | kind: TypeHint, | 321 | kind: TypeHint, |
317 | label: "i32", | 322 | label: "i32", |
318 | }, | 323 | }, |
324 | InlayHint { | ||
325 | range: [114; 122), | ||
326 | kind: TypeHint, | ||
327 | label: "|…| -> i32", | ||
328 | }, | ||
329 | InlayHint { | ||
330 | range: [126; 127), | ||
331 | kind: TypeHint, | ||
332 | label: "i32", | ||
333 | }, | ||
334 | InlayHint { | ||
335 | range: [129; 130), | ||
336 | kind: TypeHint, | ||
337 | label: "i32", | ||
338 | }, | ||
339 | InlayHint { | ||
340 | range: [132; 133), | ||
341 | kind: TypeHint, | ||
342 | label: "i32", | ||
343 | }, | ||
344 | InlayHint { | ||
345 | range: [135; 136), | ||
346 | kind: TypeHint, | ||
347 | label: "i32", | ||
348 | }, | ||
349 | InlayHint { | ||
350 | range: [201; 210), | ||
351 | kind: TypeHint, | ||
352 | label: "|| -> i32", | ||
353 | }, | ||
319 | ] | 354 | ] |
320 | "### | 355 | "### |
321 | ); | 356 | ); |
diff --git a/crates/ra_lsp_server/Cargo.toml b/crates/ra_lsp_server/Cargo.toml index 60cbc38a9..9b7dcb6e9 100644 --- a/crates/ra_lsp_server/Cargo.toml +++ b/crates/ra_lsp_server/Cargo.toml | |||
@@ -14,7 +14,7 @@ serde_json = "1.0.34" | |||
14 | serde = { version = "1.0.83", features = ["derive"] } | 14 | serde = { version = "1.0.83", features = ["derive"] } |
15 | crossbeam-channel = "0.4" | 15 | crossbeam-channel = "0.4" |
16 | log = "0.4.3" | 16 | log = "0.4.3" |
17 | lsp-types = { version = "0.66.0", features = ["proposed"] } | 17 | lsp-types = { version = "0.67.0", features = ["proposed"] } |
18 | rustc-hash = "1.0" | 18 | rustc-hash = "1.0" |
19 | parking_lot = "0.10.0" | 19 | parking_lot = "0.10.0" |
20 | jod-thread = "0.1.0" | 20 | jod-thread = "0.1.0" |
@@ -27,6 +27,7 @@ ra_project_model = { path = "../ra_project_model" } | |||
27 | ra_prof = { path = "../ra_prof" } | 27 | ra_prof = { path = "../ra_prof" } |
28 | ra_vfs_glob = { path = "../ra_vfs_glob" } | 28 | ra_vfs_glob = { path = "../ra_vfs_glob" } |
29 | env_logger = { version = "0.7.1", default-features = false, features = ["humantime"] } | 29 | env_logger = { version = "0.7.1", default-features = false, features = ["humantime"] } |
30 | ra_cargo_watch = { path = "../ra_cargo_watch" } | ||
30 | 31 | ||
31 | [dev-dependencies] | 32 | [dev-dependencies] |
32 | tempfile = "3" | 33 | tempfile = "3" |
diff --git a/crates/ra_lsp_server/src/caps.rs b/crates/ra_lsp_server/src/caps.rs index eeca67ee1..0dee1f6fe 100644 --- a/crates/ra_lsp_server/src/caps.rs +++ b/crates/ra_lsp_server/src/caps.rs | |||
@@ -3,7 +3,7 @@ | |||
3 | use lsp_types::{ | 3 | use lsp_types::{ |
4 | CodeActionProviderCapability, CodeLensOptions, CompletionOptions, | 4 | CodeActionProviderCapability, CodeLensOptions, CompletionOptions, |
5 | DocumentOnTypeFormattingOptions, FoldingRangeProviderCapability, | 5 | DocumentOnTypeFormattingOptions, FoldingRangeProviderCapability, |
6 | ImplementationProviderCapability, RenameOptions, RenameProviderCapability, | 6 | ImplementationProviderCapability, RenameOptions, RenameProviderCapability, SaveOptions, |
7 | SelectionRangeProviderCapability, ServerCapabilities, SignatureHelpOptions, | 7 | SelectionRangeProviderCapability, ServerCapabilities, SignatureHelpOptions, |
8 | TextDocumentSyncCapability, TextDocumentSyncKind, TextDocumentSyncOptions, | 8 | TextDocumentSyncCapability, TextDocumentSyncKind, TextDocumentSyncOptions, |
9 | TypeDefinitionProviderCapability, WorkDoneProgressOptions, | 9 | TypeDefinitionProviderCapability, WorkDoneProgressOptions, |
@@ -16,7 +16,7 @@ pub fn server_capabilities() -> ServerCapabilities { | |||
16 | change: Some(TextDocumentSyncKind::Full), | 16 | change: Some(TextDocumentSyncKind::Full), |
17 | will_save: None, | 17 | will_save: None, |
18 | will_save_wait_until: None, | 18 | will_save_wait_until: None, |
19 | save: None, | 19 | save: Some(SaveOptions::default()), |
20 | })), | 20 | })), |
21 | hover_provider: Some(true), | 21 | hover_provider: Some(true), |
22 | completion_provider: Some(CompletionOptions { | 22 | completion_provider: Some(CompletionOptions { |
@@ -46,6 +46,7 @@ pub fn server_capabilities() -> ServerCapabilities { | |||
46 | more_trigger_character: Some(vec![".".to_string(), ">".to_string()]), | 46 | more_trigger_character: Some(vec![".".to_string(), ">".to_string()]), |
47 | }), | 47 | }), |
48 | selection_range_provider: Some(SelectionRangeProviderCapability::Simple(true)), | 48 | selection_range_provider: Some(SelectionRangeProviderCapability::Simple(true)), |
49 | semantic_highlighting: None, | ||
49 | folding_range_provider: Some(FoldingRangeProviderCapability::Simple(true)), | 50 | folding_range_provider: Some(FoldingRangeProviderCapability::Simple(true)), |
50 | rename_provider: Some(RenameProviderCapability::Options(RenameOptions { | 51 | rename_provider: Some(RenameProviderCapability::Options(RenameOptions { |
51 | prepare_provider: Some(true), | 52 | prepare_provider: Some(true), |
diff --git a/crates/ra_lsp_server/src/config.rs b/crates/ra_lsp_server/src/config.rs index 67942aa41..2d7948d74 100644 --- a/crates/ra_lsp_server/src/config.rs +++ b/crates/ra_lsp_server/src/config.rs | |||
@@ -32,6 +32,11 @@ pub struct ServerConfig { | |||
32 | 32 | ||
33 | pub max_inlay_hint_length: Option<usize>, | 33 | pub max_inlay_hint_length: Option<usize>, |
34 | 34 | ||
35 | pub cargo_watch_enable: bool, | ||
36 | pub cargo_watch_args: Vec<String>, | ||
37 | pub cargo_watch_command: String, | ||
38 | pub cargo_watch_all_targets: bool, | ||
39 | |||
35 | /// For internal usage to make integrated tests faster. | 40 | /// For internal usage to make integrated tests faster. |
36 | #[serde(deserialize_with = "nullable_bool_true")] | 41 | #[serde(deserialize_with = "nullable_bool_true")] |
37 | pub with_sysroot: bool, | 42 | pub with_sysroot: bool, |
@@ -51,6 +56,10 @@ impl Default for ServerConfig { | |||
51 | use_client_watching: false, | 56 | use_client_watching: false, |
52 | lru_capacity: None, | 57 | lru_capacity: None, |
53 | max_inlay_hint_length: None, | 58 | max_inlay_hint_length: None, |
59 | cargo_watch_enable: true, | ||
60 | cargo_watch_args: Vec::new(), | ||
61 | cargo_watch_command: "check".to_string(), | ||
62 | cargo_watch_all_targets: true, | ||
54 | with_sysroot: true, | 63 | with_sysroot: true, |
55 | feature_flags: FxHashMap::default(), | 64 | feature_flags: FxHashMap::default(), |
56 | cargo_features: Default::default(), | 65 | cargo_features: Default::default(), |
diff --git a/crates/ra_lsp_server/src/main_loop.rs b/crates/ra_lsp_server/src/main_loop.rs index dda318e43..af1a487de 100644 --- a/crates/ra_lsp_server/src/main_loop.rs +++ b/crates/ra_lsp_server/src/main_loop.rs | |||
@@ -10,6 +10,7 @@ use std::{error::Error, fmt, panic, path::PathBuf, sync::Arc, time::Instant}; | |||
10 | use crossbeam_channel::{select, unbounded, RecvError, Sender}; | 10 | use crossbeam_channel::{select, unbounded, RecvError, Sender}; |
11 | use lsp_server::{Connection, ErrorCode, Message, Notification, Request, RequestId, Response}; | 11 | use lsp_server::{Connection, ErrorCode, Message, Notification, Request, RequestId, Response}; |
12 | use lsp_types::{ClientCapabilities, NumberOrString}; | 12 | use lsp_types::{ClientCapabilities, NumberOrString}; |
13 | use ra_cargo_watch::{CheckOptions, CheckTask}; | ||
13 | use ra_ide::{Canceled, FeatureFlags, FileId, LibraryData, SourceRootId}; | 14 | use ra_ide::{Canceled, FeatureFlags, FileId, LibraryData, SourceRootId}; |
14 | use ra_prof::profile; | 15 | use ra_prof::profile; |
15 | use ra_vfs::{VfsTask, Watch}; | 16 | use ra_vfs::{VfsTask, Watch}; |
@@ -126,6 +127,12 @@ pub fn main_loop( | |||
126 | .and_then(|it| it.line_folding_only) | 127 | .and_then(|it| it.line_folding_only) |
127 | .unwrap_or(false), | 128 | .unwrap_or(false), |
128 | max_inlay_hint_length: config.max_inlay_hint_length, | 129 | max_inlay_hint_length: config.max_inlay_hint_length, |
130 | cargo_watch: CheckOptions { | ||
131 | enable: config.cargo_watch_enable, | ||
132 | args: config.cargo_watch_args, | ||
133 | command: config.cargo_watch_command, | ||
134 | all_targets: config.cargo_watch_all_targets, | ||
135 | }, | ||
129 | } | 136 | } |
130 | }; | 137 | }; |
131 | 138 | ||
@@ -176,7 +183,11 @@ pub fn main_loop( | |||
176 | Ok(task) => Event::Vfs(task), | 183 | Ok(task) => Event::Vfs(task), |
177 | Err(RecvError) => Err("vfs died")?, | 184 | Err(RecvError) => Err("vfs died")?, |
178 | }, | 185 | }, |
179 | recv(libdata_receiver) -> data => Event::Lib(data.unwrap()) | 186 | recv(libdata_receiver) -> data => Event::Lib(data.unwrap()), |
187 | recv(world_state.check_watcher.task_recv) -> task => match task { | ||
188 | Ok(task) => Event::CheckWatcher(task), | ||
189 | Err(RecvError) => Err("check watcher died")?, | ||
190 | } | ||
180 | }; | 191 | }; |
181 | if let Event::Msg(Message::Request(req)) = &event { | 192 | if let Event::Msg(Message::Request(req)) = &event { |
182 | if connection.handle_shutdown(&req)? { | 193 | if connection.handle_shutdown(&req)? { |
@@ -222,6 +233,7 @@ enum Event { | |||
222 | Task(Task), | 233 | Task(Task), |
223 | Vfs(VfsTask), | 234 | Vfs(VfsTask), |
224 | Lib(LibraryData), | 235 | Lib(LibraryData), |
236 | CheckWatcher(CheckTask), | ||
225 | } | 237 | } |
226 | 238 | ||
227 | impl fmt::Debug for Event { | 239 | impl fmt::Debug for Event { |
@@ -259,6 +271,7 @@ impl fmt::Debug for Event { | |||
259 | Event::Task(it) => fmt::Debug::fmt(it, f), | 271 | Event::Task(it) => fmt::Debug::fmt(it, f), |
260 | Event::Vfs(it) => fmt::Debug::fmt(it, f), | 272 | Event::Vfs(it) => fmt::Debug::fmt(it, f), |
261 | Event::Lib(it) => fmt::Debug::fmt(it, f), | 273 | Event::Lib(it) => fmt::Debug::fmt(it, f), |
274 | Event::CheckWatcher(it) => fmt::Debug::fmt(it, f), | ||
262 | } | 275 | } |
263 | } | 276 | } |
264 | } | 277 | } |
@@ -318,6 +331,28 @@ fn loop_turn( | |||
318 | world_state.maybe_collect_garbage(); | 331 | world_state.maybe_collect_garbage(); |
319 | loop_state.in_flight_libraries -= 1; | 332 | loop_state.in_flight_libraries -= 1; |
320 | } | 333 | } |
334 | Event::CheckWatcher(task) => match task { | ||
335 | CheckTask::Update(uri) => { | ||
336 | // We manually send a diagnostic update when the watcher asks | ||
337 | // us to, to avoid the issue of having to change the file to | ||
338 | // receive updated diagnostics. | ||
339 | let path = uri.to_file_path().map_err(|()| format!("invalid uri: {}", uri))?; | ||
340 | if let Some(file_id) = world_state.vfs.read().path2file(&path) { | ||
341 | let params = | ||
342 | handlers::publish_diagnostics(&world_state.snapshot(), FileId(file_id.0))?; | ||
343 | let not = notification_new::<req::PublishDiagnostics>(params); | ||
344 | task_sender.send(Task::Notify(not)).unwrap(); | ||
345 | } | ||
346 | } | ||
347 | CheckTask::Status(progress) => { | ||
348 | let params = req::ProgressParams { | ||
349 | token: req::ProgressToken::String("rustAnalyzer/cargoWatcher".to_string()), | ||
350 | value: req::ProgressParamsValue::WorkDone(progress), | ||
351 | }; | ||
352 | let not = notification_new::<req::Progress>(params); | ||
353 | task_sender.send(Task::Notify(not)).unwrap(); | ||
354 | } | ||
355 | }, | ||
321 | Event::Msg(msg) => match msg { | 356 | Event::Msg(msg) => match msg { |
322 | Message::Request(req) => on_request( | 357 | Message::Request(req) => on_request( |
323 | world_state, | 358 | world_state, |
@@ -517,6 +552,13 @@ fn on_notification( | |||
517 | } | 552 | } |
518 | Err(not) => not, | 553 | Err(not) => not, |
519 | }; | 554 | }; |
555 | let not = match notification_cast::<req::DidSaveTextDocument>(not) { | ||
556 | Ok(_params) => { | ||
557 | state.check_watcher.update(); | ||
558 | return Ok(()); | ||
559 | } | ||
560 | Err(not) => not, | ||
561 | }; | ||
520 | let not = match notification_cast::<req::DidCloseTextDocument>(not) { | 562 | let not = match notification_cast::<req::DidCloseTextDocument>(not) { |
521 | Ok(params) => { | 563 | Ok(params) => { |
522 | let uri = params.text_document.uri; | 564 | let uri = params.text_document.uri; |
diff --git a/crates/ra_lsp_server/src/main_loop/handlers.rs b/crates/ra_lsp_server/src/main_loop/handlers.rs index 39eb3df3e..331beab13 100644 --- a/crates/ra_lsp_server/src/main_loop/handlers.rs +++ b/crates/ra_lsp_server/src/main_loop/handlers.rs | |||
@@ -654,6 +654,29 @@ pub fn handle_code_action( | |||
654 | res.push(action.into()); | 654 | res.push(action.into()); |
655 | } | 655 | } |
656 | 656 | ||
657 | for fix in world.check_watcher.read().fixes_for(¶ms.text_document.uri).into_iter().flatten() | ||
658 | { | ||
659 | let fix_range = fix.location.range.conv_with(&line_index); | ||
660 | if fix_range.intersection(&range).is_none() { | ||
661 | continue; | ||
662 | } | ||
663 | |||
664 | let edits = vec![TextEdit::new(fix.location.range, fix.replacement.clone())]; | ||
665 | let mut edit_map = std::collections::HashMap::new(); | ||
666 | edit_map.insert(fix.location.uri.clone(), edits); | ||
667 | let edit = WorkspaceEdit::new(edit_map); | ||
668 | |||
669 | let action = CodeAction { | ||
670 | title: fix.title.clone(), | ||
671 | kind: Some("quickfix".to_string()), | ||
672 | diagnostics: Some(fix.diagnostics.clone()), | ||
673 | edit: Some(edit), | ||
674 | command: None, | ||
675 | is_preferred: None, | ||
676 | }; | ||
677 | res.push(action.into()); | ||
678 | } | ||
679 | |||
657 | for assist in assists { | 680 | for assist in assists { |
658 | let title = assist.change.label.clone(); | 681 | let title = assist.change.label.clone(); |
659 | let edit = assist.change.try_conv_with(&world)?; | 682 | let edit = assist.change.try_conv_with(&world)?; |
@@ -820,7 +843,7 @@ pub fn publish_diagnostics( | |||
820 | let _p = profile("publish_diagnostics"); | 843 | let _p = profile("publish_diagnostics"); |
821 | let uri = world.file_id_to_uri(file_id)?; | 844 | let uri = world.file_id_to_uri(file_id)?; |
822 | let line_index = world.analysis().file_line_index(file_id)?; | 845 | let line_index = world.analysis().file_line_index(file_id)?; |
823 | let diagnostics = world | 846 | let mut diagnostics: Vec<Diagnostic> = world |
824 | .analysis() | 847 | .analysis() |
825 | .diagnostics(file_id)? | 848 | .diagnostics(file_id)? |
826 | .into_iter() | 849 | .into_iter() |
@@ -834,6 +857,9 @@ pub fn publish_diagnostics( | |||
834 | tags: None, | 857 | tags: None, |
835 | }) | 858 | }) |
836 | .collect(); | 859 | .collect(); |
860 | if let Some(check_diags) = world.check_watcher.read().diagnostics_for(&uri) { | ||
861 | diagnostics.extend(check_diags.iter().cloned()); | ||
862 | } | ||
837 | Ok(req::PublishDiagnosticsParams { uri, diagnostics, version: None }) | 863 | Ok(req::PublishDiagnosticsParams { uri, diagnostics, version: None }) |
838 | } | 864 | } |
839 | 865 | ||
diff --git a/crates/ra_lsp_server/src/req.rs b/crates/ra_lsp_server/src/req.rs index b34e6f9b8..40edaf677 100644 --- a/crates/ra_lsp_server/src/req.rs +++ b/crates/ra_lsp_server/src/req.rs | |||
@@ -9,10 +9,10 @@ pub use lsp_types::{ | |||
9 | CodeLensParams, CompletionParams, CompletionResponse, DidChangeConfigurationParams, | 9 | CodeLensParams, CompletionParams, CompletionResponse, DidChangeConfigurationParams, |
10 | DidChangeWatchedFilesParams, DidChangeWatchedFilesRegistrationOptions, | 10 | DidChangeWatchedFilesParams, DidChangeWatchedFilesRegistrationOptions, |
11 | DocumentOnTypeFormattingParams, DocumentSymbolParams, DocumentSymbolResponse, | 11 | DocumentOnTypeFormattingParams, DocumentSymbolParams, DocumentSymbolResponse, |
12 | FileSystemWatcher, Hover, InitializeResult, MessageType, PublishDiagnosticsParams, | 12 | FileSystemWatcher, Hover, InitializeResult, MessageType, ProgressParams, ProgressParamsValue, |
13 | ReferenceParams, Registration, RegistrationParams, SelectionRange, SelectionRangeParams, | 13 | ProgressToken, PublishDiagnosticsParams, ReferenceParams, Registration, RegistrationParams, |
14 | ShowMessageParams, SignatureHelp, TextDocumentEdit, TextDocumentPositionParams, TextEdit, | 14 | SelectionRange, SelectionRangeParams, ShowMessageParams, SignatureHelp, TextDocumentEdit, |
15 | WorkspaceEdit, WorkspaceSymbolParams, | 15 | TextDocumentPositionParams, TextEdit, WorkspaceEdit, WorkspaceSymbolParams, |
16 | }; | 16 | }; |
17 | 17 | ||
18 | pub enum AnalyzerStatus {} | 18 | pub enum AnalyzerStatus {} |
diff --git a/crates/ra_lsp_server/src/world.rs b/crates/ra_lsp_server/src/world.rs index 79431e7e6..4b3959e38 100644 --- a/crates/ra_lsp_server/src/world.rs +++ b/crates/ra_lsp_server/src/world.rs | |||
@@ -12,6 +12,7 @@ use crossbeam_channel::{unbounded, Receiver}; | |||
12 | use lsp_server::ErrorCode; | 12 | use lsp_server::ErrorCode; |
13 | use lsp_types::Url; | 13 | use lsp_types::Url; |
14 | use parking_lot::RwLock; | 14 | use parking_lot::RwLock; |
15 | use ra_cargo_watch::{CheckOptions, CheckWatcher, CheckWatcherSharedState}; | ||
15 | use ra_ide::{ | 16 | use ra_ide::{ |
16 | Analysis, AnalysisChange, AnalysisHost, CrateGraph, FeatureFlags, FileId, LibraryData, | 17 | Analysis, AnalysisChange, AnalysisHost, CrateGraph, FeatureFlags, FileId, LibraryData, |
17 | SourceRootId, | 18 | SourceRootId, |
@@ -34,6 +35,7 @@ pub struct Options { | |||
34 | pub supports_location_link: bool, | 35 | pub supports_location_link: bool, |
35 | pub line_folding_only: bool, | 36 | pub line_folding_only: bool, |
36 | pub max_inlay_hint_length: Option<usize>, | 37 | pub max_inlay_hint_length: Option<usize>, |
38 | pub cargo_watch: CheckOptions, | ||
37 | } | 39 | } |
38 | 40 | ||
39 | /// `WorldState` is the primary mutable state of the language server | 41 | /// `WorldState` is the primary mutable state of the language server |
@@ -52,6 +54,7 @@ pub struct WorldState { | |||
52 | pub vfs: Arc<RwLock<Vfs>>, | 54 | pub vfs: Arc<RwLock<Vfs>>, |
53 | pub task_receiver: Receiver<VfsTask>, | 55 | pub task_receiver: Receiver<VfsTask>, |
54 | pub latest_requests: Arc<RwLock<LatestRequests>>, | 56 | pub latest_requests: Arc<RwLock<LatestRequests>>, |
57 | pub check_watcher: CheckWatcher, | ||
55 | } | 58 | } |
56 | 59 | ||
57 | /// An immutable snapshot of the world's state at a point in time. | 60 | /// An immutable snapshot of the world's state at a point in time. |
@@ -61,6 +64,7 @@ pub struct WorldSnapshot { | |||
61 | pub analysis: Analysis, | 64 | pub analysis: Analysis, |
62 | pub vfs: Arc<RwLock<Vfs>>, | 65 | pub vfs: Arc<RwLock<Vfs>>, |
63 | pub latest_requests: Arc<RwLock<LatestRequests>>, | 66 | pub latest_requests: Arc<RwLock<LatestRequests>>, |
67 | pub check_watcher: Arc<RwLock<CheckWatcherSharedState>>, | ||
64 | } | 68 | } |
65 | 69 | ||
66 | impl WorldState { | 70 | impl WorldState { |
@@ -127,6 +131,10 @@ impl WorldState { | |||
127 | } | 131 | } |
128 | change.set_crate_graph(crate_graph); | 132 | change.set_crate_graph(crate_graph); |
129 | 133 | ||
134 | // FIXME: Figure out the multi-workspace situation | ||
135 | let check_watcher = | ||
136 | CheckWatcher::new(&options.cargo_watch, folder_roots.first().cloned().unwrap()); | ||
137 | |||
130 | let mut analysis_host = AnalysisHost::new(lru_capacity, feature_flags); | 138 | let mut analysis_host = AnalysisHost::new(lru_capacity, feature_flags); |
131 | analysis_host.apply_change(change); | 139 | analysis_host.apply_change(change); |
132 | WorldState { | 140 | WorldState { |
@@ -138,6 +146,7 @@ impl WorldState { | |||
138 | vfs: Arc::new(RwLock::new(vfs)), | 146 | vfs: Arc::new(RwLock::new(vfs)), |
139 | task_receiver, | 147 | task_receiver, |
140 | latest_requests: Default::default(), | 148 | latest_requests: Default::default(), |
149 | check_watcher, | ||
141 | } | 150 | } |
142 | } | 151 | } |
143 | 152 | ||
@@ -199,6 +208,7 @@ impl WorldState { | |||
199 | analysis: self.analysis_host.analysis(), | 208 | analysis: self.analysis_host.analysis(), |
200 | vfs: Arc::clone(&self.vfs), | 209 | vfs: Arc::clone(&self.vfs), |
201 | latest_requests: Arc::clone(&self.latest_requests), | 210 | latest_requests: Arc::clone(&self.latest_requests), |
211 | check_watcher: self.check_watcher.shared.clone(), | ||
202 | } | 212 | } |
203 | } | 213 | } |
204 | 214 | ||
diff --git a/crates/ra_project_model/src/cargo_workspace.rs b/crates/ra_project_model/src/cargo_workspace.rs index c862d3912..1b3c246c7 100644 --- a/crates/ra_project_model/src/cargo_workspace.rs +++ b/crates/ra_project_model/src/cargo_workspace.rs | |||
@@ -165,7 +165,7 @@ impl CargoWorkspace { | |||
165 | // FIXME: `NoDefaultFeatures` is mutual exclusive with `SomeFeatures` | 165 | // FIXME: `NoDefaultFeatures` is mutual exclusive with `SomeFeatures` |
166 | // https://github.com/oli-obk/cargo_metadata/issues/79 | 166 | // https://github.com/oli-obk/cargo_metadata/issues/79 |
167 | meta.features(CargoOpt::NoDefaultFeatures); | 167 | meta.features(CargoOpt::NoDefaultFeatures); |
168 | } else { | 168 | } else if cargo_features.features.len() > 0 { |
169 | meta.features(CargoOpt::SomeFeatures(cargo_features.features.clone())); | 169 | meta.features(CargoOpt::SomeFeatures(cargo_features.features.clone())); |
170 | } | 170 | } |
171 | if let Some(parent) = cargo_toml.parent() { | 171 | if let Some(parent) = cargo_toml.parent() { |
diff --git a/crates/ra_syntax/src/ast.rs b/crates/ra_syntax/src/ast.rs index 277532a8c..89cb9a9f3 100644 --- a/crates/ra_syntax/src/ast.rs +++ b/crates/ra_syntax/src/ast.rs | |||
@@ -17,7 +17,9 @@ use crate::{ | |||
17 | 17 | ||
18 | pub use self::{ | 18 | pub use self::{ |
19 | expr_extensions::{ArrayExprKind, BinOp, ElseBranch, LiteralKind, PrefixOp, RangeOp}, | 19 | expr_extensions::{ArrayExprKind, BinOp, ElseBranch, LiteralKind, PrefixOp, RangeOp}, |
20 | extensions::{FieldKind, PathSegmentKind, SelfParamKind, StructKind, TypeBoundKind}, | 20 | extensions::{ |
21 | FieldKind, PathSegmentKind, SelfParamKind, StructKind, TypeBoundKind, VisibilityKind, | ||
22 | }, | ||
21 | generated::*, | 23 | generated::*, |
22 | tokens::*, | 24 | tokens::*, |
23 | traits::*, | 25 | traits::*, |
diff --git a/crates/ra_syntax/src/ast/extensions.rs b/crates/ra_syntax/src/ast/extensions.rs index a8f625176..d9666cdca 100644 --- a/crates/ra_syntax/src/ast/extensions.rs +++ b/crates/ra_syntax/src/ast/extensions.rs | |||
@@ -221,6 +221,10 @@ impl ast::FnDef { | |||
221 | .and_then(|it| it.into_token()) | 221 | .and_then(|it| it.into_token()) |
222 | .filter(|it| it.kind() == T![;]) | 222 | .filter(|it| it.kind() == T![;]) |
223 | } | 223 | } |
224 | |||
225 | pub fn is_async(&self) -> bool { | ||
226 | self.syntax().children_with_tokens().any(|it| it.kind() == T![async]) | ||
227 | } | ||
224 | } | 228 | } |
225 | 229 | ||
226 | impl ast::LetStmt { | 230 | impl ast::LetStmt { |
@@ -409,3 +413,32 @@ impl ast::TraitDef { | |||
409 | self.syntax().children_with_tokens().any(|t| t.kind() == T![auto]) | 413 | self.syntax().children_with_tokens().any(|t| t.kind() == T![auto]) |
410 | } | 414 | } |
411 | } | 415 | } |
416 | |||
417 | pub enum VisibilityKind { | ||
418 | In(ast::Path), | ||
419 | PubCrate, | ||
420 | PubSuper, | ||
421 | Pub, | ||
422 | } | ||
423 | |||
424 | impl ast::Visibility { | ||
425 | pub fn kind(&self) -> VisibilityKind { | ||
426 | if let Some(path) = children(self).next() { | ||
427 | VisibilityKind::In(path) | ||
428 | } else if self.is_pub_crate() { | ||
429 | VisibilityKind::PubCrate | ||
430 | } else if self.is_pub_super() { | ||
431 | VisibilityKind::PubSuper | ||
432 | } else { | ||
433 | VisibilityKind::Pub | ||
434 | } | ||
435 | } | ||
436 | |||
437 | fn is_pub_crate(&self) -> bool { | ||
438 | self.syntax().children_with_tokens().any(|it| it.kind() == T![crate]) | ||
439 | } | ||
440 | |||
441 | fn is_pub_super(&self) -> bool { | ||
442 | self.syntax().children_with_tokens().any(|it| it.kind() == T![super]) | ||
443 | } | ||
444 | } | ||
diff --git a/crates/ra_syntax/src/ast/generated.rs b/crates/ra_syntax/src/ast/generated.rs index 9f9d6e63c..e64c83d33 100644 --- a/crates/ra_syntax/src/ast/generated.rs +++ b/crates/ra_syntax/src/ast/generated.rs | |||
@@ -1064,6 +1064,7 @@ impl AstNode for ExternCrateItem { | |||
1064 | } | 1064 | } |
1065 | } | 1065 | } |
1066 | impl ast::AttrsOwner for ExternCrateItem {} | 1066 | impl ast::AttrsOwner for ExternCrateItem {} |
1067 | impl ast::VisibilityOwner for ExternCrateItem {} | ||
1067 | impl ExternCrateItem { | 1068 | impl ExternCrateItem { |
1068 | pub fn name_ref(&self) -> Option<NameRef> { | 1069 | pub fn name_ref(&self) -> Option<NameRef> { |
1069 | AstChildren::new(&self.syntax).next() | 1070 | AstChildren::new(&self.syntax).next() |
@@ -2006,6 +2007,7 @@ impl AstNode for ModuleItem { | |||
2006 | } | 2007 | } |
2007 | } | 2008 | } |
2008 | impl ast::AttrsOwner for ModuleItem {} | 2009 | impl ast::AttrsOwner for ModuleItem {} |
2010 | impl ast::VisibilityOwner for ModuleItem {} | ||
2009 | impl ModuleItem {} | 2011 | impl ModuleItem {} |
2010 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | 2012 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
2011 | pub struct Name { | 2013 | pub struct Name { |
@@ -3893,6 +3895,7 @@ impl AstNode for UseItem { | |||
3893 | } | 3895 | } |
3894 | } | 3896 | } |
3895 | impl ast::AttrsOwner for UseItem {} | 3897 | impl ast::AttrsOwner for UseItem {} |
3898 | impl ast::VisibilityOwner for UseItem {} | ||
3896 | impl UseItem { | 3899 | impl UseItem { |
3897 | pub fn use_tree(&self) -> Option<UseTree> { | 3900 | pub fn use_tree(&self) -> Option<UseTree> { |
3898 | AstChildren::new(&self.syntax).next() | 3901 | AstChildren::new(&self.syntax).next() |
diff --git a/crates/ra_syntax/src/grammar.ron b/crates/ra_syntax/src/grammar.ron index 08aafb610..e43a724f0 100644 --- a/crates/ra_syntax/src/grammar.ron +++ b/crates/ra_syntax/src/grammar.ron | |||
@@ -412,7 +412,7 @@ Grammar( | |||
412 | "ModuleItem": ( | 412 | "ModuleItem": ( |
413 | enum: ["StructDef", "UnionDef", "EnumDef", "FnDef", "TraitDef", "TypeAliasDef", "ImplBlock", | 413 | enum: ["StructDef", "UnionDef", "EnumDef", "FnDef", "TraitDef", "TypeAliasDef", "ImplBlock", |
414 | "UseItem", "ExternCrateItem", "ConstDef", "StaticDef", "Module" ], | 414 | "UseItem", "ExternCrateItem", "ConstDef", "StaticDef", "Module" ], |
415 | traits: ["AttrsOwner"], | 415 | traits: ["AttrsOwner", "VisibilityOwner"], |
416 | ), | 416 | ), |
417 | "ImplItem": ( | 417 | "ImplItem": ( |
418 | enum: ["FnDef", "TypeAliasDef", "ConstDef"], | 418 | enum: ["FnDef", "TypeAliasDef", "ConstDef"], |
@@ -683,7 +683,7 @@ Grammar( | |||
683 | ] | 683 | ] |
684 | ), | 684 | ), |
685 | "UseItem": ( | 685 | "UseItem": ( |
686 | traits: ["AttrsOwner"], | 686 | traits: ["AttrsOwner", "VisibilityOwner"], |
687 | options: [ "UseTree" ], | 687 | options: [ "UseTree" ], |
688 | ), | 688 | ), |
689 | "UseTree": ( | 689 | "UseTree": ( |
@@ -696,7 +696,7 @@ Grammar( | |||
696 | collections: [("use_trees", "UseTree")] | 696 | collections: [("use_trees", "UseTree")] |
697 | ), | 697 | ), |
698 | "ExternCrateItem": ( | 698 | "ExternCrateItem": ( |
699 | traits: ["AttrsOwner"], | 699 | traits: ["AttrsOwner", "VisibilityOwner"], |
700 | options: ["NameRef", "Alias"], | 700 | options: ["NameRef", "Alias"], |
701 | ), | 701 | ), |
702 | "ArgList": ( | 702 | "ArgList": ( |
diff --git a/docs/user/assists.md b/docs/user/assists.md index 334ba450f..ecf206f71 100644 --- a/docs/user/assists.md +++ b/docs/user/assists.md | |||
@@ -98,26 +98,26 @@ Adds scaffold for overriding default impl members. | |||
98 | 98 | ||
99 | ```rust | 99 | ```rust |
100 | // BEFORE | 100 | // BEFORE |
101 | trait T { | 101 | trait Trait { |
102 | Type X; | 102 | Type X; |
103 | fn foo(&self); | 103 | fn foo(&self); |
104 | fn bar(&self) {} | 104 | fn bar(&self) {} |
105 | } | 105 | } |
106 | 106 | ||
107 | impl T for () { | 107 | impl Trait for () { |
108 | Type X = (); | 108 | Type X = (); |
109 | fn foo(&self) {}┃ | 109 | fn foo(&self) {}┃ |
110 | 110 | ||
111 | } | 111 | } |
112 | 112 | ||
113 | // AFTER | 113 | // AFTER |
114 | trait T { | 114 | trait Trait { |
115 | Type X; | 115 | Type X; |
116 | fn foo(&self); | 116 | fn foo(&self); |
117 | fn bar(&self) {} | 117 | fn bar(&self) {} |
118 | } | 118 | } |
119 | 119 | ||
120 | impl T for () { | 120 | impl Trait for () { |
121 | Type X = (); | 121 | Type X = (); |
122 | fn foo(&self) {} | 122 | fn foo(&self) {} |
123 | fn bar(&self) {} | 123 | fn bar(&self) {} |
@@ -131,25 +131,25 @@ Adds scaffold for required impl members. | |||
131 | 131 | ||
132 | ```rust | 132 | ```rust |
133 | // BEFORE | 133 | // BEFORE |
134 | trait T { | 134 | trait Trait<T> { |
135 | Type X; | 135 | Type X; |
136 | fn foo(&self); | 136 | fn foo(&self) -> T; |
137 | fn bar(&self) {} | 137 | fn bar(&self) {} |
138 | } | 138 | } |
139 | 139 | ||
140 | impl T for () {┃ | 140 | impl Trait<u32> for () {┃ |
141 | 141 | ||
142 | } | 142 | } |
143 | 143 | ||
144 | // AFTER | 144 | // AFTER |
145 | trait T { | 145 | trait Trait<T> { |
146 | Type X; | 146 | Type X; |
147 | fn foo(&self); | 147 | fn foo(&self) -> T; |
148 | fn bar(&self) {} | 148 | fn bar(&self) {} |
149 | } | 149 | } |
150 | 150 | ||
151 | impl T for () { | 151 | impl Trait<u32> for () { |
152 | fn foo(&self) { unimplemented!() } | 152 | fn foo(&self) -> u32 { unimplemented!() } |
153 | 153 | ||
154 | } | 154 | } |
155 | ``` | 155 | ``` |
diff --git a/editors/code/package.json b/editors/code/package.json index 8e7046418..f28ce1772 100644 --- a/editors/code/package.json +++ b/editors/code/package.json | |||
@@ -18,7 +18,7 @@ | |||
18 | "scripts": { | 18 | "scripts": { |
19 | "vscode:prepublish": "npm run compile", | 19 | "vscode:prepublish": "npm run compile", |
20 | "package": "vsce package", | 20 | "package": "vsce package", |
21 | "compile": "rollup -c && shx cp src/utils/terminateProcess.sh bundle/terminateProcess.sh", | 21 | "compile": "rollup -c", |
22 | "watch": "tsc -watch -p ./", | 22 | "watch": "tsc -watch -p ./", |
23 | "fix": "prettier **/*.{json,ts} --write && tslint --project . --fix", | 23 | "fix": "prettier **/*.{json,ts} --write && tslint --project . --fix", |
24 | "lint": "tslint --project .", | 24 | "lint": "tslint --project .", |
@@ -134,16 +134,6 @@ | |||
134 | "command": "rust-analyzer.reload", | 134 | "command": "rust-analyzer.reload", |
135 | "title": "Restart server", | 135 | "title": "Restart server", |
136 | "category": "Rust Analyzer" | 136 | "category": "Rust Analyzer" |
137 | }, | ||
138 | { | ||
139 | "command": "rust-analyzer.startCargoWatch", | ||
140 | "title": "Start Cargo Watch", | ||
141 | "category": "Rust Analyzer" | ||
142 | }, | ||
143 | { | ||
144 | "command": "rust-analyzer.stopCargoWatch", | ||
145 | "title": "Stop Cargo Watch", | ||
146 | "category": "Rust Analyzer" | ||
147 | } | 137 | } |
148 | ], | 138 | ], |
149 | "keybindings": [ | 139 | "keybindings": [ |
@@ -261,21 +251,6 @@ | |||
261 | "default": "ra_lsp_server", | 251 | "default": "ra_lsp_server", |
262 | "description": "Path to ra_lsp_server executable" | 252 | "description": "Path to ra_lsp_server executable" |
263 | }, | 253 | }, |
264 | "rust-analyzer.enableCargoWatchOnStartup": { | ||
265 | "type": "string", | ||
266 | "default": "ask", | ||
267 | "enum": [ | ||
268 | "ask", | ||
269 | "enabled", | ||
270 | "disabled" | ||
271 | ], | ||
272 | "enumDescriptions": [ | ||
273 | "Asks each time whether to run `cargo watch`", | ||
274 | "`cargo watch` is always started", | ||
275 | "Don't start `cargo watch`" | ||
276 | ], | ||
277 | "description": "Whether to run `cargo watch` on startup" | ||
278 | }, | ||
279 | "rust-analyzer.excludeGlobs": { | 254 | "rust-analyzer.excludeGlobs": { |
280 | "type": "array", | 255 | "type": "array", |
281 | "default": [], | 256 | "default": [], |
@@ -286,21 +261,21 @@ | |||
286 | "default": true, | 261 | "default": true, |
287 | "description": "client provided file watching instead of notify watching." | 262 | "description": "client provided file watching instead of notify watching." |
288 | }, | 263 | }, |
264 | "rust-analyzer.cargo-watch.enable": { | ||
265 | "type": "boolean", | ||
266 | "default": true, | ||
267 | "description": "Run `cargo check` for diagnostics on save" | ||
268 | }, | ||
289 | "rust-analyzer.cargo-watch.arguments": { | 269 | "rust-analyzer.cargo-watch.arguments": { |
290 | "type": "string", | 270 | "type": "array", |
291 | "description": "`cargo-watch` arguments. (e.g: `--features=\"shumway,pdf\"` will run as `cargo watch -x \"check --features=\"shumway,pdf\"\"` )", | 271 | "description": "`cargo-watch` arguments. (e.g: `--features=\"shumway,pdf\"` will run as `cargo watch -x \"check --features=\"shumway,pdf\"\"` )", |
292 | "default": "" | 272 | "default": [] |
293 | }, | 273 | }, |
294 | "rust-analyzer.cargo-watch.command": { | 274 | "rust-analyzer.cargo-watch.command": { |
295 | "type": "string", | 275 | "type": "string", |
296 | "description": "`cargo-watch` command. (e.g: `clippy` will run as `cargo watch -x clippy` )", | 276 | "description": "`cargo-watch` command. (e.g: `clippy` will run as `cargo watch -x clippy` )", |
297 | "default": "check" | 277 | "default": "check" |
298 | }, | 278 | }, |
299 | "rust-analyzer.cargo-watch.ignore": { | ||
300 | "type": "array", | ||
301 | "description": "A list of patterns for cargo-watch to ignore (will be passed as `--ignore`)", | ||
302 | "default": [] | ||
303 | }, | ||
304 | "rust-analyzer.cargo-watch.allTargets": { | 279 | "rust-analyzer.cargo-watch.allTargets": { |
305 | "type": "boolean", | 280 | "type": "boolean", |
306 | "description": "Check all targets and tests (will be passed as `--all-targets`)", | 281 | "description": "Check all targets and tests (will be passed as `--all-targets`)", |
@@ -322,17 +297,6 @@ | |||
322 | "default": "off", | 297 | "default": "off", |
323 | "description": "Trace requests to the ra_lsp_server" | 298 | "description": "Trace requests to the ra_lsp_server" |
324 | }, | 299 | }, |
325 | "rust-analyzer.trace.cargo-watch": { | ||
326 | "type": "string", | ||
327 | "scope": "window", | ||
328 | "enum": [ | ||
329 | "off", | ||
330 | "error", | ||
331 | "verbose" | ||
332 | ], | ||
333 | "default": "off", | ||
334 | "description": "Trace output of cargo-watch" | ||
335 | }, | ||
336 | "rust-analyzer.lruCapacity": { | 300 | "rust-analyzer.lruCapacity": { |
337 | "type": "number", | 301 | "type": "number", |
338 | "default": null, | 302 | "default": null, |
diff --git a/editors/code/src/commands/cargo_watch.ts b/editors/code/src/commands/cargo_watch.ts deleted file mode 100644 index ac62bdd48..000000000 --- a/editors/code/src/commands/cargo_watch.ts +++ /dev/null | |||
@@ -1,264 +0,0 @@ | |||
1 | import * as child_process from 'child_process'; | ||
2 | import * as path from 'path'; | ||
3 | import * as vscode from 'vscode'; | ||
4 | |||
5 | import { Server } from '../server'; | ||
6 | import { terminate } from '../utils/processes'; | ||
7 | import { LineBuffer } from './line_buffer'; | ||
8 | import { StatusDisplay } from './watch_status'; | ||
9 | |||
10 | import { | ||
11 | mapRustDiagnosticToVsCode, | ||
12 | RustDiagnostic, | ||
13 | } from '../utils/diagnostics/rust'; | ||
14 | import SuggestedFixCollection from '../utils/diagnostics/SuggestedFixCollection'; | ||
15 | import { areDiagnosticsEqual } from '../utils/diagnostics/vscode'; | ||
16 | |||
17 | export async function registerCargoWatchProvider( | ||
18 | subscriptions: vscode.Disposable[], | ||
19 | ): Promise<CargoWatchProvider | undefined> { | ||
20 | let cargoExists = false; | ||
21 | |||
22 | // Check if the working directory is valid cargo root path | ||
23 | const cargoTomlPath = path.join(vscode.workspace.rootPath!, 'Cargo.toml'); | ||
24 | const cargoTomlUri = vscode.Uri.file(cargoTomlPath); | ||
25 | const cargoTomlFileInfo = await vscode.workspace.fs.stat(cargoTomlUri); | ||
26 | |||
27 | if (cargoTomlFileInfo) { | ||
28 | cargoExists = true; | ||
29 | } | ||
30 | |||
31 | if (!cargoExists) { | ||
32 | vscode.window.showErrorMessage( | ||
33 | `Couldn\'t find \'Cargo.toml\' at ${cargoTomlPath}`, | ||
34 | ); | ||
35 | return; | ||
36 | } | ||
37 | |||
38 | const provider = new CargoWatchProvider(); | ||
39 | subscriptions.push(provider); | ||
40 | return provider; | ||
41 | } | ||
42 | |||
43 | export class CargoWatchProvider implements vscode.Disposable { | ||
44 | private readonly diagnosticCollection: vscode.DiagnosticCollection; | ||
45 | private readonly statusDisplay: StatusDisplay; | ||
46 | private readonly outputChannel: vscode.OutputChannel; | ||
47 | |||
48 | private suggestedFixCollection: SuggestedFixCollection; | ||
49 | private codeActionDispose: vscode.Disposable; | ||
50 | |||
51 | private cargoProcess?: child_process.ChildProcess; | ||
52 | |||
53 | constructor() { | ||
54 | this.diagnosticCollection = vscode.languages.createDiagnosticCollection( | ||
55 | 'rustc', | ||
56 | ); | ||
57 | this.statusDisplay = new StatusDisplay( | ||
58 | Server.config.cargoWatchOptions.command, | ||
59 | ); | ||
60 | this.outputChannel = vscode.window.createOutputChannel( | ||
61 | 'Cargo Watch Trace', | ||
62 | ); | ||
63 | |||
64 | // Track `rustc`'s suggested fixes so we can convert them to code actions | ||
65 | this.suggestedFixCollection = new SuggestedFixCollection(); | ||
66 | this.codeActionDispose = vscode.languages.registerCodeActionsProvider( | ||
67 | [{ scheme: 'file', language: 'rust' }], | ||
68 | this.suggestedFixCollection, | ||
69 | { | ||
70 | providedCodeActionKinds: | ||
71 | SuggestedFixCollection.PROVIDED_CODE_ACTION_KINDS, | ||
72 | }, | ||
73 | ); | ||
74 | } | ||
75 | |||
76 | public start() { | ||
77 | if (this.cargoProcess) { | ||
78 | vscode.window.showInformationMessage( | ||
79 | 'Cargo Watch is already running', | ||
80 | ); | ||
81 | return; | ||
82 | } | ||
83 | |||
84 | let args = | ||
85 | Server.config.cargoWatchOptions.command + ' --message-format json'; | ||
86 | if (Server.config.cargoWatchOptions.allTargets) { | ||
87 | args += ' --all-targets'; | ||
88 | } | ||
89 | if (Server.config.cargoWatchOptions.command.length > 0) { | ||
90 | // Excape the double quote string: | ||
91 | args += ' ' + Server.config.cargoWatchOptions.arguments; | ||
92 | } | ||
93 | // Windows handles arguments differently than the unix-likes, so we need to wrap the args in double quotes | ||
94 | if (process.platform === 'win32') { | ||
95 | args = '"' + args + '"'; | ||
96 | } | ||
97 | |||
98 | const ignoreFlags = Server.config.cargoWatchOptions.ignore.reduce( | ||
99 | (flags, pattern) => [...flags, '--ignore', pattern], | ||
100 | [] as string[], | ||
101 | ); | ||
102 | |||
103 | // Start the cargo watch with json message | ||
104 | this.cargoProcess = child_process.spawn( | ||
105 | 'cargo', | ||
106 | ['watch', '-x', args, ...ignoreFlags], | ||
107 | { | ||
108 | stdio: ['ignore', 'pipe', 'pipe'], | ||
109 | cwd: vscode.workspace.rootPath, | ||
110 | windowsVerbatimArguments: true, | ||
111 | }, | ||
112 | ); | ||
113 | |||
114 | if (!this.cargoProcess) { | ||
115 | vscode.window.showErrorMessage('Cargo Watch failed to start'); | ||
116 | return; | ||
117 | } | ||
118 | |||
119 | const stdoutData = new LineBuffer(); | ||
120 | this.cargoProcess.stdout?.on('data', (s: string) => { | ||
121 | stdoutData.processOutput(s, line => { | ||
122 | this.logInfo(line); | ||
123 | try { | ||
124 | this.parseLine(line); | ||
125 | } catch (err) { | ||
126 | this.logError(`Failed to parse: ${err}, content : ${line}`); | ||
127 | } | ||
128 | }); | ||
129 | }); | ||
130 | |||
131 | const stderrData = new LineBuffer(); | ||
132 | this.cargoProcess.stderr?.on('data', (s: string) => { | ||
133 | stderrData.processOutput(s, line => { | ||
134 | this.logError('Error on cargo-watch : {\n' + line + '}\n'); | ||
135 | }); | ||
136 | }); | ||
137 | |||
138 | this.cargoProcess.on('error', (err: Error) => { | ||
139 | this.logError( | ||
140 | 'Error on cargo-watch process : {\n' + err.message + '}\n', | ||
141 | ); | ||
142 | }); | ||
143 | |||
144 | this.logInfo('cargo-watch started.'); | ||
145 | } | ||
146 | |||
147 | public stop() { | ||
148 | if (this.cargoProcess) { | ||
149 | this.cargoProcess.kill(); | ||
150 | terminate(this.cargoProcess); | ||
151 | this.cargoProcess = undefined; | ||
152 | } else { | ||
153 | vscode.window.showInformationMessage('Cargo Watch is not running'); | ||
154 | } | ||
155 | } | ||
156 | |||
157 | public dispose(): void { | ||
158 | this.stop(); | ||
159 | |||
160 | this.diagnosticCollection.clear(); | ||
161 | this.diagnosticCollection.dispose(); | ||
162 | this.outputChannel.dispose(); | ||
163 | this.statusDisplay.dispose(); | ||
164 | this.codeActionDispose.dispose(); | ||
165 | } | ||
166 | |||
167 | private logInfo(line: string) { | ||
168 | if (Server.config.cargoWatchOptions.trace === 'verbose') { | ||
169 | this.outputChannel.append(line); | ||
170 | } | ||
171 | } | ||
172 | |||
173 | private logError(line: string) { | ||
174 | if ( | ||
175 | Server.config.cargoWatchOptions.trace === 'error' || | ||
176 | Server.config.cargoWatchOptions.trace === 'verbose' | ||
177 | ) { | ||
178 | this.outputChannel.append(line); | ||
179 | } | ||
180 | } | ||
181 | |||
182 | private parseLine(line: string) { | ||
183 | if (line.startsWith('[Running')) { | ||
184 | this.diagnosticCollection.clear(); | ||
185 | this.suggestedFixCollection.clear(); | ||
186 | this.statusDisplay.show(); | ||
187 | } | ||
188 | |||
189 | if (line.startsWith('[Finished running')) { | ||
190 | this.statusDisplay.hide(); | ||
191 | } | ||
192 | |||
193 | interface CargoArtifact { | ||
194 | reason: string; | ||
195 | package_id: string; | ||
196 | } | ||
197 | |||
198 | // https://github.com/rust-lang/cargo/blob/master/src/cargo/util/machine_message.rs | ||
199 | interface CargoMessage { | ||
200 | reason: string; | ||
201 | package_id: string; | ||
202 | message: RustDiagnostic; | ||
203 | } | ||
204 | |||
205 | // cargo-watch itself output non json format | ||
206 | // Ignore these lines | ||
207 | let data: CargoMessage; | ||
208 | try { | ||
209 | data = JSON.parse(line.trim()); | ||
210 | } catch (error) { | ||
211 | this.logError(`Fail to parse to json : { ${error} }`); | ||
212 | return; | ||
213 | } | ||
214 | |||
215 | if (data.reason === 'compiler-artifact') { | ||
216 | const msg = data as CargoArtifact; | ||
217 | |||
218 | // The format of the package_id is "{name} {version} ({source_id})", | ||
219 | // https://github.com/rust-lang/cargo/blob/37ad03f86e895bb80b474c1c088322634f4725f5/src/cargo/core/package_id.rs#L53 | ||
220 | this.statusDisplay.packageName = msg.package_id.split(' ')[0]; | ||
221 | } else if (data.reason === 'compiler-message') { | ||
222 | const msg = data.message as RustDiagnostic; | ||
223 | |||
224 | const mapResult = mapRustDiagnosticToVsCode(msg); | ||
225 | if (!mapResult) { | ||
226 | return; | ||
227 | } | ||
228 | |||
229 | const { location, diagnostic, suggestedFixes } = mapResult; | ||
230 | const fileUri = location.uri; | ||
231 | |||
232 | const diagnostics: vscode.Diagnostic[] = [ | ||
233 | ...(this.diagnosticCollection!.get(fileUri) || []), | ||
234 | ]; | ||
235 | |||
236 | // If we're building multiple targets it's possible we've already seen this diagnostic | ||
237 | const isDuplicate = diagnostics.some(d => | ||
238 | areDiagnosticsEqual(d, diagnostic), | ||
239 | ); | ||
240 | if (isDuplicate) { | ||
241 | return; | ||
242 | } | ||
243 | |||
244 | diagnostics.push(diagnostic); | ||
245 | this.diagnosticCollection!.set(fileUri, diagnostics); | ||
246 | |||
247 | if (suggestedFixes.length) { | ||
248 | for (const suggestedFix of suggestedFixes) { | ||
249 | this.suggestedFixCollection.addSuggestedFixForDiagnostic( | ||
250 | suggestedFix, | ||
251 | diagnostic, | ||
252 | ); | ||
253 | } | ||
254 | |||
255 | // Have VsCode query us for the code actions | ||
256 | vscode.commands.executeCommand( | ||
257 | 'vscode.executeCodeActionProvider', | ||
258 | fileUri, | ||
259 | diagnostic.range, | ||
260 | ); | ||
261 | } | ||
262 | } | ||
263 | } | ||
264 | } | ||
diff --git a/editors/code/src/commands/runnables.ts b/editors/code/src/commands/runnables.ts index cf980e257..7728541de 100644 --- a/editors/code/src/commands/runnables.ts +++ b/editors/code/src/commands/runnables.ts | |||
@@ -1,11 +1,7 @@ | |||
1 | import * as child_process from 'child_process'; | ||
2 | |||
3 | import * as util from 'util'; | ||
4 | import * as vscode from 'vscode'; | 1 | import * as vscode from 'vscode'; |
5 | import * as lc from 'vscode-languageclient'; | 2 | import * as lc from 'vscode-languageclient'; |
6 | 3 | ||
7 | import { Server } from '../server'; | 4 | import { Server } from '../server'; |
8 | import { CargoWatchProvider, registerCargoWatchProvider } from './cargo_watch'; | ||
9 | 5 | ||
10 | interface RunnablesParams { | 6 | interface RunnablesParams { |
11 | textDocument: lc.TextDocumentIdentifier; | 7 | textDocument: lc.TextDocumentIdentifier; |
@@ -131,90 +127,3 @@ export async function handleSingle(runnable: Runnable) { | |||
131 | 127 | ||
132 | return vscode.tasks.executeTask(task); | 128 | return vscode.tasks.executeTask(task); |
133 | } | 129 | } |
134 | |||
135 | /** | ||
136 | * Interactively asks the user whether we should run `cargo check` in order to | ||
137 | * provide inline diagnostics; the user is met with a series of dialog boxes | ||
138 | * that, when accepted, allow us to `cargo install cargo-watch` and then run it. | ||
139 | */ | ||
140 | export async function interactivelyStartCargoWatch( | ||
141 | context: vscode.ExtensionContext, | ||
142 | ): Promise<CargoWatchProvider | undefined> { | ||
143 | if (Server.config.cargoWatchOptions.enableOnStartup === 'disabled') { | ||
144 | return; | ||
145 | } | ||
146 | |||
147 | if (Server.config.cargoWatchOptions.enableOnStartup === 'ask') { | ||
148 | const watch = await vscode.window.showInformationMessage( | ||
149 | 'Start watching changes with cargo? (Executes `cargo watch`, provides inline diagnostics)', | ||
150 | 'yes', | ||
151 | 'no', | ||
152 | ); | ||
153 | if (watch !== 'yes') { | ||
154 | return; | ||
155 | } | ||
156 | } | ||
157 | |||
158 | return startCargoWatch(context); | ||
159 | } | ||
160 | |||
161 | export async function startCargoWatch( | ||
162 | context: vscode.ExtensionContext, | ||
163 | ): Promise<CargoWatchProvider | undefined> { | ||
164 | const execPromise = util.promisify(child_process.exec); | ||
165 | |||
166 | const { stderr, code = 0 } = await execPromise( | ||
167 | 'cargo watch --version', | ||
168 | ).catch(e => e); | ||
169 | |||
170 | if (stderr.includes('no such subcommand: `watch`')) { | ||
171 | const msg = | ||
172 | 'The `cargo-watch` subcommand is not installed. Install? (takes ~1-2 minutes)'; | ||
173 | const install = await vscode.window.showInformationMessage( | ||
174 | msg, | ||
175 | 'yes', | ||
176 | 'no', | ||
177 | ); | ||
178 | if (install !== 'yes') { | ||
179 | return; | ||
180 | } | ||
181 | |||
182 | const label = 'install-cargo-watch'; | ||
183 | const taskFinished = new Promise((resolve, _reject) => { | ||
184 | const disposable = vscode.tasks.onDidEndTask(({ execution }) => { | ||
185 | if (execution.task.name === label) { | ||
186 | disposable.dispose(); | ||
187 | resolve(); | ||
188 | } | ||
189 | }); | ||
190 | }); | ||
191 | |||
192 | vscode.tasks.executeTask( | ||
193 | createTask({ | ||
194 | label, | ||
195 | bin: 'cargo', | ||
196 | args: ['install', 'cargo-watch'], | ||
197 | env: {}, | ||
198 | }), | ||
199 | ); | ||
200 | await taskFinished; | ||
201 | const output = await execPromise('cargo watch --version').catch(e => e); | ||
202 | if (output.stderr !== '') { | ||
203 | vscode.window.showErrorMessage( | ||
204 | `Couldn't install \`cargo-\`watch: ${output.stderr}`, | ||
205 | ); | ||
206 | return; | ||
207 | } | ||
208 | } else if (code !== 0) { | ||
209 | vscode.window.showErrorMessage( | ||
210 | `\`cargo watch\` failed with ${code}: ${stderr}`, | ||
211 | ); | ||
212 | return; | ||
213 | } | ||
214 | |||
215 | const provider = await registerCargoWatchProvider(context.subscriptions); | ||
216 | if (provider) { | ||
217 | provider.start(); | ||
218 | } | ||
219 | return provider; | ||
220 | } | ||
diff --git a/editors/code/src/commands/watch_status.ts b/editors/code/src/commands/watch_status.ts index 8d64394c7..10787b510 100644 --- a/editors/code/src/commands/watch_status.ts +++ b/editors/code/src/commands/watch_status.ts | |||
@@ -57,7 +57,49 @@ export class StatusDisplay implements vscode.Disposable { | |||
57 | this.statusBarItem.dispose(); | 57 | this.statusBarItem.dispose(); |
58 | } | 58 | } |
59 | 59 | ||
60 | public handleProgressNotification(params: ProgressParams) { | ||
61 | const { token, value } = params; | ||
62 | if (token !== 'rustAnalyzer/cargoWatcher') { | ||
63 | return; | ||
64 | } | ||
65 | |||
66 | switch (value.kind) { | ||
67 | case 'begin': | ||
68 | this.show(); | ||
69 | break; | ||
70 | |||
71 | case 'report': | ||
72 | if (value.message) { | ||
73 | this.packageName = value.message; | ||
74 | } | ||
75 | break; | ||
76 | |||
77 | case 'end': | ||
78 | this.hide(); | ||
79 | break; | ||
80 | } | ||
81 | } | ||
82 | |||
60 | private frame() { | 83 | private frame() { |
61 | return spinnerFrames[(this.i = ++this.i % spinnerFrames.length)]; | 84 | return spinnerFrames[(this.i = ++this.i % spinnerFrames.length)]; |
62 | } | 85 | } |
63 | } | 86 | } |
87 | |||
88 | // FIXME: Replace this once vscode-languageclient is updated to LSP 3.15 | ||
89 | interface ProgressParams { | ||
90 | token: string; | ||
91 | value: WorkDoneProgress; | ||
92 | } | ||
93 | |||
94 | enum WorkDoneProgressKind { | ||
95 | Begin = 'begin', | ||
96 | Report = 'report', | ||
97 | End = 'end', | ||
98 | } | ||
99 | |||
100 | interface WorkDoneProgress { | ||
101 | kind: WorkDoneProgressKind; | ||
102 | message?: string; | ||
103 | cancelable?: boolean; | ||
104 | percentage?: string; | ||
105 | } | ||
diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts index c06dddb1c..a88be6e35 100644 --- a/editors/code/src/config.ts +++ b/editors/code/src/config.ts | |||
@@ -5,15 +5,10 @@ import { Server } from './server'; | |||
5 | 5 | ||
6 | const RA_LSP_DEBUG = process.env.__RA_LSP_SERVER_DEBUG; | 6 | const RA_LSP_DEBUG = process.env.__RA_LSP_SERVER_DEBUG; |
7 | 7 | ||
8 | export type CargoWatchStartupOptions = 'ask' | 'enabled' | 'disabled'; | ||
9 | export type CargoWatchTraceOptions = 'off' | 'error' | 'verbose'; | ||
10 | |||
11 | export interface CargoWatchOptions { | 8 | export interface CargoWatchOptions { |
12 | enableOnStartup: CargoWatchStartupOptions; | 9 | enable: boolean; |
13 | arguments: string; | 10 | arguments: string[]; |
14 | command: string; | 11 | command: string; |
15 | trace: CargoWatchTraceOptions; | ||
16 | ignore: string[]; | ||
17 | allTargets: boolean; | 12 | allTargets: boolean; |
18 | } | 13 | } |
19 | 14 | ||
@@ -37,11 +32,9 @@ export class Config { | |||
37 | // for internal use | 32 | // for internal use |
38 | public withSysroot: null | boolean = null; | 33 | public withSysroot: null | boolean = null; |
39 | public cargoWatchOptions: CargoWatchOptions = { | 34 | public cargoWatchOptions: CargoWatchOptions = { |
40 | enableOnStartup: 'ask', | 35 | enable: true, |
41 | trace: 'off', | 36 | arguments: [], |
42 | arguments: '', | ||
43 | command: '', | 37 | command: '', |
44 | ignore: [], | ||
45 | allTargets: true, | 38 | allTargets: true, |
46 | }; | 39 | }; |
47 | public cargoFeatures: CargoFeatures = { | 40 | public cargoFeatures: CargoFeatures = { |
@@ -104,23 +97,17 @@ export class Config { | |||
104 | RA_LSP_DEBUG || (config.get('raLspServerPath') as string); | 97 | RA_LSP_DEBUG || (config.get('raLspServerPath') as string); |
105 | } | 98 | } |
106 | 99 | ||
107 | if (config.has('enableCargoWatchOnStartup')) { | 100 | if (config.has('cargo-watch.enable')) { |
108 | this.cargoWatchOptions.enableOnStartup = config.get< | 101 | this.cargoWatchOptions.enable = config.get<boolean>( |
109 | CargoWatchStartupOptions | 102 | 'cargo-watch.enable', |
110 | >('enableCargoWatchOnStartup', 'ask'); | 103 | true, |
111 | } | ||
112 | |||
113 | if (config.has('trace.cargo-watch')) { | ||
114 | this.cargoWatchOptions.trace = config.get<CargoWatchTraceOptions>( | ||
115 | 'trace.cargo-watch', | ||
116 | 'off', | ||
117 | ); | 104 | ); |
118 | } | 105 | } |
119 | 106 | ||
120 | if (config.has('cargo-watch.arguments')) { | 107 | if (config.has('cargo-watch.arguments')) { |
121 | this.cargoWatchOptions.arguments = config.get<string>( | 108 | this.cargoWatchOptions.arguments = config.get<string[]>( |
122 | 'cargo-watch.arguments', | 109 | 'cargo-watch.arguments', |
123 | '', | 110 | [], |
124 | ); | 111 | ); |
125 | } | 112 | } |
126 | 113 | ||
@@ -131,13 +118,6 @@ export class Config { | |||
131 | ); | 118 | ); |
132 | } | 119 | } |
133 | 120 | ||
134 | if (config.has('cargo-watch.ignore')) { | ||
135 | this.cargoWatchOptions.ignore = config.get<string[]>( | ||
136 | 'cargo-watch.ignore', | ||
137 | [], | ||
138 | ); | ||
139 | } | ||
140 | |||
141 | if (config.has('cargo-watch.allTargets')) { | 121 | if (config.has('cargo-watch.allTargets')) { |
142 | this.cargoWatchOptions.allTargets = config.get<boolean>( | 122 | this.cargoWatchOptions.allTargets = config.get<boolean>( |
143 | 'cargo-watch.allTargets', | 123 | 'cargo-watch.allTargets', |
diff --git a/editors/code/src/extension.ts b/editors/code/src/extension.ts index 815f3692c..1da10ebd0 100644 --- a/editors/code/src/extension.ts +++ b/editors/code/src/extension.ts | |||
@@ -2,14 +2,10 @@ import * as vscode from 'vscode'; | |||
2 | import * as lc from 'vscode-languageclient'; | 2 | import * as lc from 'vscode-languageclient'; |
3 | 3 | ||
4 | import * as commands from './commands'; | 4 | import * as commands from './commands'; |
5 | import { CargoWatchProvider } from './commands/cargo_watch'; | ||
6 | import { ExpandMacroContentProvider } from './commands/expand_macro'; | 5 | import { ExpandMacroContentProvider } from './commands/expand_macro'; |
7 | import { HintsUpdater } from './commands/inlay_hints'; | 6 | import { HintsUpdater } from './commands/inlay_hints'; |
8 | import { | ||
9 | interactivelyStartCargoWatch, | ||
10 | startCargoWatch, | ||
11 | } from './commands/runnables'; | ||
12 | import { SyntaxTreeContentProvider } from './commands/syntaxTree'; | 7 | import { SyntaxTreeContentProvider } from './commands/syntaxTree'; |
8 | import { StatusDisplay } from './commands/watch_status'; | ||
13 | import * as events from './events'; | 9 | import * as events from './events'; |
14 | import * as notifications from './notifications'; | 10 | import * as notifications from './notifications'; |
15 | import { Server } from './server'; | 11 | import { Server } from './server'; |
@@ -88,6 +84,11 @@ export async function activate(context: vscode.ExtensionContext) { | |||
88 | overrideCommand('type', commands.onEnter.handle); | 84 | overrideCommand('type', commands.onEnter.handle); |
89 | } | 85 | } |
90 | 86 | ||
87 | const watchStatus = new StatusDisplay( | ||
88 | Server.config.cargoWatchOptions.command, | ||
89 | ); | ||
90 | disposeOnDeactivation(watchStatus); | ||
91 | |||
91 | // Notifications are events triggered by the language server | 92 | // Notifications are events triggered by the language server |
92 | const allNotifications: Iterable<[ | 93 | const allNotifications: Iterable<[ |
93 | string, | 94 | string, |
@@ -97,6 +98,10 @@ export async function activate(context: vscode.ExtensionContext) { | |||
97 | 'rust-analyzer/publishDecorations', | 98 | 'rust-analyzer/publishDecorations', |
98 | notifications.publishDecorations.handle, | 99 | notifications.publishDecorations.handle, |
99 | ], | 100 | ], |
101 | [ | ||
102 | '$/progress', | ||
103 | params => watchStatus.handleProgressNotification(params), | ||
104 | ], | ||
100 | ]; | 105 | ]; |
101 | const syntaxTreeContentProvider = new SyntaxTreeContentProvider(); | 106 | const syntaxTreeContentProvider = new SyntaxTreeContentProvider(); |
102 | const expandMacroContentProvider = new ExpandMacroContentProvider(); | 107 | const expandMacroContentProvider = new ExpandMacroContentProvider(); |
@@ -139,26 +144,6 @@ export async function activate(context: vscode.ExtensionContext) { | |||
139 | 144 | ||
140 | vscode.commands.registerCommand('rust-analyzer.reload', reloadCommand); | 145 | vscode.commands.registerCommand('rust-analyzer.reload', reloadCommand); |
141 | 146 | ||
142 | // Executing `cargo watch` provides us with inline diagnostics on save | ||
143 | let provider: CargoWatchProvider | undefined; | ||
144 | interactivelyStartCargoWatch(context).then(p => { | ||
145 | provider = p; | ||
146 | }); | ||
147 | registerCommand('rust-analyzer.startCargoWatch', () => { | ||
148 | if (provider) { | ||
149 | provider.start(); | ||
150 | } else { | ||
151 | startCargoWatch(context).then(p => { | ||
152 | provider = p; | ||
153 | }); | ||
154 | } | ||
155 | }); | ||
156 | registerCommand('rust-analyzer.stopCargoWatch', () => { | ||
157 | if (provider) { | ||
158 | provider.stop(); | ||
159 | } | ||
160 | }); | ||
161 | |||
162 | // Start the language server, finally! | 147 | // Start the language server, finally! |
163 | try { | 148 | try { |
164 | await startServer(); | 149 | await startServer(); |
diff --git a/editors/code/src/server.ts b/editors/code/src/server.ts index 5ace1d0fa..ae81af848 100644 --- a/editors/code/src/server.ts +++ b/editors/code/src/server.ts | |||
@@ -55,6 +55,11 @@ export class Server { | |||
55 | publishDecorations: true, | 55 | publishDecorations: true, |
56 | lruCapacity: Server.config.lruCapacity, | 56 | lruCapacity: Server.config.lruCapacity, |
57 | maxInlayHintLength: Server.config.maxInlayHintLength, | 57 | maxInlayHintLength: Server.config.maxInlayHintLength, |
58 | cargoWatchEnable: Server.config.cargoWatchOptions.enable, | ||
59 | cargoWatchArgumets: Server.config.cargoWatchOptions.arguments, | ||
60 | cargoWatchCommand: Server.config.cargoWatchOptions.command, | ||
61 | cargoWatchAllTargets: | ||
62 | Server.config.cargoWatchOptions.allTargets, | ||
58 | excludeGlobs: Server.config.excludeGlobs, | 63 | excludeGlobs: Server.config.excludeGlobs, |
59 | useClientWatching: Server.config.useClientWatching, | 64 | useClientWatching: Server.config.useClientWatching, |
60 | featureFlags: Server.config.featureFlags, | 65 | featureFlags: Server.config.featureFlags, |
diff --git a/editors/code/src/test/fixtures/rust-diagnostics/clippy/trivially_copy_pass_by_ref.json b/editors/code/src/test/fixtures/rust-diagnostics/clippy/trivially_copy_pass_by_ref.json deleted file mode 100644 index d874e99bc..000000000 --- a/editors/code/src/test/fixtures/rust-diagnostics/clippy/trivially_copy_pass_by_ref.json +++ /dev/null | |||
@@ -1,110 +0,0 @@ | |||
1 | { | ||
2 | "message": "this argument is passed by reference, but would be more efficient if passed by value", | ||
3 | "code": { | ||
4 | "code": "clippy::trivially_copy_pass_by_ref", | ||
5 | "explanation": null | ||
6 | }, | ||
7 | "level": "warning", | ||
8 | "spans": [ | ||
9 | { | ||
10 | "file_name": "compiler/mir/tagset.rs", | ||
11 | "byte_start": 941, | ||
12 | "byte_end": 946, | ||
13 | "line_start": 42, | ||
14 | "line_end": 42, | ||
15 | "column_start": 24, | ||
16 | "column_end": 29, | ||
17 | "is_primary": true, | ||
18 | "text": [ | ||
19 | { | ||
20 | "text": " pub fn is_disjoint(&self, other: Self) -> bool {", | ||
21 | "highlight_start": 24, | ||
22 | "highlight_end": 29 | ||
23 | } | ||
24 | ], | ||
25 | "label": null, | ||
26 | "suggested_replacement": null, | ||
27 | "suggestion_applicability": null, | ||
28 | "expansion": null | ||
29 | } | ||
30 | ], | ||
31 | "children": [ | ||
32 | { | ||
33 | "message": "lint level defined here", | ||
34 | "code": null, | ||
35 | "level": "note", | ||
36 | "spans": [ | ||
37 | { | ||
38 | "file_name": "compiler/lib.rs", | ||
39 | "byte_start": 8, | ||
40 | "byte_end": 19, | ||
41 | "line_start": 1, | ||
42 | "line_end": 1, | ||
43 | "column_start": 9, | ||
44 | "column_end": 20, | ||
45 | "is_primary": true, | ||
46 | "text": [ | ||
47 | { | ||
48 | "text": "#![warn(clippy::all)]", | ||
49 | "highlight_start": 9, | ||
50 | "highlight_end": 20 | ||
51 | } | ||
52 | ], | ||
53 | "label": null, | ||
54 | "suggested_replacement": null, | ||
55 | "suggestion_applicability": null, | ||
56 | "expansion": null | ||
57 | } | ||
58 | ], | ||
59 | "children": [], | ||
60 | "rendered": null | ||
61 | }, | ||
62 | { | ||
63 | "message": "#[warn(clippy::trivially_copy_pass_by_ref)] implied by #[warn(clippy::all)]", | ||
64 | "code": null, | ||
65 | "level": "note", | ||
66 | "spans": [], | ||
67 | "children": [], | ||
68 | "rendered": null | ||
69 | }, | ||
70 | { | ||
71 | "message": "for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref", | ||
72 | "code": null, | ||
73 | "level": "help", | ||
74 | "spans": [], | ||
75 | "children": [], | ||
76 | "rendered": null | ||
77 | }, | ||
78 | { | ||
79 | "message": "consider passing by value instead", | ||
80 | "code": null, | ||
81 | "level": "help", | ||
82 | "spans": [ | ||
83 | { | ||
84 | "file_name": "compiler/mir/tagset.rs", | ||
85 | "byte_start": 941, | ||
86 | "byte_end": 946, | ||
87 | "line_start": 42, | ||
88 | "line_end": 42, | ||
89 | "column_start": 24, | ||
90 | "column_end": 29, | ||
91 | "is_primary": true, | ||
92 | "text": [ | ||
93 | { | ||
94 | "text": " pub fn is_disjoint(&self, other: Self) -> bool {", | ||
95 | "highlight_start": 24, | ||
96 | "highlight_end": 29 | ||
97 | } | ||
98 | ], | ||
99 | "label": null, | ||
100 | "suggested_replacement": "self", | ||
101 | "suggestion_applicability": "Unspecified", | ||
102 | "expansion": null | ||
103 | } | ||
104 | ], | ||
105 | "children": [], | ||
106 | "rendered": null | ||
107 | } | ||
108 | ], | ||
109 | "rendered": "warning: this argument is passed by reference, but would be more efficient if passed by value\n --> compiler/mir/tagset.rs:42:24\n |\n42 | pub fn is_disjoint(&self, other: Self) -> bool {\n | ^^^^^ help: consider passing by value instead: `self`\n |\nnote: lint level defined here\n --> compiler/lib.rs:1:9\n |\n1 | #![warn(clippy::all)]\n | ^^^^^^^^^^^\n = note: #[warn(clippy::trivially_copy_pass_by_ref)] implied by #[warn(clippy::all)]\n = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref\n\n" | ||
110 | } | ||
diff --git a/editors/code/src/test/fixtures/rust-diagnostics/error/E0053.json b/editors/code/src/test/fixtures/rust-diagnostics/error/E0053.json deleted file mode 100644 index ea5c976d1..000000000 --- a/editors/code/src/test/fixtures/rust-diagnostics/error/E0053.json +++ /dev/null | |||
@@ -1,42 +0,0 @@ | |||
1 | { | ||
2 | "message": "method `next` has an incompatible type for trait", | ||
3 | "code": { | ||
4 | "code": "E0053", | ||
5 | "explanation": "\nThe parameters of any trait method must match between a trait implementation\nand the trait definition.\n\nHere are a couple examples of this error:\n\n```compile_fail,E0053\ntrait Foo {\n fn foo(x: u16);\n fn bar(&self);\n}\n\nstruct Bar;\n\nimpl Foo for Bar {\n // error, expected u16, found i16\n fn foo(x: i16) { }\n\n // error, types differ in mutability\n fn bar(&mut self) { }\n}\n```\n" | ||
6 | }, | ||
7 | "level": "error", | ||
8 | "spans": [ | ||
9 | { | ||
10 | "file_name": "compiler/ty/list_iter.rs", | ||
11 | "byte_start": 1307, | ||
12 | "byte_end": 1350, | ||
13 | "line_start": 52, | ||
14 | "line_end": 52, | ||
15 | "column_start": 5, | ||
16 | "column_end": 48, | ||
17 | "is_primary": true, | ||
18 | "text": [ | ||
19 | { | ||
20 | "text": " fn next(&self) -> Option<&'list ty::Ref<M>> {", | ||
21 | "highlight_start": 5, | ||
22 | "highlight_end": 48 | ||
23 | } | ||
24 | ], | ||
25 | "label": "types differ in mutability", | ||
26 | "suggested_replacement": null, | ||
27 | "suggestion_applicability": null, | ||
28 | "expansion": null | ||
29 | } | ||
30 | ], | ||
31 | "children": [ | ||
32 | { | ||
33 | "message": "expected type `fn(&mut ty::list_iter::ListIterator<'list, M>) -> std::option::Option<&ty::Ref<M>>`\n found type `fn(&ty::list_iter::ListIterator<'list, M>) -> std::option::Option<&'list ty::Ref<M>>`", | ||
34 | "code": null, | ||
35 | "level": "note", | ||
36 | "spans": [], | ||
37 | "children": [], | ||
38 | "rendered": null | ||
39 | } | ||
40 | ], | ||
41 | "rendered": "error[E0053]: method `next` has an incompatible type for trait\n --> compiler/ty/list_iter.rs:52:5\n |\n52 | fn next(&self) -> Option<&'list ty::Ref<M>> {\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ types differ in mutability\n |\n = note: expected type `fn(&mut ty::list_iter::ListIterator<'list, M>) -> std::option::Option<&ty::Ref<M>>`\n found type `fn(&ty::list_iter::ListIterator<'list, M>) -> std::option::Option<&'list ty::Ref<M>>`\n\n" | ||
42 | } | ||
diff --git a/editors/code/src/test/fixtures/rust-diagnostics/error/E0061.json b/editors/code/src/test/fixtures/rust-diagnostics/error/E0061.json deleted file mode 100644 index 3154d1098..000000000 --- a/editors/code/src/test/fixtures/rust-diagnostics/error/E0061.json +++ /dev/null | |||
@@ -1,114 +0,0 @@ | |||
1 | { | ||
2 | "message": "this function takes 2 parameters but 3 parameters were supplied", | ||
3 | "code": { | ||
4 | "code": "E0061", | ||
5 | "explanation": "\nThe number of arguments passed to a function must match the number of arguments\nspecified in the function signature.\n\nFor example, a function like:\n\n```\nfn f(a: u16, b: &str) {}\n```\n\nMust always be called with exactly two arguments, e.g., `f(2, \"test\")`.\n\nNote that Rust does not have a notion of optional function arguments or\nvariadic functions (except for its C-FFI).\n" | ||
6 | }, | ||
7 | "level": "error", | ||
8 | "spans": [ | ||
9 | { | ||
10 | "file_name": "compiler/ty/select.rs", | ||
11 | "byte_start": 8787, | ||
12 | "byte_end": 9241, | ||
13 | "line_start": 219, | ||
14 | "line_end": 231, | ||
15 | "column_start": 5, | ||
16 | "column_end": 6, | ||
17 | "is_primary": false, | ||
18 | "text": [ | ||
19 | { | ||
20 | "text": " pub fn add_evidence(", | ||
21 | "highlight_start": 5, | ||
22 | "highlight_end": 25 | ||
23 | }, | ||
24 | { | ||
25 | "text": " &mut self,", | ||
26 | "highlight_start": 1, | ||
27 | "highlight_end": 19 | ||
28 | }, | ||
29 | { | ||
30 | "text": " target_poly: &ty::Ref<ty::Poly>,", | ||
31 | "highlight_start": 1, | ||
32 | "highlight_end": 41 | ||
33 | }, | ||
34 | { | ||
35 | "text": " evidence_poly: &ty::Ref<ty::Poly>,", | ||
36 | "highlight_start": 1, | ||
37 | "highlight_end": 43 | ||
38 | }, | ||
39 | { | ||
40 | "text": " ) {", | ||
41 | "highlight_start": 1, | ||
42 | "highlight_end": 8 | ||
43 | }, | ||
44 | { | ||
45 | "text": " match target_poly {", | ||
46 | "highlight_start": 1, | ||
47 | "highlight_end": 28 | ||
48 | }, | ||
49 | { | ||
50 | "text": " ty::Ref::Var(tvar, _) => self.add_var_evidence(tvar, evidence_poly),", | ||
51 | "highlight_start": 1, | ||
52 | "highlight_end": 81 | ||
53 | }, | ||
54 | { | ||
55 | "text": " ty::Ref::Fixed(target_ty) => {", | ||
56 | "highlight_start": 1, | ||
57 | "highlight_end": 43 | ||
58 | }, | ||
59 | { | ||
60 | "text": " let evidence_ty = evidence_poly.resolve_to_ty();", | ||
61 | "highlight_start": 1, | ||
62 | "highlight_end": 65 | ||
63 | }, | ||
64 | { | ||
65 | "text": " self.add_evidence_ty(target_ty, evidence_poly, evidence_ty)", | ||
66 | "highlight_start": 1, | ||
67 | "highlight_end": 76 | ||
68 | }, | ||
69 | { | ||
70 | "text": " }", | ||
71 | "highlight_start": 1, | ||
72 | "highlight_end": 14 | ||
73 | }, | ||
74 | { | ||
75 | "text": " }", | ||
76 | "highlight_start": 1, | ||
77 | "highlight_end": 10 | ||
78 | }, | ||
79 | { | ||
80 | "text": " }", | ||
81 | "highlight_start": 1, | ||
82 | "highlight_end": 6 | ||
83 | } | ||
84 | ], | ||
85 | "label": "defined here", | ||
86 | "suggested_replacement": null, | ||
87 | "suggestion_applicability": null, | ||
88 | "expansion": null | ||
89 | }, | ||
90 | { | ||
91 | "file_name": "compiler/ty/select.rs", | ||
92 | "byte_start": 4045, | ||
93 | "byte_end": 4057, | ||
94 | "line_start": 104, | ||
95 | "line_end": 104, | ||
96 | "column_start": 18, | ||
97 | "column_end": 30, | ||
98 | "is_primary": true, | ||
99 | "text": [ | ||
100 | { | ||
101 | "text": " self.add_evidence(target_fixed, evidence_fixed, false);", | ||
102 | "highlight_start": 18, | ||
103 | "highlight_end": 30 | ||
104 | } | ||
105 | ], | ||
106 | "label": "expected 2 parameters", | ||
107 | "suggested_replacement": null, | ||
108 | "suggestion_applicability": null, | ||
109 | "expansion": null | ||
110 | } | ||
111 | ], | ||
112 | "children": [], | ||
113 | "rendered": "error[E0061]: this function takes 2 parameters but 3 parameters were supplied\n --> compiler/ty/select.rs:104:18\n |\n104 | self.add_evidence(target_fixed, evidence_fixed, false);\n | ^^^^^^^^^^^^ expected 2 parameters\n...\n219 | / pub fn add_evidence(\n220 | | &mut self,\n221 | | target_poly: &ty::Ref<ty::Poly>,\n222 | | evidence_poly: &ty::Ref<ty::Poly>,\n... |\n230 | | }\n231 | | }\n | |_____- defined here\n\n" | ||
114 | } | ||
diff --git a/editors/code/src/test/fixtures/rust-diagnostics/error/E0277.json b/editors/code/src/test/fixtures/rust-diagnostics/error/E0277.json deleted file mode 100644 index bfef33c7d..000000000 --- a/editors/code/src/test/fixtures/rust-diagnostics/error/E0277.json +++ /dev/null | |||
@@ -1,261 +0,0 @@ | |||
1 | { | ||
2 | "rendered": "error[E0277]: can't compare `{integer}` with `&str`\n --> src/main.rs:2:5\n |\n2 | assert_eq!(1, \"love\");\n | ^^^^^^^^^^^^^^^^^^^^^^ no implementation for `{integer} == &str`\n |\n = help: the trait `std::cmp::PartialEq<&str>` is not implemented for `{integer}`\n = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)\n\n", | ||
3 | "children": [ | ||
4 | { | ||
5 | "children": [], | ||
6 | "code": null, | ||
7 | "level": "help", | ||
8 | "message": "the trait `std::cmp::PartialEq<&str>` is not implemented for `{integer}`", | ||
9 | "rendered": null, | ||
10 | "spans": [] | ||
11 | } | ||
12 | ], | ||
13 | "code": { | ||
14 | "code": "E0277", | ||
15 | "explanation": "\nYou tried to use a type which doesn't implement some trait in a place which\nexpected that trait. Erroneous code example:\n\n```compile_fail,E0277\n// here we declare the Foo trait with a bar method\ntrait Foo {\n fn bar(&self);\n}\n\n// we now declare a function which takes an object implementing the Foo trait\nfn some_func<T: Foo>(foo: T) {\n foo.bar();\n}\n\nfn main() {\n // we now call the method with the i32 type, which doesn't implement\n // the Foo trait\n some_func(5i32); // error: the trait bound `i32 : Foo` is not satisfied\n}\n```\n\nIn order to fix this error, verify that the type you're using does implement\nthe trait. Example:\n\n```\ntrait Foo {\n fn bar(&self);\n}\n\nfn some_func<T: Foo>(foo: T) {\n foo.bar(); // we can now use this method since i32 implements the\n // Foo trait\n}\n\n// we implement the trait on the i32 type\nimpl Foo for i32 {\n fn bar(&self) {}\n}\n\nfn main() {\n some_func(5i32); // ok!\n}\n```\n\nOr in a generic context, an erroneous code example would look like:\n\n```compile_fail,E0277\nfn some_func<T>(foo: T) {\n println!(\"{:?}\", foo); // error: the trait `core::fmt::Debug` is not\n // implemented for the type `T`\n}\n\nfn main() {\n // We now call the method with the i32 type,\n // which *does* implement the Debug trait.\n some_func(5i32);\n}\n```\n\nNote that the error here is in the definition of the generic function: Although\nwe only call it with a parameter that does implement `Debug`, the compiler\nstill rejects the function: It must work with all possible input types. In\norder to make this example compile, we need to restrict the generic type we're\naccepting:\n\n```\nuse std::fmt;\n\n// Restrict the input type to types that implement Debug.\nfn some_func<T: fmt::Debug>(foo: T) {\n println!(\"{:?}\", foo);\n}\n\nfn main() {\n // Calling the method is still fine, as i32 implements Debug.\n some_func(5i32);\n\n // This would fail to compile now:\n // struct WithoutDebug;\n // some_func(WithoutDebug);\n}\n```\n\nRust only looks at the signature of the called function, as such it must\nalready specify all requirements that will be used for every type parameter.\n" | ||
16 | }, | ||
17 | "level": "error", | ||
18 | "message": "can't compare `{integer}` with `&str`", | ||
19 | "spans": [ | ||
20 | { | ||
21 | "byte_end": 155, | ||
22 | "byte_start": 153, | ||
23 | "column_end": 33, | ||
24 | "column_start": 31, | ||
25 | "expansion": { | ||
26 | "def_site_span": { | ||
27 | "byte_end": 940, | ||
28 | "byte_start": 0, | ||
29 | "column_end": 6, | ||
30 | "column_start": 1, | ||
31 | "expansion": null, | ||
32 | "file_name": "<::core::macros::assert_eq macros>", | ||
33 | "is_primary": false, | ||
34 | "label": null, | ||
35 | "line_end": 36, | ||
36 | "line_start": 1, | ||
37 | "suggested_replacement": null, | ||
38 | "suggestion_applicability": null, | ||
39 | "text": [ | ||
40 | { | ||
41 | "highlight_end": 35, | ||
42 | "highlight_start": 1, | ||
43 | "text": "($ left : expr, $ right : expr) =>" | ||
44 | }, | ||
45 | { | ||
46 | "highlight_end": 3, | ||
47 | "highlight_start": 1, | ||
48 | "text": "({" | ||
49 | }, | ||
50 | { | ||
51 | "highlight_end": 33, | ||
52 | "highlight_start": 1, | ||
53 | "text": " match (& $ left, & $ right)" | ||
54 | }, | ||
55 | { | ||
56 | "highlight_end": 7, | ||
57 | "highlight_start": 1, | ||
58 | "text": " {" | ||
59 | }, | ||
60 | { | ||
61 | "highlight_end": 34, | ||
62 | "highlight_start": 1, | ||
63 | "text": " (left_val, right_val) =>" | ||
64 | }, | ||
65 | { | ||
66 | "highlight_end": 11, | ||
67 | "highlight_start": 1, | ||
68 | "text": " {" | ||
69 | }, | ||
70 | { | ||
71 | "highlight_end": 46, | ||
72 | "highlight_start": 1, | ||
73 | "text": " if ! (* left_val == * right_val)" | ||
74 | }, | ||
75 | { | ||
76 | "highlight_end": 15, | ||
77 | "highlight_start": 1, | ||
78 | "text": " {" | ||
79 | }, | ||
80 | { | ||
81 | "highlight_end": 25, | ||
82 | "highlight_start": 1, | ||
83 | "text": " panic !" | ||
84 | }, | ||
85 | { | ||
86 | "highlight_end": 57, | ||
87 | "highlight_start": 1, | ||
88 | "text": " (r#\"assertion failed: `(left == right)`" | ||
89 | }, | ||
90 | { | ||
91 | "highlight_end": 16, | ||
92 | "highlight_start": 1, | ||
93 | "text": " left: `{:?}`," | ||
94 | }, | ||
95 | { | ||
96 | "highlight_end": 18, | ||
97 | "highlight_start": 1, | ||
98 | "text": " right: `{:?}`\"#," | ||
99 | }, | ||
100 | { | ||
101 | "highlight_end": 47, | ||
102 | "highlight_start": 1, | ||
103 | "text": " & * left_val, & * right_val)" | ||
104 | }, | ||
105 | { | ||
106 | "highlight_end": 15, | ||
107 | "highlight_start": 1, | ||
108 | "text": " }" | ||
109 | }, | ||
110 | { | ||
111 | "highlight_end": 11, | ||
112 | "highlight_start": 1, | ||
113 | "text": " }" | ||
114 | }, | ||
115 | { | ||
116 | "highlight_end": 7, | ||
117 | "highlight_start": 1, | ||
118 | "text": " }" | ||
119 | }, | ||
120 | { | ||
121 | "highlight_end": 42, | ||
122 | "highlight_start": 1, | ||
123 | "text": " }) ; ($ left : expr, $ right : expr,) =>" | ||
124 | }, | ||
125 | { | ||
126 | "highlight_end": 49, | ||
127 | "highlight_start": 1, | ||
128 | "text": "({ $ crate :: assert_eq ! ($ left, $ right) }) ;" | ||
129 | }, | ||
130 | { | ||
131 | "highlight_end": 53, | ||
132 | "highlight_start": 1, | ||
133 | "text": "($ left : expr, $ right : expr, $ ($ arg : tt) +) =>" | ||
134 | }, | ||
135 | { | ||
136 | "highlight_end": 3, | ||
137 | "highlight_start": 1, | ||
138 | "text": "({" | ||
139 | }, | ||
140 | { | ||
141 | "highlight_end": 37, | ||
142 | "highlight_start": 1, | ||
143 | "text": " match (& ($ left), & ($ right))" | ||
144 | }, | ||
145 | { | ||
146 | "highlight_end": 7, | ||
147 | "highlight_start": 1, | ||
148 | "text": " {" | ||
149 | }, | ||
150 | { | ||
151 | "highlight_end": 34, | ||
152 | "highlight_start": 1, | ||
153 | "text": " (left_val, right_val) =>" | ||
154 | }, | ||
155 | { | ||
156 | "highlight_end": 11, | ||
157 | "highlight_start": 1, | ||
158 | "text": " {" | ||
159 | }, | ||
160 | { | ||
161 | "highlight_end": 46, | ||
162 | "highlight_start": 1, | ||
163 | "text": " if ! (* left_val == * right_val)" | ||
164 | }, | ||
165 | { | ||
166 | "highlight_end": 15, | ||
167 | "highlight_start": 1, | ||
168 | "text": " {" | ||
169 | }, | ||
170 | { | ||
171 | "highlight_end": 25, | ||
172 | "highlight_start": 1, | ||
173 | "text": " panic !" | ||
174 | }, | ||
175 | { | ||
176 | "highlight_end": 57, | ||
177 | "highlight_start": 1, | ||
178 | "text": " (r#\"assertion failed: `(left == right)`" | ||
179 | }, | ||
180 | { | ||
181 | "highlight_end": 16, | ||
182 | "highlight_start": 1, | ||
183 | "text": " left: `{:?}`," | ||
184 | }, | ||
185 | { | ||
186 | "highlight_end": 22, | ||
187 | "highlight_start": 1, | ||
188 | "text": " right: `{:?}`: {}\"#," | ||
189 | }, | ||
190 | { | ||
191 | "highlight_end": 72, | ||
192 | "highlight_start": 1, | ||
193 | "text": " & * left_val, & * right_val, $ crate :: format_args !" | ||
194 | }, | ||
195 | { | ||
196 | "highlight_end": 33, | ||
197 | "highlight_start": 1, | ||
198 | "text": " ($ ($ arg) +))" | ||
199 | }, | ||
200 | { | ||
201 | "highlight_end": 15, | ||
202 | "highlight_start": 1, | ||
203 | "text": " }" | ||
204 | }, | ||
205 | { | ||
206 | "highlight_end": 11, | ||
207 | "highlight_start": 1, | ||
208 | "text": " }" | ||
209 | }, | ||
210 | { | ||
211 | "highlight_end": 7, | ||
212 | "highlight_start": 1, | ||
213 | "text": " }" | ||
214 | }, | ||
215 | { | ||
216 | "highlight_end": 6, | ||
217 | "highlight_start": 1, | ||
218 | "text": " }) ;" | ||
219 | } | ||
220 | ] | ||
221 | }, | ||
222 | "macro_decl_name": "assert_eq!", | ||
223 | "span": { | ||
224 | "byte_end": 38, | ||
225 | "byte_start": 16, | ||
226 | "column_end": 27, | ||
227 | "column_start": 5, | ||
228 | "expansion": null, | ||
229 | "file_name": "src/main.rs", | ||
230 | "is_primary": false, | ||
231 | "label": null, | ||
232 | "line_end": 2, | ||
233 | "line_start": 2, | ||
234 | "suggested_replacement": null, | ||
235 | "suggestion_applicability": null, | ||
236 | "text": [ | ||
237 | { | ||
238 | "highlight_end": 27, | ||
239 | "highlight_start": 5, | ||
240 | "text": " assert_eq!(1, \"love\");" | ||
241 | } | ||
242 | ] | ||
243 | } | ||
244 | }, | ||
245 | "file_name": "<::core::macros::assert_eq macros>", | ||
246 | "is_primary": true, | ||
247 | "label": "no implementation for `{integer} == &str`", | ||
248 | "line_end": 7, | ||
249 | "line_start": 7, | ||
250 | "suggested_replacement": null, | ||
251 | "suggestion_applicability": null, | ||
252 | "text": [ | ||
253 | { | ||
254 | "highlight_end": 33, | ||
255 | "highlight_start": 31, | ||
256 | "text": " if ! (* left_val == * right_val)" | ||
257 | } | ||
258 | ] | ||
259 | } | ||
260 | ] | ||
261 | } | ||
diff --git a/editors/code/src/test/fixtures/rust-diagnostics/error/E0308.json b/editors/code/src/test/fixtures/rust-diagnostics/error/E0308.json deleted file mode 100644 index fb23824a3..000000000 --- a/editors/code/src/test/fixtures/rust-diagnostics/error/E0308.json +++ /dev/null | |||
@@ -1,33 +0,0 @@ | |||
1 | { | ||
2 | "message": "mismatched types", | ||
3 | "code": { | ||
4 | "code": "E0308", | ||
5 | "explanation": "\nThis error occurs when the compiler was unable to infer the concrete type of a\nvariable. It can occur for several cases, the most common of which is a\nmismatch in the expected type that the compiler inferred for a variable's\ninitializing expression, and the actual type explicitly assigned to the\nvariable.\n\nFor example:\n\n```compile_fail,E0308\nlet x: i32 = \"I am not a number!\";\n// ~~~ ~~~~~~~~~~~~~~~~~~~~\n// | |\n// | initializing expression;\n// | compiler infers type `&str`\n// |\n// type `i32` assigned to variable `x`\n```\n" | ||
6 | }, | ||
7 | "level": "error", | ||
8 | "spans": [ | ||
9 | { | ||
10 | "file_name": "runtime/compiler_support.rs", | ||
11 | "byte_start": 1589, | ||
12 | "byte_end": 1594, | ||
13 | "line_start": 48, | ||
14 | "line_end": 48, | ||
15 | "column_start": 65, | ||
16 | "column_end": 70, | ||
17 | "is_primary": true, | ||
18 | "text": [ | ||
19 | { | ||
20 | "text": " let layout = alloc::Layout::from_size_align_unchecked(size, align);", | ||
21 | "highlight_start": 65, | ||
22 | "highlight_end": 70 | ||
23 | } | ||
24 | ], | ||
25 | "label": "expected usize, found u32", | ||
26 | "suggested_replacement": null, | ||
27 | "suggestion_applicability": null, | ||
28 | "expansion": null | ||
29 | } | ||
30 | ], | ||
31 | "children": [], | ||
32 | "rendered": "error[E0308]: mismatched types\n --> runtime/compiler_support.rs:48:65\n |\n48 | let layout = alloc::Layout::from_size_align_unchecked(size, align);\n | ^^^^^ expected usize, found u32\n\n" | ||
33 | } | ||
diff --git a/editors/code/src/test/fixtures/rust-diagnostics/warning/unused_variables.json b/editors/code/src/test/fixtures/rust-diagnostics/warning/unused_variables.json deleted file mode 100644 index d1e2be722..000000000 --- a/editors/code/src/test/fixtures/rust-diagnostics/warning/unused_variables.json +++ /dev/null | |||
@@ -1,72 +0,0 @@ | |||
1 | { | ||
2 | "message": "unused variable: `foo`", | ||
3 | "code": { | ||
4 | "code": "unused_variables", | ||
5 | "explanation": null | ||
6 | }, | ||
7 | "level": "warning", | ||
8 | "spans": [ | ||
9 | { | ||
10 | "file_name": "driver/subcommand/repl.rs", | ||
11 | "byte_start": 9228, | ||
12 | "byte_end": 9231, | ||
13 | "line_start": 291, | ||
14 | "line_end": 291, | ||
15 | "column_start": 9, | ||
16 | "column_end": 12, | ||
17 | "is_primary": true, | ||
18 | "text": [ | ||
19 | { | ||
20 | "text": " let foo = 42;", | ||
21 | "highlight_start": 9, | ||
22 | "highlight_end": 12 | ||
23 | } | ||
24 | ], | ||
25 | "label": null, | ||
26 | "suggested_replacement": null, | ||
27 | "suggestion_applicability": null, | ||
28 | "expansion": null | ||
29 | } | ||
30 | ], | ||
31 | "children": [ | ||
32 | { | ||
33 | "message": "#[warn(unused_variables)] on by default", | ||
34 | "code": null, | ||
35 | "level": "note", | ||
36 | "spans": [], | ||
37 | "children": [], | ||
38 | "rendered": null | ||
39 | }, | ||
40 | { | ||
41 | "message": "consider prefixing with an underscore", | ||
42 | "code": null, | ||
43 | "level": "help", | ||
44 | "spans": [ | ||
45 | { | ||
46 | "file_name": "driver/subcommand/repl.rs", | ||
47 | "byte_start": 9228, | ||
48 | "byte_end": 9231, | ||
49 | "line_start": 291, | ||
50 | "line_end": 291, | ||
51 | "column_start": 9, | ||
52 | "column_end": 12, | ||
53 | "is_primary": true, | ||
54 | "text": [ | ||
55 | { | ||
56 | "text": " let foo = 42;", | ||
57 | "highlight_start": 9, | ||
58 | "highlight_end": 12 | ||
59 | } | ||
60 | ], | ||
61 | "label": null, | ||
62 | "suggested_replacement": "_foo", | ||
63 | "suggestion_applicability": "MachineApplicable", | ||
64 | "expansion": null | ||
65 | } | ||
66 | ], | ||
67 | "children": [], | ||
68 | "rendered": null | ||
69 | } | ||
70 | ], | ||
71 | "rendered": "warning: unused variable: `foo`\n --> driver/subcommand/repl.rs:291:9\n |\n291 | let foo = 42;\n | ^^^ help: consider prefixing with an underscore: `_foo`\n |\n = note: #[warn(unused_variables)] on by default\n\n" | ||
72 | } | ||
diff --git a/editors/code/src/test/utils/diagnotics/SuggestedFix.test.ts b/editors/code/src/test/utils/diagnotics/SuggestedFix.test.ts deleted file mode 100644 index 2b25eb705..000000000 --- a/editors/code/src/test/utils/diagnotics/SuggestedFix.test.ts +++ /dev/null | |||
@@ -1,134 +0,0 @@ | |||
1 | import * as assert from 'assert'; | ||
2 | import * as vscode from 'vscode'; | ||
3 | |||
4 | import { SuggestionApplicability } from '../../../utils/diagnostics/rust'; | ||
5 | import SuggestedFix from '../../../utils/diagnostics/SuggestedFix'; | ||
6 | |||
7 | const location1 = new vscode.Location( | ||
8 | vscode.Uri.file('/file/1'), | ||
9 | new vscode.Range(new vscode.Position(1, 2), new vscode.Position(3, 4)), | ||
10 | ); | ||
11 | |||
12 | const location2 = new vscode.Location( | ||
13 | vscode.Uri.file('/file/2'), | ||
14 | new vscode.Range(new vscode.Position(5, 6), new vscode.Position(7, 8)), | ||
15 | ); | ||
16 | |||
17 | describe('SuggestedFix', () => { | ||
18 | describe('isEqual', () => { | ||
19 | it('should treat identical instances as equal', () => { | ||
20 | const suggestion1 = new SuggestedFix( | ||
21 | 'Replace me!', | ||
22 | location1, | ||
23 | 'With this!', | ||
24 | ); | ||
25 | |||
26 | const suggestion2 = new SuggestedFix( | ||
27 | 'Replace me!', | ||
28 | location1, | ||
29 | 'With this!', | ||
30 | ); | ||
31 | |||
32 | assert(suggestion1.isEqual(suggestion2)); | ||
33 | }); | ||
34 | |||
35 | it('should treat instances with different titles as inequal', () => { | ||
36 | const suggestion1 = new SuggestedFix( | ||
37 | 'Replace me!', | ||
38 | location1, | ||
39 | 'With this!', | ||
40 | ); | ||
41 | |||
42 | const suggestion2 = new SuggestedFix( | ||
43 | 'Not the same title!', | ||
44 | location1, | ||
45 | 'With this!', | ||
46 | ); | ||
47 | |||
48 | assert(!suggestion1.isEqual(suggestion2)); | ||
49 | }); | ||
50 | |||
51 | it('should treat instances with different replacements as inequal', () => { | ||
52 | const suggestion1 = new SuggestedFix( | ||
53 | 'Replace me!', | ||
54 | location1, | ||
55 | 'With this!', | ||
56 | ); | ||
57 | |||
58 | const suggestion2 = new SuggestedFix( | ||
59 | 'Replace me!', | ||
60 | location1, | ||
61 | 'With something else!', | ||
62 | ); | ||
63 | |||
64 | assert(!suggestion1.isEqual(suggestion2)); | ||
65 | }); | ||
66 | |||
67 | it('should treat instances with different locations as inequal', () => { | ||
68 | const suggestion1 = new SuggestedFix( | ||
69 | 'Replace me!', | ||
70 | location1, | ||
71 | 'With this!', | ||
72 | ); | ||
73 | |||
74 | const suggestion2 = new SuggestedFix( | ||
75 | 'Replace me!', | ||
76 | location2, | ||
77 | 'With this!', | ||
78 | ); | ||
79 | |||
80 | assert(!suggestion1.isEqual(suggestion2)); | ||
81 | }); | ||
82 | |||
83 | it('should treat instances with different applicability as inequal', () => { | ||
84 | const suggestion1 = new SuggestedFix( | ||
85 | 'Replace me!', | ||
86 | location1, | ||
87 | 'With this!', | ||
88 | SuggestionApplicability.MachineApplicable, | ||
89 | ); | ||
90 | |||
91 | const suggestion2 = new SuggestedFix( | ||
92 | 'Replace me!', | ||
93 | location2, | ||
94 | 'With this!', | ||
95 | SuggestionApplicability.HasPlaceholders, | ||
96 | ); | ||
97 | |||
98 | assert(!suggestion1.isEqual(suggestion2)); | ||
99 | }); | ||
100 | }); | ||
101 | |||
102 | describe('toCodeAction', () => { | ||
103 | it('should map a simple suggestion', () => { | ||
104 | const suggestion = new SuggestedFix( | ||
105 | 'Replace me!', | ||
106 | location1, | ||
107 | 'With this!', | ||
108 | ); | ||
109 | |||
110 | const codeAction = suggestion.toCodeAction(); | ||
111 | assert.strictEqual(codeAction.kind, vscode.CodeActionKind.QuickFix); | ||
112 | assert.strictEqual(codeAction.title, 'Replace me!'); | ||
113 | assert.strictEqual(codeAction.isPreferred, false); | ||
114 | |||
115 | const edit = codeAction.edit; | ||
116 | if (!edit) { | ||
117 | assert.fail('Code Action edit unexpectedly missing'); | ||
118 | return; | ||
119 | } | ||
120 | |||
121 | const editEntries = edit.entries(); | ||
122 | assert.strictEqual(editEntries.length, 1); | ||
123 | |||
124 | const [[editUri, textEdits]] = editEntries; | ||
125 | assert.strictEqual(editUri.toString(), location1.uri.toString()); | ||
126 | |||
127 | assert.strictEqual(textEdits.length, 1); | ||
128 | const [textEdit] = textEdits; | ||
129 | |||
130 | assert(textEdit.range.isEqual(location1.range)); | ||
131 | assert.strictEqual(textEdit.newText, 'With this!'); | ||
132 | }); | ||
133 | }); | ||
134 | }); | ||
diff --git a/editors/code/src/test/utils/diagnotics/SuggestedFixCollection.test.ts b/editors/code/src/test/utils/diagnotics/SuggestedFixCollection.test.ts deleted file mode 100644 index ef09013f4..000000000 --- a/editors/code/src/test/utils/diagnotics/SuggestedFixCollection.test.ts +++ /dev/null | |||
@@ -1,127 +0,0 @@ | |||
1 | import * as assert from 'assert'; | ||
2 | import * as vscode from 'vscode'; | ||
3 | |||
4 | import SuggestedFix from '../../../utils/diagnostics/SuggestedFix'; | ||
5 | import SuggestedFixCollection from '../../../utils/diagnostics/SuggestedFixCollection'; | ||
6 | |||
7 | const uri1 = vscode.Uri.file('/file/1'); | ||
8 | const uri2 = vscode.Uri.file('/file/2'); | ||
9 | |||
10 | const mockDocument1 = ({ | ||
11 | uri: uri1, | ||
12 | } as unknown) as vscode.TextDocument; | ||
13 | |||
14 | const mockDocument2 = ({ | ||
15 | uri: uri2, | ||
16 | } as unknown) as vscode.TextDocument; | ||
17 | |||
18 | const range1 = new vscode.Range( | ||
19 | new vscode.Position(1, 2), | ||
20 | new vscode.Position(3, 4), | ||
21 | ); | ||
22 | const range2 = new vscode.Range( | ||
23 | new vscode.Position(5, 6), | ||
24 | new vscode.Position(7, 8), | ||
25 | ); | ||
26 | |||
27 | const diagnostic1 = new vscode.Diagnostic(range1, 'First diagnostic'); | ||
28 | const diagnostic2 = new vscode.Diagnostic(range2, 'Second diagnostic'); | ||
29 | |||
30 | // This is a mutable object so return a fresh instance every time | ||
31 | function suggestion1(): SuggestedFix { | ||
32 | return new SuggestedFix( | ||
33 | 'Replace me!', | ||
34 | new vscode.Location(uri1, range1), | ||
35 | 'With this!', | ||
36 | ); | ||
37 | } | ||
38 | |||
39 | describe('SuggestedFixCollection', () => { | ||
40 | it('should add a suggestion then return it as a code action', () => { | ||
41 | const suggestedFixes = new SuggestedFixCollection(); | ||
42 | suggestedFixes.addSuggestedFixForDiagnostic(suggestion1(), diagnostic1); | ||
43 | |||
44 | // Specify the document and range that exactly matches | ||
45 | const codeActions = suggestedFixes.provideCodeActions( | ||
46 | mockDocument1, | ||
47 | range1, | ||
48 | ); | ||
49 | |||
50 | assert.strictEqual(codeActions.length, 1); | ||
51 | const [codeAction] = codeActions; | ||
52 | assert.strictEqual(codeAction.title, suggestion1().title); | ||
53 | |||
54 | const { diagnostics } = codeAction; | ||
55 | if (!diagnostics) { | ||
56 | assert.fail('Diagnostics unexpectedly missing'); | ||
57 | return; | ||
58 | } | ||
59 | |||
60 | assert.strictEqual(diagnostics.length, 1); | ||
61 | assert.strictEqual(diagnostics[0], diagnostic1); | ||
62 | }); | ||
63 | |||
64 | it('should not return code actions for different ranges', () => { | ||
65 | const suggestedFixes = new SuggestedFixCollection(); | ||
66 | suggestedFixes.addSuggestedFixForDiagnostic(suggestion1(), diagnostic1); | ||
67 | |||
68 | const codeActions = suggestedFixes.provideCodeActions( | ||
69 | mockDocument1, | ||
70 | range2, | ||
71 | ); | ||
72 | |||
73 | assert(!codeActions || codeActions.length === 0); | ||
74 | }); | ||
75 | |||
76 | it('should not return code actions for different documents', () => { | ||
77 | const suggestedFixes = new SuggestedFixCollection(); | ||
78 | suggestedFixes.addSuggestedFixForDiagnostic(suggestion1(), diagnostic1); | ||
79 | |||
80 | const codeActions = suggestedFixes.provideCodeActions( | ||
81 | mockDocument2, | ||
82 | range1, | ||
83 | ); | ||
84 | |||
85 | assert(!codeActions || codeActions.length === 0); | ||
86 | }); | ||
87 | |||
88 | it('should not return code actions that have been cleared', () => { | ||
89 | const suggestedFixes = new SuggestedFixCollection(); | ||
90 | suggestedFixes.addSuggestedFixForDiagnostic(suggestion1(), diagnostic1); | ||
91 | suggestedFixes.clear(); | ||
92 | |||
93 | const codeActions = suggestedFixes.provideCodeActions( | ||
94 | mockDocument1, | ||
95 | range1, | ||
96 | ); | ||
97 | |||
98 | assert(!codeActions || codeActions.length === 0); | ||
99 | }); | ||
100 | |||
101 | it('should merge identical suggestions together', () => { | ||
102 | const suggestedFixes = new SuggestedFixCollection(); | ||
103 | |||
104 | // Add the same suggestion for two diagnostics | ||
105 | suggestedFixes.addSuggestedFixForDiagnostic(suggestion1(), diagnostic1); | ||
106 | suggestedFixes.addSuggestedFixForDiagnostic(suggestion1(), diagnostic2); | ||
107 | |||
108 | const codeActions = suggestedFixes.provideCodeActions( | ||
109 | mockDocument1, | ||
110 | range1, | ||
111 | ); | ||
112 | |||
113 | assert.strictEqual(codeActions.length, 1); | ||
114 | const [codeAction] = codeActions; | ||
115 | const { diagnostics } = codeAction; | ||
116 | |||
117 | if (!diagnostics) { | ||
118 | assert.fail('Diagnostics unexpectedly missing'); | ||
119 | return; | ||
120 | } | ||
121 | |||
122 | // We should be associated with both diagnostics | ||
123 | assert.strictEqual(diagnostics.length, 2); | ||
124 | assert.strictEqual(diagnostics[0], diagnostic1); | ||
125 | assert.strictEqual(diagnostics[1], diagnostic2); | ||
126 | }); | ||
127 | }); | ||
diff --git a/editors/code/src/test/utils/diagnotics/rust.test.ts b/editors/code/src/test/utils/diagnotics/rust.test.ts deleted file mode 100644 index 358325cc8..000000000 --- a/editors/code/src/test/utils/diagnotics/rust.test.ts +++ /dev/null | |||
@@ -1,236 +0,0 @@ | |||
1 | import * as assert from 'assert'; | ||
2 | import * as fs from 'fs'; | ||
3 | import * as vscode from 'vscode'; | ||
4 | |||
5 | import { | ||
6 | MappedRustDiagnostic, | ||
7 | mapRustDiagnosticToVsCode, | ||
8 | RustDiagnostic, | ||
9 | SuggestionApplicability, | ||
10 | } from '../../../utils/diagnostics/rust'; | ||
11 | |||
12 | function loadDiagnosticFixture(name: string): RustDiagnostic { | ||
13 | const jsonText = fs | ||
14 | .readFileSync( | ||
15 | // We're actually in our JavaScript output directory, climb out | ||
16 | `${__dirname}/../../../../src/test/fixtures/rust-diagnostics/${name}.json`, | ||
17 | ) | ||
18 | .toString(); | ||
19 | |||
20 | return JSON.parse(jsonText); | ||
21 | } | ||
22 | |||
23 | function mapFixtureToVsCode(name: string): MappedRustDiagnostic { | ||
24 | const rd = loadDiagnosticFixture(name); | ||
25 | const mapResult = mapRustDiagnosticToVsCode(rd); | ||
26 | |||
27 | if (!mapResult) { | ||
28 | return assert.fail('Mapping unexpectedly failed'); | ||
29 | } | ||
30 | return mapResult; | ||
31 | } | ||
32 | |||
33 | describe('mapRustDiagnosticToVsCode', () => { | ||
34 | it('should map an incompatible type for trait error', () => { | ||
35 | const { diagnostic, suggestedFixes } = mapFixtureToVsCode( | ||
36 | 'error/E0053', | ||
37 | ); | ||
38 | |||
39 | assert.strictEqual( | ||
40 | diagnostic.severity, | ||
41 | vscode.DiagnosticSeverity.Error, | ||
42 | ); | ||
43 | assert.strictEqual(diagnostic.source, 'rustc'); | ||
44 | assert.strictEqual( | ||
45 | diagnostic.message, | ||
46 | [ | ||
47 | `method \`next\` has an incompatible type for trait`, | ||
48 | `expected type \`fn(&mut ty::list_iter::ListIterator<'list, M>) -> std::option::Option<&ty::Ref<M>>\``, | ||
49 | ` found type \`fn(&ty::list_iter::ListIterator<'list, M>) -> std::option::Option<&'list ty::Ref<M>>\``, | ||
50 | ].join('\n'), | ||
51 | ); | ||
52 | assert.strictEqual(diagnostic.code, 'E0053'); | ||
53 | assert.deepStrictEqual(diagnostic.tags, []); | ||
54 | |||
55 | // No related information | ||
56 | assert.deepStrictEqual(diagnostic.relatedInformation, []); | ||
57 | |||
58 | // There are no suggested fixes | ||
59 | assert.strictEqual(suggestedFixes.length, 0); | ||
60 | }); | ||
61 | |||
62 | it('should map an unused variable warning', () => { | ||
63 | const { diagnostic, suggestedFixes } = mapFixtureToVsCode( | ||
64 | 'warning/unused_variables', | ||
65 | ); | ||
66 | |||
67 | assert.strictEqual( | ||
68 | diagnostic.severity, | ||
69 | vscode.DiagnosticSeverity.Warning, | ||
70 | ); | ||
71 | assert.strictEqual( | ||
72 | diagnostic.message, | ||
73 | [ | ||
74 | 'unused variable: `foo`', | ||
75 | '#[warn(unused_variables)] on by default', | ||
76 | ].join('\n'), | ||
77 | ); | ||
78 | assert.strictEqual(diagnostic.code, 'unused_variables'); | ||
79 | assert.strictEqual(diagnostic.source, 'rustc'); | ||
80 | assert.deepStrictEqual(diagnostic.tags, [ | ||
81 | vscode.DiagnosticTag.Unnecessary, | ||
82 | ]); | ||
83 | |||
84 | // No related information | ||
85 | assert.deepStrictEqual(diagnostic.relatedInformation, []); | ||
86 | |||
87 | // One suggested fix available to prefix the variable | ||
88 | assert.strictEqual(suggestedFixes.length, 1); | ||
89 | const [suggestedFix] = suggestedFixes; | ||
90 | assert.strictEqual( | ||
91 | suggestedFix.title, | ||
92 | 'consider prefixing with an underscore: `_foo`', | ||
93 | ); | ||
94 | assert.strictEqual( | ||
95 | suggestedFix.applicability, | ||
96 | SuggestionApplicability.MachineApplicable, | ||
97 | ); | ||
98 | }); | ||
99 | |||
100 | it('should map a wrong number of parameters error', () => { | ||
101 | const { diagnostic, suggestedFixes } = mapFixtureToVsCode( | ||
102 | 'error/E0061', | ||
103 | ); | ||
104 | |||
105 | assert.strictEqual( | ||
106 | diagnostic.severity, | ||
107 | vscode.DiagnosticSeverity.Error, | ||
108 | ); | ||
109 | assert.strictEqual( | ||
110 | diagnostic.message, | ||
111 | [ | ||
112 | 'this function takes 2 parameters but 3 parameters were supplied', | ||
113 | 'expected 2 parameters', | ||
114 | ].join('\n'), | ||
115 | ); | ||
116 | assert.strictEqual(diagnostic.code, 'E0061'); | ||
117 | assert.strictEqual(diagnostic.source, 'rustc'); | ||
118 | assert.deepStrictEqual(diagnostic.tags, []); | ||
119 | |||
120 | // One related information for the original definition | ||
121 | const relatedInformation = diagnostic.relatedInformation; | ||
122 | if (!relatedInformation) { | ||
123 | assert.fail('Related information unexpectedly undefined'); | ||
124 | return; | ||
125 | } | ||
126 | assert.strictEqual(relatedInformation.length, 1); | ||
127 | const [related] = relatedInformation; | ||
128 | assert.strictEqual(related.message, 'defined here'); | ||
129 | |||
130 | // There are no suggested fixes | ||
131 | assert.strictEqual(suggestedFixes.length, 0); | ||
132 | }); | ||
133 | |||
134 | it('should map a Clippy copy pass by ref warning', () => { | ||
135 | const { diagnostic, suggestedFixes } = mapFixtureToVsCode( | ||
136 | 'clippy/trivially_copy_pass_by_ref', | ||
137 | ); | ||
138 | |||
139 | assert.strictEqual( | ||
140 | diagnostic.severity, | ||
141 | vscode.DiagnosticSeverity.Warning, | ||
142 | ); | ||
143 | assert.strictEqual(diagnostic.source, 'clippy'); | ||
144 | assert.strictEqual( | ||
145 | diagnostic.message, | ||
146 | [ | ||
147 | 'this argument is passed by reference, but would be more efficient if passed by value', | ||
148 | '#[warn(clippy::trivially_copy_pass_by_ref)] implied by #[warn(clippy::all)]', | ||
149 | 'for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref', | ||
150 | ].join('\n'), | ||
151 | ); | ||
152 | assert.strictEqual(diagnostic.code, 'trivially_copy_pass_by_ref'); | ||
153 | assert.deepStrictEqual(diagnostic.tags, []); | ||
154 | |||
155 | // One related information for the lint definition | ||
156 | const relatedInformation = diagnostic.relatedInformation; | ||
157 | if (!relatedInformation) { | ||
158 | assert.fail('Related information unexpectedly undefined'); | ||
159 | return; | ||
160 | } | ||
161 | assert.strictEqual(relatedInformation.length, 1); | ||
162 | const [related] = relatedInformation; | ||
163 | assert.strictEqual(related.message, 'lint level defined here'); | ||
164 | |||
165 | // One suggested fix to pass by value | ||
166 | assert.strictEqual(suggestedFixes.length, 1); | ||
167 | const [suggestedFix] = suggestedFixes; | ||
168 | assert.strictEqual( | ||
169 | suggestedFix.title, | ||
170 | 'consider passing by value instead: `self`', | ||
171 | ); | ||
172 | // Clippy does not mark this with any applicability | ||
173 | assert.strictEqual( | ||
174 | suggestedFix.applicability, | ||
175 | SuggestionApplicability.Unspecified, | ||
176 | ); | ||
177 | }); | ||
178 | |||
179 | it('should map a mismatched type error', () => { | ||
180 | const { diagnostic, suggestedFixes } = mapFixtureToVsCode( | ||
181 | 'error/E0308', | ||
182 | ); | ||
183 | |||
184 | assert.strictEqual( | ||
185 | diagnostic.severity, | ||
186 | vscode.DiagnosticSeverity.Error, | ||
187 | ); | ||
188 | assert.strictEqual( | ||
189 | diagnostic.message, | ||
190 | ['mismatched types', 'expected usize, found u32'].join('\n'), | ||
191 | ); | ||
192 | assert.strictEqual(diagnostic.code, 'E0308'); | ||
193 | assert.strictEqual(diagnostic.source, 'rustc'); | ||
194 | assert.deepStrictEqual(diagnostic.tags, []); | ||
195 | |||
196 | // No related information | ||
197 | assert.deepStrictEqual(diagnostic.relatedInformation, []); | ||
198 | |||
199 | // There are no suggested fixes | ||
200 | assert.strictEqual(suggestedFixes.length, 0); | ||
201 | }); | ||
202 | |||
203 | it('should map a macro invocation location to normal file path', () => { | ||
204 | const { location, diagnostic, suggestedFixes } = mapFixtureToVsCode( | ||
205 | 'error/E0277', | ||
206 | ); | ||
207 | |||
208 | assert.strictEqual( | ||
209 | diagnostic.severity, | ||
210 | vscode.DiagnosticSeverity.Error, | ||
211 | ); | ||
212 | assert.strictEqual( | ||
213 | diagnostic.message, | ||
214 | [ | ||
215 | "can't compare `{integer}` with `&str`", | ||
216 | 'the trait `std::cmp::PartialEq<&str>` is not implemented for `{integer}`', | ||
217 | ].join('\n'), | ||
218 | ); | ||
219 | assert.strictEqual(diagnostic.code, 'E0277'); | ||
220 | assert.strictEqual(diagnostic.source, 'rustc'); | ||
221 | assert.deepStrictEqual(diagnostic.tags, []); | ||
222 | |||
223 | // No related information | ||
224 | assert.deepStrictEqual(diagnostic.relatedInformation, []); | ||
225 | |||
226 | // There are no suggested fixes | ||
227 | assert.strictEqual(suggestedFixes.length, 0); | ||
228 | |||
229 | // The file url should be normal file | ||
230 | // Ignore the first part because it depends on vs workspace location | ||
231 | assert.strictEqual( | ||
232 | location.uri.path.substr(-'src/main.rs'.length), | ||
233 | 'src/main.rs', | ||
234 | ); | ||
235 | }); | ||
236 | }); | ||
diff --git a/editors/code/src/test/utils/diagnotics/vscode.test.ts b/editors/code/src/test/utils/diagnotics/vscode.test.ts deleted file mode 100644 index 4944dd032..000000000 --- a/editors/code/src/test/utils/diagnotics/vscode.test.ts +++ /dev/null | |||
@@ -1,98 +0,0 @@ | |||
1 | import * as assert from 'assert'; | ||
2 | import * as vscode from 'vscode'; | ||
3 | |||
4 | import { areDiagnosticsEqual } from '../../../utils/diagnostics/vscode'; | ||
5 | |||
6 | const range1 = new vscode.Range( | ||
7 | new vscode.Position(1, 2), | ||
8 | new vscode.Position(3, 4), | ||
9 | ); | ||
10 | |||
11 | const range2 = new vscode.Range( | ||
12 | new vscode.Position(5, 6), | ||
13 | new vscode.Position(7, 8), | ||
14 | ); | ||
15 | |||
16 | describe('areDiagnosticsEqual', () => { | ||
17 | it('should treat identical diagnostics as equal', () => { | ||
18 | const diagnostic1 = new vscode.Diagnostic( | ||
19 | range1, | ||
20 | 'Hello, world!', | ||
21 | vscode.DiagnosticSeverity.Error, | ||
22 | ); | ||
23 | |||
24 | const diagnostic2 = new vscode.Diagnostic( | ||
25 | range1, | ||
26 | 'Hello, world!', | ||
27 | vscode.DiagnosticSeverity.Error, | ||
28 | ); | ||
29 | |||
30 | assert(areDiagnosticsEqual(diagnostic1, diagnostic2)); | ||
31 | }); | ||
32 | |||
33 | it('should treat diagnostics with different sources as inequal', () => { | ||
34 | const diagnostic1 = new vscode.Diagnostic( | ||
35 | range1, | ||
36 | 'Hello, world!', | ||
37 | vscode.DiagnosticSeverity.Error, | ||
38 | ); | ||
39 | diagnostic1.source = 'rustc'; | ||
40 | |||
41 | const diagnostic2 = new vscode.Diagnostic( | ||
42 | range1, | ||
43 | 'Hello, world!', | ||
44 | vscode.DiagnosticSeverity.Error, | ||
45 | ); | ||
46 | diagnostic2.source = 'clippy'; | ||
47 | |||
48 | assert(!areDiagnosticsEqual(diagnostic1, diagnostic2)); | ||
49 | }); | ||
50 | |||
51 | it('should treat diagnostics with different ranges as inequal', () => { | ||
52 | const diagnostic1 = new vscode.Diagnostic( | ||
53 | range1, | ||
54 | 'Hello, world!', | ||
55 | vscode.DiagnosticSeverity.Error, | ||
56 | ); | ||
57 | |||
58 | const diagnostic2 = new vscode.Diagnostic( | ||
59 | range2, | ||
60 | 'Hello, world!', | ||
61 | vscode.DiagnosticSeverity.Error, | ||
62 | ); | ||
63 | |||
64 | assert(!areDiagnosticsEqual(diagnostic1, diagnostic2)); | ||
65 | }); | ||
66 | |||
67 | it('should treat diagnostics with different messages as inequal', () => { | ||
68 | const diagnostic1 = new vscode.Diagnostic( | ||
69 | range1, | ||
70 | 'Hello, world!', | ||
71 | vscode.DiagnosticSeverity.Error, | ||
72 | ); | ||
73 | |||
74 | const diagnostic2 = new vscode.Diagnostic( | ||
75 | range1, | ||
76 | 'Goodbye!, world!', | ||
77 | vscode.DiagnosticSeverity.Error, | ||
78 | ); | ||
79 | |||
80 | assert(!areDiagnosticsEqual(diagnostic1, diagnostic2)); | ||
81 | }); | ||
82 | |||
83 | it('should treat diagnostics with different severities as inequal', () => { | ||
84 | const diagnostic1 = new vscode.Diagnostic( | ||
85 | range1, | ||
86 | 'Hello, world!', | ||
87 | vscode.DiagnosticSeverity.Warning, | ||
88 | ); | ||
89 | |||
90 | const diagnostic2 = new vscode.Diagnostic( | ||
91 | range1, | ||
92 | 'Hello, world!', | ||
93 | vscode.DiagnosticSeverity.Error, | ||
94 | ); | ||
95 | |||
96 | assert(!areDiagnosticsEqual(diagnostic1, diagnostic2)); | ||
97 | }); | ||
98 | }); | ||
diff --git a/editors/code/src/utils/diagnostics/SuggestedFix.ts b/editors/code/src/utils/diagnostics/SuggestedFix.ts deleted file mode 100644 index 6e660bb61..000000000 --- a/editors/code/src/utils/diagnostics/SuggestedFix.ts +++ /dev/null | |||
@@ -1,67 +0,0 @@ | |||
1 | import * as vscode from 'vscode'; | ||
2 | |||
3 | import { SuggestionApplicability } from './rust'; | ||
4 | |||
5 | /** | ||
6 | * Model object for text replacements suggested by the Rust compiler | ||
7 | * | ||
8 | * This is an intermediate form between the raw `rustc` JSON and a | ||
9 | * `vscode.CodeAction`. It's optimised for the use-cases of | ||
10 | * `SuggestedFixCollection`. | ||
11 | */ | ||
12 | export default class SuggestedFix { | ||
13 | public readonly title: string; | ||
14 | public readonly location: vscode.Location; | ||
15 | public readonly replacement: string; | ||
16 | public readonly applicability: SuggestionApplicability; | ||
17 | |||
18 | /** | ||
19 | * Diagnostics this suggested fix could resolve | ||
20 | */ | ||
21 | public diagnostics: vscode.Diagnostic[]; | ||
22 | |||
23 | constructor( | ||
24 | title: string, | ||
25 | location: vscode.Location, | ||
26 | replacement: string, | ||
27 | applicability: SuggestionApplicability = SuggestionApplicability.Unspecified, | ||
28 | ) { | ||
29 | this.title = title; | ||
30 | this.location = location; | ||
31 | this.replacement = replacement; | ||
32 | this.applicability = applicability; | ||
33 | this.diagnostics = []; | ||
34 | } | ||
35 | |||
36 | /** | ||
37 | * Determines if this suggested fix is equivalent to another instance | ||
38 | */ | ||
39 | public isEqual(other: SuggestedFix): boolean { | ||
40 | return ( | ||
41 | this.title === other.title && | ||
42 | this.location.range.isEqual(other.location.range) && | ||
43 | this.replacement === other.replacement && | ||
44 | this.applicability === other.applicability | ||
45 | ); | ||
46 | } | ||
47 | |||
48 | /** | ||
49 | * Converts this suggested fix to a VS Code Quick Fix code action | ||
50 | */ | ||
51 | public toCodeAction(): vscode.CodeAction { | ||
52 | const codeAction = new vscode.CodeAction( | ||
53 | this.title, | ||
54 | vscode.CodeActionKind.QuickFix, | ||
55 | ); | ||
56 | |||
57 | const edit = new vscode.WorkspaceEdit(); | ||
58 | edit.replace(this.location.uri, this.location.range, this.replacement); | ||
59 | codeAction.edit = edit; | ||
60 | |||
61 | codeAction.isPreferred = | ||
62 | this.applicability === SuggestionApplicability.MachineApplicable; | ||
63 | |||
64 | codeAction.diagnostics = [...this.diagnostics]; | ||
65 | return codeAction; | ||
66 | } | ||
67 | } | ||
diff --git a/editors/code/src/utils/diagnostics/SuggestedFixCollection.ts b/editors/code/src/utils/diagnostics/SuggestedFixCollection.ts deleted file mode 100644 index 57c9856cf..000000000 --- a/editors/code/src/utils/diagnostics/SuggestedFixCollection.ts +++ /dev/null | |||
@@ -1,77 +0,0 @@ | |||
1 | import * as vscode from 'vscode'; | ||
2 | import SuggestedFix from './SuggestedFix'; | ||
3 | |||
4 | /** | ||
5 | * Collection of suggested fixes across multiple documents | ||
6 | * | ||
7 | * This stores `SuggestedFix` model objects and returns them via the | ||
8 | * `vscode.CodeActionProvider` interface. | ||
9 | */ | ||
10 | export default class SuggestedFixCollection | ||
11 | implements vscode.CodeActionProvider { | ||
12 | public static PROVIDED_CODE_ACTION_KINDS = [vscode.CodeActionKind.QuickFix]; | ||
13 | |||
14 | /** | ||
15 | * Map of document URI strings to suggested fixes | ||
16 | */ | ||
17 | private suggestedFixes: Map<string, SuggestedFix[]>; | ||
18 | |||
19 | constructor() { | ||
20 | this.suggestedFixes = new Map(); | ||
21 | } | ||
22 | |||
23 | /** | ||
24 | * Clears all suggested fixes across all documents | ||
25 | */ | ||
26 | public clear(): void { | ||
27 | this.suggestedFixes = new Map(); | ||
28 | } | ||
29 | |||
30 | /** | ||
31 | * Adds a suggested fix for the given diagnostic | ||
32 | * | ||
33 | * Some suggested fixes will appear in multiple diagnostics. For example, | ||
34 | * forgetting a `mut` on a variable will suggest changing the delaration on | ||
35 | * every mutable usage site. If the suggested fix has already been added | ||
36 | * this method will instead associate the existing fix with the new | ||
37 | * diagnostic. | ||
38 | */ | ||
39 | public addSuggestedFixForDiagnostic( | ||
40 | suggestedFix: SuggestedFix, | ||
41 | diagnostic: vscode.Diagnostic, | ||
42 | ): void { | ||
43 | const fileUriString = suggestedFix.location.uri.toString(); | ||
44 | const fileSuggestions = this.suggestedFixes.get(fileUriString) || []; | ||
45 | |||
46 | const existingSuggestion = fileSuggestions.find(s => | ||
47 | s.isEqual(suggestedFix), | ||
48 | ); | ||
49 | |||
50 | if (existingSuggestion) { | ||
51 | // The existing suggestion also applies to this new diagnostic | ||
52 | existingSuggestion.diagnostics.push(diagnostic); | ||
53 | } else { | ||
54 | // We haven't seen this suggestion before | ||
55 | suggestedFix.diagnostics.push(diagnostic); | ||
56 | fileSuggestions.push(suggestedFix); | ||
57 | } | ||
58 | |||
59 | this.suggestedFixes.set(fileUriString, fileSuggestions); | ||
60 | } | ||
61 | |||
62 | /** | ||
63 | * Filters suggested fixes by their document and range and converts them to | ||
64 | * code actions | ||
65 | */ | ||
66 | public provideCodeActions( | ||
67 | document: vscode.TextDocument, | ||
68 | range: vscode.Range, | ||
69 | ): vscode.CodeAction[] { | ||
70 | const documentUriString = document.uri.toString(); | ||
71 | |||
72 | const suggestedFixes = this.suggestedFixes.get(documentUriString); | ||
73 | return (suggestedFixes || []) | ||
74 | .filter(({ location }) => location.range.intersection(range)) | ||
75 | .map(suggestedEdit => suggestedEdit.toCodeAction()); | ||
76 | } | ||
77 | } | ||
diff --git a/editors/code/src/utils/diagnostics/rust.ts b/editors/code/src/utils/diagnostics/rust.ts deleted file mode 100644 index 1f0c0d3e4..000000000 --- a/editors/code/src/utils/diagnostics/rust.ts +++ /dev/null | |||
@@ -1,299 +0,0 @@ | |||
1 | import * as path from 'path'; | ||
2 | import * as vscode from 'vscode'; | ||
3 | |||
4 | import SuggestedFix from './SuggestedFix'; | ||
5 | |||
6 | export enum SuggestionApplicability { | ||
7 | MachineApplicable = 'MachineApplicable', | ||
8 | HasPlaceholders = 'HasPlaceholders', | ||
9 | MaybeIncorrect = 'MaybeIncorrect', | ||
10 | Unspecified = 'Unspecified', | ||
11 | } | ||
12 | |||
13 | export interface RustDiagnosticSpanMacroExpansion { | ||
14 | span: RustDiagnosticSpan; | ||
15 | macro_decl_name: string; | ||
16 | def_site_span?: RustDiagnosticSpan; | ||
17 | } | ||
18 | |||
19 | // Reference: | ||
20 | // https://github.com/rust-lang/rust/blob/master/src/libsyntax/json.rs | ||
21 | export interface RustDiagnosticSpan { | ||
22 | line_start: number; | ||
23 | line_end: number; | ||
24 | column_start: number; | ||
25 | column_end: number; | ||
26 | is_primary: boolean; | ||
27 | file_name: string; | ||
28 | label?: string; | ||
29 | expansion?: RustDiagnosticSpanMacroExpansion; | ||
30 | suggested_replacement?: string; | ||
31 | suggestion_applicability?: SuggestionApplicability; | ||
32 | } | ||
33 | |||
34 | export interface RustDiagnostic { | ||
35 | spans: RustDiagnosticSpan[]; | ||
36 | rendered: string; | ||
37 | message: string; | ||
38 | level: string; | ||
39 | code?: { | ||
40 | code: string; | ||
41 | }; | ||
42 | children: RustDiagnostic[]; | ||
43 | } | ||
44 | |||
45 | export interface MappedRustDiagnostic { | ||
46 | location: vscode.Location; | ||
47 | diagnostic: vscode.Diagnostic; | ||
48 | suggestedFixes: SuggestedFix[]; | ||
49 | } | ||
50 | |||
51 | interface MappedRustChildDiagnostic { | ||
52 | related?: vscode.DiagnosticRelatedInformation; | ||
53 | suggestedFix?: SuggestedFix; | ||
54 | messageLine?: string; | ||
55 | } | ||
56 | |||
57 | /** | ||
58 | * Converts a Rust level string to a VsCode severity | ||
59 | */ | ||
60 | function mapLevelToSeverity(s: string): vscode.DiagnosticSeverity { | ||
61 | if (s === 'error') { | ||
62 | return vscode.DiagnosticSeverity.Error; | ||
63 | } | ||
64 | if (s.startsWith('warn')) { | ||
65 | return vscode.DiagnosticSeverity.Warning; | ||
66 | } | ||
67 | return vscode.DiagnosticSeverity.Information; | ||
68 | } | ||
69 | |||
70 | /** | ||
71 | * Check whether a file name is from macro invocation | ||
72 | */ | ||
73 | function isFromMacro(fileName: string): boolean { | ||
74 | return fileName.startsWith('<') && fileName.endsWith('>'); | ||
75 | } | ||
76 | |||
77 | /** | ||
78 | * Converts a Rust macro span to a VsCode location recursively | ||
79 | */ | ||
80 | function mapMacroSpanToLocation( | ||
81 | spanMacro: RustDiagnosticSpanMacroExpansion, | ||
82 | ): vscode.Location | undefined { | ||
83 | if (!isFromMacro(spanMacro.span.file_name)) { | ||
84 | return mapSpanToLocation(spanMacro.span); | ||
85 | } | ||
86 | |||
87 | if (spanMacro.span.expansion) { | ||
88 | return mapMacroSpanToLocation(spanMacro.span.expansion); | ||
89 | } | ||
90 | |||
91 | return; | ||
92 | } | ||
93 | |||
94 | /** | ||
95 | * Converts a Rust span to a VsCode location | ||
96 | */ | ||
97 | function mapSpanToLocation(span: RustDiagnosticSpan): vscode.Location { | ||
98 | if (isFromMacro(span.file_name) && span.expansion) { | ||
99 | const macroLoc = mapMacroSpanToLocation(span.expansion); | ||
100 | if (macroLoc) { | ||
101 | return macroLoc; | ||
102 | } | ||
103 | } | ||
104 | |||
105 | const fileName = path.join(vscode.workspace.rootPath || '', span.file_name); | ||
106 | const fileUri = vscode.Uri.file(fileName); | ||
107 | |||
108 | const range = new vscode.Range( | ||
109 | new vscode.Position(span.line_start - 1, span.column_start - 1), | ||
110 | new vscode.Position(span.line_end - 1, span.column_end - 1), | ||
111 | ); | ||
112 | |||
113 | return new vscode.Location(fileUri, range); | ||
114 | } | ||
115 | |||
116 | /** | ||
117 | * Converts a secondary Rust span to a VsCode related information | ||
118 | * | ||
119 | * If the span is unlabelled this will return `undefined`. | ||
120 | */ | ||
121 | function mapSecondarySpanToRelated( | ||
122 | span: RustDiagnosticSpan, | ||
123 | ): vscode.DiagnosticRelatedInformation | undefined { | ||
124 | if (!span.label) { | ||
125 | // Nothing to label this with | ||
126 | return; | ||
127 | } | ||
128 | |||
129 | const location = mapSpanToLocation(span); | ||
130 | return new vscode.DiagnosticRelatedInformation(location, span.label); | ||
131 | } | ||
132 | |||
133 | /** | ||
134 | * Determines if diagnostic is related to unused code | ||
135 | */ | ||
136 | function isUnusedOrUnnecessary(rd: RustDiagnostic): boolean { | ||
137 | if (!rd.code) { | ||
138 | return false; | ||
139 | } | ||
140 | |||
141 | return [ | ||
142 | 'dead_code', | ||
143 | 'unknown_lints', | ||
144 | 'unreachable_code', | ||
145 | 'unused_attributes', | ||
146 | 'unused_imports', | ||
147 | 'unused_macros', | ||
148 | 'unused_variables', | ||
149 | ].includes(rd.code.code); | ||
150 | } | ||
151 | |||
152 | /** | ||
153 | * Determines if diagnostic is related to deprecated code | ||
154 | */ | ||
155 | function isDeprecated(rd: RustDiagnostic): boolean { | ||
156 | if (!rd.code) { | ||
157 | return false; | ||
158 | } | ||
159 | |||
160 | return ['deprecated'].includes(rd.code.code); | ||
161 | } | ||
162 | |||
163 | /** | ||
164 | * Converts a Rust child diagnostic to a VsCode related information | ||
165 | * | ||
166 | * This can have three outcomes: | ||
167 | * | ||
168 | * 1. If this is no primary span this will return a `noteLine` | ||
169 | * 2. If there is a primary span with a suggested replacement it will return a | ||
170 | * `codeAction`. | ||
171 | * 3. If there is a primary span without a suggested replacement it will return | ||
172 | * a `related`. | ||
173 | */ | ||
174 | function mapRustChildDiagnostic(rd: RustDiagnostic): MappedRustChildDiagnostic { | ||
175 | const span = rd.spans.find(s => s.is_primary); | ||
176 | |||
177 | if (!span) { | ||
178 | // `rustc` uses these spanless children as a way to print multi-line | ||
179 | // messages | ||
180 | return { messageLine: rd.message }; | ||
181 | } | ||
182 | |||
183 | // If we have a primary span use its location, otherwise use the parent | ||
184 | const location = mapSpanToLocation(span); | ||
185 | |||
186 | // We need to distinguish `null` from an empty string | ||
187 | if (span && typeof span.suggested_replacement === 'string') { | ||
188 | // Include our replacement in the title unless it's empty | ||
189 | const title = span.suggested_replacement | ||
190 | ? `${rd.message}: \`${span.suggested_replacement}\`` | ||
191 | : rd.message; | ||
192 | |||
193 | return { | ||
194 | suggestedFix: new SuggestedFix( | ||
195 | title, | ||
196 | location, | ||
197 | span.suggested_replacement, | ||
198 | span.suggestion_applicability, | ||
199 | ), | ||
200 | }; | ||
201 | } else { | ||
202 | const related = new vscode.DiagnosticRelatedInformation( | ||
203 | location, | ||
204 | rd.message, | ||
205 | ); | ||
206 | |||
207 | return { related }; | ||
208 | } | ||
209 | } | ||
210 | |||
211 | /** | ||
212 | * Converts a Rust root diagnostic to VsCode form | ||
213 | * | ||
214 | * This flattens the Rust diagnostic by: | ||
215 | * | ||
216 | * 1. Creating a `vscode.Diagnostic` with the root message and primary span. | ||
217 | * 2. Adding any labelled secondary spans to `relatedInformation` | ||
218 | * 3. Categorising child diagnostics as either `SuggestedFix`es, | ||
219 | * `relatedInformation` or additional message lines. | ||
220 | * | ||
221 | * If the diagnostic has no primary span this will return `undefined` | ||
222 | */ | ||
223 | export function mapRustDiagnosticToVsCode( | ||
224 | rd: RustDiagnostic, | ||
225 | ): MappedRustDiagnostic | undefined { | ||
226 | const primarySpan = rd.spans.find(s => s.is_primary); | ||
227 | if (!primarySpan) { | ||
228 | return; | ||
229 | } | ||
230 | |||
231 | const location = mapSpanToLocation(primarySpan); | ||
232 | const secondarySpans = rd.spans.filter(s => !s.is_primary); | ||
233 | |||
234 | const severity = mapLevelToSeverity(rd.level); | ||
235 | let primarySpanLabel = primarySpan.label; | ||
236 | |||
237 | const vd = new vscode.Diagnostic(location.range, rd.message, severity); | ||
238 | |||
239 | let source = 'rustc'; | ||
240 | let code = rd.code && rd.code.code; | ||
241 | if (code) { | ||
242 | // See if this is an RFC #2103 scoped lint (e.g. from Clippy) | ||
243 | const scopedCode = code.split('::'); | ||
244 | if (scopedCode.length === 2) { | ||
245 | [source, code] = scopedCode; | ||
246 | } | ||
247 | } | ||
248 | |||
249 | vd.source = source; | ||
250 | vd.code = code; | ||
251 | vd.relatedInformation = []; | ||
252 | vd.tags = []; | ||
253 | |||
254 | for (const secondarySpan of secondarySpans) { | ||
255 | const related = mapSecondarySpanToRelated(secondarySpan); | ||
256 | if (related) { | ||
257 | vd.relatedInformation.push(related); | ||
258 | } | ||
259 | } | ||
260 | |||
261 | const suggestedFixes = []; | ||
262 | for (const child of rd.children) { | ||
263 | const { related, suggestedFix, messageLine } = mapRustChildDiagnostic( | ||
264 | child, | ||
265 | ); | ||
266 | |||
267 | if (related) { | ||
268 | vd.relatedInformation.push(related); | ||
269 | } | ||
270 | if (suggestedFix) { | ||
271 | suggestedFixes.push(suggestedFix); | ||
272 | } | ||
273 | if (messageLine) { | ||
274 | vd.message += `\n${messageLine}`; | ||
275 | |||
276 | // These secondary messages usually duplicate the content of the | ||
277 | // primary span label. | ||
278 | primarySpanLabel = undefined; | ||
279 | } | ||
280 | } | ||
281 | |||
282 | if (primarySpanLabel) { | ||
283 | vd.message += `\n${primarySpanLabel}`; | ||
284 | } | ||
285 | |||
286 | if (isUnusedOrUnnecessary(rd)) { | ||
287 | vd.tags.push(vscode.DiagnosticTag.Unnecessary); | ||
288 | } | ||
289 | |||
290 | if (isDeprecated(rd)) { | ||
291 | vd.tags.push(vscode.DiagnosticTag.Deprecated); | ||
292 | } | ||
293 | |||
294 | return { | ||
295 | location, | ||
296 | diagnostic: vd, | ||
297 | suggestedFixes, | ||
298 | }; | ||
299 | } | ||
diff --git a/editors/code/src/utils/diagnostics/vscode.ts b/editors/code/src/utils/diagnostics/vscode.ts deleted file mode 100644 index f4a5450e2..000000000 --- a/editors/code/src/utils/diagnostics/vscode.ts +++ /dev/null | |||
@@ -1,14 +0,0 @@ | |||
1 | import * as vscode from 'vscode'; | ||
2 | |||
3 | /** Compares two `vscode.Diagnostic`s for equality */ | ||
4 | export function areDiagnosticsEqual( | ||
5 | left: vscode.Diagnostic, | ||
6 | right: vscode.Diagnostic, | ||
7 | ): boolean { | ||
8 | return ( | ||
9 | left.source === right.source && | ||
10 | left.severity === right.severity && | ||
11 | left.range.isEqual(right.range) && | ||
12 | left.message === right.message | ||
13 | ); | ||
14 | } | ||
diff --git a/editors/code/src/utils/processes.ts b/editors/code/src/utils/processes.ts deleted file mode 100644 index a1d6b7eaf..000000000 --- a/editors/code/src/utils/processes.ts +++ /dev/null | |||
@@ -1,51 +0,0 @@ | |||
1 | 'use strict'; | ||
2 | |||
3 | import * as cp from 'child_process'; | ||
4 | import ChildProcess = cp.ChildProcess; | ||
5 | |||
6 | import { join } from 'path'; | ||
7 | |||
8 | const isWindows = process.platform === 'win32'; | ||
9 | const isMacintosh = process.platform === 'darwin'; | ||
10 | const isLinux = process.platform === 'linux'; | ||
11 | |||
12 | // this is very complex, but is basically copy-pased from VSCode implementation here: | ||
13 | // https://github.com/Microsoft/vscode-languageserver-node/blob/dbfd37e35953ad0ee14c4eeced8cfbc41697b47e/client/src/utils/processes.ts#L15 | ||
14 | |||
15 | // And see discussion at | ||
16 | // https://github.com/rust-analyzer/rust-analyzer/pull/1079#issuecomment-478908109 | ||
17 | |||
18 | export function terminate(process: ChildProcess, cwd?: string): boolean { | ||
19 | if (isWindows) { | ||
20 | try { | ||
21 | // This we run in Atom execFileSync is available. | ||
22 | // Ignore stderr since this is otherwise piped to parent.stderr | ||
23 | // which might be already closed. | ||
24 | const options: any = { | ||
25 | stdio: ['pipe', 'pipe', 'ignore'], | ||
26 | }; | ||
27 | if (cwd) { | ||
28 | options.cwd = cwd; | ||
29 | } | ||
30 | cp.execFileSync( | ||
31 | 'taskkill', | ||
32 | ['/T', '/F', '/PID', process.pid.toString()], | ||
33 | options, | ||
34 | ); | ||
35 | return true; | ||
36 | } catch (err) { | ||
37 | return false; | ||
38 | } | ||
39 | } else if (isLinux || isMacintosh) { | ||
40 | try { | ||
41 | const cmd = join(__dirname, 'terminateProcess.sh'); | ||
42 | const result = cp.spawnSync(cmd, [process.pid.toString()]); | ||
43 | return result.error ? false : true; | ||
44 | } catch (err) { | ||
45 | return false; | ||
46 | } | ||
47 | } else { | ||
48 | process.kill('SIGKILL'); | ||
49 | return true; | ||
50 | } | ||
51 | } | ||
diff --git a/editors/code/src/utils/terminateProcess.sh b/editors/code/src/utils/terminateProcess.sh deleted file mode 100644 index 2ec9e1c2e..000000000 --- a/editors/code/src/utils/terminateProcess.sh +++ /dev/null | |||
@@ -1,12 +0,0 @@ | |||
1 | #!/bin/bash | ||
2 | |||
3 | terminateTree() { | ||
4 | for cpid in $(pgrep -P $1); do | ||
5 | terminateTree $cpid | ||
6 | done | ||
7 | kill -9 $1 > /dev/null 2>&1 | ||
8 | } | ||
9 | |||
10 | for pid in $*; do | ||
11 | terminateTree $pid | ||
12 | done \ No newline at end of file | ||
diff --git a/xtask/src/main.rs b/xtask/src/main.rs index 4201c6a6a..9cefad925 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs | |||
@@ -226,7 +226,7 @@ fn install_client(ClientOpt::VsCode: ClientOpt) -> Result<()> { | |||
226 | if !str::from_utf8(&output.stdout)?.contains("ra-lsp") { | 226 | if !str::from_utf8(&output.stdout)?.contains("ra-lsp") { |
227 | anyhow::bail!( | 227 | anyhow::bail!( |
228 | "Could not install the Visual Studio Code extension. \ | 228 | "Could not install the Visual Studio Code extension. \ |
229 | Please make sure you have at least NodeJS 10.x installed and try again." | 229 | Please make sure you have at least NodeJS 10.x together with the latest version of VS Code installed and try again." |
230 | ); | 230 | ); |
231 | } | 231 | } |
232 | 232 | ||
diff --git a/xtask/tests/tidy-tests/cli.rs b/xtask/tests/tidy-tests/cli.rs index 573ffadbf..f9ca45292 100644 --- a/xtask/tests/tidy-tests/cli.rs +++ b/xtask/tests/tidy-tests/cli.rs | |||
@@ -43,7 +43,7 @@ fn no_todo() { | |||
43 | return; | 43 | return; |
44 | } | 44 | } |
45 | let text = std::fs::read_to_string(e.path()).unwrap(); | 45 | let text = std::fs::read_to_string(e.path()).unwrap(); |
46 | if text.contains("TODO") || text.contains("TOOD") { | 46 | if text.contains("TODO") || text.contains("TOOD") || text.contains("todo!") { |
47 | panic!( | 47 | panic!( |
48 | "\nTODO markers should not be committed to the master branch,\n\ | 48 | "\nTODO markers should not be committed to the master branch,\n\ |
49 | use FIXME instead\n\ | 49 | use FIXME instead\n\ |