aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/ISSUE_TEMPLATE/critical_nightly_regression.md17
-rw-r--r--Cargo.lock37
-rw-r--r--crates/base_db/src/input.rs1
-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/src/syntax_highlighting/highlight.rs182
-rw-r--r--crates/ide/src/syntax_highlighting/tags.rs51
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html12
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html12
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_injection.html2
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html6
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlighting.html32
-rw-r--r--crates/ide_assists/src/ast_transform.rs33
-rw-r--r--crates/ide_assists/src/handlers/convert_tuple_struct_to_named_struct.rs516
-rw-r--r--crates/ide_assists/src/handlers/extract_function.rs36
-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/ide_assists/src/lib.rs2
-rw-r--r--crates/ide_assists/src/tests/generated.rs41
-rw-r--r--crates/ide_assists/src/utils.rs3
-rw-r--r--crates/ide_db/src/search.rs58
-rw-r--r--crates/parser/src/grammar/patterns.rs6
-rw-r--r--crates/proc_macro_api/Cargo.toml7
-rw-r--r--crates/proc_macro_api/src/lib.rs1
-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/rust-analyzer/src/reload.rs1
-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/algo.rs19
-rw-r--r--crates/syntax/src/ast/make.rs34
-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.md22
-rw-r--r--docs/user/generated_config.adoc6
-rw-r--r--docs/user/manual.adoc74
-rw-r--r--editors/code/package.json7
-rw-r--r--xtask/src/release.rs39
-rw-r--r--xtask/src/release/changelog.rs159
50 files changed, 2092 insertions, 504 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..907973412 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",
@@ -1174,6 +1175,7 @@ dependencies = [
1174 "log", 1175 "log",
1175 "memmap", 1176 "memmap",
1176 "object", 1177 "object",
1178 "profile",
1177 "serde", 1179 "serde",
1178 "serde_json", 1180 "serde_json",
1179 "snap", 1181 "snap",
@@ -1292,9 +1294,9 @@ dependencies = [
1292 1294
1293[[package]] 1295[[package]]
1294name = "redox_syscall" 1296name = "redox_syscall"
1295version = "0.2.5" 1297version = "0.2.6"
1296source = "registry+https://github.com/rust-lang/crates.io-index" 1298source = "registry+https://github.com/rust-lang/crates.io-index"
1297checksum = "94341e4e44e24f6b591b59e47a8a027df12e008d73fd5672dbea9cc22f4507d9" 1299checksum = "8270314b5ccceb518e7e578952f0b72b88222d02e8f77f5ecf7abbb673539041"
1298dependencies = [ 1300dependencies = [
1299 "bitflags", 1301 "bitflags",
1300] 1302]
@@ -1391,9 +1393,9 @@ dependencies = [
1391 1393
1392[[package]] 1394[[package]]
1393name = "rustc-ap-rustc_lexer" 1395name = "rustc-ap-rustc_lexer"
1394version = "714.0.0" 1396version = "716.0.0"
1395source = "registry+https://github.com/rust-lang/crates.io-index" 1397source = "registry+https://github.com/rust-lang/crates.io-index"
1396checksum = "a35856f140bed0dc7c7d6ba2134099d337377a3a4e11bfc79bccabf1fd4c9d42" 1398checksum = "12eac7554c1d3f49f105f14d53c0f3402220e875983113562701d8e597a0995c"
1397dependencies = [ 1399dependencies = [
1398 "unicode-xid", 1400 "unicode-xid",
1399] 1401]
@@ -1573,6 +1575,9 @@ version = "0.0.0"
1573dependencies = [ 1575dependencies = [
1574 "always-assert", 1576 "always-assert",
1575 "backtrace", 1577 "backtrace",
1578 "libc",
1579 "miow",
1580 "winapi",
1576] 1581]
1577 1582
1578[[package]] 1583[[package]]
diff --git a/crates/base_db/src/input.rs b/crates/base_db/src/input.rs
index 07628935f..0ef77ef5d 100644
--- a/crates/base_db/src/input.rs
+++ b/crates/base_db/src/input.rs
@@ -239,6 +239,7 @@ impl CrateGraph {
239 name: CrateName, 239 name: CrateName,
240 to: CrateId, 240 to: CrateId,
241 ) -> Result<(), CyclicDependenciesError> { 241 ) -> Result<(), CyclicDependenciesError> {
242 let _p = profile::span("add_dep");
242 if self.dfs_find(from, to, &mut FxHashSet::default()) { 243 if self.dfs_find(from, to, &mut FxHashSet::default()) {
243 return Err(CyclicDependenciesError { 244 return Err(CyclicDependenciesError {
244 from: (from, self[from].display_name.clone()), 245 from: (from, self[from].display_name.clone()),
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/src/syntax_highlighting/highlight.rs b/crates/ide/src/syntax_highlighting/highlight.rs
index 18552459b..8731699dc 100644
--- a/crates/ide/src/syntax_highlighting/highlight.rs
+++ b/crates/ide/src/syntax_highlighting/highlight.rs
@@ -1,6 +1,6 @@
1//! Computes color for a single element. 1//! Computes color for a single element.
2 2
3use hir::{AsAssocItem, AssocItemContainer, Semantics, VariantDef}; 3use hir::{AsAssocItem, Semantics};
4use ide_db::{ 4use ide_db::{
5 defs::{Definition, NameClass, NameRefClass}, 5 defs::{Definition, NameClass, NameRefClass},
6 RootDatabase, SymbolKind, 6 RootDatabase, SymbolKind,
@@ -45,28 +45,26 @@ pub(super) fn element(
45 }; 45 };
46 46
47 match name_kind { 47 match name_kind {
48 Some(NameClass::ExternCrate(_)) => HlTag::Symbol(SymbolKind::Module).into(), 48 Some(NameClass::ExternCrate(_)) => SymbolKind::Module.into(),
49 Some(NameClass::Definition(def)) => highlight_def(db, def) | HlMod::Definition, 49 Some(NameClass::Definition(def)) => highlight_def(db, def) | HlMod::Definition,
50 Some(NameClass::ConstReference(def)) => highlight_def(db, def), 50 Some(NameClass::ConstReference(def)) => highlight_def(db, def),
51 Some(NameClass::PatFieldShorthand { field_ref, .. }) => { 51 Some(NameClass::PatFieldShorthand { field_ref, .. }) => {
52 let mut h = HlTag::Symbol(SymbolKind::Field).into(); 52 let mut h = HlTag::Symbol(SymbolKind::Field).into();
53 if let Definition::Field(field) = field_ref { 53 if let Definition::Field(field) = field_ref {
54 if let VariantDef::Union(_) = field.parent_def(db) { 54 if let hir::VariantDef::Union(_) = field.parent_def(db) {
55 h |= HlMod::Unsafe; 55 h |= HlMod::Unsafe;
56 } 56 }
57 } 57 }
58
59 h 58 h
60 } 59 }
61 None => highlight_name_by_syntax(name) | HlMod::Definition, 60 None => highlight_name_by_syntax(name) | HlMod::Definition,
62 } 61 }
63 } 62 }
64
65 // Highlight references like the definitions they resolve to 63 // Highlight references like the definitions they resolve to
66 NAME_REF if element.ancestors().any(|it| it.kind() == ATTR) => { 64 NAME_REF if element.ancestors().any(|it| it.kind() == ATTR) => {
67 // even though we track whether we are in an attribute or not we still need this special case 65 // even though we track whether we are in an attribute or not we still need this special case
68 // as otherwise we would emit unresolved references for name refs inside attributes 66 // as otherwise we would emit unresolved references for name refs inside attributes
69 Highlight::from(HlTag::Symbol(SymbolKind::Function)) 67 SymbolKind::Function.into()
70 } 68 }
71 NAME_REF => { 69 NAME_REF => {
72 let name_ref = element.into_node().and_then(ast::NameRef::cast).unwrap(); 70 let name_ref = element.into_node().and_then(ast::NameRef::cast).unwrap();
@@ -74,7 +72,7 @@ pub(super) fn element(
74 let is_self = name_ref.self_token().is_some(); 72 let is_self = name_ref.self_token().is_some();
75 let h = match NameRefClass::classify(sema, &name_ref) { 73 let h = match NameRefClass::classify(sema, &name_ref) {
76 Some(name_kind) => match name_kind { 74 Some(name_kind) => match name_kind {
77 NameRefClass::ExternCrate(_) => HlTag::Symbol(SymbolKind::Module).into(), 75 NameRefClass::ExternCrate(_) => SymbolKind::Module.into(),
78 NameRefClass::Definition(def) => { 76 NameRefClass::Definition(def) => {
79 if let Definition::Local(local) = &def { 77 if let Definition::Local(local) = &def {
80 if let Some(name) = local.name(db) { 78 if let Some(name) = local.name(db) {
@@ -95,7 +93,7 @@ pub(super) fn element(
95 if let Some(parent) = name_ref.syntax().parent() { 93 if let Some(parent) = name_ref.syntax().parent() {
96 if matches!(parent.kind(), FIELD_EXPR | RECORD_PAT_FIELD) { 94 if matches!(parent.kind(), FIELD_EXPR | RECORD_PAT_FIELD) {
97 if let Definition::Field(field) = def { 95 if let Definition::Field(field) = def {
98 if let VariantDef::Union(_) = field.parent_def(db) { 96 if let hir::VariantDef::Union(_) = field.parent_def(db) {
99 h |= HlMod::Unsafe; 97 h |= HlMod::Unsafe;
100 } 98 }
101 } 99 }
@@ -104,9 +102,7 @@ pub(super) fn element(
104 102
105 h 103 h
106 } 104 }
107 NameRefClass::FieldShorthand { .. } => { 105 NameRefClass::FieldShorthand { .. } => SymbolKind::Field.into(),
108 HlTag::Symbol(SymbolKind::Field).into()
109 }
110 }, 106 },
111 None if syntactic_name_ref_highlighting => { 107 None if syntactic_name_ref_highlighting => {
112 highlight_name_ref_by_syntax(name_ref, sema) 108 highlight_name_ref_by_syntax(name_ref, sema)
@@ -114,7 +110,7 @@ pub(super) fn element(
114 None => HlTag::UnresolvedReference.into(), 110 None => HlTag::UnresolvedReference.into(),
115 }; 111 };
116 if h.tag == HlTag::Symbol(SymbolKind::Module) && is_self { 112 if h.tag == HlTag::Symbol(SymbolKind::Module) && is_self {
117 HlTag::Symbol(SymbolKind::SelfParam).into() 113 SymbolKind::SelfParam.into()
118 } else { 114 } else {
119 h 115 h
120 } 116 }
@@ -135,7 +131,7 @@ pub(super) fn element(
135 INT_NUMBER | FLOAT_NUMBER => HlTag::NumericLiteral.into(), 131 INT_NUMBER | FLOAT_NUMBER => HlTag::NumericLiteral.into(),
136 BYTE => HlTag::ByteLiteral.into(), 132 BYTE => HlTag::ByteLiteral.into(),
137 CHAR => HlTag::CharLiteral.into(), 133 CHAR => HlTag::CharLiteral.into(),
138 QUESTION => Highlight::new(HlTag::Operator(HlOperator::Other)) | HlMod::ControlFlow, 134 QUESTION => HlTag::Operator(HlOperator::Other) | HlMod::ControlFlow,
139 LIFETIME => { 135 LIFETIME => {
140 let lifetime = element.into_node().and_then(ast::Lifetime::cast).unwrap(); 136 let lifetime = element.into_node().and_then(ast::Lifetime::cast).unwrap();
141 137
@@ -143,44 +139,31 @@ pub(super) fn element(
143 Some(NameClass::Definition(def)) => highlight_def(db, def) | HlMod::Definition, 139 Some(NameClass::Definition(def)) => highlight_def(db, def) | HlMod::Definition,
144 None => match NameRefClass::classify_lifetime(sema, &lifetime) { 140 None => match NameRefClass::classify_lifetime(sema, &lifetime) {
145 Some(NameRefClass::Definition(def)) => highlight_def(db, def), 141 Some(NameRefClass::Definition(def)) => highlight_def(db, def),
146 _ => Highlight::new(HlTag::Symbol(SymbolKind::LifetimeParam)), 142 _ => SymbolKind::LifetimeParam.into(),
147 }, 143 },
148 _ => Highlight::new(HlTag::Symbol(SymbolKind::LifetimeParam)) | HlMod::Definition, 144 _ => Highlight::from(SymbolKind::LifetimeParam) | HlMod::Definition,
149 } 145 }
150 } 146 }
151 p if p.is_punct() => match p { 147 p if p.is_punct() => match p {
152 T![&] if element.parent().and_then(ast::BinExpr::cast).is_some() => { 148 T![&] if parent_matches::<ast::BinExpr>(&element) => HlOperator::Bitwise.into(),
153 HlTag::Operator(HlOperator::Bitwise).into()
154 }
155 T![&] => { 149 T![&] => {
156 let h = HlTag::Operator(HlOperator::Other).into(); 150 let h = HlTag::Operator(HlOperator::Other).into();
157 let is_unsafe = element 151 let is_unsafe = element
158 .parent() 152 .parent()
159 .and_then(ast::RefExpr::cast) 153 .and_then(ast::RefExpr::cast)
160 .map(|ref_expr| sema.is_unsafe_ref_expr(&ref_expr)) 154 .map_or(false, |ref_expr| sema.is_unsafe_ref_expr(&ref_expr));
161 .unwrap_or(false);
162 if is_unsafe { 155 if is_unsafe {
163 h | HlMod::Unsafe 156 h | HlMod::Unsafe
164 } else { 157 } else {
165 h 158 h
166 } 159 }
167 } 160 }
168 T![::] | T![->] | T![=>] | T![..] | T![=] | T![@] | T![.] => { 161 T![::] | T![->] | T![=>] | T![..] | T![=] | T![@] | T![.] => HlOperator::Other.into(),
169 HlTag::Operator(HlOperator::Other).into() 162 T![!] if parent_matches::<ast::MacroCall>(&element) => SymbolKind::Macro.into(),
170 } 163 T![!] if parent_matches::<ast::NeverType>(&element) => HlTag::BuiltinType.into(),
171 T![!] if element.parent().and_then(ast::MacroCall::cast).is_some() => { 164 T![!] if parent_matches::<ast::PrefixExpr>(&element) => HlOperator::Logical.into(),
172 HlTag::Symbol(SymbolKind::Macro).into() 165 T![*] if parent_matches::<ast::PtrType>(&element) => HlTag::Keyword.into(),
173 } 166 T![*] if parent_matches::<ast::PrefixExpr>(&element) => {
174 T![!] if element.parent().and_then(ast::NeverType::cast).is_some() => {
175 HlTag::BuiltinType.into()
176 }
177 T![!] if element.parent().and_then(ast::PrefixExpr::cast).is_some() => {
178 HlTag::Operator(HlOperator::Logical).into()
179 }
180 T![*] if element.parent().and_then(ast::PtrType::cast).is_some() => {
181 HlTag::Keyword.into()
182 }
183 T![*] if element.parent().and_then(ast::PrefixExpr::cast).is_some() => {
184 let prefix_expr = element.parent().and_then(ast::PrefixExpr::cast)?; 167 let prefix_expr = element.parent().and_then(ast::PrefixExpr::cast)?;
185 168
186 let expr = prefix_expr.expr()?; 169 let expr = prefix_expr.expr()?;
@@ -188,12 +171,12 @@ pub(super) fn element(
188 if ty.is_raw_ptr() { 171 if ty.is_raw_ptr() {
189 HlTag::Operator(HlOperator::Other) | HlMod::Unsafe 172 HlTag::Operator(HlOperator::Other) | HlMod::Unsafe
190 } else if let Some(ast::PrefixOp::Deref) = prefix_expr.op_kind() { 173 } else if let Some(ast::PrefixOp::Deref) = prefix_expr.op_kind() {
191 HlTag::Operator(HlOperator::Other).into() 174 HlOperator::Other.into()
192 } else { 175 } else {
193 HlTag::Punctuation(HlPunct::Other).into() 176 HlPunct::Other.into()
194 } 177 }
195 } 178 }
196 T![-] if element.parent().and_then(ast::PrefixExpr::cast).is_some() => { 179 T![-] if parent_matches::<ast::PrefixExpr>(&element) => {
197 let prefix_expr = element.parent().and_then(ast::PrefixExpr::cast)?; 180 let prefix_expr = element.parent().and_then(ast::PrefixExpr::cast)?;
198 181
199 let expr = prefix_expr.expr()?; 182 let expr = prefix_expr.expr()?;
@@ -203,41 +186,31 @@ pub(super) fn element(
203 } 186 }
204 .into() 187 .into()
205 } 188 }
206 _ if element.parent().and_then(ast::PrefixExpr::cast).is_some() => { 189 _ if parent_matches::<ast::PrefixExpr>(&element) => HlOperator::Other.into(),
207 HlTag::Operator(HlOperator::Other).into()
208 }
209 T![+] | T![-] | T![*] | T![/] | T![+=] | T![-=] | T![*=] | T![/=] 190 T![+] | T![-] | T![*] | T![/] | T![+=] | T![-=] | T![*=] | T![/=]
210 if element.parent().and_then(ast::BinExpr::cast).is_some() => 191 if parent_matches::<ast::BinExpr>(&element) =>
211 { 192 {
212 HlTag::Operator(HlOperator::Arithmetic).into() 193 HlOperator::Arithmetic.into()
213 } 194 }
214 T![|] | T![&] | T![!] | T![^] | T![|=] | T![&=] | T![^=] 195 T![|] | T![&] | T![!] | T![^] | T![|=] | T![&=] | T![^=]
215 if element.parent().and_then(ast::BinExpr::cast).is_some() => 196 if parent_matches::<ast::BinExpr>(&element) =>
216 { 197 {
217 HlTag::Operator(HlOperator::Bitwise).into() 198 HlOperator::Bitwise.into()
218 } 199 }
219 T![&&] | T![||] if element.parent().and_then(ast::BinExpr::cast).is_some() => { 200 T![&&] | T![||] if parent_matches::<ast::BinExpr>(&element) => {
220 HlTag::Operator(HlOperator::Logical).into() 201 HlOperator::Logical.into()
221 } 202 }
222 T![>] | T![<] | T![==] | T![>=] | T![<=] | T![!=] 203 T![>] | T![<] | T![==] | T![>=] | T![<=] | T![!=]
223 if element.parent().and_then(ast::BinExpr::cast).is_some() => 204 if parent_matches::<ast::BinExpr>(&element) =>
224 { 205 {
225 HlTag::Operator(HlOperator::Comparison).into() 206 HlOperator::Comparison.into()
226 }
227 _ if element.parent().and_then(ast::BinExpr::cast).is_some() => {
228 HlTag::Operator(HlOperator::Other).into()
229 } 207 }
230 _ if element.parent().and_then(ast::RangeExpr::cast).is_some() => { 208 _ if parent_matches::<ast::BinExpr>(&element) => HlOperator::Other.into(),
231 HlTag::Operator(HlOperator::Other).into() 209 _ if parent_matches::<ast::RangeExpr>(&element) => HlOperator::Other.into(),
232 } 210 _ if parent_matches::<ast::RangePat>(&element) => HlOperator::Other.into(),
233 _ if element.parent().and_then(ast::RangePat::cast).is_some() => { 211 _ if parent_matches::<ast::RestPat>(&element) => HlOperator::Other.into(),
234 HlTag::Operator(HlOperator::Other).into() 212 _ if parent_matches::<ast::Attr>(&element) => HlTag::Attribute.into(),
235 } 213 kind => match kind {
236 _ if element.parent().and_then(ast::RestPat::cast).is_some() => {
237 HlTag::Operator(HlOperator::Other).into()
238 }
239 _ if element.parent().and_then(ast::Attr::cast).is_some() => HlTag::Attribute.into(),
240 kind => HlTag::Punctuation(match kind {
241 T!['['] | T![']'] => HlPunct::Bracket, 214 T!['['] | T![']'] => HlPunct::Bracket,
242 T!['{'] | T!['}'] => HlPunct::Brace, 215 T!['{'] | T!['}'] => HlPunct::Brace,
243 T!['('] | T![')'] => HlPunct::Parenthesis, 216 T!['('] | T![')'] => HlPunct::Parenthesis,
@@ -247,22 +220,24 @@ pub(super) fn element(
247 T![;] => HlPunct::Semi, 220 T![;] => HlPunct::Semi,
248 T![.] => HlPunct::Dot, 221 T![.] => HlPunct::Dot,
249 _ => HlPunct::Other, 222 _ => HlPunct::Other,
250 }) 223 }
251 .into(), 224 .into(),
252 }, 225 },
253 226
254 k if k.is_keyword() => { 227 k if k.is_keyword() => {
255 let h = Highlight::new(HlTag::Keyword); 228 let h = Highlight::new(HlTag::Keyword);
256 match k { 229 match k {
257 T![break] 230 T![await]
231 | T![break]
258 | T![continue] 232 | T![continue]
259 | T![else] 233 | T![else]
260 | T![if] 234 | T![if]
235 | T![in]
261 | T![loop] 236 | T![loop]
262 | T![match] 237 | T![match]
263 | T![return] 238 | T![return]
264 | T![while] 239 | T![while]
265 | T![in] => h | HlMod::ControlFlow, 240 | T![yield] => h | HlMod::ControlFlow,
266 T![for] if !is_child_of_impl(&element) => h | HlMod::ControlFlow, 241 T![for] if !is_child_of_impl(&element) => h | HlMod::ControlFlow,
267 T![unsafe] => h | HlMod::Unsafe, 242 T![unsafe] => h | HlMod::Unsafe,
268 T![true] | T![false] => HlTag::BoolLiteral.into(), 243 T![true] | T![false] => HlTag::BoolLiteral.into(),
@@ -301,7 +276,6 @@ pub(super) fn element(
301 hash((name, shadow_count)) 276 hash((name, shadow_count))
302 } 277 }
303} 278}
304
305fn highlight_def(db: &RootDatabase, def: Definition) -> Highlight { 279fn highlight_def(db: &RootDatabase, def: Definition) -> Highlight {
306 match def { 280 match def {
307 Definition::Macro(_) => HlTag::Symbol(SymbolKind::Macro), 281 Definition::Macro(_) => HlTag::Symbol(SymbolKind::Macro),
@@ -317,12 +291,12 @@ fn highlight_def(db: &RootDatabase, def: Definition) -> Highlight {
317 } 291 }
318 292
319 match item.container(db) { 293 match item.container(db) {
320 AssocItemContainer::Impl(i) => { 294 hir::AssocItemContainer::Impl(i) => {
321 if i.trait_(db).is_some() { 295 if i.trait_(db).is_some() {
322 h |= HlMod::Trait; 296 h |= HlMod::Trait;
323 } 297 }
324 } 298 }
325 AssocItemContainer::Trait(_t) => { 299 hir::AssocItemContainer::Trait(_t) => {
326 h |= HlMod::Trait; 300 h |= HlMod::Trait;
327 } 301 }
328 } 302 }
@@ -342,12 +316,12 @@ fn highlight_def(db: &RootDatabase, def: Definition) -> Highlight {
342 if let Some(item) = konst.as_assoc_item(db) { 316 if let Some(item) = konst.as_assoc_item(db) {
343 h |= HlMod::Associated; 317 h |= HlMod::Associated;
344 match item.container(db) { 318 match item.container(db) {
345 AssocItemContainer::Impl(i) => { 319 hir::AssocItemContainer::Impl(i) => {
346 if i.trait_(db).is_some() { 320 if i.trait_(db).is_some() {
347 h |= HlMod::Trait; 321 h |= HlMod::Trait;
348 } 322 }
349 } 323 }
350 AssocItemContainer::Trait(_t) => { 324 hir::AssocItemContainer::Trait(_t) => {
351 h |= HlMod::Trait; 325 h |= HlMod::Trait;
352 } 326 }
353 } 327 }
@@ -361,12 +335,12 @@ fn highlight_def(db: &RootDatabase, def: Definition) -> Highlight {
361 if let Some(item) = type_.as_assoc_item(db) { 335 if let Some(item) = type_.as_assoc_item(db) {
362 h |= HlMod::Associated; 336 h |= HlMod::Associated;
363 match item.container(db) { 337 match item.container(db) {
364 AssocItemContainer::Impl(i) => { 338 hir::AssocItemContainer::Impl(i) => {
365 if i.trait_(db).is_some() { 339 if i.trait_(db).is_some() {
366 h |= HlMod::Trait; 340 h |= HlMod::Trait;
367 } 341 }
368 } 342 }
369 AssocItemContainer::Trait(_t) => { 343 hir::AssocItemContainer::Trait(_t) => {
370 h |= HlMod::Trait; 344 h |= HlMod::Trait;
371 } 345 }
372 } 346 }
@@ -425,7 +399,7 @@ fn highlight_method_call(
425 method_call: &ast::MethodCallExpr, 399 method_call: &ast::MethodCallExpr,
426) -> Option<Highlight> { 400) -> Option<Highlight> {
427 let func = sema.resolve_method_call(&method_call)?; 401 let func = sema.resolve_method_call(&method_call)?;
428 let mut h = HlTag::Symbol(SymbolKind::Function).into(); 402 let mut h = SymbolKind::Function.into();
429 h |= HlMod::Associated; 403 h |= HlMod::Associated;
430 if func.is_unsafe(sema.db) || sema.is_unsafe_method_call(&method_call) { 404 if func.is_unsafe(sema.db) || sema.is_unsafe_method_call(&method_call) {
431 h |= HlMod::Unsafe; 405 h |= HlMod::Unsafe;
@@ -461,20 +435,20 @@ fn highlight_name_by_syntax(name: ast::Name) -> Highlight {
461 }; 435 };
462 436
463 let tag = match parent.kind() { 437 let tag = match parent.kind() {
464 STRUCT => HlTag::Symbol(SymbolKind::Struct), 438 STRUCT => SymbolKind::Struct,
465 ENUM => HlTag::Symbol(SymbolKind::Enum), 439 ENUM => SymbolKind::Enum,
466 VARIANT => HlTag::Symbol(SymbolKind::Variant), 440 VARIANT => SymbolKind::Variant,
467 UNION => HlTag::Symbol(SymbolKind::Union), 441 UNION => SymbolKind::Union,
468 TRAIT => HlTag::Symbol(SymbolKind::Trait), 442 TRAIT => SymbolKind::Trait,
469 TYPE_ALIAS => HlTag::Symbol(SymbolKind::TypeAlias), 443 TYPE_ALIAS => SymbolKind::TypeAlias,
470 TYPE_PARAM => HlTag::Symbol(SymbolKind::TypeParam), 444 TYPE_PARAM => SymbolKind::TypeParam,
471 RECORD_FIELD => HlTag::Symbol(SymbolKind::Field), 445 RECORD_FIELD => SymbolKind::Field,
472 MODULE => HlTag::Symbol(SymbolKind::Module), 446 MODULE => SymbolKind::Module,
473 FN => HlTag::Symbol(SymbolKind::Function), 447 FN => SymbolKind::Function,
474 CONST => HlTag::Symbol(SymbolKind::Const), 448 CONST => SymbolKind::Const,
475 STATIC => HlTag::Symbol(SymbolKind::Static), 449 STATIC => SymbolKind::Static,
476 IDENT_PAT => HlTag::Symbol(SymbolKind::Local), 450 IDENT_PAT => SymbolKind::Local,
477 _ => default, 451 _ => return default.into(),
478 }; 452 };
479 453
480 tag.into() 454 tag.into()
@@ -492,20 +466,15 @@ fn highlight_name_ref_by_syntax(name: ast::NameRef, sema: &Semantics<RootDatabas
492 METHOD_CALL_EXPR => { 466 METHOD_CALL_EXPR => {
493 return ast::MethodCallExpr::cast(parent) 467 return ast::MethodCallExpr::cast(parent)
494 .and_then(|it| highlight_method_call(sema, &it)) 468 .and_then(|it| highlight_method_call(sema, &it))
495 .unwrap_or_else(|| HlTag::Symbol(SymbolKind::Function).into()); 469 .unwrap_or_else(|| SymbolKind::Function.into());
496 } 470 }
497 FIELD_EXPR => { 471 FIELD_EXPR => {
498 let h = HlTag::Symbol(SymbolKind::Field); 472 let h = HlTag::Symbol(SymbolKind::Field);
499 let is_union = ast::FieldExpr::cast(parent) 473 let is_union = ast::FieldExpr::cast(parent)
500 .and_then(|field_expr| { 474 .and_then(|field_expr| sema.resolve_field(&field_expr))
501 let field = sema.resolve_field(&field_expr)?; 475 .map_or(false, |field| {
502 Some(if let VariantDef::Union(_) = field.parent_def(sema.db) { 476 matches!(field.parent_def(sema.db), hir::VariantDef::Union(_))
503 true 477 });
504 } else {
505 false
506 })
507 })
508 .unwrap_or(false);
509 if is_union { 478 if is_union {
510 h | HlMod::Unsafe 479 h | HlMod::Unsafe
511 } else { 480 } else {
@@ -522,9 +491,9 @@ fn highlight_name_ref_by_syntax(name: ast::NameRef, sema: &Semantics<RootDatabas
522 _ => { 491 _ => {
523 // within path, decide whether it is module or adt by checking for uppercase name 492 // within path, decide whether it is module or adt by checking for uppercase name
524 return if name.text().chars().next().unwrap_or_default().is_uppercase() { 493 return if name.text().chars().next().unwrap_or_default().is_uppercase() {
525 HlTag::Symbol(SymbolKind::Struct) 494 SymbolKind::Struct
526 } else { 495 } else {
527 HlTag::Symbol(SymbolKind::Module) 496 SymbolKind::Module
528 } 497 }
529 .into(); 498 .into();
530 } 499 }
@@ -535,11 +504,11 @@ fn highlight_name_ref_by_syntax(name: ast::NameRef, sema: &Semantics<RootDatabas
535 }; 504 };
536 505
537 match parent.kind() { 506 match parent.kind() {
538 CALL_EXPR => HlTag::Symbol(SymbolKind::Function).into(), 507 CALL_EXPR => SymbolKind::Function.into(),
539 _ => if name.text().chars().next().unwrap_or_default().is_uppercase() { 508 _ => if name.text().chars().next().unwrap_or_default().is_uppercase() {
540 HlTag::Symbol(SymbolKind::Struct) 509 SymbolKind::Struct
541 } else { 510 } else {
542 HlTag::Symbol(SymbolKind::Const) 511 SymbolKind::Const
543 } 512 }
544 .into(), 513 .into(),
545 } 514 }
@@ -574,6 +543,11 @@ fn parents_match(mut node: NodeOrToken<SyntaxNode, SyntaxToken>, mut kinds: &[Sy
574 kinds.len() == 0 543 kinds.len() == 0
575} 544}
576 545
546#[inline]
547fn parent_matches<N: AstNode>(element: &SyntaxElement) -> bool {
548 element.parent().map_or(false, |it| N::can_cast(it.kind()))
549}
550
577fn is_child_of_impl(element: &SyntaxElement) -> bool { 551fn is_child_of_impl(element: &SyntaxElement) -> bool {
578 match element.parent() { 552 match element.parent() {
579 Some(e) => e.kind() == IMPL, 553 Some(e) => e.kind() == IMPL,
diff --git a/crates/ide/src/syntax_highlighting/tags.rs b/crates/ide/src/syntax_highlighting/tags.rs
index e58392d67..a304b3250 100644
--- a/crates/ide/src/syntax_highlighting/tags.rs
+++ b/crates/ide/src/syntax_highlighting/tags.rs
@@ -40,28 +40,33 @@ pub enum HlTag {
40#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] 40#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
41#[repr(u8)] 41#[repr(u8)]
42pub enum HlMod { 42pub enum HlMod {
43 /// Used for items in traits and impls.
44 Associated = 0,
43 /// Used to differentiate individual elements within attributes. 45 /// Used to differentiate individual elements within attributes.
44 Attribute = 0, 46 Attribute,
47 /// Callable item or value.
48 Callable,
49 /// Value that is being consumed in a function call
50 Consuming,
45 /// Used with keywords like `if` and `break`. 51 /// Used with keywords like `if` and `break`.
46 ControlFlow, 52 ControlFlow,
47 /// `foo` in `fn foo(x: i32)` is a definition, `foo` in `foo(90 + 2)` is 53 /// `foo` in `fn foo(x: i32)` is a definition, `foo` in `foo(90 + 2)` is
48 /// not. 54 /// not.
49 Definition, 55 Definition,
56 /// Doc-strings like this one.
50 Documentation, 57 Documentation,
58 /// Highlighting injection like rust code in doc strings or ra_fixture.
51 Injected, 59 Injected,
52 Mutable,
53 Consuming,
54 Callable,
55 /// Used for associated functions
56 Static,
57 /// Used for items in impls&traits.
58 Associated,
59 /// Used for intra doc links in doc injection. 60 /// Used for intra doc links in doc injection.
60 IntraDocLink, 61 IntraDocLink,
62 /// Mutable binding.
63 Mutable,
64 /// Used for associated functions.
65 Static,
61 /// Used for items in traits and trait impls. 66 /// Used for items in traits and trait impls.
62 Trait, 67 Trait,
63 68 // Keep this last!
64 /// Keep this last! 69 /// Used for unsafe functions, mutable statics, union accesses and unsafe operations.
65 Unsafe, 70 Unsafe,
66} 71}
67 72
@@ -169,17 +174,17 @@ impl fmt::Display for HlTag {
169 174
170impl HlMod { 175impl HlMod {
171 const ALL: &'static [HlMod; HlMod::Unsafe as u8 as usize + 1] = &[ 176 const ALL: &'static [HlMod; HlMod::Unsafe as u8 as usize + 1] = &[
177 HlMod::Associated,
172 HlMod::Attribute, 178 HlMod::Attribute,
179 HlMod::Callable,
180 HlMod::Consuming,
173 HlMod::ControlFlow, 181 HlMod::ControlFlow,
174 HlMod::Definition, 182 HlMod::Definition,
175 HlMod::Documentation, 183 HlMod::Documentation,
176 HlMod::IntraDocLink,
177 HlMod::Injected, 184 HlMod::Injected,
185 HlMod::IntraDocLink,
178 HlMod::Mutable, 186 HlMod::Mutable,
179 HlMod::Consuming,
180 HlMod::Callable,
181 HlMod::Static, 187 HlMod::Static,
182 HlMod::Associated,
183 HlMod::Trait, 188 HlMod::Trait,
184 HlMod::Unsafe, 189 HlMod::Unsafe,
185 ]; 190 ];
@@ -229,6 +234,24 @@ impl From<HlTag> for Highlight {
229 } 234 }
230} 235}
231 236
237impl From<HlOperator> for Highlight {
238 fn from(op: HlOperator) -> Highlight {
239 Highlight::new(HlTag::Operator(op))
240 }
241}
242
243impl From<HlPunct> for Highlight {
244 fn from(punct: HlPunct) -> Highlight {
245 Highlight::new(HlTag::Punctuation(punct))
246 }
247}
248
249impl From<SymbolKind> for Highlight {
250 fn from(sym: SymbolKind) -> Highlight {
251 Highlight::new(HlTag::Symbol(sym))
252 }
253}
254
232impl Highlight { 255impl Highlight {
233 pub(crate) fn new(tag: HlTag) -> Highlight { 256 pub(crate) fn new(tag: HlTag) -> Highlight {
234 Highlight { tag, mods: HlMods::default() } 257 Highlight { tag, mods: HlMods::default() }
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html b/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html
index 8cde3906c..a0ea1db34 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html
@@ -42,17 +42,17 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
42<span class="keyword">struct</span> <span class="struct declaration">foo</span> <span class="brace">{</span><span class="brace">}</span> 42<span class="keyword">struct</span> <span class="struct declaration">foo</span> <span class="brace">{</span><span class="brace">}</span>
43 43
44<span class="keyword">impl</span> <span class="struct">foo</span> <span class="brace">{</span> 44<span class="keyword">impl</span> <span class="struct">foo</span> <span class="brace">{</span>
45 <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration static associated">is_static</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span> 45 <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function associated declaration static">is_static</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
46 <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration associated">is_not_static</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration">self</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span> 46 <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function associated declaration">is_not_static</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration">self</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
47<span class="brace">}</span> 47<span class="brace">}</span>
48 48
49<span class="keyword">trait</span> <span class="trait declaration">t</span> <span class="brace">{</span> 49<span class="keyword">trait</span> <span class="trait declaration">t</span> <span class="brace">{</span>
50 <span class="keyword">fn</span> <span class="function declaration static associated trait">t_is_static</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span> 50 <span class="keyword">fn</span> <span class="function associated declaration static trait">t_is_static</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
51 <span class="keyword">fn</span> <span class="function declaration associated trait">t_is_not_static</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration">self</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span> 51 <span class="keyword">fn</span> <span class="function associated declaration trait">t_is_not_static</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration">self</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
52<span class="brace">}</span> 52<span class="brace">}</span>
53 53
54<span class="keyword">impl</span> <span class="trait">t</span> <span class="keyword">for</span> <span class="struct">foo</span> <span class="brace">{</span> 54<span class="keyword">impl</span> <span class="trait">t</span> <span class="keyword">for</span> <span class="struct">foo</span> <span class="brace">{</span>
55 <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration static associated trait">is_static</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span> 55 <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function associated declaration static trait">is_static</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
56 <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration associated trait">is_not_static</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration">self</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span> 56 <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function associated declaration trait">is_not_static</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration">self</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
57<span class="brace">}</span> 57<span class="brace">}</span>
58 </code></pre> \ No newline at end of file 58 </code></pre> \ No newline at end of file
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
index 6ee6d85fb..638f42c2f 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
@@ -50,7 +50,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
50 <span class="comment">// KILLER WHALE</span> 50 <span class="comment">// KILLER WHALE</span>
51 <span class="comment documentation">/// </span><span class="string_literal injected"> Ishmael."</span><span class="semicolon injected">;</span> 51 <span class="comment documentation">/// </span><span class="string_literal injected"> Ishmael."</span><span class="semicolon injected">;</span>
52 <span class="comment documentation">/// ```</span> 52 <span class="comment documentation">/// ```</span>
53 <span class="keyword">pub</span> <span class="keyword">const</span> <span class="constant declaration associated">bar</span><span class="colon">:</span> <span class="builtin_type">bool</span> <span class="operator">=</span> <span class="bool_literal">true</span><span class="semicolon">;</span> 53 <span class="keyword">pub</span> <span class="keyword">const</span> <span class="constant associated declaration">bar</span><span class="colon">:</span> <span class="builtin_type">bool</span> <span class="operator">=</span> <span class="bool_literal">true</span><span class="semicolon">;</span>
54 54
55 <span class="comment documentation">/// Constructs a new `Foo`.</span> 55 <span class="comment documentation">/// Constructs a new `Foo`.</span>
56 <span class="comment documentation">///</span> 56 <span class="comment documentation">///</span>
@@ -60,7 +60,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
60 <span class="comment documentation">/// #</span><span class="none injected"> </span><span class="attribute attribute injected">#</span><span class="attribute attribute injected">!</span><span class="attribute attribute injected">[</span><span class="function attribute injected">allow</span><span class="parenthesis attribute injected">(</span><span class="attribute attribute injected">unused_mut</span><span class="parenthesis attribute injected">)</span><span class="attribute attribute injected">]</span> 60 <span class="comment documentation">/// #</span><span class="none injected"> </span><span class="attribute attribute injected">#</span><span class="attribute attribute injected">!</span><span class="attribute attribute injected">[</span><span class="function attribute injected">allow</span><span class="parenthesis attribute injected">(</span><span class="attribute attribute injected">unused_mut</span><span class="parenthesis attribute injected">)</span><span class="attribute attribute injected">]</span>
61 <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="none injected"> </span><span class="keyword injected">mut</span><span class="none injected"> </span><span class="variable declaration injected mutable">foo</span><span class="colon injected">:</span><span class="none injected"> </span><span class="struct injected">Foo</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="struct injected">Foo</span><span class="operator injected">::</span><span class="function injected">new</span><span class="parenthesis injected">(</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span> 61 <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="none injected"> </span><span class="keyword injected">mut</span><span class="none injected"> </span><span class="variable declaration injected mutable">foo</span><span class="colon injected">:</span><span class="none injected"> </span><span class="struct injected">Foo</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="struct injected">Foo</span><span class="operator injected">::</span><span class="function injected">new</span><span class="parenthesis injected">(</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span>
62 <span class="comment documentation">/// ```</span> 62 <span class="comment documentation">/// ```</span>
63 <span class="keyword">pub</span> <span class="keyword">const</span> <span class="keyword">fn</span> <span class="function declaration static associated">new</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="struct">Foo</span> <span class="brace">{</span> 63 <span class="keyword">pub</span> <span class="keyword">const</span> <span class="keyword">fn</span> <span class="function associated declaration static">new</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="struct">Foo</span> <span class="brace">{</span>
64 <span class="struct">Foo</span> <span class="brace">{</span> <span class="field">bar</span><span class="colon">:</span> <span class="bool_literal">true</span> <span class="brace">}</span> 64 <span class="struct">Foo</span> <span class="brace">{</span> <span class="field">bar</span><span class="colon">:</span> <span class="bool_literal">true</span> <span class="brace">}</span>
65 <span class="brace">}</span> 65 <span class="brace">}</span>
66 66
@@ -94,15 +94,15 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
94 <span class="comment documentation">/// ```sh</span> 94 <span class="comment documentation">/// ```sh</span>
95 <span class="comment documentation">/// echo 1</span> 95 <span class="comment documentation">/// echo 1</span>
96 <span class="comment documentation">/// ```</span> 96 <span class="comment documentation">/// ```</span>
97 <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration associated">foo</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration">self</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">bool</span> <span class="brace">{</span> 97 <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function associated declaration">foo</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration">self</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">bool</span> <span class="brace">{</span>
98 <span class="bool_literal">true</span> 98 <span class="bool_literal">true</span>
99 <span class="brace">}</span> 99 <span class="brace">}</span>
100<span class="brace">}</span> 100<span class="brace">}</span>
101 101
102<span class="comment documentation">/// </span><span class="struct documentation intra_doc_link injected">[`Foo`](Foo)</span><span class="comment documentation"> is a struct</span> 102<span class="comment documentation">/// </span><span class="struct documentation injected intra_doc_link">[`Foo`](Foo)</span><span class="comment documentation"> is a struct</span>
103<span class="comment documentation">/// This function is &gt; </span><span class="function documentation intra_doc_link injected">[`all_the_links`](all_the_links)</span><span class="comment documentation"> &lt;</span> 103<span class="comment documentation">/// This function is &gt; </span><span class="function documentation injected intra_doc_link">[`all_the_links`](all_the_links)</span><span class="comment documentation"> &lt;</span>
104<span class="comment documentation">/// [`noop`](noop) is a macro below</span> 104<span class="comment documentation">/// [`noop`](noop) is a macro below</span>
105<span class="comment documentation">/// </span><span class="struct documentation intra_doc_link injected">[`Item`]</span><span class="comment documentation"> is a struct in the module </span><span class="module documentation intra_doc_link injected">[`module`]</span> 105<span class="comment documentation">/// </span><span class="struct documentation injected intra_doc_link">[`Item`]</span><span class="comment documentation"> is a struct in the module </span><span class="module documentation injected intra_doc_link">[`module`]</span>
106<span class="comment documentation">///</span> 106<span class="comment documentation">///</span>
107<span class="comment documentation">/// [`Item`]: module::Item</span> 107<span class="comment documentation">/// [`Item`]: module::Item</span>
108<span class="comment documentation">/// [mix_and_match]: ThisShouldntResolve</span> 108<span class="comment documentation">/// [mix_and_match]: ThisShouldntResolve</span>
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html b/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html
index 7c6694a27..6202a03ce 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html
@@ -42,7 +42,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
42<span class="keyword">fn</span> <span class="function declaration">main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span> 42<span class="keyword">fn</span> <span class="function declaration">main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
43 <span class="function">fixture</span><span class="parenthesis">(</span><span class="string_literal">r#"</span> 43 <span class="function">fixture</span><span class="parenthesis">(</span><span class="string_literal">r#"</span>
44 <span class="keyword">trait</span> <span class="trait declaration">Foo</span> <span class="brace">{</span> 44 <span class="keyword">trait</span> <span class="trait declaration">Foo</span> <span class="brace">{</span>
45 <span class="keyword">fn</span> <span class="function declaration static associated trait">foo</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span> 45 <span class="keyword">fn</span> <span class="function associated declaration static trait">foo</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
46 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"2 + 2 = {}"</span><span class="comma">,</span> <span class="numeric_literal">4</span><span class="parenthesis">)</span><span class="semicolon">;</span> 46 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"2 + 2 = {}"</span><span class="comma">,</span> <span class="numeric_literal">4</span><span class="parenthesis">)</span><span class="semicolon">;</span>
47 <span class="brace">}</span> 47 <span class="brace">}</span>
48 <span class="brace">}</span><span class="string_literal">"#</span> 48 <span class="brace">}</span><span class="string_literal">"#</span>
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html b/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
index 72910421d..68165bdbf 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
@@ -47,7 +47,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
47<span class="keyword">struct</span> <span class="struct declaration">HasUnsafeFn</span><span class="semicolon">;</span> 47<span class="keyword">struct</span> <span class="struct declaration">HasUnsafeFn</span><span class="semicolon">;</span>
48 48
49<span class="keyword">impl</span> <span class="struct">HasUnsafeFn</span> <span class="brace">{</span> 49<span class="keyword">impl</span> <span class="struct">HasUnsafeFn</span> <span class="brace">{</span>
50 <span class="keyword unsafe">unsafe</span> <span class="keyword">fn</span> <span class="function declaration associated unsafe">unsafe_method</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration">self</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span> 50 <span class="keyword unsafe">unsafe</span> <span class="keyword">fn</span> <span class="function associated declaration unsafe">unsafe_method</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration">self</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
51<span class="brace">}</span> 51<span class="brace">}</span>
52 52
53<span class="keyword">struct</span> <span class="struct declaration">TypeForStaticMut</span> <span class="brace">{</span> 53<span class="keyword">struct</span> <span class="struct declaration">TypeForStaticMut</span> <span class="brace">{</span>
@@ -62,11 +62,11 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
62<span class="brace">}</span> 62<span class="brace">}</span>
63 63
64<span class="keyword">trait</span> <span class="trait declaration">DoTheAutoref</span> <span class="brace">{</span> 64<span class="keyword">trait</span> <span class="trait declaration">DoTheAutoref</span> <span class="brace">{</span>
65 <span class="keyword">fn</span> <span class="function declaration associated trait">calls_autoref</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration">self</span><span class="parenthesis">)</span><span class="semicolon">;</span> 65 <span class="keyword">fn</span> <span class="function associated declaration trait">calls_autoref</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration">self</span><span class="parenthesis">)</span><span class="semicolon">;</span>
66<span class="brace">}</span> 66<span class="brace">}</span>
67 67
68<span class="keyword">impl</span> <span class="trait">DoTheAutoref</span> <span class="keyword">for</span> <span class="builtin_type">u16</span> <span class="brace">{</span> 68<span class="keyword">impl</span> <span class="trait">DoTheAutoref</span> <span class="keyword">for</span> <span class="builtin_type">u16</span> <span class="brace">{</span>
69 <span class="keyword">fn</span> <span class="function declaration associated trait">calls_autoref</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration">self</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span> 69 <span class="keyword">fn</span> <span class="function associated declaration trait">calls_autoref</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration">self</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
70<span class="brace">}</span> 70<span class="brace">}</span>
71 71
72<span class="keyword">fn</span> <span class="function declaration">main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span> 72<span class="keyword">fn</span> <span class="function declaration">main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlighting.html b/crates/ide/src/syntax_highlighting/test_data/highlighting.html
index c43bcb691..4319e8b50 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlighting.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlighting.html
@@ -67,25 +67,25 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
67<span class="brace">}</span> 67<span class="brace">}</span>
68 68
69<span class="keyword">trait</span> <span class="trait declaration">Bar</span> <span class="brace">{</span> 69<span class="keyword">trait</span> <span class="trait declaration">Bar</span> <span class="brace">{</span>
70 <span class="keyword">fn</span> <span class="function declaration associated trait">bar</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration">self</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">i32</span><span class="semicolon">;</span> 70 <span class="keyword">fn</span> <span class="function associated declaration trait">bar</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration">self</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">i32</span><span class="semicolon">;</span>
71<span class="brace">}</span> 71<span class="brace">}</span>
72 72
73<span class="keyword">impl</span> <span class="trait">Bar</span> <span class="keyword">for</span> <span class="struct">Foo</span> <span class="brace">{</span> 73<span class="keyword">impl</span> <span class="trait">Bar</span> <span class="keyword">for</span> <span class="struct">Foo</span> <span class="brace">{</span>
74 <span class="keyword">fn</span> <span class="function declaration associated trait">bar</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration">self</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">i32</span> <span class="brace">{</span> 74 <span class="keyword">fn</span> <span class="function associated declaration trait">bar</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration">self</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">i32</span> <span class="brace">{</span>
75 <span class="self_keyword">self</span><span class="operator">.</span><span class="field">x</span> 75 <span class="self_keyword">self</span><span class="operator">.</span><span class="field">x</span>
76 <span class="brace">}</span> 76 <span class="brace">}</span>
77<span class="brace">}</span> 77<span class="brace">}</span>
78 78
79<span class="keyword">impl</span> <span class="struct">Foo</span> <span class="brace">{</span> 79<span class="keyword">impl</span> <span class="struct">Foo</span> <span class="brace">{</span>
80 <span class="keyword">fn</span> <span class="function declaration associated">baz</span><span class="parenthesis">(</span><span class="keyword">mut</span> <span class="self_keyword declaration mutable">self</span><span class="comma">,</span> <span class="value_param declaration">f</span><span class="colon">:</span> <span class="struct">Foo</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">i32</span> <span class="brace">{</span> 80 <span class="keyword">fn</span> <span class="function associated declaration">baz</span><span class="parenthesis">(</span><span class="keyword">mut</span> <span class="self_keyword declaration mutable">self</span><span class="comma">,</span> <span class="value_param declaration">f</span><span class="colon">:</span> <span class="struct">Foo</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">i32</span> <span class="brace">{</span>
81 <span class="value_param">f</span><span class="operator">.</span><span class="function consuming associated">baz</span><span class="parenthesis">(</span><span class="self_keyword mutable consuming">self</span><span class="parenthesis">)</span> 81 <span class="value_param">f</span><span class="operator">.</span><span class="function associated consuming">baz</span><span class="parenthesis">(</span><span class="self_keyword consuming mutable">self</span><span class="parenthesis">)</span>
82 <span class="brace">}</span> 82 <span class="brace">}</span>
83 83
84 <span class="keyword">fn</span> <span class="function declaration associated">qux</span><span class="parenthesis">(</span><span class="operator">&</span><span class="keyword">mut</span> <span class="self_keyword declaration mutable">self</span><span class="parenthesis">)</span> <span class="brace">{</span> 84 <span class="keyword">fn</span> <span class="function associated declaration">qux</span><span class="parenthesis">(</span><span class="operator">&</span><span class="keyword">mut</span> <span class="self_keyword declaration mutable">self</span><span class="parenthesis">)</span> <span class="brace">{</span>
85 <span class="self_keyword mutable">self</span><span class="operator">.</span><span class="field">x</span> <span class="operator">=</span> <span class="numeric_literal">0</span><span class="semicolon">;</span> 85 <span class="self_keyword mutable">self</span><span class="operator">.</span><span class="field">x</span> <span class="operator">=</span> <span class="numeric_literal">0</span><span class="semicolon">;</span>
86 <span class="brace">}</span> 86 <span class="brace">}</span>
87 87
88 <span class="keyword">fn</span> <span class="function declaration associated">quop</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration">self</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">i32</span> <span class="brace">{</span> 88 <span class="keyword">fn</span> <span class="function associated declaration">quop</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration">self</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">i32</span> <span class="brace">{</span>
89 <span class="self_keyword">self</span><span class="operator">.</span><span class="field">x</span> 89 <span class="self_keyword">self</span><span class="operator">.</span><span class="field">x</span>
90 <span class="brace">}</span> 90 <span class="brace">}</span>
91<span class="brace">}</span> 91<span class="brace">}</span>
@@ -96,15 +96,15 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
96<span class="brace">}</span> 96<span class="brace">}</span>
97 97
98<span class="keyword">impl</span> <span class="struct">FooCopy</span> <span class="brace">{</span> 98<span class="keyword">impl</span> <span class="struct">FooCopy</span> <span class="brace">{</span>
99 <span class="keyword">fn</span> <span class="function declaration associated">baz</span><span class="parenthesis">(</span><span class="self_keyword declaration">self</span><span class="comma">,</span> <span class="value_param declaration">f</span><span class="colon">:</span> <span class="struct">FooCopy</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">u32</span> <span class="brace">{</span> 99 <span class="keyword">fn</span> <span class="function associated declaration">baz</span><span class="parenthesis">(</span><span class="self_keyword declaration">self</span><span class="comma">,</span> <span class="value_param declaration">f</span><span class="colon">:</span> <span class="struct">FooCopy</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">u32</span> <span class="brace">{</span>
100 <span class="value_param">f</span><span class="operator">.</span><span class="function associated">baz</span><span class="parenthesis">(</span><span class="self_keyword">self</span><span class="parenthesis">)</span> 100 <span class="value_param">f</span><span class="operator">.</span><span class="function associated">baz</span><span class="parenthesis">(</span><span class="self_keyword">self</span><span class="parenthesis">)</span>
101 <span class="brace">}</span> 101 <span class="brace">}</span>
102 102
103 <span class="keyword">fn</span> <span class="function declaration associated">qux</span><span class="parenthesis">(</span><span class="operator">&</span><span class="keyword">mut</span> <span class="self_keyword declaration mutable">self</span><span class="parenthesis">)</span> <span class="brace">{</span> 103 <span class="keyword">fn</span> <span class="function associated declaration">qux</span><span class="parenthesis">(</span><span class="operator">&</span><span class="keyword">mut</span> <span class="self_keyword declaration mutable">self</span><span class="parenthesis">)</span> <span class="brace">{</span>
104 <span class="self_keyword mutable">self</span><span class="operator">.</span><span class="field">x</span> <span class="operator">=</span> <span class="numeric_literal">0</span><span class="semicolon">;</span> 104 <span class="self_keyword mutable">self</span><span class="operator">.</span><span class="field">x</span> <span class="operator">=</span> <span class="numeric_literal">0</span><span class="semicolon">;</span>
105 <span class="brace">}</span> 105 <span class="brace">}</span>
106 106
107 <span class="keyword">fn</span> <span class="function declaration associated">quop</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration">self</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">u32</span> <span class="brace">{</span> 107 <span class="keyword">fn</span> <span class="function associated declaration">quop</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration">self</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">u32</span> <span class="brace">{</span>
108 <span class="self_keyword">self</span><span class="operator">.</span><span class="field">x</span> 108 <span class="self_keyword">self</span><span class="operator">.</span><span class="field">x</span>
109 <span class="brace">}</span> 109 <span class="brace">}</span>
110<span class="brace">}</span> 110<span class="brace">}</span>
@@ -128,7 +128,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
128<span class="brace">}</span> 128<span class="brace">}</span>
129 129
130<span class="keyword">use</span> <span class="module">ops</span><span class="operator">::</span><span class="trait">Fn</span><span class="semicolon">;</span> 130<span class="keyword">use</span> <span class="module">ops</span><span class="operator">::</span><span class="trait">Fn</span><span class="semicolon">;</span>
131<span class="keyword">fn</span> <span class="function declaration">baz</span><span class="angle">&lt;</span><span class="type_param declaration">F</span><span class="colon">:</span> <span class="trait">Fn</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="parenthesis">(</span><span class="parenthesis">)</span><span class="angle">&gt;</span><span class="parenthesis">(</span><span class="value_param declaration callable">f</span><span class="colon">:</span> <span class="type_param">F</span><span class="parenthesis">)</span> <span class="brace">{</span> 131<span class="keyword">fn</span> <span class="function declaration">baz</span><span class="angle">&lt;</span><span class="type_param declaration">F</span><span class="colon">:</span> <span class="trait">Fn</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="parenthesis">(</span><span class="parenthesis">)</span><span class="angle">&gt;</span><span class="parenthesis">(</span><span class="value_param callable declaration">f</span><span class="colon">:</span> <span class="type_param">F</span><span class="parenthesis">)</span> <span class="brace">{</span>
132 <span class="value_param callable">f</span><span class="parenthesis">(</span><span class="parenthesis">)</span> 132 <span class="value_param callable">f</span><span class="parenthesis">(</span><span class="parenthesis">)</span>
133<span class="brace">}</span> 133<span class="brace">}</span>
134 134
@@ -199,16 +199,16 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
199 <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">foo</span> <span class="operator">=</span> <span class="struct">Foo</span> <span class="brace">{</span> <span class="field">x</span><span class="comma">,</span> <span class="field">y</span><span class="colon">:</span> <span class="variable mutable">x</span> <span class="brace">}</span><span class="semicolon">;</span> 199 <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">foo</span> <span class="operator">=</span> <span class="struct">Foo</span> <span class="brace">{</span> <span class="field">x</span><span class="comma">,</span> <span class="field">y</span><span class="colon">:</span> <span class="variable mutable">x</span> <span class="brace">}</span><span class="semicolon">;</span>
200 <span class="keyword">let</span> <span class="variable declaration">foo2</span> <span class="operator">=</span> <span class="struct">Foo</span> <span class="brace">{</span> <span class="field">x</span><span class="comma">,</span> <span class="field">y</span><span class="colon">:</span> <span class="variable mutable">x</span> <span class="brace">}</span><span class="semicolon">;</span> 200 <span class="keyword">let</span> <span class="variable declaration">foo2</span> <span class="operator">=</span> <span class="struct">Foo</span> <span class="brace">{</span> <span class="field">x</span><span class="comma">,</span> <span class="field">y</span><span class="colon">:</span> <span class="variable mutable">x</span> <span class="brace">}</span><span class="semicolon">;</span>
201 <span class="variable mutable">foo</span><span class="operator">.</span><span class="function associated">quop</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span> 201 <span class="variable mutable">foo</span><span class="operator">.</span><span class="function associated">quop</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
202 <span class="variable mutable">foo</span><span class="operator">.</span><span class="function mutable associated">qux</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span> 202 <span class="variable mutable">foo</span><span class="operator">.</span><span class="function associated mutable">qux</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
203 <span class="variable mutable">foo</span><span class="operator">.</span><span class="function consuming associated">baz</span><span class="parenthesis">(</span><span class="variable consuming">foo2</span><span class="parenthesis">)</span><span class="semicolon">;</span> 203 <span class="variable mutable">foo</span><span class="operator">.</span><span class="function associated consuming">baz</span><span class="parenthesis">(</span><span class="variable consuming">foo2</span><span class="parenthesis">)</span><span class="semicolon">;</span>
204 204
205 <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">copy</span> <span class="operator">=</span> <span class="struct">FooCopy</span> <span class="brace">{</span> <span class="field">x</span> <span class="brace">}</span><span class="semicolon">;</span> 205 <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">copy</span> <span class="operator">=</span> <span class="struct">FooCopy</span> <span class="brace">{</span> <span class="field">x</span> <span class="brace">}</span><span class="semicolon">;</span>
206 <span class="variable mutable">copy</span><span class="operator">.</span><span class="function associated">quop</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span> 206 <span class="variable mutable">copy</span><span class="operator">.</span><span class="function associated">quop</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
207 <span class="variable mutable">copy</span><span class="operator">.</span><span class="function mutable associated">qux</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span> 207 <span class="variable mutable">copy</span><span class="operator">.</span><span class="function associated mutable">qux</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
208 <span class="variable mutable">copy</span><span class="operator">.</span><span class="function associated">baz</span><span class="parenthesis">(</span><span class="variable mutable">copy</span><span class="parenthesis">)</span><span class="semicolon">;</span> 208 <span class="variable mutable">copy</span><span class="operator">.</span><span class="function associated">baz</span><span class="parenthesis">(</span><span class="variable mutable">copy</span><span class="parenthesis">)</span><span class="semicolon">;</span>
209 209
210 <span class="keyword">let</span> <span class="variable declaration callable">a</span> <span class="operator">=</span> <span class="punctuation">|</span><span class="value_param declaration">x</span><span class="punctuation">|</span> <span class="value_param">x</span><span class="semicolon">;</span> 210 <span class="keyword">let</span> <span class="variable callable declaration">a</span> <span class="operator">=</span> <span class="punctuation">|</span><span class="value_param declaration">x</span><span class="punctuation">|</span> <span class="value_param">x</span><span class="semicolon">;</span>
211 <span class="keyword">let</span> <span class="variable declaration callable">bar</span> <span class="operator">=</span> <span class="struct">Foo</span><span class="operator">::</span><span class="function associated">baz</span><span class="semicolon">;</span> 211 <span class="keyword">let</span> <span class="variable callable declaration">bar</span> <span class="operator">=</span> <span class="struct">Foo</span><span class="operator">::</span><span class="function associated">baz</span><span class="semicolon">;</span>
212 212
213 <span class="keyword">let</span> <span class="variable declaration">baz</span> <span class="operator">=</span> <span class="numeric_literal">-</span><span class="numeric_literal">42</span><span class="semicolon">;</span> 213 <span class="keyword">let</span> <span class="variable declaration">baz</span> <span class="operator">=</span> <span class="numeric_literal">-</span><span class="numeric_literal">42</span><span class="semicolon">;</span>
214 <span class="keyword">let</span> <span class="variable declaration">baz</span> <span class="operator">=</span> <span class="operator">-</span><span class="variable">baz</span><span class="semicolon">;</span> 214 <span class="keyword">let</span> <span class="variable declaration">baz</span> <span class="operator">=</span> <span class="operator">-</span><span class="variable">baz</span><span class="semicolon">;</span>
@@ -228,7 +228,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
228<span class="keyword">use</span> <span class="enum">Option</span><span class="operator">::</span><span class="punctuation">*</span><span class="semicolon">;</span> 228<span class="keyword">use</span> <span class="enum">Option</span><span class="operator">::</span><span class="punctuation">*</span><span class="semicolon">;</span>
229 229
230<span class="keyword">impl</span><span class="angle">&lt;</span><span class="type_param declaration">T</span><span class="angle">&gt;</span> <span class="enum">Option</span><span class="angle">&lt;</span><span class="type_param">T</span><span class="angle">&gt;</span> <span class="brace">{</span> 230<span class="keyword">impl</span><span class="angle">&lt;</span><span class="type_param declaration">T</span><span class="angle">&gt;</span> <span class="enum">Option</span><span class="angle">&lt;</span><span class="type_param">T</span><span class="angle">&gt;</span> <span class="brace">{</span>
231 <span class="keyword">fn</span> <span class="function declaration associated">and</span><span class="angle">&lt;</span><span class="type_param declaration">U</span><span class="angle">&gt;</span><span class="parenthesis">(</span><span class="self_keyword declaration">self</span><span class="comma">,</span> <span class="value_param declaration">other</span><span class="colon">:</span> <span class="enum">Option</span><span class="angle">&lt;</span><span class="type_param">U</span><span class="angle">&gt;</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="enum">Option</span><span class="angle">&lt;</span><span class="parenthesis">(</span><span class="type_param">T</span><span class="comma">,</span> <span class="type_param">U</span><span class="parenthesis">)</span><span class="angle">&gt;</span> <span class="brace">{</span> 231 <span class="keyword">fn</span> <span class="function associated declaration">and</span><span class="angle">&lt;</span><span class="type_param declaration">U</span><span class="angle">&gt;</span><span class="parenthesis">(</span><span class="self_keyword declaration">self</span><span class="comma">,</span> <span class="value_param declaration">other</span><span class="colon">:</span> <span class="enum">Option</span><span class="angle">&lt;</span><span class="type_param">U</span><span class="angle">&gt;</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="enum">Option</span><span class="angle">&lt;</span><span class="parenthesis">(</span><span class="type_param">T</span><span class="comma">,</span> <span class="type_param">U</span><span class="parenthesis">)</span><span class="angle">&gt;</span> <span class="brace">{</span>
232 <span class="keyword control">match</span> <span class="value_param">other</span> <span class="brace">{</span> 232 <span class="keyword control">match</span> <span class="value_param">other</span> <span class="brace">{</span>
233 <span class="enum_variant">None</span> <span class="operator">=&gt;</span> <span class="macro">unimplemented!</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="comma">,</span> 233 <span class="enum_variant">None</span> <span class="operator">=&gt;</span> <span class="macro">unimplemented!</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="comma">,</span>
234 <span class="variable declaration">Nope</span> <span class="operator">=&gt;</span> <span class="variable">Nope</span><span class="comma">,</span> 234 <span class="variable declaration">Nope</span> <span class="operator">=&gt;</span> <span class="variable">Nope</span><span class="comma">,</span>
diff --git a/crates/ide_assists/src/ast_transform.rs b/crates/ide_assists/src/ast_transform.rs
index 4a3ed7783..e5ae718c9 100644
--- a/crates/ide_assists/src/ast_transform.rs
+++ b/crates/ide_assists/src/ast_transform.rs
@@ -3,20 +3,27 @@ use hir::{HirDisplay, PathResolution, SemanticsScope};
3use ide_db::helpers::mod_path_to_ast; 3use ide_db::helpers::mod_path_to_ast;
4use rustc_hash::FxHashMap; 4use rustc_hash::FxHashMap;
5use syntax::{ 5use syntax::{
6 algo::SyntaxRewriter,
7 ast::{self, AstNode}, 6 ast::{self, AstNode},
8 SyntaxNode, 7 ted, SyntaxNode,
9}; 8};
10 9
11pub fn apply<'a, N: AstNode>(transformer: &dyn AstTransform<'a>, node: N) -> N { 10pub fn apply<'a, N: AstNode>(transformer: &dyn AstTransform<'a>, node: &N) {
12 SyntaxRewriter::from_fn(|element| match element { 11 let mut skip_to = None;
13 syntax::SyntaxElement::Node(n) => { 12 for event in node.syntax().preorder() {
14 let replacement = transformer.get_substitution(&n, transformer)?; 13 match event {
15 Some(replacement.into()) 14 syntax::WalkEvent::Enter(node) if skip_to.is_none() => {
15 skip_to = transformer.get_substitution(&node, transformer).zip(Some(node));
16 }
17 syntax::WalkEvent::Enter(_) => (),
18 syntax::WalkEvent::Leave(node) => match &skip_to {
19 Some((replacement, skip_target)) if *skip_target == node => {
20 ted::replace(node, replacement.clone_for_update());
21 skip_to.take();
22 }
23 _ => (),
24 },
16 } 25 }
17 _ => None, 26 }
18 })
19 .rewrite_ast(&node)
20} 27}
21 28
22/// `AstTransform` helps with applying bulk transformations to syntax nodes. 29/// `AstTransform` helps with applying bulk transformations to syntax nodes.
@@ -191,11 +198,9 @@ impl<'a> AstTransform<'a> for QualifyPaths<'a> {
191 let found_path = from.find_use_path(self.source_scope.db.upcast(), def)?; 198 let found_path = from.find_use_path(self.source_scope.db.upcast(), def)?;
192 let mut path = mod_path_to_ast(&found_path); 199 let mut path = mod_path_to_ast(&found_path);
193 200
194 let type_args = p 201 let type_args = p.segment().and_then(|s| s.generic_arg_list());
195 .segment()
196 .and_then(|s| s.generic_arg_list())
197 .map(|arg_list| apply(recur, arg_list));
198 if let Some(type_args) = type_args { 202 if let Some(type_args) = type_args {
203 apply(recur, &type_args);
199 let last_segment = path.segment().unwrap(); 204 let last_segment = path.segment().unwrap();
200 path = path.with_segment(last_segment.with_generic_args(type_args)) 205 path = path.with_segment(last_segment.with_generic_args(type_args))
201 } 206 }
diff --git a/crates/ide_assists/src/handlers/convert_tuple_struct_to_named_struct.rs b/crates/ide_assists/src/handlers/convert_tuple_struct_to_named_struct.rs
new file mode 100644
index 000000000..b5b5ada5e
--- /dev/null
+++ b/crates/ide_assists/src/handlers/convert_tuple_struct_to_named_struct.rs
@@ -0,0 +1,516 @@
1use ide_db::defs::{Definition, NameRefClass};
2use syntax::{
3 ast::{self, AstNode, GenericParamsOwner, VisibilityOwner},
4 match_ast, SyntaxNode,
5};
6
7use crate::{assist_context::AssistBuilder, AssistContext, AssistId, AssistKind, Assists};
8
9// Assist: convert_tuple_struct_to_named_struct
10//
11// Converts tuple struct to struct with named fields.
12//
13// ```
14// struct Point$0(f32, f32);
15//
16// impl Point {
17// pub fn new(x: f32, y: f32) -> Self {
18// Point(x, y)
19// }
20//
21// pub fn x(&self) -> f32 {
22// self.0
23// }
24//
25// pub fn y(&self) -> f32 {
26// self.1
27// }
28// }
29// ```
30// ->
31// ```
32// struct Point { field1: f32, field2: f32 }
33//
34// impl Point {
35// pub fn new(x: f32, y: f32) -> Self {
36// Point { field1: x, field2: y }
37// }
38//
39// pub fn x(&self) -> f32 {
40// self.field1
41// }
42//
43// pub fn y(&self) -> f32 {
44// self.field2
45// }
46// }
47// ```
48pub(crate) fn convert_tuple_struct_to_named_struct(
49 acc: &mut Assists,
50 ctx: &AssistContext,
51) -> Option<()> {
52 let strukt = ctx.find_node_at_offset::<ast::Struct>()?;
53 let tuple_fields = match strukt.field_list()? {
54 ast::FieldList::TupleFieldList(it) => it,
55 ast::FieldList::RecordFieldList(_) => return None,
56 };
57 let strukt_def = ctx.sema.to_def(&strukt)?;
58
59 let target = strukt.syntax().text_range();
60 acc.add(
61 AssistId("convert_tuple_struct_to_named_struct", AssistKind::RefactorRewrite),
62 "Convert to named struct",
63 target,
64 |edit| {
65 let names = generate_names(tuple_fields.fields());
66 edit_field_references(ctx, edit, tuple_fields.fields(), &names);
67 edit_struct_references(ctx, edit, strukt_def, &names);
68 edit_struct_def(ctx, edit, &strukt, tuple_fields, names);
69 },
70 )
71}
72
73fn edit_struct_def(
74 ctx: &AssistContext,
75 edit: &mut AssistBuilder,
76 strukt: &ast::Struct,
77 tuple_fields: ast::TupleFieldList,
78 names: Vec<ast::Name>,
79) {
80 let record_fields = tuple_fields
81 .fields()
82 .zip(names)
83 .filter_map(|(f, name)| Some(ast::make::record_field(f.visibility(), name, f.ty()?)));
84 let record_fields = ast::make::record_field_list(record_fields);
85 let tuple_fields_text_range = tuple_fields.syntax().text_range();
86
87 edit.edit_file(ctx.frange.file_id);
88
89 if let Some(w) = strukt.where_clause() {
90 edit.delete(w.syntax().text_range());
91 edit.insert(tuple_fields_text_range.start(), ast::make::tokens::single_newline().text());
92 edit.insert(tuple_fields_text_range.start(), w.syntax().text());
93 edit.insert(tuple_fields_text_range.start(), ",");
94 edit.insert(tuple_fields_text_range.start(), ast::make::tokens::single_newline().text());
95 } else {
96 edit.insert(tuple_fields_text_range.start(), ast::make::tokens::single_space().text());
97 }
98
99 edit.replace(tuple_fields_text_range, record_fields.to_string());
100 strukt.semicolon_token().map(|t| edit.delete(t.text_range()));
101}
102
103fn edit_struct_references(
104 ctx: &AssistContext,
105 edit: &mut AssistBuilder,
106 strukt: hir::Struct,
107 names: &[ast::Name],
108) {
109 let strukt_def = Definition::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Struct(strukt)));
110 let usages = strukt_def.usages(&ctx.sema).include_self_kw_refs(true).all();
111
112 let edit_node = |edit: &mut AssistBuilder, node: SyntaxNode| -> Option<()> {
113 match_ast! {
114 match node {
115 ast::TupleStructPat(tuple_struct_pat) => {
116 edit.replace(
117 tuple_struct_pat.syntax().text_range(),
118 ast::make::record_pat_with_fields(
119 tuple_struct_pat.path()?,
120 ast::make::record_pat_field_list(tuple_struct_pat.fields().zip(names).map(
121 |(pat, name)| {
122 ast::make::record_pat_field(
123 ast::make::name_ref(&name.to_string()),
124 pat,
125 )
126 },
127 )),
128 )
129 .to_string(),
130 );
131 },
132 // for tuple struct creations like Foo(42)
133 ast::CallExpr(call_expr) => {
134 let path = call_expr.syntax().descendants().find_map(ast::PathExpr::cast).and_then(|expr| expr.path())?;
135
136 // this also includes method calls like Foo::new(42), we should skip them
137 if let Some(name_ref) = path.segment().and_then(|s| s.name_ref()) {
138 match NameRefClass::classify(&ctx.sema, &name_ref) {
139 Some(NameRefClass::Definition(Definition::SelfType(_))) => {},
140 Some(NameRefClass::Definition(def)) if def == strukt_def => {},
141 _ => return None,
142 };
143 }
144
145 let arg_list = call_expr.syntax().descendants().find_map(ast::ArgList::cast)?;
146
147 edit.replace(
148 call_expr.syntax().text_range(),
149 ast::make::record_expr(
150 path,
151 ast::make::record_expr_field_list(arg_list.args().zip(names).map(
152 |(expr, name)| {
153 ast::make::record_expr_field(
154 ast::make::name_ref(&name.to_string()),
155 Some(expr),
156 )
157 },
158 )),
159 )
160 .to_string(),
161 );
162 },
163 _ => return None,
164 }
165 }
166 Some(())
167 };
168
169 for (file_id, refs) in usages {
170 edit.edit_file(file_id);
171 for r in refs {
172 for node in r.name.syntax().ancestors() {
173 if edit_node(edit, node).is_some() {
174 break;
175 }
176 }
177 }
178 }
179}
180
181fn edit_field_references(
182 ctx: &AssistContext,
183 edit: &mut AssistBuilder,
184 fields: impl Iterator<Item = ast::TupleField>,
185 names: &[ast::Name],
186) {
187 for (field, name) in fields.zip(names) {
188 let field = match ctx.sema.to_def(&field) {
189 Some(it) => it,
190 None => continue,
191 };
192 let def = Definition::Field(field);
193 let usages = def.usages(&ctx.sema).all();
194 for (file_id, refs) in usages {
195 edit.edit_file(file_id);
196 for r in refs {
197 if let Some(name_ref) = r.name.as_name_ref() {
198 edit.replace(name_ref.syntax().text_range(), name.text());
199 }
200 }
201 }
202 }
203}
204
205fn generate_names(fields: impl Iterator<Item = ast::TupleField>) -> Vec<ast::Name> {
206 fields.enumerate().map(|(i, _)| ast::make::name(&format!("field{}", i + 1))).collect()
207}
208
209#[cfg(test)]
210mod tests {
211 use crate::tests::{check_assist, check_assist_not_applicable};
212
213 use super::*;
214
215 #[test]
216 fn not_applicable_other_than_tuple_struct() {
217 check_assist_not_applicable(
218 convert_tuple_struct_to_named_struct,
219 r#"struct Foo$0 { bar: u32 };"#,
220 );
221 check_assist_not_applicable(convert_tuple_struct_to_named_struct, r#"struct Foo$0;"#);
222 }
223
224 #[test]
225 fn convert_simple_struct() {
226 check_assist(
227 convert_tuple_struct_to_named_struct,
228 r#"
229struct Inner;
230struct A$0(Inner);
231
232impl A {
233 fn new(inner: Inner) -> A {
234 A(inner)
235 }
236
237 fn new_with_default() -> A {
238 A::new(Inner)
239 }
240
241 fn into_inner(self) -> Inner {
242 self.0
243 }
244}"#,
245 r#"
246struct Inner;
247struct A { field1: Inner }
248
249impl A {
250 fn new(inner: Inner) -> A {
251 A { field1: inner }
252 }
253
254 fn new_with_default() -> A {
255 A::new(Inner)
256 }
257
258 fn into_inner(self) -> Inner {
259 self.field1
260 }
261}"#,
262 );
263 }
264
265 #[test]
266 fn convert_struct_referenced_via_self_kw() {
267 check_assist(
268 convert_tuple_struct_to_named_struct,
269 r#"
270struct Inner;
271struct A$0(Inner);
272
273impl A {
274 fn new(inner: Inner) -> Self {
275 Self(inner)
276 }
277
278 fn new_with_default() -> Self {
279 Self::new(Inner)
280 }
281
282 fn into_inner(self) -> Inner {
283 self.0
284 }
285}"#,
286 r#"
287struct Inner;
288struct A { field1: Inner }
289
290impl A {
291 fn new(inner: Inner) -> Self {
292 Self { field1: inner }
293 }
294
295 fn new_with_default() -> Self {
296 Self::new(Inner)
297 }
298
299 fn into_inner(self) -> Inner {
300 self.field1
301 }
302}"#,
303 );
304 }
305
306 #[test]
307 fn convert_destructured_struct() {
308 check_assist(
309 convert_tuple_struct_to_named_struct,
310 r#"
311struct Inner;
312struct A$0(Inner);
313
314impl A {
315 fn into_inner(self) -> Inner {
316 let A(first) = self;
317 first
318 }
319
320 fn into_inner_via_self(self) -> Inner {
321 let Self(first) = self;
322 first
323 }
324}"#,
325 r#"
326struct Inner;
327struct A { field1: Inner }
328
329impl A {
330 fn into_inner(self) -> Inner {
331 let A { field1: first } = self;
332 first
333 }
334
335 fn into_inner_via_self(self) -> Inner {
336 let Self { field1: first } = self;
337 first
338 }
339}"#,
340 );
341 }
342
343 #[test]
344 fn convert_struct_with_visibility() {
345 check_assist(
346 convert_tuple_struct_to_named_struct,
347 r#"
348struct A$0(pub u32, pub(crate) u64);
349
350impl A {
351 fn new() -> A {
352 A(42, 42)
353 }
354
355 fn into_first(self) -> u32 {
356 self.0
357 }
358
359 fn into_second(self) -> u64 {
360 self.1
361 }
362}"#,
363 r#"
364struct A { pub field1: u32, pub(crate) field2: u64 }
365
366impl A {
367 fn new() -> A {
368 A { field1: 42, field2: 42 }
369 }
370
371 fn into_first(self) -> u32 {
372 self.field1
373 }
374
375 fn into_second(self) -> u64 {
376 self.field2
377 }
378}"#,
379 );
380 }
381
382 #[test]
383 fn convert_struct_with_wrapped_references() {
384 check_assist(
385 convert_tuple_struct_to_named_struct,
386 r#"
387struct Inner$0(u32);
388struct Outer(Inner);
389
390impl Outer {
391 fn new() -> Self {
392 Self(Inner(42))
393 }
394
395 fn into_inner(self) -> u32 {
396 (self.0).0
397 }
398
399 fn into_inner_destructed(self) -> u32 {
400 let Outer(Inner(x)) = self;
401 x
402 }
403}"#,
404 r#"
405struct Inner { field1: u32 }
406struct Outer(Inner);
407
408impl Outer {
409 fn new() -> Self {
410 Self(Inner { field1: 42 })
411 }
412
413 fn into_inner(self) -> u32 {
414 (self.0).field1
415 }
416
417 fn into_inner_destructed(self) -> u32 {
418 let Outer(Inner { field1: x }) = self;
419 x
420 }
421}"#,
422 );
423
424 check_assist(
425 convert_tuple_struct_to_named_struct,
426 r#"
427struct Inner(u32);
428struct Outer$0(Inner);
429
430impl Outer {
431 fn new() -> Self {
432 Self(Inner(42))
433 }
434
435 fn into_inner(self) -> u32 {
436 (self.0).0
437 }
438
439 fn into_inner_destructed(self) -> u32 {
440 let Outer(Inner(x)) = self;
441 x
442 }
443}"#,
444 r#"
445struct Inner(u32);
446struct Outer { field1: Inner }
447
448impl Outer {
449 fn new() -> Self {
450 Self { field1: Inner(42) }
451 }
452
453 fn into_inner(self) -> u32 {
454 (self.field1).0
455 }
456
457 fn into_inner_destructed(self) -> u32 {
458 let Outer { field1: Inner(x) } = self;
459 x
460 }
461}"#,
462 );
463 }
464
465 #[test]
466 fn convert_struct_with_multi_file_references() {
467 check_assist(
468 convert_tuple_struct_to_named_struct,
469 r#"
470//- /main.rs
471struct Inner;
472struct A$0(Inner);
473
474mod foo;
475
476//- /foo.rs
477use crate::{A, Inner};
478fn f() {
479 let a = A(Inner);
480}
481"#,
482 r#"
483//- /main.rs
484struct Inner;
485struct A { field1: Inner }
486
487mod foo;
488
489//- /foo.rs
490use crate::{A, Inner};
491fn f() {
492 let a = A { field1: Inner };
493}
494"#,
495 );
496 }
497
498 #[test]
499 fn convert_struct_with_where_clause() {
500 check_assist(
501 convert_tuple_struct_to_named_struct,
502 r#"
503struct Wrap$0<T>(T)
504where
505 T: Display;
506"#,
507 r#"
508struct Wrap<T>
509where
510 T: Display,
511{ field1: T }
512
513"#,
514 );
515 }
516}
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
1546fn $0fun_name() -> i32 { 1556fn $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
2528fn $0fun_name(n: &mut i32) { 2538fn $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
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/ide_assists/src/lib.rs b/crates/ide_assists/src/lib.rs
index 8996c1b61..88ae5c9a9 100644
--- a/crates/ide_assists/src/lib.rs
+++ b/crates/ide_assists/src/lib.rs
@@ -120,6 +120,7 @@ mod handlers {
120 mod convert_comment_block; 120 mod convert_comment_block;
121 mod convert_iter_for_each_to_for; 121 mod convert_iter_for_each_to_for;
122 mod convert_into_to_from; 122 mod convert_into_to_from;
123 mod convert_tuple_struct_to_named_struct;
123 mod early_return; 124 mod early_return;
124 mod expand_glob_import; 125 mod expand_glob_import;
125 mod extract_function; 126 mod extract_function;
@@ -190,6 +191,7 @@ mod handlers {
190 convert_comment_block::convert_comment_block, 191 convert_comment_block::convert_comment_block,
191 convert_iter_for_each_to_for::convert_iter_for_each_to_for, 192 convert_iter_for_each_to_for::convert_iter_for_each_to_for,
192 convert_into_to_from::convert_into_to_from, 193 convert_into_to_from::convert_into_to_from,
194 convert_tuple_struct_to_named_struct::convert_tuple_struct_to_named_struct,
193 early_return::convert_to_guarded_return, 195 early_return::convert_to_guarded_return,
194 expand_glob_import::expand_glob_import, 196 expand_glob_import::expand_glob_import,
195 extract_struct_from_enum_variant::extract_struct_from_enum_variant, 197 extract_struct_from_enum_variant::extract_struct_from_enum_variant,
diff --git a/crates/ide_assists/src/tests/generated.rs b/crates/ide_assists/src/tests/generated.rs
index 41559b43a..59bcef8fb 100644
--- a/crates/ide_assists/src/tests/generated.rs
+++ b/crates/ide_assists/src/tests/generated.rs
@@ -292,6 +292,47 @@ fn main() {
292} 292}
293 293
294#[test] 294#[test]
295fn doctest_convert_tuple_struct_to_named_struct() {
296 check_doc_test(
297 "convert_tuple_struct_to_named_struct",
298 r#####"
299struct Point$0(f32, f32);
300
301impl Point {
302 pub fn new(x: f32, y: f32) -> Self {
303 Point(x, y)
304 }
305
306 pub fn x(&self) -> f32 {
307 self.0
308 }
309
310 pub fn y(&self) -> f32 {
311 self.1
312 }
313}
314"#####,
315 r#####"
316struct Point { field1: f32, field2: f32 }
317
318impl Point {
319 pub fn new(x: f32, y: f32) -> Self {
320 Point { field1: x, field2: y }
321 }
322
323 pub fn x(&self) -> f32 {
324 self.field1
325 }
326
327 pub fn y(&self) -> f32 {
328 self.field2
329 }
330}
331"#####,
332 )
333}
334
335#[test]
295fn doctest_expand_glob_import() { 336fn doctest_expand_glob_import() {
296 check_doc_test( 337 check_doc_test(
297 "expand_glob_import", 338 "expand_glob_import",
diff --git a/crates/ide_assists/src/utils.rs b/crates/ide_assists/src/utils.rs
index d67524937..5a90ad715 100644
--- a/crates/ide_assists/src/utils.rs
+++ b/crates/ide_assists/src/utils.rs
@@ -140,7 +140,8 @@ pub fn add_trait_assoc_items_to_impl(
140 140
141 let items = items 141 let items = items
142 .into_iter() 142 .into_iter()
143 .map(|it| ast_transform::apply(&*ast_transform, it)) 143 .map(|it| it.clone_for_update())
144 .inspect(|it| ast_transform::apply(&*ast_transform, it))
144 .map(|it| match it { 145 .map(|it| match it {
145 ast::AssocItem::Fn(def) => ast::AssocItem::Fn(add_body(def)), 146 ast::AssocItem::Fn(def) => ast::AssocItem::Fn(add_body(def)),
146 ast::AssocItem::TypeAlias(def) => ast::AssocItem::TypeAlias(def.remove_bounds()), 147 ast::AssocItem::TypeAlias(def) => ast::AssocItem::TypeAlias(def.remove_bounds()),
diff --git a/crates/ide_db/src/search.rs b/crates/ide_db/src/search.rs
index b55e3851e..8f899ea56 100644
--- a/crates/ide_db/src/search.rs
+++ b/crates/ide_db/src/search.rs
@@ -7,7 +7,9 @@
7use std::{convert::TryInto, mem}; 7use std::{convert::TryInto, mem};
8 8
9use base_db::{FileId, FileRange, SourceDatabase, SourceDatabaseExt}; 9use base_db::{FileId, FileRange, SourceDatabase, SourceDatabaseExt};
10use hir::{DefWithBody, HasAttrs, HasSource, InFile, ModuleSource, Semantics, Visibility}; 10use hir::{
11 DefWithBody, HasAttrs, HasSource, InFile, ModuleDef, ModuleSource, Semantics, Visibility,
12};
11use once_cell::unsync::Lazy; 13use once_cell::unsync::Lazy;
12use rustc_hash::FxHashMap; 14use rustc_hash::FxHashMap;
13use syntax::{ast, match_ast, AstNode, TextRange, TextSize}; 15use syntax::{ast, match_ast, AstNode, TextRange, TextSize};
@@ -295,7 +297,7 @@ impl Definition {
295 } 297 }
296 298
297 pub fn usages<'a>(&'a self, sema: &'a Semantics<RootDatabase>) -> FindUsages<'a> { 299 pub fn usages<'a>(&'a self, sema: &'a Semantics<RootDatabase>) -> FindUsages<'a> {
298 FindUsages { def: self, sema, scope: None } 300 FindUsages { def: self, sema, scope: None, include_self_kw_refs: false }
299 } 301 }
300} 302}
301 303
@@ -303,9 +305,15 @@ pub struct FindUsages<'a> {
303 def: &'a Definition, 305 def: &'a Definition,
304 sema: &'a Semantics<'a, RootDatabase>, 306 sema: &'a Semantics<'a, RootDatabase>,
305 scope: Option<SearchScope>, 307 scope: Option<SearchScope>,
308 include_self_kw_refs: bool,
306} 309}
307 310
308impl<'a> FindUsages<'a> { 311impl<'a> FindUsages<'a> {
312 pub fn include_self_kw_refs(mut self, include: bool) -> FindUsages<'a> {
313 self.include_self_kw_refs = include;
314 self
315 }
316
309 pub fn in_scope(self, scope: SearchScope) -> FindUsages<'a> { 317 pub fn in_scope(self, scope: SearchScope) -> FindUsages<'a> {
310 self.set_scope(Some(scope)) 318 self.set_scope(Some(scope))
311 } 319 }
@@ -352,6 +360,8 @@ impl<'a> FindUsages<'a> {
352 }; 360 };
353 361
354 let pat = name.as_str(); 362 let pat = name.as_str();
363 let search_for_self = self.include_self_kw_refs;
364
355 for (file_id, search_range) in search_scope { 365 for (file_id, search_range) in search_scope {
356 let text = sema.db.file_text(file_id); 366 let text = sema.db.file_text(file_id);
357 let search_range = 367 let search_range =
@@ -359,31 +369,47 @@ impl<'a> FindUsages<'a> {
359 369
360 let tree = Lazy::new(|| sema.parse(file_id).syntax().clone()); 370 let tree = Lazy::new(|| sema.parse(file_id).syntax().clone());
361 371
362 for (idx, _) in text.match_indices(pat) { 372 let mut handle_match = |idx: usize| -> bool {
363 let offset: TextSize = idx.try_into().unwrap(); 373 let offset: TextSize = idx.try_into().unwrap();
364 if !search_range.contains_inclusive(offset) { 374 if !search_range.contains_inclusive(offset) {
365 continue; 375 return false;
366 } 376 }
367 377
368 if let Some(name) = sema.find_node_at_offset_with_descend(&tree, offset) { 378 if let Some(name) = sema.find_node_at_offset_with_descend(&tree, offset) {
369 match name { 379 match name {
370 ast::NameLike::NameRef(name_ref) => { 380 ast::NameLike::NameRef(name_ref) => {
371 if self.found_name_ref(&name_ref, sink) { 381 if self.found_name_ref(&name_ref, sink) {
372 return; 382 return true;
373 } 383 }
374 } 384 }
375 ast::NameLike::Name(name) => { 385 ast::NameLike::Name(name) => {
376 if self.found_name(&name, sink) { 386 if self.found_name(&name, sink) {
377 return; 387 return true;
378 } 388 }
379 } 389 }
380 ast::NameLike::Lifetime(lifetime) => { 390 ast::NameLike::Lifetime(lifetime) => {
381 if self.found_lifetime(&lifetime, sink) { 391 if self.found_lifetime(&lifetime, sink) {
382 return; 392 return true;
383 } 393 }
384 } 394 }
385 } 395 }
386 } 396 }
397
398 return false;
399 };
400
401 for (idx, _) in text.match_indices(pat) {
402 if handle_match(idx) {
403 return;
404 }
405 }
406
407 if search_for_self {
408 for (idx, _) in text.match_indices("Self") {
409 if handle_match(idx) {
410 return;
411 }
412 }
387 } 413 }
388 } 414 }
389 } 415 }
@@ -422,6 +448,24 @@ impl<'a> FindUsages<'a> {
422 }; 448 };
423 sink(file_id, reference) 449 sink(file_id, reference)
424 } 450 }
451 Some(NameRefClass::Definition(Definition::SelfType(impl_))) => {
452 let ty = impl_.self_ty(self.sema.db);
453
454 if let Some(adt) = ty.as_adt() {
455 if &Definition::ModuleDef(ModuleDef::Adt(adt)) == self.def {
456 let FileRange { file_id, range } =
457 self.sema.original_range(name_ref.syntax());
458 let reference = FileReference {
459 range,
460 name: ast::NameLike::NameRef(name_ref.clone()),
461 access: None,
462 };
463 return sink(file_id, reference);
464 }
465 }
466
467 false
468 }
425 Some(NameRefClass::FieldShorthand { local_ref: local, field_ref: field }) => { 469 Some(NameRefClass::FieldShorthand { local_ref: local, field_ref: field }) => {
426 let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax()); 470 let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax());
427 let reference = match self.def { 471 let reference = match self.def {
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/proc_macro_api/Cargo.toml b/crates/proc_macro_api/Cargo.toml
index 16fd56c7e..1ba1e4abd 100644
--- a/crates/proc_macro_api/Cargo.toml
+++ b/crates/proc_macro_api/Cargo.toml
@@ -15,10 +15,11 @@ serde_json = { version = "1.0", features = ["unbounded_depth"] }
15log = "0.4.8" 15log = "0.4.8"
16crossbeam-channel = "0.5.0" 16crossbeam-channel = "0.5.0"
17jod-thread = "0.1.1" 17jod-thread = "0.1.1"
18memmap = "0.7.0"
19object = { version = "0.23.0", default-features = false, features = ["std", "read_core", "elf", "macho", "pe", "unaligned"] }
20snap = "1.0"
18 21
19tt = { path = "../tt", version = "0.0.0" } 22tt = { path = "../tt", version = "0.0.0" }
20base_db = { path = "../base_db", version = "0.0.0" } 23base_db = { path = "../base_db", version = "0.0.0" }
21stdx = { path = "../stdx", version = "0.0.0" } 24stdx = { path = "../stdx", version = "0.0.0" }
22snap = "1" 25profile = { path = "../profile", version = "0.0.0" }
23object = { version = "0.23.0", default-features = false, features = ["std", "read_core", "elf", "macho", "pe", "unaligned"] }
24memmap = "0.7.0"
diff --git a/crates/proc_macro_api/src/lib.rs b/crates/proc_macro_api/src/lib.rs
index 2dd2a8541..654bd9943 100644
--- a/crates/proc_macro_api/src/lib.rs
+++ b/crates/proc_macro_api/src/lib.rs
@@ -77,6 +77,7 @@ impl ProcMacroClient {
77 } 77 }
78 78
79 pub fn by_dylib_path(&self, dylib_path: &Path) -> Vec<ProcMacro> { 79 pub fn by_dylib_path(&self, dylib_path: &Path) -> Vec<ProcMacro> {
80 let _p = profile::span("ProcMacroClient::by_dylib_path");
80 match version::read_dylib_info(dylib_path) { 81 match version::read_dylib_info(dylib_path) {
81 Ok(info) => { 82 Ok(info) => {
82 if info.version.0 < 1 || info.version.1 < 47 { 83 if info.version.0 < 1 || info.version.1 < 47 {
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/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs
index e51532d88..0ae2758cc 100644
--- a/crates/rust-analyzer/src/reload.rs
+++ b/crates/rust-analyzer/src/reload.rs
@@ -322,6 +322,7 @@ impl GlobalState {
322 let loader = &mut self.loader; 322 let loader = &mut self.loader;
323 let mem_docs = &self.mem_docs; 323 let mem_docs = &self.mem_docs;
324 let mut load = |path: &AbsPath| { 324 let mut load = |path: &AbsPath| {
325 let _p = profile::span("GlobalState::load");
325 let vfs_path = vfs::VfsPath::from(path.to_path_buf()); 326 let vfs_path = vfs::VfsPath::from(path.to_path_buf());
326 if !mem_docs.contains_key(&vfs_path) { 327 if !mem_docs.contains_key(&vfs_path) {
327 let contents = loader.handle.load_sync(path); 328 let contents = loader.handle.load_sync(path);
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/algo.rs b/crates/syntax/src/algo.rs
index a153a9e1c..c9229c4e0 100644
--- a/crates/syntax/src/algo.rs
+++ b/crates/syntax/src/algo.rs
@@ -342,10 +342,10 @@ enum InsertPos {
342 342
343#[derive(Default)] 343#[derive(Default)]
344pub struct SyntaxRewriter<'a> { 344pub struct SyntaxRewriter<'a> {
345 f: Option<Box<dyn Fn(&SyntaxElement) -> Option<SyntaxElement> + 'a>>,
346 //FIXME: add debug_assertions that all elements are in fact from the same file. 345 //FIXME: add debug_assertions that all elements are in fact from the same file.
347 replacements: FxHashMap<SyntaxElement, Replacement>, 346 replacements: FxHashMap<SyntaxElement, Replacement>,
348 insertions: IndexMap<InsertPos, Vec<SyntaxElement>>, 347 insertions: IndexMap<InsertPos, Vec<SyntaxElement>>,
348 _pd: std::marker::PhantomData<&'a ()>,
349} 349}
350 350
351impl fmt::Debug for SyntaxRewriter<'_> { 351impl fmt::Debug for SyntaxRewriter<'_> {
@@ -357,14 +357,7 @@ impl fmt::Debug for SyntaxRewriter<'_> {
357 } 357 }
358} 358}
359 359
360impl<'a> SyntaxRewriter<'a> { 360impl SyntaxRewriter<'_> {
361 pub fn from_fn(f: impl Fn(&SyntaxElement) -> Option<SyntaxElement> + 'a) -> SyntaxRewriter<'a> {
362 SyntaxRewriter {
363 f: Some(Box::new(f)),
364 replacements: FxHashMap::default(),
365 insertions: IndexMap::default(),
366 }
367 }
368 pub fn delete<T: Clone + Into<SyntaxElement>>(&mut self, what: &T) { 361 pub fn delete<T: Clone + Into<SyntaxElement>>(&mut self, what: &T) {
369 let what = what.clone().into(); 362 let what = what.clone().into();
370 let replacement = Replacement::Delete; 363 let replacement = Replacement::Delete;
@@ -470,7 +463,7 @@ impl<'a> SyntaxRewriter<'a> {
470 pub fn rewrite(&self, node: &SyntaxNode) -> SyntaxNode { 463 pub fn rewrite(&self, node: &SyntaxNode) -> SyntaxNode {
471 let _p = profile::span("rewrite"); 464 let _p = profile::span("rewrite");
472 465
473 if self.f.is_none() && self.replacements.is_empty() && self.insertions.is_empty() { 466 if self.replacements.is_empty() && self.insertions.is_empty() {
474 return node.clone(); 467 return node.clone();
475 } 468 }
476 let green = self.rewrite_children(node); 469 let green = self.rewrite_children(node);
@@ -495,7 +488,6 @@ impl<'a> SyntaxRewriter<'a> {
495 } 488 }
496 } 489 }
497 490
498 assert!(self.f.is_none());
499 self.replacements 491 self.replacements
500 .keys() 492 .keys()
501 .filter_map(element_to_node_or_parent) 493 .filter_map(element_to_node_or_parent)
@@ -510,10 +502,6 @@ impl<'a> SyntaxRewriter<'a> {
510 } 502 }
511 503
512 fn replacement(&self, element: &SyntaxElement) -> Option<Replacement> { 504 fn replacement(&self, element: &SyntaxElement) -> Option<Replacement> {
513 if let Some(f) = &self.f {
514 assert!(self.replacements.is_empty());
515 return f(element).map(Replacement::Single);
516 }
517 self.replacements.get(element).cloned() 505 self.replacements.get(element).cloned()
518 } 506 }
519 507
@@ -574,7 +562,6 @@ fn element_to_green(element: SyntaxElement) -> NodeOrToken<rowan::GreenNode, row
574 562
575impl ops::AddAssign for SyntaxRewriter<'_> { 563impl ops::AddAssign for SyntaxRewriter<'_> {
576 fn add_assign(&mut self, rhs: SyntaxRewriter) { 564 fn add_assign(&mut self, rhs: SyntaxRewriter) {
577 assert!(rhs.f.is_none());
578 self.replacements.extend(rhs.replacements); 565 self.replacements.extend(rhs.replacements);
579 for (pos, insertions) in rhs.insertions.into_iter() { 566 for (pos, insertions) in rhs.insertions.into_iter() {
580 match self.insertions.entry(pos) { 567 match self.insertions.entry(pos) {
diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs
index 882e9fa09..42da09606 100644
--- a/crates/syntax/src/ast/make.rs
+++ b/crates/syntax/src/ast/make.rs
@@ -137,6 +137,17 @@ pub fn use_(visibility: Option<ast::Visibility>, use_tree: ast::UseTree) -> ast:
137 ast_from_text(&format!("{}use {};", visibility, use_tree)) 137 ast_from_text(&format!("{}use {};", visibility, use_tree))
138} 138}
139 139
140pub fn record_expr(path: ast::Path, fields: ast::RecordExprFieldList) -> ast::RecordExpr {
141 ast_from_text(&format!("fn f() {{ {} {} }}", path, fields))
142}
143
144pub fn record_expr_field_list(
145 fields: impl IntoIterator<Item = ast::RecordExprField>,
146) -> ast::RecordExprFieldList {
147 let fields = fields.into_iter().join(", ");
148 ast_from_text(&format!("fn f() {{ S {{ {} }} }}", fields))
149}
150
140pub fn record_expr_field(name: ast::NameRef, expr: Option<ast::Expr>) -> ast::RecordExprField { 151pub fn record_expr_field(name: ast::NameRef, expr: Option<ast::Expr>) -> ast::RecordExprField {
141 return match expr { 152 return match expr {
142 Some(expr) => from_text(&format!("{}: {}", name, expr)), 153 Some(expr) => from_text(&format!("{}: {}", name, expr)),
@@ -294,6 +305,14 @@ pub fn wildcard_pat() -> ast::WildcardPat {
294 } 305 }
295} 306}
296 307
308pub fn literal_pat(lit: &str) -> ast::LiteralPat {
309 return from_text(lit);
310
311 fn from_text(text: &str) -> ast::LiteralPat {
312 ast_from_text(&format!("fn f() {{ match x {{ {} => {{}} }} }}", text))
313 }
314}
315
297/// Creates a tuple of patterns from an iterator of patterns. 316/// Creates a tuple of patterns from an iterator of patterns.
298/// 317///
299/// Invariant: `pats` must be length > 0 318/// Invariant: `pats` must be length > 0
@@ -331,6 +350,21 @@ pub fn record_pat(path: ast::Path, pats: impl IntoIterator<Item = ast::Pat>) ->
331 } 350 }
332} 351}
333 352
353pub fn record_pat_with_fields(path: ast::Path, fields: ast::RecordPatFieldList) -> ast::RecordPat {
354 ast_from_text(&format!("fn f({} {}: ()))", path, fields))
355}
356
357pub fn record_pat_field_list(
358 fields: impl IntoIterator<Item = ast::RecordPatField>,
359) -> ast::RecordPatFieldList {
360 let fields = fields.into_iter().join(", ");
361 ast_from_text(&format!("fn f(S {{ {} }}: ()))", fields))
362}
363
364pub fn record_pat_field(name_ref: ast::NameRef, pat: ast::Pat) -> ast::RecordPatField {
365 ast_from_text(&format!("fn f(S {{ {}: {} }}: ()))", name_ref, pat))
366}
367
334/// Returns a `BindPat` if the path has just one segment, a `PathPat` otherwise. 368/// Returns a `BindPat` if the path has just one segment, a `PathPat` otherwise.
335pub fn path_pat(path: ast::Path) -> ast::Pat { 369pub fn path_pat(path: ast::Path) -> ast::Pat {
336 return from_text(&path.to_string()); 370 return from_text(&path.to_string());
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..16b23adc6 100644
--- a/docs/dev/README.md
+++ b/docs/dev/README.md
@@ -208,20 +208,26 @@ Release process is handled by `release`, `dist` and `promote` xtasks, `release`
208 208
209Additionally, it assumes that remote for `rust-analyzer` is called `upstream` (I use `origin` to point to my fork). 209Additionally, it assumes that remote for `rust-analyzer` is called `upstream` (I use `origin` to point to my fork).
210 210
211`release` calls the GitHub API calls to scrape pull request comments and categorize them in the changelog.
212This step uses the `curl` and `jq` applications, which need to be available in `PATH`.
213Finally, you need to obtain a GitHub personal access token and set the `GITHUB_TOKEN` environment variable.
214
211Release steps: 215Release steps:
212 216
2131. Inside rust-analyzer, run `cargo xtask release`. This will: 2171. Set the `GITHUB_TOKEN` environment variable.
2182. Inside rust-analyzer, run `cargo xtask release`. This will:
214 * checkout the `release` branch 219 * checkout the `release` branch
215 * reset it to `upstream/nightly` 220 * reset it to `upstream/nightly`
216 * push it to `upstream`. This triggers GitHub Actions which: 221 * push it to `upstream`. This triggers GitHub Actions which:
217 * runs `cargo xtask dist` to package binaries and VS Code extension 222 * runs `cargo xtask dist` to package binaries and VS Code extension
218 * makes a GitHub release 223 * makes a GitHub release
219 * pushes VS Code extension to the marketplace 224 * pushes VS Code extension to the marketplace
220 * create new changelog in `rust-analyzer.github.io` 225 * call the GitHub API for PR details
2212. While the release is in progress, fill in the changelog 226 * create a new changelog in `rust-analyzer.github.io`
2223. Commit & push the changelog 2273. While the release is in progress, fill in the changelog
2234. Tweet 2284. Commit & push the changelog
2245. Inside `rust-analyzer`, run `cargo xtask promote` -- this will create a PR to rust-lang/rust updating rust-analyzer's submodule. 2295. Tweet
2306. Inside `rust-analyzer`, run `cargo xtask promote` -- this will create a PR to rust-lang/rust updating rust-analyzer's submodule.
225 Self-approve the PR. 231 Self-approve the PR.
226 232
227If the GitHub Actions release fails because of a transient problem like a timeout, you can re-run the job from the Actions console. 233If the GitHub Actions release fails because of a transient problem like a timeout, you can re-run the job from the Actions console.
@@ -229,7 +235,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. 235Make sure to remove the new changelog post created when running `cargo xtask release` a second time.
230 236
231We release "nightly" every night automatically and promote the latest nightly to "stable" manually, every week. 237We release "nightly" every night automatically and promote the latest nightly to "stable" manually, every week.
238
232We don't do "patch" releases, unless something truly egregious comes up. 239We don't do "patch" releases, unless something truly egregious comes up.
240To do a patch release, cherry-pick the fix on top of the current `release` branch and push the branch.
241There'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.
242Note: 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 243
234## Permissions 244## Permissions
235 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--
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}
diff --git a/xtask/src/release.rs b/xtask/src/release.rs
index dde5d14ee..22bb50467 100644
--- a/xtask/src/release.rs
+++ b/xtask/src/release.rs
@@ -1,4 +1,4 @@
1use std::fmt::Write; 1mod changelog;
2 2
3use xshell::{cmd, cp, pushd, read_dir, write_file}; 3use xshell::{cmd, cp, pushd, read_dir, write_file};
4 4
@@ -38,42 +38,7 @@ impl flags::Release {
38 let tags = cmd!("git tag --list").read()?; 38 let tags = cmd!("git tag --list").read()?;
39 let prev_tag = tags.lines().filter(|line| is_release_tag(line)).last().unwrap(); 39 let prev_tag = tags.lines().filter(|line| is_release_tag(line)).last().unwrap();
40 40
41 let git_log = cmd!("git log {prev_tag}..HEAD --merges --reverse").read()?; 41 let contents = changelog::get_changelog(changelog_n, &commit, prev_tag, &today)?;
42 let mut git_log_summary = String::new();
43 for line in git_log.lines() {
44 let line = line.trim_start();
45 if let Some(p) = line.find(':') {
46 if let Ok(pr) = line[..p].parse::<u32>() {
47 writeln!(git_log_summary, "* pr:{}[]{}", pr, &line[p + 1..]).unwrap();
48 }
49 }
50 }
51
52 let contents = format!(
53 "\
54= Changelog #{}
55:sectanchors:
56:page-layout: post
57
58Commit: commit:{}[] +
59Release: release:{}[]
60
61== Sponsors
62
63**Become a sponsor:** On https://opencollective.com/rust-analyzer/[OpenCollective] or
64https://github.com/sponsors/rust-analyzer[GitHub Sponsors].
65
66== New Features
67
68{}
69
70== Fixes
71
72== Internal Improvements
73",
74 changelog_n, commit, today, git_log_summary
75 );
76
77 let path = changelog_dir.join(format!("{}-changelog-{}.adoc", today, changelog_n)); 42 let path = changelog_dir.join(format!("{}-changelog-{}.adoc", today, changelog_n));
78 write_file(&path, &contents)?; 43 write_file(&path, &contents)?;
79 44
diff --git a/xtask/src/release/changelog.rs b/xtask/src/release/changelog.rs
new file mode 100644
index 000000000..ffcae2cf7
--- /dev/null
+++ b/xtask/src/release/changelog.rs
@@ -0,0 +1,159 @@
1use std::fmt::Write;
2use std::{env, iter};
3
4use anyhow::{bail, Result};
5use xshell::cmd;
6
7pub(crate) fn get_changelog(
8 changelog_n: usize,
9 commit: &str,
10 prev_tag: &str,
11 today: &str,
12) -> Result<String> {
13 let git_log = cmd!("git log {prev_tag}..HEAD --merges --reverse").read()?;
14 let mut features = String::new();
15 let mut fixes = String::new();
16 let mut internal = String::new();
17 let mut others = String::new();
18 for line in git_log.lines() {
19 let line = line.trim_start();
20 if let Some(p) = line.find(':') {
21 let pr = &line[..p];
22 if let Ok(pr_num) = pr.parse::<u32>() {
23 let accept = "Accept: application/vnd.github.v3+json";
24 let token = match env::var("GITHUB_TOKEN") {
25 Ok(token) => token,
26 Err(_) => bail!("Please obtain a personal access token from https://github.com/settings/tokens and set the `GITHUB_TOKEN` environment variable."),
27 };
28 let authorization = format!("Authorization: token {}", token);
29 let pr_url = "https://api.github.com/repos/rust-analyzer/rust-analyzer/issues";
30
31 // we don't use an HTTPS client or JSON parser to keep the build times low
32 let pr_json =
33 cmd!("curl -s -H {accept} -H {authorization} {pr_url}/{pr}").read()?;
34 let pr_title = cmd!("jq .title").stdin(&pr_json).read()?;
35 let pr_title = unescape(&pr_title[1..pr_title.len() - 1]);
36 let pr_comment = cmd!("jq .body").stdin(pr_json).read()?;
37
38 let comments_json =
39 cmd!("curl -s -H {accept} -H {authorization} {pr_url}/{pr}/comments").read()?;
40 let pr_comments = cmd!("jq .[].body").stdin(comments_json).read()?;
41
42 let l = iter::once(pr_comment.as_str())
43 .chain(pr_comments.lines())
44 .rev()
45 .find_map(|it| {
46 let it = unescape(&it[1..it.len() - 1]);
47 it.lines().find_map(parse_changelog_line)
48 })
49 .into_iter()
50 .next()
51 .unwrap_or_else(|| parse_title_line(&pr_title));
52 let s = match l.kind {
53 PrKind::Feature => &mut features,
54 PrKind::Fix => &mut fixes,
55 PrKind::Internal => &mut internal,
56 PrKind::Other => &mut others,
57 PrKind::Skip => continue,
58 };
59 writeln!(s, "* pr:{}[] {}", pr_num, l.message.as_deref().unwrap_or(&pr_title))
60 .unwrap();
61 }
62 }
63 }
64
65 let contents = format!(
66 "\
67= Changelog #{}
68:sectanchors:
69:page-layout: post
70
71Commit: commit:{}[] +
72Release: release:{}[]
73
74== Sponsors
75
76**Become a sponsor:** On https://opencollective.com/rust-analyzer/[OpenCollective] or
77https://github.com/sponsors/rust-analyzer[GitHub Sponsors].
78
79== New Features
80
81{}
82
83== Fixes
84
85{}
86
87== Internal Improvements
88
89{}
90
91== Others
92
93{}
94",
95 changelog_n, commit, today, features, fixes, internal, others
96 );
97 Ok(contents)
98}
99
100#[derive(Clone, Copy)]
101enum PrKind {
102 Feature,
103 Fix,
104 Internal,
105 Other,
106 Skip,
107}
108
109struct PrInfo {
110 message: Option<String>,
111 kind: PrKind,
112}
113
114fn unescape(s: &str) -> String {
115 s.replace(r#"\""#, "").replace(r#"\n"#, "\n").replace(r#"\r"#, "")
116}
117
118fn parse_changelog_line(s: &str) -> Option<PrInfo> {
119 let parts = s.splitn(3, ' ').collect::<Vec<_>>();
120 if parts.len() < 2 || parts[0] != "changelog" {
121 return None;
122 }
123 let message = parts.get(2).map(|it| it.to_string());
124 let kind = match parts[1].trim_end_matches(':') {
125 "feature" => PrKind::Feature,
126 "fix" => PrKind::Fix,
127 "internal" => PrKind::Internal,
128 "skip" => PrKind::Skip,
129 _ => {
130 let kind = PrKind::Other;
131 let message = format!("{} {}", parts[1], message.unwrap_or_default());
132 return Some(PrInfo { kind, message: Some(message) });
133 }
134 };
135 let res = PrInfo { kind, message };
136 Some(res)
137}
138
139fn parse_title_line(s: &str) -> PrInfo {
140 let lower = s.to_ascii_lowercase();
141 const PREFIXES: [(&str, PrKind); 5] = [
142 ("feat: ", PrKind::Feature),
143 ("feature: ", PrKind::Feature),
144 ("fix: ", PrKind::Fix),
145 ("internal: ", PrKind::Internal),
146 ("minor: ", PrKind::Skip),
147 ];
148
149 for &(prefix, kind) in &PREFIXES {
150 if lower.starts_with(prefix) {
151 let message = match &kind {
152 PrKind::Skip => None,
153 _ => Some(s[prefix.len()..].to_string()),
154 };
155 return PrInfo { kind, message };
156 }
157 }
158 PrInfo { kind: PrKind::Other, message: Some(s.to_string()) }
159}