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 retain_import_usages(&mut usages, sema);
84 let references = usages
86 .map(|(file_id, refs)| {
90 .map(|file_ref| (file_ref.range, file_ref.category))
96 ReferenceSearchResult { declaration, references }
100 match name_for_constructor_search(&syntax, position) {
102 let def = match NameClass::classify(sema, &name)? {
103 NameClass::Definition(it) | NameClass::ConstReference(it) => it,
104 NameClass::PatFieldShorthand { local_def: _, field_ref } => {
105 Definition::Field(field_ref)
108 Some(vec![make_searcher(true)(def)])
111 let search = make_searcher(false);
112 Some(find_defs(sema, &syntax, position.offset)?.map(search).collect())
117 fn retain_import_usages(usages: &mut UsageSearchResult, sema: &Semantics<'_, RootDatabase>) {
118 for (file_id, refs) in &mut usages.references {
120 let file_sema = sema.parse(file_id.clone()).syntax().clone();
122 let maybe_node = file_sema.child_or_token_at_range(x.range.clone());
124 if let Some(node) = maybe_node {
125 let res = match node {
126 syntax::NodeOrToken::Node(x) => {
127 if matches!(x.kind(), USE) {
133 syntax::NodeOrToken::Token(_) => true,
143 pub(crate) fn find_defs<'a>(
144 sema: &'a Semantics<'_, RootDatabase>,
147 ) -> Option<impl Iterator<Item = Definition> + 'a> {
148 let token = syntax.token_at_offset(offset).find(|t| {
151 IDENT | INT_NUMBER | LIFETIME_IDENT | T![self] | T![super] | T![crate] | T![Self]
155 sema.descend_into_macros_with_same_text(token)
157 .filter_map(|it| ast::NameLike::cast(it.parent()?))
158 .filter_map(move |name_like| {
159 let def = match name_like {
160 ast::NameLike::NameRef(name_ref) => {
161 match NameRefClass::classify(sema, &name_ref)? {
162 NameRefClass::Definition(def) => def,
163 NameRefClass::FieldShorthand { local_ref, field_ref: _ } => {
164 Definition::Local(local_ref)
168 ast::NameLike::Name(name) => match NameClass::classify(sema, &name)? {
169 NameClass::Definition(it) | NameClass::ConstReference(it) => it,
170 NameClass::PatFieldShorthand { local_def, field_ref: _ } => {
171 Definition::Local(local_def)
174 ast::NameLike::Lifetime(lifetime) => {
175 NameRefClass::classify_lifetime(sema, &lifetime)
176 .and_then(|class| match class {
177 NameRefClass::Definition(it) => Some(it),
181 NameClass::classify_lifetime(sema, &lifetime)
182 .and_then(NameClass::defined)
191 pub(crate) fn decl_mutability(def: &Definition, syntax: &SyntaxNode, range: TextRange) -> bool {
193 Definition::Local(_) | Definition::Field(_) => {}
197 match find_node_at_offset::<ast::LetStmt>(syntax, range.start()) {
198 Some(stmt) if stmt.initializer().is_some() => match stmt.pat() {
199 Some(ast::Pat::IdentPat(it)) => it.mut_token().is_some(),
206 /// Filter out all non-literal usages for adt-defs
207 fn retain_adt_literal_usages(
208 usages: &mut UsageSearchResult,
210 sema: &Semantics<'_, RootDatabase>,
212 let refs = usages.references.values_mut();
214 Definition::Adt(hir::Adt::Enum(enum_)) => {
216 it.retain(|reference| {
220 .map_or(false, |name_ref| is_enum_lit_name_ref(sema, enum_, name_ref))
223 usages.references.retain(|_, it| !it.is_empty());
225 Definition::Adt(_) | Definition::Variant(_) => {
227 it.retain(|reference| reference.name.as_name_ref().map_or(false, is_lit_name_ref))
229 usages.references.retain(|_, it| !it.is_empty());
235 /// Returns `Some` if the cursor is at a position for an item to search for all its constructor/literal usages
236 fn name_for_constructor_search(syntax: &SyntaxNode, position: FilePosition) -> Option<ast::Name> {
237 let token = syntax.token_at_offset(position.offset).right_biased()?;
238 let token_parent = token.parent()?;
239 let kind = token.kind();
241 ast::Struct::cast(token_parent)
242 .filter(|struct_| struct_.field_list().is_none())
243 .and_then(|struct_| struct_.name())
244 } else if kind == T!['{'] {
247 ast::RecordFieldList(rfl) => match_ast! {
248 match (rfl.syntax().parent()?) {
249 ast::Variant(it) => it.name(),
250 ast::Struct(it) => it.name(),
251 ast::Union(it) => it.name(),
255 ast::VariantList(vl) => ast::Enum::cast(vl.syntax().parent()?)?.name(),
259 } else if kind == T!['('] {
260 let tfl = ast::TupleFieldList::cast(token_parent)?;
262 match (tfl.syntax().parent()?) {
263 ast::Variant(it) => it.name(),
264 ast::Struct(it) => it.name(),
273 fn is_enum_lit_name_ref(
274 sema: &Semantics<'_, RootDatabase>,
276 name_ref: &ast::NameRef,
278 let path_is_variant_of_enum = |path: ast::Path| {
280 sema.resolve_path(&path),
281 Some(PathResolution::Def(hir::ModuleDef::Variant(variant)))
282 if variant.parent_enum(sema.db) == enum_
288 .find_map(|ancestor| {
291 ast::PathExpr(path_expr) => path_expr.path().map(path_is_variant_of_enum),
292 ast::RecordExpr(record_expr) => record_expr.path().map(path_is_variant_of_enum),
300 fn path_ends_with(path: Option<ast::Path>, name_ref: &ast::NameRef) -> bool {
301 path.and_then(|path| path.segment())
302 .and_then(|segment| segment.name_ref())
303 .map_or(false, |segment| segment == *name_ref)
306 fn is_lit_name_ref(name_ref: &ast::NameRef) -> bool {
307 name_ref.syntax().ancestors().find_map(|ancestor| {
310 ast::PathExpr(path_expr) => Some(path_ends_with(path_expr.path(), name_ref)),
311 ast::RecordExpr(record_expr) => Some(path_ends_with(record_expr.path(), name_ref)),
320 use expect_test::{expect, Expect};
321 use ide_db::{base_db::FileId, search::ReferenceCategory};
324 use crate::{fixture, SearchScope};
327 fn test_struct_literal_after_space() {
338 f = Foo {a: Foo::f()};
342 Foo Struct FileId(0) 0..26 7..10
350 fn test_struct_literal_before_space() {
360 Foo Struct FileId(0) 0..13 7..10
369 fn test_struct_literal_with_generic_type() {
379 Foo Struct FileId(0) 0..16 7..10
387 fn test_struct_literal_for_tuple() {
398 Foo Struct FileId(0) 0..16 7..10
406 fn test_struct_literal_for_union() {
419 Foo Union FileId(0) 0..24 6..9
427 fn test_enum_after_space() {
443 Foo Enum FileId(0) 0..37 5..8
453 fn test_variant_record_after_space() {
463 f = Foo::A { n: 92 };
467 A Variant FileId(0) 15..27 15..16
474 fn test_variant_tuple_before_paren() {
488 A Variant FileId(0) 15..21 15..16
496 fn test_enum_before_space() {
509 Foo Enum FileId(0) 0..26 5..8
518 fn test_enum_with_generic_type() {
531 Foo Enum FileId(0) 0..32 5..8
539 fn test_enum_for_tuple() {
552 Foo Enum FileId(0) 0..33 5..8
560 fn test_find_all_refs_for_local() {
575 i Local FileId(0) 20..25 24..25 Write
577 FileId(0) 50..51 Write
578 FileId(0) 54..55 Read
579 FileId(0) 76..77 Write
580 FileId(0) 94..95 Write
586 fn search_filters_by_range() {
599 spam Local FileId(0) 19..23 19..23
601 FileId(0) 34..38 Read
602 FileId(0) 41..45 Read
608 fn test_find_all_refs_for_param_inside() {
611 fn foo(i : u32) -> u32 { i$0 }
614 i ValueParam FileId(0) 7..8 7..8
616 FileId(0) 25..26 Read
622 fn test_find_all_refs_for_fn_param() {
625 fn foo(i$0 : u32) -> u32 { i }
628 i ValueParam FileId(0) 7..8 7..8
630 FileId(0) 25..26 Read
636 fn test_find_all_refs_field_name() {
649 spam Field FileId(0) 17..30 21..25
651 FileId(0) 67..71 Read
657 fn test_find_all_refs_impl_item_name() {
666 f Function FileId(0) 27..43 30..31
674 fn test_find_all_refs_enum_var_name() {
684 B Variant FileId(0) 22..23 22..23
692 fn test_find_all_refs_enum_var_field() {
702 field Field FileId(0) 26..35 26..31
710 fn test_find_all_refs_two_modules() {
718 let i = foo::Foo { n: 5 };
729 let i = bar::Bar { n: 5 };
740 let i = foo::Foo$0 { n: 5 };
744 Foo Struct FileId(1) 17..51 28..31
753 fn test_find_all_refs_decl_module() {
762 let i = Foo { n: 5 };
771 foo Module FileId(0) 0..8 4..7
779 fn test_find_all_refs_decl_module_on_self() {
789 foo Module FileId(0) 0..8 4..7
797 fn test_find_all_refs_decl_module_on_self_crate_root() {
804 Module FileId(0) 0..10
812 fn test_find_all_refs_super_mod_vis() {
823 let i = Foo { n: 5 };
827 pub(super) struct Foo$0 {
832 Foo Struct FileId(2) 0..41 18..21
841 fn test_find_all_refs_with_scope() {
850 fn f() { super::quux(); }
853 fn f() { super::quux(); }
860 quux Function FileId(0) 19..35 26..30
869 Some(SearchScope::single_file(FileId(2))),
871 quux Function FileId(0) 19..35 26..30
879 fn test_find_all_refs_macro_def() {
883 macro_rules! m1$0 { () => (()) }
891 m1 Macro FileId(0) 0..46 29..31
900 fn test_basic_highlight_read_write() {
909 i Local FileId(0) 19..24 23..24 Write
911 FileId(0) 34..35 Write
912 FileId(0) 38..39 Read
918 fn test_basic_highlight_field_read_write() {
931 f Field FileId(0) 15..21 15..16
933 FileId(0) 55..56 Read
934 FileId(0) 68..69 Write
940 fn test_basic_highlight_decl_no_write() {
949 i Local FileId(0) 19..20 19..20
951 FileId(0) 26..27 Write
957 fn test_find_struct_function_refs_outside_module() {
964 pub fn new$0() -> Foo { Foo }
969 let _f = foo::Foo::new();
973 new Function FileId(0) 54..81 61..64
981 fn test_find_all_refs_nested_module() {
995 f Function FileId(0) 22..31 25..26
1004 fn test_find_all_refs_struct_pat() {
1018 field Field FileId(0) 15..24 15..20
1020 FileId(0) 68..73 Read
1026 fn test_find_all_refs_enum_var_pat() {
1037 En::Variant { field } => {}
1042 field Field FileId(0) 32..41 32..37
1044 FileId(0) 102..107 Read
1050 fn test_find_all_refs_enum_var_privacy() {
1062 m::En::Variant { field: 0 }
1066 field Field FileId(0) 56..65 56..61
1068 FileId(0) 125..130 Read
1074 fn test_find_self_refs() {
1077 struct Foo { bar: i32 }
1091 self SelfParam FileId(0) 47..51 47..51
1093 FileId(0) 71..75 Read
1094 FileId(0) 152..156 Read
1100 fn test_find_self_refs_decl() {
1103 struct Foo { bar: i32 }
1112 self SelfParam FileId(0) 47..51 47..51
1114 FileId(0) 63..67 Read
1119 fn check(ra_fixture: &str, expect: Expect) {
1120 check_with_scope(ra_fixture, None, expect)
1123 fn check_with_scope(ra_fixture: &str, search_scope: Option<SearchScope>, expect: Expect) {
1124 let (analysis, pos) = fixture::position(ra_fixture);
1125 let refs = analysis.find_all_refs(pos, search_scope).unwrap().unwrap();
1127 let mut actual = String::new();
1131 if let Some(decl) = refs.declaration {
1132 format_to!(actual, "{}", decl.nav.debug_render());
1134 format_to!(actual, " {:?}", ReferenceCategory::Write)
1139 for (file_id, references) in &refs.references {
1140 for (range, access) in references {
1141 format_to!(actual, "{:?} {:?}", file_id, range);
1142 if let Some(access) = access {
1143 format_to!(actual, " {:?}", access);
1149 if refs.references.is_empty() {
1150 actual += "(no references)\n";
1153 expect.assert_eq(actual.trim_start())
1157 fn test_find_lifetimes_function() {
1161 impl<'a> Foo<'a> for &'a () {}
1162 fn foo<'a, 'b: 'a>(x: &'a$0 ()) -> &'a () where &'a (): Foo<'a> {
1163 fn bar<'a>(_: &'a ()) {}
1168 'a LifetimeParam FileId(0) 55..57 55..57
1180 fn test_find_lifetimes_type_alias() {
1183 type Foo<'a, T> where T: 'a$0 = &'a T;
1186 'a LifetimeParam FileId(0) 9..11 9..11
1195 fn test_find_lifetimes_trait_impl() {
1201 impl<'a> Foo<'a> for &'a () {
1202 fn foo() -> &'a$0 () {
1208 'a LifetimeParam FileId(0) 47..49 47..49
1218 fn test_map_range_to_original() {
1221 macro_rules! foo {($i:ident) => {$i} }
1228 a Local FileId(0) 59..60 59..60
1230 FileId(0) 80..81 Read
1236 fn test_map_range_to_original_ref() {
1239 macro_rules! foo {($i:ident) => {$i} }
1246 a Local FileId(0) 59..60 59..60
1248 FileId(0) 80..81 Read
1254 fn test_find_labels() {
1257 fn foo<'a>() -> &'a () {
1267 'a Label FileId(0) 29..32 29..31
1276 fn test_find_const_param() {
1279 fn foo<const FOO$0: usize>() -> usize {
1284 FOO ConstParam FileId(0) 7..23 13..16
1295 trait Foo$0 where Self: {}
1300 Foo Trait FileId(0) 0..24 6..9
1308 fn test_trait_self() {
1311 trait Foo where Self$0 {
1318 Self TypeParam FileId(0) 6..9 6..9
1332 impl Foo where Self: {
1337 Foo Struct FileId(0) 0..11 7..10
1348 impl Foo where Self: {
1353 impl Impl FileId(0) 13..57 18..21
1362 fn test_self_variant_with_payload() {
1370 Self::Bar$0() => (),
1377 Bar Variant FileId(0) 11..16 11..14
1385 fn test_attr_differs_from_fn_with_same_name() {
1394 test Function FileId(0) 0..33 11..15
1402 fn test_const_in_pattern() {
1405 const A$0: i32 = 42;
1416 A Const FileId(0) 0..18 6..7
1427 fn test_primitives() {
1430 fn foo(_: bool) -> bo$0ol { true }
1440 fn test_transitive() {
1443 //- /level3.rs new_source_root:local crate:level3
1445 //- /level2.rs new_source_root:local crate:level2 deps:level3
1446 pub use level3::Foo;
1447 //- /level1.rs new_source_root:local crate:level1 deps:level2
1448 pub use level2::Foo;
1449 //- /level0.rs new_source_root:local crate:level0 deps:level1
1450 pub use level1::Foo;
1453 Foo Struct FileId(0) 0..15 11..14
1463 fn test_decl_macro_references() {
1466 //- /lib.rs crate:lib
1474 macro_rules! foo$0 {
1475 () => {struct Foo;};
1479 //- /other.rs crate:other deps:lib new_source_root:local
1483 foo Macro FileId(1) 0..61 29..32
1493 fn macro_doesnt_reference_attribute_on_call() {
1500 #[proc_macro_test::attr_noop]
1505 m Macro FileId(0) 0..32 13..14
1534 func Function FileId(0) 137..146 140..144
1539 func Function FileId(0) 137..146 140..144
1547 fn attr_expanded() {
1550 //- proc_macros: identity
1551 #[proc_macros::identity]
1557 func Function FileId(0) 25..50 28..32
1565 fn attr_assoc_item() {
1568 //- proc_macros: identity
1571 #[proc_macros::identity]
1578 func Function FileId(0) 48..87 51..55
1585 // FIXME: import is classified as function
1590 //- proc_macros: identity
1591 use proc_macros::identity;
1593 #[proc_macros::$0identity]
1597 identity Attribute FileId(1) 1..107 32..40
1604 #![crate_type="proc-macro"]
1605 #[proc_macro_attribute]
1609 func Attribute FileId(0) 28..64 55..59
1616 // FIXME: import is classified as function
1621 //- proc_macros: mirror
1622 use proc_macros::mirror;
1627 mirror Macro FileId(1) 1..77 22..28
1638 //- proc_macros: derive_identity
1639 //- minicore: derive
1640 use proc_macros::DeriveIdentity;
1642 #[derive(proc_macros::DeriveIdentity$0)]
1646 derive_identity Derive FileId(2) 1..107 45..60
1654 #![crate_type="proc-macro"]
1655 #[proc_macro_derive(Derive, attributes(x))]
1656 pub fn deri$0ve(_stream: TokenStream) -> TokenStream {}
1659 derive Derive FileId(0) 28..125 79..85