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.rs101
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
9use base_db::{FileId, FileRange, SourceDatabaseExt}; 9use base_db::{FileId, FileRange, SourceDatabaseExt};
10use hir::{DefWithBody, HasSource, Module, ModuleSource, Semantics, Visibility}; 10use hir::{DefWithBody, HasSource, Module, ModuleSource, Semantics, Visibility};
11use itertools::Itertools;
11use once_cell::unsync::Lazy; 12use once_cell::unsync::Lazy;
12use rustc_hash::FxHashMap; 13use rustc_hash::FxHashMap;
13use syntax::{ast, match_ast, AstNode, TextRange, TextSize}; 14use 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)]
22pub struct Reference { 23pub struct FileReferences {
23 pub file_range: FileRange, 24 pub file_id: FileId,
25 pub references: Vec<FileReference>,
26}
27
28impl 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)]
37pub 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 }