diff options
88 files changed, 2207 insertions, 1079 deletions
diff --git a/Cargo.lock b/Cargo.lock index 715a80978..051d9e734 100644 --- a/Cargo.lock +++ b/Cargo.lock | |||
@@ -81,9 +81,9 @@ checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" | |||
81 | 81 | ||
82 | [[package]] | 82 | [[package]] |
83 | name = "backtrace" | 83 | name = "backtrace" |
84 | version = "0.3.54" | 84 | version = "0.3.55" |
85 | source = "registry+https://github.com/rust-lang/crates.io-index" | 85 | source = "registry+https://github.com/rust-lang/crates.io-index" |
86 | checksum = "2baad346b2d4e94a24347adeee9c7a93f412ee94b9cc26e5b59dea23848e9f28" | 86 | checksum = "ef5140344c85b01f9bbb4d4b7288a8aa4b3287ccef913a14bcc78a1063623598" |
87 | dependencies = [ | 87 | dependencies = [ |
88 | "addr2line", | 88 | "addr2line", |
89 | "cfg-if 1.0.0", | 89 | "cfg-if 1.0.0", |
@@ -139,9 +139,9 @@ dependencies = [ | |||
139 | 139 | ||
140 | [[package]] | 140 | [[package]] |
141 | name = "cc" | 141 | name = "cc" |
142 | version = "1.0.62" | 142 | version = "1.0.65" |
143 | source = "registry+https://github.com/rust-lang/crates.io-index" | 143 | source = "registry+https://github.com/rust-lang/crates.io-index" |
144 | checksum = "f1770ced377336a88a67c473594ccc14eca6f4559217c34f64aac8f83d641b40" | 144 | checksum = "95752358c8f7552394baf48cd82695b345628ad3f170d607de3ca03b8dacca15" |
145 | 145 | ||
146 | [[package]] | 146 | [[package]] |
147 | name = "cfg" | 147 | name = "cfg" |
@@ -242,9 +242,9 @@ dependencies = [ | |||
242 | 242 | ||
243 | [[package]] | 243 | [[package]] |
244 | name = "cmake" | 244 | name = "cmake" |
245 | version = "0.1.44" | 245 | version = "0.1.45" |
246 | source = "registry+https://github.com/rust-lang/crates.io-index" | 246 | source = "registry+https://github.com/rust-lang/crates.io-index" |
247 | checksum = "0e56268c17a6248366d66d4a47a3381369d068cce8409bb1716ed77ea32163bb" | 247 | checksum = "eb6210b637171dfba4cda12e579ac6dc73f5165ad56133e5d72ef3131f320855" |
248 | dependencies = [ | 248 | dependencies = [ |
249 | "cc", | 249 | "cc", |
250 | ] | 250 | ] |
@@ -255,6 +255,7 @@ version = "0.0.0" | |||
255 | dependencies = [ | 255 | dependencies = [ |
256 | "assists", | 256 | "assists", |
257 | "base_db", | 257 | "base_db", |
258 | "either", | ||
258 | "expect-test", | 259 | "expect-test", |
259 | "hir", | 260 | "hir", |
260 | "ide_db", | 261 | "ide_db", |
@@ -380,9 +381,9 @@ dependencies = [ | |||
380 | 381 | ||
381 | [[package]] | 382 | [[package]] |
382 | name = "env_logger" | 383 | name = "env_logger" |
383 | version = "0.8.1" | 384 | version = "0.8.2" |
384 | source = "registry+https://github.com/rust-lang/crates.io-index" | 385 | source = "registry+https://github.com/rust-lang/crates.io-index" |
385 | checksum = "54532e3223c5af90a6a757c90b5c5521564b07e5e7a958681bcd2afad421cdcd" | 386 | checksum = "f26ecb66b4bdca6c1409b40fb255eefc2bd4f6d135dab3c3124f80ffa2a9661e" |
386 | dependencies = [ | 387 | dependencies = [ |
387 | "log", | 388 | "log", |
388 | ] | 389 | ] |
@@ -735,9 +736,9 @@ dependencies = [ | |||
735 | 736 | ||
736 | [[package]] | 737 | [[package]] |
737 | name = "instant" | 738 | name = "instant" |
738 | version = "0.1.8" | 739 | version = "0.1.9" |
739 | source = "registry+https://github.com/rust-lang/crates.io-index" | 740 | source = "registry+https://github.com/rust-lang/crates.io-index" |
740 | checksum = "cb1fc4429a33e1f80d41dc9fea4d108a88bec1de8053878898ae448a0b52f613" | 741 | checksum = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec" |
741 | dependencies = [ | 742 | dependencies = [ |
742 | "cfg-if 1.0.0", | 743 | "cfg-if 1.0.0", |
743 | ] | 744 | ] |
@@ -821,9 +822,9 @@ dependencies = [ | |||
821 | 822 | ||
822 | [[package]] | 823 | [[package]] |
823 | name = "lock_api" | 824 | name = "lock_api" |
824 | version = "0.4.1" | 825 | version = "0.4.2" |
825 | source = "registry+https://github.com/rust-lang/crates.io-index" | 826 | source = "registry+https://github.com/rust-lang/crates.io-index" |
826 | checksum = "28247cc5a5be2f05fbcd76dd0cf2c7d3b5400cb978a28042abcd4fa0b3f8261c" | 827 | checksum = "dd96ffd135b2fd7b973ac026d28085defbe8983df057ced3eb4f2130b0831312" |
827 | dependencies = [ | 828 | dependencies = [ |
828 | "scopeguard", | 829 | "scopeguard", |
829 | ] | 830 | ] |
@@ -852,9 +853,9 @@ dependencies = [ | |||
852 | 853 | ||
853 | [[package]] | 854 | [[package]] |
854 | name = "lsp-server" | 855 | name = "lsp-server" |
855 | version = "0.4.1" | 856 | version = "0.5.0" |
856 | source = "registry+https://github.com/rust-lang/crates.io-index" | 857 | source = "registry+https://github.com/rust-lang/crates.io-index" |
857 | checksum = "9c85acaf36c53bf15da2b8b35afeea56747707261f59eb0b77229081dd72b04e" | 858 | checksum = "69b18dfe0e4a380b872aa79d8e0ee6c3d7a9682466e84b83ad807c88b3545f79" |
858 | dependencies = [ | 859 | dependencies = [ |
859 | "crossbeam-channel 0.5.0", | 860 | "crossbeam-channel 0.5.0", |
860 | "log", | 861 | "log", |
@@ -864,9 +865,9 @@ dependencies = [ | |||
864 | 865 | ||
865 | [[package]] | 866 | [[package]] |
866 | name = "lsp-types" | 867 | name = "lsp-types" |
867 | version = "0.83.0" | 868 | version = "0.84.0" |
868 | source = "registry+https://github.com/rust-lang/crates.io-index" | 869 | source = "registry+https://github.com/rust-lang/crates.io-index" |
869 | checksum = "25e0bd4b95038f2c23bda332ba0ca684e8dda765db1f9bdb63dc4c3e01f3b456" | 870 | checksum = "3b95be71fe205e44de754185bcf86447b65813ce1ceb298f8d3793ade5fff08d" |
870 | dependencies = [ | 871 | dependencies = [ |
871 | "base64", | 872 | "base64", |
872 | "bitflags", | 873 | "bitflags", |
@@ -1065,21 +1066,21 @@ checksum = "8d3b63360ec3cb337817c2dbd47ab4a0f170d285d8e5a2064600f3def1402397" | |||
1065 | 1066 | ||
1066 | [[package]] | 1067 | [[package]] |
1067 | name = "once_cell" | 1068 | name = "once_cell" |
1068 | version = "1.5.1" | 1069 | version = "1.5.2" |
1069 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1070 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1070 | checksum = "f53cef67919d7d247eb9a2f128ca9e522789967ef1eb4ccd8c71a95a8aedf596" | 1071 | checksum = "13bd41f508810a131401606d54ac32a467c97172d74ba7662562ebba5ad07fa0" |
1071 | 1072 | ||
1072 | [[package]] | 1073 | [[package]] |
1073 | name = "oorandom" | 1074 | name = "oorandom" |
1074 | version = "11.1.2" | 1075 | version = "11.1.3" |
1075 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1076 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1076 | checksum = "a170cebd8021a008ea92e4db85a72f80b35df514ec664b296fdcbb654eac0b2c" | 1077 | checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" |
1077 | 1078 | ||
1078 | [[package]] | 1079 | [[package]] |
1079 | name = "parking_lot" | 1080 | name = "parking_lot" |
1080 | version = "0.11.0" | 1081 | version = "0.11.1" |
1081 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1082 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1082 | checksum = "a4893845fa2ca272e647da5d0e46660a314ead9c2fdd9a883aabc32e481a8733" | 1083 | checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb" |
1083 | dependencies = [ | 1084 | dependencies = [ |
1084 | "instant", | 1085 | "instant", |
1085 | "lock_api", | 1086 | "lock_api", |
@@ -1164,9 +1165,9 @@ checksum = "28b9b4df73455c861d7cbf8be42f01d3b373ed7f02e378d55fa84eafc6f638b1" | |||
1164 | 1165 | ||
1165 | [[package]] | 1166 | [[package]] |
1166 | name = "pin-project-lite" | 1167 | name = "pin-project-lite" |
1167 | version = "0.1.11" | 1168 | version = "0.2.0" |
1168 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1169 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1169 | checksum = "c917123afa01924fc84bb20c4c03f004d9c38e5127e3c039bbf7f4b9c76a2f6b" | 1170 | checksum = "6b063f57ec186e6140e2b8b6921e5f1bd89c7356dda5b33acc5401203ca6131c" |
1170 | 1171 | ||
1171 | [[package]] | 1172 | [[package]] |
1172 | name = "plain" | 1173 | name = "plain" |
@@ -1593,9 +1594,9 @@ checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" | |||
1593 | 1594 | ||
1594 | [[package]] | 1595 | [[package]] |
1595 | name = "smallvec" | 1596 | name = "smallvec" |
1596 | version = "1.4.2" | 1597 | version = "1.5.0" |
1597 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1598 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1598 | checksum = "fbee7696b84bbf3d89a1c2eccff0850e3047ed46bfcd2e92c29a2d074d57e252" | 1599 | checksum = "7acad6f34eb9e8a259d3283d1e8c1d34d7415943d4895f65cc73813c7396fc85" |
1599 | 1600 | ||
1600 | [[package]] | 1601 | [[package]] |
1601 | name = "smol_str" | 1602 | name = "smol_str" |
@@ -1626,9 +1627,9 @@ version = "0.0.0" | |||
1626 | 1627 | ||
1627 | [[package]] | 1628 | [[package]] |
1628 | name = "syn" | 1629 | name = "syn" |
1629 | version = "1.0.48" | 1630 | version = "1.0.51" |
1630 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1631 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1631 | checksum = "cc371affeffc477f42a221a1e4297aedcea33d47d19b61455588bd9d8f6b19ac" | 1632 | checksum = "3b4f34193997d92804d359ed09953e25d5138df6bcc055a71bf68ee89fdf9223" |
1632 | dependencies = [ | 1633 | dependencies = [ |
1633 | "proc-macro2", | 1634 | "proc-macro2", |
1634 | "quote", | 1635 | "quote", |
@@ -1671,9 +1672,9 @@ dependencies = [ | |||
1671 | 1672 | ||
1672 | [[package]] | 1673 | [[package]] |
1673 | name = "termcolor" | 1674 | name = "termcolor" |
1674 | version = "1.1.0" | 1675 | version = "1.1.2" |
1675 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1676 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1676 | checksum = "bb6bfa289a4d7c5766392812c0a1f4c1ba45afa1ad47803c11e1f407d846d75f" | 1677 | checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" |
1677 | dependencies = [ | 1678 | dependencies = [ |
1678 | "winapi-util", | 1679 | "winapi-util", |
1679 | ] | 1680 | ] |
@@ -1739,9 +1740,18 @@ dependencies = [ | |||
1739 | 1740 | ||
1740 | [[package]] | 1741 | [[package]] |
1741 | name = "tinyvec" | 1742 | name = "tinyvec" |
1742 | version = "0.3.4" | 1743 | version = "1.1.0" |
1744 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1745 | checksum = "ccf8dbc19eb42fba10e8feaaec282fb50e2c14b2726d6301dbfeed0f73306a6f" | ||
1746 | dependencies = [ | ||
1747 | "tinyvec_macros", | ||
1748 | ] | ||
1749 | |||
1750 | [[package]] | ||
1751 | name = "tinyvec_macros" | ||
1752 | version = "0.1.0" | ||
1743 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1753 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1744 | checksum = "238ce071d267c5710f9d31451efec16c5ee22de34df17cc05e56cbc92e967117" | 1754 | checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" |
1745 | 1755 | ||
1746 | [[package]] | 1756 | [[package]] |
1747 | name = "toolchain" | 1757 | name = "toolchain" |
@@ -1752,11 +1762,11 @@ dependencies = [ | |||
1752 | 1762 | ||
1753 | [[package]] | 1763 | [[package]] |
1754 | name = "tracing" | 1764 | name = "tracing" |
1755 | version = "0.1.21" | 1765 | version = "0.1.22" |
1756 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1766 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1757 | checksum = "b0987850db3733619253fe60e17cb59b82d37c7e6c0236bb81e4d6b87c879f27" | 1767 | checksum = "9f47026cdc4080c07e49b37087de021820269d996f581aac150ef9e5583eefe3" |
1758 | dependencies = [ | 1768 | dependencies = [ |
1759 | "cfg-if 0.1.10", | 1769 | "cfg-if 1.0.0", |
1760 | "pin-project-lite", | 1770 | "pin-project-lite", |
1761 | "tracing-attributes", | 1771 | "tracing-attributes", |
1762 | "tracing-core", | 1772 | "tracing-core", |
@@ -1879,18 +1889,18 @@ dependencies = [ | |||
1879 | 1889 | ||
1880 | [[package]] | 1890 | [[package]] |
1881 | name = "unicode-normalization" | 1891 | name = "unicode-normalization" |
1882 | version = "0.1.13" | 1892 | version = "0.1.16" |
1883 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1893 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1884 | checksum = "6fb19cf769fa8c6a80a162df694621ebeb4dafb606470b2b2fce0be40a98a977" | 1894 | checksum = "a13e63ab62dbe32aeee58d1c5408d35c36c392bba5d9d3142287219721afe606" |
1885 | dependencies = [ | 1895 | dependencies = [ |
1886 | "tinyvec", | 1896 | "tinyvec", |
1887 | ] | 1897 | ] |
1888 | 1898 | ||
1889 | [[package]] | 1899 | [[package]] |
1890 | name = "unicode-segmentation" | 1900 | name = "unicode-segmentation" |
1891 | version = "1.6.0" | 1901 | version = "1.7.0" |
1892 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1902 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1893 | checksum = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0" | 1903 | checksum = "db8716a166f290ff49dabc18b44aa407cb7c6dbe1aa0971b44b8a24b0ca35aae" |
1894 | 1904 | ||
1895 | [[package]] | 1905 | [[package]] |
1896 | name = "unicode-xid" | 1906 | name = "unicode-xid" |
diff --git a/crates/assists/src/handlers/auto_import.rs b/crates/assists/src/handlers/auto_import.rs index 37dd61266..d665837a2 100644 --- a/crates/assists/src/handlers/auto_import.rs +++ b/crates/assists/src/handlers/auto_import.rs | |||
@@ -98,7 +98,8 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()> | |||
98 | 98 | ||
99 | let range = ctx.sema.original_range(import_assets.syntax_under_caret()).range; | 99 | let range = ctx.sema.original_range(import_assets.syntax_under_caret()).range; |
100 | let group = import_group_message(import_assets.import_candidate()); | 100 | let group = import_group_message(import_assets.import_candidate()); |
101 | let scope = ImportScope::find_insert_use_container(import_assets.syntax_under_caret(), ctx)?; | 101 | let scope = |
102 | ImportScope::find_insert_use_container(import_assets.syntax_under_caret(), &ctx.sema)?; | ||
102 | for (import, _) in proposed_imports { | 103 | for (import, _) in proposed_imports { |
103 | acc.add_group( | 104 | acc.add_group( |
104 | &group, | 105 | &group, |
diff --git a/crates/assists/src/handlers/extract_struct_from_enum_variant.rs b/crates/assists/src/handlers/extract_struct_from_enum_variant.rs index 067afabf2..cac77c49b 100644 --- a/crates/assists/src/handlers/extract_struct_from_enum_variant.rs +++ b/crates/assists/src/handlers/extract_struct_from_enum_variant.rs | |||
@@ -143,8 +143,7 @@ fn insert_import( | |||
143 | if let Some(mut mod_path) = mod_path { | 143 | if let Some(mut mod_path) = mod_path { |
144 | mod_path.segments.pop(); | 144 | mod_path.segments.pop(); |
145 | mod_path.segments.push(variant_hir_name.clone()); | 145 | mod_path.segments.push(variant_hir_name.clone()); |
146 | let scope = ImportScope::find_insert_use_container(scope_node, ctx)?; | 146 | let scope = ImportScope::find_insert_use_container(scope_node, &ctx.sema)?; |
147 | |||
148 | *rewriter += insert_use(&scope, mod_path_to_ast(&mod_path), ctx.config.insert_use.merge); | 147 | *rewriter += insert_use(&scope, mod_path_to_ast(&mod_path), ctx.config.insert_use.merge); |
149 | } | 148 | } |
150 | Some(()) | 149 | Some(()) |
diff --git a/crates/assists/src/handlers/ignore_test.rs b/crates/assists/src/handlers/ignore_test.rs new file mode 100644 index 000000000..5096a0005 --- /dev/null +++ b/crates/assists/src/handlers/ignore_test.rs | |||
@@ -0,0 +1,103 @@ | |||
1 | use syntax::{ | ||
2 | ast::{self, AttrsOwner}, | ||
3 | AstNode, AstToken, | ||
4 | }; | ||
5 | |||
6 | use crate::{utils::test_related_attribute, AssistContext, AssistId, AssistKind, Assists}; | ||
7 | |||
8 | // Assist: ignore_test | ||
9 | // | ||
10 | // Adds `#[ignore]` attribute to the test. | ||
11 | // | ||
12 | // ``` | ||
13 | // <|>#[test] | ||
14 | // fn arithmetics { | ||
15 | // assert_eq!(2 + 2, 5); | ||
16 | // } | ||
17 | // ``` | ||
18 | // -> | ||
19 | // ``` | ||
20 | // #[test] | ||
21 | // #[ignore] | ||
22 | // fn arithmetics { | ||
23 | // assert_eq!(2 + 2, 5); | ||
24 | // } | ||
25 | // ``` | ||
26 | pub(crate) fn ignore_test(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | ||
27 | let attr: ast::Attr = ctx.find_node_at_offset()?; | ||
28 | let func = attr.syntax().parent().and_then(ast::Fn::cast)?; | ||
29 | let attr = test_related_attribute(&func)?; | ||
30 | |||
31 | match has_ignore_attribute(&func) { | ||
32 | None => acc.add( | ||
33 | AssistId("ignore_test", AssistKind::None), | ||
34 | "Ignore this test", | ||
35 | attr.syntax().text_range(), | ||
36 | |builder| builder.insert(attr.syntax().text_range().end(), &format!("\n#[ignore]")), | ||
37 | ), | ||
38 | Some(ignore_attr) => acc.add( | ||
39 | AssistId("unignore_test", AssistKind::None), | ||
40 | "Re-enable this test", | ||
41 | ignore_attr.syntax().text_range(), | ||
42 | |builder| { | ||
43 | builder.delete(ignore_attr.syntax().text_range()); | ||
44 | let whitespace = ignore_attr | ||
45 | .syntax() | ||
46 | .next_sibling_or_token() | ||
47 | .and_then(|x| x.into_token()) | ||
48 | .and_then(ast::Whitespace::cast); | ||
49 | if let Some(whitespace) = whitespace { | ||
50 | builder.delete(whitespace.syntax().text_range()); | ||
51 | } | ||
52 | }, | ||
53 | ), | ||
54 | } | ||
55 | } | ||
56 | |||
57 | fn has_ignore_attribute(fn_def: &ast::Fn) -> Option<ast::Attr> { | ||
58 | fn_def.attrs().find_map(|attr| { | ||
59 | if attr.path()?.syntax().text() == "ignore" { | ||
60 | Some(attr) | ||
61 | } else { | ||
62 | None | ||
63 | } | ||
64 | }) | ||
65 | } | ||
66 | |||
67 | #[cfg(test)] | ||
68 | mod tests { | ||
69 | use super::ignore_test; | ||
70 | use crate::tests::check_assist; | ||
71 | |||
72 | #[test] | ||
73 | fn test_base_case() { | ||
74 | check_assist( | ||
75 | ignore_test, | ||
76 | r#" | ||
77 | #[test<|>] | ||
78 | fn test() {} | ||
79 | "#, | ||
80 | r#" | ||
81 | #[test] | ||
82 | #[ignore] | ||
83 | fn test() {} | ||
84 | "#, | ||
85 | ) | ||
86 | } | ||
87 | |||
88 | #[test] | ||
89 | fn test_unignore() { | ||
90 | check_assist( | ||
91 | ignore_test, | ||
92 | r#" | ||
93 | #[test<|>] | ||
94 | #[ignore] | ||
95 | fn test() {} | ||
96 | "#, | ||
97 | r#" | ||
98 | #[test] | ||
99 | fn test() {} | ||
100 | "#, | ||
101 | ) | ||
102 | } | ||
103 | } | ||
diff --git a/crates/assists/src/handlers/remove_dbg.rs b/crates/assists/src/handlers/remove_dbg.rs index 9731344b8..eae6367c1 100644 --- a/crates/assists/src/handlers/remove_dbg.rs +++ b/crates/assists/src/handlers/remove_dbg.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | use syntax::{ | 1 | use syntax::{ |
2 | ast::{self, AstNode}, | 2 | ast::{self, AstNode}, |
3 | SyntaxElement, SyntaxKind, TextRange, TextSize, T, | 3 | match_ast, SyntaxElement, SyntaxKind, TextRange, TextSize, T, |
4 | }; | 4 | }; |
5 | 5 | ||
6 | use crate::{AssistContext, AssistId, AssistKind, Assists}; | 6 | use crate::{AssistContext, AssistId, AssistKind, Assists}; |
@@ -49,12 +49,29 @@ fn adjusted_macro_contents(macro_call: &ast::MacroCall) -> Option<String> { | |||
49 | macro_text_with_brackets.len() - TextSize::of(')'), | 49 | macro_text_with_brackets.len() - TextSize::of(')'), |
50 | )); | 50 | )); |
51 | 51 | ||
52 | let is_leaf = macro_call.syntax().next_sibling().is_none(); | 52 | Some( |
53 | Some(if !is_leaf && needs_parentheses_around_macro_contents(contents) { | 53 | if !is_leaf_or_control_flow_expr(macro_call) |
54 | format!("({})", macro_text_in_brackets) | 54 | && needs_parentheses_around_macro_contents(contents) |
55 | } else { | 55 | { |
56 | macro_text_in_brackets.to_string() | 56 | format!("({})", macro_text_in_brackets) |
57 | }) | 57 | } else { |
58 | macro_text_in_brackets.to_string() | ||
59 | }, | ||
60 | ) | ||
61 | } | ||
62 | |||
63 | fn is_leaf_or_control_flow_expr(macro_call: &ast::MacroCall) -> bool { | ||
64 | macro_call.syntax().next_sibling().is_none() | ||
65 | || match macro_call.syntax().parent() { | ||
66 | Some(parent) => match_ast! { | ||
67 | match parent { | ||
68 | ast::Condition(_it) => true, | ||
69 | ast::MatchExpr(_it) => true, | ||
70 | _ => false, | ||
71 | } | ||
72 | }, | ||
73 | None => false, | ||
74 | } | ||
58 | } | 75 | } |
59 | 76 | ||
60 | /// Verifies that the given macro_call actually matches the given name | 77 | /// Verifies that the given macro_call actually matches the given name |
@@ -361,4 +378,44 @@ fn main() { | |||
361 | r#"let res = (foo..=bar).foo();"#, | 378 | r#"let res = (foo..=bar).foo();"#, |
362 | ); | 379 | ); |
363 | } | 380 | } |
381 | |||
382 | #[test] | ||
383 | fn test_remove_dbg_followed_by_block() { | ||
384 | check_assist( | ||
385 | remove_dbg, | ||
386 | r#"fn foo() { | ||
387 | if <|>dbg!(x || y) {} | ||
388 | }"#, | ||
389 | r#"fn foo() { | ||
390 | if x || y {} | ||
391 | }"#, | ||
392 | ); | ||
393 | check_assist( | ||
394 | remove_dbg, | ||
395 | r#"fn foo() { | ||
396 | while let foo = <|>dbg!(&x) {} | ||
397 | }"#, | ||
398 | r#"fn foo() { | ||
399 | while let foo = &x {} | ||
400 | }"#, | ||
401 | ); | ||
402 | check_assist( | ||
403 | remove_dbg, | ||
404 | r#"fn foo() { | ||
405 | if let foo = <|>dbg!(&x) {} | ||
406 | }"#, | ||
407 | r#"fn foo() { | ||
408 | if let foo = &x {} | ||
409 | }"#, | ||
410 | ); | ||
411 | check_assist( | ||
412 | remove_dbg, | ||
413 | r#"fn foo() { | ||
414 | match <|>dbg!(&x) {} | ||
415 | }"#, | ||
416 | r#"fn foo() { | ||
417 | match &x {} | ||
418 | }"#, | ||
419 | ); | ||
420 | } | ||
364 | } | 421 | } |
diff --git a/crates/assists/src/handlers/replace_derive_with_manual_impl.rs b/crates/assists/src/handlers/replace_derive_with_manual_impl.rs index 82625516c..453a6cebf 100644 --- a/crates/assists/src/handlers/replace_derive_with_manual_impl.rs +++ b/crates/assists/src/handlers/replace_derive_with_manual_impl.rs | |||
@@ -62,19 +62,21 @@ pub(crate) fn replace_derive_with_manual_impl( | |||
62 | let current_module = ctx.sema.scope(annotated_name.syntax()).module()?; | 62 | let current_module = ctx.sema.scope(annotated_name.syntax()).module()?; |
63 | let current_crate = current_module.krate(); | 63 | let current_crate = current_module.krate(); |
64 | 64 | ||
65 | let found_traits = imports_locator::find_imports(&ctx.sema, current_crate, trait_token.text()) | 65 | let found_traits = |
66 | .into_iter() | 66 | imports_locator::find_exact_imports(&ctx.sema, current_crate, trait_token.text()) |
67 | .filter_map(|candidate: either::Either<hir::ModuleDef, hir::MacroDef>| match candidate { | 67 | .filter_map( |
68 | either::Either::Left(hir::ModuleDef::Trait(trait_)) => Some(trait_), | 68 | |candidate: either::Either<hir::ModuleDef, hir::MacroDef>| match candidate { |
69 | _ => None, | 69 | either::Either::Left(hir::ModuleDef::Trait(trait_)) => Some(trait_), |
70 | }) | 70 | _ => None, |
71 | .flat_map(|trait_| { | 71 | }, |
72 | current_module | 72 | ) |
73 | .find_use_path(ctx.sema.db, hir::ModuleDef::Trait(trait_)) | 73 | .flat_map(|trait_| { |
74 | .as_ref() | 74 | current_module |
75 | .map(mod_path_to_ast) | 75 | .find_use_path(ctx.sema.db, hir::ModuleDef::Trait(trait_)) |
76 | .zip(Some(trait_)) | 76 | .as_ref() |
77 | }); | 77 | .map(mod_path_to_ast) |
78 | .zip(Some(trait_)) | ||
79 | }); | ||
78 | 80 | ||
79 | let mut no_traits_found = true; | 81 | let mut no_traits_found = true; |
80 | for (trait_path, trait_) in found_traits.inspect(|_| no_traits_found = false) { | 82 | for (trait_path, trait_) in found_traits.inspect(|_| no_traits_found = false) { |
diff --git a/crates/assists/src/handlers/replace_qualified_name_with_use.rs b/crates/assists/src/handlers/replace_qualified_name_with_use.rs index d7e1d9580..a66db9ae3 100644 --- a/crates/assists/src/handlers/replace_qualified_name_with_use.rs +++ b/crates/assists/src/handlers/replace_qualified_name_with_use.rs | |||
@@ -34,7 +34,7 @@ pub(crate) fn replace_qualified_name_with_use( | |||
34 | } | 34 | } |
35 | 35 | ||
36 | let target = path.syntax().text_range(); | 36 | let target = path.syntax().text_range(); |
37 | let scope = ImportScope::find_insert_use_container(path.syntax(), ctx)?; | 37 | let scope = ImportScope::find_insert_use_container(path.syntax(), &ctx.sema)?; |
38 | let syntax = scope.as_syntax_node(); | 38 | let syntax = scope.as_syntax_node(); |
39 | acc.add( | 39 | acc.add( |
40 | AssistId("replace_qualified_name_with_use", AssistKind::RefactorRewrite), | 40 | AssistId("replace_qualified_name_with_use", AssistKind::RefactorRewrite), |
diff --git a/crates/assists/src/handlers/unwrap_block.rs b/crates/assists/src/handlers/unwrap_block.rs index 36ef871b9..676db7137 100644 --- a/crates/assists/src/handlers/unwrap_block.rs +++ b/crates/assists/src/handlers/unwrap_block.rs | |||
@@ -3,7 +3,7 @@ use syntax::{ | |||
3 | self, | 3 | self, |
4 | edit::{AstNodeEdit, IndentLevel}, | 4 | edit::{AstNodeEdit, IndentLevel}, |
5 | }, | 5 | }, |
6 | AstNode, TextRange, T, | 6 | AstNode, SyntaxKind, TextRange, T, |
7 | }; | 7 | }; |
8 | 8 | ||
9 | use crate::{utils::unwrap_trivial_block, AssistContext, AssistId, AssistKind, Assists}; | 9 | use crate::{utils::unwrap_trivial_block, AssistContext, AssistId, AssistKind, Assists}; |
@@ -31,11 +31,21 @@ pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext) -> Option<()> | |||
31 | 31 | ||
32 | let l_curly_token = ctx.find_token_syntax_at_offset(T!['{'])?; | 32 | let l_curly_token = ctx.find_token_syntax_at_offset(T!['{'])?; |
33 | let mut block = ast::BlockExpr::cast(l_curly_token.parent())?; | 33 | let mut block = ast::BlockExpr::cast(l_curly_token.parent())?; |
34 | let target = block.syntax().text_range(); | ||
34 | let mut parent = block.syntax().parent()?; | 35 | let mut parent = block.syntax().parent()?; |
35 | if ast::MatchArm::can_cast(parent.kind()) { | 36 | if ast::MatchArm::can_cast(parent.kind()) { |
36 | parent = parent.ancestors().find(|it| ast::MatchExpr::can_cast(it.kind()))? | 37 | parent = parent.ancestors().find(|it| ast::MatchExpr::can_cast(it.kind()))? |
37 | } | 38 | } |
38 | 39 | ||
40 | if matches!(parent.kind(), SyntaxKind::BLOCK_EXPR | SyntaxKind::EXPR_STMT) { | ||
41 | return acc.add(assist_id, assist_label, target, |builder| { | ||
42 | builder.replace( | ||
43 | block.syntax().text_range(), | ||
44 | update_expr_string(block.to_string(), &[' ', '{', '\n']), | ||
45 | ); | ||
46 | }); | ||
47 | } | ||
48 | |||
39 | let parent = ast::Expr::cast(parent)?; | 49 | let parent = ast::Expr::cast(parent)?; |
40 | 50 | ||
41 | match parent.clone() { | 51 | match parent.clone() { |
@@ -48,7 +58,6 @@ pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext) -> Option<()> | |||
48 | // For `else if` blocks | 58 | // For `else if` blocks |
49 | let ancestor_then_branch = ancestor.then_branch()?; | 59 | let ancestor_then_branch = ancestor.then_branch()?; |
50 | 60 | ||
51 | let target = then_branch.syntax().text_range(); | ||
52 | return acc.add(assist_id, assist_label, target, |edit| { | 61 | return acc.add(assist_id, assist_label, target, |edit| { |
53 | let range_to_del_else_if = TextRange::new( | 62 | let range_to_del_else_if = TextRange::new( |
54 | ancestor_then_branch.syntax().text_range().end(), | 63 | ancestor_then_branch.syntax().text_range().end(), |
@@ -68,7 +77,6 @@ pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext) -> Option<()> | |||
68 | }); | 77 | }); |
69 | } | 78 | } |
70 | } else { | 79 | } else { |
71 | let target = block.syntax().text_range(); | ||
72 | return acc.add(assist_id, assist_label, target, |edit| { | 80 | return acc.add(assist_id, assist_label, target, |edit| { |
73 | let range_to_del = TextRange::new( | 81 | let range_to_del = TextRange::new( |
74 | then_branch.syntax().text_range().end(), | 82 | then_branch.syntax().text_range().end(), |
@@ -84,7 +92,6 @@ pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext) -> Option<()> | |||
84 | }; | 92 | }; |
85 | 93 | ||
86 | let unwrapped = unwrap_trivial_block(block); | 94 | let unwrapped = unwrap_trivial_block(block); |
87 | let target = unwrapped.syntax().text_range(); | ||
88 | acc.add(assist_id, assist_label, target, |builder| { | 95 | acc.add(assist_id, assist_label, target, |builder| { |
89 | builder.replace( | 96 | builder.replace( |
90 | parent.syntax().text_range(), | 97 | parent.syntax().text_range(), |
@@ -112,31 +119,89 @@ mod tests { | |||
112 | use super::*; | 119 | use super::*; |
113 | 120 | ||
114 | #[test] | 121 | #[test] |
122 | fn unwrap_tail_expr_block() { | ||
123 | check_assist( | ||
124 | unwrap_block, | ||
125 | r#" | ||
126 | fn main() { | ||
127 | <|>{ | ||
128 | 92 | ||
129 | } | ||
130 | } | ||
131 | "#, | ||
132 | r#" | ||
133 | fn main() { | ||
134 | 92 | ||
135 | } | ||
136 | "#, | ||
137 | ) | ||
138 | } | ||
139 | |||
140 | #[test] | ||
141 | fn unwrap_stmt_expr_block() { | ||
142 | check_assist( | ||
143 | unwrap_block, | ||
144 | r#" | ||
145 | fn main() { | ||
146 | <|>{ | ||
147 | 92; | ||
148 | } | ||
149 | () | ||
150 | } | ||
151 | "#, | ||
152 | r#" | ||
153 | fn main() { | ||
154 | 92; | ||
155 | () | ||
156 | } | ||
157 | "#, | ||
158 | ); | ||
159 | // Pedantically, we should add an `;` here... | ||
160 | check_assist( | ||
161 | unwrap_block, | ||
162 | r#" | ||
163 | fn main() { | ||
164 | <|>{ | ||
165 | 92 | ||
166 | } | ||
167 | () | ||
168 | } | ||
169 | "#, | ||
170 | r#" | ||
171 | fn main() { | ||
172 | 92 | ||
173 | () | ||
174 | } | ||
175 | "#, | ||
176 | ); | ||
177 | } | ||
178 | |||
179 | #[test] | ||
115 | fn simple_if() { | 180 | fn simple_if() { |
116 | check_assist( | 181 | check_assist( |
117 | unwrap_block, | 182 | unwrap_block, |
118 | r#" | 183 | r#" |
119 | fn main() { | 184 | fn main() { |
120 | bar(); | 185 | bar(); |
121 | if true {<|> | 186 | if true {<|> |
122 | foo(); | 187 | foo(); |
123 | 188 | ||
124 | //comment | 189 | //comment |
125 | bar(); | 190 | bar(); |
126 | } else { | 191 | } else { |
127 | println!("bar"); | 192 | println!("bar"); |
128 | } | 193 | } |
129 | } | 194 | } |
130 | "#, | 195 | "#, |
131 | r#" | 196 | r#" |
132 | fn main() { | 197 | fn main() { |
133 | bar(); | 198 | bar(); |
134 | foo(); | 199 | foo(); |
135 | 200 | ||
136 | //comment | 201 | //comment |
137 | bar(); | 202 | bar(); |
138 | } | 203 | } |
139 | "#, | 204 | "#, |
140 | ); | 205 | ); |
141 | } | 206 | } |
142 | 207 | ||
@@ -145,30 +210,30 @@ mod tests { | |||
145 | check_assist( | 210 | check_assist( |
146 | unwrap_block, | 211 | unwrap_block, |
147 | r#" | 212 | r#" |
148 | fn main() { | 213 | fn main() { |
149 | bar(); | 214 | bar(); |
150 | if true { | 215 | if true { |
151 | foo(); | 216 | foo(); |
152 | 217 | ||
153 | //comment | 218 | //comment |
154 | bar(); | 219 | bar(); |
155 | } else {<|> | 220 | } else {<|> |
156 | println!("bar"); | 221 | println!("bar"); |
157 | } | 222 | } |
158 | } | 223 | } |
159 | "#, | 224 | "#, |
160 | r#" | 225 | r#" |
161 | fn main() { | 226 | fn main() { |
162 | bar(); | 227 | bar(); |
163 | if true { | 228 | if true { |
164 | foo(); | 229 | foo(); |
165 | 230 | ||
166 | //comment | 231 | //comment |
167 | bar(); | 232 | bar(); |
168 | } | 233 | } |
169 | println!("bar"); | 234 | println!("bar"); |
170 | } | 235 | } |
171 | "#, | 236 | "#, |
172 | ); | 237 | ); |
173 | } | 238 | } |
174 | 239 | ||
@@ -177,32 +242,32 @@ mod tests { | |||
177 | check_assist( | 242 | check_assist( |
178 | unwrap_block, | 243 | unwrap_block, |
179 | r#" | 244 | r#" |
180 | fn main() { | 245 | fn main() { |
181 | //bar(); | 246 | //bar(); |
182 | if true { | 247 | if true { |
183 | println!("true"); | 248 | println!("true"); |
184 | 249 | ||
185 | //comment | 250 | //comment |
186 | //bar(); | 251 | //bar(); |
187 | } else if false {<|> | 252 | } else if false {<|> |
188 | println!("bar"); | 253 | println!("bar"); |
189 | } else { | 254 | } else { |
190 | println!("foo"); | 255 | println!("foo"); |
191 | } | 256 | } |
192 | } | 257 | } |
193 | "#, | 258 | "#, |
194 | r#" | 259 | r#" |
195 | fn main() { | 260 | fn main() { |
196 | //bar(); | 261 | //bar(); |
197 | if true { | 262 | if true { |
198 | println!("true"); | 263 | println!("true"); |
199 | 264 | ||
200 | //comment | 265 | //comment |
201 | //bar(); | 266 | //bar(); |
202 | } | 267 | } |
203 | println!("bar"); | 268 | println!("bar"); |
204 | } | 269 | } |
205 | "#, | 270 | "#, |
206 | ); | 271 | ); |
207 | } | 272 | } |
208 | 273 | ||
@@ -211,34 +276,34 @@ mod tests { | |||
211 | check_assist( | 276 | check_assist( |
212 | unwrap_block, | 277 | unwrap_block, |
213 | r#" | 278 | r#" |
214 | fn main() { | 279 | fn main() { |
215 | //bar(); | 280 | //bar(); |
216 | if true { | 281 | if true { |
217 | println!("true"); | 282 | println!("true"); |
218 | 283 | ||
219 | //comment | 284 | //comment |
220 | //bar(); | 285 | //bar(); |
221 | } else if false { | 286 | } else if false { |
222 | println!("bar"); | 287 | println!("bar"); |
223 | } else if true {<|> | 288 | } else if true {<|> |
224 | println!("foo"); | 289 | println!("foo"); |
225 | } | 290 | } |
226 | } | 291 | } |
227 | "#, | 292 | "#, |
228 | r#" | 293 | r#" |
229 | fn main() { | 294 | fn main() { |
230 | //bar(); | 295 | //bar(); |
231 | if true { | 296 | if true { |
232 | println!("true"); | 297 | println!("true"); |
233 | 298 | ||
234 | //comment | 299 | //comment |
235 | //bar(); | 300 | //bar(); |
236 | } else if false { | 301 | } else if false { |
237 | println!("bar"); | 302 | println!("bar"); |
238 | } | 303 | } |
239 | println!("foo"); | 304 | println!("foo"); |
240 | } | 305 | } |
241 | "#, | 306 | "#, |
242 | ); | 307 | ); |
243 | } | 308 | } |
244 | 309 | ||
@@ -247,38 +312,38 @@ mod tests { | |||
247 | check_assist( | 312 | check_assist( |
248 | unwrap_block, | 313 | unwrap_block, |
249 | r#" | 314 | r#" |
250 | fn main() { | 315 | fn main() { |
251 | //bar(); | 316 | //bar(); |
252 | if true { | 317 | if true { |
253 | println!("true"); | 318 | println!("true"); |
254 | 319 | ||
255 | //comment | 320 | //comment |
256 | //bar(); | 321 | //bar(); |
257 | } else if false { | 322 | } else if false { |
258 | println!("bar"); | 323 | println!("bar"); |
259 | } else if true { | 324 | } else if true { |
260 | println!("foo"); | 325 | println!("foo"); |
261 | } else {<|> | 326 | } else {<|> |
262 | println!("else"); | 327 | println!("else"); |
263 | } | 328 | } |
264 | } | 329 | } |
265 | "#, | 330 | "#, |
266 | r#" | 331 | r#" |
267 | fn main() { | 332 | fn main() { |
268 | //bar(); | 333 | //bar(); |
269 | if true { | 334 | if true { |
270 | println!("true"); | 335 | println!("true"); |
271 | 336 | ||
272 | //comment | 337 | //comment |
273 | //bar(); | 338 | //bar(); |
274 | } else if false { | 339 | } else if false { |
275 | println!("bar"); | 340 | println!("bar"); |
276 | } else if true { | 341 | } else if true { |
277 | println!("foo"); | 342 | println!("foo"); |
278 | } | 343 | } |
279 | println!("else"); | 344 | println!("else"); |
280 | } | 345 | } |
281 | "#, | 346 | "#, |
282 | ); | 347 | ); |
283 | } | 348 | } |
284 | 349 | ||
@@ -287,36 +352,36 @@ mod tests { | |||
287 | check_assist( | 352 | check_assist( |
288 | unwrap_block, | 353 | unwrap_block, |
289 | r#" | 354 | r#" |
290 | fn main() { | 355 | fn main() { |
291 | //bar(); | 356 | //bar(); |
292 | if true { | 357 | if true { |
293 | println!("true"); | 358 | println!("true"); |
294 | 359 | ||
295 | //comment | 360 | //comment |
296 | //bar(); | 361 | //bar(); |
297 | } else if false { | 362 | } else if false { |
298 | println!("bar"); | 363 | println!("bar"); |
299 | } else if true {<|> | 364 | } else if true {<|> |
300 | println!("foo"); | 365 | println!("foo"); |
301 | } else { | 366 | } else { |
302 | println!("else"); | 367 | println!("else"); |
303 | } | 368 | } |
304 | } | 369 | } |
305 | "#, | 370 | "#, |
306 | r#" | 371 | r#" |
307 | fn main() { | 372 | fn main() { |
308 | //bar(); | 373 | //bar(); |
309 | if true { | 374 | if true { |
310 | println!("true"); | 375 | println!("true"); |
311 | 376 | ||
312 | //comment | 377 | //comment |
313 | //bar(); | 378 | //bar(); |
314 | } else if false { | 379 | } else if false { |
315 | println!("bar"); | 380 | println!("bar"); |
316 | } | 381 | } |
317 | println!("foo"); | 382 | println!("foo"); |
318 | } | 383 | } |
319 | "#, | 384 | "#, |
320 | ); | 385 | ); |
321 | } | 386 | } |
322 | 387 | ||
@@ -325,18 +390,18 @@ mod tests { | |||
325 | check_assist_not_applicable( | 390 | check_assist_not_applicable( |
326 | unwrap_block, | 391 | unwrap_block, |
327 | r#" | 392 | r#" |
328 | fn main() { | 393 | fn main() { |
329 | bar();<|> | 394 | bar();<|> |
330 | if true { | 395 | if true { |
331 | foo(); | 396 | foo(); |
332 | 397 | ||
333 | //comment | 398 | //comment |
334 | bar(); | 399 | bar(); |
335 | } else { | 400 | } else { |
336 | println!("bar"); | 401 | println!("bar"); |
337 | } | 402 | } |
338 | } | 403 | } |
339 | "#, | 404 | "#, |
340 | ); | 405 | ); |
341 | } | 406 | } |
342 | 407 | ||
@@ -345,31 +410,31 @@ mod tests { | |||
345 | check_assist( | 410 | check_assist( |
346 | unwrap_block, | 411 | unwrap_block, |
347 | r#" | 412 | r#" |
348 | fn main() { | 413 | fn main() { |
349 | for i in 0..5 {<|> | 414 | for i in 0..5 {<|> |
350 | if true { | 415 | if true { |
351 | foo(); | 416 | foo(); |
352 | 417 | ||
353 | //comment | 418 | //comment |
354 | bar(); | 419 | bar(); |
355 | } else { | 420 | } else { |
356 | println!("bar"); | 421 | println!("bar"); |
357 | } | 422 | } |
358 | } | 423 | } |
359 | } | 424 | } |
360 | "#, | 425 | "#, |
361 | r#" | 426 | r#" |
362 | fn main() { | 427 | fn main() { |
363 | if true { | 428 | if true { |
364 | foo(); | 429 | foo(); |
365 | 430 | ||
366 | //comment | 431 | //comment |
367 | bar(); | 432 | bar(); |
368 | } else { | 433 | } else { |
369 | println!("bar"); | 434 | println!("bar"); |
370 | } | 435 | } |
371 | } | 436 | } |
372 | "#, | 437 | "#, |
373 | ); | 438 | ); |
374 | } | 439 | } |
375 | 440 | ||
@@ -378,29 +443,29 @@ mod tests { | |||
378 | check_assist( | 443 | check_assist( |
379 | unwrap_block, | 444 | unwrap_block, |
380 | r#" | 445 | r#" |
381 | fn main() { | 446 | fn main() { |
382 | for i in 0..5 { | 447 | for i in 0..5 { |
383 | if true {<|> | 448 | if true {<|> |
384 | foo(); | 449 | foo(); |
385 | 450 | ||
386 | //comment | 451 | //comment |
387 | bar(); | 452 | bar(); |
388 | } else { | 453 | } else { |
389 | println!("bar"); | 454 | println!("bar"); |
390 | } | 455 | } |
391 | } | 456 | } |
392 | } | 457 | } |
393 | "#, | 458 | "#, |
394 | r#" | 459 | r#" |
395 | fn main() { | 460 | fn main() { |
396 | for i in 0..5 { | 461 | for i in 0..5 { |
397 | foo(); | 462 | foo(); |
398 | 463 | ||
399 | //comment | 464 | //comment |
400 | bar(); | 465 | bar(); |
401 | } | 466 | } |
402 | } | 467 | } |
403 | "#, | 468 | "#, |
404 | ); | 469 | ); |
405 | } | 470 | } |
406 | 471 | ||
@@ -409,31 +474,31 @@ mod tests { | |||
409 | check_assist( | 474 | check_assist( |
410 | unwrap_block, | 475 | unwrap_block, |
411 | r#" | 476 | r#" |
412 | fn main() { | 477 | fn main() { |
413 | loop {<|> | 478 | loop {<|> |
414 | if true { | 479 | if true { |
415 | foo(); | 480 | foo(); |
416 | 481 | ||
417 | //comment | 482 | //comment |
418 | bar(); | 483 | bar(); |
419 | } else { | 484 | } else { |
420 | println!("bar"); | 485 | println!("bar"); |
421 | } | 486 | } |
422 | } | 487 | } |
423 | } | 488 | } |
424 | "#, | 489 | "#, |
425 | r#" | 490 | r#" |
426 | fn main() { | 491 | fn main() { |
427 | if true { | 492 | if true { |
428 | foo(); | 493 | foo(); |
429 | 494 | ||
430 | //comment | 495 | //comment |
431 | bar(); | 496 | bar(); |
432 | } else { | 497 | } else { |
433 | println!("bar"); | 498 | println!("bar"); |
434 | } | 499 | } |
435 | } | 500 | } |
436 | "#, | 501 | "#, |
437 | ); | 502 | ); |
438 | } | 503 | } |
439 | 504 | ||
@@ -442,31 +507,31 @@ mod tests { | |||
442 | check_assist( | 507 | check_assist( |
443 | unwrap_block, | 508 | unwrap_block, |
444 | r#" | 509 | r#" |
445 | fn main() { | 510 | fn main() { |
446 | while true {<|> | 511 | while true {<|> |
447 | if true { | 512 | if true { |
448 | foo(); | 513 | foo(); |
449 | 514 | ||
450 | //comment | 515 | //comment |
451 | bar(); | 516 | bar(); |
452 | } else { | 517 | } else { |
453 | println!("bar"); | 518 | println!("bar"); |
454 | } | 519 | } |
455 | } | 520 | } |
456 | } | 521 | } |
457 | "#, | 522 | "#, |
458 | r#" | 523 | r#" |
459 | fn main() { | 524 | fn main() { |
460 | if true { | 525 | if true { |
461 | foo(); | 526 | foo(); |
462 | 527 | ||
463 | //comment | 528 | //comment |
464 | bar(); | 529 | bar(); |
465 | } else { | 530 | } else { |
466 | println!("bar"); | 531 | println!("bar"); |
467 | } | 532 | } |
468 | } | 533 | } |
469 | "#, | 534 | "#, |
470 | ); | 535 | ); |
471 | } | 536 | } |
472 | 537 | ||
@@ -499,19 +564,19 @@ fn main() { | |||
499 | check_assist_not_applicable( | 564 | check_assist_not_applicable( |
500 | unwrap_block, | 565 | unwrap_block, |
501 | r#" | 566 | r#" |
502 | fn main() { | 567 | fn main() { |
503 | while true { | 568 | while true { |
504 | if true { | 569 | if true { |
505 | foo();<|> | 570 | foo();<|> |
506 | 571 | ||
507 | //comment | 572 | //comment |
508 | bar(); | 573 | bar(); |
509 | } else { | 574 | } else { |
510 | println!("bar"); | 575 | println!("bar"); |
511 | } | 576 | } |
512 | } | 577 | } |
513 | } | 578 | } |
514 | "#, | 579 | "#, |
515 | ); | 580 | ); |
516 | } | 581 | } |
517 | } | 582 | } |
diff --git a/crates/assists/src/lib.rs b/crates/assists/src/lib.rs index e8d81b33d..17e9312db 100644 --- a/crates/assists/src/lib.rs +++ b/crates/assists/src/lib.rs | |||
@@ -141,6 +141,7 @@ mod handlers { | |||
141 | mod generate_function; | 141 | mod generate_function; |
142 | mod generate_impl; | 142 | mod generate_impl; |
143 | mod generate_new; | 143 | mod generate_new; |
144 | mod ignore_test; | ||
144 | mod infer_function_return_type; | 145 | mod infer_function_return_type; |
145 | mod inline_local_variable; | 146 | mod inline_local_variable; |
146 | mod introduce_named_lifetime; | 147 | mod introduce_named_lifetime; |
@@ -189,6 +190,7 @@ mod handlers { | |||
189 | generate_function::generate_function, | 190 | generate_function::generate_function, |
190 | generate_impl::generate_impl, | 191 | generate_impl::generate_impl, |
191 | generate_new::generate_new, | 192 | generate_new::generate_new, |
193 | ignore_test::ignore_test, | ||
192 | infer_function_return_type::infer_function_return_type, | 194 | infer_function_return_type::infer_function_return_type, |
193 | inline_local_variable::inline_local_variable, | 195 | inline_local_variable::inline_local_variable, |
194 | introduce_named_lifetime::introduce_named_lifetime, | 196 | introduce_named_lifetime::introduce_named_lifetime, |
diff --git a/crates/assists/src/tests/generated.rs b/crates/assists/src/tests/generated.rs index dbf4f21aa..5a9d1a01b 100644 --- a/crates/assists/src/tests/generated.rs +++ b/crates/assists/src/tests/generated.rs | |||
@@ -474,6 +474,26 @@ impl<T: Clone> Ctx<T> { | |||
474 | } | 474 | } |
475 | 475 | ||
476 | #[test] | 476 | #[test] |
477 | fn doctest_ignore_test() { | ||
478 | check_doc_test( | ||
479 | "ignore_test", | ||
480 | r#####" | ||
481 | <|>#[test] | ||
482 | fn arithmetics { | ||
483 | assert_eq!(2 + 2, 5); | ||
484 | } | ||
485 | "#####, | ||
486 | r#####" | ||
487 | #[test] | ||
488 | #[ignore] | ||
489 | fn arithmetics { | ||
490 | assert_eq!(2 + 2, 5); | ||
491 | } | ||
492 | "#####, | ||
493 | ) | ||
494 | } | ||
495 | |||
496 | #[test] | ||
477 | fn doctest_infer_function_return_type() { | 497 | fn doctest_infer_function_return_type() { |
478 | check_doc_test( | 498 | check_doc_test( |
479 | "infer_function_return_type", | 499 | "infer_function_return_type", |
diff --git a/crates/assists/src/utils.rs b/crates/assists/src/utils.rs index 7bd338e99..66c0cdd5f 100644 --- a/crates/assists/src/utils.rs +++ b/crates/assists/src/utils.rs | |||
@@ -9,6 +9,7 @@ use ide_db::RootDatabase; | |||
9 | use itertools::Itertools; | 9 | use itertools::Itertools; |
10 | use syntax::{ | 10 | use syntax::{ |
11 | ast::edit::AstNodeEdit, | 11 | ast::edit::AstNodeEdit, |
12 | ast::AttrsOwner, | ||
12 | ast::NameOwner, | 13 | ast::NameOwner, |
13 | ast::{self, edit, make, ArgListOwner}, | 14 | ast::{self, edit, make, ArgListOwner}, |
14 | AstNode, Direction, | 15 | AstNode, Direction, |
@@ -21,8 +22,7 @@ use crate::{ | |||
21 | ast_transform::{self, AstTransform, QualifyPaths, SubstituteTypeParams}, | 22 | ast_transform::{self, AstTransform, QualifyPaths, SubstituteTypeParams}, |
22 | }; | 23 | }; |
23 | 24 | ||
24 | pub use insert_use::MergeBehaviour; | 25 | pub use insert_use::{insert_use, ImportScope, MergeBehaviour}; |
25 | pub(crate) use insert_use::{insert_use, ImportScope}; | ||
26 | 26 | ||
27 | pub fn mod_path_to_ast(path: &hir::ModPath) -> ast::Path { | 27 | pub fn mod_path_to_ast(path: &hir::ModPath) -> ast::Path { |
28 | let mut segments = Vec::new(); | 28 | let mut segments = Vec::new(); |
@@ -82,6 +82,23 @@ pub fn extract_trivial_expression(block: &ast::BlockExpr) -> Option<ast::Expr> { | |||
82 | None | 82 | None |
83 | } | 83 | } |
84 | 84 | ||
85 | /// This is a method with a heuristics to support test methods annotated with custom test annotations, such as | ||
86 | /// `#[test_case(...)]`, `#[tokio::test]` and similar. | ||
87 | /// Also a regular `#[test]` annotation is supported. | ||
88 | /// | ||
89 | /// It may produce false positives, for example, `#[wasm_bindgen_test]` requires a different command to run the test, | ||
90 | /// but it's better than not to have the runnables for the tests at all. | ||
91 | pub fn test_related_attribute(fn_def: &ast::Fn) -> Option<ast::Attr> { | ||
92 | fn_def.attrs().find_map(|attr| { | ||
93 | let path = attr.path()?; | ||
94 | if path.syntax().text().to_string().contains("test") { | ||
95 | Some(attr) | ||
96 | } else { | ||
97 | None | ||
98 | } | ||
99 | }) | ||
100 | } | ||
101 | |||
85 | #[derive(Copy, Clone, PartialEq)] | 102 | #[derive(Copy, Clone, PartialEq)] |
86 | pub enum DefaultMethods { | 103 | pub enum DefaultMethods { |
87 | Only, | 104 | Only, |
diff --git a/crates/assists/src/utils/import_assets.rs b/crates/assists/src/utils/import_assets.rs index f47edbb76..ff5c0e78e 100644 --- a/crates/assists/src/utils/import_assets.rs +++ b/crates/assists/src/utils/import_assets.rs | |||
@@ -179,21 +179,25 @@ impl ImportAssets { | |||
179 | } | 179 | } |
180 | }; | 180 | }; |
181 | 181 | ||
182 | let mut res = imports_locator::find_imports(sema, current_crate, &self.get_search_query()) | 182 | let mut res = |
183 | .into_iter() | 183 | imports_locator::find_exact_imports(sema, current_crate, &self.get_search_query()) |
184 | .filter_map(filter) | 184 | .filter_map(filter) |
185 | .filter_map(|candidate| { | 185 | .filter_map(|candidate| { |
186 | let item: hir::ItemInNs = candidate.either(Into::into, Into::into); | 186 | let item: hir::ItemInNs = candidate.either(Into::into, Into::into); |
187 | if let Some(prefix_kind) = prefixed { | 187 | if let Some(prefix_kind) = prefixed { |
188 | self.module_with_name_to_import.find_use_path_prefixed(db, item, prefix_kind) | 188 | self.module_with_name_to_import.find_use_path_prefixed( |
189 | } else { | 189 | db, |
190 | self.module_with_name_to_import.find_use_path(db, item) | 190 | item, |
191 | } | 191 | prefix_kind, |
192 | .map(|path| (path, item)) | 192 | ) |
193 | }) | 193 | } else { |
194 | .filter(|(use_path, _)| !use_path.segments.is_empty()) | 194 | self.module_with_name_to_import.find_use_path(db, item) |
195 | .take(20) | 195 | } |
196 | .collect::<Vec<_>>(); | 196 | .map(|path| (path, item)) |
197 | }) | ||
198 | .filter(|(use_path, _)| use_path.len() > 1) | ||
199 | .take(20) | ||
200 | .collect::<Vec<_>>(); | ||
197 | res.sort_by_key(|(path, _)| path.clone()); | 201 | res.sort_by_key(|(path, _)| path.clone()); |
198 | res | 202 | res |
199 | } | 203 | } |
diff --git a/crates/assists/src/utils/insert_use.rs b/crates/assists/src/utils/insert_use.rs index af3fc96b6..423782a0e 100644 --- a/crates/assists/src/utils/insert_use.rs +++ b/crates/assists/src/utils/insert_use.rs | |||
@@ -1,6 +1,8 @@ | |||
1 | //! Handle syntactic aspects of inserting a new `use`. | 1 | //! Handle syntactic aspects of inserting a new `use`. |
2 | use std::{cmp::Ordering, iter::successors}; | 2 | use std::{cmp::Ordering, iter::successors}; |
3 | 3 | ||
4 | use hir::Semantics; | ||
5 | use ide_db::RootDatabase; | ||
4 | use itertools::{EitherOrBoth, Itertools}; | 6 | use itertools::{EitherOrBoth, Itertools}; |
5 | use syntax::{ | 7 | use syntax::{ |
6 | algo::SyntaxRewriter, | 8 | algo::SyntaxRewriter, |
@@ -13,8 +15,8 @@ use syntax::{ | |||
13 | }; | 15 | }; |
14 | use test_utils::mark; | 16 | use test_utils::mark; |
15 | 17 | ||
16 | #[derive(Debug)] | 18 | #[derive(Debug, Clone)] |
17 | pub(crate) enum ImportScope { | 19 | pub enum ImportScope { |
18 | File(ast::SourceFile), | 20 | File(ast::SourceFile), |
19 | Module(ast::ItemList), | 21 | Module(ast::ItemList), |
20 | } | 22 | } |
@@ -31,14 +33,14 @@ impl ImportScope { | |||
31 | } | 33 | } |
32 | 34 | ||
33 | /// Determines the containing syntax node in which to insert a `use` statement affecting `position`. | 35 | /// Determines the containing syntax node in which to insert a `use` statement affecting `position`. |
34 | pub(crate) fn find_insert_use_container( | 36 | pub fn find_insert_use_container( |
35 | position: &SyntaxNode, | 37 | position: &SyntaxNode, |
36 | ctx: &crate::assist_context::AssistContext, | 38 | sema: &Semantics<'_, RootDatabase>, |
37 | ) -> Option<Self> { | 39 | ) -> Option<Self> { |
38 | ctx.sema.ancestors_with_macros(position.clone()).find_map(Self::from) | 40 | sema.ancestors_with_macros(position.clone()).find_map(Self::from) |
39 | } | 41 | } |
40 | 42 | ||
41 | pub(crate) fn as_syntax_node(&self) -> &SyntaxNode { | 43 | pub fn as_syntax_node(&self) -> &SyntaxNode { |
42 | match self { | 44 | match self { |
43 | ImportScope::File(file) => file.syntax(), | 45 | ImportScope::File(file) => file.syntax(), |
44 | ImportScope::Module(item_list) => item_list.syntax(), | 46 | ImportScope::Module(item_list) => item_list.syntax(), |
@@ -88,7 +90,7 @@ fn is_inner_comment(token: SyntaxToken) -> bool { | |||
88 | } | 90 | } |
89 | 91 | ||
90 | /// Insert an import path into the given file/node. A `merge` value of none indicates that no import merging is allowed to occur. | 92 | /// Insert an import path into the given file/node. A `merge` value of none indicates that no import merging is allowed to occur. |
91 | pub(crate) fn insert_use<'a>( | 93 | pub fn insert_use<'a>( |
92 | scope: &ImportScope, | 94 | scope: &ImportScope, |
93 | path: ast::Path, | 95 | path: ast::Path, |
94 | merge: Option<MergeBehaviour>, | 96 | merge: Option<MergeBehaviour>, |
diff --git a/crates/base_db/src/input.rs b/crates/base_db/src/input.rs index 31907ed98..98ba372ad 100644 --- a/crates/base_db/src/input.rs +++ b/crates/base_db/src/input.rs | |||
@@ -225,7 +225,10 @@ impl CrateGraph { | |||
225 | to: CrateId, | 225 | to: CrateId, |
226 | ) -> Result<(), CyclicDependenciesError> { | 226 | ) -> Result<(), CyclicDependenciesError> { |
227 | if self.dfs_find(from, to, &mut FxHashSet::default()) { | 227 | if self.dfs_find(from, to, &mut FxHashSet::default()) { |
228 | return Err(CyclicDependenciesError); | 228 | return Err(CyclicDependenciesError { |
229 | from: (from, self[from].display_name.clone()), | ||
230 | to: (to, self[to].display_name.clone()), | ||
231 | }); | ||
229 | } | 232 | } |
230 | self.arena.get_mut(&from).unwrap().add_dep(name, to); | 233 | self.arena.get_mut(&from).unwrap().add_dep(name, to); |
231 | Ok(()) | 234 | Ok(()) |
@@ -421,7 +424,20 @@ impl fmt::Display for ParseEditionError { | |||
421 | impl std::error::Error for ParseEditionError {} | 424 | impl std::error::Error for ParseEditionError {} |
422 | 425 | ||
423 | #[derive(Debug)] | 426 | #[derive(Debug)] |
424 | pub struct CyclicDependenciesError; | 427 | pub struct CyclicDependenciesError { |
428 | from: (CrateId, Option<CrateDisplayName>), | ||
429 | to: (CrateId, Option<CrateDisplayName>), | ||
430 | } | ||
431 | |||
432 | impl fmt::Display for CyclicDependenciesError { | ||
433 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
434 | let render = |(id, name): &(CrateId, Option<CrateDisplayName>)| match name { | ||
435 | Some(it) => format!("{}({:?})", it, id), | ||
436 | None => format!("{:?}", id), | ||
437 | }; | ||
438 | write!(f, "cyclic deps: {} -> {}", render(&self.from), render(&self.to)) | ||
439 | } | ||
440 | } | ||
425 | 441 | ||
426 | #[cfg(test)] | 442 | #[cfg(test)] |
427 | mod tests { | 443 | mod tests { |
diff --git a/crates/cfg/src/lib.rs b/crates/cfg/src/lib.rs index d0e08cf5f..d88ecf8b0 100644 --- a/crates/cfg/src/lib.rs +++ b/crates/cfg/src/lib.rs | |||
@@ -41,12 +41,6 @@ impl CfgOptions { | |||
41 | self.enabled.insert(CfgAtom::KeyValue { key, value }); | 41 | self.enabled.insert(CfgAtom::KeyValue { key, value }); |
42 | } | 42 | } |
43 | 43 | ||
44 | pub fn append(&mut self, other: &CfgOptions) { | ||
45 | for atom in &other.enabled { | ||
46 | self.enabled.insert(atom.clone()); | ||
47 | } | ||
48 | } | ||
49 | |||
50 | pub fn apply_diff(&mut self, diff: CfgDiff) { | 44 | pub fn apply_diff(&mut self, diff: CfgDiff) { |
51 | for atom in diff.enable { | 45 | for atom in diff.enable { |
52 | self.enabled.insert(atom); | 46 | self.enabled.insert(atom); |
diff --git a/crates/completion/Cargo.toml b/crates/completion/Cargo.toml index 3015ec9e0..e7df9d955 100644 --- a/crates/completion/Cargo.toml +++ b/crates/completion/Cargo.toml | |||
@@ -13,6 +13,7 @@ doctest = false | |||
13 | itertools = "0.9.0" | 13 | itertools = "0.9.0" |
14 | log = "0.4.8" | 14 | log = "0.4.8" |
15 | rustc-hash = "1.1.0" | 15 | rustc-hash = "1.1.0" |
16 | either = "1.6.1" | ||
16 | 17 | ||
17 | assists = { path = "../assists", version = "0.0.0" } | 18 | assists = { path = "../assists", version = "0.0.0" } |
18 | stdx = { path = "../stdx", version = "0.0.0" } | 19 | stdx = { path = "../stdx", version = "0.0.0" } |
diff --git a/crates/completion/src/completions.rs b/crates/completion/src/completions.rs index 75dbb1a23..9b7d6c580 100644 --- a/crates/completion/src/completions.rs +++ b/crates/completion/src/completions.rs | |||
@@ -90,7 +90,7 @@ impl Completions { | |||
90 | Some(it) => it, | 90 | Some(it) => it, |
91 | None => return, | 91 | None => return, |
92 | }; | 92 | }; |
93 | if let Some(item) = render_macro(RenderContext::new(ctx), name, macro_) { | 93 | if let Some(item) = render_macro(RenderContext::new(ctx), None, name, macro_) { |
94 | self.add(item); | 94 | self.add(item); |
95 | } | 95 | } |
96 | } | 96 | } |
@@ -101,7 +101,7 @@ impl Completions { | |||
101 | func: hir::Function, | 101 | func: hir::Function, |
102 | local_name: Option<String>, | 102 | local_name: Option<String>, |
103 | ) { | 103 | ) { |
104 | let item = render_fn(RenderContext::new(ctx), local_name, func); | 104 | let item = render_fn(RenderContext::new(ctx), None, local_name, func); |
105 | self.add(item) | 105 | self.add(item) |
106 | } | 106 | } |
107 | 107 | ||
@@ -123,7 +123,7 @@ impl Completions { | |||
123 | variant: hir::EnumVariant, | 123 | variant: hir::EnumVariant, |
124 | path: ModPath, | 124 | path: ModPath, |
125 | ) { | 125 | ) { |
126 | let item = render_enum_variant(RenderContext::new(ctx), None, variant, Some(path)); | 126 | let item = render_enum_variant(RenderContext::new(ctx), None, None, variant, Some(path)); |
127 | self.add(item); | 127 | self.add(item); |
128 | } | 128 | } |
129 | 129 | ||
@@ -133,7 +133,7 @@ impl Completions { | |||
133 | variant: hir::EnumVariant, | 133 | variant: hir::EnumVariant, |
134 | local_name: Option<String>, | 134 | local_name: Option<String>, |
135 | ) { | 135 | ) { |
136 | let item = render_enum_variant(RenderContext::new(ctx), local_name, variant, None); | 136 | let item = render_enum_variant(RenderContext::new(ctx), None, local_name, variant, None); |
137 | self.add(item); | 137 | self.add(item); |
138 | } | 138 | } |
139 | } | 139 | } |
diff --git a/crates/completion/src/completions/pattern.rs b/crates/completion/src/completions/pattern.rs index 7ab7f09fe..4f63ff0ef 100644 --- a/crates/completion/src/completions/pattern.rs +++ b/crates/completion/src/completions/pattern.rs | |||
@@ -4,7 +4,7 @@ use crate::{CompletionContext, Completions}; | |||
4 | 4 | ||
5 | /// Completes constats and paths in patterns. | 5 | /// Completes constats and paths in patterns. |
6 | pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) { | 6 | pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) { |
7 | if !ctx.is_pat_binding_or_const { | 7 | if !(ctx.is_pat_binding_or_const || ctx.is_irrefutable_let_pat_binding) { |
8 | return; | 8 | return; |
9 | } | 9 | } |
10 | if ctx.record_pat_syntax.is_some() { | 10 | if ctx.record_pat_syntax.is_some() { |
@@ -14,20 +14,27 @@ pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) { | |||
14 | // FIXME: ideally, we should look at the type we are matching against and | 14 | // FIXME: ideally, we should look at the type we are matching against and |
15 | // suggest variants + auto-imports | 15 | // suggest variants + auto-imports |
16 | ctx.scope.process_all_names(&mut |name, res| { | 16 | ctx.scope.process_all_names(&mut |name, res| { |
17 | match &res { | 17 | let add_resolution = match &res { |
18 | hir::ScopeDef::ModuleDef(def) => match def { | 18 | hir::ScopeDef::ModuleDef(def) => { |
19 | hir::ModuleDef::Adt(hir::Adt::Enum(..)) | 19 | if ctx.is_irrefutable_let_pat_binding { |
20 | | hir::ModuleDef::Adt(hir::Adt::Struct(..)) | 20 | matches!(def, hir::ModuleDef::Adt(hir::Adt::Struct(_))) |
21 | | hir::ModuleDef::EnumVariant(..) | 21 | } else { |
22 | | hir::ModuleDef::Const(..) | 22 | matches!( |
23 | | hir::ModuleDef::Module(..) => (), | 23 | def, |
24 | _ => return, | 24 | hir::ModuleDef::Adt(hir::Adt::Enum(..)) |
25 | }, | 25 | | hir::ModuleDef::Adt(hir::Adt::Struct(..)) |
26 | hir::ScopeDef::MacroDef(_) => (), | 26 | | hir::ModuleDef::EnumVariant(..) |
27 | _ => return, | 27 | | hir::ModuleDef::Const(..) |
28 | | hir::ModuleDef::Module(..) | ||
29 | ) | ||
30 | } | ||
31 | } | ||
32 | hir::ScopeDef::MacroDef(_) => true, | ||
33 | _ => false, | ||
28 | }; | 34 | }; |
29 | 35 | if add_resolution { | |
30 | acc.add_resolution(ctx, name.to_string(), &res) | 36 | acc.add_resolution(ctx, name.to_string(), &res); |
37 | } | ||
31 | }); | 38 | }); |
32 | } | 39 | } |
33 | 40 | ||
@@ -85,4 +92,26 @@ fn foo() { | |||
85 | "#]], | 92 | "#]], |
86 | ); | 93 | ); |
87 | } | 94 | } |
95 | |||
96 | #[test] | ||
97 | fn completes_in_irrefutable_let() { | ||
98 | check( | ||
99 | r#" | ||
100 | enum E { X } | ||
101 | use self::E::X; | ||
102 | const Z: E = E::X; | ||
103 | mod m {} | ||
104 | |||
105 | static FOO: E = E::X; | ||
106 | struct Bar { f: u32 } | ||
107 | |||
108 | fn foo() { | ||
109 | let <|> | ||
110 | } | ||
111 | "#, | ||
112 | expect![[r#" | ||
113 | st Bar | ||
114 | "#]], | ||
115 | ); | ||
116 | } | ||
88 | } | 117 | } |
diff --git a/crates/completion/src/completions/unqualified_path.rs b/crates/completion/src/completions/unqualified_path.rs index 7df58e1da..3bd776905 100644 --- a/crates/completion/src/completions/unqualified_path.rs +++ b/crates/completion/src/completions/unqualified_path.rs | |||
@@ -1,10 +1,16 @@ | |||
1 | //! Completion of names from the current scope, e.g. locals and imported items. | 1 | //! Completion of names from the current scope, e.g. locals and imported items. |
2 | 2 | ||
3 | use assists::utils::ImportScope; | ||
4 | use either::Either; | ||
3 | use hir::{Adt, ModuleDef, ScopeDef, Type}; | 5 | use hir::{Adt, ModuleDef, ScopeDef, Type}; |
6 | use ide_db::imports_locator; | ||
4 | use syntax::AstNode; | 7 | use syntax::AstNode; |
5 | use test_utils::mark; | 8 | use test_utils::mark; |
6 | 9 | ||
7 | use crate::{CompletionContext, Completions}; | 10 | use crate::{ |
11 | render::{render_resolution_with_import, RenderContext}, | ||
12 | CompletionContext, Completions, | ||
13 | }; | ||
8 | 14 | ||
9 | pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) { | 15 | pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) { |
10 | if !(ctx.is_trivial_path || ctx.is_pat_binding_or_const) { | 16 | if !(ctx.is_trivial_path || ctx.is_pat_binding_or_const) { |
@@ -37,6 +43,10 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC | |||
37 | } | 43 | } |
38 | acc.add_resolution(ctx, name.to_string(), &res) | 44 | acc.add_resolution(ctx, name.to_string(), &res) |
39 | }); | 45 | }); |
46 | |||
47 | if ctx.config.enable_experimental_completions { | ||
48 | fuzzy_completion(acc, ctx).unwrap_or_default() | ||
49 | } | ||
40 | } | 50 | } |
41 | 51 | ||
42 | fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext, ty: &Type) { | 52 | fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext, ty: &Type) { |
@@ -63,6 +73,47 @@ fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext, ty: &T | |||
63 | } | 73 | } |
64 | } | 74 | } |
65 | 75 | ||
76 | fn fuzzy_completion(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> { | ||
77 | let _p = profile::span("fuzzy_completion"); | ||
78 | let current_module = ctx.scope.module()?; | ||
79 | let anchor = ctx.name_ref_syntax.as_ref()?; | ||
80 | let import_scope = ImportScope::find_insert_use_container(anchor.syntax(), &ctx.sema)?; | ||
81 | |||
82 | let potential_import_name = ctx.token.to_string(); | ||
83 | |||
84 | let possible_imports = imports_locator::find_similar_imports( | ||
85 | &ctx.sema, | ||
86 | ctx.krate?, | ||
87 | &potential_import_name, | ||
88 | 50, | ||
89 | true, | ||
90 | ) | ||
91 | .filter_map(|import_candidate| { | ||
92 | Some(match import_candidate { | ||
93 | Either::Left(module_def) => { | ||
94 | (current_module.find_use_path(ctx.db, module_def)?, ScopeDef::ModuleDef(module_def)) | ||
95 | } | ||
96 | Either::Right(macro_def) => { | ||
97 | (current_module.find_use_path(ctx.db, macro_def)?, ScopeDef::MacroDef(macro_def)) | ||
98 | } | ||
99 | }) | ||
100 | }) | ||
101 | .filter(|(mod_path, _)| mod_path.len() > 1) | ||
102 | .take(20) | ||
103 | .filter_map(|(import_path, definition)| { | ||
104 | render_resolution_with_import( | ||
105 | RenderContext::new(ctx), | ||
106 | import_path.clone(), | ||
107 | import_scope.clone(), | ||
108 | ctx.config.merge, | ||
109 | &definition, | ||
110 | ) | ||
111 | }); | ||
112 | |||
113 | acc.add_all(possible_imports); | ||
114 | Some(()) | ||
115 | } | ||
116 | |||
66 | #[cfg(test)] | 117 | #[cfg(test)] |
67 | mod tests { | 118 | mod tests { |
68 | use expect_test::{expect, Expect}; | 119 | use expect_test::{expect, Expect}; |
@@ -676,4 +727,85 @@ impl My<|> | |||
676 | "#]], | 727 | "#]], |
677 | ) | 728 | ) |
678 | } | 729 | } |
730 | |||
731 | #[test] | ||
732 | fn function_fuzzy_completion() { | ||
733 | check_edit( | ||
734 | "stdin", | ||
735 | r#" | ||
736 | //- /lib.rs crate:dep | ||
737 | pub mod io { | ||
738 | pub fn stdin() {} | ||
739 | }; | ||
740 | |||
741 | //- /main.rs crate:main deps:dep | ||
742 | fn main() { | ||
743 | stdi<|> | ||
744 | } | ||
745 | "#, | ||
746 | r#" | ||
747 | use dep::io::stdin; | ||
748 | |||
749 | fn main() { | ||
750 | stdin()$0 | ||
751 | } | ||
752 | "#, | ||
753 | ); | ||
754 | } | ||
755 | |||
756 | #[test] | ||
757 | fn macro_fuzzy_completion() { | ||
758 | check_edit( | ||
759 | "macro_with_curlies!", | ||
760 | r#" | ||
761 | //- /lib.rs crate:dep | ||
762 | /// Please call me as macro_with_curlies! {} | ||
763 | #[macro_export] | ||
764 | macro_rules! macro_with_curlies { | ||
765 | () => {} | ||
766 | } | ||
767 | |||
768 | //- /main.rs crate:main deps:dep | ||
769 | fn main() { | ||
770 | curli<|> | ||
771 | } | ||
772 | "#, | ||
773 | r#" | ||
774 | use dep::macro_with_curlies; | ||
775 | |||
776 | fn main() { | ||
777 | macro_with_curlies! {$0} | ||
778 | } | ||
779 | "#, | ||
780 | ); | ||
781 | } | ||
782 | |||
783 | #[test] | ||
784 | fn struct_fuzzy_completion() { | ||
785 | check_edit( | ||
786 | "ThirdStruct", | ||
787 | r#" | ||
788 | //- /lib.rs crate:dep | ||
789 | pub struct FirstStruct; | ||
790 | pub mod some_module { | ||
791 | pub struct SecondStruct; | ||
792 | pub struct ThirdStruct; | ||
793 | } | ||
794 | |||
795 | //- /main.rs crate:main deps:dep | ||
796 | use dep::{FirstStruct, some_module::SecondStruct}; | ||
797 | |||
798 | fn main() { | ||
799 | this<|> | ||
800 | } | ||
801 | "#, | ||
802 | r#" | ||
803 | use dep::{FirstStruct, some_module::{SecondStruct, ThirdStruct}}; | ||
804 | |||
805 | fn main() { | ||
806 | ThirdStruct | ||
807 | } | ||
808 | "#, | ||
809 | ); | ||
810 | } | ||
679 | } | 811 | } |
diff --git a/crates/completion/src/config.rs b/crates/completion/src/config.rs index 71b49ace8..f50735372 100644 --- a/crates/completion/src/config.rs +++ b/crates/completion/src/config.rs | |||
@@ -4,12 +4,16 @@ | |||
4 | //! module, and we use to statically check that we only produce snippet | 4 | //! module, and we use to statically check that we only produce snippet |
5 | //! completions if we are allowed to. | 5 | //! completions if we are allowed to. |
6 | 6 | ||
7 | use assists::utils::MergeBehaviour; | ||
8 | |||
7 | #[derive(Clone, Debug, PartialEq, Eq)] | 9 | #[derive(Clone, Debug, PartialEq, Eq)] |
8 | pub struct CompletionConfig { | 10 | pub struct CompletionConfig { |
9 | pub enable_postfix_completions: bool, | 11 | pub enable_postfix_completions: bool, |
12 | pub enable_experimental_completions: bool, | ||
10 | pub add_call_parenthesis: bool, | 13 | pub add_call_parenthesis: bool, |
11 | pub add_call_argument_snippets: bool, | 14 | pub add_call_argument_snippets: bool, |
12 | pub snippet_cap: Option<SnippetCap>, | 15 | pub snippet_cap: Option<SnippetCap>, |
16 | pub merge: Option<MergeBehaviour>, | ||
13 | } | 17 | } |
14 | 18 | ||
15 | impl CompletionConfig { | 19 | impl CompletionConfig { |
@@ -27,9 +31,11 @@ impl Default for CompletionConfig { | |||
27 | fn default() -> Self { | 31 | fn default() -> Self { |
28 | CompletionConfig { | 32 | CompletionConfig { |
29 | enable_postfix_completions: true, | 33 | enable_postfix_completions: true, |
34 | enable_experimental_completions: true, | ||
30 | add_call_parenthesis: true, | 35 | add_call_parenthesis: true, |
31 | add_call_argument_snippets: true, | 36 | add_call_argument_snippets: true, |
32 | snippet_cap: Some(SnippetCap { _private: () }), | 37 | snippet_cap: Some(SnippetCap { _private: () }), |
38 | merge: Some(MergeBehaviour::Full), | ||
33 | } | 39 | } |
34 | } | 40 | } |
35 | } | 41 | } |
diff --git a/crates/completion/src/context.rs b/crates/completion/src/context.rs index bf70ee478..5cd11cf77 100644 --- a/crates/completion/src/context.rs +++ b/crates/completion/src/context.rs | |||
@@ -51,6 +51,7 @@ pub(crate) struct CompletionContext<'a> { | |||
51 | /// If a name-binding or reference to a const in a pattern. | 51 | /// If a name-binding or reference to a const in a pattern. |
52 | /// Irrefutable patterns (like let) are excluded. | 52 | /// Irrefutable patterns (like let) are excluded. |
53 | pub(super) is_pat_binding_or_const: bool, | 53 | pub(super) is_pat_binding_or_const: bool, |
54 | pub(super) is_irrefutable_let_pat_binding: bool, | ||
54 | /// A single-indent path, like `foo`. `::foo` should not be considered a trivial path. | 55 | /// A single-indent path, like `foo`. `::foo` should not be considered a trivial path. |
55 | pub(super) is_trivial_path: bool, | 56 | pub(super) is_trivial_path: bool, |
56 | /// If not a trivial path, the prefix (qualifier). | 57 | /// If not a trivial path, the prefix (qualifier). |
@@ -146,6 +147,7 @@ impl<'a> CompletionContext<'a> { | |||
146 | active_parameter: ActiveParameter::at(db, position), | 147 | active_parameter: ActiveParameter::at(db, position), |
147 | is_param: false, | 148 | is_param: false, |
148 | is_pat_binding_or_const: false, | 149 | is_pat_binding_or_const: false, |
150 | is_irrefutable_let_pat_binding: false, | ||
149 | is_trivial_path: false, | 151 | is_trivial_path: false, |
150 | path_qual: None, | 152 | path_qual: None, |
151 | after_if: false, | 153 | after_if: false, |
@@ -330,6 +332,7 @@ impl<'a> CompletionContext<'a> { | |||
330 | if pat.syntax().text_range().contains_range(bind_pat.syntax().text_range()) | 332 | if pat.syntax().text_range().contains_range(bind_pat.syntax().text_range()) |
331 | { | 333 | { |
332 | self.is_pat_binding_or_const = false; | 334 | self.is_pat_binding_or_const = false; |
335 | self.is_irrefutable_let_pat_binding = true; | ||
333 | } | 336 | } |
334 | } | 337 | } |
335 | } | 338 | } |
diff --git a/crates/completion/src/item.rs b/crates/completion/src/item.rs index 6d1d085f4..b13c3f376 100644 --- a/crates/completion/src/item.rs +++ b/crates/completion/src/item.rs | |||
@@ -2,8 +2,9 @@ | |||
2 | 2 | ||
3 | use std::fmt; | 3 | use std::fmt; |
4 | 4 | ||
5 | use hir::{Documentation, Mutability}; | 5 | use assists::utils::{insert_use, mod_path_to_ast, ImportScope, MergeBehaviour}; |
6 | use syntax::TextRange; | 6 | use hir::{Documentation, ModPath, Mutability}; |
7 | use syntax::{algo, TextRange}; | ||
7 | use text_edit::TextEdit; | 8 | use text_edit::TextEdit; |
8 | 9 | ||
9 | use crate::config::SnippetCap; | 10 | use crate::config::SnippetCap; |
@@ -31,6 +32,7 @@ pub struct CompletionItem { | |||
31 | /// | 32 | /// |
32 | /// Typically, replaces `source_range` with new identifier. | 33 | /// Typically, replaces `source_range` with new identifier. |
33 | text_edit: TextEdit, | 34 | text_edit: TextEdit, |
35 | |||
34 | insert_text_format: InsertTextFormat, | 36 | insert_text_format: InsertTextFormat, |
35 | 37 | ||
36 | /// What item (struct, function, etc) are we completing. | 38 | /// What item (struct, function, etc) are we completing. |
@@ -199,8 +201,10 @@ impl CompletionItem { | |||
199 | trigger_call_info: None, | 201 | trigger_call_info: None, |
200 | score: None, | 202 | score: None, |
201 | ref_match: None, | 203 | ref_match: None, |
204 | import_data: None, | ||
202 | } | 205 | } |
203 | } | 206 | } |
207 | |||
204 | /// What user sees in pop-up in the UI. | 208 | /// What user sees in pop-up in the UI. |
205 | pub fn label(&self) -> &str { | 209 | pub fn label(&self) -> &str { |
206 | &self.label | 210 | &self.label |
@@ -257,6 +261,7 @@ impl CompletionItem { | |||
257 | pub(crate) struct Builder { | 261 | pub(crate) struct Builder { |
258 | source_range: TextRange, | 262 | source_range: TextRange, |
259 | completion_kind: CompletionKind, | 263 | completion_kind: CompletionKind, |
264 | import_data: Option<(ModPath, ImportScope, Option<MergeBehaviour>)>, | ||
260 | label: String, | 265 | label: String, |
261 | insert_text: Option<String>, | 266 | insert_text: Option<String>, |
262 | insert_text_format: InsertTextFormat, | 267 | insert_text_format: InsertTextFormat, |
@@ -273,23 +278,50 @@ pub(crate) struct Builder { | |||
273 | 278 | ||
274 | impl Builder { | 279 | impl Builder { |
275 | pub(crate) fn build(self) -> CompletionItem { | 280 | pub(crate) fn build(self) -> CompletionItem { |
276 | let label = self.label; | 281 | let mut label = self.label; |
277 | let text_edit = match self.text_edit { | 282 | let mut lookup = self.lookup; |
283 | let mut insert_text = self.insert_text; | ||
284 | let mut text_edits = TextEdit::builder(); | ||
285 | |||
286 | if let Some((import_path, import_scope, merge_behaviour)) = self.import_data { | ||
287 | let import = mod_path_to_ast(&import_path); | ||
288 | let mut import_path_without_last_segment = import_path; | ||
289 | let _ = import_path_without_last_segment.segments.pop(); | ||
290 | |||
291 | if !import_path_without_last_segment.segments.is_empty() { | ||
292 | if lookup.is_none() { | ||
293 | lookup = Some(label.clone()); | ||
294 | } | ||
295 | if insert_text.is_none() { | ||
296 | insert_text = Some(label.clone()); | ||
297 | } | ||
298 | label = format!("{}::{}", import_path_without_last_segment, label); | ||
299 | } | ||
300 | |||
301 | let rewriter = insert_use(&import_scope, import, merge_behaviour); | ||
302 | if let Some(old_ast) = rewriter.rewrite_root() { | ||
303 | algo::diff(&old_ast, &rewriter.rewrite(&old_ast)).into_text_edit(&mut text_edits); | ||
304 | } | ||
305 | } | ||
306 | |||
307 | let original_edit = match self.text_edit { | ||
278 | Some(it) => it, | 308 | Some(it) => it, |
279 | None => TextEdit::replace( | 309 | None => { |
280 | self.source_range, | 310 | TextEdit::replace(self.source_range, insert_text.unwrap_or_else(|| label.clone())) |
281 | self.insert_text.unwrap_or_else(|| label.clone()), | 311 | } |
282 | ), | ||
283 | }; | 312 | }; |
284 | 313 | ||
314 | let mut resulting_edit = text_edits.finish(); | ||
315 | resulting_edit.union(original_edit).expect("Failed to unite text edits"); | ||
316 | |||
285 | CompletionItem { | 317 | CompletionItem { |
286 | source_range: self.source_range, | 318 | source_range: self.source_range, |
287 | label, | 319 | label, |
288 | insert_text_format: self.insert_text_format, | 320 | insert_text_format: self.insert_text_format, |
289 | text_edit, | 321 | text_edit: resulting_edit, |
290 | detail: self.detail, | 322 | detail: self.detail, |
291 | documentation: self.documentation, | 323 | documentation: self.documentation, |
292 | lookup: self.lookup, | 324 | lookup, |
293 | kind: self.kind, | 325 | kind: self.kind, |
294 | completion_kind: self.completion_kind, | 326 | completion_kind: self.completion_kind, |
295 | deprecated: self.deprecated.unwrap_or(false), | 327 | deprecated: self.deprecated.unwrap_or(false), |
@@ -358,6 +390,13 @@ impl Builder { | |||
358 | self.trigger_call_info = Some(true); | 390 | self.trigger_call_info = Some(true); |
359 | self | 391 | self |
360 | } | 392 | } |
393 | pub(crate) fn import_data( | ||
394 | mut self, | ||
395 | import_data: Option<(ModPath, ImportScope, Option<MergeBehaviour>)>, | ||
396 | ) -> Builder { | ||
397 | self.import_data = import_data; | ||
398 | self | ||
399 | } | ||
361 | pub(crate) fn set_ref_match( | 400 | pub(crate) fn set_ref_match( |
362 | mut self, | 401 | mut self, |
363 | ref_match: Option<(Mutability, CompletionScore)>, | 402 | ref_match: Option<(Mutability, CompletionScore)>, |
diff --git a/crates/completion/src/lib.rs b/crates/completion/src/lib.rs index cb6e0554e..aecc1378b 100644 --- a/crates/completion/src/lib.rs +++ b/crates/completion/src/lib.rs | |||
@@ -67,6 +67,13 @@ pub use crate::{ | |||
67 | // fn test_name() {} | 67 | // fn test_name() {} |
68 | // } | 68 | // } |
69 | // ``` | 69 | // ``` |
70 | // | ||
71 | // And experimental completions, enabled with the `rust-analyzer.completion.enableExperimental` setting. | ||
72 | // This flag enables or disables: | ||
73 | // | ||
74 | // - Auto import: additional completion options with automatic `use` import and options from all project importable items, matched for the input | ||
75 | // | ||
76 | // Experimental completions might cause issues with performance and completion list look. | ||
70 | 77 | ||
71 | /// Main entry point for completion. We run completion as a two-phase process. | 78 | /// Main entry point for completion. We run completion as a two-phase process. |
72 | /// | 79 | /// |
diff --git a/crates/completion/src/render.rs b/crates/completion/src/render.rs index 1fa02c375..bce02f577 100644 --- a/crates/completion/src/render.rs +++ b/crates/completion/src/render.rs | |||
@@ -9,7 +9,8 @@ pub(crate) mod type_alias; | |||
9 | 9 | ||
10 | mod builder_ext; | 10 | mod builder_ext; |
11 | 11 | ||
12 | use hir::{Documentation, HasAttrs, HirDisplay, Mutability, ScopeDef, Type}; | 12 | use assists::utils::{ImportScope, MergeBehaviour}; |
13 | use hir::{Documentation, HasAttrs, HirDisplay, ModPath, Mutability, ScopeDef, Type}; | ||
13 | use ide_db::RootDatabase; | 14 | use ide_db::RootDatabase; |
14 | use syntax::TextRange; | 15 | use syntax::TextRange; |
15 | use test_utils::mark; | 16 | use test_utils::mark; |
@@ -42,7 +43,22 @@ pub(crate) fn render_resolution<'a>( | |||
42 | local_name: String, | 43 | local_name: String, |
43 | resolution: &ScopeDef, | 44 | resolution: &ScopeDef, |
44 | ) -> Option<CompletionItem> { | 45 | ) -> Option<CompletionItem> { |
45 | Render::new(ctx).render_resolution(local_name, resolution) | 46 | Render::new(ctx).render_resolution(local_name, None, resolution) |
47 | } | ||
48 | |||
49 | pub(crate) fn render_resolution_with_import<'a>( | ||
50 | ctx: RenderContext<'a>, | ||
51 | import: ModPath, | ||
52 | import_scope: ImportScope, | ||
53 | merge_behaviour: Option<MergeBehaviour>, | ||
54 | resolution: &ScopeDef, | ||
55 | ) -> Option<CompletionItem> { | ||
56 | let local_name = import.segments.last()?.to_string(); | ||
57 | Render::new(ctx).render_resolution( | ||
58 | local_name, | ||
59 | Some((import, import_scope, merge_behaviour)), | ||
60 | resolution, | ||
61 | ) | ||
46 | } | 62 | } |
47 | 63 | ||
48 | /// Interface for data and methods required for items rendering. | 64 | /// Interface for data and methods required for items rendering. |
@@ -131,8 +147,10 @@ impl<'a> Render<'a> { | |||
131 | fn render_resolution( | 147 | fn render_resolution( |
132 | self, | 148 | self, |
133 | local_name: String, | 149 | local_name: String, |
150 | import_data: Option<(ModPath, ImportScope, Option<MergeBehaviour>)>, | ||
134 | resolution: &ScopeDef, | 151 | resolution: &ScopeDef, |
135 | ) -> Option<CompletionItem> { | 152 | ) -> Option<CompletionItem> { |
153 | let _p = profile::span("render_resolution"); | ||
136 | use hir::ModuleDef::*; | 154 | use hir::ModuleDef::*; |
137 | 155 | ||
138 | let completion_kind = match resolution { | 156 | let completion_kind = match resolution { |
@@ -142,15 +160,15 @@ impl<'a> Render<'a> { | |||
142 | 160 | ||
143 | let kind = match resolution { | 161 | let kind = match resolution { |
144 | ScopeDef::ModuleDef(Function(func)) => { | 162 | ScopeDef::ModuleDef(Function(func)) => { |
145 | let item = render_fn(self.ctx, Some(local_name), *func); | 163 | let item = render_fn(self.ctx, import_data, Some(local_name), *func); |
146 | return Some(item); | 164 | return Some(item); |
147 | } | 165 | } |
148 | ScopeDef::ModuleDef(EnumVariant(var)) => { | 166 | ScopeDef::ModuleDef(EnumVariant(var)) => { |
149 | let item = render_enum_variant(self.ctx, Some(local_name), *var, None); | 167 | let item = render_enum_variant(self.ctx, import_data, Some(local_name), *var, None); |
150 | return Some(item); | 168 | return Some(item); |
151 | } | 169 | } |
152 | ScopeDef::MacroDef(mac) => { | 170 | ScopeDef::MacroDef(mac) => { |
153 | let item = render_macro(self.ctx, local_name, *mac); | 171 | let item = render_macro(self.ctx, import_data, local_name, *mac); |
154 | return item; | 172 | return item; |
155 | } | 173 | } |
156 | 174 | ||
@@ -175,6 +193,7 @@ impl<'a> Render<'a> { | |||
175 | local_name, | 193 | local_name, |
176 | ) | 194 | ) |
177 | .kind(CompletionItemKind::UnresolvedReference) | 195 | .kind(CompletionItemKind::UnresolvedReference) |
196 | .import_data(import_data) | ||
178 | .build(); | 197 | .build(); |
179 | return Some(item); | 198 | return Some(item); |
180 | } | 199 | } |
@@ -227,7 +246,12 @@ impl<'a> Render<'a> { | |||
227 | } | 246 | } |
228 | } | 247 | } |
229 | 248 | ||
230 | let item = item.kind(kind).set_documentation(docs).set_ref_match(ref_match).build(); | 249 | let item = item |
250 | .kind(kind) | ||
251 | .import_data(import_data) | ||
252 | .set_documentation(docs) | ||
253 | .set_ref_match(ref_match) | ||
254 | .build(); | ||
231 | Some(item) | 255 | Some(item) |
232 | } | 256 | } |
233 | 257 | ||
@@ -426,6 +450,28 @@ fn main() { let _: m::Spam = S<|> } | |||
426 | kind: Module, | 450 | kind: Module, |
427 | }, | 451 | }, |
428 | CompletionItem { | 452 | CompletionItem { |
453 | label: "m::Spam", | ||
454 | source_range: 75..76, | ||
455 | text_edit: TextEdit { | ||
456 | indels: [ | ||
457 | Indel { | ||
458 | insert: "use m::Spam;", | ||
459 | delete: 0..0, | ||
460 | }, | ||
461 | Indel { | ||
462 | insert: "\n\n", | ||
463 | delete: 0..0, | ||
464 | }, | ||
465 | Indel { | ||
466 | insert: "Spam", | ||
467 | delete: 75..76, | ||
468 | }, | ||
469 | ], | ||
470 | }, | ||
471 | kind: Enum, | ||
472 | lookup: "Spam", | ||
473 | }, | ||
474 | CompletionItem { | ||
429 | label: "m::Spam::Foo", | 475 | label: "m::Spam::Foo", |
430 | source_range: 75..76, | 476 | source_range: 75..76, |
431 | delete: 75..76, | 477 | delete: 75..76, |
diff --git a/crates/completion/src/render/enum_variant.rs b/crates/completion/src/render/enum_variant.rs index fd412ed0e..6070e9b1d 100644 --- a/crates/completion/src/render/enum_variant.rs +++ b/crates/completion/src/render/enum_variant.rs | |||
@@ -1,5 +1,6 @@ | |||
1 | //! Renderer for `enum` variants. | 1 | //! Renderer for `enum` variants. |
2 | 2 | ||
3 | use assists::utils::{ImportScope, MergeBehaviour}; | ||
3 | use hir::{HasAttrs, HirDisplay, ModPath, StructKind}; | 4 | use hir::{HasAttrs, HirDisplay, ModPath, StructKind}; |
4 | use itertools::Itertools; | 5 | use itertools::Itertools; |
5 | use test_utils::mark; | 6 | use test_utils::mark; |
@@ -11,11 +12,12 @@ use crate::{ | |||
11 | 12 | ||
12 | pub(crate) fn render_enum_variant<'a>( | 13 | pub(crate) fn render_enum_variant<'a>( |
13 | ctx: RenderContext<'a>, | 14 | ctx: RenderContext<'a>, |
15 | import_data: Option<(ModPath, ImportScope, Option<MergeBehaviour>)>, | ||
14 | local_name: Option<String>, | 16 | local_name: Option<String>, |
15 | variant: hir::EnumVariant, | 17 | variant: hir::EnumVariant, |
16 | path: Option<ModPath>, | 18 | path: Option<ModPath>, |
17 | ) -> CompletionItem { | 19 | ) -> CompletionItem { |
18 | EnumVariantRender::new(ctx, local_name, variant, path).render() | 20 | EnumVariantRender::new(ctx, local_name, variant, path).render(import_data) |
19 | } | 21 | } |
20 | 22 | ||
21 | #[derive(Debug)] | 23 | #[derive(Debug)] |
@@ -60,7 +62,10 @@ impl<'a> EnumVariantRender<'a> { | |||
60 | } | 62 | } |
61 | } | 63 | } |
62 | 64 | ||
63 | fn render(self) -> CompletionItem { | 65 | fn render( |
66 | self, | ||
67 | import_data: Option<(ModPath, ImportScope, Option<MergeBehaviour>)>, | ||
68 | ) -> CompletionItem { | ||
64 | let mut builder = CompletionItem::new( | 69 | let mut builder = CompletionItem::new( |
65 | CompletionKind::Reference, | 70 | CompletionKind::Reference, |
66 | self.ctx.source_range(), | 71 | self.ctx.source_range(), |
@@ -69,6 +74,7 @@ impl<'a> EnumVariantRender<'a> { | |||
69 | .kind(CompletionItemKind::EnumVariant) | 74 | .kind(CompletionItemKind::EnumVariant) |
70 | .set_documentation(self.variant.docs(self.ctx.db())) | 75 | .set_documentation(self.variant.docs(self.ctx.db())) |
71 | .set_deprecated(self.ctx.is_deprecated(self.variant)) | 76 | .set_deprecated(self.ctx.is_deprecated(self.variant)) |
77 | .import_data(import_data) | ||
72 | .detail(self.detail()); | 78 | .detail(self.detail()); |
73 | 79 | ||
74 | if self.variant_kind == StructKind::Tuple { | 80 | if self.variant_kind == StructKind::Tuple { |
diff --git a/crates/completion/src/render/function.rs b/crates/completion/src/render/function.rs index 4fa6eafd7..9dd5cd18c 100644 --- a/crates/completion/src/render/function.rs +++ b/crates/completion/src/render/function.rs | |||
@@ -1,6 +1,7 @@ | |||
1 | //! Renderer for function calls. | 1 | //! Renderer for function calls. |
2 | 2 | ||
3 | use hir::{HasSource, Type}; | 3 | use assists::utils::{ImportScope, MergeBehaviour}; |
4 | use hir::{HasSource, ModPath, Type}; | ||
4 | use syntax::{ast::Fn, display::function_declaration}; | 5 | use syntax::{ast::Fn, display::function_declaration}; |
5 | 6 | ||
6 | use crate::{ | 7 | use crate::{ |
@@ -10,10 +11,11 @@ use crate::{ | |||
10 | 11 | ||
11 | pub(crate) fn render_fn<'a>( | 12 | pub(crate) fn render_fn<'a>( |
12 | ctx: RenderContext<'a>, | 13 | ctx: RenderContext<'a>, |
14 | import_data: Option<(ModPath, ImportScope, Option<MergeBehaviour>)>, | ||
13 | local_name: Option<String>, | 15 | local_name: Option<String>, |
14 | fn_: hir::Function, | 16 | fn_: hir::Function, |
15 | ) -> CompletionItem { | 17 | ) -> CompletionItem { |
16 | FunctionRender::new(ctx, local_name, fn_).render() | 18 | FunctionRender::new(ctx, local_name, fn_).render(import_data) |
17 | } | 19 | } |
18 | 20 | ||
19 | #[derive(Debug)] | 21 | #[derive(Debug)] |
@@ -36,7 +38,10 @@ impl<'a> FunctionRender<'a> { | |||
36 | FunctionRender { ctx, name, fn_, ast_node } | 38 | FunctionRender { ctx, name, fn_, ast_node } |
37 | } | 39 | } |
38 | 40 | ||
39 | fn render(self) -> CompletionItem { | 41 | fn render( |
42 | self, | ||
43 | import_data: Option<(ModPath, ImportScope, Option<MergeBehaviour>)>, | ||
44 | ) -> CompletionItem { | ||
40 | let params = self.params(); | 45 | let params = self.params(); |
41 | CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), self.name.clone()) | 46 | CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), self.name.clone()) |
42 | .kind(self.kind()) | 47 | .kind(self.kind()) |
@@ -44,6 +49,7 @@ impl<'a> FunctionRender<'a> { | |||
44 | .set_deprecated(self.ctx.is_deprecated(self.fn_)) | 49 | .set_deprecated(self.ctx.is_deprecated(self.fn_)) |
45 | .detail(self.detail()) | 50 | .detail(self.detail()) |
46 | .add_call_parens(self.ctx.completion, self.name, params) | 51 | .add_call_parens(self.ctx.completion, self.name, params) |
52 | .import_data(import_data) | ||
47 | .build() | 53 | .build() |
48 | } | 54 | } |
49 | 55 | ||
diff --git a/crates/completion/src/render/macro_.rs b/crates/completion/src/render/macro_.rs index 96be59cc3..fead59e41 100644 --- a/crates/completion/src/render/macro_.rs +++ b/crates/completion/src/render/macro_.rs | |||
@@ -1,6 +1,7 @@ | |||
1 | //! Renderer for macro invocations. | 1 | //! Renderer for macro invocations. |
2 | 2 | ||
3 | use hir::{Documentation, HasSource}; | 3 | use assists::utils::{ImportScope, MergeBehaviour}; |
4 | use hir::{Documentation, HasSource, ModPath}; | ||
4 | use syntax::display::macro_label; | 5 | use syntax::display::macro_label; |
5 | use test_utils::mark; | 6 | use test_utils::mark; |
6 | 7 | ||
@@ -11,10 +12,11 @@ use crate::{ | |||
11 | 12 | ||
12 | pub(crate) fn render_macro<'a>( | 13 | pub(crate) fn render_macro<'a>( |
13 | ctx: RenderContext<'a>, | 14 | ctx: RenderContext<'a>, |
15 | import_data: Option<(ModPath, ImportScope, Option<MergeBehaviour>)>, | ||
14 | name: String, | 16 | name: String, |
15 | macro_: hir::MacroDef, | 17 | macro_: hir::MacroDef, |
16 | ) -> Option<CompletionItem> { | 18 | ) -> Option<CompletionItem> { |
17 | MacroRender::new(ctx, name, macro_).render() | 19 | MacroRender::new(ctx, name, macro_).render(import_data) |
18 | } | 20 | } |
19 | 21 | ||
20 | #[derive(Debug)] | 22 | #[derive(Debug)] |
@@ -36,7 +38,10 @@ impl<'a> MacroRender<'a> { | |||
36 | MacroRender { ctx, name, macro_, docs, bra, ket } | 38 | MacroRender { ctx, name, macro_, docs, bra, ket } |
37 | } | 39 | } |
38 | 40 | ||
39 | fn render(&self) -> Option<CompletionItem> { | 41 | fn render( |
42 | &self, | ||
43 | import_data: Option<(ModPath, ImportScope, Option<MergeBehaviour>)>, | ||
44 | ) -> Option<CompletionItem> { | ||
40 | // FIXME: Currently proc-macro do not have ast-node, | 45 | // FIXME: Currently proc-macro do not have ast-node, |
41 | // such that it does not have source | 46 | // such that it does not have source |
42 | if self.macro_.is_proc_macro() { | 47 | if self.macro_.is_proc_macro() { |
@@ -48,6 +53,7 @@ impl<'a> MacroRender<'a> { | |||
48 | .kind(CompletionItemKind::Macro) | 53 | .kind(CompletionItemKind::Macro) |
49 | .set_documentation(self.docs.clone()) | 54 | .set_documentation(self.docs.clone()) |
50 | .set_deprecated(self.ctx.is_deprecated(self.macro_)) | 55 | .set_deprecated(self.ctx.is_deprecated(self.macro_)) |
56 | .import_data(import_data) | ||
51 | .detail(self.detail()); | 57 | .detail(self.detail()); |
52 | 58 | ||
53 | let needs_bang = self.needs_bang(); | 59 | let needs_bang = self.needs_bang(); |
diff --git a/crates/flycheck/Cargo.toml b/crates/flycheck/Cargo.toml index 44499bc79..3d9436d69 100644 --- a/crates/flycheck/Cargo.toml +++ b/crates/flycheck/Cargo.toml | |||
@@ -12,7 +12,7 @@ doctest = false | |||
12 | [dependencies] | 12 | [dependencies] |
13 | crossbeam-channel = "0.5.0" | 13 | crossbeam-channel = "0.5.0" |
14 | log = "0.4.8" | 14 | log = "0.4.8" |
15 | cargo_metadata = "0.12.0" | 15 | cargo_metadata = "=0.12.0" |
16 | serde_json = "1.0.48" | 16 | serde_json = "1.0.48" |
17 | jod-thread = "0.1.1" | 17 | jod-thread = "0.1.1" |
18 | 18 | ||
diff --git a/crates/hir/src/code_model.rs b/crates/hir/src/code_model.rs index 30a5e4580..f06b5cd9f 100644 --- a/crates/hir/src/code_model.rs +++ b/crates/hir/src/code_model.rs | |||
@@ -110,15 +110,9 @@ impl Crate { | |||
110 | pub fn query_external_importables( | 110 | pub fn query_external_importables( |
111 | self, | 111 | self, |
112 | db: &dyn DefDatabase, | 112 | db: &dyn DefDatabase, |
113 | query: &str, | 113 | query: import_map::Query, |
114 | ) -> impl Iterator<Item = Either<ModuleDef, MacroDef>> { | 114 | ) -> impl Iterator<Item = Either<ModuleDef, MacroDef>> { |
115 | import_map::search_dependencies( | 115 | import_map::search_dependencies(db, self.into(), query).into_iter().map(|item| match item { |
116 | db, | ||
117 | self.into(), | ||
118 | import_map::Query::new(query).anchor_end().case_sensitive().limit(40), | ||
119 | ) | ||
120 | .into_iter() | ||
121 | .map(|item| match item { | ||
122 | ItemInNs::Types(mod_id) | ItemInNs::Values(mod_id) => Either::Left(mod_id.into()), | 116 | ItemInNs::Types(mod_id) | ItemInNs::Values(mod_id) => Either::Left(mod_id.into()), |
123 | ItemInNs::Macros(mac_id) => Either::Right(mac_id.into()), | 117 | ItemInNs::Macros(mac_id) => Either::Right(mac_id.into()), |
124 | }) | 118 | }) |
@@ -1426,11 +1420,11 @@ impl Type { | |||
1426 | pub fn normalize_trait_assoc_type( | 1420 | pub fn normalize_trait_assoc_type( |
1427 | &self, | 1421 | &self, |
1428 | db: &dyn HirDatabase, | 1422 | db: &dyn HirDatabase, |
1429 | r#trait: Trait, | 1423 | trait_: Trait, |
1430 | args: &[Type], | 1424 | args: &[Type], |
1431 | alias: TypeAlias, | 1425 | alias: TypeAlias, |
1432 | ) -> Option<Type> { | 1426 | ) -> Option<Type> { |
1433 | let subst = Substs::build_for_def(db, r#trait.id) | 1427 | let subst = Substs::build_for_def(db, trait_.id) |
1434 | .push(self.ty.value.clone()) | 1428 | .push(self.ty.value.clone()) |
1435 | .fill(args.iter().map(|t| t.ty.value.clone())) | 1429 | .fill(args.iter().map(|t| t.ty.value.clone())) |
1436 | .build(); | 1430 | .build(); |
diff --git a/crates/hir/src/db.rs b/crates/hir/src/db.rs index 07333c453..8c767b249 100644 --- a/crates/hir/src/db.rs +++ b/crates/hir/src/db.rs | |||
@@ -11,7 +11,7 @@ pub use hir_def::db::{ | |||
11 | }; | 11 | }; |
12 | pub use hir_expand::db::{ | 12 | pub use hir_expand::db::{ |
13 | AstDatabase, AstDatabaseStorage, AstIdMapQuery, InternEagerExpansionQuery, InternMacroQuery, | 13 | AstDatabase, AstDatabaseStorage, AstIdMapQuery, InternEagerExpansionQuery, InternMacroQuery, |
14 | MacroArgTextQuery, MacroDefQuery, MacroExpandQuery, ParseMacroQuery, | 14 | MacroArgTextQuery, MacroDefQuery, MacroExpandQuery, ParseMacroExpansionQuery, |
15 | }; | 15 | }; |
16 | pub use hir_ty::db::*; | 16 | pub use hir_ty::db::*; |
17 | 17 | ||
diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs index c18c1c587..d9ad8db6f 100644 --- a/crates/hir/src/diagnostics.rs +++ b/crates/hir/src/diagnostics.rs | |||
@@ -1,6 +1,8 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | pub use hir_def::diagnostics::{InactiveCode, UnresolvedModule}; | 2 | pub use hir_def::diagnostics::{InactiveCode, UnresolvedModule}; |
3 | pub use hir_expand::diagnostics::{Diagnostic, DiagnosticSink, DiagnosticSinkBuilder}; | 3 | pub use hir_expand::diagnostics::{ |
4 | Diagnostic, DiagnosticCode, DiagnosticSink, DiagnosticSinkBuilder, | ||
5 | }; | ||
4 | pub use hir_ty::diagnostics::{ | 6 | pub use hir_ty::diagnostics::{ |
5 | IncorrectCase, MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkInTailExpr, | 7 | IncorrectCase, MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkInTailExpr, |
6 | NoSuchField, | 8 | NoSuchField, |
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 0d184379f..93bdb4472 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs | |||
@@ -49,6 +49,7 @@ pub use hir_def::{ | |||
49 | builtin_type::BuiltinType, | 49 | builtin_type::BuiltinType, |
50 | docs::Documentation, | 50 | docs::Documentation, |
51 | find_path::PrefixKind, | 51 | find_path::PrefixKind, |
52 | import_map, | ||
52 | item_scope::ItemInNs, | 53 | item_scope::ItemInNs, |
53 | nameres::ModuleSource, | 54 | nameres::ModuleSource, |
54 | path::{ModPath, PathKind}, | 55 | path::{ModPath, PathKind}, |
@@ -56,8 +57,8 @@ pub use hir_def::{ | |||
56 | visibility::Visibility, | 57 | visibility::Visibility, |
57 | }; | 58 | }; |
58 | pub use hir_expand::{ | 59 | pub use hir_expand::{ |
59 | name::known, name::AsName, name::Name, HirFileId, InFile, MacroCallId, MacroCallLoc, | 60 | name::known, name::AsName, name::Name, ExpandResult, HirFileId, InFile, MacroCallId, |
60 | /* FIXME */ MacroDefId, MacroFile, Origin, | 61 | MacroCallLoc, /* FIXME */ MacroDefId, MacroFile, Origin, |
61 | }; | 62 | }; |
62 | pub use hir_ty::display::HirDisplay; | 63 | pub use hir_ty::display::HirDisplay; |
63 | 64 | ||
diff --git a/crates/hir_def/src/import_map.rs b/crates/hir_def/src/import_map.rs index 1e24f29a8..c0f108848 100644 --- a/crates/hir_def/src/import_map.rs +++ b/crates/hir_def/src/import_map.rs | |||
@@ -7,7 +7,7 @@ use fst::{self, Streamer}; | |||
7 | use hir_expand::name::Name; | 7 | use hir_expand::name::Name; |
8 | use indexmap::{map::Entry, IndexMap}; | 8 | use indexmap::{map::Entry, IndexMap}; |
9 | use itertools::Itertools; | 9 | use itertools::Itertools; |
10 | use rustc_hash::{FxHashMap, FxHasher}; | 10 | use rustc_hash::{FxHashMap, FxHashSet, FxHasher}; |
11 | use smallvec::SmallVec; | 11 | use smallvec::SmallVec; |
12 | use syntax::SmolStr; | 12 | use syntax::SmolStr; |
13 | 13 | ||
@@ -225,6 +225,19 @@ fn cmp((_, lhs): &(&ItemInNs, &ImportInfo), (_, rhs): &(&ItemInNs, &ImportInfo)) | |||
225 | lhs_str.cmp(&rhs_str) | 225 | lhs_str.cmp(&rhs_str) |
226 | } | 226 | } |
227 | 227 | ||
228 | #[derive(Debug, Eq, PartialEq, Hash)] | ||
229 | pub enum ImportKind { | ||
230 | Module, | ||
231 | Function, | ||
232 | Adt, | ||
233 | EnumVariant, | ||
234 | Const, | ||
235 | Static, | ||
236 | Trait, | ||
237 | TypeAlias, | ||
238 | BuiltinType, | ||
239 | } | ||
240 | |||
228 | #[derive(Debug)] | 241 | #[derive(Debug)] |
229 | pub struct Query { | 242 | pub struct Query { |
230 | query: String, | 243 | query: String, |
@@ -232,6 +245,7 @@ pub struct Query { | |||
232 | anchor_end: bool, | 245 | anchor_end: bool, |
233 | case_sensitive: bool, | 246 | case_sensitive: bool, |
234 | limit: usize, | 247 | limit: usize, |
248 | exclude_import_kinds: FxHashSet<ImportKind>, | ||
235 | } | 249 | } |
236 | 250 | ||
237 | impl Query { | 251 | impl Query { |
@@ -242,6 +256,7 @@ impl Query { | |||
242 | anchor_end: false, | 256 | anchor_end: false, |
243 | case_sensitive: false, | 257 | case_sensitive: false, |
244 | limit: usize::max_value(), | 258 | limit: usize::max_value(), |
259 | exclude_import_kinds: FxHashSet::default(), | ||
245 | } | 260 | } |
246 | } | 261 | } |
247 | 262 | ||
@@ -260,6 +275,12 @@ impl Query { | |||
260 | pub fn case_sensitive(self) -> Self { | 275 | pub fn case_sensitive(self) -> Self { |
261 | Self { case_sensitive: true, ..self } | 276 | Self { case_sensitive: true, ..self } |
262 | } | 277 | } |
278 | |||
279 | /// Do not include imports of the specified kind in the search results. | ||
280 | pub fn exclude_import_kind(mut self, import_kind: ImportKind) -> Self { | ||
281 | self.exclude_import_kinds.insert(import_kind); | ||
282 | self | ||
283 | } | ||
263 | } | 284 | } |
264 | 285 | ||
265 | /// Searches dependencies of `krate` for an importable path matching `query`. | 286 | /// Searches dependencies of `krate` for an importable path matching `query`. |
@@ -303,10 +324,17 @@ pub fn search_dependencies<'a>( | |||
303 | 324 | ||
304 | // Add the items from this `ModPath` group. Those are all subsequent items in | 325 | // Add the items from this `ModPath` group. Those are all subsequent items in |
305 | // `importables` whose paths match `path`. | 326 | // `importables` whose paths match `path`. |
306 | let iter = importables.iter().copied().take_while(|item| { | 327 | let iter = importables |
307 | let item_path = &import_map.map[item].path; | 328 | .iter() |
308 | fst_path(item_path) == fst_path(path) | 329 | .copied() |
309 | }); | 330 | .take_while(|item| { |
331 | let item_path = &import_map.map[item].path; | ||
332 | fst_path(item_path) == fst_path(path) | ||
333 | }) | ||
334 | .filter(|&item| match item_import_kind(item) { | ||
335 | Some(import_kind) => !query.exclude_import_kinds.contains(&import_kind), | ||
336 | None => true, | ||
337 | }); | ||
310 | 338 | ||
311 | if query.case_sensitive { | 339 | if query.case_sensitive { |
312 | // FIXME: This does not do a subsequence match. | 340 | // FIXME: This does not do a subsequence match. |
@@ -341,6 +369,20 @@ pub fn search_dependencies<'a>( | |||
341 | res | 369 | res |
342 | } | 370 | } |
343 | 371 | ||
372 | fn item_import_kind(item: ItemInNs) -> Option<ImportKind> { | ||
373 | Some(match item.as_module_def_id()? { | ||
374 | ModuleDefId::ModuleId(_) => ImportKind::Module, | ||
375 | ModuleDefId::FunctionId(_) => ImportKind::Function, | ||
376 | ModuleDefId::AdtId(_) => ImportKind::Adt, | ||
377 | ModuleDefId::EnumVariantId(_) => ImportKind::EnumVariant, | ||
378 | ModuleDefId::ConstId(_) => ImportKind::Const, | ||
379 | ModuleDefId::StaticId(_) => ImportKind::Static, | ||
380 | ModuleDefId::TraitId(_) => ImportKind::Trait, | ||
381 | ModuleDefId::TypeAliasId(_) => ImportKind::TypeAlias, | ||
382 | ModuleDefId::BuiltinType(_) => ImportKind::BuiltinType, | ||
383 | }) | ||
384 | } | ||
385 | |||
344 | #[cfg(test)] | 386 | #[cfg(test)] |
345 | mod tests { | 387 | mod tests { |
346 | use base_db::{fixture::WithFixture, SourceDatabase, Upcast}; | 388 | use base_db::{fixture::WithFixture, SourceDatabase, Upcast}; |
@@ -758,4 +800,34 @@ mod tests { | |||
758 | "#]], | 800 | "#]], |
759 | ); | 801 | ); |
760 | } | 802 | } |
803 | |||
804 | #[test] | ||
805 | fn search_exclusions() { | ||
806 | let ra_fixture = r#" | ||
807 | //- /main.rs crate:main deps:dep | ||
808 | //- /dep.rs crate:dep | ||
809 | |||
810 | pub struct fmt; | ||
811 | pub struct FMT; | ||
812 | "#; | ||
813 | |||
814 | check_search( | ||
815 | ra_fixture, | ||
816 | "main", | ||
817 | Query::new("FMT"), | ||
818 | expect![[r#" | ||
819 | dep::fmt (t) | ||
820 | dep::fmt (v) | ||
821 | dep::FMT (t) | ||
822 | dep::FMT (v) | ||
823 | "#]], | ||
824 | ); | ||
825 | |||
826 | check_search( | ||
827 | ra_fixture, | ||
828 | "main", | ||
829 | Query::new("FMT").exclude_import_kind(ImportKind::Adt), | ||
830 | expect![[r#""#]], | ||
831 | ); | ||
832 | } | ||
761 | } | 833 | } |
diff --git a/crates/hir_def/src/nameres.rs b/crates/hir_def/src/nameres.rs index eb41d324e..202a7dcb6 100644 --- a/crates/hir_def/src/nameres.rs +++ b/crates/hir_def/src/nameres.rs | |||
@@ -287,7 +287,7 @@ mod diagnostics { | |||
287 | use hir_expand::diagnostics::DiagnosticSink; | 287 | use hir_expand::diagnostics::DiagnosticSink; |
288 | use hir_expand::hygiene::Hygiene; | 288 | use hir_expand::hygiene::Hygiene; |
289 | use hir_expand::InFile; | 289 | use hir_expand::InFile; |
290 | use syntax::{ast, AstPtr, SyntaxNodePtr}; | 290 | use syntax::{ast, AstPtr}; |
291 | 291 | ||
292 | use crate::path::ModPath; | 292 | use crate::path::ModPath; |
293 | use crate::{db::DefDatabase, diagnostics::*, nameres::LocalModuleId, AstId}; | 293 | use crate::{db::DefDatabase, diagnostics::*, nameres::LocalModuleId, AstId}; |
@@ -300,7 +300,7 @@ mod diagnostics { | |||
300 | 300 | ||
301 | UnresolvedImport { ast: AstId<ast::Use>, index: usize }, | 301 | UnresolvedImport { ast: AstId<ast::Use>, index: usize }, |
302 | 302 | ||
303 | UnconfiguredCode { ast: InFile<SyntaxNodePtr>, cfg: CfgExpr, opts: CfgOptions }, | 303 | UnconfiguredCode { ast: AstId<ast::Item>, cfg: CfgExpr, opts: CfgOptions }, |
304 | } | 304 | } |
305 | 305 | ||
306 | #[derive(Debug, PartialEq, Eq)] | 306 | #[derive(Debug, PartialEq, Eq)] |
@@ -341,7 +341,7 @@ mod diagnostics { | |||
341 | 341 | ||
342 | pub(super) fn unconfigured_code( | 342 | pub(super) fn unconfigured_code( |
343 | container: LocalModuleId, | 343 | container: LocalModuleId, |
344 | ast: InFile<SyntaxNodePtr>, | 344 | ast: AstId<ast::Item>, |
345 | cfg: CfgExpr, | 345 | cfg: CfgExpr, |
346 | opts: CfgOptions, | 346 | opts: CfgOptions, |
347 | ) -> Self { | 347 | ) -> Self { |
@@ -399,9 +399,10 @@ mod diagnostics { | |||
399 | } | 399 | } |
400 | 400 | ||
401 | DiagnosticKind::UnconfiguredCode { ast, cfg, opts } => { | 401 | DiagnosticKind::UnconfiguredCode { ast, cfg, opts } => { |
402 | let item = ast.to_node(db.upcast()); | ||
402 | sink.push(InactiveCode { | 403 | sink.push(InactiveCode { |
403 | file: ast.file_id, | 404 | file: ast.file_id, |
404 | node: ast.value.clone(), | 405 | node: AstPtr::new(&item).into(), |
405 | cfg: cfg.clone(), | 406 | cfg: cfg.clone(), |
406 | opts: opts.clone(), | 407 | opts: opts.clone(), |
407 | }); | 408 | }); |
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs index 386287518..5ed9073e0 100644 --- a/crates/hir_def/src/nameres/collector.rs +++ b/crates/hir_def/src/nameres/collector.rs | |||
@@ -1336,13 +1336,11 @@ impl ModCollector<'_, '_> { | |||
1336 | 1336 | ||
1337 | fn emit_unconfigured_diagnostic(&mut self, item: ModItem, cfg: &CfgExpr) { | 1337 | fn emit_unconfigured_diagnostic(&mut self, item: ModItem, cfg: &CfgExpr) { |
1338 | let ast_id = item.ast_id(self.item_tree); | 1338 | let ast_id = item.ast_id(self.item_tree); |
1339 | let id_map = self.def_collector.db.ast_id_map(self.file_id); | ||
1340 | let syntax_ptr = id_map.get(ast_id).syntax_node_ptr(); | ||
1341 | 1339 | ||
1342 | let ast_node = InFile::new(self.file_id, syntax_ptr); | 1340 | let ast_id = InFile::new(self.file_id, ast_id); |
1343 | self.def_collector.def_map.diagnostics.push(DefDiagnostic::unconfigured_code( | 1341 | self.def_collector.def_map.diagnostics.push(DefDiagnostic::unconfigured_code( |
1344 | self.module_id, | 1342 | self.module_id, |
1345 | ast_node, | 1343 | ast_id, |
1346 | cfg.clone(), | 1344 | cfg.clone(), |
1347 | self.def_collector.cfg_options.clone(), | 1345 | self.def_collector.cfg_options.clone(), |
1348 | )); | 1346 | )); |
diff --git a/crates/hir_def/src/nameres/tests/incremental.rs b/crates/hir_def/src/nameres/tests/incremental.rs index cfbc62cc4..8981fa7c9 100644 --- a/crates/hir_def/src/nameres/tests/incremental.rs +++ b/crates/hir_def/src/nameres/tests/incremental.rs | |||
@@ -38,6 +38,9 @@ fn typing_inside_a_function_should_not_invalidate_def_map() { | |||
38 | fn foo() -> i32 { | 38 | fn foo() -> i32 { |
39 | 1 + 1 | 39 | 1 + 1 |
40 | } | 40 | } |
41 | |||
42 | #[cfg(never)] | ||
43 | fn no() {} | ||
41 | //- /foo/mod.rs | 44 | //- /foo/mod.rs |
42 | pub mod bar; | 45 | pub mod bar; |
43 | 46 | ||
@@ -53,6 +56,9 @@ fn typing_inside_a_function_should_not_invalidate_def_map() { | |||
53 | use E::*; | 56 | use E::*; |
54 | 57 | ||
55 | fn foo() -> i32 { 92 } | 58 | fn foo() -> i32 { 92 } |
59 | |||
60 | #[cfg(never)] | ||
61 | fn no() {} | ||
56 | ", | 62 | ", |
57 | ); | 63 | ); |
58 | } | 64 | } |
diff --git a/crates/hir_expand/src/db.rs b/crates/hir_expand/src/db.rs index ade57ac1b..46ebdbc74 100644 --- a/crates/hir_expand/src/db.rs +++ b/crates/hir_expand/src/db.rs | |||
@@ -75,9 +75,11 @@ pub trait AstDatabase: SourceDatabase { | |||
75 | #[salsa::transparent] | 75 | #[salsa::transparent] |
76 | fn macro_arg(&self, id: MacroCallId) -> Option<Arc<(tt::Subtree, mbe::TokenMap)>>; | 76 | fn macro_arg(&self, id: MacroCallId) -> Option<Arc<(tt::Subtree, mbe::TokenMap)>>; |
77 | fn macro_def(&self, id: MacroDefId) -> Option<Arc<(TokenExpander, mbe::TokenMap)>>; | 77 | fn macro_def(&self, id: MacroDefId) -> Option<Arc<(TokenExpander, mbe::TokenMap)>>; |
78 | fn parse_macro(&self, macro_file: MacroFile) | 78 | fn parse_macro_expansion( |
79 | -> Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)>; | 79 | &self, |
80 | fn macro_expand(&self, macro_call: MacroCallId) -> (Option<Arc<tt::Subtree>>, Option<String>); | 80 | macro_file: MacroFile, |
81 | ) -> ExpandResult<Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)>>; | ||
82 | fn macro_expand(&self, macro_call: MacroCallId) -> ExpandResult<Option<Arc<tt::Subtree>>>; | ||
81 | 83 | ||
82 | #[salsa::interned] | 84 | #[salsa::interned] |
83 | fn intern_eager_expansion(&self, eager: EagerCallLoc) -> EagerMacroId; | 85 | fn intern_eager_expansion(&self, eager: EagerCallLoc) -> EagerMacroId; |
@@ -102,23 +104,20 @@ pub fn expand_hypothetical( | |||
102 | let token_id = tmap_1.token_by_range(range)?; | 104 | let token_id = tmap_1.token_by_range(range)?; |
103 | let macro_def = expander(db, actual_macro_call)?; | 105 | let macro_def = expander(db, actual_macro_call)?; |
104 | let (node, tmap_2) = | 106 | let (node, tmap_2) = |
105 | parse_macro_with_arg(db, macro_file, Some(std::sync::Arc::new((tt, tmap_1))))?; | 107 | parse_macro_with_arg(db, macro_file, Some(std::sync::Arc::new((tt, tmap_1)))).value?; |
106 | let token_id = macro_def.0.map_id_down(token_id); | 108 | let token_id = macro_def.0.map_id_down(token_id); |
107 | let range = tmap_2.range_by_token(token_id)?.by_kind(token_to_map.kind())?; | 109 | let range = tmap_2.range_by_token(token_id)?.by_kind(token_to_map.kind())?; |
108 | let token = syntax::algo::find_covering_element(&node.syntax_node(), range).into_token()?; | 110 | let token = syntax::algo::find_covering_element(&node.syntax_node(), range).into_token()?; |
109 | Some((node.syntax_node(), token)) | 111 | Some((node.syntax_node(), token)) |
110 | } | 112 | } |
111 | 113 | ||
112 | pub(crate) fn ast_id_map(db: &dyn AstDatabase, file_id: HirFileId) -> Arc<AstIdMap> { | 114 | fn ast_id_map(db: &dyn AstDatabase, file_id: HirFileId) -> Arc<AstIdMap> { |
113 | let map = | 115 | let map = |
114 | db.parse_or_expand(file_id).map_or_else(AstIdMap::default, |it| AstIdMap::from_source(&it)); | 116 | db.parse_or_expand(file_id).map_or_else(AstIdMap::default, |it| AstIdMap::from_source(&it)); |
115 | Arc::new(map) | 117 | Arc::new(map) |
116 | } | 118 | } |
117 | 119 | ||
118 | pub(crate) fn macro_def( | 120 | fn macro_def(db: &dyn AstDatabase, id: MacroDefId) -> Option<Arc<(TokenExpander, mbe::TokenMap)>> { |
119 | db: &dyn AstDatabase, | ||
120 | id: MacroDefId, | ||
121 | ) -> Option<Arc<(TokenExpander, mbe::TokenMap)>> { | ||
122 | match id.kind { | 121 | match id.kind { |
123 | MacroDefKind::Declarative => { | 122 | MacroDefKind::Declarative => { |
124 | let macro_call = id.ast_id?.to_node(db); | 123 | let macro_call = id.ast_id?.to_node(db); |
@@ -149,7 +148,7 @@ pub(crate) fn macro_def( | |||
149 | } | 148 | } |
150 | } | 149 | } |
151 | 150 | ||
152 | pub(crate) fn macro_arg_text(db: &dyn AstDatabase, id: MacroCallId) -> Option<GreenNode> { | 151 | fn macro_arg_text(db: &dyn AstDatabase, id: MacroCallId) -> Option<GreenNode> { |
153 | let id = match id { | 152 | let id = match id { |
154 | MacroCallId::LazyMacro(id) => id, | 153 | MacroCallId::LazyMacro(id) => id, |
155 | MacroCallId::EagerMacro(_id) => { | 154 | MacroCallId::EagerMacro(_id) => { |
@@ -162,19 +161,13 @@ pub(crate) fn macro_arg_text(db: &dyn AstDatabase, id: MacroCallId) -> Option<Gr | |||
162 | Some(arg.green().clone()) | 161 | Some(arg.green().clone()) |
163 | } | 162 | } |
164 | 163 | ||
165 | pub(crate) fn macro_arg( | 164 | fn macro_arg(db: &dyn AstDatabase, id: MacroCallId) -> Option<Arc<(tt::Subtree, mbe::TokenMap)>> { |
166 | db: &dyn AstDatabase, | ||
167 | id: MacroCallId, | ||
168 | ) -> Option<Arc<(tt::Subtree, mbe::TokenMap)>> { | ||
169 | let arg = db.macro_arg_text(id)?; | 165 | let arg = db.macro_arg_text(id)?; |
170 | let (tt, tmap) = mbe::syntax_node_to_token_tree(&SyntaxNode::new_root(arg))?; | 166 | let (tt, tmap) = mbe::syntax_node_to_token_tree(&SyntaxNode::new_root(arg))?; |
171 | Some(Arc::new((tt, tmap))) | 167 | Some(Arc::new((tt, tmap))) |
172 | } | 168 | } |
173 | 169 | ||
174 | pub(crate) fn macro_expand( | 170 | fn macro_expand(db: &dyn AstDatabase, id: MacroCallId) -> ExpandResult<Option<Arc<tt::Subtree>>> { |
175 | db: &dyn AstDatabase, | ||
176 | id: MacroCallId, | ||
177 | ) -> (Option<Arc<tt::Subtree>>, Option<String>) { | ||
178 | macro_expand_with_arg(db, id, None) | 171 | macro_expand_with_arg(db, id, None) |
179 | } | 172 | } |
180 | 173 | ||
@@ -195,17 +188,19 @@ fn macro_expand_with_arg( | |||
195 | db: &dyn AstDatabase, | 188 | db: &dyn AstDatabase, |
196 | id: MacroCallId, | 189 | id: MacroCallId, |
197 | arg: Option<Arc<(tt::Subtree, mbe::TokenMap)>>, | 190 | arg: Option<Arc<(tt::Subtree, mbe::TokenMap)>>, |
198 | ) -> (Option<Arc<tt::Subtree>>, Option<String>) { | 191 | ) -> ExpandResult<Option<Arc<tt::Subtree>>> { |
199 | let lazy_id = match id { | 192 | let lazy_id = match id { |
200 | MacroCallId::LazyMacro(id) => id, | 193 | MacroCallId::LazyMacro(id) => id, |
201 | MacroCallId::EagerMacro(id) => { | 194 | MacroCallId::EagerMacro(id) => { |
202 | if arg.is_some() { | 195 | if arg.is_some() { |
203 | return ( | 196 | return ExpandResult::str_err( |
204 | None, | 197 | "hypothetical macro expansion not implemented for eager macro".to_owned(), |
205 | Some("hypothetical macro expansion not implemented for eager macro".to_owned()), | ||
206 | ); | 198 | ); |
207 | } else { | 199 | } else { |
208 | return (Some(db.lookup_intern_eager_expansion(id).subtree), None); | 200 | return ExpandResult { |
201 | value: Some(db.lookup_intern_eager_expansion(id).subtree), | ||
202 | err: None, | ||
203 | }; | ||
209 | } | 204 | } |
210 | } | 205 | } |
211 | }; | 206 | }; |
@@ -213,23 +208,27 @@ fn macro_expand_with_arg( | |||
213 | let loc = db.lookup_intern_macro(lazy_id); | 208 | let loc = db.lookup_intern_macro(lazy_id); |
214 | let macro_arg = match arg.or_else(|| db.macro_arg(id)) { | 209 | let macro_arg = match arg.or_else(|| db.macro_arg(id)) { |
215 | Some(it) => it, | 210 | Some(it) => it, |
216 | None => return (None, Some("Fail to args in to tt::TokenTree".into())), | 211 | None => return ExpandResult::str_err("Fail to args in to tt::TokenTree".into()), |
217 | }; | 212 | }; |
218 | 213 | ||
219 | let macro_rules = match db.macro_def(loc.def) { | 214 | let macro_rules = match db.macro_def(loc.def) { |
220 | Some(it) => it, | 215 | Some(it) => it, |
221 | None => return (None, Some("Fail to find macro definition".into())), | 216 | None => return ExpandResult::str_err("Fail to find macro definition".into()), |
222 | }; | 217 | }; |
223 | let ExpandResult(tt, err) = macro_rules.0.expand(db, lazy_id, ¯o_arg.0); | 218 | let ExpandResult { value: tt, err } = macro_rules.0.expand(db, lazy_id, ¯o_arg.0); |
224 | // Set a hard limit for the expanded tt | 219 | // Set a hard limit for the expanded tt |
225 | let count = tt.count(); | 220 | let count = tt.count(); |
226 | if count > 262144 { | 221 | if count > 262144 { |
227 | return (None, Some(format!("Total tokens count exceed limit : count = {}", count))); | 222 | return ExpandResult::str_err(format!( |
223 | "Total tokens count exceed limit : count = {}", | ||
224 | count | ||
225 | )); | ||
228 | } | 226 | } |
229 | (Some(Arc::new(tt)), err.map(|e| format!("{:?}", e))) | 227 | |
228 | ExpandResult { value: Some(Arc::new(tt)), err } | ||
230 | } | 229 | } |
231 | 230 | ||
232 | pub(crate) fn expand_proc_macro( | 231 | fn expand_proc_macro( |
233 | db: &dyn AstDatabase, | 232 | db: &dyn AstDatabase, |
234 | id: MacroCallId, | 233 | id: MacroCallId, |
235 | ) -> Result<tt::Subtree, mbe::ExpandError> { | 234 | ) -> Result<tt::Subtree, mbe::ExpandError> { |
@@ -256,36 +255,36 @@ pub(crate) fn expand_proc_macro( | |||
256 | expander.expand(db, lazy_id, ¯o_arg.0) | 255 | expander.expand(db, lazy_id, ¯o_arg.0) |
257 | } | 256 | } |
258 | 257 | ||
259 | pub(crate) fn parse_or_expand(db: &dyn AstDatabase, file_id: HirFileId) -> Option<SyntaxNode> { | 258 | fn parse_or_expand(db: &dyn AstDatabase, file_id: HirFileId) -> Option<SyntaxNode> { |
260 | match file_id.0 { | 259 | match file_id.0 { |
261 | HirFileIdRepr::FileId(file_id) => Some(db.parse(file_id).tree().syntax().clone()), | 260 | HirFileIdRepr::FileId(file_id) => Some(db.parse(file_id).tree().syntax().clone()), |
262 | HirFileIdRepr::MacroFile(macro_file) => { | 261 | HirFileIdRepr::MacroFile(macro_file) => { |
263 | db.parse_macro(macro_file).map(|(it, _)| it.syntax_node()) | 262 | db.parse_macro_expansion(macro_file).value.map(|(it, _)| it.syntax_node()) |
264 | } | 263 | } |
265 | } | 264 | } |
266 | } | 265 | } |
267 | 266 | ||
268 | pub(crate) fn parse_macro( | 267 | fn parse_macro_expansion( |
269 | db: &dyn AstDatabase, | 268 | db: &dyn AstDatabase, |
270 | macro_file: MacroFile, | 269 | macro_file: MacroFile, |
271 | ) -> Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)> { | 270 | ) -> ExpandResult<Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)>> { |
272 | parse_macro_with_arg(db, macro_file, None) | 271 | parse_macro_with_arg(db, macro_file, None) |
273 | } | 272 | } |
274 | 273 | ||
275 | pub fn parse_macro_with_arg( | 274 | fn parse_macro_with_arg( |
276 | db: &dyn AstDatabase, | 275 | db: &dyn AstDatabase, |
277 | macro_file: MacroFile, | 276 | macro_file: MacroFile, |
278 | arg: Option<Arc<(tt::Subtree, mbe::TokenMap)>>, | 277 | arg: Option<Arc<(tt::Subtree, mbe::TokenMap)>>, |
279 | ) -> Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)> { | 278 | ) -> ExpandResult<Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)>> { |
280 | let _p = profile::span("parse_macro_query"); | 279 | let _p = profile::span("parse_macro_query"); |
281 | 280 | ||
282 | let macro_call_id = macro_file.macro_call_id; | 281 | let macro_call_id = macro_file.macro_call_id; |
283 | let (tt, err) = if let Some(arg) = arg { | 282 | let result = if let Some(arg) = arg { |
284 | macro_expand_with_arg(db, macro_call_id, Some(arg)) | 283 | macro_expand_with_arg(db, macro_call_id, Some(arg)) |
285 | } else { | 284 | } else { |
286 | db.macro_expand(macro_call_id) | 285 | db.macro_expand(macro_call_id) |
287 | }; | 286 | }; |
288 | if let Some(err) = &err { | 287 | if let Some(err) = &result.err { |
289 | // Note: | 288 | // Note: |
290 | // The final goal we would like to make all parse_macro success, | 289 | // The final goal we would like to make all parse_macro success, |
291 | // such that the following log will not call anyway. | 290 | // such that the following log will not call anyway. |
@@ -303,40 +302,50 @@ pub fn parse_macro_with_arg( | |||
303 | .join("\n"); | 302 | .join("\n"); |
304 | 303 | ||
305 | log::warn!( | 304 | log::warn!( |
306 | "fail on macro_parse: (reason: {} macro_call: {:#}) parents: {}", | 305 | "fail on macro_parse: (reason: {:?} macro_call: {:#}) parents: {}", |
307 | err, | 306 | err, |
308 | node.value, | 307 | node.value, |
309 | parents | 308 | parents |
310 | ); | 309 | ); |
311 | } | 310 | } |
312 | _ => { | 311 | _ => { |
313 | log::warn!("fail on macro_parse: (reason: {})", err); | 312 | log::warn!("fail on macro_parse: (reason: {:?})", err); |
314 | } | 313 | } |
315 | } | 314 | } |
315 | } | ||
316 | let tt = match result.value { | ||
317 | Some(tt) => tt, | ||
318 | None => return ExpandResult { value: None, err: result.err }, | ||
316 | }; | 319 | }; |
317 | let tt = tt?; | ||
318 | 320 | ||
319 | let fragment_kind = to_fragment_kind(db, macro_call_id); | 321 | let fragment_kind = to_fragment_kind(db, macro_call_id); |
320 | 322 | ||
321 | let (parse, rev_token_map) = mbe::token_tree_to_syntax_node(&tt, fragment_kind).ok()?; | 323 | let (parse, rev_token_map) = match mbe::token_tree_to_syntax_node(&tt, fragment_kind) { |
324 | Ok(it) => it, | ||
325 | Err(err) => { | ||
326 | return ExpandResult::only_err(err); | ||
327 | } | ||
328 | }; | ||
322 | 329 | ||
323 | if err.is_none() { | 330 | match result.err { |
324 | Some((parse, Arc::new(rev_token_map))) | 331 | Some(err) => { |
325 | } else { | 332 | // Safety check for recursive identity macro. |
326 | // FIXME: | 333 | let node = parse.syntax_node(); |
327 | // In future, we should propagate the actual error with recovery information | 334 | let file: HirFileId = macro_file.into(); |
328 | // instead of ignore the error here. | 335 | let call_node = match file.call_node(db) { |
329 | 336 | Some(it) => it, | |
330 | // Safe check for recurisve identity macro | 337 | None => { |
331 | let node = parse.syntax_node(); | 338 | return ExpandResult::only_err(err); |
332 | let file: HirFileId = macro_file.into(); | 339 | } |
333 | let call_node = file.call_node(db)?; | 340 | }; |
334 | 341 | ||
335 | if !diff(&node, &call_node.value).is_empty() { | 342 | if !diff(&node, &call_node.value).is_empty() { |
336 | Some((parse, Arc::new(rev_token_map))) | 343 | ExpandResult { value: Some((parse, Arc::new(rev_token_map))), err: Some(err) } |
337 | } else { | 344 | } else { |
338 | None | 345 | return ExpandResult::only_err(err); |
346 | } | ||
339 | } | 347 | } |
348 | None => ExpandResult { value: Some((parse, Arc::new(rev_token_map))), err: None }, | ||
340 | } | 349 | } |
341 | } | 350 | } |
342 | 351 | ||
diff --git a/crates/hir_expand/src/diagnostics.rs b/crates/hir_expand/src/diagnostics.rs index 78ccc212c..1043c6aeb 100644 --- a/crates/hir_expand/src/diagnostics.rs +++ b/crates/hir_expand/src/diagnostics.rs | |||
@@ -20,7 +20,7 @@ use syntax::SyntaxNodePtr; | |||
20 | 20 | ||
21 | use crate::InFile; | 21 | use crate::InFile; |
22 | 22 | ||
23 | #[derive(Copy, Clone, PartialEq)] | 23 | #[derive(Copy, Clone, Debug, PartialEq)] |
24 | pub struct DiagnosticCode(pub &'static str); | 24 | pub struct DiagnosticCode(pub &'static str); |
25 | 25 | ||
26 | impl DiagnosticCode { | 26 | impl DiagnosticCode { |
diff --git a/crates/hir_expand/src/lib.rs b/crates/hir_expand/src/lib.rs index 17f1178ed..d5ba691b7 100644 --- a/crates/hir_expand/src/lib.rs +++ b/crates/hir_expand/src/lib.rs | |||
@@ -15,6 +15,8 @@ pub mod proc_macro; | |||
15 | pub mod quote; | 15 | pub mod quote; |
16 | pub mod eager; | 16 | pub mod eager; |
17 | 17 | ||
18 | pub use mbe::{ExpandError, ExpandResult}; | ||
19 | |||
18 | use std::hash::Hash; | 20 | use std::hash::Hash; |
19 | use std::sync::Arc; | 21 | use std::sync::Arc; |
20 | 22 | ||
@@ -144,7 +146,7 @@ impl HirFileId { | |||
144 | let def_tt = loc.def.ast_id?.to_node(db).token_tree()?; | 146 | let def_tt = loc.def.ast_id?.to_node(db).token_tree()?; |
145 | 147 | ||
146 | let macro_def = db.macro_def(loc.def)?; | 148 | let macro_def = db.macro_def(loc.def)?; |
147 | let (parse, exp_map) = db.parse_macro(macro_file)?; | 149 | let (parse, exp_map) = db.parse_macro_expansion(macro_file).value?; |
148 | let macro_arg = db.macro_arg(macro_file.macro_call_id)?; | 150 | let macro_arg = db.macro_arg(macro_file.macro_call_id)?; |
149 | 151 | ||
150 | Some(ExpansionInfo { | 152 | Some(ExpansionInfo { |
diff --git a/crates/hir_ty/src/diagnostics/match_check.rs b/crates/hir_ty/src/diagnostics/match_check.rs index 5bd03f2ac..62c329731 100644 --- a/crates/hir_ty/src/diagnostics/match_check.rs +++ b/crates/hir_ty/src/diagnostics/match_check.rs | |||
@@ -216,14 +216,14 @@ | |||
216 | //! U(P, p) := U(P, (r_1, p_2, .., p_n)) | 216 | //! U(P, p) := U(P, (r_1, p_2, .., p_n)) |
217 | //! || U(P, (r_2, p_2, .., p_n)) | 217 | //! || U(P, (r_2, p_2, .., p_n)) |
218 | //! ``` | 218 | //! ``` |
219 | use std::sync::Arc; | 219 | use std::{iter, sync::Arc}; |
220 | 220 | ||
221 | use arena::Idx; | 221 | use arena::Idx; |
222 | use hir_def::{ | 222 | use hir_def::{ |
223 | adt::VariantData, | 223 | adt::VariantData, |
224 | body::Body, | 224 | body::Body, |
225 | expr::{Expr, Literal, Pat, PatId}, | 225 | expr::{Expr, Literal, Pat, PatId}, |
226 | AdtId, EnumVariantId, VariantId, | 226 | AdtId, EnumVariantId, StructId, VariantId, |
227 | }; | 227 | }; |
228 | use smallvec::{smallvec, SmallVec}; | 228 | use smallvec::{smallvec, SmallVec}; |
229 | 229 | ||
@@ -366,16 +366,17 @@ impl PatStack { | |||
366 | 366 | ||
367 | let head_pat = head.as_pat(cx); | 367 | let head_pat = head.as_pat(cx); |
368 | let result = match (head_pat, constructor) { | 368 | let result = match (head_pat, constructor) { |
369 | (Pat::Tuple { args: ref pat_ids, ellipsis }, Constructor::Tuple { arity: _ }) => { | 369 | (Pat::Tuple { args: pat_ids, ellipsis }, &Constructor::Tuple { arity }) => { |
370 | if ellipsis.is_some() { | 370 | if let Some(ellipsis) = ellipsis { |
371 | // If there are ellipsis here, we should add the correct number of | 371 | let (pre, post) = pat_ids.split_at(ellipsis); |
372 | // Pat::Wild patterns to `pat_ids`. We should be able to use the | 372 | let n_wild_pats = arity.saturating_sub(pat_ids.len()); |
373 | // constructors arity for this, but at the time of writing we aren't | 373 | let pre_iter = pre.iter().map(Into::into); |
374 | // correctly calculating this arity when ellipsis are present. | 374 | let wildcards = iter::repeat(PatIdOrWild::Wild).take(n_wild_pats); |
375 | return Err(MatchCheckErr::NotImplemented); | 375 | let post_iter = post.iter().map(Into::into); |
376 | Some(self.replace_head_with(pre_iter.chain(wildcards).chain(post_iter))) | ||
377 | } else { | ||
378 | Some(self.replace_head_with(pat_ids.iter())) | ||
376 | } | 379 | } |
377 | |||
378 | Some(self.replace_head_with(pat_ids.iter())) | ||
379 | } | 380 | } |
380 | (Pat::Lit(lit_expr), Constructor::Bool(constructor_val)) => { | 381 | (Pat::Lit(lit_expr), Constructor::Bool(constructor_val)) => { |
381 | match cx.body.exprs[lit_expr] { | 382 | match cx.body.exprs[lit_expr] { |
@@ -390,21 +391,28 @@ impl PatStack { | |||
390 | } | 391 | } |
391 | } | 392 | } |
392 | (Pat::Wild, constructor) => Some(self.expand_wildcard(cx, constructor)?), | 393 | (Pat::Wild, constructor) => Some(self.expand_wildcard(cx, constructor)?), |
393 | (Pat::Path(_), Constructor::Enum(constructor)) => { | 394 | (Pat::Path(_), constructor) => { |
394 | // unit enum variants become `Pat::Path` | 395 | // unit enum variants become `Pat::Path` |
395 | let pat_id = head.as_id().expect("we know this isn't a wild"); | 396 | let pat_id = head.as_id().expect("we know this isn't a wild"); |
396 | if !enum_variant_matches(cx, pat_id, *constructor) { | 397 | let variant_id: VariantId = match constructor { |
398 | &Constructor::Enum(e) => e.into(), | ||
399 | &Constructor::Struct(s) => s.into(), | ||
400 | _ => return Err(MatchCheckErr::NotImplemented), | ||
401 | }; | ||
402 | if Some(variant_id) != cx.infer.variant_resolution_for_pat(pat_id) { | ||
397 | None | 403 | None |
398 | } else { | 404 | } else { |
399 | Some(self.to_tail()) | 405 | Some(self.to_tail()) |
400 | } | 406 | } |
401 | } | 407 | } |
402 | ( | 408 | (Pat::TupleStruct { args: ref pat_ids, ellipsis, .. }, constructor) => { |
403 | Pat::TupleStruct { args: ref pat_ids, ellipsis, .. }, | ||
404 | Constructor::Enum(enum_constructor), | ||
405 | ) => { | ||
406 | let pat_id = head.as_id().expect("we know this isn't a wild"); | 409 | let pat_id = head.as_id().expect("we know this isn't a wild"); |
407 | if !enum_variant_matches(cx, pat_id, *enum_constructor) { | 410 | let variant_id: VariantId = match constructor { |
411 | &Constructor::Enum(e) => e.into(), | ||
412 | &Constructor::Struct(s) => s.into(), | ||
413 | _ => return Err(MatchCheckErr::MalformedMatchArm), | ||
414 | }; | ||
415 | if Some(variant_id) != cx.infer.variant_resolution_for_pat(pat_id) { | ||
408 | None | 416 | None |
409 | } else { | 417 | } else { |
410 | let constructor_arity = constructor.arity(cx)?; | 418 | let constructor_arity = constructor.arity(cx)?; |
@@ -442,12 +450,22 @@ impl PatStack { | |||
442 | } | 450 | } |
443 | } | 451 | } |
444 | } | 452 | } |
445 | (Pat::Record { args: ref arg_patterns, .. }, Constructor::Enum(e)) => { | 453 | (Pat::Record { args: ref arg_patterns, .. }, constructor) => { |
446 | let pat_id = head.as_id().expect("we know this isn't a wild"); | 454 | let pat_id = head.as_id().expect("we know this isn't a wild"); |
447 | if !enum_variant_matches(cx, pat_id, *e) { | 455 | let (variant_id, variant_data) = match constructor { |
456 | &Constructor::Enum(e) => ( | ||
457 | e.into(), | ||
458 | cx.db.enum_data(e.parent).variants[e.local_id].variant_data.clone(), | ||
459 | ), | ||
460 | &Constructor::Struct(s) => { | ||
461 | (s.into(), cx.db.struct_data(s).variant_data.clone()) | ||
462 | } | ||
463 | _ => return Err(MatchCheckErr::MalformedMatchArm), | ||
464 | }; | ||
465 | if Some(variant_id) != cx.infer.variant_resolution_for_pat(pat_id) { | ||
448 | None | 466 | None |
449 | } else { | 467 | } else { |
450 | match cx.db.enum_data(e.parent).variants[e.local_id].variant_data.as_ref() { | 468 | match variant_data.as_ref() { |
451 | VariantData::Record(struct_field_arena) => { | 469 | VariantData::Record(struct_field_arena) => { |
452 | // Here we treat any missing fields in the record as the wild pattern, as | 470 | // Here we treat any missing fields in the record as the wild pattern, as |
453 | // if the record has ellipsis. We want to do this here even if the | 471 | // if the record has ellipsis. We want to do this here even if the |
@@ -726,6 +744,7 @@ enum Constructor { | |||
726 | Bool(bool), | 744 | Bool(bool), |
727 | Tuple { arity: usize }, | 745 | Tuple { arity: usize }, |
728 | Enum(EnumVariantId), | 746 | Enum(EnumVariantId), |
747 | Struct(StructId), | ||
729 | } | 748 | } |
730 | 749 | ||
731 | impl Constructor { | 750 | impl Constructor { |
@@ -740,6 +759,11 @@ impl Constructor { | |||
740 | VariantData::Unit => 0, | 759 | VariantData::Unit => 0, |
741 | } | 760 | } |
742 | } | 761 | } |
762 | &Constructor::Struct(s) => match cx.db.struct_data(s).variant_data.as_ref() { | ||
763 | VariantData::Tuple(struct_field_data) => struct_field_data.len(), | ||
764 | VariantData::Record(struct_field_data) => struct_field_data.len(), | ||
765 | VariantData::Unit => 0, | ||
766 | }, | ||
743 | }; | 767 | }; |
744 | 768 | ||
745 | Ok(arity) | 769 | Ok(arity) |
@@ -748,7 +772,7 @@ impl Constructor { | |||
748 | fn all_constructors(&self, cx: &MatchCheckCtx) -> Vec<Constructor> { | 772 | fn all_constructors(&self, cx: &MatchCheckCtx) -> Vec<Constructor> { |
749 | match self { | 773 | match self { |
750 | Constructor::Bool(_) => vec![Constructor::Bool(true), Constructor::Bool(false)], | 774 | Constructor::Bool(_) => vec![Constructor::Bool(true), Constructor::Bool(false)], |
751 | Constructor::Tuple { .. } => vec![*self], | 775 | Constructor::Tuple { .. } | Constructor::Struct(_) => vec![*self], |
752 | Constructor::Enum(e) => cx | 776 | Constructor::Enum(e) => cx |
753 | .db | 777 | .db |
754 | .enum_data(e.parent) | 778 | .enum_data(e.parent) |
@@ -767,10 +791,11 @@ impl Constructor { | |||
767 | fn pat_constructor(cx: &MatchCheckCtx, pat: PatIdOrWild) -> MatchCheckResult<Option<Constructor>> { | 791 | fn pat_constructor(cx: &MatchCheckCtx, pat: PatIdOrWild) -> MatchCheckResult<Option<Constructor>> { |
768 | let res = match pat.as_pat(cx) { | 792 | let res = match pat.as_pat(cx) { |
769 | Pat::Wild => None, | 793 | Pat::Wild => None, |
770 | // FIXME somehow create the Tuple constructor with the proper arity. If there are | 794 | Pat::Tuple { .. } => { |
771 | // ellipsis, the arity is not equal to the number of patterns. | 795 | let pat_id = pat.as_id().expect("we already know this pattern is not a wild"); |
772 | Pat::Tuple { args: pats, ellipsis } if ellipsis.is_none() => { | 796 | Some(Constructor::Tuple { |
773 | Some(Constructor::Tuple { arity: pats.len() }) | 797 | arity: cx.infer.type_of_pat[pat_id].as_tuple().ok_or(MatchCheckErr::Unknown)?.len(), |
798 | }) | ||
774 | } | 799 | } |
775 | Pat::Lit(lit_expr) => match cx.body.exprs[lit_expr] { | 800 | Pat::Lit(lit_expr) => match cx.body.exprs[lit_expr] { |
776 | Expr::Literal(Literal::Bool(val)) => Some(Constructor::Bool(val)), | 801 | Expr::Literal(Literal::Bool(val)) => Some(Constructor::Bool(val)), |
@@ -784,6 +809,7 @@ fn pat_constructor(cx: &MatchCheckCtx, pat: PatIdOrWild) -> MatchCheckResult<Opt | |||
784 | VariantId::EnumVariantId(enum_variant_id) => { | 809 | VariantId::EnumVariantId(enum_variant_id) => { |
785 | Some(Constructor::Enum(enum_variant_id)) | 810 | Some(Constructor::Enum(enum_variant_id)) |
786 | } | 811 | } |
812 | VariantId::StructId(struct_id) => Some(Constructor::Struct(struct_id)), | ||
787 | _ => return Err(MatchCheckErr::NotImplemented), | 813 | _ => return Err(MatchCheckErr::NotImplemented), |
788 | } | 814 | } |
789 | } | 815 | } |
@@ -828,13 +854,13 @@ fn all_constructors_covered( | |||
828 | 854 | ||
829 | false | 855 | false |
830 | }), | 856 | }), |
857 | &Constructor::Struct(s) => used_constructors.iter().any(|constructor| match constructor { | ||
858 | &Constructor::Struct(sid) => sid == s, | ||
859 | _ => false, | ||
860 | }), | ||
831 | } | 861 | } |
832 | } | 862 | } |
833 | 863 | ||
834 | fn enum_variant_matches(cx: &MatchCheckCtx, pat_id: PatId, enum_variant_id: EnumVariantId) -> bool { | ||
835 | Some(enum_variant_id.into()) == cx.infer.variant_resolution_for_pat(pat_id) | ||
836 | } | ||
837 | |||
838 | #[cfg(test)] | 864 | #[cfg(test)] |
839 | mod tests { | 865 | mod tests { |
840 | use crate::diagnostics::tests::check_diagnostics; | 866 | use crate::diagnostics::tests::check_diagnostics; |
@@ -846,8 +872,8 @@ mod tests { | |||
846 | fn main() { | 872 | fn main() { |
847 | match () { } | 873 | match () { } |
848 | //^^ Missing match arm | 874 | //^^ Missing match arm |
849 | match (()) { } | 875 | match (()) { } |
850 | //^^^^ Missing match arm | 876 | //^^^^ Missing match arm |
851 | 877 | ||
852 | match () { _ => (), } | 878 | match () { _ => (), } |
853 | match () { () => (), } | 879 | match () { () => (), } |
@@ -1352,6 +1378,123 @@ fn main() { | |||
1352 | ); | 1378 | ); |
1353 | } | 1379 | } |
1354 | 1380 | ||
1381 | #[test] | ||
1382 | fn tuple_of_bools_with_ellipsis_at_end_missing_arm() { | ||
1383 | check_diagnostics( | ||
1384 | r#" | ||
1385 | fn main() { | ||
1386 | match (false, true, false) { | ||
1387 | //^^^^^^^^^^^^^^^^^^^^ Missing match arm | ||
1388 | (false, ..) => (), | ||
1389 | } | ||
1390 | }"#, | ||
1391 | ); | ||
1392 | } | ||
1393 | |||
1394 | #[test] | ||
1395 | fn tuple_of_bools_with_ellipsis_at_beginning_missing_arm() { | ||
1396 | check_diagnostics( | ||
1397 | r#" | ||
1398 | fn main() { | ||
1399 | match (false, true, false) { | ||
1400 | //^^^^^^^^^^^^^^^^^^^^ Missing match arm | ||
1401 | (.., false) => (), | ||
1402 | } | ||
1403 | }"#, | ||
1404 | ); | ||
1405 | } | ||
1406 | |||
1407 | #[test] | ||
1408 | fn tuple_of_bools_with_ellipsis_in_middle_missing_arm() { | ||
1409 | check_diagnostics( | ||
1410 | r#" | ||
1411 | fn main() { | ||
1412 | match (false, true, false) { | ||
1413 | //^^^^^^^^^^^^^^^^^^^^ Missing match arm | ||
1414 | (true, .., false) => (), | ||
1415 | } | ||
1416 | }"#, | ||
1417 | ); | ||
1418 | } | ||
1419 | |||
1420 | #[test] | ||
1421 | fn record_struct() { | ||
1422 | check_diagnostics( | ||
1423 | r#"struct Foo { a: bool } | ||
1424 | fn main(f: Foo) { | ||
1425 | match f {} | ||
1426 | //^ Missing match arm | ||
1427 | match f { Foo { a: true } => () } | ||
1428 | //^ Missing match arm | ||
1429 | match &f { Foo { a: true } => () } | ||
1430 | //^^ Missing match arm | ||
1431 | match f { Foo { a: _ } => () } | ||
1432 | match f { | ||
1433 | Foo { a: true } => (), | ||
1434 | Foo { a: false } => (), | ||
1435 | } | ||
1436 | match &f { | ||
1437 | Foo { a: true } => (), | ||
1438 | Foo { a: false } => (), | ||
1439 | } | ||
1440 | } | ||
1441 | "#, | ||
1442 | ); | ||
1443 | } | ||
1444 | |||
1445 | #[test] | ||
1446 | fn tuple_struct() { | ||
1447 | check_diagnostics( | ||
1448 | r#"struct Foo(bool); | ||
1449 | fn main(f: Foo) { | ||
1450 | match f {} | ||
1451 | //^ Missing match arm | ||
1452 | match f { Foo(true) => () } | ||
1453 | //^ Missing match arm | ||
1454 | match f { | ||
1455 | Foo(true) => (), | ||
1456 | Foo(false) => (), | ||
1457 | } | ||
1458 | } | ||
1459 | "#, | ||
1460 | ); | ||
1461 | } | ||
1462 | |||
1463 | #[test] | ||
1464 | fn unit_struct() { | ||
1465 | check_diagnostics( | ||
1466 | r#"struct Foo; | ||
1467 | fn main(f: Foo) { | ||
1468 | match f {} | ||
1469 | //^ Missing match arm | ||
1470 | match f { Foo => () } | ||
1471 | } | ||
1472 | "#, | ||
1473 | ); | ||
1474 | } | ||
1475 | |||
1476 | #[test] | ||
1477 | fn record_struct_ellipsis() { | ||
1478 | check_diagnostics( | ||
1479 | r#"struct Foo { foo: bool, bar: bool } | ||
1480 | fn main(f: Foo) { | ||
1481 | match f { Foo { foo: true, .. } => () } | ||
1482 | //^ Missing match arm | ||
1483 | match f { | ||
1484 | //^ Missing match arm | ||
1485 | Foo { foo: true, .. } => (), | ||
1486 | Foo { bar: false, .. } => () | ||
1487 | } | ||
1488 | match f { Foo { .. } => () } | ||
1489 | match f { | ||
1490 | Foo { foo: true, .. } => (), | ||
1491 | Foo { foo: false, .. } => () | ||
1492 | } | ||
1493 | } | ||
1494 | "#, | ||
1495 | ); | ||
1496 | } | ||
1497 | |||
1355 | mod false_negatives { | 1498 | mod false_negatives { |
1356 | //! The implementation of match checking here is a work in progress. As we roll this out, we | 1499 | //! The implementation of match checking here is a work in progress. As we roll this out, we |
1357 | //! prefer false negatives to false positives (ideally there would be no false positives). This | 1500 | //! prefer false negatives to false positives (ideally there would be no false positives). This |
@@ -1393,46 +1536,5 @@ fn main() { | |||
1393 | "#, | 1536 | "#, |
1394 | ); | 1537 | ); |
1395 | } | 1538 | } |
1396 | |||
1397 | #[test] | ||
1398 | fn tuple_of_bools_with_ellipsis_at_end_missing_arm() { | ||
1399 | // We don't currently handle tuple patterns with ellipsis. | ||
1400 | check_diagnostics( | ||
1401 | r#" | ||
1402 | fn main() { | ||
1403 | match (false, true, false) { | ||
1404 | (false, ..) => (), | ||
1405 | } | ||
1406 | } | ||
1407 | "#, | ||
1408 | ); | ||
1409 | } | ||
1410 | |||
1411 | #[test] | ||
1412 | fn tuple_of_bools_with_ellipsis_at_beginning_missing_arm() { | ||
1413 | // We don't currently handle tuple patterns with ellipsis. | ||
1414 | check_diagnostics( | ||
1415 | r#" | ||
1416 | fn main() { | ||
1417 | match (false, true, false) { | ||
1418 | (.., false) => (), | ||
1419 | } | ||
1420 | } | ||
1421 | "#, | ||
1422 | ); | ||
1423 | } | ||
1424 | |||
1425 | #[test] | ||
1426 | fn struct_missing_arm() { | ||
1427 | // We don't currently handle structs. | ||
1428 | check_diagnostics( | ||
1429 | r#" | ||
1430 | struct Foo { a: bool } | ||
1431 | fn main(f: Foo) { | ||
1432 | match f { Foo { a: true } => () } | ||
1433 | } | ||
1434 | "#, | ||
1435 | ); | ||
1436 | } | ||
1437 | } | 1539 | } |
1438 | } | 1540 | } |
diff --git a/crates/hir_ty/src/infer/pat.rs b/crates/hir_ty/src/infer/pat.rs index cde2ab82b..b70ec55eb 100644 --- a/crates/hir_ty/src/infer/pat.rs +++ b/crates/hir_ty/src/infer/pat.rs | |||
@@ -23,6 +23,7 @@ impl<'a> InferenceContext<'a> { | |||
23 | expected: &Ty, | 23 | expected: &Ty, |
24 | default_bm: BindingMode, | 24 | default_bm: BindingMode, |
25 | id: PatId, | 25 | id: PatId, |
26 | ellipsis: Option<usize>, | ||
26 | ) -> Ty { | 27 | ) -> Ty { |
27 | let (ty, def) = self.resolve_variant(path); | 28 | let (ty, def) = self.resolve_variant(path); |
28 | let var_data = def.map(|it| variant_data(self.db.upcast(), it)); | 29 | let var_data = def.map(|it| variant_data(self.db.upcast(), it)); |
@@ -34,8 +35,15 @@ impl<'a> InferenceContext<'a> { | |||
34 | let substs = ty.substs().unwrap_or_else(Substs::empty); | 35 | let substs = ty.substs().unwrap_or_else(Substs::empty); |
35 | 36 | ||
36 | let field_tys = def.map(|it| self.db.field_types(it)).unwrap_or_default(); | 37 | let field_tys = def.map(|it| self.db.field_types(it)).unwrap_or_default(); |
38 | let (pre, post) = match ellipsis { | ||
39 | Some(idx) => subpats.split_at(idx), | ||
40 | None => (&subpats[..], &[][..]), | ||
41 | }; | ||
42 | let post_idx_offset = field_tys.iter().count() - post.len(); | ||
37 | 43 | ||
38 | for (i, &subpat) in subpats.iter().enumerate() { | 44 | let pre_iter = pre.iter().enumerate(); |
45 | let post_iter = (post_idx_offset..).zip(post.iter()); | ||
46 | for (i, &subpat) in pre_iter.chain(post_iter) { | ||
39 | let expected_ty = var_data | 47 | let expected_ty = var_data |
40 | .as_ref() | 48 | .as_ref() |
41 | .and_then(|d| d.field(&Name::new_tuple_field(i))) | 49 | .and_then(|d| d.field(&Name::new_tuple_field(i))) |
@@ -111,20 +119,29 @@ impl<'a> InferenceContext<'a> { | |||
111 | let expected = expected; | 119 | let expected = expected; |
112 | 120 | ||
113 | let ty = match &body[pat] { | 121 | let ty = match &body[pat] { |
114 | Pat::Tuple { ref args, .. } => { | 122 | &Pat::Tuple { ref args, ellipsis } => { |
115 | let expectations = match expected.as_tuple() { | 123 | let expectations = match expected.as_tuple() { |
116 | Some(parameters) => &*parameters.0, | 124 | Some(parameters) => &*parameters.0, |
117 | _ => &[], | 125 | _ => &[], |
118 | }; | 126 | }; |
119 | let expectations_iter = expectations.iter().chain(repeat(&Ty::Unknown)); | ||
120 | 127 | ||
121 | let inner_tys = args | 128 | let (pre, post) = match ellipsis { |
122 | .iter() | 129 | Some(idx) => args.split_at(idx), |
123 | .zip(expectations_iter) | 130 | None => (&args[..], &[][..]), |
124 | .map(|(&pat, ty)| self.infer_pat(pat, ty, default_bm)) | 131 | }; |
125 | .collect(); | 132 | let n_uncovered_patterns = expectations.len().saturating_sub(args.len()); |
133 | let mut expectations_iter = expectations.iter().chain(repeat(&Ty::Unknown)); | ||
134 | let mut infer_pat = |(&pat, ty)| self.infer_pat(pat, ty, default_bm); | ||
135 | |||
136 | let mut inner_tys = Vec::with_capacity(n_uncovered_patterns + args.len()); | ||
137 | inner_tys.extend(pre.iter().zip(expectations_iter.by_ref()).map(&mut infer_pat)); | ||
138 | inner_tys.extend(expectations_iter.by_ref().take(n_uncovered_patterns).cloned()); | ||
139 | inner_tys.extend(post.iter().zip(expectations_iter).map(infer_pat)); | ||
126 | 140 | ||
127 | Ty::apply(TypeCtor::Tuple { cardinality: args.len() as u16 }, Substs(inner_tys)) | 141 | Ty::apply( |
142 | TypeCtor::Tuple { cardinality: inner_tys.len() as u16 }, | ||
143 | Substs(inner_tys.into()), | ||
144 | ) | ||
128 | } | 145 | } |
129 | Pat::Or(ref pats) => { | 146 | Pat::Or(ref pats) => { |
130 | if let Some((first_pat, rest)) = pats.split_first() { | 147 | if let Some((first_pat, rest)) = pats.split_first() { |
@@ -150,9 +167,14 @@ impl<'a> InferenceContext<'a> { | |||
150 | let subty = self.infer_pat(*pat, expectation, default_bm); | 167 | let subty = self.infer_pat(*pat, expectation, default_bm); |
151 | Ty::apply_one(TypeCtor::Ref(*mutability), subty) | 168 | Ty::apply_one(TypeCtor::Ref(*mutability), subty) |
152 | } | 169 | } |
153 | Pat::TupleStruct { path: p, args: subpats, .. } => { | 170 | Pat::TupleStruct { path: p, args: subpats, ellipsis } => self.infer_tuple_struct_pat( |
154 | self.infer_tuple_struct_pat(p.as_ref(), subpats, expected, default_bm, pat) | 171 | p.as_ref(), |
155 | } | 172 | subpats, |
173 | expected, | ||
174 | default_bm, | ||
175 | pat, | ||
176 | *ellipsis, | ||
177 | ), | ||
156 | Pat::Record { path: p, args: fields, ellipsis: _ } => { | 178 | Pat::Record { path: p, args: fields, ellipsis: _ } => { |
157 | self.infer_record_pat(p.as_ref(), fields, expected, default_bm, pat) | 179 | self.infer_record_pat(p.as_ref(), fields, expected, default_bm, pat) |
158 | } | 180 | } |
diff --git a/crates/hir_ty/src/tests/patterns.rs b/crates/hir_ty/src/tests/patterns.rs index 6a965ac4f..5a5f48fd0 100644 --- a/crates/hir_ty/src/tests/patterns.rs +++ b/crates/hir_ty/src/tests/patterns.rs | |||
@@ -679,3 +679,98 @@ fn box_pattern() { | |||
679 | "#]], | 679 | "#]], |
680 | ); | 680 | ); |
681 | } | 681 | } |
682 | |||
683 | #[test] | ||
684 | fn tuple_ellipsis_pattern() { | ||
685 | check_infer( | ||
686 | r#" | ||
687 | fn foo(tuple: (u8, i16, f32)) { | ||
688 | match tuple { | ||
689 | (.., b, c) => {}, | ||
690 | (a, .., c) => {}, | ||
691 | (a, b, ..) => {}, | ||
692 | (a, b) => {/*too short*/} | ||
693 | (a, b, c, d) => {/*too long*/} | ||
694 | _ => {} | ||
695 | } | ||
696 | }"#, | ||
697 | expect![[r#" | ||
698 | 7..12 'tuple': (u8, i16, f32) | ||
699 | 30..224 '{ ... } }': () | ||
700 | 36..222 'match ... }': () | ||
701 | 42..47 'tuple': (u8, i16, f32) | ||
702 | 58..68 '(.., b, c)': (u8, i16, f32) | ||
703 | 63..64 'b': i16 | ||
704 | 66..67 'c': f32 | ||
705 | 72..74 '{}': () | ||
706 | 84..94 '(a, .., c)': (u8, i16, f32) | ||
707 | 85..86 'a': u8 | ||
708 | 92..93 'c': f32 | ||
709 | 98..100 '{}': () | ||
710 | 110..120 '(a, b, ..)': (u8, i16, f32) | ||
711 | 111..112 'a': u8 | ||
712 | 114..115 'b': i16 | ||
713 | 124..126 '{}': () | ||
714 | 136..142 '(a, b)': (u8, i16, f32) | ||
715 | 137..138 'a': u8 | ||
716 | 140..141 'b': i16 | ||
717 | 146..161 '{/*too short*/}': () | ||
718 | 170..182 '(a, b, c, d)': (u8, i16, f32, {unknown}) | ||
719 | 171..172 'a': u8 | ||
720 | 174..175 'b': i16 | ||
721 | 177..178 'c': f32 | ||
722 | 180..181 'd': {unknown} | ||
723 | 186..200 '{/*too long*/}': () | ||
724 | 209..210 '_': (u8, i16, f32) | ||
725 | 214..216 '{}': () | ||
726 | "#]], | ||
727 | ); | ||
728 | } | ||
729 | |||
730 | #[test] | ||
731 | fn tuple_struct_ellipsis_pattern() { | ||
732 | check_infer( | ||
733 | r#" | ||
734 | struct Tuple(u8, i16, f32); | ||
735 | fn foo(tuple: Tuple) { | ||
736 | match tuple { | ||
737 | Tuple(.., b, c) => {}, | ||
738 | Tuple(a, .., c) => {}, | ||
739 | Tuple(a, b, ..) => {}, | ||
740 | Tuple(a, b) => {/*too short*/} | ||
741 | Tuple(a, b, c, d) => {/*too long*/} | ||
742 | _ => {} | ||
743 | } | ||
744 | }"#, | ||
745 | expect![[r#" | ||
746 | 35..40 'tuple': Tuple | ||
747 | 49..268 '{ ... } }': () | ||
748 | 55..266 'match ... }': () | ||
749 | 61..66 'tuple': Tuple | ||
750 | 77..92 'Tuple(.., b, c)': Tuple | ||
751 | 87..88 'b': i16 | ||
752 | 90..91 'c': f32 | ||
753 | 96..98 '{}': () | ||
754 | 108..123 'Tuple(a, .., c)': Tuple | ||
755 | 114..115 'a': u8 | ||
756 | 121..122 'c': f32 | ||
757 | 127..129 '{}': () | ||
758 | 139..154 'Tuple(a, b, ..)': Tuple | ||
759 | 145..146 'a': u8 | ||
760 | 148..149 'b': i16 | ||
761 | 158..160 '{}': () | ||
762 | 170..181 'Tuple(a, b)': Tuple | ||
763 | 176..177 'a': u8 | ||
764 | 179..180 'b': i16 | ||
765 | 185..200 '{/*too short*/}': () | ||
766 | 209..226 'Tuple(... c, d)': Tuple | ||
767 | 215..216 'a': u8 | ||
768 | 218..219 'b': i16 | ||
769 | 221..222 'c': f32 | ||
770 | 224..225 'd': {unknown} | ||
771 | 230..244 '{/*too long*/}': () | ||
772 | 253..254 '_': Tuple | ||
773 | 258..260 '{}': () | ||
774 | "#]], | ||
775 | ); | ||
776 | } | ||
diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs index 1c7f02763..3df73ed4f 100644 --- a/crates/ide/src/diagnostics.rs +++ b/crates/ide/src/diagnostics.rs | |||
@@ -10,7 +10,7 @@ mod field_shorthand; | |||
10 | use std::cell::RefCell; | 10 | use std::cell::RefCell; |
11 | 11 | ||
12 | use hir::{ | 12 | use hir::{ |
13 | diagnostics::{Diagnostic as _, DiagnosticSinkBuilder}, | 13 | diagnostics::{Diagnostic as _, DiagnosticCode, DiagnosticSinkBuilder}, |
14 | Semantics, | 14 | Semantics, |
15 | }; | 15 | }; |
16 | use ide_db::base_db::SourceDatabase; | 16 | use ide_db::base_db::SourceDatabase; |
@@ -35,15 +35,23 @@ pub struct Diagnostic { | |||
35 | pub severity: Severity, | 35 | pub severity: Severity, |
36 | pub fix: Option<Fix>, | 36 | pub fix: Option<Fix>, |
37 | pub unused: bool, | 37 | pub unused: bool, |
38 | pub code: Option<DiagnosticCode>, | ||
38 | } | 39 | } |
39 | 40 | ||
40 | impl Diagnostic { | 41 | impl Diagnostic { |
41 | fn error(range: TextRange, message: String) -> Self { | 42 | fn error(range: TextRange, message: String) -> Self { |
42 | Self { message, range, severity: Severity::Error, fix: None, unused: false } | 43 | Self { message, range, severity: Severity::Error, fix: None, unused: false, code: None } |
43 | } | 44 | } |
44 | 45 | ||
45 | fn hint(range: TextRange, message: String) -> Self { | 46 | fn hint(range: TextRange, message: String) -> Self { |
46 | Self { message, range, severity: Severity::WeakWarning, fix: None, unused: false } | 47 | Self { |
48 | message, | ||
49 | range, | ||
50 | severity: Severity::WeakWarning, | ||
51 | fix: None, | ||
52 | unused: false, | ||
53 | code: None, | ||
54 | } | ||
47 | } | 55 | } |
48 | 56 | ||
49 | fn with_fix(self, fix: Option<Fix>) -> Self { | 57 | fn with_fix(self, fix: Option<Fix>) -> Self { |
@@ -53,6 +61,10 @@ impl Diagnostic { | |||
53 | fn with_unused(self, unused: bool) -> Self { | 61 | fn with_unused(self, unused: bool) -> Self { |
54 | Self { unused, ..self } | 62 | Self { unused, ..self } |
55 | } | 63 | } |
64 | |||
65 | fn with_code(self, code: Option<DiagnosticCode>) -> Self { | ||
66 | Self { code, ..self } | ||
67 | } | ||
56 | } | 68 | } |
57 | 69 | ||
58 | #[derive(Debug)] | 70 | #[derive(Debug)] |
@@ -126,7 +138,8 @@ pub(crate) fn diagnostics( | |||
126 | // Override severity and mark as unused. | 138 | // Override severity and mark as unused. |
127 | res.borrow_mut().push( | 139 | res.borrow_mut().push( |
128 | Diagnostic::hint(sema.diagnostics_display_range(d).range, d.message()) | 140 | Diagnostic::hint(sema.diagnostics_display_range(d).range, d.message()) |
129 | .with_unused(true), | 141 | .with_unused(true) |
142 | .with_code(Some(d.code())), | ||
130 | ); | 143 | ); |
131 | }) | 144 | }) |
132 | // Only collect experimental diagnostics when they're enabled. | 145 | // Only collect experimental diagnostics when they're enabled. |
@@ -137,8 +150,10 @@ pub(crate) fn diagnostics( | |||
137 | let mut sink = sink_builder | 150 | let mut sink = sink_builder |
138 | // Diagnostics not handled above get no fix and default treatment. | 151 | // Diagnostics not handled above get no fix and default treatment. |
139 | .build(|d| { | 152 | .build(|d| { |
140 | res.borrow_mut() | 153 | res.borrow_mut().push( |
141 | .push(Diagnostic::error(sema.diagnostics_display_range(d).range, d.message())); | 154 | Diagnostic::error(sema.diagnostics_display_range(d).range, d.message()) |
155 | .with_code(Some(d.code())), | ||
156 | ); | ||
142 | }); | 157 | }); |
143 | 158 | ||
144 | if let Some(m) = sema.to_module_def(file_id) { | 159 | if let Some(m) = sema.to_module_def(file_id) { |
@@ -149,11 +164,15 @@ pub(crate) fn diagnostics( | |||
149 | } | 164 | } |
150 | 165 | ||
151 | fn diagnostic_with_fix<D: DiagnosticWithFix>(d: &D, sema: &Semantics<RootDatabase>) -> Diagnostic { | 166 | fn diagnostic_with_fix<D: DiagnosticWithFix>(d: &D, sema: &Semantics<RootDatabase>) -> Diagnostic { |
152 | Diagnostic::error(sema.diagnostics_display_range(d).range, d.message()).with_fix(d.fix(&sema)) | 167 | Diagnostic::error(sema.diagnostics_display_range(d).range, d.message()) |
168 | .with_fix(d.fix(&sema)) | ||
169 | .with_code(Some(d.code())) | ||
153 | } | 170 | } |
154 | 171 | ||
155 | fn warning_with_fix<D: DiagnosticWithFix>(d: &D, sema: &Semantics<RootDatabase>) -> Diagnostic { | 172 | fn warning_with_fix<D: DiagnosticWithFix>(d: &D, sema: &Semantics<RootDatabase>) -> Diagnostic { |
156 | Diagnostic::hint(sema.diagnostics_display_range(d).range, d.message()).with_fix(d.fix(&sema)) | 173 | Diagnostic::hint(sema.diagnostics_display_range(d).range, d.message()) |
174 | .with_fix(d.fix(&sema)) | ||
175 | .with_code(Some(d.code())) | ||
157 | } | 176 | } |
158 | 177 | ||
159 | fn check_unnecessary_braces_in_use_statement( | 178 | fn check_unnecessary_braces_in_use_statement( |
@@ -589,6 +608,11 @@ fn test_fn() { | |||
589 | }, | 608 | }, |
590 | ), | 609 | ), |
591 | unused: false, | 610 | unused: false, |
611 | code: Some( | ||
612 | DiagnosticCode( | ||
613 | "unresolved-module", | ||
614 | ), | ||
615 | ), | ||
592 | }, | 616 | }, |
593 | ] | 617 | ] |
594 | "#]], | 618 | "#]], |
diff --git a/crates/ide/src/fn_references.rs b/crates/ide/src/fn_references.rs index 459f201ed..5cbbe306e 100644 --- a/crates/ide/src/fn_references.rs +++ b/crates/ide/src/fn_references.rs | |||
@@ -1,11 +1,12 @@ | |||
1 | //! This module implements a methods and free functions search in the specified file. | 1 | //! This module implements a methods and free functions search in the specified file. |
2 | //! We have to skip tests, so cannot reuse file_structure module. | 2 | //! We have to skip tests, so cannot reuse file_structure module. |
3 | 3 | ||
4 | use assists::utils::test_related_attribute; | ||
4 | use hir::Semantics; | 5 | use hir::Semantics; |
5 | use ide_db::RootDatabase; | 6 | use ide_db::RootDatabase; |
6 | use syntax::{ast, ast::NameOwner, AstNode, SyntaxNode}; | 7 | use syntax::{ast, ast::NameOwner, AstNode, SyntaxNode}; |
7 | 8 | ||
8 | use crate::{runnables::has_test_related_attribute, FileId, FileRange}; | 9 | use crate::{FileId, FileRange}; |
9 | 10 | ||
10 | pub(crate) fn find_all_methods(db: &RootDatabase, file_id: FileId) -> Vec<FileRange> { | 11 | pub(crate) fn find_all_methods(db: &RootDatabase, file_id: FileId) -> Vec<FileRange> { |
11 | let sema = Semantics::new(db); | 12 | let sema = Semantics::new(db); |
@@ -15,7 +16,7 @@ pub(crate) fn find_all_methods(db: &RootDatabase, file_id: FileId) -> Vec<FileRa | |||
15 | 16 | ||
16 | fn method_range(item: SyntaxNode, file_id: FileId) -> Option<FileRange> { | 17 | fn method_range(item: SyntaxNode, file_id: FileId) -> Option<FileRange> { |
17 | ast::Fn::cast(item).and_then(|fn_def| { | 18 | ast::Fn::cast(item).and_then(|fn_def| { |
18 | if has_test_related_attribute(&fn_def) { | 19 | if test_related_attribute(&fn_def).is_some() { |
19 | None | 20 | None |
20 | } else { | 21 | } else { |
21 | fn_def.name().map(|name| FileRange { file_id, range: name.syntax().text_range() }) | 22 | fn_def.name().map(|name| FileRange { file_id, range: name.syntax().text_range() }) |
diff --git a/crates/ide/src/references/rename.rs b/crates/ide/src/references/rename.rs index b8725693a..91c64bd4a 100644 --- a/crates/ide/src/references/rename.rs +++ b/crates/ide/src/references/rename.rs | |||
@@ -1,4 +1,9 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | use std::{ | ||
3 | convert::TryInto, | ||
4 | error::Error, | ||
5 | fmt::{self, Display}, | ||
6 | }; | ||
2 | 7 | ||
3 | use hir::{Module, ModuleDef, ModuleSource, Semantics}; | 8 | use hir::{Module, ModuleDef, ModuleSource, Semantics}; |
4 | use ide_db::base_db::{FileRange, SourceDatabaseExt}; | 9 | use ide_db::base_db::{FileRange, SourceDatabaseExt}; |
@@ -6,12 +11,6 @@ use ide_db::{ | |||
6 | defs::{Definition, NameClass, NameRefClass}, | 11 | defs::{Definition, NameClass, NameRefClass}, |
7 | RootDatabase, | 12 | RootDatabase, |
8 | }; | 13 | }; |
9 | |||
10 | use std::{ | ||
11 | convert::TryInto, | ||
12 | error::Error, | ||
13 | fmt::{self, Display}, | ||
14 | }; | ||
15 | use syntax::{ | 14 | use syntax::{ |
16 | algo::find_node_at_offset, | 15 | algo::find_node_at_offset, |
17 | ast::{self, NameOwner}, | 16 | ast::{self, NameOwner}, |
diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs index 2bd0e86e5..e15411777 100644 --- a/crates/ide/src/runnables.rs +++ b/crates/ide/src/runnables.rs | |||
@@ -1,5 +1,6 @@ | |||
1 | use std::fmt; | 1 | use std::fmt; |
2 | 2 | ||
3 | use assists::utils::test_related_attribute; | ||
3 | use cfg::CfgExpr; | 4 | use cfg::CfgExpr; |
4 | use hir::{AsAssocItem, Attrs, HirFileId, InFile, Semantics}; | 5 | use hir::{AsAssocItem, Attrs, HirFileId, InFile, Semantics}; |
5 | use ide_db::RootDatabase; | 6 | use ide_db::RootDatabase; |
@@ -156,7 +157,7 @@ fn runnable_fn( | |||
156 | None => TestId::Name(name_string), | 157 | None => TestId::Name(name_string), |
157 | }; | 158 | }; |
158 | 159 | ||
159 | if has_test_related_attribute(&fn_def) { | 160 | if test_related_attribute(&fn_def).is_some() { |
160 | let attr = TestAttr::from_fn(&fn_def); | 161 | let attr = TestAttr::from_fn(&fn_def); |
161 | RunnableKind::Test { test_id, attr } | 162 | RunnableKind::Test { test_id, attr } |
162 | } else if fn_def.has_atom_attr("bench") { | 163 | } else if fn_def.has_atom_attr("bench") { |
@@ -235,20 +236,6 @@ impl TestAttr { | |||
235 | } | 236 | } |
236 | } | 237 | } |
237 | 238 | ||
238 | /// This is a method with a heuristics to support test methods annotated with custom test annotations, such as | ||
239 | /// `#[test_case(...)]`, `#[tokio::test]` and similar. | ||
240 | /// Also a regular `#[test]` annotation is supported. | ||
241 | /// | ||
242 | /// It may produce false positives, for example, `#[wasm_bindgen_test]` requires a different command to run the test, | ||
243 | /// but it's better than not to have the runnables for the tests at all. | ||
244 | pub(crate) fn has_test_related_attribute(fn_def: &ast::Fn) -> bool { | ||
245 | fn_def | ||
246 | .attrs() | ||
247 | .filter_map(|attr| attr.path()) | ||
248 | .map(|path| path.syntax().to_string().to_lowercase()) | ||
249 | .any(|attribute_text| attribute_text.contains("test")) | ||
250 | } | ||
251 | |||
252 | const RUSTDOC_FENCE: &str = "```"; | 239 | const RUSTDOC_FENCE: &str = "```"; |
253 | const RUSTDOC_CODE_BLOCK_ATTRIBUTES_RUNNABLE: &[&str] = | 240 | const RUSTDOC_CODE_BLOCK_ATTRIBUTES_RUNNABLE: &[&str] = |
254 | &["", "rust", "should_panic", "edition2015", "edition2018"]; | 241 | &["", "rust", "should_panic", "edition2015", "edition2018"]; |
@@ -307,7 +294,7 @@ fn has_test_function_or_multiple_test_submodules(module: &ast::Module) -> bool { | |||
307 | for item in item_list.items() { | 294 | for item in item_list.items() { |
308 | match item { | 295 | match item { |
309 | ast::Item::Fn(f) => { | 296 | ast::Item::Fn(f) => { |
310 | if has_test_related_attribute(&f) { | 297 | if test_related_attribute(&f).is_some() { |
311 | return true; | 298 | return true; |
312 | } | 299 | } |
313 | } | 300 | } |
diff --git a/crates/ide/src/status.rs b/crates/ide/src/status.rs index 8e91c99d7..e10d7c3a4 100644 --- a/crates/ide/src/status.rs +++ b/crates/ide/src/status.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | use std::{fmt, iter::FromIterator, sync::Arc}; | 1 | use std::{fmt, iter::FromIterator, sync::Arc}; |
2 | 2 | ||
3 | use hir::MacroFile; | 3 | use hir::{ExpandResult, MacroFile}; |
4 | use ide_db::base_db::{ | 4 | use ide_db::base_db::{ |
5 | salsa::debug::{DebugQueryTable, TableEntry}, | 5 | salsa::debug::{DebugQueryTable, TableEntry}, |
6 | CrateId, FileId, FileTextQuery, SourceDatabase, SourceRootId, | 6 | CrateId, FileId, FileTextQuery, SourceDatabase, SourceRootId, |
@@ -19,7 +19,7 @@ fn syntax_tree_stats(db: &RootDatabase) -> SyntaxTreeStats { | |||
19 | ide_db::base_db::ParseQuery.in_db(db).entries::<SyntaxTreeStats>() | 19 | ide_db::base_db::ParseQuery.in_db(db).entries::<SyntaxTreeStats>() |
20 | } | 20 | } |
21 | fn macro_syntax_tree_stats(db: &RootDatabase) -> SyntaxTreeStats { | 21 | fn macro_syntax_tree_stats(db: &RootDatabase) -> SyntaxTreeStats { |
22 | hir::db::ParseMacroQuery.in_db(db).entries::<SyntaxTreeStats>() | 22 | hir::db::ParseMacroExpansionQuery.in_db(db).entries::<SyntaxTreeStats>() |
23 | } | 23 | } |
24 | 24 | ||
25 | // Feature: Status | 25 | // Feature: Status |
@@ -115,10 +115,12 @@ impl FromIterator<TableEntry<FileId, Parse<ast::SourceFile>>> for SyntaxTreeStat | |||
115 | } | 115 | } |
116 | } | 116 | } |
117 | 117 | ||
118 | impl<M> FromIterator<TableEntry<MacroFile, Option<(Parse<SyntaxNode>, M)>>> for SyntaxTreeStats { | 118 | impl<M> FromIterator<TableEntry<MacroFile, ExpandResult<Option<(Parse<SyntaxNode>, M)>>>> |
119 | for SyntaxTreeStats | ||
120 | { | ||
119 | fn from_iter<T>(iter: T) -> SyntaxTreeStats | 121 | fn from_iter<T>(iter: T) -> SyntaxTreeStats |
120 | where | 122 | where |
121 | T: IntoIterator<Item = TableEntry<MacroFile, Option<(Parse<SyntaxNode>, M)>>>, | 123 | T: IntoIterator<Item = TableEntry<MacroFile, ExpandResult<Option<(Parse<SyntaxNode>, M)>>>>, |
122 | { | 124 | { |
123 | let mut res = SyntaxTreeStats::default(); | 125 | let mut res = SyntaxTreeStats::default(); |
124 | for entry in iter { | 126 | for entry in iter { |
diff --git a/crates/ide_db/src/apply_change.rs b/crates/ide_db/src/apply_change.rs index da16fa21d..987191fe3 100644 --- a/crates/ide_db/src/apply_change.rs +++ b/crates/ide_db/src/apply_change.rs | |||
@@ -76,7 +76,7 @@ impl RootDatabase { | |||
76 | let sweep = SweepStrategy::default().discard_values().sweep_all_revisions(); | 76 | let sweep = SweepStrategy::default().discard_values().sweep_all_revisions(); |
77 | 77 | ||
78 | base_db::ParseQuery.in_db(self).sweep(sweep); | 78 | base_db::ParseQuery.in_db(self).sweep(sweep); |
79 | hir::db::ParseMacroQuery.in_db(self).sweep(sweep); | 79 | hir::db::ParseMacroExpansionQuery.in_db(self).sweep(sweep); |
80 | 80 | ||
81 | // Macros do take significant space, but less then the syntax trees | 81 | // Macros do take significant space, but less then the syntax trees |
82 | // self.query(hir::db::MacroDefQuery).sweep(sweep); | 82 | // self.query(hir::db::MacroDefQuery).sweep(sweep); |
@@ -143,7 +143,7 @@ impl RootDatabase { | |||
143 | hir::db::AstIdMapQuery | 143 | hir::db::AstIdMapQuery |
144 | hir::db::MacroArgTextQuery | 144 | hir::db::MacroArgTextQuery |
145 | hir::db::MacroDefQuery | 145 | hir::db::MacroDefQuery |
146 | hir::db::ParseMacroQuery | 146 | hir::db::ParseMacroExpansionQuery |
147 | hir::db::MacroExpandQuery | 147 | hir::db::MacroExpandQuery |
148 | 148 | ||
149 | // DefDatabase | 149 | // DefDatabase |
diff --git a/crates/ide_db/src/imports_locator.rs b/crates/ide_db/src/imports_locator.rs index df74be00b..09046d3c3 100644 --- a/crates/ide_db/src/imports_locator.rs +++ b/crates/ide_db/src/imports_locator.rs | |||
@@ -1,36 +1,77 @@ | |||
1 | //! This module contains an import search funcionality that is provided to the assists module. | 1 | //! This module contains an import search funcionality that is provided to the assists module. |
2 | //! Later, this should be moved away to a separate crate that is accessible from the assists module. | 2 | //! Later, this should be moved away to a separate crate that is accessible from the assists module. |
3 | 3 | ||
4 | use hir::{Crate, MacroDef, ModuleDef, Semantics}; | 4 | use hir::{import_map, Crate, MacroDef, ModuleDef, Semantics}; |
5 | use syntax::{ast, AstNode, SyntaxKind::NAME}; | 5 | use syntax::{ast, AstNode, SyntaxKind::NAME}; |
6 | 6 | ||
7 | use crate::{ | 7 | use crate::{ |
8 | defs::{Definition, NameClass}, | 8 | defs::{Definition, NameClass}, |
9 | symbol_index::{self, FileSymbol, Query}, | 9 | symbol_index::{self, FileSymbol}, |
10 | RootDatabase, | 10 | RootDatabase, |
11 | }; | 11 | }; |
12 | use either::Either; | 12 | use either::Either; |
13 | use rustc_hash::FxHashSet; | 13 | use rustc_hash::FxHashSet; |
14 | 14 | ||
15 | pub fn find_imports<'a>( | 15 | pub fn find_exact_imports<'a>( |
16 | sema: &Semantics<'a, RootDatabase>, | 16 | sema: &Semantics<'a, RootDatabase>, |
17 | krate: Crate, | 17 | krate: Crate, |
18 | name_to_import: &str, | 18 | name_to_import: &str, |
19 | ) -> Vec<Either<ModuleDef, MacroDef>> { | 19 | ) -> impl Iterator<Item = Either<ModuleDef, MacroDef>> { |
20 | let _p = profile::span("search_for_imports"); | 20 | let _p = profile::span("find_exact_imports"); |
21 | find_imports( | ||
22 | sema, | ||
23 | krate, | ||
24 | { | ||
25 | let mut local_query = symbol_index::Query::new(name_to_import.to_string()); | ||
26 | local_query.exact(); | ||
27 | local_query.limit(40); | ||
28 | local_query | ||
29 | }, | ||
30 | import_map::Query::new(name_to_import).anchor_end().case_sensitive().limit(40), | ||
31 | ) | ||
32 | } | ||
33 | |||
34 | pub fn find_similar_imports<'a>( | ||
35 | sema: &Semantics<'a, RootDatabase>, | ||
36 | krate: Crate, | ||
37 | name_to_import: &str, | ||
38 | limit: usize, | ||
39 | ignore_modules: bool, | ||
40 | ) -> impl Iterator<Item = Either<ModuleDef, MacroDef>> { | ||
41 | let _p = profile::span("find_similar_imports"); | ||
42 | |||
43 | let mut external_query = import_map::Query::new(name_to_import).limit(limit); | ||
44 | if ignore_modules { | ||
45 | external_query = external_query.exclude_import_kind(import_map::ImportKind::Module); | ||
46 | } | ||
47 | |||
48 | find_imports( | ||
49 | sema, | ||
50 | krate, | ||
51 | { | ||
52 | let mut local_query = symbol_index::Query::new(name_to_import.to_string()); | ||
53 | local_query.limit(limit); | ||
54 | local_query | ||
55 | }, | ||
56 | external_query, | ||
57 | ) | ||
58 | } | ||
59 | |||
60 | fn find_imports<'a>( | ||
61 | sema: &Semantics<'a, RootDatabase>, | ||
62 | krate: Crate, | ||
63 | local_query: symbol_index::Query, | ||
64 | external_query: import_map::Query, | ||
65 | ) -> impl Iterator<Item = Either<ModuleDef, MacroDef>> { | ||
66 | let _p = profile::span("find_similar_imports"); | ||
21 | let db = sema.db; | 67 | let db = sema.db; |
22 | 68 | ||
23 | // Query dependencies first. | 69 | // Query dependencies first. |
24 | let mut candidates: FxHashSet<_> = | 70 | let mut candidates: FxHashSet<_> = |
25 | krate.query_external_importables(db, name_to_import).collect(); | 71 | krate.query_external_importables(db, external_query).collect(); |
26 | 72 | ||
27 | // Query the local crate using the symbol index. | 73 | // Query the local crate using the symbol index. |
28 | let local_results = { | 74 | let local_results = symbol_index::crate_symbols(db, krate.into(), local_query); |
29 | let mut query = Query::new(name_to_import.to_string()); | ||
30 | query.exact(); | ||
31 | query.limit(40); | ||
32 | symbol_index::crate_symbols(db, krate.into(), query) | ||
33 | }; | ||
34 | 75 | ||
35 | candidates.extend( | 76 | candidates.extend( |
36 | local_results | 77 | local_results |
@@ -43,7 +84,7 @@ pub fn find_imports<'a>( | |||
43 | }), | 84 | }), |
44 | ); | 85 | ); |
45 | 86 | ||
46 | candidates.into_iter().collect() | 87 | candidates.into_iter() |
47 | } | 88 | } |
48 | 89 | ||
49 | fn get_name_definition<'a>( | 90 | fn get_name_definition<'a>( |
diff --git a/crates/ide_db/src/lib.rs b/crates/ide_db/src/lib.rs index 38ebdbf79..05139a651 100644 --- a/crates/ide_db/src/lib.rs +++ b/crates/ide_db/src/lib.rs | |||
@@ -113,7 +113,7 @@ impl RootDatabase { | |||
113 | pub fn update_lru_capacity(&mut self, lru_capacity: Option<usize>) { | 113 | pub fn update_lru_capacity(&mut self, lru_capacity: Option<usize>) { |
114 | let lru_capacity = lru_capacity.unwrap_or(base_db::DEFAULT_LRU_CAP); | 114 | let lru_capacity = lru_capacity.unwrap_or(base_db::DEFAULT_LRU_CAP); |
115 | base_db::ParseQuery.in_db_mut(self).set_lru_capacity(lru_capacity); | 115 | base_db::ParseQuery.in_db_mut(self).set_lru_capacity(lru_capacity); |
116 | hir::db::ParseMacroQuery.in_db_mut(self).set_lru_capacity(lru_capacity); | 116 | hir::db::ParseMacroExpansionQuery.in_db_mut(self).set_lru_capacity(lru_capacity); |
117 | hir::db::MacroExpandQuery.in_db_mut(self).set_lru_capacity(lru_capacity); | 117 | hir::db::MacroExpandQuery.in_db_mut(self).set_lru_capacity(lru_capacity); |
118 | } | 118 | } |
119 | } | 119 | } |
diff --git a/crates/mbe/src/lib.rs b/crates/mbe/src/lib.rs index f854ca09a..2d0763c47 100644 --- a/crates/mbe/src/lib.rs +++ b/crates/mbe/src/lib.rs | |||
@@ -12,6 +12,8 @@ mod subtree_source; | |||
12 | #[cfg(test)] | 12 | #[cfg(test)] |
13 | mod tests; | 13 | mod tests; |
14 | 14 | ||
15 | use std::fmt; | ||
16 | |||
15 | pub use tt::{Delimiter, Punct}; | 17 | pub use tt::{Delimiter, Punct}; |
16 | 18 | ||
17 | use crate::{ | 19 | use crate::{ |
@@ -33,6 +35,7 @@ pub enum ExpandError { | |||
33 | ConversionError, | 35 | ConversionError, |
34 | InvalidRepeat, | 36 | InvalidRepeat, |
35 | ProcMacroError(tt::ExpansionError), | 37 | ProcMacroError(tt::ExpansionError), |
38 | Other(String), | ||
36 | } | 39 | } |
37 | 40 | ||
38 | impl From<tt::ExpansionError> for ExpandError { | 41 | impl From<tt::ExpansionError> for ExpandError { |
@@ -41,6 +44,20 @@ impl From<tt::ExpansionError> for ExpandError { | |||
41 | } | 44 | } |
42 | } | 45 | } |
43 | 46 | ||
47 | impl fmt::Display for ExpandError { | ||
48 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
49 | match self { | ||
50 | ExpandError::NoMatchingRule => f.write_str("no rule matches input tokens"), | ||
51 | ExpandError::UnexpectedToken => f.write_str("unexpected token in input"), | ||
52 | ExpandError::BindingError(e) => f.write_str(e), | ||
53 | ExpandError::ConversionError => f.write_str("could not convert tokens"), | ||
54 | ExpandError::InvalidRepeat => f.write_str("invalid repeat expression"), | ||
55 | ExpandError::ProcMacroError(e) => e.fmt(f), | ||
56 | ExpandError::Other(e) => f.write_str(e), | ||
57 | } | ||
58 | } | ||
59 | } | ||
60 | |||
44 | pub use crate::syntax_bridge::{ | 61 | pub use crate::syntax_bridge::{ |
45 | ast_to_token_tree, parse_to_token_tree, syntax_node_to_token_tree, token_tree_to_syntax_node, | 62 | ast_to_token_tree, parse_to_token_tree, syntax_node_to_token_tree, token_tree_to_syntax_node, |
46 | TokenMap, | 63 | TokenMap, |
@@ -246,33 +263,42 @@ fn validate(pattern: &tt::Subtree) -> Result<(), ParseError> { | |||
246 | Ok(()) | 263 | Ok(()) |
247 | } | 264 | } |
248 | 265 | ||
249 | #[derive(Debug)] | 266 | #[derive(Debug, Clone, Eq, PartialEq)] |
250 | pub struct ExpandResult<T>(pub T, pub Option<ExpandError>); | 267 | pub struct ExpandResult<T> { |
268 | pub value: T, | ||
269 | pub err: Option<ExpandError>, | ||
270 | } | ||
251 | 271 | ||
252 | impl<T> ExpandResult<T> { | 272 | impl<T> ExpandResult<T> { |
253 | pub fn ok(t: T) -> ExpandResult<T> { | 273 | pub fn ok(value: T) -> Self { |
254 | ExpandResult(t, None) | 274 | Self { value, err: None } |
275 | } | ||
276 | |||
277 | pub fn only_err(err: ExpandError) -> Self | ||
278 | where | ||
279 | T: Default, | ||
280 | { | ||
281 | Self { value: Default::default(), err: Some(err) } | ||
255 | } | 282 | } |
256 | 283 | ||
257 | pub fn only_err(err: ExpandError) -> ExpandResult<T> | 284 | pub fn str_err(err: String) -> Self |
258 | where | 285 | where |
259 | T: Default, | 286 | T: Default, |
260 | { | 287 | { |
261 | ExpandResult(Default::default(), Some(err)) | 288 | Self::only_err(ExpandError::Other(err)) |
262 | } | 289 | } |
263 | 290 | ||
264 | pub fn map<U>(self, f: impl FnOnce(T) -> U) -> ExpandResult<U> { | 291 | pub fn map<U>(self, f: impl FnOnce(T) -> U) -> ExpandResult<U> { |
265 | ExpandResult(f(self.0), self.1) | 292 | ExpandResult { value: f(self.value), err: self.err } |
266 | } | 293 | } |
267 | 294 | ||
268 | pub fn result(self) -> Result<T, ExpandError> { | 295 | pub fn result(self) -> Result<T, ExpandError> { |
269 | self.1.map(Err).unwrap_or(Ok(self.0)) | 296 | self.err.map(Err).unwrap_or(Ok(self.value)) |
270 | } | 297 | } |
271 | } | 298 | } |
272 | 299 | ||
273 | impl<T: Default> From<Result<T, ExpandError>> for ExpandResult<T> { | 300 | impl<T: Default> From<Result<T, ExpandError>> for ExpandResult<T> { |
274 | fn from(result: Result<T, ExpandError>) -> ExpandResult<T> { | 301 | fn from(result: Result<T, ExpandError>) -> Self { |
275 | result | 302 | result.map_or_else(|e| Self::only_err(e), |it| Self::ok(it)) |
276 | .map_or_else(|e| ExpandResult(Default::default(), Some(e)), |it| ExpandResult(it, None)) | ||
277 | } | 303 | } |
278 | } | 304 | } |
diff --git a/crates/mbe/src/mbe_expander.rs b/crates/mbe/src/mbe_expander.rs index 1ad8b9f8a..97bce0536 100644 --- a/crates/mbe/src/mbe_expander.rs +++ b/crates/mbe/src/mbe_expander.rs | |||
@@ -28,10 +28,10 @@ fn expand_rules(rules: &[crate::Rule], input: &tt::Subtree) -> ExpandResult<tt:: | |||
28 | // If we find a rule that applies without errors, we're done. | 28 | // If we find a rule that applies without errors, we're done. |
29 | // Unconditionally returning the transcription here makes the | 29 | // Unconditionally returning the transcription here makes the |
30 | // `test_repeat_bad_var` test fail. | 30 | // `test_repeat_bad_var` test fail. |
31 | let ExpandResult(res, transcribe_err) = | 31 | let ExpandResult { value, err: transcribe_err } = |
32 | transcriber::transcribe(&rule.rhs, &new_match.bindings); | 32 | transcriber::transcribe(&rule.rhs, &new_match.bindings); |
33 | if transcribe_err.is_none() { | 33 | if transcribe_err.is_none() { |
34 | return ExpandResult::ok(res); | 34 | return ExpandResult::ok(value); |
35 | } | 35 | } |
36 | } | 36 | } |
37 | // Use the rule if we matched more tokens, or had fewer errors | 37 | // Use the rule if we matched more tokens, or had fewer errors |
@@ -47,11 +47,11 @@ fn expand_rules(rules: &[crate::Rule], input: &tt::Subtree) -> ExpandResult<tt:: | |||
47 | } | 47 | } |
48 | if let Some((match_, rule)) = match_ { | 48 | if let Some((match_, rule)) = match_ { |
49 | // if we got here, there was no match without errors | 49 | // if we got here, there was no match without errors |
50 | let ExpandResult(result, transcribe_err) = | 50 | let ExpandResult { value, err: transcribe_err } = |
51 | transcriber::transcribe(&rule.rhs, &match_.bindings); | 51 | transcriber::transcribe(&rule.rhs, &match_.bindings); |
52 | ExpandResult(result, match_.err.or(transcribe_err)) | 52 | ExpandResult { value, err: match_.err.or(transcribe_err) } |
53 | } else { | 53 | } else { |
54 | ExpandResult(tt::Subtree::default(), Some(ExpandError::NoMatchingRule)) | 54 | ExpandResult::only_err(ExpandError::NoMatchingRule) |
55 | } | 55 | } |
56 | } | 56 | } |
57 | 57 | ||
@@ -143,7 +143,10 @@ mod tests { | |||
143 | } | 143 | } |
144 | 144 | ||
145 | fn assert_err(macro_body: &str, invocation: &str, err: ExpandError) { | 145 | fn assert_err(macro_body: &str, invocation: &str, err: ExpandError) { |
146 | assert_eq!(expand_first(&create_rules(&format_macro(macro_body)), invocation).1, Some(err)); | 146 | assert_eq!( |
147 | expand_first(&create_rules(&format_macro(macro_body)), invocation).err, | ||
148 | Some(err) | ||
149 | ); | ||
147 | } | 150 | } |
148 | 151 | ||
149 | fn format_macro(macro_body: &str) -> String { | 152 | fn format_macro(macro_body: &str) -> String { |
diff --git a/crates/mbe/src/mbe_expander/matcher.rs b/crates/mbe/src/mbe_expander/matcher.rs index 39a8eefbd..3f8445897 100644 --- a/crates/mbe/src/mbe_expander/matcher.rs +++ b/crates/mbe/src/mbe_expander/matcher.rs | |||
@@ -158,7 +158,8 @@ fn match_subtree( | |||
158 | continue; | 158 | continue; |
159 | } | 159 | } |
160 | }; | 160 | }; |
161 | let ExpandResult(matched, match_err) = match_meta_var(kind.as_str(), src); | 161 | let ExpandResult { value: matched, err: match_err } = |
162 | match_meta_var(kind.as_str(), src); | ||
162 | match matched { | 163 | match matched { |
163 | Some(fragment) => { | 164 | Some(fragment) => { |
164 | res.bindings.inner.insert(name.clone(), Binding::Fragment(fragment)); | 165 | res.bindings.inner.insert(name.clone(), Binding::Fragment(fragment)); |
@@ -342,17 +343,17 @@ impl<'a> TtIter<'a> { | |||
342 | token_trees: res.into_iter().cloned().collect(), | 343 | token_trees: res.into_iter().cloned().collect(), |
343 | })), | 344 | })), |
344 | }; | 345 | }; |
345 | ExpandResult(res, err) | 346 | ExpandResult { value: res, err } |
346 | } | 347 | } |
347 | 348 | ||
348 | pub(crate) fn eat_vis(&mut self) -> Option<tt::TokenTree> { | 349 | pub(crate) fn eat_vis(&mut self) -> Option<tt::TokenTree> { |
349 | let mut fork = self.clone(); | 350 | let mut fork = self.clone(); |
350 | match fork.expect_fragment(Visibility) { | 351 | match fork.expect_fragment(Visibility) { |
351 | ExpandResult(tt, None) => { | 352 | ExpandResult { value: tt, err: None } => { |
352 | *self = fork; | 353 | *self = fork; |
353 | tt | 354 | tt |
354 | } | 355 | } |
355 | ExpandResult(_, Some(_)) => None, | 356 | ExpandResult { value: _, err: Some(_) } => None, |
356 | } | 357 | } |
357 | } | 358 | } |
358 | } | 359 | } |
diff --git a/crates/mbe/src/mbe_expander/transcriber.rs b/crates/mbe/src/mbe_expander/transcriber.rs index c9525c5bf..616119ba9 100644 --- a/crates/mbe/src/mbe_expander/transcriber.rs +++ b/crates/mbe/src/mbe_expander/transcriber.rs | |||
@@ -93,17 +93,18 @@ fn expand_subtree( | |||
93 | match op { | 93 | match op { |
94 | Op::TokenTree(tt @ tt::TokenTree::Leaf(..)) => arena.push(tt.clone()), | 94 | Op::TokenTree(tt @ tt::TokenTree::Leaf(..)) => arena.push(tt.clone()), |
95 | Op::TokenTree(tt::TokenTree::Subtree(tt)) => { | 95 | Op::TokenTree(tt::TokenTree::Subtree(tt)) => { |
96 | let ExpandResult(tt, e) = expand_subtree(ctx, tt, arena); | 96 | let ExpandResult { value: tt, err: e } = expand_subtree(ctx, tt, arena); |
97 | err = err.or(e); | 97 | err = err.or(e); |
98 | arena.push(tt.into()); | 98 | arena.push(tt.into()); |
99 | } | 99 | } |
100 | Op::Var { name, kind: _ } => { | 100 | Op::Var { name, kind: _ } => { |
101 | let ExpandResult(fragment, e) = expand_var(ctx, name); | 101 | let ExpandResult { value: fragment, err: e } = expand_var(ctx, name); |
102 | err = err.or(e); | 102 | err = err.or(e); |
103 | push_fragment(arena, fragment); | 103 | push_fragment(arena, fragment); |
104 | } | 104 | } |
105 | Op::Repeat { subtree, kind, separator } => { | 105 | Op::Repeat { subtree, kind, separator } => { |
106 | let ExpandResult(fragment, e) = expand_repeat(ctx, subtree, kind, separator, arena); | 106 | let ExpandResult { value: fragment, err: e } = |
107 | expand_repeat(ctx, subtree, kind, separator, arena); | ||
107 | err = err.or(e); | 108 | err = err.or(e); |
108 | push_fragment(arena, fragment) | 109 | push_fragment(arena, fragment) |
109 | } | 110 | } |
@@ -111,7 +112,7 @@ fn expand_subtree( | |||
111 | } | 112 | } |
112 | // drain the elements added in this instance of expand_subtree | 113 | // drain the elements added in this instance of expand_subtree |
113 | let tts = arena.drain(start_elements..arena.len()).collect(); | 114 | let tts = arena.drain(start_elements..arena.len()).collect(); |
114 | ExpandResult(tt::Subtree { delimiter: template.delimiter, token_trees: tts }, err) | 115 | ExpandResult { value: tt::Subtree { delimiter: template.delimiter, token_trees: tts }, err } |
115 | } | 116 | } |
116 | 117 | ||
117 | fn expand_var(ctx: &mut ExpandCtx, v: &SmolStr) -> ExpandResult<Fragment> { | 118 | fn expand_var(ctx: &mut ExpandCtx, v: &SmolStr) -> ExpandResult<Fragment> { |
@@ -152,7 +153,7 @@ fn expand_var(ctx: &mut ExpandCtx, v: &SmolStr) -> ExpandResult<Fragment> { | |||
152 | ExpandResult::ok(Fragment::Tokens(tt)) | 153 | ExpandResult::ok(Fragment::Tokens(tt)) |
153 | } else { | 154 | } else { |
154 | ctx.bindings.get(&v, &mut ctx.nesting).map_or_else( | 155 | ctx.bindings.get(&v, &mut ctx.nesting).map_or_else( |
155 | |e| ExpandResult(Fragment::Tokens(tt::TokenTree::empty()), Some(e)), | 156 | |e| ExpandResult { value: Fragment::Tokens(tt::TokenTree::empty()), err: Some(e) }, |
156 | |b| ExpandResult::ok(b.clone()), | 157 | |b| ExpandResult::ok(b.clone()), |
157 | ) | 158 | ) |
158 | } | 159 | } |
@@ -174,7 +175,7 @@ fn expand_repeat( | |||
174 | let mut counter = 0; | 175 | let mut counter = 0; |
175 | 176 | ||
176 | loop { | 177 | loop { |
177 | let ExpandResult(mut t, e) = expand_subtree(ctx, template, arena); | 178 | let ExpandResult { value: mut t, err: e } = expand_subtree(ctx, template, arena); |
178 | let nesting_state = ctx.nesting.last_mut().unwrap(); | 179 | let nesting_state = ctx.nesting.last_mut().unwrap(); |
179 | if nesting_state.at_end || !nesting_state.hit { | 180 | if nesting_state.at_end || !nesting_state.hit { |
180 | break; | 181 | break; |
@@ -234,7 +235,10 @@ fn expand_repeat( | |||
234 | let tt = tt::Subtree { delimiter: None, token_trees: buf }.into(); | 235 | let tt = tt::Subtree { delimiter: None, token_trees: buf }.into(); |
235 | 236 | ||
236 | if RepeatKind::OneOrMore == kind && counter == 0 { | 237 | if RepeatKind::OneOrMore == kind && counter == 0 { |
237 | return ExpandResult(Fragment::Tokens(tt), Some(ExpandError::UnexpectedToken)); | 238 | return ExpandResult { |
239 | value: Fragment::Tokens(tt), | ||
240 | err: Some(ExpandError::UnexpectedToken), | ||
241 | }; | ||
238 | } | 242 | } |
239 | ExpandResult::ok(Fragment::Tokens(tt)) | 243 | ExpandResult::ok(Fragment::Tokens(tt)) |
240 | } | 244 | } |
diff --git a/crates/parser/src/grammar/items.rs b/crates/parser/src/grammar/items.rs index 780bc470a..ad29b82f7 100644 --- a/crates/parser/src/grammar/items.rs +++ b/crates/parser/src/grammar/items.rs | |||
@@ -112,7 +112,7 @@ pub(super) fn maybe_item(p: &mut Parser, m: Marker) -> Result<(), Marker> { | |||
112 | has_mods = true; | 112 | has_mods = true; |
113 | } | 113 | } |
114 | 114 | ||
115 | if p.at(T![extern]) { | 115 | if p.at(T![extern]) && p.nth(1) != T!['{'] && (p.nth(1) != STRING || p.nth(2) != T!['{']) { |
116 | has_mods = true; | 116 | has_mods = true; |
117 | abi(p); | 117 | abi(p); |
118 | } | 118 | } |
@@ -181,6 +181,14 @@ pub(super) fn maybe_item(p: &mut Parser, m: Marker) -> Result<(), Marker> { | |||
181 | T![type] => { | 181 | T![type] => { |
182 | type_alias(p, m); | 182 | type_alias(p, m); |
183 | } | 183 | } |
184 | |||
185 | // unsafe extern "C" {} | ||
186 | T![extern] => { | ||
187 | abi(p); | ||
188 | extern_item_list(p); | ||
189 | m.complete(p, EXTERN_BLOCK); | ||
190 | } | ||
191 | |||
184 | _ => { | 192 | _ => { |
185 | if !has_visibility && !has_mods { | 193 | if !has_visibility && !has_mods { |
186 | return Err(m); | 194 | return Err(m); |
diff --git a/crates/proc_macro_srv/Cargo.toml b/crates/proc_macro_srv/Cargo.toml index 048b32186..729372968 100644 --- a/crates/proc_macro_srv/Cargo.toml +++ b/crates/proc_macro_srv/Cargo.toml | |||
@@ -20,7 +20,7 @@ proc_macro_api = { path = "../proc_macro_api", version = "0.0.0" } | |||
20 | test_utils = { path = "../test_utils", version = "0.0.0" } | 20 | test_utils = { path = "../test_utils", version = "0.0.0" } |
21 | 21 | ||
22 | [dev-dependencies] | 22 | [dev-dependencies] |
23 | cargo_metadata = "0.12.0" | 23 | cargo_metadata = "=0.12.0" |
24 | difference = "2.0.0" | 24 | difference = "2.0.0" |
25 | 25 | ||
26 | # used as proc macro test targets | 26 | # used as proc macro test targets |
diff --git a/crates/project_model/Cargo.toml b/crates/project_model/Cargo.toml index 2d53bcbcc..e0c591603 100644 --- a/crates/project_model/Cargo.toml +++ b/crates/project_model/Cargo.toml | |||
@@ -12,7 +12,7 @@ doctest = false | |||
12 | [dependencies] | 12 | [dependencies] |
13 | log = "0.4.8" | 13 | log = "0.4.8" |
14 | rustc-hash = "1.1.0" | 14 | rustc-hash = "1.1.0" |
15 | cargo_metadata = "0.12.0" | 15 | cargo_metadata = "=0.12.0" |
16 | serde = { version = "1.0.106", features = ["derive"] } | 16 | serde = { version = "1.0.106", features = ["derive"] } |
17 | serde_json = "1.0.48" | 17 | serde_json = "1.0.48" |
18 | anyhow = "1.0.26" | 18 | anyhow = "1.0.26" |
diff --git a/crates/project_model/src/sysroot.rs b/crates/project_model/src/sysroot.rs index b0e8863f6..f0a43eaf6 100644 --- a/crates/project_model/src/sysroot.rs +++ b/crates/project_model/src/sysroot.rs | |||
@@ -37,7 +37,7 @@ impl Sysroot { | |||
37 | pub fn public_deps(&self) -> impl Iterator<Item = (&'static str, SysrootCrate)> + '_ { | 37 | pub fn public_deps(&self) -> impl Iterator<Item = (&'static str, SysrootCrate)> + '_ { |
38 | // core is added as a dependency before std in order to | 38 | // core is added as a dependency before std in order to |
39 | // mimic rustcs dependency order | 39 | // mimic rustcs dependency order |
40 | vec!["core", "alloc", "std"].into_iter().filter_map(move |it| Some((it, self.by_name(it)?))) | 40 | ["core", "alloc", "std"].iter().filter_map(move |&it| Some((it, self.by_name(it)?))) |
41 | } | 41 | } |
42 | 42 | ||
43 | pub fn proc_macro(&self) -> Option<SysrootCrate> { | 43 | pub fn proc_macro(&self) -> Option<SysrootCrate> { |
diff --git a/crates/project_model/src/workspace.rs b/crates/project_model/src/workspace.rs index 9ebb0a811..a71f96164 100644 --- a/crates/project_model/src/workspace.rs +++ b/crates/project_model/src/workspace.rs | |||
@@ -12,8 +12,8 @@ use proc_macro_api::ProcMacroClient; | |||
12 | use rustc_hash::{FxHashMap, FxHashSet}; | 12 | use rustc_hash::{FxHashMap, FxHashSet}; |
13 | 13 | ||
14 | use crate::{ | 14 | use crate::{ |
15 | cargo_workspace, cfg_flag::CfgFlag, utf8_stdout, CargoConfig, CargoWorkspace, ProjectJson, | 15 | cargo_workspace, cfg_flag::CfgFlag, sysroot::SysrootCrate, utf8_stdout, CargoConfig, |
16 | ProjectManifest, Sysroot, TargetKind, | 16 | CargoWorkspace, ProjectJson, ProjectManifest, Sysroot, TargetKind, |
17 | }; | 17 | }; |
18 | 18 | ||
19 | /// `PackageRoot` describes a package root folder. | 19 | /// `PackageRoot` describes a package root folder. |
@@ -70,12 +70,8 @@ impl ProjectWorkspace { | |||
70 | format!("Failed to deserialize json file {}", project_json.display()) | 70 | format!("Failed to deserialize json file {}", project_json.display()) |
71 | })?; | 71 | })?; |
72 | let project_location = project_json.parent().unwrap().to_path_buf(); | 72 | let project_location = project_json.parent().unwrap().to_path_buf(); |
73 | let project = ProjectJson::new(&project_location, data); | 73 | let project_json = ProjectJson::new(&project_location, data); |
74 | let sysroot = match &project.sysroot_src { | 74 | ProjectWorkspace::load_inline(project_json)? |
75 | Some(path) => Some(Sysroot::load(path)?), | ||
76 | None => None, | ||
77 | }; | ||
78 | ProjectWorkspace::Json { project, sysroot } | ||
79 | } | 75 | } |
80 | ProjectManifest::CargoToml(cargo_toml) => { | 76 | ProjectManifest::CargoToml(cargo_toml) => { |
81 | let cargo_version = utf8_stdout({ | 77 | let cargo_version = utf8_stdout({ |
@@ -150,43 +146,38 @@ impl ProjectWorkspace { | |||
150 | }) | 146 | }) |
151 | })) | 147 | })) |
152 | .collect::<Vec<_>>(), | 148 | .collect::<Vec<_>>(), |
153 | ProjectWorkspace::Cargo { cargo, sysroot, rustc } => { | 149 | ProjectWorkspace::Cargo { cargo, sysroot, rustc } => cargo |
154 | let roots = cargo | 150 | .packages() |
155 | .packages() | 151 | .map(|pkg| { |
156 | .map(|pkg| { | 152 | let is_member = cargo[pkg].is_member; |
157 | let is_member = cargo[pkg].is_member; | 153 | let pkg_root = cargo[pkg].root().to_path_buf(); |
158 | let pkg_root = cargo[pkg].root().to_path_buf(); | 154 | |
159 | 155 | let mut include = vec![pkg_root.clone()]; | |
160 | let mut include = vec![pkg_root.clone()]; | 156 | include.extend(cargo[pkg].out_dir.clone()); |
161 | include.extend(cargo[pkg].out_dir.clone()); | 157 | |
162 | 158 | let mut exclude = vec![pkg_root.join(".git")]; | |
163 | let mut exclude = vec![pkg_root.join(".git")]; | 159 | if is_member { |
164 | if is_member { | 160 | exclude.push(pkg_root.join("target")); |
165 | exclude.push(pkg_root.join("target")); | 161 | } else { |
166 | } else { | 162 | exclude.push(pkg_root.join("tests")); |
167 | exclude.push(pkg_root.join("tests")); | 163 | exclude.push(pkg_root.join("examples")); |
168 | exclude.push(pkg_root.join("examples")); | 164 | exclude.push(pkg_root.join("benches")); |
169 | exclude.push(pkg_root.join("benches")); | 165 | } |
170 | } | 166 | PackageRoot { is_member, include, exclude } |
171 | PackageRoot { is_member, include, exclude } | 167 | }) |
172 | }) | 168 | .chain(sysroot.crates().map(|krate| PackageRoot { |
173 | .chain(sysroot.crates().map(|krate| PackageRoot { | 169 | is_member: false, |
170 | include: vec![sysroot[krate].root_dir().to_path_buf()], | ||
171 | exclude: Vec::new(), | ||
172 | })) | ||
173 | .chain(rustc.into_iter().flat_map(|rustc| { | ||
174 | rustc.packages().map(move |krate| PackageRoot { | ||
174 | is_member: false, | 175 | is_member: false, |
175 | include: vec![sysroot[krate].root_dir().to_path_buf()], | 176 | include: vec![rustc[krate].root().to_path_buf()], |
176 | exclude: Vec::new(), | 177 | exclude: Vec::new(), |
177 | })); | 178 | }) |
178 | if let Some(rustc_packages) = rustc { | 179 | })) |
179 | roots | 180 | .collect(), |
180 | .chain(rustc_packages.packages().map(|krate| PackageRoot { | ||
181 | is_member: false, | ||
182 | include: vec![rustc_packages[krate].root().to_path_buf()], | ||
183 | exclude: Vec::new(), | ||
184 | })) | ||
185 | .collect() | ||
186 | } else { | ||
187 | roots.collect() | ||
188 | } | ||
189 | } | ||
190 | } | 181 | } |
191 | } | 182 | } |
192 | 183 | ||
@@ -206,312 +197,280 @@ impl ProjectWorkspace { | |||
206 | proc_macro_client: &ProcMacroClient, | 197 | proc_macro_client: &ProcMacroClient, |
207 | load: &mut dyn FnMut(&AbsPath) -> Option<FileId>, | 198 | load: &mut dyn FnMut(&AbsPath) -> Option<FileId>, |
208 | ) -> CrateGraph { | 199 | ) -> CrateGraph { |
209 | let mut crate_graph = CrateGraph::default(); | 200 | let mut crate_graph = match self { |
210 | match self { | ||
211 | ProjectWorkspace::Json { project, sysroot } => { | 201 | ProjectWorkspace::Json { project, sysroot } => { |
212 | let sysroot_dps = sysroot | 202 | project_json_to_crate_graph(target, proc_macro_client, load, project, sysroot) |
213 | .as_ref() | ||
214 | .map(|sysroot| sysroot_to_crate_graph(&mut crate_graph, sysroot, target, load)); | ||
215 | |||
216 | let mut cfg_cache: FxHashMap<Option<&str>, Vec<CfgFlag>> = FxHashMap::default(); | ||
217 | let crates: FxHashMap<_, _> = project | ||
218 | .crates() | ||
219 | .filter_map(|(crate_id, krate)| { | ||
220 | let file_path = &krate.root_module; | ||
221 | let file_id = match load(&file_path) { | ||
222 | Some(id) => id, | ||
223 | None => { | ||
224 | log::error!("failed to load crate root {}", file_path.display()); | ||
225 | return None; | ||
226 | } | ||
227 | }; | ||
228 | |||
229 | let env = krate.env.clone().into_iter().collect(); | ||
230 | let proc_macro = krate | ||
231 | .proc_macro_dylib_path | ||
232 | .clone() | ||
233 | .map(|it| proc_macro_client.by_dylib_path(&it)); | ||
234 | |||
235 | let target = krate.target.as_deref().or(target); | ||
236 | let target_cfgs = cfg_cache | ||
237 | .entry(target) | ||
238 | .or_insert_with(|| get_rustc_cfg_options(target)); | ||
239 | |||
240 | let mut cfg_options = CfgOptions::default(); | ||
241 | cfg_options.extend(target_cfgs.iter().chain(krate.cfg.iter()).cloned()); | ||
242 | |||
243 | Some(( | ||
244 | crate_id, | ||
245 | crate_graph.add_crate_root( | ||
246 | file_id, | ||
247 | krate.edition, | ||
248 | krate.display_name.clone(), | ||
249 | cfg_options, | ||
250 | env, | ||
251 | proc_macro.unwrap_or_default(), | ||
252 | ), | ||
253 | )) | ||
254 | }) | ||
255 | .collect(); | ||
256 | |||
257 | for (from, krate) in project.crates() { | ||
258 | if let Some(&from) = crates.get(&from) { | ||
259 | if let Some((public_deps, _proc_macro)) = &sysroot_dps { | ||
260 | for (name, to) in public_deps.iter() { | ||
261 | if let Err(_) = crate_graph.add_dep(from, name.clone(), *to) { | ||
262 | log::error!("cyclic dependency on {} for {:?}", name, from) | ||
263 | } | ||
264 | } | ||
265 | } | ||
266 | |||
267 | for dep in &krate.deps { | ||
268 | let to_crate_id = dep.crate_id; | ||
269 | if let Some(&to) = crates.get(&to_crate_id) { | ||
270 | if let Err(_) = crate_graph.add_dep(from, dep.name.clone(), to) { | ||
271 | log::error!("cyclic dependency {:?} -> {:?}", from, to); | ||
272 | } | ||
273 | } | ||
274 | } | ||
275 | } | ||
276 | } | ||
277 | } | 203 | } |
278 | ProjectWorkspace::Cargo { cargo, sysroot, rustc } => { | 204 | ProjectWorkspace::Cargo { cargo, sysroot, rustc } => { |
279 | let (public_deps, libproc_macro) = | 205 | cargo_to_crate_graph(target, proc_macro_client, load, cargo, sysroot, rustc) |
280 | sysroot_to_crate_graph(&mut crate_graph, sysroot, target, load); | 206 | } |
207 | }; | ||
208 | if crate_graph.patch_cfg_if() { | ||
209 | log::debug!("Patched std to depend on cfg-if") | ||
210 | } else { | ||
211 | log::debug!("Did not patch std to depend on cfg-if") | ||
212 | } | ||
213 | crate_graph | ||
214 | } | ||
215 | } | ||
216 | |||
217 | fn project_json_to_crate_graph( | ||
218 | target: Option<&str>, | ||
219 | proc_macro_client: &ProcMacroClient, | ||
220 | load: &mut dyn FnMut(&AbsPath) -> Option<FileId>, | ||
221 | project: &ProjectJson, | ||
222 | sysroot: &Option<Sysroot>, | ||
223 | ) -> CrateGraph { | ||
224 | let mut crate_graph = CrateGraph::default(); | ||
225 | let sysroot_deps = sysroot | ||
226 | .as_ref() | ||
227 | .map(|sysroot| sysroot_to_crate_graph(&mut crate_graph, sysroot, target, load)); | ||
228 | |||
229 | let mut cfg_cache: FxHashMap<Option<&str>, Vec<CfgFlag>> = FxHashMap::default(); | ||
230 | let crates: FxHashMap<CrateId, CrateId> = project | ||
231 | .crates() | ||
232 | .filter_map(|(crate_id, krate)| { | ||
233 | let file_path = &krate.root_module; | ||
234 | let file_id = load(&file_path)?; | ||
235 | Some((crate_id, krate, file_id)) | ||
236 | }) | ||
237 | .map(|(crate_id, krate, file_id)| { | ||
238 | let env = krate.env.clone().into_iter().collect(); | ||
239 | let proc_macro = | ||
240 | krate.proc_macro_dylib_path.clone().map(|it| proc_macro_client.by_dylib_path(&it)); | ||
241 | |||
242 | let target = krate.target.as_deref().or(target); | ||
243 | let target_cfgs = | ||
244 | cfg_cache.entry(target).or_insert_with(|| get_rustc_cfg_options(target)); | ||
245 | |||
246 | let mut cfg_options = CfgOptions::default(); | ||
247 | cfg_options.extend(target_cfgs.iter().chain(krate.cfg.iter()).cloned()); | ||
248 | ( | ||
249 | crate_id, | ||
250 | crate_graph.add_crate_root( | ||
251 | file_id, | ||
252 | krate.edition, | ||
253 | krate.display_name.clone(), | ||
254 | cfg_options, | ||
255 | env, | ||
256 | proc_macro.unwrap_or_default(), | ||
257 | ), | ||
258 | ) | ||
259 | }) | ||
260 | .collect(); | ||
281 | 261 | ||
282 | let mut cfg_options = CfgOptions::default(); | 262 | for (from, krate) in project.crates() { |
283 | cfg_options.extend(get_rustc_cfg_options(target)); | 263 | if let Some(&from) = crates.get(&from) { |
264 | if let Some((public_deps, _proc_macro)) = &sysroot_deps { | ||
265 | for (name, to) in public_deps.iter() { | ||
266 | add_dep(&mut crate_graph, from, name.clone(), *to) | ||
267 | } | ||
268 | } | ||
284 | 269 | ||
285 | let mut pkg_to_lib_crate = FxHashMap::default(); | 270 | for dep in &krate.deps { |
271 | if let Some(&to) = crates.get(&dep.crate_id) { | ||
272 | add_dep(&mut crate_graph, from, dep.name.clone(), to) | ||
273 | } | ||
274 | } | ||
275 | } | ||
276 | } | ||
277 | crate_graph | ||
278 | } | ||
286 | 279 | ||
287 | // Add test cfg for non-sysroot crates | 280 | fn cargo_to_crate_graph( |
288 | cfg_options.insert_atom("test".into()); | 281 | target: Option<&str>, |
289 | cfg_options.insert_atom("debug_assertions".into()); | 282 | proc_macro_client: &ProcMacroClient, |
283 | load: &mut dyn FnMut(&AbsPath) -> Option<FileId>, | ||
284 | cargo: &CargoWorkspace, | ||
285 | sysroot: &Sysroot, | ||
286 | rustc: &Option<CargoWorkspace>, | ||
287 | ) -> CrateGraph { | ||
288 | let mut crate_graph = CrateGraph::default(); | ||
289 | let (public_deps, libproc_macro) = | ||
290 | sysroot_to_crate_graph(&mut crate_graph, sysroot, target, load); | ||
290 | 291 | ||
291 | let mut pkg_crates = FxHashMap::default(); | 292 | let mut cfg_options = CfgOptions::default(); |
293 | cfg_options.extend(get_rustc_cfg_options(target)); | ||
292 | 294 | ||
293 | // Next, create crates for each package, target pair | 295 | let mut pkg_to_lib_crate = FxHashMap::default(); |
294 | for pkg in cargo.packages() { | 296 | |
295 | let mut lib_tgt = None; | 297 | // Add test cfg for non-sysroot crates |
296 | for &tgt in cargo[pkg].targets.iter() { | 298 | cfg_options.insert_atom("test".into()); |
297 | if let Some(crate_id) = add_target_crate_root( | 299 | cfg_options.insert_atom("debug_assertions".into()); |
300 | |||
301 | let mut pkg_crates = FxHashMap::default(); | ||
302 | |||
303 | // Next, create crates for each package, target pair | ||
304 | for pkg in cargo.packages() { | ||
305 | let mut lib_tgt = None; | ||
306 | for &tgt in cargo[pkg].targets.iter() { | ||
307 | if let Some(file_id) = load(&cargo[tgt].root) { | ||
308 | let crate_id = add_target_crate_root( | ||
309 | &mut crate_graph, | ||
310 | &cargo[pkg], | ||
311 | &cfg_options, | ||
312 | proc_macro_client, | ||
313 | file_id, | ||
314 | ); | ||
315 | if cargo[tgt].kind == TargetKind::Lib { | ||
316 | lib_tgt = Some((crate_id, cargo[tgt].name.clone())); | ||
317 | pkg_to_lib_crate.insert(pkg, crate_id); | ||
318 | } | ||
319 | if cargo[tgt].is_proc_macro { | ||
320 | if let Some(proc_macro) = libproc_macro { | ||
321 | add_dep( | ||
298 | &mut crate_graph, | 322 | &mut crate_graph, |
299 | &cargo[pkg], | 323 | crate_id, |
300 | &cargo[tgt], | 324 | CrateName::new("proc_macro").unwrap(), |
301 | &cfg_options, | 325 | proc_macro, |
302 | proc_macro_client, | 326 | ); |
303 | load, | ||
304 | ) { | ||
305 | if cargo[tgt].kind == TargetKind::Lib { | ||
306 | lib_tgt = Some((crate_id, cargo[tgt].name.clone())); | ||
307 | pkg_to_lib_crate.insert(pkg, crate_id); | ||
308 | } | ||
309 | if cargo[tgt].is_proc_macro { | ||
310 | if let Some(proc_macro) = libproc_macro { | ||
311 | if let Err(_) = crate_graph.add_dep( | ||
312 | crate_id, | ||
313 | CrateName::new("proc_macro").unwrap(), | ||
314 | proc_macro, | ||
315 | ) { | ||
316 | log::error!( | ||
317 | "cyclic dependency on proc_macro for {}", | ||
318 | &cargo[pkg].name | ||
319 | ) | ||
320 | } | ||
321 | } | ||
322 | } | ||
323 | |||
324 | pkg_crates.entry(pkg).or_insert_with(Vec::new).push(crate_id); | ||
325 | } | ||
326 | } | 327 | } |
328 | } | ||
327 | 329 | ||
328 | // Set deps to the core, std and to the lib target of the current package | 330 | pkg_crates.entry(pkg).or_insert_with(Vec::new).push(crate_id); |
329 | for &from in pkg_crates.get(&pkg).into_iter().flatten() { | 331 | } |
330 | if let Some((to, name)) = lib_tgt.clone() { | 332 | } |
331 | // For root projects with dashes in their name, | 333 | |
332 | // cargo metadata does not do any normalization, | 334 | // Set deps to the core, std and to the lib target of the current package |
333 | // so we do it ourselves currently | 335 | for &from in pkg_crates.get(&pkg).into_iter().flatten() { |
334 | let name = CrateName::normalize_dashes(&name); | 336 | if let Some((to, name)) = lib_tgt.clone() { |
335 | if to != from && crate_graph.add_dep(from, name, to).is_err() { | 337 | if to != from { |
336 | log::error!( | 338 | // For root projects with dashes in their name, |
337 | "cyclic dependency between targets of {}", | 339 | // cargo metadata does not do any normalization, |
338 | &cargo[pkg].name | 340 | // so we do it ourselves currently |
339 | ) | 341 | let name = CrateName::normalize_dashes(&name); |
340 | } | 342 | add_dep(&mut crate_graph, from, name, to); |
341 | } | 343 | } |
342 | for (name, krate) in public_deps.iter() { | 344 | } |
343 | if let Err(_) = crate_graph.add_dep(from, name.clone(), *krate) { | 345 | for (name, krate) in public_deps.iter() { |
344 | log::error!( | 346 | add_dep(&mut crate_graph, from, name.clone(), *krate); |
345 | "cyclic dependency on {} for {}", | 347 | } |
346 | name, | 348 | } |
347 | &cargo[pkg].name | 349 | } |
348 | ) | 350 | |
349 | } | 351 | // Now add a dep edge from all targets of upstream to the lib |
350 | } | 352 | // target of downstream. |
351 | } | 353 | for pkg in cargo.packages() { |
354 | for dep in cargo[pkg].dependencies.iter() { | ||
355 | let name = CrateName::new(&dep.name).unwrap(); | ||
356 | if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) { | ||
357 | for &from in pkg_crates.get(&pkg).into_iter().flatten() { | ||
358 | add_dep(&mut crate_graph, from, name.clone(), to) | ||
352 | } | 359 | } |
360 | } | ||
361 | } | ||
362 | } | ||
353 | 363 | ||
354 | // Now add a dep edge from all targets of upstream to the lib | 364 | let mut rustc_pkg_crates = FxHashMap::default(); |
355 | // target of downstream. | 365 | |
356 | for pkg in cargo.packages() { | 366 | // If the user provided a path to rustc sources, we add all the rustc_private crates |
357 | for dep in cargo[pkg].dependencies.iter() { | 367 | // and create dependencies on them for the crates in the current workspace |
358 | let name = CrateName::new(&dep.name).unwrap(); | 368 | if let Some(rustc_workspace) = rustc { |
359 | if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) { | 369 | for pkg in rustc_workspace.packages() { |
360 | for &from in pkg_crates.get(&pkg).into_iter().flatten() { | 370 | for &tgt in rustc_workspace[pkg].targets.iter() { |
361 | if let Err(_) = crate_graph.add_dep(from, name.clone(), to) { | 371 | if rustc_workspace[tgt].kind != TargetKind::Lib { |
362 | log::error!( | 372 | continue; |
363 | "cyclic dependency {} -> {}", | 373 | } |
364 | &cargo[pkg].name, | 374 | // Exclude alloc / core / std |
365 | &cargo[dep.pkg].name | 375 | if rustc_workspace[tgt] |
366 | ) | 376 | .root |
367 | } | 377 | .components() |
368 | } | 378 | .any(|c| c == Component::Normal("library".as_ref())) |
369 | } | 379 | { |
370 | } | 380 | continue; |
371 | } | 381 | } |
372 | 382 | ||
373 | let mut rustc_pkg_crates = FxHashMap::default(); | 383 | if let Some(file_id) = load(&rustc_workspace[tgt].root) { |
374 | 384 | let crate_id = add_target_crate_root( | |
375 | // If the user provided a path to rustc sources, we add all the rustc_private crates | 385 | &mut crate_graph, |
376 | // and create dependencies on them for the crates in the current workspace | 386 | &rustc_workspace[pkg], |
377 | if let Some(rustc_workspace) = rustc { | 387 | &cfg_options, |
378 | for pkg in rustc_workspace.packages() { | 388 | proc_macro_client, |
379 | for &tgt in rustc_workspace[pkg].targets.iter() { | 389 | file_id, |
380 | if rustc_workspace[tgt].kind != TargetKind::Lib { | 390 | ); |
381 | continue; | 391 | pkg_to_lib_crate.insert(pkg, crate_id); |
382 | } | 392 | // Add dependencies on the core / std / alloc for rustc |
383 | // Exclude alloc / core / std | 393 | for (name, krate) in public_deps.iter() { |
384 | if rustc_workspace[tgt] | 394 | add_dep(&mut crate_graph, crate_id, name.clone(), *krate); |
385 | .root | ||
386 | .components() | ||
387 | .any(|c| c == Component::Normal("library".as_ref())) | ||
388 | { | ||
389 | continue; | ||
390 | } | ||
391 | |||
392 | if let Some(crate_id) = add_target_crate_root( | ||
393 | &mut crate_graph, | ||
394 | &rustc_workspace[pkg], | ||
395 | &rustc_workspace[tgt], | ||
396 | &cfg_options, | ||
397 | proc_macro_client, | ||
398 | load, | ||
399 | ) { | ||
400 | pkg_to_lib_crate.insert(pkg, crate_id); | ||
401 | // Add dependencies on the core / std / alloc for rustc | ||
402 | for (name, krate) in public_deps.iter() { | ||
403 | if let Err(_) = | ||
404 | crate_graph.add_dep(crate_id, name.clone(), *krate) | ||
405 | { | ||
406 | log::error!( | ||
407 | "cyclic dependency on {} for {}", | ||
408 | name, | ||
409 | &cargo[pkg].name | ||
410 | ) | ||
411 | } | ||
412 | } | ||
413 | rustc_pkg_crates.entry(pkg).or_insert_with(Vec::new).push(crate_id); | ||
414 | } | ||
415 | } | ||
416 | } | 395 | } |
417 | // Now add a dep edge from all targets of upstream to the lib | 396 | rustc_pkg_crates.entry(pkg).or_insert_with(Vec::new).push(crate_id); |
418 | // target of downstream. | 397 | } |
419 | for pkg in rustc_workspace.packages() { | 398 | } |
420 | for dep in rustc_workspace[pkg].dependencies.iter() { | 399 | } |
421 | let name = CrateName::new(&dep.name).unwrap(); | 400 | // Now add a dep edge from all targets of upstream to the lib |
422 | if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) { | 401 | // target of downstream. |
423 | for &from in rustc_pkg_crates.get(&pkg).into_iter().flatten() { | 402 | for pkg in rustc_workspace.packages() { |
424 | if let Err(_) = crate_graph.add_dep(from, name.clone(), to) { | 403 | for dep in rustc_workspace[pkg].dependencies.iter() { |
425 | log::error!( | 404 | let name = CrateName::new(&dep.name).unwrap(); |
426 | "cyclic dependency {} -> {}", | 405 | if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) { |
427 | &rustc_workspace[pkg].name, | 406 | for &from in rustc_pkg_crates.get(&pkg).into_iter().flatten() { |
428 | &rustc_workspace[dep.pkg].name | 407 | add_dep(&mut crate_graph, from, name.clone(), to); |
429 | ) | ||
430 | } | ||
431 | } | ||
432 | } | ||
433 | } | ||
434 | } | 408 | } |
409 | } | ||
410 | } | ||
411 | } | ||
435 | 412 | ||
436 | // Add dependencies for all the crates of the current workspace to rustc_private libraries | 413 | // Add dependencies for all the crates of the current workspace to rustc_private libraries |
437 | for dep in rustc_workspace.packages() { | 414 | for dep in rustc_workspace.packages() { |
438 | let name = CrateName::normalize_dashes(&rustc_workspace[dep].name); | 415 | let name = CrateName::normalize_dashes(&rustc_workspace[dep].name); |
439 | 416 | ||
440 | if let Some(&to) = pkg_to_lib_crate.get(&dep) { | 417 | if let Some(&to) = pkg_to_lib_crate.get(&dep) { |
441 | for pkg in cargo.packages() { | 418 | for pkg in cargo.packages() { |
442 | if !cargo[pkg].is_member { | 419 | if !cargo[pkg].is_member { |
443 | continue; | 420 | continue; |
444 | } | 421 | } |
445 | for &from in pkg_crates.get(&pkg).into_iter().flatten() { | 422 | for &from in pkg_crates.get(&pkg).into_iter().flatten() { |
446 | if let Err(_) = crate_graph.add_dep(from, name.clone(), to) { | 423 | add_dep(&mut crate_graph, from, name.clone(), to); |
447 | log::error!( | ||
448 | "cyclic dependency {} -> {}", | ||
449 | &cargo[pkg].name, | ||
450 | &rustc_workspace[dep].name | ||
451 | ) | ||
452 | } | ||
453 | } | ||
454 | } | ||
455 | } | ||
456 | } | 424 | } |
457 | } | 425 | } |
458 | } | 426 | } |
459 | } | 427 | } |
460 | if crate_graph.patch_cfg_if() { | ||
461 | log::debug!("Patched std to depend on cfg-if") | ||
462 | } else { | ||
463 | log::debug!("Did not patch std to depend on cfg-if") | ||
464 | } | ||
465 | crate_graph | ||
466 | } | 428 | } |
429 | crate_graph | ||
467 | } | 430 | } |
468 | 431 | ||
469 | fn add_target_crate_root( | 432 | fn add_target_crate_root( |
470 | crate_graph: &mut CrateGraph, | 433 | crate_graph: &mut CrateGraph, |
471 | pkg: &cargo_workspace::PackageData, | 434 | pkg: &cargo_workspace::PackageData, |
472 | tgt: &cargo_workspace::TargetData, | ||
473 | cfg_options: &CfgOptions, | 435 | cfg_options: &CfgOptions, |
474 | proc_macro_client: &ProcMacroClient, | 436 | proc_macro_client: &ProcMacroClient, |
475 | load: &mut dyn FnMut(&AbsPath) -> Option<FileId>, | 437 | file_id: FileId, |
476 | ) -> Option<CrateId> { | 438 | ) -> CrateId { |
477 | let root = tgt.root.as_path(); | 439 | let edition = pkg.edition; |
478 | if let Some(file_id) = load(root) { | 440 | let cfg_options = { |
479 | let edition = pkg.edition; | 441 | let mut opts = cfg_options.clone(); |
480 | let cfg_options = { | 442 | for feature in pkg.features.iter() { |
481 | let mut opts = cfg_options.clone(); | 443 | opts.insert_key_value("feature".into(), feature.into()); |
482 | for feature in pkg.features.iter() { | 444 | } |
483 | opts.insert_key_value("feature".into(), feature.into()); | 445 | opts.extend(pkg.cfgs.iter().cloned()); |
484 | } | 446 | opts |
485 | opts.extend(pkg.cfgs.iter().cloned()); | 447 | }; |
486 | opts | 448 | let mut env = Env::default(); |
487 | }; | 449 | if let Some(out_dir) = &pkg.out_dir { |
488 | let mut env = Env::default(); | 450 | // NOTE: cargo and rustc seem to hide non-UTF-8 strings from env! and option_env!() |
489 | if let Some(out_dir) = &pkg.out_dir { | 451 | if let Some(out_dir) = out_dir.to_str().map(|s| s.to_owned()) { |
490 | // NOTE: cargo and rustc seem to hide non-UTF-8 strings from env! and option_env!() | 452 | env.set("OUT_DIR", out_dir); |
491 | if let Some(out_dir) = out_dir.to_str().map(|s| s.to_owned()) { | ||
492 | env.set("OUT_DIR", out_dir); | ||
493 | } | ||
494 | } | 453 | } |
495 | let proc_macro = pkg | ||
496 | .proc_macro_dylib_path | ||
497 | .as_ref() | ||
498 | .map(|it| proc_macro_client.by_dylib_path(&it)) | ||
499 | .unwrap_or_default(); | ||
500 | |||
501 | let display_name = CrateDisplayName::from_canonical_name(pkg.name.clone()); | ||
502 | let crate_id = crate_graph.add_crate_root( | ||
503 | file_id, | ||
504 | edition, | ||
505 | Some(display_name), | ||
506 | cfg_options, | ||
507 | env, | ||
508 | proc_macro.clone(), | ||
509 | ); | ||
510 | |||
511 | return Some(crate_id); | ||
512 | } | 454 | } |
513 | None | 455 | let proc_macro = pkg |
456 | .proc_macro_dylib_path | ||
457 | .as_ref() | ||
458 | .map(|it| proc_macro_client.by_dylib_path(&it)) | ||
459 | .unwrap_or_default(); | ||
460 | |||
461 | let display_name = CrateDisplayName::from_canonical_name(pkg.name.clone()); | ||
462 | let crate_id = crate_graph.add_crate_root( | ||
463 | file_id, | ||
464 | edition, | ||
465 | Some(display_name), | ||
466 | cfg_options, | ||
467 | env, | ||
468 | proc_macro.clone(), | ||
469 | ); | ||
470 | |||
471 | crate_id | ||
514 | } | 472 | } |
473 | |||
515 | fn sysroot_to_crate_graph( | 474 | fn sysroot_to_crate_graph( |
516 | crate_graph: &mut CrateGraph, | 475 | crate_graph: &mut CrateGraph, |
517 | sysroot: &Sysroot, | 476 | sysroot: &Sysroot, |
@@ -520,19 +479,18 @@ fn sysroot_to_crate_graph( | |||
520 | ) -> (Vec<(CrateName, CrateId)>, Option<CrateId>) { | 479 | ) -> (Vec<(CrateName, CrateId)>, Option<CrateId>) { |
521 | let mut cfg_options = CfgOptions::default(); | 480 | let mut cfg_options = CfgOptions::default(); |
522 | cfg_options.extend(get_rustc_cfg_options(target)); | 481 | cfg_options.extend(get_rustc_cfg_options(target)); |
523 | let sysroot_crates: FxHashMap<_, _> = sysroot | 482 | let sysroot_crates: FxHashMap<SysrootCrate, CrateId> = sysroot |
524 | .crates() | 483 | .crates() |
525 | .filter_map(|krate| { | 484 | .filter_map(|krate| { |
526 | let file_id = load(&sysroot[krate].root)?; | 485 | let file_id = load(&sysroot[krate].root)?; |
527 | 486 | ||
528 | let env = Env::default(); | 487 | let env = Env::default(); |
529 | let proc_macro = vec![]; | 488 | let proc_macro = vec![]; |
530 | let name = CrateName::new(&sysroot[krate].name) | 489 | let display_name = CrateDisplayName::from_canonical_name(sysroot[krate].name.clone()); |
531 | .expect("Sysroot crates' names do not contain dashes"); | ||
532 | let crate_id = crate_graph.add_crate_root( | 490 | let crate_id = crate_graph.add_crate_root( |
533 | file_id, | 491 | file_id, |
534 | Edition::Edition2018, | 492 | Edition::Edition2018, |
535 | Some(name.into()), | 493 | Some(display_name), |
536 | cfg_options.clone(), | 494 | cfg_options.clone(), |
537 | env, | 495 | env, |
538 | proc_macro, | 496 | proc_macro, |
@@ -545,9 +503,7 @@ fn sysroot_to_crate_graph( | |||
545 | for &to in sysroot[from].deps.iter() { | 503 | for &to in sysroot[from].deps.iter() { |
546 | let name = CrateName::new(&sysroot[to].name).unwrap(); | 504 | let name = CrateName::new(&sysroot[to].name).unwrap(); |
547 | if let (Some(&from), Some(&to)) = (sysroot_crates.get(&from), sysroot_crates.get(&to)) { | 505 | if let (Some(&from), Some(&to)) = (sysroot_crates.get(&from), sysroot_crates.get(&to)) { |
548 | if let Err(_) = crate_graph.add_dep(from, name, to) { | 506 | add_dep(crate_graph, from, name, to); |
549 | log::error!("cyclic dependency between sysroot crates") | ||
550 | } | ||
551 | } | 507 | } |
552 | } | 508 | } |
553 | } | 509 | } |
@@ -588,3 +544,9 @@ fn get_rustc_cfg_options(target: Option<&str>) -> Vec<CfgFlag> { | |||
588 | 544 | ||
589 | res | 545 | res |
590 | } | 546 | } |
547 | |||
548 | fn add_dep(graph: &mut CrateGraph, from: CrateId, name: CrateName, to: CrateId) { | ||
549 | if let Err(err) = graph.add_dep(from, name, to) { | ||
550 | log::error!("{}", err) | ||
551 | } | ||
552 | } | ||
diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml index d25c4bf83..436f5041b 100644 --- a/crates/rust-analyzer/Cargo.toml +++ b/crates/rust-analyzer/Cargo.toml | |||
@@ -21,7 +21,7 @@ env_logger = { version = "0.8.1", default-features = false } | |||
21 | itertools = "0.9.0" | 21 | itertools = "0.9.0" |
22 | jod-thread = "0.1.0" | 22 | jod-thread = "0.1.0" |
23 | log = "0.4.8" | 23 | log = "0.4.8" |
24 | lsp-types = { version = "0.83.0", features = ["proposed"] } | 24 | lsp-types = { version = "0.84.0", features = ["proposed"] } |
25 | parking_lot = "0.11.0" | 25 | parking_lot = "0.11.0" |
26 | pico-args = "0.3.1" | 26 | pico-args = "0.3.1" |
27 | oorandom = "11.1.2" | 27 | oorandom = "11.1.2" |
@@ -31,7 +31,7 @@ serde_json = "1.0.48" | |||
31 | threadpool = "1.7.1" | 31 | threadpool = "1.7.1" |
32 | rayon = "1.5" | 32 | rayon = "1.5" |
33 | mimalloc = { version = "0.1.19", default-features = false, optional = true } | 33 | mimalloc = { version = "0.1.19", default-features = false, optional = true } |
34 | lsp-server = "0.4.0" | 34 | lsp-server = "0.5.0" |
35 | tracing = "0.1" | 35 | tracing = "0.1" |
36 | tracing-subscriber = { version = "0.2", default-features = false, features = ["env-filter", "registry"] } | 36 | tracing-subscriber = { version = "0.2", default-features = false, features = ["env-filter", "registry"] } |
37 | tracing-tree = { version = "0.1.4" } | 37 | tracing-tree = { version = "0.1.4" } |
diff --git a/crates/rust-analyzer/src/caps.rs b/crates/rust-analyzer/src/caps.rs index 9a8870053..c7203451c 100644 --- a/crates/rust-analyzer/src/caps.rs +++ b/crates/rust-analyzer/src/caps.rs | |||
@@ -62,6 +62,7 @@ pub fn server_capabilities(client_caps: &ClientCapabilities) -> ServerCapabiliti | |||
62 | prepare_provider: Some(true), | 62 | prepare_provider: Some(true), |
63 | work_done_progress_options: WorkDoneProgressOptions { work_done_progress: None }, | 63 | work_done_progress_options: WorkDoneProgressOptions { work_done_progress: None }, |
64 | })), | 64 | })), |
65 | on_type_rename_provider: None, | ||
65 | document_link_provider: None, | 66 | document_link_provider: None, |
66 | color_provider: None, | 67 | color_provider: None, |
67 | execute_command_provider: None, | 68 | execute_command_provider: None, |
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index d16796590..a334cdb11 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs | |||
@@ -184,6 +184,7 @@ impl Config { | |||
184 | }, | 184 | }, |
185 | completion: CompletionConfig { | 185 | completion: CompletionConfig { |
186 | enable_postfix_completions: true, | 186 | enable_postfix_completions: true, |
187 | enable_experimental_completions: true, | ||
187 | add_call_parenthesis: true, | 188 | add_call_parenthesis: true, |
188 | add_call_argument_snippets: true, | 189 | add_call_argument_snippets: true, |
189 | ..CompletionConfig::default() | 190 | ..CompletionConfig::default() |
@@ -294,10 +295,6 @@ impl Config { | |||
294 | max_length: data.inlayHints_maxLength, | 295 | max_length: data.inlayHints_maxLength, |
295 | }; | 296 | }; |
296 | 297 | ||
297 | self.completion.enable_postfix_completions = data.completion_postfix_enable; | ||
298 | self.completion.add_call_parenthesis = data.completion_addCallParenthesis; | ||
299 | self.completion.add_call_argument_snippets = data.completion_addCallArgumentSnippets; | ||
300 | |||
301 | self.assist.insert_use.merge = match data.assist_importMergeBehaviour { | 298 | self.assist.insert_use.merge = match data.assist_importMergeBehaviour { |
302 | MergeBehaviourDef::None => None, | 299 | MergeBehaviourDef::None => None, |
303 | MergeBehaviourDef::Full => Some(MergeBehaviour::Full), | 300 | MergeBehaviourDef::Full => Some(MergeBehaviour::Full), |
@@ -309,6 +306,12 @@ impl Config { | |||
309 | ImportPrefixDef::BySelf => PrefixKind::BySelf, | 306 | ImportPrefixDef::BySelf => PrefixKind::BySelf, |
310 | }; | 307 | }; |
311 | 308 | ||
309 | self.completion.enable_postfix_completions = data.completion_postfix_enable; | ||
310 | self.completion.enable_experimental_completions = data.completion_enableExperimental; | ||
311 | self.completion.add_call_parenthesis = data.completion_addCallParenthesis; | ||
312 | self.completion.add_call_argument_snippets = data.completion_addCallArgumentSnippets; | ||
313 | self.completion.merge = self.assist.insert_use.merge; | ||
314 | |||
312 | self.call_info_full = data.callInfo_full; | 315 | self.call_info_full = data.callInfo_full; |
313 | 316 | ||
314 | self.lens = LensConfig { | 317 | self.lens = LensConfig { |
@@ -505,6 +508,7 @@ config_data! { | |||
505 | completion_addCallArgumentSnippets: bool = true, | 508 | completion_addCallArgumentSnippets: bool = true, |
506 | completion_addCallParenthesis: bool = true, | 509 | completion_addCallParenthesis: bool = true, |
507 | completion_postfix_enable: bool = true, | 510 | completion_postfix_enable: bool = true, |
511 | completion_enableExperimental: bool = true, | ||
508 | 512 | ||
509 | diagnostics_enable: bool = true, | 513 | diagnostics_enable: bool = true, |
510 | diagnostics_enableExperimental: bool = true, | 514 | diagnostics_enableExperimental: bool = true, |
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/clippy_pass_by_ref.txt b/crates/rust-analyzer/src/diagnostics/test_data/clippy_pass_by_ref.txt index 5b2e5187a..72f6c5725 100644 --- a/crates/rust-analyzer/src/diagnostics/test_data/clippy_pass_by_ref.txt +++ b/crates/rust-analyzer/src/diagnostics/test_data/clippy_pass_by_ref.txt | |||
@@ -27,7 +27,24 @@ | |||
27 | "trivially_copy_pass_by_ref", | 27 | "trivially_copy_pass_by_ref", |
28 | ), | 28 | ), |
29 | ), | 29 | ), |
30 | code_description: None, | 30 | code_description: Some( |
31 | CodeDescription { | ||
32 | href: Url { | ||
33 | scheme: "https", | ||
34 | host: Some( | ||
35 | Domain( | ||
36 | "rust-lang.github.io", | ||
37 | ), | ||
38 | ), | ||
39 | port: None, | ||
40 | path: "/rust-clippy/master/index.html", | ||
41 | query: None, | ||
42 | fragment: Some( | ||
43 | "trivially_copy_pass_by_ref", | ||
44 | ), | ||
45 | }, | ||
46 | }, | ||
47 | ), | ||
31 | source: Some( | 48 | source: Some( |
32 | "clippy", | 49 | "clippy", |
33 | ), | 50 | ), |
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/handles_macro_location.txt b/crates/rust-analyzer/src/diagnostics/test_data/handles_macro_location.txt index 116f0ff73..eb4a6b597 100644 --- a/crates/rust-analyzer/src/diagnostics/test_data/handles_macro_location.txt +++ b/crates/rust-analyzer/src/diagnostics/test_data/handles_macro_location.txt | |||
@@ -27,7 +27,24 @@ | |||
27 | "E0277", | 27 | "E0277", |
28 | ), | 28 | ), |
29 | ), | 29 | ), |
30 | code_description: None, | 30 | code_description: Some( |
31 | CodeDescription { | ||
32 | href: Url { | ||
33 | scheme: "https", | ||
34 | host: Some( | ||
35 | Domain( | ||
36 | "doc.rust-lang.org", | ||
37 | ), | ||
38 | ), | ||
39 | port: None, | ||
40 | path: "/error-index.html", | ||
41 | query: None, | ||
42 | fragment: Some( | ||
43 | "E0277", | ||
44 | ), | ||
45 | }, | ||
46 | }, | ||
47 | ), | ||
31 | source: Some( | 48 | source: Some( |
32 | "rustc", | 49 | "rustc", |
33 | ), | 50 | ), |
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/rustc_incompatible_type_for_trait.txt b/crates/rust-analyzer/src/diagnostics/test_data/rustc_incompatible_type_for_trait.txt index 2cbf657e5..19f72196d 100644 --- a/crates/rust-analyzer/src/diagnostics/test_data/rustc_incompatible_type_for_trait.txt +++ b/crates/rust-analyzer/src/diagnostics/test_data/rustc_incompatible_type_for_trait.txt | |||
@@ -27,7 +27,24 @@ | |||
27 | "E0053", | 27 | "E0053", |
28 | ), | 28 | ), |
29 | ), | 29 | ), |
30 | code_description: None, | 30 | code_description: Some( |
31 | CodeDescription { | ||
32 | href: Url { | ||
33 | scheme: "https", | ||
34 | host: Some( | ||
35 | Domain( | ||
36 | "doc.rust-lang.org", | ||
37 | ), | ||
38 | ), | ||
39 | port: None, | ||
40 | path: "/error-index.html", | ||
41 | query: None, | ||
42 | fragment: Some( | ||
43 | "E0053", | ||
44 | ), | ||
45 | }, | ||
46 | }, | ||
47 | ), | ||
31 | source: Some( | 48 | source: Some( |
32 | "rustc", | 49 | "rustc", |
33 | ), | 50 | ), |
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/rustc_mismatched_type.txt b/crates/rust-analyzer/src/diagnostics/test_data/rustc_mismatched_type.txt index 1142dc2ac..15ac95d72 100644 --- a/crates/rust-analyzer/src/diagnostics/test_data/rustc_mismatched_type.txt +++ b/crates/rust-analyzer/src/diagnostics/test_data/rustc_mismatched_type.txt | |||
@@ -27,7 +27,24 @@ | |||
27 | "E0308", | 27 | "E0308", |
28 | ), | 28 | ), |
29 | ), | 29 | ), |
30 | code_description: None, | 30 | code_description: Some( |
31 | CodeDescription { | ||
32 | href: Url { | ||
33 | scheme: "https", | ||
34 | host: Some( | ||
35 | Domain( | ||
36 | "doc.rust-lang.org", | ||
37 | ), | ||
38 | ), | ||
39 | port: None, | ||
40 | path: "/error-index.html", | ||
41 | query: None, | ||
42 | fragment: Some( | ||
43 | "E0308", | ||
44 | ), | ||
45 | }, | ||
46 | }, | ||
47 | ), | ||
31 | source: Some( | 48 | source: Some( |
32 | "rustc", | 49 | "rustc", |
33 | ), | 50 | ), |
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/rustc_wrong_number_of_parameters.txt b/crates/rust-analyzer/src/diagnostics/test_data/rustc_wrong_number_of_parameters.txt index 782c72dbd..b9650f3e4 100644 --- a/crates/rust-analyzer/src/diagnostics/test_data/rustc_wrong_number_of_parameters.txt +++ b/crates/rust-analyzer/src/diagnostics/test_data/rustc_wrong_number_of_parameters.txt | |||
@@ -27,7 +27,24 @@ | |||
27 | "E0061", | 27 | "E0061", |
28 | ), | 28 | ), |
29 | ), | 29 | ), |
30 | code_description: None, | 30 | code_description: Some( |
31 | CodeDescription { | ||
32 | href: Url { | ||
33 | scheme: "https", | ||
34 | host: Some( | ||
35 | Domain( | ||
36 | "doc.rust-lang.org", | ||
37 | ), | ||
38 | ), | ||
39 | port: None, | ||
40 | path: "/error-index.html", | ||
41 | query: None, | ||
42 | fragment: Some( | ||
43 | "E0061", | ||
44 | ), | ||
45 | }, | ||
46 | }, | ||
47 | ), | ||
31 | source: Some( | 48 | source: Some( |
32 | "rustc", | 49 | "rustc", |
33 | ), | 50 | ), |
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/snap_multi_line_fix.txt b/crates/rust-analyzer/src/diagnostics/test_data/snap_multi_line_fix.txt index d3f27ab6a..c45f68a91 100644 --- a/crates/rust-analyzer/src/diagnostics/test_data/snap_multi_line_fix.txt +++ b/crates/rust-analyzer/src/diagnostics/test_data/snap_multi_line_fix.txt | |||
@@ -27,7 +27,24 @@ | |||
27 | "let_and_return", | 27 | "let_and_return", |
28 | ), | 28 | ), |
29 | ), | 29 | ), |
30 | code_description: None, | 30 | code_description: Some( |
31 | CodeDescription { | ||
32 | href: Url { | ||
33 | scheme: "https", | ||
34 | host: Some( | ||
35 | Domain( | ||
36 | "rust-lang.github.io", | ||
37 | ), | ||
38 | ), | ||
39 | port: None, | ||
40 | path: "/rust-clippy/master/index.html", | ||
41 | query: None, | ||
42 | fragment: Some( | ||
43 | "let_and_return", | ||
44 | ), | ||
45 | }, | ||
46 | }, | ||
47 | ), | ||
31 | source: Some( | 48 | source: Some( |
32 | "clippy", | 49 | "clippy", |
33 | ), | 50 | ), |
diff --git a/crates/rust-analyzer/src/diagnostics/to_proto.rs b/crates/rust-analyzer/src/diagnostics/to_proto.rs index 15145415b..324019614 100644 --- a/crates/rust-analyzer/src/diagnostics/to_proto.rs +++ b/crates/rust-analyzer/src/diagnostics/to_proto.rs | |||
@@ -55,8 +55,8 @@ fn location_naive(workspace_root: &Path, span: &DiagnosticSpan) -> lsp_types::Lo | |||
55 | 55 | ||
56 | // FIXME: this doesn't handle UTF16 offsets correctly | 56 | // FIXME: this doesn't handle UTF16 offsets correctly |
57 | let range = lsp_types::Range::new( | 57 | let range = lsp_types::Range::new( |
58 | lsp_types::Position::new(span.line_start as u64 - 1, span.column_start as u64 - 1), | 58 | lsp_types::Position::new(span.line_start as u32 - 1, span.column_start as u32 - 1), |
59 | lsp_types::Position::new(span.line_end as u64 - 1, span.column_end as u64 - 1), | 59 | lsp_types::Position::new(span.line_end as u32 - 1, span.column_end as u32 - 1), |
60 | ); | 60 | ); |
61 | 61 | ||
62 | lsp_types::Location { uri, range } | 62 | lsp_types::Location { uri, range } |
@@ -211,6 +211,12 @@ pub(crate) fn map_rust_diagnostic_to_lsp( | |||
211 | } | 211 | } |
212 | } | 212 | } |
213 | 213 | ||
214 | let code_description = match source.as_str() { | ||
215 | "rustc" => rustc_code_description(code.as_deref()), | ||
216 | "clippy" => clippy_code_description(code.as_deref()), | ||
217 | _ => None, | ||
218 | }; | ||
219 | |||
214 | primary_spans | 220 | primary_spans |
215 | .iter() | 221 | .iter() |
216 | .map(|primary_span| { | 222 | .map(|primary_span| { |
@@ -248,7 +254,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp( | |||
248 | range: in_macro_location.range, | 254 | range: in_macro_location.range, |
249 | severity, | 255 | severity, |
250 | code: code.clone().map(lsp_types::NumberOrString::String), | 256 | code: code.clone().map(lsp_types::NumberOrString::String), |
251 | code_description: None, | 257 | code_description: code_description.clone(), |
252 | source: Some(source.clone()), | 258 | source: Some(source.clone()), |
253 | message: message.clone(), | 259 | message: message.clone(), |
254 | related_information: Some(information_for_additional_diagnostic), | 260 | related_information: Some(information_for_additional_diagnostic), |
@@ -269,7 +275,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp( | |||
269 | range: location.range, | 275 | range: location.range, |
270 | severity, | 276 | severity, |
271 | code: code.clone().map(lsp_types::NumberOrString::String), | 277 | code: code.clone().map(lsp_types::NumberOrString::String), |
272 | code_description: None, | 278 | code_description: code_description.clone(), |
273 | source: Some(source.clone()), | 279 | source: Some(source.clone()), |
274 | message, | 280 | message, |
275 | related_information: if related_information.is_empty() { | 281 | related_information: if related_information.is_empty() { |
@@ -292,6 +298,31 @@ pub(crate) fn map_rust_diagnostic_to_lsp( | |||
292 | .collect() | 298 | .collect() |
293 | } | 299 | } |
294 | 300 | ||
301 | fn rustc_code_description(code: Option<&str>) -> Option<lsp_types::CodeDescription> { | ||
302 | code.filter(|code| { | ||
303 | let mut chars = code.chars(); | ||
304 | chars.next().map_or(false, |c| c == 'E') | ||
305 | && chars.by_ref().take(4).all(|c| c.is_ascii_digit()) | ||
306 | && chars.next().is_none() | ||
307 | }) | ||
308 | .and_then(|code| { | ||
309 | lsp_types::Url::parse(&format!("https://doc.rust-lang.org/error-index.html#{}", code)) | ||
310 | .ok() | ||
311 | .map(|href| lsp_types::CodeDescription { href }) | ||
312 | }) | ||
313 | } | ||
314 | |||
315 | fn clippy_code_description(code: Option<&str>) -> Option<lsp_types::CodeDescription> { | ||
316 | code.and_then(|code| { | ||
317 | lsp_types::Url::parse(&format!( | ||
318 | "https://rust-lang.github.io/rust-clippy/master/index.html#{}", | ||
319 | code | ||
320 | )) | ||
321 | .ok() | ||
322 | .map(|href| lsp_types::CodeDescription { href }) | ||
323 | }) | ||
324 | } | ||
325 | |||
295 | #[cfg(test)] | 326 | #[cfg(test)] |
296 | #[cfg(not(windows))] | 327 | #[cfg(not(windows))] |
297 | mod tests { | 328 | mod tests { |
diff --git a/crates/rust-analyzer/src/document.rs b/crates/rust-analyzer/src/document.rs index 04c7ee150..cf091510f 100644 --- a/crates/rust-analyzer/src/document.rs +++ b/crates/rust-analyzer/src/document.rs | |||
@@ -6,11 +6,11 @@ | |||
6 | /// client notifications. | 6 | /// client notifications. |
7 | #[derive(Debug, Clone)] | 7 | #[derive(Debug, Clone)] |
8 | pub(crate) struct DocumentData { | 8 | pub(crate) struct DocumentData { |
9 | pub(crate) version: Option<i64>, | 9 | pub(crate) version: i32, |
10 | } | 10 | } |
11 | 11 | ||
12 | impl DocumentData { | 12 | impl DocumentData { |
13 | pub(crate) fn new(version: i64) -> Self { | 13 | pub(crate) fn new(version: i32) -> Self { |
14 | DocumentData { version: Some(version) } | 14 | DocumentData { version } |
15 | } | 15 | } |
16 | } | 16 | } |
diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs index 63c70a09d..defe11c55 100644 --- a/crates/rust-analyzer/src/global_state.rs +++ b/crates/rust-analyzer/src/global_state.rs | |||
@@ -263,9 +263,9 @@ impl GlobalStateSnapshot { | |||
263 | self.vfs.read().1[&id] | 263 | self.vfs.read().1[&id] |
264 | } | 264 | } |
265 | 265 | ||
266 | pub(crate) fn url_file_version(&self, url: &Url) -> Option<i64> { | 266 | pub(crate) fn url_file_version(&self, url: &Url) -> Option<i32> { |
267 | let path = from_proto::vfs_path(&url).ok()?; | 267 | let path = from_proto::vfs_path(&url).ok()?; |
268 | self.mem_docs.get(&path)?.version | 268 | Some(self.mem_docs.get(&path)?.version) |
269 | } | 269 | } |
270 | 270 | ||
271 | pub(crate) fn anchored_path(&self, file_id: FileId, path: &str) -> Url { | 271 | pub(crate) fn anchored_path(&self, file_id: FileId, path: &str) -> Url { |
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs index 782797e85..1cf4139d2 100644 --- a/crates/rust-analyzer/src/handlers.rs +++ b/crates/rust-analyzer/src/handlers.rs | |||
@@ -18,7 +18,7 @@ use lsp_types::{ | |||
18 | CallHierarchyOutgoingCall, CallHierarchyOutgoingCallsParams, CallHierarchyPrepareParams, | 18 | CallHierarchyOutgoingCall, CallHierarchyOutgoingCallsParams, CallHierarchyPrepareParams, |
19 | CodeActionKind, CodeLens, Command, CompletionItem, Diagnostic, DiagnosticTag, | 19 | CodeActionKind, CodeLens, Command, CompletionItem, Diagnostic, DiagnosticTag, |
20 | DocumentFormattingParams, DocumentHighlight, DocumentSymbol, FoldingRange, FoldingRangeParams, | 20 | DocumentFormattingParams, DocumentHighlight, DocumentSymbol, FoldingRange, FoldingRangeParams, |
21 | HoverContents, Location, Position, PrepareRenameResponse, Range, RenameParams, | 21 | HoverContents, Location, NumberOrString, Position, PrepareRenameResponse, Range, RenameParams, |
22 | SemanticTokensDeltaParams, SemanticTokensFullDeltaResult, SemanticTokensParams, | 22 | SemanticTokensDeltaParams, SemanticTokensFullDeltaResult, SemanticTokensParams, |
23 | SemanticTokensRangeParams, SemanticTokensRangeResult, SemanticTokensResult, SymbolInformation, | 23 | SemanticTokensRangeParams, SemanticTokensRangeResult, SemanticTokensResult, SymbolInformation, |
24 | SymbolTag, TextDocumentIdentifier, Url, WorkspaceEdit, | 24 | SymbolTag, TextDocumentIdentifier, Url, WorkspaceEdit, |
@@ -573,7 +573,8 @@ pub(crate) fn handle_completion( | |||
573 | .flat_map(|item| to_proto::completion_item(&line_index, line_endings, item)) | 573 | .flat_map(|item| to_proto::completion_item(&line_index, line_endings, item)) |
574 | .collect(); | 574 | .collect(); |
575 | 575 | ||
576 | Ok(Some(items.into())) | 576 | let completion_list = lsp_types::CompletionList { is_incomplete: true, items }; |
577 | Ok(Some(completion_list.into())) | ||
577 | } | 578 | } |
578 | 579 | ||
579 | pub(crate) fn handle_folding_range( | 580 | pub(crate) fn handle_folding_range( |
@@ -1127,8 +1128,15 @@ pub(crate) fn publish_diagnostics( | |||
1127 | .map(|d| Diagnostic { | 1128 | .map(|d| Diagnostic { |
1128 | range: to_proto::range(&line_index, d.range), | 1129 | range: to_proto::range(&line_index, d.range), |
1129 | severity: Some(to_proto::diagnostic_severity(d.severity)), | 1130 | severity: Some(to_proto::diagnostic_severity(d.severity)), |
1130 | code: None, | 1131 | code: d.code.map(|d| d.as_str().to_owned()).map(NumberOrString::String), |
1131 | code_description: None, | 1132 | code_description: d.code.and_then(|code| { |
1133 | lsp_types::Url::parse(&format!( | ||
1134 | "https://rust-analyzer.github.io/manual.html#{}", | ||
1135 | code.as_str() | ||
1136 | )) | ||
1137 | .ok() | ||
1138 | .map(|href| lsp_types::CodeDescription { href }) | ||
1139 | }), | ||
1132 | source: Some("rust-analyzer".to_string()), | 1140 | source: Some("rust-analyzer".to_string()), |
1133 | message: d.message, | 1141 | message: d.message, |
1134 | related_information: None, | 1142 | related_information: None, |
diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs index a5c65fa3e..93ac45415 100644 --- a/crates/rust-analyzer/src/lsp_ext.rs +++ b/crates/rust-analyzer/src/lsp_ext.rs | |||
@@ -302,7 +302,7 @@ pub enum SnippetDocumentChangeOperation { | |||
302 | #[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)] | 302 | #[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)] |
303 | #[serde(rename_all = "camelCase")] | 303 | #[serde(rename_all = "camelCase")] |
304 | pub struct SnippetTextDocumentEdit { | 304 | pub struct SnippetTextDocumentEdit { |
305 | pub text_document: lsp_types::VersionedTextDocumentIdentifier, | 305 | pub text_document: lsp_types::OptionalVersionedTextDocumentIdentifier, |
306 | pub edits: Vec<SnippetTextEdit>, | 306 | pub edits: Vec<SnippetTextEdit>, |
307 | } | 307 | } |
308 | 308 | ||
diff --git a/crates/rust-analyzer/src/lsp_utils.rs b/crates/rust-analyzer/src/lsp_utils.rs index 1d271a9d8..6427c7367 100644 --- a/crates/rust-analyzer/src/lsp_utils.rs +++ b/crates/rust-analyzer/src/lsp_utils.rs | |||
@@ -51,7 +51,7 @@ impl GlobalState { | |||
51 | } | 51 | } |
52 | let percentage = fraction.map(|f| { | 52 | let percentage = fraction.map(|f| { |
53 | assert!(0.0 <= f && f <= 1.0); | 53 | assert!(0.0 <= f && f <= 1.0); |
54 | f * 100.0 | 54 | (f * 100.0) as u32 |
55 | }); | 55 | }); |
56 | let token = lsp_types::ProgressToken::String(format!("rustAnalyzer/{}", title)); | 56 | let token = lsp_types::ProgressToken::String(format!("rustAnalyzer/{}", title)); |
57 | let work_done_progress = match state { | 57 | let work_done_progress = match state { |
@@ -98,11 +98,11 @@ pub(crate) fn apply_document_changes( | |||
98 | // The VFS will normalize the end of lines to `\n`. | 98 | // The VFS will normalize the end of lines to `\n`. |
99 | enum IndexValid { | 99 | enum IndexValid { |
100 | All, | 100 | All, |
101 | UpToLineExclusive(u64), | 101 | UpToLineExclusive(u32), |
102 | } | 102 | } |
103 | 103 | ||
104 | impl IndexValid { | 104 | impl IndexValid { |
105 | fn covers(&self, line: u64) -> bool { | 105 | fn covers(&self, line: u32) -> bool { |
106 | match *self { | 106 | match *self { |
107 | IndexValid::UpToLineExclusive(to) => to > line, | 107 | IndexValid::UpToLineExclusive(to) => to > line, |
108 | _ => true, | 108 | _ => true, |
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index 68a53bbcb..866d1d176 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs | |||
@@ -1,7 +1,7 @@ | |||
1 | //! The main loop of `rust-analyzer` responsible for dispatching LSP | 1 | //! The main loop of `rust-analyzer` responsible for dispatching LSP |
2 | //! requests/replies and notifications back to the client. | 2 | //! requests/replies and notifications back to the client. |
3 | use std::{ | 3 | use std::{ |
4 | env, fmt, panic, | 4 | env, fmt, |
5 | time::{Duration, Instant}, | 5 | time::{Duration, Instant}, |
6 | }; | 6 | }; |
7 | 7 | ||
@@ -348,13 +348,7 @@ impl GlobalState { | |||
348 | } | 348 | } |
349 | 349 | ||
350 | if self.status == Status::Ready && (state_changed || prev_status == Status::Loading) { | 350 | if self.status == Status::Ready && (state_changed || prev_status == Status::Loading) { |
351 | let subscriptions = self | 351 | self.update_file_notifications_on_threadpool(); |
352 | .mem_docs | ||
353 | .keys() | ||
354 | .map(|path| self.vfs.read().0.file_id(&path).unwrap()) | ||
355 | .collect::<Vec<_>>(); | ||
356 | |||
357 | self.update_file_notifications_on_threadpool(subscriptions); | ||
358 | 352 | ||
359 | // Refresh semantic tokens if the client supports it. | 353 | // Refresh semantic tokens if the client supports it. |
360 | if self.config.semantic_tokens_refresh { | 354 | if self.config.semantic_tokens_refresh { |
@@ -368,7 +362,7 @@ impl GlobalState { | |||
368 | let url = file_id_to_url(&self.vfs.read().0, file_id); | 362 | let url = file_id_to_url(&self.vfs.read().0, file_id); |
369 | let diagnostics = self.diagnostics.diagnostics_for(file_id).cloned().collect(); | 363 | let diagnostics = self.diagnostics.diagnostics_for(file_id).cloned().collect(); |
370 | let version = from_proto::vfs_path(&url) | 364 | let version = from_proto::vfs_path(&url) |
371 | .map(|path| self.mem_docs.get(&path)?.version) | 365 | .map(|path| self.mem_docs.get(&path).map(|it| it.version)) |
372 | .unwrap_or_default(); | 366 | .unwrap_or_default(); |
373 | 367 | ||
374 | self.send_notification::<lsp_types::notification::PublishDiagnostics>( | 368 | self.send_notification::<lsp_types::notification::PublishDiagnostics>( |
@@ -498,12 +492,19 @@ impl GlobalState { | |||
498 | .write() | 492 | .write() |
499 | .0 | 493 | .0 |
500 | .set_file_contents(path, Some(params.text_document.text.into_bytes())); | 494 | .set_file_contents(path, Some(params.text_document.text.into_bytes())); |
495 | this.update_file_notifications_on_threadpool(); | ||
501 | } | 496 | } |
502 | Ok(()) | 497 | Ok(()) |
503 | })? | 498 | })? |
504 | .on::<lsp_types::notification::DidChangeTextDocument>(|this, params| { | 499 | .on::<lsp_types::notification::DidChangeTextDocument>(|this, params| { |
505 | if let Ok(path) = from_proto::vfs_path(¶ms.text_document.uri) { | 500 | if let Ok(path) = from_proto::vfs_path(¶ms.text_document.uri) { |
506 | let doc = this.mem_docs.get_mut(&path).unwrap(); | 501 | let doc = match this.mem_docs.get_mut(&path) { |
502 | Some(doc) => doc, | ||
503 | None => { | ||
504 | log::error!("expected DidChangeTextDocument: {}", path); | ||
505 | return Ok(()); | ||
506 | } | ||
507 | }; | ||
507 | let vfs = &mut this.vfs.write().0; | 508 | let vfs = &mut this.vfs.write().0; |
508 | let file_id = vfs.file_id(&path).unwrap(); | 509 | let file_id = vfs.file_id(&path).unwrap(); |
509 | let mut text = String::from_utf8(vfs.file_contents(file_id).to_vec()).unwrap(); | 510 | let mut text = String::from_utf8(vfs.file_contents(file_id).to_vec()).unwrap(); |
@@ -521,7 +522,7 @@ impl GlobalState { | |||
521 | let mut version = None; | 522 | let mut version = None; |
522 | if let Ok(path) = from_proto::vfs_path(¶ms.text_document.uri) { | 523 | if let Ok(path) = from_proto::vfs_path(¶ms.text_document.uri) { |
523 | match this.mem_docs.remove(&path) { | 524 | match this.mem_docs.remove(&path) { |
524 | Some(doc) => version = doc.version, | 525 | Some(doc) => version = Some(doc.version), |
525 | None => log::error!("orphan DidCloseTextDocument: {}", path), | 526 | None => log::error!("orphan DidCloseTextDocument: {}", path), |
526 | } | 527 | } |
527 | 528 | ||
@@ -600,7 +601,13 @@ impl GlobalState { | |||
600 | .finish(); | 601 | .finish(); |
601 | Ok(()) | 602 | Ok(()) |
602 | } | 603 | } |
603 | fn update_file_notifications_on_threadpool(&mut self, subscriptions: Vec<FileId>) { | 604 | fn update_file_notifications_on_threadpool(&mut self) { |
605 | let subscriptions = self | ||
606 | .mem_docs | ||
607 | .keys() | ||
608 | .map(|path| self.vfs.read().0.file_id(&path).unwrap()) | ||
609 | .collect::<Vec<_>>(); | ||
610 | |||
604 | log::trace!("updating notifications for {:?}", subscriptions); | 611 | log::trace!("updating notifications for {:?}", subscriptions); |
605 | if self.config.publish_diagnostics { | 612 | if self.config.publish_diagnostics { |
606 | let snapshot = self.snapshot(); | 613 | let snapshot = self.snapshot(); |
diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs index fa6e09f42..001bf5949 100644 --- a/crates/rust-analyzer/src/reload.rs +++ b/crates/rust-analyzer/src/reload.rs | |||
@@ -203,7 +203,11 @@ impl GlobalState { | |||
203 | let contents = loader.handle.load_sync(path); | 203 | let contents = loader.handle.load_sync(path); |
204 | vfs.set_file_contents(vfs_path.clone(), contents); | 204 | vfs.set_file_contents(vfs_path.clone(), contents); |
205 | } | 205 | } |
206 | vfs.file_id(&vfs_path) | 206 | let res = vfs.file_id(&vfs_path); |
207 | if res.is_none() { | ||
208 | log::error!("failed to load {}", path.display()) | ||
209 | } | ||
210 | res | ||
207 | }; | 211 | }; |
208 | for ws in workspaces.iter() { | 212 | for ws in workspaces.iter() { |
209 | crate_graph.extend(ws.to_crate_graph( | 213 | crate_graph.extend(ws.to_crate_graph( |
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs index 4bdf4bf0f..2052b800c 100644 --- a/crates/rust-analyzer/src/to_proto.rs +++ b/crates/rust-analyzer/src/to_proto.rs | |||
@@ -21,9 +21,7 @@ use crate::{ | |||
21 | 21 | ||
22 | pub(crate) fn position(line_index: &LineIndex, offset: TextSize) -> lsp_types::Position { | 22 | pub(crate) fn position(line_index: &LineIndex, offset: TextSize) -> lsp_types::Position { |
23 | let line_col = line_index.line_col(offset); | 23 | let line_col = line_index.line_col(offset); |
24 | let line = u64::from(line_col.line); | 24 | lsp_types::Position::new(line_col.line, line_col.col_utf16) |
25 | let character = u64::from(line_col.col_utf16); | ||
26 | lsp_types::Position::new(line, character) | ||
27 | } | 25 | } |
28 | 26 | ||
29 | pub(crate) fn range(line_index: &LineIndex, range: TextRange) -> lsp_types::Range { | 27 | pub(crate) fn range(line_index: &LineIndex, range: TextRange) -> lsp_types::Range { |
@@ -278,9 +276,9 @@ pub(crate) fn signature_help( | |||
278 | label.push_str(", "); | 276 | label.push_str(", "); |
279 | } | 277 | } |
280 | first = false; | 278 | first = false; |
281 | let start = label.len() as u64; | 279 | let start = label.len() as u32; |
282 | label.push_str(param); | 280 | label.push_str(param); |
283 | let end = label.len() as u64; | 281 | let end = label.len() as u32; |
284 | params.push(lsp_types::ParameterInformation { | 282 | params.push(lsp_types::ParameterInformation { |
285 | label: lsp_types::ParameterLabel::LabelOffsets([start, end]), | 283 | label: lsp_types::ParameterLabel::LabelOffsets([start, end]), |
286 | documentation: None, | 284 | documentation: None, |
@@ -302,7 +300,7 @@ pub(crate) fn signature_help( | |||
302 | }) | 300 | }) |
303 | }; | 301 | }; |
304 | 302 | ||
305 | let active_parameter = call_info.active_parameter.map(|it| it as i64); | 303 | let active_parameter = call_info.active_parameter.map(|it| it as u32); |
306 | 304 | ||
307 | let signature = lsp_types::SignatureInformation { | 305 | let signature = lsp_types::SignatureInformation { |
308 | label, | 306 | label, |
@@ -518,13 +516,13 @@ pub(crate) fn url_from_abs_path(path: &Path) -> lsp_types::Url { | |||
518 | lsp_types::Url::parse(&url).unwrap() | 516 | lsp_types::Url::parse(&url).unwrap() |
519 | } | 517 | } |
520 | 518 | ||
521 | pub(crate) fn versioned_text_document_identifier( | 519 | pub(crate) fn optional_versioned_text_document_identifier( |
522 | snap: &GlobalStateSnapshot, | 520 | snap: &GlobalStateSnapshot, |
523 | file_id: FileId, | 521 | file_id: FileId, |
524 | ) -> lsp_types::VersionedTextDocumentIdentifier { | 522 | ) -> lsp_types::OptionalVersionedTextDocumentIdentifier { |
525 | let url = url(snap, file_id); | 523 | let url = url(snap, file_id); |
526 | let version = snap.url_file_version(&url); | 524 | let version = snap.url_file_version(&url); |
527 | lsp_types::VersionedTextDocumentIdentifier { uri: url, version } | 525 | lsp_types::OptionalVersionedTextDocumentIdentifier { uri: url, version } |
528 | } | 526 | } |
529 | 527 | ||
530 | pub(crate) fn location( | 528 | pub(crate) fn location( |
@@ -613,7 +611,7 @@ pub(crate) fn snippet_text_document_edit( | |||
613 | is_snippet: bool, | 611 | is_snippet: bool, |
614 | source_file_edit: SourceFileEdit, | 612 | source_file_edit: SourceFileEdit, |
615 | ) -> Result<lsp_ext::SnippetTextDocumentEdit> { | 613 | ) -> Result<lsp_ext::SnippetTextDocumentEdit> { |
616 | let text_document = versioned_text_document_identifier(snap, source_file_edit.file_id); | 614 | let text_document = optional_versioned_text_document_identifier(snap, source_file_edit.file_id); |
617 | let line_index = snap.analysis.file_line_index(source_file_edit.file_id)?; | 615 | let line_index = snap.analysis.file_line_index(source_file_edit.file_id)?; |
618 | let line_endings = snap.file_line_endings(source_file_edit.file_id); | 616 | let line_endings = snap.file_line_endings(source_file_edit.file_id); |
619 | let edits = source_file_edit | 617 | let edits = source_file_edit |
@@ -631,12 +629,21 @@ pub(crate) fn resource_op( | |||
631 | match file_system_edit { | 629 | match file_system_edit { |
632 | FileSystemEdit::CreateFile { anchor, dst } => { | 630 | FileSystemEdit::CreateFile { anchor, dst } => { |
633 | let uri = snap.anchored_path(anchor, &dst); | 631 | let uri = snap.anchored_path(anchor, &dst); |
634 | lsp_types::ResourceOp::Create(lsp_types::CreateFile { uri, options: None }) | 632 | lsp_types::ResourceOp::Create(lsp_types::CreateFile { |
633 | uri, | ||
634 | options: None, | ||
635 | annotation: None, | ||
636 | }) | ||
635 | } | 637 | } |
636 | FileSystemEdit::MoveFile { src, anchor, dst } => { | 638 | FileSystemEdit::MoveFile { src, anchor, dst } => { |
637 | let old_uri = snap.file_id_to_url(src); | 639 | let old_uri = snap.file_id_to_url(src); |
638 | let new_uri = snap.anchored_path(anchor, &dst); | 640 | let new_uri = snap.anchored_path(anchor, &dst); |
639 | lsp_types::ResourceOp::Rename(lsp_types::RenameFile { old_uri, new_uri, options: None }) | 641 | lsp_types::ResourceOp::Rename(lsp_types::RenameFile { |
642 | old_uri, | ||
643 | new_uri, | ||
644 | options: None, | ||
645 | annotation: None, | ||
646 | }) | ||
640 | } | 647 | } |
641 | } | 648 | } |
642 | } | 649 | } |
@@ -686,9 +693,11 @@ impl From<lsp_ext::SnippetWorkspaceEdit> for lsp_types::WorkspaceEdit { | |||
686 | edits: edit | 693 | edits: edit |
687 | .edits | 694 | .edits |
688 | .into_iter() | 695 | .into_iter() |
689 | .map(|edit| lsp_types::TextEdit { | 696 | .map(|edit| { |
690 | range: edit.range, | 697 | lsp_types::OneOf::Left(lsp_types::TextEdit { |
691 | new_text: edit.new_text, | 698 | range: edit.range, |
699 | new_text: edit.new_text, | ||
700 | }) | ||
692 | }) | 701 | }) |
693 | .collect(), | 702 | .collect(), |
694 | }, | 703 | }, |
diff --git a/crates/rust-analyzer/tests/rust-analyzer/support.rs b/crates/rust-analyzer/tests/rust-analyzer/support.rs index b210b98f0..456125789 100644 --- a/crates/rust-analyzer/tests/rust-analyzer/support.rs +++ b/crates/rust-analyzer/tests/rust-analyzer/support.rs | |||
@@ -108,7 +108,7 @@ pub(crate) fn project(fixture: &str) -> Server { | |||
108 | } | 108 | } |
109 | 109 | ||
110 | pub(crate) struct Server { | 110 | pub(crate) struct Server { |
111 | req_id: Cell<u64>, | 111 | req_id: Cell<i32>, |
112 | messages: RefCell<Vec<Message>>, | 112 | messages: RefCell<Vec<Message>>, |
113 | _thread: jod_thread::JoinHandle<()>, | 113 | _thread: jod_thread::JoinHandle<()>, |
114 | client: Connection, | 114 | client: Connection, |
@@ -165,7 +165,7 @@ impl Server { | |||
165 | R::Params: Serialize, | 165 | R::Params: Serialize, |
166 | { | 166 | { |
167 | let id = self.req_id.get(); | 167 | let id = self.req_id.get(); |
168 | self.req_id.set(id + 1); | 168 | self.req_id.set(id.wrapping_add(1)); |
169 | 169 | ||
170 | let r = Request::new(id.into(), R::METHOD.to_string(), params); | 170 | let r = Request::new(id.into(), R::METHOD.to_string(), params); |
171 | self.send_request_(r) | 171 | self.send_request_(r) |
diff --git a/crates/syntax/test_data/parser/ok/0068_item_modifiers.rast b/crates/syntax/test_data/parser/ok/0068_item_modifiers.rast index 50a6d8ee9..87eebf185 100644 --- a/crates/syntax/test_data/parser/ok/0068_item_modifiers.rast +++ b/crates/syntax/test_data/parser/ok/0068_item_modifiers.rast | |||
@@ -1,4 +1,4 @@ | |||
1 | [email protected]04 | 1 | [email protected]28 |
2 | [email protected] | 2 | [email protected] |
3 | [email protected] "async" | 3 | [email protected] "async" |
4 | [email protected] " " | 4 | [email protected] " " |
@@ -215,4 +215,16 @@ [email protected] | |||
215 | [email protected] | 215 | [email protected] |
216 | [email protected] "{" | 216 | [email protected] "{" |
217 | [email protected] "}" | 217 | [email protected] "}" |
218 | [email protected] "\n" | 218 | [email protected] "\n\n" |
219 | [email protected] | ||
220 | [email protected] "unsafe" | ||
221 | [email protected] " " | ||
222 | [email protected] | ||
223 | [email protected] "extern" | ||
224 | [email protected] " " | ||
225 | [email protected] "\"C++\"" | ||
226 | [email protected] " " | ||
227 | [email protected] | ||
228 | [email protected] "{" | ||
229 | [email protected] "}" | ||
230 | [email protected] "\n" | ||
diff --git a/crates/syntax/test_data/parser/ok/0068_item_modifiers.rs b/crates/syntax/test_data/parser/ok/0068_item_modifiers.rs index 8d697c04b..6d27a082c 100644 --- a/crates/syntax/test_data/parser/ok/0068_item_modifiers.rs +++ b/crates/syntax/test_data/parser/ok/0068_item_modifiers.rs | |||
@@ -14,3 +14,5 @@ unsafe auto trait T {} | |||
14 | unsafe impl Foo {} | 14 | unsafe impl Foo {} |
15 | default impl Foo {} | 15 | default impl Foo {} |
16 | unsafe default impl Foo {} | 16 | unsafe default impl Foo {} |
17 | |||
18 | unsafe extern "C++" {} | ||
diff --git a/crates/tt/src/lib.rs b/crates/tt/src/lib.rs index 20c3f5eab..6c1bf8d09 100644 --- a/crates/tt/src/lib.rs +++ b/crates/tt/src/lib.rs | |||
@@ -1,10 +1,7 @@ | |||
1 | //! `tt` crate defines a `TokenTree` data structure: this is the interface (both | 1 | //! `tt` crate defines a `TokenTree` data structure: this is the interface (both |
2 | //! input and output) of macros. It closely mirrors `proc_macro` crate's | 2 | //! input and output) of macros. It closely mirrors `proc_macro` crate's |
3 | //! `TokenTree`. | 3 | //! `TokenTree`. |
4 | use std::{ | 4 | use std::{fmt, panic::RefUnwindSafe}; |
5 | fmt::{self, Debug}, | ||
6 | panic::RefUnwindSafe, | ||
7 | }; | ||
8 | 5 | ||
9 | use stdx::impl_from; | 6 | use stdx::impl_from; |
10 | 7 | ||
@@ -139,7 +136,7 @@ fn print_debug_token(f: &mut fmt::Formatter<'_>, tkn: &TokenTree, level: usize) | |||
139 | Ok(()) | 136 | Ok(()) |
140 | } | 137 | } |
141 | 138 | ||
142 | impl Debug for Subtree { | 139 | impl fmt::Debug for Subtree { |
143 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | 140 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
144 | print_debug_subtree(f, self, 0) | 141 | print_debug_subtree(f, self, 0) |
145 | } | 142 | } |
@@ -240,7 +237,18 @@ pub enum ExpansionError { | |||
240 | ExpansionError(String), | 237 | ExpansionError(String), |
241 | } | 238 | } |
242 | 239 | ||
243 | pub trait TokenExpander: Debug + Send + Sync + RefUnwindSafe { | 240 | impl fmt::Display for ExpansionError { |
241 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
242 | match self { | ||
243 | ExpansionError::IOError(e) => write!(f, "I/O error: {}", e), | ||
244 | ExpansionError::JsonError(e) => write!(f, "JSON decoding error: {}", e), | ||
245 | ExpansionError::Unknown(e) => e.fmt(f), | ||
246 | ExpansionError::ExpansionError(e) => write!(f, "proc macro returned error: {}", e), | ||
247 | } | ||
248 | } | ||
249 | } | ||
250 | |||
251 | pub trait TokenExpander: fmt::Debug + Send + Sync + RefUnwindSafe { | ||
244 | fn expand(&self, subtree: &Subtree, attrs: Option<&Subtree>) | 252 | fn expand(&self, subtree: &Subtree, attrs: Option<&Subtree>) |
245 | -> Result<Subtree, ExpansionError>; | 253 | -> Result<Subtree, ExpansionError>; |
246 | } | 254 | } |
diff --git a/docs/dev/lsp-extensions.md b/docs/dev/lsp-extensions.md index db9727bee..8c01db07c 100644 --- a/docs/dev/lsp-extensions.md +++ b/docs/dev/lsp-extensions.md | |||
@@ -1,5 +1,5 @@ | |||
1 | <!--- | 1 | <!--- |
2 | lsp_ext.rs hash: 9d5daed5b25dc4f6 | 2 | lsp_ext.rs hash: 203fdf79b21b5987 |
3 | 3 | ||
4 | If you need to change the above hash to make the test pass, please check if you | 4 | If you need to change the above hash to make the test pass, please check if you |
5 | need to adjust this doc as well and ping this issue: | 5 | need to adjust this doc as well and ping this issue: |
@@ -45,7 +45,7 @@ interface SnippetTextEdit extends TextEdit { | |||
45 | 45 | ||
46 | ```typescript | 46 | ```typescript |
47 | export interface TextDocumentEdit { | 47 | export interface TextDocumentEdit { |
48 | textDocument: VersionedTextDocumentIdentifier; | 48 | textDocument: OptionalVersionedTextDocumentIdentifier; |
49 | edits: (TextEdit | SnippetTextEdit)[]; | 49 | edits: (TextEdit | SnippetTextEdit)[]; |
50 | } | 50 | } |
51 | ``` | 51 | ``` |
diff --git a/docs/dev/syntax.md b/docs/dev/syntax.md index 2eb08b7ca..1edafab68 100644 --- a/docs/dev/syntax.md +++ b/docs/dev/syntax.md | |||
@@ -195,7 +195,7 @@ Modeling this with immutable trees is possible, but annoying. | |||
195 | A function green tree is not super-convenient to use. | 195 | A function green tree is not super-convenient to use. |
196 | The biggest problem is accessing parents (there are no parent pointers!). | 196 | The biggest problem is accessing parents (there are no parent pointers!). |
197 | But there are also "identify" issues. | 197 | But there are also "identify" issues. |
198 | Let's say you want to write a code which builds a list of expressions in a file: `fn collect_exrepssions(file: GreenNode) -> HashSet<GreenNode>`. | 198 | Let's say you want to write a code which builds a list of expressions in a file: `fn collect_expressions(file: GreenNode) -> HashSet<GreenNode>`. |
199 | For the input like | 199 | For the input like |
200 | 200 | ||
201 | ```rust | 201 | ```rust |
@@ -236,7 +236,7 @@ impl SyntaxNode { | |||
236 | self.parent.clone() | 236 | self.parent.clone() |
237 | } | 237 | } |
238 | fn children(&self) -> impl Iterator<Item = SyntaxNode> { | 238 | fn children(&self) -> impl Iterator<Item = SyntaxNode> { |
239 | let mut offset = self.offset | 239 | let mut offset = self.offset; |
240 | self.green.children().map(|green_child| { | 240 | self.green.children().map(|green_child| { |
241 | let child_offset = offset; | 241 | let child_offset = offset; |
242 | offset += green_child.text_len; | 242 | offset += green_child.text_len; |
diff --git a/editors/code/package-lock.json b/editors/code/package-lock.json index 83ef00058..a60d3668b 100644 --- a/editors/code/package-lock.json +++ b/editors/code/package-lock.json | |||
@@ -2414,27 +2414,27 @@ | |||
2414 | "integrity": "sha512-1nG+6cuTtpzmXe7yYfO9GCkYlyV6Ai+jDnwidHiT2T7zhc+bJM+VTtc0T/CdTlDyTNTqIcCj0V1nD4TcVjJ7Ug==" | 2414 | "integrity": "sha512-1nG+6cuTtpzmXe7yYfO9GCkYlyV6Ai+jDnwidHiT2T7zhc+bJM+VTtc0T/CdTlDyTNTqIcCj0V1nD4TcVjJ7Ug==" |
2415 | }, | 2415 | }, |
2416 | "vscode-languageclient": { | 2416 | "vscode-languageclient": { |
2417 | "version": "7.0.0-next.12", | 2417 | "version": "7.0.0-next.14", |
2418 | "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-7.0.0-next.12.tgz", | 2418 | "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-7.0.0-next.14.tgz", |
2419 | "integrity": "sha512-OrzvOvhS5o26C0KctTJC7hkwh3avCwkVhllzy42AqwpIUZ3p2aVqkSG2uVxaeodq8ThBb3TLgtg50vxyWs6FEg==", | 2419 | "integrity": "sha512-QUccfXK2F6AXXRFR8QJCaIz7N2BhJK6ok8E1aO8LHq2IBU33+5hTSJBXs7nEqrqZ/cY2VlDDbMWtMvCxz+/y1w==", |
2420 | "requires": { | 2420 | "requires": { |
2421 | "semver": "^6.3.0", | 2421 | "semver": "^6.3.0", |
2422 | "vscode-languageserver-protocol": "3.16.0-next.10" | 2422 | "vscode-languageserver-protocol": "3.16.0-next.11" |
2423 | } | 2423 | } |
2424 | }, | 2424 | }, |
2425 | "vscode-languageserver-protocol": { | 2425 | "vscode-languageserver-protocol": { |
2426 | "version": "3.16.0-next.10", | 2426 | "version": "3.16.0-next.11", |
2427 | "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.16.0-next.10.tgz", | 2427 | "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.16.0-next.11.tgz", |
2428 | "integrity": "sha512-YRTctHUZvts0Z1xXKNYU0ha0o+Tlgtwr+6O8OmDquM086N8exiSKBMwMC+Ra1QtIE+1mfW43Wxsme2FnMkAS9A==", | 2428 | "integrity": "sha512-31FmupmSmfznuMuGp7qN6h3d/hKUbexbvcwTvrUE/igqRlzFU542s8MtGICx1ERbVuDOLGp96W2Z92qbUbmBPA==", |
2429 | "requires": { | 2429 | "requires": { |
2430 | "vscode-jsonrpc": "6.0.0-next.7", | 2430 | "vscode-jsonrpc": "6.0.0-next.7", |
2431 | "vscode-languageserver-types": "3.16.0-next.4" | 2431 | "vscode-languageserver-types": "3.16.0-next.5" |
2432 | } | 2432 | } |
2433 | }, | 2433 | }, |
2434 | "vscode-languageserver-types": { | 2434 | "vscode-languageserver-types": { |
2435 | "version": "3.16.0-next.4", | 2435 | "version": "3.16.0-next.5", |
2436 | "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.16.0-next.4.tgz", | 2436 | "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.16.0-next.5.tgz", |
2437 | "integrity": "sha512-NlKJyGcET/ZBCCLBYIPaGo2c37R03bPYeWXozUtnjyye7+9dhlbMSODyoG2INcQf8zFmB4qhm2UOJjgYEgPCNA==" | 2437 | "integrity": "sha512-lf8Y1XXMtF1r2oDDAmJe+drizNXkybSRXAQQk5dPy2rYJsY9SPXYNO074L3THu9zNYepzV5fRJZUPo/V/TLBRQ==" |
2438 | }, | 2438 | }, |
2439 | "vscode-test": { | 2439 | "vscode-test": { |
2440 | "version": "1.4.0", | 2440 | "version": "1.4.0", |
diff --git a/editors/code/package.json b/editors/code/package.json index 220d44abc..c3f1a0d8d 100644 --- a/editors/code/package.json +++ b/editors/code/package.json | |||
@@ -21,7 +21,7 @@ | |||
21 | "Programming Languages" | 21 | "Programming Languages" |
22 | ], | 22 | ], |
23 | "engines": { | 23 | "engines": { |
24 | "vscode": "^1.47.1" | 24 | "vscode": "^1.51.0" |
25 | }, | 25 | }, |
26 | "enableProposedApi": true, | 26 | "enableProposedApi": true, |
27 | "scripts": { | 27 | "scripts": { |
@@ -36,7 +36,7 @@ | |||
36 | }, | 36 | }, |
37 | "dependencies": { | 37 | "dependencies": { |
38 | "node-fetch": "^2.6.1", | 38 | "node-fetch": "^2.6.1", |
39 | "vscode-languageclient": "7.0.0-next.12" | 39 | "vscode-languageclient": "7.0.0-next.14" |
40 | }, | 40 | }, |
41 | "devDependencies": { | 41 | "devDependencies": { |
42 | "@rollup/plugin-commonjs": "^13.0.2", | 42 | "@rollup/plugin-commonjs": "^13.0.2", |
@@ -460,6 +460,11 @@ | |||
460 | "default": true, | 460 | "default": true, |
461 | "markdownDescription": "Whether to show postfix snippets like `dbg`, `if`, `not`, etc." | 461 | "markdownDescription": "Whether to show postfix snippets like `dbg`, `if`, `not`, etc." |
462 | }, | 462 | }, |
463 | "rust-analyzer.completion.enableExperimental": { | ||
464 | "type": "boolean", | ||
465 | "default": true, | ||
466 | "markdownDescription": "Display additional completions with potential false positives and performance issues" | ||
467 | }, | ||
463 | "rust-analyzer.callInfo.full": { | 468 | "rust-analyzer.callInfo.full": { |
464 | "type": "boolean", | 469 | "type": "boolean", |
465 | "default": true, | 470 | "default": true, |
@@ -952,9 +957,6 @@ | |||
952 | { | 957 | { |
953 | "language": "rust", | 958 | "language": "rust", |
954 | "scopes": { | 959 | "scopes": { |
955 | "macro": [ | ||
956 | "entity.name.function.macro.rust" | ||
957 | ], | ||
958 | "attribute": [ | 960 | "attribute": [ |
959 | "meta.attribute.rust" | 961 | "meta.attribute.rust" |
960 | ], | 962 | ], |
diff --git a/editors/code/rust.tmGrammar.json b/editors/code/rust.tmGrammar.json index cd4775d27..4759bb116 100644 --- a/editors/code/rust.tmGrammar.json +++ b/editors/code/rust.tmGrammar.json | |||
@@ -409,7 +409,7 @@ | |||
409 | { | 409 | { |
410 | "comment": "booleans", | 410 | "comment": "booleans", |
411 | "name": "constant.language.bool.rust", | 411 | "name": "constant.language.bool.rust", |
412 | "match": "\\btrue|false\\b" | 412 | "match": "\\b(true|false)\\b" |
413 | } | 413 | } |
414 | ] | 414 | ] |
415 | }, | 415 | }, |
diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts index c9d032ead..63ab82dde 100644 --- a/editors/code/src/client.ts +++ b/editors/code/src/client.ts | |||
@@ -174,6 +174,8 @@ class ExperimentalFeatures implements lc.StaticFeature { | |||
174 | } | 174 | } |
175 | initialize(_capabilities: lc.ServerCapabilities<any>, _documentSelector: lc.DocumentSelector | undefined): void { | 175 | initialize(_capabilities: lc.ServerCapabilities<any>, _documentSelector: lc.DocumentSelector | undefined): void { |
176 | } | 176 | } |
177 | dispose(): void { | ||
178 | } | ||
177 | } | 179 | } |
178 | 180 | ||
179 | function isCodeActionWithoutEditsAndCommands(value: any): boolean { | 181 | function isCodeActionWithoutEditsAndCommands(value: any): boolean { |
diff --git a/xtask/tests/tidy.rs b/xtask/tests/tidy.rs index 99652e76b..4c58aed59 100644 --- a/xtask/tests/tidy.rs +++ b/xtask/tests/tidy.rs | |||
@@ -168,6 +168,7 @@ ISC | |||
168 | MIT | 168 | MIT |
169 | MIT / Apache-2.0 | 169 | MIT / Apache-2.0 |
170 | MIT OR Apache-2.0 | 170 | MIT OR Apache-2.0 |
171 | MIT OR Apache-2.0 OR Zlib | ||
171 | MIT OR Zlib OR Apache-2.0 | 172 | MIT OR Zlib OR Apache-2.0 |
172 | MIT/Apache-2.0 | 173 | MIT/Apache-2.0 |
173 | Unlicense OR MIT | 174 | Unlicense OR MIT |