diff options
Diffstat (limited to 'crates')
42 files changed, 1817 insertions, 418 deletions
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 | |||
13 | crossbeam-channel = "0.5.0" | 13 | crossbeam-channel = "0.5.0" |
14 | log = "0.4.8" | 14 | log = "0.4.8" |
15 | cargo_metadata = "0.13" | 15 | cargo_metadata = "0.13" |
16 | serde = { version = "1.0.106", features = ["derive"] } | ||
16 | serde_json = "1.0.48" | 17 | serde_json = "1.0.48" |
17 | jod-thread = "0.1.1" | 18 | jod-thread = "0.1.1" |
18 | 19 | ||
diff --git a/crates/flycheck/src/lib.rs b/crates/flycheck/src/lib.rs index e2a59497a..1682d8bde 100644 --- a/crates/flycheck/src/lib.rs +++ b/crates/flycheck/src/lib.rs | |||
@@ -4,13 +4,14 @@ | |||
4 | 4 | ||
5 | use std::{ | 5 | use std::{ |
6 | fmt, | 6 | fmt, |
7 | io::{self, BufReader}, | 7 | io::{self, BufRead, BufReader}, |
8 | path::PathBuf, | 8 | path::PathBuf, |
9 | process::{self, Command, Stdio}, | 9 | process::{self, Command, Stdio}, |
10 | time::Duration, | 10 | time::Duration, |
11 | }; | 11 | }; |
12 | 12 | ||
13 | use crossbeam_channel::{never, select, unbounded, Receiver, Sender}; | 13 | use crossbeam_channel::{never, select, unbounded, Receiver, Sender}; |
14 | use serde::Deserialize; | ||
14 | use stdx::JodChild; | 15 | use stdx::JodChild; |
15 | 16 | ||
16 | pub use cargo_metadata::diagnostic::{ | 17 | pub use cargo_metadata::diagnostic::{ |
@@ -128,7 +129,7 @@ struct FlycheckActor { | |||
128 | 129 | ||
129 | enum Event { | 130 | enum Event { |
130 | Restart(Restart), | 131 | Restart(Restart), |
131 | CheckEvent(Option<cargo_metadata::Message>), | 132 | CheckEvent(Option<CargoMessage>), |
132 | } | 133 | } |
133 | 134 | ||
134 | impl FlycheckActor { | 135 | impl FlycheckActor { |
@@ -180,21 +181,16 @@ impl FlycheckActor { | |||
180 | self.progress(Progress::DidFinish(res)); | 181 | self.progress(Progress::DidFinish(res)); |
181 | } | 182 | } |
182 | Event::CheckEvent(Some(message)) => match message { | 183 | Event::CheckEvent(Some(message)) => match message { |
183 | cargo_metadata::Message::CompilerArtifact(msg) => { | 184 | CargoMessage::CompilerArtifact(msg) => { |
184 | self.progress(Progress::DidCheckCrate(msg.target.name)); | 185 | self.progress(Progress::DidCheckCrate(msg.target.name)); |
185 | } | 186 | } |
186 | 187 | ||
187 | cargo_metadata::Message::CompilerMessage(msg) => { | 188 | CargoMessage::Diagnostic(msg) => { |
188 | self.send(Message::AddDiagnostic { | 189 | self.send(Message::AddDiagnostic { |
189 | workspace_root: self.workspace_root.clone(), | 190 | workspace_root: self.workspace_root.clone(), |
190 | diagnostic: msg.message, | 191 | diagnostic: msg, |
191 | }); | 192 | }); |
192 | } | 193 | } |
193 | |||
194 | cargo_metadata::Message::BuildScriptExecuted(_) | ||
195 | | cargo_metadata::Message::BuildFinished(_) | ||
196 | | cargo_metadata::Message::TextLine(_) | ||
197 | | _ => {} | ||
198 | }, | 194 | }, |
199 | } | 195 | } |
200 | } | 196 | } |
@@ -261,7 +257,7 @@ struct CargoHandle { | |||
261 | child: JodChild, | 257 | child: JodChild, |
262 | #[allow(unused)] | 258 | #[allow(unused)] |
263 | thread: jod_thread::JoinHandle<io::Result<bool>>, | 259 | thread: jod_thread::JoinHandle<io::Result<bool>>, |
264 | receiver: Receiver<cargo_metadata::Message>, | 260 | receiver: Receiver<CargoMessage>, |
265 | } | 261 | } |
266 | 262 | ||
267 | impl CargoHandle { | 263 | impl CargoHandle { |
@@ -294,14 +290,11 @@ impl CargoHandle { | |||
294 | 290 | ||
295 | struct CargoActor { | 291 | struct CargoActor { |
296 | child_stdout: process::ChildStdout, | 292 | child_stdout: process::ChildStdout, |
297 | sender: Sender<cargo_metadata::Message>, | 293 | sender: Sender<CargoMessage>, |
298 | } | 294 | } |
299 | 295 | ||
300 | impl CargoActor { | 296 | impl CargoActor { |
301 | fn new( | 297 | fn new(child_stdout: process::ChildStdout, sender: Sender<CargoMessage>) -> CargoActor { |
302 | child_stdout: process::ChildStdout, | ||
303 | sender: Sender<cargo_metadata::Message>, | ||
304 | ) -> CargoActor { | ||
305 | CargoActor { child_stdout, sender } | 298 | CargoActor { child_stdout, sender } |
306 | } | 299 | } |
307 | fn run(self) -> io::Result<bool> { | 300 | fn run(self) -> io::Result<bool> { |
@@ -315,7 +308,7 @@ impl CargoActor { | |||
315 | // erroneus output. | 308 | // erroneus output. |
316 | let stdout = BufReader::new(self.child_stdout); | 309 | let stdout = BufReader::new(self.child_stdout); |
317 | let mut read_at_least_one_message = false; | 310 | let mut read_at_least_one_message = false; |
318 | for message in cargo_metadata::Message::parse_stream(stdout) { | 311 | for message in stdout.lines() { |
319 | let message = match message { | 312 | let message = match message { |
320 | Ok(message) => message, | 313 | Ok(message) => message, |
321 | Err(err) => { | 314 | Err(err) => { |
@@ -326,13 +319,44 @@ impl CargoActor { | |||
326 | 319 | ||
327 | read_at_least_one_message = true; | 320 | read_at_least_one_message = true; |
328 | 321 | ||
329 | // Skip certain kinds of messages to only spend time on what's useful | 322 | // Try to deserialize a message from Cargo or Rustc. |
330 | match &message { | 323 | let mut deserializer = serde_json::Deserializer::from_str(&message); |
331 | cargo_metadata::Message::CompilerArtifact(artifact) if artifact.fresh => (), | 324 | deserializer.disable_recursion_limit(); |
332 | cargo_metadata::Message::BuildScriptExecuted(_) => (), | 325 | if let Ok(message) = JsonMessage::deserialize(&mut deserializer) { |
333 | _ => self.sender.send(message).unwrap(), | 326 | match message { |
327 | // Skip certain kinds of messages to only spend time on what's useful | ||
328 | JsonMessage::Cargo(message) => match message { | ||
329 | cargo_metadata::Message::CompilerArtifact(artifact) if !artifact.fresh => { | ||
330 | self.sender.send(CargoMessage::CompilerArtifact(artifact)).unwrap() | ||
331 | } | ||
332 | cargo_metadata::Message::CompilerMessage(msg) => { | ||
333 | self.sender.send(CargoMessage::Diagnostic(msg.message)).unwrap() | ||
334 | } | ||
335 | |||
336 | cargo_metadata::Message::CompilerArtifact(_) | ||
337 | | cargo_metadata::Message::BuildScriptExecuted(_) | ||
338 | | cargo_metadata::Message::BuildFinished(_) | ||
339 | | cargo_metadata::Message::TextLine(_) | ||
340 | | _ => (), | ||
341 | }, | ||
342 | JsonMessage::Rustc(message) => { | ||
343 | self.sender.send(CargoMessage::Diagnostic(message)).unwrap() | ||
344 | } | ||
345 | } | ||
334 | } | 346 | } |
335 | } | 347 | } |
336 | Ok(read_at_least_one_message) | 348 | Ok(read_at_least_one_message) |
337 | } | 349 | } |
338 | } | 350 | } |
351 | |||
352 | enum CargoMessage { | ||
353 | CompilerArtifact(cargo_metadata::Artifact), | ||
354 | Diagnostic(Diagnostic), | ||
355 | } | ||
356 | |||
357 | #[derive(Deserialize)] | ||
358 | #[serde(untagged)] | ||
359 | enum JsonMessage { | ||
360 | Cargo(cargo_metadata::Message), | ||
361 | Rustc(Diagnostic), | ||
362 | } | ||
diff --git a/crates/hir_def/src/body/tests.rs b/crates/hir_def/src/body/tests.rs index 63f5fe88d..3e8f16306 100644 --- a/crates/hir_def/src/body/tests.rs +++ b/crates/hir_def/src/body/tests.rs | |||
@@ -40,6 +40,14 @@ fn block_def_map_at(ra_fixture: &str) -> String { | |||
40 | module.def_map(&db).dump(&db) | 40 | module.def_map(&db).dump(&db) |
41 | } | 41 | } |
42 | 42 | ||
43 | fn check_block_scopes_at(ra_fixture: &str, expect: Expect) { | ||
44 | let (db, position) = crate::test_db::TestDB::with_position(ra_fixture); | ||
45 | |||
46 | let module = db.module_at_position(position); | ||
47 | let actual = module.def_map(&db).dump_block_scopes(&db); | ||
48 | expect.assert_eq(&actual); | ||
49 | } | ||
50 | |||
43 | fn check_at(ra_fixture: &str, expect: Expect) { | 51 | fn check_at(ra_fixture: &str, expect: Expect) { |
44 | let actual = block_def_map_at(ra_fixture); | 52 | let actual = block_def_map_at(ra_fixture); |
45 | expect.assert_eq(&actual); | 53 | expect.assert_eq(&actual); |
diff --git a/crates/hir_def/src/body/tests/block.rs b/crates/hir_def/src/body/tests/block.rs index 3b6ba4cde..bc3d0f138 100644 --- a/crates/hir_def/src/body/tests/block.rs +++ b/crates/hir_def/src/body/tests/block.rs | |||
@@ -134,6 +134,30 @@ struct Struct {} | |||
134 | } | 134 | } |
135 | 135 | ||
136 | #[test] | 136 | #[test] |
137 | fn nested_module_scoping() { | ||
138 | check_block_scopes_at( | ||
139 | r#" | ||
140 | fn f() { | ||
141 | mod module { | ||
142 | struct Struct {} | ||
143 | fn f() { | ||
144 | use self::Struct; | ||
145 | $0 | ||
146 | } | ||
147 | } | ||
148 | } | ||
149 | "#, | ||
150 | expect![[r#" | ||
151 | BlockId(1) in ModuleId { krate: CrateId(0), block: Some(BlockId(0)), local_id: Idx::<ModuleData>(0) } | ||
152 | BlockId(0) in ModuleId { krate: CrateId(0), block: None, local_id: Idx::<ModuleData>(0) } | ||
153 | crate scope | ||
154 | "#]], | ||
155 | ); | ||
156 | // FIXME: The module nesting here is wrong! | ||
157 | // The first block map should be located in module #1 (`mod module`), not #0 (BlockId(0) root module) | ||
158 | } | ||
159 | |||
160 | #[test] | ||
137 | fn legacy_macro_items() { | 161 | fn legacy_macro_items() { |
138 | // Checks that legacy-scoped `macro_rules!` from parent namespaces are resolved and expanded | 162 | // Checks that legacy-scoped `macro_rules!` from parent namespaces are resolved and expanded |
139 | // correctly. | 163 | // correctly. |
diff --git a/crates/hir_def/src/item_tree.rs b/crates/hir_def/src/item_tree.rs index 16a94a058..eaeca01bd 100644 --- a/crates/hir_def/src/item_tree.rs +++ b/crates/hir_def/src/item_tree.rs | |||
@@ -196,13 +196,6 @@ impl ItemTree { | |||
196 | self.raw_attrs(of).clone().filter(db, krate) | 196 | self.raw_attrs(of).clone().filter(db, krate) |
197 | } | 197 | } |
198 | 198 | ||
199 | pub fn all_inner_items(&self) -> impl Iterator<Item = ModItem> + '_ { | ||
200 | match &self.data { | ||
201 | Some(data) => Some(data.inner_items.values().flatten().copied()).into_iter().flatten(), | ||
202 | None => None.into_iter().flatten(), | ||
203 | } | ||
204 | } | ||
205 | |||
206 | pub fn inner_items_of_block(&self, block: FileAstId<ast::BlockExpr>) -> &[ModItem] { | 199 | pub fn inner_items_of_block(&self, block: FileAstId<ast::BlockExpr>) -> &[ModItem] { |
207 | match &self.data { | 200 | match &self.data { |
208 | Some(data) => data.inner_items.get(&block).map(|it| &**it).unwrap_or(&[]), | 201 | Some(data) => data.inner_items.get(&block).map(|it| &**it).unwrap_or(&[]), |
diff --git a/crates/hir_def/src/nameres.rs b/crates/hir_def/src/nameres.rs index 542f190a1..ba027c44a 100644 --- a/crates/hir_def/src/nameres.rs +++ b/crates/hir_def/src/nameres.rs | |||
@@ -410,6 +410,20 @@ impl DefMap { | |||
410 | } | 410 | } |
411 | } | 411 | } |
412 | 412 | ||
413 | pub fn dump_block_scopes(&self, db: &dyn DefDatabase) -> String { | ||
414 | let mut buf = String::new(); | ||
415 | let mut arc; | ||
416 | let mut current_map = self; | ||
417 | while let Some(block) = ¤t_map.block { | ||
418 | format_to!(buf, "{:?} in {:?}\n", block.block, block.parent); | ||
419 | arc = block.parent.def_map(db); | ||
420 | current_map = &*arc; | ||
421 | } | ||
422 | |||
423 | format_to!(buf, "crate scope\n"); | ||
424 | buf | ||
425 | } | ||
426 | |||
413 | fn shrink_to_fit(&mut self) { | 427 | fn shrink_to_fit(&mut self) { |
414 | // Exhaustive match to require handling new fields. | 428 | // Exhaustive match to require handling new fields. |
415 | let Self { | 429 | let Self { |
diff --git a/crates/hir_ty/Cargo.toml b/crates/hir_ty/Cargo.toml index abc0e7532..66b3418f2 100644 --- a/crates/hir_ty/Cargo.toml +++ b/crates/hir_ty/Cargo.toml | |||
@@ -18,9 +18,9 @@ ena = "0.14.0" | |||
18 | log = "0.4.8" | 18 | log = "0.4.8" |
19 | rustc-hash = "1.1.0" | 19 | rustc-hash = "1.1.0" |
20 | scoped-tls = "1" | 20 | scoped-tls = "1" |
21 | chalk-solve = { version = "0.60", default-features = false } | 21 | chalk-solve = { version = "0.64", default-features = false } |
22 | chalk-ir = "0.60" | 22 | chalk-ir = "0.64" |
23 | chalk-recursive = "0.60" | 23 | chalk-recursive = "0.64" |
24 | la-arena = { version = "0.2.0", path = "../../lib/arena" } | 24 | la-arena = { version = "0.2.0", path = "../../lib/arena" } |
25 | 25 | ||
26 | stdx = { path = "../stdx", version = "0.0.0" } | 26 | stdx = { path = "../stdx", version = "0.0.0" } |
diff --git a/crates/ide/src/prime_caches.rs b/crates/ide/src/prime_caches.rs index ea0acfaa0..03597f507 100644 --- a/crates/ide/src/prime_caches.rs +++ b/crates/ide/src/prime_caches.rs | |||
@@ -27,6 +27,7 @@ pub(crate) fn prime_caches(db: &RootDatabase, cb: &(dyn Fn(PrimeCachesProgress) | |||
27 | let topo = &graph.crates_in_topological_order(); | 27 | let topo = &graph.crates_in_topological_order(); |
28 | 28 | ||
29 | cb(PrimeCachesProgress::Started); | 29 | cb(PrimeCachesProgress::Started); |
30 | let _d = stdx::defer(|| cb(PrimeCachesProgress::Finished)); | ||
30 | 31 | ||
31 | // FIXME: This would be easy to parallelize, since it's in the ideal ordering for that. | 32 | // FIXME: This would be easy to parallelize, since it's in the ideal ordering for that. |
32 | // Unfortunately rayon prevents panics from propagation out of a `scope`, which breaks | 33 | // Unfortunately rayon prevents panics from propagation out of a `scope`, which breaks |
@@ -41,6 +42,4 @@ pub(crate) fn prime_caches(db: &RootDatabase, cb: &(dyn Fn(PrimeCachesProgress) | |||
41 | }); | 42 | }); |
42 | db.crate_def_map(*krate); | 43 | db.crate_def_map(*krate); |
43 | } | 44 | } |
44 | |||
45 | cb(PrimeCachesProgress::Finished); | ||
46 | } | 45 | } |
diff --git a/crates/ide/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 | ||
3 | use hir::{AsAssocItem, AssocItemContainer, Semantics, VariantDef}; | 3 | use hir::{AsAssocItem, Semantics}; |
4 | use ide_db::{ | 4 | use 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 | |||
305 | fn highlight_def(db: &RootDatabase, def: Definition) -> Highlight { | 279 | fn 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] | ||
547 | fn parent_matches<N: AstNode>(element: &SyntaxElement) -> bool { | ||
548 | element.parent().map_or(false, |it| N::can_cast(it.kind())) | ||
549 | } | ||
550 | |||
577 | fn is_child_of_impl(element: &SyntaxElement) -> bool { | 551 | fn 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)] |
42 | pub enum HlMod { | 42 | pub 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 | ||
170 | impl HlMod { | 175 | impl 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 | ||
237 | impl From<HlOperator> for Highlight { | ||
238 | fn from(op: HlOperator) -> Highlight { | ||
239 | Highlight::new(HlTag::Operator(op)) | ||
240 | } | ||
241 | } | ||
242 | |||
243 | impl From<HlPunct> for Highlight { | ||
244 | fn from(punct: HlPunct) -> Highlight { | ||
245 | Highlight::new(HlTag::Punctuation(punct)) | ||
246 | } | ||
247 | } | ||
248 | |||
249 | impl From<SymbolKind> for Highlight { | ||
250 | fn from(sym: SymbolKind) -> Highlight { | ||
251 | Highlight::new(HlTag::Symbol(sym)) | ||
252 | } | ||
253 | } | ||
254 | |||
232 | impl Highlight { | 255 | impl 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">-></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">-></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">-></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">-></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 > </span><span class="function documentation intra_doc_link injected">[`all_the_links`](all_the_links)</span><span class="comment documentation"> <</span> | 103 | <span class="comment documentation">/// This function is > </span><span class="function documentation injected intra_doc_link">[`all_the_links`](all_the_links)</span><span class="comment documentation"> <</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">-></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">-></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">-></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">-></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">-></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">-></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">-></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">-></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">-></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">-></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">-></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">-></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"><</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">-></span> <span class="parenthesis">(</span><span class="parenthesis">)</span><span class="angle">></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"><</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">-></span> <span class="parenthesis">(</span><span class="parenthesis">)</span><span class="angle">></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"><</span><span class="type_param declaration">T</span><span class="angle">></span> <span class="enum">Option</span><span class="angle"><</span><span class="type_param">T</span><span class="angle">></span> <span class="brace">{</span> | 230 | <span class="keyword">impl</span><span class="angle"><</span><span class="type_param declaration">T</span><span class="angle">></span> <span class="enum">Option</span><span class="angle"><</span><span class="type_param">T</span><span class="angle">></span> <span class="brace">{</span> |
231 | <span class="keyword">fn</span> <span class="function declaration associated">and</span><span class="angle"><</span><span class="type_param declaration">U</span><span class="angle">></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"><</span><span class="type_param">U</span><span class="angle">></span><span class="parenthesis">)</span> <span class="operator">-></span> <span class="enum">Option</span><span class="angle"><</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">></span> <span class="brace">{</span> | 231 | <span class="keyword">fn</span> <span class="function associated declaration">and</span><span class="angle"><</span><span class="type_param declaration">U</span><span class="angle">></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"><</span><span class="type_param">U</span><span class="angle">></span><span class="parenthesis">)</span> <span class="operator">-></span> <span class="enum">Option</span><span class="angle"><</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">></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">=></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">=></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">=></span> <span class="variable">Nope</span><span class="comma">,</span> | 234 | <span class="variable declaration">Nope</span> <span class="operator">=></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}; | |||
3 | use ide_db::helpers::mod_path_to_ast; | 3 | use ide_db::helpers::mod_path_to_ast; |
4 | use rustc_hash::FxHashMap; | 4 | use rustc_hash::FxHashMap; |
5 | use syntax::{ | 5 | use syntax::{ |
6 | algo::SyntaxRewriter, | ||
7 | ast::{self, AstNode}, | 6 | ast::{self, AstNode}, |
8 | SyntaxNode, | 7 | ted, SyntaxNode, |
9 | }; | 8 | }; |
10 | 9 | ||
11 | pub fn apply<'a, N: AstNode>(transformer: &dyn AstTransform<'a>, node: N) -> N { | 10 | pub 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 @@ | |||
1 | use ide_db::defs::{Definition, NameRefClass}; | ||
2 | use syntax::{ | ||
3 | ast::{self, AstNode, GenericParamsOwner, VisibilityOwner}, | ||
4 | match_ast, SyntaxNode, | ||
5 | }; | ||
6 | |||
7 | use 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 | // ``` | ||
48 | pub(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 | |||
73 | fn 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 | |||
103 | fn 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 | |||
181 | fn 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 | |||
205 | fn 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)] | ||
210 | mod 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#" | ||
229 | struct Inner; | ||
230 | struct A$0(Inner); | ||
231 | |||
232 | impl 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#" | ||
246 | struct Inner; | ||
247 | struct A { field1: Inner } | ||
248 | |||
249 | impl 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#" | ||
270 | struct Inner; | ||
271 | struct A$0(Inner); | ||
272 | |||
273 | impl 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#" | ||
287 | struct Inner; | ||
288 | struct A { field1: Inner } | ||
289 | |||
290 | impl 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#" | ||
311 | struct Inner; | ||
312 | struct A$0(Inner); | ||
313 | |||
314 | impl 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#" | ||
326 | struct Inner; | ||
327 | struct A { field1: Inner } | ||
328 | |||
329 | impl 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#" | ||
348 | struct A$0(pub u32, pub(crate) u64); | ||
349 | |||
350 | impl 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#" | ||
364 | struct A { pub field1: u32, pub(crate) field2: u64 } | ||
365 | |||
366 | impl 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#" | ||
387 | struct Inner$0(u32); | ||
388 | struct Outer(Inner); | ||
389 | |||
390 | impl 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#" | ||
405 | struct Inner { field1: u32 } | ||
406 | struct Outer(Inner); | ||
407 | |||
408 | impl 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#" | ||
427 | struct Inner(u32); | ||
428 | struct Outer$0(Inner); | ||
429 | |||
430 | impl 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#" | ||
445 | struct Inner(u32); | ||
446 | struct Outer { field1: Inner } | ||
447 | |||
448 | impl 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 | ||
471 | struct Inner; | ||
472 | struct A$0(Inner); | ||
473 | |||
474 | mod foo; | ||
475 | |||
476 | //- /foo.rs | ||
477 | use crate::{A, Inner}; | ||
478 | fn f() { | ||
479 | let a = A(Inner); | ||
480 | } | ||
481 | "#, | ||
482 | r#" | ||
483 | //- /main.rs | ||
484 | struct Inner; | ||
485 | struct A { field1: Inner } | ||
486 | |||
487 | mod foo; | ||
488 | |||
489 | //- /foo.rs | ||
490 | use crate::{A, Inner}; | ||
491 | fn 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#" | ||
503 | struct Wrap$0<T>(T) | ||
504 | where | ||
505 | T: Display; | ||
506 | "#, | ||
507 | r#" | ||
508 | struct Wrap<T> | ||
509 | where | ||
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 | ||
1546 | fn $0fun_name() -> i32 { | 1556 | fn $0fun_name() -> i32 { |
1547 | { 1 + 1 } | 1557 | 1 + 1 |
1548 | }"#, | 1558 | }"#, |
1549 | ); | 1559 | ); |
1550 | } | 1560 | } |
@@ -2526,17 +2536,15 @@ fn foo() { | |||
2526 | } | 2536 | } |
2527 | 2537 | ||
2528 | fn $0fun_name(n: &mut i32) { | 2538 | fn $0fun_name(n: &mut i32) { |
2529 | { | 2539 | *n += *n; |
2530 | *n += *n; | 2540 | bar(*n); |
2531 | bar(*n); | 2541 | bar(*n+1); |
2532 | bar(*n+1); | 2542 | bar(*n**n); |
2533 | bar(*n**n); | 2543 | bar(&*n); |
2534 | bar(&*n); | 2544 | n.inc(); |
2535 | n.inc(); | 2545 | let v = n; |
2536 | let v = n; | 2546 | *v = v.succ(); |
2537 | *v = v.succ(); | 2547 | n.succ(); |
2538 | n.succ(); | ||
2539 | } | ||
2540 | }", | 2548 | }", |
2541 | ); | 2549 | ); |
2542 | } | 2550 | } |
diff --git a/crates/ide_assists/src/handlers/fill_match_arms.rs b/crates/ide_assists/src/handlers/fill_match_arms.rs index a30c4d04e..be927cc1c 100644 --- a/crates/ide_assists/src/handlers/fill_match_arms.rs +++ b/crates/ide_assists/src/handlers/fill_match_arms.rs | |||
@@ -53,7 +53,7 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option< | |||
53 | .iter() | 53 | .iter() |
54 | .filter_map(ast::MatchArm::pat) | 54 | .filter_map(ast::MatchArm::pat) |
55 | .flat_map(|pat| match pat { | 55 | .flat_map(|pat| match pat { |
56 | // Special casee OrPat as separate top-level pats | 56 | // Special case OrPat as separate top-level pats |
57 | Pat::OrPat(or_pat) => Either::Left(or_pat.pats()), | 57 | Pat::OrPat(or_pat) => Either::Left(or_pat.pats()), |
58 | _ => Either::Right(iter::once(pat)), | 58 | _ => Either::Right(iter::once(pat)), |
59 | }) | 59 | }) |
@@ -72,7 +72,11 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option< | |||
72 | .filter(|variant_pat| is_variant_missing(&top_lvl_pats, variant_pat)) | 72 | .filter(|variant_pat| is_variant_missing(&top_lvl_pats, variant_pat)) |
73 | .map(|pat| make::match_arm(iter::once(pat), make::expr_empty_block())) | 73 | .map(|pat| make::match_arm(iter::once(pat), make::expr_empty_block())) |
74 | .collect::<Vec<_>>(); | 74 | .collect::<Vec<_>>(); |
75 | if Some(enum_def) == FamousDefs(&ctx.sema, Some(module.krate())).core_option_Option() { | 75 | if Some(enum_def) |
76 | == FamousDefs(&ctx.sema, Some(module.krate())) | ||
77 | .core_option_Option() | ||
78 | .map(|x| lift_enum(x)) | ||
79 | { | ||
76 | // Match `Some` variant first. | 80 | // Match `Some` variant first. |
77 | cov_mark::hit!(option_order); | 81 | cov_mark::hit!(option_order); |
78 | variants.reverse() | 82 | variants.reverse() |
@@ -151,49 +155,99 @@ fn does_pat_match_variant(pat: &Pat, var: &Pat) -> bool { | |||
151 | } | 155 | } |
152 | } | 156 | } |
153 | 157 | ||
154 | fn resolve_enum_def(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<hir::Enum> { | 158 | #[derive(Eq, PartialEq, Clone)] |
159 | enum ExtendedEnum { | ||
160 | Bool, | ||
161 | Enum(hir::Enum), | ||
162 | } | ||
163 | |||
164 | #[derive(Eq, PartialEq, Clone)] | ||
165 | enum ExtendedVariant { | ||
166 | True, | ||
167 | False, | ||
168 | Variant(hir::Variant), | ||
169 | } | ||
170 | |||
171 | fn lift_enum(e: hir::Enum) -> ExtendedEnum { | ||
172 | ExtendedEnum::Enum(e) | ||
173 | } | ||
174 | |||
175 | impl ExtendedEnum { | ||
176 | fn variants(&self, db: &RootDatabase) -> Vec<ExtendedVariant> { | ||
177 | match self { | ||
178 | ExtendedEnum::Enum(e) => { | ||
179 | e.variants(db).into_iter().map(|x| ExtendedVariant::Variant(x)).collect::<Vec<_>>() | ||
180 | } | ||
181 | ExtendedEnum::Bool => { | ||
182 | Vec::<ExtendedVariant>::from([ExtendedVariant::True, ExtendedVariant::False]) | ||
183 | } | ||
184 | } | ||
185 | } | ||
186 | } | ||
187 | |||
188 | fn resolve_enum_def(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<ExtendedEnum> { | ||
155 | sema.type_of_expr(&expr)?.autoderef(sema.db).find_map(|ty| match ty.as_adt() { | 189 | sema.type_of_expr(&expr)?.autoderef(sema.db).find_map(|ty| match ty.as_adt() { |
156 | Some(Adt::Enum(e)) => Some(e), | 190 | Some(Adt::Enum(e)) => Some(ExtendedEnum::Enum(e)), |
157 | _ => None, | 191 | _ => { |
192 | if ty.is_bool() { | ||
193 | Some(ExtendedEnum::Bool) | ||
194 | } else { | ||
195 | None | ||
196 | } | ||
197 | } | ||
158 | }) | 198 | }) |
159 | } | 199 | } |
160 | 200 | ||
161 | fn resolve_tuple_of_enum_def( | 201 | fn resolve_tuple_of_enum_def( |
162 | sema: &Semantics<RootDatabase>, | 202 | sema: &Semantics<RootDatabase>, |
163 | expr: &ast::Expr, | 203 | expr: &ast::Expr, |
164 | ) -> Option<Vec<hir::Enum>> { | 204 | ) -> Option<Vec<ExtendedEnum>> { |
165 | sema.type_of_expr(&expr)? | 205 | sema.type_of_expr(&expr)? |
166 | .tuple_fields(sema.db) | 206 | .tuple_fields(sema.db) |
167 | .iter() | 207 | .iter() |
168 | .map(|ty| { | 208 | .map(|ty| { |
169 | ty.autoderef(sema.db).find_map(|ty| match ty.as_adt() { | 209 | ty.autoderef(sema.db).find_map(|ty| match ty.as_adt() { |
170 | Some(Adt::Enum(e)) => Some(e), | 210 | Some(Adt::Enum(e)) => Some(lift_enum(e)), |
171 | // For now we only handle expansion for a tuple of enums. Here | 211 | // For now we only handle expansion for a tuple of enums. Here |
172 | // we map non-enum items to None and rely on `collect` to | 212 | // we map non-enum items to None and rely on `collect` to |
173 | // convert Vec<Option<hir::Enum>> into Option<Vec<hir::Enum>>. | 213 | // convert Vec<Option<hir::Enum>> into Option<Vec<hir::Enum>>. |
174 | _ => None, | 214 | _ => { |
215 | if ty.is_bool() { | ||
216 | Some(ExtendedEnum::Bool) | ||
217 | } else { | ||
218 | None | ||
219 | } | ||
220 | } | ||
175 | }) | 221 | }) |
176 | }) | 222 | }) |
177 | .collect() | 223 | .collect() |
178 | } | 224 | } |
179 | 225 | ||
180 | fn build_pat(db: &RootDatabase, module: hir::Module, var: hir::Variant) -> Option<ast::Pat> { | 226 | fn build_pat(db: &RootDatabase, module: hir::Module, var: ExtendedVariant) -> Option<ast::Pat> { |
181 | let path = mod_path_to_ast(&module.find_use_path(db, ModuleDef::from(var))?); | 227 | match var { |
228 | ExtendedVariant::Variant(var) => { | ||
229 | let path = mod_path_to_ast(&module.find_use_path(db, ModuleDef::from(var))?); | ||
230 | |||
231 | // FIXME: use HIR for this; it doesn't currently expose struct vs. tuple vs. unit variants though | ||
232 | let pat: ast::Pat = match var.source(db)?.value.kind() { | ||
233 | ast::StructKind::Tuple(field_list) => { | ||
234 | let pats = | ||
235 | iter::repeat(make::wildcard_pat().into()).take(field_list.fields().count()); | ||
236 | make::tuple_struct_pat(path, pats).into() | ||
237 | } | ||
238 | ast::StructKind::Record(field_list) => { | ||
239 | let pats = | ||
240 | field_list.fields().map(|f| make::ident_pat(f.name().unwrap()).into()); | ||
241 | make::record_pat(path, pats).into() | ||
242 | } | ||
243 | ast::StructKind::Unit => make::path_pat(path), | ||
244 | }; | ||
182 | 245 | ||
183 | // FIXME: use HIR for this; it doesn't currently expose struct vs. tuple vs. unit variants though | 246 | Some(pat) |
184 | let pat: ast::Pat = match var.source(db)?.value.kind() { | ||
185 | ast::StructKind::Tuple(field_list) => { | ||
186 | let pats = iter::repeat(make::wildcard_pat().into()).take(field_list.fields().count()); | ||
187 | make::tuple_struct_pat(path, pats).into() | ||
188 | } | ||
189 | ast::StructKind::Record(field_list) => { | ||
190 | let pats = field_list.fields().map(|f| make::ident_pat(f.name().unwrap()).into()); | ||
191 | make::record_pat(path, pats).into() | ||
192 | } | 247 | } |
193 | ast::StructKind::Unit => make::path_pat(path), | 248 | ExtendedVariant::True => Some(ast::Pat::from(make::literal_pat("true"))), |
194 | }; | 249 | ExtendedVariant::False => Some(ast::Pat::from(make::literal_pat("false"))), |
195 | 250 | } | |
196 | Some(pat) | ||
197 | } | 251 | } |
198 | 252 | ||
199 | #[cfg(test)] | 253 | #[cfg(test)] |
@@ -226,6 +280,21 @@ mod tests { | |||
226 | } | 280 | } |
227 | 281 | ||
228 | #[test] | 282 | #[test] |
283 | fn all_boolean_match_arms_provided() { | ||
284 | check_assist_not_applicable( | ||
285 | fill_match_arms, | ||
286 | r#" | ||
287 | fn foo(a: bool) { | ||
288 | match a$0 { | ||
289 | true => {} | ||
290 | false => {} | ||
291 | } | ||
292 | } | ||
293 | "#, | ||
294 | ) | ||
295 | } | ||
296 | |||
297 | #[test] | ||
229 | fn tuple_of_non_enum() { | 298 | fn tuple_of_non_enum() { |
230 | // for now this case is not handled, although it potentially could be | 299 | // for now this case is not handled, although it potentially could be |
231 | // in the future | 300 | // in the future |
@@ -241,6 +310,113 @@ mod tests { | |||
241 | } | 310 | } |
242 | 311 | ||
243 | #[test] | 312 | #[test] |
313 | fn fill_match_arms_boolean() { | ||
314 | check_assist( | ||
315 | fill_match_arms, | ||
316 | r#" | ||
317 | fn foo(a: bool) { | ||
318 | match a$0 { | ||
319 | } | ||
320 | } | ||
321 | "#, | ||
322 | r#" | ||
323 | fn foo(a: bool) { | ||
324 | match a { | ||
325 | $0true => {} | ||
326 | false => {} | ||
327 | } | ||
328 | } | ||
329 | "#, | ||
330 | ) | ||
331 | } | ||
332 | |||
333 | #[test] | ||
334 | fn partial_fill_boolean() { | ||
335 | check_assist( | ||
336 | fill_match_arms, | ||
337 | r#" | ||
338 | fn foo(a: bool) { | ||
339 | match a$0 { | ||
340 | true => {} | ||
341 | } | ||
342 | } | ||
343 | "#, | ||
344 | r#" | ||
345 | fn foo(a: bool) { | ||
346 | match a { | ||
347 | true => {} | ||
348 | $0false => {} | ||
349 | } | ||
350 | } | ||
351 | "#, | ||
352 | ) | ||
353 | } | ||
354 | |||
355 | #[test] | ||
356 | fn all_boolean_tuple_arms_provided() { | ||
357 | check_assist_not_applicable( | ||
358 | fill_match_arms, | ||
359 | r#" | ||
360 | fn foo(a: bool) { | ||
361 | match (a, a)$0 { | ||
362 | (true, true) => {} | ||
363 | (true, false) => {} | ||
364 | (false, true) => {} | ||
365 | (false, false) => {} | ||
366 | } | ||
367 | } | ||
368 | "#, | ||
369 | ) | ||
370 | } | ||
371 | |||
372 | #[test] | ||
373 | fn fill_boolean_tuple() { | ||
374 | check_assist( | ||
375 | fill_match_arms, | ||
376 | r#" | ||
377 | fn foo(a: bool) { | ||
378 | match (a, a)$0 { | ||
379 | } | ||
380 | } | ||
381 | "#, | ||
382 | r#" | ||
383 | fn foo(a: bool) { | ||
384 | match (a, a) { | ||
385 | $0(true, true) => {} | ||
386 | (true, false) => {} | ||
387 | (false, true) => {} | ||
388 | (false, false) => {} | ||
389 | } | ||
390 | } | ||
391 | "#, | ||
392 | ) | ||
393 | } | ||
394 | |||
395 | #[test] | ||
396 | fn partial_fill_boolean_tuple() { | ||
397 | check_assist( | ||
398 | fill_match_arms, | ||
399 | r#" | ||
400 | fn foo(a: bool) { | ||
401 | match (a, a)$0 { | ||
402 | (false, true) => {} | ||
403 | } | ||
404 | } | ||
405 | "#, | ||
406 | r#" | ||
407 | fn foo(a: bool) { | ||
408 | match (a, a) { | ||
409 | (false, true) => {} | ||
410 | $0(true, true) => {} | ||
411 | (true, false) => {} | ||
412 | (false, false) => {} | ||
413 | } | ||
414 | } | ||
415 | "#, | ||
416 | ) | ||
417 | } | ||
418 | |||
419 | #[test] | ||
244 | fn partial_fill_record_tuple() { | 420 | fn partial_fill_record_tuple() { |
245 | check_assist( | 421 | check_assist( |
246 | fill_match_arms, | 422 | fill_match_arms, |
diff --git a/crates/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 @@ | |||
1 | use ide_db::{defs::Definition, search::FileReference}; | 1 | use either::Either; |
2 | use hir::PathResolution; | ||
3 | use ide_db::{base_db::FileId, defs::Definition, search::FileReference}; | ||
2 | use rustc_hash::FxHashMap; | 4 | use rustc_hash::FxHashMap; |
3 | use syntax::{ | 5 | use 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 | // ``` |
29 | pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | 31 | pub(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 | ||
135 | struct InlineData { | ||
136 | let_stmt: ast::LetStmt, | ||
137 | delete_let: bool, | ||
138 | target: ast::NameOrNameRef, | ||
139 | replace_usages: FxHashMap<FileId, Vec<FileReference>>, | ||
140 | } | ||
141 | |||
142 | fn 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 | |||
173 | fn 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)] |
144 | mod tests { | 211 | mod 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#" | ||
802 | fn f() { | ||
803 | let xyz = 0; | ||
804 | xyz$0; | ||
805 | } | ||
806 | "#, | ||
807 | r#" | ||
808 | fn 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#" | ||
820 | fn f() { | ||
821 | let xyz = 0; | ||
822 | xyz$0; | ||
823 | xyz; | ||
824 | } | ||
825 | "#, | ||
826 | r#" | ||
827 | fn 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#" | ||
841 | fn 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#" | ||
854 | macro_rules! m { | ||
855 | ($i:ident) => { $i } | ||
856 | } | ||
857 | fn 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#" | ||
866 | macro_rules! m { | ||
867 | ($i:ident) => { $i } | ||
868 | } | ||
869 | fn 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#" |
211 | mod fmt { | 216 | mod 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 { | |||
221 | struct Foo { | 226 | struct Foo { |
222 | bar: String, | 227 | bar: String, |
223 | } | 228 | } |
224 | ", | 229 | "#, |
225 | " | 230 | r#" |
226 | mod fmt { | 231 | mod 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#" |
252 | mod foo { | 257 | mod foo { |
253 | pub trait Bar { | 258 | pub trait Bar { |
254 | type Qux; | 259 | type Qux; |
@@ -263,8 +268,8 @@ mod foo { | |||
263 | struct Foo { | 268 | struct Foo { |
264 | bar: String, | 269 | bar: String, |
265 | } | 270 | } |
266 | ", | 271 | "#, |
267 | " | 272 | r#" |
268 | mod foo { | 273 | mod 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)] |
302 | struct Foo { | 307 | struct Foo { |
303 | bar: String, | 308 | bar: String, |
304 | } | 309 | } |
305 | ", | 310 | "#, |
306 | " | 311 | r#" |
307 | struct Foo { | 312 | struct Foo { |
308 | bar: String, | 313 | bar: String, |
309 | } | 314 | } |
@@ -311,7 +316,7 @@ struct Foo { | |||
311 | impl Debug for Foo { | 316 | impl 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)] |
324 | pub struct Foo { | 329 | pub struct Foo { |
325 | bar: String, | 330 | bar: String, |
326 | } | 331 | } |
327 | ", | 332 | "#, |
328 | " | 333 | r#" |
329 | pub struct Foo { | 334 | pub struct Foo { |
330 | bar: String, | 335 | bar: String, |
331 | } | 336 | } |
@@ -333,7 +338,7 @@ pub struct Foo { | |||
333 | impl Debug for Foo { | 338 | impl 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)] |
346 | struct Foo {} | 351 | struct Foo {} |
347 | ", | 352 | "#, |
348 | " | 353 | r#" |
349 | #[derive(Display, Serialize)] | 354 | #[derive(Display, Serialize)] |
350 | struct Foo {} | 355 | struct Foo {} |
351 | 356 | ||
352 | impl Debug for Foo { | 357 | impl 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)] |
365 | struct Foo {} | 370 | struct 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)] |
376 | struct Foo {} | 381 | struct 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] |
384 | struct Foo {} | 389 | struct 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)] |
395 | struct Foo {} | 400 | struct 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)] | ||
412 | struct 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] |
295 | fn doctest_convert_tuple_struct_to_named_struct() { | ||
296 | check_doc_test( | ||
297 | "convert_tuple_struct_to_named_struct", | ||
298 | r#####" | ||
299 | struct Point$0(f32, f32); | ||
300 | |||
301 | impl 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#####" | ||
316 | struct Point { field1: f32, field2: f32 } | ||
317 | |||
318 | impl 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] | ||
295 | fn doctest_expand_glob_import() { | 336 | fn 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 @@ | |||
7 | use std::{convert::TryInto, mem}; | 7 | use std::{convert::TryInto, mem}; |
8 | 8 | ||
9 | use base_db::{FileId, FileRange, SourceDatabase, SourceDatabaseExt}; | 9 | use base_db::{FileId, FileRange, SourceDatabase, SourceDatabaseExt}; |
10 | use hir::{DefWithBody, HasAttrs, HasSource, InFile, ModuleSource, Semantics, Visibility}; | 10 | use hir::{ |
11 | DefWithBody, HasAttrs, HasSource, InFile, ModuleDef, ModuleSource, Semantics, Visibility, | ||
12 | }; | ||
11 | use once_cell::unsync::Lazy; | 13 | use once_cell::unsync::Lazy; |
12 | use rustc_hash::FxHashMap; | 14 | use rustc_hash::FxHashMap; |
13 | use syntax::{ast, match_ast, AstNode, TextRange, TextSize}; | 15 | use 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 | ||
308 | impl<'a> FindUsages<'a> { | 311 | impl<'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"] } | |||
15 | log = "0.4.8" | 15 | log = "0.4.8" |
16 | crossbeam-channel = "0.5.0" | 16 | crossbeam-channel = "0.5.0" |
17 | jod-thread = "0.1.1" | 17 | jod-thread = "0.1.1" |
18 | memmap = "0.7.0" | ||
19 | object = { version = "0.23.0", default-features = false, features = ["std", "read_core", "elf", "macho", "pe", "unaligned"] } | ||
20 | snap = "1.0" | ||
18 | 21 | ||
19 | tt = { path = "../tt", version = "0.0.0" } | 22 | tt = { path = "../tt", version = "0.0.0" } |
20 | base_db = { path = "../base_db", version = "0.0.0" } | 23 | base_db = { path = "../base_db", version = "0.0.0" } |
21 | stdx = { path = "../stdx", version = "0.0.0" } | 24 | stdx = { path = "../stdx", version = "0.0.0" } |
22 | snap = "1" | 25 | profile = { path = "../profile", version = "0.0.0" } |
23 | object = { version = "0.23.0", default-features = false, features = ["std", "read_core", "elf", "macho", "pe", "unaligned"] } | ||
24 | memmap = "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 | ||
3 | use std::{ | 3 | use 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}; | |||
13 | use itertools::Itertools; | 12 | use itertools::Itertools; |
14 | use paths::{AbsPath, AbsPathBuf}; | 13 | use paths::{AbsPath, AbsPathBuf}; |
15 | use rustc_hash::FxHashMap; | 14 | use rustc_hash::FxHashMap; |
16 | use stdx::{format_to, JodChild}; | 15 | use serde::Deserialize; |
16 | use stdx::format_to; | ||
17 | 17 | ||
18 | use crate::{cfg_flag::CfgFlag, CargoConfig}; | 18 | use 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 | }; |
18 | use lsp_types::{ClientCapabilities, MarkupKind}; | 18 | use lsp_types::{ClientCapabilities, MarkupKind}; |
19 | use project_model::{CargoConfig, ProjectJson, ProjectJsonData, ProjectManifest, RustcSource}; | 19 | use project_model::{CargoConfig, ProjectJson, ProjectJsonData, ProjectManifest, RustcSource}; |
20 | use rustc_hash::FxHashSet; | 20 | use rustc_hash::{FxHashMap, FxHashSet}; |
21 | use serde::{de::DeserializeOwned, Deserialize}; | 21 | use serde::{de::DeserializeOwned, Deserialize}; |
22 | use vfs::AbsPathBuf; | 22 | use vfs::AbsPathBuf; |
23 | 23 | ||
@@ -99,6 +99,9 @@ config_data! { | |||
99 | diagnostics_enableExperimental: bool = "true", | 99 | diagnostics_enableExperimental: bool = "true", |
100 | /// List of rust-analyzer diagnostics to disable. | 100 | /// List of rust-analyzer diagnostics to disable. |
101 | diagnostics_disabled: FxHashSet<String> = "[]", | 101 | diagnostics_disabled: FxHashSet<String> = "[]", |
102 | /// Map of prefixes to be substituted when parsing diagnostic file paths. | ||
103 | /// This should be the reverse mapping of what is passed to `rustc` as `--remap-path-prefix`. | ||
104 | diagnostics_remapPrefix: FxHashMap<String, String> = "{}", | ||
102 | /// List of warnings that should be displayed with info severity. | 105 | /// List of warnings that should be displayed with info severity. |
103 | /// | 106 | /// |
104 | /// The warnings will be indicated by a blue squiggly underline in code | 107 | /// The warnings will be indicated by a blue squiggly underline in code |
@@ -474,6 +477,7 @@ impl Config { | |||
474 | } | 477 | } |
475 | pub fn diagnostics_map(&self) -> DiagnosticsMapConfig { | 478 | pub fn diagnostics_map(&self) -> DiagnosticsMapConfig { |
476 | DiagnosticsMapConfig { | 479 | DiagnosticsMapConfig { |
480 | remap_prefix: self.data.diagnostics_remapPrefix.clone(), | ||
477 | warnings_as_info: self.data.diagnostics_warningsAsInfo.clone(), | 481 | warnings_as_info: self.data.diagnostics_warningsAsInfo.clone(), |
478 | warnings_as_hint: self.data.diagnostics_warningsAsHint.clone(), | 482 | warnings_as_hint: self.data.diagnostics_warningsAsHint.clone(), |
479 | } | 483 | } |
@@ -835,6 +839,9 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json | |||
835 | "items": { "type": "string" }, | 839 | "items": { "type": "string" }, |
836 | "uniqueItems": true, | 840 | "uniqueItems": true, |
837 | }, | 841 | }, |
842 | "FxHashMap<String, String>" => set! { | ||
843 | "type": "object", | ||
844 | }, | ||
838 | "Option<usize>" => set! { | 845 | "Option<usize>" => set! { |
839 | "type": ["null", "integer"], | 846 | "type": ["null", "integer"], |
840 | "minimum": 0, | 847 | "minimum": 0, |
diff --git a/crates/rust-analyzer/src/diagnostics.rs b/crates/rust-analyzer/src/diagnostics.rs index f01548c50..d4b9db362 100644 --- a/crates/rust-analyzer/src/diagnostics.rs +++ b/crates/rust-analyzer/src/diagnostics.rs | |||
@@ -12,6 +12,7 @@ pub(crate) type CheckFixes = Arc<FxHashMap<FileId, Vec<Fix>>>; | |||
12 | 12 | ||
13 | #[derive(Debug, Default, Clone)] | 13 | #[derive(Debug, Default, Clone)] |
14 | pub struct DiagnosticsMapConfig { | 14 | pub struct DiagnosticsMapConfig { |
15 | pub remap_prefix: FxHashMap<String, String>, | ||
15 | pub warnings_as_info: Vec<String>, | 16 | pub warnings_as_info: Vec<String>, |
16 | pub warnings_as_hint: Vec<String>, | 17 | pub warnings_as_hint: Vec<String>, |
17 | } | 18 | } |
diff --git a/crates/rust-analyzer/src/diagnostics/to_proto.rs b/crates/rust-analyzer/src/diagnostics/to_proto.rs index ca18997e4..82dd0da9a 100644 --- a/crates/rust-analyzer/src/diagnostics/to_proto.rs +++ b/crates/rust-analyzer/src/diagnostics/to_proto.rs | |||
@@ -1,6 +1,9 @@ | |||
1 | //! This module provides the functionality needed to convert diagnostics from | 1 | //! This module provides the functionality needed to convert diagnostics from |
2 | //! `cargo check` json format to the LSP diagnostic format. | 2 | //! `cargo check` json format to the LSP diagnostic format. |
3 | use std::{collections::HashMap, path::Path}; | 3 | use std::{ |
4 | collections::HashMap, | ||
5 | path::{Path, PathBuf}, | ||
6 | }; | ||
4 | 7 | ||
5 | use flycheck::{DiagnosticLevel, DiagnosticSpan}; | 8 | use flycheck::{DiagnosticLevel, DiagnosticSpan}; |
6 | use stdx::format_to; | 9 | use stdx::format_to; |
@@ -41,8 +44,12 @@ fn is_dummy_macro_file(file_name: &str) -> bool { | |||
41 | } | 44 | } |
42 | 45 | ||
43 | /// Converts a Rust span to a LSP location | 46 | /// Converts a Rust span to a LSP location |
44 | fn location(workspace_root: &Path, span: &DiagnosticSpan) -> lsp_types::Location { | 47 | fn location( |
45 | let file_name = workspace_root.join(&span.file_name); | 48 | config: &DiagnosticsMapConfig, |
49 | workspace_root: &Path, | ||
50 | span: &DiagnosticSpan, | ||
51 | ) -> lsp_types::Location { | ||
52 | let file_name = resolve_path(config, workspace_root, &span.file_name); | ||
46 | let uri = url_from_abs_path(&file_name); | 53 | let uri = url_from_abs_path(&file_name); |
47 | 54 | ||
48 | // FIXME: this doesn't handle UTF16 offsets correctly | 55 | // FIXME: this doesn't handle UTF16 offsets correctly |
@@ -58,32 +65,50 @@ fn location(workspace_root: &Path, span: &DiagnosticSpan) -> lsp_types::Location | |||
58 | /// | 65 | /// |
59 | /// This takes locations pointing into the standard library, or generally outside the current | 66 | /// This takes locations pointing into the standard library, or generally outside the current |
60 | /// workspace into account and tries to avoid those, in case macros are involved. | 67 | /// workspace into account and tries to avoid those, in case macros are involved. |
61 | fn primary_location(workspace_root: &Path, span: &DiagnosticSpan) -> lsp_types::Location { | 68 | fn primary_location( |
69 | config: &DiagnosticsMapConfig, | ||
70 | workspace_root: &Path, | ||
71 | span: &DiagnosticSpan, | ||
72 | ) -> lsp_types::Location { | ||
62 | let span_stack = std::iter::successors(Some(span), |span| Some(&span.expansion.as_ref()?.span)); | 73 | let span_stack = std::iter::successors(Some(span), |span| Some(&span.expansion.as_ref()?.span)); |
63 | for span in span_stack.clone() { | 74 | for span in span_stack.clone() { |
64 | let abs_path = workspace_root.join(&span.file_name); | 75 | let abs_path = resolve_path(config, workspace_root, &span.file_name); |
65 | if !is_dummy_macro_file(&span.file_name) && abs_path.starts_with(workspace_root) { | 76 | if !is_dummy_macro_file(&span.file_name) && abs_path.starts_with(workspace_root) { |
66 | return location(workspace_root, span); | 77 | return location(config, workspace_root, span); |
67 | } | 78 | } |
68 | } | 79 | } |
69 | 80 | ||
70 | // Fall back to the outermost macro invocation if no suitable span comes up. | 81 | // Fall back to the outermost macro invocation if no suitable span comes up. |
71 | let last_span = span_stack.last().unwrap(); | 82 | let last_span = span_stack.last().unwrap(); |
72 | location(workspace_root, last_span) | 83 | location(config, workspace_root, last_span) |
73 | } | 84 | } |
74 | 85 | ||
75 | /// Converts a secondary Rust span to a LSP related information | 86 | /// Converts a secondary Rust span to a LSP related information |
76 | /// | 87 | /// |
77 | /// If the span is unlabelled this will return `None`. | 88 | /// If the span is unlabelled this will return `None`. |
78 | fn diagnostic_related_information( | 89 | fn diagnostic_related_information( |
90 | config: &DiagnosticsMapConfig, | ||
79 | workspace_root: &Path, | 91 | workspace_root: &Path, |
80 | span: &DiagnosticSpan, | 92 | span: &DiagnosticSpan, |
81 | ) -> Option<lsp_types::DiagnosticRelatedInformation> { | 93 | ) -> Option<lsp_types::DiagnosticRelatedInformation> { |
82 | let message = span.label.clone()?; | 94 | let message = span.label.clone()?; |
83 | let location = location(workspace_root, span); | 95 | let location = location(config, workspace_root, span); |
84 | Some(lsp_types::DiagnosticRelatedInformation { location, message }) | 96 | Some(lsp_types::DiagnosticRelatedInformation { location, message }) |
85 | } | 97 | } |
86 | 98 | ||
99 | /// Resolves paths applying any matching path prefix remappings, and then | ||
100 | /// joining the path to the workspace root. | ||
101 | fn resolve_path(config: &DiagnosticsMapConfig, workspace_root: &Path, file_name: &str) -> PathBuf { | ||
102 | match config | ||
103 | .remap_prefix | ||
104 | .iter() | ||
105 | .find_map(|(from, to)| file_name.strip_prefix(from).map(|file_name| (to, file_name))) | ||
106 | { | ||
107 | Some((to, file_name)) => workspace_root.join(format!("{}{}", to, file_name)), | ||
108 | None => workspace_root.join(file_name), | ||
109 | } | ||
110 | } | ||
111 | |||
87 | struct SubDiagnostic { | 112 | struct SubDiagnostic { |
88 | related: lsp_types::DiagnosticRelatedInformation, | 113 | related: lsp_types::DiagnosticRelatedInformation, |
89 | suggested_fix: Option<lsp_ext::CodeAction>, | 114 | suggested_fix: Option<lsp_ext::CodeAction>, |
@@ -95,6 +120,7 @@ enum MappedRustChildDiagnostic { | |||
95 | } | 120 | } |
96 | 121 | ||
97 | fn map_rust_child_diagnostic( | 122 | fn map_rust_child_diagnostic( |
123 | config: &DiagnosticsMapConfig, | ||
98 | workspace_root: &Path, | 124 | workspace_root: &Path, |
99 | rd: &flycheck::Diagnostic, | 125 | rd: &flycheck::Diagnostic, |
100 | ) -> MappedRustChildDiagnostic { | 126 | ) -> MappedRustChildDiagnostic { |
@@ -108,7 +134,7 @@ fn map_rust_child_diagnostic( | |||
108 | let mut edit_map: HashMap<lsp_types::Url, Vec<lsp_types::TextEdit>> = HashMap::new(); | 134 | let mut edit_map: HashMap<lsp_types::Url, Vec<lsp_types::TextEdit>> = HashMap::new(); |
109 | for &span in &spans { | 135 | for &span in &spans { |
110 | if let Some(suggested_replacement) = &span.suggested_replacement { | 136 | if let Some(suggested_replacement) = &span.suggested_replacement { |
111 | let location = location(workspace_root, span); | 137 | let location = location(config, workspace_root, span); |
112 | let edit = lsp_types::TextEdit::new(location.range, suggested_replacement.clone()); | 138 | let edit = lsp_types::TextEdit::new(location.range, suggested_replacement.clone()); |
113 | edit_map.entry(location.uri).or_default().push(edit); | 139 | edit_map.entry(location.uri).or_default().push(edit); |
114 | } | 140 | } |
@@ -117,7 +143,7 @@ fn map_rust_child_diagnostic( | |||
117 | if edit_map.is_empty() { | 143 | if edit_map.is_empty() { |
118 | MappedRustChildDiagnostic::SubDiagnostic(SubDiagnostic { | 144 | MappedRustChildDiagnostic::SubDiagnostic(SubDiagnostic { |
119 | related: lsp_types::DiagnosticRelatedInformation { | 145 | related: lsp_types::DiagnosticRelatedInformation { |
120 | location: location(workspace_root, spans[0]), | 146 | location: location(config, workspace_root, spans[0]), |
121 | message: rd.message.clone(), | 147 | message: rd.message.clone(), |
122 | }, | 148 | }, |
123 | suggested_fix: None, | 149 | suggested_fix: None, |
@@ -125,7 +151,7 @@ fn map_rust_child_diagnostic( | |||
125 | } else { | 151 | } else { |
126 | MappedRustChildDiagnostic::SubDiagnostic(SubDiagnostic { | 152 | MappedRustChildDiagnostic::SubDiagnostic(SubDiagnostic { |
127 | related: lsp_types::DiagnosticRelatedInformation { | 153 | related: lsp_types::DiagnosticRelatedInformation { |
128 | location: location(workspace_root, spans[0]), | 154 | location: location(config, workspace_root, spans[0]), |
129 | message: rd.message.clone(), | 155 | message: rd.message.clone(), |
130 | }, | 156 | }, |
131 | suggested_fix: Some(lsp_ext::CodeAction { | 157 | suggested_fix: Some(lsp_ext::CodeAction { |
@@ -190,7 +216,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp( | |||
190 | let mut tags = Vec::new(); | 216 | let mut tags = Vec::new(); |
191 | 217 | ||
192 | for secondary_span in rd.spans.iter().filter(|s| !s.is_primary) { | 218 | for secondary_span in rd.spans.iter().filter(|s| !s.is_primary) { |
193 | let related = diagnostic_related_information(workspace_root, secondary_span); | 219 | let related = diagnostic_related_information(config, workspace_root, secondary_span); |
194 | if let Some(related) = related { | 220 | if let Some(related) = related { |
195 | subdiagnostics.push(SubDiagnostic { related, suggested_fix: None }); | 221 | subdiagnostics.push(SubDiagnostic { related, suggested_fix: None }); |
196 | } | 222 | } |
@@ -198,7 +224,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp( | |||
198 | 224 | ||
199 | let mut message = rd.message.clone(); | 225 | let mut message = rd.message.clone(); |
200 | for child in &rd.children { | 226 | for child in &rd.children { |
201 | let child = map_rust_child_diagnostic(workspace_root, &child); | 227 | let child = map_rust_child_diagnostic(config, workspace_root, &child); |
202 | match child { | 228 | match child { |
203 | MappedRustChildDiagnostic::SubDiagnostic(sub) => { | 229 | MappedRustChildDiagnostic::SubDiagnostic(sub) => { |
204 | subdiagnostics.push(sub); | 230 | subdiagnostics.push(sub); |
@@ -242,7 +268,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp( | |||
242 | primary_spans | 268 | primary_spans |
243 | .iter() | 269 | .iter() |
244 | .flat_map(|primary_span| { | 270 | .flat_map(|primary_span| { |
245 | let primary_location = primary_location(workspace_root, &primary_span); | 271 | let primary_location = primary_location(config, workspace_root, &primary_span); |
246 | 272 | ||
247 | let mut message = message.clone(); | 273 | let mut message = message.clone(); |
248 | if needs_primary_span_label { | 274 | if needs_primary_span_label { |
@@ -272,7 +298,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp( | |||
272 | // generated that code. | 298 | // generated that code. |
273 | let is_in_macro_call = i != 0; | 299 | let is_in_macro_call = i != 0; |
274 | 300 | ||
275 | let secondary_location = location(workspace_root, &span); | 301 | let secondary_location = location(config, workspace_root, &span); |
276 | if secondary_location == primary_location { | 302 | if secondary_location == primary_location { |
277 | continue; | 303 | continue; |
278 | } | 304 | } |
diff --git a/crates/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" | |||
10 | doctest = false | 10 | doctest = false |
11 | 11 | ||
12 | [dependencies] | 12 | [dependencies] |
13 | libc = "0.2.93" | ||
13 | backtrace = { version = "0.3.44", optional = true } | 14 | backtrace = { version = "0.3.44", optional = true } |
14 | always-assert = { version = "0.1.2", features = ["log"] } | 15 | always-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] | ||
19 | miow = "0.3.6" | ||
20 | winapi = "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. |
2 | use std::{cmp::Ordering, ops, process, time::Instant}; | 2 | use std::{cmp::Ordering, ops, time::Instant}; |
3 | 3 | ||
4 | mod macros; | 4 | mod macros; |
5 | pub mod process; | ||
5 | pub mod panic_context; | 6 | pub mod panic_context; |
6 | 7 | ||
7 | pub use always_assert::{always, never}; | 8 | pub use always_assert::{always, never}; |
@@ -178,18 +179,30 @@ where | |||
178 | start..start + len | 179 | start..start + len |
179 | } | 180 | } |
180 | 181 | ||
182 | pub fn defer<F: FnOnce()>(f: F) -> impl Drop { | ||
183 | struct D<F: FnOnce()>(Option<F>); | ||
184 | impl<F: FnOnce()> Drop for D<F> { | ||
185 | fn drop(&mut self) { | ||
186 | if let Some(f) = self.0.take() { | ||
187 | f() | ||
188 | } | ||
189 | } | ||
190 | } | ||
191 | D(Some(f)) | ||
192 | } | ||
193 | |||
181 | #[repr(transparent)] | 194 | #[repr(transparent)] |
182 | pub struct JodChild(pub process::Child); | 195 | pub struct JodChild(pub std::process::Child); |
183 | 196 | ||
184 | impl ops::Deref for JodChild { | 197 | impl 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 | ||
191 | impl ops::DerefMut for JodChild { | 204 | impl 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 | ||
204 | impl JodChild { | 217 | impl 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 | |||
6 | use std::{ | ||
7 | io, | ||
8 | process::{Command, Output, Stdio}, | ||
9 | }; | ||
10 | |||
11 | pub 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)] | ||
59 | mod 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)] | ||
134 | mod 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 | |||
14 | cov-mark = { version = "1.1", features = ["thread-local"] } | 14 | cov-mark = { version = "1.1", features = ["thread-local"] } |
15 | itertools = "0.10.0" | 15 | itertools = "0.10.0" |
16 | rowan = "=0.13.0-pre.3" | 16 | rowan = "=0.13.0-pre.3" |
17 | rustc_lexer = { version = "714.0.0", package = "rustc-ap-rustc_lexer" } | 17 | rustc_lexer = { version = "716.0.0", package = "rustc-ap-rustc_lexer" } |
18 | rustc-hash = "1.1.0" | 18 | rustc-hash = "1.1.0" |
19 | arrayvec = "0.7" | 19 | arrayvec = "0.7" |
20 | once_cell = "1.3.1" | 20 | once_cell = "1.3.1" |
diff --git a/crates/syntax/src/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)] |
344 | pub struct SyntaxRewriter<'a> { | 344 | pub 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 | ||
351 | impl fmt::Debug for SyntaxRewriter<'_> { | 351 | impl fmt::Debug for SyntaxRewriter<'_> { |
@@ -357,14 +357,7 @@ impl fmt::Debug for SyntaxRewriter<'_> { | |||
357 | } | 357 | } |
358 | } | 358 | } |
359 | 359 | ||
360 | impl<'a> SyntaxRewriter<'a> { | 360 | impl 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 | ||
575 | impl ops::AddAssign for SyntaxRewriter<'_> { | 563 | impl 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 | ||
140 | pub fn record_expr(path: ast::Path, fields: ast::RecordExprFieldList) -> ast::RecordExpr { | ||
141 | ast_from_text(&format!("fn f() {{ {} {} }}", path, fields)) | ||
142 | } | ||
143 | |||
144 | pub 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 | |||
140 | pub fn record_expr_field(name: ast::NameRef, expr: Option<ast::Expr>) -> ast::RecordExprField { | 151 | pub 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 | ||
308 | pub 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 | ||
353 | pub fn record_pat_with_fields(path: ast::Path, fields: ast::RecordPatFieldList) -> ast::RecordPat { | ||
354 | ast_from_text(&format!("fn f({} {}: ()))", path, fields)) | ||
355 | } | ||
356 | |||
357 | pub 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 | |||
364 | pub 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. |
335 | pub fn path_pat(path: ast::Path) -> ast::Pat { | 369 | pub 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 @@ | |||
1 | SOURCE_FILE@0..63 | 1 | SOURCE_FILE@0..102 |
2 | FN@0..62 | 2 | FN@0..101 |
3 | [email protected] "fn" | 3 | [email protected] "fn" |
4 | [email protected] " " | 4 | [email protected] " " |
5 | [email protected] | 5 | [email protected] |
@@ -8,7 +8,7 @@ [email protected] | |||
8 | [email protected] "(" | 8 | [email protected] "(" |
9 | [email protected] ")" | 9 | [email protected] ")" |
10 | [email protected] " " | 10 | [email protected] " " |
11 | BLOCK_EXPR@9..62 | 11 | BLOCK_EXPR@9..101 |
12 | [email protected] "{" | 12 | [email protected] "{" |
13 | [email protected] "\n " | 13 | [email protected] "\n " |
14 | [email protected] | 14 | [email protected] |
@@ -70,6 +70,52 @@ [email protected] | |||
70 | [email protected] "(" | 70 | [email protected] "(" |
71 | [email protected] ")" | 71 | [email protected] ")" |
72 | [email protected] ";" | 72 | [email protected] ";" |
73 | [email protected] "\n" | 73 | [email protected] "\n " |
74 | [email protected] "}" | 74 | [email protected] |
75 | [email protected] "\n" | 75 | [email protected] "let" |
76 | [email protected] " " | ||
77 | [email protected] | ||
78 | [email protected] | ||
79 | [email protected] | ||
80 | [email protected] | ||
81 | [email protected] "S" | ||
82 | [email protected] " " | ||
83 | [email protected] | ||
84 | [email protected] "{" | ||
85 | [email protected] " " | ||
86 | [email protected] | ||
87 | [email protected] | ||
88 | [email protected] "#" | ||
89 | [email protected] "[" | ||
90 | [email protected] | ||
91 | [email protected] | ||
92 | [email protected] | ||
93 | [email protected] "cfg" | ||
94 | [email protected] | ||
95 | [email protected] "(" | ||
96 | [email protected] "any" | ||
97 | [email protected] | ||
98 | [email protected] "(" | ||
99 | [email protected] ")" | ||
100 | [email protected] ")" | ||
101 | [email protected] "]" | ||
102 | [email protected] " " | ||
103 | [email protected] | ||
104 | [email protected] "x" | ||
105 | [email protected] ":" | ||
106 | [email protected] " " | ||
107 | [email protected] | ||
108 | [email protected] | ||
109 | [email protected] "1" | ||
110 | [email protected] " " | ||
111 | [email protected] "}" | ||
112 | [email protected] " " | ||
113 | [email protected] "=" | ||
114 | [email protected] " " | ||
115 | [email protected] | ||
116 | [email protected] "(" | ||
117 | [email protected] ")" | ||
118 | [email protected] ";" | ||
119 | [email protected] "\n" | ||
120 | [email protected] "}" | ||
121 | [email protected] "\n" | ||
diff --git a/crates/syntax/test_data/parser/inline/ok/0145_record_pat_field.rs b/crates/syntax/test_data/parser/inline/ok/0145_record_pat_field.rs index 26b1d5f89..53cfdc22d 100644 --- a/crates/syntax/test_data/parser/inline/ok/0145_record_pat_field.rs +++ b/crates/syntax/test_data/parser/inline/ok/0145_record_pat_field.rs | |||
@@ -1,4 +1,5 @@ | |||
1 | fn foo() { | 1 | fn foo() { |
2 | let S { 0: 1 } = (); | 2 | let S { 0: 1 } = (); |
3 | let S { x: 1 } = (); | 3 | let S { x: 1 } = (); |
4 | let S { #[cfg(any())] x: 1 } = (); | ||
4 | } | 5 | } |