Skip to main content

ide/
annotations.rs

1use hir::{HasSource, InFile, InRealFile, Semantics};
2use ide_db::{
3    FileId, FilePosition, FileRange, FxIndexSet, RootDatabase, defs::Definition,
4    helpers::visit_file_defs, ra_fixture::RaFixtureConfig,
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 ra_fixture: RaFixtureConfig<'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 {
220                    search_scope: None,
221                    ra_fixture: config.ra_fixture,
222                    exclude_imports: false,
223                    exclude_tests: false,
224                },
225            )
226            .map(|result| {
227                result
228                    .into_iter()
229                    .flat_map(|res| res.references)
230                    .flat_map(|(file_id, access)| {
231                        access.into_iter().map(move |(range, _)| FileRange { file_id, range })
232                    })
233                    .collect()
234            });
235        }
236        _ => {}
237    };
238
239    annotation
240}
241
242fn should_skip_runnable(kind: &RunnableKind, binary_target: bool) -> bool {
243    match kind {
244        RunnableKind::Bin => !binary_target,
245        _ => false,
246    }
247}
248
249#[cfg(test)]
250mod tests {
251    use expect_test::{Expect, expect};
252    use ide_db::ra_fixture::RaFixtureConfig;
253
254    use crate::{Annotation, AnnotationConfig, fixture};
255
256    use super::AnnotationLocation;
257
258    const DEFAULT_CONFIG: AnnotationConfig<'_> = AnnotationConfig {
259        binary_target: true,
260        annotate_runnables: true,
261        annotate_impls: true,
262        annotate_references: true,
263        annotate_method_references: true,
264        annotate_enum_variant_references: true,
265        location: AnnotationLocation::AboveName,
266        ra_fixture: RaFixtureConfig::default(),
267        filter_adjacent_derive_implementations: false,
268    };
269
270    fn check_with_config(
271        #[rust_analyzer::rust_fixture] ra_fixture: &str,
272        expect: Expect,
273        config: &AnnotationConfig<'_>,
274    ) {
275        let (analysis, file_id) = fixture::file(ra_fixture);
276
277        let annotations: Vec<Annotation> = analysis
278            .annotations(config, file_id)
279            .unwrap()
280            .into_iter()
281            .map(|annotation| analysis.resolve_annotation(&DEFAULT_CONFIG, annotation).unwrap())
282            .collect();
283
284        expect.assert_debug_eq(&annotations);
285    }
286
287    fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
288        check_with_config(ra_fixture, expect, &DEFAULT_CONFIG);
289    }
290
291    #[test]
292    fn const_annotations() {
293        check(
294            r#"
295const DEMO: i32 = 123;
296
297const UNUSED: i32 = 123;
298
299fn main() {
300    let hello = DEMO;
301}
302            "#,
303            expect![[r#"
304                [
305                    Annotation {
306                        range: 6..10,
307                        kind: HasReferences {
308                            pos: FilePositionWrapper {
309                                file_id: FileId(
310                                    0,
311                                ),
312                                offset: 6,
313                            },
314                            data: Some(
315                                [
316                                    FileRangeWrapper {
317                                        file_id: FileId(
318                                            0,
319                                        ),
320                                        range: 78..82,
321                                    },
322                                ],
323                            ),
324                        },
325                    },
326                    Annotation {
327                        range: 30..36,
328                        kind: HasReferences {
329                            pos: FilePositionWrapper {
330                                file_id: FileId(
331                                    0,
332                                ),
333                                offset: 30,
334                            },
335                            data: Some(
336                                [],
337                            ),
338                        },
339                    },
340                    Annotation {
341                        range: 53..57,
342                        kind: HasReferences {
343                            pos: FilePositionWrapper {
344                                file_id: FileId(
345                                    0,
346                                ),
347                                offset: 53,
348                            },
349                            data: Some(
350                                [],
351                            ),
352                        },
353                    },
354                    Annotation {
355                        range: 53..57,
356                        kind: Runnable(
357                            Runnable {
358                                use_name_in_title: false,
359                                nav: NavigationTarget {
360                                    file_id: FileId(
361                                        0,
362                                    ),
363                                    full_range: 50..85,
364                                    focus_range: 53..57,
365                                    name: "main",
366                                    kind: Function,
367                                },
368                                kind: Bin,
369                                cfg: None,
370                                update_test: UpdateTest {
371                                    expect_test: false,
372                                    insta: false,
373                                    snapbox: false,
374                                },
375                            },
376                        ),
377                    },
378                ]
379            "#]],
380        );
381    }
382
383    #[test]
384    fn struct_references_annotations() {
385        check(
386            r#"
387struct Test;
388
389fn main() {
390    let test = Test;
391}
392            "#,
393            expect![[r#"
394                [
395                    Annotation {
396                        range: 7..11,
397                        kind: HasImpls {
398                            pos: FilePositionWrapper {
399                                file_id: FileId(
400                                    0,
401                                ),
402                                offset: 7,
403                            },
404                            data: Some(
405                                [],
406                            ),
407                        },
408                    },
409                    Annotation {
410                        range: 7..11,
411                        kind: HasReferences {
412                            pos: FilePositionWrapper {
413                                file_id: FileId(
414                                    0,
415                                ),
416                                offset: 7,
417                            },
418                            data: Some(
419                                [
420                                    FileRangeWrapper {
421                                        file_id: FileId(
422                                            0,
423                                        ),
424                                        range: 41..45,
425                                    },
426                                ],
427                            ),
428                        },
429                    },
430                    Annotation {
431                        range: 17..21,
432                        kind: HasReferences {
433                            pos: FilePositionWrapper {
434                                file_id: FileId(
435                                    0,
436                                ),
437                                offset: 17,
438                            },
439                            data: Some(
440                                [],
441                            ),
442                        },
443                    },
444                    Annotation {
445                        range: 17..21,
446                        kind: Runnable(
447                            Runnable {
448                                use_name_in_title: false,
449                                nav: NavigationTarget {
450                                    file_id: FileId(
451                                        0,
452                                    ),
453                                    full_range: 14..48,
454                                    focus_range: 17..21,
455                                    name: "main",
456                                    kind: Function,
457                                },
458                                kind: Bin,
459                                cfg: None,
460                                update_test: UpdateTest {
461                                    expect_test: false,
462                                    insta: false,
463                                    snapbox: false,
464                                },
465                            },
466                        ),
467                    },
468                ]
469            "#]],
470        );
471    }
472
473    #[test]
474    fn struct_and_trait_impls_annotations() {
475        check(
476            r#"
477struct Test;
478
479trait MyCoolTrait {}
480
481impl MyCoolTrait for Test {}
482
483fn main() {
484    let test = Test;
485}
486            "#,
487            expect![[r#"
488                [
489                    Annotation {
490                        range: 7..11,
491                        kind: HasImpls {
492                            pos: FilePositionWrapper {
493                                file_id: FileId(
494                                    0,
495                                ),
496                                offset: 7,
497                            },
498                            data: Some(
499                                [
500                                    NavigationTarget {
501                                        file_id: FileId(
502                                            0,
503                                        ),
504                                        full_range: 36..64,
505                                        focus_range: 57..61,
506                                        name: "impl",
507                                        kind: Impl,
508                                    },
509                                ],
510                            ),
511                        },
512                    },
513                    Annotation {
514                        range: 7..11,
515                        kind: HasReferences {
516                            pos: FilePositionWrapper {
517                                file_id: FileId(
518                                    0,
519                                ),
520                                offset: 7,
521                            },
522                            data: Some(
523                                [
524                                    FileRangeWrapper {
525                                        file_id: FileId(
526                                            0,
527                                        ),
528                                        range: 57..61,
529                                    },
530                                    FileRangeWrapper {
531                                        file_id: FileId(
532                                            0,
533                                        ),
534                                        range: 93..97,
535                                    },
536                                ],
537                            ),
538                        },
539                    },
540                    Annotation {
541                        range: 20..31,
542                        kind: HasImpls {
543                            pos: FilePositionWrapper {
544                                file_id: FileId(
545                                    0,
546                                ),
547                                offset: 20,
548                            },
549                            data: Some(
550                                [
551                                    NavigationTarget {
552                                        file_id: FileId(
553                                            0,
554                                        ),
555                                        full_range: 36..64,
556                                        focus_range: 57..61,
557                                        name: "impl",
558                                        kind: Impl,
559                                    },
560                                ],
561                            ),
562                        },
563                    },
564                    Annotation {
565                        range: 20..31,
566                        kind: HasReferences {
567                            pos: FilePositionWrapper {
568                                file_id: FileId(
569                                    0,
570                                ),
571                                offset: 20,
572                            },
573                            data: Some(
574                                [
575                                    FileRangeWrapper {
576                                        file_id: FileId(
577                                            0,
578                                        ),
579                                        range: 41..52,
580                                    },
581                                ],
582                            ),
583                        },
584                    },
585                    Annotation {
586                        range: 69..73,
587                        kind: HasReferences {
588                            pos: FilePositionWrapper {
589                                file_id: FileId(
590                                    0,
591                                ),
592                                offset: 69,
593                            },
594                            data: Some(
595                                [],
596                            ),
597                        },
598                    },
599                    Annotation {
600                        range: 69..73,
601                        kind: Runnable(
602                            Runnable {
603                                use_name_in_title: false,
604                                nav: NavigationTarget {
605                                    file_id: FileId(
606                                        0,
607                                    ),
608                                    full_range: 66..100,
609                                    focus_range: 69..73,
610                                    name: "main",
611                                    kind: Function,
612                                },
613                                kind: Bin,
614                                cfg: None,
615                                update_test: UpdateTest {
616                                    expect_test: false,
617                                    insta: false,
618                                    snapbox: false,
619                                },
620                            },
621                        ),
622                    },
623                ]
624            "#]],
625        );
626    }
627
628    #[test]
629    fn runnable_annotation() {
630        check(
631            r#"
632fn main() {}
633            "#,
634            expect![[r#"
635                [
636                    Annotation {
637                        range: 3..7,
638                        kind: HasReferences {
639                            pos: FilePositionWrapper {
640                                file_id: FileId(
641                                    0,
642                                ),
643                                offset: 3,
644                            },
645                            data: Some(
646                                [],
647                            ),
648                        },
649                    },
650                    Annotation {
651                        range: 3..7,
652                        kind: Runnable(
653                            Runnable {
654                                use_name_in_title: false,
655                                nav: NavigationTarget {
656                                    file_id: FileId(
657                                        0,
658                                    ),
659                                    full_range: 0..12,
660                                    focus_range: 3..7,
661                                    name: "main",
662                                    kind: Function,
663                                },
664                                kind: Bin,
665                                cfg: None,
666                                update_test: UpdateTest {
667                                    expect_test: false,
668                                    insta: false,
669                                    snapbox: false,
670                                },
671                            },
672                        ),
673                    },
674                ]
675            "#]],
676        );
677    }
678
679    #[test]
680    fn method_annotations() {
681        check(
682            r#"
683struct Test;
684
685impl Test {
686    fn self_by_ref(&self) {}
687}
688
689fn main() {
690    Test.self_by_ref();
691}
692            "#,
693            expect![[r#"
694                [
695                    Annotation {
696                        range: 7..11,
697                        kind: HasImpls {
698                            pos: FilePositionWrapper {
699                                file_id: FileId(
700                                    0,
701                                ),
702                                offset: 7,
703                            },
704                            data: Some(
705                                [
706                                    NavigationTarget {
707                                        file_id: FileId(
708                                            0,
709                                        ),
710                                        full_range: 14..56,
711                                        focus_range: 19..23,
712                                        name: "impl",
713                                        kind: Impl,
714                                    },
715                                ],
716                            ),
717                        },
718                    },
719                    Annotation {
720                        range: 7..11,
721                        kind: HasReferences {
722                            pos: FilePositionWrapper {
723                                file_id: FileId(
724                                    0,
725                                ),
726                                offset: 7,
727                            },
728                            data: Some(
729                                [
730                                    FileRangeWrapper {
731                                        file_id: FileId(
732                                            0,
733                                        ),
734                                        range: 19..23,
735                                    },
736                                    FileRangeWrapper {
737                                        file_id: FileId(
738                                            0,
739                                        ),
740                                        range: 74..78,
741                                    },
742                                ],
743                            ),
744                        },
745                    },
746                    Annotation {
747                        range: 33..44,
748                        kind: HasReferences {
749                            pos: FilePositionWrapper {
750                                file_id: FileId(
751                                    0,
752                                ),
753                                offset: 33,
754                            },
755                            data: Some(
756                                [
757                                    FileRangeWrapper {
758                                        file_id: FileId(
759                                            0,
760                                        ),
761                                        range: 79..90,
762                                    },
763                                ],
764                            ),
765                        },
766                    },
767                    Annotation {
768                        range: 61..65,
769                        kind: HasReferences {
770                            pos: FilePositionWrapper {
771                                file_id: FileId(
772                                    0,
773                                ),
774                                offset: 61,
775                            },
776                            data: Some(
777                                [],
778                            ),
779                        },
780                    },
781                    Annotation {
782                        range: 61..65,
783                        kind: Runnable(
784                            Runnable {
785                                use_name_in_title: false,
786                                nav: NavigationTarget {
787                                    file_id: FileId(
788                                        0,
789                                    ),
790                                    full_range: 58..95,
791                                    focus_range: 61..65,
792                                    name: "main",
793                                    kind: Function,
794                                },
795                                kind: Bin,
796                                cfg: None,
797                                update_test: UpdateTest {
798                                    expect_test: false,
799                                    insta: false,
800                                    snapbox: false,
801                                },
802                            },
803                        ),
804                    },
805                ]
806            "#]],
807        );
808    }
809
810    #[test]
811    fn test_annotations() {
812        check(
813            r#"
814fn main() {}
815
816mod tests {
817    #[test]
818    fn my_cool_test() {}
819}
820            "#,
821            expect![[r#"
822                [
823                    Annotation {
824                        range: 3..7,
825                        kind: HasReferences {
826                            pos: FilePositionWrapper {
827                                file_id: FileId(
828                                    0,
829                                ),
830                                offset: 3,
831                            },
832                            data: Some(
833                                [],
834                            ),
835                        },
836                    },
837                    Annotation {
838                        range: 3..7,
839                        kind: Runnable(
840                            Runnable {
841                                use_name_in_title: false,
842                                nav: NavigationTarget {
843                                    file_id: FileId(
844                                        0,
845                                    ),
846                                    full_range: 0..12,
847                                    focus_range: 3..7,
848                                    name: "main",
849                                    kind: Function,
850                                },
851                                kind: Bin,
852                                cfg: None,
853                                update_test: UpdateTest {
854                                    expect_test: false,
855                                    insta: false,
856                                    snapbox: false,
857                                },
858                            },
859                        ),
860                    },
861                    Annotation {
862                        range: 18..23,
863                        kind: Runnable(
864                            Runnable {
865                                use_name_in_title: false,
866                                nav: NavigationTarget {
867                                    file_id: FileId(
868                                        0,
869                                    ),
870                                    full_range: 14..64,
871                                    focus_range: 18..23,
872                                    name: "tests",
873                                    kind: Module,
874                                    description: "mod tests",
875                                },
876                                kind: TestMod {
877                                    path: "tests",
878                                },
879                                cfg: None,
880                                update_test: UpdateTest {
881                                    expect_test: false,
882                                    insta: false,
883                                    snapbox: false,
884                                },
885                            },
886                        ),
887                    },
888                    Annotation {
889                        range: 45..57,
890                        kind: Runnable(
891                            Runnable {
892                                use_name_in_title: false,
893                                nav: NavigationTarget {
894                                    file_id: FileId(
895                                        0,
896                                    ),
897                                    full_range: 30..62,
898                                    focus_range: 45..57,
899                                    name: "my_cool_test",
900                                    kind: Function,
901                                },
902                                kind: Test {
903                                    test_id: Path(
904                                        "tests::my_cool_test",
905                                    ),
906                                },
907                                cfg: None,
908                                update_test: UpdateTest {
909                                    expect_test: false,
910                                    insta: false,
911                                    snapbox: false,
912                                },
913                            },
914                        ),
915                    },
916                ]
917            "#]],
918        );
919    }
920
921    #[test]
922    fn test_no_annotations_outside_module_tree() {
923        check(
924            r#"
925//- /foo.rs
926struct Foo;
927//- /lib.rs
928// this file comes last since `check` checks the first file only
929"#,
930            expect![[r#"
931                []
932            "#]],
933        );
934    }
935
936    #[test]
937    fn test_no_annotations_macro_struct_def() {
938        check(
939            r#"
940//- /lib.rs
941macro_rules! m {
942    () => {
943        struct A {}
944    };
945}
946
947m!();
948"#,
949            expect![[r#"
950                []
951            "#]],
952        );
953    }
954
955    #[test]
956    fn test_annotations_macro_struct_def_call_site() {
957        check(
958            r#"
959//- /lib.rs
960macro_rules! m {
961    ($name:ident) => {
962        struct $name {}
963    };
964}
965
966m! {
967    Name
968};
969"#,
970            expect![[r#"
971                [
972                    Annotation {
973                        range: 83..87,
974                        kind: HasImpls {
975                            pos: FilePositionWrapper {
976                                file_id: FileId(
977                                    0,
978                                ),
979                                offset: 83,
980                            },
981                            data: Some(
982                                [],
983                            ),
984                        },
985                    },
986                    Annotation {
987                        range: 83..87,
988                        kind: HasReferences {
989                            pos: FilePositionWrapper {
990                                file_id: FileId(
991                                    0,
992                                ),
993                                offset: 83,
994                            },
995                            data: Some(
996                                [],
997                            ),
998                        },
999                    },
1000                ]
1001            "#]],
1002        );
1003    }
1004
1005    #[test]
1006    fn test_annotations_appear_above_whole_item_when_configured_to_do_so() {
1007        check_with_config(
1008            r#"
1009/// This is a struct named Foo, obviously.
1010#[derive(Clone)]
1011struct Foo;
1012"#,
1013            expect![[r#"
1014                [
1015                    Annotation {
1016                        range: 0..71,
1017                        kind: HasImpls {
1018                            pos: FilePositionWrapper {
1019                                file_id: FileId(
1020                                    0,
1021                                ),
1022                                offset: 67,
1023                            },
1024                            data: Some(
1025                                [],
1026                            ),
1027                        },
1028                    },
1029                    Annotation {
1030                        range: 0..71,
1031                        kind: HasReferences {
1032                            pos: FilePositionWrapper {
1033                                file_id: FileId(
1034                                    0,
1035                                ),
1036                                offset: 67,
1037                            },
1038                            data: Some(
1039                                [],
1040                            ),
1041                        },
1042                    },
1043                ]
1044            "#]],
1045            &AnnotationConfig { location: AnnotationLocation::AboveWholeItem, ..DEFAULT_CONFIG },
1046        );
1047    }
1048}