aboutsummaryrefslogtreecommitdiff
path: root/crates/ide_db
diff options
context:
space:
mode:
authorLukas Wirth <[email protected]>2021-05-08 21:34:55 +0100
committerLukas Wirth <[email protected]>2021-05-08 21:34:55 +0100
commit41f470fea84998af65292f3c297c3e2b1d897848 (patch)
tree2dee702ff58dd614a559e9f4ef3080419cabd2c4 /crates/ide_db
parent96c5df9b171730ad69e130e074584684cee35014 (diff)
Correctly support SelfType when searching for usages
Diffstat (limited to 'crates/ide_db')
-rw-r--r--crates/ide_db/src/search.rs172
1 files changed, 102 insertions, 70 deletions
diff --git a/crates/ide_db/src/search.rs b/crates/ide_db/src/search.rs
index 8f899ea56..891b8cf0a 100644
--- a/crates/ide_db/src/search.rs
+++ b/crates/ide_db/src/search.rs
@@ -233,6 +233,13 @@ impl Definition {
233 }; 233 };
234 } 234 }
235 235
236 if let Definition::SelfType(impl_) = self {
237 return match impl_.source(db).map(|src| src.value.syntax().text_range()) {
238 Some(range) => SearchScope::file_range(FileRange { file_id, range }),
239 None => SearchScope::single_file(file_id),
240 };
241 }
242
236 if let Definition::GenericParam(hir::GenericParam::LifetimeParam(param)) = self { 243 if let Definition::GenericParam(hir::GenericParam::LifetimeParam(param)) = self {
237 let range = match param.parent(db) { 244 let range = match param.parent(db) {
238 hir::GenericDef::Function(it) => { 245 hir::GenericDef::Function(it) => {
@@ -297,7 +304,7 @@ impl Definition {
297 } 304 }
298 305
299 pub fn usages<'a>(&'a self, sema: &'a Semantics<RootDatabase>) -> FindUsages<'a> { 306 pub fn usages<'a>(&'a self, sema: &'a Semantics<RootDatabase>) -> FindUsages<'a> {
300 FindUsages { def: self, sema, scope: None, include_self_kw_refs: false } 307 FindUsages { def: self, sema, scope: None, include_self_kw_refs: None }
301 } 308 }
302} 309}
303 310
@@ -305,12 +312,13 @@ pub struct FindUsages<'a> {
305 def: &'a Definition, 312 def: &'a Definition,
306 sema: &'a Semantics<'a, RootDatabase>, 313 sema: &'a Semantics<'a, RootDatabase>,
307 scope: Option<SearchScope>, 314 scope: Option<SearchScope>,
308 include_self_kw_refs: bool, 315 include_self_kw_refs: Option<hir::Type>,
309} 316}
310 317
311impl<'a> FindUsages<'a> { 318impl<'a> FindUsages<'a> {
312 pub fn include_self_kw_refs(mut self, include: bool) -> FindUsages<'a> { 319 /// Enable searching for `Self` when the definition is a type.
313 self.include_self_kw_refs = include; 320 pub fn include_self_refs(mut self) -> FindUsages<'a> {
321 self.include_self_kw_refs = def_to_ty(self.sema.db, self.def);
314 self 322 self
315 } 323 }
316 324
@@ -354,13 +362,18 @@ impl<'a> FindUsages<'a> {
354 } 362 }
355 }; 363 };
356 364
357 let name = match self.def.name(sema.db) { 365 let name = self.def.name(sema.db).or_else(|| {
358 Some(it) => it.to_string(), 366 self.include_self_kw_refs.as_ref().and_then(|ty| {
367 ty.as_adt()
368 .map(|adt| adt.name(self.sema.db))
369 .or_else(|| ty.as_builtin().map(|builtin| builtin.name()))
370 })
371 });
372 let name = match name {
373 Some(name) => name.to_string(),
359 None => return, 374 None => return,
360 }; 375 };
361 376 let name = name.as_str();
362 let pat = name.as_str();
363 let search_for_self = self.include_self_kw_refs;
364 377
365 for (file_id, search_range) in search_scope { 378 for (file_id, search_range) in search_scope {
366 let text = sema.db.file_text(file_id); 379 let text = sema.db.file_text(file_id);
@@ -369,51 +382,63 @@ impl<'a> FindUsages<'a> {
369 382
370 let tree = Lazy::new(|| sema.parse(file_id).syntax().clone()); 383 let tree = Lazy::new(|| sema.parse(file_id).syntax().clone());
371 384
372 let mut handle_match = |idx: usize| -> bool { 385 for (idx, _) in text.match_indices(name) {
373 let offset: TextSize = idx.try_into().unwrap(); 386 let offset: TextSize = idx.try_into().unwrap();
374 if !search_range.contains_inclusive(offset) { 387 if !search_range.contains_inclusive(offset) {
375 return false; 388 continue;
376 } 389 }
377 390
378 if let Some(name) = sema.find_node_at_offset_with_descend(&tree, offset) { 391 if let Some(name) = sema.find_node_at_offset_with_descend(&tree, offset) {
379 match name { 392 if match name {
380 ast::NameLike::NameRef(name_ref) => { 393 ast::NameLike::NameRef(name_ref) => self.found_name_ref(&name_ref, sink),
381 if self.found_name_ref(&name_ref, sink) { 394 ast::NameLike::Name(name) => self.found_name(&name, sink),
382 return true; 395 ast::NameLike::Lifetime(lifetime) => self.found_lifetime(&lifetime, sink),
383 } 396 } {
384 } 397 return;
385 ast::NameLike::Name(name) => {
386 if self.found_name(&name, sink) {
387 return true;
388 }
389 }
390 ast::NameLike::Lifetime(lifetime) => {
391 if self.found_lifetime(&lifetime, sink) {
392 return true;
393 }
394 }
395 } 398 }
396 } 399 }
397
398 return false;
399 };
400
401 for (idx, _) in text.match_indices(pat) {
402 if handle_match(idx) {
403 return;
404 }
405 } 400 }
406 401 if let Some(self_ty) = &self.include_self_kw_refs {
407 if search_for_self {
408 for (idx, _) in text.match_indices("Self") { 402 for (idx, _) in text.match_indices("Self") {
409 if handle_match(idx) { 403 let offset: TextSize = idx.try_into().unwrap();
410 return; 404 if !search_range.contains_inclusive(offset) {
405 continue;
406 }
407
408 if let Some(ast::NameLike::NameRef(name_ref)) =
409 sema.find_node_at_offset_with_descend(&tree, offset)
410 {
411 if self.found_self_ty_name_ref(&self_ty, &name_ref, sink) {
412 return;
413 }
411 } 414 }
412 } 415 }
413 } 416 }
414 } 417 }
415 } 418 }
416 419
420 fn found_self_ty_name_ref(
421 &self,
422 self_ty: &hir::Type,
423 name_ref: &ast::NameRef,
424 sink: &mut dyn FnMut(FileId, FileReference) -> bool,
425 ) -> bool {
426 match NameRefClass::classify(self.sema, &name_ref) {
427 Some(NameRefClass::Definition(Definition::SelfType(impl_)))
428 if impl_.self_ty(self.sema.db) == *self_ty =>
429 {
430 let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax());
431 let reference = FileReference {
432 range,
433 name: ast::NameLike::NameRef(name_ref.clone()),
434 access: None,
435 };
436 sink(file_id, reference)
437 }
438 _ => false,
439 }
440 }
441
417 fn found_lifetime( 442 fn found_lifetime(
418 &self, 443 &self,
419 lifetime: &ast::Lifetime, 444 lifetime: &ast::Lifetime,
@@ -429,7 +454,7 @@ impl<'a> FindUsages<'a> {
429 }; 454 };
430 sink(file_id, reference) 455 sink(file_id, reference)
431 } 456 }
432 _ => false, // not a usage 457 _ => false,
433 } 458 }
434 } 459 }
435 460
@@ -448,42 +473,35 @@ impl<'a> FindUsages<'a> {
448 }; 473 };
449 sink(file_id, reference) 474 sink(file_id, reference)
450 } 475 }
451 Some(NameRefClass::Definition(Definition::SelfType(impl_))) => { 476 Some(NameRefClass::Definition(def)) if self.include_self_kw_refs.is_some() => {
452 let ty = impl_.self_ty(self.sema.db); 477 if self.include_self_kw_refs == def_to_ty(self.sema.db, &def) {
453 478 let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax());
454 if let Some(adt) = ty.as_adt() { 479 let reference = FileReference {
455 if &Definition::ModuleDef(ModuleDef::Adt(adt)) == self.def { 480 range,
456 let FileRange { file_id, range } = 481 name: ast::NameLike::NameRef(name_ref.clone()),
457 self.sema.original_range(name_ref.syntax()); 482 access: reference_access(&def, &name_ref),
458 let reference = FileReference { 483 };
459 range, 484 sink(file_id, reference)
460 name: ast::NameLike::NameRef(name_ref.clone()), 485 } else {
461 access: None, 486 false
462 };
463 return sink(file_id, reference);
464 }
465 } 487 }
466
467 false
468 } 488 }
469 Some(NameRefClass::FieldShorthand { local_ref: local, field_ref: field }) => { 489 Some(NameRefClass::FieldShorthand { local_ref: local, field_ref: field }) => {
470 let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax()); 490 let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax());
471 let reference = match self.def { 491 let access = match self.def {
472 Definition::Field(_) if &field == self.def => FileReference { 492 Definition::Field(_) if &field == self.def => {
473 range, 493 reference_access(&field, &name_ref)
474 name: ast::NameLike::NameRef(name_ref.clone()), 494 }
475 access: reference_access(&field, &name_ref), 495 Definition::Local(l) if &local == l => {
476 }, 496 reference_access(&Definition::Local(local), &name_ref)
477 Definition::Local(l) if &local == l => FileReference { 497 }
478 range, 498 _ => return false,
479 name: ast::NameLike::NameRef(name_ref.clone()),
480 access: reference_access(&Definition::Local(local), &name_ref),
481 },
482 _ => return false, // not a usage
483 }; 499 };
500 let reference =
501 FileReference { range, name: ast::NameLike::NameRef(name_ref.clone()), access };
484 sink(file_id, reference) 502 sink(file_id, reference)
485 } 503 }
486 _ => false, // not a usage 504 _ => false,
487 } 505 }
488 } 506 }
489 507
@@ -513,11 +531,25 @@ impl<'a> FindUsages<'a> {
513 FileReference { range, name: ast::NameLike::Name(name.clone()), access: None }; 531 FileReference { range, name: ast::NameLike::Name(name.clone()), access: None };
514 sink(file_id, reference) 532 sink(file_id, reference)
515 } 533 }
516 _ => false, // not a usage 534 _ => false,
517 } 535 }
518 } 536 }
519} 537}
520 538
539fn def_to_ty(db: &RootDatabase, def: &Definition) -> Option<hir::Type> {
540 match def {
541 Definition::ModuleDef(def) => match def {
542 ModuleDef::Adt(adt) => Some(adt.ty(db)),
543 ModuleDef::TypeAlias(it) => Some(it.ty(db)),
544 ModuleDef::BuiltinType(_it) => None, // FIXME somehow acquire some module to construct the builtin type
545 ModuleDef::Trait(_it) => None, // FIXME turn trait into its self-type
546 _ => None,
547 },
548 Definition::SelfType(it) => Some(it.self_ty(db)),
549 _ => None,
550 }
551}
552
521fn reference_access(def: &Definition, name_ref: &ast::NameRef) -> Option<ReferenceAccess> { 553fn reference_access(def: &Definition, name_ref: &ast::NameRef) -> Option<ReferenceAccess> {
522 // Only Locals and Fields have accesses for now. 554 // Only Locals and Fields have accesses for now.
523 if !matches!(def, Definition::Local(_) | Definition::Field(_)) { 555 if !matches!(def, Definition::Local(_) | Definition::Field(_)) {