diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2021-02-12 18:01:36 +0000 |
---|---|---|
committer | GitHub <[email protected]> | 2021-02-12 18:01:36 +0000 |
commit | 88253907f4bc3beaa7b8f2e58cb652f653f92d56 (patch) | |
tree | 1669ff0e6ab614c679f245f84d0f9a8202366e0e /crates/ide_db/src | |
parent | 4d51b5644458c7dcb97a4d445f1b379cd2548a78 (diff) | |
parent | fd6cf4d566174dbdb50259bbbfdaf5a12f81544d (diff) |
Merge #7358
7358: Refactor reference searching to work with the ast r=matklad a=Veykril
Addresses #4290
This PR is still a bit unpolished. Its main purpose for now is to discuss the direction of the changes as to whether this seems to be the right approach or not. I annotated a few parts with reviews to give a better overwiew without having to read into it too much.
Big part of the diff are test output changes in the `references` module.
Co-authored-by: Lukas Wirth <[email protected]>
Diffstat (limited to 'crates/ide_db/src')
-rw-r--r-- | crates/ide_db/src/search.rs | 119 |
1 files changed, 38 insertions, 81 deletions
diff --git a/crates/ide_db/src/search.rs b/crates/ide_db/src/search.rs index b9ba0aed5..38b20f2dc 100644 --- a/crates/ide_db/src/search.rs +++ b/crates/ide_db/src/search.rs | |||
@@ -53,24 +53,36 @@ impl IntoIterator for UsageSearchResult { | |||
53 | } | 53 | } |
54 | 54 | ||
55 | #[derive(Debug, Clone)] | 55 | #[derive(Debug, Clone)] |
56 | pub enum NameLike { | ||
57 | NameRef(ast::NameRef), | ||
58 | Name(ast::Name), | ||
59 | Lifetime(ast::Lifetime), | ||
60 | } | ||
61 | |||
62 | impl NameLike { | ||
63 | pub fn as_name_ref(&self) -> Option<&ast::NameRef> { | ||
64 | match self { | ||
65 | NameLike::NameRef(name_ref) => Some(name_ref), | ||
66 | _ => None, | ||
67 | } | ||
68 | } | ||
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: NameLike, |
59 | pub access: Option<ReferenceAccess>, | 83 | pub access: Option<ReferenceAccess>, |
60 | } | 84 | } |
61 | 85 | ||
62 | #[derive(Debug, Clone, PartialEq)] | ||
63 | pub enum ReferenceKind { | ||
64 | FieldShorthandForField, | ||
65 | FieldShorthandForLocal, | ||
66 | StructLiteral, | ||
67 | RecordFieldExprOrPat, | ||
68 | SelfParam, | ||
69 | EnumLiteral, | ||
70 | Lifetime, | ||
71 | Other, | ||
72 | } | ||
73 | |||
74 | #[derive(Debug, Copy, Clone, PartialEq)] | 86 | #[derive(Debug, Copy, Clone, PartialEq)] |
75 | pub enum ReferenceAccess { | 87 | pub enum ReferenceAccess { |
76 | Read, | 88 | Read, |
@@ -369,8 +381,11 @@ impl<'a> FindUsages<'a> { | |||
369 | match NameRefClass::classify_lifetime(self.sema, lifetime) { | 381 | match NameRefClass::classify_lifetime(self.sema, lifetime) { |
370 | Some(NameRefClass::Definition(def)) if &def == self.def => { | 382 | Some(NameRefClass::Definition(def)) if &def == self.def => { |
371 | let FileRange { file_id, range } = self.sema.original_range(lifetime.syntax()); | 383 | let FileRange { file_id, range } = self.sema.original_range(lifetime.syntax()); |
372 | let reference = | 384 | let reference = FileReference { |
373 | FileReference { range, kind: ReferenceKind::Lifetime, access: None }; | 385 | range, |
386 | name: NameLike::Lifetime(lifetime.clone()), | ||
387 | access: None, | ||
388 | }; | ||
374 | sink(file_id, reference) | 389 | sink(file_id, reference) |
375 | } | 390 | } |
376 | _ => false, // not a usage | 391 | _ => false, // not a usage |
@@ -384,19 +399,12 @@ impl<'a> FindUsages<'a> { | |||
384 | ) -> bool { | 399 | ) -> bool { |
385 | match NameRefClass::classify(self.sema, &name_ref) { | 400 | match NameRefClass::classify(self.sema, &name_ref) { |
386 | Some(NameRefClass::Definition(def)) if &def == self.def => { | 401 | 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()); | 402 | let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax()); |
398 | let reference = | 403 | let reference = FileReference { |
399 | FileReference { range, kind, access: reference_access(&def, &name_ref) }; | 404 | range, |
405 | name: NameLike::NameRef(name_ref.clone()), | ||
406 | access: reference_access(&def, &name_ref), | ||
407 | }; | ||
400 | sink(file_id, reference) | 408 | sink(file_id, reference) |
401 | } | 409 | } |
402 | Some(NameRefClass::FieldShorthand { local_ref: local, field_ref: field }) => { | 410 | Some(NameRefClass::FieldShorthand { local_ref: local, field_ref: field }) => { |
@@ -404,12 +412,12 @@ impl<'a> FindUsages<'a> { | |||
404 | let reference = match self.def { | 412 | let reference = match self.def { |
405 | Definition::Field(_) if &field == self.def => FileReference { | 413 | Definition::Field(_) if &field == self.def => FileReference { |
406 | range, | 414 | range, |
407 | kind: ReferenceKind::FieldShorthandForField, | 415 | name: NameLike::NameRef(name_ref.clone()), |
408 | access: reference_access(&field, &name_ref), | 416 | access: reference_access(&field, &name_ref), |
409 | }, | 417 | }, |
410 | Definition::Local(l) if &local == l => FileReference { | 418 | Definition::Local(l) if &local == l => FileReference { |
411 | range, | 419 | range, |
412 | kind: ReferenceKind::FieldShorthandForLocal, | 420 | name: NameLike::NameRef(name_ref.clone()), |
413 | access: reference_access(&Definition::Local(local), &name_ref), | 421 | access: reference_access(&Definition::Local(local), &name_ref), |
414 | }, | 422 | }, |
415 | _ => return false, // not a usage | 423 | _ => return false, // not a usage |
@@ -433,7 +441,7 @@ impl<'a> FindUsages<'a> { | |||
433 | let FileRange { file_id, range } = self.sema.original_range(name.syntax()); | 441 | let FileRange { file_id, range } = self.sema.original_range(name.syntax()); |
434 | let reference = FileReference { | 442 | let reference = FileReference { |
435 | range, | 443 | range, |
436 | kind: ReferenceKind::FieldShorthandForField, | 444 | name: NameLike::Name(name.clone()), |
437 | // FIXME: mutable patterns should have `Write` access | 445 | // FIXME: mutable patterns should have `Write` access |
438 | access: Some(ReferenceAccess::Read), | 446 | access: Some(ReferenceAccess::Read), |
439 | }; | 447 | }; |
@@ -473,54 +481,3 @@ fn reference_access(def: &Definition, name_ref: &ast::NameRef) -> Option<Referen | |||
473 | // Default Locals and Fields to read | 481 | // Default Locals and Fields to read |
474 | mode.or(Some(ReferenceAccess::Read)) | 482 | mode.or(Some(ReferenceAccess::Read)) |
475 | } | 483 | } |
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 | } | ||