aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock69
-rw-r--r--crates/assists/src/ast_transform.rs28
-rw-r--r--crates/assists/src/handlers/add_missing_impl_members.rs35
-rw-r--r--crates/assists/src/handlers/auto_import.rs33
-rw-r--r--crates/assists/src/utils/insert_use.rs10
-rw-r--r--crates/flycheck/src/lib.rs24
-rw-r--r--crates/hir/src/code_model.rs46
-rw-r--r--crates/hir/src/semantics.rs19
-rw-r--r--crates/hir_def/src/find_path.rs170
-rw-r--r--crates/hir_expand/src/name.rs10
-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/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/rust-analyzer/Cargo.toml2
-rw-r--r--crates/rust-analyzer/src/caps.rs14
-rw-r--r--crates/rust-analyzer/src/global_state.rs4
-rw-r--r--crates/rust-analyzer/src/main_loop.rs17
-rw-r--r--crates/rust-analyzer/src/reload.rs22
-rw-r--r--docs/user/manual.adoc1
-rw-r--r--editors/code/package.json9
-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
29 files changed, 664 insertions, 161 deletions
diff --git a/Cargo.lock b/Cargo.lock
index cf88c4e17..905401914 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",
@@ -162,9 +162,9 @@ checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
162 162
163[[package]] 163[[package]]
164name = "chalk-derive" 164name = "chalk-derive"
165version = "0.27.0" 165version = "0.28.0"
166source = "registry+https://github.com/rust-lang/crates.io-index" 166source = "registry+https://github.com/rust-lang/crates.io-index"
167checksum = "d5444ff2a211fe2a863e44d16a368c3d8a314d489de21b8eeb6879f14dd5d4a8" 167checksum = "8c85b013e2dc1b46ac4a279f54e62e55556a8c4d859f7b7c4e340a9b1d013640"
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.28.0"
178source = "registry+https://github.com/rust-lang/crates.io-index" 178source = "registry+https://github.com/rust-lang/crates.io-index"
179checksum = "e39c3db1dd4abfaa7658faaa62e5fe998a982a592b710bd971fad5b6adfcfdef" 179checksum = "2135d844688dc920e3ece3012c5d3d4f06e26986fe38bc041bc98f0e7a9f4e2b"
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.28.0"
188source = "registry+https://github.com/rust-lang/crates.io-index" 188source = "registry+https://github.com/rust-lang/crates.io-index"
189checksum = "3bfae328eff80ca54dcd0d731725bbb56136ac21c59261b68f1e5498e056b306" 189checksum = "cffdc1af3a413b7dd1e776cf81a863148b17e3c3f90f70d1fd216a68f816a622"
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.28.0"
201source = "registry+https://github.com/rust-lang/crates.io-index" 201source = "registry+https://github.com/rust-lang/crates.io-index"
202checksum = "a673abe3077adc25f8ee0894198aed494a5bb0ce50ee993900d0ee1a44e1948a" 202checksum = "bc69e4e94ffd4b39f1a865824b431bb82a7b4c8f14a0ba3d461cd86e56a590ac"
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]]
@@ -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.81.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 = "5e02724627e9ef8ba91f461ebc01d48aebbd13a4b7c9dc547a0a2890f53e2171" 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]
@@ -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.41" 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 = "6690e3e9f692504b941dc6c3b188fd28df054f7fb8469ab40680df52fdcc842b" 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 8df1d786b..1ac5fefd6 100644
--- a/crates/assists/src/handlers/add_missing_impl_members.rs
+++ b/crates/assists/src/handlers/add_missing_impl_members.rs
@@ -415,6 +415,41 @@ impl foo::Foo for S {
415 } 415 }
416 416
417 #[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]
418 fn test_qualify_path_generic() { 453 fn test_qualify_path_generic() {
419 check_assist( 454 check_assist(
420 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 b5eb2c722..ee7277c04 100644
--- a/crates/assists/src/handlers/auto_import.rs
+++ b/crates/assists/src/handlers/auto_import.rs
@@ -196,10 +196,10 @@ impl AutoImportAssets {
196 }) 196 })
197 .filter_map(|candidate| match candidate { 197 .filter_map(|candidate| match candidate {
198 Either::Left(module_def) => { 198 Either::Left(module_def) => {
199 self.module_with_name_to_import.find_use_path(db, module_def) 199 self.module_with_name_to_import.find_use_path_prefixed(db, module_def)
200 } 200 }
201 Either::Right(macro_def) => { 201 Either::Right(macro_def) => {
202 self.module_with_name_to_import.find_use_path(db, macro_def) 202 self.module_with_name_to_import.find_use_path_prefixed(db, macro_def)
203 } 203 }
204 }) 204 })
205 .filter(|use_path| !use_path.segments.is_empty()) 205 .filter(|use_path| !use_path.segments.is_empty())
@@ -291,6 +291,35 @@ mod tests {
291 use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target}; 291 use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
292 292
293 #[test] 293 #[test]
294 fn applicable_when_found_an_import_partial() {
295 check_assist(
296 auto_import,
297 r"
298 mod std {
299 pub mod fmt {
300 pub struct Formatter;
301 }
302 }
303
304 use std::fmt;
305
306 <|>Formatter
307 ",
308 r"
309 mod std {
310 pub mod fmt {
311 pub struct Formatter;
312 }
313 }
314
315 use std::fmt::{self, Formatter};
316
317 Formatter
318 ",
319 );
320 }
321
322 #[test]
294 fn applicable_when_found_an_import() { 323 fn applicable_when_found_an_import() {
295 check_assist( 324 check_assist(
296 auto_import, 325 auto_import,
diff --git a/crates/assists/src/utils/insert_use.rs b/crates/assists/src/utils/insert_use.rs
index 09f4a2224..5719b06af 100644
--- a/crates/assists/src/utils/insert_use.rs
+++ b/crates/assists/src/utils/insert_use.rs
@@ -810,16 +810,6 @@ use std::io;",
810 } 810 }
811 811
812 #[test] 812 #[test]
813 #[ignore] // FIXME: Support this
814 fn merge_partial_path() {
815 check_full(
816 "ast::Foo",
817 r"use syntax::{ast, algo};",
818 r"use syntax::{ast::{self, Foo}, algo};",
819 )
820 }
821
822 #[test]
823 fn merge_glob_nested() { 813 fn merge_glob_nested() {
824 check_full( 814 check_full(
825 "foo::bar::quux::Fez", 815 "foo::bar::quux::Fez",
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 849c8f6d0..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);
@@ -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/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_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_ty/Cargo.toml b/crates/hir_ty/Cargo.toml
index bc86df2b1..03215be44 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.28.0" }
21chalk-ir = { version = "0.27.0" } 21chalk-ir = { version = "0.28.0" }
22chalk-recursive = { version = "0.27.0" } 22chalk-recursive = { version = "0.28.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/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/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml
index 0fbb9cb0d..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.81.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/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/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/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 b070087a4..de0dbcad4 100644
--- a/crates/rust-analyzer/src/reload.rs
+++ b/crates/rust-analyzer/src/reload.rs
@@ -235,29 +235,37 @@ impl GlobalState {
235 let config = match self.config.flycheck.clone() { 235 let config = match self.config.flycheck.clone() {
236 Some(it) => it, 236 Some(it) => it,
237 None => { 237 None => {
238 self.flycheck = None; 238 self.flycheck = Vec::new();
239 return; 239 return;
240 } 240 }
241 }; 241 };
242 242
243 let sender = self.flycheck_sender.clone(); 243 let sender = self.flycheck_sender.clone();
244 let sender = Box::new(move |msg| sender.send(msg).unwrap());
245 self.flycheck = self 244 self.flycheck = self
246 .workspaces 245 .workspaces
247 .iter() 246 .iter()
248 // FIXME: Figure out the multi-workspace situation 247 .enumerate()
249 .find_map(|w| match w { 248 .filter_map(|(id, w)| match w {
250 ProjectWorkspace::Cargo { cargo, sysroot: _ } => Some(cargo.workspace_root()), 249 ProjectWorkspace::Cargo { cargo, sysroot: _ } => Some((id, cargo.workspace_root())),
251 ProjectWorkspace::Json { project, .. } => { 250 ProjectWorkspace::Json { project, .. } => {
252 // 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
253 // in the workspace configuration. 252 // in the workspace configuration.
254 match config { 253 match config {
255 FlycheckConfig::CustomCommand { .. } => Some(project.path()), 254 FlycheckConfig::CustomCommand { .. } => Some((id, project.path())),
256 _ => None, 255 _ => None,
257 } 256 }
258 } 257 }
259 }) 258 })
260 .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();
261 } 269 }
262} 270}
263 271
diff --git a/docs/user/manual.adoc b/docs/user/manual.adoc
index d3e6b23ae..7d85b36cb 100644
--- a/docs/user/manual.adoc
+++ b/docs/user/manual.adoc
@@ -66,6 +66,7 @@ To disable this notification put the following to `settings.json`
66The server binary is stored in: 66The server binary is stored in:
67 67
68* 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`
69* macOS: `~/Library/Application\ Support/Code/User/globalStorage/matklad.rust-analyzer` 70* macOS: `~/Library/Application\ Support/Code/User/globalStorage/matklad.rust-analyzer`
70* Windows: `%APPDATA%\Code\User\globalStorage\matklad.rust-analyzer` 71* Windows: `%APPDATA%\Code\User\globalStorage\matklad.rust-analyzer`
71 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/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}