11#![ feature( box_patterns) ]
2+ #![ feature( control_flow_enum) ]
23#![ feature( in_band_lifetimes) ]
34#![ feature( let_else) ]
5+ #![ feature( once_cell) ]
46#![ feature( rustc_private) ]
5- #![ feature( control_flow_enum) ]
67#![ recursion_limit = "512" ]
78#![ cfg_attr( feature = "deny-warnings" , deny( warnings) ) ]
89#![ allow( clippy:: missing_errors_doc, clippy:: missing_panics_doc, clippy:: must_use_candidate) ]
@@ -60,9 +61,12 @@ pub use self::hir_utils::{both, count_eq, eq_expr_value, over, SpanlessEq, Spanl
6061
6162use std:: collections:: hash_map:: Entry ;
6263use std:: hash:: BuildHasherDefault ;
64+ use std:: lazy:: SyncOnceCell ;
65+ use std:: sync:: { Mutex , MutexGuard } ;
6366
6467use if_chain:: if_chain;
6568use rustc_ast:: ast:: { self , Attribute , LitKind } ;
69+ use rustc_data_structures:: fx:: FxHashMap ;
6670use rustc_data_structures:: unhash:: UnhashMap ;
6771use rustc_hir as hir;
6872use rustc_hir:: def:: { DefKind , Res } ;
@@ -87,6 +91,7 @@ use rustc_middle::ty::binding::BindingMode;
8791use rustc_middle:: ty:: { layout:: IntegerExt , BorrowKind , DefIdTree , Ty , TyCtxt , TypeAndMut , TypeFoldable , UpvarCapture } ;
8892use rustc_semver:: RustcVersion ;
8993use rustc_session:: Session ;
94+ use rustc_span:: def_id:: LocalDefId ;
9095use rustc_span:: hygiene:: { ExpnKind , MacroKind } ;
9196use rustc_span:: source_map:: original_sp;
9297use rustc_span:: sym;
@@ -2132,26 +2137,25 @@ pub fn is_hir_ty_cfg_dependant(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool {
21322137 false
21332138}
21342139
2135- struct VisitConstTestStruct < ' tcx > {
2140+ struct TestItemNamesVisitor < ' tcx > {
21362141 tcx : TyCtxt < ' tcx > ,
21372142 names : Vec < Symbol > ,
2138- found : bool ,
21392143}
2140- impl < ' hir > ItemLikeVisitor < ' hir > for VisitConstTestStruct < ' hir > {
2144+
2145+ impl < ' hir > ItemLikeVisitor < ' hir > for TestItemNamesVisitor < ' hir > {
21412146 fn visit_item ( & mut self , item : & Item < ' _ > ) {
21422147 if let ItemKind :: Const ( ty, _body) = item. kind {
21432148 if let TyKind :: Path ( QPath :: Resolved ( _, path) ) = ty. kind {
21442149 // We could also check for the type name `test::TestDescAndFn`
2145- // and the `#[rustc_test_marker]` attribute?
21462150 if let Res :: Def ( DefKind :: Struct , _) = path. res {
21472151 let has_test_marker = self
21482152 . tcx
21492153 . hir ( )
21502154 . attrs ( item. hir_id ( ) )
21512155 . iter ( )
21522156 . any ( |a| a. has_name ( sym:: rustc_test_marker) ) ;
2153- if has_test_marker && self . names . contains ( & item . ident . name ) {
2154- self . found = true ;
2157+ if has_test_marker {
2158+ self . names . push ( item . ident . name ) ;
21552159 }
21562160 }
21572161 }
@@ -2162,32 +2166,42 @@ impl<'hir> ItemLikeVisitor<'hir> for VisitConstTestStruct<'hir> {
21622166 fn visit_foreign_item ( & mut self , _: & ForeignItem < ' _ > ) { }
21632167}
21642168
2169+ static TEST_ITEM_NAMES_CACHE : SyncOnceCell < Mutex < FxHashMap < LocalDefId , Vec < Symbol > > > > = SyncOnceCell :: new ( ) ;
2170+
2171+ fn with_test_item_names ( tcx : TyCtxt < ' tcx > , module : LocalDefId , f : impl Fn ( & [ Symbol ] ) -> bool ) -> bool {
2172+ let cache = TEST_ITEM_NAMES_CACHE . get_or_init ( || Mutex :: new ( FxHashMap :: default ( ) ) ) ;
2173+ let mut map: MutexGuard < ' _ , FxHashMap < LocalDefId , Vec < Symbol > > > = cache. lock ( ) . unwrap ( ) ;
2174+ match map. entry ( module) {
2175+ Entry :: Occupied ( entry) => f ( entry. get ( ) ) ,
2176+ Entry :: Vacant ( entry) => {
2177+ let mut visitor = TestItemNamesVisitor { tcx, names : Vec :: new ( ) } ;
2178+ tcx. hir ( ) . visit_item_likes_in_module ( module, & mut visitor) ;
2179+ visitor. names . sort_unstable ( ) ;
2180+ f ( & * entry. insert ( visitor. names ) )
2181+ } ,
2182+ }
2183+ }
2184+
21652185/// Checks if the function containing the given `HirId` is a `#[test]` function
21662186///
21672187/// Note: If you use this function, please add a `#[test]` case in `tests/ui_test`.
21682188pub fn is_in_test_function ( tcx : TyCtxt < ' _ > , id : hir:: HirId ) -> bool {
2169- let names: Vec < _ > = tcx
2170- . hir ( )
2171- . parent_iter ( id)
2172- // Since you can nest functions we need to collect all until we leave
2173- // function scope
2174- . filter_map ( |( _id, node) | {
2175- if let Node :: Item ( item) = node {
2176- if let ItemKind :: Fn ( _, _, _) = item. kind {
2177- return Some ( item. ident . name ) ;
2189+ with_test_item_names ( tcx, tcx. parent_module ( id) , |names| {
2190+ tcx. hir ( )
2191+ . parent_iter ( id)
2192+ // Since you can nest functions we need to collect all until we leave
2193+ // function scope
2194+ . any ( |( _id, node) | {
2195+ if let Node :: Item ( item) = node {
2196+ if let ItemKind :: Fn ( _, _, _) = item. kind {
2197+ // Note that we have sorted the item names in the visitor,
2198+ // so the binary_search gets the same as `contains`, but faster.
2199+ return names. binary_search ( & item. ident . name ) . is_ok ( ) ;
2200+ }
21782201 }
2179- }
2180- None
2181- } )
2182- . collect ( ) ;
2183- let parent_mod = tcx. parent_module ( id) ;
2184- let mut vis = VisitConstTestStruct {
2185- tcx,
2186- names,
2187- found : false ,
2188- } ;
2189- tcx. hir ( ) . visit_item_likes_in_module ( parent_mod, & mut vis) ;
2190- vis. found
2202+ false
2203+ } )
2204+ } )
21912205}
21922206
21932207/// Checks whether item either has `test` attribute applied, or
0 commit comments