diff options
Diffstat (limited to 'crates/ra_ide/src/references.rs')
-rw-r--r-- | crates/ra_ide/src/references.rs | 695 |
1 files changed, 0 insertions, 695 deletions
diff --git a/crates/ra_ide/src/references.rs b/crates/ra_ide/src/references.rs deleted file mode 100644 index 453985de3..000000000 --- a/crates/ra_ide/src/references.rs +++ /dev/null | |||
@@ -1,695 +0,0 @@ | |||
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 a `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 futher 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. | ||
11 | |||
12 | mod rename; | ||
13 | |||
14 | use hir::Semantics; | ||
15 | use ra_ide_db::{ | ||
16 | defs::{classify_name, classify_name_ref, Definition}, | ||
17 | search::SearchScope, | ||
18 | RootDatabase, | ||
19 | }; | ||
20 | use ra_prof::profile; | ||
21 | use ra_syntax::{ | ||
22 | algo::find_node_at_offset, | ||
23 | ast::{self, NameOwner}, | ||
24 | AstNode, SyntaxKind, SyntaxNode, TextRange, TokenAtOffset, | ||
25 | }; | ||
26 | |||
27 | use crate::{display::TryToNav, FilePosition, FileRange, NavigationTarget, RangeInfo}; | ||
28 | |||
29 | pub(crate) use self::rename::rename; | ||
30 | |||
31 | pub use ra_ide_db::search::{Reference, ReferenceAccess, ReferenceKind}; | ||
32 | |||
33 | #[derive(Debug, Clone)] | ||
34 | pub struct ReferenceSearchResult { | ||
35 | declaration: Declaration, | ||
36 | references: Vec<Reference>, | ||
37 | } | ||
38 | |||
39 | #[derive(Debug, Clone)] | ||
40 | pub struct Declaration { | ||
41 | pub nav: NavigationTarget, | ||
42 | pub kind: ReferenceKind, | ||
43 | pub access: Option<ReferenceAccess>, | ||
44 | } | ||
45 | |||
46 | impl ReferenceSearchResult { | ||
47 | pub fn declaration(&self) -> &Declaration { | ||
48 | &self.declaration | ||
49 | } | ||
50 | |||
51 | pub fn decl_target(&self) -> &NavigationTarget { | ||
52 | &self.declaration.nav | ||
53 | } | ||
54 | |||
55 | pub fn references(&self) -> &[Reference] { | ||
56 | &self.references | ||
57 | } | ||
58 | |||
59 | /// Total number of references | ||
60 | /// At least 1 since all valid references should | ||
61 | /// Have a declaration | ||
62 | pub fn len(&self) -> usize { | ||
63 | self.references.len() + 1 | ||
64 | } | ||
65 | } | ||
66 | |||
67 | // allow turning ReferenceSearchResult into an iterator | ||
68 | // over References | ||
69 | impl IntoIterator for ReferenceSearchResult { | ||
70 | type Item = Reference; | ||
71 | type IntoIter = std::vec::IntoIter<Reference>; | ||
72 | |||
73 | fn into_iter(mut self) -> Self::IntoIter { | ||
74 | let mut v = Vec::with_capacity(self.len()); | ||
75 | v.push(Reference { | ||
76 | file_range: FileRange { | ||
77 | file_id: self.declaration.nav.file_id, | ||
78 | range: self.declaration.nav.focus_or_full_range(), | ||
79 | }, | ||
80 | kind: self.declaration.kind, | ||
81 | access: self.declaration.access, | ||
82 | }); | ||
83 | v.append(&mut self.references); | ||
84 | v.into_iter() | ||
85 | } | ||
86 | } | ||
87 | |||
88 | pub(crate) fn find_all_refs( | ||
89 | sema: &Semantics<RootDatabase>, | ||
90 | position: FilePosition, | ||
91 | search_scope: Option<SearchScope>, | ||
92 | ) -> Option<RangeInfo<ReferenceSearchResult>> { | ||
93 | let _p = profile("find_all_refs"); | ||
94 | let syntax = sema.parse(position.file_id).syntax().clone(); | ||
95 | |||
96 | let (opt_name, search_kind) = if let Some(name) = | ||
97 | get_struct_def_name_for_struct_literal_search(&sema, &syntax, position) | ||
98 | { | ||
99 | (Some(name), ReferenceKind::StructLiteral) | ||
100 | } else { | ||
101 | ( | ||
102 | sema.find_node_at_offset_with_descend::<ast::Name>(&syntax, position.offset), | ||
103 | ReferenceKind::Other, | ||
104 | ) | ||
105 | }; | ||
106 | |||
107 | let RangeInfo { range, info: def } = find_name(&sema, &syntax, position, opt_name)?; | ||
108 | |||
109 | let references = def | ||
110 | .find_usages(sema, search_scope) | ||
111 | .into_iter() | ||
112 | .filter(|r| search_kind == ReferenceKind::Other || search_kind == r.kind) | ||
113 | .collect(); | ||
114 | |||
115 | let decl_range = def.try_to_nav(sema.db)?.focus_or_full_range(); | ||
116 | |||
117 | let declaration = Declaration { | ||
118 | nav: def.try_to_nav(sema.db)?, | ||
119 | kind: ReferenceKind::Other, | ||
120 | access: decl_access(&def, &syntax, decl_range), | ||
121 | }; | ||
122 | |||
123 | Some(RangeInfo::new(range, ReferenceSearchResult { declaration, references })) | ||
124 | } | ||
125 | |||
126 | fn find_name( | ||
127 | sema: &Semantics<RootDatabase>, | ||
128 | syntax: &SyntaxNode, | ||
129 | position: FilePosition, | ||
130 | opt_name: Option<ast::Name>, | ||
131 | ) -> Option<RangeInfo<Definition>> { | ||
132 | if let Some(name) = opt_name { | ||
133 | let def = classify_name(sema, &name)?.definition(sema.db); | ||
134 | let range = name.syntax().text_range(); | ||
135 | return Some(RangeInfo::new(range, def)); | ||
136 | } | ||
137 | let name_ref = | ||
138 | sema.find_node_at_offset_with_descend::<ast::NameRef>(&syntax, position.offset)?; | ||
139 | let def = classify_name_ref(sema, &name_ref)?.definition(sema.db); | ||
140 | let range = name_ref.syntax().text_range(); | ||
141 | Some(RangeInfo::new(range, def)) | ||
142 | } | ||
143 | |||
144 | fn decl_access(def: &Definition, syntax: &SyntaxNode, range: TextRange) -> Option<ReferenceAccess> { | ||
145 | match def { | ||
146 | Definition::Local(_) | Definition::Field(_) => {} | ||
147 | _ => return None, | ||
148 | }; | ||
149 | |||
150 | let stmt = find_node_at_offset::<ast::LetStmt>(syntax, range.start())?; | ||
151 | if stmt.initializer().is_some() { | ||
152 | let pat = stmt.pat()?; | ||
153 | if let ast::Pat::IdentPat(it) = pat { | ||
154 | if it.mut_token().is_some() { | ||
155 | return Some(ReferenceAccess::Write); | ||
156 | } | ||
157 | } | ||
158 | } | ||
159 | |||
160 | None | ||
161 | } | ||
162 | |||
163 | fn get_struct_def_name_for_struct_literal_search( | ||
164 | sema: &Semantics<RootDatabase>, | ||
165 | syntax: &SyntaxNode, | ||
166 | position: FilePosition, | ||
167 | ) -> Option<ast::Name> { | ||
168 | if let TokenAtOffset::Between(ref left, ref right) = syntax.token_at_offset(position.offset) { | ||
169 | if right.kind() != SyntaxKind::L_CURLY && right.kind() != SyntaxKind::L_PAREN { | ||
170 | return None; | ||
171 | } | ||
172 | if let Some(name) = | ||
173 | sema.find_node_at_offset_with_descend::<ast::Name>(&syntax, left.text_range().start()) | ||
174 | { | ||
175 | return name.syntax().ancestors().find_map(ast::Struct::cast).and_then(|l| l.name()); | ||
176 | } | ||
177 | if sema | ||
178 | .find_node_at_offset_with_descend::<ast::GenericParamList>( | ||
179 | &syntax, | ||
180 | left.text_range().start(), | ||
181 | ) | ||
182 | .is_some() | ||
183 | { | ||
184 | return left.ancestors().find_map(ast::Struct::cast).and_then(|l| l.name()); | ||
185 | } | ||
186 | } | ||
187 | None | ||
188 | } | ||
189 | |||
190 | #[cfg(test)] | ||
191 | mod tests { | ||
192 | use crate::{ | ||
193 | mock_analysis::{analysis_and_position, MockAnalysis}, | ||
194 | Declaration, Reference, ReferenceSearchResult, SearchScope, | ||
195 | }; | ||
196 | |||
197 | #[test] | ||
198 | fn test_struct_literal_after_space() { | ||
199 | let refs = get_all_refs( | ||
200 | r#" | ||
201 | struct Foo <|>{ | ||
202 | a: i32, | ||
203 | } | ||
204 | impl Foo { | ||
205 | fn f() -> i32 { 42 } | ||
206 | } | ||
207 | fn main() { | ||
208 | let f: Foo; | ||
209 | f = Foo {a: Foo::f()}; | ||
210 | } | ||
211 | "#, | ||
212 | ); | ||
213 | check_result( | ||
214 | refs, | ||
215 | "Foo STRUCT FileId(1) 0..26 7..10 Other", | ||
216 | &["FileId(1) 101..104 StructLiteral"], | ||
217 | ); | ||
218 | } | ||
219 | |||
220 | #[test] | ||
221 | fn test_struct_literal_before_space() { | ||
222 | let refs = get_all_refs( | ||
223 | r#" | ||
224 | struct Foo<|> {} | ||
225 | fn main() { | ||
226 | let f: Foo; | ||
227 | f = Foo {}; | ||
228 | } | ||
229 | "#, | ||
230 | ); | ||
231 | check_result( | ||
232 | refs, | ||
233 | "Foo STRUCT FileId(1) 0..13 7..10 Other", | ||
234 | &["FileId(1) 41..44 Other", "FileId(1) 54..57 StructLiteral"], | ||
235 | ); | ||
236 | } | ||
237 | |||
238 | #[test] | ||
239 | fn test_struct_literal_with_generic_type() { | ||
240 | let refs = get_all_refs( | ||
241 | r#" | ||
242 | struct Foo<T> <|>{} | ||
243 | fn main() { | ||
244 | let f: Foo::<i32>; | ||
245 | f = Foo {}; | ||
246 | } | ||
247 | "#, | ||
248 | ); | ||
249 | check_result( | ||
250 | refs, | ||
251 | "Foo STRUCT FileId(1) 0..16 7..10 Other", | ||
252 | &["FileId(1) 64..67 StructLiteral"], | ||
253 | ); | ||
254 | } | ||
255 | |||
256 | #[test] | ||
257 | fn test_struct_literal_for_tuple() { | ||
258 | let refs = get_all_refs( | ||
259 | r#" | ||
260 | struct Foo<|>(i32); | ||
261 | |||
262 | fn main() { | ||
263 | let f: Foo; | ||
264 | f = Foo(1); | ||
265 | } | ||
266 | "#, | ||
267 | ); | ||
268 | check_result( | ||
269 | refs, | ||
270 | "Foo STRUCT FileId(1) 0..16 7..10 Other", | ||
271 | &["FileId(1) 54..57 StructLiteral"], | ||
272 | ); | ||
273 | } | ||
274 | |||
275 | #[test] | ||
276 | fn test_find_all_refs_for_local() { | ||
277 | let refs = get_all_refs( | ||
278 | r#" | ||
279 | fn main() { | ||
280 | let mut i = 1; | ||
281 | let j = 1; | ||
282 | i = i<|> + j; | ||
283 | |||
284 | { | ||
285 | i = 0; | ||
286 | } | ||
287 | |||
288 | i = 5; | ||
289 | }"#, | ||
290 | ); | ||
291 | check_result( | ||
292 | refs, | ||
293 | "i IDENT_PAT FileId(1) 24..25 Other Write", | ||
294 | &[ | ||
295 | "FileId(1) 50..51 Other Write", | ||
296 | "FileId(1) 54..55 Other Read", | ||
297 | "FileId(1) 76..77 Other Write", | ||
298 | "FileId(1) 94..95 Other Write", | ||
299 | ], | ||
300 | ); | ||
301 | } | ||
302 | |||
303 | #[test] | ||
304 | fn search_filters_by_range() { | ||
305 | let refs = get_all_refs( | ||
306 | r#" | ||
307 | fn foo() { | ||
308 | let spam<|> = 92; | ||
309 | spam + spam | ||
310 | } | ||
311 | fn bar() { | ||
312 | let spam = 92; | ||
313 | spam + spam | ||
314 | } | ||
315 | "#, | ||
316 | ); | ||
317 | check_result( | ||
318 | refs, | ||
319 | "spam IDENT_PAT FileId(1) 19..23 Other", | ||
320 | &["FileId(1) 34..38 Other Read", "FileId(1) 41..45 Other Read"], | ||
321 | ); | ||
322 | } | ||
323 | |||
324 | #[test] | ||
325 | fn test_find_all_refs_for_param_inside() { | ||
326 | let refs = get_all_refs( | ||
327 | r#" | ||
328 | fn foo(i : u32) -> u32 { | ||
329 | i<|> | ||
330 | } | ||
331 | "#, | ||
332 | ); | ||
333 | check_result(refs, "i IDENT_PAT FileId(1) 7..8 Other", &["FileId(1) 29..30 Other Read"]); | ||
334 | } | ||
335 | |||
336 | #[test] | ||
337 | fn test_find_all_refs_for_fn_param() { | ||
338 | let refs = get_all_refs( | ||
339 | r#" | ||
340 | fn foo(i<|> : u32) -> u32 { | ||
341 | i | ||
342 | } | ||
343 | "#, | ||
344 | ); | ||
345 | check_result(refs, "i IDENT_PAT FileId(1) 7..8 Other", &["FileId(1) 29..30 Other Read"]); | ||
346 | } | ||
347 | |||
348 | #[test] | ||
349 | fn test_find_all_refs_field_name() { | ||
350 | let refs = get_all_refs( | ||
351 | r#" | ||
352 | //- /lib.rs | ||
353 | struct Foo { | ||
354 | pub spam<|>: u32, | ||
355 | } | ||
356 | |||
357 | fn main(s: Foo) { | ||
358 | let f = s.spam; | ||
359 | } | ||
360 | "#, | ||
361 | ); | ||
362 | check_result( | ||
363 | refs, | ||
364 | "spam RECORD_FIELD FileId(1) 17..30 21..25 Other", | ||
365 | &["FileId(1) 67..71 Other Read"], | ||
366 | ); | ||
367 | } | ||
368 | |||
369 | #[test] | ||
370 | fn test_find_all_refs_impl_item_name() { | ||
371 | let refs = get_all_refs( | ||
372 | r#" | ||
373 | struct Foo; | ||
374 | impl Foo { | ||
375 | fn f<|>(&self) { } | ||
376 | } | ||
377 | "#, | ||
378 | ); | ||
379 | check_result(refs, "f FN FileId(1) 27..43 30..31 Other", &[]); | ||
380 | } | ||
381 | |||
382 | #[test] | ||
383 | fn test_find_all_refs_enum_var_name() { | ||
384 | let refs = get_all_refs( | ||
385 | r#" | ||
386 | enum Foo { | ||
387 | A, | ||
388 | B<|>, | ||
389 | C, | ||
390 | } | ||
391 | "#, | ||
392 | ); | ||
393 | check_result(refs, "B VARIANT FileId(1) 22..23 22..23 Other", &[]); | ||
394 | } | ||
395 | |||
396 | #[test] | ||
397 | fn test_find_all_refs_two_modules() { | ||
398 | let (analysis, pos) = analysis_and_position( | ||
399 | r#" | ||
400 | //- /lib.rs | ||
401 | pub mod foo; | ||
402 | pub mod bar; | ||
403 | |||
404 | fn f() { | ||
405 | let i = foo::Foo { n: 5 }; | ||
406 | } | ||
407 | |||
408 | //- /foo.rs | ||
409 | use crate::bar; | ||
410 | |||
411 | pub struct Foo { | ||
412 | pub n: u32, | ||
413 | } | ||
414 | |||
415 | fn f() { | ||
416 | let i = bar::Bar { n: 5 }; | ||
417 | } | ||
418 | |||
419 | //- /bar.rs | ||
420 | use crate::foo; | ||
421 | |||
422 | pub struct Bar { | ||
423 | pub n: u32, | ||
424 | } | ||
425 | |||
426 | fn f() { | ||
427 | let i = foo::Foo<|> { n: 5 }; | ||
428 | } | ||
429 | "#, | ||
430 | ); | ||
431 | let refs = analysis.find_all_refs(pos, None).unwrap().unwrap(); | ||
432 | check_result( | ||
433 | refs, | ||
434 | "Foo STRUCT FileId(2) 17..51 28..31 Other", | ||
435 | &["FileId(1) 53..56 StructLiteral", "FileId(3) 79..82 StructLiteral"], | ||
436 | ); | ||
437 | } | ||
438 | |||
439 | // `mod foo;` is not in the results because `foo` is an `ast::Name`. | ||
440 | // So, there are two references: the first one is a definition of the `foo` module, | ||
441 | // which is the whole `foo.rs`, and the second one is in `use foo::Foo`. | ||
442 | #[test] | ||
443 | fn test_find_all_refs_decl_module() { | ||
444 | let (analysis, pos) = analysis_and_position( | ||
445 | r#" | ||
446 | //- /lib.rs | ||
447 | mod foo<|>; | ||
448 | |||
449 | use foo::Foo; | ||
450 | |||
451 | fn f() { | ||
452 | let i = Foo { n: 5 }; | ||
453 | } | ||
454 | |||
455 | //- /foo.rs | ||
456 | pub struct Foo { | ||
457 | pub n: u32, | ||
458 | } | ||
459 | "#, | ||
460 | ); | ||
461 | let refs = analysis.find_all_refs(pos, None).unwrap().unwrap(); | ||
462 | check_result(refs, "foo SOURCE_FILE FileId(2) 0..35 Other", &["FileId(1) 14..17 Other"]); | ||
463 | } | ||
464 | |||
465 | #[test] | ||
466 | fn test_find_all_refs_super_mod_vis() { | ||
467 | let (analysis, pos) = analysis_and_position( | ||
468 | r#" | ||
469 | //- /lib.rs | ||
470 | mod foo; | ||
471 | |||
472 | //- /foo.rs | ||
473 | mod some; | ||
474 | use some::Foo; | ||
475 | |||
476 | fn f() { | ||
477 | let i = Foo { n: 5 }; | ||
478 | } | ||
479 | |||
480 | //- /foo/some.rs | ||
481 | pub(super) struct Foo<|> { | ||
482 | pub n: u32, | ||
483 | } | ||
484 | "#, | ||
485 | ); | ||
486 | let refs = analysis.find_all_refs(pos, None).unwrap().unwrap(); | ||
487 | check_result( | ||
488 | refs, | ||
489 | "Foo STRUCT FileId(3) 0..41 18..21 Other", | ||
490 | &["FileId(2) 20..23 Other", "FileId(2) 47..50 StructLiteral"], | ||
491 | ); | ||
492 | } | ||
493 | |||
494 | #[test] | ||
495 | fn test_find_all_refs_with_scope() { | ||
496 | let code = r#" | ||
497 | //- /lib.rs | ||
498 | mod foo; | ||
499 | mod bar; | ||
500 | |||
501 | pub fn quux<|>() {} | ||
502 | |||
503 | //- /foo.rs | ||
504 | fn f() { super::quux(); } | ||
505 | |||
506 | //- /bar.rs | ||
507 | fn f() { super::quux(); } | ||
508 | "#; | ||
509 | |||
510 | let (mock, pos) = MockAnalysis::with_files_and_position(code); | ||
511 | let bar = mock.id_of("/bar.rs"); | ||
512 | let analysis = mock.analysis(); | ||
513 | |||
514 | let refs = analysis.find_all_refs(pos, None).unwrap().unwrap(); | ||
515 | check_result( | ||
516 | refs, | ||
517 | "quux FN FileId(1) 19..35 26..30 Other", | ||
518 | &["FileId(2) 16..20 StructLiteral", "FileId(3) 16..20 StructLiteral"], | ||
519 | ); | ||
520 | |||
521 | let refs = | ||
522 | analysis.find_all_refs(pos, Some(SearchScope::single_file(bar))).unwrap().unwrap(); | ||
523 | check_result( | ||
524 | refs, | ||
525 | "quux FN FileId(1) 19..35 26..30 Other", | ||
526 | &["FileId(3) 16..20 StructLiteral"], | ||
527 | ); | ||
528 | } | ||
529 | |||
530 | #[test] | ||
531 | fn test_find_all_refs_macro_def() { | ||
532 | let refs = get_all_refs( | ||
533 | r#" | ||
534 | #[macro_export] | ||
535 | macro_rules! m1<|> { () => (()) } | ||
536 | |||
537 | fn foo() { | ||
538 | m1(); | ||
539 | m1(); | ||
540 | } | ||
541 | "#, | ||
542 | ); | ||
543 | check_result( | ||
544 | refs, | ||
545 | "m1 MACRO_CALL FileId(1) 0..46 29..31 Other", | ||
546 | &["FileId(1) 63..65 StructLiteral", "FileId(1) 73..75 StructLiteral"], | ||
547 | ); | ||
548 | } | ||
549 | |||
550 | #[test] | ||
551 | fn test_basic_highlight_read_write() { | ||
552 | let refs = get_all_refs( | ||
553 | r#" | ||
554 | fn foo() { | ||
555 | let mut i<|> = 0; | ||
556 | i = i + 1; | ||
557 | } | ||
558 | "#, | ||
559 | ); | ||
560 | check_result( | ||
561 | refs, | ||
562 | "i IDENT_PAT FileId(1) 23..24 Other Write", | ||
563 | &["FileId(1) 34..35 Other Write", "FileId(1) 38..39 Other Read"], | ||
564 | ); | ||
565 | } | ||
566 | |||
567 | #[test] | ||
568 | fn test_basic_highlight_field_read_write() { | ||
569 | let refs = get_all_refs( | ||
570 | r#" | ||
571 | struct S { | ||
572 | f: u32, | ||
573 | } | ||
574 | |||
575 | fn foo() { | ||
576 | let mut s = S{f: 0}; | ||
577 | s.f<|> = 0; | ||
578 | } | ||
579 | "#, | ||
580 | ); | ||
581 | check_result( | ||
582 | refs, | ||
583 | "f RECORD_FIELD FileId(1) 15..21 15..16 Other", | ||
584 | &["FileId(1) 55..56 Other Read", "FileId(1) 68..69 Other Write"], | ||
585 | ); | ||
586 | } | ||
587 | |||
588 | #[test] | ||
589 | fn test_basic_highlight_decl_no_write() { | ||
590 | let refs = get_all_refs( | ||
591 | r#" | ||
592 | fn foo() { | ||
593 | let i<|>; | ||
594 | i = 1; | ||
595 | } | ||
596 | "#, | ||
597 | ); | ||
598 | check_result(refs, "i IDENT_PAT FileId(1) 19..20 Other", &["FileId(1) 26..27 Other Write"]); | ||
599 | } | ||
600 | |||
601 | #[test] | ||
602 | fn test_find_struct_function_refs_outside_module() { | ||
603 | let refs = get_all_refs( | ||
604 | r#" | ||
605 | mod foo { | ||
606 | pub struct Foo; | ||
607 | |||
608 | impl Foo { | ||
609 | pub fn new<|>() -> Foo { | ||
610 | Foo | ||
611 | } | ||
612 | } | ||
613 | } | ||
614 | |||
615 | fn main() { | ||
616 | let _f = foo::Foo::new(); | ||
617 | } | ||
618 | "#, | ||
619 | ); | ||
620 | check_result( | ||
621 | refs, | ||
622 | "new FN FileId(1) 54..101 61..64 Other", | ||
623 | &["FileId(1) 146..149 StructLiteral"], | ||
624 | ); | ||
625 | } | ||
626 | |||
627 | #[test] | ||
628 | fn test_find_all_refs_nested_module() { | ||
629 | let code = r#" | ||
630 | //- /lib.rs | ||
631 | mod foo { | ||
632 | mod bar; | ||
633 | } | ||
634 | |||
635 | fn f<|>() {} | ||
636 | |||
637 | //- /foo/bar.rs | ||
638 | use crate::f; | ||
639 | |||
640 | fn g() { | ||
641 | f(); | ||
642 | } | ||
643 | "#; | ||
644 | |||
645 | let (analysis, pos) = analysis_and_position(code); | ||
646 | let refs = analysis.find_all_refs(pos, None).unwrap().unwrap(); | ||
647 | check_result( | ||
648 | refs, | ||
649 | "f FN FileId(1) 26..35 29..30 Other", | ||
650 | &["FileId(2) 11..12 Other", "FileId(2) 28..29 StructLiteral"], | ||
651 | ); | ||
652 | } | ||
653 | |||
654 | fn get_all_refs(ra_fixture: &str) -> ReferenceSearchResult { | ||
655 | let (analysis, position) = analysis_and_position(ra_fixture); | ||
656 | analysis.find_all_refs(position, None).unwrap().unwrap() | ||
657 | } | ||
658 | |||
659 | fn check_result(res: ReferenceSearchResult, expected_decl: &str, expected_refs: &[&str]) { | ||
660 | res.declaration().assert_match(expected_decl); | ||
661 | assert_eq!(res.references.len(), expected_refs.len()); | ||
662 | res.references() | ||
663 | .iter() | ||
664 | .enumerate() | ||
665 | .for_each(|(i, r)| ref_assert_match(r, expected_refs[i])); | ||
666 | } | ||
667 | |||
668 | impl Declaration { | ||
669 | fn debug_render(&self) -> String { | ||
670 | let mut s = format!("{} {:?}", self.nav.debug_render(), self.kind); | ||
671 | if let Some(access) = self.access { | ||
672 | s.push_str(&format!(" {:?}", access)); | ||
673 | } | ||
674 | s | ||
675 | } | ||
676 | |||
677 | fn assert_match(&self, expected: &str) { | ||
678 | let actual = self.debug_render(); | ||
679 | test_utils::assert_eq_text!(expected.trim(), actual.trim(),); | ||
680 | } | ||
681 | } | ||
682 | |||
683 | fn ref_debug_render(r: &Reference) -> String { | ||
684 | let mut s = format!("{:?} {:?} {:?}", r.file_range.file_id, r.file_range.range, r.kind); | ||
685 | if let Some(access) = r.access { | ||
686 | s.push_str(&format!(" {:?}", access)); | ||
687 | } | ||
688 | s | ||
689 | } | ||
690 | |||
691 | fn ref_assert_match(r: &Reference, expected: &str) { | ||
692 | let actual = ref_debug_render(r); | ||
693 | test_utils::assert_eq_text!(expected.trim(), actual.trim(),); | ||
694 | } | ||
695 | } | ||