aboutsummaryrefslogtreecommitdiff
path: root/crates/ide_db
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide_db')
-rw-r--r--crates/ide_db/src/search.rs140
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};
10use hir::{DefWithBody, HasSource, Module, ModuleSource, Semantics, Visibility}; 10use hir::{DefWithBody, HasSource, Module, ModuleSource, Semantics, Visibility};
11use once_cell::unsync::Lazy; 11use once_cell::unsync::Lazy;
12use rustc_hash::FxHashMap; 12use rustc_hash::FxHashMap;
13use syntax::{ast, match_ast, AstNode, TextRange, TextSize}; 13use syntax::{
14 ast, match_ast, AstNode, NodeOrToken, SyntaxElement, SyntaxNode, TextRange, TextSize,
15};
14 16
15use crate::defs::NameClass; 17use crate::defs::NameClass;
16use crate::{ 18use crate::{
@@ -18,6 +20,13 @@ use crate::{
18 RootDatabase, 20 RootDatabase,
19}; 21};
20 22
23#[derive(Debug, Clone)]
24pub enum NameKind {
25 Name,
26 NameRef,
27 Lifetime,
28}
29
21#[derive(Debug, Default, Clone)] 30#[derive(Debug, Default, Clone)]
22pub struct UsageSearchResult { 31pub 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)]
65pub enum NameLike {
66 NameRef(ast::NameRef),
67 Name(ast::Name),
68 Lifetime(ast::Lifetime),
69}
70
71mod __ {
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)]
56pub struct FileReference { 80pub 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)] 86impl FileReference {
63pub 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
106fn 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
477fn 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
491fn 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
502fn 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
516fn 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}