ide/
annotations.rs

1use hir::{HasSource, InFile, InRealFile, Semantics};
2use ide_db::{
3    FileId, FilePosition, FileRange, FxIndexSet, MiniCore, RootDatabase, defs::Definition,
4    helpers::visit_file_defs,
5};
6use itertools::Itertools;
7use syntax::{AstNode, TextRange, ast::HasName};
8
9use crate::{
10    NavigationTarget, RunnableKind,
11    annotations::fn_references::find_all_methods,
12    goto_implementation::{GotoImplementationConfig, goto_implementation},
13    navigation_target,
14    references::{FindAllRefsConfig, find_all_refs},
15    runnables::{Runnable, runnables},
16};
17
18mod fn_references;
19
20// Feature: Annotations
21//
22// Provides user with annotations above items for looking up references or impl blocks
23// and running/debugging binaries.
24//
25// ![Annotations](https://user-images.githubusercontent.com/48062697/113020672-b7c34f00-917a-11eb-8f6e-858735660a0e.png)
26#[derive(Debug, Hash, PartialEq, Eq)]
27pub struct Annotation {
28    pub range: TextRange,
29    pub kind: AnnotationKind,
30}
31
32#[derive(Debug, Hash, PartialEq, Eq)]
33pub enum AnnotationKind {
34    Runnable(Runnable),
35    HasImpls { pos: FilePosition, data: Option<Vec<NavigationTarget>> },
36    HasReferences { pos: FilePosition, data: Option<Vec<FileRange>> },
37}
38
39pub struct AnnotationConfig<'a> {
40    pub binary_target: bool,
41    pub annotate_runnables: bool,
42    pub annotate_impls: bool,
43    pub annotate_references: bool,
44    pub annotate_method_references: bool,
45    pub annotate_enum_variant_references: bool,
46    pub location: AnnotationLocation,
47    pub filter_adjacent_derive_implementations: bool,
48    pub minicore: MiniCore<'a>,
49}
50
51pub enum AnnotationLocation {
52    AboveName,
53    AboveWholeItem,
54}
55
56pub(crate) fn annotations(
57    db: &RootDatabase,
58    config: &AnnotationConfig<'_>,
59    file_id: FileId,
60) -> Vec<Annotation> {
61    let mut annotations = FxIndexSet::default();
62
63    if config.annotate_runnables {
64        for runnable in runnables(db, file_id) {
65            if should_skip_runnable(&runnable.kind, config.binary_target) {
66                continue;
67            }
68
69            let range = runnable.nav.focus_or_full_range();
70
71            annotations.insert(Annotation { range, kind: AnnotationKind::Runnable(runnable) });
72        }
73    }
74
75    let mk_ranges = |(range, focus): (_, Option<_>)| {
76        let cmd_target: TextRange = focus.unwrap_or(range);
77        let annotation_range = match config.location {
78            AnnotationLocation::AboveName => cmd_target,
79            AnnotationLocation::AboveWholeItem => range,
80        };
81        let target_pos = FilePosition { file_id, offset: cmd_target.start() };
82        (annotation_range, target_pos)
83    };
84
85    visit_file_defs(&Semantics::new(db), file_id, &mut |def| {
86        let range = match def {
87            Definition::Const(konst) if config.annotate_references => {
88                konst.source(db).and_then(|node| name_range(db, node, file_id))
89            }
90            Definition::Trait(trait_) if config.annotate_references || config.annotate_impls => {
91                trait_.source(db).and_then(|node| name_range(db, node, file_id))
92            }
93            Definition::Adt(adt) => match adt {
94                hir::Adt::Enum(enum_) => {
95                    if config.annotate_enum_variant_references {
96                        enum_
97                            .variants(db)
98                            .into_iter()
99                            .filter_map(|variant| {
100                                variant.source(db).and_then(|node| name_range(db, node, file_id))
101                            })
102                            .for_each(|range| {
103                                let (annotation_range, target_position) = mk_ranges(range);
104                                annotations.insert(Annotation {
105                                    range: annotation_range,
106                                    kind: AnnotationKind::HasReferences {
107                                        pos: target_position,
108                                        data: None,
109                                    },
110                                });
111                            })
112                    }
113                    if config.annotate_references || config.annotate_impls {
114                        enum_.source(db).and_then(|node| name_range(db, node, file_id))
115                    } else {
116                        None
117                    }
118                }
119                _ => {
120                    if config.annotate_references || config.annotate_impls {
121                        adt.source(db).and_then(|node| name_range(db, node, file_id))
122                    } else {
123                        None
124                    }
125                }
126            },
127            _ => None,
128        };
129
130        let range = match range {
131            Some(range) => range,
132            None => return,
133        };
134        let (annotation_range, target_pos) = mk_ranges(range);
135        if config.annotate_impls && !matches!(def, Definition::Const(_)) {
136            annotations.insert(Annotation {
137                range: annotation_range,
138                kind: AnnotationKind::HasImpls { pos: target_pos, data: None },
139            });
140        }
141
142        if config.annotate_references {
143            annotations.insert(Annotation {
144                range: annotation_range,
145                kind: AnnotationKind::HasReferences { pos: target_pos, data: None },
146            });
147        }
148
149        fn name_range<T: HasName>(
150            db: &RootDatabase,
151            node: InFile<T>,
152            source_file_id: FileId,
153        ) -> Option<(TextRange, Option<TextRange>)> {
154            if let Some(name) = node.value.name().map(|name| name.syntax().text_range()) {
155                // if we have a name, try mapping that out of the macro expansion as we can put the
156                // annotation on that name token
157                // See `test_no_annotations_macro_struct_def` vs `test_annotations_macro_struct_def_call_site`
158                let res = navigation_target::orig_range_with_focus_r(
159                    db,
160                    node.file_id,
161                    node.value.syntax().text_range(),
162                    Some(name),
163                );
164                if res.call_site.0.file_id == source_file_id
165                    && let Some(name_range) = res.call_site.1
166                {
167                    return Some((res.call_site.0.range, Some(name_range)));
168                }
169            };
170            // otherwise try upmapping the entire node out of attributes
171            let InRealFile { file_id, value } = node.original_ast_node_rooted(db)?;
172            if file_id.file_id(db) == source_file_id {
173                Some((
174                    value.syntax().text_range(),
175                    value.name().map(|name| name.syntax().text_range()),
176                ))
177            } else {
178                None
179            }
180        }
181    });
182
183    if config.annotate_method_references {
184        annotations.extend(find_all_methods(db, file_id).into_iter().map(|range| {
185            let (annotation_range, target_range) = mk_ranges(range);
186            Annotation {
187                range: annotation_range,
188                kind: AnnotationKind::HasReferences { pos: target_range, data: None },
189            }
190        }));
191    }
192
193    annotations
194        .into_iter()
195        .sorted_by_key(|a| {
196            (a.range.start(), a.range.end(), matches!(a.kind, AnnotationKind::Runnable(..)))
197        })
198        .collect()
199}
200
201pub(crate) fn resolve_annotation(
202    db: &RootDatabase,
203    config: &AnnotationConfig<'_>,
204    mut annotation: Annotation,
205) -> Annotation {
206    match annotation.kind {
207        AnnotationKind::HasImpls { pos, ref mut data } => {
208            let goto_implementation_config = GotoImplementationConfig {
209                filter_adjacent_derive_implementations: config
210                    .filter_adjacent_derive_implementations,
211            };
212            *data =
213                goto_implementation(db, &goto_implementation_config, pos).map(|range| range.info);
214        }
215        AnnotationKind::HasReferences { pos, ref mut data } => {
216            *data = find_all_refs(
217                &Semantics::new(db),
218                pos,
219                &FindAllRefsConfig { search_scope: None, minicore: config.minicore },
220            )
221            .map(|result| {
222                result
223                    .into_iter()
224                    .flat_map(|res| res.references)
225                    .flat_map(|(file_id, access)| {
226                        access.into_iter().map(move |(range, _)| FileRange { file_id, range })
227                    })
228                    .collect()
229            });
230        }
231        _ => {}
232    };
233
234    annotation
235}
236
237fn should_skip_runnable(kind: &RunnableKind, binary_target: bool) -> bool {
238    match kind {
239        RunnableKind::Bin => !binary_target,
240        _ => false,
241    }
242}
243
244#[cfg(test)]
245mod tests {
246    use expect_test::{Expect, expect};
247    use ide_db::MiniCore;
248
249    use crate::{Annotation, AnnotationConfig, fixture};
250
251    use super::AnnotationLocation;
252
253    const DEFAULT_CONFIG: AnnotationConfig<'_> = AnnotationConfig {
254        binary_target: true,
255        annotate_runnables: true,
256        annotate_impls: true,
257        annotate_references: true,
258        annotate_method_references: true,
259        annotate_enum_variant_references: true,
260        location: AnnotationLocation::AboveName,
261        minicore: MiniCore::default(),
262        filter_adjacent_derive_implementations: false,
263    };
264
265    fn check_with_config(
266        #[rust_analyzer::rust_fixture] ra_fixture: &str,
267        expect: Expect,
268        config: &AnnotationConfig<'_>,
269    ) {
270        let (analysis, file_id) = fixture::file(ra_fixture);
271
272        let annotations: Vec<Annotation> = analysis
273            .annotations(config, file_id)
274            .unwrap()
275            .into_iter()
276            .map(|annotation| analysis.resolve_annotation(&DEFAULT_CONFIG, annotation).unwrap())
277            .collect();
278
279        expect.assert_debug_eq(&annotations);
280    }
281
282    fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
283        check_with_config(ra_fixture, expect, &DEFAULT_CONFIG);
284    }
285
286    #[test]
287    fn const_annotations() {
288        check(
289            r#"
290const DEMO: i32 = 123;
291
292const UNUSED: i32 = 123;
293
294fn main() {
295    let hello = DEMO;
296}
297            "#,
298            expect![[r#"
299                [
300                    Annotation {
301                        range: 6..10,
302                        kind: HasReferences {
303                            pos: FilePositionWrapper {
304                                file_id: FileId(
305                                    0,
306                                ),
307                                offset: 6,
308                            },
309                            data: Some(
310                                [
311                                    FileRangeWrapper {
312                                        file_id: FileId(
313                                            0,
314                                        ),
315                                        range: 78..82,
316                                    },
317                                ],
318                            ),
319                        },
320                    },
321                    Annotation {
322                        range: 30..36,
323                        kind: HasReferences {
324                            pos: FilePositionWrapper {
325                                file_id: FileId(
326                                    0,
327                                ),
328                                offset: 30,
329                            },
330                            data: Some(
331                                [],
332                            ),
333                        },
334                    },
335                    Annotation {
336                        range: 53..57,
337                        kind: HasReferences {
338                            pos: FilePositionWrapper {
339                                file_id: FileId(
340                                    0,
341                                ),
342                                offset: 53,
343                            },
344                            data: Some(
345                                [],
346                            ),
347                        },
348                    },
349                    Annotation {
350                        range: 53..57,
351                        kind: Runnable(
352                            Runnable {
353                                use_name_in_title: false,
354                                nav: NavigationTarget {
355                                    file_id: FileId(
356                                        0,
357                                    ),
358                                    full_range: 50..85,
359                                    focus_range: 53..57,
360                                    name: "main",
361                                    kind: Function,
362                                },
363                                kind: Bin,
364                                cfg: None,
365                                update_test: UpdateTest {
366                                    expect_test: false,
367                                    insta: false,
368                                    snapbox: false,
369                                },
370                            },
371                        ),
372                    },
373                ]
374            "#]],
375        );
376    }
377
378    #[test]
379    fn struct_references_annotations() {
380        check(
381            r#"
382struct Test;
383
384fn main() {
385    let test = Test;
386}
387            "#,
388            expect![[r#"
389                [
390                    Annotation {
391                        range: 7..11,
392                        kind: HasImpls {
393                            pos: FilePositionWrapper {
394                                file_id: FileId(
395                                    0,
396                                ),
397                                offset: 7,
398                            },
399                            data: Some(
400                                [],
401                            ),
402                        },
403                    },
404                    Annotation {
405                        range: 7..11,
406                        kind: HasReferences {
407                            pos: FilePositionWrapper {
408                                file_id: FileId(
409                                    0,
410                                ),
411                                offset: 7,
412                            },
413                            data: Some(
414                                [
415                                    FileRangeWrapper {
416                                        file_id: FileId(
417                                            0,
418                                        ),
419                                        range: 41..45,
420                                    },
421                                ],
422                            ),
423                        },
424                    },
425                    Annotation {
426                        range: 17..21,
427                        kind: HasReferences {
428                            pos: FilePositionWrapper {
429                                file_id: FileId(
430                                    0,
431                                ),
432                                offset: 17,
433                            },
434                            data: Some(
435                                [],
436                            ),
437                        },
438                    },
439                    Annotation {
440                        range: 17..21,
441                        kind: Runnable(
442                            Runnable {
443                                use_name_in_title: false,
444                                nav: NavigationTarget {
445                                    file_id: FileId(
446                                        0,
447                                    ),
448                                    full_range: 14..48,
449                                    focus_range: 17..21,
450                                    name: "main",
451                                    kind: Function,
452                                },
453                                kind: Bin,
454                                cfg: None,
455                                update_test: UpdateTest {
456                                    expect_test: false,
457                                    insta: false,
458                                    snapbox: false,
459                                },
460                            },
461                        ),
462                    },
463                ]
464            "#]],
465        );
466    }
467
468    #[test]
469    fn struct_and_trait_impls_annotations() {
470        check(
471            r#"
472struct Test;
473
474trait MyCoolTrait {}
475
476impl MyCoolTrait for Test {}
477
478fn main() {
479    let test = Test;
480}
481            "#,
482            expect![[r#"
483                [
484                    Annotation {
485                        range: 7..11,
486                        kind: HasImpls {
487                            pos: FilePositionWrapper {
488                                file_id: FileId(
489                                    0,
490                                ),
491                                offset: 7,
492                            },
493                            data: Some(
494                                [
495                                    NavigationTarget {
496                                        file_id: FileId(
497                                            0,
498                                        ),
499                                        full_range: 36..64,
500                                        focus_range: 57..61,
501                                        name: "impl",
502                                        kind: Impl,
503                                    },
504                                ],
505                            ),
506                        },
507                    },
508                    Annotation {
509                        range: 7..11,
510                        kind: HasReferences {
511                            pos: FilePositionWrapper {
512                                file_id: FileId(
513                                    0,
514                                ),
515                                offset: 7,
516                            },
517                            data: Some(
518                                [
519                                    FileRangeWrapper {
520                                        file_id: FileId(
521                                            0,
522                                        ),
523                                        range: 57..61,
524                                    },
525                                    FileRangeWrapper {
526                                        file_id: FileId(
527                                            0,
528                                        ),
529                                        range: 93..97,
530                                    },
531                                ],
532                            ),
533                        },
534                    },
535                    Annotation {
536                        range: 20..31,
537                        kind: HasImpls {
538                            pos: FilePositionWrapper {
539                                file_id: FileId(
540                                    0,
541                                ),
542                                offset: 20,
543                            },
544                            data: Some(
545                                [
546                                    NavigationTarget {
547                                        file_id: FileId(
548                                            0,
549                                        ),
550                                        full_range: 36..64,
551                                        focus_range: 57..61,
552                                        name: "impl",
553                                        kind: Impl,
554                                    },
555                                ],
556                            ),
557                        },
558                    },
559                    Annotation {
560                        range: 20..31,
561                        kind: HasReferences {
562                            pos: FilePositionWrapper {
563                                file_id: FileId(
564                                    0,
565                                ),
566                                offset: 20,
567                            },
568                            data: Some(
569                                [
570                                    FileRangeWrapper {
571                                        file_id: FileId(
572                                            0,
573                                        ),
574                                        range: 41..52,
575                                    },
576                                ],
577                            ),
578                        },
579                    },
580                    Annotation {
581                        range: 69..73,
582                        kind: HasReferences {
583                            pos: FilePositionWrapper {
584                                file_id: FileId(
585                                    0,
586                                ),
587                                offset: 69,
588                            },
589                            data: Some(
590                                [],
591                            ),
592                        },
593                    },
594                    Annotation {
595                        range: 69..73,
596                        kind: Runnable(
597                            Runnable {
598                                use_name_in_title: false,
599                                nav: NavigationTarget {
600                                    file_id: FileId(
601                                        0,
602                                    ),
603                                    full_range: 66..100,
604                                    focus_range: 69..73,
605                                    name: "main",
606                                    kind: Function,
607                                },
608                                kind: Bin,
609                                cfg: None,
610                                update_test: UpdateTest {
611                                    expect_test: false,
612                                    insta: false,
613                                    snapbox: false,
614                                },
615                            },
616                        ),
617                    },
618                ]
619            "#]],
620        );
621    }
622
623    #[test]
624    fn runnable_annotation() {
625        check(
626            r#"
627fn main() {}
628            "#,
629            expect![[r#"
630                [
631                    Annotation {
632                        range: 3..7,
633                        kind: HasReferences {
634                            pos: FilePositionWrapper {
635                                file_id: FileId(
636                                    0,
637                                ),
638                                offset: 3,
639                            },
640                            data: Some(
641                                [],
642                            ),
643                        },
644                    },
645                    Annotation {
646                        range: 3..7,
647                        kind: Runnable(
648                            Runnable {
649                                use_name_in_title: false,
650                                nav: NavigationTarget {
651                                    file_id: FileId(
652                                        0,
653                                    ),
654                                    full_range: 0..12,
655                                    focus_range: 3..7,
656                                    name: "main",
657                                    kind: Function,
658                                },
659                                kind: Bin,
660                                cfg: None,
661                                update_test: UpdateTest {
662                                    expect_test: false,
663                                    insta: false,
664                                    snapbox: false,
665                                },
666                            },
667                        ),
668                    },
669                ]
670            "#]],
671        );
672    }
673
674    #[test]
675    fn method_annotations() {
676        check(
677            r#"
678struct Test;
679
680impl Test {
681    fn self_by_ref(&self) {}
682}
683
684fn main() {
685    Test.self_by_ref();
686}
687            "#,
688            expect![[r#"
689                [
690                    Annotation {
691                        range: 7..11,
692                        kind: HasImpls {
693                            pos: FilePositionWrapper {
694                                file_id: FileId(
695                                    0,
696                                ),
697                                offset: 7,
698                            },
699                            data: Some(
700                                [
701                                    NavigationTarget {
702                                        file_id: FileId(
703                                            0,
704                                        ),
705                                        full_range: 14..56,
706                                        focus_range: 19..23,
707                                        name: "impl",
708                                        kind: Impl,
709                                    },
710                                ],
711                            ),
712                        },
713                    },
714                    Annotation {
715                        range: 7..11,
716                        kind: HasReferences {
717                            pos: FilePositionWrapper {
718                                file_id: FileId(
719                                    0,
720                                ),
721                                offset: 7,
722                            },
723                            data: Some(
724                                [
725                                    FileRangeWrapper {
726                                        file_id: FileId(
727                                            0,
728                                        ),
729                                        range: 19..23,
730                                    },
731                                    FileRangeWrapper {
732                                        file_id: FileId(
733                                            0,
734                                        ),
735                                        range: 74..78,
736                                    },
737                                ],
738                            ),
739                        },
740                    },
741                    Annotation {
742                        range: 33..44,
743                        kind: HasReferences {
744                            pos: FilePositionWrapper {
745                                file_id: FileId(
746                                    0,
747                                ),
748                                offset: 33,
749                            },
750                            data: Some(
751                                [
752                                    FileRangeWrapper {
753                                        file_id: FileId(
754                                            0,
755                                        ),
756                                        range: 79..90,
757                                    },
758                                ],
759                            ),
760                        },
761                    },
762                    Annotation {
763                        range: 61..65,
764                        kind: HasReferences {
765                            pos: FilePositionWrapper {
766                                file_id: FileId(
767                                    0,
768                                ),
769                                offset: 61,
770                            },
771                            data: Some(
772                                [],
773                            ),
774                        },
775                    },
776                    Annotation {
777                        range: 61..65,
778                        kind: Runnable(
779                            Runnable {
780                                use_name_in_title: false,
781                                nav: NavigationTarget {
782                                    file_id: FileId(
783                                        0,
784                                    ),
785                                    full_range: 58..95,
786                                    focus_range: 61..65,
787                                    name: "main",
788                                    kind: Function,
789                                },
790                                kind: Bin,
791                                cfg: None,
792                                update_test: UpdateTest {
793                                    expect_test: false,
794                                    insta: false,
795                                    snapbox: false,
796                                },
797                            },
798                        ),
799                    },
800                ]
801            "#]],
802        );
803    }
804
805    #[test]
806    fn test_annotations() {
807        check(
808            r#"
809fn main() {}
810
811mod tests {
812    #[test]
813    fn my_cool_test() {}
814}
815            "#,
816            expect![[r#"
817                [
818                    Annotation {
819                        range: 3..7,
820                        kind: HasReferences {
821                            pos: FilePositionWrapper {
822                                file_id: FileId(
823                                    0,
824                                ),
825                                offset: 3,
826                            },
827                            data: Some(
828                                [],
829                            ),
830                        },
831                    },
832                    Annotation {
833                        range: 3..7,
834                        kind: Runnable(
835                            Runnable {
836                                use_name_in_title: false,
837                                nav: NavigationTarget {
838                                    file_id: FileId(
839                                        0,
840                                    ),
841                                    full_range: 0..12,
842                                    focus_range: 3..7,
843                                    name: "main",
844                                    kind: Function,
845                                },
846                                kind: Bin,
847                                cfg: None,
848                                update_test: UpdateTest {
849                                    expect_test: false,
850                                    insta: false,
851                                    snapbox: false,
852                                },
853                            },
854                        ),
855                    },
856                    Annotation {
857                        range: 18..23,
858                        kind: Runnable(
859                            Runnable {
860                                use_name_in_title: false,
861                                nav: NavigationTarget {
862                                    file_id: FileId(
863                                        0,
864                                    ),
865                                    full_range: 14..64,
866                                    focus_range: 18..23,
867                                    name: "tests",
868                                    kind: Module,
869                                    description: "mod tests",
870                                },
871                                kind: TestMod {
872                                    path: "tests",
873                                },
874                                cfg: None,
875                                update_test: UpdateTest {
876                                    expect_test: false,
877                                    insta: false,
878                                    snapbox: false,
879                                },
880                            },
881                        ),
882                    },
883                    Annotation {
884                        range: 45..57,
885                        kind: Runnable(
886                            Runnable {
887                                use_name_in_title: false,
888                                nav: NavigationTarget {
889                                    file_id: FileId(
890                                        0,
891                                    ),
892                                    full_range: 30..62,
893                                    focus_range: 45..57,
894                                    name: "my_cool_test",
895                                    kind: Function,
896                                },
897                                kind: Test {
898                                    test_id: Path(
899                                        "tests::my_cool_test",
900                                    ),
901                                    attr: TestAttr {
902                                        ignore: false,
903                                    },
904                                },
905                                cfg: None,
906                                update_test: UpdateTest {
907                                    expect_test: false,
908                                    insta: false,
909                                    snapbox: false,
910                                },
911                            },
912                        ),
913                    },
914                ]
915            "#]],
916        );
917    }
918
919    #[test]
920    fn test_no_annotations_outside_module_tree() {
921        check(
922            r#"
923//- /foo.rs
924struct Foo;
925//- /lib.rs
926// this file comes last since `check` checks the first file only
927"#,
928            expect![[r#"
929                []
930            "#]],
931        );
932    }
933
934    #[test]
935    fn test_no_annotations_macro_struct_def() {
936        check(
937            r#"
938//- /lib.rs
939macro_rules! m {
940    () => {
941        struct A {}
942    };
943}
944
945m!();
946"#,
947            expect![[r#"
948                []
949            "#]],
950        );
951    }
952
953    #[test]
954    fn test_annotations_macro_struct_def_call_site() {
955        check(
956            r#"
957//- /lib.rs
958macro_rules! m {
959    ($name:ident) => {
960        struct $name {}
961    };
962}
963
964m! {
965    Name
966};
967"#,
968            expect![[r#"
969                [
970                    Annotation {
971                        range: 83..87,
972                        kind: HasImpls {
973                            pos: FilePositionWrapper {
974                                file_id: FileId(
975                                    0,
976                                ),
977                                offset: 83,
978                            },
979                            data: Some(
980                                [],
981                            ),
982                        },
983                    },
984                    Annotation {
985                        range: 83..87,
986                        kind: HasReferences {
987                            pos: FilePositionWrapper {
988                                file_id: FileId(
989                                    0,
990                                ),
991                                offset: 83,
992                            },
993                            data: Some(
994                                [],
995                            ),
996                        },
997                    },
998                ]
999            "#]],
1000        );
1001    }
1002
1003    #[test]
1004    fn test_annotations_appear_above_whole_item_when_configured_to_do_so() {
1005        check_with_config(
1006            r#"
1007/// This is a struct named Foo, obviously.
1008#[derive(Clone)]
1009struct Foo;
1010"#,
1011            expect![[r#"
1012                [
1013                    Annotation {
1014                        range: 0..71,
1015                        kind: HasImpls {
1016                            pos: FilePositionWrapper {
1017                                file_id: FileId(
1018                                    0,
1019                                ),
1020                                offset: 67,
1021                            },
1022                            data: Some(
1023                                [],
1024                            ),
1025                        },
1026                    },
1027                    Annotation {
1028                        range: 0..71,
1029                        kind: HasReferences {
1030                            pos: FilePositionWrapper {
1031                                file_id: FileId(
1032                                    0,
1033                                ),
1034                                offset: 67,
1035                            },
1036                            data: Some(
1037                                [],
1038                            ),
1039                        },
1040                    },
1041                ]
1042            "#]],
1043            &AnnotationConfig { location: AnnotationLocation::AboveWholeItem, ..DEFAULT_CONFIG },
1044        );
1045    }
1046}