|
1 | 1 | use clippy_utils::diagnostics::span_lint_and_then; |
2 | 2 | use clippy_utils::ty::is_type_diagnostic_item; |
3 | 3 | use clippy_utils::visitors::for_each_expr_without_closures; |
4 | | -use clippy_utils::{higher, SpanlessEq}; |
| 4 | +use clippy_utils::{eq_expr_value, higher}; |
5 | 5 | use core::ops::ControlFlow; |
6 | 6 | use rustc_errors::Diag; |
7 | 7 | use rustc_hir::{Expr, ExprKind}; |
@@ -51,53 +51,45 @@ impl<'tcx> LateLintPass<'tcx> for IfLetMutex { |
51 | 51 | if_else: Some(if_else), |
52 | 52 | .. |
53 | 53 | }) = higher::IfLet::hir(cx, expr) |
| 54 | + && let Some(op_mutex) = for_each_expr_without_closures(let_expr, |e| mutex_lock_call(cx, e, None)) |
| 55 | + && let Some(arm_mutex) = |
| 56 | + for_each_expr_without_closures((if_then, if_else), |e| mutex_lock_call(cx, e, Some(op_mutex))) |
54 | 57 | { |
55 | | - let is_mutex_lock = |e: &'tcx Expr<'tcx>| { |
56 | | - if let Some(mutex) = is_mutex_lock_call(cx, e) { |
57 | | - ControlFlow::Break(mutex) |
58 | | - } else { |
59 | | - ControlFlow::Continue(()) |
60 | | - } |
| 58 | + let diag = |diag: &mut Diag<'_, ()>| { |
| 59 | + diag.span_label( |
| 60 | + op_mutex.span, |
| 61 | + "this Mutex will remain locked for the entire `if let`-block...", |
| 62 | + ); |
| 63 | + diag.span_label( |
| 64 | + arm_mutex.span, |
| 65 | + "... and is tried to lock again here, which will always deadlock.", |
| 66 | + ); |
| 67 | + diag.help("move the lock call outside of the `if let ...` expression"); |
61 | 68 | }; |
62 | | - |
63 | | - let op_mutex = for_each_expr_without_closures(let_expr, is_mutex_lock); |
64 | | - if let Some(op_mutex) = op_mutex { |
65 | | - let arm_mutex = for_each_expr_without_closures((if_then, if_else), is_mutex_lock); |
66 | | - if let Some(arm_mutex) = arm_mutex |
67 | | - && SpanlessEq::new(cx).eq_expr(op_mutex, arm_mutex) |
68 | | - { |
69 | | - let diag = |diag: &mut Diag<'_, ()>| { |
70 | | - diag.span_label( |
71 | | - op_mutex.span, |
72 | | - "this Mutex will remain locked for the entire `if let`-block...", |
73 | | - ); |
74 | | - diag.span_label( |
75 | | - arm_mutex.span, |
76 | | - "... and is tried to lock again here, which will always deadlock.", |
77 | | - ); |
78 | | - diag.help("move the lock call outside of the `if let ...` expression"); |
79 | | - }; |
80 | | - span_lint_and_then( |
81 | | - cx, |
82 | | - IF_LET_MUTEX, |
83 | | - expr.span, |
84 | | - "calling `Mutex::lock` inside the scope of another `Mutex::lock` causes a deadlock", |
85 | | - diag, |
86 | | - ); |
87 | | - } |
88 | | - } |
| 69 | + span_lint_and_then( |
| 70 | + cx, |
| 71 | + IF_LET_MUTEX, |
| 72 | + expr.span, |
| 73 | + "calling `Mutex::lock` inside the scope of another `Mutex::lock` causes a deadlock", |
| 74 | + diag, |
| 75 | + ); |
89 | 76 | } |
90 | 77 | } |
91 | 78 | } |
92 | 79 |
|
93 | | -fn is_mutex_lock_call<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { |
| 80 | +fn mutex_lock_call<'tcx>( |
| 81 | + cx: &LateContext<'tcx>, |
| 82 | + expr: &'tcx Expr<'_>, |
| 83 | + op_mutex: Option<&'tcx Expr<'_>>, |
| 84 | +) -> ControlFlow<&'tcx Expr<'tcx>> { |
94 | 85 | if let ExprKind::MethodCall(path, self_arg, ..) = &expr.kind |
95 | 86 | && path.ident.as_str() == "lock" |
96 | 87 | && let ty = cx.typeck_results().expr_ty(self_arg).peel_refs() |
97 | 88 | && is_type_diagnostic_item(cx, ty, sym::Mutex) |
| 89 | + && op_mutex.map_or(true, |op| eq_expr_value(cx, self_arg, op)) |
98 | 90 | { |
99 | | - Some(self_arg) |
| 91 | + ControlFlow::Break(self_arg) |
100 | 92 | } else { |
101 | | - None |
| 93 | + ControlFlow::Continue(()) |
102 | 94 | } |
103 | 95 | } |
0 commit comments