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