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