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// 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.