diff options
63 files changed, 1929 insertions, 580 deletions
diff --git a/Cargo.lock b/Cargo.lock index 72ec68624..74e5a8273 100644 --- a/Cargo.lock +++ b/Cargo.lock | |||
@@ -129,9 +129,9 @@ checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" | |||
129 | 129 | ||
130 | [[package]] | 130 | [[package]] |
131 | name = "cargo_metadata" | 131 | name = "cargo_metadata" |
132 | version = "0.11.2" | 132 | version = "0.11.3" |
133 | source = "registry+https://github.com/rust-lang/crates.io-index" | 133 | source = "registry+https://github.com/rust-lang/crates.io-index" |
134 | checksum = "c990b1694d29f8e477f456db1b2fcd5dd1cd6e29d5be082df45213e8834eb39a" | 134 | checksum = "e708746e51dfaeff27c6c3979a4005a7faddabe40144204a0b1ce5ad34a1d0a5" |
135 | dependencies = [ | 135 | dependencies = [ |
136 | "semver", | 136 | "semver", |
137 | "serde", | 137 | "serde", |
@@ -140,9 +140,9 @@ dependencies = [ | |||
140 | 140 | ||
141 | [[package]] | 141 | [[package]] |
142 | name = "cc" | 142 | name = "cc" |
143 | version = "1.0.59" | 143 | version = "1.0.60" |
144 | source = "registry+https://github.com/rust-lang/crates.io-index" | 144 | source = "registry+https://github.com/rust-lang/crates.io-index" |
145 | checksum = "66120af515773fb005778dc07c261bd201ec8ce50bd6e7144c927753fe013381" | 145 | checksum = "ef611cc68ff783f18535d77ddd080185275713d852c4f5cbb6122c462a7a825c" |
146 | 146 | ||
147 | [[package]] | 147 | [[package]] |
148 | name = "cfg" | 148 | name = "cfg" |
@@ -162,9 +162,9 @@ checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" | |||
162 | 162 | ||
163 | [[package]] | 163 | [[package]] |
164 | name = "chalk-derive" | 164 | name = "chalk-derive" |
165 | version = "0.27.0" | 165 | version = "0.30.0" |
166 | source = "registry+https://github.com/rust-lang/crates.io-index" | 166 | source = "registry+https://github.com/rust-lang/crates.io-index" |
167 | checksum = "d5444ff2a211fe2a863e44d16a368c3d8a314d489de21b8eeb6879f14dd5d4a8" | 167 | checksum = "a6696d18587b7470c1e357a3fa120a2b7e6ac95e91d5c408f087455f7dc31f8b" |
168 | dependencies = [ | 168 | dependencies = [ |
169 | "proc-macro2", | 169 | "proc-macro2", |
170 | "quote", | 170 | "quote", |
@@ -174,9 +174,9 @@ dependencies = [ | |||
174 | 174 | ||
175 | [[package]] | 175 | [[package]] |
176 | name = "chalk-ir" | 176 | name = "chalk-ir" |
177 | version = "0.27.0" | 177 | version = "0.30.0" |
178 | source = "registry+https://github.com/rust-lang/crates.io-index" | 178 | source = "registry+https://github.com/rust-lang/crates.io-index" |
179 | checksum = "e39c3db1dd4abfaa7658faaa62e5fe998a982a592b710bd971fad5b6adfcfdef" | 179 | checksum = "8c9538918d3e1fd6edda042d717c969a4099af67a40372dfb0a00b45d3a5a946" |
180 | dependencies = [ | 180 | dependencies = [ |
181 | "chalk-derive", | 181 | "chalk-derive", |
182 | "lazy_static", | 182 | "lazy_static", |
@@ -184,9 +184,9 @@ dependencies = [ | |||
184 | 184 | ||
185 | [[package]] | 185 | [[package]] |
186 | name = "chalk-recursive" | 186 | name = "chalk-recursive" |
187 | version = "0.27.0" | 187 | version = "0.30.0" |
188 | source = "registry+https://github.com/rust-lang/crates.io-index" | 188 | source = "registry+https://github.com/rust-lang/crates.io-index" |
189 | checksum = "3bfae328eff80ca54dcd0d731725bbb56136ac21c59261b68f1e5498e056b306" | 189 | checksum = "97ec8d95c808f2b540c39da889536e1ae0d15182107f61fe80000ec3a5c3959a" |
190 | dependencies = [ | 190 | dependencies = [ |
191 | "chalk-derive", | 191 | "chalk-derive", |
192 | "chalk-ir", | 192 | "chalk-ir", |
@@ -197,9 +197,9 @@ dependencies = [ | |||
197 | 197 | ||
198 | [[package]] | 198 | [[package]] |
199 | name = "chalk-solve" | 199 | name = "chalk-solve" |
200 | version = "0.27.0" | 200 | version = "0.30.0" |
201 | source = "registry+https://github.com/rust-lang/crates.io-index" | 201 | source = "registry+https://github.com/rust-lang/crates.io-index" |
202 | checksum = "a673abe3077adc25f8ee0894198aed494a5bb0ce50ee993900d0ee1a44e1948a" | 202 | checksum = "f373dff4bcff66004424b72bcc56ae62889c21887c1cac875f083f69a7da4448" |
203 | dependencies = [ | 203 | dependencies = [ |
204 | "chalk-derive", | 204 | "chalk-derive", |
205 | "chalk-ir", | 205 | "chalk-ir", |
@@ -214,13 +214,15 @@ dependencies = [ | |||
214 | 214 | ||
215 | [[package]] | 215 | [[package]] |
216 | name = "chrono" | 216 | name = "chrono" |
217 | version = "0.4.15" | 217 | version = "0.4.18" |
218 | source = "registry+https://github.com/rust-lang/crates.io-index" | 218 | source = "registry+https://github.com/rust-lang/crates.io-index" |
219 | checksum = "942f72db697d8767c22d46a598e01f2d3b475501ea43d0db4f16d90259182d0b" | 219 | checksum = "d021fddb7bd3e734370acfa4a83f34095571d8570c039f1420d77540f68d5772" |
220 | dependencies = [ | 220 | dependencies = [ |
221 | "libc", | ||
221 | "num-integer", | 222 | "num-integer", |
222 | "num-traits", | 223 | "num-traits", |
223 | "time", | 224 | "time", |
225 | "winapi 0.3.9", | ||
224 | ] | 226 | ] |
225 | 227 | ||
226 | [[package]] | 228 | [[package]] |
@@ -311,9 +313,9 @@ checksum = "9bda8e21c04aca2ae33ffc2fd8c23134f3cac46db123ba97bd9d3f3b8a4a85e1" | |||
311 | 313 | ||
312 | [[package]] | 314 | [[package]] |
313 | name = "either" | 315 | name = "either" |
314 | version = "1.6.0" | 316 | version = "1.6.1" |
315 | source = "registry+https://github.com/rust-lang/crates.io-index" | 317 | source = "registry+https://github.com/rust-lang/crates.io-index" |
316 | checksum = "cd56b59865bce947ac5958779cfa508f6c3b9497cc762b7e24a12d11ccde2c4f" | 318 | checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" |
317 | 319 | ||
318 | [[package]] | 320 | [[package]] |
319 | name = "ena" | 321 | name = "ena" |
@@ -387,9 +389,9 @@ dependencies = [ | |||
387 | 389 | ||
388 | [[package]] | 390 | [[package]] |
389 | name = "fs-err" | 391 | name = "fs-err" |
390 | version = "2.3.0" | 392 | version = "2.5.0" |
391 | source = "registry+https://github.com/rust-lang/crates.io-index" | 393 | source = "registry+https://github.com/rust-lang/crates.io-index" |
392 | checksum = "c1a51f8b7158efbe531f7baa74e38e49fbc41239e5d66720bb37ed39c27c241a" | 394 | checksum = "bcd1163ae48bda72a20ae26d66a04d3094135cadab911cff418ae5e33f253431" |
393 | 395 | ||
394 | [[package]] | 396 | [[package]] |
395 | name = "fsevent" | 397 | name = "fsevent" |
@@ -466,9 +468,9 @@ dependencies = [ | |||
466 | 468 | ||
467 | [[package]] | 469 | [[package]] |
468 | name = "hermit-abi" | 470 | name = "hermit-abi" |
469 | version = "0.1.15" | 471 | version = "0.1.16" |
470 | source = "registry+https://github.com/rust-lang/crates.io-index" | 472 | source = "registry+https://github.com/rust-lang/crates.io-index" |
471 | checksum = "3deed196b6e7f9e44a2ae8d94225d80302d81208b1bb673fd21fe634645c85a9" | 473 | checksum = "4c30f6d0bc6b00693347368a67d41b58f2fb851215ff1da49e90fe2c5c667151" |
472 | dependencies = [ | 474 | dependencies = [ |
473 | "libc", | 475 | "libc", |
474 | ] | 476 | ] |
@@ -662,9 +664,12 @@ dependencies = [ | |||
662 | 664 | ||
663 | [[package]] | 665 | [[package]] |
664 | name = "instant" | 666 | name = "instant" |
665 | version = "0.1.6" | 667 | version = "0.1.7" |
666 | source = "registry+https://github.com/rust-lang/crates.io-index" | 668 | source = "registry+https://github.com/rust-lang/crates.io-index" |
667 | checksum = "5b141fdc7836c525d4d594027d318c84161ca17aaf8113ab1f81ab93ae897485" | 669 | checksum = "63312a18f7ea8760cdd0a7c5aac1a619752a246b833545e3e36d1f81f7cd9e66" |
670 | dependencies = [ | ||
671 | "cfg-if", | ||
672 | ] | ||
668 | 673 | ||
669 | [[package]] | 674 | [[package]] |
670 | name = "iovec" | 675 | name = "iovec" |
@@ -736,9 +741,9 @@ dependencies = [ | |||
736 | 741 | ||
737 | [[package]] | 742 | [[package]] |
738 | name = "libmimalloc-sys" | 743 | name = "libmimalloc-sys" |
739 | version = "0.1.16" | 744 | version = "0.1.17" |
740 | source = "registry+https://github.com/rust-lang/crates.io-index" | 745 | source = "registry+https://github.com/rust-lang/crates.io-index" |
741 | checksum = "677c4be79b14bd72496b87789b702ba02cd1a9f16a59369fe847082fd03efd88" | 746 | checksum = "2bd65748dfb74a807e8379fd49bf4d38964d92b8637b56f173f87e1d70433368" |
742 | dependencies = [ | 747 | dependencies = [ |
743 | "cmake", | 748 | "cmake", |
744 | ] | 749 | ] |
@@ -775,9 +780,9 @@ dependencies = [ | |||
775 | 780 | ||
776 | [[package]] | 781 | [[package]] |
777 | name = "lsp-types" | 782 | name = "lsp-types" |
778 | version = "0.80.0" | 783 | version = "0.82.0" |
779 | source = "registry+https://github.com/rust-lang/crates.io-index" | 784 | source = "registry+https://github.com/rust-lang/crates.io-index" |
780 | checksum = "f4265e2715bdacbb4dad029fce525e420cd66dc0af24ff9cb996a8ab48ac92ef" | 785 | checksum = "db895abb8527cf59e3de893ab2acf52cf904faeb65e60ea6f373e11fe86464e8" |
781 | dependencies = [ | 786 | dependencies = [ |
782 | "base64", | 787 | "base64", |
783 | "bitflags", | 788 | "bitflags", |
@@ -839,18 +844,18 @@ dependencies = [ | |||
839 | 844 | ||
840 | [[package]] | 845 | [[package]] |
841 | name = "memoffset" | 846 | name = "memoffset" |
842 | version = "0.5.5" | 847 | version = "0.5.6" |
843 | source = "registry+https://github.com/rust-lang/crates.io-index" | 848 | source = "registry+https://github.com/rust-lang/crates.io-index" |
844 | checksum = "c198b026e1bbf08a937e94c6c60f9ec4a2267f5b0d2eec9c1b21b061ce2be55f" | 849 | checksum = "043175f069eda7b85febe4a74abbaeff828d9f8b448515d3151a14a3542811aa" |
845 | dependencies = [ | 850 | dependencies = [ |
846 | "autocfg", | 851 | "autocfg", |
847 | ] | 852 | ] |
848 | 853 | ||
849 | [[package]] | 854 | [[package]] |
850 | name = "mimalloc" | 855 | name = "mimalloc" |
851 | version = "0.1.20" | 856 | version = "0.1.21" |
852 | source = "registry+https://github.com/rust-lang/crates.io-index" | 857 | source = "registry+https://github.com/rust-lang/crates.io-index" |
853 | checksum = "00759bcf69082fa629ae8823fcc72f7454a90c6476110297a1ae5fb3a559e474" | 858 | checksum = "2e18b3d01186c4a7bec0b56cff9a4b9d5a1461aa162c7b8eaf2e5ee10b265b02" |
854 | dependencies = [ | 859 | dependencies = [ |
855 | "libmimalloc-sys", | 860 | "libmimalloc-sys", |
856 | ] | 861 | ] |
@@ -1072,9 +1077,9 @@ checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" | |||
1072 | 1077 | ||
1073 | [[package]] | 1078 | [[package]] |
1074 | name = "proc-macro2" | 1079 | name = "proc-macro2" |
1075 | version = "1.0.21" | 1080 | version = "1.0.23" |
1076 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1081 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1077 | checksum = "36e28516df94f3dd551a587da5357459d9b36d945a7c37c3557928c1c2ff2a2c" | 1082 | checksum = "51ef7cd2518ead700af67bf9d1a658d90b6037d77110fd9c0445429d0ba1c6c9" |
1078 | dependencies = [ | 1083 | dependencies = [ |
1079 | "unicode-xid", | 1084 | "unicode-xid", |
1080 | ] | 1085 | ] |
@@ -1187,9 +1192,9 @@ dependencies = [ | |||
1187 | 1192 | ||
1188 | [[package]] | 1193 | [[package]] |
1189 | name = "rayon-core" | 1194 | name = "rayon-core" |
1190 | version = "1.8.0" | 1195 | version = "1.8.1" |
1191 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1196 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1192 | checksum = "91739a34c4355b5434ce54c9086c5895604a9c278586d1f1aa95e04f66b525a0" | 1197 | checksum = "e8c4fec834fb6e6d2dd5eece3c7b432a52f0ba887cf40e595190c4107edc08bf" |
1193 | dependencies = [ | 1198 | dependencies = [ |
1194 | "crossbeam-channel", | 1199 | "crossbeam-channel", |
1195 | "crossbeam-deque", | 1200 | "crossbeam-deque", |
@@ -1465,9 +1470,9 @@ checksum = "fbee7696b84bbf3d89a1c2eccff0850e3047ed46bfcd2e92c29a2d074d57e252" | |||
1465 | 1470 | ||
1466 | [[package]] | 1471 | [[package]] |
1467 | name = "smol_str" | 1472 | name = "smol_str" |
1468 | version = "0.1.16" | 1473 | version = "0.1.17" |
1469 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1474 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1470 | checksum = "2f7909a1d8bc166a862124d84fdc11bda0ea4ed3157ccca662296919c2972db1" | 1475 | checksum = "6ca0f7ce3a29234210f0f4f0b56f8be2e722488b95cb522077943212da3b32eb" |
1471 | dependencies = [ | 1476 | dependencies = [ |
1472 | "serde", | 1477 | "serde", |
1473 | ] | 1478 | ] |
@@ -1493,9 +1498,9 @@ version = "0.0.0" | |||
1493 | 1498 | ||
1494 | [[package]] | 1499 | [[package]] |
1495 | name = "syn" | 1500 | name = "syn" |
1496 | version = "1.0.40" | 1501 | version = "1.0.42" |
1497 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1502 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1498 | checksum = "963f7d3cc59b59b9325165add223142bbf1df27655d07789f109896d353d8350" | 1503 | checksum = "9c51d92969d209b54a98397e1b91c8ae82d8c87a7bb87df0b29aa2ad81454228" |
1499 | dependencies = [ | 1504 | dependencies = [ |
1500 | "proc-macro2", | 1505 | "proc-macro2", |
1501 | "quote", | 1506 | "quote", |
diff --git a/crates/assists/src/ast_transform.rs b/crates/assists/src/ast_transform.rs index bbcd2d488..835da3bb2 100644 --- a/crates/assists/src/ast_transform.rs +++ b/crates/assists/src/ast_transform.rs | |||
@@ -18,6 +18,34 @@ pub fn apply<'a, N: AstNode>(transformer: &dyn AstTransform<'a>, node: N) -> N { | |||
18 | .rewrite_ast(&node) | 18 | .rewrite_ast(&node) |
19 | } | 19 | } |
20 | 20 | ||
21 | /// `AstTransform` helps with applying bulk transformations to syntax nodes. | ||
22 | /// | ||
23 | /// This is mostly useful for IDE code generation. If you paste some existing | ||
24 | /// code into a new context (for example, to add method overrides to an `impl` | ||
25 | /// block), you generally want to appropriately qualify the names, and sometimes | ||
26 | /// you might want to substitute generic parameters as well: | ||
27 | /// | ||
28 | /// ``` | ||
29 | /// mod x { | ||
30 | /// pub struct A; | ||
31 | /// pub trait T<U> { fn foo(&self, _: U) -> A; } | ||
32 | /// } | ||
33 | /// | ||
34 | /// mod y { | ||
35 | /// use x::T; | ||
36 | /// | ||
37 | /// impl T<()> for () { | ||
38 | /// // If we invoke **Add Missing Members** here, we want to copy-paste `foo`. | ||
39 | /// // But we want a slightly-modified version of it: | ||
40 | /// fn foo(&self, _: ()) -> x::A {} | ||
41 | /// } | ||
42 | /// } | ||
43 | /// ``` | ||
44 | /// | ||
45 | /// So, a single `AstTransform` describes such function from `SyntaxNode` to | ||
46 | /// `SyntaxNode`. Note that the API here is a bit too high-order and high-brow. | ||
47 | /// We'd want to somehow express this concept simpler, but so far nobody got to | ||
48 | /// simplifying this! | ||
21 | pub trait AstTransform<'a> { | 49 | pub trait AstTransform<'a> { |
22 | fn get_substitution(&self, node: &syntax::SyntaxNode) -> Option<syntax::SyntaxNode>; | 50 | fn get_substitution(&self, node: &syntax::SyntaxNode) -> Option<syntax::SyntaxNode>; |
23 | 51 | ||
diff --git a/crates/assists/src/handlers/add_missing_impl_members.rs b/crates/assists/src/handlers/add_missing_impl_members.rs index 83a2ada9a..1ac5fefd6 100644 --- a/crates/assists/src/handlers/add_missing_impl_members.rs +++ b/crates/assists/src/handlers/add_missing_impl_members.rs | |||
@@ -111,8 +111,6 @@ fn add_missing_impl_members_inner( | |||
111 | ) -> Option<()> { | 111 | ) -> Option<()> { |
112 | let _p = profile::span("add_missing_impl_members_inner"); | 112 | let _p = profile::span("add_missing_impl_members_inner"); |
113 | let impl_def = ctx.find_node_at_offset::<ast::Impl>()?; | 113 | let impl_def = ctx.find_node_at_offset::<ast::Impl>()?; |
114 | let impl_item_list = impl_def.assoc_item_list()?; | ||
115 | |||
116 | let trait_ = resolve_target_trait(&ctx.sema, &impl_def)?; | 114 | let trait_ = resolve_target_trait(&ctx.sema, &impl_def)?; |
117 | 115 | ||
118 | let def_name = |item: &ast::AssocItem| -> Option<SmolStr> { | 116 | let def_name = |item: &ast::AssocItem| -> Option<SmolStr> { |
@@ -148,11 +146,14 @@ fn add_missing_impl_members_inner( | |||
148 | 146 | ||
149 | let target = impl_def.syntax().text_range(); | 147 | let target = impl_def.syntax().text_range(); |
150 | acc.add(AssistId(assist_id, AssistKind::QuickFix), label, target, |builder| { | 148 | acc.add(AssistId(assist_id, AssistKind::QuickFix), label, target, |builder| { |
149 | let impl_item_list = impl_def.assoc_item_list().unwrap_or(make::assoc_item_list()); | ||
150 | |||
151 | let n_existing_items = impl_item_list.assoc_items().count(); | 151 | let n_existing_items = impl_item_list.assoc_items().count(); |
152 | let source_scope = ctx.sema.scope_for_def(trait_); | 152 | let source_scope = ctx.sema.scope_for_def(trait_); |
153 | let target_scope = ctx.sema.scope(impl_item_list.syntax()); | 153 | let target_scope = ctx.sema.scope(impl_def.syntax()); |
154 | let ast_transform = QualifyPaths::new(&target_scope, &source_scope) | 154 | let ast_transform = QualifyPaths::new(&target_scope, &source_scope) |
155 | .or(SubstituteTypeParams::for_trait_impl(&source_scope, trait_, impl_def)); | 155 | .or(SubstituteTypeParams::for_trait_impl(&source_scope, trait_, impl_def.clone())); |
156 | |||
156 | let items = missing_items | 157 | let items = missing_items |
157 | .into_iter() | 158 | .into_iter() |
158 | .map(|it| ast_transform::apply(&*ast_transform, it)) | 159 | .map(|it| ast_transform::apply(&*ast_transform, it)) |
@@ -162,12 +163,14 @@ fn add_missing_impl_members_inner( | |||
162 | _ => it, | 163 | _ => it, |
163 | }) | 164 | }) |
164 | .map(|it| edit::remove_attrs_and_docs(&it)); | 165 | .map(|it| edit::remove_attrs_and_docs(&it)); |
166 | |||
165 | let new_impl_item_list = impl_item_list.append_items(items); | 167 | let new_impl_item_list = impl_item_list.append_items(items); |
166 | let first_new_item = new_impl_item_list.assoc_items().nth(n_existing_items).unwrap(); | 168 | let new_impl_def = impl_def.with_assoc_item_list(new_impl_item_list); |
169 | let first_new_item = | ||
170 | new_impl_def.assoc_item_list().unwrap().assoc_items().nth(n_existing_items).unwrap(); | ||
167 | 171 | ||
168 | let original_range = impl_item_list.syntax().text_range(); | ||
169 | match ctx.config.snippet_cap { | 172 | match ctx.config.snippet_cap { |
170 | None => builder.replace(original_range, new_impl_item_list.to_string()), | 173 | None => builder.replace(target, new_impl_def.to_string()), |
171 | Some(cap) => { | 174 | Some(cap) => { |
172 | let mut cursor = Cursor::Before(first_new_item.syntax()); | 175 | let mut cursor = Cursor::Before(first_new_item.syntax()); |
173 | let placeholder; | 176 | let placeholder; |
@@ -181,8 +184,8 @@ fn add_missing_impl_members_inner( | |||
181 | } | 184 | } |
182 | builder.replace_snippet( | 185 | builder.replace_snippet( |
183 | cap, | 186 | cap, |
184 | original_range, | 187 | target, |
185 | render_snippet(cap, new_impl_item_list.syntax(), cursor), | 188 | render_snippet(cap, new_impl_def.syntax(), cursor), |
186 | ) | 189 | ) |
187 | } | 190 | } |
188 | }; | 191 | }; |
@@ -311,6 +314,25 @@ impl Foo for S { | |||
311 | } | 314 | } |
312 | 315 | ||
313 | #[test] | 316 | #[test] |
317 | fn test_impl_def_without_braces() { | ||
318 | check_assist( | ||
319 | add_missing_impl_members, | ||
320 | r#" | ||
321 | trait Foo { fn foo(&self); } | ||
322 | struct S; | ||
323 | impl Foo for S<|>"#, | ||
324 | r#" | ||
325 | trait Foo { fn foo(&self); } | ||
326 | struct S; | ||
327 | impl Foo for S { | ||
328 | fn foo(&self) { | ||
329 | ${0:todo!()} | ||
330 | } | ||
331 | }"#, | ||
332 | ); | ||
333 | } | ||
334 | |||
335 | #[test] | ||
314 | fn fill_in_type_params_1() { | 336 | fn fill_in_type_params_1() { |
315 | check_assist( | 337 | check_assist( |
316 | add_missing_impl_members, | 338 | add_missing_impl_members, |
@@ -393,6 +415,41 @@ impl foo::Foo for S { | |||
393 | } | 415 | } |
394 | 416 | ||
395 | #[test] | 417 | #[test] |
418 | fn test_qualify_path_2() { | ||
419 | check_assist( | ||
420 | add_missing_impl_members, | ||
421 | r#" | ||
422 | mod foo { | ||
423 | pub mod bar { | ||
424 | pub struct Bar; | ||
425 | pub trait Foo { fn foo(&self, bar: Bar); } | ||
426 | } | ||
427 | } | ||
428 | |||
429 | use foo::bar; | ||
430 | |||
431 | struct S; | ||
432 | impl bar::Foo for S { <|> }"#, | ||
433 | r#" | ||
434 | mod foo { | ||
435 | pub mod bar { | ||
436 | pub struct Bar; | ||
437 | pub trait Foo { fn foo(&self, bar: Bar); } | ||
438 | } | ||
439 | } | ||
440 | |||
441 | use foo::bar; | ||
442 | |||
443 | struct S; | ||
444 | impl bar::Foo for S { | ||
445 | fn foo(&self, bar: bar::Bar) { | ||
446 | ${0:todo!()} | ||
447 | } | ||
448 | }"#, | ||
449 | ); | ||
450 | } | ||
451 | |||
452 | #[test] | ||
396 | fn test_qualify_path_generic() { | 453 | fn test_qualify_path_generic() { |
397 | check_assist( | 454 | check_assist( |
398 | add_missing_impl_members, | 455 | add_missing_impl_members, |
diff --git a/crates/assists/src/handlers/auto_import.rs b/crates/assists/src/handlers/auto_import.rs index 5a1b5a4ac..fa524ffd9 100644 --- a/crates/assists/src/handlers/auto_import.rs +++ b/crates/assists/src/handlers/auto_import.rs | |||
@@ -192,10 +192,10 @@ impl AutoImportAssets { | |||
192 | }) | 192 | }) |
193 | .filter_map(|candidate| match candidate { | 193 | .filter_map(|candidate| match candidate { |
194 | Either::Left(module_def) => { | 194 | Either::Left(module_def) => { |
195 | self.module_with_name_to_import.find_use_path(db, module_def) | 195 | self.module_with_name_to_import.find_use_path_prefixed(db, module_def) |
196 | } | 196 | } |
197 | Either::Right(macro_def) => { | 197 | Either::Right(macro_def) => { |
198 | self.module_with_name_to_import.find_use_path(db, macro_def) | 198 | self.module_with_name_to_import.find_use_path_prefixed(db, macro_def) |
199 | } | 199 | } |
200 | }) | 200 | }) |
201 | .filter(|use_path| !use_path.segments.is_empty()) | 201 | .filter(|use_path| !use_path.segments.is_empty()) |
@@ -287,6 +287,35 @@ mod tests { | |||
287 | use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target}; | 287 | use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target}; |
288 | 288 | ||
289 | #[test] | 289 | #[test] |
290 | fn applicable_when_found_an_import_partial() { | ||
291 | check_assist( | ||
292 | auto_import, | ||
293 | r" | ||
294 | mod std { | ||
295 | pub mod fmt { | ||
296 | pub struct Formatter; | ||
297 | } | ||
298 | } | ||
299 | |||
300 | use std::fmt; | ||
301 | |||
302 | <|>Formatter | ||
303 | ", | ||
304 | r" | ||
305 | mod std { | ||
306 | pub mod fmt { | ||
307 | pub struct Formatter; | ||
308 | } | ||
309 | } | ||
310 | |||
311 | use std::fmt::{self, Formatter}; | ||
312 | |||
313 | Formatter | ||
314 | ", | ||
315 | ); | ||
316 | } | ||
317 | |||
318 | #[test] | ||
290 | fn applicable_when_found_an_import() { | 319 | fn applicable_when_found_an_import() { |
291 | check_assist( | 320 | check_assist( |
292 | auto_import, | 321 | auto_import, |
diff --git a/crates/assists/src/handlers/invert_if.rs b/crates/assists/src/handlers/invert_if.rs index 294256297..461fcf862 100644 --- a/crates/assists/src/handlers/invert_if.rs +++ b/crates/assists/src/handlers/invert_if.rs | |||
@@ -49,13 +49,14 @@ pub(crate) fn invert_if(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | |||
49 | ast::ElseBranch::IfExpr(_) => return None, | 49 | ast::ElseBranch::IfExpr(_) => return None, |
50 | }; | 50 | }; |
51 | 51 | ||
52 | let cond_range = cond.syntax().text_range(); | ||
53 | let flip_cond = invert_boolean_expression(cond); | ||
54 | let else_node = else_block.syntax(); | ||
55 | let else_range = else_node.text_range(); | ||
56 | let then_range = then_node.text_range(); | ||
57 | acc.add(AssistId("invert_if", AssistKind::RefactorRewrite), "Invert if", if_range, |edit| { | 52 | acc.add(AssistId("invert_if", AssistKind::RefactorRewrite), "Invert if", if_range, |edit| { |
58 | edit.replace(cond_range, flip_cond.syntax().text()); | 53 | let flip_cond = invert_boolean_expression(cond.clone()); |
54 | edit.replace_ast(cond, flip_cond); | ||
55 | |||
56 | let else_node = else_block.syntax(); | ||
57 | let else_range = else_node.text_range(); | ||
58 | let then_range = then_node.text_range(); | ||
59 | |||
59 | edit.replace(else_range, then_node.text()); | 60 | edit.replace(else_range, then_node.text()); |
60 | edit.replace(then_range, else_node.text()); | 61 | edit.replace(then_range, else_node.text()); |
61 | }) | 62 | }) |
diff --git a/crates/assists/src/handlers/merge_imports.rs b/crates/assists/src/handlers/merge_imports.rs index 0bd679260..fe33cee53 100644 --- a/crates/assists/src/handlers/merge_imports.rs +++ b/crates/assists/src/handlers/merge_imports.rs | |||
@@ -95,7 +95,7 @@ use std::fmt::Debug; | |||
95 | use std::fmt<|>::Display; | 95 | use std::fmt<|>::Display; |
96 | ", | 96 | ", |
97 | r" | 97 | r" |
98 | use std::fmt::{Display, Debug}; | 98 | use std::fmt::{Debug, Display}; |
99 | ", | 99 | ", |
100 | ); | 100 | ); |
101 | } | 101 | } |
@@ -122,7 +122,7 @@ use std::fmt::{self, Display}; | |||
122 | use std::{fmt, <|>fmt::Display}; | 122 | use std::{fmt, <|>fmt::Display}; |
123 | ", | 123 | ", |
124 | r" | 124 | r" |
125 | use std::{fmt::{Display, self}}; | 125 | use std::{fmt::{self, Display}}; |
126 | ", | 126 | ", |
127 | ); | 127 | ); |
128 | } | 128 | } |
@@ -210,13 +210,17 @@ use std::{fmt<|>::Debug, fmt::Display}; | |||
210 | use std::{fmt::{Debug, Display}}; | 210 | use std::{fmt::{Debug, Display}}; |
211 | ", | 211 | ", |
212 | ); | 212 | ); |
213 | } | ||
214 | |||
215 | #[test] | ||
216 | fn test_merge_nested2() { | ||
213 | check_assist( | 217 | check_assist( |
214 | merge_imports, | 218 | merge_imports, |
215 | r" | 219 | r" |
216 | use std::{fmt::Debug, fmt<|>::Display}; | 220 | use std::{fmt::Debug, fmt<|>::Display}; |
217 | ", | 221 | ", |
218 | r" | 222 | r" |
219 | use std::{fmt::{Display, Debug}}; | 223 | use std::{fmt::{Debug, Display}}; |
220 | ", | 224 | ", |
221 | ); | 225 | ); |
222 | } | 226 | } |
@@ -310,9 +314,7 @@ use foo::<|>{ | |||
310 | }; | 314 | }; |
311 | ", | 315 | ", |
312 | r" | 316 | r" |
313 | use foo::{ | 317 | use foo::{FooBar, bar::baz}; |
314 | FooBar, | ||
315 | bar::baz}; | ||
316 | ", | 318 | ", |
317 | ) | 319 | ) |
318 | } | 320 | } |
diff --git a/crates/assists/src/handlers/replace_qualified_name_with_use.rs b/crates/assists/src/handlers/replace_qualified_name_with_use.rs index 27b49455c..74afc123b 100644 --- a/crates/assists/src/handlers/replace_qualified_name_with_use.rs +++ b/crates/assists/src/handlers/replace_qualified_name_with_use.rs | |||
@@ -298,7 +298,7 @@ impl std::fmt<|> for Foo { | |||
298 | } | 298 | } |
299 | ", | 299 | ", |
300 | r" | 300 | r" |
301 | use std::fmt::{Debug, self}; | 301 | use std::fmt::{self, Debug}; |
302 | 302 | ||
303 | impl fmt for Foo { | 303 | impl fmt for Foo { |
304 | } | 304 | } |
@@ -316,9 +316,8 @@ use std::fmt::{Debug, nested::{Display}}; | |||
316 | impl std::fmt::nested<|> for Foo { | 316 | impl std::fmt::nested<|> for Foo { |
317 | } | 317 | } |
318 | ", | 318 | ", |
319 | // FIXME(veykril): should be nested::{self, Display} here | ||
320 | r" | 319 | r" |
321 | use std::fmt::{Debug, nested::{Display}, nested}; | 320 | use std::fmt::{Debug, nested::{self, Display}}; |
322 | 321 | ||
323 | impl nested for Foo { | 322 | impl nested for Foo { |
324 | } | 323 | } |
@@ -336,9 +335,8 @@ use std::fmt::{Debug, nested::{self, Display}}; | |||
336 | impl std::fmt::nested<|> for Foo { | 335 | impl std::fmt::nested<|> for Foo { |
337 | } | 336 | } |
338 | ", | 337 | ", |
339 | // FIXME(veykril): nested is duplicated now | ||
340 | r" | 338 | r" |
341 | use std::fmt::{Debug, nested::{self, Display}, nested}; | 339 | use std::fmt::{Debug, nested::{self, Display}}; |
342 | 340 | ||
343 | impl nested for Foo { | 341 | impl nested for Foo { |
344 | } | 342 | } |
@@ -357,7 +355,7 @@ impl std::fmt::nested::Debug<|> for Foo { | |||
357 | } | 355 | } |
358 | ", | 356 | ", |
359 | r" | 357 | r" |
360 | use std::fmt::{Debug, nested::{Display}, nested::Debug}; | 358 | use std::fmt::{Debug, nested::{Debug, Display}}; |
361 | 359 | ||
362 | impl Debug for Foo { | 360 | impl Debug for Foo { |
363 | } | 361 | } |
@@ -395,7 +393,7 @@ impl std::fmt::Display<|> for Foo { | |||
395 | } | 393 | } |
396 | ", | 394 | ", |
397 | r" | 395 | r" |
398 | use std::fmt::{nested::Debug, Display}; | 396 | use std::fmt::{Display, nested::Debug}; |
399 | 397 | ||
400 | impl Display for Foo { | 398 | impl Display for Foo { |
401 | } | 399 | } |
@@ -415,12 +413,8 @@ use crate::{ | |||
415 | 413 | ||
416 | fn foo() { crate::ty::lower<|>::trait_env() } | 414 | fn foo() { crate::ty::lower<|>::trait_env() } |
417 | ", | 415 | ", |
418 | // FIXME(veykril): formatting broke here | ||
419 | r" | 416 | r" |
420 | use crate::{ | 417 | use crate::{AssocItem, ty::{Substs, Ty, lower}}; |
421 | ty::{Substs, Ty}, | ||
422 | AssocItem, | ||
423 | ty::lower}; | ||
424 | 418 | ||
425 | fn foo() { lower::trait_env() } | 419 | fn foo() { lower::trait_env() } |
426 | ", | 420 | ", |
@@ -619,7 +613,7 @@ fn main() { | |||
619 | } | 613 | } |
620 | ", | 614 | ", |
621 | r" | 615 | r" |
622 | use std::fmt::{Display, self}; | 616 | use std::fmt::{self, Display}; |
623 | 617 | ||
624 | fn main() { | 618 | fn main() { |
625 | fmt; | 619 | fmt; |
diff --git a/crates/assists/src/utils/insert_use.rs b/crates/assists/src/utils/insert_use.rs index 6d110aaaf..5719b06af 100644 --- a/crates/assists/src/utils/insert_use.rs +++ b/crates/assists/src/utils/insert_use.rs | |||
@@ -1,7 +1,9 @@ | |||
1 | //! Handle syntactic aspects of inserting a new `use`. | 1 | //! Handle syntactic aspects of inserting a new `use`. |
2 | use std::iter::{self, successors}; | 2 | use std::{ |
3 | cmp::Ordering, | ||
4 | iter::{self, successors}, | ||
5 | }; | ||
3 | 6 | ||
4 | use algo::skip_trivia_token; | ||
5 | use ast::{ | 7 | use ast::{ |
6 | edit::{AstNodeEdit, IndentLevel}, | 8 | edit::{AstNodeEdit, IndentLevel}, |
7 | PathSegmentKind, VisibilityOwner, | 9 | PathSegmentKind, VisibilityOwner, |
@@ -9,9 +11,8 @@ use ast::{ | |||
9 | use syntax::{ | 11 | use syntax::{ |
10 | algo, | 12 | algo, |
11 | ast::{self, make, AstNode}, | 13 | ast::{self, make, AstNode}, |
12 | Direction, InsertPosition, SyntaxElement, SyntaxNode, T, | 14 | InsertPosition, SyntaxElement, SyntaxNode, |
13 | }; | 15 | }; |
14 | use test_utils::mark; | ||
15 | 16 | ||
16 | #[derive(Debug)] | 17 | #[derive(Debug)] |
17 | pub enum ImportScope { | 18 | pub enum ImportScope { |
@@ -119,7 +120,6 @@ pub(crate) fn insert_use( | |||
119 | } | 120 | } |
120 | 121 | ||
121 | if let ident_level @ 1..=usize::MAX = scope.indent_level().0 as usize { | 122 | if let ident_level @ 1..=usize::MAX = scope.indent_level().0 as usize { |
122 | // FIXME: this alone doesnt properly re-align all cases | ||
123 | buf.push(make::tokens::whitespace(&" ".repeat(4 * ident_level)).into()); | 123 | buf.push(make::tokens::whitespace(&" ".repeat(4 * ident_level)).into()); |
124 | } | 124 | } |
125 | buf.push(use_item.syntax().clone().into()); | 125 | buf.push(use_item.syntax().clone().into()); |
@@ -149,66 +149,123 @@ fn eq_visibility(vis0: Option<ast::Visibility>, vis1: Option<ast::Visibility>) - | |||
149 | } | 149 | } |
150 | 150 | ||
151 | pub(crate) fn try_merge_imports( | 151 | pub(crate) fn try_merge_imports( |
152 | old: &ast::Use, | 152 | lhs: &ast::Use, |
153 | new: &ast::Use, | 153 | rhs: &ast::Use, |
154 | merge_behaviour: MergeBehaviour, | 154 | merge_behaviour: MergeBehaviour, |
155 | ) -> Option<ast::Use> { | 155 | ) -> Option<ast::Use> { |
156 | // don't merge imports with different visibilities | 156 | // don't merge imports with different visibilities |
157 | if !eq_visibility(old.visibility(), new.visibility()) { | 157 | if !eq_visibility(lhs.visibility(), rhs.visibility()) { |
158 | return None; | 158 | return None; |
159 | } | 159 | } |
160 | let old_tree = old.use_tree()?; | 160 | let lhs_tree = lhs.use_tree()?; |
161 | let new_tree = new.use_tree()?; | 161 | let rhs_tree = rhs.use_tree()?; |
162 | let merged = try_merge_trees(&old_tree, &new_tree, merge_behaviour)?; | 162 | let merged = try_merge_trees(&lhs_tree, &rhs_tree, merge_behaviour)?; |
163 | Some(old.with_use_tree(merged)) | 163 | Some(lhs.with_use_tree(merged)) |
164 | } | ||
165 | |||
166 | /// Simple function that checks if a UseTreeList is deeper than one level | ||
167 | fn use_tree_list_is_nested(tl: &ast::UseTreeList) -> bool { | ||
168 | tl.use_trees().any(|use_tree| { | ||
169 | use_tree.use_tree_list().is_some() || use_tree.path().and_then(|p| p.qualifier()).is_some() | ||
170 | }) | ||
171 | } | 164 | } |
172 | 165 | ||
173 | // FIXME: currently this merely prepends the new tree into old, ideally it would insert the items in a sorted fashion | ||
174 | pub(crate) fn try_merge_trees( | 166 | pub(crate) fn try_merge_trees( |
175 | old: &ast::UseTree, | 167 | lhs: &ast::UseTree, |
176 | new: &ast::UseTree, | 168 | rhs: &ast::UseTree, |
177 | merge_behaviour: MergeBehaviour, | 169 | merge: MergeBehaviour, |
178 | ) -> Option<ast::UseTree> { | 170 | ) -> Option<ast::UseTree> { |
179 | let lhs_path = old.path()?; | 171 | let lhs_path = lhs.path()?; |
180 | let rhs_path = new.path()?; | 172 | let rhs_path = rhs.path()?; |
181 | 173 | ||
182 | let (lhs_prefix, rhs_prefix) = common_prefix(&lhs_path, &rhs_path)?; | 174 | let (lhs_prefix, rhs_prefix) = common_prefix(&lhs_path, &rhs_path)?; |
183 | let lhs = old.split_prefix(&lhs_prefix); | 175 | let lhs = lhs.split_prefix(&lhs_prefix); |
184 | let rhs = new.split_prefix(&rhs_prefix); | 176 | let rhs = rhs.split_prefix(&rhs_prefix); |
185 | let lhs_tl = lhs.use_tree_list()?; | 177 | recursive_merge(&lhs, &rhs, merge).map(|(merged, _)| merged) |
186 | let rhs_tl = rhs.use_tree_list()?; | 178 | } |
187 | |||
188 | // if we are only allowed to merge the last level check if the split off paths are only one level deep | ||
189 | if merge_behaviour == MergeBehaviour::Last | ||
190 | && (use_tree_list_is_nested(&lhs_tl) || use_tree_list_is_nested(&rhs_tl)) | ||
191 | { | ||
192 | mark::hit!(test_last_merge_too_long); | ||
193 | return None; | ||
194 | } | ||
195 | 179 | ||
196 | let should_insert_comma = lhs_tl | 180 | /// Recursively "zips" together lhs and rhs. |
197 | .r_curly_token() | 181 | fn recursive_merge( |
198 | .and_then(|it| skip_trivia_token(it.prev_token()?, Direction::Prev)) | 182 | lhs: &ast::UseTree, |
199 | .map(|it| it.kind()) | 183 | rhs: &ast::UseTree, |
200 | != Some(T![,]); | 184 | merge: MergeBehaviour, |
201 | let mut to_insert: Vec<SyntaxElement> = Vec::new(); | 185 | ) -> Option<(ast::UseTree, bool)> { |
202 | if should_insert_comma { | 186 | let mut use_trees = lhs |
203 | to_insert.push(make::token(T![,]).into()); | 187 | .use_tree_list() |
204 | to_insert.push(make::tokens::single_space().into()); | 188 | .into_iter() |
205 | } | 189 | .flat_map(|list| list.use_trees()) |
206 | to_insert.extend( | 190 | // check if any of the use trees are nested, if they are and the behaviour is `last` we are not allowed to merge this |
207 | rhs_tl.syntax().children_with_tokens().filter(|it| !matches!(it.kind(), T!['{'] | T!['}'])), | 191 | // so early exit the iterator by using Option's Intoiterator impl |
208 | ); | 192 | .map(|tree| match merge == MergeBehaviour::Last && tree.use_tree_list().is_some() { |
209 | let pos = InsertPosition::Before(lhs_tl.r_curly_token()?.into()); | 193 | true => None, |
210 | let use_tree_list = lhs_tl.insert_children(pos, to_insert); | 194 | false => Some(tree), |
211 | Some(lhs.with_use_tree_list(use_tree_list)) | 195 | }) |
196 | .collect::<Option<Vec<_>>>()?; | ||
197 | use_trees.sort_unstable_by(|a, b| path_cmp_opt(a.path(), b.path())); | ||
198 | for rhs_t in rhs.use_tree_list().into_iter().flat_map(|list| list.use_trees()) { | ||
199 | let rhs_path = rhs_t.path(); | ||
200 | match use_trees.binary_search_by(|p| path_cmp_opt(p.path(), rhs_path.clone())) { | ||
201 | Ok(idx) => { | ||
202 | let lhs_t = &mut use_trees[idx]; | ||
203 | let lhs_path = lhs_t.path()?; | ||
204 | let rhs_path = rhs_path?; | ||
205 | let (lhs_prefix, rhs_prefix) = common_prefix(&lhs_path, &rhs_path)?; | ||
206 | if lhs_prefix == lhs_path && rhs_prefix == rhs_path { | ||
207 | let tree_is_self = |tree: ast::UseTree| { | ||
208 | tree.path().as_ref().map(path_is_self).unwrap_or(false) | ||
209 | }; | ||
210 | // check if only one of the two trees has a tree list, and whether that then contains `self` or not. | ||
211 | // If this is the case we can skip this iteration since the path without the list is already included in the other one via `self` | ||
212 | let tree_contains_self = |tree: &ast::UseTree| { | ||
213 | tree.use_tree_list() | ||
214 | .map(|tree_list| tree_list.use_trees().any(tree_is_self)) | ||
215 | .unwrap_or(false) | ||
216 | }; | ||
217 | match (tree_contains_self(&lhs_t), tree_contains_self(&rhs_t)) { | ||
218 | (true, false) => continue, | ||
219 | (false, true) => { | ||
220 | *lhs_t = rhs_t; | ||
221 | continue; | ||
222 | } | ||
223 | _ => (), | ||
224 | } | ||
225 | |||
226 | // glob imports arent part of the use-tree lists so we need to special handle them here as well | ||
227 | // this special handling is only required for when we merge a module import into a glob import of said module | ||
228 | // see the `merge_self_glob` or `merge_mod_into_glob` tests | ||
229 | if lhs_t.star_token().is_some() || rhs_t.star_token().is_some() { | ||
230 | *lhs_t = make::use_tree( | ||
231 | make::path_unqualified(make::path_segment_self()), | ||
232 | None, | ||
233 | None, | ||
234 | false, | ||
235 | ); | ||
236 | use_trees.insert(idx, make::glob_use_tree()); | ||
237 | continue; | ||
238 | } | ||
239 | } | ||
240 | let lhs = lhs_t.split_prefix(&lhs_prefix); | ||
241 | let rhs = rhs_t.split_prefix(&rhs_prefix); | ||
242 | let this_has_children = use_trees.len() > 0; | ||
243 | match recursive_merge(&lhs, &rhs, merge) { | ||
244 | Some((_, has_multiple_children)) | ||
245 | if merge == MergeBehaviour::Last | ||
246 | && this_has_children | ||
247 | && has_multiple_children => | ||
248 | { | ||
249 | return None | ||
250 | } | ||
251 | Some((use_tree, _)) => use_trees[idx] = use_tree, | ||
252 | None => use_trees.insert(idx, rhs_t), | ||
253 | } | ||
254 | } | ||
255 | Err(_) | ||
256 | if merge == MergeBehaviour::Last | ||
257 | && use_trees.len() > 0 | ||
258 | && rhs_t.use_tree_list().is_some() => | ||
259 | { | ||
260 | return None | ||
261 | } | ||
262 | Err(idx) => { | ||
263 | use_trees.insert(idx, rhs_t); | ||
264 | } | ||
265 | } | ||
266 | } | ||
267 | let has_multiple_children = use_trees.len() > 1; | ||
268 | Some((lhs.with_use_tree_list(make::use_tree_list(use_trees)), has_multiple_children)) | ||
212 | } | 269 | } |
213 | 270 | ||
214 | /// Traverses both paths until they differ, returning the common prefix of both. | 271 | /// Traverses both paths until they differ, returning the common prefix of both. |
@@ -219,7 +276,7 @@ fn common_prefix(lhs: &ast::Path, rhs: &ast::Path) -> Option<(ast::Path, ast::Pa | |||
219 | loop { | 276 | loop { |
220 | match (lhs_curr.segment(), rhs_curr.segment()) { | 277 | match (lhs_curr.segment(), rhs_curr.segment()) { |
221 | (Some(lhs), Some(rhs)) if lhs.syntax().text() == rhs.syntax().text() => (), | 278 | (Some(lhs), Some(rhs)) if lhs.syntax().text() == rhs.syntax().text() => (), |
222 | _ => break, | 279 | _ => break res, |
223 | } | 280 | } |
224 | res = Some((lhs_curr.clone(), rhs_curr.clone())); | 281 | res = Some((lhs_curr.clone(), rhs_curr.clone())); |
225 | 282 | ||
@@ -228,11 +285,62 @@ fn common_prefix(lhs: &ast::Path, rhs: &ast::Path) -> Option<(ast::Path, ast::Pa | |||
228 | lhs_curr = lhs; | 285 | lhs_curr = lhs; |
229 | rhs_curr = rhs; | 286 | rhs_curr = rhs; |
230 | } | 287 | } |
231 | _ => break, | 288 | _ => break res, |
289 | } | ||
290 | } | ||
291 | } | ||
292 | |||
293 | fn path_is_self(path: &ast::Path) -> bool { | ||
294 | path.segment().and_then(|seg| seg.self_token()).is_some() && path.qualifier().is_none() | ||
295 | } | ||
296 | |||
297 | #[inline] | ||
298 | fn first_segment(path: &ast::Path) -> Option<ast::PathSegment> { | ||
299 | first_path(path).segment() | ||
300 | } | ||
301 | |||
302 | fn first_path(path: &ast::Path) -> ast::Path { | ||
303 | successors(Some(path.clone()), ast::Path::qualifier).last().unwrap() | ||
304 | } | ||
305 | |||
306 | fn segment_iter(path: &ast::Path) -> impl Iterator<Item = ast::PathSegment> + Clone { | ||
307 | // cant make use of SyntaxNode::siblings, because the returned Iterator is not clone | ||
308 | successors(first_segment(path), |p| p.parent_path().parent_path().and_then(|p| p.segment())) | ||
309 | } | ||
310 | |||
311 | /// Orders paths in the following way: | ||
312 | /// the sole self token comes first, after that come uppercase identifiers, then lowercase identifiers | ||
313 | // FIXME: rustfmt sort lowercase idents before uppercase, in general we want to have the same ordering rustfmt has | ||
314 | // which is `self` and `super` first, then identifier imports with lowercase ones first, then glob imports and at last list imports. | ||
315 | // Example foo::{self, foo, baz, Baz, Qux, *, {Bar}} | ||
316 | fn path_cmp(a: &ast::Path, b: &ast::Path) -> Ordering { | ||
317 | match (path_is_self(a), path_is_self(b)) { | ||
318 | (true, true) => Ordering::Equal, | ||
319 | (true, false) => Ordering::Less, | ||
320 | (false, true) => Ordering::Greater, | ||
321 | (false, false) => { | ||
322 | let a = segment_iter(a); | ||
323 | let b = segment_iter(b); | ||
324 | // cmp_by would be useful for us here but that is currently unstable | ||
325 | // cmp doesnt work due the lifetimes on text's return type | ||
326 | a.zip(b) | ||
327 | .flat_map(|(seg, seg2)| seg.name_ref().zip(seg2.name_ref())) | ||
328 | .find_map(|(a, b)| match a.text().cmp(b.text()) { | ||
329 | ord @ Ordering::Greater | ord @ Ordering::Less => Some(ord), | ||
330 | Ordering::Equal => None, | ||
331 | }) | ||
332 | .unwrap_or(Ordering::Equal) | ||
232 | } | 333 | } |
233 | } | 334 | } |
335 | } | ||
234 | 336 | ||
235 | res | 337 | fn path_cmp_opt(a: Option<ast::Path>, b: Option<ast::Path>) -> Ordering { |
338 | match (a, b) { | ||
339 | (None, None) => Ordering::Equal, | ||
340 | (None, Some(_)) => Ordering::Less, | ||
341 | (Some(_), None) => Ordering::Greater, | ||
342 | (Some(a), Some(b)) => path_cmp(&a, &b), | ||
343 | } | ||
236 | } | 344 | } |
237 | 345 | ||
238 | /// What type of merges are allowed. | 346 | /// What type of merges are allowed. |
@@ -279,19 +387,6 @@ impl ImportGroup { | |||
279 | } | 387 | } |
280 | } | 388 | } |
281 | 389 | ||
282 | fn first_segment(path: &ast::Path) -> Option<ast::PathSegment> { | ||
283 | first_path(path).segment() | ||
284 | } | ||
285 | |||
286 | fn first_path(path: &ast::Path) -> ast::Path { | ||
287 | successors(Some(path.clone()), ast::Path::qualifier).last().unwrap() | ||
288 | } | ||
289 | |||
290 | fn segment_iter(path: &ast::Path) -> impl Iterator<Item = ast::PathSegment> + Clone { | ||
291 | // cant make use of SyntaxNode::siblings, because the returned Iterator is not clone | ||
292 | successors(first_segment(path), |p| p.parent_path().parent_path().and_then(|p| p.segment())) | ||
293 | } | ||
294 | |||
295 | #[derive(PartialEq, Eq)] | 390 | #[derive(PartialEq, Eq)] |
296 | enum AddBlankLine { | 391 | enum AddBlankLine { |
297 | Before, | 392 | Before, |
@@ -594,7 +689,7 @@ use std::io;", | |||
594 | check_full( | 689 | check_full( |
595 | "std::foo::bar::Baz", | 690 | "std::foo::bar::Baz", |
596 | r"use std::foo::bar::Qux;", | 691 | r"use std::foo::bar::Qux;", |
597 | r"use std::foo::bar::{Qux, Baz};", | 692 | r"use std::foo::bar::{Baz, Qux};", |
598 | ) | 693 | ) |
599 | } | 694 | } |
600 | 695 | ||
@@ -603,7 +698,7 @@ use std::io;", | |||
603 | check_last( | 698 | check_last( |
604 | "std::foo::bar::Baz", | 699 | "std::foo::bar::Baz", |
605 | r"use std::foo::bar::Qux;", | 700 | r"use std::foo::bar::Qux;", |
606 | r"use std::foo::bar::{Qux, Baz};", | 701 | r"use std::foo::bar::{Baz, Qux};", |
607 | ) | 702 | ) |
608 | } | 703 | } |
609 | 704 | ||
@@ -612,7 +707,7 @@ use std::io;", | |||
612 | check_full( | 707 | check_full( |
613 | "std::foo::bar::Baz", | 708 | "std::foo::bar::Baz", |
614 | r"use std::foo::bar::{Qux, Quux};", | 709 | r"use std::foo::bar::{Qux, Quux};", |
615 | r"use std::foo::bar::{Qux, Quux, Baz};", | 710 | r"use std::foo::bar::{Baz, Quux, Qux};", |
616 | ) | 711 | ) |
617 | } | 712 | } |
618 | 713 | ||
@@ -621,7 +716,7 @@ use std::io;", | |||
621 | check_last( | 716 | check_last( |
622 | "std::foo::bar::Baz", | 717 | "std::foo::bar::Baz", |
623 | r"use std::foo::bar::{Qux, Quux};", | 718 | r"use std::foo::bar::{Qux, Quux};", |
624 | r"use std::foo::bar::{Qux, Quux, Baz};", | 719 | r"use std::foo::bar::{Baz, Quux, Qux};", |
625 | ) | 720 | ) |
626 | } | 721 | } |
627 | 722 | ||
@@ -630,7 +725,7 @@ use std::io;", | |||
630 | check_full( | 725 | check_full( |
631 | "std::foo::bar::Baz", | 726 | "std::foo::bar::Baz", |
632 | r"use std::foo::bar::{Qux, quux::{Fez, Fizz}};", | 727 | r"use std::foo::bar::{Qux, quux::{Fez, Fizz}};", |
633 | r"use std::foo::bar::{Qux, quux::{Fez, Fizz}, Baz};", | 728 | r"use std::foo::bar::{Baz, Qux, quux::{Fez, Fizz}};", |
634 | ) | 729 | ) |
635 | } | 730 | } |
636 | 731 | ||
@@ -645,6 +740,15 @@ use std::foo::bar::{Qux, quux::{Fez, Fizz}};", | |||
645 | } | 740 | } |
646 | 741 | ||
647 | #[test] | 742 | #[test] |
743 | fn merge_groups_full_nested_deep() { | ||
744 | check_full( | ||
745 | "std::foo::bar::quux::Baz", | ||
746 | r"use std::foo::bar::{Qux, quux::{Fez, Fizz}};", | ||
747 | r"use std::foo::bar::{Qux, quux::{Baz, Fez, Fizz}};", | ||
748 | ) | ||
749 | } | ||
750 | |||
751 | #[test] | ||
648 | fn merge_groups_skip_pub() { | 752 | fn merge_groups_skip_pub() { |
649 | check_full( | 753 | check_full( |
650 | "std::io", | 754 | "std::io", |
@@ -670,34 +774,53 @@ use std::io;", | |||
670 | check_last( | 774 | check_last( |
671 | "std::fmt::Result", | 775 | "std::fmt::Result", |
672 | r"use std::{fmt, io};", | 776 | r"use std::{fmt, io};", |
673 | r"use std::{self, fmt::Result}; | 777 | r"use std::fmt::{self, Result}; |
674 | use std::io;", | 778 | use std::io;", |
675 | ) | 779 | ) |
676 | } | 780 | } |
677 | 781 | ||
678 | #[test] | 782 | #[test] |
783 | fn merge_into_module_import() { | ||
784 | check_full( | ||
785 | "std::fmt::Result", | ||
786 | r"use std::{fmt, io};", | ||
787 | r"use std::{fmt::{self, Result}, io};", | ||
788 | ) | ||
789 | } | ||
790 | |||
791 | #[test] | ||
679 | fn merge_groups_self() { | 792 | fn merge_groups_self() { |
680 | check_full("std::fmt::Debug", r"use std::fmt;", r"use std::fmt::{self, Debug};") | 793 | check_full("std::fmt::Debug", r"use std::fmt;", r"use std::fmt::{self, Debug};") |
681 | } | 794 | } |
682 | 795 | ||
683 | #[test] | 796 | #[test] |
684 | fn merge_self_glob() { | 797 | fn merge_mod_into_glob() { |
685 | check_full( | 798 | check_full( |
686 | "token::TokenKind", | 799 | "token::TokenKind", |
687 | r"use token::TokenKind::*;", | 800 | r"use token::TokenKind::*;", |
688 | r"use token::TokenKind::{self::*, self};", | 801 | r"use token::TokenKind::{*, self};", |
802 | ) | ||
803 | // FIXME: have it emit `use token::TokenKind::{self, *}`? | ||
804 | } | ||
805 | |||
806 | #[test] | ||
807 | fn merge_self_glob() { | ||
808 | check_full("self", r"use self::*;", r"use self::{*, self};") | ||
809 | // FIXME: have it emit `use {self, *}`? | ||
810 | } | ||
811 | |||
812 | #[test] | ||
813 | fn merge_glob_nested() { | ||
814 | check_full( | ||
815 | "foo::bar::quux::Fez", | ||
816 | r"use foo::bar::{Baz, quux::*};", | ||
817 | r"use foo::bar::{Baz, quux::{self::*, Fez}};", | ||
689 | ) | 818 | ) |
690 | } | 819 | } |
691 | 820 | ||
692 | #[test] | 821 | #[test] |
693 | fn merge_last_too_long() { | 822 | fn merge_last_too_long() { |
694 | mark::check!(test_last_merge_too_long); | 823 | check_last("foo::bar", r"use foo::bar::baz::Qux;", r"use foo::bar::{self, baz::Qux};"); |
695 | check_last( | ||
696 | "foo::bar", | ||
697 | r"use foo::bar::baz::Qux;", | ||
698 | r"use foo::bar; | ||
699 | use foo::bar::baz::Qux;", | ||
700 | ); | ||
701 | } | 824 | } |
702 | 825 | ||
703 | #[test] | 826 | #[test] |
@@ -710,6 +833,42 @@ use foo::bar::baz::Qux;", | |||
710 | ); | 833 | ); |
711 | } | 834 | } |
712 | 835 | ||
836 | #[test] | ||
837 | fn merge_last_fail() { | ||
838 | check_merge_only_fail( | ||
839 | r"use foo::bar::{baz::{Qux, Fez}};", | ||
840 | r"use foo::bar::{baaz::{Quux, Feez}};", | ||
841 | MergeBehaviour::Last, | ||
842 | ); | ||
843 | } | ||
844 | |||
845 | #[test] | ||
846 | fn merge_last_fail1() { | ||
847 | check_merge_only_fail( | ||
848 | r"use foo::bar::{baz::{Qux, Fez}};", | ||
849 | r"use foo::bar::baaz::{Quux, Feez};", | ||
850 | MergeBehaviour::Last, | ||
851 | ); | ||
852 | } | ||
853 | |||
854 | #[test] | ||
855 | fn merge_last_fail2() { | ||
856 | check_merge_only_fail( | ||
857 | r"use foo::bar::baz::{Qux, Fez};", | ||
858 | r"use foo::bar::{baaz::{Quux, Feez}};", | ||
859 | MergeBehaviour::Last, | ||
860 | ); | ||
861 | } | ||
862 | |||
863 | #[test] | ||
864 | fn merge_last_fail3() { | ||
865 | check_merge_only_fail( | ||
866 | r"use foo::bar::baz::{Qux, Fez};", | ||
867 | r"use foo::bar::baaz::{Quux, Feez};", | ||
868 | MergeBehaviour::Last, | ||
869 | ); | ||
870 | } | ||
871 | |||
713 | fn check( | 872 | fn check( |
714 | path: &str, | 873 | path: &str, |
715 | ra_fixture_before: &str, | 874 | ra_fixture_before: &str, |
@@ -742,4 +901,23 @@ use foo::bar::baz::Qux;", | |||
742 | fn check_none(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) { | 901 | fn check_none(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) { |
743 | check(path, ra_fixture_before, ra_fixture_after, None) | 902 | check(path, ra_fixture_before, ra_fixture_after, None) |
744 | } | 903 | } |
904 | |||
905 | fn check_merge_only_fail(ra_fixture0: &str, ra_fixture1: &str, mb: MergeBehaviour) { | ||
906 | let use0 = ast::SourceFile::parse(ra_fixture0) | ||
907 | .tree() | ||
908 | .syntax() | ||
909 | .descendants() | ||
910 | .find_map(ast::Use::cast) | ||
911 | .unwrap(); | ||
912 | |||
913 | let use1 = ast::SourceFile::parse(ra_fixture1) | ||
914 | .tree() | ||
915 | .syntax() | ||
916 | .descendants() | ||
917 | .find_map(ast::Use::cast) | ||
918 | .unwrap(); | ||
919 | |||
920 | let result = try_merge_imports(&use0, &use1, mb); | ||
921 | assert_eq!(result.map(|u| u.to_string()), None); | ||
922 | } | ||
745 | } | 923 | } |
diff --git a/crates/flycheck/src/lib.rs b/crates/flycheck/src/lib.rs index 16078d104..d982c5f29 100644 --- a/crates/flycheck/src/lib.rs +++ b/crates/flycheck/src/lib.rs | |||
@@ -59,11 +59,12 @@ pub struct FlycheckHandle { | |||
59 | 59 | ||
60 | impl FlycheckHandle { | 60 | impl FlycheckHandle { |
61 | pub fn spawn( | 61 | pub fn spawn( |
62 | id: usize, | ||
62 | sender: Box<dyn Fn(Message) + Send>, | 63 | sender: Box<dyn Fn(Message) + Send>, |
63 | config: FlycheckConfig, | 64 | config: FlycheckConfig, |
64 | workspace_root: PathBuf, | 65 | workspace_root: PathBuf, |
65 | ) -> FlycheckHandle { | 66 | ) -> FlycheckHandle { |
66 | let actor = FlycheckActor::new(sender, config, workspace_root); | 67 | let actor = FlycheckActor::new(id, sender, config, workspace_root); |
67 | let (sender, receiver) = unbounded::<Restart>(); | 68 | let (sender, receiver) = unbounded::<Restart>(); |
68 | let thread = jod_thread::spawn(move || actor.run(receiver)); | 69 | let thread = jod_thread::spawn(move || actor.run(receiver)); |
69 | FlycheckHandle { sender, thread } | 70 | FlycheckHandle { sender, thread } |
@@ -81,7 +82,11 @@ pub enum Message { | |||
81 | AddDiagnostic { workspace_root: PathBuf, diagnostic: Diagnostic }, | 82 | AddDiagnostic { workspace_root: PathBuf, diagnostic: Diagnostic }, |
82 | 83 | ||
83 | /// Request check progress notification to client | 84 | /// Request check progress notification to client |
84 | Progress(Progress), | 85 | Progress { |
86 | /// Flycheck instance ID | ||
87 | id: usize, | ||
88 | progress: Progress, | ||
89 | }, | ||
85 | } | 90 | } |
86 | 91 | ||
87 | #[derive(Debug)] | 92 | #[derive(Debug)] |
@@ -95,6 +100,7 @@ pub enum Progress { | |||
95 | struct Restart; | 100 | struct Restart; |
96 | 101 | ||
97 | struct FlycheckActor { | 102 | struct FlycheckActor { |
103 | id: usize, | ||
98 | sender: Box<dyn Fn(Message) + Send>, | 104 | sender: Box<dyn Fn(Message) + Send>, |
99 | config: FlycheckConfig, | 105 | config: FlycheckConfig, |
100 | workspace_root: PathBuf, | 106 | workspace_root: PathBuf, |
@@ -113,11 +119,15 @@ enum Event { | |||
113 | 119 | ||
114 | impl FlycheckActor { | 120 | impl FlycheckActor { |
115 | fn new( | 121 | fn new( |
122 | id: usize, | ||
116 | sender: Box<dyn Fn(Message) + Send>, | 123 | sender: Box<dyn Fn(Message) + Send>, |
117 | config: FlycheckConfig, | 124 | config: FlycheckConfig, |
118 | workspace_root: PathBuf, | 125 | workspace_root: PathBuf, |
119 | ) -> FlycheckActor { | 126 | ) -> FlycheckActor { |
120 | FlycheckActor { sender, config, workspace_root, cargo_handle: None } | 127 | FlycheckActor { id, sender, config, workspace_root, cargo_handle: None } |
128 | } | ||
129 | fn progress(&self, progress: Progress) { | ||
130 | self.send(Message::Progress { id: self.id, progress }); | ||
121 | } | 131 | } |
122 | fn next_event(&self, inbox: &Receiver<Restart>) -> Option<Event> { | 132 | fn next_event(&self, inbox: &Receiver<Restart>) -> Option<Event> { |
123 | let check_chan = self.cargo_handle.as_ref().map(|cargo| &cargo.receiver); | 133 | let check_chan = self.cargo_handle.as_ref().map(|cargo| &cargo.receiver); |
@@ -139,7 +149,7 @@ impl FlycheckActor { | |||
139 | command.stdout(Stdio::piped()).stderr(Stdio::null()).stdin(Stdio::null()); | 149 | command.stdout(Stdio::piped()).stderr(Stdio::null()).stdin(Stdio::null()); |
140 | if let Ok(child) = command.spawn().map(JodChild) { | 150 | if let Ok(child) = command.spawn().map(JodChild) { |
141 | self.cargo_handle = Some(CargoHandle::spawn(child)); | 151 | self.cargo_handle = Some(CargoHandle::spawn(child)); |
142 | self.send(Message::Progress(Progress::DidStart)); | 152 | self.progress(Progress::DidStart); |
143 | } | 153 | } |
144 | } | 154 | } |
145 | Event::CheckEvent(None) => { | 155 | Event::CheckEvent(None) => { |
@@ -153,11 +163,11 @@ impl FlycheckActor { | |||
153 | self.check_command() | 163 | self.check_command() |
154 | ) | 164 | ) |
155 | } | 165 | } |
156 | self.send(Message::Progress(Progress::DidFinish(res))); | 166 | self.progress(Progress::DidFinish(res)); |
157 | } | 167 | } |
158 | Event::CheckEvent(Some(message)) => match message { | 168 | Event::CheckEvent(Some(message)) => match message { |
159 | cargo_metadata::Message::CompilerArtifact(msg) => { | 169 | cargo_metadata::Message::CompilerArtifact(msg) => { |
160 | self.send(Message::Progress(Progress::DidCheckCrate(msg.target.name))); | 170 | self.progress(Progress::DidCheckCrate(msg.target.name)); |
161 | } | 171 | } |
162 | 172 | ||
163 | cargo_metadata::Message::CompilerMessage(msg) => { | 173 | cargo_metadata::Message::CompilerMessage(msg) => { |
@@ -179,7 +189,7 @@ impl FlycheckActor { | |||
179 | } | 189 | } |
180 | fn cancel_check_process(&mut self) { | 190 | fn cancel_check_process(&mut self) { |
181 | if self.cargo_handle.take().is_some() { | 191 | if self.cargo_handle.take().is_some() { |
182 | self.send(Message::Progress(Progress::DidCancel)); | 192 | self.progress(Progress::DidCancel); |
183 | } | 193 | } |
184 | } | 194 | } |
185 | fn check_command(&self) -> Command { | 195 | fn check_command(&self) -> Command { |
diff --git a/crates/hir/src/code_model.rs b/crates/hir/src/code_model.rs index 7a9747fc7..567fd91af 100644 --- a/crates/hir/src/code_model.rs +++ b/crates/hir/src/code_model.rs | |||
@@ -383,6 +383,16 @@ impl Module { | |||
383 | pub fn find_use_path(self, db: &dyn DefDatabase, item: impl Into<ItemInNs>) -> Option<ModPath> { | 383 | pub fn find_use_path(self, db: &dyn DefDatabase, item: impl Into<ItemInNs>) -> Option<ModPath> { |
384 | hir_def::find_path::find_path(db, item.into(), self.into()) | 384 | hir_def::find_path::find_path(db, item.into(), self.into()) |
385 | } | 385 | } |
386 | |||
387 | /// Finds a path that can be used to refer to the given item from within | ||
388 | /// this module, if possible. This is used for returning import paths for use-statements. | ||
389 | pub fn find_use_path_prefixed( | ||
390 | self, | ||
391 | db: &dyn DefDatabase, | ||
392 | item: impl Into<ItemInNs>, | ||
393 | ) -> Option<ModPath> { | ||
394 | hir_def::find_path::find_path_prefixed(db, item.into(), self.into()) | ||
395 | } | ||
386 | } | 396 | } |
387 | 397 | ||
388 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | 398 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
@@ -709,11 +719,23 @@ impl Function { | |||
709 | } | 719 | } |
710 | 720 | ||
711 | pub fn params(self, db: &dyn HirDatabase) -> Vec<Param> { | 721 | pub fn params(self, db: &dyn HirDatabase) -> Vec<Param> { |
722 | let resolver = self.id.resolver(db.upcast()); | ||
723 | let ctx = hir_ty::TyLoweringContext::new(db, &resolver); | ||
724 | let environment = TraitEnvironment::lower(db, &resolver); | ||
712 | db.function_data(self.id) | 725 | db.function_data(self.id) |
713 | .params | 726 | .params |
714 | .iter() | 727 | .iter() |
715 | .skip(if self.self_param(db).is_some() { 1 } else { 0 }) | 728 | .skip(if self.self_param(db).is_some() { 1 } else { 0 }) |
716 | .map(|_| Param { _ty: () }) | 729 | .map(|type_ref| { |
730 | let ty = Type { | ||
731 | krate: self.id.lookup(db.upcast()).container.module(db.upcast()).krate, | ||
732 | ty: InEnvironment { | ||
733 | value: Ty::from_hir_ext(&ctx, type_ref).0, | ||
734 | environment: environment.clone(), | ||
735 | }, | ||
736 | }; | ||
737 | Param { ty } | ||
738 | }) | ||
717 | .collect() | 739 | .collect() |
718 | } | 740 | } |
719 | 741 | ||
@@ -742,15 +764,21 @@ impl From<Mutability> for Access { | |||
742 | } | 764 | } |
743 | } | 765 | } |
744 | 766 | ||
767 | pub struct Param { | ||
768 | ty: Type, | ||
769 | } | ||
770 | |||
771 | impl Param { | ||
772 | pub fn ty(&self) -> &Type { | ||
773 | &self.ty | ||
774 | } | ||
775 | } | ||
776 | |||
745 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | 777 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
746 | pub struct SelfParam { | 778 | pub struct SelfParam { |
747 | func: FunctionId, | 779 | func: FunctionId, |
748 | } | 780 | } |
749 | 781 | ||
750 | pub struct Param { | ||
751 | _ty: (), | ||
752 | } | ||
753 | |||
754 | impl SelfParam { | 782 | impl SelfParam { |
755 | pub fn access(self, db: &dyn HirDatabase) -> Access { | 783 | pub fn access(self, db: &dyn HirDatabase) -> Access { |
756 | let func_data = db.function_data(self.func); | 784 | let func_data = db.function_data(self.func); |
@@ -908,12 +936,12 @@ impl MacroDef { | |||
908 | 936 | ||
909 | /// Indicate it is a proc-macro | 937 | /// Indicate it is a proc-macro |
910 | pub fn is_proc_macro(&self) -> bool { | 938 | pub fn is_proc_macro(&self) -> bool { |
911 | matches!(self.id.kind, MacroDefKind::CustomDerive(_)) | 939 | matches!(self.id.kind, MacroDefKind::ProcMacro(_)) |
912 | } | 940 | } |
913 | 941 | ||
914 | /// Indicate it is a derive macro | 942 | /// Indicate it is a derive macro |
915 | pub fn is_derive_macro(&self) -> bool { | 943 | pub fn is_derive_macro(&self) -> bool { |
916 | matches!(self.id.kind, MacroDefKind::CustomDerive(_) | MacroDefKind::BuiltInDerive(_)) | 944 | matches!(self.id.kind, MacroDefKind::ProcMacro(_) | MacroDefKind::BuiltInDerive(_)) |
917 | } | 945 | } |
918 | } | 946 | } |
919 | 947 | ||
@@ -1276,6 +1304,14 @@ impl Type { | |||
1276 | ) | 1304 | ) |
1277 | } | 1305 | } |
1278 | 1306 | ||
1307 | pub fn remove_ref(&self) -> Option<Type> { | ||
1308 | if let Ty::Apply(ApplicationTy { ctor: TypeCtor::Ref(_), .. }) = self.ty.value { | ||
1309 | self.ty.value.substs().map(|substs| self.derived(substs[0].clone())) | ||
1310 | } else { | ||
1311 | None | ||
1312 | } | ||
1313 | } | ||
1314 | |||
1279 | pub fn is_unknown(&self) -> bool { | 1315 | pub fn is_unknown(&self) -> bool { |
1280 | matches!(self.ty.value, Ty::Unknown) | 1316 | matches!(self.ty.value, Ty::Unknown) |
1281 | } | 1317 | } |
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index 0516a05b4..c61a430e1 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs | |||
@@ -697,6 +697,25 @@ fn find_root(node: &SyntaxNode) -> SyntaxNode { | |||
697 | node.ancestors().last().unwrap() | 697 | node.ancestors().last().unwrap() |
698 | } | 698 | } |
699 | 699 | ||
700 | /// `SemanticScope` encapsulates the notion of a scope (the set of visible | ||
701 | /// names) at a particular program point. | ||
702 | /// | ||
703 | /// It is a bit tricky, as scopes do not really exist inside the compiler. | ||
704 | /// Rather, the compiler directly computes for each reference the definition it | ||
705 | /// refers to. It might transiently compute the explicit scope map while doing | ||
706 | /// so, but, generally, this is not something left after the analysis. | ||
707 | /// | ||
708 | /// However, we do very much need explicit scopes for IDE purposes -- | ||
709 | /// completion, at its core, lists the contents of the current scope. The notion | ||
710 | /// of scope is also useful to answer questions like "what would be the meaning | ||
711 | /// of this piece of code if we inserted it into this position?". | ||
712 | /// | ||
713 | /// So `SemanticsScope` is constructed from a specific program point (a syntax | ||
714 | /// node or just a raw offset) and provides access to the set of visible names | ||
715 | /// on a somewhat best-effort basis. | ||
716 | /// | ||
717 | /// Note that if you are wondering "what does this specific existing name mean?", | ||
718 | /// you'd better use the `resolve_` family of methods. | ||
700 | #[derive(Debug)] | 719 | #[derive(Debug)] |
701 | pub struct SemanticsScope<'a> { | 720 | pub struct SemanticsScope<'a> { |
702 | pub db: &'a dyn HirDatabase, | 721 | pub db: &'a dyn HirDatabase, |
diff --git a/crates/hir_def/src/diagnostics.rs b/crates/hir_def/src/diagnostics.rs index 3e19d9117..001b3c5db 100644 --- a/crates/hir_def/src/diagnostics.rs +++ b/crates/hir_def/src/diagnostics.rs | |||
@@ -28,3 +28,52 @@ impl Diagnostic for UnresolvedModule { | |||
28 | self | 28 | self |
29 | } | 29 | } |
30 | } | 30 | } |
31 | |||
32 | #[derive(Debug)] | ||
33 | pub struct UnresolvedExternCrate { | ||
34 | pub file: HirFileId, | ||
35 | pub item: AstPtr<ast::ExternCrate>, | ||
36 | } | ||
37 | |||
38 | impl Diagnostic for UnresolvedExternCrate { | ||
39 | fn code(&self) -> DiagnosticCode { | ||
40 | DiagnosticCode("unresolved-extern-crate") | ||
41 | } | ||
42 | fn message(&self) -> String { | ||
43 | "unresolved extern crate".to_string() | ||
44 | } | ||
45 | fn display_source(&self) -> InFile<SyntaxNodePtr> { | ||
46 | InFile::new(self.file, self.item.clone().into()) | ||
47 | } | ||
48 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | ||
49 | self | ||
50 | } | ||
51 | } | ||
52 | |||
53 | #[derive(Debug)] | ||
54 | pub struct UnresolvedImport { | ||
55 | pub file: HirFileId, | ||
56 | pub node: AstPtr<ast::UseTree>, | ||
57 | } | ||
58 | |||
59 | impl Diagnostic for UnresolvedImport { | ||
60 | fn code(&self) -> DiagnosticCode { | ||
61 | DiagnosticCode("unresolved-import") | ||
62 | } | ||
63 | fn message(&self) -> String { | ||
64 | "unresolved import".to_string() | ||
65 | } | ||
66 | fn display_source(&self) -> InFile<SyntaxNodePtr> { | ||
67 | InFile::new(self.file, self.node.clone().into()) | ||
68 | } | ||
69 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | ||
70 | self | ||
71 | } | ||
72 | fn is_experimental(&self) -> bool { | ||
73 | // This currently results in false positives in the following cases: | ||
74 | // - `cfg_if!`-generated code in libstd (we don't load the sysroot correctly) | ||
75 | // - `core::arch` (we don't handle `#[path = "../<path>"]` correctly) | ||
76 | // - proc macros and/or proc macro generated code | ||
77 | true | ||
78 | } | ||
79 | } | ||
diff --git a/crates/hir_def/src/find_path.rs b/crates/hir_def/src/find_path.rs index ac2c54ac5..baf374144 100644 --- a/crates/hir_def/src/find_path.rs +++ b/crates/hir_def/src/find_path.rs | |||
@@ -4,6 +4,7 @@ use hir_expand::name::{known, AsName, Name}; | |||
4 | use rustc_hash::FxHashSet; | 4 | use rustc_hash::FxHashSet; |
5 | use test_utils::mark; | 5 | use test_utils::mark; |
6 | 6 | ||
7 | use crate::nameres::CrateDefMap; | ||
7 | use crate::{ | 8 | use crate::{ |
8 | db::DefDatabase, | 9 | db::DefDatabase, |
9 | item_scope::ItemInNs, | 10 | item_scope::ItemInNs, |
@@ -18,7 +19,12 @@ use crate::{ | |||
18 | /// *from where* you're referring to the item, hence the `from` parameter. | 19 | /// *from where* you're referring to the item, hence the `from` parameter. |
19 | pub fn find_path(db: &dyn DefDatabase, item: ItemInNs, from: ModuleId) -> Option<ModPath> { | 20 | pub fn find_path(db: &dyn DefDatabase, item: ItemInNs, from: ModuleId) -> Option<ModPath> { |
20 | let _p = profile::span("find_path"); | 21 | let _p = profile::span("find_path"); |
21 | find_path_inner(db, item, from, MAX_PATH_LEN) | 22 | find_path_inner(db, item, from, MAX_PATH_LEN, Prefixed::Not) |
23 | } | ||
24 | |||
25 | pub fn find_path_prefixed(db: &dyn DefDatabase, item: ItemInNs, from: ModuleId) -> Option<ModPath> { | ||
26 | let _p = profile::span("find_path_absolute"); | ||
27 | find_path_inner(db, item, from, MAX_PATH_LEN, Prefixed::Plain) | ||
22 | } | 28 | } |
23 | 29 | ||
24 | const MAX_PATH_LEN: usize = 15; | 30 | const MAX_PATH_LEN: usize = 15; |
@@ -36,11 +42,67 @@ impl ModPath { | |||
36 | } | 42 | } |
37 | } | 43 | } |
38 | 44 | ||
45 | fn check_crate_self_super( | ||
46 | def_map: &CrateDefMap, | ||
47 | item: ItemInNs, | ||
48 | from: ModuleId, | ||
49 | ) -> Option<ModPath> { | ||
50 | // - if the item is the crate root, return `crate` | ||
51 | if item | ||
52 | == ItemInNs::Types(ModuleDefId::ModuleId(ModuleId { | ||
53 | krate: from.krate, | ||
54 | local_id: def_map.root, | ||
55 | })) | ||
56 | { | ||
57 | Some(ModPath::from_segments(PathKind::Crate, Vec::new())) | ||
58 | } else if item == ItemInNs::Types(from.into()) { | ||
59 | // - if the item is the module we're in, use `self` | ||
60 | Some(ModPath::from_segments(PathKind::Super(0), Vec::new())) | ||
61 | } else { | ||
62 | if let Some(parent_id) = def_map.modules[from.local_id].parent { | ||
63 | // - if the item is the parent module, use `super` (this is not used recursively, since `super::super` is ugly) | ||
64 | if item | ||
65 | == ItemInNs::Types(ModuleDefId::ModuleId(ModuleId { | ||
66 | krate: from.krate, | ||
67 | local_id: parent_id, | ||
68 | })) | ||
69 | { | ||
70 | return Some(ModPath::from_segments(PathKind::Super(1), Vec::new())); | ||
71 | } | ||
72 | } | ||
73 | None | ||
74 | } | ||
75 | } | ||
76 | |||
77 | #[derive(Copy, Clone, PartialEq, Eq)] | ||
78 | pub enum Prefixed { | ||
79 | Not, | ||
80 | BySelf, | ||
81 | Plain, | ||
82 | } | ||
83 | |||
84 | impl Prefixed { | ||
85 | #[inline] | ||
86 | fn prefix(self) -> Option<PathKind> { | ||
87 | match self { | ||
88 | Prefixed::Not => None, | ||
89 | Prefixed::BySelf => Some(PathKind::Super(0)), | ||
90 | Prefixed::Plain => Some(PathKind::Plain), | ||
91 | } | ||
92 | } | ||
93 | |||
94 | #[inline] | ||
95 | fn prefixed(self) -> bool { | ||
96 | self != Prefixed::Not | ||
97 | } | ||
98 | } | ||
99 | |||
39 | fn find_path_inner( | 100 | fn find_path_inner( |
40 | db: &dyn DefDatabase, | 101 | db: &dyn DefDatabase, |
41 | item: ItemInNs, | 102 | item: ItemInNs, |
42 | from: ModuleId, | 103 | from: ModuleId, |
43 | max_len: usize, | 104 | max_len: usize, |
105 | prefixed: Prefixed, | ||
44 | ) -> Option<ModPath> { | 106 | ) -> Option<ModPath> { |
45 | if max_len == 0 { | 107 | if max_len == 0 { |
46 | return None; | 108 | return None; |
@@ -51,41 +113,22 @@ fn find_path_inner( | |||
51 | // - if the item is already in scope, return the name under which it is | 113 | // - if the item is already in scope, return the name under which it is |
52 | let def_map = db.crate_def_map(from.krate); | 114 | let def_map = db.crate_def_map(from.krate); |
53 | let from_scope: &crate::item_scope::ItemScope = &def_map.modules[from.local_id].scope; | 115 | let from_scope: &crate::item_scope::ItemScope = &def_map.modules[from.local_id].scope; |
54 | if let Some((name, _)) = from_scope.name_of(item) { | 116 | let scope_name = |
55 | return Some(ModPath::from_segments(PathKind::Plain, vec![name.clone()])); | 117 | if let Some((name, _)) = from_scope.name_of(item) { Some(name.clone()) } else { None }; |
118 | if !prefixed.prefixed() && scope_name.is_some() { | ||
119 | return scope_name | ||
120 | .map(|scope_name| ModPath::from_segments(PathKind::Plain, vec![scope_name])); | ||
56 | } | 121 | } |
57 | 122 | ||
58 | // - if the item is the crate root, return `crate` | 123 | if let modpath @ Some(_) = check_crate_self_super(&def_map, item, from) { |
59 | if item | 124 | return modpath; |
60 | == ItemInNs::Types(ModuleDefId::ModuleId(ModuleId { | ||
61 | krate: from.krate, | ||
62 | local_id: def_map.root, | ||
63 | })) | ||
64 | { | ||
65 | return Some(ModPath::from_segments(PathKind::Crate, Vec::new())); | ||
66 | } | ||
67 | |||
68 | // - if the item is the module we're in, use `self` | ||
69 | if item == ItemInNs::Types(from.into()) { | ||
70 | return Some(ModPath::from_segments(PathKind::Super(0), Vec::new())); | ||
71 | } | ||
72 | |||
73 | // - if the item is the parent module, use `super` (this is not used recursively, since `super::super` is ugly) | ||
74 | if let Some(parent_id) = def_map.modules[from.local_id].parent { | ||
75 | if item | ||
76 | == ItemInNs::Types(ModuleDefId::ModuleId(ModuleId { | ||
77 | krate: from.krate, | ||
78 | local_id: parent_id, | ||
79 | })) | ||
80 | { | ||
81 | return Some(ModPath::from_segments(PathKind::Super(1), Vec::new())); | ||
82 | } | ||
83 | } | 125 | } |
84 | 126 | ||
85 | // - if the item is the crate root of a dependency crate, return the name from the extern prelude | 127 | // - if the item is the crate root of a dependency crate, return the name from the extern prelude |
86 | for (name, def_id) in &def_map.extern_prelude { | 128 | for (name, def_id) in &def_map.extern_prelude { |
87 | if item == ItemInNs::Types(*def_id) { | 129 | if item == ItemInNs::Types(*def_id) { |
88 | return Some(ModPath::from_segments(PathKind::Plain, vec![name.clone()])); | 130 | let name = scope_name.unwrap_or_else(|| name.clone()); |
131 | return Some(ModPath::from_segments(PathKind::Plain, vec![name])); | ||
89 | } | 132 | } |
90 | } | 133 | } |
91 | 134 | ||
@@ -138,6 +181,7 @@ fn find_path_inner( | |||
138 | ItemInNs::Types(ModuleDefId::ModuleId(module_id)), | 181 | ItemInNs::Types(ModuleDefId::ModuleId(module_id)), |
139 | from, | 182 | from, |
140 | best_path_len - 1, | 183 | best_path_len - 1, |
184 | prefixed, | ||
141 | ) { | 185 | ) { |
142 | path.segments.push(name); | 186 | path.segments.push(name); |
143 | 187 | ||
@@ -165,6 +209,7 @@ fn find_path_inner( | |||
165 | ItemInNs::Types(ModuleDefId::ModuleId(info.container)), | 209 | ItemInNs::Types(ModuleDefId::ModuleId(info.container)), |
166 | from, | 210 | from, |
167 | best_path_len - 1, | 211 | best_path_len - 1, |
212 | prefixed, | ||
168 | )?; | 213 | )?; |
169 | path.segments.push(info.path.segments.last().unwrap().clone()); | 214 | path.segments.push(info.path.segments.last().unwrap().clone()); |
170 | Some(path) | 215 | Some(path) |
@@ -181,7 +226,13 @@ fn find_path_inner( | |||
181 | } | 226 | } |
182 | } | 227 | } |
183 | 228 | ||
184 | best_path | 229 | if let Some(prefix) = prefixed.prefix() { |
230 | best_path.or_else(|| { | ||
231 | scope_name.map(|scope_name| ModPath::from_segments(prefix, vec![scope_name])) | ||
232 | }) | ||
233 | } else { | ||
234 | best_path | ||
235 | } | ||
185 | } | 236 | } |
186 | 237 | ||
187 | fn select_best_path(old_path: ModPath, new_path: ModPath, prefer_no_std: bool) -> ModPath { | 238 | fn select_best_path(old_path: ModPath, new_path: ModPath, prefer_no_std: bool) -> ModPath { |
@@ -304,7 +355,7 @@ mod tests { | |||
304 | /// `code` needs to contain a cursor marker; checks that `find_path` for the | 355 | /// `code` needs to contain a cursor marker; checks that `find_path` for the |
305 | /// item the `path` refers to returns that same path when called from the | 356 | /// item the `path` refers to returns that same path when called from the |
306 | /// module the cursor is in. | 357 | /// module the cursor is in. |
307 | fn check_found_path(ra_fixture: &str, path: &str) { | 358 | fn check_found_path_(ra_fixture: &str, path: &str, absolute: bool) { |
308 | let (db, pos) = TestDB::with_position(ra_fixture); | 359 | let (db, pos) = TestDB::with_position(ra_fixture); |
309 | let module = db.module_for_file(pos.file_id); | 360 | let module = db.module_for_file(pos.file_id); |
310 | let parsed_path_file = syntax::SourceFile::parse(&format!("use {};", path)); | 361 | let parsed_path_file = syntax::SourceFile::parse(&format!("use {};", path)); |
@@ -324,9 +375,20 @@ mod tests { | |||
324 | .take_types() | 375 | .take_types() |
325 | .unwrap(); | 376 | .unwrap(); |
326 | 377 | ||
327 | let found_path = find_path(&db, ItemInNs::Types(resolved), module); | 378 | let found_path = if absolute { find_path_prefixed } else { find_path }( |
379 | &db, | ||
380 | ItemInNs::Types(resolved), | ||
381 | module, | ||
382 | ); | ||
383 | assert_eq!(found_path, Some(mod_path), "absolute {}", absolute); | ||
384 | } | ||
385 | |||
386 | fn check_found_path(ra_fixture: &str, path: &str) { | ||
387 | check_found_path_(ra_fixture, path, false); | ||
388 | } | ||
328 | 389 | ||
329 | assert_eq!(found_path, Some(mod_path)); | 390 | fn check_found_path_abs(ra_fixture: &str, path: &str) { |
391 | check_found_path_(ra_fixture, path, true); | ||
330 | } | 392 | } |
331 | 393 | ||
332 | #[test] | 394 | #[test] |
@@ -337,6 +399,7 @@ mod tests { | |||
337 | <|> | 399 | <|> |
338 | "#; | 400 | "#; |
339 | check_found_path(code, "S"); | 401 | check_found_path(code, "S"); |
402 | check_found_path_abs(code, "S"); | ||
340 | } | 403 | } |
341 | 404 | ||
342 | #[test] | 405 | #[test] |
@@ -347,6 +410,7 @@ mod tests { | |||
347 | <|> | 410 | <|> |
348 | "#; | 411 | "#; |
349 | check_found_path(code, "E::A"); | 412 | check_found_path(code, "E::A"); |
413 | check_found_path_abs(code, "E::A"); | ||
350 | } | 414 | } |
351 | 415 | ||
352 | #[test] | 416 | #[test] |
@@ -359,6 +423,7 @@ mod tests { | |||
359 | <|> | 423 | <|> |
360 | "#; | 424 | "#; |
361 | check_found_path(code, "foo::S"); | 425 | check_found_path(code, "foo::S"); |
426 | check_found_path_abs(code, "foo::S"); | ||
362 | } | 427 | } |
363 | 428 | ||
364 | #[test] | 429 | #[test] |
@@ -373,6 +438,7 @@ mod tests { | |||
373 | <|> | 438 | <|> |
374 | "#; | 439 | "#; |
375 | check_found_path(code, "super::S"); | 440 | check_found_path(code, "super::S"); |
441 | check_found_path_abs(code, "super::S"); | ||
376 | } | 442 | } |
377 | 443 | ||
378 | #[test] | 444 | #[test] |
@@ -384,6 +450,7 @@ mod tests { | |||
384 | <|> | 450 | <|> |
385 | "#; | 451 | "#; |
386 | check_found_path(code, "self"); | 452 | check_found_path(code, "self"); |
453 | check_found_path_abs(code, "self"); | ||
387 | } | 454 | } |
388 | 455 | ||
389 | #[test] | 456 | #[test] |
@@ -395,6 +462,7 @@ mod tests { | |||
395 | <|> | 462 | <|> |
396 | "#; | 463 | "#; |
397 | check_found_path(code, "crate"); | 464 | check_found_path(code, "crate"); |
465 | check_found_path_abs(code, "crate"); | ||
398 | } | 466 | } |
399 | 467 | ||
400 | #[test] | 468 | #[test] |
@@ -407,6 +475,7 @@ mod tests { | |||
407 | <|> | 475 | <|> |
408 | "#; | 476 | "#; |
409 | check_found_path(code, "crate::S"); | 477 | check_found_path(code, "crate::S"); |
478 | check_found_path_abs(code, "crate::S"); | ||
410 | } | 479 | } |
411 | 480 | ||
412 | #[test] | 481 | #[test] |
@@ -418,6 +487,7 @@ mod tests { | |||
418 | pub struct S; | 487 | pub struct S; |
419 | "#; | 488 | "#; |
420 | check_found_path(code, "std::S"); | 489 | check_found_path(code, "std::S"); |
490 | check_found_path_abs(code, "std::S"); | ||
421 | } | 491 | } |
422 | 492 | ||
423 | #[test] | 493 | #[test] |
@@ -430,14 +500,14 @@ mod tests { | |||
430 | pub struct S; | 500 | pub struct S; |
431 | "#; | 501 | "#; |
432 | check_found_path(code, "std_renamed::S"); | 502 | check_found_path(code, "std_renamed::S"); |
503 | check_found_path_abs(code, "std_renamed::S"); | ||
433 | } | 504 | } |
434 | 505 | ||
435 | #[test] | 506 | #[test] |
436 | fn partially_imported() { | 507 | fn partially_imported() { |
437 | // Tests that short paths are used even for external items, when parts of the path are | 508 | // Tests that short paths are used even for external items, when parts of the path are |
438 | // already in scope. | 509 | // already in scope. |
439 | check_found_path( | 510 | let code = r#" |
440 | r#" | ||
441 | //- /main.rs crate:main deps:syntax | 511 | //- /main.rs crate:main deps:syntax |
442 | 512 | ||
443 | use syntax::ast; | 513 | use syntax::ast; |
@@ -449,12 +519,11 @@ mod tests { | |||
449 | A, B, C, | 519 | A, B, C, |
450 | } | 520 | } |
451 | } | 521 | } |
452 | "#, | 522 | "#; |
453 | "ast::ModuleItem", | 523 | check_found_path(code, "ast::ModuleItem"); |
454 | ); | 524 | check_found_path_abs(code, "syntax::ast::ModuleItem"); |
455 | 525 | ||
456 | check_found_path( | 526 | let code = r#" |
457 | r#" | ||
458 | //- /main.rs crate:main deps:syntax | 527 | //- /main.rs crate:main deps:syntax |
459 | 528 | ||
460 | <|> | 529 | <|> |
@@ -465,9 +534,9 @@ mod tests { | |||
465 | A, B, C, | 534 | A, B, C, |
466 | } | 535 | } |
467 | } | 536 | } |
468 | "#, | 537 | "#; |
469 | "syntax::ast::ModuleItem", | 538 | check_found_path(code, "syntax::ast::ModuleItem"); |
470 | ); | 539 | check_found_path_abs(code, "syntax::ast::ModuleItem"); |
471 | } | 540 | } |
472 | 541 | ||
473 | #[test] | 542 | #[test] |
@@ -481,6 +550,7 @@ mod tests { | |||
481 | <|> | 550 | <|> |
482 | "#; | 551 | "#; |
483 | check_found_path(code, "bar::S"); | 552 | check_found_path(code, "bar::S"); |
553 | check_found_path_abs(code, "bar::S"); | ||
484 | } | 554 | } |
485 | 555 | ||
486 | #[test] | 556 | #[test] |
@@ -494,6 +564,7 @@ mod tests { | |||
494 | <|> | 564 | <|> |
495 | "#; | 565 | "#; |
496 | check_found_path(code, "bar::U"); | 566 | check_found_path(code, "bar::U"); |
567 | check_found_path_abs(code, "bar::U"); | ||
497 | } | 568 | } |
498 | 569 | ||
499 | #[test] | 570 | #[test] |
@@ -507,6 +578,7 @@ mod tests { | |||
507 | pub struct S; | 578 | pub struct S; |
508 | "#; | 579 | "#; |
509 | check_found_path(code, "std::S"); | 580 | check_found_path(code, "std::S"); |
581 | check_found_path_abs(code, "std::S"); | ||
510 | } | 582 | } |
511 | 583 | ||
512 | #[test] | 584 | #[test] |
@@ -520,6 +592,7 @@ mod tests { | |||
520 | pub use prelude::*; | 592 | pub use prelude::*; |
521 | "#; | 593 | "#; |
522 | check_found_path(code, "S"); | 594 | check_found_path(code, "S"); |
595 | check_found_path_abs(code, "S"); | ||
523 | } | 596 | } |
524 | 597 | ||
525 | #[test] | 598 | #[test] |
@@ -537,6 +610,8 @@ mod tests { | |||
537 | "#; | 610 | "#; |
538 | check_found_path(code, "None"); | 611 | check_found_path(code, "None"); |
539 | check_found_path(code, "Some"); | 612 | check_found_path(code, "Some"); |
613 | check_found_path_abs(code, "None"); | ||
614 | check_found_path_abs(code, "Some"); | ||
540 | } | 615 | } |
541 | 616 | ||
542 | #[test] | 617 | #[test] |
@@ -553,6 +628,7 @@ mod tests { | |||
553 | pub use crate::foo::bar::S; | 628 | pub use crate::foo::bar::S; |
554 | "#; | 629 | "#; |
555 | check_found_path(code, "baz::S"); | 630 | check_found_path(code, "baz::S"); |
631 | check_found_path_abs(code, "baz::S"); | ||
556 | } | 632 | } |
557 | 633 | ||
558 | #[test] | 634 | #[test] |
@@ -567,6 +643,7 @@ mod tests { | |||
567 | "#; | 643 | "#; |
568 | // crate::S would be shorter, but using private imports seems wrong | 644 | // crate::S would be shorter, but using private imports seems wrong |
569 | check_found_path(code, "crate::bar::S"); | 645 | check_found_path(code, "crate::bar::S"); |
646 | check_found_path_abs(code, "crate::bar::S"); | ||
570 | } | 647 | } |
571 | 648 | ||
572 | #[test] | 649 | #[test] |
@@ -585,6 +662,7 @@ mod tests { | |||
585 | pub use super::foo; | 662 | pub use super::foo; |
586 | "#; | 663 | "#; |
587 | check_found_path(code, "crate::foo::S"); | 664 | check_found_path(code, "crate::foo::S"); |
665 | check_found_path_abs(code, "crate::foo::S"); | ||
588 | } | 666 | } |
589 | 667 | ||
590 | #[test] | 668 | #[test] |
@@ -605,6 +683,7 @@ mod tests { | |||
605 | } | 683 | } |
606 | "#; | 684 | "#; |
607 | check_found_path(code, "std::sync::Arc"); | 685 | check_found_path(code, "std::sync::Arc"); |
686 | check_found_path_abs(code, "std::sync::Arc"); | ||
608 | } | 687 | } |
609 | 688 | ||
610 | #[test] | 689 | #[test] |
@@ -629,6 +708,7 @@ mod tests { | |||
629 | } | 708 | } |
630 | "#; | 709 | "#; |
631 | check_found_path(code, "core::fmt::Error"); | 710 | check_found_path(code, "core::fmt::Error"); |
711 | check_found_path_abs(code, "core::fmt::Error"); | ||
632 | } | 712 | } |
633 | 713 | ||
634 | #[test] | 714 | #[test] |
@@ -652,6 +732,7 @@ mod tests { | |||
652 | } | 732 | } |
653 | "#; | 733 | "#; |
654 | check_found_path(code, "alloc::sync::Arc"); | 734 | check_found_path(code, "alloc::sync::Arc"); |
735 | check_found_path_abs(code, "alloc::sync::Arc"); | ||
655 | } | 736 | } |
656 | 737 | ||
657 | #[test] | 738 | #[test] |
@@ -669,6 +750,7 @@ mod tests { | |||
669 | pub struct Arc; | 750 | pub struct Arc; |
670 | "#; | 751 | "#; |
671 | check_found_path(code, "megaalloc::Arc"); | 752 | check_found_path(code, "megaalloc::Arc"); |
753 | check_found_path_abs(code, "megaalloc::Arc"); | ||
672 | } | 754 | } |
673 | 755 | ||
674 | #[test] | 756 | #[test] |
@@ -683,5 +765,7 @@ mod tests { | |||
683 | "#; | 765 | "#; |
684 | check_found_path(code, "u8"); | 766 | check_found_path(code, "u8"); |
685 | check_found_path(code, "u16"); | 767 | check_found_path(code, "u16"); |
768 | check_found_path_abs(code, "u8"); | ||
769 | check_found_path_abs(code, "u16"); | ||
686 | } | 770 | } |
687 | } | 771 | } |
diff --git a/crates/hir_def/src/item_scope.rs b/crates/hir_def/src/item_scope.rs index f1e9dfd5b..12c24e1ca 100644 --- a/crates/hir_def/src/item_scope.rs +++ b/crates/hir_def/src/item_scope.rs | |||
@@ -5,10 +5,12 @@ use std::collections::hash_map::Entry; | |||
5 | 5 | ||
6 | use base_db::CrateId; | 6 | use base_db::CrateId; |
7 | use hir_expand::name::Name; | 7 | use hir_expand::name::Name; |
8 | use hir_expand::MacroDefKind; | ||
8 | use once_cell::sync::Lazy; | 9 | use once_cell::sync::Lazy; |
9 | use rustc_hash::{FxHashMap, FxHashSet}; | 10 | use rustc_hash::{FxHashMap, FxHashSet}; |
10 | use test_utils::mark; | 11 | use test_utils::mark; |
11 | 12 | ||
13 | use crate::ModuleId; | ||
12 | use crate::{ | 14 | use crate::{ |
13 | db::DefDatabase, per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType, HasModule, ImplId, | 15 | db::DefDatabase, per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType, HasModule, ImplId, |
14 | LocalModuleId, Lookup, MacroDefId, ModuleDefId, TraitId, | 16 | LocalModuleId, Lookup, MacroDefId, ModuleDefId, TraitId, |
@@ -265,6 +267,26 @@ impl ItemScope { | |||
265 | pub(crate) fn collect_legacy_macros(&self) -> FxHashMap<Name, MacroDefId> { | 267 | pub(crate) fn collect_legacy_macros(&self) -> FxHashMap<Name, MacroDefId> { |
266 | self.legacy_macros.clone() | 268 | self.legacy_macros.clone() |
267 | } | 269 | } |
270 | |||
271 | /// Marks everything that is not a procedural macro as private to `this_module`. | ||
272 | pub(crate) fn censor_non_proc_macros(&mut self, this_module: ModuleId) { | ||
273 | self.types | ||
274 | .values_mut() | ||
275 | .chain(self.values.values_mut()) | ||
276 | .map(|(_, v)| v) | ||
277 | .chain(self.unnamed_trait_imports.values_mut()) | ||
278 | .for_each(|vis| *vis = Visibility::Module(this_module)); | ||
279 | |||
280 | for (mac, vis) in self.macros.values_mut() { | ||
281 | if let MacroDefKind::ProcMacro(_) = mac.kind { | ||
282 | // FIXME: Technically this is insufficient since reexports of proc macros are also | ||
283 | // forbidden. Practically nobody does that. | ||
284 | continue; | ||
285 | } | ||
286 | |||
287 | *vis = Visibility::Module(this_module); | ||
288 | } | ||
289 | } | ||
268 | } | 290 | } |
269 | 291 | ||
270 | impl PerNs { | 292 | impl PerNs { |
diff --git a/crates/hir_def/src/item_tree.rs b/crates/hir_def/src/item_tree.rs index 52abb8e7f..0fd91b9d0 100644 --- a/crates/hir_def/src/item_tree.rs +++ b/crates/hir_def/src/item_tree.rs | |||
@@ -291,7 +291,6 @@ pub enum AttrOwner { | |||
291 | 291 | ||
292 | Variant(Idx<Variant>), | 292 | Variant(Idx<Variant>), |
293 | Field(Idx<Field>), | 293 | Field(Idx<Field>), |
294 | // FIXME: Store variant and field attrs, and stop reparsing them in `attrs_query`. | ||
295 | } | 294 | } |
296 | 295 | ||
297 | macro_rules! from_attrs { | 296 | macro_rules! from_attrs { |
@@ -483,11 +482,16 @@ pub struct Import { | |||
483 | /// AST ID of the `use` or `extern crate` item this import was derived from. Note that many | 482 | /// AST ID of the `use` or `extern crate` item this import was derived from. Note that many |
484 | /// `Import`s can map to the same `use` item. | 483 | /// `Import`s can map to the same `use` item. |
485 | pub ast_id: FileAstId<ast::Use>, | 484 | pub ast_id: FileAstId<ast::Use>, |
485 | /// Index of this `Import` when the containing `Use` is visited via `ModPath::expand_use_item`. | ||
486 | /// | ||
487 | /// This can be used to get the `UseTree` this `Import` corresponds to and allows emitting | ||
488 | /// precise diagnostics. | ||
489 | pub index: usize, | ||
486 | } | 490 | } |
487 | 491 | ||
488 | #[derive(Debug, Clone, Eq, PartialEq)] | 492 | #[derive(Debug, Clone, Eq, PartialEq)] |
489 | pub struct ExternCrate { | 493 | pub struct ExternCrate { |
490 | pub path: ModPath, | 494 | pub name: Name, |
491 | pub alias: Option<ImportAlias>, | 495 | pub alias: Option<ImportAlias>, |
492 | pub visibility: RawVisibilityId, | 496 | pub visibility: RawVisibilityId, |
493 | /// Whether this is a `#[macro_use] extern crate ...`. | 497 | /// Whether this is a `#[macro_use] extern crate ...`. |
diff --git a/crates/hir_def/src/item_tree/lower.rs b/crates/hir_def/src/item_tree/lower.rs index d93377c3b..54814f141 100644 --- a/crates/hir_def/src/item_tree/lower.rs +++ b/crates/hir_def/src/item_tree/lower.rs | |||
@@ -483,7 +483,7 @@ impl Ctx { | |||
483 | ModPath::expand_use_item( | 483 | ModPath::expand_use_item( |
484 | InFile::new(self.file, use_item.clone()), | 484 | InFile::new(self.file, use_item.clone()), |
485 | &self.hygiene, | 485 | &self.hygiene, |
486 | |path, _tree, is_glob, alias| { | 486 | |path, _use_tree, is_glob, alias| { |
487 | imports.push(id(tree.imports.alloc(Import { | 487 | imports.push(id(tree.imports.alloc(Import { |
488 | path, | 488 | path, |
489 | alias, | 489 | alias, |
@@ -491,6 +491,7 @@ impl Ctx { | |||
491 | is_glob, | 491 | is_glob, |
492 | is_prelude, | 492 | is_prelude, |
493 | ast_id, | 493 | ast_id, |
494 | index: imports.len(), | ||
494 | }))); | 495 | }))); |
495 | }, | 496 | }, |
496 | ); | 497 | ); |
@@ -502,7 +503,7 @@ impl Ctx { | |||
502 | &mut self, | 503 | &mut self, |
503 | extern_crate: &ast::ExternCrate, | 504 | extern_crate: &ast::ExternCrate, |
504 | ) -> Option<FileItemTreeId<ExternCrate>> { | 505 | ) -> Option<FileItemTreeId<ExternCrate>> { |
505 | let path = ModPath::from_name_ref(&extern_crate.name_ref()?); | 506 | let name = extern_crate.name_ref()?.as_name(); |
506 | let alias = extern_crate.rename().map(|a| { | 507 | let alias = extern_crate.rename().map(|a| { |
507 | a.name().map(|it| it.as_name()).map_or(ImportAlias::Underscore, ImportAlias::Alias) | 508 | a.name().map(|it| it.as_name()).map_or(ImportAlias::Underscore, ImportAlias::Alias) |
508 | }); | 509 | }); |
@@ -511,7 +512,7 @@ impl Ctx { | |||
511 | // FIXME: cfg_attr | 512 | // FIXME: cfg_attr |
512 | let is_macro_use = extern_crate.has_atom_attr("macro_use"); | 513 | let is_macro_use = extern_crate.has_atom_attr("macro_use"); |
513 | 514 | ||
514 | let res = ExternCrate { path, alias, visibility, is_macro_use, ast_id }; | 515 | let res = ExternCrate { name, alias, visibility, is_macro_use, ast_id }; |
515 | Some(id(self.data().extern_crates.alloc(res))) | 516 | Some(id(self.data().extern_crates.alloc(res))) |
516 | } | 517 | } |
517 | 518 | ||
diff --git a/crates/hir_def/src/item_tree/tests.rs b/crates/hir_def/src/item_tree/tests.rs index eed3d0d6f..1a806cda5 100644 --- a/crates/hir_def/src/item_tree/tests.rs +++ b/crates/hir_def/src/item_tree/tests.rs | |||
@@ -228,11 +228,11 @@ fn smoke() { | |||
228 | 228 | ||
229 | top-level items: | 229 | top-level items: |
230 | #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("attr_on_use"))] }, input: None }]) }] | 230 | #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("attr_on_use"))] }, input: None }]) }] |
231 | Import { path: ModPath { kind: Plain, segments: [Name(Text("a"))] }, alias: None, visibility: RawVisibilityId("pub(self)"), is_glob: false, is_prelude: false, ast_id: FileAstId::<syntax::ast::generated::nodes::Use>(0) } | 231 | Import { path: ModPath { kind: Plain, segments: [Name(Text("a"))] }, alias: None, visibility: RawVisibilityId("pub(self)"), is_glob: false, is_prelude: false, ast_id: FileAstId::<syntax::ast::generated::nodes::Use>(0), index: 0 } |
232 | #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("attr_on_use"))] }, input: None }]) }] | 232 | #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("attr_on_use"))] }, input: None }]) }] |
233 | Import { path: ModPath { kind: Plain, segments: [Name(Text("b"))] }, alias: None, visibility: RawVisibilityId("pub(self)"), is_glob: true, is_prelude: false, ast_id: FileAstId::<syntax::ast::generated::nodes::Use>(0) } | 233 | Import { path: ModPath { kind: Plain, segments: [Name(Text("b"))] }, alias: None, visibility: RawVisibilityId("pub(self)"), is_glob: true, is_prelude: false, ast_id: FileAstId::<syntax::ast::generated::nodes::Use>(0), index: 1 } |
234 | #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("ext_crate"))] }, input: None }]) }] | 234 | #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("ext_crate"))] }, input: None }]) }] |
235 | ExternCrate { path: ModPath { kind: Plain, segments: [Name(Text("krate"))] }, alias: None, visibility: RawVisibilityId("pub(self)"), is_macro_use: false, ast_id: FileAstId::<syntax::ast::generated::nodes::ExternCrate>(1) } | 235 | ExternCrate { name: Name(Text("krate")), alias: None, visibility: RawVisibilityId("pub(self)"), is_macro_use: false, ast_id: FileAstId::<syntax::ast::generated::nodes::ExternCrate>(1) } |
236 | #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("on_trait"))] }, input: None }]) }] | 236 | #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("on_trait"))] }, input: None }]) }] |
237 | Trait { name: Name(Text("Tr")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(0), auto: false, items: [TypeAlias(Idx::<TypeAlias>(0)), Const(Idx::<Const>(0)), Function(Idx::<Function>(0)), Function(Idx::<Function>(1))], ast_id: FileAstId::<syntax::ast::generated::nodes::Trait>(2) } | 237 | Trait { name: Name(Text("Tr")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(0), auto: false, items: [TypeAlias(Idx::<TypeAlias>(0)), Const(Idx::<Const>(0)), Function(Idx::<Function>(0)), Function(Idx::<Function>(1))], ast_id: FileAstId::<syntax::ast::generated::nodes::Trait>(2) } |
238 | > #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("assoc_ty"))] }, input: None }]) }] | 238 | > #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("assoc_ty"))] }, input: None }]) }] |
diff --git a/crates/hir_def/src/nameres.rs b/crates/hir_def/src/nameres.rs index bf302172d..5e4d73c1f 100644 --- a/crates/hir_def/src/nameres.rs +++ b/crates/hir_def/src/nameres.rs | |||
@@ -288,31 +288,70 @@ pub enum ModuleSource { | |||
288 | 288 | ||
289 | mod diagnostics { | 289 | mod diagnostics { |
290 | use hir_expand::diagnostics::DiagnosticSink; | 290 | use hir_expand::diagnostics::DiagnosticSink; |
291 | use hir_expand::hygiene::Hygiene; | ||
292 | use hir_expand::InFile; | ||
291 | use syntax::{ast, AstPtr}; | 293 | use syntax::{ast, AstPtr}; |
292 | 294 | ||
293 | use crate::{db::DefDatabase, diagnostics::UnresolvedModule, nameres::LocalModuleId, AstId}; | 295 | use crate::path::ModPath; |
296 | use crate::{db::DefDatabase, diagnostics::*, nameres::LocalModuleId, AstId}; | ||
294 | 297 | ||
295 | #[derive(Debug, PartialEq, Eq)] | 298 | #[derive(Debug, PartialEq, Eq)] |
296 | pub(super) enum DefDiagnostic { | 299 | enum DiagnosticKind { |
297 | UnresolvedModule { | 300 | UnresolvedModule { declaration: AstId<ast::Module>, candidate: String }, |
298 | module: LocalModuleId, | 301 | |
299 | declaration: AstId<ast::Module>, | 302 | UnresolvedExternCrate { ast: AstId<ast::ExternCrate> }, |
300 | candidate: String, | 303 | |
301 | }, | 304 | UnresolvedImport { ast: AstId<ast::Use>, index: usize }, |
305 | } | ||
306 | |||
307 | #[derive(Debug, PartialEq, Eq)] | ||
308 | pub(super) struct DefDiagnostic { | ||
309 | in_module: LocalModuleId, | ||
310 | kind: DiagnosticKind, | ||
302 | } | 311 | } |
303 | 312 | ||
304 | impl DefDiagnostic { | 313 | impl DefDiagnostic { |
314 | pub(super) fn unresolved_module( | ||
315 | container: LocalModuleId, | ||
316 | declaration: AstId<ast::Module>, | ||
317 | candidate: String, | ||
318 | ) -> Self { | ||
319 | Self { | ||
320 | in_module: container, | ||
321 | kind: DiagnosticKind::UnresolvedModule { declaration, candidate }, | ||
322 | } | ||
323 | } | ||
324 | |||
325 | pub(super) fn unresolved_extern_crate( | ||
326 | container: LocalModuleId, | ||
327 | declaration: AstId<ast::ExternCrate>, | ||
328 | ) -> Self { | ||
329 | Self { | ||
330 | in_module: container, | ||
331 | kind: DiagnosticKind::UnresolvedExternCrate { ast: declaration }, | ||
332 | } | ||
333 | } | ||
334 | |||
335 | pub(super) fn unresolved_import( | ||
336 | container: LocalModuleId, | ||
337 | ast: AstId<ast::Use>, | ||
338 | index: usize, | ||
339 | ) -> Self { | ||
340 | Self { in_module: container, kind: DiagnosticKind::UnresolvedImport { ast, index } } | ||
341 | } | ||
342 | |||
305 | pub(super) fn add_to( | 343 | pub(super) fn add_to( |
306 | &self, | 344 | &self, |
307 | db: &dyn DefDatabase, | 345 | db: &dyn DefDatabase, |
308 | target_module: LocalModuleId, | 346 | target_module: LocalModuleId, |
309 | sink: &mut DiagnosticSink, | 347 | sink: &mut DiagnosticSink, |
310 | ) { | 348 | ) { |
311 | match self { | 349 | if self.in_module != target_module { |
312 | DefDiagnostic::UnresolvedModule { module, declaration, candidate } => { | 350 | return; |
313 | if *module != target_module { | 351 | } |
314 | return; | 352 | |
315 | } | 353 | match &self.kind { |
354 | DiagnosticKind::UnresolvedModule { declaration, candidate } => { | ||
316 | let decl = declaration.to_node(db.upcast()); | 355 | let decl = declaration.to_node(db.upcast()); |
317 | sink.push(UnresolvedModule { | 356 | sink.push(UnresolvedModule { |
318 | file: declaration.file_id, | 357 | file: declaration.file_id, |
@@ -320,6 +359,36 @@ mod diagnostics { | |||
320 | candidate: candidate.clone(), | 359 | candidate: candidate.clone(), |
321 | }) | 360 | }) |
322 | } | 361 | } |
362 | |||
363 | DiagnosticKind::UnresolvedExternCrate { ast } => { | ||
364 | let item = ast.to_node(db.upcast()); | ||
365 | sink.push(UnresolvedExternCrate { | ||
366 | file: ast.file_id, | ||
367 | item: AstPtr::new(&item), | ||
368 | }); | ||
369 | } | ||
370 | |||
371 | DiagnosticKind::UnresolvedImport { ast, index } => { | ||
372 | let use_item = ast.to_node(db.upcast()); | ||
373 | let hygiene = Hygiene::new(db.upcast(), ast.file_id); | ||
374 | let mut cur = 0; | ||
375 | let mut tree = None; | ||
376 | ModPath::expand_use_item( | ||
377 | InFile::new(ast.file_id, use_item), | ||
378 | &hygiene, | ||
379 | |_mod_path, use_tree, _is_glob, _alias| { | ||
380 | if cur == *index { | ||
381 | tree = Some(use_tree.clone()); | ||
382 | } | ||
383 | |||
384 | cur += 1; | ||
385 | }, | ||
386 | ); | ||
387 | |||
388 | if let Some(tree) = tree { | ||
389 | sink.push(UnresolvedImport { file: ast.file_id, node: AstPtr::new(&tree) }); | ||
390 | } | ||
391 | } | ||
323 | } | 392 | } |
324 | } | 393 | } |
325 | } | 394 | } |
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs index 3e99c8773..100e25ffc 100644 --- a/crates/hir_def/src/nameres/collector.rs +++ b/crates/hir_def/src/nameres/collector.rs | |||
@@ -3,8 +3,11 @@ | |||
3 | //! `DefCollector::collect` contains the fixed-point iteration loop which | 3 | //! `DefCollector::collect` contains the fixed-point iteration loop which |
4 | //! resolves imports and expands macros. | 4 | //! resolves imports and expands macros. |
5 | 5 | ||
6 | use std::iter; | ||
7 | |||
6 | use base_db::{CrateId, FileId, ProcMacroId}; | 8 | use base_db::{CrateId, FileId, ProcMacroId}; |
7 | use cfg::CfgOptions; | 9 | use cfg::CfgOptions; |
10 | use hir_expand::InFile; | ||
8 | use hir_expand::{ | 11 | use hir_expand::{ |
9 | ast_id_map::FileAstId, | 12 | ast_id_map::FileAstId, |
10 | builtin_derive::find_builtin_derive, | 13 | builtin_derive::find_builtin_derive, |
@@ -13,17 +16,16 @@ use hir_expand::{ | |||
13 | proc_macro::ProcMacroExpander, | 16 | proc_macro::ProcMacroExpander, |
14 | HirFileId, MacroCallId, MacroDefId, MacroDefKind, | 17 | HirFileId, MacroCallId, MacroDefId, MacroDefKind, |
15 | }; | 18 | }; |
16 | use rustc_hash::FxHashMap; | 19 | use rustc_hash::{FxHashMap, FxHashSet}; |
17 | use syntax::ast; | 20 | use syntax::ast; |
18 | use test_utils::mark; | 21 | use test_utils::mark; |
22 | use tt::{Leaf, TokenTree}; | ||
19 | 23 | ||
20 | use crate::{ | 24 | use crate::{ |
21 | attr::Attrs, | 25 | attr::Attrs, |
22 | db::DefDatabase, | 26 | db::DefDatabase, |
23 | item_scope::{ImportType, PerNsGlobImports}, | 27 | item_scope::{ImportType, PerNsGlobImports}, |
24 | item_tree::{ | 28 | item_tree::{self, ItemTree, ItemTreeId, MacroCall, Mod, ModItem, ModKind, StructDefKind}, |
25 | self, FileItemTreeId, ItemTree, ItemTreeId, MacroCall, Mod, ModItem, ModKind, StructDefKind, | ||
26 | }, | ||
27 | nameres::{ | 29 | nameres::{ |
28 | diagnostics::DefDiagnostic, mod_resolution::ModDir, path_resolution::ReachedFixedPoint, | 30 | diagnostics::DefDiagnostic, mod_resolution::ModDir, path_resolution::ReachedFixedPoint, |
29 | BuiltinShadowMode, CrateDefMap, ModuleData, ModuleOrigin, ResolveMode, | 31 | BuiltinShadowMode, CrateDefMap, ModuleData, ModuleOrigin, ResolveMode, |
@@ -85,6 +87,7 @@ pub(super) fn collect_defs(db: &dyn DefDatabase, mut def_map: CrateDefMap) -> Cr | |||
85 | mod_dirs: FxHashMap::default(), | 87 | mod_dirs: FxHashMap::default(), |
86 | cfg_options, | 88 | cfg_options, |
87 | proc_macros, | 89 | proc_macros, |
90 | exports_proc_macros: false, | ||
88 | from_glob_import: Default::default(), | 91 | from_glob_import: Default::default(), |
89 | }; | 92 | }; |
90 | collector.collect(); | 93 | collector.collect(); |
@@ -112,6 +115,12 @@ impl PartialResolvedImport { | |||
112 | } | 115 | } |
113 | 116 | ||
114 | #[derive(Clone, Debug, Eq, PartialEq)] | 117 | #[derive(Clone, Debug, Eq, PartialEq)] |
118 | enum ImportSource { | ||
119 | Import(ItemTreeId<item_tree::Import>), | ||
120 | ExternCrate(ItemTreeId<item_tree::ExternCrate>), | ||
121 | } | ||
122 | |||
123 | #[derive(Clone, Debug, Eq, PartialEq)] | ||
115 | struct Import { | 124 | struct Import { |
116 | pub path: ModPath, | 125 | pub path: ModPath, |
117 | pub alias: Option<ImportAlias>, | 126 | pub alias: Option<ImportAlias>, |
@@ -120,11 +129,12 @@ struct Import { | |||
120 | pub is_prelude: bool, | 129 | pub is_prelude: bool, |
121 | pub is_extern_crate: bool, | 130 | pub is_extern_crate: bool, |
122 | pub is_macro_use: bool, | 131 | pub is_macro_use: bool, |
132 | source: ImportSource, | ||
123 | } | 133 | } |
124 | 134 | ||
125 | impl Import { | 135 | impl Import { |
126 | fn from_use(tree: &ItemTree, id: FileItemTreeId<item_tree::Import>) -> Self { | 136 | fn from_use(tree: &ItemTree, id: ItemTreeId<item_tree::Import>) -> Self { |
127 | let it = &tree[id]; | 137 | let it = &tree[id.value]; |
128 | let visibility = &tree[it.visibility]; | 138 | let visibility = &tree[it.visibility]; |
129 | Self { | 139 | Self { |
130 | path: it.path.clone(), | 140 | path: it.path.clone(), |
@@ -134,20 +144,22 @@ impl Import { | |||
134 | is_prelude: it.is_prelude, | 144 | is_prelude: it.is_prelude, |
135 | is_extern_crate: false, | 145 | is_extern_crate: false, |
136 | is_macro_use: false, | 146 | is_macro_use: false, |
147 | source: ImportSource::Import(id), | ||
137 | } | 148 | } |
138 | } | 149 | } |
139 | 150 | ||
140 | fn from_extern_crate(tree: &ItemTree, id: FileItemTreeId<item_tree::ExternCrate>) -> Self { | 151 | fn from_extern_crate(tree: &ItemTree, id: ItemTreeId<item_tree::ExternCrate>) -> Self { |
141 | let it = &tree[id]; | 152 | let it = &tree[id.value]; |
142 | let visibility = &tree[it.visibility]; | 153 | let visibility = &tree[it.visibility]; |
143 | Self { | 154 | Self { |
144 | path: it.path.clone(), | 155 | path: ModPath::from_segments(PathKind::Plain, iter::once(it.name.clone())), |
145 | alias: it.alias.clone(), | 156 | alias: it.alias.clone(), |
146 | visibility: visibility.clone(), | 157 | visibility: visibility.clone(), |
147 | is_glob: false, | 158 | is_glob: false, |
148 | is_prelude: false, | 159 | is_prelude: false, |
149 | is_extern_crate: true, | 160 | is_extern_crate: true, |
150 | is_macro_use: it.is_macro_use, | 161 | is_macro_use: it.is_macro_use, |
162 | source: ImportSource::ExternCrate(id), | ||
151 | } | 163 | } |
152 | } | 164 | } |
153 | } | 165 | } |
@@ -191,7 +203,12 @@ struct DefCollector<'a> { | |||
191 | unexpanded_attribute_macros: Vec<DeriveDirective>, | 203 | unexpanded_attribute_macros: Vec<DeriveDirective>, |
192 | mod_dirs: FxHashMap<LocalModuleId, ModDir>, | 204 | mod_dirs: FxHashMap<LocalModuleId, ModDir>, |
193 | cfg_options: &'a CfgOptions, | 205 | cfg_options: &'a CfgOptions, |
206 | /// List of procedural macros defined by this crate. This is read from the dynamic library | ||
207 | /// built by the build system, and is the list of proc. macros we can actually expand. It is | ||
208 | /// empty when proc. macro support is disabled (in which case we still do name resolution for | ||
209 | /// them). | ||
194 | proc_macros: Vec<(Name, ProcMacroExpander)>, | 210 | proc_macros: Vec<(Name, ProcMacroExpander)>, |
211 | exports_proc_macros: bool, | ||
195 | from_glob_import: PerNsGlobImports, | 212 | from_glob_import: PerNsGlobImports, |
196 | } | 213 | } |
197 | 214 | ||
@@ -245,28 +262,61 @@ impl DefCollector<'_> { | |||
245 | 262 | ||
246 | let unresolved_imports = std::mem::replace(&mut self.unresolved_imports, Vec::new()); | 263 | let unresolved_imports = std::mem::replace(&mut self.unresolved_imports, Vec::new()); |
247 | // show unresolved imports in completion, etc | 264 | // show unresolved imports in completion, etc |
248 | for directive in unresolved_imports { | 265 | for directive in &unresolved_imports { |
249 | self.record_resolved_import(&directive) | 266 | self.record_resolved_import(directive) |
267 | } | ||
268 | self.unresolved_imports = unresolved_imports; | ||
269 | |||
270 | // FIXME: This condition should instead check if this is a `proc-macro` type crate. | ||
271 | if self.exports_proc_macros { | ||
272 | // A crate exporting procedural macros is not allowed to export anything else. | ||
273 | // | ||
274 | // Additionally, while the proc macro entry points must be `pub`, they are not publicly | ||
275 | // exported in type/value namespace. This function reduces the visibility of all items | ||
276 | // in the crate root that aren't proc macros. | ||
277 | let root = self.def_map.root; | ||
278 | let root = &mut self.def_map.modules[root]; | ||
279 | root.scope.censor_non_proc_macros(ModuleId { | ||
280 | krate: self.def_map.krate, | ||
281 | local_id: self.def_map.root, | ||
282 | }); | ||
250 | } | 283 | } |
251 | |||
252 | // Record proc-macros | ||
253 | self.collect_proc_macro(); | ||
254 | } | 284 | } |
255 | 285 | ||
256 | fn collect_proc_macro(&mut self) { | 286 | /// Adds a definition of procedural macro `name` to the root module. |
257 | let proc_macros = std::mem::take(&mut self.proc_macros); | 287 | /// |
258 | for (name, expander) in proc_macros { | 288 | /// # Notes on procedural macro resolution |
259 | let krate = self.def_map.krate; | 289 | /// |
260 | 290 | /// Procedural macro functionality is provided by the build system: It has to build the proc | |
261 | let macro_id = MacroDefId { | 291 | /// macro and pass the resulting dynamic library to rust-analyzer. |
292 | /// | ||
293 | /// When procedural macro support is enabled, the list of proc macros exported by a crate is | ||
294 | /// known before we resolve names in the crate. This list is stored in `self.proc_macros` and is | ||
295 | /// derived from the dynamic library. | ||
296 | /// | ||
297 | /// However, we *also* would like to be able to at least *resolve* macros on our own, without | ||
298 | /// help by the build system. So, when the macro isn't found in `self.proc_macros`, we instead | ||
299 | /// use a dummy expander that always errors. This comes with the drawback of macros potentially | ||
300 | /// going out of sync with what the build system sees (since we resolve using VFS state, but | ||
301 | /// Cargo builds only on-disk files). We could and probably should add diagnostics for that. | ||
302 | fn resolve_proc_macro(&mut self, name: &Name) { | ||
303 | self.exports_proc_macros = true; | ||
304 | let macro_def = match self.proc_macros.iter().find(|(n, _)| n == name) { | ||
305 | Some((_, expander)) => MacroDefId { | ||
262 | ast_id: None, | 306 | ast_id: None, |
263 | krate: Some(krate), | 307 | krate: Some(self.def_map.krate), |
264 | kind: MacroDefKind::CustomDerive(expander), | 308 | kind: MacroDefKind::ProcMacro(*expander), |
265 | local_inner: false, | 309 | local_inner: false, |
266 | }; | 310 | }, |
311 | None => MacroDefId { | ||
312 | ast_id: None, | ||
313 | krate: Some(self.def_map.krate), | ||
314 | kind: MacroDefKind::ProcMacro(ProcMacroExpander::dummy(self.def_map.krate)), | ||
315 | local_inner: false, | ||
316 | }, | ||
317 | }; | ||
267 | 318 | ||
268 | self.define_proc_macro(name.clone(), macro_id); | 319 | self.define_proc_macro(name.clone(), macro_def); |
269 | } | ||
270 | } | 320 | } |
271 | 321 | ||
272 | /// Define a macro with `macro_rules`. | 322 | /// Define a macro with `macro_rules`. |
@@ -346,20 +396,15 @@ impl DefCollector<'_> { | |||
346 | fn import_macros_from_extern_crate( | 396 | fn import_macros_from_extern_crate( |
347 | &mut self, | 397 | &mut self, |
348 | current_module_id: LocalModuleId, | 398 | current_module_id: LocalModuleId, |
349 | import: &item_tree::ExternCrate, | 399 | extern_crate: &item_tree::ExternCrate, |
350 | ) { | 400 | ) { |
351 | log::debug!( | 401 | log::debug!( |
352 | "importing macros from extern crate: {:?} ({:?})", | 402 | "importing macros from extern crate: {:?} ({:?})", |
353 | import, | 403 | extern_crate, |
354 | self.def_map.edition, | 404 | self.def_map.edition, |
355 | ); | 405 | ); |
356 | 406 | ||
357 | let res = self.def_map.resolve_name_in_extern_prelude( | 407 | let res = self.def_map.resolve_name_in_extern_prelude(&extern_crate.name); |
358 | &import | ||
359 | .path | ||
360 | .as_ident() | ||
361 | .expect("extern crate should have been desugared to one-element path"), | ||
362 | ); | ||
363 | 408 | ||
364 | if let Some(ModuleDefId::ModuleId(m)) = res.take_types() { | 409 | if let Some(ModuleDefId::ModuleId(m)) = res.take_types() { |
365 | mark::hit!(macro_rules_from_other_crates_are_visible_with_macro_use); | 410 | mark::hit!(macro_rules_from_other_crates_are_visible_with_macro_use); |
@@ -420,7 +465,11 @@ impl DefCollector<'_> { | |||
420 | .as_ident() | 465 | .as_ident() |
421 | .expect("extern crate should have been desugared to one-element path"), | 466 | .expect("extern crate should have been desugared to one-element path"), |
422 | ); | 467 | ); |
423 | PartialResolvedImport::Resolved(res) | 468 | if res.is_none() { |
469 | PartialResolvedImport::Unresolved | ||
470 | } else { | ||
471 | PartialResolvedImport::Resolved(res) | ||
472 | } | ||
424 | } else { | 473 | } else { |
425 | let res = self.def_map.resolve_path_fp_with_macro( | 474 | let res = self.def_map.resolve_path_fp_with_macro( |
426 | self.db, | 475 | self.db, |
@@ -774,7 +823,51 @@ impl DefCollector<'_> { | |||
774 | .collect(item_tree.top_level_items()); | 823 | .collect(item_tree.top_level_items()); |
775 | } | 824 | } |
776 | 825 | ||
777 | fn finish(self) -> CrateDefMap { | 826 | fn finish(mut self) -> CrateDefMap { |
827 | // Emit diagnostics for all remaining unresolved imports. | ||
828 | |||
829 | // We'd like to avoid emitting a diagnostics avalanche when some `extern crate` doesn't | ||
830 | // resolve. We first emit diagnostics for unresolved extern crates and collect the missing | ||
831 | // crate names. Then we emit diagnostics for unresolved imports, but only if the import | ||
832 | // doesn't start with an unresolved crate's name. Due to renaming and reexports, this is a | ||
833 | // heuristic, but it works in practice. | ||
834 | let mut diagnosed_extern_crates = FxHashSet::default(); | ||
835 | for directive in &self.unresolved_imports { | ||
836 | if let ImportSource::ExternCrate(krate) = directive.import.source { | ||
837 | let item_tree = self.db.item_tree(krate.file_id); | ||
838 | let extern_crate = &item_tree[krate.value]; | ||
839 | |||
840 | diagnosed_extern_crates.insert(extern_crate.name.clone()); | ||
841 | |||
842 | self.def_map.diagnostics.push(DefDiagnostic::unresolved_extern_crate( | ||
843 | directive.module_id, | ||
844 | InFile::new(krate.file_id, extern_crate.ast_id), | ||
845 | )); | ||
846 | } | ||
847 | } | ||
848 | |||
849 | for directive in &self.unresolved_imports { | ||
850 | if let ImportSource::Import(import) = &directive.import.source { | ||
851 | let item_tree = self.db.item_tree(import.file_id); | ||
852 | let import_data = &item_tree[import.value]; | ||
853 | |||
854 | match (import_data.path.segments.first(), &import_data.path.kind) { | ||
855 | (Some(krate), PathKind::Plain) | (Some(krate), PathKind::Abs) => { | ||
856 | if diagnosed_extern_crates.contains(krate) { | ||
857 | continue; | ||
858 | } | ||
859 | } | ||
860 | _ => {} | ||
861 | } | ||
862 | |||
863 | self.def_map.diagnostics.push(DefDiagnostic::unresolved_import( | ||
864 | directive.module_id, | ||
865 | InFile::new(import.file_id, import_data.ast_id), | ||
866 | import_data.index, | ||
867 | )); | ||
868 | } | ||
869 | } | ||
870 | |||
778 | self.def_map | 871 | self.def_map |
779 | } | 872 | } |
780 | } | 873 | } |
@@ -819,178 +912,186 @@ impl ModCollector<'_, '_> { | |||
819 | 912 | ||
820 | for &item in items { | 913 | for &item in items { |
821 | let attrs = self.item_tree.attrs(item.into()); | 914 | let attrs = self.item_tree.attrs(item.into()); |
822 | if self.is_cfg_enabled(attrs) { | 915 | if !self.is_cfg_enabled(attrs) { |
823 | let module = | 916 | continue; |
824 | ModuleId { krate: self.def_collector.def_map.krate, local_id: self.module_id }; | 917 | } |
825 | let container = ContainerId::ModuleId(module); | 918 | let module = |
826 | 919 | ModuleId { krate: self.def_collector.def_map.krate, local_id: self.module_id }; | |
827 | let mut def = None; | 920 | let container = ContainerId::ModuleId(module); |
828 | match item { | 921 | |
829 | ModItem::Mod(m) => self.collect_module(&self.item_tree[m], attrs), | 922 | let mut def = None; |
830 | ModItem::Import(import_id) => { | 923 | match item { |
831 | self.def_collector.unresolved_imports.push(ImportDirective { | 924 | ModItem::Mod(m) => self.collect_module(&self.item_tree[m], attrs), |
832 | module_id: self.module_id, | 925 | ModItem::Import(import_id) => { |
833 | import: Import::from_use(&self.item_tree, import_id), | 926 | self.def_collector.unresolved_imports.push(ImportDirective { |
834 | status: PartialResolvedImport::Unresolved, | 927 | module_id: self.module_id, |
835 | }) | 928 | import: Import::from_use( |
836 | } | 929 | &self.item_tree, |
837 | ModItem::ExternCrate(import_id) => { | 930 | InFile::new(self.file_id, import_id), |
838 | self.def_collector.unresolved_imports.push(ImportDirective { | 931 | ), |
839 | module_id: self.module_id, | 932 | status: PartialResolvedImport::Unresolved, |
840 | import: Import::from_extern_crate(&self.item_tree, import_id), | 933 | }) |
841 | status: PartialResolvedImport::Unresolved, | 934 | } |
842 | }) | 935 | ModItem::ExternCrate(import_id) => { |
843 | } | 936 | self.def_collector.unresolved_imports.push(ImportDirective { |
844 | ModItem::MacroCall(mac) => self.collect_macro(&self.item_tree[mac]), | 937 | module_id: self.module_id, |
845 | ModItem::Impl(imp) => { | 938 | import: Import::from_extern_crate( |
846 | let module = ModuleId { | 939 | &self.item_tree, |
847 | krate: self.def_collector.def_map.krate, | 940 | InFile::new(self.file_id, import_id), |
848 | local_id: self.module_id, | 941 | ), |
849 | }; | 942 | status: PartialResolvedImport::Unresolved, |
850 | let container = ContainerId::ModuleId(module); | 943 | }) |
851 | let impl_id = ImplLoc { container, id: ItemTreeId::new(self.file_id, imp) } | 944 | } |
852 | .intern(self.def_collector.db); | 945 | ModItem::MacroCall(mac) => self.collect_macro(&self.item_tree[mac]), |
853 | self.def_collector.def_map.modules[self.module_id] | 946 | ModItem::Impl(imp) => { |
854 | .scope | 947 | let module = ModuleId { |
855 | .define_impl(impl_id) | 948 | krate: self.def_collector.def_map.krate, |
856 | } | 949 | local_id: self.module_id, |
857 | ModItem::Function(id) => { | 950 | }; |
858 | let func = &self.item_tree[id]; | 951 | let container = ContainerId::ModuleId(module); |
859 | def = Some(DefData { | 952 | let impl_id = ImplLoc { container, id: ItemTreeId::new(self.file_id, imp) } |
860 | id: FunctionLoc { | 953 | .intern(self.def_collector.db); |
861 | container: container.into(), | 954 | self.def_collector.def_map.modules[self.module_id].scope.define_impl(impl_id) |
862 | id: ItemTreeId::new(self.file_id, id), | 955 | } |
863 | } | 956 | ModItem::Function(id) => { |
864 | .intern(self.def_collector.db) | 957 | let func = &self.item_tree[id]; |
865 | .into(), | ||
866 | name: &func.name, | ||
867 | visibility: &self.item_tree[func.visibility], | ||
868 | has_constructor: false, | ||
869 | }); | ||
870 | } | ||
871 | ModItem::Struct(id) => { | ||
872 | let it = &self.item_tree[id]; | ||
873 | 958 | ||
874 | // FIXME: check attrs to see if this is an attribute macro invocation; | 959 | self.collect_proc_macro_def(&func.name, attrs); |
875 | // in which case we don't add the invocation, just a single attribute | ||
876 | // macro invocation | ||
877 | self.collect_derives(attrs, it.ast_id.upcast()); | ||
878 | 960 | ||
879 | def = Some(DefData { | 961 | def = Some(DefData { |
880 | id: StructLoc { container, id: ItemTreeId::new(self.file_id, id) } | 962 | id: FunctionLoc { |
881 | .intern(self.def_collector.db) | 963 | container: container.into(), |
882 | .into(), | 964 | id: ItemTreeId::new(self.file_id, id), |
883 | name: &it.name, | 965 | } |
884 | visibility: &self.item_tree[it.visibility], | 966 | .intern(self.def_collector.db) |
885 | has_constructor: it.kind != StructDefKind::Record, | 967 | .into(), |
886 | }); | 968 | name: &func.name, |
887 | } | 969 | visibility: &self.item_tree[func.visibility], |
888 | ModItem::Union(id) => { | 970 | has_constructor: false, |
889 | let it = &self.item_tree[id]; | 971 | }); |
972 | } | ||
973 | ModItem::Struct(id) => { | ||
974 | let it = &self.item_tree[id]; | ||
890 | 975 | ||
891 | // FIXME: check attrs to see if this is an attribute macro invocation; | 976 | // FIXME: check attrs to see if this is an attribute macro invocation; |
892 | // in which case we don't add the invocation, just a single attribute | 977 | // in which case we don't add the invocation, just a single attribute |
893 | // macro invocation | 978 | // macro invocation |
894 | self.collect_derives(attrs, it.ast_id.upcast()); | 979 | self.collect_derives(attrs, it.ast_id.upcast()); |
895 | 980 | ||
896 | def = Some(DefData { | 981 | def = Some(DefData { |
897 | id: UnionLoc { container, id: ItemTreeId::new(self.file_id, id) } | 982 | id: StructLoc { container, id: ItemTreeId::new(self.file_id, id) } |
898 | .intern(self.def_collector.db) | 983 | .intern(self.def_collector.db) |
899 | .into(), | 984 | .into(), |
900 | name: &it.name, | 985 | name: &it.name, |
901 | visibility: &self.item_tree[it.visibility], | 986 | visibility: &self.item_tree[it.visibility], |
902 | has_constructor: false, | 987 | has_constructor: it.kind != StructDefKind::Record, |
903 | }); | 988 | }); |
904 | } | 989 | } |
905 | ModItem::Enum(id) => { | 990 | ModItem::Union(id) => { |
906 | let it = &self.item_tree[id]; | 991 | let it = &self.item_tree[id]; |
907 | 992 | ||
908 | // FIXME: check attrs to see if this is an attribute macro invocation; | 993 | // FIXME: check attrs to see if this is an attribute macro invocation; |
909 | // in which case we don't add the invocation, just a single attribute | 994 | // in which case we don't add the invocation, just a single attribute |
910 | // macro invocation | 995 | // macro invocation |
911 | self.collect_derives(attrs, it.ast_id.upcast()); | 996 | self.collect_derives(attrs, it.ast_id.upcast()); |
912 | 997 | ||
913 | def = Some(DefData { | 998 | def = Some(DefData { |
914 | id: EnumLoc { container, id: ItemTreeId::new(self.file_id, id) } | 999 | id: UnionLoc { container, id: ItemTreeId::new(self.file_id, id) } |
915 | .intern(self.def_collector.db) | 1000 | .intern(self.def_collector.db) |
916 | .into(), | 1001 | .into(), |
917 | name: &it.name, | 1002 | name: &it.name, |
918 | visibility: &self.item_tree[it.visibility], | 1003 | visibility: &self.item_tree[it.visibility], |
919 | has_constructor: false, | 1004 | has_constructor: false, |
920 | }); | 1005 | }); |
921 | } | 1006 | } |
922 | ModItem::Const(id) => { | 1007 | ModItem::Enum(id) => { |
923 | let it = &self.item_tree[id]; | 1008 | let it = &self.item_tree[id]; |
924 | |||
925 | if let Some(name) = &it.name { | ||
926 | def = Some(DefData { | ||
927 | id: ConstLoc { | ||
928 | container: container.into(), | ||
929 | id: ItemTreeId::new(self.file_id, id), | ||
930 | } | ||
931 | .intern(self.def_collector.db) | ||
932 | .into(), | ||
933 | name, | ||
934 | visibility: &self.item_tree[it.visibility], | ||
935 | has_constructor: false, | ||
936 | }); | ||
937 | } | ||
938 | } | ||
939 | ModItem::Static(id) => { | ||
940 | let it = &self.item_tree[id]; | ||
941 | 1009 | ||
942 | def = Some(DefData { | 1010 | // FIXME: check attrs to see if this is an attribute macro invocation; |
943 | id: StaticLoc { container, id: ItemTreeId::new(self.file_id, id) } | 1011 | // in which case we don't add the invocation, just a single attribute |
944 | .intern(self.def_collector.db) | 1012 | // macro invocation |
945 | .into(), | 1013 | self.collect_derives(attrs, it.ast_id.upcast()); |
946 | name: &it.name, | ||
947 | visibility: &self.item_tree[it.visibility], | ||
948 | has_constructor: false, | ||
949 | }); | ||
950 | } | ||
951 | ModItem::Trait(id) => { | ||
952 | let it = &self.item_tree[id]; | ||
953 | 1014 | ||
954 | def = Some(DefData { | 1015 | def = Some(DefData { |
955 | id: TraitLoc { container, id: ItemTreeId::new(self.file_id, id) } | 1016 | id: EnumLoc { container, id: ItemTreeId::new(self.file_id, id) } |
956 | .intern(self.def_collector.db) | 1017 | .intern(self.def_collector.db) |
957 | .into(), | 1018 | .into(), |
958 | name: &it.name, | 1019 | name: &it.name, |
959 | visibility: &self.item_tree[it.visibility], | 1020 | visibility: &self.item_tree[it.visibility], |
960 | has_constructor: false, | 1021 | has_constructor: false, |
961 | }); | 1022 | }); |
962 | } | 1023 | } |
963 | ModItem::TypeAlias(id) => { | 1024 | ModItem::Const(id) => { |
964 | let it = &self.item_tree[id]; | 1025 | let it = &self.item_tree[id]; |
965 | 1026 | ||
1027 | if let Some(name) = &it.name { | ||
966 | def = Some(DefData { | 1028 | def = Some(DefData { |
967 | id: TypeAliasLoc { | 1029 | id: ConstLoc { |
968 | container: container.into(), | 1030 | container: container.into(), |
969 | id: ItemTreeId::new(self.file_id, id), | 1031 | id: ItemTreeId::new(self.file_id, id), |
970 | } | 1032 | } |
971 | .intern(self.def_collector.db) | 1033 | .intern(self.def_collector.db) |
972 | .into(), | 1034 | .into(), |
973 | name: &it.name, | 1035 | name, |
974 | visibility: &self.item_tree[it.visibility], | 1036 | visibility: &self.item_tree[it.visibility], |
975 | has_constructor: false, | 1037 | has_constructor: false, |
976 | }); | 1038 | }); |
977 | } | 1039 | } |
978 | } | 1040 | } |
1041 | ModItem::Static(id) => { | ||
1042 | let it = &self.item_tree[id]; | ||
979 | 1043 | ||
980 | if let Some(DefData { id, name, visibility, has_constructor }) = def { | 1044 | def = Some(DefData { |
981 | self.def_collector.def_map.modules[self.module_id].scope.define_def(id); | 1045 | id: StaticLoc { container, id: ItemTreeId::new(self.file_id, id) } |
982 | let vis = self | 1046 | .intern(self.def_collector.db) |
983 | .def_collector | 1047 | .into(), |
984 | .def_map | 1048 | name: &it.name, |
985 | .resolve_visibility(self.def_collector.db, self.module_id, visibility) | 1049 | visibility: &self.item_tree[it.visibility], |
986 | .unwrap_or(Visibility::Public); | 1050 | has_constructor: false, |
987 | self.def_collector.update( | 1051 | }); |
988 | self.module_id, | 1052 | } |
989 | &[(Some(name.clone()), PerNs::from_def(id, vis, has_constructor))], | 1053 | ModItem::Trait(id) => { |
990 | vis, | 1054 | let it = &self.item_tree[id]; |
991 | ImportType::Named, | 1055 | |
992 | ) | 1056 | def = Some(DefData { |
1057 | id: TraitLoc { container, id: ItemTreeId::new(self.file_id, id) } | ||
1058 | .intern(self.def_collector.db) | ||
1059 | .into(), | ||
1060 | name: &it.name, | ||
1061 | visibility: &self.item_tree[it.visibility], | ||
1062 | has_constructor: false, | ||
1063 | }); | ||
993 | } | 1064 | } |
1065 | ModItem::TypeAlias(id) => { | ||
1066 | let it = &self.item_tree[id]; | ||
1067 | |||
1068 | def = Some(DefData { | ||
1069 | id: TypeAliasLoc { | ||
1070 | container: container.into(), | ||
1071 | id: ItemTreeId::new(self.file_id, id), | ||
1072 | } | ||
1073 | .intern(self.def_collector.db) | ||
1074 | .into(), | ||
1075 | name: &it.name, | ||
1076 | visibility: &self.item_tree[it.visibility], | ||
1077 | has_constructor: false, | ||
1078 | }); | ||
1079 | } | ||
1080 | } | ||
1081 | |||
1082 | if let Some(DefData { id, name, visibility, has_constructor }) = def { | ||
1083 | self.def_collector.def_map.modules[self.module_id].scope.define_def(id); | ||
1084 | let vis = self | ||
1085 | .def_collector | ||
1086 | .def_map | ||
1087 | .resolve_visibility(self.def_collector.db, self.module_id, visibility) | ||
1088 | .unwrap_or(Visibility::Public); | ||
1089 | self.def_collector.update( | ||
1090 | self.module_id, | ||
1091 | &[(Some(name.clone()), PerNs::from_def(id, vis, has_constructor))], | ||
1092 | vis, | ||
1093 | ImportType::Named, | ||
1094 | ) | ||
994 | } | 1095 | } |
995 | } | 1096 | } |
996 | } | 1097 | } |
@@ -1051,13 +1152,11 @@ impl ModCollector<'_, '_> { | |||
1051 | self.import_all_legacy_macros(module_id); | 1152 | self.import_all_legacy_macros(module_id); |
1052 | } | 1153 | } |
1053 | } | 1154 | } |
1054 | Err(candidate) => self.def_collector.def_map.diagnostics.push( | 1155 | Err(candidate) => { |
1055 | DefDiagnostic::UnresolvedModule { | 1156 | self.def_collector.def_map.diagnostics.push( |
1056 | module: self.module_id, | 1157 | DefDiagnostic::unresolved_module(self.module_id, ast_id, candidate), |
1057 | declaration: ast_id, | 1158 | ); |
1058 | candidate, | 1159 | } |
1059 | }, | ||
1060 | ), | ||
1061 | }; | 1160 | }; |
1062 | } | 1161 | } |
1063 | } | 1162 | } |
@@ -1119,6 +1218,30 @@ impl ModCollector<'_, '_> { | |||
1119 | } | 1218 | } |
1120 | } | 1219 | } |
1121 | 1220 | ||
1221 | /// If `attrs` registers a procedural macro, collects its definition. | ||
1222 | fn collect_proc_macro_def(&mut self, func_name: &Name, attrs: &Attrs) { | ||
1223 | // FIXME: this should only be done in the root module of `proc-macro` crates, not everywhere | ||
1224 | // FIXME: distinguish the type of macro | ||
1225 | let macro_name = if attrs.by_key("proc_macro").exists() | ||
1226 | || attrs.by_key("proc_macro_attribute").exists() | ||
1227 | { | ||
1228 | func_name.clone() | ||
1229 | } else { | ||
1230 | let derive = attrs.by_key("proc_macro_derive"); | ||
1231 | if let Some(arg) = derive.tt_values().next() { | ||
1232 | if let [TokenTree::Leaf(Leaf::Ident(trait_name))] = &*arg.token_trees { | ||
1233 | trait_name.as_name() | ||
1234 | } else { | ||
1235 | return; | ||
1236 | } | ||
1237 | } else { | ||
1238 | return; | ||
1239 | } | ||
1240 | }; | ||
1241 | |||
1242 | self.def_collector.resolve_proc_macro(¯o_name); | ||
1243 | } | ||
1244 | |||
1122 | fn collect_macro(&mut self, mac: &MacroCall) { | 1245 | fn collect_macro(&mut self, mac: &MacroCall) { |
1123 | let mut ast_id = AstIdWithPath::new(self.file_id, mac.ast_id, mac.path.clone()); | 1246 | let mut ast_id = AstIdWithPath::new(self.file_id, mac.ast_id, mac.path.clone()); |
1124 | 1247 | ||
@@ -1225,6 +1348,7 @@ mod tests { | |||
1225 | mod_dirs: FxHashMap::default(), | 1348 | mod_dirs: FxHashMap::default(), |
1226 | cfg_options: &CfgOptions::default(), | 1349 | cfg_options: &CfgOptions::default(), |
1227 | proc_macros: Default::default(), | 1350 | proc_macros: Default::default(), |
1351 | exports_proc_macros: false, | ||
1228 | from_glob_import: Default::default(), | 1352 | from_glob_import: Default::default(), |
1229 | }; | 1353 | }; |
1230 | collector.collect(); | 1354 | collector.collect(); |
diff --git a/crates/hir_def/src/nameres/tests.rs b/crates/hir_def/src/nameres/tests.rs index 5ca30dac9..11d84f808 100644 --- a/crates/hir_def/src/nameres/tests.rs +++ b/crates/hir_def/src/nameres/tests.rs | |||
@@ -2,6 +2,7 @@ mod globs; | |||
2 | mod incremental; | 2 | mod incremental; |
3 | mod macros; | 3 | mod macros; |
4 | mod mod_resolution; | 4 | mod mod_resolution; |
5 | mod diagnostics; | ||
5 | mod primitives; | 6 | mod primitives; |
6 | 7 | ||
7 | use std::sync::Arc; | 8 | use std::sync::Arc; |
diff --git a/crates/hir_def/src/nameres/tests/diagnostics.rs b/crates/hir_def/src/nameres/tests/diagnostics.rs new file mode 100644 index 000000000..576b813d2 --- /dev/null +++ b/crates/hir_def/src/nameres/tests/diagnostics.rs | |||
@@ -0,0 +1,131 @@ | |||
1 | use base_db::fixture::WithFixture; | ||
2 | use base_db::FileId; | ||
3 | use base_db::SourceDatabaseExt; | ||
4 | use hir_expand::db::AstDatabase; | ||
5 | use rustc_hash::FxHashMap; | ||
6 | use syntax::TextRange; | ||
7 | use syntax::TextSize; | ||
8 | |||
9 | use crate::test_db::TestDB; | ||
10 | |||
11 | fn check_diagnostics(ra_fixture: &str) { | ||
12 | let db: TestDB = TestDB::with_files(ra_fixture); | ||
13 | let annotations = db.extract_annotations(); | ||
14 | assert!(!annotations.is_empty()); | ||
15 | |||
16 | let mut actual: FxHashMap<FileId, Vec<(TextRange, String)>> = FxHashMap::default(); | ||
17 | db.diagnostics(|d| { | ||
18 | let src = d.display_source(); | ||
19 | let root = db.parse_or_expand(src.file_id).unwrap(); | ||
20 | // FIXME: macros... | ||
21 | let file_id = src.file_id.original_file(&db); | ||
22 | let range = src.value.to_node(&root).text_range(); | ||
23 | let message = d.message().to_owned(); | ||
24 | actual.entry(file_id).or_default().push((range, message)); | ||
25 | }); | ||
26 | |||
27 | for (file_id, diags) in actual.iter_mut() { | ||
28 | diags.sort_by_key(|it| it.0.start()); | ||
29 | let text = db.file_text(*file_id); | ||
30 | // For multiline spans, place them on line start | ||
31 | for (range, content) in diags { | ||
32 | if text[*range].contains('\n') { | ||
33 | *range = TextRange::new(range.start(), range.start() + TextSize::from(1)); | ||
34 | *content = format!("... {}", content); | ||
35 | } | ||
36 | } | ||
37 | } | ||
38 | |||
39 | assert_eq!(annotations, actual); | ||
40 | } | ||
41 | |||
42 | #[test] | ||
43 | fn unresolved_import() { | ||
44 | check_diagnostics( | ||
45 | r" | ||
46 | use does_exist; | ||
47 | use does_not_exist; | ||
48 | //^^^^^^^^^^^^^^ unresolved import | ||
49 | |||
50 | mod does_exist {} | ||
51 | ", | ||
52 | ); | ||
53 | } | ||
54 | |||
55 | #[test] | ||
56 | fn unresolved_import_in_use_tree() { | ||
57 | // Only the relevant part of a nested `use` item should be highlighted. | ||
58 | check_diagnostics( | ||
59 | r" | ||
60 | use does_exist::{Exists, DoesntExist}; | ||
61 | //^^^^^^^^^^^ unresolved import | ||
62 | |||
63 | use {does_not_exist::*, does_exist}; | ||
64 | //^^^^^^^^^^^^^^^^^ unresolved import | ||
65 | |||
66 | use does_not_exist::{ | ||
67 | a, | ||
68 | //^ unresolved import | ||
69 | b, | ||
70 | //^ unresolved import | ||
71 | c, | ||
72 | //^ unresolved import | ||
73 | }; | ||
74 | |||
75 | mod does_exist { | ||
76 | pub struct Exists; | ||
77 | } | ||
78 | ", | ||
79 | ); | ||
80 | } | ||
81 | |||
82 | #[test] | ||
83 | fn unresolved_extern_crate() { | ||
84 | check_diagnostics( | ||
85 | r" | ||
86 | //- /main.rs crate:main deps:core | ||
87 | extern crate core; | ||
88 | extern crate doesnotexist; | ||
89 | //^^^^^^^^^^^^^^^^^^^^^^^^^^ unresolved extern crate | ||
90 | //- /lib.rs crate:core | ||
91 | ", | ||
92 | ); | ||
93 | } | ||
94 | |||
95 | #[test] | ||
96 | fn dedup_unresolved_import_from_unresolved_crate() { | ||
97 | check_diagnostics( | ||
98 | r" | ||
99 | //- /main.rs crate:main | ||
100 | mod a { | ||
101 | extern crate doesnotexist; | ||
102 | //^^^^^^^^^^^^^^^^^^^^^^^^^^ unresolved extern crate | ||
103 | |||
104 | // Should not error, since we already errored for the missing crate. | ||
105 | use doesnotexist::{self, bla, *}; | ||
106 | |||
107 | use crate::doesnotexist; | ||
108 | //^^^^^^^^^^^^^^^^^^^ unresolved import | ||
109 | } | ||
110 | |||
111 | mod m { | ||
112 | use super::doesnotexist; | ||
113 | //^^^^^^^^^^^^^^^^^^^ unresolved import | ||
114 | } | ||
115 | ", | ||
116 | ); | ||
117 | } | ||
118 | |||
119 | #[test] | ||
120 | fn unresolved_module() { | ||
121 | check_diagnostics( | ||
122 | r" | ||
123 | //- /lib.rs | ||
124 | mod foo; | ||
125 | mod bar; | ||
126 | //^^^^^^^^ unresolved module | ||
127 | mod baz {} | ||
128 | //- /foo.rs | ||
129 | ", | ||
130 | ); | ||
131 | } | ||
diff --git a/crates/hir_def/src/nameres/tests/macros.rs b/crates/hir_def/src/nameres/tests/macros.rs index e0fb8bdef..0851c3b7d 100644 --- a/crates/hir_def/src/nameres/tests/macros.rs +++ b/crates/hir_def/src/nameres/tests/macros.rs | |||
@@ -667,3 +667,76 @@ b! { static = #[] (); } | |||
667 | "#]], | 667 | "#]], |
668 | ); | 668 | ); |
669 | } | 669 | } |
670 | |||
671 | #[test] | ||
672 | fn resolves_proc_macros() { | ||
673 | check( | ||
674 | r" | ||
675 | struct TokenStream; | ||
676 | |||
677 | #[proc_macro] | ||
678 | pub fn function_like_macro(args: TokenStream) -> TokenStream { | ||
679 | args | ||
680 | } | ||
681 | |||
682 | #[proc_macro_attribute] | ||
683 | pub fn attribute_macro(_args: TokenStream, item: TokenStream) -> TokenStream { | ||
684 | item | ||
685 | } | ||
686 | |||
687 | #[proc_macro_derive(DummyTrait)] | ||
688 | pub fn derive_macro(_item: TokenStream) -> TokenStream { | ||
689 | TokenStream | ||
690 | } | ||
691 | ", | ||
692 | expect![[r#" | ||
693 | crate | ||
694 | DummyTrait: m | ||
695 | TokenStream: t v | ||
696 | attribute_macro: v m | ||
697 | derive_macro: v | ||
698 | function_like_macro: v m | ||
699 | "#]], | ||
700 | ); | ||
701 | } | ||
702 | |||
703 | #[test] | ||
704 | fn proc_macro_censoring() { | ||
705 | // Make sure that only proc macros are publicly exported from proc-macro crates. | ||
706 | |||
707 | check( | ||
708 | r" | ||
709 | //- /main.rs crate:main deps:macros | ||
710 | pub use macros::*; | ||
711 | |||
712 | //- /macros.rs crate:macros | ||
713 | pub struct TokenStream; | ||
714 | |||
715 | #[proc_macro] | ||
716 | pub fn function_like_macro(args: TokenStream) -> TokenStream { | ||
717 | args | ||
718 | } | ||
719 | |||
720 | #[proc_macro_attribute] | ||
721 | pub fn attribute_macro(_args: TokenStream, item: TokenStream) -> TokenStream { | ||
722 | item | ||
723 | } | ||
724 | |||
725 | #[proc_macro_derive(DummyTrait)] | ||
726 | pub fn derive_macro(_item: TokenStream) -> TokenStream { | ||
727 | TokenStream | ||
728 | } | ||
729 | |||
730 | #[macro_export] | ||
731 | macro_rules! mbe { | ||
732 | () => {}; | ||
733 | } | ||
734 | ", | ||
735 | expect![[r#" | ||
736 | crate | ||
737 | DummyTrait: m | ||
738 | attribute_macro: m | ||
739 | function_like_macro: m | ||
740 | "#]], | ||
741 | ); | ||
742 | } | ||
diff --git a/crates/hir_def/src/nameres/tests/mod_resolution.rs b/crates/hir_def/src/nameres/tests/mod_resolution.rs index 1f619787e..f93337a6e 100644 --- a/crates/hir_def/src/nameres/tests/mod_resolution.rs +++ b/crates/hir_def/src/nameres/tests/mod_resolution.rs | |||
@@ -672,42 +672,6 @@ pub struct Baz; | |||
672 | } | 672 | } |
673 | 673 | ||
674 | #[test] | 674 | #[test] |
675 | fn unresolved_module_diagnostics() { | ||
676 | let db = TestDB::with_files( | ||
677 | r" | ||
678 | //- /lib.rs | ||
679 | mod foo; | ||
680 | mod bar; | ||
681 | mod baz {} | ||
682 | //- /foo.rs | ||
683 | ", | ||
684 | ); | ||
685 | let krate = db.test_crate(); | ||
686 | |||
687 | let crate_def_map = db.crate_def_map(krate); | ||
688 | |||
689 | expect![[r#" | ||
690 | [ | ||
691 | UnresolvedModule { | ||
692 | module: Idx::<ModuleData>(0), | ||
693 | declaration: InFile { | ||
694 | file_id: HirFileId( | ||
695 | FileId( | ||
696 | FileId( | ||
697 | 0, | ||
698 | ), | ||
699 | ), | ||
700 | ), | ||
701 | value: FileAstId::<syntax::ast::generated::nodes::Module>(1), | ||
702 | }, | ||
703 | candidate: "bar.rs", | ||
704 | }, | ||
705 | ] | ||
706 | "#]] | ||
707 | .assert_debug_eq(&crate_def_map.diagnostics); | ||
708 | } | ||
709 | |||
710 | #[test] | ||
711 | fn module_resolution_decl_inside_module_in_non_crate_root_2() { | 675 | fn module_resolution_decl_inside_module_in_non_crate_root_2() { |
712 | check( | 676 | check( |
713 | r#" | 677 | r#" |
diff --git a/crates/hir_def/src/path.rs b/crates/hir_def/src/path.rs index 40ec38f56..209b18e78 100644 --- a/crates/hir_def/src/path.rs +++ b/crates/hir_def/src/path.rs | |||
@@ -56,10 +56,6 @@ impl ModPath { | |||
56 | ModPath { kind, segments } | 56 | ModPath { kind, segments } |
57 | } | 57 | } |
58 | 58 | ||
59 | pub(crate) fn from_name_ref(name_ref: &ast::NameRef) -> ModPath { | ||
60 | name_ref.as_name().into() | ||
61 | } | ||
62 | |||
63 | /// Converts an `tt::Ident` into a single-identifier `Path`. | 59 | /// Converts an `tt::Ident` into a single-identifier `Path`. |
64 | pub(crate) fn from_tt_ident(ident: &tt::Ident) -> ModPath { | 60 | pub(crate) fn from_tt_ident(ident: &tt::Ident) -> ModPath { |
65 | ident.as_name().into() | 61 | ident.as_name().into() |
diff --git a/crates/hir_def/src/test_db.rs b/crates/hir_def/src/test_db.rs index 42a762936..fb1d3c974 100644 --- a/crates/hir_def/src/test_db.rs +++ b/crates/hir_def/src/test_db.rs | |||
@@ -5,9 +5,15 @@ use std::{ | |||
5 | sync::{Arc, Mutex}, | 5 | sync::{Arc, Mutex}, |
6 | }; | 6 | }; |
7 | 7 | ||
8 | use base_db::SourceDatabase; | ||
8 | use base_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, Upcast}; | 9 | use base_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, Upcast}; |
9 | use hir_expand::db::AstDatabase; | 10 | use hir_expand::db::AstDatabase; |
11 | use hir_expand::diagnostics::Diagnostic; | ||
12 | use hir_expand::diagnostics::DiagnosticSinkBuilder; | ||
13 | use rustc_hash::FxHashMap; | ||
10 | use rustc_hash::FxHashSet; | 14 | use rustc_hash::FxHashSet; |
15 | use syntax::TextRange; | ||
16 | use test_utils::extract_annotations; | ||
11 | 17 | ||
12 | use crate::db::DefDatabase; | 18 | use crate::db::DefDatabase; |
13 | 19 | ||
@@ -98,4 +104,40 @@ impl TestDB { | |||
98 | }) | 104 | }) |
99 | .collect() | 105 | .collect() |
100 | } | 106 | } |
107 | |||
108 | pub fn extract_annotations(&self) -> FxHashMap<FileId, Vec<(TextRange, String)>> { | ||
109 | let mut files = Vec::new(); | ||
110 | let crate_graph = self.crate_graph(); | ||
111 | for krate in crate_graph.iter() { | ||
112 | let crate_def_map = self.crate_def_map(krate); | ||
113 | for (module_id, _) in crate_def_map.modules.iter() { | ||
114 | let file_id = crate_def_map[module_id].origin.file_id(); | ||
115 | files.extend(file_id) | ||
116 | } | ||
117 | } | ||
118 | assert!(!files.is_empty()); | ||
119 | files | ||
120 | .into_iter() | ||
121 | .filter_map(|file_id| { | ||
122 | let text = self.file_text(file_id); | ||
123 | let annotations = extract_annotations(&text); | ||
124 | if annotations.is_empty() { | ||
125 | return None; | ||
126 | } | ||
127 | Some((file_id, annotations)) | ||
128 | }) | ||
129 | .collect() | ||
130 | } | ||
131 | |||
132 | pub fn diagnostics<F: FnMut(&dyn Diagnostic)>(&self, mut cb: F) { | ||
133 | let crate_graph = self.crate_graph(); | ||
134 | for krate in crate_graph.iter() { | ||
135 | let crate_def_map = self.crate_def_map(krate); | ||
136 | |||
137 | let mut sink = DiagnosticSinkBuilder::new().build(&mut cb); | ||
138 | for (module_id, _) in crate_def_map.modules.iter() { | ||
139 | crate_def_map.add_diagnostics(self, module_id, &mut sink); | ||
140 | } | ||
141 | } | ||
142 | } | ||
101 | } | 143 | } |
diff --git a/crates/hir_expand/src/db.rs b/crates/hir_expand/src/db.rs index 710694a34..b591130ca 100644 --- a/crates/hir_expand/src/db.rs +++ b/crates/hir_expand/src/db.rs | |||
@@ -143,7 +143,7 @@ pub(crate) fn macro_def( | |||
143 | Some(Arc::new((TokenExpander::BuiltinDerive(expander), mbe::TokenMap::default()))) | 143 | Some(Arc::new((TokenExpander::BuiltinDerive(expander), mbe::TokenMap::default()))) |
144 | } | 144 | } |
145 | MacroDefKind::BuiltInEager(_) => None, | 145 | MacroDefKind::BuiltInEager(_) => None, |
146 | MacroDefKind::CustomDerive(expander) => { | 146 | MacroDefKind::ProcMacro(expander) => { |
147 | Some(Arc::new((TokenExpander::ProcMacro(expander), mbe::TokenMap::default()))) | 147 | Some(Arc::new((TokenExpander::ProcMacro(expander), mbe::TokenMap::default()))) |
148 | } | 148 | } |
149 | } | 149 | } |
@@ -249,7 +249,7 @@ pub(crate) fn expand_proc_macro( | |||
249 | }; | 249 | }; |
250 | 250 | ||
251 | let expander = match loc.def.kind { | 251 | let expander = match loc.def.kind { |
252 | MacroDefKind::CustomDerive(expander) => expander, | 252 | MacroDefKind::ProcMacro(expander) => expander, |
253 | _ => unreachable!(), | 253 | _ => unreachable!(), |
254 | }; | 254 | }; |
255 | 255 | ||
diff --git a/crates/hir_expand/src/eager.rs b/crates/hir_expand/src/eager.rs index 10c45646f..2f37d7189 100644 --- a/crates/hir_expand/src/eager.rs +++ b/crates/hir_expand/src/eager.rs | |||
@@ -129,7 +129,7 @@ fn eager_macro_recur( | |||
129 | MacroDefKind::Declarative | 129 | MacroDefKind::Declarative |
130 | | MacroDefKind::BuiltIn(_) | 130 | | MacroDefKind::BuiltIn(_) |
131 | | MacroDefKind::BuiltInDerive(_) | 131 | | MacroDefKind::BuiltInDerive(_) |
132 | | MacroDefKind::CustomDerive(_) => { | 132 | | MacroDefKind::ProcMacro(_) => { |
133 | let expanded = lazy_expand(db, &def, curr.with_value(child.clone()), krate)?; | 133 | let expanded = lazy_expand(db, &def, curr.with_value(child.clone()), krate)?; |
134 | // replace macro inside | 134 | // replace macro inside |
135 | eager_macro_recur(db, expanded, krate, macro_resolver)? | 135 | eager_macro_recur(db, expanded, krate, macro_resolver)? |
diff --git a/crates/hir_expand/src/hygiene.rs b/crates/hir_expand/src/hygiene.rs index 845e9cbc1..d383b968d 100644 --- a/crates/hir_expand/src/hygiene.rs +++ b/crates/hir_expand/src/hygiene.rs | |||
@@ -33,7 +33,7 @@ impl Hygiene { | |||
33 | MacroDefKind::BuiltIn(_) => (None, false), | 33 | MacroDefKind::BuiltIn(_) => (None, false), |
34 | MacroDefKind::BuiltInDerive(_) => (None, false), | 34 | MacroDefKind::BuiltInDerive(_) => (None, false), |
35 | MacroDefKind::BuiltInEager(_) => (None, false), | 35 | MacroDefKind::BuiltInEager(_) => (None, false), |
36 | MacroDefKind::CustomDerive(_) => (None, false), | 36 | MacroDefKind::ProcMacro(_) => (None, false), |
37 | } | 37 | } |
38 | } | 38 | } |
39 | MacroCallId::EagerMacro(_id) => (None, false), | 39 | MacroCallId::EagerMacro(_id) => (None, false), |
diff --git a/crates/hir_expand/src/lib.rs b/crates/hir_expand/src/lib.rs index 2be15e841..17f1178ed 100644 --- a/crates/hir_expand/src/lib.rs +++ b/crates/hir_expand/src/lib.rs | |||
@@ -246,7 +246,7 @@ pub enum MacroDefKind { | |||
246 | // FIXME: maybe just Builtin and rename BuiltinFnLikeExpander to BuiltinExpander | 246 | // FIXME: maybe just Builtin and rename BuiltinFnLikeExpander to BuiltinExpander |
247 | BuiltInDerive(BuiltinDeriveExpander), | 247 | BuiltInDerive(BuiltinDeriveExpander), |
248 | BuiltInEager(EagerExpander), | 248 | BuiltInEager(EagerExpander), |
249 | CustomDerive(ProcMacroExpander), | 249 | ProcMacro(ProcMacroExpander), |
250 | } | 250 | } |
251 | 251 | ||
252 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | 252 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
diff --git a/crates/hir_expand/src/name.rs b/crates/hir_expand/src/name.rs index 49841c7a1..a5750d829 100644 --- a/crates/hir_expand/src/name.rs +++ b/crates/hir_expand/src/name.rs | |||
@@ -43,8 +43,8 @@ impl Name { | |||
43 | } | 43 | } |
44 | 44 | ||
45 | /// Shortcut to create inline plain text name | 45 | /// Shortcut to create inline plain text name |
46 | const fn new_inline_ascii(text: &[u8]) -> Name { | 46 | const fn new_inline(text: &str) -> Name { |
47 | Name::new_text(SmolStr::new_inline_from_ascii(text.len(), text)) | 47 | Name::new_text(SmolStr::new_inline(text)) |
48 | } | 48 | } |
49 | 49 | ||
50 | /// Resolve a name from the text of token. | 50 | /// Resolve a name from the text of token. |
@@ -127,7 +127,7 @@ pub mod known { | |||
127 | $( | 127 | $( |
128 | #[allow(bad_style)] | 128 | #[allow(bad_style)] |
129 | pub const $ident: super::Name = | 129 | pub const $ident: super::Name = |
130 | super::Name::new_inline_ascii(stringify!($ident).as_bytes()); | 130 | super::Name::new_inline(stringify!($ident)); |
131 | )* | 131 | )* |
132 | }; | 132 | }; |
133 | } | 133 | } |
@@ -210,8 +210,8 @@ pub mod known { | |||
210 | ); | 210 | ); |
211 | 211 | ||
212 | // self/Self cannot be used as an identifier | 212 | // self/Self cannot be used as an identifier |
213 | pub const SELF_PARAM: super::Name = super::Name::new_inline_ascii(b"self"); | 213 | pub const SELF_PARAM: super::Name = super::Name::new_inline("self"); |
214 | pub const SELF_TYPE: super::Name = super::Name::new_inline_ascii(b"Self"); | 214 | pub const SELF_TYPE: super::Name = super::Name::new_inline("Self"); |
215 | 215 | ||
216 | #[macro_export] | 216 | #[macro_export] |
217 | macro_rules! name { | 217 | macro_rules! name { |
diff --git a/crates/hir_expand/src/proc_macro.rs b/crates/hir_expand/src/proc_macro.rs index 80255ea32..7505cb061 100644 --- a/crates/hir_expand/src/proc_macro.rs +++ b/crates/hir_expand/src/proc_macro.rs | |||
@@ -7,7 +7,7 @@ use tt::buffer::{Cursor, TokenBuffer}; | |||
7 | #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] | 7 | #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] |
8 | pub struct ProcMacroExpander { | 8 | pub struct ProcMacroExpander { |
9 | krate: CrateId, | 9 | krate: CrateId, |
10 | proc_macro_id: ProcMacroId, | 10 | proc_macro_id: Option<ProcMacroId>, |
11 | } | 11 | } |
12 | 12 | ||
13 | macro_rules! err { | 13 | macro_rules! err { |
@@ -20,8 +20,14 @@ macro_rules! err { | |||
20 | } | 20 | } |
21 | 21 | ||
22 | impl ProcMacroExpander { | 22 | impl ProcMacroExpander { |
23 | pub fn new(krate: CrateId, proc_macro_id: ProcMacroId) -> ProcMacroExpander { | 23 | pub fn new(krate: CrateId, proc_macro_id: ProcMacroId) -> Self { |
24 | ProcMacroExpander { krate, proc_macro_id } | 24 | Self { krate, proc_macro_id: Some(proc_macro_id) } |
25 | } | ||
26 | |||
27 | pub fn dummy(krate: CrateId) -> Self { | ||
28 | // FIXME: Should store the name for better errors | ||
29 | // FIXME: I think this is the second layer of "dummy" expansion, we should reduce that | ||
30 | Self { krate, proc_macro_id: None } | ||
25 | } | 31 | } |
26 | 32 | ||
27 | pub fn expand( | 33 | pub fn expand( |
@@ -30,17 +36,22 @@ impl ProcMacroExpander { | |||
30 | _id: LazyMacroId, | 36 | _id: LazyMacroId, |
31 | tt: &tt::Subtree, | 37 | tt: &tt::Subtree, |
32 | ) -> Result<tt::Subtree, mbe::ExpandError> { | 38 | ) -> Result<tt::Subtree, mbe::ExpandError> { |
33 | let krate_graph = db.crate_graph(); | 39 | match self.proc_macro_id { |
34 | let proc_macro = krate_graph[self.krate] | 40 | Some(id) => { |
35 | .proc_macro | 41 | let krate_graph = db.crate_graph(); |
36 | .get(self.proc_macro_id.0 as usize) | 42 | let proc_macro = krate_graph[self.krate] |
37 | .clone() | 43 | .proc_macro |
38 | .ok_or_else(|| err!("No derive macro found."))?; | 44 | .get(id.0 as usize) |
39 | 45 | .clone() | |
40 | let tt = remove_derive_attrs(tt) | 46 | .ok_or_else(|| err!("No derive macro found."))?; |
41 | .ok_or_else(|| err!("Fail to remove derive for custom derive"))?; | 47 | |
42 | 48 | let tt = remove_derive_attrs(tt) | |
43 | proc_macro.expander.expand(&tt, None).map_err(mbe::ExpandError::from) | 49 | .ok_or_else(|| err!("Fail to remove derive for custom derive"))?; |
50 | |||
51 | proc_macro.expander.expand(&tt, None).map_err(mbe::ExpandError::from) | ||
52 | } | ||
53 | None => Err(err!("Unresolved proc macro")), | ||
54 | } | ||
44 | } | 55 | } |
45 | } | 56 | } |
46 | 57 | ||
diff --git a/crates/hir_ty/Cargo.toml b/crates/hir_ty/Cargo.toml index bc86df2b1..ed1c911c2 100644 --- a/crates/hir_ty/Cargo.toml +++ b/crates/hir_ty/Cargo.toml | |||
@@ -17,9 +17,9 @@ ena = "0.14.0" | |||
17 | log = "0.4.8" | 17 | log = "0.4.8" |
18 | rustc-hash = "1.1.0" | 18 | rustc-hash = "1.1.0" |
19 | scoped-tls = "1" | 19 | scoped-tls = "1" |
20 | chalk-solve = { version = "0.27.0" } | 20 | chalk-solve = { version = "0.30.0" } |
21 | chalk-ir = { version = "0.27.0" } | 21 | chalk-ir = { version = "0.30.0" } |
22 | chalk-recursive = { version = "0.27.0" } | 22 | chalk-recursive = { version = "0.30.0" } |
23 | 23 | ||
24 | stdx = { path = "../stdx", version = "0.0.0" } | 24 | stdx = { path = "../stdx", version = "0.0.0" } |
25 | hir_def = { path = "../hir_def", version = "0.0.0" } | 25 | hir_def = { path = "../hir_def", version = "0.0.0" } |
diff --git a/crates/hir_ty/src/infer.rs b/crates/hir_ty/src/infer.rs index 2b53b8297..9a7785c76 100644 --- a/crates/hir_ty/src/infer.rs +++ b/crates/hir_ty/src/infer.rs | |||
@@ -555,7 +555,7 @@ impl<'a> InferenceContext<'a> { | |||
555 | 555 | ||
556 | fn resolve_lang_item(&self, name: &str) -> Option<LangItemTarget> { | 556 | fn resolve_lang_item(&self, name: &str) -> Option<LangItemTarget> { |
557 | let krate = self.resolver.krate()?; | 557 | let krate = self.resolver.krate()?; |
558 | let name = SmolStr::new_inline_from_ascii(name.len(), name.as_bytes()); | 558 | let name = SmolStr::new_inline(name); |
559 | self.db.lang_item(krate, name) | 559 | self.db.lang_item(krate, name) |
560 | } | 560 | } |
561 | 561 | ||
diff --git a/crates/hir_ty/src/traits/chalk.rs b/crates/hir_ty/src/traits/chalk.rs index 27f0ed628..009b17a7f 100644 --- a/crates/hir_ty/src/traits/chalk.rs +++ b/crates/hir_ty/src/traits/chalk.rs | |||
@@ -129,8 +129,12 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> { | |||
129 | debug!("impls_for_trait returned {} impls", result.len()); | 129 | debug!("impls_for_trait returned {} impls", result.len()); |
130 | result | 130 | result |
131 | } | 131 | } |
132 | fn impl_provided_for(&self, auto_trait_id: TraitId, struct_id: AdtId) -> bool { | 132 | fn impl_provided_for( |
133 | debug!("impl_provided_for {:?}, {:?}", auto_trait_id, struct_id); | 133 | &self, |
134 | auto_trait_id: TraitId, | ||
135 | application_ty: &chalk_ir::ApplicationTy<Interner>, | ||
136 | ) -> bool { | ||
137 | debug!("impl_provided_for {:?}, {:?}", auto_trait_id, application_ty); | ||
134 | false // FIXME | 138 | false // FIXME |
135 | } | 139 | } |
136 | fn associated_ty_value(&self, id: AssociatedTyValueId) -> Arc<AssociatedTyValue> { | 140 | fn associated_ty_value(&self, id: AssociatedTyValueId) -> Arc<AssociatedTyValue> { |
@@ -422,6 +426,7 @@ fn well_known_trait_from_lang_attr(name: &str) -> Option<WellKnownTrait> { | |||
422 | "fn_mut" => WellKnownTrait::FnMut, | 426 | "fn_mut" => WellKnownTrait::FnMut, |
423 | "fn" => WellKnownTrait::Fn, | 427 | "fn" => WellKnownTrait::Fn, |
424 | "unsize" => WellKnownTrait::Unsize, | 428 | "unsize" => WellKnownTrait::Unsize, |
429 | "coerce_unsized" => WellKnownTrait::CoerceUnsized, | ||
425 | _ => return None, | 430 | _ => return None, |
426 | }) | 431 | }) |
427 | } | 432 | } |
@@ -437,6 +442,7 @@ fn lang_attr_from_well_known_trait(attr: WellKnownTrait) -> &'static str { | |||
437 | WellKnownTrait::Fn => "fn", | 442 | WellKnownTrait::Fn => "fn", |
438 | WellKnownTrait::Unsize => "unsize", | 443 | WellKnownTrait::Unsize => "unsize", |
439 | WellKnownTrait::Unpin => "unpin", | 444 | WellKnownTrait::Unpin => "unpin", |
445 | WellKnownTrait::CoerceUnsized => "coerce_unsized", | ||
440 | } | 446 | } |
441 | } | 447 | } |
442 | 448 | ||
diff --git a/crates/ide/src/completion/complete_postfix.rs b/crates/ide/src/completion/complete_postfix.rs index 84c4e129d..26a5af5b9 100644 --- a/crates/ide/src/completion/complete_postfix.rs +++ b/crates/ide/src/completion/complete_postfix.rs | |||
@@ -175,6 +175,9 @@ pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) { | |||
175 | ) | 175 | ) |
176 | .add_to(acc); | 176 | .add_to(acc); |
177 | 177 | ||
178 | postfix_snippet(ctx, cap, &dot_receiver, "ok", "Ok(expr)", &format!("Ok({})", receiver_text)) | ||
179 | .add_to(acc); | ||
180 | |||
178 | postfix_snippet( | 181 | postfix_snippet( |
179 | ctx, | 182 | ctx, |
180 | cap, | 183 | cap, |
@@ -189,6 +192,16 @@ pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) { | |||
189 | ctx, | 192 | ctx, |
190 | cap, | 193 | cap, |
191 | &dot_receiver, | 194 | &dot_receiver, |
195 | "dbgr", | ||
196 | "dbg!(&expr)", | ||
197 | &format!("dbg!(&{})", receiver_text), | ||
198 | ) | ||
199 | .add_to(acc); | ||
200 | |||
201 | postfix_snippet( | ||
202 | ctx, | ||
203 | cap, | ||
204 | &dot_receiver, | ||
192 | "call", | 205 | "call", |
193 | "function(expr)", | 206 | "function(expr)", |
194 | &format!("${{1}}({})", receiver_text), | 207 | &format!("${{1}}({})", receiver_text), |
@@ -263,9 +276,11 @@ fn main() { | |||
263 | sn box Box::new(expr) | 276 | sn box Box::new(expr) |
264 | sn call function(expr) | 277 | sn call function(expr) |
265 | sn dbg dbg!(expr) | 278 | sn dbg dbg!(expr) |
279 | sn dbgr dbg!(&expr) | ||
266 | sn if if expr {} | 280 | sn if if expr {} |
267 | sn match match expr {} | 281 | sn match match expr {} |
268 | sn not !expr | 282 | sn not !expr |
283 | sn ok Ok(expr) | ||
269 | sn ref &expr | 284 | sn ref &expr |
270 | sn refm &mut expr | 285 | sn refm &mut expr |
271 | sn while while expr {} | 286 | sn while while expr {} |
@@ -286,7 +301,9 @@ fn main() { | |||
286 | sn box Box::new(expr) | 301 | sn box Box::new(expr) |
287 | sn call function(expr) | 302 | sn call function(expr) |
288 | sn dbg dbg!(expr) | 303 | sn dbg dbg!(expr) |
304 | sn dbgr dbg!(&expr) | ||
289 | sn match match expr {} | 305 | sn match match expr {} |
306 | sn ok Ok(expr) | ||
290 | sn ref &expr | 307 | sn ref &expr |
291 | sn refm &mut expr | 308 | sn refm &mut expr |
292 | "#]], | 309 | "#]], |
diff --git a/crates/ide/src/completion/completion_context.rs b/crates/ide/src/completion/completion_context.rs index 161f59c1e..671b13328 100644 --- a/crates/ide/src/completion/completion_context.rs +++ b/crates/ide/src/completion/completion_context.rs | |||
@@ -1,7 +1,7 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | 2 | ||
3 | use base_db::SourceDatabase; | 3 | use base_db::SourceDatabase; |
4 | use hir::{Semantics, SemanticsScope, Type}; | 4 | use hir::{Local, ScopeDef, Semantics, SemanticsScope, Type}; |
5 | use ide_db::RootDatabase; | 5 | use ide_db::RootDatabase; |
6 | use syntax::{ | 6 | use syntax::{ |
7 | algo::{find_covering_element, find_node_at_offset}, | 7 | algo::{find_covering_element, find_node_at_offset}, |
@@ -91,6 +91,7 @@ pub(crate) struct CompletionContext<'a> { | |||
91 | pub(super) impl_as_prev_sibling: bool, | 91 | pub(super) impl_as_prev_sibling: bool, |
92 | pub(super) is_match_arm: bool, | 92 | pub(super) is_match_arm: bool, |
93 | pub(super) has_item_list_or_source_file_parent: bool, | 93 | pub(super) has_item_list_or_source_file_parent: bool, |
94 | pub(super) locals: Vec<(String, Local)>, | ||
94 | } | 95 | } |
95 | 96 | ||
96 | impl<'a> CompletionContext<'a> { | 97 | impl<'a> CompletionContext<'a> { |
@@ -119,6 +120,12 @@ impl<'a> CompletionContext<'a> { | |||
119 | original_file.syntax().token_at_offset(position.offset).left_biased()?; | 120 | original_file.syntax().token_at_offset(position.offset).left_biased()?; |
120 | let token = sema.descend_into_macros(original_token.clone()); | 121 | let token = sema.descend_into_macros(original_token.clone()); |
121 | let scope = sema.scope_at_offset(&token.parent(), position.offset); | 122 | let scope = sema.scope_at_offset(&token.parent(), position.offset); |
123 | let mut locals = vec![]; | ||
124 | scope.process_all_names(&mut |name, scope| { | ||
125 | if let ScopeDef::Local(local) = scope { | ||
126 | locals.push((name.to_string(), local)); | ||
127 | } | ||
128 | }); | ||
122 | let mut ctx = CompletionContext { | 129 | let mut ctx = CompletionContext { |
123 | sema, | 130 | sema, |
124 | scope, | 131 | scope, |
@@ -167,6 +174,7 @@ impl<'a> CompletionContext<'a> { | |||
167 | if_is_prev: false, | 174 | if_is_prev: false, |
168 | is_match_arm: false, | 175 | is_match_arm: false, |
169 | has_item_list_or_source_file_parent: false, | 176 | has_item_list_or_source_file_parent: false, |
177 | locals, | ||
170 | }; | 178 | }; |
171 | 179 | ||
172 | let mut original_file = original_file.syntax().clone(); | 180 | let mut original_file = original_file.syntax().clone(); |
diff --git a/crates/ide/src/completion/presentation.rs b/crates/ide/src/completion/presentation.rs index 24c507f9b..987cbfa7a 100644 --- a/crates/ide/src/completion/presentation.rs +++ b/crates/ide/src/completion/presentation.rs | |||
@@ -191,6 +191,17 @@ impl Completions { | |||
191 | func: hir::Function, | 191 | func: hir::Function, |
192 | local_name: Option<String>, | 192 | local_name: Option<String>, |
193 | ) { | 193 | ) { |
194 | fn add_arg(arg: &str, ty: &Type, ctx: &CompletionContext) -> String { | ||
195 | if let Some(derefed_ty) = ty.remove_ref() { | ||
196 | for (name, local) in ctx.locals.iter() { | ||
197 | if name == arg && local.ty(ctx.db) == derefed_ty { | ||
198 | return (if ty.is_mutable_reference() { "&mut " } else { "&" }).to_string() | ||
199 | + &arg.to_string(); | ||
200 | } | ||
201 | } | ||
202 | } | ||
203 | arg.to_string() | ||
204 | }; | ||
194 | let name = local_name.unwrap_or_else(|| func.name(ctx.db).to_string()); | 205 | let name = local_name.unwrap_or_else(|| func.name(ctx.db).to_string()); |
195 | let ast_node = func.source(ctx.db).value; | 206 | let ast_node = func.source(ctx.db).value; |
196 | 207 | ||
@@ -205,12 +216,20 @@ impl Completions { | |||
205 | .set_deprecated(is_deprecated(func, ctx.db)) | 216 | .set_deprecated(is_deprecated(func, ctx.db)) |
206 | .detail(function_declaration(&ast_node)); | 217 | .detail(function_declaration(&ast_node)); |
207 | 218 | ||
219 | let params_ty = func.params(ctx.db); | ||
208 | let params = ast_node | 220 | let params = ast_node |
209 | .param_list() | 221 | .param_list() |
210 | .into_iter() | 222 | .into_iter() |
211 | .flat_map(|it| it.params()) | 223 | .flat_map(|it| it.params()) |
212 | .flat_map(|it| it.pat()) | 224 | .zip(params_ty) |
213 | .map(|pat| pat.to_string().trim_start_matches('_').into()) | 225 | .flat_map(|(it, param_ty)| { |
226 | if let Some(pat) = it.pat() { | ||
227 | let name = pat.to_string(); | ||
228 | let arg = name.trim_start_matches("mut ").trim_start_matches('_'); | ||
229 | return Some(add_arg(arg, param_ty.ty(), ctx)); | ||
230 | } | ||
231 | None | ||
232 | }) | ||
214 | .collect(); | 233 | .collect(); |
215 | 234 | ||
216 | builder = builder.add_call_parens(ctx, name, Params::Named(params)); | 235 | builder = builder.add_call_parens(ctx, name, Params::Named(params)); |
@@ -864,6 +883,106 @@ fn main() { foo(${1:foo}, ${2:bar}, ${3:ho_ge_})$0 } | |||
864 | } | 883 | } |
865 | 884 | ||
866 | #[test] | 885 | #[test] |
886 | fn insert_ref_when_matching_local_in_scope() { | ||
887 | check_edit( | ||
888 | "ref_arg", | ||
889 | r#" | ||
890 | struct Foo {} | ||
891 | fn ref_arg(x: &Foo) {} | ||
892 | fn main() { | ||
893 | let x = Foo {}; | ||
894 | ref_ar<|> | ||
895 | } | ||
896 | "#, | ||
897 | r#" | ||
898 | struct Foo {} | ||
899 | fn ref_arg(x: &Foo) {} | ||
900 | fn main() { | ||
901 | let x = Foo {}; | ||
902 | ref_arg(${1:&x})$0 | ||
903 | } | ||
904 | "#, | ||
905 | ); | ||
906 | } | ||
907 | |||
908 | #[test] | ||
909 | fn insert_mut_ref_when_matching_local_in_scope() { | ||
910 | check_edit( | ||
911 | "ref_arg", | ||
912 | r#" | ||
913 | struct Foo {} | ||
914 | fn ref_arg(x: &mut Foo) {} | ||
915 | fn main() { | ||
916 | let x = Foo {}; | ||
917 | ref_ar<|> | ||
918 | } | ||
919 | "#, | ||
920 | r#" | ||
921 | struct Foo {} | ||
922 | fn ref_arg(x: &mut Foo) {} | ||
923 | fn main() { | ||
924 | let x = Foo {}; | ||
925 | ref_arg(${1:&mut x})$0 | ||
926 | } | ||
927 | "#, | ||
928 | ); | ||
929 | } | ||
930 | |||
931 | #[test] | ||
932 | fn insert_ref_when_matching_local_in_scope_for_method() { | ||
933 | check_edit( | ||
934 | "apply_foo", | ||
935 | r#" | ||
936 | struct Foo {} | ||
937 | struct Bar {} | ||
938 | impl Bar { | ||
939 | fn apply_foo(&self, x: &Foo) {} | ||
940 | } | ||
941 | |||
942 | fn main() { | ||
943 | let x = Foo {}; | ||
944 | let y = Bar {}; | ||
945 | y.<|> | ||
946 | } | ||
947 | "#, | ||
948 | r#" | ||
949 | struct Foo {} | ||
950 | struct Bar {} | ||
951 | impl Bar { | ||
952 | fn apply_foo(&self, x: &Foo) {} | ||
953 | } | ||
954 | |||
955 | fn main() { | ||
956 | let x = Foo {}; | ||
957 | let y = Bar {}; | ||
958 | y.apply_foo(${1:&x})$0 | ||
959 | } | ||
960 | "#, | ||
961 | ); | ||
962 | } | ||
963 | |||
964 | #[test] | ||
965 | fn trim_mut_keyword_in_func_completion() { | ||
966 | check_edit( | ||
967 | "take_mutably", | ||
968 | r#" | ||
969 | fn take_mutably(mut x: &i32) {} | ||
970 | |||
971 | fn main() { | ||
972 | take_m<|> | ||
973 | } | ||
974 | "#, | ||
975 | r#" | ||
976 | fn take_mutably(mut x: &i32) {} | ||
977 | |||
978 | fn main() { | ||
979 | take_mutably(${1:x})$0 | ||
980 | } | ||
981 | "#, | ||
982 | ); | ||
983 | } | ||
984 | |||
985 | #[test] | ||
867 | fn inserts_parens_for_tuple_enums() { | 986 | fn inserts_parens_for_tuple_enums() { |
868 | mark::check!(inserts_parens_for_tuple_enums); | 987 | mark::check!(inserts_parens_for_tuple_enums); |
869 | check_edit( | 988 | check_edit( |
diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs index b2b972b02..dc815a483 100644 --- a/crates/ide/src/diagnostics.rs +++ b/crates/ide/src/diagnostics.rs | |||
@@ -622,13 +622,65 @@ pub struct Foo { pub a: i32, pub b: i32 } | |||
622 | r#" | 622 | r#" |
623 | use a; | 623 | use a; |
624 | use a::{c, d::e}; | 624 | use a::{c, d::e}; |
625 | |||
626 | mod a { | ||
627 | mod c {} | ||
628 | mod d { | ||
629 | mod e {} | ||
630 | } | ||
631 | } | ||
625 | "#, | 632 | "#, |
626 | ); | 633 | ); |
627 | check_fix(r#"use {<|>b};"#, r#"use b;"#); | 634 | check_fix( |
628 | check_fix(r#"use {b<|>};"#, r#"use b;"#); | 635 | r" |
629 | check_fix(r#"use a::{c<|>};"#, r#"use a::c;"#); | 636 | mod b {} |
630 | check_fix(r#"use a::{self<|>};"#, r#"use a;"#); | 637 | use {<|>b}; |
631 | check_fix(r#"use a::{c, d::{e<|>}};"#, r#"use a::{c, d::e};"#); | 638 | ", |
639 | r" | ||
640 | mod b {} | ||
641 | use b; | ||
642 | ", | ||
643 | ); | ||
644 | check_fix( | ||
645 | r" | ||
646 | mod b {} | ||
647 | use {b<|>}; | ||
648 | ", | ||
649 | r" | ||
650 | mod b {} | ||
651 | use b; | ||
652 | ", | ||
653 | ); | ||
654 | check_fix( | ||
655 | r" | ||
656 | mod a { mod c {} } | ||
657 | use a::{c<|>}; | ||
658 | ", | ||
659 | r" | ||
660 | mod a { mod c {} } | ||
661 | use a::c; | ||
662 | ", | ||
663 | ); | ||
664 | check_fix( | ||
665 | r" | ||
666 | mod a {} | ||
667 | use a::{self<|>}; | ||
668 | ", | ||
669 | r" | ||
670 | mod a {} | ||
671 | use a; | ||
672 | ", | ||
673 | ); | ||
674 | check_fix( | ||
675 | r" | ||
676 | mod a { mod c {} mod d { mod e {} } } | ||
677 | use a::{c, d::{e<|>}}; | ||
678 | ", | ||
679 | r" | ||
680 | mod a { mod c {} mod d { mod e {} } } | ||
681 | use a::{c, d::e}; | ||
682 | ", | ||
683 | ); | ||
632 | } | 684 | } |
633 | 685 | ||
634 | #[test] | 686 | #[test] |
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlighting.html b/crates/ide/src/syntax_highlighting/test_data/highlighting.html index cde42024c..1d8a3c404 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlighting.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlighting.html | |||
@@ -44,7 +44,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
44 | <span class="keyword">pub</span> <span class="keyword">trait</span> <span class="trait declaration">Copy</span> <span class="punctuation">{</span><span class="punctuation">}</span> | 44 | <span class="keyword">pub</span> <span class="keyword">trait</span> <span class="trait declaration">Copy</span> <span class="punctuation">{</span><span class="punctuation">}</span> |
45 | <span class="punctuation">}</span> | 45 | <span class="punctuation">}</span> |
46 | 46 | ||
47 | <span class="attribute">#</span><span class="attribute">[</span><span class="function attribute">derive</span><span class="punctuation">(</span><span class="attribute">Clone</span><span class="punctuation">,</span><span class="attribute"> Debug</span><span class="punctuation">)</span><span class="attribute">]</span> | 47 | |
48 | <span class="keyword">struct</span> <span class="struct declaration">Foo</span> <span class="punctuation">{</span> | 48 | <span class="keyword">struct</span> <span class="struct declaration">Foo</span> <span class="punctuation">{</span> |
49 | <span class="keyword">pub</span> <span class="field declaration">x</span><span class="punctuation">:</span> <span class="builtin_type">i32</span><span class="punctuation">,</span> | 49 | <span class="keyword">pub</span> <span class="field declaration">x</span><span class="punctuation">:</span> <span class="builtin_type">i32</span><span class="punctuation">,</span> |
50 | <span class="keyword">pub</span> <span class="field declaration">y</span><span class="punctuation">:</span> <span class="builtin_type">i32</span><span class="punctuation">,</span> | 50 | <span class="keyword">pub</span> <span class="field declaration">y</span><span class="punctuation">:</span> <span class="builtin_type">i32</span><span class="punctuation">,</span> |
@@ -74,7 +74,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
74 | <span class="punctuation">}</span> | 74 | <span class="punctuation">}</span> |
75 | <span class="punctuation">}</span> | 75 | <span class="punctuation">}</span> |
76 | 76 | ||
77 | <span class="attribute">#</span><span class="attribute">[</span><span class="function attribute">derive</span><span class="punctuation">(</span><span class="attribute">Copy</span><span class="punctuation">,</span><span class="attribute"> Clone</span><span class="punctuation">)</span><span class="attribute">]</span> | 77 | <span class="attribute">#</span><span class="attribute">[</span><span class="function attribute">derive</span><span class="punctuation">(</span><span class="attribute">Copy</span><span class="punctuation">)</span><span class="attribute">]</span> |
78 | <span class="keyword">struct</span> <span class="struct declaration">FooCopy</span> <span class="punctuation">{</span> | 78 | <span class="keyword">struct</span> <span class="struct declaration">FooCopy</span> <span class="punctuation">{</span> |
79 | <span class="field declaration">x</span><span class="punctuation">:</span> <span class="builtin_type">u32</span><span class="punctuation">,</span> | 79 | <span class="field declaration">x</span><span class="punctuation">:</span> <span class="builtin_type">u32</span><span class="punctuation">,</span> |
80 | <span class="punctuation">}</span> | 80 | <span class="punctuation">}</span> |
@@ -144,7 +144,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
144 | <span class="variable">y</span><span class="punctuation">;</span> | 144 | <span class="variable">y</span><span class="punctuation">;</span> |
145 | 145 | ||
146 | <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">foo</span> <span class="operator">=</span> <span class="struct">Foo</span> <span class="punctuation">{</span> <span class="field">x</span><span class="punctuation">,</span> <span class="field">y</span><span class="punctuation">:</span> <span class="variable mutable">x</span> <span class="punctuation">}</span><span class="punctuation">;</span> | 146 | <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">foo</span> <span class="operator">=</span> <span class="struct">Foo</span> <span class="punctuation">{</span> <span class="field">x</span><span class="punctuation">,</span> <span class="field">y</span><span class="punctuation">:</span> <span class="variable mutable">x</span> <span class="punctuation">}</span><span class="punctuation">;</span> |
147 | <span class="keyword">let</span> <span class="variable declaration">foo2</span> <span class="operator">=</span> <span class="variable mutable">foo</span><span class="punctuation">.</span><span class="unresolved_reference">clone</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> | 147 | <span class="keyword">let</span> <span class="variable declaration">foo2</span> <span class="operator">=</span> <span class="struct">Foo</span> <span class="punctuation">{</span> <span class="field">x</span><span class="punctuation">,</span> <span class="field">y</span><span class="punctuation">:</span> <span class="variable mutable">x</span> <span class="punctuation">}</span><span class="punctuation">;</span> |
148 | <span class="variable mutable">foo</span><span class="punctuation">.</span><span class="function">quop</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> | 148 | <span class="variable mutable">foo</span><span class="punctuation">.</span><span class="function">quop</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> |
149 | <span class="variable mutable">foo</span><span class="punctuation">.</span><span class="function mutable">qux</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> | 149 | <span class="variable mutable">foo</span><span class="punctuation">.</span><span class="function mutable">qux</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> |
150 | <span class="variable mutable">foo</span><span class="punctuation">.</span><span class="function consuming">baz</span><span class="punctuation">(</span><span class="variable consuming">foo2</span><span class="punctuation">)</span><span class="punctuation">;</span> | 150 | <span class="variable mutable">foo</span><span class="punctuation">.</span><span class="function consuming">baz</span><span class="punctuation">(</span><span class="variable consuming">foo2</span><span class="punctuation">)</span><span class="punctuation">;</span> |
diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs index 57d4e1252..211e62ea1 100644 --- a/crates/ide/src/syntax_highlighting/tests.rs +++ b/crates/ide/src/syntax_highlighting/tests.rs | |||
@@ -18,7 +18,7 @@ pub mod marker { | |||
18 | pub trait Copy {} | 18 | pub trait Copy {} |
19 | } | 19 | } |
20 | 20 | ||
21 | #[derive(Clone, Debug)] | 21 | |
22 | struct Foo { | 22 | struct Foo { |
23 | pub x: i32, | 23 | pub x: i32, |
24 | pub y: i32, | 24 | pub y: i32, |
@@ -48,7 +48,7 @@ impl Foo { | |||
48 | } | 48 | } |
49 | } | 49 | } |
50 | 50 | ||
51 | #[derive(Copy, Clone)] | 51 | #[derive(Copy)] |
52 | struct FooCopy { | 52 | struct FooCopy { |
53 | x: u32, | 53 | x: u32, |
54 | } | 54 | } |
@@ -118,7 +118,7 @@ fn main() { | |||
118 | y; | 118 | y; |
119 | 119 | ||
120 | let mut foo = Foo { x, y: x }; | 120 | let mut foo = Foo { x, y: x }; |
121 | let foo2 = foo.clone(); | 121 | let foo2 = Foo { x, y: x }; |
122 | foo.quop(); | 122 | foo.quop(); |
123 | foo.qux(); | 123 | foo.qux(); |
124 | foo.baz(foo2); | 124 | foo.baz(foo2); |
diff --git a/crates/mbe/src/syntax_bridge.rs b/crates/mbe/src/syntax_bridge.rs index a8ad917fb..d987b2500 100644 --- a/crates/mbe/src/syntax_bridge.rs +++ b/crates/mbe/src/syntax_bridge.rs | |||
@@ -636,7 +636,10 @@ impl<'a> TreeSink for TtTreeSink<'a> { | |||
636 | let (text, id) = match leaf { | 636 | let (text, id) = match leaf { |
637 | tt::Leaf::Ident(ident) => (ident.text.clone(), ident.id), | 637 | tt::Leaf::Ident(ident) => (ident.text.clone(), ident.id), |
638 | tt::Leaf::Punct(punct) => { | 638 | tt::Leaf::Punct(punct) => { |
639 | (SmolStr::new_inline_from_ascii(1, &[punct.char as u8]), punct.id) | 639 | assert!(punct.char.is_ascii()); |
640 | let char = &(punct.char as u8); | ||
641 | let text = std::str::from_utf8(std::slice::from_ref(char)).unwrap(); | ||
642 | (SmolStr::new_inline(text), punct.id) | ||
640 | } | 643 | } |
641 | tt::Leaf::Literal(lit) => (lit.text.clone(), lit.id), | 644 | tt::Leaf::Literal(lit) => (lit.text.clone(), lit.id), |
642 | }; | 645 | }; |
diff --git a/crates/project_model/src/lib.rs b/crates/project_model/src/lib.rs index 288c39e49..258f60e28 100644 --- a/crates/project_model/src/lib.rs +++ b/crates/project_model/src/lib.rs | |||
@@ -308,7 +308,13 @@ impl ProjectWorkspace { | |||
308 | .crates() | 308 | .crates() |
309 | .filter_map(|(crate_id, krate)| { | 309 | .filter_map(|(crate_id, krate)| { |
310 | let file_path = &krate.root_module; | 310 | let file_path = &krate.root_module; |
311 | let file_id = load(&file_path)?; | 311 | let file_id = match load(&file_path) { |
312 | Some(id) => id, | ||
313 | None => { | ||
314 | log::error!("failed to load crate root {}", file_path.display()); | ||
315 | return None; | ||
316 | } | ||
317 | }; | ||
312 | 318 | ||
313 | let env = krate.env.clone().into_iter().collect(); | 319 | let env = krate.env.clone().into_iter().collect(); |
314 | let proc_macro = krate | 320 | let proc_macro = krate |
diff --git a/crates/project_model/src/project_json.rs b/crates/project_model/src/project_json.rs index 545f254aa..a6895ecdd 100644 --- a/crates/project_model/src/project_json.rs +++ b/crates/project_model/src/project_json.rs | |||
@@ -13,7 +13,7 @@ use crate::cfg_flag::CfgFlag; | |||
13 | #[derive(Clone, Debug, Eq, PartialEq)] | 13 | #[derive(Clone, Debug, Eq, PartialEq)] |
14 | pub struct ProjectJson { | 14 | pub struct ProjectJson { |
15 | pub(crate) sysroot_src: Option<AbsPathBuf>, | 15 | pub(crate) sysroot_src: Option<AbsPathBuf>, |
16 | project_root: Option<AbsPathBuf>, | 16 | project_root: AbsPathBuf, |
17 | crates: Vec<Crate>, | 17 | crates: Vec<Crate>, |
18 | } | 18 | } |
19 | 19 | ||
@@ -34,10 +34,17 @@ pub struct Crate { | |||
34 | } | 34 | } |
35 | 35 | ||
36 | impl ProjectJson { | 36 | impl ProjectJson { |
37 | /// Create a new ProjectJson instance. | ||
38 | /// | ||
39 | /// # Arguments | ||
40 | /// | ||
41 | /// * `base` - The path to the workspace root (i.e. the folder containing `rust-project.json`) | ||
42 | /// * `data` - The parsed contents of `rust-project.json`, or project json that's passed via | ||
43 | /// configuration. | ||
37 | pub fn new(base: &AbsPath, data: ProjectJsonData) -> ProjectJson { | 44 | pub fn new(base: &AbsPath, data: ProjectJsonData) -> ProjectJson { |
38 | ProjectJson { | 45 | ProjectJson { |
39 | sysroot_src: data.sysroot_src.map(|it| base.join(it)), | 46 | sysroot_src: data.sysroot_src.map(|it| base.join(it)), |
40 | project_root: base.parent().map(AbsPath::to_path_buf), | 47 | project_root: base.to_path_buf(), |
41 | crates: data | 48 | crates: data |
42 | .crates | 49 | .crates |
43 | .into_iter() | 50 | .into_iter() |
@@ -85,17 +92,17 @@ impl ProjectJson { | |||
85 | .collect::<Vec<_>>(), | 92 | .collect::<Vec<_>>(), |
86 | } | 93 | } |
87 | } | 94 | } |
95 | /// Returns the number of crates in the project. | ||
88 | pub fn n_crates(&self) -> usize { | 96 | pub fn n_crates(&self) -> usize { |
89 | self.crates.len() | 97 | self.crates.len() |
90 | } | 98 | } |
99 | /// Returns an iterator over the crates in the project. | ||
91 | pub fn crates(&self) -> impl Iterator<Item = (CrateId, &Crate)> + '_ { | 100 | pub fn crates(&self) -> impl Iterator<Item = (CrateId, &Crate)> + '_ { |
92 | self.crates.iter().enumerate().map(|(idx, krate)| (CrateId(idx as u32), krate)) | 101 | self.crates.iter().enumerate().map(|(idx, krate)| (CrateId(idx as u32), krate)) |
93 | } | 102 | } |
94 | pub fn path(&self) -> Option<&AbsPath> { | 103 | /// Returns the path to the project's root folder. |
95 | match &self.project_root { | 104 | pub fn path(&self) -> &AbsPath { |
96 | Some(p) => Some(p.as_path()), | 105 | &self.project_root |
97 | None => None, | ||
98 | } | ||
99 | } | 106 | } |
100 | } | 107 | } |
101 | 108 | ||
diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml index 8db0b0d72..631ffc4a7 100644 --- a/crates/rust-analyzer/Cargo.toml +++ b/crates/rust-analyzer/Cargo.toml | |||
@@ -21,7 +21,7 @@ env_logger = { version = "0.7.1", default-features = false } | |||
21 | itertools = "0.9.0" | 21 | itertools = "0.9.0" |
22 | jod-thread = "0.1.0" | 22 | jod-thread = "0.1.0" |
23 | log = "0.4.8" | 23 | log = "0.4.8" |
24 | lsp-types = { version = "0.80.0", features = ["proposed"] } | 24 | lsp-types = { version = "0.82.0", features = ["proposed"] } |
25 | parking_lot = "0.11.0" | 25 | parking_lot = "0.11.0" |
26 | pico-args = "0.3.1" | 26 | pico-args = "0.3.1" |
27 | oorandom = "11.1.2" | 27 | oorandom = "11.1.2" |
diff --git a/crates/rust-analyzer/src/bin/main.rs b/crates/rust-analyzer/src/bin/main.rs index ba4402ade..97b246a32 100644 --- a/crates/rust-analyzer/src/bin/main.rs +++ b/crates/rust-analyzer/src/bin/main.rs | |||
@@ -134,6 +134,10 @@ fn run_server() -> Result<()> { | |||
134 | 134 | ||
135 | let discovered = ProjectManifest::discover_all(&workspace_roots); | 135 | let discovered = ProjectManifest::discover_all(&workspace_roots); |
136 | log::info!("discovered projects: {:?}", discovered); | 136 | log::info!("discovered projects: {:?}", discovered); |
137 | if discovered.is_empty() { | ||
138 | log::error!("failed to find any projects in {:?}", workspace_roots); | ||
139 | } | ||
140 | |||
137 | config.linked_projects = discovered.into_iter().map(LinkedProject::from).collect(); | 141 | config.linked_projects = discovered.into_iter().map(LinkedProject::from).collect(); |
138 | } | 142 | } |
139 | 143 | ||
diff --git a/crates/rust-analyzer/src/caps.rs b/crates/rust-analyzer/src/caps.rs index de4bc2813..c589afeaf 100644 --- a/crates/rust-analyzer/src/caps.rs +++ b/crates/rust-analyzer/src/caps.rs | |||
@@ -5,7 +5,7 @@ use lsp_types::{ | |||
5 | CallHierarchyServerCapability, ClientCapabilities, CodeActionKind, CodeActionOptions, | 5 | CallHierarchyServerCapability, ClientCapabilities, CodeActionKind, CodeActionOptions, |
6 | CodeActionProviderCapability, CodeLensOptions, CompletionOptions, | 6 | CodeActionProviderCapability, CodeLensOptions, CompletionOptions, |
7 | DocumentOnTypeFormattingOptions, FoldingRangeProviderCapability, HoverProviderCapability, | 7 | DocumentOnTypeFormattingOptions, FoldingRangeProviderCapability, HoverProviderCapability, |
8 | ImplementationProviderCapability, RenameOptions, RenameProviderCapability, SaveOptions, | 8 | ImplementationProviderCapability, OneOf, RenameOptions, SaveOptions, |
9 | SelectionRangeProviderCapability, SemanticTokensFullOptions, SemanticTokensLegend, | 9 | SelectionRangeProviderCapability, SemanticTokensFullOptions, SemanticTokensLegend, |
10 | SemanticTokensOptions, ServerCapabilities, SignatureHelpOptions, TextDocumentSyncCapability, | 10 | SemanticTokensOptions, ServerCapabilities, SignatureHelpOptions, TextDocumentSyncCapability, |
11 | TextDocumentSyncKind, TextDocumentSyncOptions, TypeDefinitionProviderCapability, | 11 | TextDocumentSyncKind, TextDocumentSyncOptions, TypeDefinitionProviderCapability, |
@@ -42,16 +42,16 @@ pub fn server_capabilities(client_caps: &ClientCapabilities) -> ServerCapabiliti | |||
42 | work_done_progress_options: WorkDoneProgressOptions { work_done_progress: None }, | 42 | work_done_progress_options: WorkDoneProgressOptions { work_done_progress: None }, |
43 | }), | 43 | }), |
44 | declaration_provider: None, | 44 | declaration_provider: None, |
45 | definition_provider: Some(true), | 45 | definition_provider: Some(OneOf::Left(true)), |
46 | type_definition_provider: Some(TypeDefinitionProviderCapability::Simple(true)), | 46 | type_definition_provider: Some(TypeDefinitionProviderCapability::Simple(true)), |
47 | implementation_provider: Some(ImplementationProviderCapability::Simple(true)), | 47 | implementation_provider: Some(ImplementationProviderCapability::Simple(true)), |
48 | references_provider: Some(true), | 48 | references_provider: Some(OneOf::Left(true)), |
49 | document_highlight_provider: Some(true), | 49 | document_highlight_provider: Some(OneOf::Left(true)), |
50 | document_symbol_provider: Some(true), | 50 | document_symbol_provider: Some(OneOf::Left(true)), |
51 | workspace_symbol_provider: Some(true), | 51 | workspace_symbol_provider: Some(true), |
52 | code_action_provider: Some(code_action_provider), | 52 | code_action_provider: Some(code_action_provider), |
53 | code_lens_provider: Some(CodeLensOptions { resolve_provider: Some(true) }), | 53 | code_lens_provider: Some(CodeLensOptions { resolve_provider: Some(true) }), |
54 | document_formatting_provider: Some(true), | 54 | document_formatting_provider: Some(OneOf::Left(true)), |
55 | document_range_formatting_provider: None, | 55 | document_range_formatting_provider: None, |
56 | document_on_type_formatting_provider: Some(DocumentOnTypeFormattingOptions { | 56 | document_on_type_formatting_provider: Some(DocumentOnTypeFormattingOptions { |
57 | first_trigger_character: "=".to_string(), | 57 | first_trigger_character: "=".to_string(), |
@@ -60,7 +60,7 @@ pub fn server_capabilities(client_caps: &ClientCapabilities) -> ServerCapabiliti | |||
60 | selection_range_provider: Some(SelectionRangeProviderCapability::Simple(true)), | 60 | selection_range_provider: Some(SelectionRangeProviderCapability::Simple(true)), |
61 | semantic_highlighting: None, | 61 | semantic_highlighting: None, |
62 | folding_range_provider: Some(FoldingRangeProviderCapability::Simple(true)), | 62 | folding_range_provider: Some(FoldingRangeProviderCapability::Simple(true)), |
63 | rename_provider: Some(RenameProviderCapability::Options(RenameOptions { | 63 | rename_provider: Some(OneOf::Right(RenameOptions { |
64 | prepare_provider: Some(true), | 64 | prepare_provider: Some(true), |
65 | work_done_progress_options: WorkDoneProgressOptions { work_done_progress: None }, | 65 | work_done_progress_options: WorkDoneProgressOptions { work_done_progress: None }, |
66 | })), | 66 | })), |
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 1a74286f5..69d05aed5 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs | |||
@@ -288,7 +288,10 @@ impl Config { | |||
288 | let path = self.root_path.join(it); | 288 | let path = self.root_path.join(it); |
289 | match ProjectManifest::from_manifest_file(path) { | 289 | match ProjectManifest::from_manifest_file(path) { |
290 | Ok(it) => it.into(), | 290 | Ok(it) => it.into(), |
291 | Err(_) => continue, | 291 | Err(e) => { |
292 | log::error!("failed to load linked project: {}", e); | ||
293 | continue; | ||
294 | } | ||
292 | } | 295 | } |
293 | } | 296 | } |
294 | ManifestOrProjectJson::ProjectJson(it) => { | 297 | ManifestOrProjectJson::ProjectJson(it) => { |
diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs index 212f98a30..96313aaec 100644 --- a/crates/rust-analyzer/src/global_state.rs +++ b/crates/rust-analyzer/src/global_state.rs | |||
@@ -63,7 +63,7 @@ pub(crate) struct GlobalState { | |||
63 | req_queue: ReqQueue, | 63 | req_queue: ReqQueue, |
64 | pub(crate) task_pool: Handle<TaskPool<Task>, Receiver<Task>>, | 64 | pub(crate) task_pool: Handle<TaskPool<Task>, Receiver<Task>>, |
65 | pub(crate) loader: Handle<Box<dyn vfs::loader::Handle>, Receiver<vfs::loader::Message>>, | 65 | pub(crate) loader: Handle<Box<dyn vfs::loader::Handle>, Receiver<vfs::loader::Message>>, |
66 | pub(crate) flycheck: Option<FlycheckHandle>, | 66 | pub(crate) flycheck: Vec<FlycheckHandle>, |
67 | pub(crate) flycheck_sender: Sender<flycheck::Message>, | 67 | pub(crate) flycheck_sender: Sender<flycheck::Message>, |
68 | pub(crate) flycheck_receiver: Receiver<flycheck::Message>, | 68 | pub(crate) flycheck_receiver: Receiver<flycheck::Message>, |
69 | pub(crate) config: Config, | 69 | pub(crate) config: Config, |
@@ -115,7 +115,7 @@ impl GlobalState { | |||
115 | req_queue: ReqQueue::default(), | 115 | req_queue: ReqQueue::default(), |
116 | task_pool, | 116 | task_pool, |
117 | loader, | 117 | loader, |
118 | flycheck: None, | 118 | flycheck: Vec::new(), |
119 | flycheck_sender, | 119 | flycheck_sender, |
120 | flycheck_receiver, | 120 | flycheck_receiver, |
121 | config, | 121 | config, |
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs index 64cb4d96c..c0943a54d 100644 --- a/crates/rust-analyzer/src/handlers.rs +++ b/crates/rust-analyzer/src/handlers.rs | |||
@@ -748,10 +748,15 @@ pub(crate) fn handle_formatting( | |||
748 | } | 748 | } |
749 | } | 749 | } |
750 | 750 | ||
751 | Ok(Some(vec![lsp_types::TextEdit { | 751 | if *file == captured_stdout { |
752 | range: Range::new(Position::new(0, 0), end_position), | 752 | // The document is already formatted correctly -- no edits needed. |
753 | new_text: captured_stdout, | 753 | Ok(None) |
754 | }])) | 754 | } else { |
755 | Ok(Some(vec![lsp_types::TextEdit { | ||
756 | range: Range::new(Position::new(0, 0), end_position), | ||
757 | new_text: captured_stdout, | ||
758 | }])) | ||
759 | } | ||
755 | } | 760 | } |
756 | 761 | ||
757 | fn handle_fixes( | 762 | fn handle_fixes( |
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index 8d3132581..06ab9d508 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs | |||
@@ -266,8 +266,8 @@ impl GlobalState { | |||
266 | } | 266 | } |
267 | } | 267 | } |
268 | 268 | ||
269 | flycheck::Message::Progress(status) => { | 269 | flycheck::Message::Progress { id, progress } => { |
270 | let (state, message) = match status { | 270 | let (state, message) = match progress { |
271 | flycheck::Progress::DidStart => { | 271 | flycheck::Progress::DidStart => { |
272 | self.diagnostics.clear_check(); | 272 | self.diagnostics.clear_check(); |
273 | (Progress::Begin, None) | 273 | (Progress::Begin, None) |
@@ -284,14 +284,21 @@ impl GlobalState { | |||
284 | } | 284 | } |
285 | }; | 285 | }; |
286 | 286 | ||
287 | self.report_progress("cargo check", state, message, None); | 287 | // When we're running multiple flychecks, we have to include a disambiguator in |
288 | // the title, or the editor complains. Note that this is a user-facing string. | ||
289 | let title = if self.flycheck.len() == 1 { | ||
290 | "cargo check".to_string() | ||
291 | } else { | ||
292 | format!("cargo check (#{})", id + 1) | ||
293 | }; | ||
294 | self.report_progress(&title, state, message, None); | ||
288 | } | 295 | } |
289 | }, | 296 | }, |
290 | } | 297 | } |
291 | 298 | ||
292 | let state_changed = self.process_changes(); | 299 | let state_changed = self.process_changes(); |
293 | if prev_status == Status::Loading && self.status == Status::Ready { | 300 | if prev_status == Status::Loading && self.status == Status::Ready { |
294 | if let Some(flycheck) = &self.flycheck { | 301 | for flycheck in &self.flycheck { |
295 | flycheck.update(); | 302 | flycheck.update(); |
296 | } | 303 | } |
297 | } | 304 | } |
@@ -490,7 +497,7 @@ impl GlobalState { | |||
490 | Ok(()) | 497 | Ok(()) |
491 | })? | 498 | })? |
492 | .on::<lsp_types::notification::DidSaveTextDocument>(|this, params| { | 499 | .on::<lsp_types::notification::DidSaveTextDocument>(|this, params| { |
493 | if let Some(flycheck) = &this.flycheck { | 500 | for flycheck in &this.flycheck { |
494 | flycheck.update(); | 501 | flycheck.update(); |
495 | } | 502 | } |
496 | if let Ok(abs_path) = from_proto::abs_path(¶ms.text_document.uri) { | 503 | if let Ok(abs_path) = from_proto::abs_path(¶ms.text_document.uri) { |
diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs index b819618cb..de0dbcad4 100644 --- a/crates/rust-analyzer/src/reload.rs +++ b/crates/rust-analyzer/src/reload.rs | |||
@@ -201,11 +201,14 @@ impl GlobalState { | |||
201 | let mut crate_graph = CrateGraph::default(); | 201 | let mut crate_graph = CrateGraph::default(); |
202 | let vfs = &mut self.vfs.write().0; | 202 | let vfs = &mut self.vfs.write().0; |
203 | let loader = &mut self.loader; | 203 | let loader = &mut self.loader; |
204 | let mem_docs = &self.mem_docs; | ||
204 | let mut load = |path: &AbsPath| { | 205 | let mut load = |path: &AbsPath| { |
205 | let contents = loader.handle.load_sync(path); | 206 | let vfs_path = vfs::VfsPath::from(path.to_path_buf()); |
206 | let path = vfs::VfsPath::from(path.to_path_buf()); | 207 | if !mem_docs.contains_key(&vfs_path) { |
207 | vfs.set_file_contents(path.clone(), contents); | 208 | let contents = loader.handle.load_sync(path); |
208 | vfs.file_id(&path) | 209 | vfs.set_file_contents(vfs_path.clone(), contents); |
210 | } | ||
211 | vfs.file_id(&vfs_path) | ||
209 | }; | 212 | }; |
210 | for ws in workspaces.iter() { | 213 | for ws in workspaces.iter() { |
211 | crate_graph.extend(ws.to_crate_graph( | 214 | crate_graph.extend(ws.to_crate_graph( |
@@ -232,29 +235,37 @@ impl GlobalState { | |||
232 | let config = match self.config.flycheck.clone() { | 235 | let config = match self.config.flycheck.clone() { |
233 | Some(it) => it, | 236 | Some(it) => it, |
234 | None => { | 237 | None => { |
235 | self.flycheck = None; | 238 | self.flycheck = Vec::new(); |
236 | return; | 239 | return; |
237 | } | 240 | } |
238 | }; | 241 | }; |
239 | 242 | ||
240 | let sender = self.flycheck_sender.clone(); | 243 | let sender = self.flycheck_sender.clone(); |
241 | let sender = Box::new(move |msg| sender.send(msg).unwrap()); | ||
242 | self.flycheck = self | 244 | self.flycheck = self |
243 | .workspaces | 245 | .workspaces |
244 | .iter() | 246 | .iter() |
245 | // FIXME: Figure out the multi-workspace situation | 247 | .enumerate() |
246 | .find_map(|w| match w { | 248 | .filter_map(|(id, w)| match w { |
247 | ProjectWorkspace::Cargo { cargo, sysroot: _ } => Some(cargo.workspace_root()), | 249 | ProjectWorkspace::Cargo { cargo, sysroot: _ } => Some((id, cargo.workspace_root())), |
248 | ProjectWorkspace::Json { project, .. } => { | 250 | ProjectWorkspace::Json { project, .. } => { |
249 | // Enable flychecks for json projects if a custom flycheck command was supplied | 251 | // Enable flychecks for json projects if a custom flycheck command was supplied |
250 | // in the workspace configuration. | 252 | // in the workspace configuration. |
251 | match config { | 253 | match config { |
252 | FlycheckConfig::CustomCommand { .. } => project.path(), | 254 | FlycheckConfig::CustomCommand { .. } => Some((id, project.path())), |
253 | _ => None, | 255 | _ => None, |
254 | } | 256 | } |
255 | } | 257 | } |
256 | }) | 258 | }) |
257 | .map(move |root| FlycheckHandle::spawn(sender, config, root.to_path_buf().into())) | 259 | .map(|(id, root)| { |
260 | let sender = sender.clone(); | ||
261 | FlycheckHandle::spawn( | ||
262 | id, | ||
263 | Box::new(move |msg| sender.send(msg).unwrap()), | ||
264 | config.clone(), | ||
265 | root.to_path_buf().into(), | ||
266 | ) | ||
267 | }) | ||
268 | .collect(); | ||
258 | } | 269 | } |
259 | } | 270 | } |
260 | 271 | ||
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs index dcbf837d6..59e780b7d 100644 --- a/crates/rust-analyzer/src/to_proto.rs +++ b/crates/rust-analyzer/src/to_proto.rs | |||
@@ -285,12 +285,18 @@ pub(crate) fn signature_help( | |||
285 | }) | 285 | }) |
286 | }; | 286 | }; |
287 | 287 | ||
288 | let signature = | 288 | let active_parameter = call_info.active_parameter.map(|it| it as i64); |
289 | lsp_types::SignatureInformation { label, documentation, parameters: Some(parameters) }; | 289 | |
290 | let signature = lsp_types::SignatureInformation { | ||
291 | label, | ||
292 | documentation, | ||
293 | parameters: Some(parameters), | ||
294 | active_parameter, | ||
295 | }; | ||
290 | lsp_types::SignatureHelp { | 296 | lsp_types::SignatureHelp { |
291 | signatures: vec![signature], | 297 | signatures: vec![signature], |
292 | active_signature: None, | 298 | active_signature: None, |
293 | active_parameter: call_info.active_parameter.map(|it| it as i64), | 299 | active_parameter, |
294 | } | 300 | } |
295 | } | 301 | } |
296 | 302 | ||
diff --git a/crates/rust-analyzer/tests/rust-analyzer/main.rs b/crates/rust-analyzer/tests/rust-analyzer/main.rs index 0880d0425..06726f957 100644 --- a/crates/rust-analyzer/tests/rust-analyzer/main.rs +++ b/crates/rust-analyzer/tests/rust-analyzer/main.rs | |||
@@ -260,6 +260,42 @@ pub use std::collections::HashMap; | |||
260 | } | 260 | } |
261 | 261 | ||
262 | #[test] | 262 | #[test] |
263 | fn test_format_document_unchanged() { | ||
264 | if skip_slow_tests() { | ||
265 | return; | ||
266 | } | ||
267 | |||
268 | let server = project( | ||
269 | r#" | ||
270 | //- /Cargo.toml | ||
271 | [package] | ||
272 | name = "foo" | ||
273 | version = "0.0.0" | ||
274 | |||
275 | //- /src/lib.rs | ||
276 | fn main() {} | ||
277 | "#, | ||
278 | ) | ||
279 | .wait_until_workspace_is_loaded(); | ||
280 | |||
281 | server.request::<Formatting>( | ||
282 | DocumentFormattingParams { | ||
283 | text_document: server.doc_id("src/lib.rs"), | ||
284 | options: FormattingOptions { | ||
285 | tab_size: 4, | ||
286 | insert_spaces: false, | ||
287 | insert_final_newline: None, | ||
288 | trim_final_newlines: None, | ||
289 | trim_trailing_whitespace: None, | ||
290 | properties: HashMap::new(), | ||
291 | }, | ||
292 | work_done_progress_params: WorkDoneProgressParams::default(), | ||
293 | }, | ||
294 | json!(null), | ||
295 | ); | ||
296 | } | ||
297 | |||
298 | #[test] | ||
263 | fn test_missing_module_code_action() { | 299 | fn test_missing_module_code_action() { |
264 | if skip_slow_tests() { | 300 | if skip_slow_tests() { |
265 | return; | 301 | return; |
diff --git a/crates/syntax/src/ast/edit.rs b/crates/syntax/src/ast/edit.rs index 8b1c65dd6..dda0a0319 100644 --- a/crates/syntax/src/ast/edit.rs +++ b/crates/syntax/src/ast/edit.rs | |||
@@ -93,6 +93,22 @@ where | |||
93 | } | 93 | } |
94 | } | 94 | } |
95 | 95 | ||
96 | impl ast::Impl { | ||
97 | #[must_use] | ||
98 | pub fn with_assoc_item_list(&self, items: ast::AssocItemList) -> ast::Impl { | ||
99 | let mut to_insert: ArrayVec<[SyntaxElement; 2]> = ArrayVec::new(); | ||
100 | if let Some(old_items) = self.assoc_item_list() { | ||
101 | let to_replace: SyntaxElement = old_items.syntax().clone().into(); | ||
102 | to_insert.push(items.syntax().clone().into()); | ||
103 | self.replace_children(single_node(to_replace), to_insert) | ||
104 | } else { | ||
105 | to_insert.push(make::tokens::single_space().into()); | ||
106 | to_insert.push(items.syntax().clone().into()); | ||
107 | self.insert_children(InsertPosition::Last, to_insert) | ||
108 | } | ||
109 | } | ||
110 | } | ||
111 | |||
96 | impl ast::AssocItemList { | 112 | impl ast::AssocItemList { |
97 | #[must_use] | 113 | #[must_use] |
98 | pub fn append_items( | 114 | pub fn append_items( |
@@ -347,6 +363,7 @@ impl ast::UseTree { | |||
347 | self.clone() | 363 | self.clone() |
348 | } | 364 | } |
349 | 365 | ||
366 | /// Splits off the given prefix, making it the path component of the use tree, appending the rest of the path to all UseTreeList items. | ||
350 | #[must_use] | 367 | #[must_use] |
351 | pub fn split_prefix(&self, prefix: &ast::Path) -> ast::UseTree { | 368 | pub fn split_prefix(&self, prefix: &ast::Path) -> ast::UseTree { |
352 | let suffix = if self.path().as_ref() == Some(prefix) && self.use_tree_list().is_none() { | 369 | let suffix = if self.path().as_ref() == Some(prefix) && self.use_tree_list().is_none() { |
diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs index 580eb3690..3a184094c 100644 --- a/crates/syntax/src/ast/make.rs +++ b/crates/syntax/src/ast/make.rs | |||
@@ -21,6 +21,10 @@ pub fn ty(text: &str) -> ast::Type { | |||
21 | ast_from_text(&format!("impl {} for D {{}};", text)) | 21 | ast_from_text(&format!("impl {} for D {{}};", text)) |
22 | } | 22 | } |
23 | 23 | ||
24 | pub fn assoc_item_list() -> ast::AssocItemList { | ||
25 | ast_from_text("impl C for D {};") | ||
26 | } | ||
27 | |||
24 | pub fn path_segment(name_ref: ast::NameRef) -> ast::PathSegment { | 28 | pub fn path_segment(name_ref: ast::NameRef) -> ast::PathSegment { |
25 | ast_from_text(&format!("use {};", name_ref)) | 29 | ast_from_text(&format!("use {};", name_ref)) |
26 | } | 30 | } |
@@ -61,6 +65,10 @@ pub fn path_from_segments( | |||
61 | }) | 65 | }) |
62 | } | 66 | } |
63 | 67 | ||
68 | pub fn glob_use_tree() -> ast::UseTree { | ||
69 | ast_from_text("use *;") | ||
70 | } | ||
71 | |||
64 | pub fn use_tree( | 72 | pub fn use_tree( |
65 | path: ast::Path, | 73 | path: ast::Path, |
66 | use_tree_list: Option<ast::UseTreeList>, | 74 | use_tree_list: Option<ast::UseTreeList>, |
diff --git a/docs/dev/syntax.md b/docs/dev/syntax.md index c08062ef4..2eb08b7ca 100644 --- a/docs/dev/syntax.md +++ b/docs/dev/syntax.md | |||
@@ -72,7 +72,7 @@ Points of note: | |||
72 | * Trivia and non-trivia tokens are not distinguished on the type level. | 72 | * Trivia and non-trivia tokens are not distinguished on the type level. |
73 | * Each token carries its full text. | 73 | * Each token carries its full text. |
74 | * The original text can be recovered by concatenating the texts of all tokens in order. | 74 | * The original text can be recovered by concatenating the texts of all tokens in order. |
75 | * Accessing a child of particular type (for example, parameter list of a function) generally involves linerary traversing the children, looking for a specific `kind`. | 75 | * Accessing a child of particular type (for example, parameter list of a function) generally involves linearly traversing the children, looking for a specific `kind`. |
76 | * Modifying the tree is roughly `O(depth)`. | 76 | * Modifying the tree is roughly `O(depth)`. |
77 | We don't make special efforts to guarantee that the depth is not linear, but, in practice, syntax trees are branchy and shallow. | 77 | We don't make special efforts to guarantee that the depth is not linear, but, in practice, syntax trees are branchy and shallow. |
78 | * If mandatory (grammar wise) node is missing from the input, it's just missing from the tree. | 78 | * If mandatory (grammar wise) node is missing from the input, it's just missing from the tree. |
diff --git a/docs/user/manual.adoc b/docs/user/manual.adoc index b15c9ee7f..7d85b36cb 100644 --- a/docs/user/manual.adoc +++ b/docs/user/manual.adoc | |||
@@ -44,6 +44,11 @@ https://github.com/rust-analyzer/rust-analyzer/tree/master/editors/code[in tree] | |||
44 | 44 | ||
45 | You can install the latest release of the plugin from | 45 | You can install the latest release of the plugin from |
46 | https://marketplace.visualstudio.com/items?itemName=matklad.rust-analyzer[the marketplace]. | 46 | https://marketplace.visualstudio.com/items?itemName=matklad.rust-analyzer[the marketplace]. |
47 | |||
48 | Note that the plugin may cause conflicts with the | ||
49 | https://marketplace.visualstudio.com/items?itemName=rust-lang.rust[official Rust plugin]. It is | ||
50 | recommended to disable the Rust plugin when using the rust-analyzer extension. | ||
51 | |||
47 | By default, the plugin will prompt you to download the matching version of the server as well: | 52 | By default, the plugin will prompt you to download the matching version of the server as well: |
48 | 53 | ||
49 | image::https://user-images.githubusercontent.com/9021944/75067008-17502500-54ba-11ea-835a-f92aac50e866.png[] | 54 | image::https://user-images.githubusercontent.com/9021944/75067008-17502500-54ba-11ea-835a-f92aac50e866.png[] |
@@ -61,6 +66,7 @@ To disable this notification put the following to `settings.json` | |||
61 | The server binary is stored in: | 66 | The server binary is stored in: |
62 | 67 | ||
63 | * Linux: `~/.config/Code/User/globalStorage/matklad.rust-analyzer` | 68 | * Linux: `~/.config/Code/User/globalStorage/matklad.rust-analyzer` |
69 | * Linux (Remote, such as WSL): `~/.vscode-server/data/User/globalStorage/matklad.rust-analyzer` | ||
64 | * macOS: `~/Library/Application\ Support/Code/User/globalStorage/matklad.rust-analyzer` | 70 | * macOS: `~/Library/Application\ Support/Code/User/globalStorage/matklad.rust-analyzer` |
65 | * Windows: `%APPDATA%\Code\User\globalStorage\matklad.rust-analyzer` | 71 | * Windows: `%APPDATA%\Code\User\globalStorage\matklad.rust-analyzer` |
66 | 72 | ||
diff --git a/editors/code/package.json b/editors/code/package.json index c57fbdda2..132664926 100644 --- a/editors/code/package.json +++ b/editors/code/package.json | |||
@@ -159,6 +159,11 @@ | |||
159 | "category": "Rust Analyzer" | 159 | "category": "Rust Analyzer" |
160 | }, | 160 | }, |
161 | { | 161 | { |
162 | "command": "rust-analyzer.updateGithubToken", | ||
163 | "title": "Update Github API token", | ||
164 | "category": "Rust Analyzer" | ||
165 | }, | ||
166 | { | ||
162 | "command": "rust-analyzer.onEnter", | 167 | "command": "rust-analyzer.onEnter", |
163 | "title": "Enhanced enter key", | 168 | "title": "Enhanced enter key", |
164 | "category": "Rust Analyzer" | 169 | "category": "Rust Analyzer" |
@@ -985,6 +990,10 @@ | |||
985 | "when": "inRustProject" | 990 | "when": "inRustProject" |
986 | }, | 991 | }, |
987 | { | 992 | { |
993 | "command": "rust-analyzer.updateGithubToken", | ||
994 | "when": "inRustProject" | ||
995 | }, | ||
996 | { | ||
988 | "command": "rust-analyzer.onEnter", | 997 | "command": "rust-analyzer.onEnter", |
989 | "when": "inRustProject" | 998 | "when": "inRustProject" |
990 | }, | 999 | }, |
diff --git a/editors/code/src/commands.ts b/editors/code/src/commands.ts index 4321de244..e9581a9b5 100644 --- a/editors/code/src/commands.ts +++ b/editors/code/src/commands.ts | |||
@@ -21,7 +21,7 @@ export function analyzerStatus(ctx: Ctx): Cmd { | |||
21 | provideTextDocumentContent(_uri: vscode.Uri): vscode.ProviderResult<string> { | 21 | provideTextDocumentContent(_uri: vscode.Uri): vscode.ProviderResult<string> { |
22 | if (!vscode.window.activeTextEditor) return ''; | 22 | if (!vscode.window.activeTextEditor) return ''; |
23 | 23 | ||
24 | return ctx.client.sendRequest(ra.analyzerStatus, null); | 24 | return ctx.client.sendRequest(ra.analyzerStatus); |
25 | } | 25 | } |
26 | 26 | ||
27 | get onDidChange(): vscode.Event<vscode.Uri> { | 27 | get onDidChange(): vscode.Event<vscode.Uri> { |
@@ -63,7 +63,7 @@ export function memoryUsage(ctx: Ctx): Cmd { | |||
63 | provideTextDocumentContent(_uri: vscode.Uri): vscode.ProviderResult<string> { | 63 | provideTextDocumentContent(_uri: vscode.Uri): vscode.ProviderResult<string> { |
64 | if (!vscode.window.activeTextEditor) return ''; | 64 | if (!vscode.window.activeTextEditor) return ''; |
65 | 65 | ||
66 | return ctx.client.sendRequest(ra.memoryUsage, null).then((mem: any) => { | 66 | return ctx.client.sendRequest(ra.memoryUsage).then((mem: any) => { |
67 | return 'Per-query memory usage:\n' + mem + '\n(note: database has been cleared)'; | 67 | return 'Per-query memory usage:\n' + mem + '\n(note: database has been cleared)'; |
68 | }); | 68 | }); |
69 | } | 69 | } |
@@ -372,7 +372,7 @@ export function expandMacro(ctx: Ctx): Cmd { | |||
372 | } | 372 | } |
373 | 373 | ||
374 | export function reloadWorkspace(ctx: Ctx): Cmd { | 374 | export function reloadWorkspace(ctx: Ctx): Cmd { |
375 | return async () => ctx.client.sendRequest(ra.reloadWorkspace, null); | 375 | return async () => ctx.client.sendRequest(ra.reloadWorkspace); |
376 | } | 376 | } |
377 | 377 | ||
378 | export function showReferences(ctx: Ctx): Cmd { | 378 | export function showReferences(ctx: Ctx): Cmd { |
diff --git a/editors/code/src/lsp_ext.ts b/editors/code/src/lsp_ext.ts index f280bba3d..d167041c4 100644 --- a/editors/code/src/lsp_ext.ts +++ b/editors/code/src/lsp_ext.ts | |||
@@ -4,8 +4,8 @@ | |||
4 | 4 | ||
5 | import * as lc from "vscode-languageclient"; | 5 | import * as lc from "vscode-languageclient"; |
6 | 6 | ||
7 | export const analyzerStatus = new lc.RequestType<null, string, void>("rust-analyzer/analyzerStatus"); | 7 | export const analyzerStatus = new lc.RequestType0<string, void>("rust-analyzer/analyzerStatus"); |
8 | export const memoryUsage = new lc.RequestType<null, string, void>("rust-analyzer/memoryUsage"); | 8 | export const memoryUsage = new lc.RequestType0<string, void>("rust-analyzer/memoryUsage"); |
9 | 9 | ||
10 | export type Status = "loading" | "ready" | "invalid" | "needsReload"; | 10 | export type Status = "loading" | "ready" | "invalid" | "needsReload"; |
11 | export interface StatusParams { | 11 | export interface StatusParams { |
@@ -13,7 +13,7 @@ export interface StatusParams { | |||
13 | } | 13 | } |
14 | export const status = new lc.NotificationType<StatusParams>("rust-analyzer/status"); | 14 | export const status = new lc.NotificationType<StatusParams>("rust-analyzer/status"); |
15 | 15 | ||
16 | export const reloadWorkspace = new lc.RequestType<null, null, void>("rust-analyzer/reloadWorkspace"); | 16 | export const reloadWorkspace = new lc.RequestType0<null, void>("rust-analyzer/reloadWorkspace"); |
17 | 17 | ||
18 | export interface SyntaxTreeParams { | 18 | export interface SyntaxTreeParams { |
19 | textDocument: lc.TextDocumentIdentifier; | 19 | textDocument: lc.TextDocumentIdentifier; |
diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts index bd99d696a..2896d90ac 100644 --- a/editors/code/src/main.ts +++ b/editors/code/src/main.ts | |||
@@ -95,6 +95,10 @@ async function tryActivate(context: vscode.ExtensionContext) { | |||
95 | await activate(context).catch(log.error); | 95 | await activate(context).catch(log.error); |
96 | }); | 96 | }); |
97 | 97 | ||
98 | ctx.registerCommand('updateGithubToken', ctx => async () => { | ||
99 | await queryForGithubToken(new PersistentState(ctx.globalState)); | ||
100 | }); | ||
101 | |||
98 | ctx.registerCommand('analyzerStatus', commands.analyzerStatus); | 102 | ctx.registerCommand('analyzerStatus', commands.analyzerStatus); |
99 | ctx.registerCommand('memoryUsage', commands.memoryUsage); | 103 | ctx.registerCommand('memoryUsage', commands.memoryUsage); |
100 | ctx.registerCommand('reloadWorkspace', commands.reloadWorkspace); | 104 | ctx.registerCommand('reloadWorkspace', commands.reloadWorkspace); |
@@ -173,7 +177,9 @@ async function bootstrapExtension(config: Config, state: PersistentState): Promi | |||
173 | if (!shouldCheckForNewNightly) return; | 177 | if (!shouldCheckForNewNightly) return; |
174 | } | 178 | } |
175 | 179 | ||
176 | const release = await fetchRelease("nightly").catch((e) => { | 180 | const release = await downloadWithRetryDialog(state, async () => { |
181 | return await fetchRelease("nightly", state.githubToken); | ||
182 | }).catch((e) => { | ||
177 | log.error(e); | 183 | log.error(e); |
178 | if (state.releaseId === undefined) { // Show error only for the initial download | 184 | if (state.releaseId === undefined) { // Show error only for the initial download |
179 | vscode.window.showErrorMessage(`Failed to download rust-analyzer nightly ${e}`); | 185 | vscode.window.showErrorMessage(`Failed to download rust-analyzer nightly ${e}`); |
@@ -192,10 +198,14 @@ async function bootstrapExtension(config: Config, state: PersistentState): Promi | |||
192 | assert(!!artifact, `Bad release: ${JSON.stringify(release)}`); | 198 | assert(!!artifact, `Bad release: ${JSON.stringify(release)}`); |
193 | 199 | ||
194 | const dest = path.join(config.globalStoragePath, "rust-analyzer.vsix"); | 200 | const dest = path.join(config.globalStoragePath, "rust-analyzer.vsix"); |
195 | await download({ | 201 | |
196 | url: artifact.browser_download_url, | 202 | await downloadWithRetryDialog(state, async () => { |
197 | dest, | 203 | await download({ |
198 | progressTitle: "Downloading rust-analyzer extension", | 204 | url: artifact.browser_download_url, |
205 | dest, | ||
206 | progressTitle: "Downloading rust-analyzer extension", | ||
207 | overwrite: true, | ||
208 | }); | ||
199 | }); | 209 | }); |
200 | 210 | ||
201 | await vscode.commands.executeCommand("workbench.extensions.installExtension", vscode.Uri.file(dest)); | 211 | await vscode.commands.executeCommand("workbench.extensions.installExtension", vscode.Uri.file(dest)); |
@@ -308,21 +318,22 @@ async function getServer(config: Config, state: PersistentState): Promise<string | |||
308 | if (userResponse !== "Download now") return dest; | 318 | if (userResponse !== "Download now") return dest; |
309 | } | 319 | } |
310 | 320 | ||
311 | const release = await fetchRelease(config.package.releaseTag); | 321 | const releaseTag = config.package.releaseTag; |
322 | const release = await downloadWithRetryDialog(state, async () => { | ||
323 | return await fetchRelease(releaseTag, state.githubToken); | ||
324 | }); | ||
312 | const artifact = release.assets.find(artifact => artifact.name === `rust-analyzer-${platform}.gz`); | 325 | const artifact = release.assets.find(artifact => artifact.name === `rust-analyzer-${platform}.gz`); |
313 | assert(!!artifact, `Bad release: ${JSON.stringify(release)}`); | 326 | assert(!!artifact, `Bad release: ${JSON.stringify(release)}`); |
314 | 327 | ||
315 | // Unlinking the exe file before moving new one on its place should prevent ETXTBSY error. | 328 | await downloadWithRetryDialog(state, async () => { |
316 | await fs.unlink(dest).catch(err => { | 329 | await download({ |
317 | if (err.code !== "ENOENT") throw err; | 330 | url: artifact.browser_download_url, |
318 | }); | 331 | dest, |
319 | 332 | progressTitle: "Downloading rust-analyzer server", | |
320 | await download({ | 333 | gunzip: true, |
321 | url: artifact.browser_download_url, | 334 | mode: 0o755, |
322 | dest, | 335 | overwrite: true, |
323 | progressTitle: "Downloading rust-analyzer server", | 336 | }); |
324 | gunzip: true, | ||
325 | mode: 0o755 | ||
326 | }); | 337 | }); |
327 | 338 | ||
328 | // Patching executable if that's NixOS. | 339 | // Patching executable if that's NixOS. |
@@ -333,3 +344,56 @@ async function getServer(config: Config, state: PersistentState): Promise<string | |||
333 | await state.updateServerVersion(config.package.version); | 344 | await state.updateServerVersion(config.package.version); |
334 | return dest; | 345 | return dest; |
335 | } | 346 | } |
347 | |||
348 | async function downloadWithRetryDialog<T>(state: PersistentState, downloadFunc: () => Promise<T>): Promise<T> { | ||
349 | while (true) { | ||
350 | try { | ||
351 | return await downloadFunc(); | ||
352 | } catch (e) { | ||
353 | const selected = await vscode.window.showErrorMessage("Failed to download: " + e.message, {}, { | ||
354 | title: "Update Github Auth Token", | ||
355 | updateToken: true, | ||
356 | }, { | ||
357 | title: "Retry download", | ||
358 | retry: true, | ||
359 | }, { | ||
360 | title: "Dismiss", | ||
361 | }); | ||
362 | |||
363 | if (selected?.updateToken) { | ||
364 | await queryForGithubToken(state); | ||
365 | continue; | ||
366 | } else if (selected?.retry) { | ||
367 | continue; | ||
368 | } | ||
369 | throw e; | ||
370 | }; | ||
371 | } | ||
372 | } | ||
373 | |||
374 | async function queryForGithubToken(state: PersistentState): Promise<void> { | ||
375 | const githubTokenOptions: vscode.InputBoxOptions = { | ||
376 | value: state.githubToken, | ||
377 | password: true, | ||
378 | prompt: ` | ||
379 | This dialog allows to store a Github authorization token. | ||
380 | The usage of an authorization token will increase the rate | ||
381 | limit on the use of Github APIs and can thereby prevent getting | ||
382 | throttled. | ||
383 | Auth tokens can be created at https://github.com/settings/tokens`, | ||
384 | }; | ||
385 | |||
386 | const newToken = await vscode.window.showInputBox(githubTokenOptions); | ||
387 | if (newToken === undefined) { | ||
388 | // The user aborted the dialog => Do not update the stored token | ||
389 | return; | ||
390 | } | ||
391 | |||
392 | if (newToken === "") { | ||
393 | log.info("Clearing github token"); | ||
394 | await state.updateGithubToken(undefined); | ||
395 | } else { | ||
396 | log.info("Storing new github token"); | ||
397 | await state.updateGithubToken(newToken); | ||
398 | } | ||
399 | } | ||
diff --git a/editors/code/src/net.ts b/editors/code/src/net.ts index 5eba2728d..9ba17b7b5 100644 --- a/editors/code/src/net.ts +++ b/editors/code/src/net.ts | |||
@@ -18,7 +18,8 @@ const OWNER = "rust-analyzer"; | |||
18 | const REPO = "rust-analyzer"; | 18 | const REPO = "rust-analyzer"; |
19 | 19 | ||
20 | export async function fetchRelease( | 20 | export async function fetchRelease( |
21 | releaseTag: string | 21 | releaseTag: string, |
22 | githubToken: string | null | undefined, | ||
22 | ): Promise<GithubRelease> { | 23 | ): Promise<GithubRelease> { |
23 | 24 | ||
24 | const apiEndpointPath = `/repos/${OWNER}/${REPO}/releases/tags/${releaseTag}`; | 25 | const apiEndpointPath = `/repos/${OWNER}/${REPO}/releases/tags/${releaseTag}`; |
@@ -27,7 +28,12 @@ export async function fetchRelease( | |||
27 | 28 | ||
28 | log.debug("Issuing request for released artifacts metadata to", requestUrl); | 29 | log.debug("Issuing request for released artifacts metadata to", requestUrl); |
29 | 30 | ||
30 | const response = await fetch(requestUrl, { headers: { Accept: "application/vnd.github.v3+json" } }); | 31 | const headers: Record<string, string> = { Accept: "application/vnd.github.v3+json" }; |
32 | if (githubToken != null) { | ||
33 | headers.Authorization = "token " + githubToken; | ||
34 | } | ||
35 | |||
36 | const response = await fetch(requestUrl, { headers: headers }); | ||
31 | 37 | ||
32 | if (!response.ok) { | 38 | if (!response.ok) { |
33 | log.error("Error fetching artifact release info", { | 39 | log.error("Error fetching artifact release info", { |
@@ -70,6 +76,7 @@ interface DownloadOpts { | |||
70 | dest: string; | 76 | dest: string; |
71 | mode?: number; | 77 | mode?: number; |
72 | gunzip?: boolean; | 78 | gunzip?: boolean; |
79 | overwrite?: boolean; | ||
73 | } | 80 | } |
74 | 81 | ||
75 | export async function download(opts: DownloadOpts) { | 82 | export async function download(opts: DownloadOpts) { |
@@ -79,6 +86,13 @@ export async function download(opts: DownloadOpts) { | |||
79 | const randomHex = crypto.randomBytes(5).toString("hex"); | 86 | const randomHex = crypto.randomBytes(5).toString("hex"); |
80 | const tempFile = path.join(dest.dir, `${dest.name}${randomHex}`); | 87 | const tempFile = path.join(dest.dir, `${dest.name}${randomHex}`); |
81 | 88 | ||
89 | if (opts.overwrite) { | ||
90 | // Unlinking the exe file before moving new one on its place should prevent ETXTBSY error. | ||
91 | await fs.promises.unlink(opts.dest).catch(err => { | ||
92 | if (err.code !== "ENOENT") throw err; | ||
93 | }); | ||
94 | } | ||
95 | |||
82 | await vscode.window.withProgress( | 96 | await vscode.window.withProgress( |
83 | { | 97 | { |
84 | location: vscode.ProgressLocation.Notification, | 98 | location: vscode.ProgressLocation.Notification, |
diff --git a/editors/code/src/persistent_state.ts b/editors/code/src/persistent_state.ts index 5705eed81..afb652589 100644 --- a/editors/code/src/persistent_state.ts +++ b/editors/code/src/persistent_state.ts | |||
@@ -38,4 +38,15 @@ export class PersistentState { | |||
38 | async updateServerVersion(value: string | undefined) { | 38 | async updateServerVersion(value: string | undefined) { |
39 | await this.globalState.update("serverVersion", value); | 39 | await this.globalState.update("serverVersion", value); |
40 | } | 40 | } |
41 | |||
42 | /** | ||
43 | * Github authorization token. | ||
44 | * This is used for API requests against the Github API. | ||
45 | */ | ||
46 | get githubToken(): string | undefined { | ||
47 | return this.globalState.get("githubToken"); | ||
48 | } | ||
49 | async updateGithubToken(value: string | undefined) { | ||
50 | await this.globalState.update("githubToken", value); | ||
51 | } | ||
41 | } | 52 | } |