diff options
-rw-r--r-- | crates/ide/src/references.rs | 46 | ||||
-rw-r--r-- | crates/ide_db/src/search.rs | 116 |
2 files changed, 119 insertions, 43 deletions
diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs index ae68b4392..571dd5452 100644 --- a/crates/ide/src/references.rs +++ b/crates/ide/src/references.rs | |||
@@ -686,6 +686,52 @@ fn g() { f(); } | |||
686 | ); | 686 | ); |
687 | } | 687 | } |
688 | 688 | ||
689 | #[test] | ||
690 | fn test_find_all_refs_struct_pat() { | ||
691 | check( | ||
692 | r#" | ||
693 | struct S { | ||
694 | field<|>: u8, | ||
695 | } | ||
696 | |||
697 | fn f(s: S) { | ||
698 | match s { | ||
699 | S { field } => {} | ||
700 | } | ||
701 | } | ||
702 | "#, | ||
703 | expect![[r#" | ||
704 | field RECORD_FIELD FileId(0) 15..24 15..20 Other | ||
705 | |||
706 | FileId(0) 68..73 FieldShorthandForField Read | ||
707 | "#]], | ||
708 | ); | ||
709 | } | ||
710 | |||
711 | #[test] | ||
712 | fn test_find_all_refs_enum_var_pat() { | ||
713 | check( | ||
714 | r#" | ||
715 | enum En { | ||
716 | Variant { | ||
717 | field<|>: u8, | ||
718 | } | ||
719 | } | ||
720 | |||
721 | fn f(e: En) { | ||
722 | match e { | ||
723 | En::Variant { field } => {} | ||
724 | } | ||
725 | } | ||
726 | "#, | ||
727 | expect![[r#" | ||
728 | field RECORD_FIELD FileId(0) 32..41 32..37 Other | ||
729 | |||
730 | FileId(0) 102..107 FieldShorthandForField Read | ||
731 | "#]], | ||
732 | ); | ||
733 | } | ||
734 | |||
689 | fn check(ra_fixture: &str, expect: Expect) { | 735 | fn check(ra_fixture: &str, expect: Expect) { |
690 | check_with_scope(ra_fixture, None, expect) | 736 | check_with_scope(ra_fixture, None, expect) |
691 | } | 737 | } |
diff --git a/crates/ide_db/src/search.rs b/crates/ide_db/src/search.rs index edab1d644..8e3dcd99c 100644 --- a/crates/ide_db/src/search.rs +++ b/crates/ide_db/src/search.rs | |||
@@ -12,8 +12,9 @@ use once_cell::unsync::Lazy; | |||
12 | use rustc_hash::FxHashMap; | 12 | use rustc_hash::FxHashMap; |
13 | use syntax::{ast, match_ast, AstNode, TextRange, TextSize}; | 13 | use syntax::{ast, match_ast, AstNode, TextRange, TextSize}; |
14 | 14 | ||
15 | use crate::defs::NameClass; | ||
15 | use crate::{ | 16 | use crate::{ |
16 | defs::{classify_name_ref, Definition, NameRefClass}, | 17 | defs::{classify_name, classify_name_ref, Definition, NameRefClass}, |
17 | RootDatabase, | 18 | RootDatabase, |
18 | }; | 19 | }; |
19 | 20 | ||
@@ -226,9 +227,9 @@ impl<'a> FindUsages<'a> { | |||
226 | 227 | ||
227 | let search_scope = { | 228 | let search_scope = { |
228 | let base = self.def.search_scope(sema.db); | 229 | let base = self.def.search_scope(sema.db); |
229 | match self.scope { | 230 | match &self.scope { |
230 | None => base, | 231 | None => base, |
231 | Some(scope) => base.intersection(&scope), | 232 | Some(scope) => base.intersection(scope), |
232 | } | 233 | } |
233 | }; | 234 | }; |
234 | 235 | ||
@@ -251,54 +252,83 @@ impl<'a> FindUsages<'a> { | |||
251 | continue; | 252 | continue; |
252 | } | 253 | } |
253 | 254 | ||
254 | let name_ref: ast::NameRef = | 255 | match sema.find_node_at_offset_with_descend(&tree, offset) { |
255 | match sema.find_node_at_offset_with_descend(&tree, offset) { | 256 | Some(name_ref) => { |
256 | Some(it) => it, | 257 | if self.found_name_ref(&name_ref, sink) { |
257 | None => continue, | ||
258 | }; | ||
259 | |||
260 | match classify_name_ref(&sema, &name_ref) { | ||
261 | Some(NameRefClass::Definition(def)) if &def == self.def => { | ||
262 | let kind = if is_record_lit_name_ref(&name_ref) | ||
263 | || is_call_expr_name_ref(&name_ref) | ||
264 | { | ||
265 | ReferenceKind::StructLiteral | ||
266 | } else { | ||
267 | ReferenceKind::Other | ||
268 | }; | ||
269 | |||
270 | let reference = Reference { | ||
271 | file_range: sema.original_range(name_ref.syntax()), | ||
272 | kind, | ||
273 | access: reference_access(&def, &name_ref), | ||
274 | }; | ||
275 | if sink(reference) { | ||
276 | return; | 258 | return; |
277 | } | 259 | } |
278 | } | 260 | } |
279 | Some(NameRefClass::FieldShorthand { local, field }) => { | 261 | None => match sema.find_node_at_offset_with_descend(&tree, offset) { |
280 | let reference = match self.def { | 262 | Some(name) => { |
281 | Definition::Field(_) if &field == self.def => Reference { | 263 | if self.found_name(&name, sink) { |
282 | file_range: self.sema.original_range(name_ref.syntax()), | 264 | return; |
283 | kind: ReferenceKind::FieldShorthandForField, | 265 | } |
284 | access: reference_access(&field, &name_ref), | ||
285 | }, | ||
286 | Definition::Local(l) if &local == l => Reference { | ||
287 | file_range: self.sema.original_range(name_ref.syntax()), | ||
288 | kind: ReferenceKind::FieldShorthandForLocal, | ||
289 | access: reference_access(&Definition::Local(local), &name_ref), | ||
290 | }, | ||
291 | _ => continue, // not a usage | ||
292 | }; | ||
293 | if sink(reference) { | ||
294 | return; | ||
295 | } | 266 | } |
296 | } | 267 | None => {} |
297 | _ => {} // not a usage | 268 | }, |
298 | } | 269 | } |
299 | } | 270 | } |
300 | } | 271 | } |
301 | } | 272 | } |
273 | |||
274 | fn found_name_ref( | ||
275 | &self, | ||
276 | name_ref: &ast::NameRef, | ||
277 | sink: &mut dyn FnMut(Reference) -> bool, | ||
278 | ) -> bool { | ||
279 | match classify_name_ref(self.sema, &name_ref) { | ||
280 | Some(NameRefClass::Definition(def)) if &def == self.def => { | ||
281 | let kind = if is_record_lit_name_ref(&name_ref) || is_call_expr_name_ref(&name_ref) | ||
282 | { | ||
283 | ReferenceKind::StructLiteral | ||
284 | } else { | ||
285 | ReferenceKind::Other | ||
286 | }; | ||
287 | |||
288 | let reference = Reference { | ||
289 | file_range: self.sema.original_range(name_ref.syntax()), | ||
290 | kind, | ||
291 | access: reference_access(&def, &name_ref), | ||
292 | }; | ||
293 | sink(reference) | ||
294 | } | ||
295 | Some(NameRefClass::FieldShorthand { local, field }) => { | ||
296 | let reference = match self.def { | ||
297 | Definition::Field(_) if &field == self.def => Reference { | ||
298 | file_range: self.sema.original_range(name_ref.syntax()), | ||
299 | kind: ReferenceKind::FieldShorthandForField, | ||
300 | access: reference_access(&field, &name_ref), | ||
301 | }, | ||
302 | Definition::Local(l) if &local == l => Reference { | ||
303 | file_range: self.sema.original_range(name_ref.syntax()), | ||
304 | kind: ReferenceKind::FieldShorthandForLocal, | ||
305 | access: reference_access(&Definition::Local(local), &name_ref), | ||
306 | }, | ||
307 | _ => return false, // not a usage | ||
308 | }; | ||
309 | sink(reference) | ||
310 | } | ||
311 | _ => false, // not a usage | ||
312 | } | ||
313 | } | ||
314 | |||
315 | fn found_name(&self, name: &ast::Name, sink: &mut dyn FnMut(Reference) -> bool) -> bool { | ||
316 | match classify_name(self.sema, name) { | ||
317 | Some(NameClass::FieldShorthand { local: _, field }) => { | ||
318 | let reference = match self.def { | ||
319 | Definition::Field(_) if &field == self.def => Reference { | ||
320 | file_range: self.sema.original_range(name.syntax()), | ||
321 | kind: ReferenceKind::FieldShorthandForField, | ||
322 | // FIXME: mutable patterns should have `Write` access | ||
323 | access: Some(ReferenceAccess::Read), | ||
324 | }, | ||
325 | _ => return false, // not a usage | ||
326 | }; | ||
327 | sink(reference) | ||
328 | } | ||
329 | _ => false, // not a usage | ||
330 | } | ||
331 | } | ||
302 | } | 332 | } |
303 | 333 | ||
304 | fn reference_access(def: &Definition, name_ref: &ast::NameRef) -> Option<ReferenceAccess> { | 334 | fn reference_access(def: &Definition, name_ref: &ast::NameRef) -> Option<ReferenceAccess> { |