aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ide/src/annotations.rs895
1 files changed, 707 insertions, 188 deletions
diff --git a/crates/ide/src/annotations.rs b/crates/ide/src/annotations.rs
index 43250bf5d..414a60bed 100644
--- a/crates/ide/src/annotations.rs
+++ b/crates/ide/src/annotations.rs
@@ -154,103 +154,143 @@ fn should_skip_runnable(kind: &RunnableKind, binary_target: bool) -> bool {
154 154
155#[cfg(test)] 155#[cfg(test)]
156mod tests { 156mod tests {
157 use ide_db::base_db::{FileId, FileRange}; 157 use expect_test::{expect, Expect};
158 use syntax::{TextRange, TextSize};
159 158
160 use crate::{fixture, Annotation, AnnotationConfig, AnnotationKind, RunnableKind}; 159 use crate::{fixture, Annotation, AnnotationConfig};
161 160
162 fn get_annotations( 161 fn check(ra_fixture: &str, expect: Expect) {
163 ra_fixture: &str,
164 annotation_config: AnnotationConfig,
165 ) -> (FileId, Vec<Annotation>) {
166 let (analysis, file_id) = fixture::file(ra_fixture); 162 let (analysis, file_id) = fixture::file(ra_fixture);
167 163
168 let annotations: Vec<Annotation> = analysis 164 let annotations: Vec<Annotation> = analysis
169 .annotations(file_id, annotation_config) 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 )
170 .unwrap() 177 .unwrap()
171 .into_iter() 178 .into_iter()
172 .map(move |annotation| analysis.resolve_annotation(annotation).unwrap()) 179 .map(|annotation| analysis.resolve_annotation(annotation).unwrap())
173 .collect(); 180 .collect();
174 181
175 if annotations.len() == 0 { 182 expect.assert_debug_eq(&annotations);
176 panic!("unresolved annotations")
177 }
178
179 (file_id, annotations)
180 }
181
182 macro_rules! check_annotation {
183 ( $ra_fixture:expr, $config:expr, $item_positions:expr, $pattern:pat, $checker:expr ) => {
184 let (file_id, annotations) = get_annotations($ra_fixture, $config);
185
186 annotations.into_iter().for_each(|annotation| {
187 assert!($item_positions.contains(&annotation.range));
188
189 match annotation.kind {
190 $pattern => $checker(file_id),
191 _ => panic!("Unexpected annotation kind"),
192 }
193 });
194 };
195 } 183 }
196 184
197 #[test] 185 #[test]
198 fn const_annotations() { 186 fn const_annotations() {
199 check_annotation!( 187 check(
200 r#" 188 r#"
201const DEMO: i32 = 123; 189const DEMO: i32 = 123;
202 190
191const UNUSED: i32 = 123;
192
203fn main() { 193fn main() {
204 let hello = DEMO; 194 let hello = DEMO;
205} 195}
206 "#, 196 "#,
207 AnnotationConfig { 197 expect![[r#"
208 binary_target: false, 198 [
209 annotate_runnables: false, 199 Annotation {
210 annotate_impls: false, 200 range: 50..85,
211 annotate_references: true, 201 kind: Runnable {
212 annotate_method_references: false, 202 debug: true,
213 run: false, 203 runnable: Runnable {
214 debug: false, 204 nav: NavigationTarget {
215 }, 205 file_id: FileId(
216 &[TextRange::new(TextSize::from(0), TextSize::from(22))], 206 0,
217 AnnotationKind::HasReferences { data: Some(ranges), .. }, 207 ),
218 |file_id| assert_eq!( 208 full_range: 50..85,
219 *ranges.first().unwrap(), 209 focus_range: 53..57,
220 FileRange { 210 name: "main",
221 file_id, 211 kind: Function,
222 range: TextRange::new(TextSize::from(52), TextSize::from(56)) 212 },
223 } 213 kind: Bin,
224 ) 214 cfg: None,
225 ); 215 },
226 } 216 },
227 217 },
228 #[test] 218 Annotation {
229 fn unused_const_annotations() { 219 range: 50..85,
230 check_annotation!( 220 kind: Runnable {
231 r#" 221 debug: false,
232const DEMO: i32 = 123; 222 runnable: Runnable {
233 223 nav: NavigationTarget {
234fn main() {} 224 file_id: FileId(
235 "#, 225 0,
236 AnnotationConfig { 226 ),
237 binary_target: false, 227 full_range: 50..85,
238 annotate_runnables: false, 228 focus_range: 53..57,
239 annotate_impls: false, 229 name: "main",
240 annotate_references: true, 230 kind: Function,
241 annotate_method_references: false, 231 },
242 run: false, 232 kind: Bin,
243 debug: false, 233 cfg: None,
244 }, 234 },
245 &[TextRange::new(TextSize::from(0), TextSize::from(22))], 235 },
246 AnnotationKind::HasReferences { data: Some(ranges), .. }, 236 },
247 |_| assert_eq!(ranges.len(), 0) 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 "#]],
248 ); 288 );
249 } 289 }
250 290
251 #[test] 291 #[test]
252 fn struct_references_annotations() { 292 fn struct_references_annotations() {
253 check_annotation!( 293 check(
254 r#" 294 r#"
255struct Test; 295struct Test;
256 296
@@ -258,30 +298,103 @@ fn main() {
258 let test = Test; 298 let test = Test;
259} 299}
260 "#, 300 "#,
261 AnnotationConfig { 301 expect![[r#"
262 binary_target: false, 302 [
263 annotate_runnables: false, 303 Annotation {
264 annotate_impls: false, 304 range: 14..48,
265 annotate_references: true, 305 kind: Runnable {
266 annotate_method_references: false, 306 debug: true,
267 run: false, 307 runnable: Runnable {
268 debug: false, 308 nav: NavigationTarget {
269 }, 309 file_id: FileId(
270 &[TextRange::new(TextSize::from(0), TextSize::from(12))], 310 0,
271 AnnotationKind::HasReferences { data: Some(ranges), .. }, 311 ),
272 |file_id| assert_eq!( 312 full_range: 14..48,
273 *ranges.first().unwrap(), 313 focus_range: 17..21,
274 FileRange { 314 name: "main",
275 file_id, 315 kind: Function,
276 range: TextRange::new(TextSize::from(41), TextSize::from(45)) 316 },
277 } 317 kind: Bin,
278 ) 318 cfg: None,
319 },
320 },
321 },
322 Annotation {
323 range: 14..48,
324 kind: Runnable {
325 debug: false,
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 "#]],
279 ); 392 );
280 } 393 }
281 394
282 #[test] 395 #[test]
283 fn struct_and_trait_impls_annotations() { 396 fn struct_and_trait_impls_annotations() {
284 check_annotation!( 397 check(
285 r#" 398 r#"
286struct Test; 399struct Test;
287 400
@@ -293,84 +406,229 @@ fn main() {
293 let test = Test; 406 let test = Test;
294} 407}
295 "#, 408 "#,
296 AnnotationConfig { 409 expect![[r#"
297 binary_target: false, 410 [
298 annotate_runnables: false, 411 Annotation {
299 annotate_impls: true, 412 range: 66..100,
300 annotate_references: false, 413 kind: Runnable {
301 annotate_method_references: false, 414 debug: true,
302 run: false, 415 runnable: Runnable {
303 debug: false, 416 nav: NavigationTarget {
304 }, 417 file_id: FileId(
305 &[ 418 0,
306 TextRange::new(TextSize::from(0), TextSize::from(12)), 419 ),
307 TextRange::new(TextSize::from(14), TextSize::from(34)) 420 full_range: 66..100,
308 ], 421 focus_range: 69..73,
309 AnnotationKind::HasImpls { data: Some(ranges), .. }, 422 name: "main",
310 |_| assert_eq!( 423 kind: Function,
311 ranges.first().unwrap().full_range, 424 },
312 TextRange::new(TextSize::from(36), TextSize::from(64)) 425 kind: Bin,
313 ) 426 cfg: None,
314 ); 427 },
315 } 428 },
316 429 },
317 #[test] 430 Annotation {
318 fn run_annotation() { 431 range: 66..100,
319 check_annotation!( 432 kind: Runnable {
320 r#" 433 debug: false,
321fn main() {} 434 runnable: Runnable {
322 "#, 435 nav: NavigationTarget {
323 AnnotationConfig { 436 file_id: FileId(
324 binary_target: true, 437 0,
325 annotate_runnables: true, 438 ),
326 annotate_impls: false, 439 full_range: 66..100,
327 annotate_references: false, 440 focus_range: 69..73,
328 annotate_method_references: false, 441 name: "main",
329 run: true, 442 kind: Function,
330 debug: false, 443 },
331 }, 444 kind: Bin,
332 &[TextRange::new(TextSize::from(0), TextSize::from(12))], 445 cfg: None,
333 AnnotationKind::Runnable { debug: false, runnable }, 446 },
334 |_| { 447 },
335 assert!(matches!(runnable.kind, RunnableKind::Bin)); 448 },
336 assert!(runnable.action().run_title.contains("Run")); 449 Annotation {
337 } 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 "#]],
338 ); 561 );
339 } 562 }
340 563
341 #[test] 564 #[test]
342 fn debug_annotation() { 565 fn runnable_annotation() {
343 check_annotation!( 566 check(
344 r#" 567 r#"
345fn main() {} 568fn main() {}
346 "#, 569 "#,
347 AnnotationConfig { 570 expect![[r#"
348 binary_target: true, 571 [
349 annotate_runnables: true, 572 Annotation {
350 annotate_impls: false, 573 range: 0..12,
351 annotate_references: false, 574 kind: Runnable {
352 annotate_method_references: false, 575 debug: true,
353 run: false, 576 runnable: Runnable {
354 debug: true, 577 nav: NavigationTarget {
355 }, 578 file_id: FileId(
356 &[TextRange::new(TextSize::from(0), TextSize::from(12))], 579 0,
357 AnnotationKind::Runnable { debug: true, runnable }, 580 ),
358 |_| { 581 full_range: 0..12,
359 assert!(matches!(runnable.kind, RunnableKind::Bin)); 582 focus_range: 3..7,
360 assert!(runnable.action().debugee); 583 name: "main",
361 } 584 kind: Function,
585 },
586 kind: Bin,
587 cfg: None,
588 },
589 },
590 },
591 Annotation {
592 range: 0..12,
593 kind: Runnable {
594 debug: false,
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 "#]],
362 ); 626 );
363 } 627 }
364 628
365 #[test] 629 #[test]
366 fn method_annotations() { 630 fn method_annotations() {
367 // We actually want to skip `fn main` annotation, as it has no references in it 631 check(
368 // but just ignoring empty reference slices would lead to false-positive if something
369 // goes wrong in annotation resolving mechanism. By tracking if we iterated before finding
370 // an empty slice we can track if everything is settled.
371 let mut iterated_once = false;
372
373 check_annotation!(
374 r#" 632 r#"
375struct Test; 633struct Test;
376 634
@@ -382,37 +640,298 @@ fn main() {
382 Test.self_by_ref(); 640 Test.self_by_ref();
383} 641}
384 "#, 642 "#,
385 AnnotationConfig { 643 expect![[r#"
386 binary_target: false, 644 [
387 annotate_runnables: false, 645 Annotation {
388 annotate_impls: false, 646 range: 58..95,
389 annotate_references: false, 647 kind: Runnable {
390 annotate_method_references: true, 648 debug: true,
391 run: false, 649 runnable: Runnable {
392 debug: false, 650 nav: NavigationTarget {
393 }, 651 file_id: FileId(
394 &[ 652 0,
395 TextRange::new(TextSize::from(33), TextSize::from(44)), 653 ),
396 TextRange::new(TextSize::from(61), TextSize::from(65)) 654 full_range: 58..95,
397 ], 655 focus_range: 61..65,
398 AnnotationKind::HasReferences { data: Some(ranges), .. }, 656 name: "main",
399 |file_id| { 657 kind: Function,
400 match ranges.as_slice() { 658 },
401 [first, ..] => { 659 kind: Bin,
402 assert_eq!( 660 cfg: None,
403 *first, 661 },
404 FileRange { 662 },
405 file_id, 663 },
406 range: TextRange::new(TextSize::from(79), TextSize::from(90)) 664 Annotation {
407 } 665 range: 58..95,
408 ); 666 kind: Runnable {
409 667 debug: false,
410 iterated_once = true; 668 runnable: Runnable {
411 } 669 nav: NavigationTarget {
412 [] if iterated_once => {} 670 file_id: FileId(
413 [] => panic!("One reference was expected but not found"), 671 0,
414 } 672 ),
415 } 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#"
778fn main() {}
779
780mod 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: true,
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: false,
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: true,
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: false,
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: true,
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: false,
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 "#]],
416 ); 935 );
417 } 936 }
418} 937}