1 use std::fmt;
2
3 use ast::HasName;
4 use cfg::CfgExpr;
5 use hir::{AsAssocItem, HasAttrs, HasSource, 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 {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result38 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
label(&self, target: Option<String>) -> String67 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
title(&self) -> String79 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)]
test_kind(&self) -> RunnableTestKind99 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[]
runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable>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. Right-click on
199 // the selected item. The context menu opens. Select **Peek Related Tests**.
200 //
201 // |===
202 // | Editor | Action Name
203 //
204 // | VS Code | **rust-analyzer: Peek Related Tests**
205 // |===
related_tests( db: &RootDatabase, position: FilePosition, search_scope: Option<SearchScope>, ) -> Vec<Runnable>206 pub(crate) fn related_tests(
207 db: &RootDatabase,
208 position: FilePosition,
209 search_scope: Option<SearchScope>,
210 ) -> Vec<Runnable> {
211 let sema = Semantics::new(db);
212 let mut res: FxHashSet<Runnable> = FxHashSet::default();
213 let syntax = sema.parse(position.file_id).syntax().clone();
214
215 find_related_tests(&sema, &syntax, position, search_scope, &mut res);
216
217 res.into_iter().collect()
218 }
219
find_related_tests( sema: &Semantics<'_, RootDatabase>, syntax: &SyntaxNode, position: FilePosition, search_scope: Option<SearchScope>, tests: &mut FxHashSet<Runnable>, )220 fn find_related_tests(
221 sema: &Semantics<'_, RootDatabase>,
222 syntax: &SyntaxNode,
223 position: FilePosition,
224 search_scope: Option<SearchScope>,
225 tests: &mut FxHashSet<Runnable>,
226 ) {
227 // FIXME: why is this using references::find_defs, this should use ide_db::search
228 let defs = match references::find_defs(sema, syntax, position.offset) {
229 Some(defs) => defs,
230 None => return,
231 };
232 for def in defs {
233 let defs = def
234 .usages(sema)
235 .set_scope(search_scope.clone())
236 .all()
237 .references
238 .into_values()
239 .flatten();
240 for ref_ in defs {
241 let name_ref = match ref_.name {
242 ast::NameLike::NameRef(name_ref) => name_ref,
243 _ => continue,
244 };
245 if let Some(fn_def) =
246 sema.ancestors_with_macros(name_ref.syntax().clone()).find_map(ast::Fn::cast)
247 {
248 if let Some(runnable) = as_test_runnable(sema, &fn_def) {
249 // direct test
250 tests.insert(runnable);
251 } else if let Some(module) = parent_test_module(sema, &fn_def) {
252 // indirect test
253 find_related_tests_in_module(sema, syntax, &fn_def, &module, tests);
254 }
255 }
256 }
257 }
258 }
259
find_related_tests_in_module( sema: &Semantics<'_, RootDatabase>, syntax: &SyntaxNode, fn_def: &ast::Fn, parent_module: &hir::Module, tests: &mut FxHashSet<Runnable>, )260 fn find_related_tests_in_module(
261 sema: &Semantics<'_, RootDatabase>,
262 syntax: &SyntaxNode,
263 fn_def: &ast::Fn,
264 parent_module: &hir::Module,
265 tests: &mut FxHashSet<Runnable>,
266 ) {
267 let fn_name = match fn_def.name() {
268 Some(it) => it,
269 _ => return,
270 };
271 let mod_source = parent_module.definition_source(sema.db);
272 let range = match &mod_source.value {
273 hir::ModuleSource::Module(m) => m.syntax().text_range(),
274 hir::ModuleSource::BlockExpr(b) => b.syntax().text_range(),
275 hir::ModuleSource::SourceFile(f) => f.syntax().text_range(),
276 };
277
278 let file_id = mod_source.file_id.original_file(sema.db);
279 let mod_scope = SearchScope::file_range(FileRange { file_id, range });
280 let fn_pos = FilePosition { file_id, offset: fn_name.syntax().text_range().start() };
281 find_related_tests(sema, syntax, fn_pos, Some(mod_scope), tests)
282 }
283
as_test_runnable(sema: &Semantics<'_, RootDatabase>, fn_def: &ast::Fn) -> Option<Runnable>284 fn as_test_runnable(sema: &Semantics<'_, RootDatabase>, fn_def: &ast::Fn) -> Option<Runnable> {
285 if test_related_attribute(fn_def).is_some() {
286 let function = sema.to_def(fn_def)?;
287 runnable_fn(sema, function)
288 } else {
289 None
290 }
291 }
292
parent_test_module(sema: &Semantics<'_, RootDatabase>, fn_def: &ast::Fn) -> Option<hir::Module>293 fn parent_test_module(sema: &Semantics<'_, RootDatabase>, fn_def: &ast::Fn) -> Option<hir::Module> {
294 fn_def.syntax().ancestors().find_map(|node| {
295 let module = ast::Module::cast(node)?;
296 let module = sema.to_def(&module)?;
297
298 if has_test_function_or_multiple_test_submodules(sema, &module) {
299 Some(module)
300 } else {
301 None
302 }
303 })
304 }
305
runnable_fn( sema: &Semantics<'_, RootDatabase>, def: hir::Function, ) -> Option<Runnable>306 pub(crate) fn runnable_fn(
307 sema: &Semantics<'_, RootDatabase>,
308 def: hir::Function,
309 ) -> Option<Runnable> {
310 let func = def.source(sema.db)?;
311 let name = def.name(sema.db).to_smol_str();
312
313 let root = def.module(sema.db).krate().root_module(sema.db);
314
315 let kind = if name == "main" && def.module(sema.db) == root {
316 RunnableKind::Bin
317 } else {
318 let test_id = || {
319 let canonical_path = {
320 let def: hir::ModuleDef = def.into();
321 def.canonical_path(sema.db)
322 };
323 canonical_path.map(TestId::Path).unwrap_or(TestId::Name(name))
324 };
325
326 if test_related_attribute(&func.value).is_some() {
327 let attr = TestAttr::from_fn(&func.value);
328 RunnableKind::Test { test_id: test_id(), attr }
329 } else if func.value.has_atom_attr("bench") {
330 RunnableKind::Bench { test_id: test_id() }
331 } else {
332 return None;
333 }
334 };
335
336 let nav = NavigationTarget::from_named(
337 sema.db,
338 func.as_ref().map(|it| it as &dyn ast::HasName),
339 SymbolKind::Function,
340 );
341 let cfg = def.attrs(sema.db).cfg();
342 Some(Runnable { use_name_in_title: false, nav, kind, cfg })
343 }
344
runnable_mod( sema: &Semantics<'_, RootDatabase>, def: hir::Module, ) -> Option<Runnable>345 pub(crate) fn runnable_mod(
346 sema: &Semantics<'_, RootDatabase>,
347 def: hir::Module,
348 ) -> Option<Runnable> {
349 if !has_test_function_or_multiple_test_submodules(sema, &def) {
350 return None;
351 }
352 let path = def
353 .path_to_root(sema.db)
354 .into_iter()
355 .rev()
356 .filter_map(|it| it.name(sema.db))
357 .map(|it| it.display(sema.db).to_string())
358 .join("::");
359
360 let attrs = def.attrs(sema.db);
361 let cfg = attrs.cfg();
362 let nav = NavigationTarget::from_module_to_decl(sema.db, def);
363 Some(Runnable { use_name_in_title: false, nav, kind: RunnableKind::TestMod { path }, cfg })
364 }
365
runnable_impl( sema: &Semantics<'_, RootDatabase>, def: &hir::Impl, ) -> Option<Runnable>366 pub(crate) fn runnable_impl(
367 sema: &Semantics<'_, RootDatabase>,
368 def: &hir::Impl,
369 ) -> Option<Runnable> {
370 let attrs = def.attrs(sema.db);
371 if !has_runnable_doc_test(&attrs) {
372 return None;
373 }
374 let cfg = attrs.cfg();
375 let nav = def.try_to_nav(sema.db)?;
376 let ty = def.self_ty(sema.db);
377 let adt_name = ty.as_adt()?.name(sema.db);
378 let mut ty_args = ty.generic_parameters(sema.db).peekable();
379 let params = if ty_args.peek().is_some() {
380 format!("<{}>", ty_args.format_with(",", |ty, cb| cb(&ty)))
381 } else {
382 String::new()
383 };
384 let mut test_id = format!("{}{params}", adt_name.display(sema.db));
385 test_id.retain(|c| c != ' ');
386 let test_id = TestId::Path(test_id);
387
388 Some(Runnable { use_name_in_title: false, nav, kind: RunnableKind::DocTest { test_id }, cfg })
389 }
390
391 /// Creates a test mod runnable for outline modules at the top of their definition.
runnable_mod_outline_definition( sema: &Semantics<'_, RootDatabase>, def: hir::Module, ) -> Option<Runnable>392 fn runnable_mod_outline_definition(
393 sema: &Semantics<'_, RootDatabase>,
394 def: hir::Module,
395 ) -> Option<Runnable> {
396 if !has_test_function_or_multiple_test_submodules(sema, &def) {
397 return None;
398 }
399 let path = def
400 .path_to_root(sema.db)
401 .into_iter()
402 .rev()
403 .filter_map(|it| it.name(sema.db))
404 .map(|it| it.display(sema.db).to_string())
405 .join("::");
406
407 let attrs = def.attrs(sema.db);
408 let cfg = attrs.cfg();
409 match def.definition_source(sema.db).value {
410 hir::ModuleSource::SourceFile(_) => Some(Runnable {
411 use_name_in_title: false,
412 nav: def.to_nav(sema.db),
413 kind: RunnableKind::TestMod { path },
414 cfg,
415 }),
416 _ => None,
417 }
418 }
419
module_def_doctest(db: &RootDatabase, def: Definition) -> Option<Runnable>420 fn module_def_doctest(db: &RootDatabase, def: Definition) -> Option<Runnable> {
421 let attrs = match def {
422 Definition::Module(it) => it.attrs(db),
423 Definition::Function(it) => it.attrs(db),
424 Definition::Adt(it) => it.attrs(db),
425 Definition::Variant(it) => it.attrs(db),
426 Definition::Const(it) => it.attrs(db),
427 Definition::Static(it) => it.attrs(db),
428 Definition::Trait(it) => it.attrs(db),
429 Definition::TraitAlias(it) => it.attrs(db),
430 Definition::TypeAlias(it) => it.attrs(db),
431 Definition::Macro(it) => it.attrs(db),
432 Definition::SelfType(it) => it.attrs(db),
433 _ => return None,
434 };
435 if !has_runnable_doc_test(&attrs) {
436 return None;
437 }
438 let def_name = def.name(db)?;
439 let path = (|| {
440 let mut path = String::new();
441 def.canonical_module_path(db)?
442 .flat_map(|it| it.name(db))
443 .for_each(|name| format_to!(path, "{}::", name.display(db)));
444 // This probably belongs to canonical_path?
445 if let Some(assoc_item) = def.as_assoc_item(db) {
446 if let hir::AssocItemContainer::Impl(imp) = assoc_item.container(db) {
447 let ty = imp.self_ty(db);
448 if let Some(adt) = ty.as_adt() {
449 let name = adt.name(db);
450 let mut ty_args = ty.generic_parameters(db).peekable();
451 format_to!(path, "{}", name.display(db));
452 if ty_args.peek().is_some() {
453 format_to!(path, "<{}>", ty_args.format_with(",", |ty, cb| cb(&ty)));
454 }
455 format_to!(path, "::{}", def_name.display(db));
456 path.retain(|c| c != ' ');
457 return Some(path);
458 }
459 }
460 }
461 format_to!(path, "{}", def_name.display(db));
462 Some(path)
463 })();
464
465 let test_id = path.map_or_else(|| TestId::Name(def_name.to_smol_str()), TestId::Path);
466
467 let mut nav = match def {
468 Definition::Module(def) => NavigationTarget::from_module_to_decl(db, def),
469 def => def.try_to_nav(db)?,
470 };
471 nav.focus_range = None;
472 nav.description = None;
473 nav.docs = None;
474 nav.kind = None;
475 let res = Runnable {
476 use_name_in_title: false,
477 nav,
478 kind: RunnableKind::DocTest { test_id },
479 cfg: attrs.cfg(),
480 };
481 Some(res)
482 }
483
484 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
485 pub struct TestAttr {
486 pub ignore: bool,
487 }
488
489 impl TestAttr {
from_fn(fn_def: &ast::Fn) -> TestAttr490 fn from_fn(fn_def: &ast::Fn) -> TestAttr {
491 let ignore = fn_def
492 .attrs()
493 .filter_map(|attr| attr.simple_name())
494 .any(|attribute_text| attribute_text == "ignore");
495 TestAttr { ignore }
496 }
497 }
498
499 const RUSTDOC_FENCES: [&str; 2] = ["```", "~~~"];
500 const RUSTDOC_CODE_BLOCK_ATTRIBUTES_RUNNABLE: &[&str] =
501 &["", "rust", "should_panic", "edition2015", "edition2018", "edition2021"];
502
has_runnable_doc_test(attrs: &hir::Attrs) -> bool503 fn has_runnable_doc_test(attrs: &hir::Attrs) -> bool {
504 attrs.docs().map_or(false, |doc| {
505 let mut in_code_block = false;
506
507 for line in String::from(doc).lines() {
508 if let Some(header) =
509 RUSTDOC_FENCES.into_iter().find_map(|fence| line.strip_prefix(fence))
510 {
511 in_code_block = !in_code_block;
512
513 if in_code_block
514 && header
515 .split(',')
516 .all(|sub| RUSTDOC_CODE_BLOCK_ATTRIBUTES_RUNNABLE.contains(&sub.trim()))
517 {
518 return true;
519 }
520 }
521 }
522
523 false
524 })
525 }
526
527 // We could create runnables for modules with number_of_test_submodules > 0,
528 // but that bloats the runnables for no real benefit, since all tests can be run by the submodule already
has_test_function_or_multiple_test_submodules( sema: &Semantics<'_, RootDatabase>, module: &hir::Module, ) -> bool529 fn has_test_function_or_multiple_test_submodules(
530 sema: &Semantics<'_, RootDatabase>,
531 module: &hir::Module,
532 ) -> bool {
533 let mut number_of_test_submodules = 0;
534
535 for item in module.declarations(sema.db) {
536 match item {
537 hir::ModuleDef::Function(f) => {
538 if let Some(it) = f.source(sema.db) {
539 if test_related_attribute(&it.value).is_some() {
540 return true;
541 }
542 }
543 }
544 hir::ModuleDef::Module(submodule) => {
545 if has_test_function_or_multiple_test_submodules(sema, &submodule) {
546 number_of_test_submodules += 1;
547 }
548 }
549 _ => (),
550 }
551 }
552
553 number_of_test_submodules > 1
554 }
555
556 #[cfg(test)]
557 mod tests {
558 use expect_test::{expect, Expect};
559
560 use crate::fixture;
561
562 use super::{RunnableTestKind::*, *};
563
check( ra_fixture: &str, actions: &[RunnableTestKind], expect: Expect, )564 fn check(
565 ra_fixture: &str,
566 // FIXME: fold this into `expect` as well
567 actions: &[RunnableTestKind],
568 expect: Expect,
569 ) {
570 let (analysis, position) = fixture::position(ra_fixture);
571 let mut runnables = analysis.runnables(position.file_id).unwrap();
572 runnables.sort_by_key(|it| (it.nav.full_range.start(), it.nav.name.clone()));
573 expect.assert_debug_eq(&runnables);
574 assert_eq!(
575 actions,
576 runnables.into_iter().map(|it| it.test_kind()).collect::<Vec<_>>().as_slice()
577 );
578 }
579
check_tests(ra_fixture: &str, expect: Expect)580 fn check_tests(ra_fixture: &str, expect: Expect) {
581 let (analysis, position) = fixture::position(ra_fixture);
582 let tests = analysis.related_tests(position, None).unwrap();
583 expect.assert_debug_eq(&tests);
584 }
585
586 #[test]
test_runnables()587 fn test_runnables() {
588 check(
589 r#"
590 //- /lib.rs
591 $0
592 fn main() {}
593
594 #[test]
595 fn test_foo() {}
596
597 #[test]
598 #[ignore]
599 fn test_foo() {}
600
601 #[bench]
602 fn bench() {}
603
604 mod not_a_root {
605 fn main() {}
606 }
607 "#,
608 &[TestMod, Bin, Test, Test, Bench],
609 expect![[r#"
610 [
611 Runnable {
612 use_name_in_title: false,
613 nav: NavigationTarget {
614 file_id: FileId(
615 0,
616 ),
617 full_range: 0..137,
618 name: "",
619 kind: Module,
620 },
621 kind: TestMod {
622 path: "",
623 },
624 cfg: None,
625 },
626 Runnable {
627 use_name_in_title: false,
628 nav: NavigationTarget {
629 file_id: FileId(
630 0,
631 ),
632 full_range: 1..13,
633 focus_range: 4..8,
634 name: "main",
635 kind: Function,
636 },
637 kind: Bin,
638 cfg: None,
639 },
640 Runnable {
641 use_name_in_title: false,
642 nav: NavigationTarget {
643 file_id: FileId(
644 0,
645 ),
646 full_range: 15..39,
647 focus_range: 26..34,
648 name: "test_foo",
649 kind: Function,
650 },
651 kind: Test {
652 test_id: Path(
653 "test_foo",
654 ),
655 attr: TestAttr {
656 ignore: false,
657 },
658 },
659 cfg: None,
660 },
661 Runnable {
662 use_name_in_title: false,
663 nav: NavigationTarget {
664 file_id: FileId(
665 0,
666 ),
667 full_range: 41..75,
668 focus_range: 62..70,
669 name: "test_foo",
670 kind: Function,
671 },
672 kind: Test {
673 test_id: Path(
674 "test_foo",
675 ),
676 attr: TestAttr {
677 ignore: true,
678 },
679 },
680 cfg: None,
681 },
682 Runnable {
683 use_name_in_title: false,
684 nav: NavigationTarget {
685 file_id: FileId(
686 0,
687 ),
688 full_range: 77..99,
689 focus_range: 89..94,
690 name: "bench",
691 kind: Function,
692 },
693 kind: Bench {
694 test_id: Path(
695 "bench",
696 ),
697 },
698 cfg: None,
699 },
700 ]
701 "#]],
702 );
703 }
704
705 #[test]
test_runnables_doc_test()706 fn test_runnables_doc_test() {
707 check(
708 r#"
709 //- /lib.rs
710 $0
711 fn main() {}
712
713 /// ```
714 /// let x = 5;
715 /// ```
716 fn should_have_runnable() {}
717
718 /// ```edition2018
719 /// let x = 5;
720 /// ```
721 fn should_have_runnable_1() {}
722
723 /// ```
724 /// let z = 55;
725 /// ```
726 ///
727 /// ```ignore
728 /// let z = 56;
729 /// ```
730 fn should_have_runnable_2() {}
731
732 /**
733 ```rust
734 let z = 55;
735 ```
736 */
737 fn should_have_no_runnable_3() {}
738
739 /**
740 ```rust
741 let z = 55;
742 ```
743 */
744 fn should_have_no_runnable_4() {}
745
746 /// ```no_run
747 /// let z = 55;
748 /// ```
749 fn should_have_no_runnable() {}
750
751 /// ```ignore
752 /// let z = 55;
753 /// ```
754 fn should_have_no_runnable_2() {}
755
756 /// ```compile_fail
757 /// let z = 55;
758 /// ```
759 fn should_have_no_runnable_3() {}
760
761 /// ```text
762 /// arbitrary plain text
763 /// ```
764 fn should_have_no_runnable_4() {}
765
766 /// ```text
767 /// arbitrary plain text
768 /// ```
769 ///
770 /// ```sh
771 /// $ shell code
772 /// ```
773 fn should_have_no_runnable_5() {}
774
775 /// ```rust,no_run
776 /// let z = 55;
777 /// ```
778 fn should_have_no_runnable_6() {}
779
780 /// ```
781 /// let x = 5;
782 /// ```
783 struct StructWithRunnable(String);
784
785 /// ```
786 /// let x = 5;
787 /// ```
788 impl StructWithRunnable {}
789
790 trait Test {
791 fn test() -> usize {
792 5usize
793 }
794 }
795
796 /// ```
797 /// let x = 5;
798 /// ```
799 impl Test for StructWithRunnable {}
800 "#,
801 &[Bin, DocTest, DocTest, DocTest, DocTest, DocTest, DocTest, DocTest, DocTest],
802 expect![[r#"
803 [
804 Runnable {
805 use_name_in_title: false,
806 nav: NavigationTarget {
807 file_id: FileId(
808 0,
809 ),
810 full_range: 1..13,
811 focus_range: 4..8,
812 name: "main",
813 kind: Function,
814 },
815 kind: Bin,
816 cfg: None,
817 },
818 Runnable {
819 use_name_in_title: false,
820 nav: NavigationTarget {
821 file_id: FileId(
822 0,
823 ),
824 full_range: 15..74,
825 name: "should_have_runnable",
826 },
827 kind: DocTest {
828 test_id: Path(
829 "should_have_runnable",
830 ),
831 },
832 cfg: None,
833 },
834 Runnable {
835 use_name_in_title: false,
836 nav: NavigationTarget {
837 file_id: FileId(
838 0,
839 ),
840 full_range: 76..148,
841 name: "should_have_runnable_1",
842 },
843 kind: DocTest {
844 test_id: Path(
845 "should_have_runnable_1",
846 ),
847 },
848 cfg: None,
849 },
850 Runnable {
851 use_name_in_title: false,
852 nav: NavigationTarget {
853 file_id: FileId(
854 0,
855 ),
856 full_range: 150..254,
857 name: "should_have_runnable_2",
858 },
859 kind: DocTest {
860 test_id: Path(
861 "should_have_runnable_2",
862 ),
863 },
864 cfg: None,
865 },
866 Runnable {
867 use_name_in_title: false,
868 nav: NavigationTarget {
869 file_id: FileId(
870 0,
871 ),
872 full_range: 256..320,
873 name: "should_have_no_runnable_3",
874 },
875 kind: DocTest {
876 test_id: Path(
877 "should_have_no_runnable_3",
878 ),
879 },
880 cfg: None,
881 },
882 Runnable {
883 use_name_in_title: false,
884 nav: NavigationTarget {
885 file_id: FileId(
886 0,
887 ),
888 full_range: 322..398,
889 name: "should_have_no_runnable_4",
890 },
891 kind: DocTest {
892 test_id: Path(
893 "should_have_no_runnable_4",
894 ),
895 },
896 cfg: None,
897 },
898 Runnable {
899 use_name_in_title: false,
900 nav: NavigationTarget {
901 file_id: FileId(
902 0,
903 ),
904 full_range: 900..965,
905 name: "StructWithRunnable",
906 },
907 kind: DocTest {
908 test_id: Path(
909 "StructWithRunnable",
910 ),
911 },
912 cfg: None,
913 },
914 Runnable {
915 use_name_in_title: false,
916 nav: NavigationTarget {
917 file_id: FileId(
918 0,
919 ),
920 full_range: 967..1024,
921 focus_range: 1003..1021,
922 name: "impl",
923 kind: Impl,
924 },
925 kind: DocTest {
926 test_id: Path(
927 "StructWithRunnable",
928 ),
929 },
930 cfg: None,
931 },
932 Runnable {
933 use_name_in_title: false,
934 nav: NavigationTarget {
935 file_id: FileId(
936 0,
937 ),
938 full_range: 1088..1154,
939 focus_range: 1133..1151,
940 name: "impl",
941 kind: Impl,
942 },
943 kind: DocTest {
944 test_id: Path(
945 "StructWithRunnable",
946 ),
947 },
948 cfg: None,
949 },
950 ]
951 "#]],
952 );
953 }
954
955 #[test]
test_runnables_doc_test_in_impl()956 fn test_runnables_doc_test_in_impl() {
957 check(
958 r#"
959 //- /lib.rs
960 $0
961 fn main() {}
962
963 struct Data;
964 impl Data {
965 /// ```
966 /// let x = 5;
967 /// ```
968 fn foo() {}
969 }
970 "#,
971 &[Bin, DocTest],
972 expect![[r#"
973 [
974 Runnable {
975 use_name_in_title: false,
976 nav: NavigationTarget {
977 file_id: FileId(
978 0,
979 ),
980 full_range: 1..13,
981 focus_range: 4..8,
982 name: "main",
983 kind: Function,
984 },
985 kind: Bin,
986 cfg: None,
987 },
988 Runnable {
989 use_name_in_title: false,
990 nav: NavigationTarget {
991 file_id: FileId(
992 0,
993 ),
994 full_range: 44..98,
995 name: "foo",
996 },
997 kind: DocTest {
998 test_id: Path(
999 "Data::foo",
1000 ),
1001 },
1002 cfg: None,
1003 },
1004 ]
1005 "#]],
1006 );
1007 }
1008
1009 #[test]
test_runnables_doc_test_in_impl_with_lifetime()1010 fn test_runnables_doc_test_in_impl_with_lifetime() {
1011 check(
1012 r#"
1013 //- /lib.rs
1014 $0
1015 fn main() {}
1016
1017 struct Data<'a>;
1018 impl Data<'a> {
1019 /// ```
1020 /// let x = 5;
1021 /// ```
1022 fn foo() {}
1023 }
1024 "#,
1025 &[Bin, DocTest],
1026 expect![[r#"
1027 [
1028 Runnable {
1029 use_name_in_title: false,
1030 nav: NavigationTarget {
1031 file_id: FileId(
1032 0,
1033 ),
1034 full_range: 1..13,
1035 focus_range: 4..8,
1036 name: "main",
1037 kind: Function,
1038 },
1039 kind: Bin,
1040 cfg: None,
1041 },
1042 Runnable {
1043 use_name_in_title: false,
1044 nav: NavigationTarget {
1045 file_id: FileId(
1046 0,
1047 ),
1048 full_range: 52..106,
1049 name: "foo",
1050 },
1051 kind: DocTest {
1052 test_id: Path(
1053 "Data<'a>::foo",
1054 ),
1055 },
1056 cfg: None,
1057 },
1058 ]
1059 "#]],
1060 );
1061 }
1062
1063 #[test]
test_runnables_doc_test_in_impl_with_lifetime_and_types()1064 fn test_runnables_doc_test_in_impl_with_lifetime_and_types() {
1065 check(
1066 r#"
1067 //- /lib.rs
1068 $0
1069 fn main() {}
1070
1071 struct Data<'a, T, U>;
1072 impl<T, U> Data<'a, T, U> {
1073 /// ```
1074 /// let x = 5;
1075 /// ```
1076 fn foo() {}
1077 }
1078 "#,
1079 &[Bin, DocTest],
1080 expect![[r#"
1081 [
1082 Runnable {
1083 use_name_in_title: false,
1084 nav: NavigationTarget {
1085 file_id: FileId(
1086 0,
1087 ),
1088 full_range: 1..13,
1089 focus_range: 4..8,
1090 name: "main",
1091 kind: Function,
1092 },
1093 kind: Bin,
1094 cfg: None,
1095 },
1096 Runnable {
1097 use_name_in_title: false,
1098 nav: NavigationTarget {
1099 file_id: FileId(
1100 0,
1101 ),
1102 full_range: 70..124,
1103 name: "foo",
1104 },
1105 kind: DocTest {
1106 test_id: Path(
1107 "Data<'a,T,U>::foo",
1108 ),
1109 },
1110 cfg: None,
1111 },
1112 ]
1113 "#]],
1114 );
1115 }
1116
1117 #[test]
test_runnables_doc_test_in_impl_with_const()1118 fn test_runnables_doc_test_in_impl_with_const() {
1119 check(
1120 r#"
1121 //- /lib.rs
1122 $0
1123 fn main() {}
1124
1125 struct Data<const N: usize>;
1126 impl<const N: usize> Data<N> {
1127 /// ```
1128 /// let x = 5;
1129 /// ```
1130 fn foo() {}
1131 }
1132 "#,
1133 &[Bin, DocTest],
1134 expect![[r#"
1135 [
1136 Runnable {
1137 use_name_in_title: false,
1138 nav: NavigationTarget {
1139 file_id: FileId(
1140 0,
1141 ),
1142 full_range: 1..13,
1143 focus_range: 4..8,
1144 name: "main",
1145 kind: Function,
1146 },
1147 kind: Bin,
1148 cfg: None,
1149 },
1150 Runnable {
1151 use_name_in_title: false,
1152 nav: NavigationTarget {
1153 file_id: FileId(
1154 0,
1155 ),
1156 full_range: 79..133,
1157 name: "foo",
1158 },
1159 kind: DocTest {
1160 test_id: Path(
1161 "Data<N>::foo",
1162 ),
1163 },
1164 cfg: None,
1165 },
1166 ]
1167 "#]],
1168 );
1169 }
1170
1171 #[test]
test_runnables_doc_test_in_impl_with_lifetime_types_and_const()1172 fn test_runnables_doc_test_in_impl_with_lifetime_types_and_const() {
1173 check(
1174 r#"
1175 //- /lib.rs
1176 $0
1177 fn main() {}
1178
1179 struct Data<'a, T, const N: usize>;
1180 impl<'a, T, const N: usize> Data<'a, T, N> {
1181 /// ```
1182 /// let x = 5;
1183 /// ```
1184 fn foo() {}
1185 }
1186 "#,
1187 &[Bin, DocTest],
1188 expect![[r#"
1189 [
1190 Runnable {
1191 use_name_in_title: false,
1192 nav: NavigationTarget {
1193 file_id: FileId(
1194 0,
1195 ),
1196 full_range: 1..13,
1197 focus_range: 4..8,
1198 name: "main",
1199 kind: Function,
1200 },
1201 kind: Bin,
1202 cfg: None,
1203 },
1204 Runnable {
1205 use_name_in_title: false,
1206 nav: NavigationTarget {
1207 file_id: FileId(
1208 0,
1209 ),
1210 full_range: 100..154,
1211 name: "foo",
1212 },
1213 kind: DocTest {
1214 test_id: Path(
1215 "Data<'a,T,N>::foo",
1216 ),
1217 },
1218 cfg: None,
1219 },
1220 ]
1221 "#]],
1222 );
1223 }
1224 #[test]
test_runnables_module()1225 fn test_runnables_module() {
1226 check(
1227 r#"
1228 //- /lib.rs
1229 $0
1230 mod test_mod {
1231 #[test]
1232 fn test_foo1() {}
1233 }
1234 "#,
1235 &[TestMod, Test],
1236 expect![[r#"
1237 [
1238 Runnable {
1239 use_name_in_title: false,
1240 nav: NavigationTarget {
1241 file_id: FileId(
1242 0,
1243 ),
1244 full_range: 1..51,
1245 focus_range: 5..13,
1246 name: "test_mod",
1247 kind: Module,
1248 description: "mod test_mod",
1249 },
1250 kind: TestMod {
1251 path: "test_mod",
1252 },
1253 cfg: None,
1254 },
1255 Runnable {
1256 use_name_in_title: false,
1257 nav: NavigationTarget {
1258 file_id: FileId(
1259 0,
1260 ),
1261 full_range: 20..49,
1262 focus_range: 35..44,
1263 name: "test_foo1",
1264 kind: Function,
1265 },
1266 kind: Test {
1267 test_id: Path(
1268 "test_mod::test_foo1",
1269 ),
1270 attr: TestAttr {
1271 ignore: false,
1272 },
1273 },
1274 cfg: None,
1275 },
1276 ]
1277 "#]],
1278 );
1279 }
1280
1281 #[test]
only_modules_with_test_functions_or_more_than_one_test_submodule_have_runners()1282 fn only_modules_with_test_functions_or_more_than_one_test_submodule_have_runners() {
1283 check(
1284 r#"
1285 //- /lib.rs
1286 $0
1287 mod root_tests {
1288 mod nested_tests_0 {
1289 mod nested_tests_1 {
1290 #[test]
1291 fn nested_test_11() {}
1292
1293 #[test]
1294 fn nested_test_12() {}
1295 }
1296
1297 mod nested_tests_2 {
1298 #[test]
1299 fn nested_test_2() {}
1300 }
1301
1302 mod nested_tests_3 {}
1303 }
1304
1305 mod nested_tests_4 {}
1306 }
1307 "#,
1308 &[TestMod, TestMod, Test, Test, TestMod, Test],
1309 expect![[r#"
1310 [
1311 Runnable {
1312 use_name_in_title: false,
1313 nav: NavigationTarget {
1314 file_id: FileId(
1315 0,
1316 ),
1317 full_range: 22..323,
1318 focus_range: 26..40,
1319 name: "nested_tests_0",
1320 kind: Module,
1321 description: "mod nested_tests_0",
1322 },
1323 kind: TestMod {
1324 path: "root_tests::nested_tests_0",
1325 },
1326 cfg: None,
1327 },
1328 Runnable {
1329 use_name_in_title: false,
1330 nav: NavigationTarget {
1331 file_id: FileId(
1332 0,
1333 ),
1334 full_range: 51..192,
1335 focus_range: 55..69,
1336 name: "nested_tests_1",
1337 kind: Module,
1338 description: "mod nested_tests_1",
1339 },
1340 kind: TestMod {
1341 path: "root_tests::nested_tests_0::nested_tests_1",
1342 },
1343 cfg: None,
1344 },
1345 Runnable {
1346 use_name_in_title: false,
1347 nav: NavigationTarget {
1348 file_id: FileId(
1349 0,
1350 ),
1351 full_range: 84..126,
1352 focus_range: 107..121,
1353 name: "nested_test_11",
1354 kind: Function,
1355 },
1356 kind: Test {
1357 test_id: Path(
1358 "root_tests::nested_tests_0::nested_tests_1::nested_test_11",
1359 ),
1360 attr: TestAttr {
1361 ignore: false,
1362 },
1363 },
1364 cfg: None,
1365 },
1366 Runnable {
1367 use_name_in_title: false,
1368 nav: NavigationTarget {
1369 file_id: FileId(
1370 0,
1371 ),
1372 full_range: 140..182,
1373 focus_range: 163..177,
1374 name: "nested_test_12",
1375 kind: Function,
1376 },
1377 kind: Test {
1378 test_id: Path(
1379 "root_tests::nested_tests_0::nested_tests_1::nested_test_12",
1380 ),
1381 attr: TestAttr {
1382 ignore: false,
1383 },
1384 },
1385 cfg: None,
1386 },
1387 Runnable {
1388 use_name_in_title: false,
1389 nav: NavigationTarget {
1390 file_id: FileId(
1391 0,
1392 ),
1393 full_range: 202..286,
1394 focus_range: 206..220,
1395 name: "nested_tests_2",
1396 kind: Module,
1397 description: "mod nested_tests_2",
1398 },
1399 kind: TestMod {
1400 path: "root_tests::nested_tests_0::nested_tests_2",
1401 },
1402 cfg: None,
1403 },
1404 Runnable {
1405 use_name_in_title: false,
1406 nav: NavigationTarget {
1407 file_id: FileId(
1408 0,
1409 ),
1410 full_range: 235..276,
1411 focus_range: 258..271,
1412 name: "nested_test_2",
1413 kind: Function,
1414 },
1415 kind: Test {
1416 test_id: Path(
1417 "root_tests::nested_tests_0::nested_tests_2::nested_test_2",
1418 ),
1419 attr: TestAttr {
1420 ignore: false,
1421 },
1422 },
1423 cfg: None,
1424 },
1425 ]
1426 "#]],
1427 );
1428 }
1429
1430 #[test]
test_runnables_with_feature()1431 fn test_runnables_with_feature() {
1432 check(
1433 r#"
1434 //- /lib.rs crate:foo cfg:feature=foo
1435 $0
1436 #[test]
1437 #[cfg(feature = "foo")]
1438 fn test_foo1() {}
1439 "#,
1440 &[TestMod, Test],
1441 expect![[r#"
1442 [
1443 Runnable {
1444 use_name_in_title: false,
1445 nav: NavigationTarget {
1446 file_id: FileId(
1447 0,
1448 ),
1449 full_range: 0..51,
1450 name: "",
1451 kind: Module,
1452 },
1453 kind: TestMod {
1454 path: "",
1455 },
1456 cfg: None,
1457 },
1458 Runnable {
1459 use_name_in_title: false,
1460 nav: NavigationTarget {
1461 file_id: FileId(
1462 0,
1463 ),
1464 full_range: 1..50,
1465 focus_range: 36..45,
1466 name: "test_foo1",
1467 kind: Function,
1468 },
1469 kind: Test {
1470 test_id: Path(
1471 "test_foo1",
1472 ),
1473 attr: TestAttr {
1474 ignore: false,
1475 },
1476 },
1477 cfg: Some(
1478 Atom(
1479 KeyValue {
1480 key: "feature",
1481 value: "foo",
1482 },
1483 ),
1484 ),
1485 },
1486 ]
1487 "#]],
1488 );
1489 }
1490
1491 #[test]
test_runnables_with_features()1492 fn test_runnables_with_features() {
1493 check(
1494 r#"
1495 //- /lib.rs crate:foo cfg:feature=foo,feature=bar
1496 $0
1497 #[test]
1498 #[cfg(all(feature = "foo", feature = "bar"))]
1499 fn test_foo1() {}
1500 "#,
1501 &[TestMod, Test],
1502 expect![[r#"
1503 [
1504 Runnable {
1505 use_name_in_title: false,
1506 nav: NavigationTarget {
1507 file_id: FileId(
1508 0,
1509 ),
1510 full_range: 0..73,
1511 name: "",
1512 kind: Module,
1513 },
1514 kind: TestMod {
1515 path: "",
1516 },
1517 cfg: None,
1518 },
1519 Runnable {
1520 use_name_in_title: false,
1521 nav: NavigationTarget {
1522 file_id: FileId(
1523 0,
1524 ),
1525 full_range: 1..72,
1526 focus_range: 58..67,
1527 name: "test_foo1",
1528 kind: Function,
1529 },
1530 kind: Test {
1531 test_id: Path(
1532 "test_foo1",
1533 ),
1534 attr: TestAttr {
1535 ignore: false,
1536 },
1537 },
1538 cfg: Some(
1539 All(
1540 [
1541 Atom(
1542 KeyValue {
1543 key: "feature",
1544 value: "foo",
1545 },
1546 ),
1547 Atom(
1548 KeyValue {
1549 key: "feature",
1550 value: "bar",
1551 },
1552 ),
1553 ],
1554 ),
1555 ),
1556 },
1557 ]
1558 "#]],
1559 );
1560 }
1561
1562 #[test]
test_runnables_no_test_function_in_module()1563 fn test_runnables_no_test_function_in_module() {
1564 check(
1565 r#"
1566 //- /lib.rs
1567 $0
1568 mod test_mod {
1569 fn foo1() {}
1570 }
1571 "#,
1572 &[],
1573 expect![[r#"
1574 []
1575 "#]],
1576 );
1577 }
1578
1579 #[test]
test_doc_runnables_impl_mod()1580 fn test_doc_runnables_impl_mod() {
1581 check(
1582 r#"
1583 //- /lib.rs
1584 mod foo;
1585 //- /foo.rs
1586 struct Foo;$0
1587 impl Foo {
1588 /// ```
1589 /// let x = 5;
1590 /// ```
1591 fn foo() {}
1592 }
1593 "#,
1594 &[DocTest],
1595 expect![[r#"
1596 [
1597 Runnable {
1598 use_name_in_title: false,
1599 nav: NavigationTarget {
1600 file_id: FileId(
1601 1,
1602 ),
1603 full_range: 27..81,
1604 name: "foo",
1605 },
1606 kind: DocTest {
1607 test_id: Path(
1608 "foo::Foo::foo",
1609 ),
1610 },
1611 cfg: None,
1612 },
1613 ]
1614 "#]],
1615 );
1616 }
1617
1618 #[test]
test_runnables_in_macro()1619 fn test_runnables_in_macro() {
1620 check(
1621 r#"
1622 //- /lib.rs
1623 $0
1624 macro_rules! gen {
1625 () => {
1626 #[test]
1627 fn foo_test() {}
1628 }
1629 }
1630 macro_rules! gen2 {
1631 () => {
1632 mod tests2 {
1633 #[test]
1634 fn foo_test2() {}
1635 }
1636 }
1637 }
1638 mod tests {
1639 gen!();
1640 }
1641 gen2!();
1642 "#,
1643 &[TestMod, TestMod, Test, Test, TestMod],
1644 expect![[r#"
1645 [
1646 Runnable {
1647 use_name_in_title: false,
1648 nav: NavigationTarget {
1649 file_id: FileId(
1650 0,
1651 ),
1652 full_range: 0..237,
1653 name: "",
1654 kind: Module,
1655 },
1656 kind: TestMod {
1657 path: "",
1658 },
1659 cfg: None,
1660 },
1661 Runnable {
1662 use_name_in_title: false,
1663 nav: NavigationTarget {
1664 file_id: FileId(
1665 0,
1666 ),
1667 full_range: 202..227,
1668 focus_range: 206..211,
1669 name: "tests",
1670 kind: Module,
1671 description: "mod tests",
1672 },
1673 kind: TestMod {
1674 path: "tests",
1675 },
1676 cfg: None,
1677 },
1678 Runnable {
1679 use_name_in_title: false,
1680 nav: NavigationTarget {
1681 file_id: FileId(
1682 0,
1683 ),
1684 full_range: 218..225,
1685 name: "foo_test",
1686 kind: Function,
1687 },
1688 kind: Test {
1689 test_id: Path(
1690 "tests::foo_test",
1691 ),
1692 attr: TestAttr {
1693 ignore: false,
1694 },
1695 },
1696 cfg: None,
1697 },
1698 Runnable {
1699 use_name_in_title: true,
1700 nav: NavigationTarget {
1701 file_id: FileId(
1702 0,
1703 ),
1704 full_range: 228..236,
1705 name: "foo_test2",
1706 kind: Function,
1707 },
1708 kind: Test {
1709 test_id: Path(
1710 "tests2::foo_test2",
1711 ),
1712 attr: TestAttr {
1713 ignore: false,
1714 },
1715 },
1716 cfg: None,
1717 },
1718 Runnable {
1719 use_name_in_title: true,
1720 nav: NavigationTarget {
1721 file_id: FileId(
1722 0,
1723 ),
1724 full_range: 228..236,
1725 name: "tests2",
1726 kind: Module,
1727 description: "mod tests2",
1728 },
1729 kind: TestMod {
1730 path: "tests2",
1731 },
1732 cfg: None,
1733 },
1734 ]
1735 "#]],
1736 );
1737 }
1738
1739 #[test]
big_mac()1740 fn big_mac() {
1741 check(
1742 r#"
1743 //- /lib.rs
1744 $0
1745 macro_rules! foo {
1746 () => {
1747 mod foo_tests {
1748 #[test]
1749 fn foo0() {}
1750 #[test]
1751 fn foo1() {}
1752 #[test]
1753 fn foo2() {}
1754 }
1755 };
1756 }
1757 foo!();
1758 "#,
1759 &[Test, Test, Test, TestMod],
1760 expect![[r#"
1761 [
1762 Runnable {
1763 use_name_in_title: true,
1764 nav: NavigationTarget {
1765 file_id: FileId(
1766 0,
1767 ),
1768 full_range: 210..217,
1769 name: "foo0",
1770 kind: Function,
1771 },
1772 kind: Test {
1773 test_id: Path(
1774 "foo_tests::foo0",
1775 ),
1776 attr: TestAttr {
1777 ignore: false,
1778 },
1779 },
1780 cfg: None,
1781 },
1782 Runnable {
1783 use_name_in_title: true,
1784 nav: NavigationTarget {
1785 file_id: FileId(
1786 0,
1787 ),
1788 full_range: 210..217,
1789 name: "foo1",
1790 kind: Function,
1791 },
1792 kind: Test {
1793 test_id: Path(
1794 "foo_tests::foo1",
1795 ),
1796 attr: TestAttr {
1797 ignore: false,
1798 },
1799 },
1800 cfg: None,
1801 },
1802 Runnable {
1803 use_name_in_title: true,
1804 nav: NavigationTarget {
1805 file_id: FileId(
1806 0,
1807 ),
1808 full_range: 210..217,
1809 name: "foo2",
1810 kind: Function,
1811 },
1812 kind: Test {
1813 test_id: Path(
1814 "foo_tests::foo2",
1815 ),
1816 attr: TestAttr {
1817 ignore: false,
1818 },
1819 },
1820 cfg: None,
1821 },
1822 Runnable {
1823 use_name_in_title: true,
1824 nav: NavigationTarget {
1825 file_id: FileId(
1826 0,
1827 ),
1828 full_range: 210..217,
1829 name: "foo_tests",
1830 kind: Module,
1831 description: "mod foo_tests",
1832 },
1833 kind: TestMod {
1834 path: "foo_tests",
1835 },
1836 cfg: None,
1837 },
1838 ]
1839 "#]],
1840 );
1841 }
1842
1843 #[test]
dont_recurse_in_outline_submodules()1844 fn dont_recurse_in_outline_submodules() {
1845 check(
1846 r#"
1847 //- /lib.rs
1848 $0
1849 mod m;
1850 //- /m.rs
1851 mod tests {
1852 #[test]
1853 fn t() {}
1854 }
1855 "#,
1856 &[],
1857 expect![[r#"
1858 []
1859 "#]],
1860 );
1861 }
1862
1863 #[test]
outline_submodule1()1864 fn outline_submodule1() {
1865 check(
1866 r#"
1867 //- /lib.rs
1868 $0
1869 mod m;
1870 //- /m.rs
1871 #[test]
1872 fn t0() {}
1873 #[test]
1874 fn t1() {}
1875 "#,
1876 &[TestMod],
1877 expect![[r#"
1878 [
1879 Runnable {
1880 use_name_in_title: false,
1881 nav: NavigationTarget {
1882 file_id: FileId(
1883 0,
1884 ),
1885 full_range: 1..7,
1886 focus_range: 5..6,
1887 name: "m",
1888 kind: Module,
1889 description: "mod m",
1890 },
1891 kind: TestMod {
1892 path: "m",
1893 },
1894 cfg: None,
1895 },
1896 ]
1897 "#]],
1898 );
1899 }
1900
1901 #[test]
outline_submodule2()1902 fn outline_submodule2() {
1903 check(
1904 r#"
1905 //- /lib.rs
1906 mod m;
1907 //- /m.rs
1908 $0
1909 #[test]
1910 fn t0() {}
1911 #[test]
1912 fn t1() {}
1913 "#,
1914 &[TestMod, Test, Test],
1915 expect![[r#"
1916 [
1917 Runnable {
1918 use_name_in_title: false,
1919 nav: NavigationTarget {
1920 file_id: FileId(
1921 1,
1922 ),
1923 full_range: 0..39,
1924 name: "m",
1925 kind: Module,
1926 },
1927 kind: TestMod {
1928 path: "m",
1929 },
1930 cfg: None,
1931 },
1932 Runnable {
1933 use_name_in_title: false,
1934 nav: NavigationTarget {
1935 file_id: FileId(
1936 1,
1937 ),
1938 full_range: 1..19,
1939 focus_range: 12..14,
1940 name: "t0",
1941 kind: Function,
1942 },
1943 kind: Test {
1944 test_id: Path(
1945 "m::t0",
1946 ),
1947 attr: TestAttr {
1948 ignore: false,
1949 },
1950 },
1951 cfg: None,
1952 },
1953 Runnable {
1954 use_name_in_title: false,
1955 nav: NavigationTarget {
1956 file_id: FileId(
1957 1,
1958 ),
1959 full_range: 20..38,
1960 focus_range: 31..33,
1961 name: "t1",
1962 kind: Function,
1963 },
1964 kind: Test {
1965 test_id: Path(
1966 "m::t1",
1967 ),
1968 attr: TestAttr {
1969 ignore: false,
1970 },
1971 },
1972 cfg: None,
1973 },
1974 ]
1975 "#]],
1976 );
1977 }
1978
1979 #[test]
attributed_module()1980 fn attributed_module() {
1981 check(
1982 r#"
1983 //- proc_macros: identity
1984 //- /lib.rs
1985 $0
1986 #[proc_macros::identity]
1987 mod module {
1988 #[test]
1989 fn t0() {}
1990 #[test]
1991 fn t1() {}
1992 }
1993 "#,
1994 &[TestMod, Test, Test],
1995 expect![[r#"
1996 [
1997 Runnable {
1998 use_name_in_title: true,
1999 nav: NavigationTarget {
2000 file_id: FileId(
2001 0,
2002 ),
2003 full_range: 26..94,
2004 focus_range: 30..36,
2005 name: "module",
2006 kind: Module,
2007 description: "mod module",
2008 },
2009 kind: TestMod {
2010 path: "module",
2011 },
2012 cfg: None,
2013 },
2014 Runnable {
2015 use_name_in_title: true,
2016 nav: NavigationTarget {
2017 file_id: FileId(
2018 0,
2019 ),
2020 full_range: 43..65,
2021 focus_range: 58..60,
2022 name: "t0",
2023 kind: Function,
2024 },
2025 kind: Test {
2026 test_id: Path(
2027 "module::t0",
2028 ),
2029 attr: TestAttr {
2030 ignore: false,
2031 },
2032 },
2033 cfg: None,
2034 },
2035 Runnable {
2036 use_name_in_title: true,
2037 nav: NavigationTarget {
2038 file_id: FileId(
2039 0,
2040 ),
2041 full_range: 70..92,
2042 focus_range: 85..87,
2043 name: "t1",
2044 kind: Function,
2045 },
2046 kind: Test {
2047 test_id: Path(
2048 "module::t1",
2049 ),
2050 attr: TestAttr {
2051 ignore: false,
2052 },
2053 },
2054 cfg: None,
2055 },
2056 ]
2057 "#]],
2058 );
2059 }
2060
2061 #[test]
find_no_tests()2062 fn find_no_tests() {
2063 check_tests(
2064 r#"
2065 //- /lib.rs
2066 fn foo$0() { };
2067 "#,
2068 expect![[r#"
2069 []
2070 "#]],
2071 );
2072 }
2073
2074 #[test]
find_direct_fn_test()2075 fn find_direct_fn_test() {
2076 check_tests(
2077 r#"
2078 //- /lib.rs
2079 fn foo$0() { };
2080
2081 mod tests {
2082 #[test]
2083 fn foo_test() {
2084 super::foo()
2085 }
2086 }
2087 "#,
2088 expect![[r#"
2089 [
2090 Runnable {
2091 use_name_in_title: false,
2092 nav: NavigationTarget {
2093 file_id: FileId(
2094 0,
2095 ),
2096 full_range: 31..85,
2097 focus_range: 46..54,
2098 name: "foo_test",
2099 kind: Function,
2100 },
2101 kind: Test {
2102 test_id: Path(
2103 "tests::foo_test",
2104 ),
2105 attr: TestAttr {
2106 ignore: false,
2107 },
2108 },
2109 cfg: None,
2110 },
2111 ]
2112 "#]],
2113 );
2114 }
2115
2116 #[test]
find_direct_struct_test()2117 fn find_direct_struct_test() {
2118 check_tests(
2119 r#"
2120 //- /lib.rs
2121 struct Fo$0o;
2122 fn foo(arg: &Foo) { };
2123
2124 mod tests {
2125 use super::*;
2126
2127 #[test]
2128 fn foo_test() {
2129 foo(Foo);
2130 }
2131 }
2132 "#,
2133 expect![[r#"
2134 [
2135 Runnable {
2136 use_name_in_title: false,
2137 nav: NavigationTarget {
2138 file_id: FileId(
2139 0,
2140 ),
2141 full_range: 71..122,
2142 focus_range: 86..94,
2143 name: "foo_test",
2144 kind: Function,
2145 },
2146 kind: Test {
2147 test_id: Path(
2148 "tests::foo_test",
2149 ),
2150 attr: TestAttr {
2151 ignore: false,
2152 },
2153 },
2154 cfg: None,
2155 },
2156 ]
2157 "#]],
2158 );
2159 }
2160
2161 #[test]
find_indirect_fn_test()2162 fn find_indirect_fn_test() {
2163 check_tests(
2164 r#"
2165 //- /lib.rs
2166 fn foo$0() { };
2167
2168 mod tests {
2169 use super::foo;
2170
2171 fn check1() {
2172 check2()
2173 }
2174
2175 fn check2() {
2176 foo()
2177 }
2178
2179 #[test]
2180 fn foo_test() {
2181 check1()
2182 }
2183 }
2184 "#,
2185 expect![[r#"
2186 [
2187 Runnable {
2188 use_name_in_title: false,
2189 nav: NavigationTarget {
2190 file_id: FileId(
2191 0,
2192 ),
2193 full_range: 133..183,
2194 focus_range: 148..156,
2195 name: "foo_test",
2196 kind: Function,
2197 },
2198 kind: Test {
2199 test_id: Path(
2200 "tests::foo_test",
2201 ),
2202 attr: TestAttr {
2203 ignore: false,
2204 },
2205 },
2206 cfg: None,
2207 },
2208 ]
2209 "#]],
2210 );
2211 }
2212
2213 #[test]
tests_are_unique()2214 fn tests_are_unique() {
2215 check_tests(
2216 r#"
2217 //- /lib.rs
2218 fn foo$0() { };
2219
2220 mod tests {
2221 use super::foo;
2222
2223 #[test]
2224 fn foo_test() {
2225 foo();
2226 foo();
2227 }
2228
2229 #[test]
2230 fn foo2_test() {
2231 foo();
2232 foo();
2233 }
2234
2235 }
2236 "#,
2237 expect![[r#"
2238 [
2239 Runnable {
2240 use_name_in_title: false,
2241 nav: NavigationTarget {
2242 file_id: FileId(
2243 0,
2244 ),
2245 full_range: 121..185,
2246 focus_range: 136..145,
2247 name: "foo2_test",
2248 kind: Function,
2249 },
2250 kind: Test {
2251 test_id: Path(
2252 "tests::foo2_test",
2253 ),
2254 attr: TestAttr {
2255 ignore: false,
2256 },
2257 },
2258 cfg: None,
2259 },
2260 Runnable {
2261 use_name_in_title: false,
2262 nav: NavigationTarget {
2263 file_id: FileId(
2264 0,
2265 ),
2266 full_range: 52..115,
2267 focus_range: 67..75,
2268 name: "foo_test",
2269 kind: Function,
2270 },
2271 kind: Test {
2272 test_id: Path(
2273 "tests::foo_test",
2274 ),
2275 attr: TestAttr {
2276 ignore: false,
2277 },
2278 },
2279 cfg: None,
2280 },
2281 ]
2282 "#]],
2283 );
2284 }
2285
2286 #[test]
test_runnables_doc_test_in_impl_with_lifetime_type_const_value()2287 fn test_runnables_doc_test_in_impl_with_lifetime_type_const_value() {
2288 check(
2289 r#"
2290 //- /lib.rs
2291 $0
2292 fn main() {}
2293
2294 struct Data<'a, A, const B: usize, C, const D: u32>;
2295 impl<A, C, const D: u32> Data<'a, A, 12, C, D> {
2296 /// ```
2297 /// ```
2298 fn foo() {}
2299 }
2300 "#,
2301 &[Bin, DocTest],
2302 expect![[r#"
2303 [
2304 Runnable {
2305 use_name_in_title: false,
2306 nav: NavigationTarget {
2307 file_id: FileId(
2308 0,
2309 ),
2310 full_range: 1..13,
2311 focus_range: 4..8,
2312 name: "main",
2313 kind: Function,
2314 },
2315 kind: Bin,
2316 cfg: None,
2317 },
2318 Runnable {
2319 use_name_in_title: false,
2320 nav: NavigationTarget {
2321 file_id: FileId(
2322 0,
2323 ),
2324 full_range: 121..156,
2325 name: "foo",
2326 },
2327 kind: DocTest {
2328 test_id: Path(
2329 "Data<'a,A,12,C,D>::foo",
2330 ),
2331 },
2332 cfg: None,
2333 },
2334 ]
2335 "#]],
2336 );
2337 }
2338
2339 #[test]
doc_test_type_params()2340 fn doc_test_type_params() {
2341 check(
2342 r#"
2343 //- /lib.rs
2344 $0
2345 struct Foo<T, U>;
2346
2347 /// ```
2348 /// ```
2349 impl<T, U> Foo<T, U> {
2350 /// ```rust
2351 /// ````
2352 fn t() {}
2353 }
2354
2355 /// ```
2356 /// ```
2357 impl Foo<Foo<(), ()>, ()> {
2358 /// ```
2359 /// ```
2360 fn t() {}
2361 }
2362 "#,
2363 &[DocTest, DocTest, DocTest, DocTest],
2364 expect![[r#"
2365 [
2366 Runnable {
2367 use_name_in_title: false,
2368 nav: NavigationTarget {
2369 file_id: FileId(
2370 0,
2371 ),
2372 full_range: 20..103,
2373 focus_range: 47..56,
2374 name: "impl",
2375 kind: Impl,
2376 },
2377 kind: DocTest {
2378 test_id: Path(
2379 "Foo<T,U>",
2380 ),
2381 },
2382 cfg: None,
2383 },
2384 Runnable {
2385 use_name_in_title: false,
2386 nav: NavigationTarget {
2387 file_id: FileId(
2388 0,
2389 ),
2390 full_range: 63..101,
2391 name: "t",
2392 },
2393 kind: DocTest {
2394 test_id: Path(
2395 "Foo<T,U>::t",
2396 ),
2397 },
2398 cfg: None,
2399 },
2400 Runnable {
2401 use_name_in_title: false,
2402 nav: NavigationTarget {
2403 file_id: FileId(
2404 0,
2405 ),
2406 full_range: 105..188,
2407 focus_range: 126..146,
2408 name: "impl",
2409 kind: Impl,
2410 },
2411 kind: DocTest {
2412 test_id: Path(
2413 "Foo<Foo<(),()>,()>",
2414 ),
2415 },
2416 cfg: None,
2417 },
2418 Runnable {
2419 use_name_in_title: false,
2420 nav: NavigationTarget {
2421 file_id: FileId(
2422 0,
2423 ),
2424 full_range: 153..186,
2425 name: "t",
2426 },
2427 kind: DocTest {
2428 test_id: Path(
2429 "Foo<Foo<(),()>,()>::t",
2430 ),
2431 },
2432 cfg: None,
2433 },
2434 ]
2435 "#]],
2436 );
2437 }
2438
2439 #[test]
doc_test_macro_export_mbe()2440 fn doc_test_macro_export_mbe() {
2441 check(
2442 r#"
2443 //- /lib.rs
2444 $0
2445 mod foo;
2446
2447 //- /foo.rs
2448 /// ```
2449 /// fn foo() {
2450 /// }
2451 /// ```
2452 #[macro_export]
2453 macro_rules! foo {
2454 () => {
2455
2456 };
2457 }
2458 "#,
2459 &[],
2460 expect![[r#"
2461 []
2462 "#]],
2463 );
2464 check(
2465 r#"
2466 //- /lib.rs
2467 $0
2468 /// ```
2469 /// fn foo() {
2470 /// }
2471 /// ```
2472 #[macro_export]
2473 macro_rules! foo {
2474 () => {
2475
2476 };
2477 }
2478 "#,
2479 &[DocTest],
2480 expect![[r#"
2481 [
2482 Runnable {
2483 use_name_in_title: false,
2484 nav: NavigationTarget {
2485 file_id: FileId(
2486 0,
2487 ),
2488 full_range: 1..94,
2489 name: "foo",
2490 },
2491 kind: DocTest {
2492 test_id: Path(
2493 "foo",
2494 ),
2495 },
2496 cfg: None,
2497 },
2498 ]
2499 "#]],
2500 );
2501 }
2502
2503 #[test]
test_paths_with_raw_ident()2504 fn test_paths_with_raw_ident() {
2505 check(
2506 r#"
2507 //- /lib.rs
2508 $0
2509 mod r#mod {
2510 #[test]
2511 fn r#fn() {}
2512
2513 /// ```
2514 /// ```
2515 fn r#for() {}
2516
2517 /// ```
2518 /// ```
2519 struct r#struct<r#type>(r#type);
2520
2521 /// ```
2522 /// ```
2523 impl<r#type> r#struct<r#type> {
2524 /// ```
2525 /// ```
2526 fn r#fn() {}
2527 }
2528
2529 enum r#enum {}
2530 impl r#struct<r#enum> {
2531 /// ```
2532 /// ```
2533 fn r#fn() {}
2534 }
2535
2536 trait r#trait {}
2537
2538 /// ```
2539 /// ```
2540 impl<T> r#trait for r#struct<T> {}
2541 }
2542 "#,
2543 &[TestMod, Test, DocTest, DocTest, DocTest, DocTest, DocTest, DocTest],
2544 expect![[r#"
2545 [
2546 Runnable {
2547 use_name_in_title: false,
2548 nav: NavigationTarget {
2549 file_id: FileId(
2550 0,
2551 ),
2552 full_range: 1..461,
2553 focus_range: 5..10,
2554 name: "r#mod",
2555 kind: Module,
2556 description: "mod r#mod",
2557 },
2558 kind: TestMod {
2559 path: "r#mod",
2560 },
2561 cfg: None,
2562 },
2563 Runnable {
2564 use_name_in_title: false,
2565 nav: NavigationTarget {
2566 file_id: FileId(
2567 0,
2568 ),
2569 full_range: 17..41,
2570 focus_range: 32..36,
2571 name: "r#fn",
2572 kind: Function,
2573 },
2574 kind: Test {
2575 test_id: Path(
2576 "r#mod::r#fn",
2577 ),
2578 attr: TestAttr {
2579 ignore: false,
2580 },
2581 },
2582 cfg: None,
2583 },
2584 Runnable {
2585 use_name_in_title: false,
2586 nav: NavigationTarget {
2587 file_id: FileId(
2588 0,
2589 ),
2590 full_range: 47..84,
2591 name: "r#for",
2592 container_name: "r#mod",
2593 },
2594 kind: DocTest {
2595 test_id: Path(
2596 "r#mod::r#for",
2597 ),
2598 },
2599 cfg: None,
2600 },
2601 Runnable {
2602 use_name_in_title: false,
2603 nav: NavigationTarget {
2604 file_id: FileId(
2605 0,
2606 ),
2607 full_range: 90..146,
2608 name: "r#struct",
2609 container_name: "r#mod",
2610 },
2611 kind: DocTest {
2612 test_id: Path(
2613 "r#mod::r#struct",
2614 ),
2615 },
2616 cfg: None,
2617 },
2618 Runnable {
2619 use_name_in_title: false,
2620 nav: NavigationTarget {
2621 file_id: FileId(
2622 0,
2623 ),
2624 full_range: 152..266,
2625 focus_range: 189..205,
2626 name: "impl",
2627 kind: Impl,
2628 },
2629 kind: DocTest {
2630 test_id: Path(
2631 "r#struct<r#type>",
2632 ),
2633 },
2634 cfg: None,
2635 },
2636 Runnable {
2637 use_name_in_title: false,
2638 nav: NavigationTarget {
2639 file_id: FileId(
2640 0,
2641 ),
2642 full_range: 216..260,
2643 name: "r#fn",
2644 },
2645 kind: DocTest {
2646 test_id: Path(
2647 "r#mod::r#struct<r#type>::r#fn",
2648 ),
2649 },
2650 cfg: None,
2651 },
2652 Runnable {
2653 use_name_in_title: false,
2654 nav: NavigationTarget {
2655 file_id: FileId(
2656 0,
2657 ),
2658 full_range: 323..367,
2659 name: "r#fn",
2660 },
2661 kind: DocTest {
2662 test_id: Path(
2663 "r#mod::r#struct<r#enum>::r#fn",
2664 ),
2665 },
2666 cfg: None,
2667 },
2668 Runnable {
2669 use_name_in_title: false,
2670 nav: NavigationTarget {
2671 file_id: FileId(
2672 0,
2673 ),
2674 full_range: 401..459,
2675 focus_range: 445..456,
2676 name: "impl",
2677 kind: Impl,
2678 },
2679 kind: DocTest {
2680 test_id: Path(
2681 "r#struct<T>",
2682 ),
2683 },
2684 cfg: None,
2685 },
2686 ]
2687 "#]],
2688 )
2689 }
2690 }
2691