src/tools/rust-analyzer/crates/ide/src/runnables.rs RUST 1,716 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.docs = None;549    nav.kind = None;550    let res = Runnable {551        use_name_in_title: false,552        nav,553        kind: RunnableKind::DocTest { test_id },554        cfg: attrs.cfgs(db).cloned(),555        update_test: UpdateTest::default(),556    };557    Some(res)558}559560fn has_runnable_doc_test(db: &RootDatabase, attrs: &hir::AttrsWithOwner) -> bool {561    const RUSTDOC_FENCES: [&str; 2] = ["```", "~~~"];562    const RUSTDOC_CODE_BLOCK_ATTRIBUTES_RUNNABLE: &[&str] =563        &["", "rust", "should_panic", "edition2015", "edition2018", "edition2021"];564565    attrs.hir_docs(db).is_some_and(|doc| {566        let mut in_code_block = false;567568        for line in doc.docs().lines() {569            if let Some(header) =570                RUSTDOC_FENCES.into_iter().find_map(|fence| line.strip_prefix(fence))571            {572                in_code_block = !in_code_block;573574                if in_code_block575                    && header576                        .split(',')577                        .all(|sub| RUSTDOC_CODE_BLOCK_ATTRIBUTES_RUNNABLE.contains(&sub.trim()))578                {579                    return true;580                }581            }582        }583584        false585    })586}587588// We could create runnables for modules with number_of_test_submodules > 0,589// but that bloats the runnables for no real benefit, since all tests can be run by the submodule already590fn has_test_function_or_multiple_test_submodules(591    sema: &Semantics<'_, RootDatabase>,592    module: &hir::Module,593    consider_exported_main: bool,594) -> bool {595    let mut number_of_test_submodules = 0;596597    for item in module.declarations(sema.db) {598        match item {599            hir::ModuleDef::Function(f) => {600                if has_test_related_attribute(&f.attrs(sema.db)) {601                    return true;602                }603                if consider_exported_main && f.exported_main(sema.db) {604                    // an exported main in a test module can be considered a test wrt to custom test605                    // runners606                    return true;607                }608            }609            hir::ModuleDef::Module(submodule)610                if has_test_function_or_multiple_test_submodules(611                    sema,612                    &submodule,613                    consider_exported_main,614                ) =>615            {616                number_of_test_submodules += 1;617            }618            _ => (),619        }620    }621622    number_of_test_submodules > 1623}624625#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]626pub struct UpdateTest {627    pub expect_test: bool,628    pub insta: bool,629    pub snapbox: bool,630}631632static SNAPSHOT_TEST_MACROS: OnceLock<FxHashMap<&str, Vec<[Symbol; 2]>>> = OnceLock::new();633634impl UpdateTest {635    const EXPECT_CRATE: &str = "expect_test";636    const EXPECT_MACROS: &[&str] = &["expect", "expect_file"];637638    const INSTA_CRATE: &str = "insta";639    const INSTA_MACROS: &[&str] = &[640        "assert_snapshot",641        "assert_debug_snapshot",642        "assert_display_snapshot",643        "assert_json_snapshot",644        "assert_yaml_snapshot",645        "assert_ron_snapshot",646        "assert_toml_snapshot",647        "assert_csv_snapshot",648        "assert_compact_json_snapshot",649        "assert_compact_debug_snapshot",650        "assert_binary_snapshot",651    ];652653    const SNAPBOX_CRATE: &str = "snapbox";654    const SNAPBOX_MACROS: &[&str] = &["assert_data_eq", "file", "str"];655656    fn find_snapshot_macro(sema: &Semantics<'_, RootDatabase>, file_range: hir::FileRange) -> Self {657        fn init<'a>(658            krate_name: &'a str,659            paths: &[&str],660            map: &mut FxHashMap<&'a str, Vec<[Symbol; 2]>>,661        ) {662            let mut res = Vec::with_capacity(paths.len());663            let krate = Symbol::intern(krate_name);664            for path in paths {665                let segments = [krate.clone(), Symbol::intern(path)];666                res.push(segments);667            }668            map.insert(krate_name, res);669        }670671        let mod_paths = SNAPSHOT_TEST_MACROS.get_or_init(|| {672            let mut map = FxHashMap::default();673            init(Self::EXPECT_CRATE, Self::EXPECT_MACROS, &mut map);674            init(Self::INSTA_CRATE, Self::INSTA_MACROS, &mut map);675            init(Self::SNAPBOX_CRATE, Self::SNAPBOX_MACROS, &mut map);676            map677        });678679        let search_scope = SearchScope::file_range(file_range);680        let find_macro = |paths: &[[Symbol; 2]]| {681            for path in paths {682                let items = hir::resolve_absolute_path(sema.db, path.iter().cloned());683                for item in items {684                    if let hir::ItemInNs::Macros(makro) = item685                        && Definition::Macro(makro)686                            .usages(sema)687                            .in_scope(&search_scope)688                            .at_least_one()689                    {690                        return true;691                    }692                }693            }694            false695        };696697        UpdateTest {698            expect_test: find_macro(mod_paths.get(Self::EXPECT_CRATE).unwrap()),699            insta: find_macro(mod_paths.get(Self::INSTA_CRATE).unwrap()),700            snapbox: find_macro(mod_paths.get(Self::SNAPBOX_CRATE).unwrap()),701        }702    }703704    pub fn label(&self) -> Option<SmolStr> {705        let mut builder: SmallVec<[_; 3]> = SmallVec::new();706        if self.expect_test {707            builder.push("Expect");708        }709        if self.insta {710            builder.push("Insta");711        }712        if self.snapbox {713            builder.push("Snapbox");714        }715716        let res: SmolStr = builder.join(" + ").into();717        if res.is_empty() {718            None719        } else {720            Some(format_smolstr!("↺\u{fe0e} Update Tests ({res})"))721        }722    }723724    pub fn env(&self) -> ArrayVec<(&str, &str), 3> {725        let mut env = ArrayVec::new();726        if self.expect_test {727            env.push(("UPDATE_EXPECT", "1"));728        }729        if self.insta {730            env.push(("INSTA_UPDATE", "always"));731        }732        if self.snapbox {733            env.push(("SNAPSHOTS", "overwrite"));734        }735        env736    }737}738739#[cfg(test)]740mod tests {741    use expect_test::{Expect, expect};742743    use crate::fixture;744745    fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {746        let (analysis, position) = fixture::position(ra_fixture);747        let result = analysis748            .runnables(position.file_id)749            .unwrap()750            .into_iter()751            .map(|runnable| {752                let mut a = format!("({:?}, {:?}", runnable.kind.disc(), runnable.nav);753                if runnable.use_name_in_title {754                    a.push_str(", true");755                }756                if let Some(cfg) = runnable.cfg {757                    a.push_str(&format!(", {cfg:?}"));758                }759                a.push(')');760                a761            })762            .collect::<Vec<_>>();763        expect.assert_debug_eq(&result);764    }765766    fn check_tests(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {767        let (analysis, position) = fixture::position(ra_fixture);768        let tests = analysis.related_tests(position, None).unwrap();769        let navigation_targets = tests.into_iter().map(|runnable| runnable.nav).collect::<Vec<_>>();770        expect.assert_debug_eq(&navigation_targets);771    }772773    #[test]774    fn test_runnables() {775        check(776            r#"777//- /lib.rs778$0779fn main() {}780781#[export_name = "main"]782fn __cortex_m_rt_main_trampoline() {}783784#[unsafe(export_name = "main")]785fn __cortex_m_rt_main_trampoline_unsafe() {}786787#[test]788fn test_foo() {}789790#[::core::prelude::v1::test]791fn test_full_path() {}792793#[test]794#[ignore]795fn test_foo() {}796797#[bench]798fn bench() {}799800mod not_a_root {801    fn main() {}802}803"#,804            expect![[r#"805                [806                    "(TestMod, NavigationTarget { file_id: FileId(0), full_range: 0..331, name: \"_\", kind: CrateRoot })",807                    "(Bin, NavigationTarget { file_id: FileId(0), full_range: 1..13, focus_range: 4..8, name: \"main\", kind: Function })",808                    "(Bin, NavigationTarget { file_id: FileId(0), full_range: 15..76, focus_range: 42..71, name: \"__cortex_m_rt_main_trampoline\", kind: Function })",809                    "(Bin, NavigationTarget { file_id: FileId(0), full_range: 78..154, focus_range: 113..149, name: \"__cortex_m_rt_main_trampoline_unsafe\", kind: Function })",810                    "(Test, NavigationTarget { file_id: FileId(0), full_range: 156..180, focus_range: 167..175, name: \"test_foo\", kind: Function })",811                    "(Test, NavigationTarget { file_id: FileId(0), full_range: 182..233, focus_range: 214..228, name: \"test_full_path\", kind: Function })",812                    "(Test, NavigationTarget { file_id: FileId(0), full_range: 235..269, focus_range: 256..264, name: \"test_foo\", kind: Function })",813                    "(Bench, NavigationTarget { file_id: FileId(0), full_range: 271..293, focus_range: 283..288, name: \"bench\", kind: Function })",814                ]815            "#]],816        );817    }818819    #[test]820    fn test_runnables_doc_test() {821        check(822            r#"823//- /lib.rs824$0825fn main() {}826827/// ```828/// let x = 5;829/// ```830fn should_have_runnable() {}831832/// ```edition2018833/// let x = 5;834/// ```835fn should_have_runnable_1() {}836837/// ```838/// let z = 55;839/// ```840///841/// ```ignore842/// let z = 56;843/// ```844fn should_have_runnable_2() {}845846/**847```rust848let z = 55;849```850*/851fn should_have_no_runnable_3() {}852853/**854    ```rust855    let z = 55;856    ```857*/858fn should_have_no_runnable_4() {}859860/// ```no_run861/// let z = 55;862/// ```863fn should_have_no_runnable() {}864865/// ```ignore866/// let z = 55;867/// ```868fn should_have_no_runnable_2() {}869870/// ```compile_fail871/// let z = 55;872/// ```873fn should_have_no_runnable_3() {}874875/// ```text876/// arbitrary plain text877/// ```878fn should_have_no_runnable_4() {}879880/// ```text881/// arbitrary plain text882/// ```883///884/// ```sh885/// $ shell code886/// ```887fn should_have_no_runnable_5() {}888889/// ```rust,no_run890/// let z = 55;891/// ```892fn should_have_no_runnable_6() {}893894/// ```895/// let x = 5;896/// ```897struct StructWithRunnable(String);898899/// ```900/// let x = 5;901/// ```902impl StructWithRunnable {}903904trait Test {905    fn test() -> usize {906        5usize907    }908}909910/// ```911/// let x = 5;912/// ```913impl Test for StructWithRunnable {}914"#,915            expect![[r#"916                [917                    "(Bin, NavigationTarget { file_id: FileId(0), full_range: 1..13, focus_range: 4..8, name: \"main\", kind: Function })",918                    "(DocTest, NavigationTarget { file_id: FileId(0), full_range: 15..74, name: \"should_have_runnable\" })",919                    "(DocTest, NavigationTarget { file_id: FileId(0), full_range: 76..148, name: \"should_have_runnable_1\" })",920                    "(DocTest, NavigationTarget { file_id: FileId(0), full_range: 150..254, name: \"should_have_runnable_2\" })",921                    "(DocTest, NavigationTarget { file_id: FileId(0), full_range: 256..320, name: \"should_have_no_runnable_3\" })",922                    "(DocTest, NavigationTarget { file_id: FileId(0), full_range: 322..398, name: \"should_have_no_runnable_4\" })",923                    "(DocTest, NavigationTarget { file_id: FileId(0), full_range: 900..965, name: \"StructWithRunnable\" })",924                    "(DocTest, NavigationTarget { file_id: FileId(0), full_range: 967..1024, focus_range: 1003..1021, name: \"impl\", kind: Impl })",925                    "(DocTest, NavigationTarget { file_id: FileId(0), full_range: 1088..1154, focus_range: 1133..1151, name: \"impl\", kind: Impl })",926                ]927            "#]],928        );929    }930931    #[test]932    fn test_runnables_doc_test_in_impl() {933        check(934            r#"935//- /lib.rs936$0937fn main() {}938939struct Data;940impl Data {941    /// ```942    /// let x = 5;943    /// ```944    fn foo() {}945}946"#,947            expect![[r#"948                [949                    "(Bin, NavigationTarget { file_id: FileId(0), full_range: 1..13, focus_range: 4..8, name: \"main\", kind: Function })",950                    "(DocTest, NavigationTarget { file_id: FileId(0), full_range: 44..98, name: \"foo\" })",951                ]952            "#]],953        );954    }955956    #[test]957    fn test_runnables_doc_test_in_impl_with_lifetime() {958        check(959            r#"960//- /lib.rs961$0962fn main() {}963964struct Data<'a>;965impl Data<'a> {966    /// ```967    /// let x = 5;968    /// ```969    fn foo() {}970}971"#,972            expect![[r#"973                [974                    "(Bin, NavigationTarget { file_id: FileId(0), full_range: 1..13, focus_range: 4..8, name: \"main\", kind: Function })",975                    "(DocTest, NavigationTarget { file_id: FileId(0), full_range: 52..106, name: \"foo\" })",976                ]977            "#]],978        );979    }980981    #[test]982    fn test_runnables_doc_test_in_impl_with_lifetime_and_types() {983        check(984            r#"985//- /lib.rs986$0987fn main() {}988989struct Data<'a, T, U>;990impl<T, U> Data<'a, T, U> {991    /// ```992    /// let x = 5;993    /// ```994    fn foo() {}995}996"#,997            expect![[r#"998                [999                    "(Bin, NavigationTarget { file_id: FileId(0), full_range: 1..13, focus_range: 4..8, name: \"main\", kind: Function })",1000                    "(DocTest, NavigationTarget { file_id: FileId(0), full_range: 70..124, name: \"foo\" })",1001                ]1002            "#]],1003        );1004    }10051006    #[test]1007    fn test_runnables_doc_test_in_impl_with_const() {1008        check(1009            r#"1010//- /lib.rs1011$01012fn main() {}10131014struct Data<const N: usize>;1015impl<const N: usize> Data<N> {1016    /// ```1017    /// let x = 5;1018    /// ```1019    fn foo() {}1020}1021"#,1022            expect![[r#"1023                [1024                    "(Bin, NavigationTarget { file_id: FileId(0), full_range: 1..13, focus_range: 4..8, name: \"main\", kind: Function })",1025                    "(DocTest, NavigationTarget { file_id: FileId(0), full_range: 79..133, name: \"foo\" })",1026                ]1027            "#]],1028        );1029    }10301031    #[test]1032    fn test_runnables_doc_test_in_impl_with_lifetime_types_and_const() {1033        check(1034            r#"1035//- /lib.rs1036$01037fn main() {}10381039struct Data<'a, T, const N: usize>;1040impl<'a, T, const N: usize> Data<'a, T, N> {1041    /// ```1042    /// let x = 5;1043    /// ```1044    fn foo() {}1045}1046"#,1047            expect![[r#"1048                [1049                    "(Bin, NavigationTarget { file_id: FileId(0), full_range: 1..13, focus_range: 4..8, name: \"main\", kind: Function })",1050                    "(DocTest, NavigationTarget { file_id: FileId(0), full_range: 100..154, name: \"foo\" })",1051                ]1052            "#]],1053        );1054    }1055    #[test]1056    fn test_runnables_module() {1057        check(1058            r#"1059//- /lib.rs1060$01061mod test_mod {1062    #[test]1063    fn test_foo1() {}1064}1065"#,1066            expect![[r#"1067                [1068                    "(TestMod, NavigationTarget { file_id: FileId(0), full_range: 1..51, focus_range: 5..13, name: \"test_mod\", kind: Module, description: \"mod test_mod\" })",1069                    "(Test, NavigationTarget { file_id: FileId(0), full_range: 20..49, focus_range: 35..44, name: \"test_foo1\", kind: Function })",1070                ]1071            "#]],1072        );1073    }10741075    #[test]1076    fn only_modules_with_test_functions_or_more_than_one_test_submodule_have_runners() {1077        check(1078            r#"1079//- /lib.rs1080$01081mod root_tests {1082    mod nested_tests_0 {1083        mod nested_tests_1 {1084            #[test]1085            fn nested_test_11() {}10861087            #[test]1088            fn nested_test_12() {}1089        }10901091        mod nested_tests_2 {1092            #[test]1093            fn nested_test_2() {}1094        }10951096        mod nested_tests_3 {}1097    }10981099    mod nested_tests_4 {}1100}1101"#,1102            expect![[r#"1103                [1104                    "(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\" })",1105                    "(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\" })",1106                    "(Test, NavigationTarget { file_id: FileId(0), full_range: 84..126, focus_range: 107..121, name: \"nested_test_11\", kind: Function })",1107                    "(Test, NavigationTarget { file_id: FileId(0), full_range: 140..182, focus_range: 163..177, name: \"nested_test_12\", kind: Function })",1108                    "(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\" })",1109                    "(Test, NavigationTarget { file_id: FileId(0), full_range: 235..276, focus_range: 258..271, name: \"nested_test_2\", kind: Function })",1110                ]1111            "#]],1112        );1113    }11141115    #[test]1116    fn test_runnables_with_feature() {1117        check(1118            r#"1119//- /lib.rs crate:foo cfg:feature=foo1120$01121#[test]1122#[cfg(feature = "foo")]1123fn test_foo1() {}1124"#,1125            expect![[r#"1126                [1127                    "(TestMod, NavigationTarget { file_id: FileId(0), full_range: 0..51, name: \"_\", kind: CrateRoot })",1128                    "(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\" }))",1129                ]1130            "#]],1131        );1132    }11331134    #[test]1135    fn test_runnables_with_features() {1136        check(1137            r#"1138//- /lib.rs crate:foo cfg:feature=foo,feature=bar1139$01140#[test]1141#[cfg(all(feature = "foo", feature = "bar"))]1142fn test_foo1() {}1143"#,1144            expect![[r#"1145                [1146                    "(TestMod, NavigationTarget { file_id: FileId(0), full_range: 0..73, name: \"_\", kind: CrateRoot })",1147                    "(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\" })]))",1148                ]1149            "#]],1150        );1151    }11521153    #[test]1154    fn test_runnables_no_test_function_in_module() {1155        check(1156            r#"1157//- /lib.rs1158$01159mod test_mod {1160    fn foo1() {}1161}1162"#,1163            expect![[r#"1164                []1165            "#]],1166        );1167    }11681169    #[test]1170    fn test_doc_runnables_impl_mod() {1171        check(1172            r#"1173//- /lib.rs1174mod foo;1175//- /foo.rs1176struct Foo;$01177impl Foo {1178    /// ```1179    /// let x = 5;1180    /// ```1181    fn foo() {}1182}1183        "#,1184            expect![[r#"1185                [1186                    "(DocTest, NavigationTarget { file_id: FileId(1), full_range: 27..81, name: \"foo\" })",1187                ]1188            "#]],1189        );1190    }11911192    #[test]1193    fn test_runnables_in_macro() {1194        check(1195            r#"1196//- /lib.rs1197$01198macro_rules! generate {1199    () => {1200        #[test]1201        fn foo_test() {}1202    }1203}1204macro_rules! generate2 {1205    () => {1206        mod tests2 {1207            #[test]1208            fn foo_test2() {}1209        }1210    }1211}1212macro_rules! generate_main {1213    () => {1214        fn main() {}1215    }1216}1217mod tests {1218    generate!();1219}1220generate2!();1221generate_main!();1222"#,1223            expect![[r#"1224                [1225                    "(TestMod, NavigationTarget { file_id: FileId(0), full_range: 0..345, name: \"_\", kind: CrateRoot })",1226                    "(TestMod, NavigationTarget { file_id: FileId(0), full_range: 282..312, focus_range: 286..291, name: \"tests\", kind: Module, description: \"mod tests\" })",1227                    "(Test, NavigationTarget { file_id: FileId(0), full_range: 298..307, name: \"foo_test\", kind: Function })",1228                    "(TestMod, NavigationTarget { file_id: FileId(0), full_range: 313..323, name: \"tests2\", kind: Module, description: \"mod tests2\" }, true)",1229                    "(Test, NavigationTarget { file_id: FileId(0), full_range: 313..323, name: \"foo_test2\", kind: Function }, true)",1230                    "(Bin, NavigationTarget { file_id: FileId(0), full_range: 327..341, name: \"main\", kind: Function })",1231                ]1232            "#]],1233        );1234    }12351236    #[test]1237    fn big_mac() {1238        check(1239            r#"1240//- /lib.rs1241$01242macro_rules! foo {1243    () => {1244        mod foo_tests {1245            #[test]1246            fn foo0() {}1247            #[test]1248            fn foo1() {}1249            #[test]1250            fn foo2() {}1251        }1252    };1253}1254foo!();1255"#,1256            expect![[r#"1257                [1258                    "(TestMod, NavigationTarget { file_id: FileId(0), full_range: 210..214, name: \"foo_tests\", kind: Module, description: \"mod foo_tests\" }, true)",1259                    "(Test, NavigationTarget { file_id: FileId(0), full_range: 210..214, name: \"foo0\", kind: Function }, true)",1260                    "(Test, NavigationTarget { file_id: FileId(0), full_range: 210..214, name: \"foo1\", kind: Function }, true)",1261                    "(Test, NavigationTarget { file_id: FileId(0), full_range: 210..214, name: \"foo2\", kind: Function }, true)",1262                ]1263            "#]],1264        );1265    }12661267    #[test]1268    fn dont_recurse_in_outline_submodules() {1269        check(1270            r#"1271//- /lib.rs1272$01273mod m;1274//- /m.rs1275mod tests {1276    #[test]1277    fn t() {}1278}1279"#,1280            expect![[r#"1281                []1282            "#]],1283        );1284    }12851286    #[test]1287    fn outline_submodule1() {1288        check(1289            r#"1290//- /lib.rs1291$01292mod m;1293//- /m.rs1294#[test]1295fn t0() {}1296#[test]1297fn t1() {}1298"#,1299            expect![[r#"1300                [1301                    "(TestMod, NavigationTarget { file_id: FileId(0), full_range: 1..7, focus_range: 5..6, name: \"m\", kind: Module, description: \"mod m\" })",1302                ]1303            "#]],1304        );1305    }13061307    #[test]1308    fn outline_submodule2() {1309        check(1310            r#"1311//- /lib.rs1312mod m;1313//- /m.rs1314$01315#[test]1316fn t0() {}1317#[test]1318fn t1() {}1319"#,1320            expect![[r#"1321                [1322                    "(TestMod, NavigationTarget { file_id: FileId(1), full_range: 0..39, name: \"m\", kind: Module })",1323                    "(Test, NavigationTarget { file_id: FileId(1), full_range: 1..19, focus_range: 12..14, name: \"t0\", kind: Function })",1324                    "(Test, NavigationTarget { file_id: FileId(1), full_range: 20..38, focus_range: 31..33, name: \"t1\", kind: Function })",1325                ]1326            "#]],1327        );1328    }13291330    #[test]1331    fn attributed_module() {1332        check(1333            r#"1334//- proc_macros: identity1335//- /lib.rs1336$01337#[proc_macros::identity]1338mod module {1339    #[test]1340    fn t0() {}1341    #[test]1342    fn t1() {}1343}1344"#,1345            expect![[r#"1346                [1347                    "(TestMod, NavigationTarget { file_id: FileId(0), full_range: 26..94, focus_range: 30..36, name: \"module\", kind: Module, description: \"mod module\" }, true)",1348                    "(Test, NavigationTarget { file_id: FileId(0), full_range: 43..65, focus_range: 58..60, name: \"t0\", kind: Function }, true)",1349                    "(Test, NavigationTarget { file_id: FileId(0), full_range: 70..92, focus_range: 85..87, name: \"t1\", kind: Function }, true)",1350                ]1351            "#]],1352        );1353    }13541355    #[test]1356    fn find_no_tests() {1357        check_tests(1358            r#"1359//- /lib.rs1360fn foo$0() {  };1361"#,1362            expect![[r#"1363                []1364            "#]],1365        );1366    }13671368    #[test]1369    fn find_direct_fn_test() {1370        check_tests(1371            r#"1372//- /lib.rs1373fn foo$0() { };13741375mod tests {1376    #[test]1377    fn foo_test() {1378        super::foo()1379    }1380}1381"#,1382            expect![[r#"1383                [1384                    NavigationTarget {1385                        file_id: FileId(1386                            0,1387                        ),1388                        full_range: 31..85,1389                        focus_range: 46..54,1390                        name: "foo_test",1391                        kind: Function,1392                    },1393                ]1394            "#]],1395        );1396    }13971398    #[test]1399    fn find_direct_struct_test() {1400        check_tests(1401            r#"1402//- /lib.rs1403struct Fo$0o;1404fn foo(arg: &Foo) { };14051406mod tests {1407    use super::*;14081409    #[test]1410    fn foo_test() {1411        foo(Foo);1412    }1413}1414"#,1415            expect![[r#"1416                [1417                    NavigationTarget {1418                        file_id: FileId(1419                            0,1420                        ),1421                        full_range: 71..122,1422                        focus_range: 86..94,1423                        name: "foo_test",1424                        kind: Function,1425                    },1426                ]1427            "#]],1428        );1429    }14301431    #[test]1432    fn find_indirect_fn_test() {1433        check_tests(1434            r#"1435//- /lib.rs1436fn foo$0() { };14371438mod tests {1439    use super::foo;14401441    fn check1() {1442        check2()1443    }14441445    fn check2() {1446        foo()1447    }14481449    #[test]1450    fn foo_test() {1451        check1()1452    }1453}1454"#,1455            expect![[r#"1456                [1457                    NavigationTarget {1458                        file_id: FileId(1459                            0,1460                        ),1461                        full_range: 133..183,1462                        focus_range: 148..156,1463                        name: "foo_test",1464                        kind: Function,1465                    },1466                ]1467            "#]],1468        );1469    }14701471    #[test]1472    fn tests_are_unique() {1473        check_tests(1474            r#"1475//- /lib.rs1476fn foo$0() { };14771478mod tests {1479    use super::foo;14801481    #[test]1482    fn foo_test() {1483        foo();1484        foo();1485    }14861487    #[test]1488    fn foo2_test() {1489        foo();1490        foo();1491    }14921493}1494"#,1495            expect![[r#"1496                [1497                    NavigationTarget {1498                        file_id: FileId(1499                            0,1500                        ),1501                        full_range: 52..115,1502                        focus_range: 67..75,1503                        name: "foo_test",1504                        kind: Function,1505                    },1506                    NavigationTarget {1507                        file_id: FileId(1508                            0,1509                        ),1510                        full_range: 121..185,1511                        focus_range: 136..145,1512                        name: "foo2_test",1513                        kind: Function,1514                    },1515                ]1516            "#]],1517        );1518    }15191520    #[test]1521    fn test_runnables_doc_test_in_impl_with_lifetime_type_const_value() {1522        check(1523            r#"1524//- /lib.rs1525$01526fn main() {}15271528struct Data<'a, A, const B: usize, C, const D: u32>;1529impl<A, C, const D: u32> Data<'a, A, 12, C, D> {1530    /// ```1531    /// ```1532    fn foo() {}1533}1534"#,1535            expect![[r#"1536                [1537                    "(Bin, NavigationTarget { file_id: FileId(0), full_range: 1..13, focus_range: 4..8, name: \"main\", kind: Function })",1538                    "(DocTest, NavigationTarget { file_id: FileId(0), full_range: 121..156, name: \"foo\" })",1539                ]1540            "#]],1541        );1542    }15431544    #[test]1545    fn doc_test_type_params() {1546        check(1547            r#"1548//- /lib.rs1549$01550struct Foo<T, U>;15511552/// ```1553/// ```1554impl<T, U> Foo<T, U> {1555    /// ```rust1556    /// ````1557    fn t() {}1558}15591560/// ```1561/// ```1562impl Foo<Foo<(), ()>, ()> {1563    /// ```1564    /// ```1565    fn t() {}1566}1567"#,1568            expect![[r#"1569                [1570                    "(DocTest, NavigationTarget { file_id: FileId(0), full_range: 20..103, focus_range: 47..56, name: \"impl\", kind: Impl })",1571                    "(DocTest, NavigationTarget { file_id: FileId(0), full_range: 63..101, name: \"t\" })",1572                    "(DocTest, NavigationTarget { file_id: FileId(0), full_range: 105..188, focus_range: 126..146, name: \"impl\", kind: Impl })",1573                    "(DocTest, NavigationTarget { file_id: FileId(0), full_range: 153..186, name: \"t\" })",1574                ]1575            "#]],1576        );1577    }15781579    #[test]1580    fn doc_test_macro_export_mbe() {1581        check(1582            r#"1583//- /lib.rs1584$01585mod foo;15861587//- /foo.rs1588/// ```1589/// fn foo() {1590/// }1591/// ```1592#[macro_export]1593macro_rules! foo {1594    () => {15951596    };1597}1598"#,1599            expect![[r#"1600                []1601            "#]],1602        );1603        check(1604            r#"1605//- /lib.rs1606$01607/// ```1608/// fn foo() {1609/// }1610/// ```1611#[macro_export]1612macro_rules! foo {1613    () => {16141615    };1616}1617"#,1618            expect![[r#"1619                [1620                    "(DocTest, NavigationTarget { file_id: FileId(0), full_range: 1..94, name: \"foo\" })",1621                ]1622            "#]],1623        );1624    }16251626    #[test]1627    fn test_paths_with_raw_ident() {1628        check(1629            r#"1630//- /lib.rs1631$01632mod r#mod {1633    #[test]1634    fn r#fn() {}16351636    /// ```1637    /// ```1638    fn r#for() {}16391640    /// ```1641    /// ```1642    struct r#struct<r#type>(r#type);16431644    /// ```1645    /// ```1646    impl<r#type> r#struct<r#type> {1647        /// ```1648        /// ```1649        fn r#fn() {}1650    }16511652    enum r#enum {}1653    impl r#struct<r#enum> {1654        /// ```1655        /// ```1656        fn r#fn() {}1657    }16581659    trait r#trait {}16601661    /// ```1662    /// ```1663    impl<T> r#trait for r#struct<T> {}1664}1665"#,1666            expect![[r#"1667                [1668                    "(TestMod, NavigationTarget { file_id: FileId(0), full_range: 1..461, focus_range: 5..10, name: \"mod\", kind: Module, description: \"mod r#mod\" })",1669                    "(Test, NavigationTarget { file_id: FileId(0), full_range: 17..41, focus_range: 32..36, name: \"r#fn\", kind: Function })",1670                    "(DocTest, NavigationTarget { file_id: FileId(0), full_range: 47..84, name: \"for\", container_name: \"mod\" })",1671                    "(DocTest, NavigationTarget { file_id: FileId(0), full_range: 90..146, name: \"struct\", container_name: \"mod\" })",1672                    "(DocTest, NavigationTarget { file_id: FileId(0), full_range: 152..266, focus_range: 189..205, name: \"impl\", kind: Impl })",1673                    "(DocTest, NavigationTarget { file_id: FileId(0), full_range: 216..260, name: \"fn\" })",1674                    "(DocTest, NavigationTarget { file_id: FileId(0), full_range: 323..367, name: \"fn\" })",1675                    "(DocTest, NavigationTarget { file_id: FileId(0), full_range: 401..459, focus_range: 445..456, name: \"impl\", kind: Impl })",1676                ]1677            "#]],1678        )1679    }16801681    #[test]1682    fn exported_main_is_test_in_cfg_test_mod() {1683        check(1684            r#"1685//- /lib.rs crate:foo cfg:test1686$01687mod not_a_test_module_inline {1688    #[export_name = "main"]1689    fn exp_main() {}1690}1691#[cfg(test)]1692mod test_mod_inline {1693    #[export_name = "main"]1694    fn exp_main() {}1695}1696mod not_a_test_module;1697#[cfg(test)]1698mod test_mod;1699//- /not_a_test_module.rs1700#[export_name = "main"]1701fn exp_main() {}1702//- /test_mod.rs1703#[export_name = "main"]1704fn exp_main() {}1705"#,1706            expect![[r#"1707                [1708                    "(Bin, NavigationTarget { file_id: FileId(0), full_range: 36..80, focus_range: 67..75, name: \"exp_main\", kind: Function })",1709                    "(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\")))",1710                    "(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\")))",1711                ]1712            "#]],1713        )1714    }1715}

Code quality findings 13

Warning: '.expect()' will panic with a custom message on None/Err. While better than unwrap() for debugging, prefer non-panicking error handling in production code (match, if let, ?).
warning correctness expect-usage
.unwrap_or_else(|| (*all_crates(db).last().expect("no crate graph present")).into())
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning correctness unwrap-usage
expect_test: find_macro(mod_paths.get(Self::EXPECT_CRATE).unwrap()),
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning correctness unwrap-usage
insta: find_macro(mod_paths.get(Self::INSTA_CRATE).unwrap()),
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning correctness unwrap-usage
snapbox: find_macro(mod_paths.get(Self::SNAPBOX_CRATE).unwrap()),
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info correctness match-wildcard
let file_id = match def {
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info correctness match-wildcard
let runnable = match def {
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info correctness match-wildcard
let runnable = match assoc {
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info correctness match-wildcard
let name_ref = match ref_.name {
Performance Info: Frequent cloning, especially of Strings, Vecs, or other heap-allocated types inside loops, can be expensive. Consider using references/borrowing where possible.
info performance clone-in-loop
sema.ancestors_with_macros(name_ref.syntax().clone()).find_map(ast::Fn::cast)
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info correctness match-wildcard
let fn_name = match fn_def.name() {
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info correctness match-wildcard
match cfg {
Performance Info: Frequent cloning, especially of Strings, Vecs, or other heap-allocated types inside loops, can be expensive. Consider using references/borrowing where possible.
info performance clone-in-loop
let segments = [krate.clone(), Symbol::intern(path)];
Info: Wildcard imports (`use some::path::*;`) can obscure the origin of names and lead to conflicts. Prefer importing specific items explicitly.
info maintainability wildcard-import
use super::*;

Get this view in your editor

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