1- use clippy_utils:: diagnostics:: span_lint_and_help ;
1+ use clippy_utils:: diagnostics:: span_lint_and_then ;
22use clippy_utils:: higher;
33use clippy_utils:: ty:: is_type_diagnostic_item;
44use clippy_utils:: SpanlessEq ;
55use if_chain:: if_chain;
6+ use rustc_errors:: Diagnostic ;
67use rustc_hir:: intravisit:: { self as visit, Visitor } ;
78use rustc_hir:: { Expr , ExprKind } ;
89use rustc_lint:: { LateContext , LateLintPass } ;
@@ -45,16 +46,8 @@ declare_lint_pass!(IfLetMutex => [IF_LET_MUTEX]);
4546
4647impl < ' tcx > LateLintPass < ' tcx > for IfLetMutex {
4748 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 } ;
5851 if let Some ( higher:: IfLet {
5952 let_expr,
6053 if_then,
@@ -63,18 +56,28 @@ impl<'tcx> LateLintPass<'tcx> for IfLetMutex {
6356 } ) = higher:: IfLet :: hir ( cx, expr)
6457 {
6558 op_visit. visit_expr ( let_expr) ;
66- if op_visit. mutex_lock_called {
59+ if let Some ( op_mutex ) = op_visit. found_mutex {
6760 arm_visit. visit_expr ( if_then) ;
6861 arm_visit. visit_expr ( if_else) ;
6962
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 (
7276 cx,
7377 IF_LET_MUTEX ,
7478 expr. span ,
7579 "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,
7881 ) ;
7982 }
8083 }
@@ -84,7 +87,6 @@ impl<'tcx> LateLintPass<'tcx> for IfLetMutex {
8487
8588/// Checks if `Mutex::lock` is called in the `if let` expr.
8689pub struct OppVisitor < ' a , ' tcx > {
87- mutex_lock_called : bool ,
8890 found_mutex : Option < & ' tcx Expr < ' tcx > > ,
8991 cx : & ' a LateContext < ' tcx > ,
9092}
@@ -93,7 +95,6 @@ impl<'tcx> Visitor<'tcx> for OppVisitor<'_, 'tcx> {
9395 fn visit_expr ( & mut self , expr : & ' tcx Expr < ' _ > ) {
9496 if let Some ( mutex) = is_mutex_lock_call ( self . cx , expr) {
9597 self . found_mutex = Some ( mutex) ;
96- self . mutex_lock_called = true ;
9798 return ;
9899 }
99100 visit:: walk_expr ( self , expr) ;
@@ -102,7 +103,6 @@ impl<'tcx> Visitor<'tcx> for OppVisitor<'_, 'tcx> {
102103
103104/// Checks if `Mutex::lock` is called in any of the branches.
104105pub struct ArmVisitor < ' a , ' tcx > {
105- mutex_lock_called : bool ,
106106 found_mutex : Option < & ' tcx Expr < ' tcx > > ,
107107 cx : & ' a LateContext < ' tcx > ,
108108}
@@ -111,25 +111,27 @@ impl<'tcx> Visitor<'tcx> for ArmVisitor<'_, 'tcx> {
111111 fn visit_expr ( & mut self , expr : & ' tcx Expr < ' tcx > ) {
112112 if let Some ( mutex) = is_mutex_lock_call ( self . cx , expr) {
113113 self . found_mutex = Some ( mutex) ;
114- self . mutex_lock_called = true ;
115114 return ;
116115 }
117116 visit:: walk_expr ( self , expr) ;
118117 }
119118}
120119
121120impl < ' 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+ } )
125127 }
126128}
127129
128130fn is_mutex_lock_call < ' tcx > ( cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' _ > ) -> Option < & ' tcx Expr < ' tcx > > {
129131 if_chain ! {
130132 if let ExprKind :: MethodCall ( path, [ self_arg, ..] , _) = & expr. kind;
131133 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 ( ) ;
133135 if is_type_diagnostic_item( cx, ty, sym:: Mutex ) ;
134136 then {
135137 Some ( self_arg)
0 commit comments