]> git.lizzy.rs Git - rust.git/blob - crates/ide/src/runnables.rs
Cleanup runnables canonical path impl
[rust.git] / crates / ide / src / runnables.rs
1 use std::fmt;
2
3 use ast::NameOwner;
4 use cfg::CfgExpr;
5 use either::Either;
6 use hir::{AsAssocItem, HasAttrs, HasSource, HirDisplay, Semantics};
7 use ide_assists::utils::test_related_attribute;
8 use ide_db::{
9     base_db::{FilePosition, FileRange},
10     helpers::visit_file_defs,
11     search::SearchScope,
12     RootDatabase, SymbolKind,
13 };
14 use itertools::Itertools;
15 use rustc_hash::{FxHashMap, FxHashSet};
16 use stdx::{always, format_to};
17 use syntax::ast::{self, AstNode, AttrsOwner};
18
19 use crate::{
20     display::{ToNav, TryToNav},
21     references, FileId, NavigationTarget,
22 };
23
24 #[derive(Debug, Clone, Hash, PartialEq, Eq)]
25 pub struct Runnable {
26     pub use_name_in_title: bool,
27     pub nav: NavigationTarget,
28     pub kind: RunnableKind,
29     pub cfg: Option<CfgExpr>,
30 }
31
32 #[derive(Debug, Clone, Hash, PartialEq, Eq)]
33 pub enum TestId {
34     Name(String),
35     Path(String),
36 }
37
38 impl fmt::Display for TestId {
39     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
40         match self {
41             TestId::Name(name) => write!(f, "{}", name),
42             TestId::Path(path) => write!(f, "{}", path),
43         }
44     }
45 }
46
47 #[derive(Debug, Clone, Hash, PartialEq, Eq)]
48 pub enum RunnableKind {
49     Test { test_id: TestId, attr: TestAttr },
50     TestMod { path: String },
51     Bench { test_id: TestId },
52     DocTest { test_id: TestId },
53     Bin,
54 }
55
56 #[cfg(test)]
57 #[derive(Debug, Clone, Hash, PartialEq, Eq)]
58 enum RunnableTestKind {
59     Test,
60     TestMod,
61     DocTest,
62     Bench,
63     Bin,
64 }
65
66 impl Runnable {
67     // test package::module::testname
68     pub fn label(&self, target: Option<String>) -> String {
69         match &self.kind {
70             RunnableKind::Test { test_id, .. } => format!("test {}", test_id),
71             RunnableKind::TestMod { path } => format!("test-mod {}", path),
72             RunnableKind::Bench { test_id } => format!("bench {}", test_id),
73             RunnableKind::DocTest { test_id, .. } => format!("doctest {}", test_id),
74             RunnableKind::Bin => {
75                 target.map_or_else(|| "run binary".to_string(), |t| format!("run {}", t))
76             }
77         }
78     }
79
80     pub fn title(&self) -> String {
81         let mut s = String::from("▶\u{fe0e} Run ");
82         if self.use_name_in_title {
83             format_to!(s, "{}", self.nav.name);
84             if !matches!(self.kind, RunnableKind::Bin) {
85                 s.push(' ');
86             }
87         }
88         let suffix = match &self.kind {
89             RunnableKind::TestMod { .. } => "Tests",
90             RunnableKind::Test { .. } => "Test",
91             RunnableKind::DocTest { .. } => "Doctest",
92             RunnableKind::Bench { .. } => "Bench",
93             RunnableKind::Bin => return s,
94         };
95         s.push_str(suffix);
96         s
97     }
98
99     #[cfg(test)]
100     fn test_kind(&self) -> RunnableTestKind {
101         match &self.kind {
102             RunnableKind::TestMod { .. } => RunnableTestKind::TestMod,
103             RunnableKind::Test { .. } => RunnableTestKind::Test,
104             RunnableKind::DocTest { .. } => RunnableTestKind::DocTest,
105             RunnableKind::Bench { .. } => RunnableTestKind::Bench,
106             RunnableKind::Bin => RunnableTestKind::Bin,
107         }
108     }
109 }
110
111 // Feature: Run
112 //
113 // Shows a popup suggesting to run a test/benchmark/binary **at the current cursor
114 // location**. Super useful for repeatedly running just a single test. Do bind this
115 // to a shortcut!
116 //
117 // |===
118 // | Editor  | Action Name
119 //
120 // | VS Code | **Rust Analyzer: Run**
121 // |===
122 // image::https://user-images.githubusercontent.com/48062697/113065583-055aae80-91b1-11eb-958f-d67efcaf6a2f.gif[]
123 pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> {
124     let sema = Semantics::new(db);
125
126     let mut res = Vec::new();
127     // Record all runnables that come from macro expansions here instead.
128     // In case an expansion creates multiple runnables we want to name them to avoid emitting a bunch of equally named runnables.
129     let mut in_macro_expansion = FxHashMap::<hir::HirFileId, Vec<Runnable>>::default();
130     let mut add_opt = |runnable: Option<Runnable>, def| {
131         if let Some(runnable) = runnable.filter(|runnable| {
132             always!(
133                 runnable.nav.file_id == file_id,
134                 "tried adding a runnable pointing to a different file: {:?} for {:?}",
135                 runnable.kind,
136                 file_id
137             )
138         }) {
139             if let Some(def) = def {
140                 let file_id = match def {
141                     hir::ModuleDef::Module(it) => it.declaration_source(db).map(|src| src.file_id),
142                     hir::ModuleDef::Function(it) => it.source(db).map(|src| src.file_id),
143                     _ => None,
144                 };
145                 if let Some(file_id) = file_id.filter(|file| file.call_node(db).is_some()) {
146                     in_macro_expansion.entry(file_id).or_default().push(runnable);
147                     return;
148                 }
149             }
150             res.push(runnable);
151         }
152     };
153     visit_file_defs(&sema, file_id, &mut |def| match def {
154         Either::Left(def) => {
155             let runnable = match def {
156                 hir::ModuleDef::Module(it) => runnable_mod(&sema, it),
157                 hir::ModuleDef::Function(it) => runnable_fn(&sema, it),
158                 _ => None,
159             };
160             add_opt(runnable.or_else(|| module_def_doctest(sema.db, def)), Some(def));
161         }
162         Either::Right(impl_) => {
163             add_opt(runnable_impl(&sema, &impl_), None);
164             impl_
165                 .items(db)
166                 .into_iter()
167                 .map(|assoc| {
168                     (
169                         match assoc {
170                             hir::AssocItem::Function(it) => runnable_fn(&sema, it)
171                                 .or_else(|| module_def_doctest(sema.db, it.into())),
172                             hir::AssocItem::Const(it) => module_def_doctest(sema.db, it.into()),
173                             hir::AssocItem::TypeAlias(it) => module_def_doctest(sema.db, it.into()),
174                         },
175                         assoc,
176                     )
177                 })
178                 .for_each(|(r, assoc)| add_opt(r, Some(assoc.into())));
179         }
180     });
181
182     sema.to_module_defs(file_id)
183         .map(|it| runnable_mod_outline_definition(&sema, it))
184         .for_each(|it| add_opt(it, None));
185
186     res.extend(in_macro_expansion.into_iter().flat_map(|(_, runnables)| {
187         let use_name_in_title = runnables.len() != 1;
188         runnables.into_iter().map(move |mut r| {
189             r.use_name_in_title = use_name_in_title;
190             r
191         })
192     }));
193     res
194 }
195
196 // Feature: Related Tests
197 //
198 // Provides a sneak peek of all tests where the current item is used.
199 //
200 // The simplest way to use this feature is via the context menu:
201 //  - Right-click on the selected item. The context menu opens.
202 //  - Select **Peek related tests**
203 //
204 // |===
205 // | Editor  | Action Name
206 //
207 // | VS Code | **Rust Analyzer: Peek related tests**
208 // |===
209 pub(crate) fn related_tests(
210     db: &RootDatabase,
211     position: FilePosition,
212     search_scope: Option<SearchScope>,
213 ) -> Vec<Runnable> {
214     let sema = Semantics::new(db);
215     let mut res: FxHashSet<Runnable> = FxHashSet::default();
216
217     find_related_tests(&sema, position, search_scope, &mut res);
218
219     res.into_iter().collect_vec()
220 }
221
222 fn find_related_tests(
223     sema: &Semantics<RootDatabase>,
224     position: FilePosition,
225     search_scope: Option<SearchScope>,
226     tests: &mut FxHashSet<Runnable>,
227 ) {
228     if let Some(refs) = references::find_all_refs(sema, position, search_scope) {
229         for (file_id, refs) in refs.references {
230             let file = sema.parse(file_id);
231             let file = file.syntax();
232             let functions = refs.iter().filter_map(|(range, _)| {
233                 let token = file.token_at_offset(range.start()).next()?;
234                 let token = sema.descend_into_macros(token);
235                 token.ancestors().find_map(ast::Fn::cast)
236             });
237
238             for fn_def in functions {
239                 if let Some(runnable) = as_test_runnable(sema, &fn_def) {
240                     // direct test
241                     tests.insert(runnable);
242                 } else if let Some(module) = parent_test_module(sema, &fn_def) {
243                     // indirect test
244                     find_related_tests_in_module(sema, &fn_def, &module, tests);
245                 }
246             }
247         }
248     }
249 }
250
251 fn find_related_tests_in_module(
252     sema: &Semantics<RootDatabase>,
253     fn_def: &ast::Fn,
254     parent_module: &hir::Module,
255     tests: &mut FxHashSet<Runnable>,
256 ) {
257     if let Some(fn_name) = fn_def.name() {
258         let mod_source = parent_module.definition_source(sema.db);
259         let range = match mod_source.value {
260             hir::ModuleSource::Module(m) => m.syntax().text_range(),
261             hir::ModuleSource::BlockExpr(b) => b.syntax().text_range(),
262             hir::ModuleSource::SourceFile(f) => f.syntax().text_range(),
263         };
264
265         let file_id = mod_source.file_id.original_file(sema.db);
266         let mod_scope = SearchScope::file_range(FileRange { file_id, range });
267         let fn_pos = FilePosition { file_id, offset: fn_name.syntax().text_range().start() };
268         find_related_tests(sema, fn_pos, Some(mod_scope), tests)
269     }
270 }
271
272 fn as_test_runnable(sema: &Semantics<RootDatabase>, fn_def: &ast::Fn) -> Option<Runnable> {
273     if test_related_attribute(fn_def).is_some() {
274         let function = sema.to_def(fn_def)?;
275         runnable_fn(sema, function)
276     } else {
277         None
278     }
279 }
280
281 fn parent_test_module(sema: &Semantics<RootDatabase>, fn_def: &ast::Fn) -> Option<hir::Module> {
282     fn_def.syntax().ancestors().find_map(|node| {
283         let module = ast::Module::cast(node)?;
284         let module = sema.to_def(&module)?;
285
286         if has_test_function_or_multiple_test_submodules(sema, &module) {
287             Some(module)
288         } else {
289             None
290         }
291     })
292 }
293
294 pub(crate) fn runnable_fn(sema: &Semantics<RootDatabase>, def: hir::Function) -> Option<Runnable> {
295     let func = def.source(sema.db)?;
296     let name_string = def.name(sema.db).to_string();
297
298     let root = def.module(sema.db).krate().root_module(sema.db);
299
300     let kind = if name_string == "main" && def.module(sema.db) == root {
301         RunnableKind::Bin
302     } else {
303         let canonical_path = {
304             let def: hir::ModuleDef = def.into();
305             def.canonical_path(sema.db)
306         };
307         let test_id = canonical_path.map(TestId::Path).unwrap_or(TestId::Name(name_string));
308
309         if test_related_attribute(&func.value).is_some() {
310             let attr = TestAttr::from_fn(&func.value);
311             RunnableKind::Test { test_id, attr }
312         } else if func.value.has_atom_attr("bench") {
313             RunnableKind::Bench { test_id }
314         } else {
315             return None;
316         }
317     };
318
319     let nav = NavigationTarget::from_named(
320         sema.db,
321         func.as_ref().map(|it| it as &dyn ast::NameOwner),
322         SymbolKind::Function,
323     );
324     let cfg = def.attrs(sema.db).cfg();
325     Some(Runnable { use_name_in_title: false, nav, kind, cfg })
326 }
327
328 pub(crate) fn runnable_mod(sema: &Semantics<RootDatabase>, def: hir::Module) -> Option<Runnable> {
329     if !has_test_function_or_multiple_test_submodules(sema, &def) {
330         return None;
331     }
332     let path =
333         def.path_to_root(sema.db).into_iter().rev().filter_map(|it| it.name(sema.db)).join("::");
334
335     let attrs = def.attrs(sema.db);
336     let cfg = attrs.cfg();
337     let nav = NavigationTarget::from_module_to_decl(sema.db, def);
338     Some(Runnable { use_name_in_title: false, nav, kind: RunnableKind::TestMod { path }, cfg })
339 }
340
341 pub(crate) fn runnable_impl(sema: &Semantics<RootDatabase>, def: &hir::Impl) -> Option<Runnable> {
342     let attrs = def.attrs(sema.db);
343     if !has_runnable_doc_test(&attrs) {
344         return None;
345     }
346     let cfg = attrs.cfg();
347     let nav = def.try_to_nav(sema.db)?;
348     let ty = def.self_ty(sema.db);
349     let adt_name = ty.as_adt()?.name(sema.db);
350     let mut ty_args = ty.type_arguments().peekable();
351     let params = if ty_args.peek().is_some() {
352         format!("<{}>", ty_args.format_with(", ", |ty, cb| cb(&ty.display(sema.db))))
353     } else {
354         String::new()
355     };
356     let test_id = TestId::Path(format!("{}{}", adt_name, params));
357
358     Some(Runnable { use_name_in_title: false, nav, kind: RunnableKind::DocTest { test_id }, cfg })
359 }
360
361 /// Creates a test mod runnable for outline modules at the top of their definition.
362 fn runnable_mod_outline_definition(
363     sema: &Semantics<RootDatabase>,
364     def: hir::Module,
365 ) -> Option<Runnable> {
366     if !has_test_function_or_multiple_test_submodules(sema, &def) {
367         return None;
368     }
369     let path =
370         def.path_to_root(sema.db).into_iter().rev().filter_map(|it| it.name(sema.db)).join("::");
371
372     let attrs = def.attrs(sema.db);
373     let cfg = attrs.cfg();
374     match def.definition_source(sema.db).value {
375         hir::ModuleSource::SourceFile(_) => Some(Runnable {
376             use_name_in_title: false,
377             nav: def.to_nav(sema.db),
378             kind: RunnableKind::TestMod { path },
379             cfg,
380         }),
381         _ => None,
382     }
383 }
384
385 fn module_def_doctest(db: &RootDatabase, def: hir::ModuleDef) -> Option<Runnable> {
386     let attrs = match def {
387         hir::ModuleDef::Module(it) => it.attrs(db),
388         hir::ModuleDef::Function(it) => it.attrs(db),
389         hir::ModuleDef::Adt(it) => it.attrs(db),
390         hir::ModuleDef::Variant(it) => it.attrs(db),
391         hir::ModuleDef::Const(it) => it.attrs(db),
392         hir::ModuleDef::Static(it) => it.attrs(db),
393         hir::ModuleDef::Trait(it) => it.attrs(db),
394         hir::ModuleDef::TypeAlias(it) => it.attrs(db),
395         hir::ModuleDef::BuiltinType(_) => return None,
396     };
397     if !has_runnable_doc_test(&attrs) {
398         return None;
399     }
400     let def_name = def.name(db)?;
401     let path = (|| {
402         let mut path = String::new();
403         def.module(db)?
404             .path_to_root(db)
405             .into_iter()
406             .rev()
407             .flat_map(|it| it.name(db))
408             .for_each(|name| format_to!(path, "{}::", name));
409         // This probably belongs to canonical_path?
410         if let Some(assoc_item) = def.as_assoc_item(db) {
411             if let hir::AssocItemContainer::Impl(imp) = assoc_item.container(db) {
412                 let ty = imp.self_ty(db);
413                 if let Some(adt) = ty.as_adt() {
414                     let name = adt.name(db);
415                     let mut ty_args = ty.type_arguments().peekable();
416                     format_to!(path, "{}", name);
417                     if ty_args.peek().is_some() {
418                         format_to!(
419                             path,
420                             "<{}>",
421                             ty_args.format_with(", ", |ty, cb| cb(&ty.display(db)))
422                         );
423                     }
424                     format_to!(path, "::{}", def_name);
425                     return Some(path);
426                 }
427             }
428         }
429         format_to!(path, "{}", def_name);
430         Some(path)
431     })();
432
433     let test_id = path.map_or_else(|| TestId::Name(def_name.to_string()), TestId::Path);
434
435     let mut nav = match def {
436         hir::ModuleDef::Module(def) => NavigationTarget::from_module_to_decl(db, def),
437         def => def.try_to_nav(db)?,
438     };
439     nav.focus_range = None;
440     nav.description = None;
441     nav.docs = None;
442     nav.kind = None;
443     let res = Runnable {
444         use_name_in_title: false,
445         nav,
446         kind: RunnableKind::DocTest { test_id },
447         cfg: attrs.cfg(),
448     };
449     Some(res)
450 }
451
452 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
453 pub struct TestAttr {
454     pub ignore: bool,
455 }
456
457 impl TestAttr {
458     fn from_fn(fn_def: &ast::Fn) -> TestAttr {
459         let ignore = fn_def
460             .attrs()
461             .filter_map(|attr| attr.simple_name())
462             .any(|attribute_text| attribute_text == "ignore");
463         TestAttr { ignore }
464     }
465 }
466
467 const RUSTDOC_FENCE: &str = "```";
468 const RUSTDOC_CODE_BLOCK_ATTRIBUTES_RUNNABLE: &[&str] =
469     &["", "rust", "should_panic", "edition2015", "edition2018", "edition2021"];
470
471 fn has_runnable_doc_test(attrs: &hir::Attrs) -> bool {
472     attrs.docs().map_or(false, |doc| {
473         let mut in_code_block = false;
474
475         for line in String::from(doc).lines() {
476             if let Some(header) = line.strip_prefix(RUSTDOC_FENCE) {
477                 in_code_block = !in_code_block;
478
479                 if in_code_block
480                     && header
481                         .split(',')
482                         .all(|sub| RUSTDOC_CODE_BLOCK_ATTRIBUTES_RUNNABLE.contains(&sub.trim()))
483                 {
484                     return true;
485                 }
486             }
487         }
488
489         false
490     })
491 }
492
493 // We could create runnables for modules with number_of_test_submodules > 0,
494 // but that bloats the runnables for no real benefit, since all tests can be run by the submodule already
495 fn has_test_function_or_multiple_test_submodules(
496     sema: &Semantics<RootDatabase>,
497     module: &hir::Module,
498 ) -> bool {
499     let mut number_of_test_submodules = 0;
500
501     for item in module.declarations(sema.db) {
502         match item {
503             hir::ModuleDef::Function(f) => {
504                 if let Some(it) = f.source(sema.db) {
505                     if test_related_attribute(&it.value).is_some() {
506                         return true;
507                     }
508                 }
509             }
510             hir::ModuleDef::Module(submodule) => {
511                 if has_test_function_or_multiple_test_submodules(sema, &submodule) {
512                     number_of_test_submodules += 1;
513                 }
514             }
515             _ => (),
516         }
517     }
518
519     number_of_test_submodules > 1
520 }
521
522 #[cfg(test)]
523 mod tests {
524     use expect_test::{expect, Expect};
525
526     use crate::fixture;
527
528     use super::{RunnableTestKind::*, *};
529
530     fn check(
531         ra_fixture: &str,
532         // FIXME: fold this into `expect` as well
533         actions: &[RunnableTestKind],
534         expect: Expect,
535     ) {
536         let (analysis, position) = fixture::position(ra_fixture);
537         let runnables = analysis.runnables(position.file_id).unwrap();
538         expect.assert_debug_eq(&runnables);
539         assert_eq!(
540             actions,
541             runnables.into_iter().map(|it| it.test_kind()).collect::<Vec<_>>().as_slice()
542         );
543     }
544
545     fn check_tests(ra_fixture: &str, expect: Expect) {
546         let (analysis, position) = fixture::position(ra_fixture);
547         let tests = analysis.related_tests(position, None).unwrap();
548         expect.assert_debug_eq(&tests);
549     }
550
551     #[test]
552     fn test_runnables() {
553         check(
554             r#"
555 //- /lib.rs
556 $0
557 fn main() {}
558
559 #[test]
560 fn test_foo() {}
561
562 #[test]
563 #[ignore]
564 fn test_foo() {}
565
566 #[bench]
567 fn bench() {}
568
569 mod not_a_root {
570     fn main() {}
571 }
572 "#,
573             &[Bin, Test, Test, Bench, TestMod],
574             expect![[r#"
575                 [
576                     Runnable {
577                         use_name_in_title: false,
578                         nav: NavigationTarget {
579                             file_id: FileId(
580                                 0,
581                             ),
582                             full_range: 1..13,
583                             focus_range: 4..8,
584                             name: "main",
585                             kind: Function,
586                         },
587                         kind: Bin,
588                         cfg: None,
589                     },
590                     Runnable {
591                         use_name_in_title: false,
592                         nav: NavigationTarget {
593                             file_id: FileId(
594                                 0,
595                             ),
596                             full_range: 15..39,
597                             focus_range: 26..34,
598                             name: "test_foo",
599                             kind: Function,
600                         },
601                         kind: Test {
602                             test_id: Path(
603                                 "test_foo",
604                             ),
605                             attr: TestAttr {
606                                 ignore: false,
607                             },
608                         },
609                         cfg: None,
610                     },
611                     Runnable {
612                         use_name_in_title: false,
613                         nav: NavigationTarget {
614                             file_id: FileId(
615                                 0,
616                             ),
617                             full_range: 41..75,
618                             focus_range: 62..70,
619                             name: "test_foo",
620                             kind: Function,
621                         },
622                         kind: Test {
623                             test_id: Path(
624                                 "test_foo",
625                             ),
626                             attr: TestAttr {
627                                 ignore: true,
628                             },
629                         },
630                         cfg: None,
631                     },
632                     Runnable {
633                         use_name_in_title: false,
634                         nav: NavigationTarget {
635                             file_id: FileId(
636                                 0,
637                             ),
638                             full_range: 77..99,
639                             focus_range: 89..94,
640                             name: "bench",
641                             kind: Function,
642                         },
643                         kind: Bench {
644                             test_id: Path(
645                                 "bench",
646                             ),
647                         },
648                         cfg: None,
649                     },
650                     Runnable {
651                         use_name_in_title: false,
652                         nav: NavigationTarget {
653                             file_id: FileId(
654                                 0,
655                             ),
656                             full_range: 0..137,
657                             name: "",
658                             kind: Module,
659                         },
660                         kind: TestMod {
661                             path: "",
662                         },
663                         cfg: None,
664                     },
665                 ]
666             "#]],
667         );
668     }
669
670     #[test]
671     fn test_runnables_doc_test() {
672         check(
673             r#"
674 //- /lib.rs
675 $0
676 fn main() {}
677
678 /// ```
679 /// let x = 5;
680 /// ```
681 fn should_have_runnable() {}
682
683 /// ```edition2018
684 /// let x = 5;
685 /// ```
686 fn should_have_runnable_1() {}
687
688 /// ```
689 /// let z = 55;
690 /// ```
691 ///
692 /// ```ignore
693 /// let z = 56;
694 /// ```
695 fn should_have_runnable_2() {}
696
697 /**
698 ```rust
699 let z = 55;
700 ```
701 */
702 fn should_have_no_runnable_3() {}
703
704 /**
705     ```rust
706     let z = 55;
707     ```
708 */
709 fn should_have_no_runnable_4() {}
710
711 /// ```no_run
712 /// let z = 55;
713 /// ```
714 fn should_have_no_runnable() {}
715
716 /// ```ignore
717 /// let z = 55;
718 /// ```
719 fn should_have_no_runnable_2() {}
720
721 /// ```compile_fail
722 /// let z = 55;
723 /// ```
724 fn should_have_no_runnable_3() {}
725
726 /// ```text
727 /// arbitrary plain text
728 /// ```
729 fn should_have_no_runnable_4() {}
730
731 /// ```text
732 /// arbitrary plain text
733 /// ```
734 ///
735 /// ```sh
736 /// $ shell code
737 /// ```
738 fn should_have_no_runnable_5() {}
739
740 /// ```rust,no_run
741 /// let z = 55;
742 /// ```
743 fn should_have_no_runnable_6() {}
744
745 /// ```
746 /// let x = 5;
747 /// ```
748 struct StructWithRunnable(String);
749
750 /// ```
751 /// let x = 5;
752 /// ```
753 impl StructWithRunnable {}
754
755 trait Test {
756     fn test() -> usize {
757         5usize
758     }
759 }
760
761 /// ```
762 /// let x = 5;
763 /// ```
764 impl Test for StructWithRunnable {}
765 "#,
766             &[Bin, DocTest, DocTest, DocTest, DocTest, DocTest, DocTest, DocTest, DocTest],
767             expect![[r#"
768                 [
769                     Runnable {
770                         use_name_in_title: false,
771                         nav: NavigationTarget {
772                             file_id: FileId(
773                                 0,
774                             ),
775                             full_range: 1..13,
776                             focus_range: 4..8,
777                             name: "main",
778                             kind: Function,
779                         },
780                         kind: Bin,
781                         cfg: None,
782                     },
783                     Runnable {
784                         use_name_in_title: false,
785                         nav: NavigationTarget {
786                             file_id: FileId(
787                                 0,
788                             ),
789                             full_range: 15..74,
790                             name: "should_have_runnable",
791                         },
792                         kind: DocTest {
793                             test_id: Path(
794                                 "should_have_runnable",
795                             ),
796                         },
797                         cfg: None,
798                     },
799                     Runnable {
800                         use_name_in_title: false,
801                         nav: NavigationTarget {
802                             file_id: FileId(
803                                 0,
804                             ),
805                             full_range: 76..148,
806                             name: "should_have_runnable_1",
807                         },
808                         kind: DocTest {
809                             test_id: Path(
810                                 "should_have_runnable_1",
811                             ),
812                         },
813                         cfg: None,
814                     },
815                     Runnable {
816                         use_name_in_title: false,
817                         nav: NavigationTarget {
818                             file_id: FileId(
819                                 0,
820                             ),
821                             full_range: 150..254,
822                             name: "should_have_runnable_2",
823                         },
824                         kind: DocTest {
825                             test_id: Path(
826                                 "should_have_runnable_2",
827                             ),
828                         },
829                         cfg: None,
830                     },
831                     Runnable {
832                         use_name_in_title: false,
833                         nav: NavigationTarget {
834                             file_id: FileId(
835                                 0,
836                             ),
837                             full_range: 256..320,
838                             name: "should_have_no_runnable_3",
839                         },
840                         kind: DocTest {
841                             test_id: Path(
842                                 "should_have_no_runnable_3",
843                             ),
844                         },
845                         cfg: None,
846                     },
847                     Runnable {
848                         use_name_in_title: false,
849                         nav: NavigationTarget {
850                             file_id: FileId(
851                                 0,
852                             ),
853                             full_range: 322..398,
854                             name: "should_have_no_runnable_4",
855                         },
856                         kind: DocTest {
857                             test_id: Path(
858                                 "should_have_no_runnable_4",
859                             ),
860                         },
861                         cfg: None,
862                     },
863                     Runnable {
864                         use_name_in_title: false,
865                         nav: NavigationTarget {
866                             file_id: FileId(
867                                 0,
868                             ),
869                             full_range: 900..965,
870                             name: "StructWithRunnable",
871                         },
872                         kind: DocTest {
873                             test_id: Path(
874                                 "StructWithRunnable",
875                             ),
876                         },
877                         cfg: None,
878                     },
879                     Runnable {
880                         use_name_in_title: false,
881                         nav: NavigationTarget {
882                             file_id: FileId(
883                                 0,
884                             ),
885                             full_range: 967..1024,
886                             focus_range: 1003..1021,
887                             name: "impl",
888                             kind: Impl,
889                         },
890                         kind: DocTest {
891                             test_id: Path(
892                                 "StructWithRunnable",
893                             ),
894                         },
895                         cfg: None,
896                     },
897                     Runnable {
898                         use_name_in_title: false,
899                         nav: NavigationTarget {
900                             file_id: FileId(
901                                 0,
902                             ),
903                             full_range: 1088..1154,
904                             focus_range: 1133..1151,
905                             name: "impl",
906                             kind: Impl,
907                         },
908                         kind: DocTest {
909                             test_id: Path(
910                                 "StructWithRunnable",
911                             ),
912                         },
913                         cfg: None,
914                     },
915                 ]
916             "#]],
917         );
918     }
919
920     #[test]
921     fn test_runnables_doc_test_in_impl() {
922         check(
923             r#"
924 //- /lib.rs
925 $0
926 fn main() {}
927
928 struct Data;
929 impl Data {
930     /// ```
931     /// let x = 5;
932     /// ```
933     fn foo() {}
934 }
935 "#,
936             &[Bin, DocTest],
937             expect![[r#"
938                 [
939                     Runnable {
940                         use_name_in_title: false,
941                         nav: NavigationTarget {
942                             file_id: FileId(
943                                 0,
944                             ),
945                             full_range: 1..13,
946                             focus_range: 4..8,
947                             name: "main",
948                             kind: Function,
949                         },
950                         kind: Bin,
951                         cfg: None,
952                     },
953                     Runnable {
954                         use_name_in_title: false,
955                         nav: NavigationTarget {
956                             file_id: FileId(
957                                 0,
958                             ),
959                             full_range: 44..98,
960                             name: "foo",
961                         },
962                         kind: DocTest {
963                             test_id: Path(
964                                 "Data::foo",
965                             ),
966                         },
967                         cfg: None,
968                     },
969                 ]
970             "#]],
971         );
972     }
973
974     #[test]
975     fn test_runnables_module() {
976         check(
977             r#"
978 //- /lib.rs
979 $0
980 mod test_mod {
981     #[test]
982     fn test_foo1() {}
983 }
984 "#,
985             &[TestMod, Test],
986             expect![[r#"
987                 [
988                     Runnable {
989                         use_name_in_title: false,
990                         nav: NavigationTarget {
991                             file_id: FileId(
992                                 0,
993                             ),
994                             full_range: 1..51,
995                             focus_range: 5..13,
996                             name: "test_mod",
997                             kind: Module,
998                             description: "mod test_mod",
999                         },
1000                         kind: TestMod {
1001                             path: "test_mod",
1002                         },
1003                         cfg: None,
1004                     },
1005                     Runnable {
1006                         use_name_in_title: false,
1007                         nav: NavigationTarget {
1008                             file_id: FileId(
1009                                 0,
1010                             ),
1011                             full_range: 20..49,
1012                             focus_range: 35..44,
1013                             name: "test_foo1",
1014                             kind: Function,
1015                         },
1016                         kind: Test {
1017                             test_id: Path(
1018                                 "test_mod::test_foo1",
1019                             ),
1020                             attr: TestAttr {
1021                                 ignore: false,
1022                             },
1023                         },
1024                         cfg: None,
1025                     },
1026                 ]
1027             "#]],
1028         );
1029     }
1030
1031     #[test]
1032     fn only_modules_with_test_functions_or_more_than_one_test_submodule_have_runners() {
1033         check(
1034             r#"
1035 //- /lib.rs
1036 $0
1037 mod root_tests {
1038     mod nested_tests_0 {
1039         mod nested_tests_1 {
1040             #[test]
1041             fn nested_test_11() {}
1042
1043             #[test]
1044             fn nested_test_12() {}
1045         }
1046
1047         mod nested_tests_2 {
1048             #[test]
1049             fn nested_test_2() {}
1050         }
1051
1052         mod nested_tests_3 {}
1053     }
1054
1055     mod nested_tests_4 {}
1056 }
1057 "#,
1058             &[TestMod, TestMod, TestMod, Test, Test, Test],
1059             expect![[r#"
1060                 [
1061                     Runnable {
1062                         use_name_in_title: false,
1063                         nav: NavigationTarget {
1064                             file_id: FileId(
1065                                 0,
1066                             ),
1067                             full_range: 22..323,
1068                             focus_range: 26..40,
1069                             name: "nested_tests_0",
1070                             kind: Module,
1071                             description: "mod nested_tests_0",
1072                         },
1073                         kind: TestMod {
1074                             path: "root_tests::nested_tests_0",
1075                         },
1076                         cfg: None,
1077                     },
1078                     Runnable {
1079                         use_name_in_title: false,
1080                         nav: NavigationTarget {
1081                             file_id: FileId(
1082                                 0,
1083                             ),
1084                             full_range: 51..192,
1085                             focus_range: 55..69,
1086                             name: "nested_tests_1",
1087                             kind: Module,
1088                             description: "mod nested_tests_1",
1089                         },
1090                         kind: TestMod {
1091                             path: "root_tests::nested_tests_0::nested_tests_1",
1092                         },
1093                         cfg: None,
1094                     },
1095                     Runnable {
1096                         use_name_in_title: false,
1097                         nav: NavigationTarget {
1098                             file_id: FileId(
1099                                 0,
1100                             ),
1101                             full_range: 202..286,
1102                             focus_range: 206..220,
1103                             name: "nested_tests_2",
1104                             kind: Module,
1105                             description: "mod nested_tests_2",
1106                         },
1107                         kind: TestMod {
1108                             path: "root_tests::nested_tests_0::nested_tests_2",
1109                         },
1110                         cfg: None,
1111                     },
1112                     Runnable {
1113                         use_name_in_title: false,
1114                         nav: NavigationTarget {
1115                             file_id: FileId(
1116                                 0,
1117                             ),
1118                             full_range: 84..126,
1119                             focus_range: 107..121,
1120                             name: "nested_test_11",
1121                             kind: Function,
1122                         },
1123                         kind: Test {
1124                             test_id: Path(
1125                                 "root_tests::nested_tests_0::nested_tests_1::nested_test_11",
1126                             ),
1127                             attr: TestAttr {
1128                                 ignore: false,
1129                             },
1130                         },
1131                         cfg: None,
1132                     },
1133                     Runnable {
1134                         use_name_in_title: false,
1135                         nav: NavigationTarget {
1136                             file_id: FileId(
1137                                 0,
1138                             ),
1139                             full_range: 140..182,
1140                             focus_range: 163..177,
1141                             name: "nested_test_12",
1142                             kind: Function,
1143                         },
1144                         kind: Test {
1145                             test_id: Path(
1146                                 "root_tests::nested_tests_0::nested_tests_1::nested_test_12",
1147                             ),
1148                             attr: TestAttr {
1149                                 ignore: false,
1150                             },
1151                         },
1152                         cfg: None,
1153                     },
1154                     Runnable {
1155                         use_name_in_title: false,
1156                         nav: NavigationTarget {
1157                             file_id: FileId(
1158                                 0,
1159                             ),
1160                             full_range: 235..276,
1161                             focus_range: 258..271,
1162                             name: "nested_test_2",
1163                             kind: Function,
1164                         },
1165                         kind: Test {
1166                             test_id: Path(
1167                                 "root_tests::nested_tests_0::nested_tests_2::nested_test_2",
1168                             ),
1169                             attr: TestAttr {
1170                                 ignore: false,
1171                             },
1172                         },
1173                         cfg: None,
1174                     },
1175                 ]
1176             "#]],
1177         );
1178     }
1179
1180     #[test]
1181     fn test_runnables_with_feature() {
1182         check(
1183             r#"
1184 //- /lib.rs crate:foo cfg:feature=foo
1185 $0
1186 #[test]
1187 #[cfg(feature = "foo")]
1188 fn test_foo1() {}
1189 "#,
1190             &[Test, TestMod],
1191             expect![[r#"
1192                 [
1193                     Runnable {
1194                         use_name_in_title: false,
1195                         nav: NavigationTarget {
1196                             file_id: FileId(
1197                                 0,
1198                             ),
1199                             full_range: 1..50,
1200                             focus_range: 36..45,
1201                             name: "test_foo1",
1202                             kind: Function,
1203                         },
1204                         kind: Test {
1205                             test_id: Path(
1206                                 "test_foo1",
1207                             ),
1208                             attr: TestAttr {
1209                                 ignore: false,
1210                             },
1211                         },
1212                         cfg: Some(
1213                             Atom(
1214                                 KeyValue {
1215                                     key: "feature",
1216                                     value: "foo",
1217                                 },
1218                             ),
1219                         ),
1220                     },
1221                     Runnable {
1222                         use_name_in_title: false,
1223                         nav: NavigationTarget {
1224                             file_id: FileId(
1225                                 0,
1226                             ),
1227                             full_range: 0..51,
1228                             name: "",
1229                             kind: Module,
1230                         },
1231                         kind: TestMod {
1232                             path: "",
1233                         },
1234                         cfg: None,
1235                     },
1236                 ]
1237             "#]],
1238         );
1239     }
1240
1241     #[test]
1242     fn test_runnables_with_features() {
1243         check(
1244             r#"
1245 //- /lib.rs crate:foo cfg:feature=foo,feature=bar
1246 $0
1247 #[test]
1248 #[cfg(all(feature = "foo", feature = "bar"))]
1249 fn test_foo1() {}
1250 "#,
1251             &[Test, TestMod],
1252             expect![[r#"
1253                 [
1254                     Runnable {
1255                         use_name_in_title: false,
1256                         nav: NavigationTarget {
1257                             file_id: FileId(
1258                                 0,
1259                             ),
1260                             full_range: 1..72,
1261                             focus_range: 58..67,
1262                             name: "test_foo1",
1263                             kind: Function,
1264                         },
1265                         kind: Test {
1266                             test_id: Path(
1267                                 "test_foo1",
1268                             ),
1269                             attr: TestAttr {
1270                                 ignore: false,
1271                             },
1272                         },
1273                         cfg: Some(
1274                             All(
1275                                 [
1276                                     Atom(
1277                                         KeyValue {
1278                                             key: "feature",
1279                                             value: "foo",
1280                                         },
1281                                     ),
1282                                     Atom(
1283                                         KeyValue {
1284                                             key: "feature",
1285                                             value: "bar",
1286                                         },
1287                                     ),
1288                                 ],
1289                             ),
1290                         ),
1291                     },
1292                     Runnable {
1293                         use_name_in_title: false,
1294                         nav: NavigationTarget {
1295                             file_id: FileId(
1296                                 0,
1297                             ),
1298                             full_range: 0..73,
1299                             name: "",
1300                             kind: Module,
1301                         },
1302                         kind: TestMod {
1303                             path: "",
1304                         },
1305                         cfg: None,
1306                     },
1307                 ]
1308             "#]],
1309         );
1310     }
1311
1312     #[test]
1313     fn test_runnables_no_test_function_in_module() {
1314         check(
1315             r#"
1316 //- /lib.rs
1317 $0
1318 mod test_mod {
1319     fn foo1() {}
1320 }
1321 "#,
1322             &[],
1323             expect![[r#"
1324                 []
1325             "#]],
1326         );
1327     }
1328
1329     #[test]
1330     fn test_doc_runnables_impl_mod() {
1331         check(
1332             r#"
1333 //- /lib.rs
1334 mod foo;
1335 //- /foo.rs
1336 struct Foo;$0
1337 impl Foo {
1338     /// ```
1339     /// let x = 5;
1340     /// ```
1341     fn foo() {}
1342 }
1343         "#,
1344             &[DocTest],
1345             expect![[r#"
1346                 [
1347                     Runnable {
1348                         use_name_in_title: false,
1349                         nav: NavigationTarget {
1350                             file_id: FileId(
1351                                 1,
1352                             ),
1353                             full_range: 27..81,
1354                             name: "foo",
1355                         },
1356                         kind: DocTest {
1357                             test_id: Path(
1358                                 "foo::Foo::foo",
1359                             ),
1360                         },
1361                         cfg: None,
1362                     },
1363                 ]
1364             "#]],
1365         );
1366     }
1367
1368     #[test]
1369     fn test_runnables_in_macro() {
1370         check(
1371             r#"
1372 //- /lib.rs
1373 $0
1374 macro_rules! gen {
1375     () => {
1376         #[test]
1377         fn foo_test() {}
1378     }
1379 }
1380 macro_rules! gen2 {
1381     () => {
1382         mod tests2 {
1383             #[test]
1384             fn foo_test2() {}
1385         }
1386     }
1387 }
1388 mod tests {
1389     gen!();
1390 }
1391 gen2!();
1392 "#,
1393             &[TestMod, TestMod, TestMod, Test, Test],
1394             expect![[r#"
1395                 [
1396                     Runnable {
1397                         use_name_in_title: false,
1398                         nav: NavigationTarget {
1399                             file_id: FileId(
1400                                 0,
1401                             ),
1402                             full_range: 202..227,
1403                             focus_range: 206..211,
1404                             name: "tests",
1405                             kind: Module,
1406                             description: "mod tests",
1407                         },
1408                         kind: TestMod {
1409                             path: "tests",
1410                         },
1411                         cfg: None,
1412                     },
1413                     Runnable {
1414                         use_name_in_title: false,
1415                         nav: NavigationTarget {
1416                             file_id: FileId(
1417                                 0,
1418                             ),
1419                             full_range: 0..237,
1420                             name: "",
1421                             kind: Module,
1422                         },
1423                         kind: TestMod {
1424                             path: "",
1425                         },
1426                         cfg: None,
1427                     },
1428                     Runnable {
1429                         use_name_in_title: true,
1430                         nav: NavigationTarget {
1431                             file_id: FileId(
1432                                 0,
1433                             ),
1434                             full_range: 228..236,
1435                             focus_range: 228..236,
1436                             name: "tests2",
1437                             kind: Module,
1438                             description: "mod tests2",
1439                         },
1440                         kind: TestMod {
1441                             path: "tests2",
1442                         },
1443                         cfg: None,
1444                     },
1445                     Runnable {
1446                         use_name_in_title: true,
1447                         nav: NavigationTarget {
1448                             file_id: FileId(
1449                                 0,
1450                             ),
1451                             full_range: 228..236,
1452                             focus_range: 228..236,
1453                             name: "foo_test2",
1454                             kind: Function,
1455                         },
1456                         kind: Test {
1457                             test_id: Path(
1458                                 "tests2::foo_test2",
1459                             ),
1460                             attr: TestAttr {
1461                                 ignore: false,
1462                             },
1463                         },
1464                         cfg: None,
1465                     },
1466                     Runnable {
1467                         use_name_in_title: false,
1468                         nav: NavigationTarget {
1469                             file_id: FileId(
1470                                 0,
1471                             ),
1472                             full_range: 218..225,
1473                             focus_range: 218..225,
1474                             name: "foo_test",
1475                             kind: Function,
1476                         },
1477                         kind: Test {
1478                             test_id: Path(
1479                                 "tests::foo_test",
1480                             ),
1481                             attr: TestAttr {
1482                                 ignore: false,
1483                             },
1484                         },
1485                         cfg: None,
1486                     },
1487                 ]
1488             "#]],
1489         );
1490     }
1491
1492     #[test]
1493     fn big_mac() {
1494         check(
1495             r#"
1496 //- /lib.rs
1497 $0
1498 macro_rules! foo {
1499     () => {
1500         mod foo_tests {
1501             #[test]
1502             fn foo0() {}
1503             #[test]
1504             fn foo1() {}
1505             #[test]
1506             fn foo2() {}
1507         }
1508     };
1509 }
1510 foo!();
1511 "#,
1512             &[TestMod, Test, Test, Test],
1513             expect![[r#"
1514                 [
1515                     Runnable {
1516                         use_name_in_title: true,
1517                         nav: NavigationTarget {
1518                             file_id: FileId(
1519                                 0,
1520                             ),
1521                             full_range: 210..217,
1522                             focus_range: 210..217,
1523                             name: "foo_tests",
1524                             kind: Module,
1525                             description: "mod foo_tests",
1526                         },
1527                         kind: TestMod {
1528                             path: "foo_tests",
1529                         },
1530                         cfg: None,
1531                     },
1532                     Runnable {
1533                         use_name_in_title: true,
1534                         nav: NavigationTarget {
1535                             file_id: FileId(
1536                                 0,
1537                             ),
1538                             full_range: 210..217,
1539                             focus_range: 210..217,
1540                             name: "foo0",
1541                             kind: Function,
1542                         },
1543                         kind: Test {
1544                             test_id: Path(
1545                                 "foo_tests::foo0",
1546                             ),
1547                             attr: TestAttr {
1548                                 ignore: false,
1549                             },
1550                         },
1551                         cfg: None,
1552                     },
1553                     Runnable {
1554                         use_name_in_title: true,
1555                         nav: NavigationTarget {
1556                             file_id: FileId(
1557                                 0,
1558                             ),
1559                             full_range: 210..217,
1560                             focus_range: 210..217,
1561                             name: "foo1",
1562                             kind: Function,
1563                         },
1564                         kind: Test {
1565                             test_id: Path(
1566                                 "foo_tests::foo1",
1567                             ),
1568                             attr: TestAttr {
1569                                 ignore: false,
1570                             },
1571                         },
1572                         cfg: None,
1573                     },
1574                     Runnable {
1575                         use_name_in_title: true,
1576                         nav: NavigationTarget {
1577                             file_id: FileId(
1578                                 0,
1579                             ),
1580                             full_range: 210..217,
1581                             focus_range: 210..217,
1582                             name: "foo2",
1583                             kind: Function,
1584                         },
1585                         kind: Test {
1586                             test_id: Path(
1587                                 "foo_tests::foo2",
1588                             ),
1589                             attr: TestAttr {
1590                                 ignore: false,
1591                             },
1592                         },
1593                         cfg: None,
1594                     },
1595                 ]
1596             "#]],
1597         );
1598     }
1599
1600     #[test]
1601     fn dont_recurse_in_outline_submodules() {
1602         check(
1603             r#"
1604 //- /lib.rs
1605 $0
1606 mod m;
1607 //- /m.rs
1608 mod tests {
1609     #[test]
1610     fn t() {}
1611 }
1612 "#,
1613             &[],
1614             expect![[r#"
1615                 []
1616             "#]],
1617         );
1618     }
1619
1620     #[test]
1621     fn outline_submodule1() {
1622         check(
1623             r#"
1624 //- /lib.rs
1625 $0
1626 mod m;
1627 //- /m.rs
1628 #[test]
1629 fn t0() {}
1630 #[test]
1631 fn t1() {}
1632 "#,
1633             &[TestMod],
1634             expect![[r#"
1635                 [
1636                     Runnable {
1637                         use_name_in_title: false,
1638                         nav: NavigationTarget {
1639                             file_id: FileId(
1640                                 0,
1641                             ),
1642                             full_range: 1..7,
1643                             focus_range: 5..6,
1644                             name: "m",
1645                             kind: Module,
1646                             description: "mod m",
1647                         },
1648                         kind: TestMod {
1649                             path: "m",
1650                         },
1651                         cfg: None,
1652                     },
1653                 ]
1654             "#]],
1655         );
1656     }
1657
1658     #[test]
1659     fn outline_submodule2() {
1660         check(
1661             r#"
1662 //- /lib.rs
1663 mod m;
1664 //- /m.rs
1665 $0
1666 #[test]
1667 fn t0() {}
1668 #[test]
1669 fn t1() {}
1670 "#,
1671             &[Test, Test, TestMod],
1672             expect![[r#"
1673                 [
1674                     Runnable {
1675                         use_name_in_title: false,
1676                         nav: NavigationTarget {
1677                             file_id: FileId(
1678                                 1,
1679                             ),
1680                             full_range: 1..19,
1681                             focus_range: 12..14,
1682                             name: "t0",
1683                             kind: Function,
1684                         },
1685                         kind: Test {
1686                             test_id: Path(
1687                                 "m::t0",
1688                             ),
1689                             attr: TestAttr {
1690                                 ignore: false,
1691                             },
1692                         },
1693                         cfg: None,
1694                     },
1695                     Runnable {
1696                         use_name_in_title: false,
1697                         nav: NavigationTarget {
1698                             file_id: FileId(
1699                                 1,
1700                             ),
1701                             full_range: 20..38,
1702                             focus_range: 31..33,
1703                             name: "t1",
1704                             kind: Function,
1705                         },
1706                         kind: Test {
1707                             test_id: Path(
1708                                 "m::t1",
1709                             ),
1710                             attr: TestAttr {
1711                                 ignore: false,
1712                             },
1713                         },
1714                         cfg: None,
1715                     },
1716                     Runnable {
1717                         use_name_in_title: false,
1718                         nav: NavigationTarget {
1719                             file_id: FileId(
1720                                 1,
1721                             ),
1722                             full_range: 0..39,
1723                             name: "m",
1724                             kind: Module,
1725                         },
1726                         kind: TestMod {
1727                             path: "m",
1728                         },
1729                         cfg: None,
1730                     },
1731                 ]
1732             "#]],
1733         );
1734     }
1735
1736     #[test]
1737     fn find_no_tests() {
1738         check_tests(
1739             r#"
1740 //- /lib.rs
1741 fn foo$0() {  };
1742 "#,
1743             expect![[r#"
1744                 []
1745             "#]],
1746         );
1747     }
1748
1749     #[test]
1750     fn find_direct_fn_test() {
1751         check_tests(
1752             r#"
1753 //- /lib.rs
1754 fn foo$0() { };
1755
1756 mod tests {
1757     #[test]
1758     fn foo_test() {
1759         super::foo()
1760     }
1761 }
1762 "#,
1763             expect![[r#"
1764                 [
1765                     Runnable {
1766                         use_name_in_title: false,
1767                         nav: NavigationTarget {
1768                             file_id: FileId(
1769                                 0,
1770                             ),
1771                             full_range: 31..85,
1772                             focus_range: 46..54,
1773                             name: "foo_test",
1774                             kind: Function,
1775                         },
1776                         kind: Test {
1777                             test_id: Path(
1778                                 "tests::foo_test",
1779                             ),
1780                             attr: TestAttr {
1781                                 ignore: false,
1782                             },
1783                         },
1784                         cfg: None,
1785                     },
1786                 ]
1787             "#]],
1788         );
1789     }
1790
1791     #[test]
1792     fn find_direct_struct_test() {
1793         check_tests(
1794             r#"
1795 //- /lib.rs
1796 struct Fo$0o;
1797 fn foo(arg: &Foo) { };
1798
1799 mod tests {
1800     use super::*;
1801
1802     #[test]
1803     fn foo_test() {
1804         foo(Foo);
1805     }
1806 }
1807 "#,
1808             expect![[r#"
1809                 [
1810                     Runnable {
1811                         use_name_in_title: false,
1812                         nav: NavigationTarget {
1813                             file_id: FileId(
1814                                 0,
1815                             ),
1816                             full_range: 71..122,
1817                             focus_range: 86..94,
1818                             name: "foo_test",
1819                             kind: Function,
1820                         },
1821                         kind: Test {
1822                             test_id: Path(
1823                                 "tests::foo_test",
1824                             ),
1825                             attr: TestAttr {
1826                                 ignore: false,
1827                             },
1828                         },
1829                         cfg: None,
1830                     },
1831                 ]
1832             "#]],
1833         );
1834     }
1835
1836     #[test]
1837     fn find_indirect_fn_test() {
1838         check_tests(
1839             r#"
1840 //- /lib.rs
1841 fn foo$0() { };
1842
1843 mod tests {
1844     use super::foo;
1845
1846     fn check1() {
1847         check2()
1848     }
1849
1850     fn check2() {
1851         foo()
1852     }
1853
1854     #[test]
1855     fn foo_test() {
1856         check1()
1857     }
1858 }
1859 "#,
1860             expect![[r#"
1861                 [
1862                     Runnable {
1863                         use_name_in_title: false,
1864                         nav: NavigationTarget {
1865                             file_id: FileId(
1866                                 0,
1867                             ),
1868                             full_range: 133..183,
1869                             focus_range: 148..156,
1870                             name: "foo_test",
1871                             kind: Function,
1872                         },
1873                         kind: Test {
1874                             test_id: Path(
1875                                 "tests::foo_test",
1876                             ),
1877                             attr: TestAttr {
1878                                 ignore: false,
1879                             },
1880                         },
1881                         cfg: None,
1882                     },
1883                 ]
1884             "#]],
1885         );
1886     }
1887
1888     #[test]
1889     fn tests_are_unique() {
1890         check_tests(
1891             r#"
1892 //- /lib.rs
1893 fn foo$0() { };
1894
1895 mod tests {
1896     use super::foo;
1897
1898     #[test]
1899     fn foo_test() {
1900         foo();
1901         foo();
1902     }
1903
1904     #[test]
1905     fn foo2_test() {
1906         foo();
1907         foo();
1908     }
1909
1910 }
1911 "#,
1912             expect![[r#"
1913                 [
1914                     Runnable {
1915                         use_name_in_title: false,
1916                         nav: NavigationTarget {
1917                             file_id: FileId(
1918                                 0,
1919                             ),
1920                             full_range: 52..115,
1921                             focus_range: 67..75,
1922                             name: "foo_test",
1923                             kind: Function,
1924                         },
1925                         kind: Test {
1926                             test_id: Path(
1927                                 "tests::foo_test",
1928                             ),
1929                             attr: TestAttr {
1930                                 ignore: false,
1931                             },
1932                         },
1933                         cfg: None,
1934                     },
1935                     Runnable {
1936                         use_name_in_title: false,
1937                         nav: NavigationTarget {
1938                             file_id: FileId(
1939                                 0,
1940                             ),
1941                             full_range: 121..185,
1942                             focus_range: 136..145,
1943                             name: "foo2_test",
1944                             kind: Function,
1945                         },
1946                         kind: Test {
1947                             test_id: Path(
1948                                 "tests::foo2_test",
1949                             ),
1950                             attr: TestAttr {
1951                                 ignore: false,
1952                             },
1953                         },
1954                         cfg: None,
1955                     },
1956                 ]
1957             "#]],
1958         );
1959     }
1960
1961     #[test]
1962     fn doc_test_type_params() {
1963         check(
1964             r#"
1965 //- /lib.rs
1966 $0
1967 struct Foo<T, U>;
1968
1969 impl<T, U> Foo<T, U> {
1970     /// ```rust
1971     /// ````
1972     fn t() {}
1973 }
1974 "#,
1975             &[DocTest],
1976             expect![[r#"
1977                 [
1978                     Runnable {
1979                         use_name_in_title: false,
1980                         nav: NavigationTarget {
1981                             file_id: FileId(
1982                                 0,
1983                             ),
1984                             full_range: 47..85,
1985                             name: "t",
1986                         },
1987                         kind: DocTest {
1988                             test_id: Path(
1989                                 "Foo<T, U>::t",
1990                             ),
1991                         },
1992                         cfg: None,
1993                     },
1994                 ]
1995             "#]],
1996         );
1997     }
1998 }