1- use std:: fmt;
1+ use std:: { fmt, sync :: OnceLock } ;
22
3+ use arrayvec:: ArrayVec ;
34use ast:: HasName ;
45use cfg:: { CfgAtom , CfgExpr } ;
56use hir:: {
67 db:: HirDatabase , sym, AsAssocItem , AttrsWithOwner , HasAttrs , HasCrate , HasSource , HirFileIdExt ,
7- Semantics ,
8+ ModPath , Name , PathKind , Semantics , Symbol ,
89} ;
910use ide_assists:: utils:: { has_test_related_attribute, test_related_attribute_syn} ;
1011use ide_db:: {
@@ -336,7 +337,7 @@ pub(crate) fn runnable_fn(
336337 }
337338 } ;
338339
339- let fn_source = def . source ( sema . db ) ?;
340+ let fn_source = sema . source ( def ) ?;
340341 let nav = NavigationTarget :: from_named (
341342 sema. db ,
342343 fn_source. as_ref ( ) . map ( |it| it as & dyn ast:: HasName ) ,
@@ -345,7 +346,8 @@ pub(crate) fn runnable_fn(
345346 . call_site ( ) ;
346347
347348 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) ;
349351
350352 let cfg = def. attrs ( sema. db ) . cfg ( ) ;
351353 Some ( Runnable { use_name_in_title : false , nav, kind, cfg, update_test } )
@@ -374,13 +376,13 @@ pub(crate) fn runnable_mod(
374376 let cfg = attrs. cfg ( ) ;
375377 let nav = NavigationTarget :: from_module_to_decl ( sema. db , def) . call_site ( ) ;
376378
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 ( ) ,
382384 } ;
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) ;
384386
385387 Some ( Runnable {
386388 use_name_in_title : false ,
@@ -414,9 +416,11 @@ pub(crate) fn runnable_impl(
414416 test_id. retain ( |c| c != ' ' ) ;
415417 let test_id = TestId :: Path ( test_id) ;
416418
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) ;
420424
421425 Some ( Runnable {
422426 use_name_in_title : false ,
@@ -456,13 +460,13 @@ fn runnable_mod_outline_definition(
456460 let attrs = def. attrs ( sema. db ) ;
457461 let cfg = attrs. cfg ( ) ;
458462
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 ( ) ,
464468 } ;
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) ;
466470
467471 Some ( Runnable {
468472 use_name_in_title : false ,
@@ -616,16 +620,93 @@ fn has_test_function_or_multiple_test_submodules(
616620 number_of_test_submodules > 1
617621}
618622
619- struct TestDefs < ' a , ' b > ( & ' a Semantics < ' b , RootDatabase > , hir:: Crate , hir:: FileRange ) ;
620-
621623#[ derive( Debug , Default , Clone , Copy , PartialEq , Eq , Hash ) ]
622624pub struct UpdateTest {
623625 pub expect_test : bool ,
624626 pub insta : bool ,
625627 pub snapbox : bool ,
626628}
627629
630+ static SNAPSHOT_TEST_MACROS : OnceLock < FxHashMap < & str , Vec < ModPath > > > = OnceLock :: new ( ) ;
631+
628632impl 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+
629710 pub fn label ( & self ) -> Option < SmolStr > {
630711 let mut builder: SmallVec < [ _ ; 3 ] > = SmallVec :: new ( ) ;
631712 if self . expect_test {
@@ -646,8 +727,8 @@ impl UpdateTest {
646727 }
647728 }
648729
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 ( ) ;
651732 if self . expect_test {
652733 env. push ( ( "UPDATE_EXPECT" , "1" ) ) ;
653734 }
@@ -661,75 +742,6 @@ impl UpdateTest {
661742 }
662743}
663744
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-
733745#[ cfg( test) ]
734746mod tests {
735747 use expect_test:: { expect, Expect } ;
0 commit comments