]> git.lizzy.rs Git - rust.git/blob - crates/ide/src/runnables.rs
2cf801751ca7a50f995d20850608c79e9a57ef85
[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.canonical_module_path(db)?
404             .flat_map(|it| it.name(db))
405             .for_each(|name| format_to!(path, "{}::", name));
406         // This probably belongs to canonical_path?
407         if let Some(assoc_item) = def.as_assoc_item(db) {
408             if let hir::AssocItemContainer::Impl(imp) = assoc_item.container(db) {
409                 let ty = imp.self_ty(db);
410                 if let Some(adt) = ty.as_adt() {
411                     let name = adt.name(db);
412                     let mut ty_args = ty.type_arguments().peekable();
413                     format_to!(path, "{}", name);
414                     if ty_args.peek().is_some() {
415                         format_to!(
416                             path,
417                             "<{}>",
418                             ty_args.format_with(", ", |ty, cb| cb(&ty.display(db)))
419                         );
420                     }
421                     format_to!(path, "::{}", def_name);
422                     return Some(path);
423                 }
424             }
425         }
426         format_to!(path, "{}", def_name);
427         Some(path)
428     })();
429
430     let test_id = path.map_or_else(|| TestId::Name(def_name.to_string()), TestId::Path);
431
432     let mut nav = match def {
433         hir::ModuleDef::Module(def) => NavigationTarget::from_module_to_decl(db, def),
434         def => def.try_to_nav(db)?,
435     };
436     nav.focus_range = None;
437     nav.description = None;
438     nav.docs = None;
439     nav.kind = None;
440     let res = Runnable {
441         use_name_in_title: false,
442         nav,
443         kind: RunnableKind::DocTest { test_id },
444         cfg: attrs.cfg(),
445     };
446     Some(res)
447 }
448
449 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
450 pub struct TestAttr {
451     pub ignore: bool,
452 }
453
454 impl TestAttr {
455     fn from_fn(fn_def: &ast::Fn) -> TestAttr {
456         let ignore = fn_def
457             .attrs()
458             .filter_map(|attr| attr.simple_name())
459             .any(|attribute_text| attribute_text == "ignore");
460         TestAttr { ignore }
461     }
462 }
463
464 const RUSTDOC_FENCE: &str = "```";
465 const RUSTDOC_CODE_BLOCK_ATTRIBUTES_RUNNABLE: &[&str] =
466     &["", "rust", "should_panic", "edition2015", "edition2018", "edition2021"];
467
468 fn has_runnable_doc_test(attrs: &hir::Attrs) -> bool {
469     attrs.docs().map_or(false, |doc| {
470         let mut in_code_block = false;
471
472         for line in String::from(doc).lines() {
473             if let Some(header) = line.strip_prefix(RUSTDOC_FENCE) {
474                 in_code_block = !in_code_block;
475
476                 if in_code_block
477                     && header
478                         .split(',')
479                         .all(|sub| RUSTDOC_CODE_BLOCK_ATTRIBUTES_RUNNABLE.contains(&sub.trim()))
480                 {
481                     return true;
482                 }
483             }
484         }
485
486         false
487     })
488 }
489
490 // We could create runnables for modules with number_of_test_submodules > 0,
491 // but that bloats the runnables for no real benefit, since all tests can be run by the submodule already
492 fn has_test_function_or_multiple_test_submodules(
493     sema: &Semantics<RootDatabase>,
494     module: &hir::Module,
495 ) -> bool {
496     let mut number_of_test_submodules = 0;
497
498     for item in module.declarations(sema.db) {
499         match item {
500             hir::ModuleDef::Function(f) => {
501                 if let Some(it) = f.source(sema.db) {
502                     if test_related_attribute(&it.value).is_some() {
503                         return true;
504                     }
505                 }
506             }
507             hir::ModuleDef::Module(submodule) => {
508                 if has_test_function_or_multiple_test_submodules(sema, &submodule) {
509                     number_of_test_submodules += 1;
510                 }
511             }
512             _ => (),
513         }
514     }
515
516     number_of_test_submodules > 1
517 }
518
519 #[cfg(test)]
520 mod tests {
521     use expect_test::{expect, Expect};
522
523     use crate::fixture;
524
525     use super::{RunnableTestKind::*, *};
526
527     fn check(
528         ra_fixture: &str,
529         // FIXME: fold this into `expect` as well
530         actions: &[RunnableTestKind],
531         expect: Expect,
532     ) {
533         let (analysis, position) = fixture::position(ra_fixture);
534         let runnables = analysis.runnables(position.file_id).unwrap();
535         expect.assert_debug_eq(&runnables);
536         assert_eq!(
537             actions,
538             runnables.into_iter().map(|it| it.test_kind()).collect::<Vec<_>>().as_slice()
539         );
540     }
541
542     fn check_tests(ra_fixture: &str, expect: Expect) {
543         let (analysis, position) = fixture::position(ra_fixture);
544         let tests = analysis.related_tests(position, None).unwrap();
545         expect.assert_debug_eq(&tests);
546     }
547
548     #[test]
549     fn test_runnables() {
550         check(
551             r#"
552 //- /lib.rs
553 $0
554 fn main() {}
555
556 #[test]
557 fn test_foo() {}
558
559 #[test]
560 #[ignore]
561 fn test_foo() {}
562
563 #[bench]
564 fn bench() {}
565
566 mod not_a_root {
567     fn main() {}
568 }
569 "#,
570             &[Bin, Test, Test, Bench, TestMod],
571             expect![[r#"
572                 [
573                     Runnable {
574                         use_name_in_title: false,
575                         nav: NavigationTarget {
576                             file_id: FileId(
577                                 0,
578                             ),
579                             full_range: 1..13,
580                             focus_range: 4..8,
581                             name: "main",
582                             kind: Function,
583                         },
584                         kind: Bin,
585                         cfg: None,
586                     },
587                     Runnable {
588                         use_name_in_title: false,
589                         nav: NavigationTarget {
590                             file_id: FileId(
591                                 0,
592                             ),
593                             full_range: 15..39,
594                             focus_range: 26..34,
595                             name: "test_foo",
596                             kind: Function,
597                         },
598                         kind: Test {
599                             test_id: Path(
600                                 "test_foo",
601                             ),
602                             attr: TestAttr {
603                                 ignore: false,
604                             },
605                         },
606                         cfg: None,
607                     },
608                     Runnable {
609                         use_name_in_title: false,
610                         nav: NavigationTarget {
611                             file_id: FileId(
612                                 0,
613                             ),
614                             full_range: 41..75,
615                             focus_range: 62..70,
616                             name: "test_foo",
617                             kind: Function,
618                         },
619                         kind: Test {
620                             test_id: Path(
621                                 "test_foo",
622                             ),
623                             attr: TestAttr {
624                                 ignore: true,
625                             },
626                         },
627                         cfg: None,
628                     },
629                     Runnable {
630                         use_name_in_title: false,
631                         nav: NavigationTarget {
632                             file_id: FileId(
633                                 0,
634                             ),
635                             full_range: 77..99,
636                             focus_range: 89..94,
637                             name: "bench",
638                             kind: Function,
639                         },
640                         kind: Bench {
641                             test_id: Path(
642                                 "bench",
643                             ),
644                         },
645                         cfg: None,
646                     },
647                     Runnable {
648                         use_name_in_title: false,
649                         nav: NavigationTarget {
650                             file_id: FileId(
651                                 0,
652                             ),
653                             full_range: 0..137,
654                             name: "",
655                             kind: Module,
656                         },
657                         kind: TestMod {
658                             path: "",
659                         },
660                         cfg: None,
661                     },
662                 ]
663             "#]],
664         );
665     }
666
667     #[test]
668     fn test_runnables_doc_test() {
669         check(
670             r#"
671 //- /lib.rs
672 $0
673 fn main() {}
674
675 /// ```
676 /// let x = 5;
677 /// ```
678 fn should_have_runnable() {}
679
680 /// ```edition2018
681 /// let x = 5;
682 /// ```
683 fn should_have_runnable_1() {}
684
685 /// ```
686 /// let z = 55;
687 /// ```
688 ///
689 /// ```ignore
690 /// let z = 56;
691 /// ```
692 fn should_have_runnable_2() {}
693
694 /**
695 ```rust
696 let z = 55;
697 ```
698 */
699 fn should_have_no_runnable_3() {}
700
701 /**
702     ```rust
703     let z = 55;
704     ```
705 */
706 fn should_have_no_runnable_4() {}
707
708 /// ```no_run
709 /// let z = 55;
710 /// ```
711 fn should_have_no_runnable() {}
712
713 /// ```ignore
714 /// let z = 55;
715 /// ```
716 fn should_have_no_runnable_2() {}
717
718 /// ```compile_fail
719 /// let z = 55;
720 /// ```
721 fn should_have_no_runnable_3() {}
722
723 /// ```text
724 /// arbitrary plain text
725 /// ```
726 fn should_have_no_runnable_4() {}
727
728 /// ```text
729 /// arbitrary plain text
730 /// ```
731 ///
732 /// ```sh
733 /// $ shell code
734 /// ```
735 fn should_have_no_runnable_5() {}
736
737 /// ```rust,no_run
738 /// let z = 55;
739 /// ```
740 fn should_have_no_runnable_6() {}
741
742 /// ```
743 /// let x = 5;
744 /// ```
745 struct StructWithRunnable(String);
746
747 /// ```
748 /// let x = 5;
749 /// ```
750 impl StructWithRunnable {}
751
752 trait Test {
753     fn test() -> usize {
754         5usize
755     }
756 }
757
758 /// ```
759 /// let x = 5;
760 /// ```
761 impl Test for StructWithRunnable {}
762 "#,
763             &[Bin, DocTest, DocTest, DocTest, DocTest, DocTest, DocTest, DocTest, DocTest],
764             expect![[r#"
765                 [
766                     Runnable {
767                         use_name_in_title: false,
768                         nav: NavigationTarget {
769                             file_id: FileId(
770                                 0,
771                             ),
772                             full_range: 1..13,
773                             focus_range: 4..8,
774                             name: "main",
775                             kind: Function,
776                         },
777                         kind: Bin,
778                         cfg: None,
779                     },
780                     Runnable {
781                         use_name_in_title: false,
782                         nav: NavigationTarget {
783                             file_id: FileId(
784                                 0,
785                             ),
786                             full_range: 15..74,
787                             name: "should_have_runnable",
788                         },
789                         kind: DocTest {
790                             test_id: Path(
791                                 "should_have_runnable",
792                             ),
793                         },
794                         cfg: None,
795                     },
796                     Runnable {
797                         use_name_in_title: false,
798                         nav: NavigationTarget {
799                             file_id: FileId(
800                                 0,
801                             ),
802                             full_range: 76..148,
803                             name: "should_have_runnable_1",
804                         },
805                         kind: DocTest {
806                             test_id: Path(
807                                 "should_have_runnable_1",
808                             ),
809                         },
810                         cfg: None,
811                     },
812                     Runnable {
813                         use_name_in_title: false,
814                         nav: NavigationTarget {
815                             file_id: FileId(
816                                 0,
817                             ),
818                             full_range: 150..254,
819                             name: "should_have_runnable_2",
820                         },
821                         kind: DocTest {
822                             test_id: Path(
823                                 "should_have_runnable_2",
824                             ),
825                         },
826                         cfg: None,
827                     },
828                     Runnable {
829                         use_name_in_title: false,
830                         nav: NavigationTarget {
831                             file_id: FileId(
832                                 0,
833                             ),
834                             full_range: 256..320,
835                             name: "should_have_no_runnable_3",
836                         },
837                         kind: DocTest {
838                             test_id: Path(
839                                 "should_have_no_runnable_3",
840                             ),
841                         },
842                         cfg: None,
843                     },
844                     Runnable {
845                         use_name_in_title: false,
846                         nav: NavigationTarget {
847                             file_id: FileId(
848                                 0,
849                             ),
850                             full_range: 322..398,
851                             name: "should_have_no_runnable_4",
852                         },
853                         kind: DocTest {
854                             test_id: Path(
855                                 "should_have_no_runnable_4",
856                             ),
857                         },
858                         cfg: None,
859                     },
860                     Runnable {
861                         use_name_in_title: false,
862                         nav: NavigationTarget {
863                             file_id: FileId(
864                                 0,
865                             ),
866                             full_range: 900..965,
867                             name: "StructWithRunnable",
868                         },
869                         kind: DocTest {
870                             test_id: Path(
871                                 "StructWithRunnable",
872                             ),
873                         },
874                         cfg: None,
875                     },
876                     Runnable {
877                         use_name_in_title: false,
878                         nav: NavigationTarget {
879                             file_id: FileId(
880                                 0,
881                             ),
882                             full_range: 967..1024,
883                             focus_range: 1003..1021,
884                             name: "impl",
885                             kind: Impl,
886                         },
887                         kind: DocTest {
888                             test_id: Path(
889                                 "StructWithRunnable",
890                             ),
891                         },
892                         cfg: None,
893                     },
894                     Runnable {
895                         use_name_in_title: false,
896                         nav: NavigationTarget {
897                             file_id: FileId(
898                                 0,
899                             ),
900                             full_range: 1088..1154,
901                             focus_range: 1133..1151,
902                             name: "impl",
903                             kind: Impl,
904                         },
905                         kind: DocTest {
906                             test_id: Path(
907                                 "StructWithRunnable",
908                             ),
909                         },
910                         cfg: None,
911                     },
912                 ]
913             "#]],
914         );
915     }
916
917     #[test]
918     fn test_runnables_doc_test_in_impl() {
919         check(
920             r#"
921 //- /lib.rs
922 $0
923 fn main() {}
924
925 struct Data;
926 impl Data {
927     /// ```
928     /// let x = 5;
929     /// ```
930     fn foo() {}
931 }
932 "#,
933             &[Bin, DocTest],
934             expect![[r#"
935                 [
936                     Runnable {
937                         use_name_in_title: false,
938                         nav: NavigationTarget {
939                             file_id: FileId(
940                                 0,
941                             ),
942                             full_range: 1..13,
943                             focus_range: 4..8,
944                             name: "main",
945                             kind: Function,
946                         },
947                         kind: Bin,
948                         cfg: None,
949                     },
950                     Runnable {
951                         use_name_in_title: false,
952                         nav: NavigationTarget {
953                             file_id: FileId(
954                                 0,
955                             ),
956                             full_range: 44..98,
957                             name: "foo",
958                         },
959                         kind: DocTest {
960                             test_id: Path(
961                                 "Data::foo",
962                             ),
963                         },
964                         cfg: None,
965                     },
966                 ]
967             "#]],
968         );
969     }
970
971     #[test]
972     fn test_runnables_module() {
973         check(
974             r#"
975 //- /lib.rs
976 $0
977 mod test_mod {
978     #[test]
979     fn test_foo1() {}
980 }
981 "#,
982             &[TestMod, Test],
983             expect![[r#"
984                 [
985                     Runnable {
986                         use_name_in_title: false,
987                         nav: NavigationTarget {
988                             file_id: FileId(
989                                 0,
990                             ),
991                             full_range: 1..51,
992                             focus_range: 5..13,
993                             name: "test_mod",
994                             kind: Module,
995                             description: "mod test_mod",
996                         },
997                         kind: TestMod {
998                             path: "test_mod",
999                         },
1000                         cfg: None,
1001                     },
1002                     Runnable {
1003                         use_name_in_title: false,
1004                         nav: NavigationTarget {
1005                             file_id: FileId(
1006                                 0,
1007                             ),
1008                             full_range: 20..49,
1009                             focus_range: 35..44,
1010                             name: "test_foo1",
1011                             kind: Function,
1012                         },
1013                         kind: Test {
1014                             test_id: Path(
1015                                 "test_mod::test_foo1",
1016                             ),
1017                             attr: TestAttr {
1018                                 ignore: false,
1019                             },
1020                         },
1021                         cfg: None,
1022                     },
1023                 ]
1024             "#]],
1025         );
1026     }
1027
1028     #[test]
1029     fn only_modules_with_test_functions_or_more_than_one_test_submodule_have_runners() {
1030         check(
1031             r#"
1032 //- /lib.rs
1033 $0
1034 mod root_tests {
1035     mod nested_tests_0 {
1036         mod nested_tests_1 {
1037             #[test]
1038             fn nested_test_11() {}
1039
1040             #[test]
1041             fn nested_test_12() {}
1042         }
1043
1044         mod nested_tests_2 {
1045             #[test]
1046             fn nested_test_2() {}
1047         }
1048
1049         mod nested_tests_3 {}
1050     }
1051
1052     mod nested_tests_4 {}
1053 }
1054 "#,
1055             &[TestMod, TestMod, TestMod, Test, Test, Test],
1056             expect![[r#"
1057                 [
1058                     Runnable {
1059                         use_name_in_title: false,
1060                         nav: NavigationTarget {
1061                             file_id: FileId(
1062                                 0,
1063                             ),
1064                             full_range: 22..323,
1065                             focus_range: 26..40,
1066                             name: "nested_tests_0",
1067                             kind: Module,
1068                             description: "mod nested_tests_0",
1069                         },
1070                         kind: TestMod {
1071                             path: "root_tests::nested_tests_0",
1072                         },
1073                         cfg: None,
1074                     },
1075                     Runnable {
1076                         use_name_in_title: false,
1077                         nav: NavigationTarget {
1078                             file_id: FileId(
1079                                 0,
1080                             ),
1081                             full_range: 51..192,
1082                             focus_range: 55..69,
1083                             name: "nested_tests_1",
1084                             kind: Module,
1085                             description: "mod nested_tests_1",
1086                         },
1087                         kind: TestMod {
1088                             path: "root_tests::nested_tests_0::nested_tests_1",
1089                         },
1090                         cfg: None,
1091                     },
1092                     Runnable {
1093                         use_name_in_title: false,
1094                         nav: NavigationTarget {
1095                             file_id: FileId(
1096                                 0,
1097                             ),
1098                             full_range: 202..286,
1099                             focus_range: 206..220,
1100                             name: "nested_tests_2",
1101                             kind: Module,
1102                             description: "mod nested_tests_2",
1103                         },
1104                         kind: TestMod {
1105                             path: "root_tests::nested_tests_0::nested_tests_2",
1106                         },
1107                         cfg: None,
1108                     },
1109                     Runnable {
1110                         use_name_in_title: false,
1111                         nav: NavigationTarget {
1112                             file_id: FileId(
1113                                 0,
1114                             ),
1115                             full_range: 84..126,
1116                             focus_range: 107..121,
1117                             name: "nested_test_11",
1118                             kind: Function,
1119                         },
1120                         kind: Test {
1121                             test_id: Path(
1122                                 "root_tests::nested_tests_0::nested_tests_1::nested_test_11",
1123                             ),
1124                             attr: TestAttr {
1125                                 ignore: false,
1126                             },
1127                         },
1128                         cfg: None,
1129                     },
1130                     Runnable {
1131                         use_name_in_title: false,
1132                         nav: NavigationTarget {
1133                             file_id: FileId(
1134                                 0,
1135                             ),
1136                             full_range: 140..182,
1137                             focus_range: 163..177,
1138                             name: "nested_test_12",
1139                             kind: Function,
1140                         },
1141                         kind: Test {
1142                             test_id: Path(
1143                                 "root_tests::nested_tests_0::nested_tests_1::nested_test_12",
1144                             ),
1145                             attr: TestAttr {
1146                                 ignore: false,
1147                             },
1148                         },
1149                         cfg: None,
1150                     },
1151                     Runnable {
1152                         use_name_in_title: false,
1153                         nav: NavigationTarget {
1154                             file_id: FileId(
1155                                 0,
1156                             ),
1157                             full_range: 235..276,
1158                             focus_range: 258..271,
1159                             name: "nested_test_2",
1160                             kind: Function,
1161                         },
1162                         kind: Test {
1163                             test_id: Path(
1164                                 "root_tests::nested_tests_0::nested_tests_2::nested_test_2",
1165                             ),
1166                             attr: TestAttr {
1167                                 ignore: false,
1168                             },
1169                         },
1170                         cfg: None,
1171                     },
1172                 ]
1173             "#]],
1174         );
1175     }
1176
1177     #[test]
1178     fn test_runnables_with_feature() {
1179         check(
1180             r#"
1181 //- /lib.rs crate:foo cfg:feature=foo
1182 $0
1183 #[test]
1184 #[cfg(feature = "foo")]
1185 fn test_foo1() {}
1186 "#,
1187             &[Test, TestMod],
1188             expect![[r#"
1189                 [
1190                     Runnable {
1191                         use_name_in_title: false,
1192                         nav: NavigationTarget {
1193                             file_id: FileId(
1194                                 0,
1195                             ),
1196                             full_range: 1..50,
1197                             focus_range: 36..45,
1198                             name: "test_foo1",
1199                             kind: Function,
1200                         },
1201                         kind: Test {
1202                             test_id: Path(
1203                                 "test_foo1",
1204                             ),
1205                             attr: TestAttr {
1206                                 ignore: false,
1207                             },
1208                         },
1209                         cfg: Some(
1210                             Atom(
1211                                 KeyValue {
1212                                     key: "feature",
1213                                     value: "foo",
1214                                 },
1215                             ),
1216                         ),
1217                     },
1218                     Runnable {
1219                         use_name_in_title: false,
1220                         nav: NavigationTarget {
1221                             file_id: FileId(
1222                                 0,
1223                             ),
1224                             full_range: 0..51,
1225                             name: "",
1226                             kind: Module,
1227                         },
1228                         kind: TestMod {
1229                             path: "",
1230                         },
1231                         cfg: None,
1232                     },
1233                 ]
1234             "#]],
1235         );
1236     }
1237
1238     #[test]
1239     fn test_runnables_with_features() {
1240         check(
1241             r#"
1242 //- /lib.rs crate:foo cfg:feature=foo,feature=bar
1243 $0
1244 #[test]
1245 #[cfg(all(feature = "foo", feature = "bar"))]
1246 fn test_foo1() {}
1247 "#,
1248             &[Test, TestMod],
1249             expect![[r#"
1250                 [
1251                     Runnable {
1252                         use_name_in_title: false,
1253                         nav: NavigationTarget {
1254                             file_id: FileId(
1255                                 0,
1256                             ),
1257                             full_range: 1..72,
1258                             focus_range: 58..67,
1259                             name: "test_foo1",
1260                             kind: Function,
1261                         },
1262                         kind: Test {
1263                             test_id: Path(
1264                                 "test_foo1",
1265                             ),
1266                             attr: TestAttr {
1267                                 ignore: false,
1268                             },
1269                         },
1270                         cfg: Some(
1271                             All(
1272                                 [
1273                                     Atom(
1274                                         KeyValue {
1275                                             key: "feature",
1276                                             value: "foo",
1277                                         },
1278                                     ),
1279                                     Atom(
1280                                         KeyValue {
1281                                             key: "feature",
1282                                             value: "bar",
1283                                         },
1284                                     ),
1285                                 ],
1286                             ),
1287                         ),
1288                     },
1289                     Runnable {
1290                         use_name_in_title: false,
1291                         nav: NavigationTarget {
1292                             file_id: FileId(
1293                                 0,
1294                             ),
1295                             full_range: 0..73,
1296                             name: "",
1297                             kind: Module,
1298                         },
1299                         kind: TestMod {
1300                             path: "",
1301                         },
1302                         cfg: None,
1303                     },
1304                 ]
1305             "#]],
1306         );
1307     }
1308
1309     #[test]
1310     fn test_runnables_no_test_function_in_module() {
1311         check(
1312             r#"
1313 //- /lib.rs
1314 $0
1315 mod test_mod {
1316     fn foo1() {}
1317 }
1318 "#,
1319             &[],
1320             expect![[r#"
1321                 []
1322             "#]],
1323         );
1324     }
1325
1326     #[test]
1327     fn test_doc_runnables_impl_mod() {
1328         check(
1329             r#"
1330 //- /lib.rs
1331 mod foo;
1332 //- /foo.rs
1333 struct Foo;$0
1334 impl Foo {
1335     /// ```
1336     /// let x = 5;
1337     /// ```
1338     fn foo() {}
1339 }
1340         "#,
1341             &[DocTest],
1342             expect![[r#"
1343                 [
1344                     Runnable {
1345                         use_name_in_title: false,
1346                         nav: NavigationTarget {
1347                             file_id: FileId(
1348                                 1,
1349                             ),
1350                             full_range: 27..81,
1351                             name: "foo",
1352                         },
1353                         kind: DocTest {
1354                             test_id: Path(
1355                                 "foo::Foo::foo",
1356                             ),
1357                         },
1358                         cfg: None,
1359                     },
1360                 ]
1361             "#]],
1362         );
1363     }
1364
1365     #[test]
1366     fn test_runnables_in_macro() {
1367         check(
1368             r#"
1369 //- /lib.rs
1370 $0
1371 macro_rules! gen {
1372     () => {
1373         #[test]
1374         fn foo_test() {}
1375     }
1376 }
1377 macro_rules! gen2 {
1378     () => {
1379         mod tests2 {
1380             #[test]
1381             fn foo_test2() {}
1382         }
1383     }
1384 }
1385 mod tests {
1386     gen!();
1387 }
1388 gen2!();
1389 "#,
1390             &[TestMod, TestMod, TestMod, Test, Test],
1391             expect![[r#"
1392                 [
1393                     Runnable {
1394                         use_name_in_title: false,
1395                         nav: NavigationTarget {
1396                             file_id: FileId(
1397                                 0,
1398                             ),
1399                             full_range: 202..227,
1400                             focus_range: 206..211,
1401                             name: "tests",
1402                             kind: Module,
1403                             description: "mod tests",
1404                         },
1405                         kind: TestMod {
1406                             path: "tests",
1407                         },
1408                         cfg: None,
1409                     },
1410                     Runnable {
1411                         use_name_in_title: false,
1412                         nav: NavigationTarget {
1413                             file_id: FileId(
1414                                 0,
1415                             ),
1416                             full_range: 0..237,
1417                             name: "",
1418                             kind: Module,
1419                         },
1420                         kind: TestMod {
1421                             path: "",
1422                         },
1423                         cfg: None,
1424                     },
1425                     Runnable {
1426                         use_name_in_title: true,
1427                         nav: NavigationTarget {
1428                             file_id: FileId(
1429                                 0,
1430                             ),
1431                             full_range: 228..236,
1432                             focus_range: 228..236,
1433                             name: "tests2",
1434                             kind: Module,
1435                             description: "mod tests2",
1436                         },
1437                         kind: TestMod {
1438                             path: "tests2",
1439                         },
1440                         cfg: None,
1441                     },
1442                     Runnable {
1443                         use_name_in_title: true,
1444                         nav: NavigationTarget {
1445                             file_id: FileId(
1446                                 0,
1447                             ),
1448                             full_range: 228..236,
1449                             focus_range: 228..236,
1450                             name: "foo_test2",
1451                             kind: Function,
1452                         },
1453                         kind: Test {
1454                             test_id: Path(
1455                                 "tests2::foo_test2",
1456                             ),
1457                             attr: TestAttr {
1458                                 ignore: false,
1459                             },
1460                         },
1461                         cfg: None,
1462                     },
1463                     Runnable {
1464                         use_name_in_title: false,
1465                         nav: NavigationTarget {
1466                             file_id: FileId(
1467                                 0,
1468                             ),
1469                             full_range: 218..225,
1470                             focus_range: 218..225,
1471                             name: "foo_test",
1472                             kind: Function,
1473                         },
1474                         kind: Test {
1475                             test_id: Path(
1476                                 "tests::foo_test",
1477                             ),
1478                             attr: TestAttr {
1479                                 ignore: false,
1480                             },
1481                         },
1482                         cfg: None,
1483                     },
1484                 ]
1485             "#]],
1486         );
1487     }
1488
1489     #[test]
1490     fn big_mac() {
1491         check(
1492             r#"
1493 //- /lib.rs
1494 $0
1495 macro_rules! foo {
1496     () => {
1497         mod foo_tests {
1498             #[test]
1499             fn foo0() {}
1500             #[test]
1501             fn foo1() {}
1502             #[test]
1503             fn foo2() {}
1504         }
1505     };
1506 }
1507 foo!();
1508 "#,
1509             &[TestMod, Test, Test, Test],
1510             expect![[r#"
1511                 [
1512                     Runnable {
1513                         use_name_in_title: true,
1514                         nav: NavigationTarget {
1515                             file_id: FileId(
1516                                 0,
1517                             ),
1518                             full_range: 210..217,
1519                             focus_range: 210..217,
1520                             name: "foo_tests",
1521                             kind: Module,
1522                             description: "mod foo_tests",
1523                         },
1524                         kind: TestMod {
1525                             path: "foo_tests",
1526                         },
1527                         cfg: None,
1528                     },
1529                     Runnable {
1530                         use_name_in_title: true,
1531                         nav: NavigationTarget {
1532                             file_id: FileId(
1533                                 0,
1534                             ),
1535                             full_range: 210..217,
1536                             focus_range: 210..217,
1537                             name: "foo0",
1538                             kind: Function,
1539                         },
1540                         kind: Test {
1541                             test_id: Path(
1542                                 "foo_tests::foo0",
1543                             ),
1544                             attr: TestAttr {
1545                                 ignore: false,
1546                             },
1547                         },
1548                         cfg: None,
1549                     },
1550                     Runnable {
1551                         use_name_in_title: true,
1552                         nav: NavigationTarget {
1553                             file_id: FileId(
1554                                 0,
1555                             ),
1556                             full_range: 210..217,
1557                             focus_range: 210..217,
1558                             name: "foo1",
1559                             kind: Function,
1560                         },
1561                         kind: Test {
1562                             test_id: Path(
1563                                 "foo_tests::foo1",
1564                             ),
1565                             attr: TestAttr {
1566                                 ignore: false,
1567                             },
1568                         },
1569                         cfg: None,
1570                     },
1571                     Runnable {
1572                         use_name_in_title: true,
1573                         nav: NavigationTarget {
1574                             file_id: FileId(
1575                                 0,
1576                             ),
1577                             full_range: 210..217,
1578                             focus_range: 210..217,
1579                             name: "foo2",
1580                             kind: Function,
1581                         },
1582                         kind: Test {
1583                             test_id: Path(
1584                                 "foo_tests::foo2",
1585                             ),
1586                             attr: TestAttr {
1587                                 ignore: false,
1588                             },
1589                         },
1590                         cfg: None,
1591                     },
1592                 ]
1593             "#]],
1594         );
1595     }
1596
1597     #[test]
1598     fn dont_recurse_in_outline_submodules() {
1599         check(
1600             r#"
1601 //- /lib.rs
1602 $0
1603 mod m;
1604 //- /m.rs
1605 mod tests {
1606     #[test]
1607     fn t() {}
1608 }
1609 "#,
1610             &[],
1611             expect![[r#"
1612                 []
1613             "#]],
1614         );
1615     }
1616
1617     #[test]
1618     fn outline_submodule1() {
1619         check(
1620             r#"
1621 //- /lib.rs
1622 $0
1623 mod m;
1624 //- /m.rs
1625 #[test]
1626 fn t0() {}
1627 #[test]
1628 fn t1() {}
1629 "#,
1630             &[TestMod],
1631             expect![[r#"
1632                 [
1633                     Runnable {
1634                         use_name_in_title: false,
1635                         nav: NavigationTarget {
1636                             file_id: FileId(
1637                                 0,
1638                             ),
1639                             full_range: 1..7,
1640                             focus_range: 5..6,
1641                             name: "m",
1642                             kind: Module,
1643                             description: "mod m",
1644                         },
1645                         kind: TestMod {
1646                             path: "m",
1647                         },
1648                         cfg: None,
1649                     },
1650                 ]
1651             "#]],
1652         );
1653     }
1654
1655     #[test]
1656     fn outline_submodule2() {
1657         check(
1658             r#"
1659 //- /lib.rs
1660 mod m;
1661 //- /m.rs
1662 $0
1663 #[test]
1664 fn t0() {}
1665 #[test]
1666 fn t1() {}
1667 "#,
1668             &[Test, Test, TestMod],
1669             expect![[r#"
1670                 [
1671                     Runnable {
1672                         use_name_in_title: false,
1673                         nav: NavigationTarget {
1674                             file_id: FileId(
1675                                 1,
1676                             ),
1677                             full_range: 1..19,
1678                             focus_range: 12..14,
1679                             name: "t0",
1680                             kind: Function,
1681                         },
1682                         kind: Test {
1683                             test_id: Path(
1684                                 "m::t0",
1685                             ),
1686                             attr: TestAttr {
1687                                 ignore: false,
1688                             },
1689                         },
1690                         cfg: None,
1691                     },
1692                     Runnable {
1693                         use_name_in_title: false,
1694                         nav: NavigationTarget {
1695                             file_id: FileId(
1696                                 1,
1697                             ),
1698                             full_range: 20..38,
1699                             focus_range: 31..33,
1700                             name: "t1",
1701                             kind: Function,
1702                         },
1703                         kind: Test {
1704                             test_id: Path(
1705                                 "m::t1",
1706                             ),
1707                             attr: TestAttr {
1708                                 ignore: false,
1709                             },
1710                         },
1711                         cfg: None,
1712                     },
1713                     Runnable {
1714                         use_name_in_title: false,
1715                         nav: NavigationTarget {
1716                             file_id: FileId(
1717                                 1,
1718                             ),
1719                             full_range: 0..39,
1720                             name: "m",
1721                             kind: Module,
1722                         },
1723                         kind: TestMod {
1724                             path: "m",
1725                         },
1726                         cfg: None,
1727                     },
1728                 ]
1729             "#]],
1730         );
1731     }
1732
1733     #[test]
1734     fn find_no_tests() {
1735         check_tests(
1736             r#"
1737 //- /lib.rs
1738 fn foo$0() {  };
1739 "#,
1740             expect![[r#"
1741                 []
1742             "#]],
1743         );
1744     }
1745
1746     #[test]
1747     fn find_direct_fn_test() {
1748         check_tests(
1749             r#"
1750 //- /lib.rs
1751 fn foo$0() { };
1752
1753 mod tests {
1754     #[test]
1755     fn foo_test() {
1756         super::foo()
1757     }
1758 }
1759 "#,
1760             expect![[r#"
1761                 [
1762                     Runnable {
1763                         use_name_in_title: false,
1764                         nav: NavigationTarget {
1765                             file_id: FileId(
1766                                 0,
1767                             ),
1768                             full_range: 31..85,
1769                             focus_range: 46..54,
1770                             name: "foo_test",
1771                             kind: Function,
1772                         },
1773                         kind: Test {
1774                             test_id: Path(
1775                                 "tests::foo_test",
1776                             ),
1777                             attr: TestAttr {
1778                                 ignore: false,
1779                             },
1780                         },
1781                         cfg: None,
1782                     },
1783                 ]
1784             "#]],
1785         );
1786     }
1787
1788     #[test]
1789     fn find_direct_struct_test() {
1790         check_tests(
1791             r#"
1792 //- /lib.rs
1793 struct Fo$0o;
1794 fn foo(arg: &Foo) { };
1795
1796 mod tests {
1797     use super::*;
1798
1799     #[test]
1800     fn foo_test() {
1801         foo(Foo);
1802     }
1803 }
1804 "#,
1805             expect![[r#"
1806                 [
1807                     Runnable {
1808                         use_name_in_title: false,
1809                         nav: NavigationTarget {
1810                             file_id: FileId(
1811                                 0,
1812                             ),
1813                             full_range: 71..122,
1814                             focus_range: 86..94,
1815                             name: "foo_test",
1816                             kind: Function,
1817                         },
1818                         kind: Test {
1819                             test_id: Path(
1820                                 "tests::foo_test",
1821                             ),
1822                             attr: TestAttr {
1823                                 ignore: false,
1824                             },
1825                         },
1826                         cfg: None,
1827                     },
1828                 ]
1829             "#]],
1830         );
1831     }
1832
1833     #[test]
1834     fn find_indirect_fn_test() {
1835         check_tests(
1836             r#"
1837 //- /lib.rs
1838 fn foo$0() { };
1839
1840 mod tests {
1841     use super::foo;
1842
1843     fn check1() {
1844         check2()
1845     }
1846
1847     fn check2() {
1848         foo()
1849     }
1850
1851     #[test]
1852     fn foo_test() {
1853         check1()
1854     }
1855 }
1856 "#,
1857             expect![[r#"
1858                 [
1859                     Runnable {
1860                         use_name_in_title: false,
1861                         nav: NavigationTarget {
1862                             file_id: FileId(
1863                                 0,
1864                             ),
1865                             full_range: 133..183,
1866                             focus_range: 148..156,
1867                             name: "foo_test",
1868                             kind: Function,
1869                         },
1870                         kind: Test {
1871                             test_id: Path(
1872                                 "tests::foo_test",
1873                             ),
1874                             attr: TestAttr {
1875                                 ignore: false,
1876                             },
1877                         },
1878                         cfg: None,
1879                     },
1880                 ]
1881             "#]],
1882         );
1883     }
1884
1885     #[test]
1886     fn tests_are_unique() {
1887         check_tests(
1888             r#"
1889 //- /lib.rs
1890 fn foo$0() { };
1891
1892 mod tests {
1893     use super::foo;
1894
1895     #[test]
1896     fn foo_test() {
1897         foo();
1898         foo();
1899     }
1900
1901     #[test]
1902     fn foo2_test() {
1903         foo();
1904         foo();
1905     }
1906
1907 }
1908 "#,
1909             expect![[r#"
1910                 [
1911                     Runnable {
1912                         use_name_in_title: false,
1913                         nav: NavigationTarget {
1914                             file_id: FileId(
1915                                 0,
1916                             ),
1917                             full_range: 52..115,
1918                             focus_range: 67..75,
1919                             name: "foo_test",
1920                             kind: Function,
1921                         },
1922                         kind: Test {
1923                             test_id: Path(
1924                                 "tests::foo_test",
1925                             ),
1926                             attr: TestAttr {
1927                                 ignore: false,
1928                             },
1929                         },
1930                         cfg: None,
1931                     },
1932                     Runnable {
1933                         use_name_in_title: false,
1934                         nav: NavigationTarget {
1935                             file_id: FileId(
1936                                 0,
1937                             ),
1938                             full_range: 121..185,
1939                             focus_range: 136..145,
1940                             name: "foo2_test",
1941                             kind: Function,
1942                         },
1943                         kind: Test {
1944                             test_id: Path(
1945                                 "tests::foo2_test",
1946                             ),
1947                             attr: TestAttr {
1948                                 ignore: false,
1949                             },
1950                         },
1951                         cfg: None,
1952                     },
1953                 ]
1954             "#]],
1955         );
1956     }
1957
1958     #[test]
1959     fn doc_test_type_params() {
1960         check(
1961             r#"
1962 //- /lib.rs
1963 $0
1964 struct Foo<T, U>;
1965
1966 impl<T, U> Foo<T, U> {
1967     /// ```rust
1968     /// ````
1969     fn t() {}
1970 }
1971 "#,
1972             &[DocTest],
1973             expect![[r#"
1974                 [
1975                     Runnable {
1976                         use_name_in_title: false,
1977                         nav: NavigationTarget {
1978                             file_id: FileId(
1979                                 0,
1980                             ),
1981                             full_range: 47..85,
1982                             name: "t",
1983                         },
1984                         kind: DocTest {
1985                             test_id: Path(
1986                                 "Foo<T, U>::t",
1987                             ),
1988                         },
1989                         cfg: None,
1990                     },
1991                 ]
1992             "#]],
1993         );
1994     }
1995 }