diff options
Diffstat (limited to 'crates/ide_db/src/search.rs')
-rw-r--r-- | crates/ide_db/src/search.rs | 140 |
1 files changed, 60 insertions, 80 deletions
diff --git a/crates/ide_db/src/search.rs b/crates/ide_db/src/search.rs index b9ba0aed5..d0aed26f7 100644 --- a/crates/ide_db/src/search.rs +++ b/crates/ide_db/src/search.rs | |||
@@ -10,7 +10,9 @@ use base_db::{FileId, FileRange, SourceDatabaseExt}; | |||
10 | use hir::{DefWithBody, HasSource, Module, ModuleSource, Semantics, Visibility}; | 10 | use hir::{DefWithBody, HasSource, Module, ModuleSource, Semantics, Visibility}; |
11 | use once_cell::unsync::Lazy; | 11 | 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::{ |
14 | ast, match_ast, AstNode, NodeOrToken, SyntaxElement, SyntaxNode, TextRange, TextSize, | ||
15 | }; | ||
14 | 16 | ||
15 | use crate::defs::NameClass; | 17 | use crate::defs::NameClass; |
16 | use crate::{ | 18 | use crate::{ |
@@ -18,6 +20,13 @@ use crate::{ | |||
18 | RootDatabase, | 20 | RootDatabase, |
19 | }; | 21 | }; |
20 | 22 | ||
23 | #[derive(Debug, Clone)] | ||
24 | pub enum NameKind { | ||
25 | Name, | ||
26 | NameRef, | ||
27 | Lifetime, | ||
28 | } | ||
29 | |||
21 | #[derive(Debug, Default, Clone)] | 30 | #[derive(Debug, Default, Clone)] |
22 | pub struct UsageSearchResult { | 31 | pub struct UsageSearchResult { |
23 | pub references: FxHashMap<FileId, Vec<FileReference>>, | 32 | pub references: FxHashMap<FileId, Vec<FileReference>>, |
@@ -53,22 +62,52 @@ impl IntoIterator for UsageSearchResult { | |||
53 | } | 62 | } |
54 | 63 | ||
55 | #[derive(Debug, Clone)] | 64 | #[derive(Debug, Clone)] |
65 | pub enum NameLike { | ||
66 | NameRef(ast::NameRef), | ||
67 | Name(ast::Name), | ||
68 | Lifetime(ast::Lifetime), | ||
69 | } | ||
70 | |||
71 | mod __ { | ||
72 | use super::{ | ||
73 | ast::{Lifetime, Name, NameRef}, | ||
74 | NameLike, | ||
75 | }; | ||
76 | stdx::impl_from!(NameRef, Name, Lifetime for NameLike); | ||
77 | } | ||
78 | |||
79 | #[derive(Debug, Clone)] | ||
56 | pub struct FileReference { | 80 | pub struct FileReference { |
57 | pub range: TextRange, | 81 | pub range: TextRange, |
58 | pub kind: ReferenceKind, | 82 | pub name: NameKind, |
59 | pub access: Option<ReferenceAccess>, | 83 | pub access: Option<ReferenceAccess>, |
60 | } | 84 | } |
61 | 85 | ||
62 | #[derive(Debug, Clone, PartialEq)] | 86 | impl FileReference { |
63 | pub enum ReferenceKind { | 87 | pub fn name_from_syntax(&self, root: &SyntaxNode) -> Option<NameLike> { |
64 | FieldShorthandForField, | 88 | let node = node_or_parent(root.covering_element(self.range)); |
65 | FieldShorthandForLocal, | 89 | match self.name { |
66 | StructLiteral, | 90 | NameKind::Name => ast::Name::cast(node).map(Into::into), |
67 | RecordFieldExprOrPat, | 91 | NameKind::NameRef => ast::NameRef::cast(node).map(Into::into), |
68 | SelfParam, | 92 | NameKind::Lifetime => ast::Lifetime::cast(node).map(Into::into), |
69 | EnumLiteral, | 93 | } |
70 | Lifetime, | 94 | } |
71 | Other, | 95 | |
96 | pub fn as_name_ref(&self, root: &SyntaxNode) -> Option<ast::NameRef> { | ||
97 | match self.name { | ||
98 | NameKind::NameRef => { | ||
99 | ast::NameRef::cast(node_or_parent(root.covering_element(self.range))) | ||
100 | } | ||
101 | _ => None, | ||
102 | } | ||
103 | } | ||
104 | } | ||
105 | |||
106 | fn node_or_parent(ele: SyntaxElement) -> SyntaxNode { | ||
107 | match ele { | ||
108 | NodeOrToken::Node(node) => node, | ||
109 | NodeOrToken::Token(token) => token.parent(), | ||
110 | } | ||
72 | } | 111 | } |
73 | 112 | ||
74 | #[derive(Debug, Copy, Clone, PartialEq)] | 113 | #[derive(Debug, Copy, Clone, PartialEq)] |
@@ -369,8 +408,7 @@ impl<'a> FindUsages<'a> { | |||
369 | match NameRefClass::classify_lifetime(self.sema, lifetime) { | 408 | match NameRefClass::classify_lifetime(self.sema, lifetime) { |
370 | Some(NameRefClass::Definition(def)) if &def == self.def => { | 409 | Some(NameRefClass::Definition(def)) if &def == self.def => { |
371 | let FileRange { file_id, range } = self.sema.original_range(lifetime.syntax()); | 410 | let FileRange { file_id, range } = self.sema.original_range(lifetime.syntax()); |
372 | let reference = | 411 | let reference = FileReference { range, name: NameKind::Lifetime, access: None }; |
373 | FileReference { range, kind: ReferenceKind::Lifetime, access: None }; | ||
374 | sink(file_id, reference) | 412 | sink(file_id, reference) |
375 | } | 413 | } |
376 | _ => false, // not a usage | 414 | _ => false, // not a usage |
@@ -384,19 +422,12 @@ impl<'a> FindUsages<'a> { | |||
384 | ) -> bool { | 422 | ) -> bool { |
385 | match NameRefClass::classify(self.sema, &name_ref) { | 423 | match NameRefClass::classify(self.sema, &name_ref) { |
386 | Some(NameRefClass::Definition(def)) if &def == self.def => { | 424 | Some(NameRefClass::Definition(def)) if &def == self.def => { |
387 | let kind = if is_record_field_expr_or_pat(&name_ref) { | ||
388 | ReferenceKind::RecordFieldExprOrPat | ||
389 | } else if is_record_lit_name_ref(&name_ref) || is_call_expr_name_ref(&name_ref) { | ||
390 | ReferenceKind::StructLiteral | ||
391 | } else if is_enum_lit_name_ref(&name_ref) { | ||
392 | ReferenceKind::EnumLiteral | ||
393 | } else { | ||
394 | ReferenceKind::Other | ||
395 | }; | ||
396 | |||
397 | let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax()); | 425 | let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax()); |
398 | let reference = | 426 | let reference = FileReference { |
399 | FileReference { range, kind, access: reference_access(&def, &name_ref) }; | 427 | range, |
428 | name: NameKind::NameRef, | ||
429 | access: reference_access(&def, &name_ref), | ||
430 | }; | ||
400 | sink(file_id, reference) | 431 | sink(file_id, reference) |
401 | } | 432 | } |
402 | Some(NameRefClass::FieldShorthand { local_ref: local, field_ref: field }) => { | 433 | Some(NameRefClass::FieldShorthand { local_ref: local, field_ref: field }) => { |
@@ -404,12 +435,12 @@ impl<'a> FindUsages<'a> { | |||
404 | let reference = match self.def { | 435 | let reference = match self.def { |
405 | Definition::Field(_) if &field == self.def => FileReference { | 436 | Definition::Field(_) if &field == self.def => FileReference { |
406 | range, | 437 | range, |
407 | kind: ReferenceKind::FieldShorthandForField, | 438 | name: NameKind::NameRef, |
408 | access: reference_access(&field, &name_ref), | 439 | access: reference_access(&field, &name_ref), |
409 | }, | 440 | }, |
410 | Definition::Local(l) if &local == l => FileReference { | 441 | Definition::Local(l) if &local == l => FileReference { |
411 | range, | 442 | range, |
412 | kind: ReferenceKind::FieldShorthandForLocal, | 443 | name: NameKind::NameRef, |
413 | access: reference_access(&Definition::Local(local), &name_ref), | 444 | access: reference_access(&Definition::Local(local), &name_ref), |
414 | }, | 445 | }, |
415 | _ => return false, // not a usage | 446 | _ => return false, // not a usage |
@@ -433,7 +464,7 @@ impl<'a> FindUsages<'a> { | |||
433 | let FileRange { file_id, range } = self.sema.original_range(name.syntax()); | 464 | let FileRange { file_id, range } = self.sema.original_range(name.syntax()); |
434 | let reference = FileReference { | 465 | let reference = FileReference { |
435 | range, | 466 | range, |
436 | kind: ReferenceKind::FieldShorthandForField, | 467 | name: NameKind::Name, |
437 | // FIXME: mutable patterns should have `Write` access | 468 | // FIXME: mutable patterns should have `Write` access |
438 | access: Some(ReferenceAccess::Read), | 469 | access: Some(ReferenceAccess::Read), |
439 | }; | 470 | }; |
@@ -473,54 +504,3 @@ fn reference_access(def: &Definition, name_ref: &ast::NameRef) -> Option<Referen | |||
473 | // Default Locals and Fields to read | 504 | // Default Locals and Fields to read |
474 | mode.or(Some(ReferenceAccess::Read)) | 505 | mode.or(Some(ReferenceAccess::Read)) |
475 | } | 506 | } |
476 | |||
477 | fn is_call_expr_name_ref(name_ref: &ast::NameRef) -> bool { | ||
478 | name_ref | ||
479 | .syntax() | ||
480 | .ancestors() | ||
481 | .find_map(ast::CallExpr::cast) | ||
482 | .and_then(|c| match c.expr()? { | ||
483 | ast::Expr::PathExpr(p) => { | ||
484 | Some(p.path()?.segment()?.name_ref().as_ref() == Some(name_ref)) | ||
485 | } | ||
486 | _ => None, | ||
487 | }) | ||
488 | .unwrap_or(false) | ||
489 | } | ||
490 | |||
491 | fn is_record_lit_name_ref(name_ref: &ast::NameRef) -> bool { | ||
492 | name_ref | ||
493 | .syntax() | ||
494 | .ancestors() | ||
495 | .find_map(ast::RecordExpr::cast) | ||
496 | .and_then(|l| l.path()) | ||
497 | .and_then(|p| p.segment()) | ||
498 | .map(|p| p.name_ref().as_ref() == Some(name_ref)) | ||
499 | .unwrap_or(false) | ||
500 | } | ||
501 | |||
502 | fn is_record_field_expr_or_pat(name_ref: &ast::NameRef) -> bool { | ||
503 | if let Some(parent) = name_ref.syntax().parent() { | ||
504 | match_ast! { | ||
505 | match parent { | ||
506 | ast::RecordExprField(it) => true, | ||
507 | ast::RecordPatField(_it) => true, | ||
508 | _ => false, | ||
509 | } | ||
510 | } | ||
511 | } else { | ||
512 | false | ||
513 | } | ||
514 | } | ||
515 | |||
516 | fn is_enum_lit_name_ref(name_ref: &ast::NameRef) -> bool { | ||
517 | name_ref | ||
518 | .syntax() | ||
519 | .ancestors() | ||
520 | .find_map(ast::PathExpr::cast) | ||
521 | .and_then(|p| p.path()) | ||
522 | .and_then(|p| p.qualifier()) | ||
523 | .and_then(|p| p.segment()) | ||
524 | .map(|p| p.name_ref().as_ref() == Some(name_ref)) | ||
525 | .unwrap_or(false) | ||
526 | } | ||