Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/ide/src/runnables.rs')
-rw-r--r--crates/ide/src/runnables.rs78
1 files changed, 65 insertions, 13 deletions
diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs
index 79324bf387..b6c6753755 100644
--- a/crates/ide/src/runnables.rs
+++ b/crates/ide/src/runnables.rs
@@ -1,9 +1,11 @@
use std::fmt;
use ast::HasName;
-use cfg::CfgExpr;
-use hir::{db::HirDatabase, AsAssocItem, HasAttrs, HasSource, HirFileIdExt, Semantics};
-use ide_assists::utils::test_related_attribute;
+use cfg::{CfgAtom, CfgExpr};
+use hir::{
+ db::HirDatabase, AsAssocItem, AttrsWithOwner, HasAttrs, HasSource, HirFileIdExt, Semantics,
+};
+use ide_assists::utils::{has_test_related_attribute, test_related_attribute_syn};
use ide_db::{
base_db::{FilePosition, FileRange},
defs::Definition,
@@ -280,7 +282,7 @@ fn find_related_tests_in_module(
}
fn as_test_runnable(sema: &Semantics<'_, RootDatabase>, fn_def: &ast::Fn) -> Option<Runnable> {
- if test_related_attribute(fn_def).is_some() {
+ if test_related_attribute_syn(fn_def).is_some() {
let function = sema.to_def(fn_def)?;
runnable_fn(sema, function)
} else {
@@ -293,7 +295,7 @@ fn parent_test_module(sema: &Semantics<'_, RootDatabase>, fn_def: &ast::Fn) -> O
let module = ast::Module::cast(node)?;
let module = sema.to_def(&module)?;
- if has_test_function_or_multiple_test_submodules(sema, &module) {
+ if has_test_function_or_multiple_test_submodules(sema, &module, false) {
Some(module)
} else {
None
@@ -305,7 +307,8 @@ pub(crate) fn runnable_fn(
sema: &Semantics<'_, RootDatabase>,
def: hir::Function,
) -> Option<Runnable> {
- let kind = if def.is_main(sema.db) {
+ let under_cfg_test = has_cfg_test(def.module(sema.db).attrs(sema.db));
+ let kind = if !under_cfg_test && def.is_main(sema.db) {
RunnableKind::Bin
} else {
let test_id = || {
@@ -342,7 +345,8 @@ pub(crate) fn runnable_mod(
sema: &Semantics<'_, RootDatabase>,
def: hir::Module,
) -> Option<Runnable> {
- if !has_test_function_or_multiple_test_submodules(sema, &def) {
+ if !has_test_function_or_multiple_test_submodules(sema, &def, has_cfg_test(def.attrs(sema.db)))
+ {
return None;
}
let path = def
@@ -384,12 +388,17 @@ pub(crate) fn runnable_impl(
Some(Runnable { use_name_in_title: false, nav, kind: RunnableKind::DocTest { test_id }, cfg })
}
+fn has_cfg_test(attrs: AttrsWithOwner) -> bool {
+ attrs.cfgs().any(|cfg| matches!(cfg, CfgExpr::Atom(CfgAtom::Flag(s)) if s == "test"))
+}
+
/// Creates a test mod runnable for outline modules at the top of their definition.
fn runnable_mod_outline_definition(
sema: &Semantics<'_, RootDatabase>,
def: hir::Module,
) -> Option<Runnable> {
- if !has_test_function_or_multiple_test_submodules(sema, &def) {
+ if !has_test_function_or_multiple_test_submodules(sema, &def, has_cfg_test(def.attrs(sema.db)))
+ {
return None;
}
let path = def
@@ -522,20 +531,28 @@ fn has_runnable_doc_test(attrs: &hir::Attrs) -> bool {
fn has_test_function_or_multiple_test_submodules(
sema: &Semantics<'_, RootDatabase>,
module: &hir::Module,
+ consider_exported_main: bool,
) -> bool {
let mut number_of_test_submodules = 0;
for item in module.declarations(sema.db) {
match item {
hir::ModuleDef::Function(f) => {
- if let Some(it) = f.source(sema.db) {
- if test_related_attribute(&it.value).is_some() {
- return true;
- }
+ if has_test_related_attribute(&f.attrs(sema.db)) {
+ return true;
+ }
+ if consider_exported_main && f.exported_main(sema.db) {
+ // an exported main in a test module can be considered a test wrt to custom test
+ // runners
+ return true;
}
}
hir::ModuleDef::Module(submodule) => {
- if has_test_function_or_multiple_test_submodules(sema, &submodule) {
+ if has_test_function_or_multiple_test_submodules(
+ sema,
+ &submodule,
+ consider_exported_main,
+ ) {
number_of_test_submodules += 1;
}
}
@@ -1484,4 +1501,39 @@ mod r#mod {
"#]],
)
}
+
+ #[test]
+ fn exported_main_is_test_in_cfg_test_mod() {
+ check(
+ r#"
+//- /lib.rs crate:foo cfg:test
+$0
+mod not_a_test_module_inline {
+ #[export_name = "main"]
+ fn exp_main() {}
+}
+#[cfg(test)]
+mod test_mod_inline {
+ #[export_name = "main"]
+ fn exp_main() {}
+}
+mod not_a_test_module;
+#[cfg(test)]
+mod test_mod;
+//- /not_a_test_module.rs
+#[export_name = "main"]
+fn exp_main() {}
+//- /test_mod.rs
+#[export_name = "main"]
+fn exp_main() {}
+"#,
+ expect![[r#"
+ [
+ "(Bin, NavigationTarget { file_id: FileId(0), full_range: 36..80, focus_range: 67..75, name: \"exp_main\", kind: Function })",
+ "(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\")))",
+ "(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\")))",
+ ]
+ "#]],
+ )
+ }
}