diff options
Diffstat (limited to 'crates/ide')
32 files changed, 2752 insertions, 1211 deletions
diff --git a/crates/ide/Cargo.toml b/crates/ide/Cargo.toml index bb28cca4d..f6aaaeda4 100644 --- a/crates/ide/Cargo.toml +++ b/crates/ide/Cargo.toml | |||
@@ -27,9 +27,9 @@ ide_db = { path = "../ide_db", version = "0.0.0" } | |||
27 | cfg = { path = "../cfg", version = "0.0.0" } | 27 | cfg = { path = "../cfg", version = "0.0.0" } |
28 | profile = { path = "../profile", version = "0.0.0" } | 28 | profile = { path = "../profile", version = "0.0.0" } |
29 | test_utils = { path = "../test_utils", version = "0.0.0" } | 29 | test_utils = { path = "../test_utils", version = "0.0.0" } |
30 | assists = { path = "../assists", version = "0.0.0" } | 30 | ide_assists = { path = "../ide_assists", version = "0.0.0" } |
31 | ssr = { path = "../ssr", version = "0.0.0" } | 31 | ide_ssr = { path = "../ide_ssr", version = "0.0.0" } |
32 | completion = { path = "../completion", version = "0.0.0" } | 32 | ide_completion = { path = "../ide_completion", version = "0.0.0" } |
33 | 33 | ||
34 | # ide should depend only on the top-level `hir` package. if you need | 34 | # ide should depend only on the top-level `hir` package. if you need |
35 | # something from some `hir_xxx` subpackage, reexport the API via `hir`. | 35 | # something from some `hir_xxx` subpackage, reexport the API via `hir`. |
diff --git a/crates/ide/src/annotations.rs b/crates/ide/src/annotations.rs new file mode 100644 index 000000000..2e8e82b70 --- /dev/null +++ b/crates/ide/src/annotations.rs | |||
@@ -0,0 +1,937 @@ | |||
1 | use hir::Semantics; | ||
2 | use ide_db::{ | ||
3 | base_db::{FileId, FilePosition, FileRange, SourceDatabase}, | ||
4 | RootDatabase, SymbolKind, | ||
5 | }; | ||
6 | use syntax::TextRange; | ||
7 | |||
8 | use crate::{ | ||
9 | file_structure::file_structure, | ||
10 | fn_references::find_all_methods, | ||
11 | goto_implementation::goto_implementation, | ||
12 | references::find_all_refs, | ||
13 | runnables::{runnables, Runnable}, | ||
14 | NavigationTarget, RunnableKind, | ||
15 | }; | ||
16 | |||
17 | // Feature: Annotations | ||
18 | // | ||
19 | // Provides user with annotations above items for looking up references or impl blocks | ||
20 | // and running/debugging binaries. | ||
21 | #[derive(Debug)] | ||
22 | pub struct Annotation { | ||
23 | pub range: TextRange, | ||
24 | pub kind: AnnotationKind, | ||
25 | } | ||
26 | |||
27 | #[derive(Debug)] | ||
28 | pub enum AnnotationKind { | ||
29 | Runnable { debug: bool, runnable: Runnable }, | ||
30 | HasImpls { position: FilePosition, data: Option<Vec<NavigationTarget>> }, | ||
31 | HasReferences { position: FilePosition, data: Option<Vec<FileRange>> }, | ||
32 | } | ||
33 | |||
34 | pub struct AnnotationConfig { | ||
35 | pub binary_target: bool, | ||
36 | pub annotate_runnables: bool, | ||
37 | pub annotate_impls: bool, | ||
38 | pub annotate_references: bool, | ||
39 | pub annotate_method_references: bool, | ||
40 | pub run: bool, | ||
41 | pub debug: bool, | ||
42 | } | ||
43 | |||
44 | pub(crate) fn annotations( | ||
45 | db: &RootDatabase, | ||
46 | file_id: FileId, | ||
47 | config: AnnotationConfig, | ||
48 | ) -> Vec<Annotation> { | ||
49 | let mut annotations = Vec::default(); | ||
50 | |||
51 | if config.annotate_runnables { | ||
52 | for runnable in runnables(db, file_id) { | ||
53 | if should_skip_runnable(&runnable.kind, config.binary_target) { | ||
54 | continue; | ||
55 | } | ||
56 | |||
57 | let action = runnable.action(); | ||
58 | let range = runnable.nav.full_range; | ||
59 | |||
60 | if config.run { | ||
61 | annotations.push(Annotation { | ||
62 | range, | ||
63 | |||
64 | // FIXME: This one allocates without reason if run is enabled, but debug is disabled | ||
65 | kind: AnnotationKind::Runnable { debug: false, runnable: runnable.clone() }, | ||
66 | }); | ||
67 | } | ||
68 | |||
69 | if action.debugee && config.debug { | ||
70 | annotations.push(Annotation { | ||
71 | range, | ||
72 | kind: AnnotationKind::Runnable { debug: true, runnable }, | ||
73 | }); | ||
74 | } | ||
75 | } | ||
76 | } | ||
77 | |||
78 | file_structure(&db.parse(file_id).tree()) | ||
79 | .into_iter() | ||
80 | .filter(|node| { | ||
81 | matches!( | ||
82 | node.kind, | ||
83 | SymbolKind::Trait | ||
84 | | SymbolKind::Struct | ||
85 | | SymbolKind::Enum | ||
86 | | SymbolKind::Union | ||
87 | | SymbolKind::Const | ||
88 | ) | ||
89 | }) | ||
90 | .for_each(|node| { | ||
91 | if config.annotate_impls && node.kind != SymbolKind::Const { | ||
92 | annotations.push(Annotation { | ||
93 | range: node.node_range, | ||
94 | kind: AnnotationKind::HasImpls { | ||
95 | position: FilePosition { file_id, offset: node.navigation_range.start() }, | ||
96 | data: None, | ||
97 | }, | ||
98 | }); | ||
99 | } | ||
100 | |||
101 | if config.annotate_references { | ||
102 | annotations.push(Annotation { | ||
103 | range: node.node_range, | ||
104 | kind: AnnotationKind::HasReferences { | ||
105 | position: FilePosition { file_id, offset: node.navigation_range.start() }, | ||
106 | data: None, | ||
107 | }, | ||
108 | }); | ||
109 | } | ||
110 | }); | ||
111 | |||
112 | if config.annotate_method_references { | ||
113 | annotations.extend(find_all_methods(db, file_id).into_iter().map(|method| Annotation { | ||
114 | range: method.range, | ||
115 | kind: AnnotationKind::HasReferences { | ||
116 | position: FilePosition { file_id, offset: method.range.start() }, | ||
117 | data: None, | ||
118 | }, | ||
119 | })); | ||
120 | } | ||
121 | |||
122 | annotations | ||
123 | } | ||
124 | |||
125 | pub(crate) fn resolve_annotation(db: &RootDatabase, mut annotation: Annotation) -> Annotation { | ||
126 | match annotation.kind { | ||
127 | AnnotationKind::HasImpls { position, ref mut data } => { | ||
128 | *data = goto_implementation(db, position).map(|range| range.info); | ||
129 | } | ||
130 | AnnotationKind::HasReferences { position, ref mut data } => { | ||
131 | *data = find_all_refs(&Semantics::new(db), position, None).map(|result| { | ||
132 | result | ||
133 | .references | ||
134 | .into_iter() | ||
135 | .map(|(file_id, access)| { | ||
136 | access.into_iter().map(move |(range, _)| FileRange { file_id, range }) | ||
137 | }) | ||
138 | .flatten() | ||
139 | .collect() | ||
140 | }); | ||
141 | } | ||
142 | _ => {} | ||
143 | }; | ||
144 | |||
145 | annotation | ||
146 | } | ||
147 | |||
148 | fn should_skip_runnable(kind: &RunnableKind, binary_target: bool) -> bool { | ||
149 | match kind { | ||
150 | RunnableKind::Bin => !binary_target, | ||
151 | _ => false, | ||
152 | } | ||
153 | } | ||
154 | |||
155 | #[cfg(test)] | ||
156 | mod tests { | ||
157 | use expect_test::{expect, Expect}; | ||
158 | |||
159 | use crate::{fixture, Annotation, AnnotationConfig}; | ||
160 | |||
161 | fn check(ra_fixture: &str, expect: Expect) { | ||
162 | let (analysis, file_id) = fixture::file(ra_fixture); | ||
163 | |||
164 | let annotations: Vec<Annotation> = analysis | ||
165 | .annotations( | ||
166 | file_id, | ||
167 | AnnotationConfig { | ||
168 | binary_target: true, | ||
169 | annotate_runnables: true, | ||
170 | annotate_impls: true, | ||
171 | annotate_references: true, | ||
172 | annotate_method_references: true, | ||
173 | run: true, | ||
174 | debug: true, | ||
175 | }, | ||
176 | ) | ||
177 | .unwrap() | ||
178 | .into_iter() | ||
179 | .map(|annotation| analysis.resolve_annotation(annotation).unwrap()) | ||
180 | .collect(); | ||
181 | |||
182 | expect.assert_debug_eq(&annotations); | ||
183 | } | ||
184 | |||
185 | #[test] | ||
186 | fn const_annotations() { | ||
187 | check( | ||
188 | r#" | ||
189 | const DEMO: i32 = 123; | ||
190 | |||
191 | const UNUSED: i32 = 123; | ||
192 | |||
193 | fn main() { | ||
194 | let hello = DEMO; | ||
195 | } | ||
196 | "#, | ||
197 | expect![[r#" | ||
198 | [ | ||
199 | Annotation { | ||
200 | range: 50..85, | ||
201 | kind: Runnable { | ||
202 | debug: false, | ||
203 | runnable: Runnable { | ||
204 | nav: NavigationTarget { | ||
205 | file_id: FileId( | ||
206 | 0, | ||
207 | ), | ||
208 | full_range: 50..85, | ||
209 | focus_range: 53..57, | ||
210 | name: "main", | ||
211 | kind: Function, | ||
212 | }, | ||
213 | kind: Bin, | ||
214 | cfg: None, | ||
215 | }, | ||
216 | }, | ||
217 | }, | ||
218 | Annotation { | ||
219 | range: 50..85, | ||
220 | kind: Runnable { | ||
221 | debug: true, | ||
222 | runnable: Runnable { | ||
223 | nav: NavigationTarget { | ||
224 | file_id: FileId( | ||
225 | 0, | ||
226 | ), | ||
227 | full_range: 50..85, | ||
228 | focus_range: 53..57, | ||
229 | name: "main", | ||
230 | kind: Function, | ||
231 | }, | ||
232 | kind: Bin, | ||
233 | cfg: None, | ||
234 | }, | ||
235 | }, | ||
236 | }, | ||
237 | Annotation { | ||
238 | range: 0..22, | ||
239 | kind: HasReferences { | ||
240 | position: FilePosition { | ||
241 | file_id: FileId( | ||
242 | 0, | ||
243 | ), | ||
244 | offset: 6, | ||
245 | }, | ||
246 | data: Some( | ||
247 | [ | ||
248 | FileRange { | ||
249 | file_id: FileId( | ||
250 | 0, | ||
251 | ), | ||
252 | range: 78..82, | ||
253 | }, | ||
254 | ], | ||
255 | ), | ||
256 | }, | ||
257 | }, | ||
258 | Annotation { | ||
259 | range: 24..48, | ||
260 | kind: HasReferences { | ||
261 | position: FilePosition { | ||
262 | file_id: FileId( | ||
263 | 0, | ||
264 | ), | ||
265 | offset: 30, | ||
266 | }, | ||
267 | data: Some( | ||
268 | [], | ||
269 | ), | ||
270 | }, | ||
271 | }, | ||
272 | Annotation { | ||
273 | range: 53..57, | ||
274 | kind: HasReferences { | ||
275 | position: FilePosition { | ||
276 | file_id: FileId( | ||
277 | 0, | ||
278 | ), | ||
279 | offset: 53, | ||
280 | }, | ||
281 | data: Some( | ||
282 | [], | ||
283 | ), | ||
284 | }, | ||
285 | }, | ||
286 | ] | ||
287 | "#]], | ||
288 | ); | ||
289 | } | ||
290 | |||
291 | #[test] | ||
292 | fn struct_references_annotations() { | ||
293 | check( | ||
294 | r#" | ||
295 | struct Test; | ||
296 | |||
297 | fn main() { | ||
298 | let test = Test; | ||
299 | } | ||
300 | "#, | ||
301 | expect![[r#" | ||
302 | [ | ||
303 | Annotation { | ||
304 | range: 14..48, | ||
305 | kind: Runnable { | ||
306 | debug: false, | ||
307 | runnable: Runnable { | ||
308 | nav: NavigationTarget { | ||
309 | file_id: FileId( | ||
310 | 0, | ||
311 | ), | ||
312 | full_range: 14..48, | ||
313 | focus_range: 17..21, | ||
314 | name: "main", | ||
315 | kind: Function, | ||
316 | }, | ||
317 | kind: Bin, | ||
318 | cfg: None, | ||
319 | }, | ||
320 | }, | ||
321 | }, | ||
322 | Annotation { | ||
323 | range: 14..48, | ||
324 | kind: Runnable { | ||
325 | debug: true, | ||
326 | runnable: Runnable { | ||
327 | nav: NavigationTarget { | ||
328 | file_id: FileId( | ||
329 | 0, | ||
330 | ), | ||
331 | full_range: 14..48, | ||
332 | focus_range: 17..21, | ||
333 | name: "main", | ||
334 | kind: Function, | ||
335 | }, | ||
336 | kind: Bin, | ||
337 | cfg: None, | ||
338 | }, | ||
339 | }, | ||
340 | }, | ||
341 | Annotation { | ||
342 | range: 0..12, | ||
343 | kind: HasImpls { | ||
344 | position: FilePosition { | ||
345 | file_id: FileId( | ||
346 | 0, | ||
347 | ), | ||
348 | offset: 7, | ||
349 | }, | ||
350 | data: Some( | ||
351 | [], | ||
352 | ), | ||
353 | }, | ||
354 | }, | ||
355 | Annotation { | ||
356 | range: 0..12, | ||
357 | kind: HasReferences { | ||
358 | position: FilePosition { | ||
359 | file_id: FileId( | ||
360 | 0, | ||
361 | ), | ||
362 | offset: 7, | ||
363 | }, | ||
364 | data: Some( | ||
365 | [ | ||
366 | FileRange { | ||
367 | file_id: FileId( | ||
368 | 0, | ||
369 | ), | ||
370 | range: 41..45, | ||
371 | }, | ||
372 | ], | ||
373 | ), | ||
374 | }, | ||
375 | }, | ||
376 | Annotation { | ||
377 | range: 17..21, | ||
378 | kind: HasReferences { | ||
379 | position: FilePosition { | ||
380 | file_id: FileId( | ||
381 | 0, | ||
382 | ), | ||
383 | offset: 17, | ||
384 | }, | ||
385 | data: Some( | ||
386 | [], | ||
387 | ), | ||
388 | }, | ||
389 | }, | ||
390 | ] | ||
391 | "#]], | ||
392 | ); | ||
393 | } | ||
394 | |||
395 | #[test] | ||
396 | fn struct_and_trait_impls_annotations() { | ||
397 | check( | ||
398 | r#" | ||
399 | struct Test; | ||
400 | |||
401 | trait MyCoolTrait {} | ||
402 | |||
403 | impl MyCoolTrait for Test {} | ||
404 | |||
405 | fn main() { | ||
406 | let test = Test; | ||
407 | } | ||
408 | "#, | ||
409 | expect![[r#" | ||
410 | [ | ||
411 | Annotation { | ||
412 | range: 66..100, | ||
413 | kind: Runnable { | ||
414 | debug: false, | ||
415 | runnable: Runnable { | ||
416 | nav: NavigationTarget { | ||
417 | file_id: FileId( | ||
418 | 0, | ||
419 | ), | ||
420 | full_range: 66..100, | ||
421 | focus_range: 69..73, | ||
422 | name: "main", | ||
423 | kind: Function, | ||
424 | }, | ||
425 | kind: Bin, | ||
426 | cfg: None, | ||
427 | }, | ||
428 | }, | ||
429 | }, | ||
430 | Annotation { | ||
431 | range: 66..100, | ||
432 | kind: Runnable { | ||
433 | debug: true, | ||
434 | runnable: Runnable { | ||
435 | nav: NavigationTarget { | ||
436 | file_id: FileId( | ||
437 | 0, | ||
438 | ), | ||
439 | full_range: 66..100, | ||
440 | focus_range: 69..73, | ||
441 | name: "main", | ||
442 | kind: Function, | ||
443 | }, | ||
444 | kind: Bin, | ||
445 | cfg: None, | ||
446 | }, | ||
447 | }, | ||
448 | }, | ||
449 | Annotation { | ||
450 | range: 0..12, | ||
451 | kind: HasImpls { | ||
452 | position: FilePosition { | ||
453 | file_id: FileId( | ||
454 | 0, | ||
455 | ), | ||
456 | offset: 7, | ||
457 | }, | ||
458 | data: Some( | ||
459 | [ | ||
460 | NavigationTarget { | ||
461 | file_id: FileId( | ||
462 | 0, | ||
463 | ), | ||
464 | full_range: 36..64, | ||
465 | focus_range: 57..61, | ||
466 | name: "impl", | ||
467 | kind: Impl, | ||
468 | }, | ||
469 | ], | ||
470 | ), | ||
471 | }, | ||
472 | }, | ||
473 | Annotation { | ||
474 | range: 0..12, | ||
475 | kind: HasReferences { | ||
476 | position: FilePosition { | ||
477 | file_id: FileId( | ||
478 | 0, | ||
479 | ), | ||
480 | offset: 7, | ||
481 | }, | ||
482 | data: Some( | ||
483 | [ | ||
484 | FileRange { | ||
485 | file_id: FileId( | ||
486 | 0, | ||
487 | ), | ||
488 | range: 57..61, | ||
489 | }, | ||
490 | FileRange { | ||
491 | file_id: FileId( | ||
492 | 0, | ||
493 | ), | ||
494 | range: 93..97, | ||
495 | }, | ||
496 | ], | ||
497 | ), | ||
498 | }, | ||
499 | }, | ||
500 | Annotation { | ||
501 | range: 14..34, | ||
502 | kind: HasImpls { | ||
503 | position: FilePosition { | ||
504 | file_id: FileId( | ||
505 | 0, | ||
506 | ), | ||
507 | offset: 20, | ||
508 | }, | ||
509 | data: Some( | ||
510 | [ | ||
511 | NavigationTarget { | ||
512 | file_id: FileId( | ||
513 | 0, | ||
514 | ), | ||
515 | full_range: 36..64, | ||
516 | focus_range: 57..61, | ||
517 | name: "impl", | ||
518 | kind: Impl, | ||
519 | }, | ||
520 | ], | ||
521 | ), | ||
522 | }, | ||
523 | }, | ||
524 | Annotation { | ||
525 | range: 14..34, | ||
526 | kind: HasReferences { | ||
527 | position: FilePosition { | ||
528 | file_id: FileId( | ||
529 | 0, | ||
530 | ), | ||
531 | offset: 20, | ||
532 | }, | ||
533 | data: Some( | ||
534 | [ | ||
535 | FileRange { | ||
536 | file_id: FileId( | ||
537 | 0, | ||
538 | ), | ||
539 | range: 41..52, | ||
540 | }, | ||
541 | ], | ||
542 | ), | ||
543 | }, | ||
544 | }, | ||
545 | Annotation { | ||
546 | range: 69..73, | ||
547 | kind: HasReferences { | ||
548 | position: FilePosition { | ||
549 | file_id: FileId( | ||
550 | 0, | ||
551 | ), | ||
552 | offset: 69, | ||
553 | }, | ||
554 | data: Some( | ||
555 | [], | ||
556 | ), | ||
557 | }, | ||
558 | }, | ||
559 | ] | ||
560 | "#]], | ||
561 | ); | ||
562 | } | ||
563 | |||
564 | #[test] | ||
565 | fn runnable_annotation() { | ||
566 | check( | ||
567 | r#" | ||
568 | fn main() {} | ||
569 | "#, | ||
570 | expect![[r#" | ||
571 | [ | ||
572 | Annotation { | ||
573 | range: 0..12, | ||
574 | kind: Runnable { | ||
575 | debug: false, | ||
576 | runnable: Runnable { | ||
577 | nav: NavigationTarget { | ||
578 | file_id: FileId( | ||
579 | 0, | ||
580 | ), | ||
581 | full_range: 0..12, | ||
582 | focus_range: 3..7, | ||
583 | name: "main", | ||
584 | kind: Function, | ||
585 | }, | ||
586 | kind: Bin, | ||
587 | cfg: None, | ||
588 | }, | ||
589 | }, | ||
590 | }, | ||
591 | Annotation { | ||
592 | range: 0..12, | ||
593 | kind: Runnable { | ||
594 | debug: true, | ||
595 | runnable: Runnable { | ||
596 | nav: NavigationTarget { | ||
597 | file_id: FileId( | ||
598 | 0, | ||
599 | ), | ||
600 | full_range: 0..12, | ||
601 | focus_range: 3..7, | ||
602 | name: "main", | ||
603 | kind: Function, | ||
604 | }, | ||
605 | kind: Bin, | ||
606 | cfg: None, | ||
607 | }, | ||
608 | }, | ||
609 | }, | ||
610 | Annotation { | ||
611 | range: 3..7, | ||
612 | kind: HasReferences { | ||
613 | position: FilePosition { | ||
614 | file_id: FileId( | ||
615 | 0, | ||
616 | ), | ||
617 | offset: 3, | ||
618 | }, | ||
619 | data: Some( | ||
620 | [], | ||
621 | ), | ||
622 | }, | ||
623 | }, | ||
624 | ] | ||
625 | "#]], | ||
626 | ); | ||
627 | } | ||
628 | |||
629 | #[test] | ||
630 | fn method_annotations() { | ||
631 | check( | ||
632 | r#" | ||
633 | struct Test; | ||
634 | |||
635 | impl Test { | ||
636 | fn self_by_ref(&self) {} | ||
637 | } | ||
638 | |||
639 | fn main() { | ||
640 | Test.self_by_ref(); | ||
641 | } | ||
642 | "#, | ||
643 | expect![[r#" | ||
644 | [ | ||
645 | Annotation { | ||
646 | range: 58..95, | ||
647 | kind: Runnable { | ||
648 | debug: false, | ||
649 | runnable: Runnable { | ||
650 | nav: NavigationTarget { | ||
651 | file_id: FileId( | ||
652 | 0, | ||
653 | ), | ||
654 | full_range: 58..95, | ||
655 | focus_range: 61..65, | ||
656 | name: "main", | ||
657 | kind: Function, | ||
658 | }, | ||
659 | kind: Bin, | ||
660 | cfg: None, | ||
661 | }, | ||
662 | }, | ||
663 | }, | ||
664 | Annotation { | ||
665 | range: 58..95, | ||
666 | kind: Runnable { | ||
667 | debug: true, | ||
668 | runnable: Runnable { | ||
669 | nav: NavigationTarget { | ||
670 | file_id: FileId( | ||
671 | 0, | ||
672 | ), | ||
673 | full_range: 58..95, | ||
674 | focus_range: 61..65, | ||
675 | name: "main", | ||
676 | kind: Function, | ||
677 | }, | ||
678 | kind: Bin, | ||
679 | cfg: None, | ||
680 | }, | ||
681 | }, | ||
682 | }, | ||
683 | Annotation { | ||
684 | range: 0..12, | ||
685 | kind: HasImpls { | ||
686 | position: FilePosition { | ||
687 | file_id: FileId( | ||
688 | 0, | ||
689 | ), | ||
690 | offset: 7, | ||
691 | }, | ||
692 | data: Some( | ||
693 | [ | ||
694 | NavigationTarget { | ||
695 | file_id: FileId( | ||
696 | 0, | ||
697 | ), | ||
698 | full_range: 14..56, | ||
699 | focus_range: 19..23, | ||
700 | name: "impl", | ||
701 | kind: Impl, | ||
702 | }, | ||
703 | ], | ||
704 | ), | ||
705 | }, | ||
706 | }, | ||
707 | Annotation { | ||
708 | range: 0..12, | ||
709 | kind: HasReferences { | ||
710 | position: FilePosition { | ||
711 | file_id: FileId( | ||
712 | 0, | ||
713 | ), | ||
714 | offset: 7, | ||
715 | }, | ||
716 | data: Some( | ||
717 | [ | ||
718 | FileRange { | ||
719 | file_id: FileId( | ||
720 | 0, | ||
721 | ), | ||
722 | range: 19..23, | ||
723 | }, | ||
724 | FileRange { | ||
725 | file_id: FileId( | ||
726 | 0, | ||
727 | ), | ||
728 | range: 74..78, | ||
729 | }, | ||
730 | ], | ||
731 | ), | ||
732 | }, | ||
733 | }, | ||
734 | Annotation { | ||
735 | range: 33..44, | ||
736 | kind: HasReferences { | ||
737 | position: FilePosition { | ||
738 | file_id: FileId( | ||
739 | 0, | ||
740 | ), | ||
741 | offset: 33, | ||
742 | }, | ||
743 | data: Some( | ||
744 | [ | ||
745 | FileRange { | ||
746 | file_id: FileId( | ||
747 | 0, | ||
748 | ), | ||
749 | range: 79..90, | ||
750 | }, | ||
751 | ], | ||
752 | ), | ||
753 | }, | ||
754 | }, | ||
755 | Annotation { | ||
756 | range: 61..65, | ||
757 | kind: HasReferences { | ||
758 | position: FilePosition { | ||
759 | file_id: FileId( | ||
760 | 0, | ||
761 | ), | ||
762 | offset: 61, | ||
763 | }, | ||
764 | data: Some( | ||
765 | [], | ||
766 | ), | ||
767 | }, | ||
768 | }, | ||
769 | ] | ||
770 | "#]], | ||
771 | ); | ||
772 | } | ||
773 | |||
774 | #[test] | ||
775 | fn test_annotations() { | ||
776 | check( | ||
777 | r#" | ||
778 | fn main() {} | ||
779 | |||
780 | mod tests { | ||
781 | #[test] | ||
782 | fn my_cool_test() {} | ||
783 | } | ||
784 | "#, | ||
785 | expect![[r#" | ||
786 | [ | ||
787 | Annotation { | ||
788 | range: 0..12, | ||
789 | kind: Runnable { | ||
790 | debug: false, | ||
791 | runnable: Runnable { | ||
792 | nav: NavigationTarget { | ||
793 | file_id: FileId( | ||
794 | 0, | ||
795 | ), | ||
796 | full_range: 0..12, | ||
797 | focus_range: 3..7, | ||
798 | name: "main", | ||
799 | kind: Function, | ||
800 | }, | ||
801 | kind: Bin, | ||
802 | cfg: None, | ||
803 | }, | ||
804 | }, | ||
805 | }, | ||
806 | Annotation { | ||
807 | range: 0..12, | ||
808 | kind: Runnable { | ||
809 | debug: true, | ||
810 | runnable: Runnable { | ||
811 | nav: NavigationTarget { | ||
812 | file_id: FileId( | ||
813 | 0, | ||
814 | ), | ||
815 | full_range: 0..12, | ||
816 | focus_range: 3..7, | ||
817 | name: "main", | ||
818 | kind: Function, | ||
819 | }, | ||
820 | kind: Bin, | ||
821 | cfg: None, | ||
822 | }, | ||
823 | }, | ||
824 | }, | ||
825 | Annotation { | ||
826 | range: 14..64, | ||
827 | kind: Runnable { | ||
828 | debug: false, | ||
829 | runnable: Runnable { | ||
830 | nav: NavigationTarget { | ||
831 | file_id: FileId( | ||
832 | 0, | ||
833 | ), | ||
834 | full_range: 14..64, | ||
835 | focus_range: 18..23, | ||
836 | name: "tests", | ||
837 | kind: Module, | ||
838 | }, | ||
839 | kind: TestMod { | ||
840 | path: "tests", | ||
841 | }, | ||
842 | cfg: None, | ||
843 | }, | ||
844 | }, | ||
845 | }, | ||
846 | Annotation { | ||
847 | range: 14..64, | ||
848 | kind: Runnable { | ||
849 | debug: true, | ||
850 | runnable: Runnable { | ||
851 | nav: NavigationTarget { | ||
852 | file_id: FileId( | ||
853 | 0, | ||
854 | ), | ||
855 | full_range: 14..64, | ||
856 | focus_range: 18..23, | ||
857 | name: "tests", | ||
858 | kind: Module, | ||
859 | }, | ||
860 | kind: TestMod { | ||
861 | path: "tests", | ||
862 | }, | ||
863 | cfg: None, | ||
864 | }, | ||
865 | }, | ||
866 | }, | ||
867 | Annotation { | ||
868 | range: 30..62, | ||
869 | kind: Runnable { | ||
870 | debug: false, | ||
871 | runnable: Runnable { | ||
872 | nav: NavigationTarget { | ||
873 | file_id: FileId( | ||
874 | 0, | ||
875 | ), | ||
876 | full_range: 30..62, | ||
877 | focus_range: 45..57, | ||
878 | name: "my_cool_test", | ||
879 | kind: Function, | ||
880 | }, | ||
881 | kind: Test { | ||
882 | test_id: Path( | ||
883 | "tests::my_cool_test", | ||
884 | ), | ||
885 | attr: TestAttr { | ||
886 | ignore: false, | ||
887 | }, | ||
888 | }, | ||
889 | cfg: None, | ||
890 | }, | ||
891 | }, | ||
892 | }, | ||
893 | Annotation { | ||
894 | range: 30..62, | ||
895 | kind: Runnable { | ||
896 | debug: true, | ||
897 | runnable: Runnable { | ||
898 | nav: NavigationTarget { | ||
899 | file_id: FileId( | ||
900 | 0, | ||
901 | ), | ||
902 | full_range: 30..62, | ||
903 | focus_range: 45..57, | ||
904 | name: "my_cool_test", | ||
905 | kind: Function, | ||
906 | }, | ||
907 | kind: Test { | ||
908 | test_id: Path( | ||
909 | "tests::my_cool_test", | ||
910 | ), | ||
911 | attr: TestAttr { | ||
912 | ignore: false, | ||
913 | }, | ||
914 | }, | ||
915 | cfg: None, | ||
916 | }, | ||
917 | }, | ||
918 | }, | ||
919 | Annotation { | ||
920 | range: 3..7, | ||
921 | kind: HasReferences { | ||
922 | position: FilePosition { | ||
923 | file_id: FileId( | ||
924 | 0, | ||
925 | ), | ||
926 | offset: 3, | ||
927 | }, | ||
928 | data: Some( | ||
929 | [], | ||
930 | ), | ||
931 | }, | ||
932 | }, | ||
933 | ] | ||
934 | "#]], | ||
935 | ); | ||
936 | } | ||
937 | } | ||
diff --git a/crates/ide/src/call_hierarchy.rs b/crates/ide/src/call_hierarchy.rs index e8999a7f3..b848945d7 100644 --- a/crates/ide/src/call_hierarchy.rs +++ b/crates/ide/src/call_hierarchy.rs | |||
@@ -47,11 +47,11 @@ pub(crate) fn incoming_calls(db: &RootDatabase, position: FilePosition) -> Optio | |||
47 | 47 | ||
48 | let mut calls = CallLocations::default(); | 48 | let mut calls = CallLocations::default(); |
49 | 49 | ||
50 | for (&file_id, references) in refs.info.references().iter() { | 50 | for (file_id, references) in refs.references { |
51 | let file = sema.parse(file_id); | 51 | let file = sema.parse(file_id); |
52 | let file = file.syntax(); | 52 | let file = file.syntax(); |
53 | for reference in references { | 53 | for (r_range, _) in references { |
54 | let token = file.token_at_offset(reference.range.start()).next()?; | 54 | let token = file.token_at_offset(r_range.start()).next()?; |
55 | let token = sema.descend_into_macros(token); | 55 | let token = sema.descend_into_macros(token); |
56 | let syntax = token.parent(); | 56 | let syntax = token.parent(); |
57 | 57 | ||
@@ -61,7 +61,7 @@ pub(crate) fn incoming_calls(db: &RootDatabase, position: FilePosition) -> Optio | |||
61 | let def = sema.to_def(&fn_)?; | 61 | let def = sema.to_def(&fn_)?; |
62 | def.try_to_nav(sema.db) | 62 | def.try_to_nav(sema.db) |
63 | }) { | 63 | }) { |
64 | let relative_range = reference.range; | 64 | let relative_range = r_range; |
65 | calls.add(&nav, relative_range); | 65 | calls.add(&nav, relative_range); |
66 | } | 66 | } |
67 | } | 67 | } |
diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs index b35bc2bae..fe32f39b6 100644 --- a/crates/ide/src/diagnostics.rs +++ b/crates/ide/src/diagnostics.rs | |||
@@ -10,15 +10,16 @@ mod field_shorthand; | |||
10 | use std::cell::RefCell; | 10 | use std::cell::RefCell; |
11 | 11 | ||
12 | use hir::{ | 12 | use hir::{ |
13 | db::AstDatabase, | ||
13 | diagnostics::{Diagnostic as _, DiagnosticCode, DiagnosticSinkBuilder}, | 14 | diagnostics::{Diagnostic as _, DiagnosticCode, DiagnosticSinkBuilder}, |
14 | Semantics, | 15 | InFile, Semantics, |
15 | }; | 16 | }; |
16 | use ide_db::{base_db::SourceDatabase, RootDatabase}; | 17 | use ide_db::{base_db::SourceDatabase, RootDatabase}; |
17 | use itertools::Itertools; | 18 | use itertools::Itertools; |
18 | use rustc_hash::FxHashSet; | 19 | use rustc_hash::FxHashSet; |
19 | use syntax::{ | 20 | use syntax::{ |
20 | ast::{self, AstNode}, | 21 | ast::{self, AstNode}, |
21 | SyntaxNode, TextRange, | 22 | SyntaxNode, SyntaxNodePtr, TextRange, |
22 | }; | 23 | }; |
23 | use text_edit::TextEdit; | 24 | use text_edit::TextEdit; |
24 | 25 | ||
@@ -136,6 +137,9 @@ pub(crate) fn diagnostics( | |||
136 | .on::<hir::diagnostics::IncorrectCase, _>(|d| { | 137 | .on::<hir::diagnostics::IncorrectCase, _>(|d| { |
137 | res.borrow_mut().push(warning_with_fix(d, &sema)); | 138 | res.borrow_mut().push(warning_with_fix(d, &sema)); |
138 | }) | 139 | }) |
140 | .on::<hir::diagnostics::ReplaceFilterMapNextWithFindMap, _>(|d| { | ||
141 | res.borrow_mut().push(warning_with_fix(d, &sema)); | ||
142 | }) | ||
139 | .on::<hir::diagnostics::InactiveCode, _>(|d| { | 143 | .on::<hir::diagnostics::InactiveCode, _>(|d| { |
140 | // If there's inactive code somewhere in a macro, don't propagate to the call-site. | 144 | // If there's inactive code somewhere in a macro, don't propagate to the call-site. |
141 | if d.display_source().file_id.expansion_info(db).is_some() { | 145 | if d.display_source().file_id.expansion_info(db).is_some() { |
@@ -144,20 +148,38 @@ pub(crate) fn diagnostics( | |||
144 | 148 | ||
145 | // Override severity and mark as unused. | 149 | // Override severity and mark as unused. |
146 | res.borrow_mut().push( | 150 | res.borrow_mut().push( |
147 | Diagnostic::hint(sema.diagnostics_display_range(d).range, d.message()) | 151 | Diagnostic::hint( |
148 | .with_unused(true) | 152 | sema.diagnostics_display_range(d.display_source()).range, |
149 | .with_code(Some(d.code())), | 153 | d.message(), |
154 | ) | ||
155 | .with_unused(true) | ||
156 | .with_code(Some(d.code())), | ||
150 | ); | 157 | ); |
151 | }) | 158 | }) |
152 | .on::<hir::diagnostics::UnresolvedProcMacro, _>(|d| { | 159 | .on::<hir::diagnostics::UnresolvedProcMacro, _>(|d| { |
153 | // Use more accurate position if available. | 160 | // Use more accurate position if available. |
154 | let display_range = | 161 | let display_range = d |
155 | d.precise_location.unwrap_or_else(|| sema.diagnostics_display_range(d).range); | 162 | .precise_location |
163 | .unwrap_or_else(|| sema.diagnostics_display_range(d.display_source()).range); | ||
156 | 164 | ||
157 | // FIXME: it would be nice to tell the user whether proc macros are currently disabled | 165 | // FIXME: it would be nice to tell the user whether proc macros are currently disabled |
158 | res.borrow_mut() | 166 | res.borrow_mut() |
159 | .push(Diagnostic::hint(display_range, d.message()).with_code(Some(d.code()))); | 167 | .push(Diagnostic::hint(display_range, d.message()).with_code(Some(d.code()))); |
160 | }) | 168 | }) |
169 | .on::<hir::diagnostics::UnresolvedMacroCall, _>(|d| { | ||
170 | let last_path_segment = sema.db.parse_or_expand(d.file).and_then(|root| { | ||
171 | d.node | ||
172 | .to_node(&root) | ||
173 | .path() | ||
174 | .and_then(|it| it.segment()) | ||
175 | .and_then(|it| it.name_ref()) | ||
176 | .map(|it| InFile::new(d.file, SyntaxNodePtr::new(it.syntax()))) | ||
177 | }); | ||
178 | let diagnostics = last_path_segment.unwrap_or_else(|| d.display_source()); | ||
179 | let display_range = sema.diagnostics_display_range(diagnostics).range; | ||
180 | res.borrow_mut() | ||
181 | .push(Diagnostic::error(display_range, d.message()).with_code(Some(d.code()))); | ||
182 | }) | ||
161 | // Only collect experimental diagnostics when they're enabled. | 183 | // Only collect experimental diagnostics when they're enabled. |
162 | .filter(|diag| !(diag.is_experimental() && config.disable_experimental)) | 184 | .filter(|diag| !(diag.is_experimental() && config.disable_experimental)) |
163 | .filter(|diag| !config.disabled.contains(diag.code().as_str())); | 185 | .filter(|diag| !config.disabled.contains(diag.code().as_str())); |
@@ -167,8 +189,11 @@ pub(crate) fn diagnostics( | |||
167 | // Diagnostics not handled above get no fix and default treatment. | 189 | // Diagnostics not handled above get no fix and default treatment. |
168 | .build(|d| { | 190 | .build(|d| { |
169 | res.borrow_mut().push( | 191 | res.borrow_mut().push( |
170 | Diagnostic::error(sema.diagnostics_display_range(d).range, d.message()) | 192 | Diagnostic::error( |
171 | .with_code(Some(d.code())), | 193 | sema.diagnostics_display_range(d.display_source()).range, |
194 | d.message(), | ||
195 | ) | ||
196 | .with_code(Some(d.code())), | ||
172 | ); | 197 | ); |
173 | }); | 198 | }); |
174 | 199 | ||
@@ -180,13 +205,13 @@ pub(crate) fn diagnostics( | |||
180 | } | 205 | } |
181 | 206 | ||
182 | fn diagnostic_with_fix<D: DiagnosticWithFix>(d: &D, sema: &Semantics<RootDatabase>) -> Diagnostic { | 207 | fn diagnostic_with_fix<D: DiagnosticWithFix>(d: &D, sema: &Semantics<RootDatabase>) -> Diagnostic { |
183 | Diagnostic::error(sema.diagnostics_display_range(d).range, d.message()) | 208 | Diagnostic::error(sema.diagnostics_display_range(d.display_source()).range, d.message()) |
184 | .with_fix(d.fix(&sema)) | 209 | .with_fix(d.fix(&sema)) |
185 | .with_code(Some(d.code())) | 210 | .with_code(Some(d.code())) |
186 | } | 211 | } |
187 | 212 | ||
188 | fn warning_with_fix<D: DiagnosticWithFix>(d: &D, sema: &Semantics<RootDatabase>) -> Diagnostic { | 213 | fn warning_with_fix<D: DiagnosticWithFix>(d: &D, sema: &Semantics<RootDatabase>) -> Diagnostic { |
189 | Diagnostic::hint(sema.diagnostics_display_range(d).range, d.message()) | 214 | Diagnostic::hint(sema.diagnostics_display_range(d.display_source()).range, d.message()) |
190 | .with_fix(d.fix(&sema)) | 215 | .with_fix(d.fix(&sema)) |
191 | .with_code(Some(d.code())) | 216 | .with_code(Some(d.code())) |
192 | } | 217 | } |
@@ -643,6 +668,29 @@ fn test_fn() { | |||
643 | } | 668 | } |
644 | 669 | ||
645 | #[test] | 670 | #[test] |
671 | fn test_unresolved_macro_range() { | ||
672 | check_expect( | ||
673 | r#"foo::bar!(92);"#, | ||
674 | expect![[r#" | ||
675 | [ | ||
676 | Diagnostic { | ||
677 | message: "unresolved macro call", | ||
678 | range: 5..8, | ||
679 | severity: Error, | ||
680 | fix: None, | ||
681 | unused: false, | ||
682 | code: Some( | ||
683 | DiagnosticCode( | ||
684 | "unresolved-macro-call", | ||
685 | ), | ||
686 | ), | ||
687 | }, | ||
688 | ] | ||
689 | "#]], | ||
690 | ); | ||
691 | } | ||
692 | |||
693 | #[test] | ||
646 | fn range_mapping_out_of_macros() { | 694 | fn range_mapping_out_of_macros() { |
647 | // FIXME: this is very wrong, but somewhat tricky to fix. | 695 | // FIXME: this is very wrong, but somewhat tricky to fix. |
648 | check_fix( | 696 | check_fix( |
diff --git a/crates/ide/src/diagnostics/fixes.rs b/crates/ide/src/diagnostics/fixes.rs index e4335119b..cbfc66ab3 100644 --- a/crates/ide/src/diagnostics/fixes.rs +++ b/crates/ide/src/diagnostics/fixes.rs | |||
@@ -4,7 +4,7 @@ use hir::{ | |||
4 | db::AstDatabase, | 4 | db::AstDatabase, |
5 | diagnostics::{ | 5 | diagnostics::{ |
6 | Diagnostic, IncorrectCase, MissingFields, MissingOkOrSomeInTailExpr, NoSuchField, | 6 | Diagnostic, IncorrectCase, MissingFields, MissingOkOrSomeInTailExpr, NoSuchField, |
7 | RemoveThisSemicolon, UnresolvedModule, | 7 | RemoveThisSemicolon, ReplaceFilterMapNextWithFindMap, UnresolvedModule, |
8 | }, | 8 | }, |
9 | HasSource, HirDisplay, InFile, Semantics, VariantDef, | 9 | HasSource, HirDisplay, InFile, Semantics, VariantDef, |
10 | }; | 10 | }; |
@@ -15,8 +15,8 @@ use ide_db::{ | |||
15 | }; | 15 | }; |
16 | use syntax::{ | 16 | use syntax::{ |
17 | algo, | 17 | algo, |
18 | ast::{self, edit::IndentLevel, make}, | 18 | ast::{self, edit::IndentLevel, make, ArgListOwner}, |
19 | AstNode, | 19 | AstNode, TextRange, |
20 | }; | 20 | }; |
21 | use text_edit::TextEdit; | 21 | use text_edit::TextEdit; |
22 | 22 | ||
@@ -140,7 +140,34 @@ impl DiagnosticWithFix for IncorrectCase { | |||
140 | rename_with_semantics(sema, file_position, &self.suggested_text).ok()?; | 140 | rename_with_semantics(sema, file_position, &self.suggested_text).ok()?; |
141 | 141 | ||
142 | let label = format!("Rename to {}", self.suggested_text); | 142 | let label = format!("Rename to {}", self.suggested_text); |
143 | Some(Fix::new(&label, rename_changes.info, rename_changes.range)) | 143 | Some(Fix::new(&label, rename_changes, frange.range)) |
144 | } | ||
145 | } | ||
146 | |||
147 | impl DiagnosticWithFix for ReplaceFilterMapNextWithFindMap { | ||
148 | fn fix(&self, sema: &Semantics<RootDatabase>) -> Option<Fix> { | ||
149 | let root = sema.db.parse_or_expand(self.file)?; | ||
150 | let next_expr = self.next_expr.to_node(&root); | ||
151 | let next_call = ast::MethodCallExpr::cast(next_expr.syntax().clone())?; | ||
152 | |||
153 | let filter_map_call = ast::MethodCallExpr::cast(next_call.receiver()?.syntax().clone())?; | ||
154 | let filter_map_name_range = filter_map_call.name_ref()?.ident_token()?.text_range(); | ||
155 | let filter_map_args = filter_map_call.arg_list()?; | ||
156 | |||
157 | let range_to_replace = | ||
158 | TextRange::new(filter_map_name_range.start(), next_expr.syntax().text_range().end()); | ||
159 | let replacement = format!("find_map{}", filter_map_args.syntax().text()); | ||
160 | let trigger_range = next_expr.syntax().text_range(); | ||
161 | |||
162 | let edit = TextEdit::replace(range_to_replace, replacement); | ||
163 | |||
164 | let source_change = SourceChange::from_text_edit(self.file.original_file(sema.db), edit); | ||
165 | |||
166 | Some(Fix::new( | ||
167 | "Replace filter_map(..).next() with find_map()", | ||
168 | source_change, | ||
169 | trigger_range, | ||
170 | )) | ||
144 | } | 171 | } |
145 | } | 172 | } |
146 | 173 | ||
diff --git a/crates/ide/src/display/navigation_target.rs b/crates/ide/src/display/navigation_target.rs index 00e601244..198243466 100644 --- a/crates/ide/src/display/navigation_target.rs +++ b/crates/ide/src/display/navigation_target.rs | |||
@@ -7,6 +7,7 @@ use hir::{AssocItem, Documentation, FieldSource, HasAttrs, HasSource, InFile, Mo | |||
7 | use ide_db::{ | 7 | use ide_db::{ |
8 | base_db::{FileId, FileRange, SourceDatabase}, | 8 | base_db::{FileId, FileRange, SourceDatabase}, |
9 | symbol_index::FileSymbolKind, | 9 | symbol_index::FileSymbolKind, |
10 | SymbolKind, | ||
10 | }; | 11 | }; |
11 | use ide_db::{defs::Definition, RootDatabase}; | 12 | use ide_db::{defs::Definition, RootDatabase}; |
12 | use syntax::{ | 13 | use syntax::{ |
@@ -18,30 +19,6 @@ use crate::FileSymbol; | |||
18 | 19 | ||
19 | use super::short_label::ShortLabel; | 20 | use super::short_label::ShortLabel; |
20 | 21 | ||
21 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] | ||
22 | pub enum SymbolKind { | ||
23 | Module, | ||
24 | Impl, | ||
25 | Field, | ||
26 | TypeParam, | ||
27 | ConstParam, | ||
28 | LifetimeParam, | ||
29 | ValueParam, | ||
30 | SelfParam, | ||
31 | Local, | ||
32 | Label, | ||
33 | Function, | ||
34 | Const, | ||
35 | Static, | ||
36 | Struct, | ||
37 | Enum, | ||
38 | Variant, | ||
39 | Union, | ||
40 | TypeAlias, | ||
41 | Trait, | ||
42 | Macro, | ||
43 | } | ||
44 | |||
45 | /// `NavigationTarget` represents and element in the editor's UI which you can | 22 | /// `NavigationTarget` represents and element in the editor's UI which you can |
46 | /// click on to navigate to a particular piece of code. | 23 | /// click on to navigate to a particular piece of code. |
47 | /// | 24 | /// |
@@ -108,12 +85,16 @@ impl NavigationTarget { | |||
108 | let name = module.name(db).map(|it| it.to_string().into()).unwrap_or_default(); | 85 | let name = module.name(db).map(|it| it.to_string().into()).unwrap_or_default(); |
109 | if let Some(src) = module.declaration_source(db) { | 86 | if let Some(src) = module.declaration_source(db) { |
110 | let node = src.as_ref().map(|it| it.syntax()); | 87 | let node = src.as_ref().map(|it| it.syntax()); |
111 | let frange = node.original_file_range(db); | 88 | let full_range = node.original_file_range(db); |
89 | let focus_range = src | ||
90 | .value | ||
91 | .name() | ||
92 | .map(|name| src.with_value(name.syntax()).original_file_range(db).range); | ||
112 | let mut res = NavigationTarget::from_syntax( | 93 | let mut res = NavigationTarget::from_syntax( |
113 | frange.file_id, | 94 | full_range.file_id, |
114 | name, | 95 | name, |
115 | None, | 96 | focus_range, |
116 | frange.range, | 97 | full_range.range, |
117 | SymbolKind::Module, | 98 | SymbolKind::Module, |
118 | ); | 99 | ); |
119 | res.docs = module.attrs(db).docs(); | 100 | res.docs = module.attrs(db).docs(); |
@@ -153,8 +134,7 @@ impl NavigationTarget { | |||
153 | node: InFile<&dyn ast::NameOwner>, | 134 | node: InFile<&dyn ast::NameOwner>, |
154 | kind: SymbolKind, | 135 | kind: SymbolKind, |
155 | ) -> NavigationTarget { | 136 | ) -> NavigationTarget { |
156 | let name = | 137 | let name = node.value.name().map(|it| it.text().into()).unwrap_or_else(|| "_".into()); |
157 | node.value.name().map(|it| it.text().clone()).unwrap_or_else(|| SmolStr::new("_")); | ||
158 | let focus_range = | 138 | let focus_range = |
159 | node.value.name().map(|it| node.with_value(it.syntax()).original_file_range(db).range); | 139 | node.value.name().map(|it| node.with_value(it.syntax()).original_file_range(db).range); |
160 | let frange = node.map(|it| it.syntax()).original_file_range(db); | 140 | let frange = node.map(|it| it.syntax()).original_file_range(db); |
@@ -197,6 +177,7 @@ impl ToNav for FileSymbol { | |||
197 | FileSymbolKind::Const => SymbolKind::Const, | 177 | FileSymbolKind::Const => SymbolKind::Const, |
198 | FileSymbolKind::Static => SymbolKind::Static, | 178 | FileSymbolKind::Static => SymbolKind::Static, |
199 | FileSymbolKind::Macro => SymbolKind::Macro, | 179 | FileSymbolKind::Macro => SymbolKind::Macro, |
180 | FileSymbolKind::Union => SymbolKind::Union, | ||
200 | }), | 181 | }), |
201 | full_range: self.range, | 182 | full_range: self.range, |
202 | focus_range: self.name_range, | 183 | focus_range: self.name_range, |
@@ -295,6 +276,7 @@ impl ToNav for hir::Module { | |||
295 | ModuleSource::Module(node) => { | 276 | ModuleSource::Module(node) => { |
296 | (node.syntax(), node.name().map(|it| it.syntax().text_range())) | 277 | (node.syntax(), node.name().map(|it| it.syntax().text_range())) |
297 | } | 278 | } |
279 | ModuleSource::BlockExpr(node) => (node.syntax(), None), | ||
298 | }; | 280 | }; |
299 | let frange = src.with_value(syntax).original_file_range(db); | 281 | let frange = src.with_value(syntax).original_file_range(db); |
300 | NavigationTarget::from_syntax(frange.file_id, name, focus, frange.range, SymbolKind::Module) | 282 | NavigationTarget::from_syntax(frange.file_id, name, focus, frange.range, SymbolKind::Module) |
@@ -457,13 +439,16 @@ impl TryToNav for hir::TypeParam { | |||
457 | fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> { | 439 | fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> { |
458 | let src = self.source(db)?; | 440 | let src = self.source(db)?; |
459 | let full_range = match &src.value { | 441 | let full_range = match &src.value { |
460 | Either::Left(it) => it.syntax().text_range(), | 442 | Either::Left(it) => it |
443 | .name() | ||
444 | .map_or_else(|| it.syntax().text_range(), |name| name.syntax().text_range()), | ||
461 | Either::Right(it) => it.syntax().text_range(), | 445 | Either::Right(it) => it.syntax().text_range(), |
462 | }; | 446 | }; |
463 | let focus_range = match &src.value { | 447 | let focus_range = match &src.value { |
464 | Either::Left(_) => None, | 448 | Either::Left(it) => it.name(), |
465 | Either::Right(it) => it.name().map(|it| it.syntax().text_range()), | 449 | Either::Right(it) => it.name(), |
466 | }; | 450 | } |
451 | .map(|it| it.syntax().text_range()); | ||
467 | Some(NavigationTarget { | 452 | Some(NavigationTarget { |
468 | file_id: src.file_id.original_file(db), | 453 | file_id: src.file_id.original_file(db), |
469 | name: self.name(db).to_string().into(), | 454 | name: self.name(db).to_string().into(), |
diff --git a/crates/ide/src/display/short_label.rs b/crates/ide/src/display/short_label.rs index 990f740b8..84b8883de 100644 --- a/crates/ide/src/display/short_label.rs +++ b/crates/ide/src/display/short_label.rs | |||
@@ -53,9 +53,19 @@ impl ShortLabel for ast::SourceFile { | |||
53 | } | 53 | } |
54 | } | 54 | } |
55 | 55 | ||
56 | impl ShortLabel for ast::BlockExpr { | ||
57 | fn short_label(&self) -> Option<String> { | ||
58 | None | ||
59 | } | ||
60 | } | ||
61 | |||
56 | impl ShortLabel for ast::TypeAlias { | 62 | impl ShortLabel for ast::TypeAlias { |
57 | fn short_label(&self) -> Option<String> { | 63 | fn short_label(&self) -> Option<String> { |
58 | short_label_from_node(self, "type ") | 64 | let mut buf = short_label_from_node(self, "type ")?; |
65 | if let Some(type_ref) = self.ty() { | ||
66 | format_to!(buf, " = {}", type_ref.syntax()); | ||
67 | } | ||
68 | Some(buf) | ||
59 | } | 69 | } |
60 | } | 70 | } |
61 | 71 | ||
@@ -90,7 +100,7 @@ impl ShortLabel for ast::Variant { | |||
90 | impl ShortLabel for ast::ConstParam { | 100 | impl ShortLabel for ast::ConstParam { |
91 | fn short_label(&self) -> Option<String> { | 101 | fn short_label(&self) -> Option<String> { |
92 | let mut buf = "const ".to_owned(); | 102 | let mut buf = "const ".to_owned(); |
93 | buf.push_str(self.name()?.text().as_str()); | 103 | buf.push_str(self.name()?.text()); |
94 | if let Some(type_ref) = self.ty() { | 104 | if let Some(type_ref) = self.ty() { |
95 | format_to!(buf, ": {}", type_ref.syntax()); | 105 | format_to!(buf, ": {}", type_ref.syntax()); |
96 | } | 106 | } |
@@ -117,6 +127,6 @@ where | |||
117 | { | 127 | { |
118 | let mut buf = node.visibility().map(|v| format!("{} ", v.syntax())).unwrap_or_default(); | 128 | let mut buf = node.visibility().map(|v| format!("{} ", v.syntax())).unwrap_or_default(); |
119 | buf.push_str(label); | 129 | buf.push_str(label); |
120 | buf.push_str(node.name()?.text().as_str()); | 130 | buf.push_str(node.name()?.text()); |
121 | Some(buf) | 131 | Some(buf) |
122 | } | 132 | } |
diff --git a/crates/ide/src/doc_links.rs b/crates/ide/src/doc_links.rs index de10406bc..7bdd3cca3 100644 --- a/crates/ide/src/doc_links.rs +++ b/crates/ide/src/doc_links.rs | |||
@@ -9,8 +9,7 @@ use url::Url; | |||
9 | 9 | ||
10 | use hir::{ | 10 | use hir::{ |
11 | db::{DefDatabase, HirDatabase}, | 11 | db::{DefDatabase, HirDatabase}, |
12 | Adt, AsAssocItem, AsName, AssocItem, AssocItemContainer, Crate, Field, HasAttrs, ItemInNs, | 12 | Adt, AsAssocItem, AssocItem, AssocItemContainer, Crate, Field, HasAttrs, ItemInNs, ModuleDef, |
13 | ModuleDef, | ||
14 | }; | 13 | }; |
15 | use ide_db::{ | 14 | use ide_db::{ |
16 | defs::{Definition, NameClass, NameRefClass}, | 15 | defs::{Definition, NameClass, NameRefClass}, |
@@ -221,14 +220,31 @@ fn rewrite_intra_doc_link( | |||
221 | }?; | 220 | }?; |
222 | let krate = resolved.module(db)?.krate(); | 221 | let krate = resolved.module(db)?.krate(); |
223 | let canonical_path = resolved.canonical_path(db)?; | 222 | let canonical_path = resolved.canonical_path(db)?; |
224 | let new_target = get_doc_url(db, &krate)? | 223 | let mut new_url = get_doc_url(db, &krate)? |
225 | .join(&format!("{}/", krate.display_name(db)?)) | 224 | .join(&format!("{}/", krate.display_name(db)?)) |
226 | .ok()? | 225 | .ok()? |
227 | .join(&canonical_path.replace("::", "/")) | 226 | .join(&canonical_path.replace("::", "/")) |
228 | .ok()? | 227 | .ok()? |
229 | .join(&get_symbol_filename(db, &resolved)?) | 228 | .join(&get_symbol_filename(db, &resolved)?) |
230 | .ok()? | 229 | .ok()?; |
231 | .into_string(); | 230 | |
231 | if let ModuleDef::Trait(t) = resolved { | ||
232 | let items = t.items(db); | ||
233 | if let Some(field_or_assoc_item) = items.iter().find_map(|assoc_item| { | ||
234 | if let Some(name) = assoc_item.name(db) { | ||
235 | if *link == format!("{}::{}", canonical_path, name) { | ||
236 | return Some(FieldOrAssocItem::AssocItem(*assoc_item)); | ||
237 | } | ||
238 | } | ||
239 | None | ||
240 | }) { | ||
241 | if let Some(fragment) = get_symbol_fragment(db, &field_or_assoc_item) { | ||
242 | new_url = new_url.join(&fragment).ok()?; | ||
243 | } | ||
244 | }; | ||
245 | } | ||
246 | |||
247 | let new_target = new_url.into_string(); | ||
232 | let new_title = strip_prefixes_suffixes(title); | 248 | let new_title = strip_prefixes_suffixes(title); |
233 | Some((new_target, new_title.to_string())) | 249 | Some((new_target, new_title.to_string())) |
234 | } | 250 | } |
@@ -412,7 +428,7 @@ fn get_symbol_filename(db: &dyn HirDatabase, definition: &ModuleDef) -> Option<S | |||
412 | ModuleDef::Module(_) => "index.html".to_string(), | 428 | ModuleDef::Module(_) => "index.html".to_string(), |
413 | ModuleDef::Trait(t) => format!("trait.{}.html", t.name(db)), | 429 | ModuleDef::Trait(t) => format!("trait.{}.html", t.name(db)), |
414 | ModuleDef::TypeAlias(t) => format!("type.{}.html", t.name(db)), | 430 | ModuleDef::TypeAlias(t) => format!("type.{}.html", t.name(db)), |
415 | ModuleDef::BuiltinType(t) => format!("primitive.{}.html", t.as_name()), | 431 | ModuleDef::BuiltinType(t) => format!("primitive.{}.html", t.name()), |
416 | ModuleDef::Function(f) => format!("fn.{}.html", f.name(db)), | 432 | ModuleDef::Function(f) => format!("fn.{}.html", f.name(db)), |
417 | ModuleDef::Variant(ev) => { | 433 | ModuleDef::Variant(ev) => { |
418 | format!("enum.{}.html#variant.{}", ev.parent_enum(db).name(db), ev.name(db)) | 434 | format!("enum.{}.html#variant.{}", ev.parent_enum(db).name(db), ev.name(db)) |
@@ -438,10 +454,10 @@ fn get_symbol_fragment(db: &dyn HirDatabase, field_or_assoc: &FieldOrAssocItem) | |||
438 | FieldOrAssocItem::Field(field) => format!("#structfield.{}", field.name(db)), | 454 | FieldOrAssocItem::Field(field) => format!("#structfield.{}", field.name(db)), |
439 | FieldOrAssocItem::AssocItem(assoc) => match assoc { | 455 | FieldOrAssocItem::AssocItem(assoc) => match assoc { |
440 | AssocItem::Function(function) => { | 456 | AssocItem::Function(function) => { |
441 | let is_trait_method = matches!( | 457 | let is_trait_method = function |
442 | function.as_assoc_item(db).map(|assoc| assoc.container(db)), | 458 | .as_assoc_item(db) |
443 | Some(AssocItemContainer::Trait(..)) | 459 | .and_then(|assoc| assoc.containing_trait(db)) |
444 | ); | 460 | .is_some(); |
445 | // This distinction may get more complicated when specialization is available. | 461 | // This distinction may get more complicated when specialization is available. |
446 | // Rustdoc makes this decision based on whether a method 'has defaultness'. | 462 | // Rustdoc makes this decision based on whether a method 'has defaultness'. |
447 | // Currently this is only the case for provided trait methods. | 463 | // Currently this is only the case for provided trait methods. |
diff --git a/crates/ide/src/extend_selection.rs b/crates/ide/src/extend_selection.rs index 17a540972..b540d04fe 100644 --- a/crates/ide/src/extend_selection.rs +++ b/crates/ide/src/extend_selection.rs | |||
@@ -12,15 +12,17 @@ use syntax::{ | |||
12 | 12 | ||
13 | use crate::FileRange; | 13 | use crate::FileRange; |
14 | 14 | ||
15 | // Feature: Extend Selection | 15 | // Feature: Expand and Shrink Selection |
16 | // | 16 | // |
17 | // Extends the current selection to the encompassing syntactic construct | 17 | // Extends or shrinks the current selection to the encompassing syntactic construct |
18 | // (expression, statement, item, module, etc). It works with multiple cursors. | 18 | // (expression, statement, item, module, etc). It works with multiple cursors. |
19 | // | 19 | // |
20 | // This is a standard LSP feature and not a protocol extension. | ||
21 | // | ||
20 | // |=== | 22 | // |=== |
21 | // | Editor | Shortcut | 23 | // | Editor | Shortcut |
22 | // | 24 | // |
23 | // | VS Code | kbd:[Ctrl+Shift+→] | 25 | // | VS Code | kbd:[Alt+Shift+→], kbd:[Alt+Shift+←] |
24 | // |=== | 26 | // |=== |
25 | pub(crate) fn extend_selection(db: &RootDatabase, frange: FileRange) -> TextRange { | 27 | pub(crate) fn extend_selection(db: &RootDatabase, frange: FileRange) -> TextRange { |
26 | let sema = Semantics::new(db); | 28 | let sema = Semantics::new(db); |
@@ -213,8 +215,8 @@ fn extend_ws(root: &SyntaxNode, ws: SyntaxToken, offset: TextSize) -> TextRange | |||
213 | let ws_text = ws.text(); | 215 | let ws_text = ws.text(); |
214 | let suffix = TextRange::new(offset, ws.text_range().end()) - ws.text_range().start(); | 216 | let suffix = TextRange::new(offset, ws.text_range().end()) - ws.text_range().start(); |
215 | let prefix = TextRange::new(ws.text_range().start(), offset) - ws.text_range().start(); | 217 | let prefix = TextRange::new(ws.text_range().start(), offset) - ws.text_range().start(); |
216 | let ws_suffix = &ws_text.as_str()[suffix]; | 218 | let ws_suffix = &ws_text[suffix]; |
217 | let ws_prefix = &ws_text.as_str()[prefix]; | 219 | let ws_prefix = &ws_text[prefix]; |
218 | if ws_text.contains('\n') && !ws_suffix.contains('\n') { | 220 | if ws_text.contains('\n') && !ws_suffix.contains('\n') { |
219 | if let Some(node) = ws.next_sibling_or_token() { | 221 | if let Some(node) = ws.next_sibling_or_token() { |
220 | let start = match ws_prefix.rfind('\n') { | 222 | let start = match ws_prefix.rfind('\n') { |
diff --git a/crates/ide/src/file_structure.rs b/crates/ide/src/file_structure.rs index 32556dad3..26793bdb4 100644 --- a/crates/ide/src/file_structure.rs +++ b/crates/ide/src/file_structure.rs | |||
@@ -1,10 +1,9 @@ | |||
1 | use ide_db::SymbolKind; | ||
1 | use syntax::{ | 2 | use syntax::{ |
2 | ast::{self, AttrsOwner, GenericParamsOwner, NameOwner}, | 3 | ast::{self, AttrsOwner, GenericParamsOwner, NameOwner}, |
3 | match_ast, AstNode, SourceFile, SyntaxNode, TextRange, WalkEvent, | 4 | match_ast, AstNode, SourceFile, SyntaxNode, TextRange, WalkEvent, |
4 | }; | 5 | }; |
5 | 6 | ||
6 | use crate::SymbolKind; | ||
7 | |||
8 | #[derive(Debug, Clone)] | 7 | #[derive(Debug, Clone)] |
9 | pub struct StructureNode { | 8 | pub struct StructureNode { |
10 | pub parent: Option<usize>, | 9 | pub parent: Option<usize>, |
diff --git a/crates/ide/src/fixture.rs b/crates/ide/src/fixture.rs index cc8218885..cc6641ba1 100644 --- a/crates/ide/src/fixture.rs +++ b/crates/ide/src/fixture.rs | |||
@@ -1,5 +1,6 @@ | |||
1 | //! Utilities for creating `Analysis` instances for tests. | 1 | //! Utilities for creating `Analysis` instances for tests. |
2 | use ide_db::base_db::fixture::ChangeFixture; | 2 | use ide_db::base_db::fixture::ChangeFixture; |
3 | use syntax::{TextRange, TextSize}; | ||
3 | use test_utils::{extract_annotations, RangeOrOffset}; | 4 | use test_utils::{extract_annotations, RangeOrOffset}; |
4 | 5 | ||
5 | use crate::{Analysis, AnalysisHost, FileId, FilePosition, FileRange}; | 6 | use crate::{Analysis, AnalysisHost, FileId, FilePosition, FileRange}; |
@@ -68,3 +69,18 @@ pub(crate) fn annotations(ra_fixture: &str) -> (Analysis, FilePosition, Vec<(Fil | |||
68 | .collect(); | 69 | .collect(); |
69 | (host.analysis(), FilePosition { file_id, offset }, annotations) | 70 | (host.analysis(), FilePosition { file_id, offset }, annotations) |
70 | } | 71 | } |
72 | |||
73 | pub(crate) fn nav_target_annotation(ra_fixture: &str) -> (Analysis, FilePosition, FileRange) { | ||
74 | let (analysis, position, mut annotations) = annotations(ra_fixture); | ||
75 | let (mut expected, data) = annotations.pop().unwrap(); | ||
76 | assert!(annotations.is_empty()); | ||
77 | match data.as_str() { | ||
78 | "" => (), | ||
79 | "file" => { | ||
80 | expected.range = | ||
81 | TextRange::up_to(TextSize::of(&*analysis.file_text(expected.file_id).unwrap())) | ||
82 | } | ||
83 | data => panic!("bad data: {}", data), | ||
84 | } | ||
85 | (analysis, position, expected) | ||
86 | } | ||
diff --git a/crates/ide/src/fn_references.rs b/crates/ide/src/fn_references.rs index f6e5a522b..1a99a1f37 100644 --- a/crates/ide/src/fn_references.rs +++ b/crates/ide/src/fn_references.rs | |||
@@ -1,8 +1,8 @@ | |||
1 | //! This module implements a methods and free functions search in the specified file. | 1 | //! This module implements a methods and free functions search in the specified file. |
2 | //! We have to skip tests, so cannot reuse file_structure module. | 2 | //! We have to skip tests, so cannot reuse file_structure module. |
3 | 3 | ||
4 | use assists::utils::test_related_attribute; | ||
5 | use hir::Semantics; | 4 | use hir::Semantics; |
5 | use ide_assists::utils::test_related_attribute; | ||
6 | use ide_db::RootDatabase; | 6 | use ide_db::RootDatabase; |
7 | use syntax::{ast, ast::NameOwner, AstNode, SyntaxNode}; | 7 | use syntax::{ast, ast::NameOwner, AstNode, SyntaxNode}; |
8 | 8 | ||
diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs index a1d2bce1d..abed1969e 100644 --- a/crates/ide/src/goto_definition.rs +++ b/crates/ide/src/goto_definition.rs | |||
@@ -2,16 +2,14 @@ use either::Either; | |||
2 | use hir::{HasAttrs, ModuleDef, Semantics}; | 2 | use hir::{HasAttrs, ModuleDef, Semantics}; |
3 | use ide_db::{ | 3 | use ide_db::{ |
4 | defs::{Definition, NameClass, NameRefClass}, | 4 | defs::{Definition, NameClass, NameRefClass}, |
5 | symbol_index, RootDatabase, | 5 | RootDatabase, |
6 | }; | 6 | }; |
7 | use syntax::{ | 7 | use syntax::{ |
8 | ast, match_ast, AstNode, AstToken, SyntaxKind::*, SyntaxToken, TextSize, TokenAtOffset, T, | 8 | ast, match_ast, AstNode, AstToken, SyntaxKind::*, SyntaxToken, TextSize, TokenAtOffset, T, |
9 | }; | 9 | }; |
10 | 10 | ||
11 | use crate::{ | 11 | use crate::{ |
12 | display::{ToNav, TryToNav}, | 12 | display::TryToNav, doc_links::extract_definitions_from_markdown, runnables::doc_owner_to_def, |
13 | doc_links::extract_definitions_from_markdown, | ||
14 | runnables::doc_owner_to_def, | ||
15 | FilePosition, NavigationTarget, RangeInfo, | 13 | FilePosition, NavigationTarget, RangeInfo, |
16 | }; | 14 | }; |
17 | 15 | ||
@@ -33,33 +31,31 @@ pub(crate) fn goto_definition( | |||
33 | let original_token = pick_best(file.token_at_offset(position.offset))?; | 31 | let original_token = pick_best(file.token_at_offset(position.offset))?; |
34 | let token = sema.descend_into_macros(original_token.clone()); | 32 | let token = sema.descend_into_macros(original_token.clone()); |
35 | let parent = token.parent(); | 33 | let parent = token.parent(); |
36 | if let Some(comment) = ast::Comment::cast(token.clone()) { | 34 | if let Some(comment) = ast::Comment::cast(token) { |
37 | let nav = def_for_doc_comment(&sema, position, &comment)?.try_to_nav(db)?; | 35 | let nav = def_for_doc_comment(&sema, position, &comment)?.try_to_nav(db)?; |
38 | return Some(RangeInfo::new(original_token.text_range(), vec![nav])); | 36 | return Some(RangeInfo::new(original_token.text_range(), vec![nav])); |
39 | } | 37 | } |
40 | 38 | ||
41 | let nav_targets = match_ast! { | 39 | let nav = match_ast! { |
42 | match parent { | 40 | match parent { |
43 | ast::NameRef(name_ref) => { | 41 | ast::NameRef(name_ref) => { |
44 | reference_definition(&sema, Either::Right(&name_ref)).to_vec() | 42 | reference_definition(&sema, Either::Right(&name_ref)) |
45 | }, | 43 | }, |
46 | ast::Name(name) => { | 44 | ast::Name(name) => { |
47 | let def = NameClass::classify(&sema, &name)?.referenced_or_defined(sema.db); | 45 | let def = NameClass::classify(&sema, &name)?.referenced_or_defined(sema.db); |
48 | let nav = def.try_to_nav(sema.db)?; | 46 | def.try_to_nav(sema.db) |
49 | vec![nav] | ||
50 | }, | 47 | }, |
51 | ast::Lifetime(lt) => if let Some(name_class) = NameClass::classify_lifetime(&sema, <) { | 48 | ast::Lifetime(lt) => if let Some(name_class) = NameClass::classify_lifetime(&sema, <) { |
52 | let def = name_class.referenced_or_defined(sema.db); | 49 | let def = name_class.referenced_or_defined(sema.db); |
53 | let nav = def.try_to_nav(sema.db)?; | 50 | def.try_to_nav(sema.db) |
54 | vec![nav] | ||
55 | } else { | 51 | } else { |
56 | reference_definition(&sema, Either::Left(<)).to_vec() | 52 | reference_definition(&sema, Either::Left(<)) |
57 | }, | 53 | }, |
58 | _ => return None, | 54 | _ => return None, |
59 | } | 55 | } |
60 | }; | 56 | }; |
61 | 57 | ||
62 | Some(RangeInfo::new(original_token.text_range(), nav_targets)) | 58 | Some(RangeInfo::new(original_token.text_range(), nav.into_iter().collect())) |
63 | } | 59 | } |
64 | 60 | ||
65 | fn def_for_doc_comment( | 61 | fn def_for_doc_comment( |
@@ -120,63 +116,26 @@ fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> { | |||
120 | } | 116 | } |
121 | } | 117 | } |
122 | 118 | ||
123 | #[derive(Debug)] | ||
124 | pub(crate) enum ReferenceResult { | ||
125 | Exact(NavigationTarget), | ||
126 | Approximate(Vec<NavigationTarget>), | ||
127 | } | ||
128 | |||
129 | impl ReferenceResult { | ||
130 | fn to_vec(self) -> Vec<NavigationTarget> { | ||
131 | match self { | ||
132 | ReferenceResult::Exact(target) => vec![target], | ||
133 | ReferenceResult::Approximate(vec) => vec, | ||
134 | } | ||
135 | } | ||
136 | } | ||
137 | |||
138 | pub(crate) fn reference_definition( | 119 | pub(crate) fn reference_definition( |
139 | sema: &Semantics<RootDatabase>, | 120 | sema: &Semantics<RootDatabase>, |
140 | name_ref: Either<&ast::Lifetime, &ast::NameRef>, | 121 | name_ref: Either<&ast::Lifetime, &ast::NameRef>, |
141 | ) -> ReferenceResult { | 122 | ) -> Option<NavigationTarget> { |
142 | let name_kind = name_ref.either( | 123 | let name_kind = name_ref.either( |
143 | |lifetime| NameRefClass::classify_lifetime(sema, lifetime), | 124 | |lifetime| NameRefClass::classify_lifetime(sema, lifetime), |
144 | |name_ref| NameRefClass::classify(sema, name_ref), | 125 | |name_ref| NameRefClass::classify(sema, name_ref), |
145 | ); | 126 | )?; |
146 | if let Some(def) = name_kind { | 127 | let def = name_kind.referenced(sema.db); |
147 | let def = def.referenced(sema.db); | 128 | def.try_to_nav(sema.db) |
148 | return match def.try_to_nav(sema.db) { | ||
149 | Some(nav) => ReferenceResult::Exact(nav), | ||
150 | None => ReferenceResult::Approximate(Vec::new()), | ||
151 | }; | ||
152 | } | ||
153 | |||
154 | // Fallback index based approach: | ||
155 | let name = name_ref.either(ast::Lifetime::text, ast::NameRef::text); | ||
156 | let navs = | ||
157 | symbol_index::index_resolve(sema.db, name).into_iter().map(|s| s.to_nav(sema.db)).collect(); | ||
158 | ReferenceResult::Approximate(navs) | ||
159 | } | 129 | } |
160 | 130 | ||
161 | #[cfg(test)] | 131 | #[cfg(test)] |
162 | mod tests { | 132 | mod tests { |
163 | use ide_db::base_db::FileRange; | 133 | use ide_db::base_db::FileRange; |
164 | use syntax::{TextRange, TextSize}; | ||
165 | 134 | ||
166 | use crate::fixture; | 135 | use crate::fixture; |
167 | 136 | ||
168 | fn check(ra_fixture: &str) { | 137 | fn check(ra_fixture: &str) { |
169 | let (analysis, position, mut annotations) = fixture::annotations(ra_fixture); | 138 | let (analysis, position, expected) = fixture::nav_target_annotation(ra_fixture); |
170 | let (mut expected, data) = annotations.pop().unwrap(); | ||
171 | match data.as_str() { | ||
172 | "" => (), | ||
173 | "file" => { | ||
174 | expected.range = | ||
175 | TextRange::up_to(TextSize::of(&*analysis.file_text(expected.file_id).unwrap())) | ||
176 | } | ||
177 | data => panic!("bad data: {}", data), | ||
178 | } | ||
179 | |||
180 | let mut navs = | 139 | let mut navs = |
181 | analysis.goto_definition(position).unwrap().expect("no definition found").info; | 140 | analysis.goto_definition(position).unwrap().expect("no definition found").info; |
182 | if navs.len() == 0 { | 141 | if navs.len() == 0 { |
@@ -192,12 +151,12 @@ mod tests { | |||
192 | fn goto_def_for_extern_crate() { | 151 | fn goto_def_for_extern_crate() { |
193 | check( | 152 | check( |
194 | r#" | 153 | r#" |
195 | //- /main.rs crate:main deps:std | 154 | //- /main.rs crate:main deps:std |
196 | extern crate std$0; | 155 | extern crate std$0; |
197 | //- /std/lib.rs crate:std | 156 | //- /std/lib.rs crate:std |
198 | // empty | 157 | // empty |
199 | //^ file | 158 | //^ file |
200 | "#, | 159 | "#, |
201 | ) | 160 | ) |
202 | } | 161 | } |
203 | 162 | ||
@@ -205,12 +164,12 @@ mod tests { | |||
205 | fn goto_def_for_renamed_extern_crate() { | 164 | fn goto_def_for_renamed_extern_crate() { |
206 | check( | 165 | check( |
207 | r#" | 166 | r#" |
208 | //- /main.rs crate:main deps:std | 167 | //- /main.rs crate:main deps:std |
209 | extern crate std as abc$0; | 168 | extern crate std as abc$0; |
210 | //- /std/lib.rs crate:std | 169 | //- /std/lib.rs crate:std |
211 | // empty | 170 | // empty |
212 | //^ file | 171 | //^ file |
213 | "#, | 172 | "#, |
214 | ) | 173 | ) |
215 | } | 174 | } |
216 | 175 | ||
@@ -297,13 +256,13 @@ fn bar() { | |||
297 | fn goto_def_for_macros_from_other_crates() { | 256 | fn goto_def_for_macros_from_other_crates() { |
298 | check( | 257 | check( |
299 | r#" | 258 | r#" |
300 | //- /lib.rs | 259 | //- /lib.rs crate:main deps:foo |
301 | use foo::foo; | 260 | use foo::foo; |
302 | fn bar() { | 261 | fn bar() { |
303 | $0foo!(); | 262 | $0foo!(); |
304 | } | 263 | } |
305 | 264 | ||
306 | //- /foo/lib.rs | 265 | //- /foo/lib.rs crate:foo |
307 | #[macro_export] | 266 | #[macro_export] |
308 | macro_rules! foo { () => { () } } | 267 | macro_rules! foo { () => { () } } |
309 | //^^^ | 268 | //^^^ |
@@ -315,10 +274,10 @@ macro_rules! foo { () => { () } } | |||
315 | fn goto_def_for_macros_in_use_tree() { | 274 | fn goto_def_for_macros_in_use_tree() { |
316 | check( | 275 | check( |
317 | r#" | 276 | r#" |
318 | //- /lib.rs | 277 | //- /lib.rs crate:main deps:foo |
319 | use foo::foo$0; | 278 | use foo::foo$0; |
320 | 279 | ||
321 | //- /foo/lib.rs | 280 | //- /foo/lib.rs crate:foo |
322 | #[macro_export] | 281 | #[macro_export] |
323 | macro_rules! foo { () => { () } } | 282 | macro_rules! foo { () => { () } } |
324 | //^^^ | 283 | //^^^ |
@@ -976,10 +935,10 @@ type Alias<T> = T$0; | |||
976 | fn goto_def_for_macro_container() { | 935 | fn goto_def_for_macro_container() { |
977 | check( | 936 | check( |
978 | r#" | 937 | r#" |
979 | //- /lib.rs | 938 | //- /lib.rs crate:main deps:foo |
980 | foo::module$0::mac!(); | 939 | foo::module$0::mac!(); |
981 | 940 | ||
982 | //- /foo/lib.rs | 941 | //- /foo/lib.rs crate:foo |
983 | pub mod module { | 942 | pub mod module { |
984 | //^^^^^^ | 943 | //^^^^^^ |
985 | #[macro_export] | 944 | #[macro_export] |
diff --git a/crates/ide/src/goto_implementation.rs b/crates/ide/src/goto_implementation.rs index 761a98b2c..3990305fc 100644 --- a/crates/ide/src/goto_implementation.rs +++ b/crates/ide/src/goto_implementation.rs | |||
@@ -23,7 +23,7 @@ pub(crate) fn goto_implementation( | |||
23 | 23 | ||
24 | let krate = sema.to_module_def(position.file_id)?.krate(); | 24 | let krate = sema.to_module_def(position.file_id)?.krate(); |
25 | 25 | ||
26 | if let Some(nominal_def) = find_node_at_offset::<ast::AdtDef>(&syntax, position.offset) { | 26 | if let Some(nominal_def) = find_node_at_offset::<ast::Adt>(&syntax, position.offset) { |
27 | return Some(RangeInfo::new( | 27 | return Some(RangeInfo::new( |
28 | nominal_def.syntax().text_range(), | 28 | nominal_def.syntax().text_range(), |
29 | impls_for_def(&sema, &nominal_def, krate)?, | 29 | impls_for_def(&sema, &nominal_def, krate)?, |
@@ -40,13 +40,13 @@ pub(crate) fn goto_implementation( | |||
40 | 40 | ||
41 | fn impls_for_def( | 41 | fn impls_for_def( |
42 | sema: &Semantics<RootDatabase>, | 42 | sema: &Semantics<RootDatabase>, |
43 | node: &ast::AdtDef, | 43 | node: &ast::Adt, |
44 | krate: Crate, | 44 | krate: Crate, |
45 | ) -> Option<Vec<NavigationTarget>> { | 45 | ) -> Option<Vec<NavigationTarget>> { |
46 | let ty = match node { | 46 | let ty = match node { |
47 | ast::AdtDef::Struct(def) => sema.to_def(def)?.ty(sema.db), | 47 | ast::Adt::Struct(def) => sema.to_def(def)?.ty(sema.db), |
48 | ast::AdtDef::Enum(def) => sema.to_def(def)?.ty(sema.db), | 48 | ast::Adt::Enum(def) => sema.to_def(def)?.ty(sema.db), |
49 | ast::AdtDef::Union(def) => sema.to_def(def)?.ty(sema.db), | 49 | ast::Adt::Union(def) => sema.to_def(def)?.ty(sema.db), |
50 | }; | 50 | }; |
51 | 51 | ||
52 | let impls = Impl::all_in_crate(sema.db, krate); | 52 | let impls = Impl::all_in_crate(sema.db, krate); |
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs index 2024acd94..20b799490 100644 --- a/crates/ide/src/hover.rs +++ b/crates/ide/src/hover.rs | |||
@@ -2,8 +2,8 @@ use hir::{ | |||
2 | Adt, AsAssocItem, AssocItemContainer, FieldSource, GenericParam, HasAttrs, HasSource, | 2 | Adt, AsAssocItem, AssocItemContainer, FieldSource, GenericParam, HasAttrs, HasSource, |
3 | HirDisplay, Module, ModuleDef, ModuleSource, Semantics, | 3 | HirDisplay, Module, ModuleDef, ModuleSource, Semantics, |
4 | }; | 4 | }; |
5 | use ide_db::base_db::SourceDatabase; | ||
6 | use ide_db::{ | 5 | use ide_db::{ |
6 | base_db::SourceDatabase, | ||
7 | defs::{Definition, NameClass, NameRefClass}, | 7 | defs::{Definition, NameClass, NameRefClass}, |
8 | RootDatabase, | 8 | RootDatabase, |
9 | }; | 9 | }; |
@@ -94,7 +94,12 @@ pub(crate) fn hover( | |||
94 | let node = token.parent(); | 94 | let node = token.parent(); |
95 | let definition = match_ast! { | 95 | let definition = match_ast! { |
96 | match node { | 96 | match node { |
97 | ast::Name(name) => NameClass::classify(&sema, &name).and_then(|d| d.defined(sema.db)), | 97 | // we don't use NameClass::referenced_or_defined here as we do not want to resolve |
98 | // field pattern shorthands to their definition | ||
99 | ast::Name(name) => NameClass::classify(&sema, &name).and_then(|class| match class { | ||
100 | NameClass::ConstReference(def) => Some(def), | ||
101 | def => def.defined(sema.db), | ||
102 | }), | ||
98 | ast::NameRef(name_ref) => NameRefClass::classify(&sema, &name_ref).map(|d| d.referenced(sema.db)), | 103 | ast::NameRef(name_ref) => NameRefClass::classify(&sema, &name_ref).map(|d| d.referenced(sema.db)), |
99 | ast::Lifetime(lifetime) => NameClass::classify_lifetime(&sema, &lifetime) | 104 | ast::Lifetime(lifetime) => NameClass::classify_lifetime(&sema, &lifetime) |
100 | .map_or_else(|| NameRefClass::classify_lifetime(&sema, &lifetime).map(|d| d.referenced(sema.db)), |d| d.defined(sema.db)), | 105 | .map_or_else(|| NameRefClass::classify_lifetime(&sema, &lifetime).map(|d| d.referenced(sema.db)), |d| d.defined(sema.db)), |
@@ -182,12 +187,7 @@ fn runnable_action( | |||
182 | ) -> Option<HoverAction> { | 187 | ) -> Option<HoverAction> { |
183 | match def { | 188 | match def { |
184 | Definition::ModuleDef(it) => match it { | 189 | Definition::ModuleDef(it) => match it { |
185 | ModuleDef::Module(it) => match it.definition_source(sema.db).value { | 190 | ModuleDef::Module(it) => runnable_mod(&sema, it).map(|it| HoverAction::Runnable(it)), |
186 | ModuleSource::Module(it) => { | ||
187 | runnable_mod(&sema, it).map(|it| HoverAction::Runnable(it)) | ||
188 | } | ||
189 | _ => None, | ||
190 | }, | ||
191 | ModuleDef::Function(func) => { | 191 | ModuleDef::Function(func) => { |
192 | let src = func.source(sema.db)?; | 192 | let src = func.source(sema.db)?; |
193 | if src.file_id != file_id.into() { | 193 | if src.file_id != file_id.into() { |
@@ -326,6 +326,7 @@ fn hover_for_definition(db: &RootDatabase, def: Definition) -> Option<Markup> { | |||
326 | match it.definition_source(db).value { | 326 | match it.definition_source(db).value { |
327 | ModuleSource::Module(it) => it.short_label(), | 327 | ModuleSource::Module(it) => it.short_label(), |
328 | ModuleSource::SourceFile(it) => it.short_label(), | 328 | ModuleSource::SourceFile(it) => it.short_label(), |
329 | ModuleSource::BlockExpr(it) => it.short_label(), | ||
329 | }, | 330 | }, |
330 | mod_path, | 331 | mod_path, |
331 | ), | 332 | ), |
@@ -338,7 +339,7 @@ fn hover_for_definition(db: &RootDatabase, def: Definition) -> Option<Markup> { | |||
338 | ModuleDef::Static(it) => from_def_source(db, it, mod_path), | 339 | ModuleDef::Static(it) => from_def_source(db, it, mod_path), |
339 | ModuleDef::Trait(it) => from_def_source(db, it, mod_path), | 340 | ModuleDef::Trait(it) => from_def_source(db, it, mod_path), |
340 | ModuleDef::TypeAlias(it) => from_def_source(db, it, mod_path), | 341 | ModuleDef::TypeAlias(it) => from_def_source(db, it, mod_path), |
341 | ModuleDef::BuiltinType(it) => Some(Markup::fenced_block(&it)), | 342 | ModuleDef::BuiltinType(it) => Some(Markup::fenced_block(&it.name())), |
342 | }, | 343 | }, |
343 | Definition::Local(it) => Some(Markup::fenced_block(&it.ty(db).display(db))), | 344 | Definition::Local(it) => Some(Markup::fenced_block(&it.ty(db).display(db))), |
344 | Definition::SelfType(impl_def) => { | 345 | Definition::SelfType(impl_def) => { |
@@ -1830,6 +1831,35 @@ pub struct B$0ar | |||
1830 | "#]], | 1831 | "#]], |
1831 | ); | 1832 | ); |
1832 | } | 1833 | } |
1834 | #[test] | ||
1835 | fn test_hover_intra_link_reference_to_trait_method() { | ||
1836 | check( | ||
1837 | r#" | ||
1838 | pub trait Foo { | ||
1839 | fn buzz() -> usize; | ||
1840 | } | ||
1841 | /// [Foo][buzz] | ||
1842 | /// | ||
1843 | /// [buzz]: Foo::buzz | ||
1844 | pub struct B$0ar | ||
1845 | "#, | ||
1846 | expect | ||
1860 | "#]], | ||
1861 | ); | ||
1862 | } | ||
1833 | 1863 | ||
1834 | #[test] | 1864 | #[test] |
1835 | fn test_hover_external_url() { | 1865 | fn test_hover_external_url() { |
@@ -3387,7 +3417,7 @@ impl<T> Foo<T$0> {} | |||
3387 | ``` | 3417 | ``` |
3388 | "#]], | 3418 | "#]], |
3389 | ); | 3419 | ); |
3390 | // lifetimes aren't being substituted yet | 3420 | // lifetimes bounds arent being tracked yet |
3391 | check( | 3421 | check( |
3392 | r#" | 3422 | r#" |
3393 | struct Foo<T>(T); | 3423 | struct Foo<T>(T); |
@@ -3397,7 +3427,7 @@ impl<T: 'static> Foo<T$0> {} | |||
3397 | *T* | 3427 | *T* |
3398 | 3428 | ||
3399 | ```rust | 3429 | ```rust |
3400 | T: {error} | 3430 | T |
3401 | ``` | 3431 | ``` |
3402 | "#]], | 3432 | "#]], |
3403 | ); | 3433 | ); |
@@ -3419,4 +3449,80 @@ impl<const LEN: usize> Foo<LEN$0> {} | |||
3419 | "#]], | 3449 | "#]], |
3420 | ); | 3450 | ); |
3421 | } | 3451 | } |
3452 | |||
3453 | #[test] | ||
3454 | fn hover_const_pat() { | ||
3455 | check( | ||
3456 | r#" | ||
3457 | /// This is a doc | ||
3458 | const FOO: usize = 3; | ||
3459 | fn foo() { | ||
3460 | match 5 { | ||
3461 | FOO$0 => (), | ||
3462 | _ => () | ||
3463 | } | ||
3464 | } | ||
3465 | "#, | ||
3466 | expect![[r#" | ||
3467 | *FOO* | ||
3468 | |||
3469 | ```rust | ||
3470 | test | ||
3471 | ``` | ||
3472 | |||
3473 | ```rust | ||
3474 | const FOO: usize = 3 | ||
3475 | ``` | ||
3476 | |||
3477 | --- | ||
3478 | |||
3479 | This is a doc | ||
3480 | "#]], | ||
3481 | ); | ||
3482 | } | ||
3483 | |||
3484 | #[test] | ||
3485 | fn hover_mod_def() { | ||
3486 | check( | ||
3487 | r#" | ||
3488 | //- /main.rs | ||
3489 | mod foo$0; | ||
3490 | //- /foo.rs | ||
3491 | //! For the horde! | ||
3492 | "#, | ||
3493 | expect![[r#" | ||
3494 | *foo* | ||
3495 | For the horde! | ||
3496 | "#]], | ||
3497 | ); | ||
3498 | } | ||
3499 | |||
3500 | #[test] | ||
3501 | fn hover_self_in_use() { | ||
3502 | check( | ||
3503 | r#" | ||
3504 | //! This should not appear | ||
3505 | mod foo { | ||
3506 | /// But this should appear | ||
3507 | pub mod bar {} | ||
3508 | } | ||
3509 | use foo::bar::{self$0}; | ||
3510 | "#, | ||
3511 | expect![[r#" | ||
3512 | *self* | ||
3513 | |||
3514 | ```rust | ||
3515 | test::foo | ||
3516 | ``` | ||
3517 | |||
3518 | ```rust | ||
3519 | pub mod bar | ||
3520 | ``` | ||
3521 | |||
3522 | --- | ||
3523 | |||
3524 | But this should appear | ||
3525 | "#]], | ||
3526 | ); | ||
3527 | } | ||
3422 | } | 3528 | } |
diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs index a2039fcc7..4ceb20742 100644 --- a/crates/ide/src/inlay_hints.rs +++ b/crates/ide/src/inlay_hints.rs | |||
@@ -109,26 +109,31 @@ fn get_chaining_hints( | |||
109 | // Chaining can be defined as an expression whose next sibling tokens are newline and dot | 109 | // Chaining can be defined as an expression whose next sibling tokens are newline and dot |
110 | // Ignoring extra whitespace and comments | 110 | // Ignoring extra whitespace and comments |
111 | let next = tokens.next()?.kind(); | 111 | let next = tokens.next()?.kind(); |
112 | let next_next = tokens.next()?.kind(); | 112 | if next == SyntaxKind::WHITESPACE { |
113 | if next == SyntaxKind::WHITESPACE && next_next == T![.] { | 113 | let mut next_next = tokens.next()?.kind(); |
114 | let ty = sema.type_of_expr(&expr)?; | 114 | while next_next == SyntaxKind::WHITESPACE { |
115 | if ty.is_unknown() { | 115 | next_next = tokens.next()?.kind(); |
116 | return None; | ||
117 | } | 116 | } |
118 | if matches!(expr, ast::Expr::PathExpr(_)) { | 117 | if next_next == T![.] { |
119 | if let Some(hir::Adt::Struct(st)) = ty.as_adt() { | 118 | let ty = sema.type_of_expr(&expr)?; |
120 | if st.fields(sema.db).is_empty() { | 119 | if ty.is_unknown() { |
121 | return None; | 120 | return None; |
121 | } | ||
122 | if matches!(expr, ast::Expr::PathExpr(_)) { | ||
123 | if let Some(hir::Adt::Struct(st)) = ty.as_adt() { | ||
124 | if st.fields(sema.db).is_empty() { | ||
125 | return None; | ||
126 | } | ||
122 | } | 127 | } |
123 | } | 128 | } |
129 | acc.push(InlayHint { | ||
130 | range: expr.syntax().text_range(), | ||
131 | kind: InlayKind::ChainingHint, | ||
132 | label: hint_iterator(sema, &famous_defs, config, &ty).unwrap_or_else(|| { | ||
133 | ty.display_truncated(sema.db, config.max_length).to_string().into() | ||
134 | }), | ||
135 | }); | ||
124 | } | 136 | } |
125 | acc.push(InlayHint { | ||
126 | range: expr.syntax().text_range(), | ||
127 | kind: InlayKind::ChainingHint, | ||
128 | label: hint_iterator(sema, &famous_defs, config, &ty).unwrap_or_else(|| { | ||
129 | ty.display_truncated(sema.db, config.max_length).to_string().into() | ||
130 | }), | ||
131 | }); | ||
132 | } | 137 | } |
133 | Some(()) | 138 | Some(()) |
134 | } | 139 | } |
@@ -411,13 +416,16 @@ fn get_string_representation(expr: &ast::Expr) -> Option<String> { | |||
411 | match expr { | 416 | match expr { |
412 | ast::Expr::MethodCallExpr(method_call_expr) => { | 417 | ast::Expr::MethodCallExpr(method_call_expr) => { |
413 | let name_ref = method_call_expr.name_ref()?; | 418 | let name_ref = method_call_expr.name_ref()?; |
414 | match name_ref.text().as_str() { | 419 | match name_ref.text() { |
415 | "clone" => method_call_expr.receiver().map(|rec| rec.to_string()), | 420 | "clone" => method_call_expr.receiver().map(|rec| rec.to_string()), |
416 | name_ref => Some(name_ref.to_owned()), | 421 | name_ref => Some(name_ref.to_owned()), |
417 | } | 422 | } |
418 | } | 423 | } |
424 | ast::Expr::FieldExpr(field_expr) => Some(field_expr.name_ref()?.to_string()), | ||
425 | ast::Expr::PathExpr(path_expr) => Some(path_expr.to_string()), | ||
426 | ast::Expr::PrefixExpr(prefix_expr) => get_string_representation(&prefix_expr.expr()?), | ||
419 | ast::Expr::RefExpr(ref_expr) => get_string_representation(&ref_expr.expr()?), | 427 | ast::Expr::RefExpr(ref_expr) => get_string_representation(&ref_expr.expr()?), |
420 | _ => Some(expr.to_string()), | 428 | _ => None, |
421 | } | 429 | } |
422 | } | 430 | } |
423 | 431 | ||
@@ -651,6 +659,7 @@ fn main() { | |||
651 | let test = "test"; | 659 | let test = "test"; |
652 | //^^^^ &str | 660 | //^^^^ &str |
653 | let test = InnerStruct {}; | 661 | let test = InnerStruct {}; |
662 | //^^^^ InnerStruct | ||
654 | 663 | ||
655 | let test = unresolved(); | 664 | let test = unresolved(); |
656 | 665 | ||
@@ -979,6 +988,7 @@ struct C; | |||
979 | fn main() { | 988 | fn main() { |
980 | let c = A(B(C)) | 989 | let c = A(B(C)) |
981 | .into_b() // This is a comment | 990 | .into_b() // This is a comment |
991 | // This is another comment | ||
982 | .into_c(); | 992 | .into_c(); |
983 | } | 993 | } |
984 | "#, | 994 | "#, |
@@ -1438,4 +1448,19 @@ fn main() { | |||
1438 | "#, | 1448 | "#, |
1439 | ) | 1449 | ) |
1440 | } | 1450 | } |
1451 | |||
1452 | #[test] | ||
1453 | fn param_name_hints_show_for_literals() { | ||
1454 | check( | ||
1455 | r#"pub fn test(a: i32, b: i32) -> [i32; 2] { [a, b] } | ||
1456 | fn main() { | ||
1457 | test( | ||
1458 | 0x0fab272b, | ||
1459 | //^^^^^^^^^^ a | ||
1460 | 0x0fab272b | ||
1461 | //^^^^^^^^^^ b | ||
1462 | ); | ||
1463 | }"#, | ||
1464 | ) | ||
1465 | } | ||
1441 | } | 1466 | } |
diff --git a/crates/ide/src/join_lines.rs b/crates/ide/src/join_lines.rs index 981467c8d..2c077ed1f 100644 --- a/crates/ide/src/join_lines.rs +++ b/crates/ide/src/join_lines.rs | |||
@@ -1,4 +1,4 @@ | |||
1 | use assists::utils::extract_trivial_expression; | 1 | use ide_assists::utils::extract_trivial_expression; |
2 | use itertools::Itertools; | 2 | use itertools::Itertools; |
3 | use syntax::{ | 3 | use syntax::{ |
4 | algo::non_trivia_sibling, | 4 | algo::non_trivia_sibling, |
@@ -59,7 +59,7 @@ fn remove_newline(edit: &mut TextEditBuilder, token: &SyntaxToken, offset: TextS | |||
59 | // The node is either the first or the last in the file | 59 | // The node is either the first or the last in the file |
60 | let suff = &token.text()[TextRange::new( | 60 | let suff = &token.text()[TextRange::new( |
61 | offset - token.text_range().start() + TextSize::of('\n'), | 61 | offset - token.text_range().start() + TextSize::of('\n'), |
62 | TextSize::of(token.text().as_str()), | 62 | TextSize::of(token.text()), |
63 | )]; | 63 | )]; |
64 | let spaces = suff.bytes().take_while(|&b| b == b' ').count(); | 64 | let spaces = suff.bytes().take_while(|&b| b == b' ').count(); |
65 | 65 | ||
@@ -270,27 +270,28 @@ fn foo() { | |||
270 | 270 | ||
271 | #[test] | 271 | #[test] |
272 | fn test_join_lines_diverging_block() { | 272 | fn test_join_lines_diverging_block() { |
273 | let before = r" | 273 | check_join_lines( |
274 | fn foo() { | 274 | r" |
275 | loop { | 275 | fn foo() { |
276 | match x { | 276 | loop { |
277 | 92 => $0{ | 277 | match x { |
278 | continue; | 278 | 92 => $0{ |
279 | } | 279 | continue; |
280 | } | ||
281 | } | ||
282 | } | ||
283 | "; | ||
284 | let after = r" | ||
285 | fn foo() { | ||
286 | loop { | ||
287 | match x { | ||
288 | 92 => $0continue, | ||
289 | } | ||
290 | } | ||
291 | } | 280 | } |
292 | "; | 281 | } |
293 | check_join_lines(before, after); | 282 | } |
283 | } | ||
284 | ", | ||
285 | r" | ||
286 | fn foo() { | ||
287 | loop { | ||
288 | match x { | ||
289 | 92 => $0continue, | ||
290 | } | ||
291 | } | ||
292 | } | ||
293 | ", | ||
294 | ); | ||
294 | } | 295 | } |
295 | 296 | ||
296 | #[test] | 297 | #[test] |
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs index 6c94c26b5..b600178ee 100644 --- a/crates/ide/src/lib.rs +++ b/crates/ide/src/lib.rs | |||
@@ -22,6 +22,7 @@ mod markup; | |||
22 | mod prime_caches; | 22 | mod prime_caches; |
23 | mod display; | 23 | mod display; |
24 | 24 | ||
25 | mod annotations; | ||
25 | mod call_hierarchy; | 26 | mod call_hierarchy; |
26 | mod diagnostics; | 27 | mod diagnostics; |
27 | mod expand_macro; | 28 | mod expand_macro; |
@@ -63,9 +64,10 @@ use syntax::SourceFile; | |||
63 | use crate::display::ToNav; | 64 | use crate::display::ToNav; |
64 | 65 | ||
65 | pub use crate::{ | 66 | pub use crate::{ |
67 | annotations::{Annotation, AnnotationConfig, AnnotationKind}, | ||
66 | call_hierarchy::CallItem, | 68 | call_hierarchy::CallItem, |
67 | diagnostics::{Diagnostic, DiagnosticsConfig, Fix, Severity}, | 69 | diagnostics::{Diagnostic, DiagnosticsConfig, Fix, Severity}, |
68 | display::navigation_target::{NavigationTarget, SymbolKind}, | 70 | display::navigation_target::NavigationTarget, |
69 | expand_macro::ExpandedMacro, | 71 | expand_macro::ExpandedMacro, |
70 | file_structure::StructureNode, | 72 | file_structure::StructureNode, |
71 | folding_ranges::{Fold, FoldKind}, | 73 | folding_ranges::{Fold, FoldKind}, |
@@ -73,19 +75,19 @@ pub use crate::{ | |||
73 | inlay_hints::{InlayHint, InlayHintsConfig, InlayKind}, | 75 | inlay_hints::{InlayHint, InlayHintsConfig, InlayKind}, |
74 | markup::Markup, | 76 | markup::Markup, |
75 | prime_caches::PrimeCachesProgress, | 77 | prime_caches::PrimeCachesProgress, |
76 | references::{rename::RenameError, Declaration, ReferenceSearchResult}, | 78 | references::{rename::RenameError, ReferenceSearchResult}, |
77 | runnables::{Runnable, RunnableKind, TestId}, | 79 | runnables::{Runnable, RunnableKind, TestId}, |
78 | syntax_highlighting::{ | 80 | syntax_highlighting::{ |
79 | tags::{Highlight, HlMod, HlMods, HlPunct, HlTag}, | 81 | tags::{Highlight, HlMod, HlMods, HlPunct, HlTag}, |
80 | HlRange, | 82 | HlRange, |
81 | }, | 83 | }, |
82 | }; | 84 | }; |
83 | pub use assists::{Assist, AssistConfig, AssistId, AssistKind}; | 85 | pub use hir::{Documentation, Semantics}; |
84 | pub use completion::{ | 86 | pub use ide_assists::{Assist, AssistConfig, AssistId, AssistKind}; |
87 | pub use ide_completion::{ | ||
85 | CompletionConfig, CompletionItem, CompletionItemKind, CompletionScore, ImportEdit, | 88 | CompletionConfig, CompletionItem, CompletionItemKind, CompletionScore, ImportEdit, |
86 | InsertTextFormat, | 89 | InsertTextFormat, |
87 | }; | 90 | }; |
88 | pub use hir::{Documentation, Semantics}; | ||
89 | pub use ide_db::{ | 91 | pub use ide_db::{ |
90 | base_db::{ | 92 | base_db::{ |
91 | Canceled, Change, CrateGraph, CrateId, Edition, FileId, FilePosition, FileRange, | 93 | Canceled, Change, CrateGraph, CrateId, Edition, FileId, FilePosition, FileRange, |
@@ -93,13 +95,13 @@ pub use ide_db::{ | |||
93 | }, | 95 | }, |
94 | call_info::CallInfo, | 96 | call_info::CallInfo, |
95 | label::Label, | 97 | label::Label, |
96 | line_index::{LineCol, LineIndex}, | 98 | line_index::{LineCol, LineColUtf16, LineIndex}, |
97 | search::{FileReference, ReferenceAccess, ReferenceKind, SearchScope}, | 99 | search::{ReferenceAccess, SearchScope}, |
98 | source_change::{FileSystemEdit, SourceChange}, | 100 | source_change::{FileSystemEdit, SourceChange}, |
99 | symbol_index::Query, | 101 | symbol_index::Query, |
100 | RootDatabase, | 102 | RootDatabase, |
101 | }; | 103 | }; |
102 | pub use ssr::SsrError; | 104 | pub use ide_ssr::SsrError; |
103 | pub use syntax::{TextRange, TextSize}; | 105 | pub use syntax::{TextRange, TextSize}; |
104 | pub use text_edit::{Indel, TextEdit}; | 106 | pub use text_edit::{Indel, TextEdit}; |
105 | 107 | ||
@@ -369,9 +371,7 @@ impl Analysis { | |||
369 | position: FilePosition, | 371 | position: FilePosition, |
370 | search_scope: Option<SearchScope>, | 372 | search_scope: Option<SearchScope>, |
371 | ) -> Cancelable<Option<ReferenceSearchResult>> { | 373 | ) -> Cancelable<Option<ReferenceSearchResult>> { |
372 | self.with_db(|db| { | 374 | self.with_db(|db| references::find_all_refs(&Semantics::new(db), position, search_scope)) |
373 | references::find_all_refs(&Semantics::new(db), position, search_scope).map(|it| it.info) | ||
374 | }) | ||
375 | } | 375 | } |
376 | 376 | ||
377 | /// Finds all methods and free functions for the file. Does not return tests! | 377 | /// Finds all methods and free functions for the file. Does not return tests! |
@@ -468,7 +468,7 @@ impl Analysis { | |||
468 | config: &CompletionConfig, | 468 | config: &CompletionConfig, |
469 | position: FilePosition, | 469 | position: FilePosition, |
470 | ) -> Cancelable<Option<Vec<CompletionItem>>> { | 470 | ) -> Cancelable<Option<Vec<CompletionItem>>> { |
471 | self.with_db(|db| completion::completions(db, config, position).map(Into::into)) | 471 | self.with_db(|db| ide_completion::completions(db, config, position).map(Into::into)) |
472 | } | 472 | } |
473 | 473 | ||
474 | /// Resolves additional completion data at the position given. | 474 | /// Resolves additional completion data at the position given. |
@@ -478,15 +478,17 @@ impl Analysis { | |||
478 | position: FilePosition, | 478 | position: FilePosition, |
479 | full_import_path: &str, | 479 | full_import_path: &str, |
480 | imported_name: String, | 480 | imported_name: String, |
481 | import_for_trait_assoc_item: bool, | ||
481 | ) -> Cancelable<Vec<TextEdit>> { | 482 | ) -> Cancelable<Vec<TextEdit>> { |
482 | Ok(self | 483 | Ok(self |
483 | .with_db(|db| { | 484 | .with_db(|db| { |
484 | completion::resolve_completion_edits( | 485 | ide_completion::resolve_completion_edits( |
485 | db, | 486 | db, |
486 | config, | 487 | config, |
487 | position, | 488 | position, |
488 | full_import_path, | 489 | full_import_path, |
489 | imported_name, | 490 | imported_name, |
491 | import_for_trait_assoc_item, | ||
490 | ) | 492 | ) |
491 | })? | 493 | })? |
492 | .unwrap_or_default()) | 494 | .unwrap_or_default()) |
@@ -520,7 +522,7 @@ impl Analysis { | |||
520 | &self, | 522 | &self, |
521 | position: FilePosition, | 523 | position: FilePosition, |
522 | new_name: &str, | 524 | new_name: &str, |
523 | ) -> Cancelable<Result<RangeInfo<SourceChange>, RenameError>> { | 525 | ) -> Cancelable<Result<SourceChange, RenameError>> { |
524 | self.with_db(|db| references::rename::rename(db, position, new_name)) | 526 | self.with_db(|db| references::rename::rename(db, position, new_name)) |
525 | } | 527 | } |
526 | 528 | ||
@@ -547,14 +549,27 @@ impl Analysis { | |||
547 | selections: Vec<FileRange>, | 549 | selections: Vec<FileRange>, |
548 | ) -> Cancelable<Result<SourceChange, SsrError>> { | 550 | ) -> Cancelable<Result<SourceChange, SsrError>> { |
549 | self.with_db(|db| { | 551 | self.with_db(|db| { |
550 | let rule: ssr::SsrRule = query.parse()?; | 552 | let rule: ide_ssr::SsrRule = query.parse()?; |
551 | let mut match_finder = ssr::MatchFinder::in_context(db, resolve_context, selections); | 553 | let mut match_finder = |
554 | ide_ssr::MatchFinder::in_context(db, resolve_context, selections); | ||
552 | match_finder.add_rule(rule)?; | 555 | match_finder.add_rule(rule)?; |
553 | let edits = if parse_only { Default::default() } else { match_finder.edits() }; | 556 | let edits = if parse_only { Default::default() } else { match_finder.edits() }; |
554 | Ok(SourceChange::from(edits)) | 557 | Ok(SourceChange::from(edits)) |
555 | }) | 558 | }) |
556 | } | 559 | } |
557 | 560 | ||
561 | pub fn annotations( | ||
562 | &self, | ||
563 | file_id: FileId, | ||
564 | config: AnnotationConfig, | ||
565 | ) -> Cancelable<Vec<Annotation>> { | ||
566 | self.with_db(|db| annotations::annotations(db, file_id, config)) | ||
567 | } | ||
568 | |||
569 | pub fn resolve_annotation(&self, annotation: Annotation) -> Cancelable<Annotation> { | ||
570 | self.with_db(|db| annotations::resolve_annotation(db, annotation)) | ||
571 | } | ||
572 | |||
558 | /// Performs an operation on that may be Canceled. | 573 | /// Performs an operation on that may be Canceled. |
559 | fn with_db<F, T>(&self, f: F) -> Cancelable<T> | 574 | fn with_db<F, T>(&self, f: F) -> Cancelable<T> |
560 | where | 575 | where |
diff --git a/crates/ide/src/parent_module.rs b/crates/ide/src/parent_module.rs index d343638fb..ddbaf22b7 100644 --- a/crates/ide/src/parent_module.rs +++ b/crates/ide/src/parent_module.rs | |||
@@ -63,57 +63,62 @@ pub(crate) fn crate_for(db: &RootDatabase, file_id: FileId) -> Vec<CrateId> { | |||
63 | 63 | ||
64 | #[cfg(test)] | 64 | #[cfg(test)] |
65 | mod tests { | 65 | mod tests { |
66 | use ide_db::base_db::FileRange; | ||
66 | use test_utils::mark; | 67 | use test_utils::mark; |
67 | 68 | ||
68 | use crate::fixture::{self}; | 69 | use crate::fixture; |
70 | |||
71 | fn check(ra_fixture: &str) { | ||
72 | let (analysis, position, expected) = fixture::nav_target_annotation(ra_fixture); | ||
73 | let mut navs = analysis.parent_module(position).unwrap(); | ||
74 | assert_eq!(navs.len(), 1); | ||
75 | let nav = navs.pop().unwrap(); | ||
76 | assert_eq!(expected, FileRange { file_id: nav.file_id, range: nav.focus_or_full_range() }); | ||
77 | } | ||
69 | 78 | ||
70 | #[test] | 79 | #[test] |
71 | fn test_resolve_parent_module() { | 80 | fn test_resolve_parent_module() { |
72 | let (analysis, pos) = fixture::position( | 81 | check( |
73 | " | 82 | r#" |
74 | //- /lib.rs | 83 | //- /lib.rs |
75 | mod foo; | 84 | mod foo; |
76 | //- /foo.rs | 85 | //^^^ |
77 | $0// empty | 86 | |
78 | ", | 87 | //- /foo.rs |
88 | $0// empty | ||
89 | "#, | ||
79 | ); | 90 | ); |
80 | let nav = analysis.parent_module(pos).unwrap().pop().unwrap(); | ||
81 | nav.assert_match("foo Module FileId(0) 0..8"); | ||
82 | } | 91 | } |
83 | 92 | ||
84 | #[test] | 93 | #[test] |
85 | fn test_resolve_parent_module_on_module_decl() { | 94 | fn test_resolve_parent_module_on_module_decl() { |
86 | mark::check!(test_resolve_parent_module_on_module_decl); | 95 | mark::check!(test_resolve_parent_module_on_module_decl); |
87 | let (analysis, pos) = fixture::position( | 96 | check( |
88 | " | 97 | r#" |
89 | //- /lib.rs | 98 | //- /lib.rs |
90 | mod foo; | 99 | mod foo; |
91 | 100 | //^^^ | |
92 | //- /foo.rs | 101 | //- /foo.rs |
93 | mod $0bar; | 102 | mod $0bar; |
94 | 103 | ||
95 | //- /foo/bar.rs | 104 | //- /foo/bar.rs |
96 | // empty | 105 | // empty |
97 | ", | 106 | "#, |
98 | ); | 107 | ); |
99 | let nav = analysis.parent_module(pos).unwrap().pop().unwrap(); | ||
100 | nav.assert_match("foo Module FileId(0) 0..8"); | ||
101 | } | 108 | } |
102 | 109 | ||
103 | #[test] | 110 | #[test] |
104 | fn test_resolve_parent_module_for_inline() { | 111 | fn test_resolve_parent_module_for_inline() { |
105 | let (analysis, pos) = fixture::position( | 112 | check( |
106 | " | 113 | r#" |
107 | //- /lib.rs | 114 | //- /lib.rs |
108 | mod foo { | 115 | mod foo { |
109 | mod bar { | 116 | mod bar { |
110 | mod baz { $0 } | 117 | mod baz { $0 } |
111 | } | 118 | } //^^^ |
112 | } | 119 | } |
113 | ", | 120 | "#, |
114 | ); | 121 | ); |
115 | let nav = analysis.parent_module(pos).unwrap().pop().unwrap(); | ||
116 | nav.assert_match("baz Module FileId(0) 32..44"); | ||
117 | } | 122 | } |
118 | 123 | ||
119 | #[test] | 124 | #[test] |
diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs index df9c31aef..fef70533d 100644 --- a/crates/ide/src/references.rs +++ b/crates/ide/src/references.rs | |||
@@ -11,171 +11,120 @@ | |||
11 | 11 | ||
12 | pub(crate) mod rename; | 12 | pub(crate) mod rename; |
13 | 13 | ||
14 | use either::Either; | 14 | use hir::{PathResolution, Semantics}; |
15 | use hir::Semantics; | ||
16 | use ide_db::{ | 15 | use ide_db::{ |
17 | base_db::FileId, | 16 | base_db::FileId, |
18 | defs::{Definition, NameClass, NameRefClass}, | 17 | defs::{Definition, NameClass, NameRefClass}, |
19 | search::{FileReference, ReferenceAccess, ReferenceKind, SearchScope, UsageSearchResult}, | 18 | search::{ReferenceAccess, SearchScope}, |
20 | RootDatabase, | 19 | RootDatabase, |
21 | }; | 20 | }; |
21 | use rustc_hash::FxHashMap; | ||
22 | use syntax::{ | 22 | use syntax::{ |
23 | algo::find_node_at_offset, | 23 | algo::find_node_at_offset, |
24 | ast::{self, NameOwner}, | 24 | ast::{self, NameOwner}, |
25 | AstNode, SyntaxNode, TextRange, TokenAtOffset, T, | 25 | match_ast, AstNode, SyntaxNode, TextRange, T, |
26 | }; | 26 | }; |
27 | 27 | ||
28 | use crate::{display::TryToNav, FilePosition, FileRange, NavigationTarget, RangeInfo}; | 28 | use crate::{display::TryToNav, FilePosition, NavigationTarget}; |
29 | 29 | ||
30 | #[derive(Debug, Clone)] | 30 | #[derive(Debug, Clone)] |
31 | pub struct ReferenceSearchResult { | 31 | pub struct ReferenceSearchResult { |
32 | declaration: Declaration, | 32 | pub declaration: Declaration, |
33 | references: UsageSearchResult, | 33 | pub references: FxHashMap<FileId, Vec<(TextRange, Option<ReferenceAccess>)>>, |
34 | } | 34 | } |
35 | 35 | ||
36 | #[derive(Debug, Clone)] | 36 | #[derive(Debug, Clone)] |
37 | pub struct Declaration { | 37 | pub struct Declaration { |
38 | pub nav: NavigationTarget, | 38 | pub nav: NavigationTarget, |
39 | pub kind: ReferenceKind, | ||
40 | pub access: Option<ReferenceAccess>, | 39 | pub access: Option<ReferenceAccess>, |
41 | } | 40 | } |
42 | 41 | ||
43 | impl ReferenceSearchResult { | 42 | // Feature: Find All References |
44 | pub fn declaration(&self) -> &Declaration { | 43 | // |
45 | &self.declaration | 44 | // Shows all references of the item at the cursor location |
46 | } | 45 | // |
47 | 46 | // |=== | |
48 | pub fn decl_target(&self) -> &NavigationTarget { | 47 | // | Editor | Shortcut |
49 | &self.declaration.nav | 48 | // |
50 | } | 49 | // | VS Code | kbd:[Shift+Alt+F12] |
51 | 50 | // |=== | |
52 | pub fn references(&self) -> &UsageSearchResult { | ||
53 | &self.references | ||
54 | } | ||
55 | |||
56 | pub fn references_with_declaration(mut self) -> UsageSearchResult { | ||
57 | let decl_ref = FileReference { | ||
58 | range: self.declaration.nav.focus_or_full_range(), | ||
59 | kind: self.declaration.kind, | ||
60 | access: self.declaration.access, | ||
61 | }; | ||
62 | let file_id = self.declaration.nav.file_id; | ||
63 | self.references.references.entry(file_id).or_default().push(decl_ref); | ||
64 | self.references | ||
65 | } | ||
66 | |||
67 | /// Total number of references | ||
68 | /// At least 1 since all valid references should | ||
69 | /// Have a declaration | ||
70 | pub fn len(&self) -> usize { | ||
71 | self.references.len() + 1 | ||
72 | } | ||
73 | } | ||
74 | |||
75 | // allow turning ReferenceSearchResult into an iterator | ||
76 | // over References | ||
77 | impl IntoIterator for ReferenceSearchResult { | ||
78 | type Item = (FileId, Vec<FileReference>); | ||
79 | type IntoIter = std::collections::hash_map::IntoIter<FileId, Vec<FileReference>>; | ||
80 | |||
81 | fn into_iter(self) -> Self::IntoIter { | ||
82 | self.references_with_declaration().into_iter() | ||
83 | } | ||
84 | } | ||
85 | |||
86 | pub(crate) fn find_all_refs( | 51 | pub(crate) fn find_all_refs( |
87 | sema: &Semantics<RootDatabase>, | 52 | sema: &Semantics<RootDatabase>, |
88 | position: FilePosition, | 53 | position: FilePosition, |
89 | search_scope: Option<SearchScope>, | 54 | search_scope: Option<SearchScope>, |
90 | ) -> Option<RangeInfo<ReferenceSearchResult>> { | 55 | ) -> Option<ReferenceSearchResult> { |
91 | let _p = profile::span("find_all_refs"); | 56 | let _p = profile::span("find_all_refs"); |
92 | let syntax = sema.parse(position.file_id).syntax().clone(); | 57 | let syntax = sema.parse(position.file_id).syntax().clone(); |
93 | 58 | ||
94 | let (opt_name, search_kind) = if let Some(name) = | 59 | let (def, is_literal_search) = |
95 | get_struct_def_name_for_struct_literal_search(&sema, &syntax, position) | 60 | if let Some(name) = get_name_of_item_declaration(&syntax, position) { |
96 | { | 61 | (NameClass::classify(sema, &name)?.referenced_or_defined(sema.db), true) |
97 | (Some(name), ReferenceKind::StructLiteral) | 62 | } else { |
98 | } else if let Some(name) = get_enum_def_name_for_struct_literal_search(&sema, &syntax, position) | 63 | (find_def(&sema, &syntax, position)?, false) |
99 | { | 64 | }; |
100 | (Some(name), ReferenceKind::EnumLiteral) | ||
101 | } else { | ||
102 | ( | ||
103 | sema.find_node_at_offset_with_descend::<ast::Name>(&syntax, position.offset), | ||
104 | ReferenceKind::Other, | ||
105 | ) | ||
106 | }; | ||
107 | |||
108 | let RangeInfo { range, info: def } = find_name(&sema, &syntax, position, opt_name)?; | ||
109 | 65 | ||
110 | let mut usages = def.usages(sema).set_scope(search_scope).all(); | 66 | let mut usages = def.usages(sema).set_scope(search_scope).all(); |
111 | usages | 67 | if is_literal_search { |
112 | .references | 68 | // filter for constructor-literals |
113 | .values_mut() | 69 | let refs = usages.references.values_mut(); |
114 | .for_each(|it| it.retain(|r| search_kind == ReferenceKind::Other || search_kind == r.kind)); | 70 | match def { |
115 | usages.references.retain(|_, it| !it.is_empty()); | 71 | Definition::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Enum(enum_))) => { |
116 | 72 | refs.for_each(|it| { | |
117 | let nav = def.try_to_nav(sema.db)?; | 73 | it.retain(|reference| { |
118 | let decl_range = nav.focus_or_full_range(); | 74 | reference |
119 | 75 | .name | |
120 | let mut kind = ReferenceKind::Other; | 76 | .as_name_ref() |
121 | if let Definition::Local(local) = def { | 77 | .map_or(false, |name_ref| is_enum_lit_name_ref(sema, enum_, name_ref)) |
122 | match local.source(sema.db).value { | 78 | }) |
123 | Either::Left(pat) => { | 79 | }); |
124 | if matches!( | 80 | usages.references.retain(|_, it| !it.is_empty()); |
125 | pat.syntax().parent().and_then(ast::RecordPatField::cast), | ||
126 | Some(pat_field) if pat_field.name_ref().is_none() | ||
127 | ) { | ||
128 | kind = ReferenceKind::FieldShorthandForLocal; | ||
129 | } | ||
130 | } | 81 | } |
131 | Either::Right(_) => kind = ReferenceKind::SelfParam, | 82 | Definition::ModuleDef(hir::ModuleDef::Adt(_)) |
83 | | Definition::ModuleDef(hir::ModuleDef::Variant(_)) => { | ||
84 | refs.for_each(|it| { | ||
85 | it.retain(|reference| { | ||
86 | reference.name.as_name_ref().map_or(false, is_lit_name_ref) | ||
87 | }) | ||
88 | }); | ||
89 | usages.references.retain(|_, it| !it.is_empty()); | ||
90 | } | ||
91 | _ => {} | ||
132 | } | 92 | } |
133 | } else if matches!( | 93 | } |
134 | def, | 94 | let nav = def.try_to_nav(sema.db)?; |
135 | Definition::GenericParam(hir::GenericParam::LifetimeParam(_)) | Definition::Label(_) | 95 | let decl_range = nav.focus_or_full_range(); |
136 | ) { | ||
137 | kind = ReferenceKind::Lifetime; | ||
138 | }; | ||
139 | 96 | ||
140 | let declaration = Declaration { nav, kind, access: decl_access(&def, &syntax, decl_range) }; | 97 | let declaration = Declaration { nav, access: decl_access(&def, &syntax, decl_range) }; |
98 | let references = usages | ||
99 | .into_iter() | ||
100 | .map(|(file_id, refs)| { | ||
101 | (file_id, refs.into_iter().map(|file_ref| (file_ref.range, file_ref.access)).collect()) | ||
102 | }) | ||
103 | .collect(); | ||
141 | 104 | ||
142 | Some(RangeInfo::new(range, ReferenceSearchResult { declaration, references: usages })) | 105 | Some(ReferenceSearchResult { declaration, references }) |
143 | } | 106 | } |
144 | 107 | ||
145 | fn find_name( | 108 | fn find_def( |
146 | sema: &Semantics<RootDatabase>, | 109 | sema: &Semantics<RootDatabase>, |
147 | syntax: &SyntaxNode, | 110 | syntax: &SyntaxNode, |
148 | position: FilePosition, | 111 | position: FilePosition, |
149 | opt_name: Option<ast::Name>, | 112 | ) -> Option<Definition> { |
150 | ) -> Option<RangeInfo<Definition>> { | 113 | let def = match sema.find_node_at_offset_with_descend(syntax, position.offset)? { |
151 | if let Some(name) = opt_name { | 114 | ast::NameLike::NameRef(name_ref) => { |
152 | let def = NameClass::classify(sema, &name)?.referenced_or_defined(sema.db); | 115 | NameRefClass::classify(sema, &name_ref)?.referenced(sema.db) |
153 | let FileRange { range, .. } = sema.original_range(name.syntax()); | ||
154 | return Some(RangeInfo::new(range, def)); | ||
155 | } | ||
156 | |||
157 | let (FileRange { range, .. }, def) = if let Some(lifetime) = | ||
158 | sema.find_node_at_offset_with_descend::<ast::Lifetime>(&syntax, position.offset) | ||
159 | { | ||
160 | if let Some(def) = NameRefClass::classify_lifetime(sema, &lifetime) | ||
161 | .map(|class| NameRefClass::referenced(class, sema.db)) | ||
162 | { | ||
163 | (sema.original_range(lifetime.syntax()), def) | ||
164 | } else { | ||
165 | ( | ||
166 | sema.original_range(lifetime.syntax()), | ||
167 | NameClass::classify_lifetime(sema, &lifetime)?.referenced_or_defined(sema.db), | ||
168 | ) | ||
169 | } | 116 | } |
170 | } else { | 117 | ast::NameLike::Name(name) => { |
171 | let name_ref = | 118 | NameClass::classify(sema, &name)?.referenced_or_defined(sema.db) |
172 | sema.find_node_at_offset_with_descend::<ast::NameRef>(&syntax, position.offset)?; | 119 | } |
173 | ( | 120 | ast::NameLike::Lifetime(lifetime) => NameRefClass::classify_lifetime(sema, &lifetime) |
174 | sema.original_range(name_ref.syntax()), | 121 | .map(|class| class.referenced(sema.db)) |
175 | NameRefClass::classify(sema, &name_ref)?.referenced(sema.db), | 122 | .or_else(|| { |
176 | ) | 123 | NameClass::classify_lifetime(sema, &lifetime) |
124 | .map(|class| class.referenced_or_defined(sema.db)) | ||
125 | })?, | ||
177 | }; | 126 | }; |
178 | Some(RangeInfo::new(range, def)) | 127 | Some(def) |
179 | } | 128 | } |
180 | 129 | ||
181 | fn decl_access(def: &Definition, syntax: &SyntaxNode, range: TextRange) -> Option<ReferenceAccess> { | 130 | fn decl_access(def: &Definition, syntax: &SyntaxNode, range: TextRange) -> Option<ReferenceAccess> { |
@@ -197,58 +146,85 @@ fn decl_access(def: &Definition, syntax: &SyntaxNode, range: TextRange) -> Optio | |||
197 | None | 146 | None |
198 | } | 147 | } |
199 | 148 | ||
200 | fn get_struct_def_name_for_struct_literal_search( | 149 | fn get_name_of_item_declaration(syntax: &SyntaxNode, position: FilePosition) -> Option<ast::Name> { |
201 | sema: &Semantics<RootDatabase>, | 150 | let token = syntax.token_at_offset(position.offset).right_biased()?; |
202 | syntax: &SyntaxNode, | 151 | let kind = token.kind(); |
203 | position: FilePosition, | 152 | if kind == T![;] { |
204 | ) -> Option<ast::Name> { | 153 | ast::Struct::cast(token.parent()) |
205 | if let TokenAtOffset::Between(ref left, ref right) = syntax.token_at_offset(position.offset) { | 154 | .filter(|struct_| struct_.field_list().is_none()) |
206 | if right.kind() != T!['{'] && right.kind() != T!['('] { | 155 | .and_then(|struct_| struct_.name()) |
207 | return None; | 156 | } else if kind == T!['{'] { |
208 | } | 157 | match_ast! { |
209 | if let Some(name) = | 158 | match (token.parent()) { |
210 | sema.find_node_at_offset_with_descend::<ast::Name>(&syntax, left.text_range().start()) | 159 | ast::RecordFieldList(rfl) => match_ast! { |
211 | { | 160 | match (rfl.syntax().parent()?) { |
212 | return name.syntax().ancestors().find_map(ast::Struct::cast).and_then(|l| l.name()); | 161 | ast::Variant(it) => it.name(), |
162 | ast::Struct(it) => it.name(), | ||
163 | ast::Union(it) => it.name(), | ||
164 | _ => None, | ||
165 | } | ||
166 | }, | ||
167 | ast::VariantList(vl) => ast::Enum::cast(vl.syntax().parent()?)?.name(), | ||
168 | _ => None, | ||
169 | } | ||
213 | } | 170 | } |
214 | if sema | 171 | } else if kind == T!['('] { |
215 | .find_node_at_offset_with_descend::<ast::GenericParamList>( | 172 | let tfl = ast::TupleFieldList::cast(token.parent())?; |
216 | &syntax, | 173 | match_ast! { |
217 | left.text_range().start(), | 174 | match (tfl.syntax().parent()?) { |
218 | ) | 175 | ast::Variant(it) => it.name(), |
219 | .is_some() | 176 | ast::Struct(it) => it.name(), |
220 | { | 177 | _ => None, |
221 | return left.ancestors().find_map(ast::Struct::cast).and_then(|l| l.name()); | 178 | } |
222 | } | 179 | } |
180 | } else { | ||
181 | None | ||
223 | } | 182 | } |
224 | None | ||
225 | } | 183 | } |
226 | 184 | ||
227 | fn get_enum_def_name_for_struct_literal_search( | 185 | fn is_enum_lit_name_ref( |
228 | sema: &Semantics<RootDatabase>, | 186 | sema: &Semantics<RootDatabase>, |
229 | syntax: &SyntaxNode, | 187 | enum_: hir::Enum, |
230 | position: FilePosition, | 188 | name_ref: &ast::NameRef, |
231 | ) -> Option<ast::Name> { | 189 | ) -> bool { |
232 | if let TokenAtOffset::Between(ref left, ref right) = syntax.token_at_offset(position.offset) { | 190 | let path_is_variant_of_enum = |path: ast::Path| { |
233 | if right.kind() != T!['{'] && right.kind() != T!['('] { | 191 | matches!( |
234 | return None; | 192 | sema.resolve_path(&path), |
235 | } | 193 | Some(PathResolution::Def(hir::ModuleDef::Variant(variant))) |
236 | if let Some(name) = | 194 | if variant.parent_enum(sema.db) == enum_ |
237 | sema.find_node_at_offset_with_descend::<ast::Name>(&syntax, left.text_range().start()) | 195 | ) |
238 | { | 196 | }; |
239 | return name.syntax().ancestors().find_map(ast::Enum::cast).and_then(|l| l.name()); | 197 | name_ref |
240 | } | 198 | .syntax() |
241 | if sema | 199 | .ancestors() |
242 | .find_node_at_offset_with_descend::<ast::GenericParamList>( | 200 | .find_map(|ancestor| { |
243 | &syntax, | 201 | match_ast! { |
244 | left.text_range().start(), | 202 | match ancestor { |
245 | ) | 203 | ast::PathExpr(path_expr) => path_expr.path().map(path_is_variant_of_enum), |
246 | .is_some() | 204 | ast::RecordExpr(record_expr) => record_expr.path().map(path_is_variant_of_enum), |
247 | { | 205 | _ => None, |
248 | return left.ancestors().find_map(ast::Enum::cast).and_then(|l| l.name()); | 206 | } |
207 | } | ||
208 | }) | ||
209 | .unwrap_or(false) | ||
210 | } | ||
211 | |||
212 | fn path_ends_with(path: Option<ast::Path>, name_ref: &ast::NameRef) -> bool { | ||
213 | path.and_then(|path| path.segment()) | ||
214 | .and_then(|segment| segment.name_ref()) | ||
215 | .map_or(false, |segment| segment == *name_ref) | ||
216 | } | ||
217 | |||
218 | fn is_lit_name_ref(name_ref: &ast::NameRef) -> bool { | ||
219 | name_ref.syntax().ancestors().find_map(|ancestor| { | ||
220 | match_ast! { | ||
221 | match ancestor { | ||
222 | ast::PathExpr(path_expr) => Some(path_ends_with(path_expr.path(), name_ref)), | ||
223 | ast::RecordExpr(record_expr) => Some(path_ends_with(record_expr.path(), name_ref)), | ||
224 | _ => None, | ||
225 | } | ||
249 | } | 226 | } |
250 | } | 227 | }).unwrap_or(false) |
251 | None | ||
252 | } | 228 | } |
253 | 229 | ||
254 | #[cfg(test)] | 230 | #[cfg(test)] |
@@ -275,9 +251,9 @@ fn main() { | |||
275 | } | 251 | } |
276 | "#, | 252 | "#, |
277 | expect![[r#" | 253 | expect![[r#" |
278 | Foo Struct FileId(0) 0..26 7..10 Other | 254 | Foo Struct FileId(0) 0..26 7..10 |
279 | 255 | ||
280 | FileId(0) 101..104 StructLiteral | 256 | FileId(0) 101..104 |
281 | "#]], | 257 | "#]], |
282 | ); | 258 | ); |
283 | } | 259 | } |
@@ -293,10 +269,10 @@ struct Foo$0 {} | |||
293 | } | 269 | } |
294 | "#, | 270 | "#, |
295 | expect![[r#" | 271 | expect![[r#" |
296 | Foo Struct FileId(0) 0..13 7..10 Other | 272 | Foo Struct FileId(0) 0..13 7..10 |
297 | 273 | ||
298 | FileId(0) 41..44 Other | 274 | FileId(0) 41..44 |
299 | FileId(0) 54..57 StructLiteral | 275 | FileId(0) 54..57 |
300 | "#]], | 276 | "#]], |
301 | ); | 277 | ); |
302 | } | 278 | } |
@@ -312,9 +288,9 @@ struct Foo<T> $0{} | |||
312 | } | 288 | } |
313 | "#, | 289 | "#, |
314 | expect![[r#" | 290 | expect![[r#" |
315 | Foo Struct FileId(0) 0..16 7..10 Other | 291 | Foo Struct FileId(0) 0..16 7..10 |
316 | 292 | ||
317 | FileId(0) 64..67 StructLiteral | 293 | FileId(0) 64..67 |
318 | "#]], | 294 | "#]], |
319 | ); | 295 | ); |
320 | } | 296 | } |
@@ -331,9 +307,30 @@ fn main() { | |||
331 | } | 307 | } |
332 | "#, | 308 | "#, |
333 | expect![[r#" | 309 | expect![[r#" |
334 | Foo Struct FileId(0) 0..16 7..10 Other | 310 | Foo Struct FileId(0) 0..16 7..10 |
311 | |||
312 | FileId(0) 54..57 | ||
313 | "#]], | ||
314 | ); | ||
315 | } | ||
316 | |||
317 | #[test] | ||
318 | fn test_struct_literal_for_union() { | ||
319 | check( | ||
320 | r#" | ||
321 | union Foo $0{ | ||
322 | x: u32 | ||
323 | } | ||
324 | |||
325 | fn main() { | ||
326 | let f: Foo; | ||
327 | f = Foo { x: 1 }; | ||
328 | } | ||
329 | "#, | ||
330 | expect![[r#" | ||
331 | Foo Union FileId(0) 0..24 6..9 | ||
335 | 332 | ||
336 | FileId(0) 54..57 StructLiteral | 333 | FileId(0) 62..65 |
337 | "#]], | 334 | "#]], |
338 | ); | 335 | ); |
339 | } | 336 | } |
@@ -344,17 +341,65 @@ fn main() { | |||
344 | r#" | 341 | r#" |
345 | enum Foo $0{ | 342 | enum Foo $0{ |
346 | A, | 343 | A, |
347 | B, | 344 | B(), |
345 | C{}, | ||
348 | } | 346 | } |
349 | fn main() { | 347 | fn main() { |
350 | let f: Foo; | 348 | let f: Foo; |
351 | f = Foo::A; | 349 | f = Foo::A; |
350 | f = Foo::B(); | ||
351 | f = Foo::C{}; | ||
352 | } | ||
353 | "#, | ||
354 | expect![[r#" | ||
355 | Foo Enum FileId(0) 0..37 5..8 | ||
356 | |||
357 | FileId(0) 74..77 | ||
358 | FileId(0) 90..93 | ||
359 | FileId(0) 108..111 | ||
360 | "#]], | ||
361 | ); | ||
362 | } | ||
363 | |||
364 | #[test] | ||
365 | fn test_variant_record_after_space() { | ||
366 | check( | ||
367 | r#" | ||
368 | enum Foo { | ||
369 | A $0{ n: i32 }, | ||
370 | B, | ||
371 | } | ||
372 | fn main() { | ||
373 | let f: Foo; | ||
374 | f = Foo::B; | ||
375 | f = Foo::A { n: 92 }; | ||
352 | } | 376 | } |
353 | "#, | 377 | "#, |
354 | expect![[r#" | 378 | expect![[r#" |
355 | Foo Enum FileId(0) 0..26 5..8 Other | 379 | A Variant FileId(0) 15..27 15..16 |
356 | 380 | ||
357 | FileId(0) 63..66 EnumLiteral | 381 | FileId(0) 95..96 |
382 | "#]], | ||
383 | ); | ||
384 | } | ||
385 | #[test] | ||
386 | fn test_variant_tuple_before_paren() { | ||
387 | check( | ||
388 | r#" | ||
389 | enum Foo { | ||
390 | A$0(i32), | ||
391 | B, | ||
392 | } | ||
393 | fn main() { | ||
394 | let f: Foo; | ||
395 | f = Foo::B; | ||
396 | f = Foo::A(92); | ||
397 | } | ||
398 | "#, | ||
399 | expect![[r#" | ||
400 | A Variant FileId(0) 15..21 15..16 | ||
401 | |||
402 | FileId(0) 89..90 | ||
358 | "#]], | 403 | "#]], |
359 | ); | 404 | ); |
360 | } | 405 | } |
@@ -373,10 +418,10 @@ fn main() { | |||
373 | } | 418 | } |
374 | "#, | 419 | "#, |
375 | expect![[r#" | 420 | expect![[r#" |
376 | Foo Enum FileId(0) 0..26 5..8 Other | 421 | Foo Enum FileId(0) 0..26 5..8 |
377 | 422 | ||
378 | FileId(0) 50..53 Other | 423 | FileId(0) 50..53 |
379 | FileId(0) 63..66 EnumLiteral | 424 | FileId(0) 63..66 |
380 | "#]], | 425 | "#]], |
381 | ); | 426 | ); |
382 | } | 427 | } |
@@ -395,9 +440,9 @@ fn main() { | |||
395 | } | 440 | } |
396 | "#, | 441 | "#, |
397 | expect![[r#" | 442 | expect![[r#" |
398 | Foo Enum FileId(0) 0..32 5..8 Other | 443 | Foo Enum FileId(0) 0..32 5..8 |
399 | 444 | ||
400 | FileId(0) 73..76 EnumLiteral | 445 | FileId(0) 73..76 |
401 | "#]], | 446 | "#]], |
402 | ); | 447 | ); |
403 | } | 448 | } |
@@ -416,9 +461,9 @@ fn main() { | |||
416 | } | 461 | } |
417 | "#, | 462 | "#, |
418 | expect![[r#" | 463 | expect![[r#" |
419 | Foo Enum FileId(0) 0..33 5..8 Other | 464 | Foo Enum FileId(0) 0..33 5..8 |
420 | 465 | ||
421 | FileId(0) 70..73 EnumLiteral | 466 | FileId(0) 70..73 |
422 | "#]], | 467 | "#]], |
423 | ); | 468 | ); |
424 | } | 469 | } |
@@ -439,12 +484,12 @@ fn main() { | |||
439 | i = 5; | 484 | i = 5; |
440 | }"#, | 485 | }"#, |
441 | expect![[r#" | 486 | expect![[r#" |
442 | i Local FileId(0) 20..25 24..25 Other Write | 487 | i Local FileId(0) 20..25 24..25 Write |
443 | 488 | ||
444 | FileId(0) 50..51 Other Write | 489 | FileId(0) 50..51 Write |
445 | FileId(0) 54..55 Other Read | 490 | FileId(0) 54..55 Read |
446 | FileId(0) 76..77 Other Write | 491 | FileId(0) 76..77 Write |
447 | FileId(0) 94..95 Other Write | 492 | FileId(0) 94..95 Write |
448 | "#]], | 493 | "#]], |
449 | ); | 494 | ); |
450 | } | 495 | } |
@@ -463,10 +508,10 @@ fn bar() { | |||
463 | } | 508 | } |
464 | "#, | 509 | "#, |
465 | expect![[r#" | 510 | expect![[r#" |
466 | spam Local FileId(0) 19..23 19..23 Other | 511 | spam Local FileId(0) 19..23 19..23 |
467 | 512 | ||
468 | FileId(0) 34..38 Other Read | 513 | FileId(0) 34..38 Read |
469 | FileId(0) 41..45 Other Read | 514 | FileId(0) 41..45 Read |
470 | "#]], | 515 | "#]], |
471 | ); | 516 | ); |
472 | } | 517 | } |
@@ -478,9 +523,9 @@ fn bar() { | |||
478 | fn foo(i : u32) -> u32 { i$0 } | 523 | fn foo(i : u32) -> u32 { i$0 } |
479 | "#, | 524 | "#, |
480 | expect![[r#" | 525 | expect![[r#" |
481 | i ValueParam FileId(0) 7..8 7..8 Other | 526 | i ValueParam FileId(0) 7..8 7..8 |
482 | 527 | ||
483 | FileId(0) 25..26 Other Read | 528 | FileId(0) 25..26 Read |
484 | "#]], | 529 | "#]], |
485 | ); | 530 | ); |
486 | } | 531 | } |
@@ -492,9 +537,9 @@ fn foo(i : u32) -> u32 { i$0 } | |||
492 | fn foo(i$0 : u32) -> u32 { i } | 537 | fn foo(i$0 : u32) -> u32 { i } |
493 | "#, | 538 | "#, |
494 | expect![[r#" | 539 | expect![[r#" |
495 | i ValueParam FileId(0) 7..8 7..8 Other | 540 | i ValueParam FileId(0) 7..8 7..8 |
496 | 541 | ||
497 | FileId(0) 25..26 Other Read | 542 | FileId(0) 25..26 Read |
498 | "#]], | 543 | "#]], |
499 | ); | 544 | ); |
500 | } | 545 | } |
@@ -513,9 +558,9 @@ fn main(s: Foo) { | |||
513 | } | 558 | } |
514 | "#, | 559 | "#, |
515 | expect![[r#" | 560 | expect![[r#" |
516 | spam Field FileId(0) 17..30 21..25 Other | 561 | spam Field FileId(0) 17..30 21..25 |
517 | 562 | ||
518 | FileId(0) 67..71 Other Read | 563 | FileId(0) 67..71 Read |
519 | "#]], | 564 | "#]], |
520 | ); | 565 | ); |
521 | } | 566 | } |
@@ -530,7 +575,7 @@ impl Foo { | |||
530 | } | 575 | } |
531 | "#, | 576 | "#, |
532 | expect![[r#" | 577 | expect![[r#" |
533 | f Function FileId(0) 27..43 30..31 Other | 578 | f Function FileId(0) 27..43 30..31 |
534 | 579 | ||
535 | "#]], | 580 | "#]], |
536 | ); | 581 | ); |
@@ -547,7 +592,7 @@ enum Foo { | |||
547 | } | 592 | } |
548 | "#, | 593 | "#, |
549 | expect![[r#" | 594 | expect![[r#" |
550 | B Variant FileId(0) 22..23 22..23 Other | 595 | B Variant FileId(0) 22..23 22..23 |
551 | 596 | ||
552 | "#]], | 597 | "#]], |
553 | ); | 598 | ); |
@@ -564,7 +609,7 @@ enum Foo { | |||
564 | } | 609 | } |
565 | "#, | 610 | "#, |
566 | expect![[r#" | 611 | expect![[r#" |
567 | field Field FileId(0) 26..35 26..31 Other | 612 | field Field FileId(0) 26..35 26..31 |
568 | 613 | ||
569 | "#]], | 614 | "#]], |
570 | ); | 615 | ); |
@@ -605,10 +650,10 @@ fn f() { | |||
605 | } | 650 | } |
606 | "#, | 651 | "#, |
607 | expect![[r#" | 652 | expect![[r#" |
608 | Foo Struct FileId(1) 17..51 28..31 Other | 653 | Foo Struct FileId(1) 17..51 28..31 |
609 | 654 | ||
610 | FileId(0) 53..56 StructLiteral | 655 | FileId(0) 53..56 |
611 | FileId(2) 79..82 StructLiteral | 656 | FileId(2) 79..82 |
612 | "#]], | 657 | "#]], |
613 | ); | 658 | ); |
614 | } | 659 | } |
@@ -635,9 +680,9 @@ pub struct Foo { | |||
635 | } | 680 | } |
636 | "#, | 681 | "#, |
637 | expect![[r#" | 682 | expect![[r#" |
638 | foo Module FileId(1) 0..35 Other | 683 | foo Module FileId(1) 0..35 |
639 | 684 | ||
640 | FileId(0) 14..17 Other | 685 | FileId(0) 14..17 |
641 | "#]], | 686 | "#]], |
642 | ); | 687 | ); |
643 | } | 688 | } |
@@ -663,10 +708,10 @@ pub(super) struct Foo$0 { | |||
663 | } | 708 | } |
664 | "#, | 709 | "#, |
665 | expect![[r#" | 710 | expect![[r#" |
666 | Foo Struct FileId(2) 0..41 18..21 Other | 711 | Foo Struct FileId(2) 0..41 18..21 |
667 | 712 | ||
668 | FileId(1) 20..23 Other | 713 | FileId(1) 20..23 |
669 | FileId(1) 47..50 StructLiteral | 714 | FileId(1) 47..50 |
670 | "#]], | 715 | "#]], |
671 | ); | 716 | ); |
672 | } | 717 | } |
@@ -691,10 +736,10 @@ pub(super) struct Foo$0 { | |||
691 | code, | 736 | code, |
692 | None, | 737 | None, |
693 | expect![[r#" | 738 | expect![[r#" |
694 | quux Function FileId(0) 19..35 26..30 Other | 739 | quux Function FileId(0) 19..35 26..30 |
695 | 740 | ||
696 | FileId(1) 16..20 StructLiteral | 741 | FileId(1) 16..20 |
697 | FileId(2) 16..20 StructLiteral | 742 | FileId(2) 16..20 |
698 | "#]], | 743 | "#]], |
699 | ); | 744 | ); |
700 | 745 | ||
@@ -702,9 +747,9 @@ pub(super) struct Foo$0 { | |||
702 | code, | 747 | code, |
703 | Some(SearchScope::single_file(FileId(2))), | 748 | Some(SearchScope::single_file(FileId(2))), |
704 | expect![[r#" | 749 | expect![[r#" |
705 | quux Function FileId(0) 19..35 26..30 Other | 750 | quux Function FileId(0) 19..35 26..30 |
706 | 751 | ||
707 | FileId(2) 16..20 StructLiteral | 752 | FileId(2) 16..20 |
708 | "#]], | 753 | "#]], |
709 | ); | 754 | ); |
710 | } | 755 | } |
@@ -722,10 +767,10 @@ fn foo() { | |||
722 | } | 767 | } |
723 | "#, | 768 | "#, |
724 | expect![[r#" | 769 | expect![[r#" |
725 | m1 Macro FileId(0) 0..46 29..31 Other | 770 | m1 Macro FileId(0) 0..46 29..31 |
726 | 771 | ||
727 | FileId(0) 63..65 StructLiteral | 772 | FileId(0) 63..65 |
728 | FileId(0) 73..75 StructLiteral | 773 | FileId(0) 73..75 |
729 | "#]], | 774 | "#]], |
730 | ); | 775 | ); |
731 | } | 776 | } |
@@ -740,10 +785,10 @@ fn foo() { | |||
740 | } | 785 | } |
741 | "#, | 786 | "#, |
742 | expect![[r#" | 787 | expect![[r#" |
743 | i Local FileId(0) 19..24 23..24 Other Write | 788 | i Local FileId(0) 19..24 23..24 Write |
744 | 789 | ||
745 | FileId(0) 34..35 Other Write | 790 | FileId(0) 34..35 Write |
746 | FileId(0) 38..39 Other Read | 791 | FileId(0) 38..39 Read |
747 | "#]], | 792 | "#]], |
748 | ); | 793 | ); |
749 | } | 794 | } |
@@ -762,10 +807,10 @@ fn foo() { | |||
762 | } | 807 | } |
763 | "#, | 808 | "#, |
764 | expect![[r#" | 809 | expect![[r#" |
765 | f Field FileId(0) 15..21 15..16 Other | 810 | f Field FileId(0) 15..21 15..16 |
766 | 811 | ||
767 | FileId(0) 55..56 RecordFieldExprOrPat Read | 812 | FileId(0) 55..56 Read |
768 | FileId(0) 68..69 Other Write | 813 | FileId(0) 68..69 Write |
769 | "#]], | 814 | "#]], |
770 | ); | 815 | ); |
771 | } | 816 | } |
@@ -780,9 +825,9 @@ fn foo() { | |||
780 | } | 825 | } |
781 | "#, | 826 | "#, |
782 | expect![[r#" | 827 | expect![[r#" |
783 | i Local FileId(0) 19..20 19..20 Other | 828 | i Local FileId(0) 19..20 19..20 |
784 | 829 | ||
785 | FileId(0) 26..27 Other Write | 830 | FileId(0) 26..27 Write |
786 | "#]], | 831 | "#]], |
787 | ); | 832 | ); |
788 | } | 833 | } |
@@ -804,9 +849,9 @@ fn main() { | |||
804 | } | 849 | } |
805 | "#, | 850 | "#, |
806 | expect![[r#" | 851 | expect![[r#" |
807 | new Function FileId(0) 54..81 61..64 Other | 852 | new Function FileId(0) 54..81 61..64 |
808 | 853 | ||
809 | FileId(0) 126..129 StructLiteral | 854 | FileId(0) 126..129 |
810 | "#]], | 855 | "#]], |
811 | ); | 856 | ); |
812 | } | 857 | } |
@@ -826,10 +871,10 @@ use crate::f; | |||
826 | fn g() { f(); } | 871 | fn g() { f(); } |
827 | "#, | 872 | "#, |
828 | expect![[r#" | 873 | expect![[r#" |
829 | f Function FileId(0) 22..31 25..26 Other | 874 | f Function FileId(0) 22..31 25..26 |
830 | 875 | ||
831 | FileId(1) 11..12 Other | 876 | FileId(1) 11..12 |
832 | FileId(1) 24..25 StructLiteral | 877 | FileId(1) 24..25 |
833 | "#]], | 878 | "#]], |
834 | ); | 879 | ); |
835 | } | 880 | } |
@@ -849,9 +894,9 @@ fn f(s: S) { | |||
849 | } | 894 | } |
850 | "#, | 895 | "#, |
851 | expect![[r#" | 896 | expect![[r#" |
852 | field Field FileId(0) 15..24 15..20 Other | 897 | field Field FileId(0) 15..24 15..20 |
853 | 898 | ||
854 | FileId(0) 68..73 FieldShorthandForField Read | 899 | FileId(0) 68..73 Read |
855 | "#]], | 900 | "#]], |
856 | ); | 901 | ); |
857 | } | 902 | } |
@@ -873,9 +918,9 @@ fn f(e: En) { | |||
873 | } | 918 | } |
874 | "#, | 919 | "#, |
875 | expect![[r#" | 920 | expect![[r#" |
876 | field Field FileId(0) 32..41 32..37 Other | 921 | field Field FileId(0) 32..41 32..37 |
877 | 922 | ||
878 | FileId(0) 102..107 FieldShorthandForField Read | 923 | FileId(0) 102..107 Read |
879 | "#]], | 924 | "#]], |
880 | ); | 925 | ); |
881 | } | 926 | } |
@@ -897,9 +942,9 @@ fn f() -> m::En { | |||
897 | } | 942 | } |
898 | "#, | 943 | "#, |
899 | expect![[r#" | 944 | expect![[r#" |
900 | field Field FileId(0) 56..65 56..61 Other | 945 | field Field FileId(0) 56..65 56..61 |
901 | 946 | ||
902 | FileId(0) 125..130 RecordFieldExprOrPat Read | 947 | FileId(0) 125..130 Read |
903 | "#]], | 948 | "#]], |
904 | ); | 949 | ); |
905 | } | 950 | } |
@@ -922,10 +967,10 @@ impl Foo { | |||
922 | } | 967 | } |
923 | "#, | 968 | "#, |
924 | expect![[r#" | 969 | expect![[r#" |
925 | self SelfParam FileId(0) 47..51 47..51 SelfParam | 970 | self SelfParam FileId(0) 47..51 47..51 |
926 | 971 | ||
927 | FileId(0) 71..75 Other Read | 972 | FileId(0) 71..75 Read |
928 | FileId(0) 152..156 Other Read | 973 | FileId(0) 152..156 Read |
929 | "#]], | 974 | "#]], |
930 | ); | 975 | ); |
931 | } | 976 | } |
@@ -943,9 +988,9 @@ impl Foo { | |||
943 | } | 988 | } |
944 | "#, | 989 | "#, |
945 | expect![[r#" | 990 | expect![[r#" |
946 | self SelfParam FileId(0) 47..51 47..51 SelfParam | 991 | self SelfParam FileId(0) 47..51 47..51 |
947 | 992 | ||
948 | FileId(0) 63..67 Other Read | 993 | FileId(0) 63..67 Read |
949 | "#]], | 994 | "#]], |
950 | ); | 995 | ); |
951 | } | 996 | } |
@@ -961,7 +1006,7 @@ impl Foo { | |||
961 | let mut actual = String::new(); | 1006 | let mut actual = String::new(); |
962 | { | 1007 | { |
963 | let decl = refs.declaration; | 1008 | let decl = refs.declaration; |
964 | format_to!(actual, "{} {:?}", decl.nav.debug_render(), decl.kind); | 1009 | format_to!(actual, "{}", decl.nav.debug_render()); |
965 | if let Some(access) = decl.access { | 1010 | if let Some(access) = decl.access { |
966 | format_to!(actual, " {:?}", access) | 1011 | format_to!(actual, " {:?}", access) |
967 | } | 1012 | } |
@@ -969,9 +1014,9 @@ impl Foo { | |||
969 | } | 1014 | } |
970 | 1015 | ||
971 | for (file_id, references) in refs.references { | 1016 | for (file_id, references) in refs.references { |
972 | for r in references { | 1017 | for (range, access) in references { |
973 | format_to!(actual, "{:?} {:?} {:?}", file_id, r.range, r.kind); | 1018 | format_to!(actual, "{:?} {:?}", file_id, range); |
974 | if let Some(access) = r.access { | 1019 | if let Some(access) = access { |
975 | format_to!(actual, " {:?}", access); | 1020 | format_to!(actual, " {:?}", access); |
976 | } | 1021 | } |
977 | actual += "\n"; | 1022 | actual += "\n"; |
@@ -992,13 +1037,13 @@ fn foo<'a, 'b: 'a>(x: &'a$0 ()) -> &'a () where &'a (): Foo<'a> { | |||
992 | } | 1037 | } |
993 | "#, | 1038 | "#, |
994 | expect![[r#" | 1039 | expect![[r#" |
995 | 'a LifetimeParam FileId(0) 55..57 55..57 Lifetime | 1040 | 'a LifetimeParam FileId(0) 55..57 55..57 |
996 | 1041 | ||
997 | FileId(0) 63..65 Lifetime | 1042 | FileId(0) 63..65 |
998 | FileId(0) 71..73 Lifetime | 1043 | FileId(0) 71..73 |
999 | FileId(0) 82..84 Lifetime | 1044 | FileId(0) 82..84 |
1000 | FileId(0) 95..97 Lifetime | 1045 | FileId(0) 95..97 |
1001 | FileId(0) 106..108 Lifetime | 1046 | FileId(0) 106..108 |
1002 | "#]], | 1047 | "#]], |
1003 | ); | 1048 | ); |
1004 | } | 1049 | } |
@@ -1010,10 +1055,10 @@ fn foo<'a, 'b: 'a>(x: &'a$0 ()) -> &'a () where &'a (): Foo<'a> { | |||
1010 | type Foo<'a, T> where T: 'a$0 = &'a T; | 1055 | type Foo<'a, T> where T: 'a$0 = &'a T; |
1011 | "#, | 1056 | "#, |
1012 | expect![[r#" | 1057 | expect![[r#" |
1013 | 'a LifetimeParam FileId(0) 9..11 9..11 Lifetime | 1058 | 'a LifetimeParam FileId(0) 9..11 9..11 |
1014 | 1059 | ||
1015 | FileId(0) 25..27 Lifetime | 1060 | FileId(0) 25..27 |
1016 | FileId(0) 31..33 Lifetime | 1061 | FileId(0) 31..33 |
1017 | "#]], | 1062 | "#]], |
1018 | ); | 1063 | ); |
1019 | } | 1064 | } |
@@ -1032,11 +1077,11 @@ impl<'a> Foo<'a> for &'a () { | |||
1032 | } | 1077 | } |
1033 | "#, | 1078 | "#, |
1034 | expect![[r#" | 1079 | expect![[r#" |
1035 | 'a LifetimeParam FileId(0) 47..49 47..49 Lifetime | 1080 | 'a LifetimeParam FileId(0) 47..49 47..49 |
1036 | 1081 | ||
1037 | FileId(0) 55..57 Lifetime | 1082 | FileId(0) 55..57 |
1038 | FileId(0) 64..66 Lifetime | 1083 | FileId(0) 64..66 |
1039 | FileId(0) 89..91 Lifetime | 1084 | FileId(0) 89..91 |
1040 | "#]], | 1085 | "#]], |
1041 | ); | 1086 | ); |
1042 | } | 1087 | } |
@@ -1052,9 +1097,9 @@ fn main() { | |||
1052 | } | 1097 | } |
1053 | "#, | 1098 | "#, |
1054 | expect![[r#" | 1099 | expect![[r#" |
1055 | a Local FileId(0) 59..60 59..60 Other | 1100 | a Local FileId(0) 59..60 59..60 |
1056 | 1101 | ||
1057 | FileId(0) 80..81 Other Read | 1102 | FileId(0) 80..81 Read |
1058 | "#]], | 1103 | "#]], |
1059 | ); | 1104 | ); |
1060 | } | 1105 | } |
@@ -1070,9 +1115,9 @@ fn main() { | |||
1070 | } | 1115 | } |
1071 | "#, | 1116 | "#, |
1072 | expect![[r#" | 1117 | expect![[r#" |
1073 | a Local FileId(0) 59..60 59..60 Other | 1118 | a Local FileId(0) 59..60 59..60 |
1074 | 1119 | ||
1075 | FileId(0) 80..81 Other Read | 1120 | FileId(0) 80..81 Read |
1076 | "#]], | 1121 | "#]], |
1077 | ); | 1122 | ); |
1078 | } | 1123 | } |
@@ -1091,10 +1136,10 @@ fn foo<'a>() -> &'a () { | |||
1091 | } | 1136 | } |
1092 | "#, | 1137 | "#, |
1093 | expect![[r#" | 1138 | expect![[r#" |
1094 | 'a Label FileId(0) 29..32 29..31 Lifetime | 1139 | 'a Label FileId(0) 29..32 29..31 |
1095 | 1140 | ||
1096 | FileId(0) 80..82 Lifetime | 1141 | FileId(0) 80..82 |
1097 | FileId(0) 108..110 Lifetime | 1142 | FileId(0) 108..110 |
1098 | "#]], | 1143 | "#]], |
1099 | ); | 1144 | ); |
1100 | } | 1145 | } |
@@ -1108,9 +1153,108 @@ fn foo<const FOO$0: usize>() -> usize { | |||
1108 | } | 1153 | } |
1109 | "#, | 1154 | "#, |
1110 | expect![[r#" | 1155 | expect![[r#" |
1111 | FOO ConstParam FileId(0) 7..23 13..16 Other | 1156 | FOO ConstParam FileId(0) 7..23 13..16 |
1157 | |||
1158 | FileId(0) 42..45 | ||
1159 | "#]], | ||
1160 | ); | ||
1161 | } | ||
1162 | |||
1163 | #[test] | ||
1164 | fn test_find_self_ty_in_trait_def() { | ||
1165 | check( | ||
1166 | r#" | ||
1167 | trait Foo { | ||
1168 | fn f() -> Self$0; | ||
1169 | } | ||
1170 | "#, | ||
1171 | expect![[r#" | ||
1172 | Self TypeParam FileId(0) 6..9 6..9 | ||
1173 | |||
1174 | FileId(0) 26..30 | ||
1175 | "#]], | ||
1176 | ); | ||
1177 | } | ||
1178 | |||
1179 | #[test] | ||
1180 | fn test_self_variant_with_payload() { | ||
1181 | check( | ||
1182 | r#" | ||
1183 | enum Foo { Bar() } | ||
1184 | |||
1185 | impl Foo { | ||
1186 | fn foo(self) { | ||
1187 | match self { | ||
1188 | Self::Bar$0() => (), | ||
1189 | } | ||
1190 | } | ||
1191 | } | ||
1192 | |||
1193 | "#, | ||
1194 | expect![[r#" | ||
1195 | Bar Variant FileId(0) 11..16 11..14 | ||
1196 | |||
1197 | FileId(0) 89..92 | ||
1198 | "#]], | ||
1199 | ); | ||
1200 | } | ||
1201 | |||
1202 | #[test] | ||
1203 | fn test_attr_differs_from_fn_with_same_name() { | ||
1204 | check( | ||
1205 | r#" | ||
1206 | #[test] | ||
1207 | fn test$0() { | ||
1208 | test(); | ||
1209 | } | ||
1210 | "#, | ||
1211 | expect![[r#" | ||
1212 | test Function FileId(0) 0..33 11..15 | ||
1213 | |||
1214 | FileId(0) 24..28 | ||
1215 | "#]], | ||
1216 | ); | ||
1217 | } | ||
1218 | |||
1219 | #[test] | ||
1220 | fn test_attr_matches_proc_macro_fn() { | ||
1221 | check( | ||
1222 | r#" | ||
1223 | #[proc_macro_attribute] | ||
1224 | fn my_proc_macro() {} | ||
1225 | |||
1226 | #[my_proc_macro$0] | ||
1227 | fn test() {} | ||
1228 | "#, | ||
1229 | expect![[r#" | ||
1230 | my_proc_macro Function FileId(0) 0..45 27..40 | ||
1231 | |||
1232 | FileId(0) 49..62 | ||
1233 | "#]], | ||
1234 | ); | ||
1235 | } | ||
1236 | |||
1237 | #[test] | ||
1238 | fn test_const_in_pattern() { | ||
1239 | check( | ||
1240 | r#" | ||
1241 | const A$0: i32 = 42; | ||
1242 | |||
1243 | fn main() { | ||
1244 | match A { | ||
1245 | A => (), | ||
1246 | _ => (), | ||
1247 | } | ||
1248 | if let A = A {} | ||
1249 | } | ||
1250 | "#, | ||
1251 | expect![[r#" | ||
1252 | A Const FileId(0) 0..18 6..7 | ||
1112 | 1253 | ||
1113 | FileId(0) 42..45 Other | 1254 | FileId(0) 42..43 |
1255 | FileId(0) 54..55 | ||
1256 | FileId(0) 97..98 | ||
1257 | FileId(0) 101..102 | ||
1114 | "#]], | 1258 | "#]], |
1115 | ); | 1259 | ); |
1116 | } | 1260 | } |
diff --git a/crates/ide/src/references/rename.rs b/crates/ide/src/references/rename.rs index 4df189c98..22ddeeae3 100644 --- a/crates/ide/src/references/rename.rs +++ b/crates/ide/src/references/rename.rs | |||
@@ -1,25 +1,23 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | use std::fmt::{self, Display}; | 2 | use std::fmt::{self, Display}; |
3 | 3 | ||
4 | use hir::{Module, ModuleDef, ModuleSource, Semantics}; | 4 | use either::Either; |
5 | use hir::{HasSource, InFile, Module, ModuleDef, ModuleSource, Semantics}; | ||
5 | use ide_db::{ | 6 | use ide_db::{ |
6 | base_db::{AnchoredPathBuf, FileId, FileRange}, | 7 | base_db::{AnchoredPathBuf, FileId}, |
7 | defs::{Definition, NameClass, NameRefClass}, | 8 | defs::{Definition, NameClass, NameRefClass}, |
8 | search::FileReference, | 9 | search::FileReference, |
9 | RootDatabase, | 10 | RootDatabase, |
10 | }; | 11 | }; |
12 | use stdx::never; | ||
11 | use syntax::{ | 13 | use syntax::{ |
12 | algo::find_node_at_offset, | ||
13 | ast::{self, NameOwner}, | 14 | ast::{self, NameOwner}, |
14 | lex_single_syntax_kind, match_ast, AstNode, SyntaxKind, SyntaxNode, T, | 15 | lex_single_syntax_kind, AstNode, SyntaxKind, SyntaxNode, T, |
15 | }; | 16 | }; |
16 | use test_utils::mark; | 17 | use test_utils::mark; |
17 | use text_edit::TextEdit; | 18 | use text_edit::TextEdit; |
18 | 19 | ||
19 | use crate::{ | 20 | use crate::{display::TryToNav, FilePosition, FileSystemEdit, RangeInfo, Sourc |