aboutsummaryrefslogtreecommitdiff
path: root/crates/ide
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide')
-rw-r--r--crates/ide/src/references.rs357
1 files changed, 181 insertions, 176 deletions
diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs
index 722c8f406..1e3e944e9 100644
--- a/crates/ide/src/references.rs
+++ b/crates/ide/src/references.rs
@@ -190,14 +190,14 @@ fn get_struct_def_name_for_struct_literal_search(
190 190
191#[cfg(test)] 191#[cfg(test)]
192mod tests { 192mod tests {
193 use crate::{ 193 use expect_test::{expect, Expect};
194 mock_analysis::{analysis_and_position, MockAnalysis}, 194 use stdx::format_to;
195 Declaration, Reference, ReferenceSearchResult, SearchScope, 195
196 }; 196 use crate::{mock_analysis::MockAnalysis, SearchScope};
197 197
198 #[test] 198 #[test]
199 fn test_struct_literal_after_space() { 199 fn test_struct_literal_after_space() {
200 let refs = get_all_refs( 200 check(
201 r#" 201 r#"
202struct Foo <|>{ 202struct Foo <|>{
203 a: i32, 203 a: i32,
@@ -210,17 +210,17 @@ fn main() {
210 f = Foo {a: Foo::f()}; 210 f = Foo {a: Foo::f()};
211} 211}
212"#, 212"#,
213 ); 213 expect![[r#"
214 check_result( 214 Foo STRUCT FileId(1) 0..26 7..10 Other
215 refs, 215
216 "Foo STRUCT FileId(1) 0..26 7..10 Other", 216 FileId(1) 101..104 StructLiteral
217 &["FileId(1) 101..104 StructLiteral"], 217 "#]],
218 ); 218 );
219 } 219 }
220 220
221 #[test] 221 #[test]
222 fn test_struct_literal_before_space() { 222 fn test_struct_literal_before_space() {
223 let refs = get_all_refs( 223 check(
224 r#" 224 r#"
225struct Foo<|> {} 225struct Foo<|> {}
226 fn main() { 226 fn main() {
@@ -228,17 +228,18 @@ struct Foo<|> {}
228 f = Foo {}; 228 f = Foo {};
229} 229}
230"#, 230"#,
231 ); 231 expect![[r#"
232 check_result( 232 Foo STRUCT FileId(1) 0..13 7..10 Other
233 refs, 233
234 "Foo STRUCT FileId(1) 0..13 7..10 Other", 234 FileId(1) 41..44 Other
235 &["FileId(1) 41..44 Other", "FileId(1) 54..57 StructLiteral"], 235 FileId(1) 54..57 StructLiteral
236 "#]],
236 ); 237 );
237 } 238 }
238 239
239 #[test] 240 #[test]
240 fn test_struct_literal_with_generic_type() { 241 fn test_struct_literal_with_generic_type() {
241 let refs = get_all_refs( 242 check(
242 r#" 243 r#"
243struct Foo<T> <|>{} 244struct Foo<T> <|>{}
244 fn main() { 245 fn main() {
@@ -246,17 +247,17 @@ struct Foo<T> <|>{}
246 f = Foo {}; 247 f = Foo {};
247} 248}
248"#, 249"#,
249 ); 250 expect![[r#"
250 check_result( 251 Foo STRUCT FileId(1) 0..16 7..10 Other
251 refs, 252
252 "Foo STRUCT FileId(1) 0..16 7..10 Other", 253 FileId(1) 64..67 StructLiteral
253 &["FileId(1) 64..67 StructLiteral"], 254 "#]],
254 ); 255 );
255 } 256 }
256 257
257 #[test] 258 #[test]
258 fn test_struct_literal_for_tuple() { 259 fn test_struct_literal_for_tuple() {
259 let refs = get_all_refs( 260 check(
260 r#" 261 r#"
261struct Foo<|>(i32); 262struct Foo<|>(i32);
262 263
@@ -265,17 +266,17 @@ fn main() {
265 f = Foo(1); 266 f = Foo(1);
266} 267}
267"#, 268"#,
268 ); 269 expect![[r#"
269 check_result( 270 Foo STRUCT FileId(1) 0..16 7..10 Other
270 refs, 271
271 "Foo STRUCT FileId(1) 0..16 7..10 Other", 272 FileId(1) 54..57 StructLiteral
272 &["FileId(1) 54..57 StructLiteral"], 273 "#]],
273 ); 274 );
274 } 275 }
275 276
276 #[test] 277 #[test]
277 fn test_find_all_refs_for_local() { 278 fn test_find_all_refs_for_local() {
278 let refs = get_all_refs( 279 check(
279 r#" 280 r#"
280fn main() { 281fn main() {
281 let mut i = 1; 282 let mut i = 1;
@@ -288,22 +289,20 @@ fn main() {
288 289
289 i = 5; 290 i = 5;
290}"#, 291}"#,
291 ); 292 expect![[r#"
292 check_result( 293 i IDENT_PAT FileId(1) 24..25 Other Write
293 refs, 294
294 "i IDENT_PAT FileId(1) 24..25 Other Write", 295 FileId(1) 50..51 Other Write
295 &[ 296 FileId(1) 54..55 Other Read
296 "FileId(1) 50..51 Other Write", 297 FileId(1) 76..77 Other Write
297 "FileId(1) 54..55 Other Read", 298 FileId(1) 94..95 Other Write
298 "FileId(1) 76..77 Other Write", 299 "#]],
299 "FileId(1) 94..95 Other Write",
300 ],
301 ); 300 );
302 } 301 }
303 302
304 #[test] 303 #[test]
305 fn search_filters_by_range() { 304 fn search_filters_by_range() {
306 let refs = get_all_refs( 305 check(
307 r#" 306 r#"
308fn foo() { 307fn foo() {
309 let spam<|> = 92; 308 let spam<|> = 92;
@@ -314,41 +313,46 @@ fn bar() {
314 spam + spam 313 spam + spam
315} 314}
316"#, 315"#,
317 ); 316 expect![[r#"
318 check_result( 317 spam IDENT_PAT FileId(1) 19..23 Other
319 refs, 318
320 "spam IDENT_PAT FileId(1) 19..23 Other", 319 FileId(1) 34..38 Other Read
321 &["FileId(1) 34..38 Other Read", "FileId(1) 41..45 Other Read"], 320 FileId(1) 41..45 Other Read
321 "#]],
322 ); 322 );
323 } 323 }
324 324
325 #[test] 325 #[test]
326 fn test_find_all_refs_for_param_inside() { 326 fn test_find_all_refs_for_param_inside() {
327 let refs = get_all_refs( 327 check(
328 r#" 328 r#"
329fn foo(i : u32) -> u32 { 329fn foo(i : u32) -> u32 { i<|> }
330 i<|>
331}
332"#, 330"#,
331 expect![[r#"
332 i IDENT_PAT FileId(1) 7..8 Other
333
334 FileId(1) 25..26 Other Read
335 "#]],
333 ); 336 );
334 check_result(refs, "i IDENT_PAT FileId(1) 7..8 Other", &["FileId(1) 29..30 Other Read"]);
335 } 337 }
336 338
337 #[test] 339 #[test]
338 fn test_find_all_refs_for_fn_param() { 340 fn test_find_all_refs_for_fn_param() {
339 let refs = get_all_refs( 341 check(
340 r#" 342 r#"
341fn foo(i<|> : u32) -> u32 { 343fn foo(i<|> : u32) -> u32 { i }
342 i
343}
344"#, 344"#,
345 expect![[r#"
346 i IDENT_PAT FileId(1) 7..8 Other
347
348 FileId(1) 25..26 Other Read
349 "#]],
345 ); 350 );
346 check_result(refs, "i IDENT_PAT FileId(1) 7..8 Other", &["FileId(1) 29..30 Other Read"]);
347 } 351 }
348 352
349 #[test] 353 #[test]
350 fn test_find_all_refs_field_name() { 354 fn test_find_all_refs_field_name() {
351 let refs = get_all_refs( 355 check(
352 r#" 356 r#"
353//- /lib.rs 357//- /lib.rs
354struct Foo { 358struct Foo {
@@ -359,30 +363,33 @@ fn main(s: Foo) {
359 let f = s.spam; 363 let f = s.spam;
360} 364}
361"#, 365"#,
362 ); 366 expect![[r#"
363 check_result( 367 spam RECORD_FIELD FileId(1) 17..30 21..25 Other
364 refs, 368
365 "spam RECORD_FIELD FileId(1) 17..30 21..25 Other", 369 FileId(1) 67..71 Other Read
366 &["FileId(1) 67..71 Other Read"], 370 "#]],
367 ); 371 );
368 } 372 }
369 373
370 #[test] 374 #[test]
371 fn test_find_all_refs_impl_item_name() { 375 fn test_find_all_refs_impl_item_name() {
372 let refs = get_all_refs( 376 check(
373 r#" 377 r#"
374struct Foo; 378struct Foo;
375impl Foo { 379impl Foo {
376 fn f<|>(&self) { } 380 fn f<|>(&self) { }
377} 381}
378"#, 382"#,
383 expect![[r#"
384 f FN FileId(1) 27..43 30..31 Other
385
386 "#]],
379 ); 387 );
380 check_result(refs, "f FN FileId(1) 27..43 30..31 Other", &[]);
381 } 388 }
382 389
383 #[test] 390 #[test]
384 fn test_find_all_refs_enum_var_name() { 391 fn test_find_all_refs_enum_var_name() {
385 let refs = get_all_refs( 392 check(
386 r#" 393 r#"
387enum Foo { 394enum Foo {
388 A, 395 A,
@@ -390,13 +397,16 @@ enum Foo {
390 C, 397 C,
391} 398}
392"#, 399"#,
400 expect![[r#"
401 B VARIANT FileId(1) 22..23 22..23 Other
402
403 "#]],
393 ); 404 );
394 check_result(refs, "B VARIANT FileId(1) 22..23 22..23 Other", &[]);
395 } 405 }
396 406
397 #[test] 407 #[test]
398 fn test_find_all_refs_two_modules() { 408 fn test_find_all_refs_two_modules() {
399 let (analysis, pos) = analysis_and_position( 409 check(
400 r#" 410 r#"
401//- /lib.rs 411//- /lib.rs
402pub mod foo; 412pub mod foo;
@@ -428,12 +438,12 @@ fn f() {
428 let i = foo::Foo<|> { n: 5 }; 438 let i = foo::Foo<|> { n: 5 };
429} 439}
430"#, 440"#,
431 ); 441 expect![[r#"
432 let refs = analysis.find_all_refs(pos, None).unwrap().unwrap(); 442 Foo STRUCT FileId(2) 17..51 28..31 Other
433 check_result( 443
434 refs, 444 FileId(1) 53..56 StructLiteral
435 "Foo STRUCT FileId(2) 17..51 28..31 Other", 445 FileId(3) 79..82 StructLiteral
436 &["FileId(1) 53..56 StructLiteral", "FileId(3) 79..82 StructLiteral"], 446 "#]],
437 ); 447 );
438 } 448 }
439 449
@@ -442,7 +452,7 @@ fn f() {
442 // which is the whole `foo.rs`, and the second one is in `use foo::Foo`. 452 // which is the whole `foo.rs`, and the second one is in `use foo::Foo`.
443 #[test] 453 #[test]
444 fn test_find_all_refs_decl_module() { 454 fn test_find_all_refs_decl_module() {
445 let (analysis, pos) = analysis_and_position( 455 check(
446 r#" 456 r#"
447//- /lib.rs 457//- /lib.rs
448mod foo<|>; 458mod foo<|>;
@@ -458,14 +468,17 @@ pub struct Foo {
458 pub n: u32, 468 pub n: u32,
459} 469}
460"#, 470"#,
471 expect![[r#"
472 foo SOURCE_FILE FileId(2) 0..35 Other
473
474 FileId(1) 14..17 Other
475 "#]],
461 ); 476 );
462 let refs = analysis.find_all_refs(pos, None).unwrap().unwrap();
463 check_result(refs, "foo SOURCE_FILE FileId(2) 0..35 Other", &["FileId(1) 14..17 Other"]);
464 } 477 }
465 478
466 #[test] 479 #[test]
467 fn test_find_all_refs_super_mod_vis() { 480 fn test_find_all_refs_super_mod_vis() {
468 let (analysis, pos) = analysis_and_position( 481 check(
469 r#" 482 r#"
470//- /lib.rs 483//- /lib.rs
471mod foo; 484mod foo;
@@ -483,12 +496,12 @@ pub(super) struct Foo<|> {
483 pub n: u32, 496 pub n: u32,
484} 497}
485"#, 498"#,
486 ); 499 expect![[r#"
487 let refs = analysis.find_all_refs(pos, None).unwrap().unwrap(); 500 Foo STRUCT FileId(3) 0..41 18..21 Other
488 check_result( 501
489 refs, 502 FileId(2) 20..23 Other
490 "Foo STRUCT FileId(3) 0..41 18..21 Other", 503 FileId(2) 47..50 StructLiteral
491 &["FileId(2) 20..23 Other", "FileId(2) 47..50 StructLiteral"], 504 "#]],
492 ); 505 );
493 } 506 }
494 507
@@ -508,29 +521,31 @@ pub(super) struct Foo<|> {
508 fn f() { super::quux(); } 521 fn f() { super::quux(); }
509 "#; 522 "#;
510 523
511 let (mock, pos) = MockAnalysis::with_files_and_position(code); 524 check_with_scope(
512 let bar = mock.id_of("/bar.rs"); 525 code,
513 let analysis = mock.analysis(); 526 None,
527 expect![[r#"
528 quux FN FileId(1) 19..35 26..30 Other
514 529
515 let refs = analysis.find_all_refs(pos, None).unwrap().unwrap(); 530 FileId(2) 16..20 StructLiteral
516 check_result( 531 FileId(3) 16..20 StructLiteral
517 refs, 532 "#]],
518 "quux FN FileId(1) 19..35 26..30 Other",
519 &["FileId(2) 16..20 StructLiteral", "FileId(3) 16..20 StructLiteral"],
520 ); 533 );
521 534
522 let refs = 535 check_with_scope(
523 analysis.find_all_refs(pos, Some(SearchScope::single_file(bar))).unwrap().unwrap(); 536 code,
524 check_result( 537 Some("/bar.rs"),
525 refs, 538 expect![[r#"
526 "quux FN FileId(1) 19..35 26..30 Other", 539 quux FN FileId(1) 19..35 26..30 Other
527 &["FileId(3) 16..20 StructLiteral"], 540
541 FileId(3) 16..20 StructLiteral
542 "#]],
528 ); 543 );
529 } 544 }
530 545
531 #[test] 546 #[test]
532 fn test_find_all_refs_macro_def() { 547 fn test_find_all_refs_macro_def() {
533 let refs = get_all_refs( 548 check(
534 r#" 549 r#"
535#[macro_export] 550#[macro_export]
536macro_rules! m1<|> { () => (()) } 551macro_rules! m1<|> { () => (()) }
@@ -540,34 +555,36 @@ fn foo() {
540 m1(); 555 m1();
541} 556}
542"#, 557"#,
543 ); 558 expect![[r#"
544 check_result( 559 m1 MACRO_CALL FileId(1) 0..46 29..31 Other
545 refs, 560
546 "m1 MACRO_CALL FileId(1) 0..46 29..31 Other", 561 FileId(1) 63..65 StructLiteral
547 &["FileId(1) 63..65 StructLiteral", "FileId(1) 73..75 StructLiteral"], 562 FileId(1) 73..75 StructLiteral
563 "#]],
548 ); 564 );
549 } 565 }
550 566
551 #[test] 567 #[test]
552 fn test_basic_highlight_read_write() { 568 fn test_basic_highlight_read_write() {
553 let refs = get_all_refs( 569 check(
554 r#" 570 r#"
555fn foo() { 571fn foo() {
556 let mut i<|> = 0; 572 let mut i<|> = 0;
557 i = i + 1; 573 i = i + 1;
558} 574}
559"#, 575"#,
560 ); 576 expect![[r#"
561 check_result( 577 i IDENT_PAT FileId(1) 23..24 Other Write
562 refs, 578
563 "i IDENT_PAT FileId(1) 23..24 Other Write", 579 FileId(1) 34..35 Other Write
564 &["FileId(1) 34..35 Other Write", "FileId(1) 38..39 Other Read"], 580 FileId(1) 38..39 Other Read
581 "#]],
565 ); 582 );
566 } 583 }
567 584
568 #[test] 585 #[test]
569 fn test_basic_highlight_field_read_write() { 586 fn test_basic_highlight_field_read_write() {
570 let refs = get_all_refs( 587 check(
571 r#" 588 r#"
572struct S { 589struct S {
573 f: u32, 590 f: u32,
@@ -578,38 +595,41 @@ fn foo() {
578 s.f<|> = 0; 595 s.f<|> = 0;
579} 596}
580"#, 597"#,
581 ); 598 expect![[r#"
582 check_result( 599 f RECORD_FIELD FileId(1) 15..21 15..16 Other
583 refs, 600
584 "f RECORD_FIELD FileId(1) 15..21 15..16 Other", 601 FileId(1) 55..56 Other Read
585 &["FileId(1) 55..56 Other Read", "FileId(1) 68..69 Other Write"], 602 FileId(1) 68..69 Other Write
603 "#]],
586 ); 604 );
587 } 605 }
588 606
589 #[test] 607 #[test]
590 fn test_basic_highlight_decl_no_write() { 608 fn test_basic_highlight_decl_no_write() {
591 let refs = get_all_refs( 609 check(
592 r#" 610 r#"
593fn foo() { 611fn foo() {
594 let i<|>; 612 let i<|>;
595 i = 1; 613 i = 1;
596} 614}
597"#, 615"#,
616 expect![[r#"
617 i IDENT_PAT FileId(1) 19..20 Other
618
619 FileId(1) 26..27 Other Write
620 "#]],
598 ); 621 );
599 check_result(refs, "i IDENT_PAT FileId(1) 19..20 Other", &["FileId(1) 26..27 Other Write"]);
600 } 622 }
601 623
602 #[test] 624 #[test]
603 fn test_find_struct_function_refs_outside_module() { 625 fn test_find_struct_function_refs_outside_module() {
604 let refs = get_all_refs( 626 check(
605 r#" 627 r#"
606mod foo { 628mod foo {
607 pub struct Foo; 629 pub struct Foo;
608 630
609 impl Foo { 631 impl Foo {
610 pub fn new<|>() -> Foo { 632 pub fn new<|>() -> Foo { Foo }
611 Foo
612 }
613 } 633 }
614} 634}
615 635
@@ -617,80 +637,65 @@ fn main() {
617 let _f = foo::Foo::new(); 637 let _f = foo::Foo::new();
618} 638}
619"#, 639"#,
620 ); 640 expect![[r#"
621 check_result( 641 new FN FileId(1) 54..81 61..64 Other
622 refs, 642
623 "new FN FileId(1) 54..101 61..64 Other", 643 FileId(1) 126..129 StructLiteral
624 &["FileId(1) 146..149 StructLiteral"], 644 "#]],
625 ); 645 );
626 } 646 }
627 647
628 #[test] 648 #[test]
629 fn test_find_all_refs_nested_module() { 649 fn test_find_all_refs_nested_module() {
630 let code = r#" 650 check(
631 //- /lib.rs 651 r#"
632 mod foo { 652//- /lib.rs
633 mod bar; 653mod foo { mod bar; }
634 }
635 654
636 fn f<|>() {} 655fn f<|>() {}
637 656
638 //- /foo/bar.rs 657//- /foo/bar.rs
639 use crate::f; 658use crate::f;
640 659
641 fn g() { 660fn g() { f(); }
642 f(); 661"#,
643 } 662 expect![[r#"
644 "#; 663 f FN FileId(1) 22..31 25..26 Other
645 664
646 let (analysis, pos) = analysis_and_position(code); 665 FileId(2) 11..12 Other
647 let refs = analysis.find_all_refs(pos, None).unwrap().unwrap(); 666 FileId(2) 24..25 StructLiteral
648 check_result( 667 "#]],
649 refs,
650 "f FN FileId(1) 26..35 29..30 Other",
651 &["FileId(2) 11..12 Other", "FileId(2) 28..29 StructLiteral"],
652 ); 668 );
653 } 669 }
654 670
655 fn get_all_refs(ra_fixture: &str) -> ReferenceSearchResult { 671 fn check(ra_fixture: &str, expect: Expect) {
656 let (analysis, position) = analysis_and_position(ra_fixture); 672 check_with_scope(ra_fixture, None, expect)
657 analysis.find_all_refs(position, None).unwrap().unwrap()
658 } 673 }
659 674
660 fn check_result(res: ReferenceSearchResult, expected_decl: &str, expected_refs: &[&str]) { 675 fn check_with_scope(ra_fixture: &str, search_scope: Option<&str>, expect: Expect) {
661 res.declaration().assert_match(expected_decl); 676 let (mock_analysis, pos) = MockAnalysis::with_files_and_position(ra_fixture);
662 assert_eq!(res.references.len(), expected_refs.len()); 677 let search_scope =
663 res.references() 678 search_scope.map(|path| SearchScope::single_file(mock_analysis.id_of(path)));
664 .iter() 679 let analysis = mock_analysis.analysis();
665 .enumerate() 680 let refs = analysis.find_all_refs(pos, search_scope).unwrap().unwrap();
666 .for_each(|(i, r)| ref_assert_match(r, expected_refs[i]));
667 }
668 681
669 impl Declaration { 682 let mut actual = String::new();
670 fn debug_render(&self) -> String { 683 {
671 let mut s = format!("{} {:?}", self.nav.debug_render(), self.kind); 684 let decl = refs.declaration;
672 if let Some(access) = self.access { 685 format_to!(actual, "{} {:?}", decl.nav.debug_render(), decl.kind);
673 s.push_str(&format!(" {:?}", access)); 686 if let Some(access) = decl.access {
687 format_to!(actual, " {:?}", access)
674 } 688 }
675 s 689 actual += "\n\n";
676 } 690 }
677 691
678 fn assert_match(&self, expected: &str) { 692 for r in &refs.references {
679 let actual = self.debug_render(); 693 format_to!(actual, "{:?} {:?} {:?}", r.file_range.file_id, r.file_range.range, r.kind);
680 test_utils::assert_eq_text!(expected.trim(), actual.trim(),); 694 if let Some(access) = r.access {
681 } 695 format_to!(actual, " {:?}", access);
682 } 696 }
683 697 actual += "\n";
684 fn ref_debug_render(r: &Reference) -> String {
685 let mut s = format!("{:?} {:?} {:?}", r.file_range.file_id, r.file_range.range, r.kind);
686 if let Some(access) = r.access {
687 s.push_str(&format!(" {:?}", access));
688 } 698 }
689 s 699 expect.assert_eq(&actual)
690 }
691
692 fn ref_assert_match(r: &Reference, expected: &str) {
693 let actual = ref_debug_render(r);
694 test_utils::assert_eq_text!(expected.trim(), actual.trim(),);
695 } 700 }
696} 701}