Skip to main content

ide/
runnables.rs

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