diff options
Diffstat (limited to 'crates/ide_db/src/search.rs')
-rw-r--r-- | crates/ide_db/src/search.rs | 101 |
1 files changed, 64 insertions, 37 deletions
diff --git a/crates/ide_db/src/search.rs b/crates/ide_db/src/search.rs index 773bfbc2c..b8359a9b4 100644 --- a/crates/ide_db/src/search.rs +++ b/crates/ide_db/src/search.rs | |||
@@ -8,6 +8,7 @@ use std::{convert::TryInto, mem}; | |||
8 | 8 | ||
9 | use base_db::{FileId, FileRange, SourceDatabaseExt}; | 9 | use base_db::{FileId, FileRange, SourceDatabaseExt}; |
10 | use hir::{DefWithBody, HasSource, Module, ModuleSource, Semantics, Visibility}; | 10 | use hir::{DefWithBody, HasSource, Module, ModuleSource, Semantics, Visibility}; |
11 | use itertools::Itertools; | ||
11 | use once_cell::unsync::Lazy; | 12 | use once_cell::unsync::Lazy; |
12 | use rustc_hash::FxHashMap; | 13 | use rustc_hash::FxHashMap; |
13 | use syntax::{ast, match_ast, AstNode, TextRange, TextSize}; | 14 | use syntax::{ast, match_ast, AstNode, TextRange, TextSize}; |
@@ -19,8 +20,22 @@ use crate::{ | |||
19 | }; | 20 | }; |
20 | 21 | ||
21 | #[derive(Debug, Clone)] | 22 | #[derive(Debug, Clone)] |
22 | pub struct Reference { | 23 | pub struct FileReferences { |
23 | pub file_range: FileRange, | 24 | pub file_id: FileId, |
25 | pub references: Vec<FileReference>, | ||
26 | } | ||
27 | |||
28 | impl FileReferences { | ||
29 | pub fn file_ranges(&self) -> impl Iterator<Item = FileRange> + '_ { | ||
30 | self.references | ||
31 | .iter() | ||
32 | .map(move |&FileReference { range, .. }| FileRange { file_id: self.file_id, range }) | ||
33 | } | ||
34 | } | ||
35 | |||
36 | #[derive(Debug, Clone)] | ||
37 | pub struct FileReference { | ||
38 | pub range: TextRange, | ||
24 | pub kind: ReferenceKind, | 39 | pub kind: ReferenceKind, |
25 | pub access: Option<ReferenceAccess>, | 40 | pub access: Option<ReferenceAccess>, |
26 | } | 41 | } |
@@ -252,23 +267,33 @@ impl<'a> FindUsages<'a> { | |||
252 | 267 | ||
253 | pub fn at_least_one(self) -> bool { | 268 | pub fn at_least_one(self) -> bool { |
254 | let mut found = false; | 269 | let mut found = false; |
255 | self.search(&mut |_reference| { | 270 | self.search(&mut |_, _| { |
256 | found = true; | 271 | found = true; |
257 | true | 272 | true |
258 | }); | 273 | }); |
259 | found | 274 | found |
260 | } | 275 | } |
261 | 276 | ||
262 | pub fn all(self) -> Vec<Reference> { | 277 | /// The [`FileReferences`] returned always have unique [`FileId`]s. |
263 | let mut res = Vec::new(); | 278 | pub fn all(self) -> Vec<FileReferences> { |
264 | self.search(&mut |reference| { | 279 | let mut res = <Vec<FileReferences>>::new(); |
265 | res.push(reference); | 280 | self.search(&mut |file_id, reference| { |
281 | match res.iter_mut().find(|it| it.file_id == file_id) { | ||
282 | Some(file_refs) => file_refs.references.push(reference), | ||
283 | _ => res.push(FileReferences { file_id, references: vec![reference] }), | ||
284 | } | ||
266 | false | 285 | false |
267 | }); | 286 | }); |
287 | assert!(res | ||
288 | .iter() | ||
289 | .map(|refs| refs.file_id) | ||
290 | .sorted_unstable() | ||
291 | .tuple_windows::<(_, _)>() | ||
292 | .all(|(a, b)| a < b)); | ||
268 | res | 293 | res |
269 | } | 294 | } |
270 | 295 | ||
271 | fn search(self, sink: &mut dyn FnMut(Reference) -> bool) { | 296 | fn search(self, sink: &mut dyn FnMut(FileId, FileReference) -> bool) { |
272 | let _p = profile::span("FindUsages:search"); | 297 | let _p = profile::span("FindUsages:search"); |
273 | let sema = self.sema; | 298 | let sema = self.sema; |
274 | 299 | ||
@@ -320,16 +345,14 @@ impl<'a> FindUsages<'a> { | |||
320 | fn found_lifetime( | 345 | fn found_lifetime( |
321 | &self, | 346 | &self, |
322 | lifetime: &ast::Lifetime, | 347 | lifetime: &ast::Lifetime, |
323 | sink: &mut dyn FnMut(Reference) -> bool, | 348 | sink: &mut dyn FnMut(FileId, FileReference) -> bool, |
324 | ) -> bool { | 349 | ) -> bool { |
325 | match NameRefClass::classify_lifetime(self.sema, lifetime) { | 350 | match NameRefClass::classify_lifetime(self.sema, lifetime) { |
326 | Some(NameRefClass::Definition(def)) if &def == self.def => { | 351 | Some(NameRefClass::Definition(def)) if &def == self.def => { |
327 | let reference = Reference { | 352 | let FileRange { file_id, range } = self.sema.original_range(lifetime.syntax()); |
328 | file_range: self.sema.original_range(lifetime.syntax()), | 353 | let reference = |
329 | kind: ReferenceKind::Lifetime, | 354 | FileReference { range, kind: ReferenceKind::Lifetime, access: None }; |
330 | access: None, | 355 | sink(file_id, reference) |
331 | }; | ||
332 | sink(reference) | ||
333 | } | 356 | } |
334 | _ => false, // not a usage | 357 | _ => false, // not a usage |
335 | } | 358 | } |
@@ -338,7 +361,7 @@ impl<'a> FindUsages<'a> { | |||
338 | fn found_name_ref( | 361 | fn found_name_ref( |
339 | &self, | 362 | &self, |
340 | name_ref: &ast::NameRef, | 363 | name_ref: &ast::NameRef, |
341 | sink: &mut dyn FnMut(Reference) -> bool, | 364 | sink: &mut dyn FnMut(FileId, FileReference) -> bool, |
342 | ) -> bool { | 365 | ) -> bool { |
343 | match NameRefClass::classify(self.sema, &name_ref) { | 366 | match NameRefClass::classify(self.sema, &name_ref) { |
344 | Some(NameRefClass::Definition(def)) if &def == self.def => { | 367 | Some(NameRefClass::Definition(def)) if &def == self.def => { |
@@ -352,46 +375,50 @@ impl<'a> FindUsages<'a> { | |||
352 | ReferenceKind::Other | 375 | ReferenceKind::Other |
353 | }; | 376 | }; |
354 | 377 | ||
355 | let reference = Reference { | 378 | let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax()); |
356 | file_range: self.sema.original_range(name_ref.syntax()), | 379 | let reference = |
357 | kind, | 380 | FileReference { range, kind, access: reference_access(&def, &name_ref) }; |
358 | access: reference_access(&def, &name_ref), | 381 | sink(file_id, reference) |
359 | }; | ||
360 | sink(reference) | ||
361 | } | 382 | } |
362 | Some(NameRefClass::FieldShorthand { local_ref: local, field_ref: field }) => { | 383 | Some(NameRefClass::FieldShorthand { local_ref: local, field_ref: field }) => { |
384 | let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax()); | ||
363 | let reference = match self.def { | 385 | let reference = match self.def { |
364 | Definition::Field(_) if &field == self.def => Reference { | 386 | Definition::Field(_) if &field == self.def => FileReference { |
365 | file_range: self.sema.original_range(name_ref.syntax()), | 387 | range, |
366 | kind: ReferenceKind::FieldShorthandForField, | 388 | kind: ReferenceKind::FieldShorthandForField, |
367 | access: reference_access(&field, &name_ref), | 389 | access: reference_access(&field, &name_ref), |
368 | }, | 390 | }, |
369 | Definition::Local(l) if &local == l => Reference { | 391 | Definition::Local(l) if &local == l => FileReference { |
370 | file_range: self.sema.original_range(name_ref.syntax()), | 392 | range, |
371 | kind: ReferenceKind::FieldShorthandForLocal, | 393 | kind: ReferenceKind::FieldShorthandForLocal, |
372 | access: reference_access(&Definition::Local(local), &name_ref), | 394 | access: reference_access(&Definition::Local(local), &name_ref), |
373 | }, | 395 | }, |
374 | _ => return false, // not a usage | 396 | _ => return false, // not a usage |
375 | }; | 397 | }; |
376 | sink(reference) | 398 | sink(file_id, reference) |
377 | } | 399 | } |
378 | _ => false, // not a usage | 400 | _ => false, // not a usage |
379 | } | 401 | } |
380 | } | 402 | } |
381 | 403 | ||
382 | fn found_name(&self, name: &ast::Name, sink: &mut dyn FnMut(Reference) -> bool) -> bool { | 404 | fn found_name( |
405 | &self, | ||
406 | name: &ast::Name, | ||
407 | sink: &mut dyn FnMut(FileId, FileReference) -> bool, | ||
408 | ) -> bool { | ||
383 | match NameClass::classify(self.sema, name) { | 409 | match NameClass::classify(self.sema, name) { |
384 | Some(NameClass::PatFieldShorthand { local_def: _, field_ref }) => { | 410 | Some(NameClass::PatFieldShorthand { local_def: _, field_ref }) => { |
385 | let reference = match self.def { | 411 | if !matches!(self.def, Definition::Field(_) if &field_ref == self.def) { |
386 | Definition::Field(_) if &field_ref == self.def => Reference { | 412 | return false; |
387 | file_range: self.sema.original_range(name.syntax()), | 413 | } |
388 | kind: ReferenceKind::FieldShorthandForField, | 414 | let FileRange { file_id, range } = self.sema.original_range(name.syntax()); |
389 | // FIXME: mutable patterns should have `Write` access | 415 | let reference = FileReference { |
390 | access: Some(ReferenceAccess::Read), | 416 | range, |
391 | }, | 417 | kind: ReferenceKind::FieldShorthandForField, |
392 | _ => return false, // not a usage | 418 | // FIXME: mutable patterns should have `Write` access |
419 | access: Some(ReferenceAccess::Read), | ||
393 | }; | 420 | }; |
394 | sink(reference) | 421 | sink(file_id, reference) |
395 | } | 422 | } |
396 | _ => false, // not a usage | 423 | _ => false, // not a usage |
397 | } | 424 | } |