diff options
Diffstat (limited to 'crates/ra_ide_api/src/references.rs')
-rw-r--r-- | crates/ra_ide_api/src/references.rs | 154 |
1 files changed, 126 insertions, 28 deletions
diff --git a/crates/ra_ide_api/src/references.rs b/crates/ra_ide_api/src/references.rs index 4247c6d90..9335bc8ca 100644 --- a/crates/ra_ide_api/src/references.rs +++ b/crates/ra_ide_api/src/references.rs | |||
@@ -1,13 +1,18 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | 2 | ||
3 | use hir::{Either, ModuleSource}; | 3 | use hir::{FromSource, ModuleSource}; |
4 | use ra_db::{SourceDatabase, SourceDatabaseExt}; | 4 | use ra_db::{SourceDatabase, SourceDatabaseExt}; |
5 | use ra_syntax::{algo::find_node_at_offset, ast, AstNode, SourceFile, SyntaxNode}; | 5 | use ra_syntax::{algo::find_node_at_offset, ast, AstNode, AstPtr, SyntaxNode}; |
6 | use relative_path::{RelativePath, RelativePathBuf}; | 6 | use relative_path::{RelativePath, RelativePathBuf}; |
7 | 7 | ||
8 | use crate::{ | 8 | use crate::{ |
9 | db::RootDatabase, FileId, FilePosition, FileRange, FileSystemEdit, NavigationTarget, RangeInfo, | 9 | db::RootDatabase, |
10 | SourceChange, SourceFileEdit, TextRange, | 10 | name_ref_kind::{ |
11 | classify_name_ref, | ||
12 | NameKind::{self, *}, | ||
13 | }, | ||
14 | FileId, FilePosition, FileRange, FileSystemEdit, NavigationTarget, RangeInfo, SourceChange, | ||
15 | SourceFileEdit, TextRange, | ||
11 | }; | 16 | }; |
12 | 17 | ||
13 | #[derive(Debug, Clone)] | 18 | #[derive(Debug, Clone)] |
@@ -52,41 +57,92 @@ pub(crate) fn find_all_refs( | |||
52 | position: FilePosition, | 57 | position: FilePosition, |
53 | ) -> Option<RangeInfo<ReferenceSearchResult>> { | 58 | ) -> Option<RangeInfo<ReferenceSearchResult>> { |
54 | let parse = db.parse(position.file_id); | 59 | let parse = db.parse(position.file_id); |
55 | let RangeInfo { range, info: (binding, analyzer) } = find_binding(db, &parse.tree(), position)?; | 60 | let syntax = parse.tree().syntax().clone(); |
56 | let declaration = NavigationTarget::from_bind_pat(position.file_id, &binding); | 61 | let RangeInfo { range, info: (analyzer, name_kind) } = find_name(db, &syntax, position)?; |
62 | |||
63 | let declaration = match name_kind { | ||
64 | Macro(mac) => NavigationTarget::from_macro_def(db, mac), | ||
65 | FieldAccess(field) => NavigationTarget::from_field(db, field), | ||
66 | AssocItem(assoc) => NavigationTarget::from_assoc_item(db, assoc), | ||
67 | Method(func) => NavigationTarget::from_def_source(db, func), | ||
68 | Def(def) => NavigationTarget::from_def(db, def)?, | ||
69 | SelfType(ref ty) => match ty.as_adt() { | ||
70 | Some((def_id, _)) => NavigationTarget::from_adt_def(db, def_id), | ||
71 | None => return None, | ||
72 | }, | ||
73 | Pat(pat) => NavigationTarget::from_pat(db, position.file_id, pat), | ||
74 | SelfParam(par) => NavigationTarget::from_self_param(position.file_id, par), | ||
75 | GenericParam(_) => return None, | ||
76 | }; | ||
57 | 77 | ||
58 | let references = analyzer | 78 | let references = match name_kind { |
59 | .find_all_refs(&binding) | 79 | Pat(pat) => analyzer |
60 | .into_iter() | 80 | .find_all_refs(&pat.to_node(&syntax)) |
61 | .map(move |ref_desc| FileRange { file_id: position.file_id, range: ref_desc.range }) | 81 | .into_iter() |
62 | .collect::<Vec<_>>(); | 82 | .map(move |ref_desc| FileRange { file_id: position.file_id, range: ref_desc.range }) |
83 | .collect::<Vec<_>>(), | ||
84 | _ => vec![], | ||
85 | }; | ||
63 | 86 | ||
64 | return Some(RangeInfo::new(range, ReferenceSearchResult { declaration, references })); | 87 | return Some(RangeInfo::new(range, ReferenceSearchResult { declaration, references })); |
65 | 88 | ||
66 | fn find_binding<'a>( | 89 | fn find_name<'a>( |
67 | db: &RootDatabase, | 90 | db: &RootDatabase, |
68 | source_file: &SourceFile, | 91 | syntax: &SyntaxNode, |
69 | position: FilePosition, | 92 | position: FilePosition, |
70 | ) -> Option<RangeInfo<(ast::BindPat, hir::SourceAnalyzer)>> { | 93 | ) -> Option<RangeInfo<(hir::SourceAnalyzer, NameKind)>> { |
71 | let syntax = source_file.syntax(); | 94 | if let Some(name) = find_node_at_offset::<ast::Name>(&syntax, position.offset) { |
72 | if let Some(binding) = find_node_at_offset::<ast::BindPat>(syntax, position.offset) { | 95 | let analyzer = hir::SourceAnalyzer::new(db, position.file_id, name.syntax(), None); |
73 | let range = binding.syntax().text_range(); | 96 | let name_kind = classify_name(db, position.file_id, &name)?; |
74 | let analyzer = hir::SourceAnalyzer::new(db, position.file_id, binding.syntax(), None); | 97 | let range = name.syntax().text_range(); |
75 | return Some(RangeInfo::new(range, (binding, analyzer))); | 98 | return Some(RangeInfo::new(range, (analyzer, name_kind))); |
76 | }; | 99 | } |
77 | let name_ref = find_node_at_offset::<ast::NameRef>(syntax, position.offset)?; | 100 | let name_ref = find_node_at_offset::<ast::NameRef>(&syntax, position.offset)?; |
78 | let range = name_ref.syntax().text_range(); | 101 | let range = name_ref.syntax().text_range(); |
79 | let analyzer = hir::SourceAnalyzer::new(db, position.file_id, name_ref.syntax(), None); | 102 | let analyzer = hir::SourceAnalyzer::new(db, position.file_id, name_ref.syntax(), None); |
80 | let resolved = analyzer.resolve_local_name(&name_ref)?; | 103 | let name_kind = classify_name_ref(db, &analyzer, &name_ref)?; |
81 | if let Either::A(ptr) = resolved.ptr() { | 104 | Some(RangeInfo::new(range, (analyzer, name_kind))) |
82 | if let ast::Pat::BindPat(binding) = ptr.to_node(source_file.syntax()) { | ||
83 | return Some(RangeInfo::new(range, (binding, analyzer))); | ||
84 | } | ||
85 | } | ||
86 | None | ||
87 | } | 105 | } |
88 | } | 106 | } |
89 | 107 | ||
108 | fn classify_name(db: &RootDatabase, file_id: FileId, name: &ast::Name) -> Option<NameKind> { | ||
109 | let parent = name.syntax().parent()?; | ||
110 | let file_id = file_id.into(); | ||
111 | |||
112 | if let Some(pat) = ast::BindPat::cast(parent.clone()) { | ||
113 | return Some(Pat(AstPtr::new(&pat))); | ||
114 | } | ||
115 | if let Some(var) = ast::EnumVariant::cast(parent.clone()) { | ||
116 | let src = hir::Source { file_id, ast: var }; | ||
117 | let var = hir::EnumVariant::from_source(db, src)?; | ||
118 | return Some(Def(var.into())); | ||
119 | } | ||
120 | if let Some(field) = ast::RecordFieldDef::cast(parent.clone()) { | ||
121 | let src = hir::Source { file_id, ast: hir::FieldSource::Named(field) }; | ||
122 | let field = hir::StructField::from_source(db, src)?; | ||
123 | return Some(FieldAccess(field)); | ||
124 | } | ||
125 | if let Some(field) = ast::TupleFieldDef::cast(parent.clone()) { | ||
126 | let src = hir::Source { file_id, ast: hir::FieldSource::Pos(field) }; | ||
127 | let field = hir::StructField::from_source(db, src)?; | ||
128 | return Some(FieldAccess(field)); | ||
129 | } | ||
130 | if let Some(_) = parent.parent().and_then(ast::ItemList::cast) { | ||
131 | let ast = ast::ImplItem::cast(parent.clone())?; | ||
132 | let src = hir::Source { file_id, ast }; | ||
133 | let item = hir::AssocItem::from_source(db, src)?; | ||
134 | return Some(AssocItem(item)); | ||
135 | } | ||
136 | if let Some(item) = ast::ModuleItem::cast(parent.clone()) { | ||
137 | let src = hir::Source { file_id, ast: item }; | ||
138 | let def = hir::ModuleDef::from_source(db, src)?; | ||
139 | return Some(Def(def)); | ||
140 | } | ||
141 | // FIXME: TYPE_PARAM, ALIAS, MACRO_CALL; Union | ||
142 | |||
143 | None | ||
144 | } | ||
145 | |||
90 | pub(crate) fn rename( | 146 | pub(crate) fn rename( |
91 | db: &RootDatabase, | 147 | db: &RootDatabase, |
92 | position: FilePosition, | 148 | position: FilePosition, |
@@ -249,6 +305,48 @@ mod tests { | |||
249 | assert_eq!(refs.len(), 2); | 305 | assert_eq!(refs.len(), 2); |
250 | } | 306 | } |
251 | 307 | ||
308 | #[test] | ||
309 | fn test_find_all_refs_field_name() { | ||
310 | let code = r#" | ||
311 | //- /lib.rs | ||
312 | struct Foo { | ||
313 | spam<|>: u32, | ||
314 | } | ||
315 | "#; | ||
316 | |||
317 | let refs = get_all_refs(code); | ||
318 | assert_eq!(refs.len(), 1); | ||
319 | } | ||
320 | |||
321 | #[test] | ||
322 | fn test_find_all_refs_impl_item_name() { | ||
323 | let code = r#" | ||
324 | //- /lib.rs | ||
325 | struct Foo; | ||
326 | impl Foo { | ||
327 | fn f<|>(&self) { } | ||
328 | } | ||
329 | "#; | ||
330 | |||
331 | let refs = get_all_refs(code); | ||
332 | assert_eq!(refs.len(), 1); | ||
333 | } | ||
334 | |||
335 | #[test] | ||
336 | fn test_find_all_refs_enum_var_name() { | ||
337 | let code = r#" | ||
338 | //- /lib.rs | ||
339 | enum Foo { | ||
340 | A, | ||
341 | B<|>, | ||
342 | C, | ||
343 | } | ||
344 | "#; | ||
345 | |||
346 | let refs = get_all_refs(code); | ||
347 | assert_eq!(refs.len(), 1); | ||
348 | } | ||
349 | |||
252 | fn get_all_refs(text: &str) -> ReferenceSearchResult { | 350 | fn get_all_refs(text: &str) -> ReferenceSearchResult { |
253 | let (analysis, position) = single_file_with_position(text); | 351 | let (analysis, position) = single_file_with_position(text); |
254 | analysis.find_all_refs(position).unwrap().unwrap() | 352 | analysis.find_all_refs(position).unwrap().unwrap() |