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