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