src/tools/rust-analyzer/crates/ide/src/runnables.rs RUST 1,715 lines View on github.com → Search inside
1use std::{fmt, sync::OnceLock};23use 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};2627use crate::{FileId, NavigationTarget, ToNav, TryToNav, references};2829#[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}3738impl_empty_upmap_from_ra_fixture!(RunnableKind, UpdateTest);3940#[derive(Debug, Clone, Hash, PartialEq, Eq)]41pub enum TestId {42    Name(SmolStr),43    Path(String),44}4546impl 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}5455#[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}6364#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]65enum RunnableDiscKind {66    TestMod,67    Test,68    DocTest,69    Bench,70    Bin,71}7273impl 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}8485impl 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    }9798    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        s115    }116}117118// Feature: Run119//120// Shows a popup suggesting to run a test/benchmark/binary **at the current cursor121// location**. Super useful for repeatedly running just a single test. Do bind this122// 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);131132    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    });175176    sema.file_to_module_defs(file_id)177        .map(|it| runnable_mod_outline_definition(&sema, it))178        .for_each(|it| add_opt(it, None));179180    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            r185        })186    }));187    res.sort_by(cmp_runnables);188    res189}190191// Feature: Related Tests192//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 on196// 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();209210    find_related_tests(&sema, &syntax, position, search_scope, &mut res);211212    res.into_iter().sorted_by(cmp_runnables).collect()213}214215fn 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 ordering220    nav.full_range221        .start()222        .cmp(&nav_b.full_range.start())223        .then_with(|| {224            let t_0 = || TextSize::from(0);225            nav.focus_range226                .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}232233fn 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::search241    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 = def247            .usages(sema)248            .set_scope(search_scope.as_ref())249            .all()250            .references251            .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 test263                    tests.insert(runnable);264                } else if let Some(module) = parent_test_module(sema, &fn_def) {265                    // indirect test266                    find_related_tests_in_module(sema, syntax, &fn_def, &module, tests);267                }268            }269        }270    }271}272273fn 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);285286    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}294295fn 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        None301    }302}303304fn 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)?;308309        if has_test_function_or_multiple_test_submodules(sema, &module, false) {310            Some(module)311        } else {312            None313        }314    })315}316317pub(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::Bin325    } 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_path332                .map(TestId::Path)333                .unwrap_or(TestId::Name(def.name(sema.db).display_no_db(edition).to_smolstr()))334        };335336        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    };344345    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();352353    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);355356    let cfg = def.attrs(sema.db).cfgs(sema.db).cloned();357    Some(Runnable { use_name_in_title: false, nav, kind, cfg, update_test })358}359360pub(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 = def369        .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("::");378379    let cfg = cfg.cloned();380    let nav = NavigationTarget::from_module_to_decl(sema.db, def).call_site();381382    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);389390    Some(Runnable {391        use_name_in_title: false,392        nav,393        kind: RunnableKind::TestMod { path },394        cfg,395        update_test,396    })397}398399pub(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);422423    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);427428    Some(Runnable {429        use_name_in_title: false,430        nav,431        kind: RunnableKind::DocTest { test_id },432        cfg,433        update_test,434    })435}436437fn has_cfg_test(cfg: Option<&CfgExpr>) -> bool {438    return cfg.is_some_and(has_cfg_test_impl);439440    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}448449/// 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)?;455456    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 = def461        .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("::");470471    let cfg = cfg.cloned();472473    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);480481    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}489490fn 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 = krate508        .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    })();537538    let test_id = path539        .map_or_else(|| TestId::Name(def_name.display_no_db(edition).to_smolstr()), TestId::Path);540541    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}558559fn 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"];563564    attrs.hir_docs(db).is_some_and(|doc| {565        let mut in_code_block = false;566567        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;572573                if in_code_block574                    && header575                        .split(',')576                        .all(|sub| RUSTDOC_CODE_BLOCK_ATTRIBUTES_RUNNABLE.contains(&sub.trim()))577                {578                    return true;579                }580            }581        }582583        false584    })585}586587// 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 already589fn 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;595596    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 test604                    // runners605                    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    }620621    number_of_test_submodules > 1622}623624#[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}630631static SNAPSHOT_TEST_MACROS: OnceLock<FxHashMap<&str, Vec<[Symbol; 2]>>> = OnceLock::new();632633impl UpdateTest {634    const EXPECT_CRATE: &str = "expect_test";635    const EXPECT_MACROS: &[&str] = &["expect", "expect_file"];636637    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    ];651652    const SNAPBOX_CRATE: &str = "snapbox";653    const SNAPBOX_MACROS: &[&str] = &["assert_data_eq", "file", "str"];654655    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        }669670        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            map676        });677678        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) = item684                        && Definition::Macro(makro)685                            .usages(sema)686                            .in_scope(&search_scope)687                            .at_least_one()688                    {689                        return true;690                    }691                }692            }693            false694        };695696        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    }702703    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        }714715        let res: SmolStr = builder.join(" + ").into();716        if res.is_empty() {717            None718        } else {719            Some(format_smolstr!("↺\u{fe0e} Update Tests ({res})"))720        }721    }722723    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        env735    }736}737738#[cfg(test)]739mod tests {740    use expect_test::{Expect, expect};741742    use crate::fixture;743744    fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {745        let (analysis, position) = fixture::position(ra_fixture);746        let result = analysis747            .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                a760            })761            .collect::<Vec<_>>();762        expect.assert_debug_eq(&result);763    }764765    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    }771772    #[test]773    fn test_runnables() {774        check(775            r#"776//- /lib.rs777$0778fn main() {}779780#[export_name = "main"]781fn __cortex_m_rt_main_trampoline() {}782783#[unsafe(export_name = "main")]784fn __cortex_m_rt_main_trampoline_unsafe() {}785786#[test]787fn test_foo() {}788789#[::core::prelude::v1::test]790fn test_full_path() {}791792#[test]793#[ignore]794fn test_foo() {}795796#[bench]797fn bench() {}798799mod 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    }817818    #[test]819    fn test_runnables_doc_test() {820        check(821            r#"822//- /lib.rs823$0824fn main() {}825826/// ```827/// let x = 5;828/// ```829fn should_have_runnable() {}830831/// ```edition2018832/// let x = 5;833/// ```834fn should_have_runnable_1() {}835836/// ```837/// let z = 55;838/// ```839///840/// ```ignore841/// let z = 56;842/// ```843fn should_have_runnable_2() {}844845/**846```rust847let z = 55;848```849*/850fn should_have_no_runnable_3() {}851852/**853    ```rust854    let z = 55;855    ```856*/857fn should_have_no_runnable_4() {}858859/// ```no_run860/// let z = 55;861/// ```862fn should_have_no_runnable() {}863864/// ```ignore865/// let z = 55;866/// ```867fn should_have_no_runnable_2() {}868869/// ```compile_fail870/// let z = 55;871/// ```872fn should_have_no_runnable_3() {}873874/// ```text875/// arbitrary plain text876/// ```877fn should_have_no_runnable_4() {}878879/// ```text880/// arbitrary plain text881/// ```882///883/// ```sh884/// $ shell code885/// ```886fn should_have_no_runnable_5() {}887888/// ```rust,no_run889/// let z = 55;890/// ```891fn should_have_no_runnable_6() {}892893/// ```894/// let x = 5;895/// ```896struct StructWithRunnable(String);897898/// ```899/// let x = 5;900/// ```901impl StructWithRunnable {}902903trait Test {904    fn test() -> usize {905        5usize906    }907}908909/// ```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    }929930    #[test]931    fn test_runnables_doc_test_in_impl() {932        check(933            r#"934//- /lib.rs935$0936fn main() {}937938struct 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    }954955    #[test]956    fn test_runnables_doc_test_in_impl_with_lifetime() {957        check(958            r#"959//- /lib.rs960$0961fn main() {}962963struct 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    }979980    #[test]981    fn test_runnables_doc_test_in_impl_with_lifetime_and_types() {982        check(983            r#"984//- /lib.rs985$0986fn main() {}987988struct 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    }10041005    #[test]1006    fn test_runnables_doc_test_in_impl_with_const() {1007        check(1008            r#"1009//- /lib.rs1010$01011fn main() {}10121013struct 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    }10291030    #[test]1031    fn test_runnables_doc_test_in_impl_with_lifetime_types_and_const() {1032        check(1033            r#"1034//- /lib.rs1035$01036fn main() {}10371038struct 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.rs1059$01060mod 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    }10731074    #[test]1075    fn only_modules_with_test_functions_or_more_than_one_test_submodule_have_runners() {1076        check(1077            r#"1078//- /lib.rs1079$01080mod root_tests {1081    mod nested_tests_0 {1082        mod nested_tests_1 {1083            #[test]1084            fn nested_test_11() {}10851086            #[test]1087            fn nested_test_12() {}1088        }10891090        mod nested_tests_2 {1091            #[test]1092            fn nested_test_2() {}1093        }10941095        mod nested_tests_3 {}1096    }10971098    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    }11131114    #[test]1115    fn test_runnables_with_feature() {1116        check(1117            r#"1118//- /lib.rs crate:foo cfg:feature=foo1119$01120#[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    }11321133    #[test]1134    fn test_runnables_with_features() {1135        check(1136            r#"1137//- /lib.rs crate:foo cfg:feature=foo,feature=bar1138$01139#[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    }11511152    #[test]1153    fn test_runnables_no_test_function_in_module() {1154        check(1155            r#"1156//- /lib.rs1157$01158mod test_mod {1159    fn foo1() {}1160}1161"#,1162            expect![[r#"1163                []1164            "#]],1165        );1166    }11671168    #[test]1169    fn test_doc_runnables_impl_mod() {1170        check(1171            r#"1172//- /lib.rs1173mod foo;1174//- /foo.rs1175struct Foo;$01176impl 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    }11901191    #[test]1192    fn test_runnables_in_macro() {1193        check(1194            r#"1195//- /lib.rs1196$01197macro_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    }12341235    #[test]1236    fn big_mac() {1237        check(1238            r#"1239//- /lib.rs1240$01241macro_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    }12651266    #[test]1267    fn dont_recurse_in_outline_submodules() {1268        check(1269            r#"1270//- /lib.rs1271$01272mod m;1273//- /m.rs1274mod tests {1275    #[test]1276    fn t() {}1277}1278"#,1279            expect![[r#"1280                []1281            "#]],1282        );1283    }12841285    #[test]1286    fn outline_submodule1() {1287        check(1288            r#"1289//- /lib.rs1290$01291mod m;1292//- /m.rs1293#[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    }13051306    #[test]1307    fn outline_submodule2() {1308        check(1309            r#"1310//- /lib.rs1311mod m;1312//- /m.rs1313$01314#[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    }13281329    #[test]1330    fn attributed_module() {1331        check(1332            r#"1333//- proc_macros: identity1334//- /lib.rs1335$01336#[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    }13531354    #[test]1355    fn find_no_tests() {1356        check_tests(1357            r#"1358//- /lib.rs1359fn foo$0() {  };1360"#,1361            expect![[r#"1362                []1363            "#]],1364        );1365    }13661367    #[test]1368    fn find_direct_fn_test() {1369        check_tests(1370            r#"1371//- /lib.rs1372fn foo$0() { };13731374mod 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    }13961397    #[test]1398    fn find_direct_struct_test() {1399        check_tests(1400            r#"1401//- /lib.rs1402struct Fo$0o;1403fn foo(arg: &Foo) { };14041405mod tests {1406    use super::*;14071408    #[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    }14291430    #[test]1431    fn find_indirect_fn_test() {1432        check_tests(1433            r#"1434//- /lib.rs1435fn foo$0() { };14361437mod tests {1438    use super::foo;14391440    fn check1() {1441        check2()1442    }14431444    fn check2() {1445        foo()1446    }14471448    #[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    }14691470    #[test]1471    fn tests_are_unique() {1472        check_tests(1473            r#"1474//- /lib.rs1475fn foo$0() { };14761477mod tests {1478    use super::foo;14791480    #[test]1481    fn foo_test() {1482        foo();1483        foo();1484    }14851486    #[test]1487    fn foo2_test() {1488        foo();1489        foo();1490    }14911492}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    }15181519    #[test]1520    fn test_runnables_doc_test_in_impl_with_lifetime_type_const_value() {1521        check(1522            r#"1523//- /lib.rs1524$01525fn main() {}15261527struct 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    }15421543    #[test]1544    fn doc_test_type_params() {1545        check(1546            r#"1547//- /lib.rs1548$01549struct Foo<T, U>;15501551/// ```1552/// ```1553impl<T, U> Foo<T, U> {1554    /// ```rust1555    /// ````1556    fn t() {}1557}15581559/// ```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    }15771578    #[test]1579    fn doc_test_macro_export_mbe() {1580        check(1581            r#"1582//- /lib.rs1583$01584mod foo;15851586//- /foo.rs1587/// ```1588/// fn foo() {1589/// }1590/// ```1591#[macro_export]1592macro_rules! foo {1593    () => {15941595    };1596}1597"#,1598            expect![[r#"1599                []1600            "#]],1601        );1602        check(1603            r#"1604//- /lib.rs1605$01606/// ```1607/// fn foo() {1608/// }1609/// ```1610#[macro_export]1611macro_rules! foo {1612    () => {16131614    };1615}1616"#,1617            expect![[r#"1618                [1619                    "(DocTest, NavigationTarget { file_id: FileId(0), full_range: 1..94, name: \"foo\" })",1620                ]1621            "#]],1622        );1623    }16241625    #[test]1626    fn test_paths_with_raw_ident() {1627        check(1628            r#"1629//- /lib.rs1630$01631mod r#mod {1632    #[test]1633    fn r#fn() {}16341635    /// ```1636    /// ```1637    fn r#for() {}16381639    /// ```1640    /// ```1641    struct r#struct<r#type>(r#type);16421643    /// ```1644    /// ```1645    impl<r#type> r#struct<r#type> {1646        /// ```1647        /// ```1648        fn r#fn() {}1649    }16501651    enum r#enum {}1652    impl r#struct<r#enum> {1653        /// ```1654        /// ```1655        fn r#fn() {}1656    }16571658    trait r#trait {}16591660    /// ```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    }16791680    #[test]1681    fn exported_main_is_test_in_cfg_test_mod() {1682        check(1683            r#"1684//- /lib.rs crate:foo cfg:test1685$01686mod 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.rs1699#[export_name = "main"]1700fn exp_main() {}1701//- /test_mod.rs1702#[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}

Findings

✓ No findings reported for this file.

Get this view in your editor

Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.