diff options
Diffstat (limited to 'crates/ide_db/src/search.rs')
-rw-r--r-- | crates/ide_db/src/search.rs | 152 |
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)] |
56 | pub struct FileReference { | 56 | pub 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)] | ||
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)] | 62 | #[derive(Debug, Copy, Clone, PartialEq)] |
75 | pub enum ReferenceAccess { | 63 | pub 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 | |||
467 | fn 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 | |||
481 | fn 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 | |||
492 | fn 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 | |||
506 | fn 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 | } | ||