1
- use clippy_utils:: diagnostics:: span_lint_and_help ;
1
+ use clippy_utils:: diagnostics:: span_lint_and_then ;
2
2
use clippy_utils:: higher;
3
3
use clippy_utils:: ty:: is_type_diagnostic_item;
4
4
use clippy_utils:: SpanlessEq ;
5
5
use if_chain:: if_chain;
6
+ use rustc_errors:: Diagnostic ;
6
7
use rustc_hir:: intravisit:: { self as visit, Visitor } ;
7
8
use rustc_hir:: { Expr , ExprKind } ;
8
9
use rustc_lint:: { LateContext , LateLintPass } ;
@@ -45,16 +46,8 @@ declare_lint_pass!(IfLetMutex => [IF_LET_MUTEX]);
45
46
46
47
impl < ' tcx > LateLintPass < ' tcx > for IfLetMutex {
47
48
fn check_expr ( & mut self , cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' tcx > ) {
48
- let mut arm_visit = ArmVisitor {
49
- mutex_lock_called : false ,
50
- found_mutex : None ,
51
- cx,
52
- } ;
53
- let mut op_visit = OppVisitor {
54
- mutex_lock_called : false ,
55
- found_mutex : None ,
56
- cx,
57
- } ;
49
+ let mut arm_visit = ArmVisitor { found_mutex : None , cx } ;
50
+ let mut op_visit = OppVisitor { found_mutex : None , cx } ;
58
51
if let Some ( higher:: IfLet {
59
52
let_expr,
60
53
if_then,
@@ -63,18 +56,28 @@ impl<'tcx> LateLintPass<'tcx> for IfLetMutex {
63
56
} ) = higher:: IfLet :: hir ( cx, expr)
64
57
{
65
58
op_visit. visit_expr ( let_expr) ;
66
- if op_visit. mutex_lock_called {
59
+ if let Some ( op_mutex ) = op_visit. found_mutex {
67
60
arm_visit. visit_expr ( if_then) ;
68
61
arm_visit. visit_expr ( if_else) ;
69
62
70
- if arm_visit. mutex_lock_called && arm_visit. same_mutex ( cx, op_visit. found_mutex . unwrap ( ) ) {
71
- span_lint_and_help (
63
+ if let Some ( arm_mutex) = arm_visit. found_mutex_if_same_as ( op_mutex) {
64
+ let diag = |diag : & mut Diagnostic | {
65
+ diag. span_label (
66
+ op_mutex. span ,
67
+ "this Mutex will remain locked for the entire `if let`-block..." ,
68
+ ) ;
69
+ diag. span_label (
70
+ arm_mutex. span ,
71
+ "... and is tried to lock again here, which will always deadlock." ,
72
+ ) ;
73
+ diag. help ( "move the lock call outside of the `if let ...` expression" ) ;
74
+ } ;
75
+ span_lint_and_then (
72
76
cx,
73
77
IF_LET_MUTEX ,
74
78
expr. span ,
75
79
"calling `Mutex::lock` inside the scope of another `Mutex::lock` causes a deadlock" ,
76
- None ,
77
- "move the lock call outside of the `if let ...` expression" ,
80
+ diag,
78
81
) ;
79
82
}
80
83
}
@@ -84,7 +87,6 @@ impl<'tcx> LateLintPass<'tcx> for IfLetMutex {
84
87
85
88
/// Checks if `Mutex::lock` is called in the `if let` expr.
86
89
pub struct OppVisitor < ' a , ' tcx > {
87
- mutex_lock_called : bool ,
88
90
found_mutex : Option < & ' tcx Expr < ' tcx > > ,
89
91
cx : & ' a LateContext < ' tcx > ,
90
92
}
@@ -93,7 +95,6 @@ impl<'tcx> Visitor<'tcx> for OppVisitor<'_, 'tcx> {
93
95
fn visit_expr ( & mut self , expr : & ' tcx Expr < ' _ > ) {
94
96
if let Some ( mutex) = is_mutex_lock_call ( self . cx , expr) {
95
97
self . found_mutex = Some ( mutex) ;
96
- self . mutex_lock_called = true ;
97
98
return ;
98
99
}
99
100
visit:: walk_expr ( self , expr) ;
@@ -102,7 +103,6 @@ impl<'tcx> Visitor<'tcx> for OppVisitor<'_, 'tcx> {
102
103
103
104
/// Checks if `Mutex::lock` is called in any of the branches.
104
105
pub struct ArmVisitor < ' a , ' tcx > {
105
- mutex_lock_called : bool ,
106
106
found_mutex : Option < & ' tcx Expr < ' tcx > > ,
107
107
cx : & ' a LateContext < ' tcx > ,
108
108
}
@@ -111,25 +111,27 @@ impl<'tcx> Visitor<'tcx> for ArmVisitor<'_, 'tcx> {
111
111
fn visit_expr ( & mut self , expr : & ' tcx Expr < ' tcx > ) {
112
112
if let Some ( mutex) = is_mutex_lock_call ( self . cx , expr) {
113
113
self . found_mutex = Some ( mutex) ;
114
- self . mutex_lock_called = true ;
115
114
return ;
116
115
}
117
116
visit:: walk_expr ( self , expr) ;
118
117
}
119
118
}
120
119
121
120
impl < ' tcx , ' l > ArmVisitor < ' tcx , ' l > {
122
- fn same_mutex ( & self , cx : & LateContext < ' _ > , op_mutex : & Expr < ' _ > ) -> bool {
123
- self . found_mutex
124
- . map_or ( false , |arm_mutex| SpanlessEq :: new ( cx) . eq_expr ( op_mutex, arm_mutex) )
121
+ fn found_mutex_if_same_as ( & self , op_mutex : & Expr < ' _ > ) -> Option < & Expr < ' _ > > {
122
+ self . found_mutex . and_then ( |arm_mutex| {
123
+ SpanlessEq :: new ( self . cx )
124
+ . eq_expr ( op_mutex, arm_mutex)
125
+ . then_some ( arm_mutex)
126
+ } )
125
127
}
126
128
}
127
129
128
130
fn is_mutex_lock_call < ' tcx > ( cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' _ > ) -> Option < & ' tcx Expr < ' tcx > > {
129
131
if_chain ! {
130
132
if let ExprKind :: MethodCall ( path, [ self_arg, ..] , _) = & expr. kind;
131
133
if path. ident. as_str( ) == "lock" ;
132
- let ty = cx. typeck_results( ) . expr_ty( self_arg) ;
134
+ let ty = cx. typeck_results( ) . expr_ty( self_arg) . peel_refs ( ) ;
133
135
if is_type_diagnostic_item( cx, ty, sym:: Mutex ) ;
134
136
then {
135
137
Some ( self_arg)
0 commit comments