diff options
Diffstat (limited to 'crates/ide/src')
-rw-r--r-- | crates/ide/src/references.rs | 103 |
1 files changed, 102 insertions, 1 deletions
diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs index 5693dd400..7395b81bd 100644 --- a/crates/ide/src/references.rs +++ b/crates/ide/src/references.rs | |||
@@ -21,7 +21,7 @@ use ide_db::{ | |||
21 | use syntax::{ | 21 | use syntax::{ |
22 | algo::find_node_at_offset, | 22 | algo::find_node_at_offset, |
23 | ast::{self, NameOwner}, | 23 | ast::{self, NameOwner}, |
24 | AstNode, SyntaxKind, SyntaxNode, TextRange, TokenAtOffset, | 24 | match_ast, AstNode, SyntaxKind, SyntaxNode, TextRange, TokenAtOffset, |
25 | }; | 25 | }; |
26 | 26 | ||
27 | use crate::{display::TryToNav, FilePosition, FileRange, NavigationTarget, RangeInfo}; | 27 | use crate::{display::TryToNav, FilePosition, FileRange, NavigationTarget, RangeInfo}; |
@@ -89,6 +89,10 @@ pub(crate) fn find_all_refs( | |||
89 | let _p = profile::span("find_all_refs"); | 89 | let _p = profile::span("find_all_refs"); |
90 | let syntax = sema.parse(position.file_id).syntax().clone(); | 90 | let syntax = sema.parse(position.file_id).syntax().clone(); |
91 | 91 | ||
92 | if let Some(res) = try_find_self_references(&syntax, position) { | ||
93 | return Some(res); | ||
94 | } | ||
95 | |||
92 | let (opt_name, search_kind) = if let Some(name) = | 96 | let (opt_name, search_kind) = if let Some(name) = |
93 | get_struct_def_name_for_struct_literal_search(&sema, &syntax, position) | 97 | get_struct_def_name_for_struct_literal_search(&sema, &syntax, position) |
94 | { | 98 | { |
@@ -194,6 +198,77 @@ fn get_struct_def_name_for_struct_literal_search( | |||
194 | None | 198 | None |
195 | } | 199 | } |
196 | 200 | ||
201 | fn try_find_self_references( | ||
202 | syntax: &SyntaxNode, | ||
203 | position: FilePosition, | ||
204 | ) -> Option<RangeInfo<ReferenceSearchResult>> { | ||
205 | let self_token = | ||
206 | syntax.token_at_offset(position.offset).find(|t| t.kind() == SyntaxKind::SELF_KW)?; | ||
207 | let parent = self_token.parent(); | ||
208 | match_ast! { | ||
209 | match parent { | ||
210 | ast::SelfParam(it) => (), | ||
211 | ast::PathSegment(segment) => { | ||
212 | segment.self_token()?; | ||
213 | let path = segment.parent_path(); | ||
214 | if path.qualifier().is_some() && !ast::PathExpr::can_cast(path.syntax().parent()?.kind()) { | ||
215 | return None; | ||
216 | } | ||
217 | }, | ||
218 | _ => return None, | ||
219 | } | ||
220 | }; | ||
221 | let function = parent.ancestors().find_map(ast::Fn::cast)?; | ||
222 | let self_param = function.param_list()?.self_param()?; | ||
223 | let param_self_token = self_param.self_token()?; | ||
224 | |||
225 | let declaration = Declaration { | ||
226 | nav: NavigationTarget { | ||
227 | file_id: position.file_id, | ||
228 | full_range: self_param.syntax().text_range(), | ||
229 | focus_range: Some(param_self_token.text_range()), | ||
230 | name: param_self_token.text().clone(), | ||
231 | kind: param_self_token.kind(), | ||
232 | container_name: None, | ||
233 | description: None, | ||
234 | docs: None, | ||
235 | }, | ||
236 | kind: ReferenceKind::SelfKw, | ||
237 | access: Some(if self_param.mut_token().is_some() { | ||
238 | ReferenceAccess::Write | ||
239 | } else { | ||
240 | ReferenceAccess::Read | ||
241 | }), | ||
242 | }; | ||
243 | let references = function | ||
244 | .body() | ||
245 | .map(|body| { | ||
246 | body.syntax() | ||
247 | .descendants() | ||
248 | .filter_map(ast::PathExpr::cast) | ||
249 | .filter_map(|expr| { | ||
250 | let path = expr.path()?; | ||
251 | if path.qualifier().is_none() { | ||
252 | path.segment()?.self_token() | ||
253 | } else { | ||
254 | None | ||
255 | } | ||
256 | }) | ||
257 | .map(|token| Reference { | ||
258 | file_range: FileRange { file_id: position.file_id, range: token.text_range() }, | ||
259 | kind: ReferenceKind::SelfKw, | ||
260 | access: declaration.access, // FIXME: properly check access kind here instead of copying it from the declaration | ||
261 | }) | ||
262 | .collect() | ||
263 | }) | ||
264 | .unwrap_or_default(); | ||
265 | |||
266 | Some(RangeInfo::new( | ||
267 | param_self_token.text_range(), | ||
268 | ReferenceSearchResult { declaration, references }, | ||
269 | )) | ||
270 | } | ||
271 | |||
197 | #[cfg(test)] | 272 | #[cfg(test)] |
198 | mod tests { | 273 | mod tests { |
199 | use expect_test::{expect, Expect}; | 274 | use expect_test::{expect, Expect}; |
@@ -762,6 +837,32 @@ fn f() -> m::En { | |||
762 | ); | 837 | ); |
763 | } | 838 | } |
764 | 839 | ||
840 | #[test] | ||
841 | fn test_find_self_refs() { | ||
842 | check( | ||
843 | r#" | ||
844 | struct Foo { bar: i32 } | ||
845 | |||
846 | impl Foo { | ||
847 | fn foo(self) { | ||
848 | let x = self<|>.bar; | ||
849 | if true { | ||
850 | let _ = match () { | ||
851 | () => self, | ||
852 | }; | ||
853 | } | ||
854 | } | ||
855 | } | ||
856 | "#, | ||
857 | expect![[r#" | ||
858 | self SELF_KW FileId(0) 47..51 47..51 SelfKw Read | ||
859 | |||
860 | FileId(0) 71..75 SelfKw Read | ||
861 | FileId(0) 152..156 SelfKw Read | ||
862 | "#]], | ||
863 | ); | ||
864 | } | ||
865 | |||
765 | fn check(ra_fixture: &str, expect: Expect) { | 866 | fn check(ra_fixture: &str, expect: Expect) { |
766 | check_with_scope(ra_fixture, None, expect) | 867 | check_with_scope(ra_fixture, None, expect) |
767 | } | 868 | } |