11use clippy_config:: msrvs:: Msrv ;
22use clippy_utils:: diagnostics:: span_lint_and_sugg;
3- use clippy_utils:: fn_has_unsatisfiable_preds;
43use clippy_utils:: qualify_min_const_fn:: is_min_const_fn;
54use clippy_utils:: source:: snippet;
5+ use clippy_utils:: { fn_has_unsatisfiable_preds, peel_blocks} ;
66use rustc_errors:: Applicability ;
77use rustc_hir:: { intravisit, ExprKind } ;
8- use rustc_lint:: { LateContext , LateLintPass , LintContext } ;
9- use rustc_middle:: lint:: in_external_macro;
8+ use rustc_lint:: { LateContext , LateLintPass } ;
109use rustc_session:: impl_lint_pass;
1110use rustc_span:: sym:: thread_local_macro;
1211
@@ -57,6 +56,31 @@ impl ThreadLocalInitializerCanBeMadeConst {
5756
5857impl_lint_pass ! ( ThreadLocalInitializerCanBeMadeConst => [ THREAD_LOCAL_INITIALIZER_CAN_BE_MADE_CONST ] ) ;
5958
59+ #[ inline]
60+ fn is_thread_local_initializer (
61+ cx : & LateContext < ' _ > ,
62+ fn_kind : rustc_hir:: intravisit:: FnKind < ' _ > ,
63+ span : rustc_span:: Span ,
64+ ) -> Option < bool > {
65+ let macro_def_id = span. source_callee ( ) ?. macro_def_id ?;
66+ Some (
67+ cx. tcx . is_diagnostic_item ( thread_local_macro, macro_def_id)
68+ && matches ! ( fn_kind, intravisit:: FnKind :: ItemFn ( ..) ) ,
69+ )
70+ }
71+
72+ #[ inline]
73+ fn initializer_can_be_made_const ( cx : & LateContext < ' _ > , defid : rustc_span:: def_id:: DefId , msrv : & Msrv ) -> bool {
74+ // Building MIR for `fn`s with unsatisfiable preds results in ICE.
75+ if !fn_has_unsatisfiable_preds ( cx, defid)
76+ && let mir = cx. tcx . optimized_mir ( defid)
77+ && let Ok ( ( ) ) = is_min_const_fn ( cx. tcx , mir, msrv)
78+ {
79+ return true ;
80+ }
81+ false
82+ }
83+
6084impl < ' tcx > LateLintPass < ' tcx > for ThreadLocalInitializerCanBeMadeConst {
6185 fn check_fn (
6286 & mut self ,
@@ -65,31 +89,31 @@ impl<'tcx> LateLintPass<'tcx> for ThreadLocalInitializerCanBeMadeConst {
6589 _: & ' tcx rustc_hir:: FnDecl < ' tcx > ,
6690 body : & ' tcx rustc_hir:: Body < ' tcx > ,
6791 span : rustc_span:: Span ,
68- defid : rustc_span:: def_id:: LocalDefId ,
92+ local_defid : rustc_span:: def_id:: LocalDefId ,
6993 ) {
70- if in_external_macro ( cx. sess ( ) , span)
71- && let Some ( callee) = span. source_callee ( )
72- && let Some ( macro_def_id) = callee. macro_def_id
73- && cx. tcx . is_diagnostic_item ( thread_local_macro, macro_def_id)
74- && let intravisit:: FnKind :: ItemFn ( ..) = fn_kind
75- // Building MIR for `fn`s with unsatisfiable preds results in ICE.
76- && !fn_has_unsatisfiable_preds ( cx, defid. to_def_id ( ) )
77- && let mir = cx. tcx . optimized_mir ( defid. to_def_id ( ) )
78- && let Ok ( ( ) ) = is_min_const_fn ( cx. tcx , mir, & self . msrv )
79- // this is the `__init` function emitted by the `thread_local!` macro
80- // when the `const` keyword is not used. We avoid checking the `__init` directly
81- // as that is not a public API.
82- // we know that the function is const-qualifiable, so now we need only to get the
83- // initializer expression to span-lint it.
94+ let defid = local_defid. to_def_id ( ) ;
95+ if is_thread_local_initializer ( cx, fn_kind, span) . unwrap_or ( false )
96+ // Some implementations of `thread_local!` include an initializer fn.
97+ // In the case of a const initializer, the init fn is also const,
98+ // so we can skip the lint in that case. This occurs only on some
99+ // backends due to conditional compilation:
100+ // https://doc.rust-lang.org/src/std/sys/common/thread_local/mod.rs.html
101+ // for details on this issue, see:
102+ // https://github.com/rust-lang/rust-clippy/pull/12276
103+ && !cx. tcx . is_const_fn ( defid)
104+ && initializer_can_be_made_const ( cx, defid, & self . msrv )
105+ // we know that the function is const-qualifiable, so now
106+ // we need only to get the initializer expression to span-lint it.
84107 && let ExprKind :: Block ( block, _) = body. value . kind
85- && let Some ( ret_expr) = block. expr
108+ && let Some ( unpeeled) = block. expr
109+ && let ret_expr = peel_blocks ( unpeeled)
86110 && let initializer_snippet = snippet ( cx, ret_expr. span , "thread_local! { ... }" )
87111 && initializer_snippet != "thread_local! { ... }"
88112 {
89113 span_lint_and_sugg (
90114 cx,
91115 THREAD_LOCAL_INITIALIZER_CAN_BE_MADE_CONST ,
92- ret_expr . span ,
116+ unpeeled . span ,
93117 "initializer for `thread_local` value can be made `const`" ,
94118 "replace with" ,
95119 format ! ( "const {{ {initializer_snippet} }}" ) ,
0 commit comments