]> git.lizzy.rs Git - rust.git/blob - crates/ide/src/references.rs
rename mock_analysis -> fixture
[rust.git] / crates / ide / src / references.rs
1 //! This module implements a reference search.
2 //! First, the element at the cursor position must be either an `ast::Name`
3 //! or `ast::NameRef`. If it's a `ast::NameRef`, at the classification step we
4 //! try to resolve the direct tree parent of this element, otherwise we
5 //! already have a definition and just need to get its HIR together with
6 //! some information that is needed for futher steps of searching.
7 //! After that, we collect files that might contain references and look
8 //! for text occurrences of the identifier. If there's an `ast::NameRef`
9 //! at the index that the match starts at and its tree parent is
10 //! resolved to the search element definition, we get a reference.
11
12 mod rename;
13
14 use hir::Semantics;
15 use ide_db::{
16     defs::{classify_name, classify_name_ref, Definition},
17     search::SearchScope,
18     RootDatabase,
19 };
20 use syntax::{
21     algo::find_node_at_offset,
22     ast::{self, NameOwner},
23     AstNode, SyntaxKind, SyntaxNode, TextRange, TokenAtOffset,
24 };
25
26 use crate::{display::TryToNav, FilePosition, FileRange, NavigationTarget, RangeInfo};
27
28 pub(crate) use self::rename::rename;
29
30 pub use ide_db::search::{Reference, ReferenceAccess, ReferenceKind};
31
32 #[derive(Debug, Clone)]
33 pub struct ReferenceSearchResult {
34     declaration: Declaration,
35     references: Vec<Reference>,
36 }
37
38 #[derive(Debug, Clone)]
39 pub struct Declaration {
40     pub nav: NavigationTarget,
41     pub kind: ReferenceKind,
42     pub access: Option<ReferenceAccess>,
43 }
44
45 impl ReferenceSearchResult {
46     pub fn declaration(&self) -> &Declaration {
47         &self.declaration
48     }
49
50     pub fn decl_target(&self) -> &NavigationTarget {
51         &self.declaration.nav
52     }
53
54     pub fn references(&self) -> &[Reference] {
55         &self.references
56     }
57
58     /// Total number of references
59     /// At least 1 since all valid references should
60     /// Have a declaration
61     pub fn len(&self) -> usize {
62         self.references.len() + 1
63     }
64 }
65
66 // allow turning ReferenceSearchResult into an iterator
67 // over References
68 impl IntoIterator for ReferenceSearchResult {
69     type Item = Reference;
70     type IntoIter = std::vec::IntoIter<Reference>;
71
72     fn into_iter(mut self) -> Self::IntoIter {
73         let mut v = Vec::with_capacity(self.len());
74         v.push(Reference {
75             file_range: FileRange {
76                 file_id: self.declaration.nav.file_id,
77                 range: self.declaration.nav.focus_or_full_range(),
78             },
79             kind: self.declaration.kind,
80             access: self.declaration.access,
81         });
82         v.append(&mut self.references);
83         v.into_iter()
84     }
85 }
86
87 pub(crate) fn find_all_refs(
88     sema: &Semantics<RootDatabase>,
89     position: FilePosition,
90     search_scope: Option<SearchScope>,
91 ) -> Option<RangeInfo<ReferenceSearchResult>> {
92     let _p = profile::span("find_all_refs");
93     let syntax = sema.parse(position.file_id).syntax().clone();
94
95     let (opt_name, search_kind) = if let Some(name) =
96         get_struct_def_name_for_struct_literal_search(&sema, &syntax, position)
97     {
98         (Some(name), ReferenceKind::StructLiteral)
99     } else {
100         (
101             sema.find_node_at_offset_with_descend::<ast::Name>(&syntax, position.offset),
102             ReferenceKind::Other,
103         )
104     };
105
106     let RangeInfo { range, info: def } = find_name(&sema, &syntax, position, opt_name)?;
107
108     let references = def
109         .usages(sema)
110         .set_scope(search_scope)
111         .all()
112         .into_iter()
113         .filter(|r| search_kind == ReferenceKind::Other || search_kind == r.kind)
114         .collect();
115
116     let decl_range = def.try_to_nav(sema.db)?.focus_or_full_range();
117
118     let declaration = Declaration {
119         nav: def.try_to_nav(sema.db)?,
120         kind: ReferenceKind::Other,
121         access: decl_access(&def, &syntax, decl_range),
122     };
123
124     Some(RangeInfo::new(range, ReferenceSearchResult { declaration, references }))
125 }
126
127 fn find_name(
128     sema: &Semantics<RootDatabase>,
129     syntax: &SyntaxNode,
130     position: FilePosition,
131     opt_name: Option<ast::Name>,
132 ) -> Option<RangeInfo<Definition>> {
133     if let Some(name) = opt_name {
134         let def = classify_name(sema, &name)?.definition(sema.db);
135         let range = name.syntax().text_range();
136         return Some(RangeInfo::new(range, def));
137     }
138     let name_ref =
139         sema.find_node_at_offset_with_descend::<ast::NameRef>(&syntax, position.offset)?;
140     let def = classify_name_ref(sema, &name_ref)?.definition(sema.db);
141     let range = name_ref.syntax().text_range();
142     Some(RangeInfo::new(range, def))
143 }
144
145 fn decl_access(def: &Definition, syntax: &SyntaxNode, range: TextRange) -> Option<ReferenceAccess> {
146     match def {
147         Definition::Local(_) | Definition::Field(_) => {}
148         _ => return None,
149     };
150
151     let stmt = find_node_at_offset::<ast::LetStmt>(syntax, range.start())?;
152     if stmt.initializer().is_some() {
153         let pat = stmt.pat()?;
154         if let ast::Pat::IdentPat(it) = pat {
155             if it.mut_token().is_some() {
156                 return Some(ReferenceAccess::Write);
157             }
158         }
159     }
160
161     None
162 }
163
164 fn get_struct_def_name_for_struct_literal_search(
165     sema: &Semantics<RootDatabase>,
166     syntax: &SyntaxNode,
167     position: FilePosition,
168 ) -> Option<ast::Name> {
169     if let TokenAtOffset::Between(ref left, ref right) = syntax.token_at_offset(position.offset) {
170         if right.kind() != SyntaxKind::L_CURLY && right.kind() != SyntaxKind::L_PAREN {
171             return None;
172         }
173         if let Some(name) =
174             sema.find_node_at_offset_with_descend::<ast::Name>(&syntax, left.text_range().start())
175         {
176             return name.syntax().ancestors().find_map(ast::Struct::cast).and_then(|l| l.name());
177         }
178         if sema
179             .find_node_at_offset_with_descend::<ast::GenericParamList>(
180                 &syntax,
181                 left.text_range().start(),
182             )
183             .is_some()
184         {
185             return left.ancestors().find_map(ast::Struct::cast).and_then(|l| l.name());
186         }
187     }
188     None
189 }
190
191 #[cfg(test)]
192 mod tests {
193     use base_db::FileId;
194     use expect_test::{expect, Expect};
195     use stdx::format_to;
196
197     use crate::{fixture, SearchScope};
198
199     #[test]
200     fn test_struct_literal_after_space() {
201         check(
202             r#"
203 struct Foo <|>{
204     a: i32,
205 }
206 impl Foo {
207     fn f() -> i32 { 42 }
208 }
209 fn main() {
210     let f: Foo;
211     f = Foo {a: Foo::f()};
212 }
213 "#,
214             expect![[r#"
215                 Foo STRUCT FileId(0) 0..26 7..10 Other
216
217                 FileId(0) 101..104 StructLiteral
218             "#]],
219         );
220     }
221
222     #[test]
223     fn test_struct_literal_before_space() {
224         check(
225             r#"
226 struct Foo<|> {}
227     fn main() {
228     let f: Foo;
229     f = Foo {};
230 }
231 "#,
232             expect![[r#"
233                 Foo STRUCT FileId(0) 0..13 7..10 Other
234
235                 FileId(0) 41..44 Other
236                 FileId(0) 54..57 StructLiteral
237             "#]],
238         );
239     }
240
241     #[test]
242     fn test_struct_literal_with_generic_type() {
243         check(
244             r#"
245 struct Foo<T> <|>{}
246     fn main() {
247     let f: Foo::<i32>;
248     f = Foo {};
249 }
250 "#,
251             expect![[r#"
252                 Foo STRUCT FileId(0) 0..16 7..10 Other
253
254                 FileId(0) 64..67 StructLiteral
255             "#]],
256         );
257     }
258
259     #[test]
260     fn test_struct_literal_for_tuple() {
261         check(
262             r#"
263 struct Foo<|>(i32);
264
265 fn main() {
266     let f: Foo;
267     f = Foo(1);
268 }
269 "#,
270             expect![[r#"
271                 Foo STRUCT FileId(0) 0..16 7..10 Other
272
273                 FileId(0) 54..57 StructLiteral
274             "#]],
275         );
276     }
277
278     #[test]
279     fn test_find_all_refs_for_local() {
280         check(
281             r#"
282 fn main() {
283     let mut i = 1;
284     let j = 1;
285     i = i<|> + j;
286
287     {
288         i = 0;
289     }
290
291     i = 5;
292 }"#,
293             expect![[r#"
294                 i IDENT_PAT FileId(0) 24..25 Other Write
295
296                 FileId(0) 50..51 Other Write
297                 FileId(0) 54..55 Other Read
298                 FileId(0) 76..77 Other Write
299                 FileId(0) 94..95 Other Write
300             "#]],
301         );
302     }
303
304     #[test]
305     fn search_filters_by_range() {
306         check(
307             r#"
308 fn foo() {
309     let spam<|> = 92;
310     spam + spam
311 }
312 fn bar() {
313     let spam = 92;
314     spam + spam
315 }
316 "#,
317             expect![[r#"
318                 spam IDENT_PAT FileId(0) 19..23 Other
319
320                 FileId(0) 34..38 Other Read
321                 FileId(0) 41..45 Other Read
322             "#]],
323         );
324     }
325
326     #[test]
327     fn test_find_all_refs_for_param_inside() {
328         check(
329             r#"
330 fn foo(i : u32) -> u32 { i<|> }
331 "#,
332             expect![[r#"
333                 i IDENT_PAT FileId(0) 7..8 Other
334
335                 FileId(0) 25..26 Other Read
336             "#]],
337         );
338     }
339
340     #[test]
341     fn test_find_all_refs_for_fn_param() {
342         check(
343             r#"
344 fn foo(i<|> : u32) -> u32 { i }
345 "#,
346             expect![[r#"
347                 i IDENT_PAT FileId(0) 7..8 Other
348
349                 FileId(0) 25..26 Other Read
350             "#]],
351         );
352     }
353
354     #[test]
355     fn test_find_all_refs_field_name() {
356         check(
357             r#"
358 //- /lib.rs
359 struct Foo {
360     pub spam<|>: u32,
361 }
362
363 fn main(s: Foo) {
364     let f = s.spam;
365 }
366 "#,
367             expect![[r#"
368                 spam RECORD_FIELD FileId(0) 17..30 21..25 Other
369
370                 FileId(0) 67..71 Other Read
371             "#]],
372         );
373     }
374
375     #[test]
376     fn test_find_all_refs_impl_item_name() {
377         check(
378             r#"
379 struct Foo;
380 impl Foo {
381     fn f<|>(&self) {  }
382 }
383 "#,
384             expect![[r#"
385                 f FN FileId(0) 27..43 30..31 Other
386
387             "#]],
388         );
389     }
390
391     #[test]
392     fn test_find_all_refs_enum_var_name() {
393         check(
394             r#"
395 enum Foo {
396     A,
397     B<|>,
398     C,
399 }
400 "#,
401             expect![[r#"
402                 B VARIANT FileId(0) 22..23 22..23 Other
403
404             "#]],
405         );
406     }
407
408     #[test]
409     fn test_find_all_refs_two_modules() {
410         check(
411             r#"
412 //- /lib.rs
413 pub mod foo;
414 pub mod bar;
415
416 fn f() {
417     let i = foo::Foo { n: 5 };
418 }
419
420 //- /foo.rs
421 use crate::bar;
422
423 pub struct Foo {
424     pub n: u32,
425 }
426
427 fn f() {
428     let i = bar::Bar { n: 5 };
429 }
430
431 //- /bar.rs
432 use crate::foo;
433
434 pub struct Bar {
435     pub n: u32,
436 }
437
438 fn f() {
439     let i = foo::Foo<|> { n: 5 };
440 }
441 "#,
442             expect![[r#"
443                 Foo STRUCT FileId(1) 17..51 28..31 Other
444
445                 FileId(0) 53..56 StructLiteral
446                 FileId(2) 79..82 StructLiteral
447             "#]],
448         );
449     }
450
451     // `mod foo;` is not in the results because `foo` is an `ast::Name`.
452     // So, there are two references: the first one is a definition of the `foo` module,
453     // which is the whole `foo.rs`, and the second one is in `use foo::Foo`.
454     #[test]
455     fn test_find_all_refs_decl_module() {
456         check(
457             r#"
458 //- /lib.rs
459 mod foo<|>;
460
461 use foo::Foo;
462
463 fn f() {
464     let i = Foo { n: 5 };
465 }
466
467 //- /foo.rs
468 pub struct Foo {
469     pub n: u32,
470 }
471 "#,
472             expect![[r#"
473                 foo SOURCE_FILE FileId(1) 0..35 Other
474
475                 FileId(0) 14..17 Other
476             "#]],
477         );
478     }
479
480     #[test]
481     fn test_find_all_refs_super_mod_vis() {
482         check(
483             r#"
484 //- /lib.rs
485 mod foo;
486
487 //- /foo.rs
488 mod some;
489 use some::Foo;
490
491 fn f() {
492     let i = Foo { n: 5 };
493 }
494
495 //- /foo/some.rs
496 pub(super) struct Foo<|> {
497     pub n: u32,
498 }
499 "#,
500             expect![[r#"
501                 Foo STRUCT FileId(2) 0..41 18..21 Other
502
503                 FileId(1) 20..23 Other
504                 FileId(1) 47..50 StructLiteral
505             "#]],
506         );
507     }
508
509     #[test]
510     fn test_find_all_refs_with_scope() {
511         let code = r#"
512             //- /lib.rs
513             mod foo;
514             mod bar;
515
516             pub fn quux<|>() {}
517
518             //- /foo.rs
519             fn f() { super::quux(); }
520
521             //- /bar.rs
522             fn f() { super::quux(); }
523         "#;
524
525         check_with_scope(
526             code,
527             None,
528             expect![[r#"
529                 quux FN FileId(0) 19..35 26..30 Other
530
531                 FileId(1) 16..20 StructLiteral
532                 FileId(2) 16..20 StructLiteral
533             "#]],
534         );
535
536         check_with_scope(
537             code,
538             Some(SearchScope::single_file(FileId(2))),
539             expect![[r#"
540                 quux FN FileId(0) 19..35 26..30 Other
541
542                 FileId(2) 16..20 StructLiteral
543             "#]],
544         );
545     }
546
547     #[test]
548     fn test_find_all_refs_macro_def() {
549         check(
550             r#"
551 #[macro_export]
552 macro_rules! m1<|> { () => (()) }
553
554 fn foo() {
555     m1();
556     m1();
557 }
558 "#,
559             expect![[r#"
560                 m1 MACRO_CALL FileId(0) 0..46 29..31 Other
561
562                 FileId(0) 63..65 StructLiteral
563                 FileId(0) 73..75 StructLiteral
564             "#]],
565         );
566     }
567
568     #[test]
569     fn test_basic_highlight_read_write() {
570         check(
571             r#"
572 fn foo() {
573     let mut i<|> = 0;
574     i = i + 1;
575 }
576 "#,
577             expect![[r#"
578                 i IDENT_PAT FileId(0) 23..24 Other Write
579
580                 FileId(0) 34..35 Other Write
581                 FileId(0) 38..39 Other Read
582             "#]],
583         );
584     }
585
586     #[test]
587     fn test_basic_highlight_field_read_write() {
588         check(
589             r#"
590 struct S {
591     f: u32,
592 }
593
594 fn foo() {
595     let mut s = S{f: 0};
596     s.f<|> = 0;
597 }
598 "#,
599             expect![[r#"
600                 f RECORD_FIELD FileId(0) 15..21 15..16 Other
601
602                 FileId(0) 55..56 Other Read
603                 FileId(0) 68..69 Other Write
604             "#]],
605         );
606     }
607
608     #[test]
609     fn test_basic_highlight_decl_no_write() {
610         check(
611             r#"
612 fn foo() {
613     let i<|>;
614     i = 1;
615 }
616 "#,
617             expect![[r#"
618                 i IDENT_PAT FileId(0) 19..20 Other
619
620                 FileId(0) 26..27 Other Write
621             "#]],
622         );
623     }
624
625     #[test]
626     fn test_find_struct_function_refs_outside_module() {
627         check(
628             r#"
629 mod foo {
630     pub struct Foo;
631
632     impl Foo {
633         pub fn new<|>() -> Foo { Foo }
634     }
635 }
636
637 fn main() {
638     let _f = foo::Foo::new();
639 }
640 "#,
641             expect![[r#"
642                 new FN FileId(0) 54..81 61..64 Other
643
644                 FileId(0) 126..129 StructLiteral
645             "#]],
646         );
647     }
648
649     #[test]
650     fn test_find_all_refs_nested_module() {
651         check(
652             r#"
653 //- /lib.rs
654 mod foo { mod bar; }
655
656 fn f<|>() {}
657
658 //- /foo/bar.rs
659 use crate::f;
660
661 fn g() { f(); }
662 "#,
663             expect![[r#"
664                 f FN FileId(0) 22..31 25..26 Other
665
666                 FileId(1) 11..12 Other
667                 FileId(1) 24..25 StructLiteral
668             "#]],
669         );
670     }
671
672     fn check(ra_fixture: &str, expect: Expect) {
673         check_with_scope(ra_fixture, None, expect)
674     }
675
676     fn check_with_scope(ra_fixture: &str, search_scope: Option<SearchScope>, expect: Expect) {
677         let (analysis, pos) = fixture::position(ra_fixture);
678         let refs = analysis.find_all_refs(pos, search_scope).unwrap().unwrap();
679
680         let mut actual = String::new();
681         {
682             let decl = refs.declaration;
683             format_to!(actual, "{} {:?}", decl.nav.debug_render(), decl.kind);
684             if let Some(access) = decl.access {
685                 format_to!(actual, " {:?}", access)
686             }
687             actual += "\n\n";
688         }
689
690         for r in &refs.references {
691             format_to!(actual, "{:?} {:?} {:?}", r.file_range.file_id, r.file_range.range, r.kind);
692             if let Some(access) = r.access {
693                 format_to!(actual, " {:?}", access);
694             }
695             actual += "\n";
696         }
697         expect.assert_eq(&actual)
698     }
699 }