diff options
24 files changed, 496 insertions, 113 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 | --- | ||
2 | name: Critical Nightly Regression | ||
3 | about: You are using nightly rust-analyzer and the latest version is unusable. | ||
4 | title: '' | ||
5 | labels: '' | ||
6 | assignees: 'matklad' | ||
7 | |||
8 | --- | ||
9 | |||
10 | <!-- | ||
11 | Troubleshooting guide: https://rust-analyzer.github.io/manual.html#troubleshooting | ||
12 | |||
13 | Please 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 | |||
16 | This 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 14decc14e..98141f5cd 100644 --- a/Cargo.lock +++ b/Cargo.lock | |||
@@ -72,9 +72,9 @@ checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" | |||
72 | 72 | ||
73 | [[package]] | 73 | [[package]] |
74 | name = "backtrace" | 74 | name = "backtrace" |
75 | version = "0.3.56" | 75 | version = "0.3.57" |
76 | source = "registry+https://github.com/rust-lang/crates.io-index" | 76 | source = "registry+https://github.com/rust-lang/crates.io-index" |
77 | checksum = "9d117600f438b1707d4e4ae15d3595657288f8235a0eb593e80ecc98ab34e1bc" | 77 | checksum = "78ed203b9ba68b242c62b3fb7480f589dd49829be1edb3fe8fc8b4ffda2dcb8d" |
78 | dependencies = [ | 78 | dependencies = [ |
79 | "addr2line", | 79 | "addr2line", |
80 | "cfg-if", | 80 | "cfg-if", |
@@ -168,9 +168,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" | |||
168 | 168 | ||
169 | [[package]] | 169 | [[package]] |
170 | name = "chalk-derive" | 170 | name = "chalk-derive" |
171 | version = "0.60.0" | 171 | version = "0.64.0" |
172 | source = "registry+https://github.com/rust-lang/crates.io-index" | 172 | source = "registry+https://github.com/rust-lang/crates.io-index" |
173 | checksum = "ab0f74445d4fbeaf0217bc1d23978cc73b95b28e8a738b81894580dd646822d2" | 173 | checksum = "d9acf2a9eab79ae7d44cd77ad86a8b1569d7a5e6d9a7db4a0a57a7344dd82c24" |
174 | dependencies = [ | 174 | dependencies = [ |
175 | "proc-macro2", | 175 | "proc-macro2", |
176 | "quote", | 176 | "quote", |
@@ -180,9 +180,9 @@ dependencies = [ | |||
180 | 180 | ||
181 | [[package]] | 181 | [[package]] |
182 | name = "chalk-ir" | 182 | name = "chalk-ir" |
183 | version = "0.60.0" | 183 | version = "0.64.0" |
184 | source = "registry+https://github.com/rust-lang/crates.io-index" | 184 | source = "registry+https://github.com/rust-lang/crates.io-index" |
185 | checksum = "294b1fc6210a5b3bd06c1d01dda48a581e2cafec80b8d659139ce45456644be2" | 185 | checksum = "877661627f54ba3666a72943c43b326cb170d60899e50a8426111e7a657ff032" |
186 | dependencies = [ | 186 | dependencies = [ |
187 | "bitflags", | 187 | "bitflags", |
188 | "chalk-derive", | 188 | "chalk-derive", |
@@ -191,9 +191,9 @@ dependencies = [ | |||
191 | 191 | ||
192 | [[package]] | 192 | [[package]] |
193 | name = "chalk-recursive" | 193 | name = "chalk-recursive" |
194 | version = "0.60.0" | 194 | version = "0.64.0" |
195 | source = "registry+https://github.com/rust-lang/crates.io-index" | 195 | source = "registry+https://github.com/rust-lang/crates.io-index" |
196 | checksum = "1b9386936070be4545bfa22b094b7065af79aa2aeaccc945438f1c5ffe74c30a" | 196 | checksum = "072ffcf17243c2aa3e4b9ea6de3d29e7ef64cfdb0ceccaa431965070a1dc1475" |
197 | dependencies = [ | 197 | dependencies = [ |
198 | "chalk-derive", | 198 | "chalk-derive", |
199 | "chalk-ir", | 199 | "chalk-ir", |
@@ -204,9 +204,9 @@ dependencies = [ | |||
204 | 204 | ||
205 | [[package]] | 205 | [[package]] |
206 | name = "chalk-solve" | 206 | name = "chalk-solve" |
207 | version = "0.60.0" | 207 | version = "0.64.0" |
208 | source = "registry+https://github.com/rust-lang/crates.io-index" | 208 | source = "registry+https://github.com/rust-lang/crates.io-index" |
209 | checksum = "7c12a1ec7e850b50a049f27ef9cf5df3056bbd1acbb3eeb44d024e501a641f3a" | 209 | checksum = "97d4920c9ef2b26dd0b98ffdf070e27fa31e0b6f637463132083cee597e3d326" |
210 | dependencies = [ | 210 | dependencies = [ |
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]] |
686 | name = "idna" | 687 | name = "idna" |
687 | version = "0.2.2" | 688 | version = "0.2.3" |
688 | source = "registry+https://github.com/rust-lang/crates.io-index" | 689 | source = "registry+https://github.com/rust-lang/crates.io-index" |
689 | checksum = "89829a5d69c23d348314a7ac337fe39173b61149a9864deabd260983aed48c21" | 690 | checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" |
690 | dependencies = [ | 691 | dependencies = [ |
691 | "matches", | 692 | "matches", |
692 | "unicode-bidi", | 693 | "unicode-bidi", |
@@ -1292,9 +1293,9 @@ dependencies = [ | |||
1292 | 1293 | ||
1293 | [[package]] | 1294 | [[package]] |
1294 | name = "redox_syscall" | 1295 | name = "redox_syscall" |
1295 | version = "0.2.5" | 1296 | version = "0.2.6" |
1296 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1297 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1297 | checksum = "94341e4e44e24f6b591b59e47a8a027df12e008d73fd5672dbea9cc22f4507d9" | 1298 | checksum = "8270314b5ccceb518e7e578952f0b72b88222d02e8f77f5ecf7abbb673539041" |
1298 | dependencies = [ | 1299 | dependencies = [ |
1299 | "bitflags", | 1300 | "bitflags", |
1300 | ] | 1301 | ] |
@@ -1391,9 +1392,9 @@ dependencies = [ | |||
1391 | 1392 | ||
1392 | [[package]] | 1393 | [[package]] |
1393 | name = "rustc-ap-rustc_lexer" | 1394 | name = "rustc-ap-rustc_lexer" |
1394 | version = "714.0.0" | 1395 | version = "716.0.0" |
1395 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1396 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1396 | checksum = "a35856f140bed0dc7c7d6ba2134099d337377a3a4e11bfc79bccabf1fd4c9d42" | 1397 | checksum = "12eac7554c1d3f49f105f14d53c0f3402220e875983113562701d8e597a0995c" |
1397 | dependencies = [ | 1398 | dependencies = [ |
1398 | "unicode-xid", | 1399 | "unicode-xid", |
1399 | ] | 1400 | ] |
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 | |||
13 | crossbeam-channel = "0.5.0" | 13 | crossbeam-channel = "0.5.0" |
14 | log = "0.4.8" | 14 | log = "0.4.8" |
15 | cargo_metadata = "0.13" | 15 | cargo_metadata = "0.13" |
16 | serde = { version = "1.0.106", features = ["derive"] } | ||
16 | serde_json = "1.0.48" | 17 | serde_json = "1.0.48" |
17 | jod-thread = "0.1.1" | 18 | jod-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 | ||
5 | use std::{ | 5 | use 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 | ||
13 | use crossbeam_channel::{never, select, unbounded, Receiver, Sender}; | 13 | use crossbeam_channel::{never, select, unbounded, Receiver, Sender}; |
14 | use serde::Deserialize; | ||
14 | use stdx::JodChild; | 15 | use stdx::JodChild; |
15 | 16 | ||
16 | pub use cargo_metadata::diagnostic::{ | 17 | pub use cargo_metadata::diagnostic::{ |
@@ -128,7 +129,7 @@ struct FlycheckActor { | |||
128 | 129 | ||
129 | enum Event { | 130 | enum Event { |
130 | Restart(Restart), | 131 | Restart(Restart), |
131 | CheckEvent(Option<cargo_metadata::Message>), | 132 | CheckEvent(Option<CargoMessage>), |
132 | } | 133 | } |
133 | 134 | ||
134 | impl FlycheckActor { | 135 | impl 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 | ||
267 | impl CargoHandle { | 263 | impl CargoHandle { |
@@ -294,14 +290,11 @@ impl CargoHandle { | |||
294 | 290 | ||
295 | struct CargoActor { | 291 | struct 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 | ||
300 | impl CargoActor { | 296 | impl 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 | |||
352 | enum CargoMessage { | ||
353 | CompilerArtifact(cargo_metadata::Artifact), | ||
354 | Diagnostic(Diagnostic), | ||
355 | } | ||
356 | |||
357 | #[derive(Deserialize)] | ||
358 | #[serde(untagged)] | ||
359 | enum 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 | ||
43 | fn 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 | |||
43 | fn check_at(ra_fixture: &str, expect: Expect) { | 51 | fn 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] |
137 | fn nested_module_scoping() { | ||
138 | check_block_scopes_at( | ||
139 | r#" | ||
140 | fn 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] | ||
137 | fn legacy_macro_items() { | 161 | fn 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) = ¤t_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" | |||
18 | log = "0.4.8" | 18 | log = "0.4.8" |
19 | rustc-hash = "1.1.0" | 19 | rustc-hash = "1.1.0" |
20 | scoped-tls = "1" | 20 | scoped-tls = "1" |
21 | chalk-solve = { version = "0.60", default-features = false } | 21 | chalk-solve = { version = "0.64", default-features = false } |
22 | chalk-ir = "0.60" | 22 | chalk-ir = "0.64" |
23 | chalk-recursive = "0.60" | 23 | chalk-recursive = "0.64" |
24 | la-arena = { version = "0.2.0", path = "../../lib/arena" } | 24 | la-arena = { version = "0.2.0", path = "../../lib/arena" } |
25 | 25 | ||
26 | stdx = { path = "../stdx", version = "0.0.0" } | 26 | stdx = { path = "../stdx", version = "0.0.0" } |
diff --git a/crates/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/extract_function.rs b/crates/ide_assists/src/handlers/extract_function.rs index 78a57fbdc..5f80a40c8 100644 --- a/crates/ide_assists/src/handlers/extract_function.rs +++ b/crates/ide_assists/src/handlers/extract_function.rs | |||
@@ -1227,9 +1227,19 @@ fn make_body( | |||
1227 | FunctionBody::Expr(expr) => { | 1227 | FunctionBody::Expr(expr) => { |
1228 | let expr = rewrite_body_segment(ctx, &fun.params, &handler, expr.syntax()); | 1228 | let expr = rewrite_body_segment(ctx, &fun.params, &handler, expr.syntax()); |
1229 | let expr = ast::Expr::cast(expr).unwrap(); | 1229 | let expr = ast::Expr::cast(expr).unwrap(); |
1230 | let expr = expr.dedent(old_indent).indent(IndentLevel(1)); | 1230 | match expr { |
1231 | ast::Expr::BlockExpr(block) => { | ||
1232 | // If the extracted expression is itself a block, there is no need to wrap it inside another block. | ||
1233 | let block = block.dedent(old_indent); | ||
1234 | // Recreate the block for formatting consistency with other extracted functions. | ||
1235 | make::block_expr(block.statements(), block.tail_expr()) | ||
1236 | } | ||
1237 | _ => { | ||
1238 | let expr = expr.dedent(old_indent).indent(IndentLevel(1)); | ||
1231 | 1239 | ||
1232 | make::block_expr(Vec::new(), Some(expr)) | 1240 | make::block_expr(Vec::new(), Some(expr)) |
1241 | } | ||
1242 | } | ||
1233 | } | 1243 | } |
1234 | FunctionBody::Span { parent, text_range } => { | 1244 | FunctionBody::Span { parent, text_range } => { |
1235 | let mut elements: Vec<_> = parent | 1245 | let mut elements: Vec<_> = parent |
@@ -1544,7 +1554,7 @@ fn foo() { | |||
1544 | } | 1554 | } |
1545 | 1555 | ||
1546 | fn $0fun_name() -> i32 { | 1556 | fn $0fun_name() -> i32 { |
1547 | { 1 + 1 } | 1557 | 1 + 1 |
1548 | }"#, | 1558 | }"#, |
1549 | ); | 1559 | ); |
1550 | } | 1560 | } |
@@ -2526,17 +2536,15 @@ fn foo() { | |||
2526 | } | 2536 | } |
2527 | 2537 | ||
2528 | fn $0fun_name(n: &mut i32) { | 2538 | fn $0fun_name(n: &mut i32) { |
2529 | { | 2539 | *n += *n; |
2530 | *n += *n; | 2540 | bar(*n); |
2531 | bar(*n); | 2541 | bar(*n+1); |
2532 | bar(*n+1); | 2542 | bar(*n**n); |
2533 | bar(*n**n); | 2543 | bar(&*n); |
2534 | bar(&*n); | 2544 | n.inc(); |
2535 | n.inc(); | 2545 | let v = n; |
2536 | let v = n; | 2546 | *v = v.succ(); |
2537 | *v = v.succ(); | 2547 | n.succ(); |
2538 | n.succ(); | ||
2539 | } | ||
2540 | }", | 2548 | }", |
2541 | ); | 2549 | ); |
2542 | } | 2550 | } |
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 | ||
154 | fn resolve_enum_def(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<hir::Enum> { | 158 | #[derive(Eq, PartialEq, Clone)] |
159 | enum ExtendedEnum { | ||
160 | Bool, | ||
161 | Enum(hir::Enum), | ||
162 | } | ||
163 | |||
164 | #[derive(Eq, PartialEq, Clone)] | ||
165 | enum ExtendedVariant { | ||
166 | True, | ||
167 | False, | ||
168 | Variant(hir::Variant), | ||
169 | } | ||
170 | |||
171 | fn lift_enum(e: hir::Enum) -> ExtendedEnum { | ||
172 | ExtendedEnum::Enum(e) | ||
173 | } | ||
174 | |||
175 | impl 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 | |||
188 | fn 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 | ||
161 | fn resolve_tuple_of_enum_def( | 201 | fn 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 | ||
180 | fn build_pat(db: &RootDatabase, module: hir::Module, var: hir::Variant) -> Option<ast::Pat> { | 226 | fn 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/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/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 | }; |
18 | use lsp_types::{ClientCapabilities, MarkupKind}; | 18 | use lsp_types::{ClientCapabilities, MarkupKind}; |
19 | use project_model::{CargoConfig, ProjectJson, ProjectJsonData, ProjectManifest, RustcSource}; | 19 | use project_model::{CargoConfig, ProjectJson, ProjectJsonData, ProjectManifest, RustcSource}; |
20 | use rustc_hash::FxHashSet; | 20 | use rustc_hash::{FxHashMap, FxHashSet}; |
21 | use serde::{de::DeserializeOwned, Deserialize}; | 21 | use serde::{de::DeserializeOwned, Deserialize}; |
22 | use vfs::AbsPathBuf; | 22 | use 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)] |
14 | pub struct DiagnosticsMapConfig { | 14 | pub 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. |
3 | use std::{collections::HashMap, path::Path}; | 3 | use std::{ |
4 | collections::HashMap, | ||
5 | path::{Path, PathBuf}, | ||
6 | }; | ||
4 | 7 | ||
5 | use flycheck::{DiagnosticLevel, DiagnosticSpan}; | 8 | use flycheck::{DiagnosticLevel, DiagnosticSpan}; |
6 | use stdx::format_to; | 9 | use 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 |
44 | fn location(workspace_root: &Path, span: &DiagnosticSpan) -> lsp_types::Location { | 47 | fn 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. |
61 | fn primary_location(workspace_root: &Path, span: &DiagnosticSpan) -> lsp_types::Location { | 68 | fn 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`. |
78 | fn diagnostic_related_information( | 89 | fn 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. | ||
101 | fn 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 | |||
87 | struct SubDiagnostic { | 112 | struct 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 | ||
97 | fn map_rust_child_diagnostic( | 122 | fn 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/src/lib.rs b/crates/stdx/src/lib.rs index e3eb10915..857567a85 100644 --- a/crates/stdx/src/lib.rs +++ b/crates/stdx/src/lib.rs | |||
@@ -179,6 +179,18 @@ where | |||
179 | start..start + len | 179 | start..start + len |
180 | } | 180 | } |
181 | 181 | ||
182 | pub 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 | |||
182 | #[repr(transparent)] | 194 | #[repr(transparent)] |
183 | pub struct JodChild(pub std::process::Child); | 195 | pub struct JodChild(pub std::process::Child); |
184 | 196 | ||
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 | |||
14 | cov-mark = { version = "1.1", features = ["thread-local"] } | 14 | cov-mark = { version = "1.1", features = ["thread-local"] } |
15 | itertools = "0.10.0" | 15 | itertools = "0.10.0" |
16 | rowan = "=0.13.0-pre.3" | 16 | rowan = "=0.13.0-pre.3" |
17 | rustc_lexer = { version = "714.0.0", package = "rustc-ap-rustc_lexer" } | 17 | rustc_lexer = { version = "716.0.0", package = "rustc-ap-rustc_lexer" } |
18 | rustc-hash = "1.1.0" | 18 | rustc-hash = "1.1.0" |
19 | arrayvec = "0.7" | 19 | arrayvec = "0.7" |
20 | once_cell = "1.3.1" | 20 | once_cell = "1.3.1" |
diff --git a/crates/syntax/src/ast/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 | ||
297 | pub 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 @@ | |||
1 | SOURCE_FILE@0..63 | 1 | SOURCE_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 @@ | |||
1 | fn foo() { | 1 | fn 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 699a48e63..16b23adc6 100644 --- a/docs/dev/README.md +++ b/docs/dev/README.md | |||
@@ -235,7 +235,11 @@ If it fails because of something that needs to be fixed, remove the release tag | |||
235 | Make sure to remove the new changelog post created when running `cargo xtask release` a second time. | 235 | Make sure to remove the new changelog post created when running `cargo xtask release` a second time. |
236 | 236 | ||
237 | We release "nightly" every night automatically and promote the latest nightly to "stable" manually, every week. | 237 | We release "nightly" every night automatically and promote the latest nightly to "stable" manually, every week. |
238 | |||
238 | We don't do "patch" releases, unless something truly egregious comes up. | 239 | We don't do "patch" releases, unless something truly egregious comes up. |
240 | To do a patch release, cherry-pick the fix on top of the current `release` branch and push the branch. | ||
241 | There'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. | ||
242 | Note: 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. | ||
239 | 243 | ||
240 | ## Permissions | 244 | ## Permissions |
241 | 245 | ||
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 | -- |
148 | List of rust-analyzer diagnostics to disable. | 148 | List of rust-analyzer diagnostics to disable. |
149 | -- | 149 | -- |
150 | [[rust-analyzer.diagnostics.remapPrefix]]rust-analyzer.diagnostics.remapPrefix (default: `{}`):: | ||
151 | + | ||
152 | -- | ||
153 | Map of prefixes to be substituted when parsing diagnostic file paths. | ||
154 | This 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/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 | } |