@@ -10,7 +10,7 @@ use rustc_lint::LateContext;
1010use rustc_span:: def_id:: DefId ;
1111use rustc_span:: hygiene:: { self , MacroKind , SyntaxContext } ;
1212use rustc_span:: { sym, BytePos , ExpnData , ExpnId , ExpnKind , Span , SpanData , Symbol } ;
13- use std:: cell:: RefCell ;
13+ use std:: cell:: OnceCell ;
1414use std:: ops:: ControlFlow ;
1515use std:: sync:: atomic:: { AtomicBool , Ordering } ;
1616
@@ -374,28 +374,31 @@ thread_local! {
374374 /// A thread local is used because [`FormatArgs`] is `!Send` and `!Sync`, we are making an
375375 /// assumption that the early pass that populates the map and the later late passes will all be
376376 /// running on the same thread.
377- static AST_FORMAT_ARGS : RefCell <FxHashMap <Span , FormatArgs >> = {
377+ static AST_FORMAT_ARGS : OnceCell <FxHashMap <Span , FormatArgs >> = {
378378 static CALLED : AtomicBool = AtomicBool :: new( false ) ;
379379 debug_assert!(
380380 !CALLED . swap( true , Ordering :: SeqCst ) ,
381381 "incorrect assumption: `AST_FORMAT_ARGS` should only be accessed by a single thread" ,
382382 ) ;
383383
384- RefCell :: default ( )
384+ OnceCell :: new ( )
385385 } ;
386386}
387387
388388/// Record [`rustc_ast::FormatArgs`] for use in late lint passes, this should only be called by
389389/// `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 > ) {
391393 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" ) ;
393396 } ) ;
394397}
395398
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 > {
399402 let format_args_expr = for_each_expr ( start, |expr| {
400403 let ctxt = expr. span . ctxt ( ) ;
401404 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,
410413 } else {
411414 ControlFlow :: Continue ( Descend :: No )
412415 }
413- } ) ;
416+ } ) ? ;
414417
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+ } )
420428}
421429
422430/// Attempt to find the [`rustc_hir::Expr`] that corresponds to the [`FormatArgument`]'s value, if
0 commit comments