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 an `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 further 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.
12 use hir
::{PathResolution, Semantics}
;
15 defs
::{Definition, NameClass, NameRefClass}
,
16 search
::{ReferenceCategory, SearchScope, UsageSearchResult}
,
19 use stdx
::hash
::NoHashHashMap
;
21 algo
::find_node_at_offset
,
25 SyntaxNode
, TextRange
, TextSize
, T
,
28 use crate::{FilePosition, NavigationTarget, TryToNav}
;
30 #[derive(Debug, Clone)]
31 pub struct ReferenceSearchResult
{
32 pub declaration
: Option
<Declaration
>,
33 pub references
: NoHashHashMap
<FileId
, Vec
<(TextRange
, Option
<ReferenceCategory
>)>>,
36 #[derive(Debug, Clone)]
37 pub struct Declaration
{
38 pub nav
: NavigationTarget
,
42 // Feature: Find All References
44 // Shows all references of the item at the cursor location
47 // | Editor | Shortcut
49 // | VS Code | kbd:[Shift+Alt+F12]
52 // image::https://user-images.githubusercontent.com/48062697/113020670-b7c34f00-917a-11eb-8003-370ac5f2b3cb.gif[]
53 pub(crate) fn find_all_refs(
54 sema
: &Semantics
<'_
, RootDatabase
>,
55 position
: FilePosition
,
56 search_scope
: Option
<SearchScope
>,
57 ) -> Option
<Vec
<ReferenceSearchResult
>> {
58 let _p
= profile
::span("find_all_refs");
59 let syntax
= sema
.parse(position
.file_id
).syntax().clone();
60 let make_searcher
= |literal_search
: bool
| {
61 move |def
: Definition
| {
62 let declaration
= match def
{
63 Definition
::Module(module
) => {
64 Some(NavigationTarget
::from_module_to_decl(sema
.db
, module
))
66 def
=> def
.try_to_nav(sema
.db
),
69 let decl_range
= nav
.focus_or_full_range();
71 is_mut
: decl_mutability(&def
, sema
.parse(nav
.file_id
).syntax(), decl_range
),
76 def
.usages(sema
).set_scope(search_scope
.clone()).include_self_refs().all();
79 retain_adt_literal_usages(&mut usages
, def
, sema
);
82 let references
= usages
84 .map(|(file_id
, refs
)| {
88 .map(|file_ref
| (file_ref
.range
, file_ref
.category
))
94 ReferenceSearchResult { declaration, references }
98 match name_for_constructor_search(&syntax
, position
) {
100 let def
= match NameClass
::classify(sema
, &name
)?
{
101 NameClass
::Definition(it
) | NameClass
::ConstReference(it
) => it
,
102 NameClass
::PatFieldShorthand { local_def: _, field_ref }
=> {
103 Definition
::Field(field_ref
)
106 Some(vec
![make_searcher(true)(def
)])
109 let search
= make_searcher(false);
110 Some(find_defs(sema
, &syntax
, position
.offset
)?
.map(search
).collect())
115 pub(crate) fn find_defs
<'a
>(
116 sema
: &'a Semantics
<'_
, RootDatabase
>,
119 ) -> Option
<impl Iterator
<Item
= Definition
> + 'a
> {
120 let token
= syntax
.token_at_offset(offset
).find(|t
| {
123 IDENT
| INT_NUMBER
| LIFETIME_IDENT
| T
![self] | T
![super] | T
![crate] | T
![Self]
127 sema
.descend_into_macros_with_same_text(token
)
129 .filter_map(|it
| ast
::NameLike
::cast(it
.parent()?
))
130 .filter_map(move |name_like
| {
131 let def
= match name_like
{
132 ast
::NameLike
::NameRef(name_ref
) => {
133 match NameRefClass
::classify(sema
, &name_ref
)?
{
134 NameRefClass
::Definition(def
) => def
,
135 NameRefClass
::FieldShorthand { local_ref, field_ref: _ }
=> {
136 Definition
::Local(local_ref
)
140 ast
::NameLike
::Name(name
) => match NameClass
::classify(sema
, &name
)?
{
141 NameClass
::Definition(it
) | NameClass
::ConstReference(it
) => it
,
142 NameClass
::PatFieldShorthand { local_def, field_ref: _ }
=> {
143 Definition
::Local(local_def
)
146 ast
::NameLike
::Lifetime(lifetime
) => {
147 NameRefClass
::classify_lifetime(sema
, &lifetime
)
148 .and_then(|class
| match class
{
149 NameRefClass
::Definition(it
) => Some(it
),
153 NameClass
::classify_lifetime(sema
, &lifetime
)
154 .and_then(NameClass
::defined
)
163 pub(crate) fn decl_mutability(def
: &Definition
, syntax
: &SyntaxNode
, range
: TextRange
) -> bool
{
165 Definition
::Local(_
) | Definition
::Field(_
) => {}
169 match find_node_at_offset
::<ast
::LetStmt
>(syntax
, range
.start()) {
170 Some(stmt
) if stmt
.initializer().is_some() => match stmt
.pat() {
171 Some(ast
::Pat
::IdentPat(it
)) => it
.mut_token().is_some(),
178 /// Filter out all non-literal usages for adt-defs
179 fn retain_adt_literal_usages(
180 usages
: &mut UsageSearchResult
,
182 sema
: &Semantics
<'_
, RootDatabase
>,
184 let refs
= usages
.references
.values_mut();
186 Definition
::Adt(hir
::Adt
::Enum(enum_
)) => {
188 it
.retain(|reference
| {
192 .map_or(false, |name_ref
| is_enum_lit_name_ref(sema
, enum_
, name_ref
))
195 usages
.references
.retain(|_
, it
| !it
.is_empty());
197 Definition
::Adt(_
) | Definition
::Variant(_
) => {
199 it
.retain(|reference
| reference
.name
.as_name_ref().map_or(false, is_lit_name_ref
))
201 usages
.references
.retain(|_
, it
| !it
.is_empty());
207 /// Returns `Some` if the cursor is at a position for an item to search for all its constructor/literal usages
208 fn name_for_constructor_search(syntax
: &SyntaxNode
, position
: FilePosition
) -> Option
<ast
::Name
> {
209 let token
= syntax
.token_at_offset(position
.offset
).right_biased()?
;
210 let token_parent
= token
.parent()?
;
211 let kind
= token
.kind();
213 ast
::Struct
::cast(token_parent
)
214 .filter(|struct_
| struct_
.field_list().is_none())
215 .and_then(|struct_
| struct_
.name())
216 } else if kind
== T
!['
{'
] {
219 ast
::RecordFieldList(rfl
) => match_ast
! {
220 match (rfl
.syntax().parent()?
) {
221 ast
::Variant(it
) => it
.name(),
222 ast
::Struct(it
) => it
.name(),
223 ast
::Union(it
) => it
.name(),
227 ast
::VariantList(vl
) => ast
::Enum
::cast(vl
.syntax().parent()?
)?
.name(),
231 } else if kind
== T
!['
('
] {
232 let tfl
= ast
::TupleFieldList
::cast(token_parent
)?
;
234 match (tfl
.syntax().parent()?
) {
235 ast
::Variant(it
) => it
.name(),
236 ast
::Struct(it
) => it
.name(),
245 fn is_enum_lit_name_ref(
246 sema
: &Semantics
<'_
, RootDatabase
>,
248 name_ref
: &ast
::NameRef
,
250 let path_is_variant_of_enum
= |path
: ast
::Path
| {
252 sema
.resolve_path(&path
),
253 Some(PathResolution
::Def(hir
::ModuleDef
::Variant(variant
)))
254 if variant
.parent_enum(sema
.db
) == enum_
260 .find_map(|ancestor
| {
263 ast
::PathExpr(path_expr
) => path_expr
.path().map(path_is_variant_of_enum
),
264 ast
::RecordExpr(record_expr
) => record_expr
.path().map(path_is_variant_of_enum
),
272 fn path_ends_with(path
: Option
<ast
::Path
>, name_ref
: &ast
::NameRef
) -> bool
{
273 path
.and_then(|path
| path
.segment())
274 .and_then(|segment
| segment
.name_ref())
275 .map_or(false, |segment
| segment
== *name_ref
)
278 fn is_lit_name_ref(name_ref
: &ast
::NameRef
) -> bool
{
279 name_ref
.syntax().ancestors().find_map(|ancestor
| {
282 ast
::PathExpr(path_expr
) => Some(path_ends_with(path_expr
.path(), name_ref
)),
283 ast
::RecordExpr(record_expr
) => Some(path_ends_with(record_expr
.path(), name_ref
)),
292 use expect_test
::{expect, Expect}
;
293 use ide_db
::{base_db::FileId, search::ReferenceCategory}
;
296 use crate::{fixture, SearchScope}
;
299 fn test_struct_literal_after_space() {
310 f = Foo {a: Foo::f()};
314 Foo Struct FileId(0) 0..26 7..10
322 fn test_struct_literal_before_space() {
332 Foo Struct FileId(0) 0..13 7..10
341 fn test_struct_literal_with_generic_type() {
351 Foo Struct FileId(0) 0..16 7..10
359 fn test_struct_literal_for_tuple() {
370 Foo Struct FileId(0) 0..16 7..10
378 fn test_struct_literal_for_union() {
391 Foo Union FileId(0) 0..24 6..9
399 fn test_enum_after_space() {
415 Foo Enum FileId(0) 0..37 5..8
425 fn test_variant_record_after_space() {
435 f = Foo::A { n: 92 };
439 A Variant FileId(0) 15..27 15..16
446 fn test_variant_tuple_before_paren() {
460 A Variant FileId(0) 15..21 15..16
468 fn test_enum_before_space() {
481 Foo Enum FileId(0) 0..26 5..8
490 fn test_enum_with_generic_type() {
503 Foo Enum FileId(0) 0..32 5..8
511 fn test_enum_for_tuple() {
524 Foo Enum FileId(0) 0..33 5..8
532 fn test_find_all_refs_for_local() {
547 i Local FileId(0) 20..25 24..25 Write
549 FileId(0) 50..51 Write
550 FileId(0) 54..55 Read
551 FileId(0) 76..77 Write
552 FileId(0) 94..95 Write
558 fn search_filters_by_range() {
571 spam Local FileId(0) 19..23 19..23
573 FileId(0) 34..38 Read
574 FileId(0) 41..45 Read
580 fn test_find_all_refs_for_param_inside() {
583 fn foo(i : u32) -> u32 { i$0 }
586 i ValueParam FileId(0) 7..8 7..8
588 FileId(0) 25..26 Read
594 fn test_find_all_refs_for_fn_param() {
597 fn foo(i$0 : u32) -> u32 { i }
600 i ValueParam FileId(0) 7..8 7..8
602 FileId(0) 25..26 Read
608 fn test_find_all_refs_field_name() {
621 spam Field FileId(0) 17..30 21..25
623 FileId(0) 67..71 Read
629 fn test_find_all_refs_impl_item_name() {
638 f Function FileId(0) 27..43 30..31
646 fn test_find_all_refs_enum_var_name() {
656 B Variant FileId(0) 22..23 22..23
664 fn test_find_all_refs_enum_var_field() {
674 field Field FileId(0) 26..35 26..31
682 fn test_find_all_refs_two_modules() {
690 let i = foo::Foo { n: 5 };
701 let i = bar::Bar { n: 5 };
712 let i = foo::Foo$0 { n: 5 };
716 Foo Struct FileId(1) 17..51 28..31
725 fn test_find_all_refs_decl_module() {
734 let i = Foo { n: 5 };
743 foo Module FileId(0) 0..8 4..7
751 fn test_find_all_refs_decl_module_on_self() {
761 foo Module FileId(0) 0..8 4..7
769 fn test_find_all_refs_decl_module_on_self_crate_root() {
776 Module FileId(0) 0..10
784 fn test_find_all_refs_super_mod_vis() {
795 let i = Foo { n: 5 };
799 pub(super) struct Foo$0 {
804 Foo Struct FileId(2) 0..41 18..21
813 fn test_find_all_refs_with_scope() {
822 fn f() { super::quux(); }
825 fn f() { super::quux(); }
832 quux Function FileId(0) 19..35 26..30
841 Some(SearchScope
::single_file(FileId(2))),
843 quux Function FileId(0) 19..35 26..30
851 fn test_find_all_refs_macro_def() {
855 macro_rules! m1$0 { () => (()) }
863 m1 Macro FileId(0) 0..46 29..31
872 fn test_basic_highlight_read_write() {
881 i Local FileId(0) 19..24 23..24 Write
883 FileId(0) 34..35 Write
884 FileId(0) 38..39 Read
890 fn test_basic_highlight_field_read_write() {
903 f Field FileId(0) 15..21 15..16
905 FileId(0) 55..56 Read
906 FileId(0) 68..69 Write
912 fn test_basic_highlight_decl_no_write() {
921 i Local FileId(0) 19..20 19..20
923 FileId(0) 26..27 Write
929 fn test_find_struct_function_refs_outside_module() {
936 pub fn new$0() -> Foo { Foo }
941 let _f = foo::Foo::new();
945 new Function FileId(0) 54..81 61..64
953 fn test_find_all_refs_nested_module() {
967 f Function FileId(0) 22..31 25..26
976 fn test_find_all_refs_struct_pat() {
990 field Field FileId(0) 15..24 15..20
992 FileId(0) 68..73 Read
998 fn test_find_all_refs_enum_var_pat() {
1009 En::Variant { field } => {}
1014 field Field FileId(0) 32..41 32..37
1016 FileId(0) 102..107 Read
1022 fn test_find_all_refs_enum_var_privacy() {
1034 m::En::Variant { field: 0 }
1038 field Field FileId(0) 56..65 56..61
1040 FileId(0) 125..130 Read
1046 fn test_find_self_refs() {
1049 struct Foo { bar: i32 }
1063 self SelfParam FileId(0) 47..51 47..51
1065 FileId(0) 71..75 Read
1066 FileId(0) 152..156 Read
1072 fn test_find_self_refs_decl() {
1075 struct Foo { bar: i32 }
1084 self SelfParam FileId(0) 47..51 47..51
1086 FileId(0) 63..67 Read
1091 fn check(ra_fixture
: &str, expect
: Expect
) {
1092 check_with_scope(ra_fixture
, None
, expect
)
1095 fn check_with_scope(ra_fixture
: &str, search_scope
: Option
<SearchScope
>, expect
: Expect
) {
1096 let (analysis
, pos
) = fixture
::position(ra_fixture
);
1097 let refs
= analysis
.find_all_refs(pos
, search_scope
).unwrap().unwrap();
1099 let mut actual
= String
::new();
1103 if let Some(decl
) = refs
.declaration
{
1104 format_to
!(actual
, "{}", decl
.nav
.debug_render());
1106 format_to
!(actual
, " {:?}", ReferenceCategory
::Write
)
1111 for (file_id
, references
) in &refs
.references
{
1112 for (range
, access
) in references
{
1113 format_to
!(actual
, "{:?} {:?}", file_id
, range
);
1114 if let Some(access
) = access
{
1115 format_to
!(actual
, " {:?}", access
);
1121 if refs
.references
.is_empty() {
1122 actual
+= "(no references)\n";
1125 expect
.assert_eq(actual
.trim_start())
1129 fn test_find_lifetimes_function() {
1133 impl<'a> Foo<'a> for &'a () {}
1134 fn foo<'a, 'b: 'a>(x: &'a$0 ()) -> &'a () where &'a (): Foo<'a> {
1135 fn bar<'a>(_: &'a ()) {}
1140 'a LifetimeParam FileId(0) 55..57 55..57
1152 fn test_find_lifetimes_type_alias() {
1155 type Foo<'a, T> where T: 'a$0 = &'a T;
1158 'a LifetimeParam FileId(0) 9..11 9..11
1167 fn test_find_lifetimes_trait_impl() {
1173 impl<'a> Foo<'a> for &'a () {
1174 fn foo() -> &'a$0 () {
1180 'a LifetimeParam FileId(0) 47..49 47..49
1190 fn test_map_range_to_original() {
1193 macro_rules! foo {($i:ident) => {$i} }
1200 a Local FileId(0) 59..60 59..60
1202 FileId(0) 80..81 Read
1208 fn test_map_range_to_original_ref() {
1211 macro_rules! foo {($i:ident) => {$i} }
1218 a Local FileId(0) 59..60 59..60
1220 FileId(0) 80..81 Read
1226 fn test_find_labels() {
1229 fn foo<'a>() -> &'a () {
1239 'a Label FileId(0) 29..32 29..31
1248 fn test_find_const_param() {
1251 fn foo<const FOO$0: usize>() -> usize {
1256 FOO ConstParam FileId(0) 7..23 13..16
1267 trait Foo$0 where Self: {}
1272 Foo Trait FileId(0) 0..24 6..9
1280 fn test_trait_self() {
1283 trait Foo where Self$0 {
1290 Self TypeParam FileId(0) 6..9 6..9
1304 impl Foo where Self: {
1309 Foo Struct FileId(0) 0..11 7..10
1320 impl Foo where Self: {
1325 impl Impl FileId(0) 13..57 18..21
1334 fn test_self_variant_with_payload() {
1342 Self::Bar$0() => (),
1349 Bar Variant FileId(0) 11..16 11..14
1357 fn test_attr_differs_from_fn_with_same_name() {
1366 test Function FileId(0) 0..33 11..15
1374 fn test_const_in_pattern() {
1377 const A$0: i32 = 42;
1388 A Const FileId(0) 0..18 6..7
1399 fn test_primitives() {
1402 fn foo(_: bool) -> bo$0ol { true }
1412 fn test_transitive() {
1415 //- /level3.rs new_source_root:local crate:level3
1417 //- /level2.rs new_source_root:local crate:level2 deps:level3
1418 pub use level3::Foo;
1419 //- /level1.rs new_source_root:local crate:level1 deps:level2
1420 pub use level2::Foo;
1421 //- /level0.rs new_source_root:local crate:level0 deps:level1
1422 pub use level1::Foo;
1425 Foo Struct FileId(0) 0..15 11..14
1435 fn test_decl_macro_references() {
1438 //- /lib.rs crate:lib
1446 macro_rules! foo$0 {
1447 () => {struct Foo;};
1451 //- /other.rs crate:other deps:lib new_source_root:local
1455 foo Macro FileId(1) 0..61 29..32
1465 fn macro_doesnt_reference_attribute_on_call() {
1472 #[proc_macro_test::attr_noop]
1477 m Macro FileId(0) 0..32 13..14
1506 func Function FileId(0) 137..146 140..144
1511 func Function FileId(0) 137..146 140..144
1519 fn attr_expanded() {
1522 //- proc_macros: identity
1523 #[proc_macros::identity]
1529 func Function FileId(0) 25..50 28..32
1537 fn attr_assoc_item() {
1540 //- proc_macros: identity
1543 #[proc_macros::identity]
1550 func Function FileId(0) 48..87 51..55
1557 // FIXME: import is classified as function
1562 //- proc_macros: identity
1563 use proc_macros::identity;
1565 #[proc_macros::$0identity]
1569 identity Attribute FileId(1) 1..107 32..40
1576 #![crate_type="proc-macro"]
1577 #[proc_macro_attribute]
1581 func Attribute FileId(0) 28..64 55..59
1588 // FIXME: import is classified as function
1593 //- proc_macros: mirror
1594 use proc_macros::mirror;
1599 mirror Macro FileId(1) 1..77 22..28
1610 //- proc_macros: derive_identity
1611 //- minicore: derive
1612 use proc_macros::DeriveIdentity;
1614 #[derive(proc_macros::DeriveIdentity$0)]
1618 derive_identity Derive FileId(2) 1..107 45..60
1626 #![crate_type="proc-macro"]
1627 #[proc_macro_derive(Derive, attributes(x))]
1628 pub fn deri$0ve(_stream: TokenStream) -> TokenStream {}
1631 derive Derive FileId(0) 28..125 79..85