1- use crate :: utils:: {
2- match_type, method_calls, method_chain_args, paths, snippet, snippet_with_applicability, span_lint_and_sugg,
3- } ;
1+ use crate :: utils:: { match_type, paths, span_lint_and_help} ;
42use if_chain:: if_chain;
5- use rustc:: ty;
6- use rustc_errors:: Applicability ;
7- use rustc_hir:: { print, Expr , ExprKind , MatchSource , PatKind , QPath , StmtKind } ;
3+ use rustc_hir:: { Expr , ExprKind , MatchSource , StmtKind } ;
84use rustc_lint:: { LateContext , LateLintPass } ;
95use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
106
@@ -15,19 +11,26 @@ declare_clippy_lint! {
1511 /// **Why is this bad?** The Mutex lock remains held for the whole
1612 /// `if let ... else` block and deadlocks.
1713 ///
18- /// **Known problems:** None .
14+ /// **Known problems:** This lint does not generate an auto-applicable suggestion .
1915 ///
2016 /// **Example:**
2117 ///
22- /// ```rust
23- /// # use std::sync::Mutex;
24- /// let mutex = Mutex::new(10);
18+ /// ```rust,ignore
2519 /// if let Ok(thing) = mutex.lock() {
2620 /// do_thing();
2721 /// } else {
2822 /// mutex.lock();
2923 /// }
3024 /// ```
25+ /// Should be written
26+ /// ```rust,ignore
27+ /// let locked = mutex.lock();
28+ /// if let Ok(thing) = locked {
29+ /// do_thing(thing);
30+ /// } else {
31+ /// use_locked(locked);
32+ /// }
33+ /// ```
3134 pub IF_LET_MUTEX ,
3235 correctness,
3336 "locking a `Mutex` in an `if let` block can cause deadlocks"
@@ -43,93 +46,92 @@ impl LateLintPass<'_, '_> for IfLetMutex {
4346 } ) = ex. kind; // if let ... {} else {}
4447 if let ExprKind :: MethodCall ( _, _, ref args) = op. kind;
4548 let ty = cx. tables. expr_ty( & args[ 0 ] ) ;
46- if let ty:: Adt ( _, subst) = ty. kind;
4749 if match_type( cx, ty, & paths:: MUTEX ) ; // make sure receiver is Mutex
4850 if method_chain_names( op, 10 ) . iter( ) . any( |s| s == "lock" ) ; // and lock is called
4951
50- let mut suggestion = String :: from( & format!( "if let _ = {} {{\n " , snippet( cx, op. span, "_" ) ) ) ;
51-
5252 if arms. iter( ) . any( |arm| if_chain! {
5353 if let ExprKind :: Block ( ref block, _l) = arm. body. kind;
5454 if block. stmts. iter( ) . any( |stmt| match stmt. kind {
5555 StmtKind :: Local ( l) => if_chain! {
5656 if let Some ( ex) = l. init;
57- if let ExprKind :: MethodCall ( _, _, ref args ) = op. kind;
57+ if let ExprKind :: MethodCall ( _, _, _ ) = op. kind;
5858 if method_chain_names( ex, 10 ) . iter( ) . any( |s| s == "lock" ) ; // and lock is called
5959 then {
60- let ty = cx. tables. expr_ty( & args[ 0 ] ) ;
61- // // make sure receiver is Result<MutexGuard<...>>
62- match_type( cx, ty, & paths:: RESULT )
60+ match_type_method_chain( cx, ex, 5 )
6361 } else {
64- suggestion. push_str( & format!( " {}\n " , snippet( cx, l. span, "_" ) ) ) ;
6562 false
6663 }
6764 } ,
6865 StmtKind :: Expr ( e) => if_chain! {
69- if let ExprKind :: MethodCall ( _, _, ref args ) = e. kind;
66+ if let ExprKind :: MethodCall ( _, _, _ ) = e. kind;
7067 if method_chain_names( e, 10 ) . iter( ) . any( |s| s == "lock" ) ; // and lock is called
7168 then {
72- let ty = cx. tables. expr_ty( & args[ 0 ] ) ;
73- // // make sure receiver is Result<MutexGuard<...>>
74- match_type( cx, ty, & paths:: RESULT )
69+ match_type_method_chain( cx, ex, 5 )
7570 } else {
76- suggestion. push_str( & format!( " {}\n " , snippet( cx, e. span, "_" ) ) ) ;
7771 false
7872 }
7973 } ,
8074 StmtKind :: Semi ( e) => if_chain! {
81- if let ExprKind :: MethodCall ( _, _, ref args ) = e. kind;
75+ if let ExprKind :: MethodCall ( _, _, _ ) = e. kind;
8276 if method_chain_names( e, 10 ) . iter( ) . any( |s| s == "lock" ) ; // and lock is called
8377 then {
84- let ty = cx. tables. expr_ty( & args[ 0 ] ) ;
85- // // make sure receiver is Result<MutexGuard<...>>
86- match_type( cx, ty, & paths:: RESULT )
78+ match_type_method_chain( cx, ex, 5 )
8779 } else {
88- suggestion. push_str( & format!( " {}\n " , snippet( cx, e. span, "_" ) ) ) ;
8980 false
9081 }
9182 } ,
92- _ => { suggestion. push_str( & format!( " {}\n " , snippet( cx, stmt. span, "_" ) ) ) ; false } ,
83+ _ => {
84+ false
85+ } ,
9386 } ) ;
9487 then {
9588 true
9689 } else {
97- suggestion. push_str( & format!( "else {}\n " , snippet( cx, arm. span, "_" ) ) ) ;
9890 false
9991 }
10092 } ) ;
10193 then {
102- println!( "{}" , suggestion) ;
103- span_lint_and_sugg(
94+ span_lint_and_help(
10495 cx,
10596 IF_LET_MUTEX ,
10697 ex. span,
107- "using a `Mutex` in inner scope of `.lock` call" ,
108- "try" ,
109- format!( "{:?}" , "hello" ) ,
110- Applicability :: MachineApplicable ,
98+ "calling `Mutex::lock` inside the scope of another `Mutex::lock` causes a deadlock" ,
99+ "move the lock call outside of the `if let ...` expression" ,
111100 ) ;
112101 }
113102 }
114103 }
115104}
116105
106+ /// Return the names of `max_depth` number of methods called in the chain.
117107fn method_chain_names < ' tcx > ( expr : & ' tcx Expr < ' tcx > , max_depth : usize ) -> Vec < String > {
118108 let mut method_names = Vec :: with_capacity ( max_depth) ;
119-
120109 let mut current = expr;
121110 for _ in 0 ..max_depth {
122- if let ExprKind :: MethodCall ( path, span , args) = & current. kind {
111+ if let ExprKind :: MethodCall ( path, _ , args) = & current. kind {
123112 if args. iter ( ) . any ( |e| e. span . from_expansion ( ) ) {
124113 break ;
125114 }
126115 method_names. push ( path. ident . to_string ( ) ) ;
127- println ! ( "{:?}" , method_names) ;
128116 current = & args[ 0 ] ;
129117 } else {
130118 break ;
131119 }
132120 }
133-
134121 method_names
135122}
123+
124+ /// Check that lock is called on a `Mutex`.
125+ fn match_type_method_chain < ' tcx > ( cx : & LateContext < ' _ , ' _ > , expr : & ' tcx Expr < ' tcx > , max_depth : usize ) -> bool {
126+ let mut current = expr;
127+ for _ in 0 ..max_depth {
128+ if let ExprKind :: MethodCall ( _, _, args) = & current. kind {
129+ let ty = cx. tables . expr_ty ( & args[ 0 ] ) ;
130+ if match_type ( cx, ty, & paths:: MUTEX ) {
131+ return true ;
132+ }
133+ current = & args[ 0 ] ;
134+ }
135+ }
136+ false
137+ }
0 commit comments