@@ -10,7 +10,7 @@ use rustc_lint::LateContext;
10
10
use rustc_span:: def_id:: DefId ;
11
11
use rustc_span:: hygiene:: { self , MacroKind , SyntaxContext } ;
12
12
use rustc_span:: { sym, BytePos , ExpnData , ExpnId , ExpnKind , Span , SpanData , Symbol } ;
13
- use std:: cell:: RefCell ;
13
+ use std:: cell:: OnceCell ;
14
14
use std:: ops:: ControlFlow ;
15
15
use std:: sync:: atomic:: { AtomicBool , Ordering } ;
16
16
@@ -374,28 +374,31 @@ thread_local! {
374
374
/// A thread local is used because [`FormatArgs`] is `!Send` and `!Sync`, we are making an
375
375
/// assumption that the early pass that populates the map and the later late passes will all be
376
376
/// running on the same thread.
377
- static AST_FORMAT_ARGS : RefCell <FxHashMap <Span , FormatArgs >> = {
377
+ static AST_FORMAT_ARGS : OnceCell <FxHashMap <Span , FormatArgs >> = {
378
378
static CALLED : AtomicBool = AtomicBool :: new( false ) ;
379
379
debug_assert!(
380
380
!CALLED . swap( true , Ordering :: SeqCst ) ,
381
381
"incorrect assumption: `AST_FORMAT_ARGS` should only be accessed by a single thread" ,
382
382
) ;
383
383
384
- RefCell :: default ( )
384
+ OnceCell :: new ( )
385
385
} ;
386
386
}
387
387
388
388
/// Record [`rustc_ast::FormatArgs`] for use in late lint passes, this should only be called by
389
389
/// `FormatArgsCollector`
390
- pub fn collect_ast_format_args ( span : Span , format_args : & FormatArgs ) {
390
+ #[ allow( clippy:: implicit_hasher) ]
391
+ #[ doc( hidden) ]
392
+ pub fn collect_ast_format_args ( format_args : FxHashMap < Span , FormatArgs > ) {
391
393
AST_FORMAT_ARGS . with ( |ast_format_args| {
392
- ast_format_args. borrow_mut ( ) . insert ( span, format_args. clone ( ) ) ;
394
+ let result = ast_format_args. set ( format_args) ;
395
+ debug_assert ! ( result. is_ok( ) , "`collect_ast_format_args` should only be called once" ) ;
393
396
} ) ;
394
397
}
395
398
396
- /// Calls `callback` with an AST [`FormatArgs`] node if a `format_args` expansion is found as a
397
- /// descendant of `expn_id`
398
- pub fn find_format_args ( cx : & LateContext < ' _ > , start : & Expr < ' _ > , expn_id : ExpnId , callback : impl FnOnce ( & FormatArgs ) ) {
399
+ /// Returns an AST [`FormatArgs`] node if a `format_args` expansion is found as a descendant of
400
+ /// `expn_id`
401
+ pub fn find_format_args < ' a > ( cx : & ' a LateContext < ' _ > , start : & Expr < ' _ > , expn_id : ExpnId ) -> Option < & ' a FormatArgs > {
399
402
let format_args_expr = for_each_expr ( start, |expr| {
400
403
let ctxt = expr. span . ctxt ( ) ;
401
404
if ctxt. outer_expn ( ) . is_descendant_of ( expn_id) {
@@ -410,13 +413,18 @@ pub fn find_format_args(cx: &LateContext<'_>, start: &Expr<'_>, expn_id: ExpnId,
410
413
} else {
411
414
ControlFlow :: Continue ( Descend :: No )
412
415
}
413
- } ) ;
416
+ } ) ? ;
414
417
415
- if let Some ( expr) = format_args_expr {
416
- AST_FORMAT_ARGS . with ( |ast_format_args| {
417
- ast_format_args. borrow ( ) . get ( & expr. span ) . map ( callback) ;
418
- } ) ;
419
- }
418
+ AST_FORMAT_ARGS . with ( |ast_format_args| {
419
+ let found = ast_format_args. get ( ) ?. get ( & format_args_expr. span ) ?;
420
+
421
+ // SAFETY: the lifetime is 'thread which isn't nameable, so we instead use the lifetime of the
422
+ // reference to the `LateContext`
423
+ //
424
+ // It's possible for a `LateContext` that outlives the thread to exist e.g. with `Box::leak`, but
425
+ // only `rustc_lint` can construct a `LateContext` and it does not do that
426
+ unsafe { Some ( std:: mem:: transmute :: < & FormatArgs , & ' a FormatArgs > ( found) ) }
427
+ } )
420
428
}
421
429
422
430
/// Attempt to find the [`rustc_hir::Expr`] that corresponds to the [`FormatArgument`]'s value, if
0 commit comments