aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock81
-rw-r--r--crates/assists/src/ast_transform.rs28
-rw-r--r--crates/assists/src/handlers/add_missing_impl_members.rs75
-rw-r--r--crates/assists/src/handlers/auto_import.rs33
-rw-r--r--crates/assists/src/handlers/invert_if.rs13
-rw-r--r--crates/assists/src/handlers/merge_imports.rs14
-rw-r--r--crates/assists/src/handlers/replace_qualified_name_with_use.rs20
-rw-r--r--crates/assists/src/utils/insert_use.rs346
-rw-r--r--crates/flycheck/src/lib.rs24
-rw-r--r--crates/hir/src/code_model.rs50
-rw-r--r--crates/hir/src/semantics.rs19
-rw-r--r--crates/hir_def/src/diagnostics.rs49
-rw-r--r--crates/hir_def/src/find_path.rs170
-rw-r--r--crates/hir_def/src/item_scope.rs22
-rw-r--r--crates/hir_def/src/item_tree.rs8
-rw-r--r--crates/hir_def/src/item_tree/lower.rs7
-rw-r--r--crates/hir_def/src/item_tree/tests.rs6
-rw-r--r--crates/hir_def/src/nameres.rs93
-rw-r--r--crates/hir_def/src/nameres/collector.rs508
-rw-r--r--crates/hir_def/src/nameres/tests.rs1
-rw-r--r--crates/hir_def/src/nameres/tests/diagnostics.rs131
-rw-r--r--crates/hir_def/src/nameres/tests/macros.rs73
-rw-r--r--crates/hir_def/src/nameres/tests/mod_resolution.rs36
-rw-r--r--crates/hir_def/src/path.rs4
-rw-r--r--crates/hir_def/src/test_db.rs42
-rw-r--r--crates/hir_expand/src/db.rs4
-rw-r--r--crates/hir_expand/src/eager.rs2
-rw-r--r--crates/hir_expand/src/hygiene.rs2
-rw-r--r--crates/hir_expand/src/lib.rs2
-rw-r--r--crates/hir_expand/src/name.rs10
-rw-r--r--crates/hir_expand/src/proc_macro.rs39
-rw-r--r--crates/hir_ty/Cargo.toml6
-rw-r--r--crates/hir_ty/src/infer.rs2
-rw-r--r--crates/hir_ty/src/traits/chalk.rs10
-rw-r--r--crates/ide/src/completion/complete_postfix.rs17
-rw-r--r--crates/ide/src/completion/completion_context.rs10
-rw-r--r--crates/ide/src/completion/presentation.rs123
-rw-r--r--crates/ide/src/diagnostics.rs62
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlighting.html6
-rw-r--r--crates/ide/src/syntax_highlighting/tests.rs6
-rw-r--r--crates/mbe/src/syntax_bridge.rs5
-rw-r--r--crates/project_model/src/lib.rs8
-rw-r--r--crates/project_model/src/project_json.rs21
-rw-r--r--crates/rust-analyzer/Cargo.toml2
-rw-r--r--crates/rust-analyzer/src/bin/main.rs4
-rw-r--r--crates/rust-analyzer/src/caps.rs14
-rw-r--r--crates/rust-analyzer/src/config.rs5
-rw-r--r--crates/rust-analyzer/src/global_state.rs4
-rw-r--r--crates/rust-analyzer/src/handlers.rs13
-rw-r--r--crates/rust-analyzer/src/main_loop.rs17
-rw-r--r--crates/rust-analyzer/src/reload.rs33
-rw-r--r--crates/rust-analyzer/src/to_proto.rs12
-rw-r--r--crates/rust-analyzer/tests/rust-analyzer/main.rs36
-rw-r--r--crates/syntax/src/ast/edit.rs17
-rw-r--r--crates/syntax/src/ast/make.rs8
-rw-r--r--docs/dev/syntax.md2
-rw-r--r--docs/user/manual.adoc6
-rw-r--r--editors/code/package.json9
-rw-r--r--editors/code/src/commands.ts6
-rw-r--r--editors/code/src/lsp_ext.ts6
-rw-r--r--editors/code/src/main.ts98
-rw-r--r--editors/code/src/net.ts18
-rw-r--r--editors/code/src/persistent_state.ts11
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]]
131name = "cargo_metadata" 131name = "cargo_metadata"
132version = "0.11.2" 132version = "0.11.3"
133source = "registry+https://github.com/rust-lang/crates.io-index" 133source = "registry+https://github.com/rust-lang/crates.io-index"
134checksum = "c990b1694d29f8e477f456db1b2fcd5dd1cd6e29d5be082df45213e8834eb39a" 134checksum = "e708746e51dfaeff27c6c3979a4005a7faddabe40144204a0b1ce5ad34a1d0a5"
135dependencies = [ 135dependencies = [
136 "semver", 136 "semver",
137 "serde", 137 "serde",
@@ -140,9 +140,9 @@ dependencies = [
140 140
141[[package]] 141[[package]]
142name = "cc" 142name = "cc"
143version = "1.0.59" 143version = "1.0.60"
144source = "registry+https://github.com/rust-lang/crates.io-index" 144source = "registry+https://github.com/rust-lang/crates.io-index"
145checksum = "66120af515773fb005778dc07c261bd201ec8ce50bd6e7144c927753fe013381" 145checksum = "ef611cc68ff783f18535d77ddd080185275713d852c4f5cbb6122c462a7a825c"
146 146
147[[package]] 147[[package]]
148name = "cfg" 148name = "cfg"
@@ -162,9 +162,9 @@ checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
162 162
163[[package]] 163[[package]]
164name = "chalk-derive" 164name = "chalk-derive"
165version = "0.27.0" 165version = "0.30.0"
166source = "registry+https://github.com/rust-lang/crates.io-index" 166source = "registry+https://github.com/rust-lang/crates.io-index"
167checksum = "d5444ff2a211fe2a863e44d16a368c3d8a314d489de21b8eeb6879f14dd5d4a8" 167checksum = "a6696d18587b7470c1e357a3fa120a2b7e6ac95e91d5c408f087455f7dc31f8b"
168dependencies = [ 168dependencies = [
169 "proc-macro2", 169 "proc-macro2",
170 "quote", 170 "quote",
@@ -174,9 +174,9 @@ dependencies = [
174 174
175[[package]] 175[[package]]
176name = "chalk-ir" 176name = "chalk-ir"
177version = "0.27.0" 177version = "0.30.0"
178source = "registry+https://github.com/rust-lang/crates.io-index" 178source = "registry+https://github.com/rust-lang/crates.io-index"
179checksum = "e39c3db1dd4abfaa7658faaa62e5fe998a982a592b710bd971fad5b6adfcfdef" 179checksum = "8c9538918d3e1fd6edda042d717c969a4099af67a40372dfb0a00b45d3a5a946"
180dependencies = [ 180dependencies = [
181 "chalk-derive", 181 "chalk-derive",
182 "lazy_static", 182 "lazy_static",
@@ -184,9 +184,9 @@ dependencies = [
184 184
185[[package]] 185[[package]]
186name = "chalk-recursive" 186name = "chalk-recursive"
187version = "0.27.0" 187version = "0.30.0"
188source = "registry+https://github.com/rust-lang/crates.io-index" 188source = "registry+https://github.com/rust-lang/crates.io-index"
189checksum = "3bfae328eff80ca54dcd0d731725bbb56136ac21c59261b68f1e5498e056b306" 189checksum = "97ec8d95c808f2b540c39da889536e1ae0d15182107f61fe80000ec3a5c3959a"
190dependencies = [ 190dependencies = [
191 "chalk-derive", 191 "chalk-derive",
192 "chalk-ir", 192 "chalk-ir",
@@ -197,9 +197,9 @@ dependencies = [
197 197
198[[package]] 198[[package]]
199name = "chalk-solve" 199name = "chalk-solve"
200version = "0.27.0" 200version = "0.30.0"
201source = "registry+https://github.com/rust-lang/crates.io-index" 201source = "registry+https://github.com/rust-lang/crates.io-index"
202checksum = "a673abe3077adc25f8ee0894198aed494a5bb0ce50ee993900d0ee1a44e1948a" 202checksum = "f373dff4bcff66004424b72bcc56ae62889c21887c1cac875f083f69a7da4448"
203dependencies = [ 203dependencies = [
204 "chalk-derive", 204 "chalk-derive",
205 "chalk-ir", 205 "chalk-ir",
@@ -214,13 +214,15 @@ dependencies = [
214 214
215[[package]] 215[[package]]
216name = "chrono" 216name = "chrono"
217version = "0.4.15" 217version = "0.4.18"
218source = "registry+https://github.com/rust-lang/crates.io-index" 218source = "registry+https://github.com/rust-lang/crates.io-index"
219checksum = "942f72db697d8767c22d46a598e01f2d3b475501ea43d0db4f16d90259182d0b" 219checksum = "d021fddb7bd3e734370acfa4a83f34095571d8570c039f1420d77540f68d5772"
220dependencies = [ 220dependencies = [
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]]
313name = "either" 315name = "either"
314version = "1.6.0" 316version = "1.6.1"
315source = "registry+https://github.com/rust-lang/crates.io-index" 317source = "registry+https://github.com/rust-lang/crates.io-index"
316checksum = "cd56b59865bce947ac5958779cfa508f6c3b9497cc762b7e24a12d11ccde2c4f" 318checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
317 319
318[[package]] 320[[package]]
319name = "ena" 321name = "ena"
@@ -387,9 +389,9 @@ dependencies = [
387 389
388[[package]] 390[[package]]
389name = "fs-err" 391name = "fs-err"
390version = "2.3.0" 392version = "2.5.0"
391source = "registry+https://github.com/rust-lang/crates.io-index" 393source = "registry+https://github.com/rust-lang/crates.io-index"
392checksum = "c1a51f8b7158efbe531f7baa74e38e49fbc41239e5d66720bb37ed39c27c241a" 394checksum = "bcd1163ae48bda72a20ae26d66a04d3094135cadab911cff418ae5e33f253431"
393 395
394[[package]] 396[[package]]
395name = "fsevent" 397name = "fsevent"
@@ -466,9 +468,9 @@ dependencies = [
466 468
467[[package]] 469[[package]]
468name = "hermit-abi" 470name = "hermit-abi"
469version = "0.1.15" 471version = "0.1.16"
470source = "registry+https://github.com/rust-lang/crates.io-index" 472source = "registry+https://github.com/rust-lang/crates.io-index"
471checksum = "3deed196b6e7f9e44a2ae8d94225d80302d81208b1bb673fd21fe634645c85a9" 473checksum = "4c30f6d0bc6b00693347368a67d41b58f2fb851215ff1da49e90fe2c5c667151"
472dependencies = [ 474dependencies = [
473 "libc", 475 "libc",
474] 476]
@@ -662,9 +664,12 @@ dependencies = [
662 664
663[[package]] 665[[package]]
664name = "instant" 666name = "instant"
665version = "0.1.6" 667version = "0.1.7"
666source = "registry+https://github.com/rust-lang/crates.io-index" 668source = "registry+https://github.com/rust-lang/crates.io-index"
667checksum = "5b141fdc7836c525d4d594027d318c84161ca17aaf8113ab1f81ab93ae897485" 669checksum = "63312a18f7ea8760cdd0a7c5aac1a619752a246b833545e3e36d1f81f7cd9e66"
670dependencies = [
671 "cfg-if",
672]
668 673
669[[package]] 674[[package]]
670name = "iovec" 675name = "iovec"
@@ -736,9 +741,9 @@ dependencies = [
736 741
737[[package]] 742[[package]]
738name = "libmimalloc-sys" 743name = "libmimalloc-sys"
739version = "0.1.16" 744version = "0.1.17"
740source = "registry+https://github.com/rust-lang/crates.io-index" 745source = "registry+https://github.com/rust-lang/crates.io-index"
741checksum = "677c4be79b14bd72496b87789b702ba02cd1a9f16a59369fe847082fd03efd88" 746checksum = "2bd65748dfb74a807e8379fd49bf4d38964d92b8637b56f173f87e1d70433368"
742dependencies = [ 747dependencies = [
743 "cmake", 748 "cmake",
744] 749]
@@ -775,9 +780,9 @@ dependencies = [
775 780
776[[package]] 781[[package]]
777name = "lsp-types" 782name = "lsp-types"
778version = "0.80.0" 783version = "0.82.0"
779source = "registry+https://github.com/rust-lang/crates.io-index" 784source = "registry+https://github.com/rust-lang/crates.io-index"
780checksum = "f4265e2715bdacbb4dad029fce525e420cd66dc0af24ff9cb996a8ab48ac92ef" 785checksum = "db895abb8527cf59e3de893ab2acf52cf904faeb65e60ea6f373e11fe86464e8"
781dependencies = [ 786dependencies = [
782 "base64", 787 "base64",
783 "bitflags", 788 "bitflags",
@@ -839,18 +844,18 @@ dependencies = [
839 844
840[[package]] 845[[package]]
841name = "memoffset" 846name = "memoffset"
842version = "0.5.5" 847version = "0.5.6"
843source = "registry+https://github.com/rust-lang/crates.io-index" 848source = "registry+https://github.com/rust-lang/crates.io-index"
844checksum = "c198b026e1bbf08a937e94c6c60f9ec4a2267f5b0d2eec9c1b21b061ce2be55f" 849checksum = "043175f069eda7b85febe4a74abbaeff828d9f8b448515d3151a14a3542811aa"
845dependencies = [ 850dependencies = [
846 "autocfg", 851 "autocfg",
847] 852]
848 853
849[[package]] 854[[package]]
850name = "mimalloc" 855name = "mimalloc"
851version = "0.1.20" 856version = "0.1.21"
852source = "registry+https://github.com/rust-lang/crates.io-index" 857source = "registry+https://github.com/rust-lang/crates.io-index"
853checksum = "00759bcf69082fa629ae8823fcc72f7454a90c6476110297a1ae5fb3a559e474" 858checksum = "2e18b3d01186c4a7bec0b56cff9a4b9d5a1461aa162c7b8eaf2e5ee10b265b02"
854dependencies = [ 859dependencies = [
855 "libmimalloc-sys", 860 "libmimalloc-sys",
856] 861]
@@ -1072,9 +1077,9 @@ checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6"
1072 1077
1073[[package]] 1078[[package]]
1074name = "proc-macro2" 1079name = "proc-macro2"
1075version = "1.0.21" 1080version = "1.0.23"
1076source = "registry+https://github.com/rust-lang/crates.io-index" 1081source = "registry+https://github.com/rust-lang/crates.io-index"
1077checksum = "36e28516df94f3dd551a587da5357459d9b36d945a7c37c3557928c1c2ff2a2c" 1082checksum = "51ef7cd2518ead700af67bf9d1a658d90b6037d77110fd9c0445429d0ba1c6c9"
1078dependencies = [ 1083dependencies = [
1079 "unicode-xid", 1084 "unicode-xid",
1080] 1085]
@@ -1187,9 +1192,9 @@ dependencies = [
1187 1192
1188[[package]] 1193[[package]]
1189name = "rayon-core" 1194name = "rayon-core"
1190version = "1.8.0" 1195version = "1.8.1"
1191source = "registry+https://github.com/rust-lang/crates.io-index" 1196source = "registry+https://github.com/rust-lang/crates.io-index"
1192checksum = "91739a34c4355b5434ce54c9086c5895604a9c278586d1f1aa95e04f66b525a0" 1197checksum = "e8c4fec834fb6e6d2dd5eece3c7b432a52f0ba887cf40e595190c4107edc08bf"
1193dependencies = [ 1198dependencies = [
1194 "crossbeam-channel", 1199 "crossbeam-channel",
1195 "crossbeam-deque", 1200 "crossbeam-deque",
@@ -1465,9 +1470,9 @@ checksum = "fbee7696b84bbf3d89a1c2eccff0850e3047ed46bfcd2e92c29a2d074d57e252"
1465 1470
1466[[package]] 1471[[package]]
1467name = "smol_str" 1472name = "smol_str"
1468version = "0.1.16" 1473version = "0.1.17"
1469source = "registry+https://github.com/rust-lang/crates.io-index" 1474source = "registry+https://github.com/rust-lang/crates.io-index"
1470checksum = "2f7909a1d8bc166a862124d84fdc11bda0ea4ed3157ccca662296919c2972db1" 1475checksum = "6ca0f7ce3a29234210f0f4f0b56f8be2e722488b95cb522077943212da3b32eb"
1471dependencies = [ 1476dependencies = [
1472 "serde", 1477 "serde",
1473] 1478]
@@ -1493,9 +1498,9 @@ version = "0.0.0"
1493 1498
1494[[package]] 1499[[package]]
1495name = "syn" 1500name = "syn"
1496version = "1.0.40" 1501version = "1.0.42"
1497source = "registry+https://github.com/rust-lang/crates.io-index" 1502source = "registry+https://github.com/rust-lang/crates.io-index"
1498checksum = "963f7d3cc59b59b9325165add223142bbf1df27655d07789f109896d353d8350" 1503checksum = "9c51d92969d209b54a98397e1b91c8ae82d8c87a7bb87df0b29aa2ad81454228"
1499dependencies = [ 1504dependencies = [
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!
21pub trait AstTransform<'a> { 49pub 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#"
321trait Foo { fn foo(&self); }
322struct S;
323impl Foo for S<|>"#,
324 r#"
325trait Foo { fn foo(&self); }
326struct S;
327impl 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#"
422mod foo {
423 pub mod bar {
424 pub struct Bar;
425 pub trait Foo { fn foo(&self, bar: Bar); }
426 }
427}
428
429use foo::bar;
430
431struct S;
432impl bar::Foo for S { <|> }"#,
433 r#"
434mod foo {
435 pub mod bar {
436 pub struct Bar;
437 pub trait Foo { fn foo(&self, bar: Bar); }
438 }
439}
440
441use foo::bar;
442
443struct S;
444impl 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;
95use std::fmt<|>::Display; 95use std::fmt<|>::Display;
96", 96",
97 r" 97 r"
98use std::fmt::{Display, Debug}; 98use std::fmt::{Debug, Display};
99", 99",
100 ); 100 );
101 } 101 }
@@ -122,7 +122,7 @@ use std::fmt::{self, Display};
122use std::{fmt, <|>fmt::Display}; 122use std::{fmt, <|>fmt::Display};
123", 123",
124 r" 124 r"
125use std::{fmt::{Display, self}}; 125use std::{fmt::{self, Display}};
126", 126",
127 ); 127 );
128 } 128 }
@@ -210,13 +210,17 @@ use std::{fmt<|>::Debug, fmt::Display};
210use std::{fmt::{Debug, Display}}; 210use 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"
216use std::{fmt::Debug, fmt<|>::Display}; 220use std::{fmt::Debug, fmt<|>::Display};
217", 221",
218 r" 222 r"
219use std::{fmt::{Display, Debug}}; 223use std::{fmt::{Debug, Display}};
220", 224",
221 ); 225 );
222 } 226 }
@@ -310,9 +314,7 @@ use foo::<|>{
310}; 314};
311", 315",
312 r" 316 r"
313use foo::{ 317use foo::{FooBar, bar::baz};
314 FooBar,
315bar::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"
301use std::fmt::{Debug, self}; 301use std::fmt::{self, Debug};
302 302
303impl fmt for Foo { 303impl fmt for Foo {
304} 304}
@@ -316,9 +316,8 @@ use std::fmt::{Debug, nested::{Display}};
316impl std::fmt::nested<|> for Foo { 316impl std::fmt::nested<|> for Foo {
317} 317}
318", 318",
319 // FIXME(veykril): should be nested::{self, Display} here
320 r" 319 r"
321use std::fmt::{Debug, nested::{Display}, nested}; 320use std::fmt::{Debug, nested::{self, Display}};
322 321
323impl nested for Foo { 322impl nested for Foo {
324} 323}
@@ -336,9 +335,8 @@ use std::fmt::{Debug, nested::{self, Display}};
336impl std::fmt::nested<|> for Foo { 335impl std::fmt::nested<|> for Foo {
337} 336}
338", 337",
339 // FIXME(veykril): nested is duplicated now
340 r" 338 r"
341use std::fmt::{Debug, nested::{self, Display}, nested}; 339use std::fmt::{Debug, nested::{self, Display}};
342 340
343impl nested for Foo { 341impl nested for Foo {
344} 342}
@@ -357,7 +355,7 @@ impl std::fmt::nested::Debug<|> for Foo {
357} 355}
358", 356",
359 r" 357 r"
360use std::fmt::{Debug, nested::{Display}, nested::Debug}; 358use std::fmt::{Debug, nested::{Debug, Display}};
361 359
362impl Debug for Foo { 360impl Debug for Foo {
363} 361}
@@ -395,7 +393,7 @@ impl std::fmt::Display<|> for Foo {
395} 393}
396", 394",
397 r" 395 r"
398use std::fmt::{nested::Debug, Display}; 396use std::fmt::{Display, nested::Debug};
399 397
400impl Display for Foo { 398impl Display for Foo {
401} 399}
@@ -415,12 +413,8 @@ use crate::{
415 413
416fn foo() { crate::ty::lower<|>::trait_env() } 414fn foo() { crate::ty::lower<|>::trait_env() }
417", 415",
418 // FIXME(veykril): formatting broke here
419 r" 416 r"
420use crate::{ 417use crate::{AssocItem, ty::{Substs, Ty, lower}};
421 ty::{Substs, Ty},
422 AssocItem,
423ty::lower};
424 418
425fn foo() { lower::trait_env() } 419fn foo() { lower::trait_env() }
426", 420",
@@ -619,7 +613,7 @@ fn main() {
619} 613}
620 ", 614 ",
621 r" 615 r"
622use std::fmt::{Display, self}; 616use std::fmt::{self, Display};
623 617
624fn main() { 618fn 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`.
2use std::iter::{self, successors}; 2use std::{
3 cmp::Ordering,
4 iter::{self, successors},
5};
3 6
4use algo::skip_trivia_token;
5use ast::{ 7use ast::{
6 edit::{AstNodeEdit, IndentLevel}, 8 edit::{AstNodeEdit, IndentLevel},
7 PathSegmentKind, VisibilityOwner, 9 PathSegmentKind, VisibilityOwner,
@@ -9,9 +11,8 @@ use ast::{
9use syntax::{ 11use 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};
14use test_utils::mark;
15 16
16#[derive(Debug)] 17#[derive(Debug)]
17pub enum ImportScope { 18pub 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
151pub(crate) fn try_merge_imports( 151pub(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
167fn 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
174pub(crate) fn try_merge_trees( 166pub(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() 181fn 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
293fn 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]
298fn first_segment(path: &ast::Path) -> Option<ast::PathSegment> {
299 first_path(path).segment()
300}
301
302fn first_path(path: &ast::Path) -> ast::Path {
303 successors(Some(path.clone()), ast::Path::qualifier).last().unwrap()
304}
305
306fn 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}}
316fn 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 337fn 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
282fn first_segment(path: &ast::Path) -> Option<ast::PathSegment> {
283 first_path(path).segment()
284}
285
286fn first_path(path: &ast::Path) -> ast::Path {
287 successors(Some(path.clone()), ast::Path::qualifier).last().unwrap()
288}
289
290fn 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)]
296enum AddBlankLine { 391enum 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};
674use std::io;", 778use 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;
699use 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
60impl FlycheckHandle { 60impl 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 {
95struct Restart; 100struct Restart;
96 101
97struct FlycheckActor { 102struct 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
114impl FlycheckActor { 120impl 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
767pub struct Param {
768 ty: Type,
769}
770
771impl 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)]
746pub struct SelfParam { 778pub struct SelfParam {
747 func: FunctionId, 779 func: FunctionId,
748} 780}
749 781
750pub struct Param {
751 _ty: (),
752}
753
754impl SelfParam { 782impl 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)]
701pub struct SemanticsScope<'a> { 720pub 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)]
33pub struct UnresolvedExternCrate {
34 pub file: HirFileId,
35 pub item: AstPtr<ast::ExternCrate>,
36}
37
38impl 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)]
54pub struct UnresolvedImport {
55 pub file: HirFileId,
56 pub node: AstPtr<ast::UseTree>,
57}
58
59impl 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};
4use rustc_hash::FxHashSet; 4use rustc_hash::FxHashSet;
5use test_utils::mark; 5use test_utils::mark;
6 6
7use crate::nameres::CrateDefMap;
7use crate::{ 8use 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.
19pub fn find_path(db: &dyn DefDatabase, item: ItemInNs, from: ModuleId) -> Option<ModPath> { 20pub 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
25pub 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
24const MAX_PATH_LEN: usize = 15; 30const MAX_PATH_LEN: usize = 15;
@@ -36,11 +42,67 @@ impl ModPath {
36 } 42 }
37} 43}
38 44
45fn 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)]
78pub enum Prefixed {
79 Not,
80 BySelf,
81 Plain,
82}
83
84impl 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
39fn find_path_inner( 100fn 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
187fn select_best_path(old_path: ModPath, new_path: ModPath, prefer_no_std: bool) -> ModPath { 238fn 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
6use base_db::CrateId; 6use base_db::CrateId;
7use hir_expand::name::Name; 7use hir_expand::name::Name;
8use hir_expand::MacroDefKind;
8use once_cell::sync::Lazy; 9use once_cell::sync::Lazy;
9use rustc_hash::{FxHashMap, FxHashSet}; 10use rustc_hash::{FxHashMap, FxHashSet};
10use test_utils::mark; 11use test_utils::mark;
11 12
13use crate::ModuleId;
12use crate::{ 14use 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
270impl PerNs { 292impl 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
297macro_rules! from_attrs { 296macro_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)]
489pub struct ExternCrate { 493pub 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
289mod diagnostics { 289mod 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
6use std::iter;
7
6use base_db::{CrateId, FileId, ProcMacroId}; 8use base_db::{CrateId, FileId, ProcMacroId};
7use cfg::CfgOptions; 9use cfg::CfgOptions;
10use hir_expand::InFile;
8use hir_expand::{ 11use 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};
16use rustc_hash::FxHashMap; 19use rustc_hash::{FxHashMap, FxHashSet};
17use syntax::ast; 20use syntax::ast;
18use test_utils::mark; 21use test_utils::mark;
22use tt::{Leaf, TokenTree};
19 23
20use crate::{ 24use 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)]
118enum ImportSource {
119 Import(ItemTreeId<item_tree::Import>),
120 ExternCrate(ItemTreeId<item_tree::ExternCrate>),
121}
122
123#[derive(Clone, Debug, Eq, PartialEq)]
115struct Import { 124struct 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
125impl Import { 135impl 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(&macro_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;
2mod incremental; 2mod incremental;
3mod macros; 3mod macros;
4mod mod_resolution; 4mod mod_resolution;
5mod diagnostics;
5mod primitives; 6mod primitives;
6 7
7use std::sync::Arc; 8use 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 @@
1use base_db::fixture::WithFixture;
2use base_db::FileId;
3use base_db::SourceDatabaseExt;
4use hir_expand::db::AstDatabase;
5use rustc_hash::FxHashMap;
6use syntax::TextRange;
7use syntax::TextSize;
8
9use crate::test_db::TestDB;
10
11fn 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]
43fn 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]
56fn 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]
83fn 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]
96fn 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]
120fn 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]
672fn 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]
704fn 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]
675fn 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]
711fn module_resolution_decl_inside_module_in_non_crate_root_2() { 675fn 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
8use base_db::SourceDatabase;
8use base_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, Upcast}; 9use base_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, Upcast};
9use hir_expand::db::AstDatabase; 10use hir_expand::db::AstDatabase;
11use hir_expand::diagnostics::Diagnostic;
12use hir_expand::diagnostics::DiagnosticSinkBuilder;
13use rustc_hash::FxHashMap;
10use rustc_hash::FxHashSet; 14use rustc_hash::FxHashSet;
15use syntax::TextRange;
16use test_utils::extract_annotations;
11 17
12use crate::db::DefDatabase; 18use 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)]
8pub struct ProcMacroExpander { 8pub struct ProcMacroExpander {
9 krate: CrateId, 9 krate: CrateId,
10 proc_macro_id: ProcMacroId, 10 proc_macro_id: Option<ProcMacroId>,
11} 11}
12 12
13macro_rules! err { 13macro_rules! err {
@@ -20,8 +20,14 @@ macro_rules! err {
20} 20}
21 21
22impl ProcMacroExpander { 22impl 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"
17log = "0.4.8" 17log = "0.4.8"
18rustc-hash = "1.1.0" 18rustc-hash = "1.1.0"
19scoped-tls = "1" 19scoped-tls = "1"
20chalk-solve = { version = "0.27.0" } 20chalk-solve = { version = "0.30.0" }
21chalk-ir = { version = "0.27.0" } 21chalk-ir = { version = "0.30.0" }
22chalk-recursive = { version = "0.27.0" } 22chalk-recursive = { version = "0.30.0" }
23 23
24stdx = { path = "../stdx", version = "0.0.0" } 24stdx = { path = "../stdx", version = "0.0.0" }
25hir_def = { path = "../hir_def", version = "0.0.0" } 25hir_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
3use base_db::SourceDatabase; 3use base_db::SourceDatabase;
4use hir::{Semantics, SemanticsScope, Type}; 4use hir::{Local, ScopeDef, Semantics, SemanticsScope, Type};
5use ide_db::RootDatabase; 5use ide_db::RootDatabase;
6use syntax::{ 6use 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
96impl<'a> CompletionContext<'a> { 97impl<'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#"
890struct Foo {}
891fn ref_arg(x: &Foo) {}
892fn main() {
893 let x = Foo {};
894 ref_ar<|>
895}
896"#,
897 r#"
898struct Foo {}
899fn ref_arg(x: &Foo) {}
900fn 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#"
913struct Foo {}
914fn ref_arg(x: &mut Foo) {}
915fn main() {
916 let x = Foo {};
917 ref_ar<|>
918}
919"#,
920 r#"
921struct Foo {}
922fn ref_arg(x: &mut Foo) {}
923fn 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#"
936struct Foo {}
937struct Bar {}
938impl Bar {
939 fn apply_foo(&self, x: &Foo) {}
940}
941
942fn main() {
943 let x = Foo {};
944 let y = Bar {};
945 y.<|>
946}
947"#,
948 r#"
949struct Foo {}
950struct Bar {}
951impl Bar {
952 fn apply_foo(&self, x: &Foo) {}
953}
954
955fn 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#"
969fn take_mutably(mut x: &i32) {}
970
971fn main() {
972 take_m<|>
973}
974"#,
975 r#"
976fn take_mutably(mut x: &i32) {}
977
978fn 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#"
623use a; 623use a;
624use a::{c, d::e}; 624use a::{c, d::e};
625
626mod 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
22struct Foo { 22struct 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)]
52struct FooCopy { 52struct 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)]
14pub struct ProjectJson { 14pub 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
36impl ProjectJson { 36impl 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 }
21itertools = "0.9.0" 21itertools = "0.9.0"
22jod-thread = "0.1.0" 22jod-thread = "0.1.0"
23log = "0.4.8" 23log = "0.4.8"
24lsp-types = { version = "0.80.0", features = ["proposed"] } 24lsp-types = { version = "0.82.0", features = ["proposed"] }
25parking_lot = "0.11.0" 25parking_lot = "0.11.0"
26pico-args = "0.3.1" 26pico-args = "0.3.1"
27oorandom = "11.1.2" 27oorandom = "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
757fn handle_fixes( 762fn 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(&params.text_document.uri) { 503 if let Ok(abs_path) = from_proto::abs_path(&params.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]
263fn test_format_document_unchanged() {
264 if skip_slow_tests() {
265 return;
266 }
267
268 let server = project(
269 r#"
270//- /Cargo.toml
271[package]
272name = "foo"
273version = "0.0.0"
274
275//- /src/lib.rs
276fn 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]
263fn test_missing_module_code_action() { 299fn 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
96impl 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
96impl ast::AssocItemList { 112impl 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
24pub fn assoc_item_list() -> ast::AssocItemList {
25 ast_from_text("impl C for D {};")
26}
27
24pub fn path_segment(name_ref: ast::NameRef) -> ast::PathSegment { 28pub 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
68pub fn glob_use_tree() -> ast::UseTree {
69 ast_from_text("use *;")
70}
71
64pub fn use_tree( 72pub 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
45You can install the latest release of the plugin from 45You can install the latest release of the plugin from
46https://marketplace.visualstudio.com/items?itemName=matklad.rust-analyzer[the marketplace]. 46https://marketplace.visualstudio.com/items?itemName=matklad.rust-analyzer[the marketplace].
47
48Note that the plugin may cause conflicts with the
49https://marketplace.visualstudio.com/items?itemName=rust-lang.rust[official Rust plugin]. It is
50recommended to disable the Rust plugin when using the rust-analyzer extension.
51
47By default, the plugin will prompt you to download the matching version of the server as well: 52By default, the plugin will prompt you to download the matching version of the server as well:
48 53
49image::https://user-images.githubusercontent.com/9021944/75067008-17502500-54ba-11ea-835a-f92aac50e866.png[] 54image::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`
61The server binary is stored in: 66The 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
374export function reloadWorkspace(ctx: Ctx): Cmd { 374export 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
378export function showReferences(ctx: Ctx): Cmd { 378export 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
5import * as lc from "vscode-languageclient"; 5import * as lc from "vscode-languageclient";
6 6
7export const analyzerStatus = new lc.RequestType<null, string, void>("rust-analyzer/analyzerStatus"); 7export const analyzerStatus = new lc.RequestType0<string, void>("rust-analyzer/analyzerStatus");
8export const memoryUsage = new lc.RequestType<null, string, void>("rust-analyzer/memoryUsage"); 8export const memoryUsage = new lc.RequestType0<string, void>("rust-analyzer/memoryUsage");
9 9
10export type Status = "loading" | "ready" | "invalid" | "needsReload"; 10export type Status = "loading" | "ready" | "invalid" | "needsReload";
11export interface StatusParams { 11export interface StatusParams {
@@ -13,7 +13,7 @@ export interface StatusParams {
13} 13}
14export const status = new lc.NotificationType<StatusParams>("rust-analyzer/status"); 14export const status = new lc.NotificationType<StatusParams>("rust-analyzer/status");
15 15
16export const reloadWorkspace = new lc.RequestType<null, null, void>("rust-analyzer/reloadWorkspace"); 16export const reloadWorkspace = new lc.RequestType0<null, void>("rust-analyzer/reloadWorkspace");
17 17
18export interface SyntaxTreeParams { 18export 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
348async 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
374async 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";
18const REPO = "rust-analyzer"; 18const REPO = "rust-analyzer";
19 19
20export async function fetchRelease( 20export 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
75export async function download(opts: DownloadOpts) { 82export 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}