|
1 | 1 | use clippy_config::msrvs::{self, Msrv};
|
2 | 2 | use clippy_utils::diagnostics::span_lint_and_sugg;
|
| 3 | +use clippy_utils::macros::{is_panic, macro_backtrace}; |
3 | 4 | use clippy_utils::qualify_min_const_fn::is_min_const_fn;
|
4 | 5 | use clippy_utils::source::snippet;
|
5 | 6 | use clippy_utils::{fn_has_unsatisfiable_preds, peel_blocks};
|
6 | 7 | use rustc_errors::Applicability;
|
7 |
| -use rustc_hir::{intravisit, ExprKind}; |
| 8 | +use rustc_hir::{intravisit, Expr, ExprKind}; |
8 | 9 | use rustc_lint::{LateContext, LateLintPass};
|
9 | 10 | use rustc_session::impl_lint_pass;
|
10 | 11 | use rustc_span::sym::thread_local_macro;
|
@@ -69,6 +70,19 @@ fn is_thread_local_initializer(
|
69 | 70 | )
|
70 | 71 | }
|
71 | 72 |
|
| 73 | +fn is_unreachable(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { |
| 74 | + let Some(macro_call) = macro_backtrace(expr.span).next() else { |
| 75 | + return false; |
| 76 | + }; |
| 77 | + if is_panic(cx, macro_call.def_id) { |
| 78 | + return !cx.tcx.hir().is_inside_const_context(expr.hir_id); |
| 79 | + } |
| 80 | + matches!( |
| 81 | + cx.tcx.item_name(macro_call.def_id).as_str(), |
| 82 | + "todo" | "unimplemented" | "unreachable" |
| 83 | + ) |
| 84 | +} |
| 85 | + |
72 | 86 | #[inline]
|
73 | 87 | fn initializer_can_be_made_const(cx: &LateContext<'_>, defid: rustc_span::def_id::DefId, msrv: &Msrv) -> bool {
|
74 | 88 | // Building MIR for `fn`s with unsatisfiable preds results in ICE.
|
@@ -102,12 +116,17 @@ impl<'tcx> LateLintPass<'tcx> for ThreadLocalInitializerCanBeMadeConst {
|
102 | 116 | // for details on this issue, see:
|
103 | 117 | // https://github.com/rust-lang/rust-clippy/pull/12276
|
104 | 118 | && !cx.tcx.is_const_fn(defid)
|
105 |
| - && initializer_can_be_made_const(cx, defid, &self.msrv) |
106 |
| - // we know that the function is const-qualifiable, so now |
107 |
| - // we need only to get the initializer expression to span-lint it. |
108 | 119 | && let ExprKind::Block(block, _) = body.value.kind
|
109 | 120 | && let Some(unpeeled) = block.expr
|
110 | 121 | && let ret_expr = peel_blocks(unpeeled)
|
| 122 | + // A common pattern around threadlocal! is to make the value unreachable |
| 123 | + // to force an initialization before usage |
| 124 | + // https://github.com/rust-lang/rust-clippy/issues/12637 |
| 125 | + // we ensure that this is reachable before we check in mir |
| 126 | + && !is_unreachable(cx, ret_expr) |
| 127 | + && initializer_can_be_made_const(cx, defid, &self.msrv) |
| 128 | + // we know that the function is const-qualifiable, so now |
| 129 | + // we need only to get the initializer expression to span-lint it. |
111 | 130 | && let initializer_snippet = snippet(cx, ret_expr.span, "thread_local! { ... }")
|
112 | 131 | && initializer_snippet != "thread_local! { ... }"
|
113 | 132 | {
|
|
0 commit comments