aboutsummaryrefslogtreecommitdiff
path: root/crates/ide_db/src/search.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide_db/src/search.rs')
-rw-r--r--crates/ide_db/src/search.rs152
1 files changed, 54 insertions, 98 deletions
diff --git a/crates/ide_db/src/search.rs b/crates/ide_db/src/search.rs
index 0ecb13a64..ddcfbd3f3 100644
--- a/crates/ide_db/src/search.rs
+++ b/crates/ide_db/src/search.rs
@@ -55,22 +55,10 @@ impl IntoIterator for UsageSearchResult {
55#[derive(Debug, Clone)] 55#[derive(Debug, Clone)]
56pub struct FileReference { 56pub struct FileReference {
57 pub range: TextRange, 57 pub range: TextRange,
58 pub kind: ReferenceKind, 58 pub name: ast::NameLike,
59 pub access: Option<ReferenceAccess>, 59 pub access: Option<ReferenceAccess>,
60} 60}
61 61
62#[derive(Debug, Clone, PartialEq)]
63pub 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)] 62#[derive(Debug, Copy, Clone, PartialEq)]
75pub enum ReferenceAccess { 63pub enum ReferenceAccess {
76 Read, 64 Read,
@@ -228,6 +216,15 @@ impl Definition {
228 // so do nothing. 216 // so do nothing.
229 } 217 }
230 } 218 }
219 ModuleSource::BlockExpr(b) => {
220 if is_first {
221 let range = Some(b.syntax().text_range());
222 res.insert(file_id, range);
223 } else {
224 // We have already added the enclosing file to the search scope,
225 // so do nothing.
226 }
227 }
231 ModuleSource::SourceFile(_) => { 228 ModuleSource::SourceFile(_) => {
232 res.insert(file_id, None); 229 res.insert(file_id, None);
233 } 230 }
@@ -257,6 +254,7 @@ impl Definition {
257 let mut res = FxHashMap::default(); 254 let mut res = FxHashMap::default();
258 let range = match module_src.value { 255 let range = match module_src.value {
259 ModuleSource::Module(m) => Some(m.syntax().text_range()), 256 ModuleSource::Module(m) => Some(m.syntax().text_range()),
257 ModuleSource::BlockExpr(b) => Some(b.syntax().text_range()),
260 ModuleSource::SourceFile(_) => None, 258 ModuleSource::SourceFile(_) => None,
261 }; 259 };
262 res.insert(file_id, range); 260 res.insert(file_id, range);
@@ -278,6 +276,7 @@ impl<'a> FindUsages<'a> {
278 pub fn in_scope(self, scope: SearchScope) -> FindUsages<'a> { 276 pub fn in_scope(self, scope: SearchScope) -> FindUsages<'a> {
279 self.set_scope(Some(scope)) 277 self.set_scope(Some(scope))
280 } 278 }
279
281 pub fn set_scope(mut self, scope: Option<SearchScope>) -> FindUsages<'a> { 280 pub fn set_scope(mut self, scope: Option<SearchScope>) -> FindUsages<'a> {
282 assert!(self.scope.is_none()); 281 assert!(self.scope.is_none());
283 self.scope = scope; 282 self.scope = scope;
@@ -323,7 +322,7 @@ impl<'a> FindUsages<'a> {
323 for (file_id, search_range) in search_scope { 322 for (file_id, search_range) in search_scope {
324 let text = sema.db.file_text(file_id); 323 let text = sema.db.file_text(file_id);
325 let search_range = 324 let search_range =
326 search_range.unwrap_or(TextRange::up_to(TextSize::of(text.as_str()))); 325 search_range.unwrap_or_else(|| TextRange::up_to(TextSize::of(text.as_str())));
327 326
328 let tree = Lazy::new(|| sema.parse(file_id).syntax().clone()); 327 let tree = Lazy::new(|| sema.parse(file_id).syntax().clone());
329 328
@@ -333,18 +332,23 @@ impl<'a> FindUsages<'a> {
333 continue; 332 continue;
334 } 333 }
335 334
336 if let Some(name_ref) = sema.find_node_at_offset_with_descend(&tree, offset) { 335 if let Some(name) = sema.find_node_at_offset_with_descend(&tree, offset) {
337 if self.found_name_ref(&name_ref, sink) { 336 match name {
338 return; 337 ast::NameLike::NameRef(name_ref) => {
339 } 338 if self.found_name_ref(&name_ref, sink) {
340 } else if let Some(name) = sema.find_node_at_offset_with_descend(&tree, offset) { 339 return;
341 if self.found_name(&name, sink) { 340 }
342 return; 341 }
343 } 342 ast::NameLike::Name(name) => {
344 } else if let Some(lifetime) = sema.find_node_at_offset_with_descend(&tree, offset) 343 if self.found_name(&name, sink) {
345 { 344 return;
346 if self.found_lifetime(&lifetime, sink) { 345 }
347 return; 346 }
347 ast::NameLike::Lifetime(lifetime) => {
348 if self.found_lifetime(&lifetime, sink) {
349 return;
350 }
351 }
348 } 352 }
349 } 353 }
350 } 354 }
@@ -359,8 +363,11 @@ impl<'a> FindUsages<'a> {
359 match NameRefClass::classify_lifetime(self.sema, lifetime) { 363 match NameRefClass::classify_lifetime(self.sema, lifetime) {
360 Some(NameRefClass::Definition(def)) if &def == self.def => { 364 Some(NameRefClass::Definition(def)) if &def == self.def => {
361 let FileRange { file_id, range } = self.sema.original_range(lifetime.syntax()); 365 let FileRange { file_id, range } = self.sema.original_range(lifetime.syntax());
362 let reference = 366 let reference = FileReference {
363 FileReference { range, kind: ReferenceKind::Lifetime, access: None }; 367 range,
368 name: ast::NameLike::Lifetime(lifetime.clone()),
369 access: None,
370 };
364 sink(file_id, reference) 371 sink(file_id, reference)
365 } 372 }
366 _ => false, // not a usage 373 _ => false, // not a usage
@@ -374,19 +381,12 @@ impl<'a> FindUsages<'a> {
374 ) -> bool { 381 ) -> bool {
375 match NameRefClass::classify(self.sema, &name_ref) { 382 match NameRefClass::classify(self.sema, &name_ref) {
376 Some(NameRefClass::Definition(def)) if &def == self.def => { 383 Some(NameRefClass::Definition(def)) if &def == self.def => {
377 let kind = if is_record_field_expr_or_pat(&name_ref) {
378 ReferenceKind::RecordFieldExprOrPat
379 } else if is_record_lit_name_ref(&name_ref) || is_call_expr_name_ref(&name_ref) {
380 ReferenceKind::StructLiteral
381 } else if is_enum_lit_name_ref(&name_ref) {
382 ReferenceKind::EnumLiteral
383 } else {
384 ReferenceKind::Other
385 };
386
387 let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax()); 384 let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax());
388 let reference = 385 let reference = FileReference {
389 FileReference { range, kind, access: reference_access(&def, &name_ref) }; 386 range,
387 name: ast::NameLike::NameRef(name_ref.clone()),
388 access: reference_access(&def, &name_ref),
389 };
390 sink(file_id, reference) 390 sink(file_id, reference)
391 } 391 }
392 Some(NameRefClass::FieldShorthand { local_ref: local, field_ref: field }) => { 392 Some(NameRefClass::FieldShorthand { local_ref: local, field_ref: field }) => {
@@ -394,12 +394,12 @@ impl<'a> FindUsages<'a> {
394 let reference = match self.def { 394 let reference = match self.def {
395 Definition::Field(_) if &field == self.def => FileReference { 395 Definition::Field(_) if &field == self.def => FileReference {
396 range, 396 range,
397 kind: ReferenceKind::FieldShorthandForField, 397 name: ast::NameLike::NameRef(name_ref.clone()),
398 access: reference_access(&field, &name_ref), 398 access: reference_access(&field, &name_ref),
399 }, 399 },
400 Definition::Local(l) if &local == l => FileReference { 400 Definition::Local(l) if &local == l => FileReference {
401 range, 401 range,
402 kind: ReferenceKind::FieldShorthandForLocal, 402 name: ast::NameLike::NameRef(name_ref.clone()),
403 access: reference_access(&Definition::Local(local), &name_ref), 403 access: reference_access(&Definition::Local(local), &name_ref),
404 }, 404 },
405 _ => return false, // not a usage 405 _ => return false, // not a usage
@@ -416,19 +416,26 @@ impl<'a> FindUsages<'a> {
416 sink: &mut dyn FnMut(FileId, FileReference) -> bool, 416 sink: &mut dyn FnMut(FileId, FileReference) -> bool,
417 ) -> bool { 417 ) -> bool {
418 match NameClass::classify(self.sema, name) { 418 match NameClass::classify(self.sema, name) {
419 Some(NameClass::PatFieldShorthand { local_def: _, field_ref }) => { 419 Some(NameClass::PatFieldShorthand { local_def: _, field_ref })
420 if !matches!(self.def, Definition::Field(_) if &field_ref == self.def) { 420 if matches!(
421 return false; 421 self.def, Definition::Field(_) if &field_ref == self.def
422 } 422 ) =>
423 {
423 let FileRange { file_id, range } = self.sema.original_range(name.syntax()); 424 let FileRange { file_id, range } = self.sema.original_range(name.syntax());
424 let reference = FileReference { 425 let reference = FileReference {
425 range, 426 range,
426 kind: ReferenceKind::FieldShorthandForField, 427 name: ast::NameLike::Name(name.clone()),
427 // FIXME: mutable patterns should have `Write` access 428 // FIXME: mutable patterns should have `Write` access
428 access: Some(ReferenceAccess::Read), 429 access: Some(ReferenceAccess::Read),
429 }; 430 };
430 sink(file_id, reference) 431 sink(file_id, reference)
431 } 432 }
433 Some(NameClass::ConstReference(def)) if *self.def == def => {
434 let FileRange { file_id, range } = self.sema.original_range(name.syntax());
435 let reference =
436 FileReference { range, name: ast::NameLike::Name(name.clone()), access: None };
437 sink(file_id, reference)
438 }
432 _ => false, // not a usage 439 _ => false, // not a usage
433 } 440 }
434 } 441 }
@@ -463,54 +470,3 @@ fn reference_access(def: &Definition, name_ref: &ast::NameRef) -> Option<Referen
463 // Default Locals and Fields to read 470 // Default Locals and Fields to read
464 mode.or(Some(ReferenceAccess::Read)) 471 mode.or(Some(ReferenceAccess::Read))
465} 472}
466
467fn is_call_expr_name_ref(name_ref: &ast::NameRef) -> bool {
468 name_ref
469 .syntax()
470 .ancestors()
471 .find_map(ast::CallExpr::cast)
472 .and_then(|c| match c.expr()? {
473 ast::Expr::PathExpr(p) => {
474 Some(p.path()?.segment()?.name_ref().as_ref() == Some(name_ref))
475 }
476 _ => None,
477 })
478 .unwrap_or(false)
479}
480
481fn is_record_lit_name_ref(name_ref: &ast::NameRef) -> bool {
482 name_ref
483 .syntax()
484 .ancestors()
485 .find_map(ast::RecordExpr::cast)
486 .and_then(|l| l.path())
487 .and_then(|p| p.segment())
488 .map(|p| p.name_ref().as_ref() == Some(name_ref))
489 .unwrap_or(false)
490}
491
492fn is_record_field_expr_or_pat(name_ref: &ast::NameRef) -> bool {
493 if let Some(parent) = name_ref.syntax().parent() {
494 match_ast! {
495 match parent {
496 ast::RecordExprField(it) => true,
497 ast::RecordPatField(_it) => true,
498 _ => false,
499 }
500 }
501 } else {
502 false
503 }
504}
505
506fn is_enum_lit_name_ref(name_ref: &ast::NameRef) -> bool {
507 name_ref
508 .syntax()
509 .ancestors()
510 .find_map(ast::PathExpr::cast)
511 .and_then(|p| p.path())
512 .and_then(|p| p.qualifier())
513 .and_then(|p| p.segment())
514 .map(|p| p.name_ref().as_ref() == Some(name_ref))
515 .unwrap_or(false)
516}