|
1 | 1 | use clippy_config::msrvs::{self, Msrv};
|
2 | 2 | use clippy_utils::diagnostics::span_lint_and_sugg;
|
| 3 | +use clippy_utils::macros::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 |
| -use rustc_span::sym::thread_local_macro; |
| 11 | +use rustc_span::sym::{self, thread_local_macro}; |
11 | 12 |
|
12 | 13 | declare_clippy_lint! {
|
13 | 14 | /// ### What it does
|
@@ -69,6 +70,26 @@ fn is_thread_local_initializer(
|
69 | 70 | )
|
70 | 71 | }
|
71 | 72 |
|
| 73 | +fn is_unreachable(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { |
| 74 | + if let Some(macro_call) = macro_backtrace(expr.span).next() |
| 75 | + && let Some(diag_name) = cx.tcx.get_diagnostic_name(macro_call.def_id) |
| 76 | + { |
| 77 | + return (matches!( |
| 78 | + diag_name, |
| 79 | + sym::core_panic_macro |
| 80 | + | sym::std_panic_macro |
| 81 | + | sym::core_panic_2015_macro |
| 82 | + | sym::std_panic_2015_macro |
| 83 | + | sym::core_panic_2021_macro |
| 84 | + ) && !cx.tcx.hir().is_inside_const_context(expr.hir_id)) |
| 85 | + || matches!( |
| 86 | + diag_name, |
| 87 | + sym::unimplemented_macro | sym::todo_macro | sym::unreachable_macro | sym::unreachable_2015_macro |
| 88 | + ); |
| 89 | + } |
| 90 | + false |
| 91 | +} |
| 92 | + |
72 | 93 | #[inline]
|
73 | 94 | fn initializer_can_be_made_const(cx: &LateContext<'_>, defid: rustc_span::def_id::DefId, msrv: &Msrv) -> bool {
|
74 | 95 | // Building MIR for `fn`s with unsatisfiable preds results in ICE.
|
@@ -102,12 +123,17 @@ impl<'tcx> LateLintPass<'tcx> for ThreadLocalInitializerCanBeMadeConst {
|
102 | 123 | // for details on this issue, see:
|
103 | 124 | // https://github.com/rust-lang/rust-clippy/pull/12276
|
104 | 125 | && !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 | 126 | && let ExprKind::Block(block, _) = body.value.kind
|
109 | 127 | && let Some(unpeeled) = block.expr
|
110 | 128 | && let ret_expr = peel_blocks(unpeeled)
|
| 129 | + // A common pattern around threadlocal! is to make the value unreachable |
| 130 | + // to force an initialization before usage |
| 131 | + // https://github.com/rust-lang/rust-clippy/issues/12637 |
| 132 | + // we ensure that this is reachable before we check in mir |
| 133 | + && !is_unreachable(cx, ret_expr) |
| 134 | + && initializer_can_be_made_const(cx, defid, &self.msrv) |
| 135 | + // we know that the function is const-qualifiable, so now |
| 136 | + // we need only to get the initializer expression to span-lint it. |
111 | 137 | && let initializer_snippet = snippet(cx, ret_expr.span, "thread_local! { ... }")
|
112 | 138 | && initializer_snippet != "thread_local! { ... }"
|
113 | 139 | {
|
|
0 commit comments