1
1
use clippy_config:: msrvs:: Msrv ;
2
2
use clippy_utils:: diagnostics:: span_lint_and_sugg;
3
- use clippy_utils:: fn_has_unsatisfiable_preds;
4
3
use clippy_utils:: qualify_min_const_fn:: is_min_const_fn;
5
4
use clippy_utils:: source:: snippet;
5
+ use clippy_utils:: { fn_has_unsatisfiable_preds, peel_blocks} ;
6
6
use rustc_errors:: Applicability ;
7
7
use 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 } ;
10
9
use rustc_session:: impl_lint_pass;
11
10
use rustc_span:: sym:: thread_local_macro;
12
11
@@ -57,6 +56,31 @@ impl ThreadLocalInitializerCanBeMadeConst {
57
56
58
57
impl_lint_pass ! ( ThreadLocalInitializerCanBeMadeConst => [ THREAD_LOCAL_INITIALIZER_CAN_BE_MADE_CONST ] ) ;
59
58
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
+
60
84
impl < ' tcx > LateLintPass < ' tcx > for ThreadLocalInitializerCanBeMadeConst {
61
85
fn check_fn (
62
86
& mut self ,
@@ -65,31 +89,31 @@ impl<'tcx> LateLintPass<'tcx> for ThreadLocalInitializerCanBeMadeConst {
65
89
_: & ' tcx rustc_hir:: FnDecl < ' tcx > ,
66
90
body : & ' tcx rustc_hir:: Body < ' tcx > ,
67
91
span : rustc_span:: Span ,
68
- defid : rustc_span:: def_id:: LocalDefId ,
92
+ local_defid : rustc_span:: def_id:: LocalDefId ,
69
93
) {
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.
84
107
&& 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)
86
110
&& let initializer_snippet = snippet ( cx, ret_expr. span , "thread_local! { ... }" )
87
111
&& initializer_snippet != "thread_local! { ... }"
88
112
{
89
113
span_lint_and_sugg (
90
114
cx,
91
115
THREAD_LOCAL_INITIALIZER_CAN_BE_MADE_CONST ,
92
- ret_expr . span ,
116
+ unpeeled . span ,
93
117
"initializer for `thread_local` value can be made `const`" ,
94
118
"replace with" ,
95
119
format ! ( "const {{ {initializer_snippet} }}" ) ,
0 commit comments