1
- use std:: fmt;
1
+ use std:: { fmt, sync :: OnceLock } ;
2
2
3
+ use arrayvec:: ArrayVec ;
3
4
use ast:: HasName ;
4
5
use cfg:: { CfgAtom , CfgExpr } ;
5
6
use hir:: {
6
7
db:: HirDatabase , sym, AsAssocItem , AttrsWithOwner , HasAttrs , HasCrate , HasSource , HirFileIdExt ,
7
- Semantics ,
8
+ ModPath , Name , PathKind , Semantics , Symbol ,
8
9
} ;
9
10
use ide_assists:: utils:: { has_test_related_attribute, test_related_attribute_syn} ;
10
11
use ide_db:: {
@@ -336,7 +337,7 @@ pub(crate) fn runnable_fn(
336
337
}
337
338
} ;
338
339
339
- let fn_source = def . source ( sema . db ) ?;
340
+ let fn_source = sema . source ( def ) ?;
340
341
let nav = NavigationTarget :: from_named (
341
342
sema. db ,
342
343
fn_source. as_ref ( ) . map ( |it| it as & dyn ast:: HasName ) ,
@@ -345,7 +346,8 @@ pub(crate) fn runnable_fn(
345
346
. call_site ( ) ;
346
347
347
348
let file_range = fn_source. syntax ( ) . original_file_range_with_macro_call_body ( sema. db ) ;
348
- let update_test = TestDefs :: new ( sema, def. krate ( sema. db ) , file_range) . update_test ( ) ;
349
+ let update_test =
350
+ UpdateTest :: find_snapshot_macro ( sema, & fn_source. file_syntax ( sema. db ) , file_range) ;
349
351
350
352
let cfg = def. attrs ( sema. db ) . cfg ( ) ;
351
353
Some ( Runnable { use_name_in_title : false , nav, kind, cfg, update_test } )
@@ -374,13 +376,13 @@ pub(crate) fn runnable_mod(
374
376
let cfg = attrs. cfg ( ) ;
375
377
let nav = NavigationTarget :: from_module_to_decl ( sema. db , def) . call_site ( ) ;
376
378
377
- let file_range = {
378
- let src = def . definition_source ( sema. db ) ;
379
- let file_id = src . file_id . original_file ( sema . db ) ;
380
- let range = src . file_syntax ( sema. db ) . text_range ( ) ;
381
- hir :: FileRange { file_id , range }
379
+ let module_source = sema . module_definition_node ( def ) ;
380
+ let module_syntax = module_source . file_syntax ( sema. db ) ;
381
+ let file_range = hir :: FileRange {
382
+ file_id : module_source . file_id . original_file ( sema. db ) ,
383
+ range : module_syntax . text_range ( ) ,
382
384
} ;
383
- let update_test = TestDefs :: new ( sema, def . krate ( ) , file_range) . update_test ( ) ;
385
+ let update_test = UpdateTest :: find_snapshot_macro ( sema, & module_syntax , file_range) ;
384
386
385
387
Some ( Runnable {
386
388
use_name_in_title : false ,
@@ -414,9 +416,11 @@ pub(crate) fn runnable_impl(
414
416
test_id. retain ( |c| c != ' ' ) ;
415
417
let test_id = TestId :: Path ( test_id) ;
416
418
417
- let impl_source =
418
- def. source ( sema. db ) ?. syntax ( ) . original_file_range_with_macro_call_body ( sema. db ) ;
419
- let update_test = TestDefs :: new ( sema, def. krate ( sema. db ) , impl_source) . update_test ( ) ;
419
+ let impl_source = sema. source ( * def) ?;
420
+ let impl_syntax = impl_source. syntax ( ) ;
421
+ let file_range = impl_syntax. original_file_range_with_macro_call_body ( sema. db ) ;
422
+ let update_test =
423
+ UpdateTest :: find_snapshot_macro ( sema, & impl_syntax. file_syntax ( sema. db ) , file_range) ;
420
424
421
425
Some ( Runnable {
422
426
use_name_in_title : false ,
@@ -456,13 +460,13 @@ fn runnable_mod_outline_definition(
456
460
let attrs = def. attrs ( sema. db ) ;
457
461
let cfg = attrs. cfg ( ) ;
458
462
459
- let file_range = {
460
- let src = def . definition_source ( sema. db ) ;
461
- let file_id = src . file_id . original_file ( sema . db ) ;
462
- let range = src . file_syntax ( sema. db ) . text_range ( ) ;
463
- hir :: FileRange { file_id , range }
463
+ let mod_source = sema . module_definition_node ( def ) ;
464
+ let mod_syntax = mod_source . file_syntax ( sema. db ) ;
465
+ let file_range = hir :: FileRange {
466
+ file_id : mod_source . file_id . original_file ( sema. db ) ,
467
+ range : mod_syntax . text_range ( ) ,
464
468
} ;
465
- let update_test = TestDefs :: new ( sema, def . krate ( ) , file_range) . update_test ( ) ;
469
+ let update_test = UpdateTest :: find_snapshot_macro ( sema, & mod_syntax , file_range) ;
466
470
467
471
Some ( Runnable {
468
472
use_name_in_title : false ,
@@ -616,16 +620,93 @@ fn has_test_function_or_multiple_test_submodules(
616
620
number_of_test_submodules > 1
617
621
}
618
622
619
- struct TestDefs < ' a , ' b > ( & ' a Semantics < ' b , RootDatabase > , hir:: Crate , hir:: FileRange ) ;
620
-
621
623
#[ derive( Debug , Default , Clone , Copy , PartialEq , Eq , Hash ) ]
622
624
pub struct UpdateTest {
623
625
pub expect_test : bool ,
624
626
pub insta : bool ,
625
627
pub snapbox : bool ,
626
628
}
627
629
630
+ static SNAPSHOT_TEST_MACROS : OnceLock < FxHashMap < & str , Vec < ModPath > > > = OnceLock :: new ( ) ;
631
+
628
632
impl UpdateTest {
633
+ const EXPECT_CRATE : & str = "expect_test" ;
634
+ const EXPECT_MACROS : & [ & str ] = & [ "expect" , "expect_file" ] ;
635
+
636
+ const INSTA_CRATE : & str = "insta" ;
637
+ const INSTA_MACROS : & [ & str ] = & [
638
+ "assert_snapshot" ,
639
+ "assert_debug_snapshot" ,
640
+ "assert_display_snapshot" ,
641
+ "assert_json_snapshot" ,
642
+ "assert_yaml_snapshot" ,
643
+ "assert_ron_snapshot" ,
644
+ "assert_toml_snapshot" ,
645
+ "assert_csv_snapshot" ,
646
+ "assert_compact_json_snapshot" ,
647
+ "assert_compact_debug_snapshot" ,
648
+ "assert_binary_snapshot" ,
649
+ ] ;
650
+
651
+ const SNAPBOX_CRATE : & str = "snapbox" ;
652
+ const SNAPBOX_MACROS : & [ & str ] = & [ "assert_data_eq" , "file" , "str" ] ;
653
+
654
+ fn find_snapshot_macro (
655
+ sema : & Semantics < ' _ , RootDatabase > ,
656
+ scope : & SyntaxNode ,
657
+ file_range : hir:: FileRange ,
658
+ ) -> Self {
659
+ fn init < ' a > (
660
+ krate_name : & ' a str ,
661
+ paths : & [ & str ] ,
662
+ map : & mut FxHashMap < & ' a str , Vec < ModPath > > ,
663
+ ) {
664
+ let mut res = Vec :: with_capacity ( paths. len ( ) ) ;
665
+ let krate = Name :: new_symbol_root ( Symbol :: intern ( krate_name) ) ;
666
+ for path in paths {
667
+ let segments = [ krate. clone ( ) , Name :: new_symbol_root ( Symbol :: intern ( path) ) ] ;
668
+ let mod_path = ModPath :: from_segments ( PathKind :: Abs , segments) ;
669
+ res. push ( mod_path) ;
670
+ }
671
+ map. insert ( krate_name, res) ;
672
+ }
673
+
674
+ let mod_paths = SNAPSHOT_TEST_MACROS . get_or_init ( || {
675
+ let mut map = FxHashMap :: default ( ) ;
676
+ init ( Self :: EXPECT_CRATE , Self :: EXPECT_MACROS , & mut map) ;
677
+ init ( Self :: INSTA_CRATE , Self :: INSTA_MACROS , & mut map) ;
678
+ init ( Self :: SNAPBOX_CRATE , Self :: SNAPBOX_MACROS , & mut map) ;
679
+ map
680
+ } ) ;
681
+
682
+ let search_scope = SearchScope :: file_range ( file_range) ;
683
+ let find_macro = |paths : & [ ModPath ] | {
684
+ for path in paths {
685
+ let Some ( items) = sema. resolve_mod_path ( scope, path) else {
686
+ continue ;
687
+ } ;
688
+ for item in items {
689
+ if let hir:: ItemInNs :: Macros ( makro) = item {
690
+ if Definition :: Macro ( makro)
691
+ . usages ( sema)
692
+ . in_scope ( & search_scope)
693
+ . at_least_one ( )
694
+ {
695
+ return true ;
696
+ }
697
+ }
698
+ }
699
+ }
700
+ false
701
+ } ;
702
+
703
+ UpdateTest {
704
+ expect_test : find_macro ( mod_paths. get ( Self :: EXPECT_CRATE ) . unwrap ( ) ) ,
705
+ insta : find_macro ( mod_paths. get ( Self :: INSTA_CRATE ) . unwrap ( ) ) ,
706
+ snapbox : find_macro ( mod_paths. get ( Self :: SNAPBOX_CRATE ) . unwrap ( ) ) ,
707
+ }
708
+ }
709
+
629
710
pub fn label ( & self ) -> Option < SmolStr > {
630
711
let mut builder: SmallVec < [ _ ; 3 ] > = SmallVec :: new ( ) ;
631
712
if self . expect_test {
@@ -646,8 +727,8 @@ impl UpdateTest {
646
727
}
647
728
}
648
729
649
- pub fn env ( & self ) -> SmallVec < [ ( & str , & str ) ; 3 ] > {
650
- let mut env = SmallVec :: new ( ) ;
730
+ pub fn env ( & self ) -> ArrayVec < ( & str , & str ) , 3 > {
731
+ let mut env = ArrayVec :: new ( ) ;
651
732
if self . expect_test {
652
733
env. push ( ( "UPDATE_EXPECT" , "1" ) ) ;
653
734
}
@@ -661,75 +742,6 @@ impl UpdateTest {
661
742
}
662
743
}
663
744
664
- impl < ' a , ' b > TestDefs < ' a , ' b > {
665
- fn new (
666
- sema : & ' a Semantics < ' b , RootDatabase > ,
667
- current_krate : hir:: Crate ,
668
- file_range : hir:: FileRange ,
669
- ) -> Self {
670
- Self ( sema, current_krate, file_range)
671
- }
672
-
673
- fn update_test ( & self ) -> UpdateTest {
674
- UpdateTest { expect_test : self . expect_test ( ) , insta : self . insta ( ) , snapbox : self . snapbox ( ) }
675
- }
676
-
677
- fn expect_test ( & self ) -> bool {
678
- self . find_macro ( "expect_test" , & [ "expect" , "expect_file" ] )
679
- }
680
-
681
- fn insta ( & self ) -> bool {
682
- self . find_macro (
683
- "insta" ,
684
- & [
685
- "assert_snapshot" ,
686
- "assert_debug_snapshot" ,
687
- "assert_display_snapshot" ,
688
- "assert_json_snapshot" ,
689
- "assert_yaml_snapshot" ,
690
- "assert_ron_snapshot" ,
691
- "assert_toml_snapshot" ,
692
- "assert_csv_snapshot" ,
693
- "assert_compact_json_snapshot" ,
694
- "assert_compact_debug_snapshot" ,
695
- "assert_binary_snapshot" ,
696
- ] ,
697
- )
698
- }
699
-
700
- fn snapbox ( & self ) -> bool {
701
- self . find_macro ( "snapbox" , & [ "assert_data_eq" , "file" , "str" ] )
702
- }
703
-
704
- fn find_macro ( & self , crate_name : & str , paths : & [ & str ] ) -> bool {
705
- let db = self . 0 . db ;
706
-
707
- let Some ( dep) =
708
- self . 1 . dependencies ( db) . into_iter ( ) . find ( |dep| dep. name . eq_ident ( crate_name) )
709
- else {
710
- return false ;
711
- } ;
712
- let module = dep. krate . root_module ( ) ;
713
- let scope = module. scope ( db, None ) ;
714
-
715
- paths
716
- . iter ( )
717
- . filter_map ( |path| {
718
- let ( _, def) = scope. iter ( ) . find ( |( name, _) | name. eq_ident ( path) ) ?;
719
- match def {
720
- hir:: ScopeDef :: ModuleDef ( hir:: ModuleDef :: Macro ( it) ) => Some ( it) ,
721
- _ => None ,
722
- }
723
- } )
724
- . any ( |makro| {
725
- Definition :: Macro ( * makro)
726
- . usages ( self . 0 )
727
- . in_scope ( & SearchScope :: file_range ( self . 2 ) )
728
- . at_least_one ( )
729
- } )
730
- }
731
- }
732
-
733
745
#[ cfg( test) ]
734
746
mod tests {
735
747
use expect_test:: { expect, Expect } ;
0 commit comments