1
1
use std:: fmt;
2
2
3
3
use ast:: HasName ;
4
- use cfg:: CfgExpr ;
5
- use hir:: { db:: HirDatabase , AsAssocItem , HasAttrs , HasSource , HirFileIdExt , Semantics } ;
6
- use ide_assists:: utils:: test_related_attribute;
4
+ use cfg:: { CfgAtom , CfgExpr } ;
5
+ use hir:: {
6
+ db:: HirDatabase , AsAssocItem , AttrsWithOwner , HasAttrs , HasSource , HirFileIdExt , Semantics ,
7
+ } ;
8
+ use ide_assists:: utils:: { has_test_related_attribute, test_related_attribute_syn} ;
7
9
use ide_db:: {
8
10
base_db:: { FilePosition , FileRange } ,
9
11
defs:: Definition ,
@@ -280,7 +282,7 @@ fn find_related_tests_in_module(
280
282
}
281
283
282
284
fn as_test_runnable ( sema : & Semantics < ' _ , RootDatabase > , fn_def : & ast:: Fn ) -> Option < Runnable > {
283
- if test_related_attribute ( fn_def) . is_some ( ) {
285
+ if test_related_attribute_syn ( fn_def) . is_some ( ) {
284
286
let function = sema. to_def ( fn_def) ?;
285
287
runnable_fn ( sema, function)
286
288
} else {
@@ -293,7 +295,7 @@ fn parent_test_module(sema: &Semantics<'_, RootDatabase>, fn_def: &ast::Fn) -> O
293
295
let module = ast:: Module :: cast ( node) ?;
294
296
let module = sema. to_def ( & module) ?;
295
297
296
- if has_test_function_or_multiple_test_submodules ( sema, & module) {
298
+ if has_test_function_or_multiple_test_submodules ( sema, & module, false ) {
297
299
Some ( module)
298
300
} else {
299
301
None
@@ -305,7 +307,8 @@ pub(crate) fn runnable_fn(
305
307
sema : & Semantics < ' _ , RootDatabase > ,
306
308
def : hir:: Function ,
307
309
) -> Option < Runnable > {
308
- let kind = if def. is_main ( sema. db ) {
310
+ let under_cfg_test = has_cfg_test ( def. module ( sema. db ) . attrs ( sema. db ) ) ;
311
+ let kind = if !under_cfg_test && def. is_main ( sema. db ) {
309
312
RunnableKind :: Bin
310
313
} else {
311
314
let test_id = || {
@@ -342,7 +345,8 @@ pub(crate) fn runnable_mod(
342
345
sema : & Semantics < ' _ , RootDatabase > ,
343
346
def : hir:: Module ,
344
347
) -> Option < Runnable > {
345
- if !has_test_function_or_multiple_test_submodules ( sema, & def) {
348
+ if !has_test_function_or_multiple_test_submodules ( sema, & def, has_cfg_test ( def. attrs ( sema. db ) ) )
349
+ {
346
350
return None ;
347
351
}
348
352
let path = def
@@ -384,12 +388,17 @@ pub(crate) fn runnable_impl(
384
388
Some ( Runnable { use_name_in_title : false , nav, kind : RunnableKind :: DocTest { test_id } , cfg } )
385
389
}
386
390
391
+ fn has_cfg_test ( attrs : AttrsWithOwner ) -> bool {
392
+ attrs. cfgs ( ) . any ( |cfg| matches ! ( cfg, CfgExpr :: Atom ( CfgAtom :: Flag ( s) ) if s == "test" ) )
393
+ }
394
+
387
395
/// Creates a test mod runnable for outline modules at the top of their definition.
388
396
fn runnable_mod_outline_definition (
389
397
sema : & Semantics < ' _ , RootDatabase > ,
390
398
def : hir:: Module ,
391
399
) -> Option < Runnable > {
392
- if !has_test_function_or_multiple_test_submodules ( sema, & def) {
400
+ if !has_test_function_or_multiple_test_submodules ( sema, & def, has_cfg_test ( def. attrs ( sema. db ) ) )
401
+ {
393
402
return None ;
394
403
}
395
404
let path = def
@@ -522,20 +531,28 @@ fn has_runnable_doc_test(attrs: &hir::Attrs) -> bool {
522
531
fn has_test_function_or_multiple_test_submodules (
523
532
sema : & Semantics < ' _ , RootDatabase > ,
524
533
module : & hir:: Module ,
534
+ consider_exported_main : bool ,
525
535
) -> bool {
526
536
let mut number_of_test_submodules = 0 ;
527
537
528
538
for item in module. declarations ( sema. db ) {
529
539
match item {
530
540
hir:: ModuleDef :: Function ( f) => {
531
- if let Some ( it) = f. source ( sema. db ) {
532
- if test_related_attribute ( & it. value ) . is_some ( ) {
533
- return true ;
534
- }
541
+ if has_test_related_attribute ( & f. attrs ( sema. db ) ) {
542
+ return true ;
543
+ }
544
+ if consider_exported_main && f. exported_main ( sema. db ) {
545
+ // an exported main in a test module can be considered a test wrt to custom test
546
+ // runners
547
+ return true ;
535
548
}
536
549
}
537
550
hir:: ModuleDef :: Module ( submodule) => {
538
- if has_test_function_or_multiple_test_submodules ( sema, & submodule) {
551
+ if has_test_function_or_multiple_test_submodules (
552
+ sema,
553
+ & submodule,
554
+ consider_exported_main,
555
+ ) {
539
556
number_of_test_submodules += 1 ;
540
557
}
541
558
}
@@ -1484,4 +1501,39 @@ mod r#mod {
1484
1501
"# ] ] ,
1485
1502
)
1486
1503
}
1504
+
1505
+ #[ test]
1506
+ fn exported_main_is_test_in_cfg_test_mod ( ) {
1507
+ check (
1508
+ r#"
1509
+ //- /lib.rs crate:foo cfg:test
1510
+ $0
1511
+ mod not_a_test_module_inline {
1512
+ #[export_name = "main"]
1513
+ fn exp_main() {}
1514
+ }
1515
+ #[cfg(test)]
1516
+ mod test_mod_inline {
1517
+ #[export_name = "main"]
1518
+ fn exp_main() {}
1519
+ }
1520
+ mod not_a_test_module;
1521
+ #[cfg(test)]
1522
+ mod test_mod;
1523
+ //- /not_a_test_module.rs
1524
+ #[export_name = "main"]
1525
+ fn exp_main() {}
1526
+ //- /test_mod.rs
1527
+ #[export_name = "main"]
1528
+ fn exp_main() {}
1529
+ "# ,
1530
+ expect ! [ [ r#"
1531
+ [
1532
+ "(Bin, NavigationTarget { file_id: FileId(0), full_range: 36..80, focus_range: 67..75, name: \"exp_main\", kind: Function })",
1533
+ "(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\")))",
1534
+ "(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\")))",
1535
+ ]
1536
+ "# ] ] ,
1537
+ )
1538
+ }
1487
1539
}
0 commit comments