aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/ISSUE_TEMPLATE/critical_nightly_regression.md17
-rw-r--r--Cargo.lock36
-rw-r--r--crates/flycheck/Cargo.toml1
-rw-r--r--crates/flycheck/src/lib.rs68
-rw-r--r--crates/hir_def/src/body/tests.rs8
-rw-r--r--crates/hir_def/src/body/tests/block.rs24
-rw-r--r--crates/hir_def/src/item_tree.rs7
-rw-r--r--crates/hir_def/src/nameres.rs14
-rw-r--r--crates/hir_ty/Cargo.toml6
-rw-r--r--crates/ide/src/prime_caches.rs3
-rw-r--r--crates/ide_assists/src/handlers/fill_match_arms.rs222
-rw-r--r--crates/ide_assists/src/handlers/inline_local_variable.rs223
-rw-r--r--crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs73
-rw-r--r--crates/parser/src/grammar/patterns.rs6
-rw-r--r--crates/project_model/src/build_data.rs134
-rw-r--r--crates/rust-analyzer/src/config.rs9
-rw-r--r--crates/rust-analyzer/src/diagnostics.rs1
-rw-r--r--crates/rust-analyzer/src/diagnostics/to_proto.rs56
-rw-r--r--crates/stdx/Cargo.toml5
-rw-r--r--crates/stdx/src/lib.rs27
-rw-r--r--crates/stdx/src/process.rs238
-rw-r--r--crates/syntax/Cargo.toml2
-rw-r--r--crates/syntax/src/ast/make.rs8
-rw-r--r--crates/syntax/test_data/parser/inline/ok/0145_record_pat_field.rast58
-rw-r--r--crates/syntax/test_data/parser/inline/ok/0145_record_pat_field.rs1
-rw-r--r--docs/dev/README.md4
-rw-r--r--docs/user/generated_config.adoc6
-rw-r--r--docs/user/manual.adoc74
-rw-r--r--editors/code/package.json7
29 files changed, 1082 insertions, 256 deletions
diff --git a/.github/ISSUE_TEMPLATE/critical_nightly_regression.md b/.github/ISSUE_TEMPLATE/critical_nightly_regression.md
new file mode 100644
index 000000000..a0b1627d7
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/critical_nightly_regression.md
@@ -0,0 +1,17 @@
1---
2name: Critical Nightly Regression
3about: You are using nightly rust-analyzer and the latest version is unusable.
4title: ''
5labels: ''
6assignees: 'matklad'
7
8---
9
10<!--
11Troubleshooting guide: https://rust-analyzer.github.io/manual.html#troubleshooting
12
13Please try to provide information which will help us to fix the issue faster. Minimal reproducible examples with few dependencies are especially lovely <3.
14-->
15
16This is a serious regression in nightly and it's important to fix it before the next release.
17@matklad, please take a look.
diff --git a/Cargo.lock b/Cargo.lock
index 1bb66c66e..98141f5cd 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -72,9 +72,9 @@ checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
72 72
73[[package]] 73[[package]]
74name = "backtrace" 74name = "backtrace"
75version = "0.3.56" 75version = "0.3.57"
76source = "registry+https://github.com/rust-lang/crates.io-index" 76source = "registry+https://github.com/rust-lang/crates.io-index"
77checksum = "9d117600f438b1707d4e4ae15d3595657288f8235a0eb593e80ecc98ab34e1bc" 77checksum = "78ed203b9ba68b242c62b3fb7480f589dd49829be1edb3fe8fc8b4ffda2dcb8d"
78dependencies = [ 78dependencies = [
79 "addr2line", 79 "addr2line",
80 "cfg-if", 80 "cfg-if",
@@ -168,9 +168,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
168 168
169[[package]] 169[[package]]
170name = "chalk-derive" 170name = "chalk-derive"
171version = "0.60.0" 171version = "0.64.0"
172source = "registry+https://github.com/rust-lang/crates.io-index" 172source = "registry+https://github.com/rust-lang/crates.io-index"
173checksum = "ab0f74445d4fbeaf0217bc1d23978cc73b95b28e8a738b81894580dd646822d2" 173checksum = "d9acf2a9eab79ae7d44cd77ad86a8b1569d7a5e6d9a7db4a0a57a7344dd82c24"
174dependencies = [ 174dependencies = [
175 "proc-macro2", 175 "proc-macro2",
176 "quote", 176 "quote",
@@ -180,9 +180,9 @@ dependencies = [
180 180
181[[package]] 181[[package]]
182name = "chalk-ir" 182name = "chalk-ir"
183version = "0.60.0" 183version = "0.64.0"
184source = "registry+https://github.com/rust-lang/crates.io-index" 184source = "registry+https://github.com/rust-lang/crates.io-index"
185checksum = "294b1fc6210a5b3bd06c1d01dda48a581e2cafec80b8d659139ce45456644be2" 185checksum = "877661627f54ba3666a72943c43b326cb170d60899e50a8426111e7a657ff032"
186dependencies = [ 186dependencies = [
187 "bitflags", 187 "bitflags",
188 "chalk-derive", 188 "chalk-derive",
@@ -191,9 +191,9 @@ dependencies = [
191 191
192[[package]] 192[[package]]
193name = "chalk-recursive" 193name = "chalk-recursive"
194version = "0.60.0" 194version = "0.64.0"
195source = "registry+https://github.com/rust-lang/crates.io-index" 195source = "registry+https://github.com/rust-lang/crates.io-index"
196checksum = "1b9386936070be4545bfa22b094b7065af79aa2aeaccc945438f1c5ffe74c30a" 196checksum = "072ffcf17243c2aa3e4b9ea6de3d29e7ef64cfdb0ceccaa431965070a1dc1475"
197dependencies = [ 197dependencies = [
198 "chalk-derive", 198 "chalk-derive",
199 "chalk-ir", 199 "chalk-ir",
@@ -204,9 +204,9 @@ dependencies = [
204 204
205[[package]] 205[[package]]
206name = "chalk-solve" 206name = "chalk-solve"
207version = "0.60.0" 207version = "0.64.0"
208source = "registry+https://github.com/rust-lang/crates.io-index" 208source = "registry+https://github.com/rust-lang/crates.io-index"
209checksum = "7c12a1ec7e850b50a049f27ef9cf5df3056bbd1acbb3eeb44d024e501a641f3a" 209checksum = "97d4920c9ef2b26dd0b98ffdf070e27fa31e0b6f637463132083cee597e3d326"
210dependencies = [ 210dependencies = [
211 "chalk-derive", 211 "chalk-derive",
212 "chalk-ir", 212 "chalk-ir",
@@ -396,6 +396,7 @@ dependencies = [
396 "crossbeam-channel", 396 "crossbeam-channel",
397 "jod-thread", 397 "jod-thread",
398 "log", 398 "log",
399 "serde",
399 "serde_json", 400 "serde_json",
400 "stdx", 401 "stdx",
401 "toolchain", 402 "toolchain",
@@ -684,9 +685,9 @@ dependencies = [
684 685
685[[package]] 686[[package]]
686name = "idna" 687name = "idna"
687version = "0.2.2" 688version = "0.2.3"
688source = "registry+https://github.com/rust-lang/crates.io-index" 689source = "registry+https://github.com/rust-lang/crates.io-index"
689checksum = "89829a5d69c23d348314a7ac337fe39173b61149a9864deabd260983aed48c21" 690checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8"
690dependencies = [ 691dependencies = [
691 "matches", 692 "matches",
692 "unicode-bidi", 693 "unicode-bidi",
@@ -1292,9 +1293,9 @@ dependencies = [
1292 1293
1293[[package]] 1294[[package]]
1294name = "redox_syscall" 1295name = "redox_syscall"
1295version = "0.2.5" 1296version = "0.2.6"
1296source = "registry+https://github.com/rust-lang/crates.io-index" 1297source = "registry+https://github.com/rust-lang/crates.io-index"
1297checksum = "94341e4e44e24f6b591b59e47a8a027df12e008d73fd5672dbea9cc22f4507d9" 1298checksum = "8270314b5ccceb518e7e578952f0b72b88222d02e8f77f5ecf7abbb673539041"
1298dependencies = [ 1299dependencies = [
1299 "bitflags", 1300 "bitflags",
1300] 1301]
@@ -1391,9 +1392,9 @@ dependencies = [
1391 1392
1392[[package]] 1393[[package]]
1393name = "rustc-ap-rustc_lexer" 1394name = "rustc-ap-rustc_lexer"
1394version = "714.0.0" 1395version = "716.0.0"
1395source = "registry+https://github.com/rust-lang/crates.io-index" 1396source = "registry+https://github.com/rust-lang/crates.io-index"
1396checksum = "a35856f140bed0dc7c7d6ba2134099d337377a3a4e11bfc79bccabf1fd4c9d42" 1397checksum = "12eac7554c1d3f49f105f14d53c0f3402220e875983113562701d8e597a0995c"
1397dependencies = [ 1398dependencies = [
1398 "unicode-xid", 1399 "unicode-xid",
1399] 1400]
@@ -1573,6 +1574,9 @@ version = "0.0.0"
1573dependencies = [ 1574dependencies = [
1574 "always-assert", 1575 "always-assert",
1575 "backtrace", 1576 "backtrace",
1577 "libc",
1578 "miow",
1579 "winapi",
1576] 1580]
1577 1581
1578[[package]] 1582[[package]]
diff --git a/crates/flycheck/Cargo.toml b/crates/flycheck/Cargo.toml
index 2a1a21b28..18b9ce7df 100644
--- a/crates/flycheck/Cargo.toml
+++ b/crates/flycheck/Cargo.toml
@@ -13,6 +13,7 @@ doctest = false
13crossbeam-channel = "0.5.0" 13crossbeam-channel = "0.5.0"
14log = "0.4.8" 14log = "0.4.8"
15cargo_metadata = "0.13" 15cargo_metadata = "0.13"
16serde = { version = "1.0.106", features = ["derive"] }
16serde_json = "1.0.48" 17serde_json = "1.0.48"
17jod-thread = "0.1.1" 18jod-thread = "0.1.1"
18 19
diff --git a/crates/flycheck/src/lib.rs b/crates/flycheck/src/lib.rs
index e2a59497a..1682d8bde 100644
--- a/crates/flycheck/src/lib.rs
+++ b/crates/flycheck/src/lib.rs
@@ -4,13 +4,14 @@
4 4
5use std::{ 5use std::{
6 fmt, 6 fmt,
7 io::{self, BufReader}, 7 io::{self, BufRead, BufReader},
8 path::PathBuf, 8 path::PathBuf,
9 process::{self, Command, Stdio}, 9 process::{self, Command, Stdio},
10 time::Duration, 10 time::Duration,
11}; 11};
12 12
13use crossbeam_channel::{never, select, unbounded, Receiver, Sender}; 13use crossbeam_channel::{never, select, unbounded, Receiver, Sender};
14use serde::Deserialize;
14use stdx::JodChild; 15use stdx::JodChild;
15 16
16pub use cargo_metadata::diagnostic::{ 17pub use cargo_metadata::diagnostic::{
@@ -128,7 +129,7 @@ struct FlycheckActor {
128 129
129enum Event { 130enum Event {
130 Restart(Restart), 131 Restart(Restart),
131 CheckEvent(Option<cargo_metadata::Message>), 132 CheckEvent(Option<CargoMessage>),
132} 133}
133 134
134impl FlycheckActor { 135impl FlycheckActor {
@@ -180,21 +181,16 @@ impl FlycheckActor {
180 self.progress(Progress::DidFinish(res)); 181 self.progress(Progress::DidFinish(res));
181 } 182 }
182 Event::CheckEvent(Some(message)) => match message { 183 Event::CheckEvent(Some(message)) => match message {
183 cargo_metadata::Message::CompilerArtifact(msg) => { 184 CargoMessage::CompilerArtifact(msg) => {
184 self.progress(Progress::DidCheckCrate(msg.target.name)); 185 self.progress(Progress::DidCheckCrate(msg.target.name));
185 } 186 }
186 187
187 cargo_metadata::Message::CompilerMessage(msg) => { 188 CargoMessage::Diagnostic(msg) => {
188 self.send(Message::AddDiagnostic { 189 self.send(Message::AddDiagnostic {
189 workspace_root: self.workspace_root.clone(), 190 workspace_root: self.workspace_root.clone(),
190 diagnostic: msg.message, 191 diagnostic: msg,
191 }); 192 });
192 } 193 }
193
194 cargo_metadata::Message::BuildScriptExecuted(_)
195 | cargo_metadata::Message::BuildFinished(_)
196 | cargo_metadata::Message::TextLine(_)
197 | _ => {}
198 }, 194 },
199 } 195 }
200 } 196 }
@@ -261,7 +257,7 @@ struct CargoHandle {
261 child: JodChild, 257 child: JodChild,
262 #[allow(unused)] 258 #[allow(unused)]
263 thread: jod_thread::JoinHandle<io::Result<bool>>, 259 thread: jod_thread::JoinHandle<io::Result<bool>>,
264 receiver: Receiver<cargo_metadata::Message>, 260 receiver: Receiver<CargoMessage>,
265} 261}
266 262
267impl CargoHandle { 263impl CargoHandle {
@@ -294,14 +290,11 @@ impl CargoHandle {
294 290
295struct CargoActor { 291struct CargoActor {
296 child_stdout: process::ChildStdout, 292 child_stdout: process::ChildStdout,
297 sender: Sender<cargo_metadata::Message>, 293 sender: Sender<CargoMessage>,
298} 294}
299 295
300impl CargoActor { 296impl CargoActor {
301 fn new( 297 fn new(child_stdout: process::ChildStdout, sender: Sender<CargoMessage>) -> CargoActor {
302 child_stdout: process::ChildStdout,
303 sender: Sender<cargo_metadata::Message>,
304 ) -> CargoActor {
305 CargoActor { child_stdout, sender } 298 CargoActor { child_stdout, sender }
306 } 299 }
307 fn run(self) -> io::Result<bool> { 300 fn run(self) -> io::Result<bool> {
@@ -315,7 +308,7 @@ impl CargoActor {
315 // erroneus output. 308 // erroneus output.
316 let stdout = BufReader::new(self.child_stdout); 309 let stdout = BufReader::new(self.child_stdout);
317 let mut read_at_least_one_message = false; 310 let mut read_at_least_one_message = false;
318 for message in cargo_metadata::Message::parse_stream(stdout) { 311 for message in stdout.lines() {
319 let message = match message { 312 let message = match message {
320 Ok(message) => message, 313 Ok(message) => message,
321 Err(err) => { 314 Err(err) => {
@@ -326,13 +319,44 @@ impl CargoActor {
326 319
327 read_at_least_one_message = true; 320 read_at_least_one_message = true;
328 321
329 // Skip certain kinds of messages to only spend time on what's useful 322 // Try to deserialize a message from Cargo or Rustc.
330 match &message { 323 let mut deserializer = serde_json::Deserializer::from_str(&message);
331 cargo_metadata::Message::CompilerArtifact(artifact) if artifact.fresh => (), 324 deserializer.disable_recursion_limit();
332 cargo_metadata::Message::BuildScriptExecuted(_) => (), 325 if let Ok(message) = JsonMessage::deserialize(&mut deserializer) {
333 _ => self.sender.send(message).unwrap(), 326 match message {
327 // Skip certain kinds of messages to only spend time on what's useful
328 JsonMessage::Cargo(message) => match message {
329 cargo_metadata::Message::CompilerArtifact(artifact) if !artifact.fresh => {
330 self.sender.send(CargoMessage::CompilerArtifact(artifact)).unwrap()
331 }
332 cargo_metadata::Message::CompilerMessage(msg) => {
333 self.sender.send(CargoMessage::Diagnostic(msg.message)).unwrap()
334 }
335
336 cargo_metadata::Message::CompilerArtifact(_)
337 | cargo_metadata::Message::BuildScriptExecuted(_)
338 | cargo_metadata::Message::BuildFinished(_)
339 | cargo_metadata::Message::TextLine(_)
340 | _ => (),
341 },
342 JsonMessage::Rustc(message) => {
343 self.sender.send(CargoMessage::Diagnostic(message)).unwrap()
344 }
345 }
334 } 346 }
335 } 347 }
336 Ok(read_at_least_one_message) 348 Ok(read_at_least_one_message)
337 } 349 }
338} 350}
351
352enum CargoMessage {
353 CompilerArtifact(cargo_metadata::Artifact),
354 Diagnostic(Diagnostic),
355}
356
357#[derive(Deserialize)]
358#[serde(untagged)]
359enum JsonMessage {
360 Cargo(cargo_metadata::Message),
361 Rustc(Diagnostic),
362}
diff --git a/crates/hir_def/src/body/tests.rs b/crates/hir_def/src/body/tests.rs
index 63f5fe88d..3e8f16306 100644
--- a/crates/hir_def/src/body/tests.rs
+++ b/crates/hir_def/src/body/tests.rs
@@ -40,6 +40,14 @@ fn block_def_map_at(ra_fixture: &str) -> String {
40 module.def_map(&db).dump(&db) 40 module.def_map(&db).dump(&db)
41} 41}
42 42
43fn check_block_scopes_at(ra_fixture: &str, expect: Expect) {
44 let (db, position) = crate::test_db::TestDB::with_position(ra_fixture);
45
46 let module = db.module_at_position(position);
47 let actual = module.def_map(&db).dump_block_scopes(&db);
48 expect.assert_eq(&actual);
49}
50
43fn check_at(ra_fixture: &str, expect: Expect) { 51fn check_at(ra_fixture: &str, expect: Expect) {
44 let actual = block_def_map_at(ra_fixture); 52 let actual = block_def_map_at(ra_fixture);
45 expect.assert_eq(&actual); 53 expect.assert_eq(&actual);
diff --git a/crates/hir_def/src/body/tests/block.rs b/crates/hir_def/src/body/tests/block.rs
index 3b6ba4cde..bc3d0f138 100644
--- a/crates/hir_def/src/body/tests/block.rs
+++ b/crates/hir_def/src/body/tests/block.rs
@@ -134,6 +134,30 @@ struct Struct {}
134} 134}
135 135
136#[test] 136#[test]
137fn nested_module_scoping() {
138 check_block_scopes_at(
139 r#"
140fn f() {
141 mod module {
142 struct Struct {}
143 fn f() {
144 use self::Struct;
145 $0
146 }
147 }
148}
149 "#,
150 expect![[r#"
151 BlockId(1) in ModuleId { krate: CrateId(0), block: Some(BlockId(0)), local_id: Idx::<ModuleData>(0) }
152 BlockId(0) in ModuleId { krate: CrateId(0), block: None, local_id: Idx::<ModuleData>(0) }
153 crate scope
154 "#]],
155 );
156 // FIXME: The module nesting here is wrong!
157 // The first block map should be located in module #1 (`mod module`), not #0 (BlockId(0) root module)
158}
159
160#[test]
137fn legacy_macro_items() { 161fn legacy_macro_items() {
138 // Checks that legacy-scoped `macro_rules!` from parent namespaces are resolved and expanded 162 // Checks that legacy-scoped `macro_rules!` from parent namespaces are resolved and expanded
139 // correctly. 163 // correctly.
diff --git a/crates/hir_def/src/item_tree.rs b/crates/hir_def/src/item_tree.rs
index 16a94a058..eaeca01bd 100644
--- a/crates/hir_def/src/item_tree.rs
+++ b/crates/hir_def/src/item_tree.rs
@@ -196,13 +196,6 @@ impl ItemTree {
196 self.raw_attrs(of).clone().filter(db, krate) 196 self.raw_attrs(of).clone().filter(db, krate)
197 } 197 }
198 198
199 pub fn all_inner_items(&self) -> impl Iterator<Item = ModItem> + '_ {
200 match &self.data {
201 Some(data) => Some(data.inner_items.values().flatten().copied()).into_iter().flatten(),
202 None => None.into_iter().flatten(),
203 }
204 }
205
206 pub fn inner_items_of_block(&self, block: FileAstId<ast::BlockExpr>) -> &[ModItem] { 199 pub fn inner_items_of_block(&self, block: FileAstId<ast::BlockExpr>) -> &[ModItem] {
207 match &self.data { 200 match &self.data {
208 Some(data) => data.inner_items.get(&block).map(|it| &**it).unwrap_or(&[]), 201 Some(data) => data.inner_items.get(&block).map(|it| &**it).unwrap_or(&[]),
diff --git a/crates/hir_def/src/nameres.rs b/crates/hir_def/src/nameres.rs
index 542f190a1..ba027c44a 100644
--- a/crates/hir_def/src/nameres.rs
+++ b/crates/hir_def/src/nameres.rs
@@ -410,6 +410,20 @@ impl DefMap {
410 } 410 }
411 } 411 }
412 412
413 pub fn dump_block_scopes(&self, db: &dyn DefDatabase) -> String {
414 let mut buf = String::new();
415 let mut arc;
416 let mut current_map = self;
417 while let Some(block) = &current_map.block {
418 format_to!(buf, "{:?} in {:?}\n", block.block, block.parent);
419 arc = block.parent.def_map(db);
420 current_map = &*arc;
421 }
422
423 format_to!(buf, "crate scope\n");
424 buf
425 }
426
413 fn shrink_to_fit(&mut self) { 427 fn shrink_to_fit(&mut self) {
414 // Exhaustive match to require handling new fields. 428 // Exhaustive match to require handling new fields.
415 let Self { 429 let Self {
diff --git a/crates/hir_ty/Cargo.toml b/crates/hir_ty/Cargo.toml
index abc0e7532..66b3418f2 100644
--- a/crates/hir_ty/Cargo.toml
+++ b/crates/hir_ty/Cargo.toml
@@ -18,9 +18,9 @@ ena = "0.14.0"
18log = "0.4.8" 18log = "0.4.8"
19rustc-hash = "1.1.0" 19rustc-hash = "1.1.0"
20scoped-tls = "1" 20scoped-tls = "1"
21chalk-solve = { version = "0.60", default-features = false } 21chalk-solve = { version = "0.64", default-features = false }
22chalk-ir = "0.60" 22chalk-ir = "0.64"
23chalk-recursive = "0.60" 23chalk-recursive = "0.64"
24la-arena = { version = "0.2.0", path = "../../lib/arena" } 24la-arena = { version = "0.2.0", path = "../../lib/arena" }
25 25
26stdx = { path = "../stdx", version = "0.0.0" } 26stdx = { path = "../stdx", version = "0.0.0" }
diff --git a/crates/ide/src/prime_caches.rs b/crates/ide/src/prime_caches.rs
index ea0acfaa0..03597f507 100644
--- a/crates/ide/src/prime_caches.rs
+++ b/crates/ide/src/prime_caches.rs
@@ -27,6 +27,7 @@ pub(crate) fn prime_caches(db: &RootDatabase, cb: &(dyn Fn(PrimeCachesProgress)
27 let topo = &graph.crates_in_topological_order(); 27 let topo = &graph.crates_in_topological_order();
28 28
29 cb(PrimeCachesProgress::Started); 29 cb(PrimeCachesProgress::Started);
30 let _d = stdx::defer(|| cb(PrimeCachesProgress::Finished));
30 31
31 // FIXME: This would be easy to parallelize, since it's in the ideal ordering for that. 32 // FIXME: This would be easy to parallelize, since it's in the ideal ordering for that.
32 // Unfortunately rayon prevents panics from propagation out of a `scope`, which breaks 33 // Unfortunately rayon prevents panics from propagation out of a `scope`, which breaks
@@ -41,6 +42,4 @@ pub(crate) fn prime_caches(db: &RootDatabase, cb: &(dyn Fn(PrimeCachesProgress)
41 }); 42 });
42 db.crate_def_map(*krate); 43 db.crate_def_map(*krate);
43 } 44 }
44
45 cb(PrimeCachesProgress::Finished);
46} 45}
diff --git a/crates/ide_assists/src/handlers/fill_match_arms.rs b/crates/ide_assists/src/handlers/fill_match_arms.rs
index a30c4d04e..be927cc1c 100644
--- a/crates/ide_assists/src/handlers/fill_match_arms.rs
+++ b/crates/ide_assists/src/handlers/fill_match_arms.rs
@@ -53,7 +53,7 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<
53 .iter() 53 .iter()
54 .filter_map(ast::MatchArm::pat) 54 .filter_map(ast::MatchArm::pat)
55 .flat_map(|pat| match pat { 55 .flat_map(|pat| match pat {
56 // Special casee OrPat as separate top-level pats 56 // Special case OrPat as separate top-level pats
57 Pat::OrPat(or_pat) => Either::Left(or_pat.pats()), 57 Pat::OrPat(or_pat) => Either::Left(or_pat.pats()),
58 _ => Either::Right(iter::once(pat)), 58 _ => Either::Right(iter::once(pat)),
59 }) 59 })
@@ -72,7 +72,11 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<
72 .filter(|variant_pat| is_variant_missing(&top_lvl_pats, variant_pat)) 72 .filter(|variant_pat| is_variant_missing(&top_lvl_pats, variant_pat))
73 .map(|pat| make::match_arm(iter::once(pat), make::expr_empty_block())) 73 .map(|pat| make::match_arm(iter::once(pat), make::expr_empty_block()))
74 .collect::<Vec<_>>(); 74 .collect::<Vec<_>>();
75 if Some(enum_def) == FamousDefs(&ctx.sema, Some(module.krate())).core_option_Option() { 75 if Some(enum_def)
76 == FamousDefs(&ctx.sema, Some(module.krate()))
77 .core_option_Option()
78 .map(|x| lift_enum(x))
79 {
76 // Match `Some` variant first. 80 // Match `Some` variant first.
77 cov_mark::hit!(option_order); 81 cov_mark::hit!(option_order);
78 variants.reverse() 82 variants.reverse()
@@ -151,49 +155,99 @@ fn does_pat_match_variant(pat: &Pat, var: &Pat) -> bool {
151 } 155 }
152} 156}
153 157
154fn resolve_enum_def(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<hir::Enum> { 158#[derive(Eq, PartialEq, Clone)]
159enum ExtendedEnum {
160 Bool,
161 Enum(hir::Enum),
162}
163
164#[derive(Eq, PartialEq, Clone)]
165enum ExtendedVariant {
166 True,
167 False,
168 Variant(hir::Variant),
169}
170
171fn lift_enum(e: hir::Enum) -> ExtendedEnum {
172 ExtendedEnum::Enum(e)
173}
174
175impl ExtendedEnum {
176 fn variants(&self, db: &RootDatabase) -> Vec<ExtendedVariant> {
177 match self {
178 ExtendedEnum::Enum(e) => {
179 e.variants(db).into_iter().map(|x| ExtendedVariant::Variant(x)).collect::<Vec<_>>()
180 }
181 ExtendedEnum::Bool => {
182 Vec::<ExtendedVariant>::from([ExtendedVariant::True, ExtendedVariant::False])
183 }
184 }
185 }
186}
187
188fn resolve_enum_def(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<ExtendedEnum> {
155 sema.type_of_expr(&expr)?.autoderef(sema.db).find_map(|ty| match ty.as_adt() { 189 sema.type_of_expr(&expr)?.autoderef(sema.db).find_map(|ty| match ty.as_adt() {
156 Some(Adt::Enum(e)) => Some(e), 190 Some(Adt::Enum(e)) => Some(ExtendedEnum::Enum(e)),
157 _ => None, 191 _ => {
192 if ty.is_bool() {
193 Some(ExtendedEnum::Bool)
194 } else {
195 None
196 }
197 }
158 }) 198 })
159} 199}
160 200
161fn resolve_tuple_of_enum_def( 201fn resolve_tuple_of_enum_def(
162 sema: &Semantics<RootDatabase>, 202 sema: &Semantics<RootDatabase>,
163 expr: &ast::Expr, 203 expr: &ast::Expr,
164) -> Option<Vec<hir::Enum>> { 204) -> Option<Vec<ExtendedEnum>> {
165 sema.type_of_expr(&expr)? 205 sema.type_of_expr(&expr)?
166 .tuple_fields(sema.db) 206 .tuple_fields(sema.db)
167 .iter() 207 .iter()
168 .map(|ty| { 208 .map(|ty| {
169 ty.autoderef(sema.db).find_map(|ty| match ty.as_adt() { 209 ty.autoderef(sema.db).find_map(|ty| match ty.as_adt() {
170 Some(Adt::Enum(e)) => Some(e), 210 Some(Adt::Enum(e)) => Some(lift_enum(e)),
171 // For now we only handle expansion for a tuple of enums. Here 211 // For now we only handle expansion for a tuple of enums. Here
172 // we map non-enum items to None and rely on `collect` to 212 // we map non-enum items to None and rely on `collect` to
173 // convert Vec<Option<hir::Enum>> into Option<Vec<hir::Enum>>. 213 // convert Vec<Option<hir::Enum>> into Option<Vec<hir::Enum>>.
174 _ => None, 214 _ => {
215 if ty.is_bool() {
216 Some(ExtendedEnum::Bool)
217 } else {
218 None
219 }
220 }
175 }) 221 })
176 }) 222 })
177 .collect() 223 .collect()
178} 224}
179 225
180fn build_pat(db: &RootDatabase, module: hir::Module, var: hir::Variant) -> Option<ast::Pat> { 226fn build_pat(db: &RootDatabase, module: hir::Module, var: ExtendedVariant) -> Option<ast::Pat> {
181 let path = mod_path_to_ast(&module.find_use_path(db, ModuleDef::from(var))?); 227 match var {
228 ExtendedVariant::Variant(var) => {
229 let path = mod_path_to_ast(&module.find_use_path(db, ModuleDef::from(var))?);
230
231 // FIXME: use HIR for this; it doesn't currently expose struct vs. tuple vs. unit variants though
232 let pat: ast::Pat = match var.source(db)?.value.kind() {
233 ast::StructKind::Tuple(field_list) => {
234 let pats =
235 iter::repeat(make::wildcard_pat().into()).take(field_list.fields().count());
236 make::tuple_struct_pat(path, pats).into()
237 }
238 ast::StructKind::Record(field_list) => {
239 let pats =
240 field_list.fields().map(|f| make::ident_pat(f.name().unwrap()).into());
241 make::record_pat(path, pats).into()
242 }
243 ast::StructKind::Unit => make::path_pat(path),
244 };
182 245
183 // FIXME: use HIR for this; it doesn't currently expose struct vs. tuple vs. unit variants though 246 Some(pat)
184 let pat: ast::Pat = match var.source(db)?.value.kind() {
185 ast::StructKind::Tuple(field_list) => {
186 let pats = iter::repeat(make::wildcard_pat().into()).take(field_list.fields().count());
187 make::tuple_struct_pat(path, pats).into()
188 }
189 ast::StructKind::Record(field_list) => {
190 let pats = field_list.fields().map(|f| make::ident_pat(f.name().unwrap()).into());
191 make::record_pat(path, pats).into()
192 } 247 }
193 ast::StructKind::Unit => make::path_pat(path), 248 ExtendedVariant::True => Some(ast::Pat::from(make::literal_pat("true"))),
194 }; 249 ExtendedVariant::False => Some(ast::Pat::from(make::literal_pat("false"))),
195 250 }
196 Some(pat)
197} 251}
198 252
199#[cfg(test)] 253#[cfg(test)]
@@ -226,6 +280,21 @@ mod tests {
226 } 280 }
227 281
228 #[test] 282 #[test]
283 fn all_boolean_match_arms_provided() {
284 check_assist_not_applicable(
285 fill_match_arms,
286 r#"
287 fn foo(a: bool) {
288 match a$0 {
289 true => {}
290 false => {}
291 }
292 }
293 "#,
294 )
295 }
296
297 #[test]
229 fn tuple_of_non_enum() { 298 fn tuple_of_non_enum() {
230 // for now this case is not handled, although it potentially could be 299 // for now this case is not handled, although it potentially could be
231 // in the future 300 // in the future
@@ -241,6 +310,113 @@ mod tests {
241 } 310 }
242 311
243 #[test] 312 #[test]
313 fn fill_match_arms_boolean() {
314 check_assist(
315 fill_match_arms,
316 r#"
317 fn foo(a: bool) {
318 match a$0 {
319 }
320 }
321 "#,
322 r#"
323 fn foo(a: bool) {
324 match a {
325 $0true => {}
326 false => {}
327 }
328 }
329 "#,
330 )
331 }
332
333 #[test]
334 fn partial_fill_boolean() {
335 check_assist(
336 fill_match_arms,
337 r#"
338 fn foo(a: bool) {
339 match a$0 {
340 true => {}
341 }
342 }
343 "#,
344 r#"
345 fn foo(a: bool) {
346 match a {
347 true => {}
348 $0false => {}
349 }
350 }
351 "#,
352 )
353 }
354
355 #[test]
356 fn all_boolean_tuple_arms_provided() {
357 check_assist_not_applicable(
358 fill_match_arms,
359 r#"
360 fn foo(a: bool) {
361 match (a, a)$0 {
362 (true, true) => {}
363 (true, false) => {}
364 (false, true) => {}
365 (false, false) => {}
366 }
367 }
368 "#,
369 )
370 }
371
372 #[test]
373 fn fill_boolean_tuple() {
374 check_assist(
375 fill_match_arms,
376 r#"
377 fn foo(a: bool) {
378 match (a, a)$0 {
379 }
380 }
381 "#,
382 r#"
383 fn foo(a: bool) {
384 match (a, a) {
385 $0(true, true) => {}
386 (true, false) => {}
387 (false, true) => {}
388 (false, false) => {}
389 }
390 }
391 "#,
392 )
393 }
394
395 #[test]
396 fn partial_fill_boolean_tuple() {
397 check_assist(
398 fill_match_arms,
399 r#"
400 fn foo(a: bool) {
401 match (a, a)$0 {
402 (false, true) => {}
403 }
404 }
405 "#,
406 r#"
407 fn foo(a: bool) {
408 match (a, a) {
409 (false, true) => {}
410 $0(true, true) => {}
411 (true, false) => {}
412 (false, false) => {}
413 }
414 }
415 "#,
416 )
417 }
418
419 #[test]
244 fn partial_fill_record_tuple() { 420 fn partial_fill_record_tuple() {
245 check_assist( 421 check_assist(
246 fill_match_arms, 422 fill_match_arms,
diff --git a/crates/ide_assists/src/handlers/inline_local_variable.rs b/crates/ide_assists/src/handlers/inline_local_variable.rs
index ea1466dc8..f5dafc8cb 100644
--- a/crates/ide_assists/src/handlers/inline_local_variable.rs
+++ b/crates/ide_assists/src/handlers/inline_local_variable.rs
@@ -1,7 +1,9 @@
1use ide_db::{defs::Definition, search::FileReference}; 1use either::Either;
2use hir::PathResolution;
3use ide_db::{base_db::FileId, defs::Definition, search::FileReference};
2use rustc_hash::FxHashMap; 4use rustc_hash::FxHashMap;
3use syntax::{ 5use syntax::{
4 ast::{self, AstNode, AstToken}, 6 ast::{self, AstNode, AstToken, NameOwner},
5 TextRange, 7 TextRange,
6}; 8};
7 9
@@ -27,44 +29,28 @@ use crate::{
27// } 29// }
28// ``` 30// ```
29pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { 31pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
30 let let_stmt = ctx.find_node_at_offset::<ast::LetStmt>()?; 32 let InlineData { let_stmt, delete_let, replace_usages, target } =
31 let bind_pat = match let_stmt.pat()? { 33 inline_let(ctx).or_else(|| inline_usage(ctx))?;
32 ast::Pat::IdentPat(pat) => pat,
33 _ => return None,
34 };
35 if bind_pat.mut_token().is_some() {
36 cov_mark::hit!(test_not_inline_mut_variable);
37 return None;
38 }
39 if !bind_pat.syntax().text_range().contains_inclusive(ctx.offset()) {
40 cov_mark::hit!(not_applicable_outside_of_bind_pat);
41 return None;
42 }
43 let initializer_expr = let_stmt.initializer()?; 34 let initializer_expr = let_stmt.initializer()?;
44 35
45 let def = ctx.sema.to_def(&bind_pat)?; 36 let delete_range = if delete_let {
46 let def = Definition::Local(def); 37 if let Some(whitespace) = let_stmt
47 let usages = def.usages(&ctx.sema).all(); 38 .syntax()
48 if usages.is_empty() { 39 .next_sibling_or_token()
49 cov_mark::hit!(test_not_applicable_if_variable_unused); 40 .and_then(|it| ast::Whitespace::cast(it.as_token()?.clone()))
50 return None; 41 {
51 }; 42 Some(TextRange::new(
52 43 let_stmt.syntax().text_range().start(),
53 let delete_range = if let Some(whitespace) = let_stmt 44 whitespace.syntax().text_range().end(),
54 .syntax() 45 ))
55 .next_sibling_or_token() 46 } else {
56 .and_then(|it| ast::Whitespace::cast(it.as_token()?.clone())) 47 Some(let_stmt.syntax().text_range())
57 { 48 }
58 TextRange::new(
59 let_stmt.syntax().text_range().start(),
60 whitespace.syntax().text_range().end(),
61 )
62 } else { 49 } else {
63 let_stmt.syntax().text_range() 50 None
64 }; 51 };
65 52
66 let wrap_in_parens = usages 53 let wrap_in_parens = replace_usages
67 .references
68 .iter() 54 .iter()
69 .map(|(&file_id, refs)| { 55 .map(|(&file_id, refs)| {
70 refs.iter() 56 refs.iter()
@@ -114,14 +100,20 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> O
114 let init_str = initializer_expr.syntax().text().to_string(); 100 let init_str = initializer_expr.syntax().text().to_string();
115 let init_in_paren = format!("({})", &init_str); 101 let init_in_paren = format!("({})", &init_str);
116 102
117 let target = bind_pat.syntax().text_range(); 103 let target = match target {
104 ast::NameOrNameRef::Name(it) => it.syntax().text_range(),
105 ast::NameOrNameRef::NameRef(it) => it.syntax().text_range(),
106 };
107
118 acc.add( 108 acc.add(
119 AssistId("inline_local_variable", AssistKind::RefactorInline), 109 AssistId("inline_local_variable", AssistKind::RefactorInline),
120 "Inline variable", 110 "Inline variable",
121 target, 111 target,
122 move |builder| { 112 move |builder| {
123 builder.delete(delete_range); 113 if let Some(range) = delete_range {
124 for (file_id, references) in usages.references { 114 builder.delete(range);
115 }
116 for (file_id, references) in replace_usages {
125 for (&should_wrap, reference) in wrap_in_parens[&file_id].iter().zip(references) { 117 for (&should_wrap, reference) in wrap_in_parens[&file_id].iter().zip(references) {
126 let replacement = 118 let replacement =
127 if should_wrap { init_in_paren.clone() } else { init_str.clone() }; 119 if should_wrap { init_in_paren.clone() } else { init_str.clone() };
@@ -140,6 +132,81 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> O
140 ) 132 )
141} 133}
142 134
135struct InlineData {
136 let_stmt: ast::LetStmt,
137 delete_let: bool,
138 target: ast::NameOrNameRef,
139 replace_usages: FxHashMap<FileId, Vec<FileReference>>,
140}
141
142fn inline_let(ctx: &AssistContext) -> Option<InlineData> {
143 let let_stmt = ctx.find_node_at_offset::<ast::LetStmt>()?;
144 let bind_pat = match let_stmt.pat()? {
145 ast::Pat::IdentPat(pat) => pat,
146 _ => return None,
147 };
148 if bind_pat.mut_token().is_some() {
149 cov_mark::hit!(test_not_inline_mut_variable);
150 return None;
151 }
152 if !bind_pat.syntax().text_range().contains_inclusive(ctx.offset()) {
153 cov_mark::hit!(not_applicable_outside_of_bind_pat);
154 return None;
155 }
156
157 let def = ctx.sema.to_def(&bind_pat)?;
158 let def = Definition::Local(def);
159 let usages = def.usages(&ctx.sema).all();
160 if usages.is_empty() {
161 cov_mark::hit!(test_not_applicable_if_variable_unused);
162 return None;
163 };
164
165 Some(InlineData {
166 let_stmt,
167 delete_let: true,
168 target: ast::NameOrNameRef::Name(bind_pat.name()?),
169 replace_usages: usages.references,
170 })
171}
172
173fn inline_usage(ctx: &AssistContext) -> Option<InlineData> {
174 let path_expr = ctx.find_node_at_offset::<ast::PathExpr>()?;
175 let path = path_expr.path()?;
176 let name = match path.as_single_segment()?.kind()? {
177 ast::PathSegmentKind::Name(name) => name,
178 _ => return None,
179 };
180
181 let local = match ctx.sema.resolve_path(&path)? {
182 PathResolution::Local(local) => local,
183 _ => return None,
184 };
185
186 let bind_pat = match local.source(ctx.db()).value {
187 Either::Left(ident) => ident,
188 _ => return None,
189 };
190
191 let let_stmt = ast::LetStmt::cast(bind_pat.syntax().parent()?)?;
192
193 let def = Definition::Local(local);
194 let mut usages = def.usages(&ctx.sema).all();
195
196 let delete_let = usages.references.values().map(|v| v.len()).sum::<usize>() == 1;
197
198 for references in usages.references.values_mut() {
199 references.retain(|reference| reference.name.as_name_ref() == Some(&name));
200 }
201
202 Some(InlineData {
203 let_stmt,
204 delete_let,
205 target: ast::NameOrNameRef::NameRef(name),
206 replace_usages: usages.references,
207 })
208}
209
143#[cfg(test)] 210#[cfg(test)]
144mod tests { 211mod tests {
145 use crate::tests::{check_assist, check_assist_not_applicable}; 212 use crate::tests::{check_assist, check_assist_not_applicable};
@@ -726,4 +793,84 @@ fn main() {
726", 793",
727 ) 794 )
728 } 795 }
796
797 #[test]
798 fn works_on_local_usage() {
799 check_assist(
800 inline_local_variable,
801 r#"
802fn f() {
803 let xyz = 0;
804 xyz$0;
805}
806"#,
807 r#"
808fn f() {
809 0;
810}
811"#,
812 );
813 }
814
815 #[test]
816 fn does_not_remove_let_when_multiple_usages() {
817 check_assist(
818 inline_local_variable,
819 r#"
820fn f() {
821 let xyz = 0;
822 xyz$0;
823 xyz;
824}
825"#,
826 r#"
827fn f() {
828 let xyz = 0;
829 0;
830 xyz;
831}
832"#,
833 );
834 }
835
836 #[test]
837 fn not_applicable_with_non_ident_pattern() {
838 check_assist_not_applicable(
839 inline_local_variable,
840 r#"
841fn main() {
842 let (x, y) = (0, 1);
843 x$0;
844}
845"#,
846 );
847 }
848
849 #[test]
850 fn not_applicable_on_local_usage_in_macro() {
851 check_assist_not_applicable(
852 inline_local_variable,
853 r#"
854macro_rules! m {
855 ($i:ident) => { $i }
856}
857fn f() {
858 let xyz = 0;
859 m!(xyz$0); // replacing it would break the macro
860}
861"#,
862 );
863 check_assist_not_applicable(
864 inline_local_variable,
865 r#"
866macro_rules! m {
867 ($i:ident) => { $i }
868}
869fn f() {
870 let xyz$0 = 0;
871 m!(xyz); // replacing it would break the macro
872}
873"#,
874 );
875 }
729} 876}
diff --git a/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs b/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs
index 870a8d4ff..694d897d1 100644
--- a/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs
+++ b/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs
@@ -47,6 +47,11 @@ pub(crate) fn replace_derive_with_manual_impl(
47 return None; 47 return None;
48 } 48 }
49 49
50 if !args.syntax().text_range().contains(ctx.offset()) {
51 cov_mark::hit!(outside_of_attr_args);
52 return None;
53 }
54
50 let trait_token = args.syntax().token_at_offset(ctx.offset()).find(|t| t.kind() == IDENT)?; 55 let trait_token = args.syntax().token_at_offset(ctx.offset()).find(|t| t.kind() == IDENT)?;
51 let trait_name = trait_token.text(); 56 let trait_name = trait_token.text();
52 57
@@ -207,7 +212,7 @@ mod tests {
207 fn add_custom_impl_debug() { 212 fn add_custom_impl_debug() {
208 check_assist( 213 check_assist(
209 replace_derive_with_manual_impl, 214 replace_derive_with_manual_impl,
210 " 215 r#"
211mod fmt { 216mod fmt {
212 pub struct Error; 217 pub struct Error;
213 pub type Result = Result<(), Error>; 218 pub type Result = Result<(), Error>;
@@ -221,8 +226,8 @@ mod fmt {
221struct Foo { 226struct Foo {
222 bar: String, 227 bar: String,
223} 228}
224", 229"#,
225 " 230 r#"
226mod fmt { 231mod fmt {
227 pub struct Error; 232 pub struct Error;
228 pub type Result = Result<(), Error>; 233 pub type Result = Result<(), Error>;
@@ -241,14 +246,14 @@ impl fmt::Debug for Foo {
241 ${0:todo!()} 246 ${0:todo!()}
242 } 247 }
243} 248}
244", 249"#,
245 ) 250 )
246 } 251 }
247 #[test] 252 #[test]
248 fn add_custom_impl_all() { 253 fn add_custom_impl_all() {
249 check_assist( 254 check_assist(
250 replace_derive_with_manual_impl, 255 replace_derive_with_manual_impl,
251 " 256 r#"
252mod foo { 257mod foo {
253 pub trait Bar { 258 pub trait Bar {
254 type Qux; 259 type Qux;
@@ -263,8 +268,8 @@ mod foo {
263struct Foo { 268struct Foo {
264 bar: String, 269 bar: String,
265} 270}
266", 271"#,
267 " 272 r#"
268mod foo { 273mod foo {
269 pub trait Bar { 274 pub trait Bar {
270 type Qux; 275 type Qux;
@@ -290,20 +295,20 @@ impl foo::Bar for Foo {
290 todo!() 295 todo!()
291 } 296 }
292} 297}
293", 298"#,
294 ) 299 )
295 } 300 }
296 #[test] 301 #[test]
297 fn add_custom_impl_for_unique_input() { 302 fn add_custom_impl_for_unique_input() {
298 check_assist( 303 check_assist(
299 replace_derive_with_manual_impl, 304 replace_derive_with_manual_impl,
300 " 305 r#"
301#[derive(Debu$0g)] 306#[derive(Debu$0g)]
302struct Foo { 307struct Foo {
303 bar: String, 308 bar: String,
304} 309}
305 ", 310 "#,
306 " 311 r#"
307struct Foo { 312struct Foo {
308 bar: String, 313 bar: String,
309} 314}
@@ -311,7 +316,7 @@ struct Foo {
311impl Debug for Foo { 316impl Debug for Foo {
312 $0 317 $0
313} 318}
314 ", 319 "#,
315 ) 320 )
316 } 321 }
317 322
@@ -319,13 +324,13 @@ impl Debug for Foo {
319 fn add_custom_impl_for_with_visibility_modifier() { 324 fn add_custom_impl_for_with_visibility_modifier() {
320 check_assist( 325 check_assist(
321 replace_derive_with_manual_impl, 326 replace_derive_with_manual_impl,
322 " 327 r#"
323#[derive(Debug$0)] 328#[derive(Debug$0)]
324pub struct Foo { 329pub struct Foo {
325 bar: String, 330 bar: String,
326} 331}
327 ", 332 "#,
328 " 333 r#"
329pub struct Foo { 334pub struct Foo {
330 bar: String, 335 bar: String,
331} 336}
@@ -333,7 +338,7 @@ pub struct Foo {
333impl Debug for Foo { 338impl Debug for Foo {
334 $0 339 $0
335} 340}
336 ", 341 "#,
337 ) 342 )
338 } 343 }
339 344
@@ -341,18 +346,18 @@ impl Debug for Foo {
341 fn add_custom_impl_when_multiple_inputs() { 346 fn add_custom_impl_when_multiple_inputs() {
342 check_assist( 347 check_assist(
343 replace_derive_with_manual_impl, 348 replace_derive_with_manual_impl,
344 " 349 r#"
345#[derive(Display, Debug$0, Serialize)] 350#[derive(Display, Debug$0, Serialize)]
346struct Foo {} 351struct Foo {}
347 ", 352 "#,
348 " 353 r#"
349#[derive(Display, Serialize)] 354#[derive(Display, Serialize)]
350struct Foo {} 355struct Foo {}
351 356
352impl Debug for Foo { 357impl Debug for Foo {
353 $0 358 $0
354} 359}
355 ", 360 "#,
356 ) 361 )
357 } 362 }
358 363
@@ -360,10 +365,10 @@ impl Debug for Foo {
360 fn test_ignore_derive_macro_without_input() { 365 fn test_ignore_derive_macro_without_input() {
361 check_assist_not_applicable( 366 check_assist_not_applicable(
362 replace_derive_with_manual_impl, 367 replace_derive_with_manual_impl,
363 " 368 r#"
364#[derive($0)] 369#[derive($0)]
365struct Foo {} 370struct Foo {}
366 ", 371 "#,
367 ) 372 )
368 } 373 }
369 374
@@ -371,18 +376,18 @@ struct Foo {}
371 fn test_ignore_if_cursor_on_param() { 376 fn test_ignore_if_cursor_on_param() {
372 check_assist_not_applicable( 377 check_assist_not_applicable(
373 replace_derive_with_manual_impl, 378 replace_derive_with_manual_impl,
374 " 379 r#"
375#[derive$0(Debug)] 380#[derive$0(Debug)]
376struct Foo {} 381struct Foo {}
377 ", 382 "#,
378 ); 383 );
379 384
380 check_assist_not_applicable( 385 check_assist_not_applicable(
381 replace_derive_with_manual_impl, 386 replace_derive_with_manual_impl,
382 " 387 r#"
383#[derive(Debug)$0] 388#[derive(Debug)$0]
384struct Foo {} 389struct Foo {}
385 ", 390 "#,
386 ) 391 )
387 } 392 }
388 393
@@ -390,10 +395,22 @@ struct Foo {}
390 fn test_ignore_if_not_derive() { 395 fn test_ignore_if_not_derive() {
391 check_assist_not_applicable( 396 check_assist_not_applicable(
392 replace_derive_with_manual_impl, 397 replace_derive_with_manual_impl,
393 " 398 r#"
394#[allow(non_camel_$0case_types)] 399#[allow(non_camel_$0case_types)]
395struct Foo {} 400struct Foo {}
396 ", 401 "#,
397 ) 402 )
398 } 403 }
404
405 #[test]
406 fn works_at_start_of_file() {
407 cov_mark::check!(outside_of_attr_args);
408 check_assist_not_applicable(
409 replace_derive_with_manual_impl,
410 r#"
411$0#[derive(Debug)]
412struct S;
413 "#,
414 );
415 }
399} 416}
diff --git a/crates/parser/src/grammar/patterns.rs b/crates/parser/src/grammar/patterns.rs
index da71498a8..3ab347834 100644
--- a/crates/parser/src/grammar/patterns.rs
+++ b/crates/parser/src/grammar/patterns.rs
@@ -206,13 +206,15 @@ fn record_pat_field_list(p: &mut Parser) {
206 T![.] if p.at(T![..]) => p.bump(T![..]), 206 T![.] if p.at(T![..]) => p.bump(T![..]),
207 T!['{'] => error_block(p, "expected ident"), 207 T!['{'] => error_block(p, "expected ident"),
208 208
209 c => { 209 _ => {
210 let m = p.start(); 210 let m = p.start();
211 match c { 211 attributes::outer_attrs(p);
212 match p.current() {
212 // test record_pat_field 213 // test record_pat_field
213 // fn foo() { 214 // fn foo() {
214 // let S { 0: 1 } = (); 215 // let S { 0: 1 } = ();
215 // let S { x: 1 } = (); 216 // let S { x: 1 } = ();
217 // let S { #[cfg(any())] x: 1 } = ();
216 // } 218 // }
217 IDENT | INT_NUMBER if p.nth(1) == T![:] => { 219 IDENT | INT_NUMBER if p.nth(1) == T![:] => {
218 name_ref_or_index(p); 220 name_ref_or_index(p);
diff --git a/crates/project_model/src/build_data.rs b/crates/project_model/src/build_data.rs
index ab5cc8c49..faca336de 100644
--- a/crates/project_model/src/build_data.rs
+++ b/crates/project_model/src/build_data.rs
@@ -1,7 +1,6 @@
1//! Handles build script specific information 1//! Handles build script specific information
2 2
3use std::{ 3use std::{
4 io::BufReader,
5 path::PathBuf, 4 path::PathBuf,
6 process::{Command, Stdio}, 5 process::{Command, Stdio},
7 sync::Arc, 6 sync::Arc,
@@ -13,7 +12,8 @@ use cargo_metadata::{BuildScript, Message};
13use itertools::Itertools; 12use itertools::Itertools;
14use paths::{AbsPath, AbsPathBuf}; 13use paths::{AbsPath, AbsPathBuf};
15use rustc_hash::FxHashMap; 14use rustc_hash::FxHashMap;
16use stdx::{format_to, JodChild}; 15use serde::Deserialize;
16use stdx::format_to;
17 17
18use crate::{cfg_flag::CfgFlag, CargoConfig}; 18use crate::{cfg_flag::CfgFlag, CargoConfig};
19 19
@@ -171,67 +171,86 @@ impl WorkspaceBuildData {
171 171
172 cmd.stdout(Stdio::piped()).stderr(Stdio::piped()).stdin(Stdio::null()); 172 cmd.stdout(Stdio::piped()).stderr(Stdio::piped()).stdin(Stdio::null());
173 173
174 let mut child = cmd.spawn().map(JodChild)?;
175 let child_stdout = child.stdout.take().unwrap();
176 let stdout = BufReader::new(child_stdout);
177
178 let mut res = WorkspaceBuildData::default(); 174 let mut res = WorkspaceBuildData::default();
179 for message in cargo_metadata::Message::parse_stream(stdout).flatten() {
180 match message {
181 Message::BuildScriptExecuted(BuildScript {
182 package_id,
183 out_dir,
184 cfgs,
185 env,
186 ..
187 }) => {
188 let cfgs = {
189 let mut acc = Vec::new();
190 for cfg in cfgs {
191 match cfg.parse::<CfgFlag>() {
192 Ok(it) => acc.push(it),
193 Err(err) => {
194 anyhow::bail!("invalid cfg from cargo-metadata: {}", err)
195 }
196 };
197 }
198 acc
199 };
200 let package_build_data =
201 res.per_package.entry(package_id.repr.clone()).or_default();
202 // cargo_metadata crate returns default (empty) path for
203 // older cargos, which is not absolute, so work around that.
204 if !out_dir.as_str().is_empty() {
205 let out_dir = AbsPathBuf::assert(PathBuf::from(out_dir.into_os_string()));
206 package_build_data.out_dir = Some(out_dir);
207 package_build_data.cfgs = cfgs;
208 }
209 175
210 package_build_data.envs = env; 176 let mut callback_err = None;
177 let output = stdx::process::streaming_output(
178 cmd,
179 &mut |line| {
180 if callback_err.is_some() {
181 return;
211 } 182 }
212 Message::CompilerArtifact(message) => { 183
213 progress(format!("metadata {}", message.target.name)); 184 // Copy-pasted from existing cargo_metadata. It seems like we
214 185 // should be using sered_stacker here?
215 if message.target.kind.contains(&"proc-macro".to_string()) { 186 let mut deserializer = serde_json::Deserializer::from_str(&line);
216 let package_id = message.package_id; 187 deserializer.disable_recursion_limit();
217 // Skip rmeta file 188 let message = Message::deserialize(&mut deserializer)
218 if let Some(filename) = message.filenames.iter().find(|name| is_dylib(name)) 189 .unwrap_or(Message::TextLine(line.to_string()));
219 { 190
220 let filename = AbsPathBuf::assert(PathBuf::from(&filename)); 191 match message {
221 let package_build_data = 192 Message::BuildScriptExecuted(BuildScript {
222 res.per_package.entry(package_id.repr.clone()).or_default(); 193 package_id,
223 package_build_data.proc_macro_dylib_path = Some(filename); 194 out_dir,
195 cfgs,
196 env,
197 ..
198 }) => {
199 let cfgs = {
200 let mut acc = Vec::new();
201 for cfg in cfgs {
202 match cfg.parse::<CfgFlag>() {
203 Ok(it) => acc.push(it),
204 Err(err) => {
205 callback_err = Some(anyhow::format_err!(
206 "invalid cfg from cargo-metadata: {}",
207 err
208 ));
209 return;
210 }
211 };
212 }
213 acc
214 };
215 let package_build_data =
216 res.per_package.entry(package_id.repr.clone()).or_default();
217 // cargo_metadata crate returns default (empty) path for
218 // older cargos, which is not absolute, so work around that.
219 if !out_dir.as_str().is_empty() {
220 let out_dir =
221 AbsPathBuf::assert(PathBuf::from(out_dir.into_os_string()));
222 package_build_data.out_dir = Some(out_dir);
223 package_build_data.cfgs = cfgs;
224 } 224 }
225
226 package_build_data.envs = env;
225 } 227 }
228 Message::CompilerArtifact(message) => {
229 progress(format!("metadata {}", message.target.name));
230
231 if message.target.kind.contains(&"proc-macro".to_string()) {
232 let package_id = message.package_id;
233 // Skip rmeta file
234 if let Some(filename) =
235 message.filenames.iter().find(|name| is_dylib(name))
236 {
237 let filename = AbsPathBuf::assert(PathBuf::from(&filename));
238 let package_build_data =
239 res.per_package.entry(package_id.repr.clone()).or_default();
240 package_build_data.proc_macro_dylib_path = Some(filename);
241 }
242 }
243 }
244 Message::CompilerMessage(message) => {
245 progress(message.target.name.clone());
246 }
247 Message::BuildFinished(_) => {}
248 Message::TextLine(_) => {}
249 _ => {}
226 } 250 }
227 Message::CompilerMessage(message) => { 251 },
228 progress(message.target.name.clone()); 252 &mut |_| (),
229 } 253 )?;
230 Message::BuildFinished(_) => {}
231 Message::TextLine(_) => {}
232 _ => {}
233 }
234 }
235 254
236 for package in packages { 255 for package in packages {
237 let package_build_data = res.per_package.entry(package.id.repr.clone()).or_default(); 256 let package_build_data = res.per_package.entry(package.id.repr.clone()).or_default();
@@ -244,7 +263,6 @@ impl WorkspaceBuildData {
244 } 263 }
245 } 264 }
246 265
247 let output = child.into_inner().wait_with_output()?;
248 if !output.status.success() { 266 if !output.status.success() {
249 let mut stderr = String::from_utf8(output.stderr).unwrap_or_default(); 267 let mut stderr = String::from_utf8(output.stderr).unwrap_or_default();
250 if stderr.is_empty() { 268 if stderr.is_empty() {
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index 7ddea22c8..1109d2daf 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -17,7 +17,7 @@ use ide_db::helpers::{
17}; 17};
18use lsp_types::{ClientCapabilities, MarkupKind}; 18use lsp_types::{ClientCapabilities, MarkupKind};
19use project_model::{CargoConfig, ProjectJson, ProjectJsonData, ProjectManifest, RustcSource}; 19use project_model::{CargoConfig, ProjectJson, ProjectJsonData, ProjectManifest, RustcSource};
20use rustc_hash::FxHashSet; 20use rustc_hash::{FxHashMap, FxHashSet};
21use serde::{de::DeserializeOwned, Deserialize}; 21use serde::{de::DeserializeOwned, Deserialize};
22use vfs::AbsPathBuf; 22use vfs::AbsPathBuf;
23 23
@@ -99,6 +99,9 @@ config_data! {
99 diagnostics_enableExperimental: bool = "true", 99 diagnostics_enableExperimental: bool = "true",
100 /// List of rust-analyzer diagnostics to disable. 100 /// List of rust-analyzer diagnostics to disable.
101 diagnostics_disabled: FxHashSet<String> = "[]", 101 diagnostics_disabled: FxHashSet<String> = "[]",
102 /// Map of prefixes to be substituted when parsing diagnostic file paths.
103 /// This should be the reverse mapping of what is passed to `rustc` as `--remap-path-prefix`.
104 diagnostics_remapPrefix: FxHashMap<String, String> = "{}",
102 /// List of warnings that should be displayed with info severity. 105 /// List of warnings that should be displayed with info severity.
103 /// 106 ///
104 /// The warnings will be indicated by a blue squiggly underline in code 107 /// The warnings will be indicated by a blue squiggly underline in code
@@ -474,6 +477,7 @@ impl Config {
474 } 477 }
475 pub fn diagnostics_map(&self) -> DiagnosticsMapConfig { 478 pub fn diagnostics_map(&self) -> DiagnosticsMapConfig {
476 DiagnosticsMapConfig { 479 DiagnosticsMapConfig {
480 remap_prefix: self.data.diagnostics_remapPrefix.clone(),
477 warnings_as_info: self.data.diagnostics_warningsAsInfo.clone(), 481 warnings_as_info: self.data.diagnostics_warningsAsInfo.clone(),
478 warnings_as_hint: self.data.diagnostics_warningsAsHint.clone(), 482 warnings_as_hint: self.data.diagnostics_warningsAsHint.clone(),
479 } 483 }
@@ -835,6 +839,9 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json
835 "items": { "type": "string" }, 839 "items": { "type": "string" },
836 "uniqueItems": true, 840 "uniqueItems": true,
837 }, 841 },
842 "FxHashMap<String, String>" => set! {
843 "type": "object",
844 },
838 "Option<usize>" => set! { 845 "Option<usize>" => set! {
839 "type": ["null", "integer"], 846 "type": ["null", "integer"],
840 "minimum": 0, 847 "minimum": 0,
diff --git a/crates/rust-analyzer/src/diagnostics.rs b/crates/rust-analyzer/src/diagnostics.rs
index f01548c50..d4b9db362 100644
--- a/crates/rust-analyzer/src/diagnostics.rs
+++ b/crates/rust-analyzer/src/diagnostics.rs
@@ -12,6 +12,7 @@ pub(crate) type CheckFixes = Arc<FxHashMap<FileId, Vec<Fix>>>;
12 12
13#[derive(Debug, Default, Clone)] 13#[derive(Debug, Default, Clone)]
14pub struct DiagnosticsMapConfig { 14pub struct DiagnosticsMapConfig {
15 pub remap_prefix: FxHashMap<String, String>,
15 pub warnings_as_info: Vec<String>, 16 pub warnings_as_info: Vec<String>,
16 pub warnings_as_hint: Vec<String>, 17 pub warnings_as_hint: Vec<String>,
17} 18}
diff --git a/crates/rust-analyzer/src/diagnostics/to_proto.rs b/crates/rust-analyzer/src/diagnostics/to_proto.rs
index ca18997e4..82dd0da9a 100644
--- a/crates/rust-analyzer/src/diagnostics/to_proto.rs
+++ b/crates/rust-analyzer/src/diagnostics/to_proto.rs
@@ -1,6 +1,9 @@
1//! This module provides the functionality needed to convert diagnostics from 1//! This module provides the functionality needed to convert diagnostics from
2//! `cargo check` json format to the LSP diagnostic format. 2//! `cargo check` json format to the LSP diagnostic format.
3use std::{collections::HashMap, path::Path}; 3use std::{
4 collections::HashMap,
5 path::{Path, PathBuf},
6};
4 7
5use flycheck::{DiagnosticLevel, DiagnosticSpan}; 8use flycheck::{DiagnosticLevel, DiagnosticSpan};
6use stdx::format_to; 9use stdx::format_to;
@@ -41,8 +44,12 @@ fn is_dummy_macro_file(file_name: &str) -> bool {
41} 44}
42 45
43/// Converts a Rust span to a LSP location 46/// Converts a Rust span to a LSP location
44fn location(workspace_root: &Path, span: &DiagnosticSpan) -> lsp_types::Location { 47fn location(
45 let file_name = workspace_root.join(&span.file_name); 48 config: &DiagnosticsMapConfig,
49 workspace_root: &Path,
50 span: &DiagnosticSpan,
51) -> lsp_types::Location {
52 let file_name = resolve_path(config, workspace_root, &span.file_name);
46 let uri = url_from_abs_path(&file_name); 53 let uri = url_from_abs_path(&file_name);
47 54
48 // FIXME: this doesn't handle UTF16 offsets correctly 55 // FIXME: this doesn't handle UTF16 offsets correctly
@@ -58,32 +65,50 @@ fn location(workspace_root: &Path, span: &DiagnosticSpan) -> lsp_types::Location
58/// 65///
59/// This takes locations pointing into the standard library, or generally outside the current 66/// This takes locations pointing into the standard library, or generally outside the current
60/// workspace into account and tries to avoid those, in case macros are involved. 67/// workspace into account and tries to avoid those, in case macros are involved.
61fn primary_location(workspace_root: &Path, span: &DiagnosticSpan) -> lsp_types::Location { 68fn primary_location(
69 config: &DiagnosticsMapConfig,
70 workspace_root: &Path,
71 span: &DiagnosticSpan,
72) -> lsp_types::Location {
62 let span_stack = std::iter::successors(Some(span), |span| Some(&span.expansion.as_ref()?.span)); 73 let span_stack = std::iter::successors(Some(span), |span| Some(&span.expansion.as_ref()?.span));
63 for span in span_stack.clone() { 74 for span in span_stack.clone() {
64 let abs_path = workspace_root.join(&span.file_name); 75 let abs_path = resolve_path(config, workspace_root, &span.file_name);
65 if !is_dummy_macro_file(&span.file_name) && abs_path.starts_with(workspace_root) { 76 if !is_dummy_macro_file(&span.file_name) && abs_path.starts_with(workspace_root) {
66 return location(workspace_root, span); 77 return location(config, workspace_root, span);
67 } 78 }
68 } 79 }
69 80
70 // Fall back to the outermost macro invocation if no suitable span comes up. 81 // Fall back to the outermost macro invocation if no suitable span comes up.
71 let last_span = span_stack.last().unwrap(); 82 let last_span = span_stack.last().unwrap();
72 location(workspace_root, last_span) 83 location(config, workspace_root, last_span)
73} 84}
74 85
75/// Converts a secondary Rust span to a LSP related information 86/// Converts a secondary Rust span to a LSP related information
76/// 87///
77/// If the span is unlabelled this will return `None`. 88/// If the span is unlabelled this will return `None`.
78fn diagnostic_related_information( 89fn diagnostic_related_information(
90 config: &DiagnosticsMapConfig,
79 workspace_root: &Path, 91 workspace_root: &Path,
80 span: &DiagnosticSpan, 92 span: &DiagnosticSpan,
81) -> Option<lsp_types::DiagnosticRelatedInformation> { 93) -> Option<lsp_types::DiagnosticRelatedInformation> {
82 let message = span.label.clone()?; 94 let message = span.label.clone()?;
83 let location = location(workspace_root, span); 95 let location = location(config, workspace_root, span);
84 Some(lsp_types::DiagnosticRelatedInformation { location, message }) 96 Some(lsp_types::DiagnosticRelatedInformation { location, message })
85} 97}
86 98
99/// Resolves paths applying any matching path prefix remappings, and then
100/// joining the path to the workspace root.
101fn resolve_path(config: &DiagnosticsMapConfig, workspace_root: &Path, file_name: &str) -> PathBuf {
102 match config
103 .remap_prefix
104 .iter()
105 .find_map(|(from, to)| file_name.strip_prefix(from).map(|file_name| (to, file_name)))
106 {
107 Some((to, file_name)) => workspace_root.join(format!("{}{}", to, file_name)),
108 None => workspace_root.join(file_name),
109 }
110}
111
87struct SubDiagnostic { 112struct SubDiagnostic {
88 related: lsp_types::DiagnosticRelatedInformation, 113 related: lsp_types::DiagnosticRelatedInformation,
89 suggested_fix: Option<lsp_ext::CodeAction>, 114 suggested_fix: Option<lsp_ext::CodeAction>,
@@ -95,6 +120,7 @@ enum MappedRustChildDiagnostic {
95} 120}
96 121
97fn map_rust_child_diagnostic( 122fn map_rust_child_diagnostic(
123 config: &DiagnosticsMapConfig,
98 workspace_root: &Path, 124 workspace_root: &Path,
99 rd: &flycheck::Diagnostic, 125 rd: &flycheck::Diagnostic,
100) -> MappedRustChildDiagnostic { 126) -> MappedRustChildDiagnostic {
@@ -108,7 +134,7 @@ fn map_rust_child_diagnostic(
108 let mut edit_map: HashMap<lsp_types::Url, Vec<lsp_types::TextEdit>> = HashMap::new(); 134 let mut edit_map: HashMap<lsp_types::Url, Vec<lsp_types::TextEdit>> = HashMap::new();
109 for &span in &spans { 135 for &span in &spans {
110 if let Some(suggested_replacement) = &span.suggested_replacement { 136 if let Some(suggested_replacement) = &span.suggested_replacement {
111 let location = location(workspace_root, span); 137 let location = location(config, workspace_root, span);
112 let edit = lsp_types::TextEdit::new(location.range, suggested_replacement.clone()); 138 let edit = lsp_types::TextEdit::new(location.range, suggested_replacement.clone());
113 edit_map.entry(location.uri).or_default().push(edit); 139 edit_map.entry(location.uri).or_default().push(edit);
114 } 140 }
@@ -117,7 +143,7 @@ fn map_rust_child_diagnostic(
117 if edit_map.is_empty() { 143 if edit_map.is_empty() {
118 MappedRustChildDiagnostic::SubDiagnostic(SubDiagnostic { 144 MappedRustChildDiagnostic::SubDiagnostic(SubDiagnostic {
119 related: lsp_types::DiagnosticRelatedInformation { 145 related: lsp_types::DiagnosticRelatedInformation {
120 location: location(workspace_root, spans[0]), 146 location: location(config, workspace_root, spans[0]),
121 message: rd.message.clone(), 147 message: rd.message.clone(),
122 }, 148 },
123 suggested_fix: None, 149 suggested_fix: None,
@@ -125,7 +151,7 @@ fn map_rust_child_diagnostic(
125 } else { 151 } else {
126 MappedRustChildDiagnostic::SubDiagnostic(SubDiagnostic { 152 MappedRustChildDiagnostic::SubDiagnostic(SubDiagnostic {
127 related: lsp_types::DiagnosticRelatedInformation { 153 related: lsp_types::DiagnosticRelatedInformation {
128 location: location(workspace_root, spans[0]), 154 location: location(config, workspace_root, spans[0]),
129 message: rd.message.clone(), 155 message: rd.message.clone(),
130 }, 156 },
131 suggested_fix: Some(lsp_ext::CodeAction { 157 suggested_fix: Some(lsp_ext::CodeAction {
@@ -190,7 +216,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
190 let mut tags = Vec::new(); 216 let mut tags = Vec::new();
191 217
192 for secondary_span in rd.spans.iter().filter(|s| !s.is_primary) { 218 for secondary_span in rd.spans.iter().filter(|s| !s.is_primary) {
193 let related = diagnostic_related_information(workspace_root, secondary_span); 219 let related = diagnostic_related_information(config, workspace_root, secondary_span);
194 if let Some(related) = related { 220 if let Some(related) = related {
195 subdiagnostics.push(SubDiagnostic { related, suggested_fix: None }); 221 subdiagnostics.push(SubDiagnostic { related, suggested_fix: None });
196 } 222 }
@@ -198,7 +224,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
198 224
199 let mut message = rd.message.clone(); 225 let mut message = rd.message.clone();
200 for child in &rd.children { 226 for child in &rd.children {
201 let child = map_rust_child_diagnostic(workspace_root, &child); 227 let child = map_rust_child_diagnostic(config, workspace_root, &child);
202 match child { 228 match child {
203 MappedRustChildDiagnostic::SubDiagnostic(sub) => { 229 MappedRustChildDiagnostic::SubDiagnostic(sub) => {
204 subdiagnostics.push(sub); 230 subdiagnostics.push(sub);
@@ -242,7 +268,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
242 primary_spans 268 primary_spans
243 .iter() 269 .iter()
244 .flat_map(|primary_span| { 270 .flat_map(|primary_span| {
245 let primary_location = primary_location(workspace_root, &primary_span); 271 let primary_location = primary_location(config, workspace_root, &primary_span);
246 272
247 let mut message = message.clone(); 273 let mut message = message.clone();
248 if needs_primary_span_label { 274 if needs_primary_span_label {
@@ -272,7 +298,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
272 // generated that code. 298 // generated that code.
273 let is_in_macro_call = i != 0; 299 let is_in_macro_call = i != 0;
274 300
275 let secondary_location = location(workspace_root, &span); 301 let secondary_location = location(config, workspace_root, &span);
276 if secondary_location == primary_location { 302 if secondary_location == primary_location {
277 continue; 303 continue;
278 } 304 }
diff --git a/crates/stdx/Cargo.toml b/crates/stdx/Cargo.toml
index d28b5e658..f78c5da7c 100644
--- a/crates/stdx/Cargo.toml
+++ b/crates/stdx/Cargo.toml
@@ -10,10 +10,15 @@ edition = "2018"
10doctest = false 10doctest = false
11 11
12[dependencies] 12[dependencies]
13libc = "0.2.93"
13backtrace = { version = "0.3.44", optional = true } 14backtrace = { version = "0.3.44", optional = true }
14always-assert = { version = "0.1.2", features = ["log"] } 15always-assert = { version = "0.1.2", features = ["log"] }
15# Think twice before adding anything here 16# Think twice before adding anything here
16 17
18[target.'cfg(windows)'.dependencies]
19miow = "0.3.6"
20winapi = "0.3.9"
21
17[features] 22[features]
18# Uncomment to enable for the whole crate graph 23# Uncomment to enable for the whole crate graph
19# default = [ "backtrace" ] 24# default = [ "backtrace" ]
diff --git a/crates/stdx/src/lib.rs b/crates/stdx/src/lib.rs
index b0a18d58d..857567a85 100644
--- a/crates/stdx/src/lib.rs
+++ b/crates/stdx/src/lib.rs
@@ -1,7 +1,8 @@
1//! Missing batteries for standard libraries. 1//! Missing batteries for standard libraries.
2use std::{cmp::Ordering, ops, process, time::Instant}; 2use std::{cmp::Ordering, ops, time::Instant};
3 3
4mod macros; 4mod macros;
5pub mod process;
5pub mod panic_context; 6pub mod panic_context;
6 7
7pub use always_assert::{always, never}; 8pub use always_assert::{always, never};
@@ -178,18 +179,30 @@ where
178 start..start + len 179 start..start + len
179} 180}
180 181
182pub fn defer<F: FnOnce()>(f: F) -> impl Drop {
183 struct D<F: FnOnce()>(Option<F>);
184 impl<F: FnOnce()> Drop for D<F> {
185 fn drop(&mut self) {
186 if let Some(f) = self.0.take() {
187 f()
188 }
189 }
190 }
191 D(Some(f))
192}
193
181#[repr(transparent)] 194#[repr(transparent)]
182pub struct JodChild(pub process::Child); 195pub struct JodChild(pub std::process::Child);
183 196
184impl ops::Deref for JodChild { 197impl ops::Deref for JodChild {
185 type Target = process::Child; 198 type Target = std::process::Child;
186 fn deref(&self) -> &process::Child { 199 fn deref(&self) -> &std::process::Child {
187 &self.0 200 &self.0
188 } 201 }
189} 202}
190 203
191impl ops::DerefMut for JodChild { 204impl ops::DerefMut for JodChild {
192 fn deref_mut(&mut self) -> &mut process::Child { 205 fn deref_mut(&mut self) -> &mut std::process::Child {
193 &mut self.0 206 &mut self.0
194 } 207 }
195} 208}
@@ -202,9 +215,9 @@ impl Drop for JodChild {
202} 215}
203 216
204impl JodChild { 217impl JodChild {
205 pub fn into_inner(self) -> process::Child { 218 pub fn into_inner(self) -> std::process::Child {
206 // SAFETY: repr transparent 219 // SAFETY: repr transparent
207 unsafe { std::mem::transmute::<JodChild, process::Child>(self) } 220 unsafe { std::mem::transmute::<JodChild, std::process::Child>(self) }
208 } 221 }
209} 222}
210 223
diff --git a/crates/stdx/src/process.rs b/crates/stdx/src/process.rs
new file mode 100644
index 000000000..b0fa12f76
--- /dev/null
+++ b/crates/stdx/src/process.rs
@@ -0,0 +1,238 @@
1//! Read both stdout and stderr of child without deadlocks.
2//!
3//! https://github.com/rust-lang/cargo/blob/905af549966f23a9288e9993a85d1249a5436556/crates/cargo-util/src/read2.rs
4//! https://github.com/rust-lang/cargo/blob/58a961314437258065e23cb6316dfc121d96fb71/crates/cargo-util/src/process_builder.rs#L231
5
6use std::{
7 io,
8 process::{Command, Output, Stdio},
9};
10
11pub fn streaming_output(
12 mut cmd: Command,
13 on_stdout_line: &mut dyn FnMut(&str),
14 on_stderr_line: &mut dyn FnMut(&str),
15) -> io::Result<Output> {
16 let mut stdout = Vec::new();
17 let mut stderr = Vec::new();
18
19 let cmd = cmd.stdout(Stdio::piped()).stderr(Stdio::piped()).stdin(Stdio::null());
20
21 let status = {
22 let mut child = cmd.spawn()?;
23 let out = child.stdout.take().unwrap();
24 let err = child.stderr.take().unwrap();
25 imp::read2(out, err, &mut |is_out, data, eof| {
26 let idx = if eof {
27 data.len()
28 } else {
29 match data.iter().rposition(|b| *b == b'\n') {
30 Some(i) => i + 1,
31 None => return,
32 }
33 };
34 {
35 // scope for new_lines
36 let new_lines = {
37 let dst = if is_out { &mut stdout } else { &mut stderr };
38 let start = dst.len();
39 let data = data.drain(..idx);
40 dst.extend(data);
41 &dst[start..]
42 };
43 for line in String::from_utf8_lossy(new_lines).lines() {
44 if is_out {
45 on_stdout_line(line)
46 } else {
47 on_stderr_line(line)
48 }
49 }
50 }
51 })?;
52 child.wait()?
53 };
54
55 Ok(Output { status, stdout, stderr })
56}
57
58#[cfg(unix)]
59mod imp {
60 use std::{
61 io::{self, prelude::*},
62 mem,
63 os::unix::prelude::*,
64 process::{ChildStderr, ChildStdout},
65 };
66
67 pub(crate) fn read2(
68 mut out_pipe: ChildStdout,
69 mut err_pipe: ChildStderr,
70 data: &mut dyn FnMut(bool, &mut Vec<u8>, bool),
71 ) -> io::Result<()> {
72 unsafe {
73 libc::fcntl(out_pipe.as_raw_fd(), libc::F_SETFL, libc::O_NONBLOCK);
74 libc::fcntl(err_pipe.as_raw_fd(), libc::F_SETFL, libc::O_NONBLOCK);
75 }
76
77 let mut out_done = false;
78 let mut err_done = false;
79 let mut out = Vec::new();
80 let mut err = Vec::new();
81
82 let mut fds: [libc::pollfd; 2] = unsafe { mem::zeroed() };
83 fds[0].fd = out_pipe.as_raw_fd();
84 fds[0].events = libc::POLLIN;
85 fds[1].fd = err_pipe.as_raw_fd();
86 fds[1].events = libc::POLLIN;
87 let mut nfds = 2;
88 let mut errfd = 1;
89
90 while nfds > 0 {
91 // wait for either pipe to become readable using `select`
92 let r = unsafe { libc::poll(fds.as_mut_ptr(), nfds, -1) };
93 if r == -1 {
94 let err = io::Error::last_os_error();
95 if err.kind() == io::ErrorKind::Interrupted {
96 continue;
97 }
98 return Err(err);
99 }
100
101 // Read as much as we can from each pipe, ignoring EWOULDBLOCK or
102 // EAGAIN. If we hit EOF, then this will happen because the underlying
103 // reader will return Ok(0), in which case we'll see `Ok` ourselves. In
104 // this case we flip the other fd back into blocking mode and read
105 // whatever's leftover on that file descriptor.
106 let handle = |res: io::Result<_>| match res {
107 Ok(_) => Ok(true),
108 Err(e) => {
109 if e.kind() == io::ErrorKind::WouldBlock {
110 Ok(false)
111 } else {
112 Err(e)
113 }
114 }
115 };
116 if !err_done && fds[errfd].revents != 0 && handle(err_pipe.read_to_end(&mut err))? {
117 err_done = true;
118 nfds -= 1;
119 }
120 data(false, &mut err, err_done);
121 if !out_done && fds[0].revents != 0 && handle(out_pipe.read_to_end(&mut out))? {
122 out_done = true;
123 fds[0].fd = err_pipe.as_raw_fd();
124 errfd = 0;
125 nfds -= 1;
126 }
127 data(true, &mut out, out_done);
128 }
129 Ok(())
130 }
131}
132
133#[cfg(windows)]
134mod imp {
135 use std::{
136 io,
137 os::windows::prelude::*,
138 process::{ChildStderr, ChildStdout},
139 slice,
140 };
141
142 use miow::{
143 iocp::{CompletionPort, CompletionStatus},
144 pipe::NamedPipe,
145 Overlapped,
146 };
147 use winapi::shared::winerror::ERROR_BROKEN_PIPE;
148
149 struct Pipe<'a> {
150 dst: &'a mut Vec<u8>,
151 overlapped: Overlapped,
152 pipe: NamedPipe,
153 done: bool,
154 }
155
156 pub(crate) fn read2(
157 out_pipe: ChildStdout,
158 err_pipe: ChildStderr,
159 data: &mut dyn FnMut(bool, &mut Vec<u8>, bool),
160 ) -> io::Result<()> {
161 let mut out = Vec::new();
162 let mut err = Vec::new();
163
164 let port = CompletionPort::new(1)?;
165 port.add_handle(0, &out_pipe)?;
166 port.add_handle(1, &err_pipe)?;
167
168 unsafe {
169 let mut out_pipe = Pipe::new(out_pipe, &mut out);
170 let mut err_pipe = Pipe::new(err_pipe, &mut err);
171
172 out_pipe.read()?;
173 err_pipe.read()?;
174
175 let mut status = [CompletionStatus::zero(), CompletionStatus::zero()];
176
177 while !out_pipe.done || !err_pipe.done {
178 for status in port.get_many(&mut status, None)? {
179 if status.token() == 0 {
180 out_pipe.complete(status);
181 data(true, out_pipe.dst, out_pipe.done);
182 out_pipe.read()?;
183 } else {
184 err_pipe.complete(status);
185 data(false, err_pipe.dst, err_pipe.done);
186 err_pipe.read()?;
187 }
188 }
189 }
190
191 Ok(())
192 }
193 }
194
195 impl<'a> Pipe<'a> {
196 unsafe fn new<P: IntoRawHandle>(p: P, dst: &'a mut Vec<u8>) -> Pipe<'a> {
197 Pipe {
198 dst,
199 pipe: NamedPipe::from_raw_handle(p.into_raw_handle()),
200 overlapped: Overlapped::zero(),
201 done: false,
202 }
203 }
204
205 unsafe fn read(&mut self) -> io::Result<()> {
206 let dst = slice_to_end(self.dst);
207 match self.pipe.read_overlapped(dst, self.overlapped.raw()) {
208 Ok(_) => Ok(()),
209 Err(e) => {
210 if e.raw_os_error() == Some(ERROR_BROKEN_PIPE as i32) {
211 self.done = true;
212 Ok(())
213 } else {
214 Err(e)
215 }
216 }
217 }
218 }
219
220 unsafe fn complete(&mut self, status: &CompletionStatus) {
221 let prev = self.dst.len();
222 self.dst.set_len(prev + status.bytes_transferred() as usize);
223 if status.bytes_transferred() == 0 {
224 self.done = true;
225 }
226 }
227 }
228
229 unsafe fn slice_to_end(v: &mut Vec<u8>) -> &mut [u8] {
230 if v.capacity() == 0 {
231 v.reserve(16);
232 }
233 if v.capacity() == v.len() {
234 v.reserve(1);
235 }
236 slice::from_raw_parts_mut(v.as_mut_ptr().add(v.len()), v.capacity() - v.len())
237 }
238}
diff --git a/crates/syntax/Cargo.toml b/crates/syntax/Cargo.toml
index a8c1a8075..556f80882 100644
--- a/crates/syntax/Cargo.toml
+++ b/crates/syntax/Cargo.toml
@@ -14,7 +14,7 @@ doctest = false
14cov-mark = { version = "1.1", features = ["thread-local"] } 14cov-mark = { version = "1.1", features = ["thread-local"] }
15itertools = "0.10.0" 15itertools = "0.10.0"
16rowan = "=0.13.0-pre.3" 16rowan = "=0.13.0-pre.3"
17rustc_lexer = { version = "714.0.0", package = "rustc-ap-rustc_lexer" } 17rustc_lexer = { version = "716.0.0", package = "rustc-ap-rustc_lexer" }
18rustc-hash = "1.1.0" 18rustc-hash = "1.1.0"
19arrayvec = "0.7" 19arrayvec = "0.7"
20once_cell = "1.3.1" 20once_cell = "1.3.1"
diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs
index 94d4f2cf0..4cf6f871e 100644
--- a/crates/syntax/src/ast/make.rs
+++ b/crates/syntax/src/ast/make.rs
@@ -294,6 +294,14 @@ pub fn wildcard_pat() -> ast::WildcardPat {
294 } 294 }
295} 295}
296 296
297pub fn literal_pat(lit: &str) -> ast::LiteralPat {
298 return from_text(lit);
299
300 fn from_text(text: &str) -> ast::LiteralPat {
301 ast_from_text(&format!("fn f() {{ match x {{ {} => {{}} }} }}", text))
302 }
303}
304
297/// Creates a tuple of patterns from an iterator of patterns. 305/// Creates a tuple of patterns from an iterator of patterns.
298/// 306///
299/// Invariant: `pats` must be length > 0 307/// Invariant: `pats` must be length > 0
diff --git a/crates/syntax/test_data/parser/inline/ok/0145_record_pat_field.rast b/crates/syntax/test_data/parser/inline/ok/0145_record_pat_field.rast
index 925409bdf..e9202a612 100644
--- a/crates/syntax/test_data/parser/inline/ok/0145_record_pat_field.rast
+++ b/crates/syntax/test_data/parser/inline/ok/0145_record_pat_field.rast
@@ -1,5 +1,5 @@
1SOURCE_FILE@0..63 1SOURCE_FILE@0..102
2 FN@0..62 2 FN@0..101
3 [email protected] "fn" 3 [email protected] "fn"
4 [email protected] " " 4 [email protected] " "
5 [email protected] 5 [email protected]
@@ -8,7 +8,7 @@ [email protected]
8 [email protected] "(" 8 [email protected] "("
9 [email protected] ")" 9 [email protected] ")"
10 [email protected] " " 10 [email protected] " "
11 BLOCK_EXPR@9..62 11 BLOCK_EXPR@9..101
12 [email protected] "{" 12 [email protected] "{"
13 [email protected] "\n " 13 [email protected] "\n "
14 [email protected] 14 [email protected]
@@ -70,6 +70,52 @@ [email protected]
70 [email protected] "(" 70 [email protected] "("
71 [email protected] ")" 71 [email protected] ")"
72 [email protected] ";" 72 [email protected] ";"
73 [email protected] "\n" 73 [email protected] "\n "
74 [email protected] "}" 74 [email protected]
75 [email protected] "\n" 75 [email protected] "let"
76 [email protected] " "
77 [email protected]
78 [email protected]
79 [email protected]
80 [email protected]
81 [email protected] "S"
82 [email protected] " "
83 [email protected]
84 [email protected] "{"
85 [email protected] " "
86 [email protected]
87 [email protected]
88 [email protected] "#"
89 [email protected] "["
90 [email protected]
91 [email protected]
92 [email protected]
93 [email protected] "cfg"
94 [email protected]
95 [email protected] "("
96 [email protected] "any"
97 [email protected]
98 [email protected] "("
99 [email protected] ")"
100 [email protected] ")"
101 [email protected] "]"
102 [email protected] " "
103 [email protected]
104 [email protected] "x"
105 [email protected] ":"
106 [email protected] " "
107 [email protected]
108 [email protected]
109 [email protected] "1"
110 [email protected] " "
111 [email protected] "}"
112 [email protected] " "
113 [email protected] "="
114 [email protected] " "
115 [email protected]
116 [email protected] "("
117 [email protected] ")"
118 [email protected] ";"
119 [email protected] "\n"
120 [email protected] "}"
121 [email protected] "\n"
diff --git a/crates/syntax/test_data/parser/inline/ok/0145_record_pat_field.rs b/crates/syntax/test_data/parser/inline/ok/0145_record_pat_field.rs
index 26b1d5f89..53cfdc22d 100644
--- a/crates/syntax/test_data/parser/inline/ok/0145_record_pat_field.rs
+++ b/crates/syntax/test_data/parser/inline/ok/0145_record_pat_field.rs
@@ -1,4 +1,5 @@
1fn foo() { 1fn foo() {
2 let S { 0: 1 } = (); 2 let S { 0: 1 } = ();
3 let S { x: 1 } = (); 3 let S { x: 1 } = ();
4 let S { #[cfg(any())] x: 1 } = ();
4} 5}
diff --git a/docs/dev/README.md b/docs/dev/README.md
index 7e4488a41..b98ac4c0a 100644
--- a/docs/dev/README.md
+++ b/docs/dev/README.md
@@ -229,7 +229,11 @@ If it fails because of something that needs to be fixed, remove the release tag
229Make sure to remove the new changelog post created when running `cargo xtask release` a second time. 229Make sure to remove the new changelog post created when running `cargo xtask release` a second time.
230 230
231We release "nightly" every night automatically and promote the latest nightly to "stable" manually, every week. 231We release "nightly" every night automatically and promote the latest nightly to "stable" manually, every week.
232
232We don't do "patch" releases, unless something truly egregious comes up. 233We don't do "patch" releases, unless something truly egregious comes up.
234To do a patch release, cherry-pick the fix on top of the current `release` branch and push the branch.
235There's no need to write a changelog for a patch release, it's OK to include the notes about the fix into the next weekly one.
236Note: we tag releases by dates, releasing a patch release on the same day should work (by overwriting a tag), but I am not 100% sure.
233 237
234## Permissions 238## Permissions
235 239
diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc
index e0ee35b4e..e28423e99 100644
--- a/docs/user/generated_config.adoc
+++ b/docs/user/generated_config.adoc
@@ -147,6 +147,12 @@ have more false positives than usual.
147-- 147--
148List of rust-analyzer diagnostics to disable. 148List of rust-analyzer diagnostics to disable.
149-- 149--
150[[rust-analyzer.diagnostics.remapPrefix]]rust-analyzer.diagnostics.remapPrefix (default: `{}`)::
151+
152--
153Map of prefixes to be substituted when parsing diagnostic file paths.
154This should be the reverse mapping of what is passed to `rustc` as `--remap-path-prefix`.
155--
150[[rust-analyzer.diagnostics.warningsAsHint]]rust-analyzer.diagnostics.warningsAsHint (default: `[]`):: 156[[rust-analyzer.diagnostics.warningsAsHint]]rust-analyzer.diagnostics.warningsAsHint (default: `[]`)::
151+ 157+
152-- 158--
diff --git a/docs/user/manual.adoc b/docs/user/manual.adoc
index b86e91772..54195adb7 100644
--- a/docs/user/manual.adoc
+++ b/docs/user/manual.adoc
@@ -429,24 +429,32 @@ However, if you use some other build system, you'll have to describe the structu
429[source,TypeScript] 429[source,TypeScript]
430---- 430----
431interface JsonProject { 431interface JsonProject {
432 /// Path to the directory with *source code* of sysroot crates. 432 /// Path to the directory with *source code* of
433 /// sysroot crates.
434 ///
435 /// It should point to the directory where std,
436 /// core, and friends can be found:
433 /// 437 ///
434 /// It should point to the directory where std, core, and friends can be found:
435 /// https://github.com/rust-lang/rust/tree/master/library. 438 /// https://github.com/rust-lang/rust/tree/master/library.
436 /// 439 ///
437 /// If provided, rust-analyzer automatically adds dependencies on sysroot 440 /// If provided, rust-analyzer automatically adds
438 /// crates. Conversely, if you omit this path, you can specify sysroot 441 /// dependencies on sysroot crates. Conversely,
439 /// dependencies yourself and, for example, have several different "sysroots" in 442 /// if you omit this path, you can specify sysroot
440 /// one graph of crates. 443 /// dependencies yourself and, for example, have
444 /// several different "sysroots" in one graph of
445 /// crates.
441 sysroot_src?: string; 446 sysroot_src?: string;
442 /// The set of crates comprising the current project. 447 /// The set of crates comprising the current
443 /// Must include all transitive dependencies as well as sysroot crate (libstd, libcore and such). 448 /// project. Must include all transitive
449 /// dependencies as well as sysroot crate (libstd,
450 /// libcore and such).
444 crates: Crate[]; 451 crates: Crate[];
445} 452}
446 453
447interface Crate { 454interface Crate {
448 /// Optional crate name used for display purposes, without affecting semantics. 455 /// Optional crate name used for display purposes,
449 /// See the `deps` key for semantically-significant crate names. 456 /// without affecting semantics. See the `deps`
457 /// key for semantically-significant crate names.
450 display_name?: string; 458 display_name?: string;
451 /// Path to the root module of the crate. 459 /// Path to the root module of the crate.
452 root_module: string; 460 root_module: string;
@@ -454,45 +462,59 @@ interface Crate {
454 edition: "2015" | "2018" | "2021"; 462 edition: "2015" | "2018" | "2021";
455 /// Dependencies 463 /// Dependencies
456 deps: Dep[]; 464 deps: Dep[];
457 /// Should this crate be treated as a member of current "workspace". 465 /// Should this crate be treated as a member of
466 /// current "workspace".
458 /// 467 ///
459 /// By default, inferred from the `root_module` (members are the crates which reside 468 /// By default, inferred from the `root_module`
460 /// inside the directory opened in the editor). 469 /// (members are the crates which reside inside
470 /// the directory opened in the editor).
461 /// 471 ///
462 /// Set this to `false` for things like standard library and 3rd party crates to 472 /// Set this to `false` for things like standard
463 /// enable performance optimizations (rust-analyzer assumes that non-member crates 473 /// library and 3rd party crates to enable
464 /// don't change). 474 /// performance optimizations (rust-analyzer
475 /// assumes that non-member crates don't change).
465 is_workspace_member?: boolean; 476 is_workspace_member?: boolean;
466 /// Optionally specify the (super)set of `.rs` files comprising this crate. 477 /// Optionally specify the (super)set of `.rs`
478 /// files comprising this crate.
467 /// 479 ///
468 /// By default, rust-analyzer assumes that only files under `root_module.parent` can belong to a crate. 480 /// By default, rust-analyzer assumes that only
469 /// `include_dirs` are included recursively, unless a subdirectory is in `exclude_dirs`. 481 /// files under `root_module.parent` can belong
482 /// to a crate. `include_dirs` are included
483 /// recursively, unless a subdirectory is in
484 /// `exclude_dirs`.
470 /// 485 ///
471 /// Different crates can share the same `source`. 486 /// Different crates can share the same `source`.
472 /// 487 ///
473 /// If two crates share an `.rs` file in common, they *must* have the same `source`. 488 /// If two crates share an `.rs` file in common,
474 /// rust-analyzer assumes that files from one source can't refer to files in another source. 489 /// they *must* have the same `source`.
490 /// rust-analyzer assumes that files from one
491 /// source can't refer to files in another source.
475 source?: { 492 source?: {
476 include_dirs: string[], 493 include_dirs: string[],
477 exclude_dirs: string[], 494 exclude_dirs: string[],
478 }, 495 },
479 /// The set of cfgs activated for a given crate, like `["unix", "feature=\"foo\"", "feature=\"bar\""]`. 496 /// The set of cfgs activated for a given crate, like
497 /// `["unix", "feature=\"foo\"", "feature=\"bar\""]`.
480 cfg: string[]; 498 cfg: string[];
481 /// Target triple for this Crate. 499 /// Target triple for this Crate.
482 /// 500 ///
483 /// Used when running `rustc --print cfg` to get target-specific cfgs. 501 /// Used when running `rustc --print cfg`
502 /// to get target-specific cfgs.
484 target?: string; 503 target?: string;
485 /// Environment variables, used for the `env!` macro 504 /// Environment variables, used for
505 /// the `env!` macro
486 env: : { [key: string]: string; }, 506 env: : { [key: string]: string; },
487 507
488 /// For proc-macro crates, path to compiles proc-macro (.so file). 508 /// For proc-macro crates, path to compiled
509 /// proc-macro (.so file).
489 proc_macro_dylib_path?: string; 510 proc_macro_dylib_path?: string;
490} 511}
491 512
492interface Dep { 513interface Dep {
493 /// Index of a crate in the `crates` array. 514 /// Index of a crate in the `crates` array.
494 crate: number, 515 crate: number,
495 /// Name as should appear in the (implicit) `extern crate name` declaration. 516 /// Name as should appear in the (implicit)
517 /// `extern crate name` declaration.
496 name: string, 518 name: string,
497} 519}
498---- 520----
diff --git a/editors/code/package.json b/editors/code/package.json
index 06ed62d8d..fa5632f90 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -565,6 +565,11 @@
565 }, 565 },
566 "uniqueItems": true 566 "uniqueItems": true
567 }, 567 },
568 "rust-analyzer.diagnostics.remapPrefix": {
569 "markdownDescription": "Map of prefixes to be substituted when parsing diagnostic file paths.\nThis should be the reverse mapping of what is passed to `rustc` as `--remap-path-prefix`.",
570 "default": {},
571 "type": "object"
572 },
568 "rust-analyzer.diagnostics.warningsAsHint": { 573 "rust-analyzer.diagnostics.warningsAsHint": {
569 "markdownDescription": "List of warnings that should be displayed with info severity.\n\nThe warnings will be indicated by a blue squiggly underline in code\nand a blue icon in the `Problems Panel`.", 574 "markdownDescription": "List of warnings that should be displayed with info severity.\n\nThe warnings will be indicated by a blue squiggly underline in code\nand a blue icon in the `Problems Panel`.",
570 "default": [], 575 "default": [],
@@ -1195,4 +1200,4 @@
1195 ] 1200 ]
1196 } 1201 }
1197 } 1202 }
1198} \ No newline at end of file 1203}