1
1
#![ feature( box_patterns) ]
2
+ #![ feature( control_flow_enum) ]
2
3
#![ feature( in_band_lifetimes) ]
3
4
#![ feature( let_else) ]
5
+ #![ feature( once_cell) ]
4
6
#![ feature( rustc_private) ]
5
- #![ feature( control_flow_enum) ]
6
7
#![ recursion_limit = "512" ]
7
8
#![ cfg_attr( feature = "deny-warnings" , deny( warnings) ) ]
8
9
#![ 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
60
61
61
62
use std:: collections:: hash_map:: Entry ;
62
63
use std:: hash:: BuildHasherDefault ;
64
+ use std:: lazy:: SyncOnceCell ;
65
+ use std:: sync:: { Mutex , MutexGuard } ;
63
66
64
67
use if_chain:: if_chain;
65
68
use rustc_ast:: ast:: { self , Attribute , LitKind } ;
69
+ use rustc_data_structures:: fx:: FxHashMap ;
66
70
use rustc_data_structures:: unhash:: UnhashMap ;
67
71
use rustc_hir as hir;
68
72
use rustc_hir:: def:: { DefKind , Res } ;
@@ -87,6 +91,7 @@ use rustc_middle::ty::binding::BindingMode;
87
91
use rustc_middle:: ty:: { layout:: IntegerExt , BorrowKind , DefIdTree , Ty , TyCtxt , TypeAndMut , TypeFoldable , UpvarCapture } ;
88
92
use rustc_semver:: RustcVersion ;
89
93
use rustc_session:: Session ;
94
+ use rustc_span:: def_id:: LocalDefId ;
90
95
use rustc_span:: hygiene:: { ExpnKind , MacroKind } ;
91
96
use rustc_span:: source_map:: original_sp;
92
97
use rustc_span:: sym;
@@ -2139,26 +2144,25 @@ pub fn is_hir_ty_cfg_dependant(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool {
2139
2144
false
2140
2145
}
2141
2146
2142
- struct VisitConstTestStruct < ' tcx > {
2147
+ struct TestItemNamesVisitor < ' tcx > {
2143
2148
tcx : TyCtxt < ' tcx > ,
2144
2149
names : Vec < Symbol > ,
2145
- found : bool ,
2146
2150
}
2147
- impl < ' hir > ItemLikeVisitor < ' hir > for VisitConstTestStruct < ' hir > {
2151
+
2152
+ impl < ' hir > ItemLikeVisitor < ' hir > for TestItemNamesVisitor < ' hir > {
2148
2153
fn visit_item ( & mut self , item : & Item < ' _ > ) {
2149
2154
if let ItemKind :: Const ( ty, _body) = item. kind {
2150
2155
if let TyKind :: Path ( QPath :: Resolved ( _, path) ) = ty. kind {
2151
2156
// We could also check for the type name `test::TestDescAndFn`
2152
- // and the `#[rustc_test_marker]` attribute?
2153
2157
if let Res :: Def ( DefKind :: Struct , _) = path. res {
2154
2158
let has_test_marker = self
2155
2159
. tcx
2156
2160
. hir ( )
2157
2161
. attrs ( item. hir_id ( ) )
2158
2162
. iter ( )
2159
2163
. any ( |a| a. has_name ( sym:: rustc_test_marker) ) ;
2160
- if has_test_marker && self . names . contains ( & item . ident . name ) {
2161
- self . found = true ;
2164
+ if has_test_marker {
2165
+ self . names . push ( item . ident . name ) ;
2162
2166
}
2163
2167
}
2164
2168
}
@@ -2169,32 +2173,42 @@ impl<'hir> ItemLikeVisitor<'hir> for VisitConstTestStruct<'hir> {
2169
2173
fn visit_foreign_item ( & mut self , _: & ForeignItem < ' _ > ) { }
2170
2174
}
2171
2175
2176
+ static TEST_ITEM_NAMES_CACHE : SyncOnceCell < Mutex < FxHashMap < LocalDefId , Vec < Symbol > > > > = SyncOnceCell :: new ( ) ;
2177
+
2178
+ fn with_test_item_names ( tcx : TyCtxt < ' tcx > , module : LocalDefId , f : impl Fn ( & [ Symbol ] ) -> bool ) -> bool {
2179
+ let cache = TEST_ITEM_NAMES_CACHE . get_or_init ( || Mutex :: new ( FxHashMap :: default ( ) ) ) ;
2180
+ let mut map: MutexGuard < ' _ , FxHashMap < LocalDefId , Vec < Symbol > > > = cache. lock ( ) . unwrap ( ) ;
2181
+ match map. entry ( module) {
2182
+ Entry :: Occupied ( entry) => f ( entry. get ( ) ) ,
2183
+ Entry :: Vacant ( entry) => {
2184
+ let mut visitor = TestItemNamesVisitor { tcx, names : Vec :: new ( ) } ;
2185
+ tcx. hir ( ) . visit_item_likes_in_module ( module, & mut visitor) ;
2186
+ visitor. names . sort_unstable ( ) ;
2187
+ f ( & * entry. insert ( visitor. names ) )
2188
+ } ,
2189
+ }
2190
+ }
2191
+
2172
2192
/// Checks if the function containing the given `HirId` is a `#[test]` function
2173
2193
///
2174
2194
/// Note: If you use this function, please add a `#[test]` case in `tests/ui_test`.
2175
2195
pub fn is_in_test_function ( tcx : TyCtxt < ' _ > , id : hir:: HirId ) -> bool {
2176
- let names: Vec < _ > = tcx
2177
- . hir ( )
2178
- . parent_iter ( id)
2179
- // Since you can nest functions we need to collect all until we leave
2180
- // function scope
2181
- . filter_map ( |( _id, node) | {
2182
- if let Node :: Item ( item) = node {
2183
- if let ItemKind :: Fn ( _, _, _) = item. kind {
2184
- return Some ( item. ident . name ) ;
2196
+ with_test_item_names ( tcx, tcx. parent_module ( id) , |names| {
2197
+ tcx. hir ( )
2198
+ . parent_iter ( id)
2199
+ // Since you can nest functions we need to collect all until we leave
2200
+ // function scope
2201
+ . any ( |( _id, node) | {
2202
+ if let Node :: Item ( item) = node {
2203
+ if let ItemKind :: Fn ( _, _, _) = item. kind {
2204
+ // Note that we have sorted the item names in the visitor,
2205
+ // so the binary_search gets the same as `contains`, but faster.
2206
+ return names. binary_search ( & item. ident . name ) . is_ok ( ) ;
2207
+ }
2185
2208
}
2186
- }
2187
- None
2188
- } )
2189
- . collect ( ) ;
2190
- let parent_mod = tcx. parent_module ( id) ;
2191
- let mut vis = VisitConstTestStruct {
2192
- tcx,
2193
- names,
2194
- found : false ,
2195
- } ;
2196
- tcx. hir ( ) . visit_item_likes_in_module ( parent_mod, & mut vis) ;
2197
- vis. found
2209
+ false
2210
+ } )
2211
+ } )
2198
2212
}
2199
2213
2200
2214
/// Checks whether item either has `test` attribute applied, or
0 commit comments