diff options
53 files changed, 2010 insertions, 1011 deletions
diff --git a/Cargo.lock b/Cargo.lock index 557d5f5f3..15ccf4146 100644 --- a/Cargo.lock +++ b/Cargo.lock | |||
@@ -146,9 +146,9 @@ dependencies = [ | |||
146 | 146 | ||
147 | [[package]] | 147 | [[package]] |
148 | name = "cc" | 148 | name = "cc" |
149 | version = "1.0.67" | 149 | version = "1.0.68" |
150 | source = "registry+https://github.com/rust-lang/crates.io-index" | 150 | source = "registry+https://github.com/rust-lang/crates.io-index" |
151 | checksum = "e3c69b077ad434294d3ce9f1f6143a2a4b89a8a2d54ef813d85003a4fd1137fd" | 151 | checksum = "4a72c244c1ff497a746a7e1fb3d14bd08420ecda70c8f25c7112f2781652d787" |
152 | 152 | ||
153 | [[package]] | 153 | [[package]] |
154 | name = "cfg" | 154 | name = "cfg" |
@@ -169,9 +169,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" | |||
169 | 169 | ||
170 | [[package]] | 170 | [[package]] |
171 | name = "chalk-derive" | 171 | name = "chalk-derive" |
172 | version = "0.67.0" | 172 | version = "0.68.0" |
173 | source = "registry+https://github.com/rust-lang/crates.io-index" | 173 | source = "registry+https://github.com/rust-lang/crates.io-index" |
174 | checksum = "751a3cd9eeb24d7165e9f90daf1f10a23ffa16a7986f349027f8dfb60f51ee0c" | 174 | checksum = "ea1552e7666a857f5417e6051ce705ea6856ab2cda39be7605e5b626fa47416b" |
175 | dependencies = [ | 175 | dependencies = [ |
176 | "proc-macro2", | 176 | "proc-macro2", |
177 | "quote", | 177 | "quote", |
@@ -181,9 +181,9 @@ dependencies = [ | |||
181 | 181 | ||
182 | [[package]] | 182 | [[package]] |
183 | name = "chalk-ir" | 183 | name = "chalk-ir" |
184 | version = "0.67.0" | 184 | version = "0.68.0" |
185 | source = "registry+https://github.com/rust-lang/crates.io-index" | 185 | source = "registry+https://github.com/rust-lang/crates.io-index" |
186 | checksum = "4236da905504740d3f72cc8f0509aa01321cc236208e2c33b55eda2db74bc495" | 186 | checksum = "19d7d5f1448dbac493541e97221f7f4c32326c4c76c6ecf543daf72a1dd93e66" |
187 | dependencies = [ | 187 | dependencies = [ |
188 | "bitflags", | 188 | "bitflags", |
189 | "chalk-derive", | 189 | "chalk-derive", |
@@ -192,9 +192,9 @@ dependencies = [ | |||
192 | 192 | ||
193 | [[package]] | 193 | [[package]] |
194 | name = "chalk-recursive" | 194 | name = "chalk-recursive" |
195 | version = "0.67.0" | 195 | version = "0.68.0" |
196 | source = "registry+https://github.com/rust-lang/crates.io-index" | 196 | source = "registry+https://github.com/rust-lang/crates.io-index" |
197 | checksum = "7d0b123fe45a34c4cd5cb329650a0d163525d2acbe9d754a4538d3340884002e" | 197 | checksum = "e0df406d2927321021b48acd193459dd33c913732155c93442d03f5ae8275385" |
198 | dependencies = [ | 198 | dependencies = [ |
199 | "chalk-derive", | 199 | "chalk-derive", |
200 | "chalk-ir", | 200 | "chalk-ir", |
@@ -205,9 +205,9 @@ dependencies = [ | |||
205 | 205 | ||
206 | [[package]] | 206 | [[package]] |
207 | name = "chalk-solve" | 207 | name = "chalk-solve" |
208 | version = "0.67.0" | 208 | version = "0.68.0" |
209 | source = "registry+https://github.com/rust-lang/crates.io-index" | 209 | source = "registry+https://github.com/rust-lang/crates.io-index" |
210 | checksum = "9c54788f0ae3b38e2bb2266f395d462d988c64f92dbd55c68219908fd1ce7ddc" | 210 | checksum = "0cbfcd5daa5ab8b1c9e5e10e83b0ac26271480f6ae5b5f35e5b19e1f6a0e6e37" |
211 | dependencies = [ | 211 | dependencies = [ |
212 | "chalk-derive", | 212 | "chalk-derive", |
213 | "chalk-ir", | 213 | "chalk-ir", |
@@ -281,9 +281,9 @@ dependencies = [ | |||
281 | 281 | ||
282 | [[package]] | 282 | [[package]] |
283 | name = "crossbeam-epoch" | 283 | name = "crossbeam-epoch" |
284 | version = "0.9.4" | 284 | version = "0.9.5" |
285 | source = "registry+https://github.com/rust-lang/crates.io-index" | 285 | source = "registry+https://github.com/rust-lang/crates.io-index" |
286 | checksum = "52fb27eab85b17fbb9f6fd667089e07d6a2eb8743d02639ee7f6a7a7729c9c94" | 286 | checksum = "4ec02e091aa634e2c3ada4a392989e7c3116673ef0ac5b72232439094d73b7fd" |
287 | dependencies = [ | 287 | dependencies = [ |
288 | "cfg-if", | 288 | "cfg-if", |
289 | "crossbeam-utils", | 289 | "crossbeam-utils", |
@@ -294,11 +294,10 @@ dependencies = [ | |||
294 | 294 | ||
295 | [[package]] | 295 | [[package]] |
296 | name = "crossbeam-utils" | 296 | name = "crossbeam-utils" |
297 | version = "0.8.4" | 297 | version = "0.8.5" |
298 | source = "registry+https://github.com/rust-lang/crates.io-index" | 298 | source = "registry+https://github.com/rust-lang/crates.io-index" |
299 | checksum = "4feb231f0d4d6af81aed15928e58ecf5816aa62a2393e2c82f46973e92a9a278" | 299 | checksum = "d82cfc11ce7f2c3faef78d8a684447b40d503d9681acebed6cb728d45940c4db" |
300 | dependencies = [ | 300 | dependencies = [ |
301 | "autocfg", | ||
302 | "cfg-if", | 301 | "cfg-if", |
303 | "lazy_static", | 302 | "lazy_static", |
304 | ] | 303 | ] |
@@ -426,20 +425,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | |||
426 | checksum = "2022715d62ab30faffd124d40b76f4134a550a87792276512b18d63272333394" | 425 | checksum = "2022715d62ab30faffd124d40b76f4134a550a87792276512b18d63272333394" |
427 | 426 | ||
428 | [[package]] | 427 | [[package]] |
429 | name = "fsevent" | ||
430 | version = "2.0.2" | ||
431 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
432 | checksum = "97f347202c95c98805c216f9e1df210e8ebaec9fdb2365700a43c10797a35e63" | ||
433 | dependencies = [ | ||
434 | "bitflags", | ||
435 | "fsevent-sys", | ||
436 | ] | ||
437 | |||
438 | [[package]] | ||
439 | name = "fsevent-sys" | 428 | name = "fsevent-sys" |
440 | version = "3.1.0" | 429 | version = "4.0.0" |
441 | source = "registry+https://github.com/rust-lang/crates.io-index" | 430 | source = "registry+https://github.com/rust-lang/crates.io-index" |
442 | checksum = "ca6f5e6817058771c10f0eb0f05ddf1e35844266f972004fe8e4b21fda295bd5" | 431 | checksum = "5c0e564d24da983c053beff1bb7178e237501206840a3e6bf4e267b9e8ae734a" |
443 | dependencies = [ | 432 | dependencies = [ |
444 | "libc", | 433 | "libc", |
445 | ] | 434 | ] |
@@ -647,6 +636,7 @@ dependencies = [ | |||
647 | "ide_db", | 636 | "ide_db", |
648 | "itertools", | 637 | "itertools", |
649 | "log", | 638 | "log", |
639 | "once_cell", | ||
650 | "profile", | 640 | "profile", |
651 | "rustc-hash", | 641 | "rustc-hash", |
652 | "stdx", | 642 | "stdx", |
@@ -775,9 +765,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" | |||
775 | 765 | ||
776 | [[package]] | 766 | [[package]] |
777 | name = "libc" | 767 | name = "libc" |
778 | version = "0.2.94" | 768 | version = "0.2.95" |
779 | source = "registry+https://github.com/rust-lang/crates.io-index" | 769 | source = "registry+https://github.com/rust-lang/crates.io-index" |
780 | checksum = "18794a8ad5b29321f790b55d93dfba91e125cb1a9edbd4f8e3150acc771c1a5e" | 770 | checksum = "789da6d93f1b866ffe175afc5322a4d76c038605a1c3319bb57b06967ca98a36" |
781 | 771 | ||
782 | [[package]] | 772 | [[package]] |
783 | name = "libloading" | 773 | name = "libloading" |
@@ -880,18 +870,18 @@ checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc" | |||
880 | 870 | ||
881 | [[package]] | 871 | [[package]] |
882 | name = "memmap2" | 872 | name = "memmap2" |
883 | version = "0.2.2" | 873 | version = "0.2.3" |
884 | source = "registry+https://github.com/rust-lang/crates.io-index" | 874 | source = "registry+https://github.com/rust-lang/crates.io-index" |
885 | checksum = "397d1a6d6d0563c0f5462bbdae662cf6c784edf5e828e40c7257f85d82bf56dd" | 875 | checksum = "723e3ebdcdc5c023db1df315364573789f8857c11b631a2fdfad7c00f5c046b4" |
886 | dependencies = [ | 876 | dependencies = [ |
887 | "libc", | 877 | "libc", |
888 | ] | 878 | ] |
889 | 879 | ||
890 | [[package]] | 880 | [[package]] |
891 | name = "memoffset" | 881 | name = "memoffset" |
892 | version = "0.6.3" | 882 | version = "0.6.4" |
893 | source = "registry+https://github.com/rust-lang/crates.io-index" | 883 | source = "registry+https://github.com/rust-lang/crates.io-index" |
894 | checksum = "f83fb6581e8ed1f85fd45c116db8405483899489e38406156c25eb743554361d" | 884 | checksum = "59accc507f1338036a0477ef61afdae33cde60840f4dfe481319ce3ad116ddf9" |
895 | dependencies = [ | 885 | dependencies = [ |
896 | "autocfg", | 886 | "autocfg", |
897 | ] | 887 | ] |
@@ -939,14 +929,13 @@ dependencies = [ | |||
939 | 929 | ||
940 | [[package]] | 930 | [[package]] |
941 | name = "notify" | 931 | name = "notify" |
942 | version = "5.0.0-pre.8" | 932 | version = "5.0.0-pre.9" |
943 | source = "registry+https://github.com/rust-lang/crates.io-index" | 933 | source = "registry+https://github.com/rust-lang/crates.io-index" |
944 | checksum = "46bbbcd078f1f00ddb7a9abe70b96e91229b44b0b3afdec610f8e5137f8f014b" | 934 | checksum = "b89869d77edd64db917d7903abeadc166f93686b342c56cc0ca51acb68441d09" |
945 | dependencies = [ | 935 | dependencies = [ |
946 | "bitflags", | 936 | "bitflags", |
947 | "crossbeam-channel", | 937 | "crossbeam-channel", |
948 | "filetime", | 938 | "filetime", |
949 | "fsevent", | ||
950 | "fsevent-sys", | 939 | "fsevent-sys", |
951 | "inotify", | 940 | "inotify", |
952 | "libc", | 941 | "libc", |
@@ -1359,9 +1348,9 @@ dependencies = [ | |||
1359 | 1348 | ||
1360 | [[package]] | 1349 | [[package]] |
1361 | name = "rustc-ap-rustc_lexer" | 1350 | name = "rustc-ap-rustc_lexer" |
1362 | version = "720.0.0" | 1351 | version = "721.0.0" |
1363 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1352 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1364 | checksum = "a025b453b0ae85335336f991f920ca9af5c0dc851171cb9035a16cea5619e9b2" | 1353 | checksum = "2ba1f60e2942dc7dc5ea64edeaae01cfba2303871b14936e1af0f54d5420b3d1" |
1365 | dependencies = [ | 1354 | dependencies = [ |
1366 | "unicode-xid", | 1355 | "unicode-xid", |
1367 | ] | 1356 | ] |
@@ -1827,9 +1816,9 @@ dependencies = [ | |||
1827 | 1816 | ||
1828 | [[package]] | 1817 | [[package]] |
1829 | name = "unicode-normalization" | 1818 | name = "unicode-normalization" |
1830 | version = "0.1.17" | 1819 | version = "0.1.18" |
1831 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1820 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1832 | checksum = "07fbfce1c8a97d547e8b5334978438d9d6ec8c20e38f56d4a4374d181493eaef" | 1821 | checksum = "33717dca7ac877f497014e10d73f3acf948c342bee31b5ca7892faf94ccc6b49" |
1833 | dependencies = [ | 1822 | dependencies = [ |
1834 | "tinyvec", | 1823 | "tinyvec", |
1835 | ] | 1824 | ] |
@@ -1957,18 +1946,18 @@ dependencies = [ | |||
1957 | 1946 | ||
1958 | [[package]] | 1947 | [[package]] |
1959 | name = "xshell" | 1948 | name = "xshell" |
1960 | version = "0.1.13" | 1949 | version = "0.1.14" |
1961 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1950 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1962 | checksum = "eb15bb1b41eb14efe628006294c294e10c366e03a0283b9c2063fc27d97934c6" | 1951 | checksum = "c640362f1b150e186b76e88606e4b01a7b2f1d56cc50fcc184ddb683fb567c23" |
1963 | dependencies = [ | 1952 | dependencies = [ |
1964 | "xshell-macros", | 1953 | "xshell-macros", |
1965 | ] | 1954 | ] |
1966 | 1955 | ||
1967 | [[package]] | 1956 | [[package]] |
1968 | name = "xshell-macros" | 1957 | name = "xshell-macros" |
1969 | version = "0.1.13" | 1958 | version = "0.1.14" |
1970 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1959 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1971 | checksum = "cf7ed94a2c75b9bcc57031229be2b57ee47ba71122f71aabef8610ec66a97e52" | 1960 | checksum = "0607c095c96c1d8420ce4a018a0954fb15d73d5eb59b521a05a0f04cffc05059" |
1972 | 1961 | ||
1973 | [[package]] | 1962 | [[package]] |
1974 | name = "xtask" | 1963 | name = "xtask" |
diff --git a/Cargo.toml b/Cargo.toml index 498cf7d62..32ba3923b 100644 --- a/Cargo.toml +++ b/Cargo.toml | |||
@@ -3,10 +3,6 @@ resolver = "2" | |||
3 | members = ["xtask/", "lib/*", "crates/*"] | 3 | members = ["xtask/", "lib/*", "crates/*"] |
4 | 4 | ||
5 | [profile.dev] | 5 | [profile.dev] |
6 | # We do want incremental builds, but they are broken at the moment :( | ||
7 | # https://github.com/rust-lang/rust/issues/85003#issuecomment-833796289 | ||
8 | incremental = false | ||
9 | |||
10 | # Disabling debug info speeds up builds a bunch, | 6 | # Disabling debug info speeds up builds a bunch, |
11 | # and we don't rely on it for debugging that much. | 7 | # and we don't rely on it for debugging that much. |
12 | debug = 0 | 8 | debug = 0 |
@@ -21,14 +17,9 @@ text-size.opt-level = 3 | |||
21 | miniz_oxide.opt-level = 3 | 17 | miniz_oxide.opt-level = 3 |
22 | 18 | ||
23 | [profile.release] | 19 | [profile.release] |
24 | # We do want incremental release builds, but they are broken at the moment :( | 20 | incremental = true |
25 | # https://github.com/rust-lang/rust/issues/85003#issuecomment-833796289 | ||
26 | incremental = false | ||
27 | debug = 0 # Set this to 1 or 2 to get more useful backtraces in debugger. | 21 | debug = 0 # Set this to 1 or 2 to get more useful backtraces in debugger. |
28 | 22 | ||
29 | [profile.test] | ||
30 | incremental = false | ||
31 | |||
32 | [patch.'crates-io'] | 23 | [patch.'crates-io'] |
33 | # rowan = { path = "../rowan" } | 24 | # rowan = { path = "../rowan" } |
34 | 25 | ||
diff --git a/crates/base_db/src/fixture.rs b/crates/base_db/src/fixture.rs index 0132565e4..69ceba735 100644 --- a/crates/base_db/src/fixture.rs +++ b/crates/base_db/src/fixture.rs | |||
@@ -34,19 +34,13 @@ pub trait WithFixture: Default + SourceDatabaseExt + 'static { | |||
34 | 34 | ||
35 | fn with_position(ra_fixture: &str) -> (Self, FilePosition) { | 35 | fn with_position(ra_fixture: &str) -> (Self, FilePosition) { |
36 | let (db, file_id, range_or_offset) = Self::with_range_or_offset(ra_fixture); | 36 | let (db, file_id, range_or_offset) = Self::with_range_or_offset(ra_fixture); |
37 | let offset = match range_or_offset { | 37 | let offset = range_or_offset.expect_offset(); |
38 | RangeOrOffset::Range(_) => panic!("Expected a cursor position, got a range instead"), | ||
39 | RangeOrOffset::Offset(it) => it, | ||
40 | }; | ||
41 | (db, FilePosition { file_id, offset }) | 38 | (db, FilePosition { file_id, offset }) |
42 | } | 39 | } |
43 | 40 | ||
44 | fn with_range(ra_fixture: &str) -> (Self, FileRange) { | 41 | fn with_range(ra_fixture: &str) -> (Self, FileRange) { |
45 | let (db, file_id, range_or_offset) = Self::with_range_or_offset(ra_fixture); | 42 | let (db, file_id, range_or_offset) = Self::with_range_or_offset(ra_fixture); |
46 | let range = match range_or_offset { | 43 | let range = range_or_offset.expect_range(); |
47 | RangeOrOffset::Range(it) => it, | ||
48 | RangeOrOffset::Offset(_) => panic!("Expected a cursor range, got a position instead"), | ||
49 | }; | ||
50 | (db, FileRange { file_id, range }) | 44 | (db, FileRange { file_id, range }) |
51 | } | 45 | } |
52 | 46 | ||
diff --git a/crates/base_db/src/input.rs b/crates/base_db/src/input.rs index 64ccd11ee..23cb0c839 100644 --- a/crates/base_db/src/input.rs +++ b/crates/base_db/src/input.rs | |||
@@ -147,7 +147,7 @@ impl CrateDisplayName { | |||
147 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] | 147 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] |
148 | pub struct ProcMacroId(pub u32); | 148 | pub struct ProcMacroId(pub u32); |
149 | 149 | ||
150 | #[derive(Copy, Clone, Eq, PartialEq, Debug)] | 150 | #[derive(Copy, Clone, Eq, PartialEq, Debug, Hash)] |
151 | pub enum ProcMacroKind { | 151 | pub enum ProcMacroKind { |
152 | CustomDerive, | 152 | CustomDerive, |
153 | FuncLike, | 153 | FuncLike, |
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 01b2de515..975ae4869 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs | |||
@@ -1282,10 +1282,16 @@ impl BuiltinType { | |||
1282 | 1282 | ||
1283 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | 1283 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
1284 | pub enum MacroKind { | 1284 | pub enum MacroKind { |
1285 | /// `macro_rules!` or Macros 2.0 macro. | ||
1285 | Declarative, | 1286 | Declarative, |
1286 | ProcMacro, | 1287 | /// A built-in or custom derive. |
1287 | Derive, | 1288 | Derive, |
1289 | /// A built-in function-like macro. | ||
1288 | BuiltIn, | 1290 | BuiltIn, |
1291 | /// A procedural attribute macro. | ||
1292 | Attr, | ||
1293 | /// A function-like procedural macro. | ||
1294 | ProcMacro, | ||
1289 | } | 1295 | } |
1290 | 1296 | ||
1291 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | 1297 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
@@ -1315,11 +1321,13 @@ impl MacroDef { | |||
1315 | pub fn kind(&self) -> MacroKind { | 1321 | pub fn kind(&self) -> MacroKind { |
1316 | match self.id.kind { | 1322 | match self.id.kind { |
1317 | MacroDefKind::Declarative(_) => MacroKind::Declarative, | 1323 | MacroDefKind::Declarative(_) => MacroKind::Declarative, |
1318 | MacroDefKind::BuiltIn(_, _) => MacroKind::BuiltIn, | 1324 | MacroDefKind::BuiltIn(_, _) | MacroDefKind::BuiltInEager(_, _) => MacroKind::BuiltIn, |
1319 | MacroDefKind::BuiltInDerive(_, _) => MacroKind::Derive, | 1325 | MacroDefKind::BuiltInDerive(_, _) => MacroKind::Derive, |
1320 | MacroDefKind::BuiltInEager(_, _) => MacroKind::BuiltIn, | 1326 | MacroDefKind::ProcMacro(_, base_db::ProcMacroKind::CustomDerive, _) => { |
1321 | // FIXME might be a derive | 1327 | MacroKind::Derive |
1322 | MacroDefKind::ProcMacro(_, _) => MacroKind::ProcMacro, | 1328 | } |
1329 | MacroDefKind::ProcMacro(_, base_db::ProcMacroKind::Attr, _) => MacroKind::Attr, | ||
1330 | MacroDefKind::ProcMacro(_, base_db::ProcMacroKind::FuncLike, _) => MacroKind::ProcMacro, | ||
1323 | } | 1331 | } |
1324 | } | 1332 | } |
1325 | } | 1333 | } |
diff --git a/crates/hir_def/src/data.rs b/crates/hir_def/src/data.rs index a04f73352..d2bb381be 100644 --- a/crates/hir_def/src/data.rs +++ b/crates/hir_def/src/data.rs | |||
@@ -22,6 +22,7 @@ pub struct FunctionData { | |||
22 | pub name: Name, | 22 | pub name: Name, |
23 | pub params: Vec<Interned<TypeRef>>, | 23 | pub params: Vec<Interned<TypeRef>>, |
24 | pub ret_type: Interned<TypeRef>, | 24 | pub ret_type: Interned<TypeRef>, |
25 | pub async_ret_type: Option<Interned<TypeRef>>, | ||
25 | pub attrs: Attrs, | 26 | pub attrs: Attrs, |
26 | pub visibility: RawVisibility, | 27 | pub visibility: RawVisibility, |
27 | pub abi: Option<Interned<str>>, | 28 | pub abi: Option<Interned<str>>, |
@@ -63,6 +64,7 @@ impl FunctionData { | |||
63 | }) | 64 | }) |
64 | .collect(), | 65 | .collect(), |
65 | ret_type: func.ret_type.clone(), | 66 | ret_type: func.ret_type.clone(), |
67 | async_ret_type: func.async_ret_type.clone(), | ||
66 | attrs: item_tree.attrs(db, krate, ModItem::from(loc.id.value).into()), | 68 | attrs: item_tree.attrs(db, krate, ModItem::from(loc.id.value).into()), |
67 | visibility: item_tree[func.visibility].clone(), | 69 | visibility: item_tree[func.visibility].clone(), |
68 | abi: func.abi.clone(), | 70 | abi: func.abi.clone(), |
diff --git a/crates/hir_def/src/item_tree.rs b/crates/hir_def/src/item_tree.rs index c4d20c416..227337a8d 100644 --- a/crates/hir_def/src/item_tree.rs +++ b/crates/hir_def/src/item_tree.rs | |||
@@ -580,6 +580,7 @@ pub struct Function { | |||
580 | pub abi: Option<Interned<str>>, | 580 | pub abi: Option<Interned<str>>, |
581 | pub params: IdRange<Param>, | 581 | pub params: IdRange<Param>, |
582 | pub ret_type: Interned<TypeRef>, | 582 | pub ret_type: Interned<TypeRef>, |
583 | pub async_ret_type: Option<Interned<TypeRef>>, | ||
583 | pub ast_id: FileAstId<ast::Fn>, | 584 | pub ast_id: FileAstId<ast::Fn>, |
584 | pub(crate) flags: FnFlags, | 585 | pub(crate) flags: FnFlags, |
585 | } | 586 | } |
diff --git a/crates/hir_def/src/item_tree/lower.rs b/crates/hir_def/src/item_tree/lower.rs index b83adec46..6208facd5 100644 --- a/crates/hir_def/src/item_tree/lower.rs +++ b/crates/hir_def/src/item_tree/lower.rs | |||
@@ -356,12 +356,13 @@ impl<'a> Ctx<'a> { | |||
356 | _ => TypeRef::unit(), | 356 | _ => TypeRef::unit(), |
357 | }; | 357 | }; |
358 | 358 | ||
359 | let ret_type = if func.async_token().is_some() { | 359 | let (ret_type, async_ret_type) = if func.async_token().is_some() { |
360 | let async_ret_type = ret_type.clone(); | ||
360 | let future_impl = desugar_future_path(ret_type); | 361 | let future_impl = desugar_future_path(ret_type); |
361 | let ty_bound = Interned::new(TypeBound::Path(future_impl)); | 362 | let ty_bound = Interned::new(TypeBound::Path(future_impl)); |
362 | TypeRef::ImplTrait(vec![ty_bound]) | 363 | (TypeRef::ImplTrait(vec![ty_bound]), Some(async_ret_type)) |
363 | } else { | 364 | } else { |
364 | ret_type | 365 | (ret_type, None) |
365 | }; | 366 | }; |
366 | 367 | ||
367 | let abi = func.abi().map(lower_abi); | 368 | let abi = func.abi().map(lower_abi); |
@@ -395,6 +396,7 @@ impl<'a> Ctx<'a> { | |||
395 | abi, | 396 | abi, |
396 | params, | 397 | params, |
397 | ret_type: Interned::new(ret_type), | 398 | ret_type: Interned::new(ret_type), |
399 | async_ret_type: async_ret_type.map(Interned::new), | ||
398 | ast_id, | 400 | ast_id, |
399 | flags, | 401 | flags, |
400 | }; | 402 | }; |
diff --git a/crates/hir_def/src/item_tree/pretty.rs b/crates/hir_def/src/item_tree/pretty.rs index d1ee697cb..cc9944a22 100644 --- a/crates/hir_def/src/item_tree/pretty.rs +++ b/crates/hir_def/src/item_tree/pretty.rs | |||
@@ -235,6 +235,7 @@ impl<'a> Printer<'a> { | |||
235 | abi, | 235 | abi, |
236 | params, | 236 | params, |
237 | ret_type, | 237 | ret_type, |
238 | async_ret_type: _, | ||
238 | ast_id: _, | 239 | ast_id: _, |
239 | flags, | 240 | flags, |
240 | } = &self.tree[it]; | 241 | } = &self.tree[it]; |
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs index 4296c6304..d9d6c91a8 100644 --- a/crates/hir_def/src/nameres/collector.rs +++ b/crates/hir_def/src/nameres/collector.rs | |||
@@ -477,16 +477,21 @@ impl DefCollector<'_> { | |||
477 | /// going out of sync with what the build system sees (since we resolve using VFS state, but | 477 | /// going out of sync with what the build system sees (since we resolve using VFS state, but |
478 | /// Cargo builds only on-disk files). We could and probably should add diagnostics for that. | 478 | /// Cargo builds only on-disk files). We could and probably should add diagnostics for that. |
479 | fn export_proc_macro(&mut self, def: ProcMacroDef, ast_id: AstId<ast::Fn>) { | 479 | fn export_proc_macro(&mut self, def: ProcMacroDef, ast_id: AstId<ast::Fn>) { |
480 | let kind = def.kind.to_basedb_kind(); | ||
480 | self.exports_proc_macros = true; | 481 | self.exports_proc_macros = true; |
481 | let macro_def = match self.proc_macros.iter().find(|(n, _)| n == &def.name) { | 482 | let macro_def = match self.proc_macros.iter().find(|(n, _)| n == &def.name) { |
482 | Some((_, expander)) => MacroDefId { | 483 | Some((_, expander)) => MacroDefId { |
483 | krate: self.def_map.krate, | 484 | krate: self.def_map.krate, |
484 | kind: MacroDefKind::ProcMacro(*expander, ast_id), | 485 | kind: MacroDefKind::ProcMacro(*expander, kind, ast_id), |
485 | local_inner: false, | 486 | local_inner: false, |
486 | }, | 487 | }, |
487 | None => MacroDefId { | 488 | None => MacroDefId { |
488 | krate: self.def_map.krate, | 489 | krate: self.def_map.krate, |
489 | kind: MacroDefKind::ProcMacro(ProcMacroExpander::dummy(self.def_map.krate), ast_id), | 490 | kind: MacroDefKind::ProcMacro( |
491 | ProcMacroExpander::dummy(self.def_map.krate), | ||
492 | kind, | ||
493 | ast_id, | ||
494 | ), | ||
490 | local_inner: false, | 495 | local_inner: false, |
491 | }, | 496 | }, |
492 | }; | 497 | }; |
diff --git a/crates/hir_def/src/nameres/proc_macro.rs b/crates/hir_def/src/nameres/proc_macro.rs index 156598f19..3f095d623 100644 --- a/crates/hir_def/src/nameres/proc_macro.rs +++ b/crates/hir_def/src/nameres/proc_macro.rs | |||
@@ -18,6 +18,16 @@ pub(super) enum ProcMacroKind { | |||
18 | Attr, | 18 | Attr, |
19 | } | 19 | } |
20 | 20 | ||
21 | impl ProcMacroKind { | ||
22 | pub(super) fn to_basedb_kind(&self) -> base_db::ProcMacroKind { | ||
23 | match self { | ||
24 | ProcMacroKind::CustomDerive { .. } => base_db::ProcMacroKind::CustomDerive, | ||
25 | ProcMacroKind::FnLike => base_db::ProcMacroKind::FuncLike, | ||
26 | ProcMacroKind::Attr => base_db::ProcMacroKind::Attr, | ||
27 | } | ||
28 | } | ||
29 | } | ||
30 | |||
21 | impl Attrs { | 31 | impl Attrs { |
22 | #[rustfmt::skip] | 32 | #[rustfmt::skip] |
23 | pub(super) fn parse_proc_macro_decl(&self, func_name: &Name) -> Option<ProcMacroDef> { | 33 | pub(super) fn parse_proc_macro_decl(&self, func_name: &Name) -> Option<ProcMacroDef> { |
diff --git a/crates/hir_def/src/nameres/tests/incremental.rs b/crates/hir_def/src/nameres/tests/incremental.rs index d884a6eb4..7bf152e26 100644 --- a/crates/hir_def/src/nameres/tests/incremental.rs +++ b/crates/hir_def/src/nameres/tests/incremental.rs | |||
@@ -1,6 +1,8 @@ | |||
1 | use std::sync::Arc; | 1 | use std::sync::Arc; |
2 | 2 | ||
3 | use base_db::SourceDatabaseExt; | 3 | use base_db::{salsa::SweepStrategy, SourceDatabaseExt}; |
4 | |||
5 | use crate::{AdtId, ModuleDefId}; | ||
4 | 6 | ||
5 | use super::*; | 7 | use super::*; |
6 | 8 | ||
@@ -163,3 +165,73 @@ m!(Z); | |||
163 | assert_eq!(n_reparsed_macros, 0); | 165 | assert_eq!(n_reparsed_macros, 0); |
164 | } | 166 | } |
165 | } | 167 | } |
168 | |||
169 | #[test] | ||
170 | fn item_tree_prevents_reparsing() { | ||
171 | // The `ItemTree` is used by both name resolution and the various queries in `adt.rs` and | ||
172 | // `data.rs`. After computing the `ItemTree` and deleting the parse tree, we should be able to | ||
173 | // run those other queries without triggering a reparse. | ||
174 | |||
175 | let (db, pos) = TestDB::with_position( | ||
176 | r#" | ||
177 | pub struct S; | ||
178 | pub union U {} | ||
179 | pub enum E { | ||
180 | Variant, | ||
181 | } | ||
182 | pub fn f(_: S) { $0 } | ||
183 | pub trait Tr {} | ||
184 | impl Tr for () {} | ||
185 | pub const C: u8 = 0; | ||
186 | pub static ST: u8 = 0; | ||
187 | pub type Ty = (); | ||
188 | "#, | ||
189 | ); | ||
190 | let krate = db.test_crate(); | ||
191 | { | ||
192 | let events = db.log_executed(|| { | ||
193 | db.file_item_tree(pos.file_id.into()); | ||
194 | }); | ||
195 | let n_calculated_item_trees = events.iter().filter(|it| it.contains("item_tree")).count(); | ||
196 | assert_eq!(n_calculated_item_trees, 1); | ||
197 | let n_parsed_files = events.iter().filter(|it| it.contains("parse(")).count(); | ||
198 | assert_eq!(n_parsed_files, 1); | ||
199 | } | ||
200 | |||
201 | // Delete the parse tree. | ||
202 | let sweep = SweepStrategy::default().discard_values().sweep_all_revisions(); | ||
203 | base_db::ParseQuery.in_db(&db).sweep(sweep); | ||
204 | |||
205 | { | ||
206 | let events = db.log_executed(|| { | ||
207 | let crate_def_map = db.crate_def_map(krate); | ||
208 | let (_, module_data) = crate_def_map.modules.iter().last().unwrap(); | ||
209 | assert_eq!(module_data.scope.resolutions().count(), 8); | ||
210 | assert_eq!(module_data.scope.impls().count(), 1); | ||
211 | |||
212 | for imp in module_data.scope.impls() { | ||
213 | db.impl_data(imp); | ||
214 | } | ||
215 | |||
216 | for (_, res) in module_data.scope.resolutions() { | ||
217 | match res.values.or(res.types).unwrap().0 { | ||
218 | ModuleDefId::FunctionId(f) => drop(db.function_data(f)), | ||
219 | ModuleDefId::AdtId(adt) => match adt { | ||
220 | AdtId::StructId(it) => drop(db.struct_data(it)), | ||
221 | AdtId::UnionId(it) => drop(db.union_data(it)), | ||
222 | AdtId::EnumId(it) => drop(db.enum_data(it)), | ||
223 | }, | ||
224 | ModuleDefId::ConstId(it) => drop(db.const_data(it)), | ||
225 | ModuleDefId::StaticId(it) => drop(db.static_data(it)), | ||
226 | ModuleDefId::TraitId(it) => drop(db.trait_data(it)), | ||
227 | ModuleDefId::TypeAliasId(it) => drop(db.type_alias_data(it)), | ||
228 | ModuleDefId::EnumVariantId(_) | ||
229 | | ModuleDefId::ModuleId(_) | ||
230 | | ModuleDefId::BuiltinType(_) => unreachable!(), | ||
231 | } | ||
232 | } | ||
233 | }); | ||
234 | let n_reparsed_files = events.iter().filter(|it| it.contains("parse(")).count(); | ||
235 | assert_eq!(n_reparsed_files, 0); | ||
236 | } | ||
237 | } | ||
diff --git a/crates/hir_expand/src/builtin_macro.rs b/crates/hir_expand/src/builtin_macro.rs index 94d7aecb6..0b310ba2f 100644 --- a/crates/hir_expand/src/builtin_macro.rs +++ b/crates/hir_expand/src/builtin_macro.rs | |||
@@ -8,7 +8,6 @@ use base_db::{AnchoredPath, Edition, FileId}; | |||
8 | use cfg::CfgExpr; | 8 | use cfg::CfgExpr; |
9 | use either::Either; | 9 | use either::Either; |
10 | use mbe::{parse_exprs_with_sep, parse_to_token_tree, ExpandResult}; | 10 | use mbe::{parse_exprs_with_sep, parse_to_token_tree, ExpandResult}; |
11 | use parser::FragmentKind; | ||
12 | use syntax::ast::{self, AstToken}; | 11 | use syntax::ast::{self, AstToken}; |
13 | 12 | ||
14 | macro_rules! register_builtin { | 13 | macro_rules! register_builtin { |
@@ -47,7 +46,7 @@ macro_rules! register_builtin { | |||
47 | let expander = match *self { | 46 | let expander = match *self { |
48 | $( EagerExpander::$e_kind => $e_expand, )* | 47 | $( EagerExpander::$e_kind => $e_expand, )* |
49 | }; | 48 | }; |
50 | expander(db,arg_id,tt) | 49 | expander(db, arg_id, tt) |
51 | } | 50 | } |
52 | } | 51 | } |
53 | 52 | ||
@@ -64,14 +63,13 @@ macro_rules! register_builtin { | |||
64 | #[derive(Debug)] | 63 | #[derive(Debug)] |
65 | pub struct ExpandedEager { | 64 | pub struct ExpandedEager { |
66 | pub(crate) subtree: tt::Subtree, | 65 | pub(crate) subtree: tt::Subtree, |
67 | pub(crate) fragment: FragmentKind, | ||
68 | /// The included file ID of the include macro. | 66 | /// The included file ID of the include macro. |
69 | pub(crate) included_file: Option<FileId>, | 67 | pub(crate) included_file: Option<FileId>, |
70 | } | 68 | } |
71 | 69 | ||
72 | impl ExpandedEager { | 70 | impl ExpandedEager { |
73 | fn new(subtree: tt::Subtree, fragment: FragmentKind) -> Self { | 71 | fn new(subtree: tt::Subtree) -> Self { |
74 | ExpandedEager { subtree, fragment, included_file: None } | 72 | ExpandedEager { subtree, included_file: None } |
75 | } | 73 | } |
76 | } | 74 | } |
77 | 75 | ||
@@ -340,7 +338,7 @@ fn compile_error_expand( | |||
340 | _ => mbe::ExpandError::BindingError("`compile_error!` argument must be a string".into()), | 338 | _ => mbe::ExpandError::BindingError("`compile_error!` argument must be a string".into()), |
341 | }; | 339 | }; |
342 | 340 | ||
343 | ExpandResult { value: Some(ExpandedEager::new(quote! {}, FragmentKind::Items)), err: Some(err) } | 341 | ExpandResult { value: Some(ExpandedEager::new(quote! {})), err: Some(err) } |
344 | } | 342 | } |
345 | 343 | ||
346 | fn concat_expand( | 344 | fn concat_expand( |
@@ -371,7 +369,7 @@ fn concat_expand( | |||
371 | } | 369 | } |
372 | } | 370 | } |
373 | } | 371 | } |
374 | ExpandResult { value: Some(ExpandedEager::new(quote!(#text), FragmentKind::Expr)), err } | 372 | ExpandResult { value: Some(ExpandedEager::new(quote!(#text))), err } |
375 | } | 373 | } |
376 | 374 | ||
377 | fn concat_idents_expand( | 375 | fn concat_idents_expand( |
@@ -393,7 +391,7 @@ fn concat_idents_expand( | |||
393 | } | 391 | } |
394 | } | 392 | } |
395 | let ident = tt::Ident { text: ident.into(), id: tt::TokenId::unspecified() }; | 393 | let ident = tt::Ident { text: ident.into(), id: tt::TokenId::unspecified() }; |
396 | ExpandResult { value: Some(ExpandedEager::new(quote!(#ident), FragmentKind::Expr)), err } | 394 | ExpandResult { value: Some(ExpandedEager::new(quote!(#ident))), err } |
397 | } | 395 | } |
398 | 396 | ||
399 | fn relative_file( | 397 | fn relative_file( |
@@ -442,14 +440,7 @@ fn include_expand( | |||
442 | 440 | ||
443 | match res { | 441 | match res { |
444 | Ok((subtree, file_id)) => { | 442 | Ok((subtree, file_id)) => { |
445 | // FIXME: | 443 | ExpandResult::ok(Some(ExpandedEager { subtree, included_file: Some(file_id) })) |
446 | // Handle include as expression | ||
447 | |||
448 | ExpandResult::ok(Some(ExpandedEager { | ||
449 | subtree, | ||
450 | fragment: FragmentKind::Items, | ||
451 | included_file: Some(file_id), | ||
452 | })) | ||
453 | } | 444 | } |
454 | Err(e) => ExpandResult::only_err(e), | 445 | Err(e) => ExpandResult::only_err(e), |
455 | } | 446 | } |
@@ -472,7 +463,7 @@ fn include_bytes_expand( | |||
472 | id: tt::TokenId::unspecified(), | 463 | id: tt::TokenId::unspecified(), |
473 | }))], | 464 | }))], |
474 | }; | 465 | }; |
475 | ExpandResult::ok(Some(ExpandedEager::new(res, FragmentKind::Expr))) | 466 | ExpandResult::ok(Some(ExpandedEager::new(res))) |
476 | } | 467 | } |
477 | 468 | ||
478 | fn include_str_expand( | 469 | fn include_str_expand( |
@@ -492,14 +483,14 @@ fn include_str_expand( | |||
492 | let file_id = match relative_file(db, arg_id.into(), &path, true) { | 483 | let file_id = match relative_file(db, arg_id.into(), &path, true) { |
493 | Ok(file_id) => file_id, | 484 | Ok(file_id) => file_id, |
494 | Err(_) => { | 485 | Err(_) => { |
495 | return ExpandResult::ok(Some(ExpandedEager::new(quote!(""), FragmentKind::Expr))); | 486 | return ExpandResult::ok(Some(ExpandedEager::new(quote!("")))); |
496 | } | 487 | } |
497 | }; | 488 | }; |
498 | 489 | ||
499 | let text = db.file_text(file_id); | 490 | let text = db.file_text(file_id); |
500 | let text = &*text; | 491 | let text = &*text; |
501 | 492 | ||
502 | ExpandResult::ok(Some(ExpandedEager::new(quote!(#text), FragmentKind::Expr))) | 493 | ExpandResult::ok(Some(ExpandedEager::new(quote!(#text)))) |
503 | } | 494 | } |
504 | 495 | ||
505 | fn get_env_inner(db: &dyn AstDatabase, arg_id: MacroCallId, key: &str) -> Option<String> { | 496 | fn get_env_inner(db: &dyn AstDatabase, arg_id: MacroCallId, key: &str) -> Option<String> { |
@@ -535,7 +526,7 @@ fn env_expand( | |||
535 | }); | 526 | }); |
536 | let expanded = quote! { #s }; | 527 | let expanded = quote! { #s }; |
537 | 528 | ||
538 | ExpandResult { value: Some(ExpandedEager::new(expanded, FragmentKind::Expr)), err } | 529 | ExpandResult { value: Some(ExpandedEager::new(expanded)), err } |
539 | } | 530 | } |
540 | 531 | ||
541 | fn option_env_expand( | 532 | fn option_env_expand( |
@@ -553,7 +544,7 @@ fn option_env_expand( | |||
553 | Some(s) => quote! { std::option::Some(#s) }, | 544 | Some(s) => quote! { std::option::Some(#s) }, |
554 | }; | 545 | }; |
555 | 546 | ||
556 | ExpandResult::ok(Some(ExpandedEager::new(expanded, FragmentKind::Expr))) | 547 | ExpandResult::ok(Some(ExpandedEager::new(expanded))) |
557 | } | 548 | } |
558 | 549 | ||
559 | #[cfg(test)] | 550 | #[cfg(test)] |
@@ -565,6 +556,7 @@ mod tests { | |||
565 | }; | 556 | }; |
566 | use base_db::{fixture::WithFixture, SourceDatabase}; | 557 | use base_db::{fixture::WithFixture, SourceDatabase}; |
567 | use expect_test::{expect, Expect}; | 558 | use expect_test::{expect, Expect}; |
559 | use parser::FragmentKind; | ||
568 | use std::sync::Arc; | 560 | use std::sync::Arc; |
569 | use syntax::ast::NameOwner; | 561 | use syntax::ast::NameOwner; |
570 | 562 | ||
@@ -617,6 +609,7 @@ mod tests { | |||
617 | local_inner: false, | 609 | local_inner: false, |
618 | }; | 610 | }; |
619 | 611 | ||
612 | let fragment = crate::to_fragment_kind(¯o_call); | ||
620 | let args = macro_call.token_tree().unwrap(); | 613 | let args = macro_call.token_tree().unwrap(); |
621 | let parsed_args = mbe::ast_to_token_tree(&args).0; | 614 | let parsed_args = mbe::ast_to_token_tree(&args).0; |
622 | let call_id = AstId::new(file_id.into(), ast_id_map.ast_id(¯o_call)); | 615 | let call_id = AstId::new(file_id.into(), ast_id_map.ast_id(¯o_call)); |
@@ -639,7 +632,7 @@ mod tests { | |||
639 | arg_or_expansion: Arc::new(expanded.subtree), | 632 | arg_or_expansion: Arc::new(expanded.subtree), |
640 | included_file: expanded.included_file, | 633 | included_file: expanded.included_file, |
641 | }), | 634 | }), |
642 | kind: MacroCallKind::FnLike { ast_id: call_id, fragment: expanded.fragment }, | 635 | kind: MacroCallKind::FnLike { ast_id: call_id, fragment }, |
643 | }; | 636 | }; |
644 | 637 | ||
645 | let id: MacroCallId = db.intern_macro(loc).into(); | 638 | let id: MacroCallId = db.intern_macro(loc).into(); |
diff --git a/crates/hir_expand/src/eager.rs b/crates/hir_expand/src/eager.rs index 1464320ba..14af628a1 100644 --- a/crates/hir_expand/src/eager.rs +++ b/crates/hir_expand/src/eager.rs | |||
@@ -113,6 +113,7 @@ pub fn expand_eager_macro( | |||
113 | 113 | ||
114 | let ast_map = db.ast_id_map(macro_call.file_id); | 114 | let ast_map = db.ast_id_map(macro_call.file_id); |
115 | let call_id = InFile::new(macro_call.file_id, ast_map.ast_id(¯o_call.value)); | 115 | let call_id = InFile::new(macro_call.file_id, ast_map.ast_id(¯o_call.value)); |
116 | let fragment = crate::to_fragment_kind(¯o_call.value); | ||
116 | 117 | ||
117 | // Note: | 118 | // Note: |
118 | // When `lazy_expand` is called, its *parent* file must be already exists. | 119 | // When `lazy_expand` is called, its *parent* file must be already exists. |
@@ -152,7 +153,7 @@ pub fn expand_eager_macro( | |||
152 | arg_or_expansion: Arc::new(expanded.subtree), | 153 | arg_or_expansion: Arc::new(expanded.subtree), |
153 | included_file: expanded.included_file, | 154 | included_file: expanded.included_file, |
154 | }), | 155 | }), |
155 | kind: MacroCallKind::FnLike { ast_id: call_id, fragment: expanded.fragment }, | 156 | kind: MacroCallKind::FnLike { ast_id: call_id, fragment }, |
156 | }; | 157 | }; |
157 | 158 | ||
158 | Ok(db.intern_macro(loc)) | 159 | Ok(db.intern_macro(loc)) |
diff --git a/crates/hir_expand/src/lib.rs b/crates/hir_expand/src/lib.rs index 10d37234e..90d8ae240 100644 --- a/crates/hir_expand/src/lib.rs +++ b/crates/hir_expand/src/lib.rs | |||
@@ -15,6 +15,7 @@ pub mod quote; | |||
15 | pub mod eager; | 15 | pub mod eager; |
16 | mod input; | 16 | mod input; |
17 | 17 | ||
18 | use base_db::ProcMacroKind; | ||
18 | use either::Either; | 19 | use either::Either; |
19 | 20 | ||
20 | pub use mbe::{ExpandError, ExpandResult}; | 21 | pub use mbe::{ExpandError, ExpandResult}; |
@@ -207,7 +208,7 @@ impl MacroDefId { | |||
207 | MacroDefKind::BuiltIn(_, id) => id, | 208 | MacroDefKind::BuiltIn(_, id) => id, |
208 | MacroDefKind::BuiltInDerive(_, id) => id, | 209 | MacroDefKind::BuiltInDerive(_, id) => id, |
209 | MacroDefKind::BuiltInEager(_, id) => id, | 210 | MacroDefKind::BuiltInEager(_, id) => id, |
210 | MacroDefKind::ProcMacro(_, id) => return Either::Right(*id), | 211 | MacroDefKind::ProcMacro(.., id) => return Either::Right(*id), |
211 | }; | 212 | }; |
212 | Either::Left(*id) | 213 | Either::Left(*id) |
213 | } | 214 | } |
@@ -224,7 +225,7 @@ pub enum MacroDefKind { | |||
224 | // FIXME: maybe just Builtin and rename BuiltinFnLikeExpander to BuiltinExpander | 225 | // FIXME: maybe just Builtin and rename BuiltinFnLikeExpander to BuiltinExpander |
225 | BuiltInDerive(BuiltinDeriveExpander, AstId<ast::Macro>), | 226 | BuiltInDerive(BuiltinDeriveExpander, AstId<ast::Macro>), |
226 | BuiltInEager(EagerExpander, AstId<ast::Macro>), | 227 | BuiltInEager(EagerExpander, AstId<ast::Macro>), |
227 | ProcMacro(ProcMacroExpander, AstId<ast::Fn>), | 228 | ProcMacro(ProcMacroExpander, ProcMacroKind, AstId<ast::Fn>), |
228 | } | 229 | } |
229 | 230 | ||
230 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | 231 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
diff --git a/crates/hir_ty/Cargo.toml b/crates/hir_ty/Cargo.toml index a9994082a..c3d02424d 100644 --- a/crates/hir_ty/Cargo.toml +++ b/crates/hir_ty/Cargo.toml | |||
@@ -18,9 +18,9 @@ ena = "0.14.0" | |||
18 | log = "0.4.8" | 18 | log = "0.4.8" |
19 | rustc-hash = "1.1.0" | 19 | rustc-hash = "1.1.0" |
20 | scoped-tls = "1" | 20 | scoped-tls = "1" |
21 | chalk-solve = { version = "0.67", default-features = false } | 21 | chalk-solve = { version = "0.68", default-features = false } |
22 | chalk-ir = "0.67" | 22 | chalk-ir = "0.68" |
23 | chalk-recursive = "0.67" | 23 | chalk-recursive = "0.68" |
24 | la-arena = { version = "0.2.0", path = "../../lib/arena" } | 24 | la-arena = { version = "0.2.0", path = "../../lib/arena" } |
25 | 25 | ||
26 | stdx = { path = "../stdx", version = "0.0.0" } | 26 | stdx = { path = "../stdx", version = "0.0.0" } |
diff --git a/crates/hir_ty/src/chalk_db.rs b/crates/hir_ty/src/chalk_db.rs index b108fd559..4e042bf42 100644 --- a/crates/hir_ty/src/chalk_db.rs +++ b/crates/hir_ty/src/chalk_db.rs | |||
@@ -383,7 +383,7 @@ pub(crate) fn associated_ty_data_query( | |||
383 | // Lower bounds -- we could/should maybe move this to a separate query in `lower` | 383 | // Lower bounds -- we could/should maybe move this to a separate query in `lower` |
384 | let type_alias_data = db.type_alias_data(type_alias); | 384 | let type_alias_data = db.type_alias_data(type_alias); |
385 | let generic_params = generics(db.upcast(), type_alias.into()); | 385 | let generic_params = generics(db.upcast(), type_alias.into()); |
386 | let bound_vars = generic_params.bound_vars_subst(DebruijnIndex::INNERMOST); | 386 | // let bound_vars = generic_params.bound_vars_subst(DebruijnIndex::INNERMOST); |
387 | let resolver = hir_def::resolver::HasResolver::resolver(type_alias, db.upcast()); | 387 | let resolver = hir_def::resolver::HasResolver::resolver(type_alias, db.upcast()); |
388 | let ctx = crate::TyLoweringContext::new(db, &resolver) | 388 | let ctx = crate::TyLoweringContext::new(db, &resolver) |
389 | .with_type_param_mode(crate::lower::TypeParamLoweringMode::Variable); | 389 | .with_type_param_mode(crate::lower::TypeParamLoweringMode::Variable); |
@@ -396,8 +396,10 @@ pub(crate) fn associated_ty_data_query( | |||
396 | .filter_map(|pred| generic_predicate_to_inline_bound(db, &pred, &self_ty)) | 396 | .filter_map(|pred| generic_predicate_to_inline_bound(db, &pred, &self_ty)) |
397 | .collect(); | 397 | .collect(); |
398 | 398 | ||
399 | let where_clauses = convert_where_clauses(db, type_alias.into(), &bound_vars); | 399 | // FIXME: Re-enable where clauses on associated types when an upstream chalk bug is fixed. |
400 | let bound_data = rust_ir::AssociatedTyDatumBound { bounds, where_clauses }; | 400 | // (rust-analyzer#9052) |
401 | // let where_clauses = convert_where_clauses(db, type_alias.into(), &bound_vars); | ||
402 | let bound_data = rust_ir::AssociatedTyDatumBound { bounds, where_clauses: vec![] }; | ||
401 | let datum = AssociatedTyDatum { | 403 | let datum = AssociatedTyDatum { |
402 | trait_id: to_chalk_trait_id(trait_), | 404 | trait_id: to_chalk_trait_id(trait_), |
403 | id, | 405 | id, |
diff --git a/crates/hir_ty/src/infer.rs b/crates/hir_ty/src/infer.rs index 8cefd80f3..7a4268819 100644 --- a/crates/hir_ty/src/infer.rs +++ b/crates/hir_ty/src/infer.rs | |||
@@ -558,7 +558,13 @@ impl<'a> InferenceContext<'a> { | |||
558 | 558 | ||
559 | self.infer_pat(*pat, &ty, BindingMode::default()); | 559 | self.infer_pat(*pat, &ty, BindingMode::default()); |
560 | } | 560 | } |
561 | let return_ty = self.make_ty_with_mode(&data.ret_type, ImplTraitLoweringMode::Disallowed); // FIXME implement RPIT | 561 | let error_ty = &TypeRef::Error; |
562 | let return_ty = if data.is_async() { | ||
563 | data.async_ret_type.as_deref().unwrap_or(error_ty) | ||
564 | } else { | ||
565 | &*data.ret_type | ||
566 | }; | ||
567 | let return_ty = self.make_ty_with_mode(return_ty, ImplTraitLoweringMode::Disallowed); // FIXME implement RPIT | ||
562 | self.return_ty = return_ty; | 568 | self.return_ty = return_ty; |
563 | } | 569 | } |
564 | 570 | ||
diff --git a/crates/hir_ty/src/tests.rs b/crates/hir_ty/src/tests.rs index cc819373c..9d726b024 100644 --- a/crates/hir_ty/src/tests.rs +++ b/crates/hir_ty/src/tests.rs | |||
@@ -7,6 +7,7 @@ mod traits; | |||
7 | mod method_resolution; | 7 | mod method_resolution; |
8 | mod macros; | 8 | mod macros; |
9 | mod display_source_code; | 9 | mod display_source_code; |
10 | mod incremental; | ||
10 | 11 | ||
11 | use std::{env, sync::Arc}; | 12 | use std::{env, sync::Arc}; |
12 | 13 | ||
@@ -317,50 +318,6 @@ fn ellipsize(mut text: String, max_len: usize) -> String { | |||
317 | text | 318 | text |
318 | } | 319 | } |
319 | 320 | ||
320 | #[test] | ||
321 | fn typing_whitespace_inside_a_function_should_not_invalidate_types() { | ||
322 | let (mut db, pos) = TestDB::with_position( | ||
323 | " | ||
324 | //- /lib.rs | ||
325 | fn foo() -> i32 { | ||
326 | $01 + 1 | ||
327 | } | ||
328 | ", | ||
329 | ); | ||
330 | { | ||
331 | let events = db.log_executed(|| { | ||
332 | let module = db.module_for_file(pos.file_id); | ||
333 | let crate_def_map = module.def_map(&db); | ||
334 | visit_module(&db, &crate_def_map, module.local_id, &mut |def| { | ||
335 | db.infer(def); | ||
336 | }); | ||
337 | }); | ||
338 | assert!(format!("{:?}", events).contains("infer")) | ||
339 | } | ||
340 | |||
341 | let new_text = " | ||
342 | fn foo() -> i32 { | ||
343 | 1 | ||
344 | + | ||
345 | 1 | ||
346 | } | ||
347 | " | ||
348 | .to_string(); | ||
349 | |||
350 | db.set_file_text(pos.file_id, Arc::new(new_text)); | ||
351 | |||
352 | { | ||
353 | let events = db.log_executed(|| { | ||
354 | let module = db.module_for_file(pos.file_id); | ||
355 | let crate_def_map = module.def_map(&db); | ||
356 | visit_module(&db, &crate_def_map, module.local_id, &mut |def| { | ||
357 | db.infer(def); | ||
358 | }); | ||
359 | }); | ||
360 | assert!(!format!("{:?}", events).contains("infer"), "{:#?}", events) | ||
361 | } | ||
362 | } | ||
363 | |||
364 | fn check_infer(ra_fixture: &str, expect: Expect) { | 321 | fn check_infer(ra_fixture: &str, expect: Expect) { |
365 | let mut actual = infer(ra_fixture); | 322 | let mut actual = infer(ra_fixture); |
366 | actual.push('\n'); | 323 | actual.push('\n'); |
diff --git a/crates/hir_ty/src/tests/incremental.rs b/crates/hir_ty/src/tests/incremental.rs new file mode 100644 index 000000000..3e08e83e8 --- /dev/null +++ b/crates/hir_ty/src/tests/incremental.rs | |||
@@ -0,0 +1,51 @@ | |||
1 | use std::sync::Arc; | ||
2 | |||
3 | use base_db::{fixture::WithFixture, SourceDatabaseExt}; | ||
4 | |||
5 | use crate::{db::HirDatabase, test_db::TestDB}; | ||
6 | |||
7 | use super::visit_module; | ||
8 | |||
9 | #[test] | ||
10 | fn typing_whitespace_inside_a_function_should_not_invalidate_types() { | ||
11 | let (mut db, pos) = TestDB::with_position( | ||
12 | " | ||
13 | //- /lib.rs | ||
14 | fn foo() -> i32 { | ||
15 | $01 + 1 | ||
16 | } | ||
17 | ", | ||
18 | ); | ||
19 | { | ||
20 | let events = db.log_executed(|| { | ||
21 | let module = db.module_for_file(pos.file_id); | ||
22 | let crate_def_map = module.def_map(&db); | ||
23 | visit_module(&db, &crate_def_map, module.local_id, &mut |def| { | ||
24 | db.infer(def); | ||
25 | }); | ||
26 | }); | ||
27 | assert!(format!("{:?}", events).contains("infer")) | ||
28 | } | ||
29 | |||
30 | let new_text = " | ||
31 | fn foo() -> i32 { | ||
32 | 1 | ||
33 | + | ||
34 | 1 | ||
35 | } | ||
36 | " | ||
37 | .to_string(); | ||
38 | |||
39 | db.set_file_text(pos.file_id, Arc::new(new_text)); | ||
40 | |||
41 | { | ||
42 | let events = db.log_executed(|| { | ||
43 | let module = db.module_for_file(pos.file_id); | ||
44 | let crate_def_map = module.def_map(&db); | ||
45 | visit_module(&db, &crate_def_map, module.local_id, &mut |def| { | ||
46 | db.infer(def); | ||
47 | }); | ||
48 | }); | ||
49 | assert!(!format!("{:?}", events).contains("infer"), "{:#?}", events) | ||
50 | } | ||
51 | } | ||
diff --git a/crates/hir_ty/src/tests/macros.rs b/crates/hir_ty/src/tests/macros.rs index 6588aa46c..7647bb08b 100644 --- a/crates/hir_ty/src/tests/macros.rs +++ b/crates/hir_ty/src/tests/macros.rs | |||
@@ -752,6 +752,24 @@ fn bar() -> u32 {0} | |||
752 | } | 752 | } |
753 | 753 | ||
754 | #[test] | 754 | #[test] |
755 | fn infer_builtin_macros_include_expression() { | ||
756 | check_types( | ||
757 | r#" | ||
758 | //- /main.rs | ||
759 | #[rustc_builtin_macro] | ||
760 | macro_rules! include {() => {}} | ||
761 | fn main() { | ||
762 | let i = include!("bla.rs"); | ||
763 | i; | ||
764 | //^ i32 | ||
765 | } | ||
766 | //- /bla.rs | ||
767 | 0 | ||
768 | "#, | ||
769 | ) | ||
770 | } | ||
771 | |||
772 | #[test] | ||
755 | fn infer_builtin_macros_include_child_mod() { | 773 | fn infer_builtin_macros_include_child_mod() { |
756 | check_types( | 774 | check_types( |
757 | r#" | 775 | r#" |
diff --git a/crates/hir_ty/src/tests/traits.rs b/crates/hir_ty/src/tests/traits.rs index 6ad96bfe3..49add4ab9 100644 --- a/crates/hir_ty/src/tests/traits.rs +++ b/crates/hir_ty/src/tests/traits.rs | |||
@@ -161,7 +161,7 @@ mod result { | |||
161 | } | 161 | } |
162 | 162 | ||
163 | #[test] | 163 | #[test] |
164 | fn infer_tryv2() { | 164 | fn infer_try_trait_v2() { |
165 | check_types( | 165 | check_types( |
166 | r#" | 166 | r#" |
167 | //- /main.rs crate:main deps:core | 167 | //- /main.rs crate:main deps:core |
@@ -172,26 +172,41 @@ fn test() { | |||
172 | } //^ i32 | 172 | } //^ i32 |
173 | 173 | ||
174 | //- /core.rs crate:core | 174 | //- /core.rs crate:core |
175 | #[prelude_import] use ops::*; | ||
176 | mod ops { | 175 | mod ops { |
177 | trait Try { | 176 | mod try_trait { |
178 | type Output; | 177 | pub trait Try: FromResidual { |
179 | type Residual; | 178 | type Output; |
179 | type Residual; | ||
180 | } | ||
181 | pub trait FromResidual<R = <Self as Try>::Residual> {} | ||
180 | } | 182 | } |
183 | |||
184 | pub use self::try_trait::FromResidual; | ||
185 | pub use self::try_trait::Try; | ||
186 | } | ||
187 | |||
188 | mov convert { | ||
189 | pub trait From<T> {} | ||
190 | impl<T> From<T> for T {} | ||
181 | } | 191 | } |
182 | 192 | ||
183 | #[prelude_import] use result::*; | 193 | #[prelude_import] use result::*; |
184 | mod result { | 194 | mod result { |
185 | enum Infallible {} | 195 | use crate::convert::From; |
186 | enum Result<O, E> { | 196 | use crate::ops::{Try, FromResidual}; |
197 | |||
198 | pub enum Infallible {} | ||
199 | pub enum Result<O, E> { | ||
187 | Ok(O), | 200 | Ok(O), |
188 | Err(E) | 201 | Err(E) |
189 | } | 202 | } |
190 | 203 | ||
191 | impl<O, E> crate::ops::Try for Result<O, E> { | 204 | impl<O, E> Try for Result<O, E> { |
192 | type Output = O; | 205 | type Output = O; |
193 | type Error = Result<Infallible, E>; | 206 | type Error = Result<Infallible, E>; |
194 | } | 207 | } |
208 | |||
209 | impl<T, E, F: From<E>> FromResidual<Result<Infallible, E>> for Result<T, F> {} | ||
195 | } | 210 | } |
196 | "#, | 211 | "#, |
197 | ); | 212 | ); |
@@ -3660,3 +3675,52 @@ impl foo::Foo for u32 { | |||
3660 | "#]], | 3675 | "#]], |
3661 | ); | 3676 | ); |
3662 | } | 3677 | } |
3678 | |||
3679 | #[test] | ||
3680 | fn infer_async_ret_type() { | ||
3681 | check_types( | ||
3682 | r#" | ||
3683 | //- /main.rs crate:main deps:core | ||
3684 | |||
3685 | enum Result<T, E> { | ||
3686 | Ok(T), | ||
3687 | Err(E), | ||
3688 | } | ||
3689 | |||
3690 | use Result::*; | ||
3691 | |||
3692 | |||
3693 | struct Fooey; | ||
3694 | |||
3695 | impl Fooey { | ||
3696 | fn collect<B: Convert>(self) -> B { | ||
3697 | B::new() | ||
3698 | } | ||
3699 | } | ||
3700 | |||
3701 | trait Convert { | ||
3702 | fn new() -> Self; | ||
3703 | } | ||
3704 | impl Convert for u32 { | ||
3705 | fn new() -> Self { | ||
3706 | 0 | ||
3707 | } | ||
3708 | } | ||
3709 | |||
3710 | async fn get_accounts() -> Result<u32, ()> { | ||
3711 | let ret = Fooey.collect(); | ||
3712 | // ^ u32 | ||
3713 | Ok(ret) | ||
3714 | } | ||
3715 | |||
3716 | //- /core.rs crate:core | ||
3717 | #[prelude_import] use future::*; | ||
3718 | mod future { | ||
3719 | #[lang = "future_trait"] | ||
3720 | trait Future { | ||
3721 | type Output; | ||
3722 | } | ||
3723 | } | ||
3724 | "#, | ||
3725 | ); | ||
3726 | } | ||
diff --git a/crates/ide/src/annotations.rs b/crates/ide/src/annotations.rs index b0c4ed60a..8d68dce05 100644 --- a/crates/ide/src/annotations.rs +++ b/crates/ide/src/annotations.rs | |||
@@ -58,7 +58,7 @@ pub(crate) fn annotations( | |||
58 | } | 58 | } |
59 | 59 | ||
60 | let action = runnable.action(); | 60 | let action = runnable.action(); |
61 | let range = runnable.nav.full_range; | 61 | let range = runnable.nav.focus_or_full_range(); |
62 | 62 | ||
63 | if config.run { | 63 | if config.run { |
64 | annotations.push(Annotation { | 64 | annotations.push(Annotation { |
@@ -224,7 +224,7 @@ fn main() { | |||
224 | expect![[r#" | 224 | expect![[r#" |
225 | [ | 225 | [ |
226 | Annotation { | 226 | Annotation { |
227 | range: 50..85, | 227 | range: 53..57, |
228 | kind: Runnable { | 228 | kind: Runnable { |
229 | debug: false, | 229 | debug: false, |
230 | runnable: Runnable { | 230 | runnable: Runnable { |
@@ -243,7 +243,7 @@ fn main() { | |||
243 | }, | 243 | }, |
244 | }, | 244 | }, |
245 | Annotation { | 245 | Annotation { |
246 | range: 50..85, | 246 | range: 53..57, |
247 | kind: Runnable { | 247 | kind: Runnable { |
248 | debug: true, | 248 | debug: true, |
249 | runnable: Runnable { | 249 | runnable: Runnable { |
@@ -328,7 +328,7 @@ fn main() { | |||
328 | expect![[r#" | 328 | expect![[r#" |
329 | [ | 329 | [ |
330 | Annotation { | 330 | Annotation { |
331 | range: 14..48, | 331 | range: 17..21, |
332 | kind: Runnable { | 332 | kind: Runnable { |
333 | debug: false, | 333 | debug: false, |
334 | runnable: Runnable { | 334 | runnable: Runnable { |
@@ -347,7 +347,7 @@ fn main() { | |||
347 | }, | 347 | }, |
348 | }, | 348 | }, |
349 | Annotation { | 349 | Annotation { |
350 | range: 14..48, | 350 | range: 17..21, |
351 | kind: Runnable { | 351 | kind: Runnable { |
352 | debug: true, | 352 | debug: true, |
353 | runnable: Runnable { | 353 | runnable: Runnable { |
@@ -436,7 +436,7 @@ fn main() { | |||
436 | expect![[r#" | 436 | expect![[r#" |
437 | [ | 437 | [ |
438 | Annotation { | 438 | Annotation { |
439 | range: 66..100, | 439 | range: 69..73, |
440 | kind: Runnable { | 440 | kind: Runnable { |
441 | debug: false, | 441 | debug: false, |
442 | runnable: Runnable { | 442 | runnable: Runnable { |
@@ -455,7 +455,7 @@ fn main() { | |||
455 | }, | 455 | }, |
456 | }, | 456 | }, |
457 | Annotation { | 457 | Annotation { |
458 | range: 66..100, | 458 | range: 69..73, |
459 | kind: Runnable { | 459 | kind: Runnable { |
460 | debug: true, | 460 | debug: true, |
461 | runnable: Runnable { | 461 | runnable: Runnable { |
@@ -597,7 +597,7 @@ fn main() {} | |||
597 | expect![[r#" | 597 | expect![[r#" |
598 | [ | 598 | [ |
599 | Annotation { | 599 | Annotation { |
600 | range: 0..12, | 600 | range: 3..7, |
601 | kind: Runnable { | 601 | kind: Runnable { |
602 | debug: false, | 602 | debug: false, |
603 | runnable: Runnable { | 603 | runnable: Runnable { |
@@ -616,7 +616,7 @@ fn main() {} | |||
616 | }, | 616 | }, |
617 | }, | 617 | }, |
618 | Annotation { | 618 | Annotation { |
619 | range: 0..12, | 619 | range: 3..7, |
620 | kind: Runnable { | 620 | kind: Runnable { |
621 | debug: true, | 621 | debug: true, |
622 | runnable: Runnable { | 622 | runnable: Runnable { |
@@ -670,7 +670,7 @@ fn main() { | |||
670 | expect![[r#" | 670 | expect![[r#" |
671 | [ | 671 | [ |
672 | Annotation { | 672 | Annotation { |
673 | range: 58..95, | 673 | range: 61..65, |
674 | kind: Runnable { | 674 | kind: Runnable { |
675 | debug: false, | 675 | debug: false, |
676 | runnable: Runnable { | 676 | runnable: Runnable { |
@@ -689,7 +689,7 @@ fn main() { | |||
689 | }, | 689 | }, |
690 | }, | 690 | }, |
691 | Annotation { | 691 | Annotation { |
692 | range: 58..95, | 692 | range: 61..65, |
693 | kind: Runnable { | 693 | kind: Runnable { |
694 | debug: true, | 694 | debug: true, |
695 | runnable: Runnable { | 695 | runnable: Runnable { |
@@ -812,7 +812,7 @@ mod tests { | |||
812 | expect![[r#" | 812 | expect![[r#" |
813 | [ | 813 | [ |
814 | Annotation { | 814 | Annotation { |
815 | range: 0..12, | 815 | range: 3..7, |
816 | kind: Runnable { | 816 | kind: Runnable { |
817 | debug: false, | 817 | debug: false, |
818 | runnable: Runnable { | 818 | runnable: Runnable { |
@@ -831,7 +831,7 @@ mod tests { | |||
831 | }, | 831 | }, |
832 | }, | 832 | }, |
833 | Annotation { | 833 | Annotation { |
834 | range: 0..12, | 834 | range: 3..7, |
835 | kind: Runnable { | 835 | kind: Runnable { |
836 | debug: true, | 836 | debug: true, |
837 | runnable: Runnable { | 837 | runnable: Runnable { |
@@ -850,7 +850,7 @@ mod tests { | |||
850 | }, | 850 | }, |
851 | }, | 851 | }, |
852 | Annotation { | 852 | Annotation { |
853 | range: 14..64, | 853 | range: 18..23, |
854 | kind: Runnable { | 854 | kind: Runnable { |
855 | debug: false, | 855 | debug: false, |
856 | runnable: Runnable { | 856 | runnable: Runnable { |
@@ -871,7 +871,7 @@ mod tests { | |||
871 | }, | 871 | }, |
872 | }, | 872 | }, |
873 | Annotation { | 873 | Annotation { |
874 | range: 14..64, | 874 | range: 18..23, |
875 | kind: Runnable { | 875 | kind: Runnable { |
876 | debug: true, | 876 | debug: true, |
877 | runnable: Runnable { | 877 | runnable: Runnable { |
@@ -892,7 +892,7 @@ mod tests { | |||
892 | }, | 892 | }, |
893 | }, | 893 | }, |
894 | Annotation { | 894 | Annotation { |
895 | range: 30..62, | 895 | range: 45..57, |
896 | kind: Runnable { | 896 | kind: Runnable { |
897 | debug: false, | 897 | debug: false, |
898 | runnable: Runnable { | 898 | runnable: Runnable { |
@@ -918,7 +918,7 @@ mod tests { | |||
918 | }, | 918 | }, |
919 | }, | 919 | }, |
920 | Annotation { | 920 | Annotation { |
921 | range: 30..62, | 921 | range: 45..57, |
922 | kind: Runnable { | 922 | kind: Runnable { |
923 | debug: true, | 923 | debug: true, |
924 | runnable: Runnable { | 924 | runnable: Runnable { |
diff --git a/crates/ide/src/fixture.rs b/crates/ide/src/fixture.rs index cc6641ba1..6780af617 100644 --- a/crates/ide/src/fixture.rs +++ b/crates/ide/src/fixture.rs | |||
@@ -1,7 +1,7 @@ | |||
1 | //! Utilities for creating `Analysis` instances for tests. | 1 | //! Utilities for creating `Analysis` instances for tests. |
2 | use ide_db::base_db::fixture::ChangeFixture; | 2 | use ide_db::base_db::fixture::ChangeFixture; |
3 | use syntax::{TextRange, TextSize}; | 3 | use syntax::{TextRange, TextSize}; |
4 | use test_utils::{extract_annotations, RangeOrOffset}; | 4 | use test_utils::extract_annotations; |
5 | 5 | ||
6 | use crate::{Analysis, AnalysisHost, FileId, FilePosition, FileRange}; | 6 | use crate::{Analysis, AnalysisHost, FileId, FilePosition, FileRange}; |
7 | 7 | ||
@@ -27,10 +27,7 @@ pub(crate) fn position(ra_fixture: &str) -> (Analysis, FilePosition) { | |||
27 | let change_fixture = ChangeFixture::parse(ra_fixture); | 27 | let change_fixture = ChangeFixture::parse(ra_fixture); |
28 | host.db.apply_change(change_fixture.change); | 28 | host.db.apply_change(change_fixture.change); |
29 | let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)"); | 29 | let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)"); |
30 | let offset = match range_or_offset { | 30 | let offset = range_or_offset.expect_offset(); |
31 | RangeOrOffset::Range(_) => panic!(), | ||
32 | RangeOrOffset::Offset(it) => it, | ||
33 | }; | ||
34 | (host.analysis(), FilePosition { file_id, offset }) | 31 | (host.analysis(), FilePosition { file_id, offset }) |
35 | } | 32 | } |
36 | 33 | ||
@@ -40,10 +37,7 @@ pub(crate) fn range(ra_fixture: &str) -> (Analysis, FileRange) { | |||
40 | let change_fixture = ChangeFixture::parse(ra_fixture); | 37 | let change_fixture = ChangeFixture::parse(ra_fixture); |
41 | host.db.apply_change(change_fixture.change); | 38 | host.db.apply_change(change_fixture.change); |
42 | let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)"); | 39 | let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)"); |
43 | let range = match range_or_offset { | 40 | let range = range_or_offset.expect_range(); |
44 | RangeOrOffset::Range(it) => it, | ||
45 | RangeOrOffset::Offset(_) => panic!(), | ||
46 | }; | ||
47 | (host.analysis(), FileRange { file_id, range }) | 41 | (host.analysis(), FileRange { file_id, range }) |
48 | } | 42 | } |
49 | 43 | ||
@@ -53,10 +47,7 @@ pub(crate) fn annotations(ra_fixture: &str) -> (Analysis, FilePosition, Vec<(Fil | |||
53 | let change_fixture = ChangeFixture::parse(ra_fixture); | 47 | let change_fixture = ChangeFixture::parse(ra_fixture); |
54 | host.db.apply_change(change_fixture.change); | 48 | host.db.apply_change(change_fixture.change); |
55 | let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)"); | 49 | let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)"); |
56 | let offset = match range_or_offset { | 50 | let offset = range_or_offset.expect_offset(); |
57 | RangeOrOffset::Range(_) => panic!(), | ||
58 | RangeOrOffset::Offset(it) => it, | ||
59 | }; | ||
60 | 51 | ||
61 | let annotations = change_fixture | 52 | let annotations = change_fixture |
62 | .files | 53 | .files |
diff --git a/crates/ide/src/folding_ranges.rs b/crates/ide/src/folding_ranges.rs index b893c1c54..c5015a345 100755 --- a/crates/ide/src/folding_ranges.rs +++ b/crates/ide/src/folding_ranges.rs | |||
@@ -19,6 +19,7 @@ pub enum FoldKind { | |||
19 | Statics, | 19 | Statics, |
20 | Array, | 20 | Array, |
21 | WhereClause, | 21 | WhereClause, |
22 | ReturnType, | ||
22 | } | 23 | } |
23 | 24 | ||
24 | #[derive(Debug)] | 25 | #[derive(Debug)] |
@@ -131,6 +132,7 @@ fn fold_kind(kind: SyntaxKind) -> Option<FoldKind> { | |||
131 | COMMENT => Some(FoldKind::Comment), | 132 | COMMENT => Some(FoldKind::Comment), |
132 | ARG_LIST | PARAM_LIST => Some(FoldKind::ArgList), | 133 | ARG_LIST | PARAM_LIST => Some(FoldKind::ArgList), |
133 | ARRAY_EXPR => Some(FoldKind::Array), | 134 | ARRAY_EXPR => Some(FoldKind::Array), |
135 | RET_TYPE => Some(FoldKind::ReturnType), | ||
134 | ASSOC_ITEM_LIST | 136 | ASSOC_ITEM_LIST |
135 | | RECORD_FIELD_LIST | 137 | | RECORD_FIELD_LIST |
136 | | RECORD_PAT_FIELD_LIST | 138 | | RECORD_PAT_FIELD_LIST |
@@ -300,6 +302,7 @@ mod tests { | |||
300 | FoldKind::Statics => "statics", | 302 | FoldKind::Statics => "statics", |
301 | FoldKind::Array => "array", | 303 | FoldKind::Array => "array", |
302 | FoldKind::WhereClause => "whereclause", | 304 | FoldKind::WhereClause => "whereclause", |
305 | FoldKind::ReturnType => "returntype", | ||
303 | }; | 306 | }; |
304 | assert_eq!(kind, &attr.unwrap()); | 307 | assert_eq!(kind, &attr.unwrap()); |
305 | } | 308 | } |
@@ -560,4 +563,18 @@ where | |||
560 | "#, | 563 | "#, |
561 | ) | 564 | ) |
562 | } | 565 | } |
566 | |||
567 | #[test] | ||
568 | fn fold_return_type() { | ||
569 | check( | ||
570 | r#" | ||
571 | fn foo()<fold returntype>-> ( | ||
572 | bool, | ||
573 | bool, | ||
574 | )</fold> { (true, true) } | ||
575 | |||
576 | fn bar() -> (bool, bool) { (true, true) } | ||
577 | "#, | ||
578 | ) | ||
579 | } | ||
563 | } | 580 | } |
diff --git a/crates/ide_completion/Cargo.toml b/crates/ide_completion/Cargo.toml index 6bd8a5500..ba81c9e04 100644 --- a/crates/ide_completion/Cargo.toml +++ b/crates/ide_completion/Cargo.toml | |||
@@ -15,6 +15,7 @@ itertools = "0.10.0" | |||
15 | log = "0.4.8" | 15 | log = "0.4.8" |
16 | rustc-hash = "1.1.0" | 16 | rustc-hash = "1.1.0" |
17 | either = "1.6.1" | 17 | either = "1.6.1" |
18 | once_cell = "1.7" | ||
18 | 19 | ||
19 | stdx = { path = "../stdx", version = "0.0.0" } | 20 | stdx = { path = "../stdx", version = "0.0.0" } |
20 | syntax = { path = "../syntax", version = "0.0.0" } | 21 | syntax = { path = "../syntax", version = "0.0.0" } |
diff --git a/crates/ide_completion/src/completions/attribute.rs b/crates/ide_completion/src/completions/attribute.rs index b1505c74b..76d926157 100644 --- a/crates/ide_completion/src/completions/attribute.rs +++ b/crates/ide_completion/src/completions/attribute.rs | |||
@@ -3,9 +3,9 @@ | |||
3 | //! This module uses a bit of static metadata to provide completions | 3 | //! This module uses a bit of static metadata to provide completions |
4 | //! for built-in attributes. | 4 | //! for built-in attributes. |
5 | 5 | ||
6 | use itertools::Itertools; | 6 | use once_cell::sync::Lazy; |
7 | use rustc_hash::FxHashSet; | 7 | use rustc_hash::{FxHashMap, FxHashSet}; |
8 | use syntax::{ast, AstNode, T}; | 8 | use syntax::{ast, AstNode, NodeOrToken, SyntaxKind, T}; |
9 | 9 | ||
10 | use crate::{ | 10 | use crate::{ |
11 | context::CompletionContext, | 11 | context::CompletionContext, |
@@ -14,33 +14,40 @@ use crate::{ | |||
14 | Completions, | 14 | Completions, |
15 | }; | 15 | }; |
16 | 16 | ||
17 | pub(crate) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> { | 17 | mod derive; |
18 | if ctx.mod_declaration_under_caret.is_some() { | 18 | mod lint; |
19 | return None; | 19 | pub(crate) use self::lint::LintCompletion; |
20 | } | ||
21 | 20 | ||
21 | pub(crate) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> { | ||
22 | let attribute = ctx.attribute_under_caret.as_ref()?; | 22 | let attribute = ctx.attribute_under_caret.as_ref()?; |
23 | match (attribute.path(), attribute.token_tree()) { | 23 | match (attribute.path().and_then(|p| p.as_single_name_ref()), attribute.token_tree()) { |
24 | (Some(path), Some(token_tree)) => { | 24 | (Some(path), Some(token_tree)) => match path.text().as_str() { |
25 | let path = path.syntax().text(); | 25 | "derive" => derive::complete_derive(acc, ctx, token_tree), |
26 | if path == "derive" { | 26 | "feature" => lint::complete_lint(acc, ctx, token_tree, FEATURES), |
27 | complete_derive(acc, ctx, token_tree) | 27 | "allow" | "warn" | "deny" | "forbid" => { |
28 | } else if path == "feature" { | 28 | lint::complete_lint(acc, ctx, token_tree.clone(), lint::DEFAULT_LINT_COMPLETIONS); |
29 | complete_lint(acc, ctx, token_tree, FEATURES) | 29 | lint::complete_lint(acc, ctx, token_tree, CLIPPY_LINTS); |
30 | } else if path == "allow" || path == "warn" || path == "deny" || path == "forbid" { | ||
31 | complete_lint(acc, ctx, token_tree.clone(), DEFAULT_LINT_COMPLETIONS); | ||
32 | complete_lint(acc, ctx, token_tree, CLIPPY_LINTS); | ||
33 | } | 30 | } |
34 | } | 31 | _ => (), |
35 | (_, Some(_token_tree)) => {} | 32 | }, |
36 | _ => complete_attribute_start(acc, ctx, attribute), | 33 | (None, Some(_)) => (), |
34 | _ => complete_new_attribute(acc, ctx, attribute), | ||
37 | } | 35 | } |
38 | Some(()) | 36 | Some(()) |
39 | } | 37 | } |
40 | 38 | ||
41 | fn complete_attribute_start(acc: &mut Completions, ctx: &CompletionContext, attribute: &ast::Attr) { | 39 | fn complete_new_attribute(acc: &mut Completions, ctx: &CompletionContext, attribute: &ast::Attr) { |
40 | let attribute_annotated_item_kind = attribute.syntax().parent().map(|it| it.kind()); | ||
41 | let attributes = attribute_annotated_item_kind.and_then(|kind| { | ||
42 | if ast::Expr::can_cast(kind) { | ||
43 | Some(EXPR_ATTRIBUTES) | ||
44 | } else { | ||
45 | KIND_TO_ATTRIBUTES.get(&kind).copied() | ||
46 | } | ||
47 | }); | ||
42 | let is_inner = attribute.kind() == ast::AttrKind::Inner; | 48 | let is_inner = attribute.kind() == ast::AttrKind::Inner; |
43 | for attr_completion in ATTRIBUTES.iter().filter(|compl| is_inner || !compl.prefer_inner) { | 49 | |
50 | let add_completion = |attr_completion: &AttrCompletion| { | ||
44 | let mut item = CompletionItem::new( | 51 | let mut item = CompletionItem::new( |
45 | CompletionKind::Attribute, | 52 | CompletionKind::Attribute, |
46 | ctx.source_range(), | 53 | ctx.source_range(), |
@@ -56,9 +63,19 @@ fn complete_attribute_start(acc: &mut Completions, ctx: &CompletionContext, attr | |||
56 | item.insert_snippet(cap, snippet); | 63 | item.insert_snippet(cap, snippet); |
57 | } | 64 | } |
58 | 65 | ||
59 | if attribute.kind() == ast::AttrKind::Inner || !attr_completion.prefer_inner { | 66 | if is_inner || !attr_completion.prefer_inner { |
60 | acc.add(item.build()); | 67 | acc.add(item.build()); |
61 | } | 68 | } |
69 | }; | ||
70 | |||
71 | match attributes { | ||
72 | Some(applicable) => applicable | ||
73 | .iter() | ||
74 | .flat_map(|name| ATTRIBUTES.binary_search_by(|attr| attr.key().cmp(name)).ok()) | ||
75 | .flat_map(|idx| ATTRIBUTES.get(idx)) | ||
76 | .for_each(add_completion), | ||
77 | None if is_inner => ATTRIBUTES.iter().for_each(add_completion), | ||
78 | None => ATTRIBUTES.iter().filter(|compl| !compl.prefer_inner).for_each(add_completion), | ||
62 | } | 79 | } |
63 | } | 80 | } |
64 | 81 | ||
@@ -70,6 +87,10 @@ struct AttrCompletion { | |||
70 | } | 87 | } |
71 | 88 | ||
72 | impl AttrCompletion { | 89 | impl AttrCompletion { |
90 | fn key(&self) -> &'static str { | ||
91 | self.lookup.unwrap_or(self.label) | ||
92 | } | ||
93 | |||
73 | const fn prefer_inner(self) -> AttrCompletion { | 94 | const fn prefer_inner(self) -> AttrCompletion { |
74 | AttrCompletion { prefer_inner: true, ..self } | 95 | AttrCompletion { prefer_inner: true, ..self } |
75 | } | 96 | } |
@@ -83,30 +104,122 @@ const fn attr( | |||
83 | AttrCompletion { label, lookup, snippet, prefer_inner: false } | 104 | AttrCompletion { label, lookup, snippet, prefer_inner: false } |
84 | } | 105 | } |
85 | 106 | ||
107 | macro_rules! attrs { | ||
108 | // attributes applicable to all items | ||
109 | [@ { item $($tt:tt)* } {$($acc:tt)*}] => { | ||
110 | attrs!(@ { $($tt)* } { $($acc)*, "deprecated", "doc", "dochidden", "docalias", "must_use", "no_mangle" }) | ||
111 | }; | ||
112 | // attributes applicable to all adts | ||
113 | [@ { adt $($tt:tt)* } {$($acc:tt)*}] => { | ||
114 | attrs!(@ { $($tt)* } { $($acc)*, "derive", "repr" }) | ||
115 | }; | ||
116 | // attributes applicable to all linkable things aka functions/statics | ||
117 | [@ { linkable $($tt:tt)* } {$($acc:tt)*}] => { | ||
118 | attrs!(@ { $($tt)* } { $($acc)*, "export_name", "link_name", "link_section" }) | ||
119 | }; | ||
120 | // error fallback for nicer error message | ||
121 | [@ { $ty:ident $($tt:tt)* } {$($acc:tt)*}] => { | ||
122 | compile_error!(concat!("unknown attr subtype ", stringify!($ty))) | ||
123 | }; | ||
124 | // general push down accumulation | ||
125 | [@ { $lit:literal $($tt:tt)*} {$($acc:tt)*}] => { | ||
126 | attrs!(@ { $($tt)* } { $($acc)*, $lit }) | ||
127 | }; | ||
128 | [@ {$($tt:tt)+} {$($tt2:tt)*}] => { | ||
129 | compile_error!(concat!("Unexpected input ", stringify!($($tt)+))) | ||
130 | }; | ||
131 | // final output construction | ||
132 | [@ {} {$($tt:tt)*}] => { &[$($tt)*] as _ }; | ||
133 | // starting matcher | ||
134 | [$($tt:tt),*] => { | ||
135 | attrs!(@ { $($tt)* } { "allow", "cfg", "cfg_attr", "deny", "forbid", "warn" }) | ||
136 | }; | ||
137 | } | ||
138 | |||
139 | #[rustfmt::skip] | ||
140 | static KIND_TO_ATTRIBUTES: Lazy<FxHashMap<SyntaxKind, &[&str]>> = Lazy::new(|| { | ||
141 | use SyntaxKind::*; | ||
142 | std::array::IntoIter::new([ | ||
143 | ( | ||
144 | SOURCE_FILE, | ||
145 | attrs!( | ||
146 | item, | ||
147 | "crate_name", "feature", "no_implicit_prelude", "no_main", "no_std", | ||
148 | "recursion_limit", "type_length_limit", "windows_subsystem" | ||
149 | ), | ||
150 | ), | ||
151 | (MODULE, attrs!(item, "no_implicit_prelude", "path")), | ||
152 | (ITEM_LIST, attrs!(item, "no_implicit_prelude")), | ||
153 | (MACRO_RULES, attrs!(item, "macro_export", "macro_use")), | ||
154 | (MACRO_DEF, attrs!(item)), | ||
155 | (EXTERN_CRATE, attrs!(item, "macro_use", "no_link")), | ||
156 | (USE, attrs!(item)), | ||
157 | (TYPE_ALIAS, attrs!(item)), | ||
158 | (STRUCT, attrs!(item, adt, "non_exhaustive")), | ||
159 | (ENUM, attrs!(item, adt, "non_exhaustive")), | ||
160 | (UNION, attrs!(item, adt)), | ||
161 | (CONST, attrs!(item)), | ||
162 | ( | ||
163 | FN, | ||
164 | attrs!( | ||
165 | item, linkable, | ||
166 | "cold", "ignore", "inline", "must_use", "panic_handler", "proc_macro", | ||
167 | "proc_macro_derive", "proc_macro_attribute", "should_panic", "target_feature", | ||
168 | "test", "track_caller" | ||
169 | ), | ||
170 | ), | ||
171 | (STATIC, attrs!(item, linkable, "global_allocator", "used")), | ||
172 | (TRAIT, attrs!(item, "must_use")), | ||
173 | (IMPL, attrs!(item, "automatically_derived")), | ||
174 | (ASSOC_ITEM_LIST, attrs!(item)), | ||
175 | (EXTERN_BLOCK, attrs!(item, "link")), | ||
176 | (EXTERN_ITEM_LIST, attrs!(item, "link")), | ||
177 | (MACRO_CALL, attrs!()), | ||
178 | (SELF_PARAM, attrs!()), | ||
179 | (PARAM, attrs!()), | ||
180 | (RECORD_FIELD, attrs!()), | ||
181 | (VARIANT, attrs!("non_exhaustive")), | ||
182 | (TYPE_PARAM, attrs!()), | ||
183 | (CONST_PARAM, attrs!()), | ||
184 | (LIFETIME_PARAM, attrs!()), | ||
185 | (LET_STMT, attrs!()), | ||
186 | (EXPR_STMT, attrs!()), | ||
187 | (LITERAL, attrs!()), | ||
188 | (RECORD_EXPR_FIELD_LIST, attrs!()), | ||
189 | (RECORD_EXPR_FIELD, attrs!()), | ||
190 | (MATCH_ARM_LIST, attrs!()), | ||
191 | (MATCH_ARM, attrs!()), | ||
192 | (IDENT_PAT, attrs!()), | ||
193 | (RECORD_PAT_FIELD, attrs!()), | ||
194 | ]) | ||
195 | .collect() | ||
196 | }); | ||
197 | const EXPR_ATTRIBUTES: &[&str] = attrs!(); | ||
198 | |||
86 | /// https://doc.rust-lang.org/reference/attributes.html#built-in-attributes-index | 199 | /// https://doc.rust-lang.org/reference/attributes.html#built-in-attributes-index |
200 | // Keep these sorted for the binary search! | ||
87 | const ATTRIBUTES: &[AttrCompletion] = &[ | 201 | const ATTRIBUTES: &[AttrCompletion] = &[ |
88 | attr("allow(…)", Some("allow"), Some("allow(${0:lint})")), | 202 | attr("allow(…)", Some("allow"), Some("allow(${0:lint})")), |
89 | attr("automatically_derived", None, None), | 203 | attr("automatically_derived", None, None), |
90 | attr("cfg_attr(…)", Some("cfg_attr"), Some("cfg_attr(${1:predicate}, ${0:attr})")), | ||
91 | attr("cfg(…)", Some("cfg"), Some("cfg(${0:predicate})")), | 204 | attr("cfg(…)", Some("cfg"), Some("cfg(${0:predicate})")), |
205 | attr("cfg_attr(…)", Some("cfg_attr"), Some("cfg_attr(${1:predicate}, ${0:attr})")), | ||
92 | attr("cold", None, None), | 206 | attr("cold", None, None), |
93 | attr(r#"crate_name = """#, Some("crate_name"), Some(r#"crate_name = "${0:crate_name}""#)) | 207 | attr(r#"crate_name = """#, Some("crate_name"), Some(r#"crate_name = "${0:crate_name}""#)) |
94 | .prefer_inner(), | 208 | .prefer_inner(), |
95 | attr("deny(…)", Some("deny"), Some("deny(${0:lint})")), | 209 | attr("deny(…)", Some("deny"), Some("deny(${0:lint})")), |
96 | attr(r#"deprecated"#, Some("deprecated"), Some(r#"deprecated"#)), | 210 | attr(r#"deprecated"#, Some("deprecated"), Some(r#"deprecated"#)), |
97 | attr("derive(…)", Some("derive"), Some(r#"derive(${0:Debug})"#)), | 211 | attr("derive(…)", Some("derive"), Some(r#"derive(${0:Debug})"#)), |
212 | attr(r#"doc = "…""#, Some("doc"), Some(r#"doc = "${0:docs}""#)), | ||
213 | attr(r#"doc(alias = "…")"#, Some("docalias"), Some(r#"doc(alias = "${0:docs}")"#)), | ||
214 | attr(r#"doc(hidden)"#, Some("dochidden"), Some(r#"doc(hidden)"#)), | ||
98 | attr( | 215 | attr( |
99 | r#"export_name = "…""#, | 216 | r#"export_name = "…""#, |
100 | Some("export_name"), | 217 | Some("export_name"), |
101 | Some(r#"export_name = "${0:exported_symbol_name}""#), | 218 | Some(r#"export_name = "${0:exported_symbol_name}""#), |
102 | ), | 219 | ), |
103 | attr(r#"doc(alias = "…")"#, Some("docalias"), Some(r#"doc(alias = "${0:docs}")"#)), | ||
104 | attr(r#"doc = "…""#, Some("doc"), Some(r#"doc = "${0:docs}""#)), | ||
105 | attr(r#"doc(hidden)"#, Some("dochidden"), Some(r#"doc(hidden)"#)), | ||
106 | attr("feature(…)", Some("feature"), Some("feature(${0:flag})")).prefer_inner(), | 220 | attr("feature(…)", Some("feature"), Some("feature(${0:flag})")).prefer_inner(), |
107 | attr("forbid(…)", Some("forbid"), Some("forbid(${0:lint})")), | 221 | attr("forbid(…)", Some("forbid"), Some("forbid(${0:lint})")), |
108 | // FIXME: resolve through macro resolution? | 222 | attr("global_allocator", None, None), |
109 | attr("global_allocator", None, None).prefer_inner(), | ||
110 | attr(r#"ignore = "…""#, Some("ignore"), Some(r#"ignore = "${0:reason}""#)), | 223 | attr(r#"ignore = "…""#, Some("ignore"), Some(r#"ignore = "${0:reason}""#)), |
111 | attr("inline", Some("inline"), Some("inline")), | 224 | attr("inline", Some("inline"), Some("inline")), |
112 | attr("link", None, None), | 225 | attr("link", None, None), |
@@ -119,13 +232,13 @@ const ATTRIBUTES: &[AttrCompletion] = &[ | |||
119 | attr("macro_export", None, None), | 232 | attr("macro_export", None, None), |
120 | attr("macro_use", None, None), | 233 | attr("macro_use", None, None), |
121 | attr(r#"must_use"#, Some("must_use"), Some(r#"must_use"#)), | 234 | attr(r#"must_use"#, Some("must_use"), Some(r#"must_use"#)), |
122 | attr("no_link", None, None).prefer_inner(), | ||
123 | attr("no_implicit_prelude", None, None).prefer_inner(), | 235 | attr("no_implicit_prelude", None, None).prefer_inner(), |
236 | attr("no_link", None, None).prefer_inner(), | ||
124 | attr("no_main", None, None).prefer_inner(), | 237 | attr("no_main", None, None).prefer_inner(), |
125 | attr("no_mangle", None, None), | 238 | attr("no_mangle", None, None), |
126 | attr("no_std", None, None).prefer_inner(), | 239 | attr("no_std", None, None).prefer_inner(), |
127 | attr("non_exhaustive", None, None), | 240 | attr("non_exhaustive", None, None), |
128 | attr("panic_handler", None, None).prefer_inner(), | 241 | attr("panic_handler", None, None), |
129 | attr(r#"path = "…""#, Some("path"), Some(r#"path ="${0:path}""#)), | 242 | attr(r#"path = "…""#, Some("path"), Some(r#"path ="${0:path}""#)), |
130 | attr("proc_macro", None, None), | 243 | attr("proc_macro", None, None), |
131 | attr("proc_macro_attribute", None, None), | 244 | attr("proc_macro_attribute", None, None), |
@@ -153,412 +266,518 @@ const ATTRIBUTES: &[AttrCompletion] = &[ | |||
153 | .prefer_inner(), | 266 | .prefer_inner(), |
154 | ]; | 267 | ]; |
155 | 268 | ||
156 | fn complete_derive(acc: &mut Completions, ctx: &CompletionContext, derive_input: ast::TokenTree) { | 269 | fn parse_comma_sep_input(derive_input: ast::TokenTree) -> Option<FxHashSet<String>> { |
157 | if let Ok(existing_derives) = parse_comma_sep_input(derive_input) { | 270 | let (l_paren, r_paren) = derive_input.l_paren_token().zip(derive_input.r_paren_token())?; |
158 | for derive_completion in DEFAULT_DERIVE_COMPLETIONS | 271 | let mut input_derives = FxHashSet::default(); |
159 | .iter() | 272 | let mut tokens = derive_input |
160 | .filter(|completion| !existing_derives.contains(completion.label)) | 273 | .syntax() |
161 | { | 274 | .children_with_tokens() |
162 | let mut components = vec![derive_completion.label]; | 275 | .filter_map(NodeOrToken::into_token) |
163 | components.extend( | 276 | .skip_while(|token| token != &l_paren) |
164 | derive_completion | 277 | .skip(1) |
165 | .dependencies | 278 | .take_while(|token| token != &r_paren) |
166 | .iter() | 279 | .peekable(); |
167 | .filter(|&&dependency| !existing_derives.contains(dependency)), | 280 | let mut input = String::new(); |
168 | ); | 281 | while tokens.peek().is_some() { |
169 | let lookup = components.join(", "); | 282 | for token in tokens.by_ref().take_while(|t| t.kind() != T![,]) { |
170 | let label = components.iter().rev().join(", "); | 283 | input.push_str(token.text()); |
171 | let mut item = | ||
172 | CompletionItem::new(CompletionKind::Attribute, ctx.source_range(), label); | ||
173 | item.lookup_by(lookup).kind(CompletionItemKind::Attribute); | ||
174 | item.add_to(acc); | ||
175 | } | ||
176 | |||
177 | for custom_derive_name in get_derive_names_in_scope(ctx).difference(&existing_derives) { | ||
178 | let mut item = CompletionItem::new( | ||
179 | CompletionKind::Attribute, | ||
180 | ctx.source_range(), | ||
181 | custom_derive_name, | ||
182 | ); | ||
183 | item.kind(CompletionItemKind::Attribute); | ||
184 | item.add_to(acc); | ||
185 | } | 284 | } |
186 | } | ||
187 | } | ||
188 | 285 | ||
189 | fn complete_lint( | 286 | if !input.is_empty() { |
190 | acc: &mut Completions, | 287 | input_derives.insert(input.trim().to_owned()); |
191 | ctx: &CompletionContext, | ||
192 | derive_input: ast::TokenTree, | ||
193 | lints_completions: &[LintCompletion], | ||
194 | ) { | ||
195 | if let Ok(existing_lints) = parse_comma_sep_input(derive_input) { | ||
196 | for lint_completion in lints_completions | ||
197 | .into_iter() | ||
198 | .filter(|completion| !existing_lints.contains(completion.label)) | ||
199 | { | ||
200 | let mut item = CompletionItem::new( | ||
201 | CompletionKind::Attribute, | ||
202 | ctx.source_range(), | ||
203 | lint_completion.label, | ||
204 | ); | ||
205 | item.kind(CompletionItemKind::Attribute).detail(lint_completion.description); | ||
206 | item.add_to(acc) | ||
207 | } | 288 | } |
208 | } | ||
209 | } | ||
210 | 289 | ||
211 | fn parse_comma_sep_input(derive_input: ast::TokenTree) -> Result<FxHashSet<String>, ()> { | 290 | input.clear(); |
212 | match (derive_input.left_delimiter_token(), derive_input.right_delimiter_token()) { | ||
213 | (Some(left_paren), Some(right_paren)) | ||
214 | if left_paren.kind() == T!['('] && right_paren.kind() == T![')'] => | ||
215 | { | ||
216 | let mut input_derives = FxHashSet::default(); | ||
217 | let mut current_derive = String::new(); | ||
218 | for token in derive_input | ||
219 | .syntax() | ||
220 | .children_with_tokens() | ||
221 | .filter_map(|token| token.into_token()) | ||
222 | .skip_while(|token| token != &left_paren) | ||
223 | .skip(1) | ||
224 | .take_while(|token| token != &right_paren) | ||
225 | { | ||
226 | if T![,] == token.kind() { | ||
227 | if !current_derive.is_empty() { | ||
228 | input_derives.insert(current_derive); | ||
229 | current_derive = String::new(); | ||
230 | } | ||
231 | } else { | ||
232 | current_derive.push_str(token.text().trim()); | ||
233 | } | ||
234 | } | ||
235 | |||
236 | if !current_derive.is_empty() { | ||
237 | input_derives.insert(current_derive); | ||
238 | } | ||
239 | Ok(input_derives) | ||
240 | } | ||
241 | _ => Err(()), | ||
242 | } | 291 | } |
243 | } | ||
244 | |||
245 | fn get_derive_names_in_scope(ctx: &CompletionContext) -> FxHashSet<String> { | ||
246 | let mut result = FxHashSet::default(); | ||
247 | ctx.scope.process_all_names(&mut |name, scope_def| { | ||
248 | if let hir::ScopeDef::MacroDef(mac) = scope_def { | ||
249 | // FIXME kind() doesn't check whether proc-macro is a derive | ||
250 | if mac.kind() == hir::MacroKind::Derive || mac.kind() == hir::MacroKind::ProcMacro { | ||
251 | result.insert(name.to_string()); | ||
252 | } | ||
253 | } | ||
254 | }); | ||
255 | result | ||
256 | } | ||
257 | |||
258 | struct DeriveCompletion { | ||
259 | label: &'static str, | ||
260 | dependencies: &'static [&'static str], | ||
261 | } | ||
262 | |||
263 | /// Standard Rust derives and the information about their dependencies | ||
264 | /// (the dependencies are needed so that the main derive don't break the compilation when added) | ||
265 | const DEFAULT_DERIVE_COMPLETIONS: &[DeriveCompletion] = &[ | ||
266 | DeriveCompletion { label: "Clone", dependencies: &[] }, | ||
267 | DeriveCompletion { label: "Copy", dependencies: &["Clone"] }, | ||
268 | DeriveCompletion { label: "Debug", dependencies: &[] }, | ||
269 | DeriveCompletion { label: "Default", dependencies: &[] }, | ||
270 | DeriveCompletion { label: "Hash", dependencies: &[] }, | ||
271 | DeriveCompletion { label: "PartialEq", dependencies: &[] }, | ||
272 | DeriveCompletion { label: "Eq", dependencies: &["PartialEq"] }, | ||
273 | DeriveCompletion { label: "PartialOrd", dependencies: &["PartialEq"] }, | ||
274 | DeriveCompletion { label: "Ord", dependencies: &["PartialOrd", "Eq", "PartialEq"] }, | ||
275 | ]; | ||
276 | 292 | ||
277 | pub(crate) struct LintCompletion { | 293 | Some(input_derives) |
278 | pub(crate) label: &'static str, | ||
279 | pub(crate) description: &'static str, | ||
280 | } | 294 | } |
281 | 295 | ||
282 | #[rustfmt::skip] | ||
283 | const DEFAULT_LINT_COMPLETIONS: &[LintCompletion] = &[ | ||
284 | LintCompletion { label: "absolute_paths_not_starting_with_crate", description: r#"fully qualified paths that start with a module name instead of `crate`, `self`, or an extern crate name"# }, | ||
285 | LintCompletion { label: "anonymous_parameters", description: r#"detects anonymous parameters"# }, | ||
286 | LintCompletion { label: "box_pointers", description: r#"use of owned (Box type) heap memory"# }, | ||
287 | LintCompletion { label: "deprecated_in_future", description: r#"detects use of items that will be deprecated in a future version"# }, | ||
288 | LintCompletion { label: "elided_lifetimes_in_paths", description: r#"hidden lifetime parameters in types are deprecated"# }, | ||
289 | LintCompletion { label: "explicit_outlives_requirements", description: r#"outlives requirements can be inferred"# }, | ||
290 | LintCompletion { label: "indirect_structural_match", description: r#"pattern with const indirectly referencing non-structural-match type"# }, | ||
291 | LintCompletion { label: "keyword_idents", description: r#"detects edition keywords being used as an identifier"# }, | ||
292 | LintCompletion { label: "macro_use_extern_crate", description: r#"the `#[macro_use]` attribute is now deprecated in favor of using macros via the module system"# }, | ||
293 | LintCompletion { label: "meta_variable_misuse", description: r#"possible meta-variable misuse at macro definition"# }, | ||
294 | LintCompletion { label: "missing_copy_implementations", description: r#"detects potentially-forgotten implementations of `Copy`"# }, | ||
295 | LintCompletion { label: "missing_crate_level_docs", description: r#"detects crates with no crate-level documentation"# }, | ||
296 | LintCompletion { label: "missing_debug_implementations", description: r#"detects missing implementations of Debug"# }, | ||
297 | LintCompletion { label: "missing_docs", description: r#"detects missing documentation for public members"# }, | ||
298 | LintCompletion { label: "missing_doc_code_examples", description: r#"detects publicly-exported items without code samples in their documentation"# }, | ||
299 | LintCompletion { label: "non_ascii_idents", description: r#"detects non-ASCII identifiers"# }, | ||
300 | LintCompletion { label: "private_doc_tests", description: r#"detects code samples in docs of private items not documented by rustdoc"# }, | ||
301 | LintCompletion { label: "single_use_lifetimes", description: r#"detects lifetime parameters that are only used once"# }, | ||
302 | LintCompletion { label: "trivial_casts", description: r#"detects trivial casts which could be removed"# }, | ||
303 | LintCompletion { label: "trivial_numeric_casts", description: r#"detects trivial casts of numeric types which could be removed"# }, | ||
304 | LintCompletion { label: "unaligned_references", description: r#"detects unaligned references to fields of packed structs"# }, | ||
305 | LintCompletion { label: "unreachable_pub", description: r#"`pub` items not reachable from crate root"# }, | ||
306 | LintCompletion { label: "unsafe_code", description: r#"usage of `unsafe` code"# }, | ||
307 | LintCompletion { label: "unsafe_op_in_unsafe_fn", description: r#"unsafe operations in unsafe functions without an explicit unsafe block are deprecated"# }, | ||
308 | LintCompletion { label: "unstable_features", description: r#"enabling unstable features (deprecated. do not use)"# }, | ||
309 | LintCompletion { label: "unused_crate_dependencies", description: r#"crate dependencies that are never used"# }, | ||
310 | LintCompletion { label: "unused_extern_crates", description: r#"extern crates that are never used"# }, | ||
311 | LintCompletion { label: "unused_import_braces", description: r#"unnecessary braces around an imported item"# }, | ||
312 | LintCompletion { label: "unused_lifetimes", description: r#"detects lifetime parameters that are never used"# }, | ||
313 | LintCompletion { label: "unused_qualifications", description: r#"detects unnecessarily qualified names"# }, | ||
314 | LintCompletion { label: "unused_results", description: r#"unused result of an expression in a statement"# }, | ||
315 | LintCompletion { label: "variant_size_differences", description: r#"detects enums with widely varying variant sizes"# }, | ||
316 | LintCompletion { label: "array_into_iter", description: r#"detects calling `into_iter` on arrays"# }, | ||
317 | LintCompletion { label: "asm_sub_register", description: r#"using only a subset of a register for inline asm inputs"# }, | ||
318 | LintCompletion { label: "bare_trait_objects", description: r#"suggest using `dyn Trait` for trait objects"# }, | ||
319 | LintCompletion { label: "bindings_with_variant_name", description: r#"detects pattern bindings with the same name as one of the matched variants"# }, | ||
320 | LintCompletion { label: "cenum_impl_drop_cast", description: r#"a C-like enum implementing Drop is cast"# }, | ||
321 | LintCompletion { label: "clashing_extern_declarations", description: r#"detects when an extern fn has been declared with the same name but different types"# }, | ||
322 | LintCompletion { label: "coherence_leak_check", description: r#"distinct impls distinguished only by the leak-check code"# }, | ||
323 | LintCompletion { label: "confusable_idents", description: r#"detects visually confusable pairs between identifiers"# }, | ||
324 | LintCompletion { label: "dead_code", description: r#"detect unused, unexported items"# }, | ||
325 | LintCompletion { label: "deprecated", description: r#"detects use of deprecated items"# }, | ||
326 | LintCompletion { label: "ellipsis_inclusive_range_patterns", description: r#"`...` range patterns are deprecated"# }, | ||
327 | LintCompletion { label: "exported_private_dependencies", description: r#"public interface leaks type from a private dependency"# }, | ||
328 | LintCompletion { label: "illegal_floating_point_literal_pattern", description: r#"floating-point literals cannot be used in patterns"# }, | ||
329 | LintCompletion { label: "improper_ctypes", description: r#"proper use of libc types in foreign modules"# }, | ||
330 | LintCompletion { label: "improper_ctypes_definitions", description: r#"proper use of libc types in foreign item definitions"# }, | ||
331 | LintCompletion { label: "incomplete_features", description: r#"incomplete features that may function improperly in some or all cases"# }, | ||
332 | LintCompletion { label: "inline_no_sanitize", description: r#"detects incompatible use of `#[inline(always)]` and `#[no_sanitize(...)]`"# }, | ||
333 | LintCompletion { label: "intra_doc_link_resolution_failure", description: r#"failures in resolving intra-doc link targets"# }, | ||
334 | LintCompletion { label: "invalid_codeblock_attributes", description: r#"codeblock attribute looks a lot like a known one"# }, | ||
335 | LintCompletion { label: "invalid_value", description: r#"an invalid value is being created (such as a NULL reference)"# }, | ||
336 | LintCompletion { label: "irrefutable_let_patterns", description: r#"detects irrefutable patterns in if-let and while-let statements"# }, | ||
337 | LintCompletion { label: "late_bound_lifetime_arguments", description: r#"detects generic lifetime arguments in path segments with late bound lifetime parameters"# }, | ||
338 | LintCompletion { label: "mixed_script_confusables", description: r#"detects Unicode scripts whose mixed script confusables codepoints are solely used"# }, | ||
339 | LintCompletion { label: "mutable_borrow_reservation_conflict", description: r#"reservation of a two-phased borrow conflicts with other shared borrows"# }, | ||
340 | LintCompletion { label: "non_camel_case_types", description: r#"types, variants, traits and type parameters should have camel case names"# }, | ||
341 | LintCompletion { label: "non_shorthand_field_patterns", description: r#"using `Struct { x: x }` instead of `Struct { x }` in a pattern"# }, | ||
342 | LintCompletion { label: "non_snake_case", description: r#"variables, methods, functions, lifetime parameters and modules should have snake case names"# }, | ||
343 | LintCompletion { label: "non_upper_case_globals", description: r#"static constants should have uppercase identifiers"# }, | ||
344 | LintCompletion { label: "no_mangle_generic_items", description: r#"generic items must be mangled"# }, | ||
345 | LintCompletion { label: "overlapping_patterns", description: r#"detects overlapping patterns"# }, | ||
346 | LintCompletion { label: "path_statements", description: r#"path statements with no effect"# }, | ||
347 | LintCompletion { label: "private_in_public", description: r#"detect private items in public interfaces not caught by the old implementation"# }, | ||
348 | LintCompletion { label: "proc_macro_derive_resolution_fallback", description: r#"detects proc macro derives using inaccessible names from parent modules"# }, | ||
349 | LintCompletion { label: "redundant_semicolons", description: r#"detects unnecessary trailing semicolons"# }, | ||
350 | LintCompletion { label: "renamed_and_removed_lints", description: r#"lints that have been renamed or removed"# }, | ||
351 | LintCompletion { label: "safe_packed_borrows", description: r#"safe borrows of fields of packed structs were erroneously allowed"# }, | ||
352 | LintCompletion { label: "stable_features", description: r#"stable features found in `#[feature]` directive"# }, | ||
353 | LintCompletion { label: "trivial_bounds", description: r#"these bounds don't depend on an type parameters"# }, | ||
354 | LintCompletion { label: "type_alias_bounds", description: r#"bounds in type aliases are not enforced"# }, | ||
355 | LintCompletion { label: "tyvar_behind_raw_pointer", description: r#"raw pointer to an inference variable"# }, | ||
356 | LintCompletion { label: "uncommon_codepoints", description: r#"detects uncommon Unicode codepoints in identifiers"# }, | ||
357 | LintCompletion { label: "unconditional_recursion", description: r#"functions that cannot return without calling themselves"# }, | ||
358 | LintCompletion { label: "unknown_lints", description: r#"unrecognized lint attribute"# }, | ||
359 | LintCompletion { label: "unnameable_test_items", description: r#"detects an item that cannot be named being marked as `#[test_case]`"# }, | ||
360 | LintCompletion { label: "unreachable_code", description: r#"detects unreachable code paths"# }, | ||
361 | LintCompletion { label: "unreachable_patterns", description: r#"detects unreachable patterns"# }, | ||
362 | LintCompletion { label: "unstable_name_collisions", description: r#"detects name collision with an existing but unstable method"# }, | ||
363 | LintCompletion { label: "unused_allocation", description: r#"detects unnecessary allocations that can be eliminated"# }, | ||
364 | LintCompletion { label: "unused_assignments", description: r#"detect assignments that will never be read"# }, | ||
365 | LintCompletion { label: "unused_attributes", description: r#"detects attributes that were not used by the compiler"# }, | ||
366 | LintCompletion { label: "unused_braces", description: r#"unnecessary braces around an expression"# }, | ||
367 | LintCompletion { label: "unused_comparisons", description: r#"comparisons made useless by limits of the types involved"# }, | ||
368 | LintCompletion { label: "unused_doc_comments", description: r#"detects doc comments that aren't used by rustdoc"# }, | ||
369 | LintCompletion { label: "unused_features", description: r#"unused features found in crate-level `#[feature]` directives"# }, | ||
370 | LintCompletion { label: "unused_imports", description: r#"imports that are never used"# }, | ||
371 | LintCompletion { label: "unused_labels", description: r#"detects labels that are never used"# }, | ||
372 | LintCompletion { label: "unused_macros", description: r#"detects macros that were not used"# }, | ||
373 | LintCompletion { label: "unused_must_use", description: r#"unused result of a type flagged as `#[must_use]`"# }, | ||
374 | LintCompletion { label: "unused_mut", description: r#"detect mut variables which don't need to be mutable"# }, | ||
375 | LintCompletion { label: "unused_parens", description: r#"`if`, `match`, `while` and `return` do not need parentheses"# }, | ||
376 | LintCompletion { label: "unused_unsafe", description: r#"unnecessary use of an `unsafe` block"# }, | ||
377 | LintCompletion { label: "unused_variables", description: r#"detect variables which are not used in any way"# }, | ||
378 | LintCompletion { label: "warnings", description: r#"mass-change the level for lints which produce warnings"# }, | ||
379 | LintCompletion { label: "where_clauses_object_safety", description: r#"checks the object safety of where clauses"# }, | ||
380 | LintCompletion { label: "while_true", description: r#"suggest using `loop { }` instead of `while true { }`"# }, | ||
381 | LintCompletion { label: "ambiguous_associated_items", description: r#"ambiguous associated items"# }, | ||
382 | LintCompletion { label: "arithmetic_overflow", description: r#"arithmetic operation overflows"# }, | ||
383 | LintCompletion { label: "conflicting_repr_hints", description: r#"conflicts between `#[repr(..)]` hints that were previously accepted and used in practice"# }, | ||
384 | LintCompletion { label: "const_err", description: r#"constant evaluation detected erroneous expression"# }, | ||
385 | LintCompletion { label: "ill_formed_attribute_input", description: r#"ill-formed attribute inputs that were previously accepted and used in practice"# }, | ||
386 | LintCompletion { label: "incomplete_include", description: r#"trailing content in included file"# }, | ||
387 | LintCompletion { label: "invalid_type_param_default", description: r#"type parameter default erroneously allowed in invalid location"# }, | ||
388 | LintCompletion { label: "macro_expanded_macro_exports_accessed_by_absolute_paths", description: r#"macro-expanded `macro_export` macros from the current crate cannot be referred to by absolute paths"# }, | ||
389 | LintCompletion { label: "missing_fragment_specifier", description: r#"detects missing fragment specifiers in unused `macro_rules!` patterns"# }, | ||
390 | LintCompletion { label: "mutable_transmutes", description: r#"mutating transmuted &mut T from &T may cause undefined behavior"# }, | ||
391 | LintCompletion { label: "no_mangle_const_items", description: r#"const items will not have their symbols exported"# }, | ||
392 | LintCompletion { label: "order_dependent_trait_objects", description: r#"trait-object types were treated as different depending on marker-trait order"# }, | ||
393 | LintCompletion { label: "overflowing_literals", description: r#"literal out of range for its type"# }, | ||
394 | LintCompletion { label: "patterns_in_fns_without_body", description: r#"patterns in functions without body were erroneously allowed"# }, | ||
395 | LintCompletion { label: "pub_use_of_private_extern_crate", description: r#"detect public re-exports of private extern crates"# }, | ||
396 | LintCompletion { label: "soft_unstable", description: r#"a feature gate that doesn't break dependent crates"# }, | ||
397 | LintCompletion { label: "unconditional_panic", description: r#"operation will cause a panic at runtime"# }, | ||
398 | LintCompletion { label: "unknown_crate_types", description: r#"unknown crate type found in `#[crate_type]` directive"# }, | ||
399 | ]; | ||
400 | |||
401 | #[cfg(test)] | 296 | #[cfg(test)] |
402 | mod tests { | 297 | mod tests { |
298 | use super::*; | ||
299 | |||
403 | use expect_test::{expect, Expect}; | 300 | use expect_test::{expect, Expect}; |
404 | 301 | ||
405 | use crate::{test_utils::completion_list, CompletionKind}; | 302 | use crate::{test_utils::completion_list, CompletionKind}; |
406 | 303 | ||
304 | #[test] | ||
305 | fn attributes_are_sorted() { | ||
306 | let mut attrs = ATTRIBUTES.iter().map(|attr| attr.key()); | ||
307 | let mut prev = attrs.next().unwrap(); | ||
308 | |||
309 | attrs.for_each(|next| { | ||
310 | assert!( | ||
311 | prev < next, | ||
312 | r#"ATTRIBUTES array is not sorted, "{}" should come after "{}""#, | ||
313 | prev, | ||
314 | next | ||
315 | ); | ||
316 | prev = next; | ||
317 | }); | ||
318 | } | ||
319 | |||
407 | fn check(ra_fixture: &str, expect: Expect) { | 320 | fn check(ra_fixture: &str, expect: Expect) { |
408 | let actual = completion_list(ra_fixture, CompletionKind::Attribute); | 321 | let actual = completion_list(ra_fixture, CompletionKind::Attribute); |
409 | expect.assert_eq(&actual); | 322 | expect.assert_eq(&actual); |
410 | } | 323 | } |
411 | 324 | ||
412 | #[test] | 325 | #[test] |
413 | fn empty_derive_completion() { | 326 | fn test_attribute_completion_inside_nested_attr() { |
327 | check(r#"#[cfg($0)]"#, expect![[]]) | ||
328 | } | ||
329 | |||
330 | #[test] | ||
331 | fn test_attribute_completion_with_existing_attr() { | ||
414 | check( | 332 | check( |
415 | r#" | 333 | r#"#[no_mangle] #[$0] mcall!();"#, |
416 | #[derive($0)] | ||
417 | struct Test {} | ||
418 | "#, | ||
419 | expect![[r#" | 334 | expect![[r#" |
420 | at Clone | 335 | at allow(…) |
421 | at Clone, Copy | 336 | at cfg(…) |
422 | at Debug | 337 | at cfg_attr(…) |
423 | at Default | 338 | at deny(…) |
424 | at Hash | 339 | at forbid(…) |
425 | at PartialEq | 340 | at warn(…) |
426 | at PartialEq, Eq | 341 | "#]], |
427 | at PartialEq, PartialOrd | 342 | ) |
428 | at PartialEq, Eq, PartialOrd, Ord | 343 | } |
344 | |||
345 | #[test] | ||
346 | fn complete_attribute_on_source_file() { | ||
347 | check( | ||
348 | r#"#![$0]"#, | ||
349 | expect![[r#" | ||
350 | at allow(…) | ||
351 | at cfg(…) | ||
352 | at cfg_attr(…) | ||
353 | at deny(…) | ||
354 | at forbid(…) | ||
355 | at warn(…) | ||
356 | at deprecated | ||
357 | at doc = "…" | ||
358 | at doc(hidden) | ||
359 | at doc(alias = "…") | ||
360 | at must_use | ||
361 | at no_mangle | ||
362 | at crate_name = "" | ||
363 | at feature(…) | ||
364 | at no_implicit_prelude | ||
365 | at no_main | ||
366 | at no_std | ||
367 | at recursion_limit = … | ||
368 | at type_length_limit = … | ||
369 | at windows_subsystem = "…" | ||
429 | "#]], | 370 | "#]], |
430 | ); | 371 | ); |
431 | } | 372 | } |
432 | 373 | ||
433 | #[test] | 374 | #[test] |
434 | fn no_completion_for_incorrect_derive() { | 375 | fn complete_attribute_on_module() { |
435 | check( | 376 | check( |
436 | r#" | 377 | r#"#[$0] mod foo;"#, |
437 | #[derive{$0)] | 378 | expect![[r#" |
438 | struct Test {} | 379 | at allow(…) |
439 | "#, | 380 | at cfg(…) |
440 | expect![[r#""#]], | 381 | at cfg_attr(…) |
441 | ) | 382 | at deny(…) |
383 | at forbid(…) | ||
384 | at warn(…) | ||
385 | at deprecated | ||
386 | at doc = "…" | ||
387 | at doc(hidden) | ||
388 | at doc(alias = "…") | ||
389 | at must_use | ||
390 | at no_mangle | ||
391 | at path = "…" | ||
392 | "#]], | ||
393 | ); | ||
394 | check( | ||
395 | r#"mod foo {#![$0]}"#, | ||
396 | expect![[r#" | ||
397 | at allow(…) | ||
398 | at cfg(…) | ||
399 | at cfg_attr(…) | ||
400 | at deny(…) | ||
401 | at forbid(…) | ||
402 | at warn(…) | ||
403 | at deprecated | ||
404 | at doc = "…" | ||
405 | at doc(hidden) | ||
406 | at doc(alias = "…") | ||
407 | at must_use | ||
408 | at no_mangle | ||
409 | at no_implicit_prelude | ||
410 | "#]], | ||
411 | ); | ||
442 | } | 412 | } |
443 | 413 | ||
444 | #[test] | 414 | #[test] |
445 | fn derive_with_input_completion() { | 415 | fn complete_attribute_on_macro_rules() { |
446 | check( | 416 | check( |
447 | r#" | 417 | r#"#[$0] macro_rules! foo {}"#, |
448 | #[derive(serde::Serialize, PartialEq, $0)] | ||
449 | struct Test {} | ||
450 | "#, | ||
451 | expect![[r#" | 418 | expect![[r#" |
452 | at Clone | 419 | at allow(…) |
453 | at Clone, Copy | 420 | at cfg(…) |
454 | at Debug | 421 | at cfg_attr(…) |
455 | at Default | 422 | at deny(…) |
456 | at Hash | 423 | at forbid(…) |
457 | at Eq | 424 | at warn(…) |
458 | at PartialOrd | 425 | at deprecated |
459 | at Eq, PartialOrd, Ord | 426 | at doc = "…" |
427 | at doc(hidden) | ||
428 | at doc(alias = "…") | ||
429 | at must_use | ||
430 | at no_mangle | ||
431 | at macro_export | ||
432 | at macro_use | ||
460 | "#]], | 433 | "#]], |
461 | ) | 434 | ); |
462 | } | 435 | } |
463 | 436 | ||
464 | #[test] | 437 | #[test] |
465 | fn test_attribute_completion() { | 438 | fn complete_attribute_on_macro_def() { |
466 | check( | 439 | check( |
467 | r#"#[$0]"#, | 440 | r#"#[$0] macro foo {}"#, |
468 | expect![[r#" | 441 | expect![[r#" |
469 | at allow(…) | 442 | at allow(…) |
470 | at automatically_derived | 443 | at cfg(…) |
471 | at cfg_attr(…) | 444 | at cfg_attr(…) |
445 | at deny(…) | ||
446 | at forbid(…) | ||
447 | at warn(…) | ||
448 | at deprecated | ||
449 | at doc = "…" | ||
450 | at doc(hidden) | ||
451 | at doc(alias = "…") | ||
452 | at must_use | ||
453 | at no_mangle | ||
454 | "#]], | ||
455 | ); | ||
456 | } | ||
457 | |||
458 | #[test] | ||
459 | fn complete_attribute_on_extern_crate() { | ||
460 | check( | ||
461 | r#"#[$0] extern crate foo;"#, | ||
462 | expect![[r#" | ||
463 | at allow(…) | ||
472 | at cfg(…) | 464 | at cfg(…) |
473 | at cold | 465 | at cfg_attr(…) |
474 | at deny(…) | 466 | at deny(…) |
467 | at forbid(…) | ||
468 | at warn(…) | ||
475 | at deprecated | 469 | at deprecated |
476 | at derive(…) | 470 | at doc = "…" |
477 | at export_name = "…" | 471 | at doc(hidden) |
478 | at doc(alias = "…") | 472 | at doc(alias = "…") |
473 | at must_use | ||
474 | at no_mangle | ||
475 | at macro_use | ||
476 | "#]], | ||
477 | ); | ||
478 | } | ||
479 | |||
480 | #[test] | ||
481 | fn complete_attribute_on_use() { | ||
482 | check( | ||
483 | r#"#[$0] use foo;"#, | ||
484 | expect![[r#" | ||
485 | at allow(…) | ||
486 | at cfg(…) | ||
487 | at cfg_attr(…) | ||
488 | at deny(…) | ||
489 | at forbid(…) | ||
490 | at warn(…) | ||
491 | at deprecated | ||
479 | at doc = "…" | 492 | at doc = "…" |
480 | at doc(hidden) | 493 | at doc(hidden) |
494 | at doc(alias = "…") | ||
495 | at must_use | ||
496 | at no_mangle | ||
497 | "#]], | ||
498 | ); | ||
499 | } | ||
500 | |||
501 | #[test] | ||
502 | fn complete_attribute_on_type_alias() { | ||
503 | check( | ||
504 | r#"#[$0] type foo = ();"#, | ||
505 | expect![[r#" | ||
506 | at allow(…) | ||
507 | at cfg(…) | ||
508 | at cfg_attr(…) | ||
509 | at deny(…) | ||
481 | at forbid(…) | 510 | at forbid(…) |
482 | at ignore = "…" | 511 | at warn(…) |
483 | at inline | 512 | at deprecated |
484 | at link | 513 | at doc = "…" |
485 | at link_name = "…" | 514 | at doc(hidden) |
486 | at link_section = "…" | 515 | at doc(alias = "…") |
487 | at macro_export | 516 | at must_use |
488 | at macro_use | 517 | at no_mangle |
518 | "#]], | ||
519 | ); | ||
520 | } | ||
521 | |||
522 | #[test] | ||
523 | fn complete_attribute_on_struct() { | ||
524 | check( | ||
525 | r#"#[$0] struct Foo;"#, | ||
526 | expect![[r#" | ||
527 | at allow(…) | ||
528 | at cfg(…) | ||
529 | at cfg_attr(…) | ||
530 | at deny(…) | ||
531 | at forbid(…) | ||
532 | at warn(…) | ||
533 | at deprecated | ||
534 | at doc = "…" | ||
535 | at doc(hidden) | ||
536 | at doc(alias = "…") | ||
489 | at must_use | 537 | at must_use |
490 | at no_mangle | 538 | at no_mangle |
539 | at derive(…) | ||
540 | at repr(…) | ||
491 | at non_exhaustive | 541 | at non_exhaustive |
492 | at path = "…" | 542 | "#]], |
493 | at proc_macro | 543 | ); |
494 | at proc_macro_attribute | 544 | } |
495 | at proc_macro_derive(…) | 545 | |
546 | #[test] | ||
547 | fn complete_attribute_on_enum() { | ||
548 | check( | ||
549 | r#"#[$0] enum Foo {}"#, | ||
550 | expect![[r#" | ||
551 | at allow(…) | ||
552 | at cfg(…) | ||
553 | at cfg_attr(…) | ||
554 | at deny(…) | ||
555 | at forbid(…) | ||
556 | at warn(…) | ||
557 | at deprecated | ||
558 | at doc = "…" | ||
559 | at doc(hidden) | ||
560 | at doc(alias = "…") | ||
561 | at must_use | ||
562 | at no_mangle | ||
563 | at derive(…) | ||
496 | at repr(…) | 564 | at repr(…) |
497 | at should_panic | 565 | at non_exhaustive |
498 | at target_feature = "…" | 566 | "#]], |
499 | at test | 567 | ); |
500 | at track_caller | 568 | } |
501 | at used | 569 | |
570 | #[test] | ||
571 | fn complete_attribute_on_const() { | ||
572 | check( | ||
573 | r#"#[$0] const FOO: () = ();"#, | ||
574 | expect![[r#" | ||
575 | at allow(…) | ||
576 | at cfg(…) | ||
577 | at cfg_attr(…) | ||
578 | at deny(…) | ||
579 | at forbid(…) | ||
502 | at warn(…) | 580 | at warn(…) |
581 | at deprecated | ||
582 | at doc = "…" | ||
583 | at doc(hidden) | ||
584 | at doc(alias = "…") | ||
585 | at must_use | ||
586 | at no_mangle | ||
503 | "#]], | 587 | "#]], |
504 | ) | 588 | ); |
505 | } | 589 | } |
506 | 590 | ||
507 | #[test] | 591 | #[test] |
508 | fn test_attribute_completion_inside_nested_attr() { | 592 | fn complete_attribute_on_static() { |
509 | check(r#"#[cfg($0)]"#, expect![[]]) | 593 | check( |
594 | r#"#[$0] static FOO: () = ()"#, | ||
595 | expect![[r#" | ||
596 | at allow(…) | ||
597 | at cfg(…) | ||
598 | at cfg_attr(…) | ||
599 | at deny(…) | ||
600 | at forbid(…) | ||
601 | at warn(…) | ||
602 | at deprecated | ||
603 | at doc = "…" | ||
604 | at doc(hidden) | ||
605 | at doc(alias = "…") | ||
606 | at must_use | ||
607 | at no_mangle | ||
608 | at export_name = "…" | ||
609 | at link_name = "…" | ||
610 | at link_section = "…" | ||
611 | at global_allocator | ||
612 | at used | ||
613 | "#]], | ||
614 | ); | ||
510 | } | 615 | } |
511 | 616 | ||
512 | #[test] | 617 | #[test] |
513 | fn test_inner_attribute_completion() { | 618 | fn complete_attribute_on_trait() { |
514 | check( | 619 | check( |
515 | r"#![$0]", | 620 | r#"#[$0] trait Foo {}"#, |
516 | expect![[r#" | 621 | expect![[r#" |
517 | at allow(…) | 622 | at allow(…) |
518 | at automatically_derived | 623 | at cfg(…) |
519 | at cfg_attr(…) | 624 | at cfg_attr(…) |
625 | at deny(…) | ||
626 | at forbid(…) | ||
627 | at warn(…) | ||
628 | at deprecated | ||
629 | at doc = "…" | ||
630 | at doc(hidden) | ||
631 | at doc(alias = "…") | ||
632 | at must_use | ||
633 | at no_mangle | ||
634 | at must_use | ||
635 | "#]], | ||
636 | ); | ||
637 | } | ||
638 | |||
639 | #[test] | ||
640 | fn complete_attribute_on_impl() { | ||
641 | check( | ||
642 | r#"#[$0] impl () {}"#, | ||
643 | expect![[r#" | ||
644 | at allow(…) | ||
520 | at cfg(…) | 645 | at cfg(…) |
521 | at cold | 646 | at cfg_attr(…) |
522 | at crate_name = "" | ||
523 | at deny(…) | 647 | at deny(…) |
648 | at forbid(…) | ||
649 | at warn(…) | ||
524 | at deprecated | 650 | at deprecated |
525 | at derive(…) | 651 | at doc = "…" |
526 | at export_name = "…" | 652 | at doc(hidden) |
653 | at doc(alias = "…") | ||
654 | at must_use | ||
655 | at no_mangle | ||
656 | at automatically_derived | ||
657 | "#]], | ||
658 | ); | ||
659 | check( | ||
660 | r#"impl () {#![$0]}"#, | ||
661 | expect![[r#" | ||
662 | at allow(…) | ||
663 | at cfg(…) | ||
664 | at cfg_attr(…) | ||
665 | at deny(…) | ||
666 | at forbid(…) | ||
667 | at warn(…) | ||
668 | at deprecated | ||
669 | at doc = "…" | ||
670 | at doc(hidden) | ||
527 | at doc(alias = "…") | 671 | at doc(alias = "…") |
672 | at must_use | ||
673 | at no_mangle | ||
674 | "#]], | ||
675 | ); | ||
676 | } | ||
677 | |||
678 | #[test] | ||
679 | fn complete_attribute_on_extern_block() { | ||
680 | check( | ||
681 | r#"#[$0] extern {}"#, | ||
682 | expect![[r#" | ||
683 | at allow(…) | ||
684 | at cfg(…) | ||
685 | at cfg_attr(…) | ||
686 | at deny(…) | ||
687 | at forbid(…) | ||
688 | at warn(…) | ||
689 | at deprecated | ||
528 | at doc = "…" | 690 | at doc = "…" |
529 | at doc(hidden) | 691 | at doc(hidden) |
530 | at feature(…) | 692 | at doc(alias = "…") |
693 | at must_use | ||
694 | at no_mangle | ||
695 | at link | ||
696 | "#]], | ||
697 | ); | ||
698 | check( | ||
699 | r#"extern {#![$0]}"#, | ||
700 | expect![[r#" | ||
701 | at allow(…) | ||
702 | at cfg(…) | ||
703 | at cfg_attr(…) | ||
704 | at deny(…) | ||
531 | at forbid(…) | 705 | at forbid(…) |
532 | at global_allocator | 706 | at warn(…) |
533 | at ignore = "…" | 707 | at deprecated |
534 | at inline | 708 | at doc = "…" |
709 | at doc(hidden) | ||
710 | at doc(alias = "…") | ||
711 | at must_use | ||
712 | at no_mangle | ||
535 | at link | 713 | at link |
714 | "#]], | ||
715 | ); | ||
716 | } | ||
717 | |||
718 | #[test] | ||
719 | fn complete_attribute_on_variant() { | ||
720 | check( | ||
721 | r#"enum Foo { #[$0] Bar }"#, | ||
722 | expect![[r#" | ||
723 | at allow(…) | ||
724 | at cfg(…) | ||
725 | at cfg_attr(…) | ||
726 | at deny(…) | ||
727 | at forbid(…) | ||
728 | at warn(…) | ||
729 | at non_exhaustive | ||
730 | "#]], | ||
731 | ); | ||
732 | } | ||
733 | |||
734 | #[test] | ||
735 | fn complete_attribute_on_fn() { | ||
736 | check( | ||
737 | r#"#[$0] fn main() {}"#, | ||
738 | expect![[r#" | ||
739 | at allow(…) | ||
740 | at cfg(…) | ||
741 | at cfg_attr(…) | ||
742 | at deny(…) | ||
743 | at forbid(…) | ||
744 | at warn(…) | ||
745 | at deprecated | ||
746 | at doc = "…" | ||
747 | at doc(hidden) | ||
748 | at doc(alias = "…") | ||
749 | at must_use | ||
750 | at no_mangle | ||
751 | at export_name = "…" | ||
536 | at link_name = "…" | 752 | at link_name = "…" |
537 | at link_section = "…" | 753 | at link_section = "…" |
538 | at macro_export | 754 | at cold |
539 | at macro_use | 755 | at ignore = "…" |
756 | at inline | ||
540 | at must_use | 757 | at must_use |
541 | at no_link | ||
542 | at no_implicit_prelude | ||
543 | at no_main | ||
544 | at no_mangle | ||
545 | at no_std | ||
546 | at non_exhaustive | ||
547 | at panic_handler | 758 | at panic_handler |
548 | at path = "…" | ||
549 | at proc_macro | 759 | at proc_macro |
550 | at proc_macro_attribute | ||
551 | at proc_macro_derive(…) | 760 | at proc_macro_derive(…) |
552 | at recursion_limit = … | 761 | at proc_macro_attribute |
553 | at repr(…) | ||
554 | at should_panic | 762 | at should_panic |
555 | at target_feature = "…" | 763 | at target_feature = "…" |
556 | at test | 764 | at test |
557 | at track_caller | 765 | at track_caller |
558 | at type_length_limit = … | 766 | "#]], |
559 | at used | 767 | ); |
768 | } | ||
769 | |||
770 | #[test] | ||
771 | fn complete_attribute_on_expr() { | ||
772 | check( | ||
773 | r#"fn main() { #[$0] foo() }"#, | ||
774 | expect![[r#" | ||
775 | at allow(…) | ||
776 | at cfg(…) | ||
777 | at cfg_attr(…) | ||
778 | at deny(…) | ||
779 | at forbid(…) | ||
560 | at warn(…) | 780 | at warn(…) |
561 | at windows_subsystem = "…" | ||
562 | "#]], | 781 | "#]], |
563 | ); | 782 | ); |
564 | } | 783 | } |
diff --git a/crates/ide_completion/src/completions/attribute/derive.rs b/crates/ide_completion/src/completions/attribute/derive.rs new file mode 100644 index 000000000..0bc3eab98 --- /dev/null +++ b/crates/ide_completion/src/completions/attribute/derive.rs | |||
@@ -0,0 +1,147 @@ | |||
1 | //! Completion for derives | ||
2 | use itertools::Itertools; | ||
3 | use rustc_hash::FxHashSet; | ||
4 | use syntax::ast; | ||
5 | |||
6 | use crate::{ | ||
7 | context::CompletionContext, | ||
8 | item::{CompletionItem, CompletionItemKind, CompletionKind}, | ||
9 | Completions, | ||
10 | }; | ||
11 | |||
12 | pub(super) fn complete_derive( | ||
13 | acc: &mut Completions, | ||
14 | ctx: &CompletionContext, | ||
15 | derive_input: ast::TokenTree, | ||
16 | ) { | ||
17 | if let Some(existing_derives) = super::parse_comma_sep_input(derive_input) { | ||
18 | for derive_completion in DEFAULT_DERIVE_COMPLETIONS | ||
19 | .iter() | ||
20 | .filter(|completion| !existing_derives.contains(completion.label)) | ||
21 | { | ||
22 | let mut components = vec![derive_completion.label]; | ||
23 | components.extend( | ||
24 | derive_completion | ||
25 | .dependencies | ||
26 | .iter() | ||
27 | .filter(|&&dependency| !existing_derives.contains(dependency)), | ||
28 | ); | ||
29 | let lookup = components.join(", "); | ||
30 | let label = components.iter().rev().join(", "); | ||
31 | let mut item = | ||
32 | CompletionItem::new(CompletionKind::Attribute, ctx.source_range(), label); | ||
33 | item.lookup_by(lookup).kind(CompletionItemKind::Attribute); | ||
34 | item.add_to(acc); | ||
35 | } | ||
36 | |||
37 | for custom_derive_name in get_derive_names_in_scope(ctx).difference(&existing_derives) { | ||
38 | let mut item = CompletionItem::new( | ||
39 | CompletionKind::Attribute, | ||
40 | ctx.source_range(), | ||
41 | custom_derive_name, | ||
42 | ); | ||
43 | item.kind(CompletionItemKind::Attribute); | ||
44 | item.add_to(acc); | ||
45 | } | ||
46 | } | ||
47 | } | ||
48 | |||
49 | fn get_derive_names_in_scope(ctx: &CompletionContext) -> FxHashSet<String> { | ||
50 | let mut result = FxHashSet::default(); | ||
51 | ctx.scope.process_all_names(&mut |name, scope_def| { | ||
52 | if let hir::ScopeDef::MacroDef(mac) = scope_def { | ||
53 | if mac.kind() == hir::MacroKind::Derive { | ||
54 | result.insert(name.to_string()); | ||
55 | } | ||
56 | } | ||
57 | }); | ||
58 | result | ||
59 | } | ||
60 | |||
61 | struct DeriveCompletion { | ||
62 | label: &'static str, | ||
63 | dependencies: &'static [&'static str], | ||
64 | } | ||
65 | |||
66 | /// Standard Rust derives and the information about their dependencies | ||
67 | /// (the dependencies are needed so that the main derive don't break the compilation when added) | ||
68 | const DEFAULT_DERIVE_COMPLETIONS: &[DeriveCompletion] = &[ | ||
69 | DeriveCompletion { label: "Clone", dependencies: &[] }, | ||
70 | DeriveCompletion { label: "Copy", dependencies: &["Clone"] }, | ||
71 | DeriveCompletion { label: "Debug", dependencies: &[] }, | ||
72 | DeriveCompletion { label: "Default", dependencies: &[] }, | ||
73 | DeriveCompletion { label: "Hash", dependencies: &[] }, | ||
74 | DeriveCompletion { label: "PartialEq", dependencies: &[] }, | ||
75 | DeriveCompletion { label: "Eq", dependencies: &["PartialEq"] }, | ||
76 | DeriveCompletion { label: "PartialOrd", dependencies: &["PartialEq"] }, | ||
77 | DeriveCompletion { label: "Ord", dependencies: &["PartialOrd", "Eq", "PartialEq"] }, | ||
78 | ]; | ||
79 | |||
80 | #[cfg(test)] | ||
81 | mod tests { | ||
82 | use expect_test::{expect, Expect}; | ||
83 | |||
84 | use crate::{test_utils::completion_list, CompletionKind}; | ||
85 | |||
86 | fn check(ra_fixture: &str, expect: Expect) { | ||
87 | let actual = completion_list(ra_fixture, CompletionKind::Attribute); | ||
88 | expect.assert_eq(&actual); | ||
89 | } | ||
90 | |||
91 | #[test] | ||
92 | fn no_completion_for_incorrect_derive() { | ||
93 | check(r#"#[derive{$0)] struct Test;"#, expect![[]]) | ||
94 | } | ||
95 | |||
96 | #[test] | ||
97 | fn empty_derive() { | ||
98 | check( | ||
99 | r#"#[derive($0)] struct Test;"#, | ||
100 | expect![[r#" | ||
101 | at Clone | ||
102 | at Clone, Copy | ||
103 | at Debug | ||
104 | at Default | ||
105 | at Hash | ||
106 | at PartialEq | ||
107 | at PartialEq, Eq | ||
108 | at PartialEq, PartialOrd | ||
109 | at PartialEq, Eq, PartialOrd, Ord | ||
110 | "#]], | ||
111 | ); | ||
112 | } | ||
113 | |||
114 | #[test] | ||
115 | fn derive_with_input() { | ||
116 | check( | ||
117 | r#"#[derive(serde::Serialize, PartialEq, $0)] struct Test;"#, | ||
118 | expect![[r#" | ||
119 | at Clone | ||
120 | at Clone, Copy | ||
121 | at Debug | ||
122 | at Default | ||
123 | at Hash | ||
124 | at Eq | ||
125 | at PartialOrd | ||
126 | at Eq, PartialOrd, Ord | ||
127 | "#]], | ||
128 | ) | ||
129 | } | ||
130 | |||
131 | #[test] | ||
132 | fn derive_with_input2() { | ||
133 | check( | ||
134 | r#"#[derive($0 serde::Serialize, PartialEq)] struct Test;"#, | ||
135 | expect![[r#" | ||
136 | at Clone | ||
137 | at Clone, Copy | ||
138 | at Debug | ||
139 | at Default | ||
140 | at Hash | ||
141 | at Eq | ||
142 | at PartialOrd | ||
143 | at Eq, PartialOrd, Ord | ||
144 | "#]], | ||
145 | ) | ||
146 | } | ||
147 | } | ||
diff --git a/crates/ide_completion/src/completions/attribute/lint.rs b/crates/ide_completion/src/completions/attribute/lint.rs new file mode 100644 index 000000000..403630dce --- /dev/null +++ b/crates/ide_completion/src/completions/attribute/lint.rs | |||
@@ -0,0 +1,187 @@ | |||
1 | //! Completion for lints | ||
2 | use syntax::ast; | ||
3 | |||
4 | use crate::{ | ||
5 | context::CompletionContext, | ||
6 | item::{CompletionItem, CompletionItemKind, CompletionKind}, | ||
7 | Completions, | ||
8 | }; | ||
9 | |||
10 | pub(super) fn complete_lint( | ||
11 | acc: &mut Completions, | ||
12 | ctx: &CompletionContext, | ||
13 | derive_input: ast::TokenTree, | ||
14 | lints_completions: &[LintCompletion], | ||
15 | ) { | ||
16 | if let Some(existing_lints) = super::parse_comma_sep_input(derive_input) { | ||
17 | for lint_completion in lints_completions | ||
18 | .into_iter() | ||
19 | .filter(|completion| !existing_lints.contains(completion.label)) | ||
20 | { | ||
21 | let mut item = CompletionItem::new( | ||
22 | CompletionKind::Attribute, | ||
23 | ctx.source_range(), | ||
24 | lint_completion.label, | ||
25 | ); | ||
26 | item.kind(CompletionItemKind::Attribute).detail(lint_completion.description); | ||
27 | item.add_to(acc) | ||
28 | } | ||
29 | } | ||
30 | } | ||
31 | |||
32 | pub(crate) struct LintCompletion { | ||
33 | pub(crate) label: &'static str, | ||
34 | pub(crate) description: &'static str, | ||
35 | } | ||
36 | |||
37 | #[rustfmt::skip] | ||
38 | pub(super) const DEFAULT_LINT_COMPLETIONS: &[LintCompletion] = &[ | ||
39 | LintCompletion { label: "absolute_paths_not_starting_with_crate", description: r#"fully qualified paths that start with a module name instead of `crate`, `self`, or an extern crate name"# }, | ||
40 | LintCompletion { label: "anonymous_parameters", description: r#"detects anonymous parameters"# }, | ||
41 | LintCompletion { label: "box_pointers", description: r#"use of owned (Box type) heap memory"# }, | ||
42 | LintCompletion { label: "deprecated_in_future", description: r#"detects use of items that will be deprecated in a future version"# }, | ||
43 | LintCompletion { label: "elided_lifetimes_in_paths", description: r#"hidden lifetime parameters in types are deprecated"# }, | ||
44 | LintCompletion { label: "explicit_outlives_requirements", description: r#"outlives requirements can be inferred"# }, | ||
45 | LintCompletion { label: "indirect_structural_match", description: r#"pattern with const indirectly referencing non-structural-match type"# }, | ||
46 | LintCompletion { label: "keyword_idents", description: r#"detects edition keywords being used as an identifier"# }, | ||
47 | LintCompletion { label: "macro_use_extern_crate", description: r#"the `#[macro_use]` attribute is now deprecated in favor of using macros via the module system"# }, | ||
48 | LintCompletion { label: "meta_variable_misuse", description: r#"possible meta-variable misuse at macro definition"# }, | ||
49 | LintCompletion { label: "missing_copy_implementations", description: r#"detects potentially-forgotten implementations of `Copy`"# }, | ||
50 | LintCompletion { label: "missing_crate_level_docs", description: r#"detects crates with no crate-level documentation"# }, | ||
51 | LintCompletion { label: "missing_debug_implementations", description: r#"detects missing implementations of Debug"# }, | ||
52 | LintCompletion { label: "missing_docs", description: r#"detects missing documentation for public members"# }, | ||
53 | LintCompletion { label: "missing_doc_code_examples", description: r#"detects publicly-exported items without code samples in their documentation"# }, | ||
54 | LintCompletion { label: "non_ascii_idents", description: r#"detects non-ASCII identifiers"# }, | ||
55 | LintCompletion { label: "private_doc_tests", description: r#"detects code samples in docs of private items not documented by rustdoc"# }, | ||
56 | LintCompletion { label: "single_use_lifetimes", description: r#"detects lifetime parameters that are only used once"# }, | ||
57 | LintCompletion { label: "trivial_casts", description: r#"detects trivial casts which could be removed"# }, | ||
58 | LintCompletion { label: "trivial_numeric_casts", description: r#"detects trivial casts of numeric types which could be removed"# }, | ||
59 | LintCompletion { label: "unaligned_references", description: r#"detects unaligned references to fields of packed structs"# }, | ||
60 | LintCompletion { label: "unreachable_pub", description: r#"`pub` items not reachable from crate root"# }, | ||
61 | LintCompletion { label: "unsafe_code", description: r#"usage of `unsafe` code"# }, | ||
62 | LintCompletion { label: "unsafe_op_in_unsafe_fn", description: r#"unsafe operations in unsafe functions without an explicit unsafe block are deprecated"# }, | ||
63 | LintCompletion { label: "unstable_features", description: r#"enabling unstable features (deprecated. do not use)"# }, | ||
64 | LintCompletion { label: "unused_crate_dependencies", description: r#"crate dependencies that are never used"# }, | ||
65 | LintCompletion { label: "unused_extern_crates", description: r#"extern crates that are never used"# }, | ||
66 | LintCompletion { label: "unused_import_braces", description: r#"unnecessary braces around an imported item"# }, | ||
67 | LintCompletion { label: "unused_lifetimes", description: r#"detects lifetime parameters that are never used"# }, | ||
68 | LintCompletion { label: "unused_qualifications", description: r#"detects unnecessarily qualified names"# }, | ||
69 | LintCompletion { label: "unused_results", description: r#"unused result of an expression in a statement"# }, | ||
70 | LintCompletion { label: "variant_size_differences", description: r#"detects enums with widely varying variant sizes"# }, | ||
71 | LintCompletion { label: "array_into_iter", description: r#"detects calling `into_iter` on arrays"# }, | ||
72 | LintCompletion { label: "asm_sub_register", description: r#"using only a subset of a register for inline asm inputs"# }, | ||
73 | LintCompletion { label: "bare_trait_objects", description: r#"suggest using `dyn Trait` for trait objects"# }, | ||
74 | LintCompletion { label: "bindings_with_variant_name", description: r#"detects pattern bindings with the same name as one of the matched variants"# }, | ||
75 | LintCompletion { label: "cenum_impl_drop_cast", description: r#"a C-like enum implementing Drop is cast"# }, | ||
76 | LintCompletion { label: "clashing_extern_declarations", description: r#"detects when an extern fn has been declared with the same name but different types"# }, | ||
77 | LintCompletion { label: "coherence_leak_check", description: r#"distinct impls distinguished only by the leak-check code"# }, | ||
78 | LintCompletion { label: "confusable_idents", description: r#"detects visually confusable pairs between identifiers"# }, | ||
79 | LintCompletion { label: "dead_code", description: r#"detect unused, unexported items"# }, | ||
80 | LintCompletion { label: "deprecated", description: r#"detects use of deprecated items"# }, | ||
81 | LintCompletion { label: "ellipsis_inclusive_range_patterns", description: r#"`...` range patterns are deprecated"# }, | ||
82 | LintCompletion { label: "exported_private_dependencies", description: r#"public interface leaks type from a private dependency"# }, | ||
83 | LintCompletion { label: "illegal_floating_point_literal_pattern", description: r#"floating-point literals cannot be used in patterns"# }, | ||
84 | LintCompletion { label: "improper_ctypes", description: r#"proper use of libc types in foreign modules"# }, | ||
85 | LintCompletion { label: "improper_ctypes_definitions", description: r#"proper use of libc types in foreign item definitions"# }, | ||
86 | LintCompletion { label: "incomplete_features", description: r#"incomplete features that may function improperly in some or all cases"# }, | ||
87 | LintCompletion { label: "inline_no_sanitize", description: r#"detects incompatible use of `#[inline(always)]` and `#[no_sanitize(...)]`"# }, | ||
88 | LintCompletion { label: "intra_doc_link_resolution_failure", description: r#"failures in resolving intra-doc link targets"# }, | ||
89 | LintCompletion { label: "invalid_codeblock_attributes", description: r#"codeblock attribute looks a lot like a known one"# }, | ||
90 | LintCompletion { label: "invalid_value", description: r#"an invalid value is being created (such as a NULL reference)"# }, | ||
91 | LintCompletion { label: "irrefutable_let_patterns", description: r#"detects irrefutable patterns in if-let and while-let statements"# }, | ||
92 | LintCompletion { label: "late_bound_lifetime_arguments", description: r#"detects generic lifetime arguments in path segments with late bound lifetime parameters"# }, | ||
93 | LintCompletion { label: "mixed_script_confusables", description: r#"detects Unicode scripts whose mixed script confusables codepoints are solely used"# }, | ||
94 | LintCompletion { label: "mutable_borrow_reservation_conflict", description: r#"reservation of a two-phased borrow conflicts with other shared borrows"# }, | ||
95 | LintCompletion { label: "non_camel_case_types", description: r#"types, variants, traits and type parameters should have camel case names"# }, | ||
96 | LintCompletion { label: "non_shorthand_field_patterns", description: r#"using `Struct { x: x }` instead of `Struct { x }` in a pattern"# }, | ||
97 | LintCompletion { label: "non_snake_case", description: r#"variables, methods, functions, lifetime parameters and modules should have snake case names"# }, | ||
98 | LintCompletion { label: "non_upper_case_globals", description: r#"static constants should have uppercase identifiers"# }, | ||
99 | LintCompletion { label: "no_mangle_generic_items", description: r#"generic items must be mangled"# }, | ||
100 | LintCompletion { label: "overlapping_patterns", description: r#"detects overlapping patterns"# }, | ||
101 | LintCompletion { label: "path_statements", description: r#"path statements with no effect"# }, | ||
102 | LintCompletion { label: "private_in_public", description: r#"detect private items in public interfaces not caught by the old implementation"# }, | ||
103 | LintCompletion { label: "proc_macro_derive_resolution_fallback", description: r#"detects proc macro derives using inaccessible names from parent modules"# }, | ||
104 | LintCompletion { label: "redundant_semicolons", description: r#"detects unnecessary trailing semicolons"# }, | ||
105 | LintCompletion { label: "renamed_and_removed_lints", description: r#"lints that have been renamed or removed"# }, | ||
106 | LintCompletion { label: "safe_packed_borrows", description: r#"safe borrows of fields of packed structs were erroneously allowed"# }, | ||
107 | LintCompletion { label: "stable_features", description: r#"stable features found in `#[feature]` directive"# }, | ||
108 | LintCompletion { label: "trivial_bounds", description: r#"these bounds don't depend on an type parameters"# }, | ||
109 | LintCompletion { label: "type_alias_bounds", description: r#"bounds in type aliases are not enforced"# }, | ||
110 | LintCompletion { label: "tyvar_behind_raw_pointer", description: r#"raw pointer to an inference variable"# }, | ||
111 | LintCompletion { label: "uncommon_codepoints", description: r#"detects uncommon Unicode codepoints in identifiers"# }, | ||
112 | LintCompletion { label: "unconditional_recursion", description: r#"functions that cannot return without calling themselves"# }, | ||
113 | LintCompletion { label: "unknown_lints", description: r#"unrecognized lint attribute"# }, | ||
114 | LintCompletion { label: "unnameable_test_items", description: r#"detects an item that cannot be named being marked as `#[test_case]`"# }, | ||
115 | LintCompletion { label: "unreachable_code", description: r#"detects unreachable code paths"# }, | ||
116 | LintCompletion { label: "unreachable_patterns", description: r#"detects unreachable patterns"# }, | ||
117 | LintCompletion { label: "unstable_name_collisions", description: r#"detects name collision with an existing but unstable method"# }, | ||
118 | LintCompletion { label: "unused_allocation", description: r#"detects unnecessary allocations that can be eliminated"# }, | ||
119 | LintCompletion { label: "unused_assignments", description: r#"detect assignments that will never be read"# }, | ||
120 | LintCompletion { label: "unused_attributes", description: r#"detects attributes that were not used by the compiler"# }, | ||
121 | LintCompletion { label: "unused_braces", description: r#"unnecessary braces around an expression"# }, | ||
122 | LintCompletion { label: "unused_comparisons", description: r#"comparisons made useless by limits of the types involved"# }, | ||
123 | LintCompletion { label: "unused_doc_comments", description: r#"detects doc comments that aren't used by rustdoc"# }, | ||
124 | LintCompletion { label: "unused_features", description: r#"unused features found in crate-level `#[feature]` directives"# }, | ||
125 | LintCompletion { label: "unused_imports", description: r#"imports that are never used"# }, | ||
126 | LintCompletion { label: "unused_labels", description: r#"detects labels that are never used"# }, | ||
127 | LintCompletion { label: "unused_macros", description: r#"detects macros that were not used"# }, | ||
128 | LintCompletion { label: "unused_must_use", description: r#"unused result of a type flagged as `#[must_use]`"# }, | ||
129 | LintCompletion { label: "unused_mut", description: r#"detect mut variables which don't need to be mutable"# }, | ||
130 | LintCompletion { label: "unused_parens", description: r#"`if`, `match`, `while` and `return` do not need parentheses"# }, | ||
131 | LintCompletion { label: "unused_unsafe", description: r#"unnecessary use of an `unsafe` block"# }, | ||
132 | LintCompletion { label: "unused_variables", description: r#"detect variables which are not used in any way"# }, | ||
133 | LintCompletion { label: "warnings", description: r#"mass-change the level for lints which produce warnings"# }, | ||
134 | LintCompletion { label: "where_clauses_object_safety", description: r#"checks the object safety of where clauses"# }, | ||
135 | LintCompletion { label: "while_true", description: r#"suggest using `loop { }` instead of `while true { }`"# }, | ||
136 | LintCompletion { label: "ambiguous_associated_items", description: r#"ambiguous associated items"# }, | ||
137 | LintCompletion { label: "arithmetic_overflow", description: r#"arithmetic operation overflows"# }, | ||
138 | LintCompletion { label: "conflicting_repr_hints", description: r#"conflicts between `#[repr(..)]` hints that were previously accepted and used in practice"# }, | ||
139 | LintCompletion { label: "const_err", description: r#"constant evaluation detected erroneous expression"# }, | ||
140 | LintCompletion { label: "ill_formed_attribute_input", description: r#"ill-formed attribute inputs that were previously accepted and used in practice"# }, | ||
141 | LintCompletion { label: "incomplete_include", description: r#"trailing content in included file"# }, | ||
142 | LintCompletion { label: "invalid_type_param_default", description: r#"type parameter default erroneously allowed in invalid location"# }, | ||
143 | LintCompletion { label: "macro_expanded_macro_exports_accessed_by_absolute_paths", description: r#"macro-expanded `macro_export` macros from the current crate cannot be referred to by absolute paths"# }, | ||
144 | LintCompletion { label: "missing_fragment_specifier", description: r#"detects missing fragment specifiers in unused `macro_rules!` patterns"# }, | ||
145 | LintCompletion { label: "mutable_transmutes", description: r#"mutating transmuted &mut T from &T may cause undefined behavior"# }, | ||
146 | LintCompletion { label: "no_mangle_const_items", description: r#"const items will not have their symbols exported"# }, | ||
147 | LintCompletion { label: "order_dependent_trait_objects", description: r#"trait-object types were treated as different depending on marker-trait order"# }, | ||
148 | LintCompletion { label: "overflowing_literals", description: r#"literal out of range for its type"# }, | ||
149 | LintCompletion { label: "patterns_in_fns_without_body", description: r#"patterns in functions without body were erroneously allowed"# }, | ||
150 | LintCompletion { label: "pub_use_of_private_extern_crate", description: r#"detect public re-exports of private extern crates"# }, | ||
151 | LintCompletion { label: "soft_unstable", description: r#"a feature gate that doesn't break dependent crates"# }, | ||
152 | LintCompletion { label: "unconditional_panic", description: r#"operation will cause a panic at runtime"# }, | ||
153 | LintCompletion { label: "unknown_crate_types", description: r#"unknown crate type found in `#[crate_type]` directive"# }, | ||
154 | ]; | ||
155 | |||
156 | #[cfg(test)] | ||
157 | mod tests { | ||
158 | |||
159 | use crate::test_utils::check_edit; | ||
160 | |||
161 | #[test] | ||
162 | fn check_empty() { | ||
163 | check_edit( | ||
164 | "deprecated", | ||
165 | r#"#[allow($0)] struct Test;"#, | ||
166 | r#"#[allow(deprecated)] struct Test;"#, | ||
167 | ) | ||
168 | } | ||
169 | |||
170 | #[test] | ||
171 | fn check_with_existing() { | ||
172 | check_edit( | ||
173 | "deprecated", | ||
174 | r#"#[allow(keyword_idents, $0)] struct Test;"#, | ||
175 | r#"#[allow(keyword_idents, deprecated)] struct Test;"#, | ||
176 | ) | ||
177 | } | ||
178 | |||
179 | #[test] | ||
180 | fn check_qualified() { | ||
181 | check_edit( | ||
182 | "deprecated", | ||
183 | r#"#[allow(keyword_idents, $0)] struct Test;"#, | ||
184 | r#"#[allow(keyword_idents, deprecated)] struct Test;"#, | ||
185 | ) | ||
186 | } | ||
187 | } | ||
diff --git a/crates/ide_completion/src/completions/flyimport.rs b/crates/ide_completion/src/completions/flyimport.rs index be9cfbded..df27e7a84 100644 --- a/crates/ide_completion/src/completions/flyimport.rs +++ b/crates/ide_completion/src/completions/flyimport.rs | |||
@@ -110,7 +110,11 @@ pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext) | |||
110 | if !ctx.config.enable_imports_on_the_fly { | 110 | if !ctx.config.enable_imports_on_the_fly { |
111 | return None; | 111 | return None; |
112 | } | 112 | } |
113 | if ctx.use_item_syntax.is_some() || ctx.is_path_disallowed() { | 113 | if ctx.use_item_syntax.is_some() |
114 | || ctx.is_path_disallowed() | ||
115 | || ctx.expects_item() | ||
116 | || ctx.expects_assoc_item() | ||
117 | { | ||
114 | return None; | 118 | return None; |
115 | } | 119 | } |
116 | let potential_import_name = { | 120 | let potential_import_name = { |
diff --git a/crates/ide_completion/src/completions/fn_param.rs b/crates/ide_completion/src/completions/fn_param.rs index 0ea558489..cb90e8a3e 100644 --- a/crates/ide_completion/src/completions/fn_param.rs +++ b/crates/ide_completion/src/completions/fn_param.rs | |||
@@ -128,4 +128,19 @@ fn outer(text: String) { | |||
128 | "#]], | 128 | "#]], |
129 | ) | 129 | ) |
130 | } | 130 | } |
131 | |||
132 | #[test] | ||
133 | fn completes_non_ident_pat_param() { | ||
134 | check( | ||
135 | r#" | ||
136 | struct Bar { bar: u32 } | ||
137 | |||
138 | fn foo(Bar { bar }: Bar) {} | ||
139 | fn foo2($0) {} | ||
140 | "#, | ||
141 | expect![[r#" | ||
142 | bn Bar { bar }: Bar | ||
143 | "#]], | ||
144 | ) | ||
145 | } | ||
131 | } | 146 | } |
diff --git a/crates/ide_completion/src/completions/keyword.rs b/crates/ide_completion/src/completions/keyword.rs index 58e35bad9..0d035c611 100644 --- a/crates/ide_completion/src/completions/keyword.rs +++ b/crates/ide_completion/src/completions/keyword.rs | |||
@@ -4,7 +4,10 @@ use std::iter; | |||
4 | 4 | ||
5 | use syntax::{SyntaxKind, T}; | 5 | use syntax::{SyntaxKind, T}; |
6 | 6 | ||
7 | use crate::{CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions}; | 7 | use crate::{ |
8 | patterns::ImmediateLocation, CompletionContext, CompletionItem, CompletionItemKind, | ||
9 | CompletionKind, Completions, | ||
10 | }; | ||
8 | 11 | ||
9 | pub(crate) fn complete_use_tree_keyword(acc: &mut Completions, ctx: &CompletionContext) { | 12 | pub(crate) fn complete_use_tree_keyword(acc: &mut Completions, ctx: &CompletionContext) { |
10 | // complete keyword "crate" in use stmt | 13 | // complete keyword "crate" in use stmt |
@@ -44,96 +47,95 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte | |||
44 | cov_mark::hit!(no_keyword_completion_in_comments); | 47 | cov_mark::hit!(no_keyword_completion_in_comments); |
45 | return; | 48 | return; |
46 | } | 49 | } |
47 | if ctx.record_lit_syntax.is_some() { | 50 | if matches!(ctx.completion_location, Some(ImmediateLocation::RecordExpr(_))) { |
48 | cov_mark::hit!(no_keyword_completion_in_record_lit); | 51 | cov_mark::hit!(no_keyword_completion_in_record_lit); |
49 | return; | 52 | return; |
50 | } | 53 | } |
54 | let mut add_keyword = |kw, snippet| add_keyword(ctx, acc, kw, snippet); | ||
51 | 55 | ||
52 | let has_trait_or_impl_parent = ctx.has_impl_or_trait_parent(); | 56 | let expects_assoc_item = ctx.expects_assoc_item(); |
53 | let has_block_expr_parent = ctx.has_block_expr_parent(); | 57 | let has_block_expr_parent = ctx.has_block_expr_parent(); |
54 | let has_item_list_parent = ctx.has_item_list_parent(); | 58 | let expects_item = ctx.expects_item(); |
59 | |||
55 | if ctx.has_impl_or_trait_prev_sibling() { | 60 | if ctx.has_impl_or_trait_prev_sibling() { |
56 | add_keyword(ctx, acc, "where", "where "); | 61 | add_keyword("where", "where "); |
57 | return; | 62 | return; |
58 | } | 63 | } |
59 | if ctx.previous_token_is(T![unsafe]) { | 64 | if ctx.previous_token_is(T![unsafe]) { |
60 | if has_item_list_parent || has_block_expr_parent { | 65 | if expects_item || expects_assoc_item || has_block_expr_parent { |
61 | add_keyword(ctx, acc, "fn", "fn $1($2) {\n $0\n}") | 66 | add_keyword("fn", "fn $1($2) {\n $0\n}") |
62 | } | 67 | } |
63 | 68 | ||
64 | if has_item_list_parent || has_block_expr_parent { | 69 | if expects_item || has_block_expr_parent { |
65 | add_keyword(ctx, acc, "trait", "trait $1 {\n $0\n}"); | 70 | add_keyword("trait", "trait $1 {\n $0\n}"); |
66 | add_keyword(ctx, acc, "impl", "impl $1 {\n $0\n}"); | 71 | add_keyword("impl", "impl $1 {\n $0\n}"); |
67 | } | 72 | } |
68 | 73 | ||
69 | return; | 74 | return; |
70 | } | 75 | } |
71 | if has_item_list_parent || has_trait_or_impl_parent || has_block_expr_parent { | ||
72 | add_keyword(ctx, acc, "fn", "fn $1($2) {\n $0\n}"); | ||
73 | } | ||
74 | if has_item_list_parent || has_block_expr_parent { | ||
75 | add_keyword(ctx, acc, "use", "use "); | ||
76 | add_keyword(ctx, acc, "impl", "impl $1 {\n $0\n}"); | ||
77 | add_keyword(ctx, acc, "trait", "trait $1 {\n $0\n}"); | ||
78 | } | ||
79 | 76 | ||
80 | if has_item_list_parent { | 77 | if expects_item || ctx.expects_non_trait_assoc_item() || ctx.expect_record_field() { |
81 | add_keyword(ctx, acc, "enum", "enum $1 {\n $0\n}"); | 78 | add_keyword("pub(crate)", "pub(crate) "); |
82 | add_keyword(ctx, acc, "struct", "struct $0"); | 79 | add_keyword("pub", "pub "); |
83 | add_keyword(ctx, acc, "union", "union $1 {\n $0\n}"); | ||
84 | } | 80 | } |
85 | 81 | ||
86 | if ctx.is_expr { | 82 | if expects_item || expects_assoc_item || has_block_expr_parent { |
87 | add_keyword(ctx, acc, "match", "match $1 {\n $0\n}"); | 83 | add_keyword("unsafe", "unsafe "); |
88 | add_keyword(ctx, acc, "while", "while $1 {\n $0\n}"); | 84 | add_keyword("fn", "fn $1($2) {\n $0\n}"); |
89 | add_keyword(ctx, acc, "while let", "while let $1 = $2 {\n $0\n}"); | 85 | add_keyword("const", "const $0"); |
90 | add_keyword(ctx, acc, "loop", "loop {\n $0\n}"); | 86 | add_keyword("type", "type $0"); |
91 | add_keyword(ctx, acc, "if", "if $1 {\n $0\n}"); | ||
92 | add_keyword(ctx, acc, "if let", "if let $1 = $2 {\n $0\n}"); | ||
93 | add_keyword(ctx, acc, "for", "for $1 in $2 {\n $0\n}"); | ||
94 | } | 87 | } |
95 | 88 | ||
96 | if ctx.previous_token_is(T![if]) || ctx.previous_token_is(T![while]) || has_block_expr_parent { | 89 | if expects_item || has_block_expr_parent { |
97 | add_keyword(ctx, acc, "let", "let "); | 90 | add_keyword("use", "use $0"); |
91 | add_keyword("impl", "impl $1 {\n $0\n}"); | ||
92 | add_keyword("trait", "trait $1 {\n $0\n}"); | ||
93 | add_keyword("static", "static $0"); | ||
94 | add_keyword("extern", "extern $0"); | ||
95 | add_keyword("mod", "mod $0"); | ||
98 | } | 96 | } |
99 | 97 | ||
100 | if ctx.after_if { | 98 | if expects_item { |
101 | add_keyword(ctx, acc, "else", "else {\n $0\n}"); | 99 | add_keyword("enum", "enum $1 {\n $0\n}"); |
102 | add_keyword(ctx, acc, "else if", "else if $1 {\n $0\n}"); | 100 | add_keyword("struct", "struct $0"); |
103 | } | 101 | add_keyword("union", "union $1 {\n $0\n}"); |
104 | if has_item_list_parent || has_block_expr_parent { | ||
105 | add_keyword(ctx, acc, "mod", "mod $0"); | ||
106 | } | 102 | } |
107 | if ctx.has_ident_or_ref_pat_parent() { | 103 | |
108 | add_keyword(ctx, acc, "mut", "mut "); | 104 | if ctx.expects_expression() { |
105 | if !has_block_expr_parent { | ||
106 | add_keyword("unsafe", "unsafe {\n $0\n}"); | ||
107 | } | ||
108 | add_keyword("match", "match $1 {\n $0\n}"); | ||
109 | add_keyword("while", "while $1 {\n $0\n}"); | ||
110 | add_keyword("while let", "while let $1 = $2 {\n $0\n}"); | ||
111 | add_keyword("loop", "loop {\n $0\n}"); | ||
112 | add_keyword("if", "if $1 {\n $0\n}"); | ||
113 | add_keyword("if let", "if let $1 = $2 {\n $0\n}"); | ||
114 | add_keyword("for", "for $1 in $2 {\n $0\n}"); | ||
109 | } | 115 | } |
110 | if has_item_list_parent || has_trait_or_impl_parent || has_block_expr_parent { | 116 | |
111 | add_keyword(ctx, acc, "const", "const "); | 117 | if ctx.previous_token_is(T![if]) || ctx.previous_token_is(T![while]) || has_block_expr_parent { |
112 | add_keyword(ctx, acc, "type", "type "); | 118 | add_keyword("let", "let "); |
113 | } | 119 | } |
114 | if has_item_list_parent || has_block_expr_parent { | 120 | |
115 | add_keyword(ctx, acc, "static", "static "); | 121 | if ctx.after_if() { |
116 | }; | 122 | add_keyword("else", "else {\n $0\n}"); |
117 | if has_item_list_parent || has_block_expr_parent { | 123 | add_keyword("else if", "else if $1 {\n $0\n}"); |
118 | add_keyword(ctx, acc, "extern", "extern "); | ||
119 | } | 124 | } |
120 | if has_item_list_parent || has_trait_or_impl_parent || has_block_expr_parent || ctx.is_match_arm | 125 | |
121 | { | 126 | if ctx.expects_ident_pat_or_ref_expr() { |
122 | add_keyword(ctx, acc, "unsafe", "unsafe "); | 127 | add_keyword("mut", "mut "); |
123 | } | 128 | } |
129 | |||
124 | if ctx.in_loop_body { | 130 | if ctx.in_loop_body { |
125 | if ctx.can_be_stmt { | 131 | if ctx.can_be_stmt { |
126 | add_keyword(ctx, acc, "continue", "continue;"); | 132 | add_keyword("continue", "continue;"); |
127 | add_keyword(ctx, acc, "break", "break;"); | 133 | add_keyword("break", "break;"); |
128 | } else { | 134 | } else { |
129 | add_keyword(ctx, acc, "continue", "continue"); | 135 | add_keyword("continue", "continue"); |
130 | add_keyword(ctx, acc, "break", "break"); | 136 | add_keyword("break", "break"); |
131 | } | 137 | } |
132 | } | 138 | } |
133 | if has_item_list_parent || ctx.has_impl_parent() || ctx.has_field_list_parent() { | ||
134 | add_keyword(ctx, acc, "pub(crate)", "pub(crate) "); | ||
135 | add_keyword(ctx, acc, "pub", "pub "); | ||
136 | } | ||
137 | 139 | ||
138 | if !ctx.is_trivial_path { | 140 | if !ctx.is_trivial_path { |
139 | return; | 141 | return; |
@@ -144,8 +146,6 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte | |||
144 | }; | 146 | }; |
145 | 147 | ||
146 | add_keyword( | 148 | add_keyword( |
147 | ctx, | ||
148 | acc, | ||
149 | "return", | 149 | "return", |
150 | match (ctx.can_be_stmt, fn_def.ret_type().is_some()) { | 150 | match (ctx.can_be_stmt, fn_def.ret_type().is_some()) { |
151 | (true, true) => "return $0;", | 151 | (true, true) => "return $0;", |
@@ -162,15 +162,12 @@ fn add_keyword(ctx: &CompletionContext, acc: &mut Completions, kw: &str, snippet | |||
162 | 162 | ||
163 | match ctx.config.snippet_cap { | 163 | match ctx.config.snippet_cap { |
164 | Some(cap) => { | 164 | Some(cap) => { |
165 | let tmp; | 165 | if snippet.ends_with('}') && ctx.incomplete_let { |
166 | let snippet = if snippet.ends_with('}') && ctx.incomplete_let { | ||
167 | cov_mark::hit!(let_semi); | 166 | cov_mark::hit!(let_semi); |
168 | tmp = format!("{};", snippet); | 167 | item.insert_snippet(cap, format!("{};", snippet)); |
169 | &tmp | ||
170 | } else { | 168 | } else { |
171 | snippet | 169 | item.insert_snippet(cap, snippet); |
172 | }; | 170 | } |
173 | item.insert_snippet(cap, snippet); | ||
174 | } | 171 | } |
175 | None => { | 172 | None => { |
176 | item.insert_text(if snippet.contains('$') { kw } else { snippet }); | 173 | item.insert_text(if snippet.contains('$') { kw } else { snippet }); |
@@ -233,21 +230,21 @@ mod tests { | |||
233 | check( | 230 | check( |
234 | r"m$0", | 231 | r"m$0", |
235 | expect![[r#" | 232 | expect![[r#" |
233 | kw pub(crate) | ||
234 | kw pub | ||
235 | kw unsafe | ||
236 | kw fn | 236 | kw fn |
237 | kw const | ||
238 | kw type | ||
237 | kw use | 239 | kw use |
238 | kw impl | 240 | kw impl |
239 | kw trait | 241 | kw trait |
242 | kw static | ||
243 | kw extern | ||
244 | kw mod | ||
240 | kw enum | 245 | kw enum |
241 | kw struct | 246 | kw struct |
242 | kw union | 247 | kw union |
243 | kw mod | ||
244 | kw const | ||
245 | kw type | ||
246 | kw static | ||
247 | kw extern | ||
248 | kw unsafe | ||
249 | kw pub(crate) | ||
250 | kw pub | ||
251 | "#]], | 248 | "#]], |
252 | ); | 249 | ); |
253 | } | 250 | } |
@@ -257,10 +254,16 @@ mod tests { | |||
257 | check( | 254 | check( |
258 | r"fn quux() { $0 }", | 255 | r"fn quux() { $0 }", |
259 | expect![[r#" | 256 | expect![[r#" |
257 | kw unsafe | ||
260 | kw fn | 258 | kw fn |
259 | kw const | ||
260 | kw type | ||
261 | kw use | 261 | kw use |
262 | kw impl | 262 | kw impl |
263 | kw trait | 263 | kw trait |
264 | kw static | ||
265 | kw extern | ||
266 | kw mod | ||
264 | kw match | 267 | kw match |
265 | kw while | 268 | kw while |
266 | kw while let | 269 | kw while let |
@@ -269,12 +272,6 @@ mod tests { | |||
269 | kw if let | 272 | kw if let |
270 | kw for | 273 | kw for |
271 | kw let | 274 | kw let |
272 | kw mod | ||
273 | kw const | ||
274 | kw type | ||
275 | kw static | ||
276 | kw extern | ||
277 | kw unsafe | ||
278 | kw return | 275 | kw return |
279 | "#]], | 276 | "#]], |
280 | ); | 277 | ); |
@@ -285,10 +282,16 @@ mod tests { | |||
285 | check( | 282 | check( |
286 | r"fn quux() { if true { $0 } }", | 283 | r"fn quux() { if true { $0 } }", |
287 | expect![[r#" | 284 | expect![[r#" |
285 | kw unsafe | ||
288 | kw fn | 286 | kw fn |
287 | kw const | ||
288 | kw type | ||
289 | kw use | 289 | kw use |
290 | kw impl | 290 | kw impl |
291 | kw trait | 291 | kw trait |
292 | kw static | ||
293 | kw extern | ||
294 | kw mod | ||
292 | kw match | 295 | kw match |
293 | kw while | 296 | kw while |
294 | kw while let | 297 | kw while let |
@@ -297,12 +300,6 @@ mod tests { | |||
297 | kw if let | 300 | kw if let |
298 | kw for | 301 | kw for |
299 | kw let | 302 | kw let |
300 | kw mod | ||
301 | kw const | ||
302 | kw type | ||
303 | kw static | ||
304 | kw extern | ||
305 | kw unsafe | ||
306 | kw return | 303 | kw return |
307 | "#]], | 304 | "#]], |
308 | ); | 305 | ); |
@@ -313,10 +310,16 @@ mod tests { | |||
313 | check( | 310 | check( |
314 | r#"fn quux() { if true { () } $0 }"#, | 311 | r#"fn quux() { if true { () } $0 }"#, |
315 | expect![[r#" | 312 | expect![[r#" |
313 | kw unsafe | ||
316 | kw fn | 314 | kw fn |
315 | kw const | ||
316 | kw type | ||
317 | kw use | 317 | kw use |
318 | kw impl | 318 | kw impl |
319 | kw trait | 319 | kw trait |
320 | kw static | ||
321 | kw extern | ||
322 | kw mod | ||
320 | kw match | 323 | kw match |
321 | kw while | 324 | kw while |
322 | kw while let | 325 | kw while let |
@@ -327,12 +330,6 @@ mod tests { | |||
327 | kw let | 330 | kw let |
328 | kw else | 331 | kw else |
329 | kw else if | 332 | kw else if |
330 | kw mod | ||
331 | kw const | ||
332 | kw type | ||
333 | kw static | ||
334 | kw extern | ||
335 | kw unsafe | ||
336 | kw return | 333 | kw return |
337 | "#]], | 334 | "#]], |
338 | ); | 335 | ); |
@@ -354,6 +351,7 @@ fn quux() -> i32 { | |||
354 | } | 351 | } |
355 | "#, | 352 | "#, |
356 | expect![[r#" | 353 | expect![[r#" |
354 | kw unsafe | ||
357 | kw match | 355 | kw match |
358 | kw while | 356 | kw while |
359 | kw while let | 357 | kw while let |
@@ -361,7 +359,6 @@ fn quux() -> i32 { | |||
361 | kw if | 359 | kw if |
362 | kw if let | 360 | kw if let |
363 | kw for | 361 | kw for |
364 | kw unsafe | ||
365 | kw return | 362 | kw return |
366 | "#]], | 363 | "#]], |
367 | ); | 364 | ); |
@@ -372,10 +369,10 @@ fn quux() -> i32 { | |||
372 | check( | 369 | check( |
373 | r"trait My { $0 }", | 370 | r"trait My { $0 }", |
374 | expect![[r#" | 371 | expect![[r#" |
372 | kw unsafe | ||
375 | kw fn | 373 | kw fn |
376 | kw const | 374 | kw const |
377 | kw type | 375 | kw type |
378 | kw unsafe | ||
379 | "#]], | 376 | "#]], |
380 | ); | 377 | ); |
381 | } | 378 | } |
@@ -385,12 +382,27 @@ fn quux() -> i32 { | |||
385 | check( | 382 | check( |
386 | r"impl My { $0 }", | 383 | r"impl My { $0 }", |
387 | expect![[r#" | 384 | expect![[r#" |
385 | kw pub(crate) | ||
386 | kw pub | ||
387 | kw unsafe | ||
388 | kw fn | 388 | kw fn |
389 | kw const | 389 | kw const |
390 | kw type | 390 | kw type |
391 | kw unsafe | 391 | "#]], |
392 | ); | ||
393 | } | ||
394 | |||
395 | #[test] | ||
396 | fn test_keywords_in_impl_def_with_attr() { | ||
397 | check( | ||
398 | r"impl My { #[foo] $0 }", | ||
399 | expect![[r#" | ||
392 | kw pub(crate) | 400 | kw pub(crate) |
393 | kw pub | 401 | kw pub |
402 | kw unsafe | ||
403 | kw fn | ||
404 | kw const | ||
405 | kw type | ||
394 | "#]], | 406 | "#]], |
395 | ); | 407 | ); |
396 | } | 408 | } |
@@ -400,10 +412,16 @@ fn quux() -> i32 { | |||
400 | check( | 412 | check( |
401 | r"fn my() { loop { $0 } }", | 413 | r"fn my() { loop { $0 } }", |
402 | expect![[r#" | 414 | expect![[r#" |
415 | kw unsafe | ||
403 | kw fn | 416 | kw fn |
417 | kw const | ||
418 | kw type | ||
404 | kw use | 419 | kw use |
405 | kw impl | 420 | kw impl |
406 | kw trait | 421 | kw trait |
422 | kw static | ||
423 | kw extern | ||
424 | kw mod | ||
407 | kw match | 425 | kw match |
408 | kw while | 426 | kw while |
409 | kw while let | 427 | kw while let |
@@ -412,12 +430,6 @@ fn quux() -> i32 { | |||
412 | kw if let | 430 | kw if let |
413 | kw for | 431 | kw for |
414 | kw let | 432 | kw let |
415 | kw mod | ||
416 | kw const | ||
417 | kw type | ||
418 | kw static | ||
419 | kw extern | ||
420 | kw unsafe | ||
421 | kw continue | 433 | kw continue |
422 | kw break | 434 | kw break |
423 | kw return | 435 | kw return |
@@ -564,6 +576,7 @@ pub mod future { | |||
564 | check( | 576 | check( |
565 | r#"fn main() { let _ = $0 }"#, | 577 | r#"fn main() { let _ = $0 }"#, |
566 | expect![[r#" | 578 | expect![[r#" |
579 | kw unsafe | ||
567 | kw match | 580 | kw match |
568 | kw while | 581 | kw while |
569 | kw while let | 582 | kw while let |
@@ -624,6 +637,7 @@ fn foo() { | |||
624 | } | 637 | } |
625 | "#, | 638 | "#, |
626 | expect![[r#" | 639 | expect![[r#" |
640 | kw unsafe | ||
627 | kw match | 641 | kw match |
628 | kw while | 642 | kw while |
629 | kw while let | 643 | kw while let |
diff --git a/crates/ide_completion/src/completions/macro_in_item_position.rs b/crates/ide_completion/src/completions/macro_in_item_position.rs index c5e377500..ec57aee30 100644 --- a/crates/ide_completion/src/completions/macro_in_item_position.rs +++ b/crates/ide_completion/src/completions/macro_in_item_position.rs | |||
@@ -2,6 +2,7 @@ | |||
2 | 2 | ||
3 | use crate::{CompletionContext, Completions}; | 3 | use crate::{CompletionContext, Completions}; |
4 | 4 | ||
5 | // Ideally this should be removed and moved into `(un)qualified_path` respectively | ||
5 | pub(crate) fn complete_macro_in_item_position(acc: &mut Completions, ctx: &CompletionContext) { | 6 | pub(crate) fn complete_macro_in_item_position(acc: &mut Completions, ctx: &CompletionContext) { |
6 | // Show only macros in top level. | 7 | // Show only macros in top level. |
7 | if !ctx.is_new_item { | 8 | if !ctx.is_new_item { |
@@ -12,6 +13,10 @@ pub(crate) fn complete_macro_in_item_position(acc: &mut Completions, ctx: &Compl | |||
12 | if let hir::ScopeDef::MacroDef(mac) = res { | 13 | if let hir::ScopeDef::MacroDef(mac) = res { |
13 | acc.add_macro(ctx, Some(name.to_string()), mac); | 14 | acc.add_macro(ctx, Some(name.to_string()), mac); |
14 | } | 15 | } |
16 | // FIXME: This should be done in qualified_path/unqualified_path instead? | ||
17 | if let hir::ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) = res { | ||
18 | acc.add_resolution(ctx, name.to_string(), &res); | ||
19 | } | ||
15 | }) | 20 | }) |
16 | } | 21 | } |
17 | 22 | ||
diff --git a/crates/ide_completion/src/completions/mod_.rs b/crates/ide_completion/src/completions/mod_.rs index 4f9415736..6a5746fb9 100644 --- a/crates/ide_completion/src/completions/mod_.rs +++ b/crates/ide_completion/src/completions/mod_.rs | |||
@@ -9,14 +9,14 @@ use ide_db::{ | |||
9 | }; | 9 | }; |
10 | use rustc_hash::FxHashSet; | 10 | use rustc_hash::FxHashSet; |
11 | 11 | ||
12 | use crate::CompletionItem; | 12 | use crate::{patterns::ImmediateLocation, CompletionItem}; |
13 | 13 | ||
14 | use crate::{context::CompletionContext, item::CompletionKind, Completions}; | 14 | use crate::{context::CompletionContext, item::CompletionKind, Completions}; |
15 | 15 | ||
16 | /// Complete mod declaration, i.e. `mod $0 ;` | 16 | /// Complete mod declaration, i.e. `mod $0 ;` |
17 | pub(crate) fn complete_mod(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> { | 17 | pub(crate) fn complete_mod(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> { |
18 | let mod_under_caret = match &ctx.mod_declaration_under_caret { | 18 | let mod_under_caret = match &ctx.completion_location { |
19 | Some(mod_under_caret) if mod_under_caret.item_list().is_none() => mod_under_caret, | 19 | Some(ImmediateLocation::ModDeclaration(mod_under_caret)) => mod_under_caret, |
20 | _ => return None, | 20 | _ => return None, |
21 | }; | 21 | }; |
22 | 22 | ||
diff --git a/crates/ide_completion/src/completions/qualified_path.rs b/crates/ide_completion/src/completions/qualified_path.rs index ed48f61af..7a0e1ead3 100644 --- a/crates/ide_completion/src/completions/qualified_path.rs +++ b/crates/ide_completion/src/completions/qualified_path.rs | |||
@@ -7,7 +7,7 @@ use syntax::AstNode; | |||
7 | use crate::{CompletionContext, Completions}; | 7 | use crate::{CompletionContext, Completions}; |
8 | 8 | ||
9 | pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionContext) { | 9 | pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionContext) { |
10 | if ctx.is_path_disallowed() { | 10 | if ctx.is_path_disallowed() || ctx.expects_item() { |
11 | return; | 11 | return; |
12 | } | 12 | } |
13 | let path = match &ctx.path_qual { | 13 | let path = match &ctx.path_qual { |
@@ -20,6 +20,20 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon | |||
20 | None => return, | 20 | None => return, |
21 | }; | 21 | }; |
22 | let context_module = ctx.scope.module(); | 22 | let context_module = ctx.scope.module(); |
23 | if ctx.expects_assoc_item() { | ||
24 | if let PathResolution::Def(hir::ModuleDef::Module(module)) = resolution { | ||
25 | let module_scope = module.scope(ctx.db, context_module); | ||
26 | for (name, def) in module_scope { | ||
27 | if let ScopeDef::MacroDef(macro_def) = def { | ||
28 | acc.add_macro(ctx, Some(name.to_string()), macro_def); | ||
29 | } | ||
30 | if let ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) = def { | ||
31 | acc.add_resolution(ctx, name.to_string(), &def); | ||
32 | } | ||
33 | } | ||
34 | } | ||
35 | return; | ||
36 | } | ||
23 | 37 | ||
24 | // Add associated types on type parameters and `Self`. | 38 | // Add associated types on type parameters and `Self`. |
25 | resolution.assoc_type_shorthand_candidates(ctx.db, |_, alias| { | 39 | resolution.assoc_type_shorthand_candidates(ctx.db, |_, alias| { |
@@ -594,7 +608,7 @@ fn main() { T::$0; } | |||
594 | macro_rules! foo { () => {} } | 608 | macro_rules! foo { () => {} } |
595 | 609 | ||
596 | fn main() { let _ = crate::$0 } | 610 | fn main() { let _ = crate::$0 } |
597 | "#, | 611 | "#, |
598 | expect![[r##" | 612 | expect![[r##" |
599 | fn main() fn() | 613 | fn main() fn() |
600 | ma foo!(…) #[macro_export] macro_rules! foo | 614 | ma foo!(…) #[macro_export] macro_rules! foo |
@@ -603,6 +617,44 @@ fn main() { let _ = crate::$0 } | |||
603 | } | 617 | } |
604 | 618 | ||
605 | #[test] | 619 | #[test] |
620 | fn completes_in_assoc_item_list() { | ||
621 | check( | ||
622 | r#" | ||
623 | #[macro_export] | ||
624 | macro_rules! foo { () => {} } | ||
625 | mod bar {} | ||
626 | |||
627 | struct MyStruct {} | ||
628 | impl MyStruct { | ||
629 | crate::$0 | ||
630 | } | ||
631 | "#, | ||
632 | expect![[r##" | ||
633 | md bar | ||
634 | ma foo! #[macro_export] macro_rules! foo | ||
635 | "##]], | ||
636 | ); | ||
637 | } | ||
638 | |||
639 | #[test] | ||
640 | #[ignore] // FIXME doesn't complete anything atm | ||
641 | fn completes_in_item_list() { | ||
642 | check( | ||
643 | r#" | ||
644 | struct MyStruct {} | ||
645 | macro_rules! foo {} | ||
646 | mod bar {} | ||
647 | |||
648 | crate::$0 | ||
649 | "#, | ||
650 | expect![[r#" | ||
651 | md bar | ||
652 | ma foo! macro_rules! foo | ||
653 | "#]], | ||
654 | ) | ||
655 | } | ||
656 | |||
657 | #[test] | ||
606 | fn test_super_super_completion() { | 658 | fn test_super_super_completion() { |
607 | check( | 659 | check( |
608 | r#" | 660 | r#" |
diff --git a/crates/ide_completion/src/completions/record.rs b/crates/ide_completion/src/completions/record.rs index e1526b70b..227c08d01 100644 --- a/crates/ide_completion/src/completions/record.rs +++ b/crates/ide_completion/src/completions/record.rs | |||
@@ -2,21 +2,21 @@ | |||
2 | use ide_db::{helpers::FamousDefs, SymbolKind}; | 2 | use ide_db::{helpers::FamousDefs, SymbolKind}; |
3 | use syntax::ast::Expr; | 3 | use syntax::ast::Expr; |
4 | 4 | ||
5 | use crate::{item::CompletionKind, CompletionContext, CompletionItem, Completions}; | 5 | use crate::{ |
6 | item::CompletionKind, patterns::ImmediateLocation, CompletionContext, CompletionItem, | ||
7 | Completions, | ||
8 | }; | ||
6 | 9 | ||
7 | pub(crate) fn complete_record(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> { | 10 | pub(crate) fn complete_record(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> { |
8 | let missing_fields = match (ctx.record_pat_syntax.as_ref(), ctx.record_lit_syntax.as_ref()) { | 11 | let missing_fields = match &ctx.completion_location { |
9 | (None, None) => return None, | 12 | Some(ImmediateLocation::RecordExpr(record_expr)) => { |
10 | (Some(_), Some(_)) => unreachable!("A record cannot be both a literal and a pattern"), | 13 | let ty = ctx.sema.type_of_expr(&Expr::RecordExpr(record_expr.clone())); |
11 | (Some(record_pat), _) => ctx.sema.record_pattern_missing_fields(record_pat), | ||
12 | (_, Some(record_lit)) => { | ||
13 | let ty = ctx.sema.type_of_expr(&Expr::RecordExpr(record_lit.clone())); | ||
14 | let default_trait = FamousDefs(&ctx.sema, ctx.krate).core_default_Default(); | 14 | let default_trait = FamousDefs(&ctx.sema, ctx.krate).core_default_Default(); |
15 | let impl_default_trait = default_trait | 15 | let impl_default_trait = default_trait |
16 | .zip(ty) | 16 | .zip(ty) |
17 | .map_or(false, |(default_trait, ty)| ty.impls_trait(ctx.db, default_trait, &[])); | 17 | .map_or(false, |(default_trait, ty)| ty.impls_trait(ctx.db, default_trait, &[])); |
18 | 18 | ||
19 | let missing_fields = ctx.sema.record_literal_missing_fields(record_lit); | 19 | let missing_fields = ctx.sema.record_literal_missing_fields(record_expr); |
20 | if impl_default_trait && !missing_fields.is_empty() { | 20 | if impl_default_trait && !missing_fields.is_empty() { |
21 | let completion_text = "..Default::default()"; | 21 | let completion_text = "..Default::default()"; |
22 | let mut item = CompletionItem::new( | 22 | let mut item = CompletionItem::new( |
@@ -32,6 +32,10 @@ pub(crate) fn complete_record(acc: &mut Completions, ctx: &CompletionContext) -> | |||
32 | 32 | ||
33 | missing_fields | 33 | missing_fields |
34 | } | 34 | } |
35 | Some(ImmediateLocation::RecordPat(record_pat)) => { | ||
36 | ctx.sema.record_pattern_missing_fields(record_pat) | ||
37 | } | ||
38 | _ => return None, | ||
35 | }; | 39 | }; |
36 | 40 | ||
37 | for (field, ty) in missing_fields { | 41 | for (field, ty) in missing_fields { |
diff --git a/crates/ide_completion/src/completions/unqualified_path.rs b/crates/ide_completion/src/completions/unqualified_path.rs index 046a393ae..ede07f605 100644 --- a/crates/ide_completion/src/completions/unqualified_path.rs +++ b/crates/ide_completion/src/completions/unqualified_path.rs | |||
@@ -1,7 +1,6 @@ | |||
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 hir::ScopeDef; | 3 | use hir::ScopeDef; |
4 | use syntax::AstNode; | ||
5 | 4 | ||
6 | use crate::{CompletionContext, Completions}; | 5 | use crate::{CompletionContext, Completions}; |
7 | 6 | ||
@@ -9,10 +8,30 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC | |||
9 | if !ctx.is_trivial_path { | 8 | if !ctx.is_trivial_path { |
10 | return; | 9 | return; |
11 | } | 10 | } |
12 | if ctx.is_path_disallowed() { | 11 | if ctx.is_path_disallowed() || ctx.expects_item() { |
12 | return; | ||
13 | } | ||
14 | if ctx.expects_assoc_item() { | ||
15 | ctx.scope.process_all_names(&mut |name, def| { | ||
16 | if let ScopeDef::MacroDef(macro_def) = def { | ||
17 | acc.add_macro(ctx, Some(name.to_string()), macro_def); | ||
18 | } | ||
19 | if let ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) = def { | ||
20 | acc.add_resolution(ctx, name.to_string(), &def); | ||
21 | } | ||
22 | }); | ||
13 | return; | 23 | return; |
14 | } | 24 | } |
15 | 25 | ||
26 | if ctx.expects_use_tree() { | ||
27 | cov_mark::hit!(only_completes_modules_in_import); | ||
28 | ctx.scope.process_all_names(&mut |name, res| { | ||
29 | if let ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) = res { | ||
30 | acc.add_resolution(ctx, name.to_string(), &res); | ||
31 | } | ||
32 | }); | ||
33 | return; | ||
34 | } | ||
16 | if let Some(hir::Adt::Enum(e)) = | 35 | if let Some(hir::Adt::Enum(e)) = |
17 | ctx.expected_type.as_ref().and_then(|ty| ty.strip_references().as_adt()) | 36 | ctx.expected_type.as_ref().and_then(|ty| ty.strip_references().as_adt()) |
18 | { | 37 | { |
@@ -26,14 +45,6 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC | |||
26 | cov_mark::hit!(skip_lifetime_completion); | 45 | cov_mark::hit!(skip_lifetime_completion); |
27 | return; | 46 | return; |
28 | } | 47 | } |
29 | if ctx.use_item_syntax.is_some() { | ||
30 | if let (ScopeDef::Unknown, Some(name_ref)) = (&res, &ctx.name_ref_syntax) { | ||
31 | if name_ref.syntax().text() == name.to_string().as_str() { | ||
32 | cov_mark::hit!(self_fulfilling_completion); | ||
33 | return; | ||
34 | } | ||
35 | } | ||
36 | } | ||
37 | acc.add_resolution(ctx, name.to_string(), &res); | 48 | acc.add_resolution(ctx, name.to_string(), &res); |
38 | }); | 49 | }); |
39 | } | 50 | } |
@@ -57,15 +68,17 @@ mod tests { | |||
57 | } | 68 | } |
58 | 69 | ||
59 | #[test] | 70 | #[test] |
60 | fn self_fulfilling_completion() { | 71 | fn only_completes_modules_in_import() { |
61 | cov_mark::check!(self_fulfilling_completion); | 72 | cov_mark::check!(only_completes_modules_in_import); |
62 | check( | 73 | check( |
63 | r#" | 74 | r#" |
64 | use foo$0 | 75 | use f$0 |
65 | use std::collections; | 76 | |
77 | struct Foo; | ||
78 | mod foo {} | ||
66 | "#, | 79 | "#, |
67 | expect![[r#" | 80 | expect![[r#" |
68 | ?? collections | 81 | md foo |
69 | "#]], | 82 | "#]], |
70 | ); | 83 | ); |
71 | } | 84 | } |
@@ -647,7 +660,7 @@ fn f() {} | |||
647 | } | 660 | } |
648 | 661 | ||
649 | #[test] | 662 | #[test] |
650 | fn completes_type_or_trait_in_impl_block() { | 663 | fn completes_target_type_or_trait_in_impl_block() { |
651 | check( | 664 | check( |
652 | r#" | 665 | r#" |
653 | trait MyTrait {} | 666 | trait MyTrait {} |
@@ -662,4 +675,41 @@ impl My$0 | |||
662 | "#]], | 675 | "#]], |
663 | ) | 676 | ) |
664 | } | 677 | } |
678 | |||
679 | #[test] | ||
680 | fn completes_in_assoc_item_list() { | ||
681 | check( | ||
682 | r#" | ||
683 | macro_rules! foo {} | ||
684 | mod bar {} | ||
685 | |||
686 | struct MyStruct {} | ||
687 | impl MyStruct { | ||
688 | $0 | ||
689 | } | ||
690 | "#, | ||
691 | expect![[r#" | ||
692 | md bar | ||
693 | ma foo! macro_rules! foo | ||
694 | "#]], | ||
695 | ) | ||
696 | } | ||
697 | |||
698 | // FIXME: The completions here currently come from `macro_in_item_position`, but they shouldn't | ||
699 | #[test] | ||
700 | fn completes_in_item_list() { | ||
701 | check( | ||
702 | r#" | ||
703 | struct MyStruct {} | ||
704 | macro_rules! foo {} | ||
705 | mod bar {} | ||
706 | |||
707 | $0 | ||
708 | "#, | ||
709 | expect![[r#" | ||
710 | md bar | ||
711 | ma foo!(…) macro_rules! foo | ||
712 | "#]], | ||
713 | ) | ||
714 | } | ||
665 | } | 715 | } |
diff --git a/crates/ide_completion/src/context.rs b/crates/ide_completion/src/context.rs index 66577df94..7c46c815d 100644 --- a/crates/ide_completion/src/context.rs +++ b/crates/ide_completion/src/context.rs | |||
@@ -17,9 +17,8 @@ use text_edit::Indel; | |||
17 | 17 | ||
18 | use crate::{ | 18 | use crate::{ |
19 | patterns::{ | 19 | patterns::{ |
20 | for_is_prev2, has_bind_pat_parent, has_block_expr_parent, has_field_list_parent, | 20 | determine_location, determine_prev_sibling, for_is_prev2, inside_impl_trait_block, |
21 | has_impl_parent, has_item_list_or_source_file_parent, has_prev_sibling, has_ref_parent, | 21 | is_in_loop_body, previous_token, ImmediateLocation, ImmediatePrevSibling, |
22 | has_trait_parent, inside_impl_trait_block, is_in_loop_body, is_match_arm, previous_token, | ||
23 | }, | 22 | }, |
24 | CompletionConfig, | 23 | CompletionConfig, |
25 | }; | 24 | }; |
@@ -30,24 +29,6 @@ pub(crate) enum PatternRefutability { | |||
30 | Irrefutable, | 29 | Irrefutable, |
31 | } | 30 | } |
32 | 31 | ||
33 | /// Direct parent container of the cursor position | ||
34 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] | ||
35 | pub(crate) enum ImmediateLocation { | ||
36 | Impl, | ||
37 | Trait, | ||
38 | RecordFieldList, | ||
39 | RefPatOrExpr, | ||
40 | IdentPat, | ||
41 | BlockExpr, | ||
42 | ItemList, | ||
43 | } | ||
44 | |||
45 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] | ||
46 | pub(crate) enum PrevSibling { | ||
47 | Trait, | ||
48 | Impl, | ||
49 | } | ||
50 | |||
51 | /// `CompletionContext` is created early during completion to figure out, where | 32 | /// `CompletionContext` is created early during completion to figure out, where |
52 | /// exactly is the cursor, syntax-wise. | 33 | /// exactly is the cursor, syntax-wise. |
53 | #[derive(Debug)] | 34 | #[derive(Debug)] |
@@ -73,11 +54,6 @@ pub(crate) struct CompletionContext<'a> { | |||
73 | /// The parent impl of the cursor position if it exists. | 54 | /// The parent impl of the cursor position if it exists. |
74 | pub(super) impl_def: Option<ast::Impl>, | 55 | pub(super) impl_def: Option<ast::Impl>, |
75 | 56 | ||
76 | /// RecordExpr the token is a field of | ||
77 | pub(super) record_lit_syntax: Option<ast::RecordExpr>, | ||
78 | /// RecordPat the token is a field of | ||
79 | pub(super) record_pat_syntax: Option<ast::RecordPat>, | ||
80 | |||
81 | // potentially set if we are completing a lifetime | 57 | // potentially set if we are completing a lifetime |
82 | pub(super) lifetime_syntax: Option<ast::Lifetime>, | 58 | pub(super) lifetime_syntax: Option<ast::Lifetime>, |
83 | pub(super) lifetime_param_syntax: Option<ast::LifetimeParam>, | 59 | pub(super) lifetime_param_syntax: Option<ast::LifetimeParam>, |
@@ -89,6 +65,8 @@ pub(crate) struct CompletionContext<'a> { | |||
89 | pub(super) is_param: bool, | 65 | pub(super) is_param: bool, |
90 | 66 | ||
91 | pub(super) completion_location: Option<ImmediateLocation>, | 67 | pub(super) completion_location: Option<ImmediateLocation>, |
68 | pub(super) prev_sibling: Option<ImmediatePrevSibling>, | ||
69 | pub(super) attribute_under_caret: Option<ast::Attr>, | ||
92 | 70 | ||
93 | /// FIXME: `ActiveParameter` is string-based, which is very very wrong | 71 | /// FIXME: `ActiveParameter` is string-based, which is very very wrong |
94 | pub(super) active_parameter: Option<ActiveParameter>, | 72 | pub(super) active_parameter: Option<ActiveParameter>, |
@@ -96,7 +74,6 @@ pub(crate) struct CompletionContext<'a> { | |||
96 | pub(super) is_trivial_path: bool, | 74 | pub(super) is_trivial_path: bool, |
97 | /// If not a trivial path, the prefix (qualifier). | 75 | /// If not a trivial path, the prefix (qualifier). |
98 | pub(super) path_qual: Option<ast::Path>, | 76 | pub(super) path_qual: Option<ast::Path>, |
99 | pub(super) after_if: bool, | ||
100 | /// `true` if we are a statement or a last expr in the block. | 77 | /// `true` if we are a statement or a last expr in the block. |
101 | pub(super) can_be_stmt: bool, | 78 | pub(super) can_be_stmt: bool, |
102 | /// `true` if we expect an expression at the cursor position. | 79 | /// `true` if we expect an expression at the cursor position. |
@@ -114,19 +91,15 @@ pub(crate) struct CompletionContext<'a> { | |||
114 | pub(super) is_macro_call: bool, | 91 | pub(super) is_macro_call: bool, |
115 | pub(super) is_path_type: bool, | 92 | pub(super) is_path_type: bool, |
116 | pub(super) has_type_args: bool, | 93 | pub(super) has_type_args: bool, |
117 | pub(super) attribute_under_caret: Option<ast::Attr>, | ||
118 | pub(super) mod_declaration_under_caret: Option<ast::Module>, | ||
119 | pub(super) locals: Vec<(String, Local)>, | 94 | pub(super) locals: Vec<(String, Local)>, |
120 | 95 | ||
121 | // keyword patterns | ||
122 | pub(super) previous_token: Option<SyntaxToken>, | 96 | pub(super) previous_token: Option<SyntaxToken>, |
123 | pub(super) prev_sibling: Option<PrevSibling>, | ||
124 | pub(super) in_loop_body: bool, | 97 | pub(super) in_loop_body: bool, |
125 | pub(super) is_match_arm: bool, | ||
126 | pub(super) incomplete_let: bool, | 98 | pub(super) incomplete_let: bool, |
127 | 99 | ||
128 | no_completion_required: bool, | 100 | no_completion_required: bool, |
129 | } | 101 | } |
102 | |||
130 | impl<'a> CompletionContext<'a> { | 103 | impl<'a> CompletionContext<'a> { |
131 | pub(super) fn new( | 104 | pub(super) fn new( |
132 | db: &'a RootDatabase, | 105 | db: &'a RootDatabase, |
@@ -176,8 +149,6 @@ impl<'a> CompletionContext<'a> { | |||
176 | lifetime_param_syntax: None, | 149 | lifetime_param_syntax: None, |
177 | function_def: None, | 150 | function_def: None, |
178 | use_item_syntax: None, | 151 | use_item_syntax: None, |
179 | record_lit_syntax: None, | ||
180 | record_pat_syntax: None, | ||
181 | impl_def: None, | 152 | impl_def: None, |
182 | active_parameter: ActiveParameter::at(db, position), | 153 | active_parameter: ActiveParameter::at(db, position), |
183 | is_label_ref: false, | 154 | is_label_ref: false, |
@@ -185,7 +156,6 @@ impl<'a> CompletionContext<'a> { | |||
185 | is_pat_or_const: None, | 156 | is_pat_or_const: None, |
186 | is_trivial_path: false, | 157 | is_trivial_path: false, |
187 | path_qual: None, | 158 | path_qual: None, |
188 | after_if: false, | ||
189 | can_be_stmt: false, | 159 | can_be_stmt: false, |
190 | is_expr: false, | 160 | is_expr: false, |
191 | is_new_item: false, | 161 | is_new_item: false, |
@@ -196,15 +166,13 @@ impl<'a> CompletionContext<'a> { | |||
196 | is_macro_call: false, | 166 | is_macro_call: false, |
197 | is_path_type: false, | 167 | is_path_type: false, |
198 | has_type_args: false, | 168 | has_type_args: false, |
199 | attribute_under_caret: None, | ||
200 | mod_declaration_under_caret: None, | ||
201 | previous_token: None, | 169 | previous_token: None, |
202 | in_loop_body: false, | 170 | in_loop_body: false, |
203 | completion_location: None, | 171 | completion_location: None, |
204 | prev_sibling: None, | 172 | prev_sibling: None, |
205 | is_match_arm: false, | ||
206 | no_completion_required: false, | 173 | no_completion_required: false, |
207 | incomplete_let: false, | 174 | incomplete_let: false, |
175 | attribute_under_caret: None, | ||
208 | locals, | 176 | locals, |
209 | }; | 177 | }; |
210 | 178 | ||
@@ -247,7 +215,6 @@ impl<'a> CompletionContext<'a> { | |||
247 | break; | 215 | break; |
248 | } | 216 | } |
249 | } | 217 | } |
250 | ctx.fill_keyword_patterns(&speculative_file, offset); | ||
251 | ctx.fill(&original_file, speculative_file, offset); | 218 | ctx.fill(&original_file, speculative_file, offset); |
252 | Some(ctx) | 219 | Some(ctx) |
253 | } | 220 | } |
@@ -281,88 +248,63 @@ impl<'a> CompletionContext<'a> { | |||
281 | self.previous_token.as_ref().map_or(false, |tok| tok.kind() == kind) | 248 | self.previous_token.as_ref().map_or(false, |tok| tok.kind() == kind) |
282 | } | 249 | } |
283 | 250 | ||
284 | pub(crate) fn has_impl_or_trait_parent(&self) -> bool { | 251 | pub(crate) fn expects_assoc_item(&self) -> bool { |
285 | matches!( | 252 | matches!( |
286 | self.completion_location, | 253 | self.completion_location, |
287 | Some(ImmediateLocation::Trait) | Some(ImmediateLocation::Impl) | 254 | Some(ImmediateLocation::Trait) | Some(ImmediateLocation::Impl) |
288 | ) | 255 | ) |
289 | } | 256 | } |
290 | 257 | ||
291 | pub(crate) fn has_block_expr_parent(&self) -> bool { | 258 | pub(crate) fn expects_use_tree(&self) -> bool { |
292 | matches!(self.completion_location, Some(ImmediateLocation::BlockExpr)) | 259 | matches!(self.completion_location, Some(ImmediateLocation::Use)) |
293 | } | 260 | } |
294 | 261 | ||
295 | pub(crate) fn has_item_list_parent(&self) -> bool { | 262 | pub(crate) fn expects_non_trait_assoc_item(&self) -> bool { |
263 | matches!(self.completion_location, Some(ImmediateLocation::Impl)) | ||
264 | } | ||
265 | |||
266 | pub(crate) fn expects_item(&self) -> bool { | ||
296 | matches!(self.completion_location, Some(ImmediateLocation::ItemList)) | 267 | matches!(self.completion_location, Some(ImmediateLocation::ItemList)) |
297 | } | 268 | } |
298 | 269 | ||
299 | pub(crate) fn has_ident_or_ref_pat_parent(&self) -> bool { | 270 | pub(crate) fn expects_expression(&self) -> bool { |
271 | self.is_expr | ||
272 | } | ||
273 | |||
274 | pub(crate) fn has_block_expr_parent(&self) -> bool { | ||
275 | matches!(self.completion_location, Some(ImmediateLocation::BlockExpr)) | ||
276 | } | ||
277 | |||
278 | pub(crate) fn expects_ident_pat_or_ref_expr(&self) -> bool { | ||
300 | matches!( | 279 | matches!( |
301 | self.completion_location, | 280 | self.completion_location, |
302 | Some(ImmediateLocation::IdentPat) | Some(ImmediateLocation::RefPatOrExpr) | 281 | Some(ImmediateLocation::IdentPat) | Some(ImmediateLocation::RefExpr) |
303 | ) | 282 | ) |
304 | } | 283 | } |
305 | 284 | ||
306 | pub(crate) fn has_impl_parent(&self) -> bool { | 285 | pub(crate) fn expect_record_field(&self) -> bool { |
307 | matches!(self.completion_location, Some(ImmediateLocation::Impl)) | 286 | matches!(self.completion_location, Some(ImmediateLocation::RecordField)) |
308 | } | ||
309 | |||
310 | pub(crate) fn has_field_list_parent(&self) -> bool { | ||
311 | matches!(self.completion_location, Some(ImmediateLocation::RecordFieldList)) | ||
312 | } | 287 | } |
313 | 288 | ||
314 | pub(crate) fn has_impl_or_trait_prev_sibling(&self) -> bool { | 289 | pub(crate) fn has_impl_or_trait_prev_sibling(&self) -> bool { |
315 | self.prev_sibling.is_some() | 290 | matches!( |
291 | self.prev_sibling, | ||
292 | Some(ImmediatePrevSibling::ImplDefType) | Some(ImmediatePrevSibling::TraitDefName) | ||
293 | ) | ||
316 | } | 294 | } |
317 | 295 | ||
318 | pub(crate) fn is_path_disallowed(&self) -> bool { | 296 | pub(crate) fn after_if(&self) -> bool { |
319 | self.record_lit_syntax.is_some() | 297 | matches!(self.prev_sibling, Some(ImmediatePrevSibling::IfExpr)) |
320 | || self.record_pat_syntax.is_some() | ||
321 | || self.attribute_under_caret.is_some() | ||
322 | || self.mod_declaration_under_caret.is_some() | ||
323 | || self.has_impl_or_trait_parent() | ||
324 | } | 298 | } |
325 | 299 | ||
326 | fn fill_keyword_patterns(&mut self, file_with_fake_ident: &SyntaxNode, offset: TextSize) { | 300 | pub(crate) fn is_path_disallowed(&self) -> bool { |
327 | let fake_ident_token = file_with_fake_ident.token_at_offset(offset).right_biased().unwrap(); | 301 | matches!( |
328 | let syntax_element = NodeOrToken::Token(fake_ident_token); | 302 | self.completion_location, |
329 | self.previous_token = previous_token(syntax_element.clone()); | 303 | Some(ImmediateLocation::Attribute(_)) |
330 | self.in_loop_body = is_in_loop_body(syntax_element.clone()); | 304 | | Some(ImmediateLocation::ModDeclaration(_)) |
331 | self.is_match_arm = is_match_arm(syntax_element.clone()); | 305 | | Some(ImmediateLocation::RecordPat(_)) |
332 | if has_prev_sibling(syntax_element.clone(), IMPL) { | 306 | | Some(ImmediateLocation::RecordExpr(_)) |
333 | self.prev_sibling = Some(PrevSibling::Impl) | 307 | ) || self.attribute_under_caret.is_some() |
334 | } else if has_prev_sibling(syntax_element.clone(), TRAIT) { | ||
335 | self.prev_sibling = Some(PrevSibling::Trait) | ||
336 | } | ||
337 | |||
338 | if has_block_expr_parent(syntax_element.clone()) { | ||
339 | self.completion_location = Some(ImmediateLocation::BlockExpr); | ||
340 | } else if has_bind_pat_parent(syntax_element.clone()) { | ||
341 | self.completion_location = Some(ImmediateLocation::IdentPat); | ||
342 | } else if has_ref_parent(syntax_element.clone()) { | ||
343 | self.completion_location = Some(ImmediateLocation::RefPatOrExpr); | ||
344 | } else if has_impl_parent(syntax_element.clone()) { | ||
345 | self.completion_location = Some(ImmediateLocation::Impl); | ||
346 | } else if has_field_list_parent(syntax_element.clone()) { | ||
347 | self.completion_location = Some(ImmediateLocation::RecordFieldList); | ||
348 | } else if has_trait_parent(syntax_element.clone()) { | ||
349 | self.completion_location = Some(ImmediateLocation::Trait); | ||
350 | } else if has_item_list_or_source_file_parent(syntax_element.clone()) { | ||
351 | self.completion_location = Some(ImmediateLocation::ItemList); | ||
352 | } | ||
353 | |||
354 | self.mod_declaration_under_caret = | ||
355 | find_node_at_offset::<ast::Module>(&file_with_fake_ident, offset) | ||
356 | .filter(|module| module.item_list().is_none()); | ||
357 | self.incomplete_let = | ||
358 | syntax_element.ancestors().take(6).find_map(ast::LetStmt::cast).map_or(false, |it| { | ||
359 | it.syntax().text_range().end() == syntax_element.text_range().end() | ||
360 | }); | ||
361 | |||
362 | let inside_impl_trait_block = inside_impl_trait_block(syntax_element.clone()); | ||
363 | let fn_is_prev = self.previous_token_is(T![fn]); | ||
364 | let for_is_prev2 = for_is_prev2(syntax_element.clone()); | ||
365 | self.no_completion_required = (fn_is_prev && !inside_impl_trait_block) || for_is_prev2; | ||
366 | } | 308 | } |
367 | 309 | ||
368 | fn fill_impl_def(&mut self) { | 310 | fn fill_impl_def(&mut self) { |
@@ -480,23 +422,43 @@ impl<'a> CompletionContext<'a> { | |||
480 | file_with_fake_ident: SyntaxNode, | 422 | file_with_fake_ident: SyntaxNode, |
481 | offset: TextSize, | 423 | offset: TextSize, |
482 | ) { | 424 | ) { |
425 | let fake_ident_token = file_with_fake_ident.token_at_offset(offset).right_biased().unwrap(); | ||
426 | let syntax_element = NodeOrToken::Token(fake_ident_token); | ||
427 | self.previous_token = previous_token(syntax_element.clone()); | ||
428 | self.attribute_under_caret = syntax_element.ancestors().find_map(ast::Attr::cast); | ||
429 | self.no_completion_required = { | ||
430 | let inside_impl_trait_block = inside_impl_trait_block(syntax_element.clone()); | ||
431 | let fn_is_prev = self.previous_token_is(T![fn]); | ||
432 | let for_is_prev2 = for_is_prev2(syntax_element.clone()); | ||
433 | (fn_is_prev && !inside_impl_trait_block) || for_is_prev2 | ||
434 | }; | ||
435 | self.in_loop_body = is_in_loop_body(syntax_element.clone()); | ||
436 | |||
437 | self.incomplete_let = | ||
438 | syntax_element.ancestors().take(6).find_map(ast::LetStmt::cast).map_or(false, |it| { | ||
439 | it.syntax().text_range().end() == syntax_element.text_range().end() | ||
440 | }); | ||
441 | |||
483 | let (expected_type, expected_name) = self.expected_type_and_name(); | 442 | let (expected_type, expected_name) = self.expected_type_and_name(); |
484 | self.expected_type = expected_type; | 443 | self.expected_type = expected_type; |
485 | self.expected_name = expected_name; | 444 | self.expected_name = expected_name; |
486 | self.attribute_under_caret = find_node_at_offset(&file_with_fake_ident, offset); | 445 | |
487 | let name_like = match find_node_at_offset(&&file_with_fake_ident, offset) { | 446 | let name_like = match find_node_at_offset(&&file_with_fake_ident, offset) { |
488 | Some(it) => it, | 447 | Some(it) => it, |
489 | None => return, | 448 | None => return, |
490 | }; | 449 | }; |
450 | self.completion_location = | ||
451 | determine_location(&self.sema, original_file, offset, &name_like); | ||
452 | self.prev_sibling = determine_prev_sibling(&name_like); | ||
491 | match name_like { | 453 | match name_like { |
492 | ast::NameLike::Lifetime(lifetime) => { | 454 | ast::NameLike::Lifetime(lifetime) => { |
493 | self.classify_lifetime(original_file, lifetime, offset); | 455 | self.classify_lifetime(original_file, lifetime, offset); |
494 | } | 456 | } |
495 | ast::NameLike::NameRef(name_ref) => { | 457 | ast::NameLike::NameRef(name_ref) => { |
496 | self.classify_name_ref(original_file, name_ref, offset); | 458 | self.classify_name_ref(original_file, name_ref); |
497 | } | 459 | } |
498 | ast::NameLike::Name(name) => { | 460 | ast::NameLike::Name(name) => { |
499 | self.classify_name(original_file, name, offset); | 461 | self.classify_name(name); |
500 | } | 462 | } |
501 | } | 463 | } |
502 | } | 464 | } |
@@ -530,7 +492,7 @@ impl<'a> CompletionContext<'a> { | |||
530 | } | 492 | } |
531 | } | 493 | } |
532 | 494 | ||
533 | fn classify_name(&mut self, original_file: &SyntaxNode, name: ast::Name, offset: TextSize) { | 495 | fn classify_name(&mut self, name: ast::Name) { |
534 | if let Some(bind_pat) = name.syntax().parent().and_then(ast::IdentPat::cast) { | 496 | if let Some(bind_pat) = name.syntax().parent().and_then(ast::IdentPat::cast) { |
535 | self.is_pat_or_const = Some(PatternRefutability::Refutable); | 497 | self.is_pat_or_const = Some(PatternRefutability::Refutable); |
536 | // if any of these is here our bind pat can't be a const pat anymore | 498 | // if any of these is here our bind pat can't be a const pat anymore |
@@ -568,28 +530,12 @@ impl<'a> CompletionContext<'a> { | |||
568 | 530 | ||
569 | self.fill_impl_def(); | 531 | self.fill_impl_def(); |
570 | } | 532 | } |
533 | |||
571 | self.is_param |= is_node::<ast::Param>(name.syntax()); | 534 | self.is_param |= is_node::<ast::Param>(name.syntax()); |
572 | if ast::RecordPatField::for_field_name(&name).is_some() { | ||
573 | self.record_pat_syntax = | ||
574 | self.sema.find_node_at_offset_with_macros(&original_file, offset); | ||
575 | } | ||
576 | } | 535 | } |
577 | 536 | ||
578 | fn classify_name_ref( | 537 | fn classify_name_ref(&mut self, original_file: &SyntaxNode, name_ref: ast::NameRef) { |
579 | &mut self, | ||
580 | original_file: &SyntaxNode, | ||
581 | name_ref: ast::NameRef, | ||
582 | offset: TextSize, | ||
583 | ) { | ||
584 | self.fill_impl_def(); | 538 | self.fill_impl_def(); |
585 | if ast::RecordExprField::for_field_name(&name_ref).is_some() { | ||
586 | self.record_lit_syntax = | ||
587 | self.sema.find_node_at_offset_with_macros(original_file, offset); | ||
588 | } | ||
589 | if ast::RecordPatField::for_field_name_ref(&name_ref).is_some() { | ||
590 | self.record_pat_syntax = | ||
591 | self.sema.find_node_at_offset_with_macros(&original_file, offset); | ||
592 | } | ||
593 | 539 | ||
594 | self.name_ref_syntax = | 540 | self.name_ref_syntax = |
595 | find_node_at_offset(original_file, name_ref.syntax().text_range().start()); | 541 | find_node_at_offset(original_file, name_ref.syntax().text_range().start()); |
@@ -676,17 +622,6 @@ impl<'a> CompletionContext<'a> { | |||
676 | }) | 622 | }) |
677 | .unwrap_or(false); | 623 | .unwrap_or(false); |
678 | self.is_expr = path.syntax().parent().and_then(ast::PathExpr::cast).is_some(); | 624 | self.is_expr = path.syntax().parent().and_then(ast::PathExpr::cast).is_some(); |
679 | |||
680 | if let Some(off) = name_ref.syntax().text_range().start().checked_sub(2.into()) { | ||
681 | if let Some(if_expr) = | ||
682 | self.sema.find_node_at_offset_with_macros::<ast::IfExpr>(original_file, off) | ||
683 | { | ||
684 | if if_expr.syntax().text_range().end() < name_ref.syntax().text_range().start() | ||
685 | { | ||
686 | self.after_if = true; | ||
687 | } | ||
688 | } | ||
689 | } | ||
690 | } | 625 | } |
691 | 626 | ||
692 | if let Some(field_expr) = ast::FieldExpr::cast(parent.clone()) { | 627 | if let Some(field_expr) = ast::FieldExpr::cast(parent.clone()) { |
diff --git a/crates/ide_completion/src/patterns.rs b/crates/ide_completion/src/patterns.rs index 04f2c532b..26516046b 100644 --- a/crates/ide_completion/src/patterns.rs +++ b/crates/ide_completion/src/patterns.rs | |||
@@ -1,38 +1,197 @@ | |||
1 | //! Patterns telling us certain facts about current syntax element, they are used in completion context | 1 | //! Patterns telling us certain facts about current syntax element, they are used in completion context |
2 | 2 | ||
3 | use hir::Semantics; | ||
4 | use ide_db::RootDatabase; | ||
3 | use syntax::{ | 5 | use syntax::{ |
4 | algo::non_trivia_sibling, | 6 | algo::non_trivia_sibling, |
5 | ast::{self, LoopBodyOwner}, | 7 | ast::{self, LoopBodyOwner}, |
6 | match_ast, AstNode, Direction, NodeOrToken, SyntaxElement, | 8 | match_ast, AstNode, Direction, SyntaxElement, |
7 | SyntaxKind::{self, *}, | 9 | SyntaxKind::*, |
8 | SyntaxNode, SyntaxToken, T, | 10 | SyntaxNode, SyntaxToken, TextSize, T, |
9 | }; | 11 | }; |
10 | 12 | ||
11 | #[cfg(test)] | 13 | #[cfg(test)] |
12 | use crate::test_utils::{check_pattern_is_applicable, check_pattern_is_not_applicable}; | 14 | use crate::test_utils::{check_pattern_is_applicable, check_pattern_is_not_applicable}; |
13 | 15 | ||
14 | pub(crate) fn has_trait_parent(element: SyntaxElement) -> bool { | 16 | /// Direct parent container of the cursor position |
15 | not_same_range_ancestor(element) | 17 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] |
16 | .filter(|it| it.kind() == ASSOC_ITEM_LIST) | 18 | pub(crate) enum ImmediatePrevSibling { |
17 | .and_then(|it| it.parent()) | 19 | IfExpr, |
18 | .filter(|it| it.kind() == TRAIT) | 20 | TraitDefName, |
19 | .is_some() | 21 | ImplDefType, |
20 | } | 22 | } |
21 | #[test] | 23 | |
22 | fn test_has_trait_parent() { | 24 | /// Direct parent container of the cursor position |
23 | check_pattern_is_applicable(r"trait A { f$0 }", has_trait_parent); | 25 | #[derive(Clone, Debug, PartialEq, Eq)] |
26 | pub(crate) enum ImmediateLocation { | ||
27 | Use, | ||
28 | Impl, | ||
29 | Trait, | ||
30 | RecordField, | ||
31 | RefExpr, | ||
32 | IdentPat, | ||
33 | BlockExpr, | ||
34 | ItemList, | ||
35 | // Fake file ast node | ||
36 | Attribute(ast::Attr), | ||
37 | // Fake file ast node | ||
38 | ModDeclaration(ast::Module), | ||
39 | // Original file ast node | ||
40 | /// The record expr of the field name we are completing | ||
41 | RecordExpr(ast::RecordExpr), | ||
42 | // Original file ast node | ||
43 | /// The record pat of the field name we are completing | ||
44 | RecordPat(ast::RecordPat), | ||
24 | } | 45 | } |
25 | 46 | ||
26 | pub(crate) fn has_impl_parent(element: SyntaxElement) -> bool { | 47 | pub(crate) fn determine_prev_sibling(name_like: &ast::NameLike) -> Option<ImmediatePrevSibling> { |
27 | not_same_range_ancestor(element) | 48 | let node = match name_like { |
28 | .filter(|it| it.kind() == ASSOC_ITEM_LIST) | 49 | ast::NameLike::NameRef(name_ref) => maximize_name_ref(name_ref), |
29 | .and_then(|it| it.parent()) | 50 | ast::NameLike::Name(n) => n.syntax().clone(), |
30 | .filter(|it| it.kind() == IMPL) | 51 | ast::NameLike::Lifetime(lt) => lt.syntax().clone(), |
31 | .is_some() | 52 | }; |
53 | let node = match node.parent().and_then(ast::MacroCall::cast) { | ||
54 | // When a path is being typed after the name of a trait/type of an impl it is being | ||
55 | // parsed as a macro, so when the trait/impl has a block following it an we are between the | ||
56 | // name and block the macro will attach the block to itself so maximizing fails to take | ||
57 | // that into account | ||
58 | // FIXME path expr and statement have a similar problem with attrs | ||
59 | Some(call) | ||
60 | if call.excl_token().is_none() | ||
61 | && call.token_tree().map_or(false, |t| t.l_curly_token().is_some()) | ||
62 | && call.semicolon_token().is_none() => | ||
63 | { | ||
64 | call.syntax().clone() | ||
65 | } | ||
66 | _ => node, | ||
67 | }; | ||
68 | let prev_sibling = non_trivia_sibling(node.into(), Direction::Prev)?.into_node()?; | ||
69 | let res = match_ast! { | ||
70 | match prev_sibling { | ||
71 | ast::ExprStmt(it) => { | ||
72 | let node = it.expr().filter(|_| it.semicolon_token().is_none())?.syntax().clone(); | ||
73 | match_ast! { | ||
74 | match node { | ||
75 | ast::IfExpr(_it) => ImmediatePrevSibling::IfExpr, | ||
76 | _ => return None, | ||
77 | } | ||
78 | } | ||
79 | }, | ||
80 | ast::Trait(it) => if it.assoc_item_list().is_none() { | ||
81 | ImmediatePrevSibling::TraitDefName | ||
82 | } else { | ||
83 | return None | ||
84 | }, | ||
85 | ast::Impl(it) => if it.assoc_item_list().is_none() | ||
86 | && (it.for_token().is_none() || it.self_ty().is_some()) { | ||
87 | ImmediatePrevSibling::ImplDefType | ||
88 | } else { | ||
89 | return None | ||
90 | }, | ||
91 | _ => return None, | ||
92 | } | ||
93 | }; | ||
94 | Some(res) | ||
32 | } | 95 | } |
33 | #[test] | 96 | |
34 | fn test_has_impl_parent() { | 97 | pub(crate) fn determine_location( |
35 | check_pattern_is_applicable(r"impl A { f$0 }", has_impl_parent); | 98 | sema: &Semantics<RootDatabase>, |
99 | original_file: &SyntaxNode, | ||
100 | offset: TextSize, | ||
101 | name_like: &ast::NameLike, | ||
102 | ) -> Option<ImmediateLocation> { | ||
103 | let node = match name_like { | ||
104 | ast::NameLike::NameRef(name_ref) => { | ||
105 | if ast::RecordExprField::for_field_name(&name_ref).is_some() { | ||
106 | return sema | ||
107 | .find_node_at_offset_with_macros(original_file, offset) | ||
108 | .map(ImmediateLocation::RecordExpr); | ||
109 | } | ||
110 | if ast::RecordPatField::for_field_name_ref(&name_ref).is_some() { | ||
111 | return sema | ||
112 | .find_node_at_offset_with_macros(original_file, offset) | ||
113 | .map(ImmediateLocation::RecordPat); | ||
114 | } | ||
115 | maximize_name_ref(name_ref) | ||
116 | } | ||
117 | ast::NameLike::Name(name) => { | ||
118 | if ast::RecordPatField::for_field_name(&name).is_some() { | ||
119 | return sema | ||
120 | .find_node_at_offset_with_macros(original_file, offset) | ||
121 | .map(ImmediateLocation::RecordPat); | ||
122 | } | ||
123 | name.syntax().clone() | ||
124 | } | ||
125 | ast::NameLike::Lifetime(lt) => lt.syntax().clone(), | ||
126 | }; | ||
127 | |||
128 | let parent = match node.parent() { | ||
129 | Some(parent) => match ast::MacroCall::cast(parent.clone()) { | ||
130 | // When a path is being typed in an (Assoc)ItemList the parser will always emit a macro_call. | ||
131 | // This is usually fine as the node expansion code above already accounts for that with | ||
132 | // the ancestors call, but there is one exception to this which is that when an attribute | ||
133 | // precedes it the code above will not walk the Path to the parent MacroCall as their ranges differ. | ||
134 | // FIXME path expr and statement have a similar problem | ||
135 | Some(call) | ||
136 | if call.excl_token().is_none() | ||
137 | && call.token_tree().is_none() | ||
138 | && call.semicolon_token().is_none() => | ||
139 | { | ||
140 | call.syntax().parent()? | ||
141 | } | ||
142 | _ => parent, | ||
143 | }, | ||
144 | // SourceFile | ||
145 | None => { | ||
146 | return match node.kind() { | ||
147 | MACRO_ITEMS | SOURCE_FILE => Some(ImmediateLocation::ItemList), | ||
148 | _ => None, | ||
149 | } | ||
150 | } | ||
151 | }; | ||
152 | |||
153 | let res = match_ast! { | ||
154 | match parent { | ||
155 | ast::IdentPat(_it) => ImmediateLocation::IdentPat, | ||
156 | ast::Use(_it) => ImmediateLocation::Use, | ||
157 | ast::BlockExpr(_it) => ImmediateLocation::BlockExpr, | ||
158 | ast::SourceFile(_it) => ImmediateLocation::ItemList, | ||
159 | ast::ItemList(_it) => ImmediateLocation::ItemList, | ||
160 | ast::RefExpr(_it) => ImmediateLocation::RefExpr, | ||
161 | ast::RecordField(_it) => ImmediateLocation::RecordField, | ||
162 | ast::AssocItemList(it) => match it.syntax().parent().map(|it| it.kind()) { | ||
163 | Some(IMPL) => ImmediateLocation::Impl, | ||
164 | Some(TRAIT) => ImmediateLocation::Trait, | ||
165 | _ => return None, | ||
166 | }, | ||
167 | ast::Module(it) => if it.item_list().is_none() { | ||
168 | ImmediateLocation::ModDeclaration(it) | ||
169 | } else { | ||
170 | return None | ||
171 | }, | ||
172 | ast::Attr(it) => ImmediateLocation::Attribute(it), | ||
173 | _ => return None, | ||
174 | } | ||
175 | }; | ||
176 | Some(res) | ||
177 | } | ||
178 | |||
179 | fn maximize_name_ref(name_ref: &ast::NameRef) -> SyntaxNode { | ||
180 | // Maximize a nameref to its enclosing path if its the last segment of said path | ||
181 | if let Some(segment) = name_ref.syntax().parent().and_then(ast::PathSegment::cast) { | ||
182 | let p = segment.parent_path(); | ||
183 | if p.parent_path().is_none() { | ||
184 | if let Some(it) = p | ||
185 | .syntax() | ||
186 | .ancestors() | ||
187 | .take_while(|it| it.text_range() == p.syntax().text_range()) | ||
188 | .last() | ||
189 | { | ||
190 | return it; | ||
191 | } | ||
192 | } | ||
193 | } | ||
194 | name_ref.syntax().clone() | ||
36 | } | 195 | } |
37 | 196 | ||
38 | pub(crate) fn inside_impl_trait_block(element: SyntaxElement) -> bool { | 197 | pub(crate) fn inside_impl_trait_block(element: SyntaxElement) -> bool { |
@@ -53,68 +212,6 @@ fn test_inside_impl_trait_block() { | |||
53 | check_pattern_is_not_applicable(r"impl A { fn f$0 }", inside_impl_trait_block); | 212 | check_pattern_is_not_applicable(r"impl A { fn f$0 }", inside_impl_trait_block); |
54 | } | 213 | } |
55 | 214 | ||
56 | pub(crate) fn has_field_list_parent(element: SyntaxElement) -> bool { | ||
57 | not_same_range_ancestor(element).filter(|it| it.kind() == RECORD_FIELD_LIST).is_some() | ||
58 | } | ||
59 | #[test] | ||
60 | fn test_has_field_list_parent() { | ||
61 | check_pattern_is_applicable(r"struct Foo { f$0 }", has_field_list_parent); | ||
62 | check_pattern_is_applicable(r"struct Foo { f$0 pub f: i32}", has_field_list_parent); | ||
63 | } | ||
64 | |||
65 | pub(crate) fn has_block_expr_parent(element: SyntaxElement) -> bool { | ||
66 | not_same_range_ancestor(element).filter(|it| it.kind() == BLOCK_EXPR).is_some() | ||
67 | } | ||
68 | #[test] | ||
69 | fn test_has_block_expr_parent() { | ||
70 | check_pattern_is_applicable(r"fn my_fn() { let a = 2; f$0 }", has_block_expr_parent); | ||
71 | } | ||
72 | |||
73 | pub(crate) fn has_bind_pat_parent(element: SyntaxElement) -> bool { | ||
74 | element.ancestors().any(|it| it.kind() == IDENT_PAT) | ||
75 | } | ||
76 | |||
77 | #[test] | ||
78 | fn test_has_bind_pat_parent() { | ||
79 | check_pattern_is_applicable(r"fn my_fn(m$0) {}", has_bind_pat_parent); | ||
80 | check_pattern_is_applicable(r"fn my_fn() { let m$0 }", has_bind_pat_parent); | ||
81 | } | ||
82 | |||
83 | pub(crate) fn has_ref_parent(element: SyntaxElement) -> bool { | ||
84 | not_same_range_ancestor(element) | ||
85 | .filter(|it| it.kind() == REF_PAT || it.kind() == REF_EXPR) | ||
86 | .is_some() | ||
87 | } | ||
88 | #[test] | ||
89 | fn test_has_ref_parent() { | ||
90 | check_pattern_is_applicable(r"fn my_fn(&m$0) {}", has_ref_parent); | ||
91 | check_pattern_is_applicable(r"fn my() { let &m$0 }", has_ref_parent); | ||
92 | } | ||
93 | |||
94 | pub(crate) fn has_item_list_or_source_file_parent(element: SyntaxElement) -> bool { | ||
95 | match not_same_range_ancestor(element) { | ||
96 | Some(it) => it.kind() == SOURCE_FILE || it.kind() == ITEM_LIST, | ||
97 | None => true, | ||
98 | } | ||
99 | } | ||
100 | #[test] | ||
101 | fn test_has_item_list_or_source_file_parent() { | ||
102 | check_pattern_is_applicable(r"i$0", has_item_list_or_source_file_parent); | ||
103 | check_pattern_is_applicable(r"mod foo { f$0 }", has_item_list_or_source_file_parent); | ||
104 | } | ||
105 | |||
106 | pub(crate) fn is_match_arm(element: SyntaxElement) -> bool { | ||
107 | not_same_range_ancestor(element.clone()).filter(|it| it.kind() == MATCH_ARM).is_some() | ||
108 | && previous_sibling_or_ancestor_sibling(element) | ||
109 | .and_then(|it| it.into_token()) | ||
110 | .filter(|it| it.kind() == FAT_ARROW) | ||
111 | .is_some() | ||
112 | } | ||
113 | #[test] | ||
114 | fn test_is_match_arm() { | ||
115 | check_pattern_is_applicable(r"fn my_fn() { match () { () => m$0 } }", is_match_arm); | ||
116 | } | ||
117 | |||
118 | pub(crate) fn previous_token(element: SyntaxElement) -> Option<SyntaxToken> { | 215 | pub(crate) fn previous_token(element: SyntaxElement) -> Option<SyntaxToken> { |
119 | element.into_token().and_then(|it| previous_non_trivia_token(it)) | 216 | element.into_token().and_then(|it| previous_non_trivia_token(it)) |
120 | } | 217 | } |
@@ -134,14 +231,6 @@ fn test_for_is_prev2() { | |||
134 | check_pattern_is_applicable(r"for i i$0", for_is_prev2); | 231 | check_pattern_is_applicable(r"for i i$0", for_is_prev2); |
135 | } | 232 | } |
136 | 233 | ||
137 | pub(crate) fn has_prev_sibling(element: SyntaxElement, kind: SyntaxKind) -> bool { | ||
138 | previous_sibling_or_ancestor_sibling(element).filter(|it| it.kind() == kind).is_some() | ||
139 | } | ||
140 | #[test] | ||
141 | fn test_has_impl_as_prev_sibling() { | ||
142 | check_pattern_is_applicable(r"impl A w$0 {}", |it| has_prev_sibling(it, IMPL)); | ||
143 | } | ||
144 | |||
145 | pub(crate) fn is_in_loop_body(element: SyntaxElement) -> bool { | 234 | pub(crate) fn is_in_loop_body(element: SyntaxElement) -> bool { |
146 | element | 235 | element |
147 | .ancestors() | 236 | .ancestors() |
@@ -160,14 +249,6 @@ pub(crate) fn is_in_loop_body(element: SyntaxElement) -> bool { | |||
160 | .is_some() | 249 | .is_some() |
161 | } | 250 | } |
162 | 251 | ||
163 | fn not_same_range_ancestor(element: SyntaxElement) -> Option<SyntaxNode> { | ||
164 | element | ||
165 | .ancestors() | ||
166 | .take_while(|it| it.text_range() == element.text_range()) | ||
167 | .last() | ||
168 | .and_then(|it| it.parent()) | ||
169 | } | ||
170 | |||
171 | fn previous_non_trivia_token(token: SyntaxToken) -> Option<SyntaxToken> { | 252 | fn previous_non_trivia_token(token: SyntaxToken) -> Option<SyntaxToken> { |
172 | let mut token = token.prev_token(); | 253 | let mut token = token.prev_token(); |
173 | while let Some(inner) = token.clone() { | 254 | while let Some(inner) = token.clone() { |
@@ -180,17 +261,119 @@ fn previous_non_trivia_token(token: SyntaxToken) -> Option<SyntaxToken> { | |||
180 | None | 261 | None |
181 | } | 262 | } |
182 | 263 | ||
183 | fn previous_sibling_or_ancestor_sibling(element: SyntaxElement) -> Option<SyntaxElement> { | 264 | #[cfg(test)] |
184 | let token_sibling = non_trivia_sibling(element.clone(), Direction::Prev); | 265 | mod tests { |
185 | if let Some(sibling) = token_sibling { | 266 | use syntax::algo::find_node_at_offset; |
186 | Some(sibling) | 267 | |
187 | } else { | 268 | use crate::test_utils::position; |
188 | // if not trying to find first ancestor which has such a sibling | 269 | |
189 | let range = element.text_range(); | 270 | use super::*; |
190 | let top_node = element.ancestors().take_while(|it| it.text_range() == range).last()?; | 271 | |
191 | let prev_sibling_node = top_node.ancestors().find(|it| { | 272 | fn check_location(code: &str, loc: impl Into<Option<ImmediateLocation>>) { |
192 | non_trivia_sibling(NodeOrToken::Node(it.to_owned()), Direction::Prev).is_some() | 273 | let (db, pos) = position(code); |
193 | })?; | 274 | |
194 | non_trivia_sibling(NodeOrToken::Node(prev_sibling_node), Direction::Prev) | 275 | let sema = Semantics::new(&db); |
276 | let original_file = sema.parse(pos.file_id); | ||
277 | |||
278 | let name_like = find_node_at_offset(original_file.syntax(), pos.offset).unwrap(); | ||
279 | assert_eq!( | ||
280 | determine_location(&sema, original_file.syntax(), pos.offset, &name_like), | ||
281 | loc.into() | ||
282 | ); | ||
283 | } | ||
284 | |||
285 | fn check_prev_sibling(code: &str, sibling: impl Into<Option<ImmediatePrevSibling>>) { | ||
286 | check_pattern_is_applicable(code, |e| { | ||
287 | let name = &e.parent().and_then(ast::NameLike::cast).expect("Expected a namelike"); | ||
288 | assert_eq!(determine_prev_sibling(name), sibling.into()); | ||
289 | true | ||
290 | }); | ||
291 | } | ||
292 | |||
293 | #[test] | ||
294 | fn test_trait_loc() { | ||
295 | check_location(r"trait A { f$0 }", ImmediateLocation::Trait); | ||
296 | check_location(r"trait A { #[attr] f$0 }", ImmediateLocation::Trait); | ||
297 | check_location(r"trait A { f$0 fn f() {} }", ImmediateLocation::Trait); | ||
298 | check_location(r"trait A { fn f() {} f$0 }", ImmediateLocation::Trait); | ||
299 | check_location(r"trait A$0 {}", None); | ||
300 | check_location(r"trait A { fn f$0 }", None); | ||
301 | } | ||
302 | |||
303 | #[test] | ||
304 | fn test_impl_loc() { | ||
305 | check_location(r"impl A { f$0 }", ImmediateLocation::Impl); | ||
306 | check_location(r"impl A { #[attr] f$0 }", ImmediateLocation::Impl); | ||
307 | check_location(r"impl A { f$0 fn f() {} }", ImmediateLocation::Impl); | ||
308 | check_location(r"impl A { fn f() {} f$0 }", ImmediateLocation::Impl); | ||
309 | check_location(r"impl A$0 {}", None); | ||
310 | check_location(r"impl A { fn f$0 }", None); | ||
311 | } | ||
312 | |||
313 | #[test] | ||
314 | fn test_use_loc() { | ||
315 | check_location(r"use f$0", ImmediateLocation::Use); | ||
316 | check_location(r"use f$0;", ImmediateLocation::Use); | ||
317 | check_location(r"use f::{f$0}", None); | ||
318 | check_location(r"use {f$0}", None); | ||
319 | } | ||
320 | |||
321 | #[test] | ||
322 | fn test_record_field_loc() { | ||
323 | check_location(r"struct Foo { f$0 }", ImmediateLocation::RecordField); | ||
324 | check_location(r"struct Foo { f$0 pub f: i32}", ImmediateLocation::RecordField); | ||
325 | check_location(r"struct Foo { pub f: i32, f$0 }", ImmediateLocation::RecordField); | ||
326 | } | ||
327 | |||
328 | #[test] | ||
329 | fn test_block_expr_loc() { | ||
330 | check_location(r"fn my_fn() { let a = 2; f$0 }", ImmediateLocation::BlockExpr); | ||
331 | check_location(r"fn my_fn() { f$0 f }", ImmediateLocation::BlockExpr); | ||
332 | } | ||
333 | |||
334 | #[test] | ||
335 | fn test_ident_pat_loc() { | ||
336 | check_location(r"fn my_fn(m$0) {}", ImmediateLocation::IdentPat); | ||
337 | check_location(r"fn my_fn() { let m$0 }", ImmediateLocation::IdentPat); | ||
338 | check_location(r"fn my_fn(&m$0) {}", ImmediateLocation::IdentPat); | ||
339 | check_location(r"fn my_fn() { let &m$0 }", ImmediateLocation::IdentPat); | ||
340 | } | ||
341 | |||
342 | #[test] | ||
343 | fn test_ref_expr_loc() { | ||
344 | check_location(r"fn my_fn() { let x = &m$0 foo; }", ImmediateLocation::RefExpr); | ||
345 | } | ||
346 | |||
347 | #[test] | ||
348 | fn test_item_list_loc() { | ||
349 | check_location(r"i$0", ImmediateLocation::ItemList); | ||
350 | check_location(r"#[attr] i$0", ImmediateLocation::ItemList); | ||
351 | check_location(r"fn f() {} i$0", ImmediateLocation::ItemList); | ||
352 | check_location(r"mod foo { f$0 }", ImmediateLocation::ItemList); | ||
353 | check_location(r"mod foo { #[attr] f$0 }", ImmediateLocation::ItemList); | ||
354 | check_location(r"mod foo { fn f() {} f$0 }", ImmediateLocation::ItemList); | ||
355 | check_location(r"mod foo$0 {}", None); | ||
356 | } | ||
357 | |||
358 | #[test] | ||
359 | fn test_impl_prev_sibling() { | ||
360 | check_prev_sibling(r"impl A w$0 ", ImmediatePrevSibling::ImplDefType); | ||
361 | check_prev_sibling(r"impl A w$0 {}", ImmediatePrevSibling::ImplDefType); | ||
362 | check_prev_sibling(r"impl A for A w$0 ", ImmediatePrevSibling::ImplDefType); | ||
363 | check_prev_sibling(r"impl A for A w$0 {}", ImmediatePrevSibling::ImplDefType); | ||
364 | check_prev_sibling(r"impl A for w$0 {}", None); | ||
365 | check_prev_sibling(r"impl A for w$0", None); | ||
366 | } | ||
367 | |||
368 | #[test] | ||
369 | fn test_trait_prev_sibling() { | ||
370 | check_prev_sibling(r"trait A w$0 ", ImmediatePrevSibling::TraitDefName); | ||
371 | check_prev_sibling(r"trait A w$0 {}", ImmediatePrevSibling::TraitDefName); | ||
372 | } | ||
373 | |||
374 | #[test] | ||
375 | fn test_if_expr_prev_sibling() { | ||
376 | check_prev_sibling(r"fn foo() { if true {} w$0", ImmediatePrevSibling::IfExpr); | ||
377 | check_prev_sibling(r"fn foo() { if true {}; w$0", None); | ||
195 | } | 378 | } |
196 | } | 379 | } |
diff --git a/crates/ide_completion/src/render/macro_.rs b/crates/ide_completion/src/render/macro_.rs index 7578ad50b..b90fd3890 100644 --- a/crates/ide_completion/src/render/macro_.rs +++ b/crates/ide_completion/src/render/macro_.rs | |||
@@ -74,7 +74,11 @@ impl<'a> MacroRender<'a> { | |||
74 | if self.needs_bang() && self.ctx.snippet_cap().is_some() { | 74 | if self.needs_bang() && self.ctx.snippet_cap().is_some() { |
75 | format!("{}!{}…{}", self.name, self.bra, self.ket) | 75 | format!("{}!{}…{}", self.name, self.bra, self.ket) |
76 | } else { | 76 | } else { |
77 | self.banged_name() | 77 | if self.macro_.kind() == hir::MacroKind::Derive { |
78 | self.name.to_string() | ||
79 | } else { | ||
80 | self.banged_name() | ||
81 | } | ||
78 | } | 82 | } |
79 | } | 83 | } |
80 | 84 | ||
diff --git a/crates/ide_completion/src/test_utils.rs b/crates/ide_completion/src/test_utils.rs index 37be575e5..93c7c872c 100644 --- a/crates/ide_completion/src/test_utils.rs +++ b/crates/ide_completion/src/test_utils.rs | |||
@@ -12,7 +12,7 @@ use ide_db::{ | |||
12 | use itertools::Itertools; | 12 | use itertools::Itertools; |
13 | use stdx::{format_to, trim_indent}; | 13 | use stdx::{format_to, trim_indent}; |
14 | use syntax::{AstNode, NodeOrToken, SyntaxElement}; | 14 | use syntax::{AstNode, NodeOrToken, SyntaxElement}; |
15 | use test_utils::{assert_eq_text, RangeOrOffset}; | 15 | use test_utils::assert_eq_text; |
16 | 16 | ||
17 | use crate::{item::CompletionKind, CompletionConfig, CompletionItem}; | 17 | use crate::{item::CompletionKind, CompletionConfig, CompletionItem}; |
18 | 18 | ||
@@ -36,10 +36,7 @@ pub(crate) fn position(ra_fixture: &str) -> (RootDatabase, FilePosition) { | |||
36 | let mut database = RootDatabase::default(); | 36 | let mut database = RootDatabase::default(); |
37 | database.apply_change(change_fixture.change); | 37 | database.apply_change(change_fixture.change); |
38 | let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)"); | 38 | let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)"); |
39 | let offset = match range_or_offset { | 39 | let offset = range_or_offset.expect_offset(); |
40 | RangeOrOffset::Range(_) => panic!(), | ||
41 | RangeOrOffset::Offset(it) => it, | ||
42 | }; | ||
43 | (database, FilePosition { file_id, offset }) | 40 | (database, FilePosition { file_id, offset }) |
44 | } | 41 | } |
45 | 42 | ||
@@ -52,10 +49,11 @@ pub(crate) fn do_completion_with_config( | |||
52 | code: &str, | 49 | code: &str, |
53 | kind: CompletionKind, | 50 | kind: CompletionKind, |
54 | ) -> Vec<CompletionItem> { | 51 | ) -> Vec<CompletionItem> { |
55 | let mut kind_completions: Vec<CompletionItem> = | 52 | get_all_items(config, code) |
56 | get_all_items(config, code).into_iter().filter(|c| c.completion_kind == kind).collect(); | 53 | .into_iter() |
57 | kind_completions.sort_by(|l, r| l.label().cmp(r.label())); | 54 | .filter(|c| c.completion_kind == kind) |
58 | kind_completions | 55 | .sorted_by(|l, r| l.label().cmp(r.label())) |
56 | .collect() | ||
59 | } | 57 | } |
60 | 58 | ||
61 | pub(crate) fn completion_list(code: &str, kind: CompletionKind) -> String { | 59 | pub(crate) fn completion_list(code: &str, kind: CompletionKind) -> String { |
@@ -132,7 +130,7 @@ pub(crate) fn check_edit_with_config( | |||
132 | assert_eq_text!(&ra_fixture_after, &actual) | 130 | assert_eq_text!(&ra_fixture_after, &actual) |
133 | } | 131 | } |
134 | 132 | ||
135 | pub(crate) fn check_pattern_is_applicable(code: &str, check: fn(SyntaxElement) -> bool) { | 133 | pub(crate) fn check_pattern_is_applicable(code: &str, check: impl FnOnce(SyntaxElement) -> bool) { |
136 | let (db, pos) = position(code); | 134 | let (db, pos) = position(code); |
137 | 135 | ||
138 | let sema = Semantics::new(&db); | 136 | let sema = Semantics::new(&db); |
diff --git a/crates/ide_db/src/call_info/tests.rs b/crates/ide_db/src/call_info/tests.rs index 1aeda08e5..b585085f3 100644 --- a/crates/ide_db/src/call_info/tests.rs +++ b/crates/ide_db/src/call_info/tests.rs | |||
@@ -1,6 +1,5 @@ | |||
1 | use base_db::{fixture::ChangeFixture, FilePosition}; | 1 | use base_db::{fixture::ChangeFixture, FilePosition}; |
2 | use expect_test::{expect, Expect}; | 2 | use expect_test::{expect, Expect}; |
3 | use test_utils::RangeOrOffset; | ||
4 | 3 | ||
5 | use crate::RootDatabase; | 4 | use crate::RootDatabase; |
6 | 5 | ||
@@ -10,10 +9,7 @@ pub(crate) fn position(ra_fixture: &str) -> (RootDatabase, FilePosition) { | |||
10 | let mut database = RootDatabase::default(); | 9 | let mut database = RootDatabase::default(); |
11 | database.apply_change(change_fixture.change); | 10 | database.apply_change(change_fixture.change); |
12 | let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)"); | 11 | let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)"); |
13 | let offset = match range_or_offset { | 12 | let offset = range_or_offset.expect_offset(); |
14 | RangeOrOffset::Range(_) => panic!(), | ||
15 | RangeOrOffset::Offset(it) => it, | ||
16 | }; | ||
17 | (database, FilePosition { file_id, offset }) | 13 | (database, FilePosition { file_id, offset }) |
18 | } | 14 | } |
19 | 15 | ||
diff --git a/crates/ide_db/src/traits/tests.rs b/crates/ide_db/src/traits/tests.rs index 2a5482024..de994407c 100644 --- a/crates/ide_db/src/traits/tests.rs +++ b/crates/ide_db/src/traits/tests.rs | |||
@@ -2,7 +2,6 @@ use base_db::{fixture::ChangeFixture, FilePosition}; | |||
2 | use expect_test::{expect, Expect}; | 2 | use expect_test::{expect, Expect}; |
3 | use hir::Semantics; | 3 | use hir::Semantics; |
4 | use syntax::ast::{self, AstNode}; | 4 | use syntax::ast::{self, AstNode}; |
5 | use test_utils::RangeOrOffset; | ||
6 | 5 | ||
7 | use crate::RootDatabase; | 6 | use crate::RootDatabase; |
8 | 7 | ||
@@ -12,10 +11,7 @@ pub(crate) fn position(ra_fixture: &str) -> (RootDatabase, FilePosition) { | |||
12 | let mut database = RootDatabase::default(); | 11 | let mut database = RootDatabase::default(); |
13 | database.apply_change(change_fixture.change); | 12 | database.apply_change(change_fixture.change); |
14 | let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)"); | 13 | let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)"); |
15 | let offset = match range_or_offset { | 14 | let offset = range_or_offset.expect_offset(); |
16 | RangeOrOffset::Range(_) => panic!(), | ||
17 | RangeOrOffset::Offset(it) => it, | ||
18 | }; | ||
19 | (database, FilePosition { file_id, offset }) | 15 | (database, FilePosition { file_id, offset }) |
20 | } | 16 | } |
21 | 17 | ||
diff --git a/crates/mbe/src/expander/matcher.rs b/crates/mbe/src/expander/matcher.rs index 75d2f2eed..84ca3ff87 100644 --- a/crates/mbe/src/expander/matcher.rs +++ b/crates/mbe/src/expander/matcher.rs | |||
@@ -701,7 +701,7 @@ fn match_meta_var(kind: &str, input: &mut TtIter) -> ExpandResult<Option<Fragmen | |||
701 | "path" => Path, | 701 | "path" => Path, |
702 | "expr" => Expr, | 702 | "expr" => Expr, |
703 | "ty" => Type, | 703 | "ty" => Type, |
704 | "pat" => Pattern, | 704 | "pat" | "pat_param" => Pattern, // FIXME: edition2021 |
705 | "stmt" => Statement, | 705 | "stmt" => Statement, |
706 | "block" => Block, | 706 | "block" => Block, |
707 | "meta" => MetaItem, | 707 | "meta" => MetaItem, |
diff --git a/crates/rust-analyzer/src/bin/main.rs b/crates/rust-analyzer/src/bin/main.rs index 6c883dd58..2b842d393 100644 --- a/crates/rust-analyzer/src/bin/main.rs +++ b/crates/rust-analyzer/src/bin/main.rs | |||
@@ -158,7 +158,24 @@ fn run_server() -> Result<()> { | |||
158 | let initialize_params = | 158 | let initialize_params = |
159 | from_json::<lsp_types::InitializeParams>("InitializeParams", initialize_params)?; | 159 | from_json::<lsp_types::InitializeParams>("InitializeParams", initialize_params)?; |
160 | 160 | ||
161 | let server_capabilities = rust_analyzer::server_capabilities(&initialize_params.capabilities); | 161 | let root_path = match initialize_params |
162 | .root_uri | ||
163 | .and_then(|it| it.to_file_path().ok()) | ||
164 | .and_then(|it| AbsPathBuf::try_from(it).ok()) | ||
165 | { | ||
166 | Some(it) => it, | ||
167 | None => { | ||
168 | let cwd = env::current_dir()?; | ||
169 | AbsPathBuf::assert(cwd) | ||
170 | } | ||
171 | }; | ||
172 | |||
173 | let mut config = Config::new(root_path, initialize_params.capabilities); | ||
174 | if let Some(json) = initialize_params.initialization_options { | ||
175 | config.update(json); | ||
176 | } | ||
177 | |||
178 | let server_capabilities = rust_analyzer::server_capabilities(&config); | ||
162 | 179 | ||
163 | let initialize_result = lsp_types::InitializeResult { | 180 | let initialize_result = lsp_types::InitializeResult { |
164 | capabilities: server_capabilities, | 181 | capabilities: server_capabilities, |
@@ -166,11 +183,7 @@ fn run_server() -> Result<()> { | |||
166 | name: String::from("rust-analyzer"), | 183 | name: String::from("rust-analyzer"), |
167 | version: Some(String::from(env!("REV"))), | 184 | version: Some(String::from(env!("REV"))), |
168 | }), | 185 | }), |
169 | offset_encoding: if supports_utf8(&initialize_params.capabilities) { | 186 | offset_encoding: if supports_utf8(&config.caps) { Some("utf-8".to_string()) } else { None }, |
170 | Some("utf-8".to_string()) | ||
171 | } else { | ||
172 | None | ||
173 | }, | ||
174 | }; | 187 | }; |
175 | 188 | ||
176 | let initialize_result = serde_json::to_value(initialize_result).unwrap(); | 189 | let initialize_result = serde_json::to_value(initialize_result).unwrap(); |
@@ -181,47 +194,26 @@ fn run_server() -> Result<()> { | |||
181 | log::info!("Client '{}' {}", client_info.name, client_info.version.unwrap_or_default()); | 194 | log::info!("Client '{}' {}", client_info.name, client_info.version.unwrap_or_default()); |
182 | } | 195 | } |
183 | 196 | ||
184 | let config = { | 197 | if config.linked_projects().is_empty() && config.detached_files().is_empty() { |
185 | let root_path = match initialize_params | 198 | let workspace_roots = initialize_params |
186 | .root_uri | 199 | .workspace_folders |
187 | .and_then(|it| it.to_file_path().ok()) | 200 | .map(|workspaces| { |
188 | .and_then(|it| AbsPathBuf::try_from(it).ok()) | 201 | workspaces |
189 | { | 202 | .into_iter() |
190 | Some(it) => it, | 203 | .filter_map(|it| it.uri.to_file_path().ok()) |
191 | None => { | 204 | .filter_map(|it| AbsPathBuf::try_from(it).ok()) |
192 | let cwd = env::current_dir()?; | 205 | .collect::<Vec<_>>() |
193 | AbsPathBuf::assert(cwd) | 206 | }) |
194 | } | 207 | .filter(|workspaces| !workspaces.is_empty()) |
195 | }; | 208 | .unwrap_or_else(|| vec![config.root_path.clone()]); |
196 | 209 | ||
197 | let mut config = Config::new(root_path, initialize_params.capabilities); | 210 | let discovered = ProjectManifest::discover_all(&workspace_roots); |
198 | if let Some(json) = initialize_params.initialization_options { | 211 | log::info!("discovered projects: {:?}", discovered); |
199 | config.update(json); | 212 | if discovered.is_empty() { |
200 | } | 213 | log::error!("failed to find any projects in {:?}", workspace_roots); |
201 | |||
202 | if config.linked_projects().is_empty() && config.detached_files().is_empty() { | ||
203 | let workspace_roots = initialize_params | ||
204 | .workspace_folders | ||
205 | .map(|workspaces| { | ||
206 | workspaces | ||
207 | .into_iter() | ||
208 | .filter_map(|it| it.uri.to_file_path().ok()) | ||
209 | .filter_map(|it| AbsPathBuf::try_from(it).ok()) | ||
210 | .collect::<Vec<_>>() | ||
211 | }) | ||
212 | .filter(|workspaces| !workspaces.is_empty()) | ||
213 | .unwrap_or_else(|| vec![config.root_path.clone()]); | ||
214 | |||
215 | let discovered = ProjectManifest::discover_all(&workspace_roots); | ||
216 | log::info!("discovered projects: {:?}", discovered); | ||
217 | if discovered.is_empty() { | ||
218 | log::error!("failed to find any projects in {:?}", workspace_roots); | ||
219 | } | ||
220 | config.discovered_projects = Some(discovered); | ||
221 | } | 214 | } |
222 | 215 | config.discovered_projects = Some(discovered); | |
223 | config | 216 | } |
224 | }; | ||
225 | 217 | ||
226 | rust_analyzer::main_loop(config, connection)?; | 218 | rust_analyzer::main_loop(config, connection)?; |
227 | 219 | ||
diff --git a/crates/rust-analyzer/src/caps.rs b/crates/rust-analyzer/src/caps.rs index 4d88932ca..fe5255240 100644 --- a/crates/rust-analyzer/src/caps.rs +++ b/crates/rust-analyzer/src/caps.rs | |||
@@ -1,6 +1,4 @@ | |||
1 | //! Advertises the capabilities of the LSP Server. | 1 | //! Advertises the capabilities of the LSP Server. |
2 | use std::env; | ||
3 | |||
4 | use lsp_types::{ | 2 | use lsp_types::{ |
5 | CallHierarchyServerCapability, ClientCapabilities, CodeActionKind, CodeActionOptions, | 3 | CallHierarchyServerCapability, ClientCapabilities, CodeActionKind, CodeActionOptions, |
6 | CodeActionProviderCapability, CodeLensOptions, CompletionOptions, | 4 | CodeActionProviderCapability, CodeLensOptions, CompletionOptions, |
@@ -15,24 +13,21 @@ use lsp_types::{ | |||
15 | }; | 13 | }; |
16 | use serde_json::json; | 14 | use serde_json::json; |
17 | 15 | ||
16 | use crate::config::{Config, RustfmtConfig}; | ||
18 | use crate::semantic_tokens; | 17 | use crate::semantic_tokens; |
19 | 18 | ||
20 | pub fn server_capabilities(client_caps: &ClientCapabilities) -> ServerCapabilities { | 19 | pub fn server_capabilities(config: &Config) -> ServerCapabilities { |
21 | ServerCapabilities { | 20 | ServerCapabilities { |
22 | text_document_sync: Some(TextDocumentSyncCapability::Options(TextDocumentSyncOptions { | 21 | text_document_sync: Some(TextDocumentSyncCapability::Options(TextDocumentSyncOptions { |
23 | open_close: Some(true), | 22 | open_close: Some(true), |
24 | change: Some(if env::var("RA_NO_INCREMENTAL_SYNC").is_ok() { | 23 | change: Some(TextDocumentSyncKind::Incremental), |
25 | TextDocumentSyncKind::Full | ||
26 | } else { | ||
27 | TextDocumentSyncKind::Incremental | ||
28 | }), | ||
29 | will_save: None, | 24 | will_save: None, |
30 | will_save_wait_until: None, | 25 | will_save_wait_until: None, |
31 | save: Some(SaveOptions::default().into()), | 26 | save: Some(SaveOptions::default().into()), |
32 | })), | 27 | })), |
33 | hover_provider: Some(HoverProviderCapability::Simple(true)), | 28 | hover_provider: Some(HoverProviderCapability::Simple(true)), |
34 | completion_provider: Some(CompletionOptions { | 29 | completion_provider: Some(CompletionOptions { |
35 | resolve_provider: completions_resolve_provider(client_caps), | 30 | resolve_provider: completions_resolve_provider(&config.caps), |
36 | trigger_characters: Some(vec![":".to_string(), ".".to_string(), "'".to_string()]), | 31 | trigger_characters: Some(vec![":".to_string(), ".".to_string(), "'".to_string()]), |
37 | all_commit_characters: None, | 32 | all_commit_characters: None, |
38 | completion_item: None, | 33 | completion_item: None, |
@@ -51,10 +46,13 @@ pub fn server_capabilities(client_caps: &ClientCapabilities) -> ServerCapabiliti | |||
51 | document_highlight_provider: Some(OneOf::Left(true)), | 46 | document_highlight_provider: Some(OneOf::Left(true)), |
52 | document_symbol_provider: Some(OneOf::Left(true)), | 47 | document_symbol_provider: Some(OneOf::Left(true)), |
53 | workspace_symbol_provider: Some(OneOf::Left(true)), | 48 | workspace_symbol_provider: Some(OneOf::Left(true)), |
54 | code_action_provider: Some(code_action_capabilities(client_caps)), | 49 | code_action_provider: Some(code_action_capabilities(&config.caps)), |
55 | code_lens_provider: Some(CodeLensOptions { resolve_provider: Some(true) }), | 50 | code_lens_provider: Some(CodeLensOptions { resolve_provider: Some(true) }), |
56 | document_formatting_provider: Some(OneOf::Left(true)), | 51 | document_formatting_provider: Some(OneOf::Left(true)), |
57 | document_range_formatting_provider: Some(OneOf::Left(true)), | 52 | document_range_formatting_provider: match config.rustfmt() { |
53 | RustfmtConfig::Rustfmt { enable_range_formatting: true, .. } => Some(OneOf::Left(true)), | ||
54 | _ => Some(OneOf::Left(false)), | ||
55 | }, | ||
58 | document_on_type_formatting_provider: Some(DocumentOnTypeFormattingOptions { | 56 | document_on_type_formatting_provider: Some(DocumentOnTypeFormattingOptions { |
59 | first_trigger_character: "=".to_string(), | 57 | first_trigger_character: "=".to_string(), |
60 | more_trigger_character: Some(vec![".".to_string(), ">".to_string(), "{".to_string()]), | 58 | more_trigger_character: Some(vec![".".to_string(), ">".to_string(), "{".to_string()]), |
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 7620a2fe1..a67b0bb25 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs | |||
@@ -111,15 +111,15 @@ config_data! { | |||
111 | /// Map of prefixes to be substituted when parsing diagnostic file paths. | 111 | /// Map of prefixes to be substituted when parsing diagnostic file paths. |
112 | /// This should be the reverse mapping of what is passed to `rustc` as `--remap-path-prefix`. | 112 | /// This should be the reverse mapping of what is passed to `rustc` as `--remap-path-prefix`. |
113 | diagnostics_remapPrefix: FxHashMap<String, String> = "{}", | 113 | diagnostics_remapPrefix: FxHashMap<String, String> = "{}", |
114 | /// List of warnings that should be displayed with info severity. | ||
115 | /// | ||
116 | /// The warnings will be indicated by a blue squiggly underline in code | ||
117 | /// and a blue icon in the `Problems Panel`. | ||
118 | diagnostics_warningsAsHint: Vec<String> = "[]", | ||
119 | /// List of warnings that should be displayed with hint severity. | 114 | /// List of warnings that should be displayed with hint severity. |
120 | /// | 115 | /// |
121 | /// The warnings will be indicated by faded text or three dots in code | 116 | /// The warnings will be indicated by faded text or three dots in code |
122 | /// and will not show up in the `Problems Panel`. | 117 | /// and will not show up in the `Problems Panel`. |
118 | diagnostics_warningsAsHint: Vec<String> = "[]", | ||
119 | /// List of warnings that should be displayed with info severity. | ||
120 | /// | ||
121 | /// The warnings will be indicated by a blue squiggly underline in code | ||
122 | /// and a blue icon in the `Problems Panel`. | ||
123 | diagnostics_warningsAsInfo: Vec<String> = "[]", | 123 | diagnostics_warningsAsInfo: Vec<String> = "[]", |
124 | 124 | ||
125 | /// Controls file watching implementation. | 125 | /// Controls file watching implementation. |
@@ -238,7 +238,7 @@ impl Default for ConfigData { | |||
238 | 238 | ||
239 | #[derive(Debug, Clone)] | 239 | #[derive(Debug, Clone)] |
240 | pub struct Config { | 240 | pub struct Config { |
241 | caps: lsp_types::ClientCapabilities, | 241 | pub caps: lsp_types::ClientCapabilities, |
242 | data: ConfigData, | 242 | data: ConfigData, |
243 | detached_files: Vec<AbsPathBuf>, | 243 | detached_files: Vec<AbsPathBuf>, |
244 | pub discovered_projects: Option<Vec<ProjectManifest>>, | 244 | pub discovered_projects: Option<Vec<ProjectManifest>>, |
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs index 6d18d0ffc..f5c8535a2 100644 --- a/crates/rust-analyzer/src/to_proto.rs +++ b/crates/rust-analyzer/src/to_proto.rs | |||
@@ -534,6 +534,7 @@ pub(crate) fn folding_range( | |||
534 | | FoldKind::Consts | 534 | | FoldKind::Consts |
535 | | FoldKind::Statics | 535 | | FoldKind::Statics |
536 | | FoldKind::WhereClause | 536 | | FoldKind::WhereClause |
537 | | FoldKind::ReturnType | ||
537 | | FoldKind::Array => None, | 538 | | FoldKind::Array => None, |
538 | }; | 539 | }; |
539 | 540 | ||
diff --git a/crates/syntax/Cargo.toml b/crates/syntax/Cargo.toml index a6c294245..2106732cd 100644 --- a/crates/syntax/Cargo.toml +++ b/crates/syntax/Cargo.toml | |||
@@ -14,7 +14,7 @@ doctest = false | |||
14 | cov-mark = { version = "1.1", features = ["thread-local"] } | 14 | cov-mark = { version = "1.1", features = ["thread-local"] } |
15 | itertools = "0.10.0" | 15 | itertools = "0.10.0" |
16 | rowan = "=0.13.0-pre.6" | 16 | rowan = "=0.13.0-pre.6" |
17 | rustc_lexer = { version = "720.0.0", package = "rustc-ap-rustc_lexer" } | 17 | rustc_lexer = { version = "721.0.0", package = "rustc-ap-rustc_lexer" } |
18 | rustc-hash = "1.1.0" | 18 | rustc-hash = "1.1.0" |
19 | arrayvec = "0.7" | 19 | arrayvec = "0.7" |
20 | once_cell = "1.3.1" | 20 | once_cell = "1.3.1" |
diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs index df8f98b5b..884fe0739 100644 --- a/crates/syntax/src/ast/node_ext.rs +++ b/crates/syntax/src/ast/node_ext.rs | |||
@@ -243,6 +243,13 @@ impl ast::Path { | |||
243 | } | 243 | } |
244 | } | 244 | } |
245 | 245 | ||
246 | pub fn as_single_name_ref(&self) -> Option<ast::NameRef> { | ||
247 | match self.qualifier() { | ||
248 | Some(_) => None, | ||
249 | None => self.segment()?.name_ref(), | ||
250 | } | ||
251 | } | ||
252 | |||
246 | pub fn first_qualifier_or_self(&self) -> ast::Path { | 253 | pub fn first_qualifier_or_self(&self) -> ast::Path { |
247 | successors(Some(self.clone()), ast::Path::qualifier).last().unwrap() | 254 | successors(Some(self.clone()), ast::Path::qualifier).last().unwrap() |
248 | } | 255 | } |
diff --git a/crates/test_utils/src/lib.rs b/crates/test_utils/src/lib.rs index fce4fd6bf..bd017567c 100644 --- a/crates/test_utils/src/lib.rs +++ b/crates/test_utils/src/lib.rs | |||
@@ -96,6 +96,21 @@ pub enum RangeOrOffset { | |||
96 | Offset(TextSize), | 96 | Offset(TextSize), |
97 | } | 97 | } |
98 | 98 | ||
99 | impl RangeOrOffset { | ||
100 | pub fn expect_offset(self) -> TextSize { | ||
101 | match self { | ||
102 | RangeOrOffset::Offset(it) => it, | ||
103 | RangeOrOffset::Range(_) => panic!("expected an offset but got a range instead"), | ||
104 | } | ||
105 | } | ||
106 | pub fn expect_range(self) -> TextRange { | ||
107 | match self { | ||
108 | RangeOrOffset::Range(it) => it, | ||
109 | RangeOrOffset::Offset(_) => panic!("expected a range but got an offset"), | ||
110 | } | ||
111 | } | ||
112 | } | ||
113 | |||
99 | impl From<RangeOrOffset> for TextRange { | 114 | impl From<RangeOrOffset> for TextRange { |
100 | fn from(selection: RangeOrOffset) -> Self { | 115 | fn from(selection: RangeOrOffset) -> Self { |
101 | match selection { | 116 | match selection { |
diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc index f3da82feb..4a5782a57 100644 --- a/docs/user/generated_config.adoc +++ b/docs/user/generated_config.adoc | |||
@@ -161,18 +161,18 @@ This should be the reverse mapping of what is passed to `rustc` as `--remap-path | |||
161 | [[rust-analyzer.diagnostics.warningsAsHint]]rust-analyzer.diagnostics.warningsAsHint (default: `[]`):: | 161 | [[rust-analyzer.diagnostics.warningsAsHint]]rust-analyzer.diagnostics.warningsAsHint (default: `[]`):: |
162 | + | 162 | + |
163 | -- | 163 | -- |
164 | List of warnings that should be displayed with info severity. | 164 | List of warnings that should be displayed with hint severity. |
165 | 165 | ||
166 | The warnings will be indicated by a blue squiggly underline in code | 166 | The warnings will be indicated by faded text or three dots in code |
167 | and a blue icon in the `Problems Panel`. | 167 | and will not show up in the `Problems Panel`. |
168 | -- | 168 | -- |
169 | [[rust-analyzer.diagnostics.warningsAsInfo]]rust-analyzer.diagnostics.warningsAsInfo (default: `[]`):: | 169 | [[rust-analyzer.diagnostics.warningsAsInfo]]rust-analyzer.diagnostics.warningsAsInfo (default: `[]`):: |
170 | + | 170 | + |
171 | -- | 171 | -- |
172 | List of warnings that should be displayed with hint severity. | 172 | List of warnings that should be displayed with info severity. |
173 | 173 | ||
174 | The warnings will be indicated by faded text or three dots in code | 174 | The warnings will be indicated by a blue squiggly underline in code |
175 | and will not show up in the `Problems Panel`. | 175 | and a blue icon in the `Problems Panel`. |
176 | -- | 176 | -- |
177 | [[rust-analyzer.files.watcher]]rust-analyzer.files.watcher (default: `"client"`):: | 177 | [[rust-analyzer.files.watcher]]rust-analyzer.files.watcher (default: `"client"`):: |
178 | + | 178 | + |
diff --git a/editors/code/package.json b/editors/code/package.json index 05cbccf94..5b80cc1f9 100644 --- a/editors/code/package.json +++ b/editors/code/package.json | |||
@@ -597,7 +597,7 @@ | |||
597 | "type": "object" | 597 | "type": "object" |
598 | }, | 598 | }, |
599 | "rust-analyzer.diagnostics.warningsAsHint": { | 599 | "rust-analyzer.diagnostics.warningsAsHint": { |
600 | "markdownDescription": "List of warnings that should be displayed with info severity.\n\nThe warnings will be indicated by a blue squiggly underline in code\nand a blue icon in the `Problems Panel`.", | 600 | "markdownDescription": "List of warnings that should be displayed with hint severity.\n\nThe warnings will be indicated by faded text or three dots in code\nand will not show up in the `Problems Panel`.", |
601 | "default": [], | 601 | "default": [], |
602 | "type": "array", | 602 | "type": "array", |
603 | "items": { | 603 | "items": { |
@@ -605,7 +605,7 @@ | |||
605 | } | 605 | } |
606 | }, | 606 | }, |
607 | "rust-analyzer.diagnostics.warningsAsInfo": { | 607 | "rust-analyzer.diagnostics.warningsAsInfo": { |
608 | "markdownDescription": "List of warnings that should be displayed with hint severity.\n\nThe warnings will be indicated by faded text or three dots in code\nand will not show up in the `Problems Panel`.", | 608 | "markdownDescription": "List of warnings that should be displayed with info severity.\n\nThe warnings will be indicated by a blue squiggly underline in code\nand a blue icon in the `Problems Panel`.", |
609 | "default": [], | 609 | "default": [], |
610 | "type": "array", | 610 | "type": "array", |
611 | "items": { | 611 | "items": { |