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