diff options
Diffstat (limited to 'crates/ra_ide/src/references.rs')
-rw-r--r-- | crates/ra_ide/src/references.rs | 205 |
1 files changed, 181 insertions, 24 deletions
diff --git a/crates/ra_ide/src/references.rs b/crates/ra_ide/src/references.rs index e3ecde50d..5a3ec4eb9 100644 --- a/crates/ra_ide/src/references.rs +++ b/crates/ra_ide/src/references.rs | |||
@@ -18,7 +18,10 @@ use hir::InFile; | |||
18 | use once_cell::unsync::Lazy; | 18 | use once_cell::unsync::Lazy; |
19 | use ra_db::{SourceDatabase, SourceDatabaseExt}; | 19 | use ra_db::{SourceDatabase, SourceDatabaseExt}; |
20 | use ra_prof::profile; | 20 | use ra_prof::profile; |
21 | use ra_syntax::{algo::find_node_at_offset, ast, AstNode, SourceFile, SyntaxNode, TextUnit}; | 21 | use ra_syntax::{ |
22 | algo::find_node_at_offset, ast, AstNode, SourceFile, SyntaxKind, SyntaxNode, TextUnit, | ||
23 | TokenAtOffset, | ||
24 | }; | ||
22 | 25 | ||
23 | use crate::{ | 26 | use crate::{ |
24 | db::RootDatabase, display::ToNav, FilePosition, FileRange, NavigationTarget, RangeInfo, | 27 | db::RootDatabase, display::ToNav, FilePosition, FileRange, NavigationTarget, RangeInfo, |
@@ -35,7 +38,20 @@ pub use self::search_scope::SearchScope; | |||
35 | #[derive(Debug, Clone)] | 38 | #[derive(Debug, Clone)] |
36 | pub struct ReferenceSearchResult { | 39 | pub struct ReferenceSearchResult { |
37 | declaration: NavigationTarget, | 40 | declaration: NavigationTarget, |
38 | references: Vec<FileRange>, | 41 | declaration_kind: ReferenceKind, |
42 | references: Vec<Reference>, | ||
43 | } | ||
44 | |||
45 | #[derive(Debug, Clone)] | ||
46 | pub struct Reference { | ||
47 | pub file_range: FileRange, | ||
48 | pub kind: ReferenceKind, | ||
49 | } | ||
50 | |||
51 | #[derive(Debug, Clone, PartialEq)] | ||
52 | pub enum ReferenceKind { | ||
53 | StructLiteral, | ||
54 | Other, | ||
39 | } | 55 | } |
40 | 56 | ||
41 | impl ReferenceSearchResult { | 57 | impl ReferenceSearchResult { |
@@ -43,7 +59,7 @@ impl ReferenceSearchResult { | |||
43 | &self.declaration | 59 | &self.declaration |
44 | } | 60 | } |
45 | 61 | ||
46 | pub fn references(&self) -> &[FileRange] { | 62 | pub fn references(&self) -> &[Reference] { |
47 | &self.references | 63 | &self.references |
48 | } | 64 | } |
49 | 65 | ||
@@ -58,12 +74,18 @@ impl ReferenceSearchResult { | |||
58 | // allow turning ReferenceSearchResult into an iterator | 74 | // allow turning ReferenceSearchResult into an iterator |
59 | // over FileRanges | 75 | // over FileRanges |
60 | impl IntoIterator for ReferenceSearchResult { | 76 | impl IntoIterator for ReferenceSearchResult { |
61 | type Item = FileRange; | 77 | type Item = Reference; |
62 | type IntoIter = std::vec::IntoIter<FileRange>; | 78 | type IntoIter = std::vec::IntoIter<Reference>; |
63 | 79 | ||
64 | fn into_iter(mut self) -> Self::IntoIter { | 80 | fn into_iter(mut self) -> Self::IntoIter { |
65 | let mut v = Vec::with_capacity(self.len()); | 81 | let mut v = Vec::with_capacity(self.len()); |
66 | v.push(FileRange { file_id: self.declaration.file_id(), range: self.declaration.range() }); | 82 | v.push(Reference { |
83 | file_range: FileRange { | ||
84 | file_id: self.declaration.file_id(), | ||
85 | range: self.declaration.range(), | ||
86 | }, | ||
87 | kind: self.declaration_kind, | ||
88 | }); | ||
67 | v.append(&mut self.references); | 89 | v.append(&mut self.references); |
68 | v.into_iter() | 90 | v.into_iter() |
69 | } | 91 | } |
@@ -71,11 +93,24 @@ impl IntoIterator for ReferenceSearchResult { | |||
71 | 93 | ||
72 | pub(crate) fn find_all_refs( | 94 | pub(crate) fn find_all_refs( |
73 | db: &RootDatabase, | 95 | db: &RootDatabase, |
74 | position: FilePosition, | 96 | mut position: FilePosition, |
75 | search_scope: Option<SearchScope>, | 97 | search_scope: Option<SearchScope>, |
76 | ) -> Option<RangeInfo<ReferenceSearchResult>> { | 98 | ) -> Option<RangeInfo<ReferenceSearchResult>> { |
77 | let parse = db.parse(position.file_id); | 99 | let parse = db.parse(position.file_id); |
78 | let syntax = parse.tree().syntax().clone(); | 100 | let syntax = parse.tree().syntax().clone(); |
101 | |||
102 | let token = syntax.token_at_offset(position.offset); | ||
103 | let mut search_kind = ReferenceKind::Other; | ||
104 | |||
105 | if let TokenAtOffset::Between(ref left, ref right) = token { | ||
106 | if (right.kind() == SyntaxKind::L_CURLY || right.kind() == SyntaxKind::L_PAREN) | ||
107 | && left.kind() != SyntaxKind::IDENT | ||
108 | { | ||
109 | position = FilePosition { offset: left.text_range().start(), ..position }; | ||
110 | search_kind = ReferenceKind::StructLiteral; | ||
111 | } | ||
112 | } | ||
113 | |||
79 | let RangeInfo { range, info: (name, def) } = find_name(db, &syntax, position)?; | 114 | let RangeInfo { range, info: (name, def) } = find_name(db, &syntax, position)?; |
80 | 115 | ||
81 | let declaration = match def.kind { | 116 | let declaration = match def.kind { |
@@ -96,9 +131,15 @@ pub(crate) fn find_all_refs( | |||
96 | } | 131 | } |
97 | }; | 132 | }; |
98 | 133 | ||
99 | let references = process_definition(db, def, name, search_scope); | 134 | let references = process_definition(db, def, name, search_scope) |
135 | .into_iter() | ||
136 | .filter(|r| search_kind == ReferenceKind::Other || search_kind == r.kind) | ||
137 | .collect(); | ||
100 | 138 | ||
101 | Some(RangeInfo::new(range, ReferenceSearchResult { declaration, references })) | 139 | Some(RangeInfo::new( |
140 | range, | ||
141 | ReferenceSearchResult { declaration, references, declaration_kind: ReferenceKind::Other }, | ||
142 | )) | ||
102 | } | 143 | } |
103 | 144 | ||
104 | fn find_name<'a>( | 145 | fn find_name<'a>( |
@@ -122,7 +163,7 @@ fn process_definition( | |||
122 | def: NameDefinition, | 163 | def: NameDefinition, |
123 | name: String, | 164 | name: String, |
124 | scope: SearchScope, | 165 | scope: SearchScope, |
125 | ) -> Vec<FileRange> { | 166 | ) -> Vec<Reference> { |
126 | let _p = profile("process_definition"); | 167 | let _p = profile("process_definition"); |
127 | 168 | ||
128 | let pat = name.as_str(); | 169 | let pat = name.as_str(); |
@@ -146,7 +187,21 @@ fn process_definition( | |||
146 | } | 187 | } |
147 | if let Some(d) = classify_name_ref(db, InFile::new(file_id.into(), &name_ref)) { | 188 | if let Some(d) = classify_name_ref(db, InFile::new(file_id.into(), &name_ref)) { |
148 | if d == def { | 189 | if d == def { |
149 | refs.push(FileRange { file_id, range }); | 190 | let kind = if name_ref |
191 | .syntax() | ||
192 | .ancestors() | ||
193 | .find_map(ast::RecordLit::cast) | ||
194 | .and_then(|l| l.path()) | ||
195 | .and_then(|p| p.segment()) | ||
196 | .and_then(|p| p.name_ref()) | ||
197 | .map(|n| n == name_ref) | ||
198 | .unwrap_or(false) | ||
199 | { | ||
200 | ReferenceKind::StructLiteral | ||
201 | } else { | ||
202 | ReferenceKind::Other | ||
203 | }; | ||
204 | refs.push(Reference { file_range: FileRange { file_id, range }, kind }); | ||
150 | } | 205 | } |
151 | } | 206 | } |
152 | } | 207 | } |
@@ -159,10 +214,33 @@ fn process_definition( | |||
159 | mod tests { | 214 | mod tests { |
160 | use crate::{ | 215 | use crate::{ |
161 | mock_analysis::{analysis_and_position, single_file_with_position, MockAnalysis}, | 216 | mock_analysis::{analysis_and_position, single_file_with_position, MockAnalysis}, |
162 | ReferenceSearchResult, SearchScope, | 217 | Reference, ReferenceKind, ReferenceSearchResult, SearchScope, |
163 | }; | 218 | }; |
164 | 219 | ||
165 | #[test] | 220 | #[test] |
221 | fn test_struct_literal() { | ||
222 | let code = r#" | ||
223 | struct Foo <|>{ | ||
224 | a: i32, | ||
225 | } | ||
226 | impl Foo { | ||
227 | fn f() -> i32 { 42 } | ||
228 | } | ||
229 | fn main() { | ||
230 | let f: Foo; | ||
231 | f = Foo {a: Foo::f()}; | ||
232 | }"#; | ||
233 | |||
234 | let refs = get_all_refs(code); | ||
235 | check_result( | ||
236 | refs, | ||
237 | "Foo STRUCT_DEF FileId(1) [5; 39) [12; 15)", | ||
238 | ReferenceKind::Other, | ||
239 | &["FileId(1) [142; 145) StructLiteral"], | ||
240 | ); | ||
241 | } | ||
242 | |||
243 | #[test] | ||
166 | fn test_find_all_refs_for_local() { | 244 | fn test_find_all_refs_for_local() { |
167 | let code = r#" | 245 | let code = r#" |
168 | fn main() { | 246 | fn main() { |
@@ -178,7 +256,17 @@ mod tests { | |||
178 | }"#; | 256 | }"#; |
179 | 257 | ||
180 | let refs = get_all_refs(code); | 258 | let refs = get_all_refs(code); |
181 | assert_eq!(refs.len(), 5); | 259 | check_result( |
260 | refs, | ||
261 | "i BIND_PAT FileId(1) [33; 34)", | ||
262 | ReferenceKind::Other, | ||
263 | &[ | ||
264 | "FileId(1) [67; 68) Other", | ||
265 | "FileId(1) [71; 72) Other", | ||
266 | "FileId(1) [101; 102) Other", | ||
267 | "FileId(1) [127; 128) Other", | ||
268 | ], | ||
269 | ); | ||
182 | } | 270 | } |
183 | 271 | ||
184 | #[test] | 272 | #[test] |
@@ -189,7 +277,12 @@ mod tests { | |||
189 | }"#; | 277 | }"#; |
190 | 278 | ||
191 | let refs = get_all_refs(code); | 279 | let refs = get_all_refs(code); |
192 | assert_eq!(refs.len(), 2); | 280 | check_result( |
281 | refs, | ||
282 | "i BIND_PAT FileId(1) [12; 13)", | ||
283 | ReferenceKind::Other, | ||
284 | &["FileId(1) [38; 39) Other"], | ||
285 | ); | ||
193 | } | 286 | } |
194 | 287 | ||
195 | #[test] | 288 | #[test] |
@@ -200,7 +293,12 @@ mod tests { | |||
200 | }"#; | 293 | }"#; |
201 | 294 | ||
202 | let refs = get_all_refs(code); | 295 | let refs = get_all_refs(code); |
203 | assert_eq!(refs.len(), 2); | 296 | check_result( |
297 | refs, | ||
298 | "i BIND_PAT FileId(1) [12; 13)", | ||
299 | ReferenceKind::Other, | ||
300 | &["FileId(1) [38; 39) Other"], | ||
301 | ); | ||
204 | } | 302 | } |
205 | 303 | ||
206 | #[test] | 304 | #[test] |
@@ -217,7 +315,12 @@ mod tests { | |||
217 | "#; | 315 | "#; |
218 | 316 | ||
219 | let refs = get_all_refs(code); | 317 | let refs = get_all_refs(code); |
220 | assert_eq!(refs.len(), 2); | 318 | check_result( |
319 | refs, | ||
320 | "spam RECORD_FIELD_DEF FileId(1) [66; 79) [70; 74)", | ||
321 | ReferenceKind::Other, | ||
322 | &["FileId(1) [152; 156) Other"], | ||
323 | ); | ||
221 | } | 324 | } |
222 | 325 | ||
223 | #[test] | 326 | #[test] |
@@ -231,7 +334,7 @@ mod tests { | |||
231 | "#; | 334 | "#; |
232 | 335 | ||
233 | let refs = get_all_refs(code); | 336 | let refs = get_all_refs(code); |
234 | assert_eq!(refs.len(), 1); | 337 | check_result(refs, "f FN_DEF FileId(1) [88; 104) [91; 92)", ReferenceKind::Other, &[]); |
235 | } | 338 | } |
236 | 339 | ||
237 | #[test] | 340 | #[test] |
@@ -246,7 +349,7 @@ mod tests { | |||
246 | "#; | 349 | "#; |
247 | 350 | ||
248 | let refs = get_all_refs(code); | 351 | let refs = get_all_refs(code); |
249 | assert_eq!(refs.len(), 1); | 352 | check_result(refs, "B ENUM_VARIANT FileId(1) [83; 84) [83; 84)", ReferenceKind::Other, &[]); |
250 | } | 353 | } |
251 | 354 | ||
252 | #[test] | 355 | #[test] |
@@ -285,7 +388,12 @@ mod tests { | |||
285 | 388 | ||
286 | let (analysis, pos) = analysis_and_position(code); | 389 | let (analysis, pos) = analysis_and_position(code); |
287 | let refs = analysis.find_all_refs(pos, None).unwrap().unwrap(); | 390 | let refs = analysis.find_all_refs(pos, None).unwrap().unwrap(); |
288 | assert_eq!(refs.len(), 3); | 391 | check_result( |
392 | refs, | ||
393 | "Foo STRUCT_DEF FileId(2) [16; 50) [27; 30)", | ||
394 | ReferenceKind::Other, | ||
395 | &["FileId(1) [52; 55) StructLiteral", "FileId(3) [77; 80) StructLiteral"], | ||
396 | ); | ||
289 | } | 397 | } |
290 | 398 | ||
291 | // `mod foo;` is not in the results because `foo` is an `ast::Name`. | 399 | // `mod foo;` is not in the results because `foo` is an `ast::Name`. |
@@ -311,7 +419,12 @@ mod tests { | |||
311 | 419 | ||
312 | let (analysis, pos) = analysis_and_position(code); | 420 | let (analysis, pos) = analysis_and_position(code); |
313 | let refs = analysis.find_all_refs(pos, None).unwrap().unwrap(); | 421 | let refs = analysis.find_all_refs(pos, None).unwrap().unwrap(); |
314 | assert_eq!(refs.len(), 2); | 422 | check_result( |
423 | refs, | ||
424 | "foo SOURCE_FILE FileId(2) [0; 35)", | ||
425 | ReferenceKind::Other, | ||
426 | &["FileId(1) [13; 16) Other"], | ||
427 | ); | ||
315 | } | 428 | } |
316 | 429 | ||
317 | #[test] | 430 | #[test] |
@@ -336,7 +449,12 @@ mod tests { | |||
336 | 449 | ||
337 | let (analysis, pos) = analysis_and_position(code); | 450 | let (analysis, pos) = analysis_and_position(code); |
338 | let refs = analysis.find_all_refs(pos, None).unwrap().unwrap(); | 451 | let refs = analysis.find_all_refs(pos, None).unwrap().unwrap(); |
339 | assert_eq!(refs.len(), 3); | 452 | check_result( |
453 | refs, | ||
454 | "Foo STRUCT_DEF FileId(3) [0; 41) [18; 21)", | ||
455 | ReferenceKind::Other, | ||
456 | &["FileId(2) [20; 23) Other", "FileId(2) [46; 49) StructLiteral"], | ||
457 | ); | ||
340 | } | 458 | } |
341 | 459 | ||
342 | #[test] | 460 | #[test] |
@@ -360,11 +478,21 @@ mod tests { | |||
360 | let analysis = mock.analysis(); | 478 | let analysis = mock.analysis(); |
361 | 479 | ||
362 | let refs = analysis.find_all_refs(pos, None).unwrap().unwrap(); | 480 | let refs = analysis.find_all_refs(pos, None).unwrap().unwrap(); |
363 | assert_eq!(refs.len(), 3); | 481 | check_result( |
482 | refs, | ||
483 | "quux FN_DEF FileId(1) [18; 34) [25; 29)", | ||
484 | ReferenceKind::Other, | ||
485 | &["FileId(2) [16; 20) Other", "FileId(3) [16; 20) Other"], | ||
486 | ); | ||
364 | 487 | ||
365 | let refs = | 488 | let refs = |
366 | analysis.find_all_refs(pos, Some(SearchScope::single_file(bar))).unwrap().unwrap(); | 489 | analysis.find_all_refs(pos, Some(SearchScope::single_file(bar))).unwrap().unwrap(); |
367 | assert_eq!(refs.len(), 2); | 490 | check_result( |
491 | refs, | ||
492 | "quux FN_DEF FileId(1) [18; 34) [25; 29)", | ||
493 | ReferenceKind::Other, | ||
494 | &["FileId(3) [16; 20) Other"], | ||
495 | ); | ||
368 | } | 496 | } |
369 | 497 | ||
370 | #[test] | 498 | #[test] |
@@ -379,11 +507,40 @@ mod tests { | |||
379 | }"#; | 507 | }"#; |
380 | 508 | ||
381 | let refs = get_all_refs(code); | 509 | let refs = get_all_refs(code); |
382 | assert_eq!(refs.len(), 3); | 510 | check_result( |
511 | refs, | ||
512 | "m1 MACRO_CALL FileId(1) [9; 63) [46; 48)", | ||
513 | ReferenceKind::Other, | ||
514 | &["FileId(1) [96; 98) Other", "FileId(1) [114; 116) Other"], | ||
515 | ); | ||
383 | } | 516 | } |
384 | 517 | ||
385 | fn get_all_refs(text: &str) -> ReferenceSearchResult { | 518 | fn get_all_refs(text: &str) -> ReferenceSearchResult { |
386 | let (analysis, position) = single_file_with_position(text); | 519 | let (analysis, position) = single_file_with_position(text); |
387 | analysis.find_all_refs(position, None).unwrap().unwrap() | 520 | analysis.find_all_refs(position, None).unwrap().unwrap() |
388 | } | 521 | } |
522 | |||
523 | fn check_result( | ||
524 | res: ReferenceSearchResult, | ||
525 | expected_decl: &str, | ||
526 | decl_kind: ReferenceKind, | ||
527 | expected_refs: &[&str], | ||
528 | ) { | ||
529 | res.declaration().assert_match(expected_decl); | ||
530 | assert_eq!(res.declaration_kind, decl_kind); | ||
531 | |||
532 | assert_eq!(res.references.len(), expected_refs.len()); | ||
533 | res.references().iter().enumerate().for_each(|(i, r)| r.assert_match(expected_refs[i])); | ||
534 | } | ||
535 | |||
536 | impl Reference { | ||
537 | fn debug_render(&self) -> String { | ||
538 | format!("{:?} {:?} {:?}", self.file_range.file_id, self.file_range.range, self.kind) | ||
539 | } | ||
540 | |||
541 | fn assert_match(&self, expected: &str) { | ||
542 | let actual = self.debug_render(); | ||
543 | test_utils::assert_eq_text!(expected.trim(), actual.trim(),); | ||
544 | } | ||
545 | } | ||
389 | } | 546 | } |