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