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} ;
4
2
use 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 } ;
8
4
use rustc_lint:: { LateContext , LateLintPass } ;
9
5
use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
10
6
@@ -15,19 +11,26 @@ declare_clippy_lint! {
15
11
/// **Why is this bad?** The Mutex lock remains held for the whole
16
12
/// `if let ... else` block and deadlocks.
17
13
///
18
- /// **Known problems:** None .
14
+ /// **Known problems:** This lint does not generate an auto-applicable suggestion .
19
15
///
20
16
/// **Example:**
21
17
///
22
- /// ```rust
23
- /// # use std::sync::Mutex;
24
- /// let mutex = Mutex::new(10);
18
+ /// ```rust,ignore
25
19
/// if let Ok(thing) = mutex.lock() {
26
20
/// do_thing();
27
21
/// } else {
28
22
/// mutex.lock();
29
23
/// }
30
24
/// ```
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
+ /// ```
31
34
pub IF_LET_MUTEX ,
32
35
correctness,
33
36
"locking a `Mutex` in an `if let` block can cause deadlocks"
@@ -43,93 +46,92 @@ impl LateLintPass<'_, '_> for IfLetMutex {
43
46
} ) = ex. kind; // if let ... {} else {}
44
47
if let ExprKind :: MethodCall ( _, _, ref args) = op. kind;
45
48
let ty = cx. tables. expr_ty( & args[ 0 ] ) ;
46
- if let ty:: Adt ( _, subst) = ty. kind;
47
49
if match_type( cx, ty, & paths:: MUTEX ) ; // make sure receiver is Mutex
48
50
if method_chain_names( op, 10 ) . iter( ) . any( |s| s == "lock" ) ; // and lock is called
49
51
50
- let mut suggestion = String :: from( & format!( "if let _ = {} {{\n " , snippet( cx, op. span, "_" ) ) ) ;
51
-
52
52
if arms. iter( ) . any( |arm| if_chain! {
53
53
if let ExprKind :: Block ( ref block, _l) = arm. body. kind;
54
54
if block. stmts. iter( ) . any( |stmt| match stmt. kind {
55
55
StmtKind :: Local ( l) => if_chain! {
56
56
if let Some ( ex) = l. init;
57
- if let ExprKind :: MethodCall ( _, _, ref args ) = op. kind;
57
+ if let ExprKind :: MethodCall ( _, _, _ ) = op. kind;
58
58
if method_chain_names( ex, 10 ) . iter( ) . any( |s| s == "lock" ) ; // and lock is called
59
59
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 )
63
61
} else {
64
- suggestion. push_str( & format!( " {}\n " , snippet( cx, l. span, "_" ) ) ) ;
65
62
false
66
63
}
67
64
} ,
68
65
StmtKind :: Expr ( e) => if_chain! {
69
- if let ExprKind :: MethodCall ( _, _, ref args ) = e. kind;
66
+ if let ExprKind :: MethodCall ( _, _, _ ) = e. kind;
70
67
if method_chain_names( e, 10 ) . iter( ) . any( |s| s == "lock" ) ; // and lock is called
71
68
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 )
75
70
} else {
76
- suggestion. push_str( & format!( " {}\n " , snippet( cx, e. span, "_" ) ) ) ;
77
71
false
78
72
}
79
73
} ,
80
74
StmtKind :: Semi ( e) => if_chain! {
81
- if let ExprKind :: MethodCall ( _, _, ref args ) = e. kind;
75
+ if let ExprKind :: MethodCall ( _, _, _ ) = e. kind;
82
76
if method_chain_names( e, 10 ) . iter( ) . any( |s| s == "lock" ) ; // and lock is called
83
77
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 )
87
79
} else {
88
- suggestion. push_str( & format!( " {}\n " , snippet( cx, e. span, "_" ) ) ) ;
89
80
false
90
81
}
91
82
} ,
92
- _ => { suggestion. push_str( & format!( " {}\n " , snippet( cx, stmt. span, "_" ) ) ) ; false } ,
83
+ _ => {
84
+ false
85
+ } ,
93
86
} ) ;
94
87
then {
95
88
true
96
89
} else {
97
- suggestion. push_str( & format!( "else {}\n " , snippet( cx, arm. span, "_" ) ) ) ;
98
90
false
99
91
}
100
92
} ) ;
101
93
then {
102
- println!( "{}" , suggestion) ;
103
- span_lint_and_sugg(
94
+ span_lint_and_help(
104
95
cx,
105
96
IF_LET_MUTEX ,
106
97
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" ,
111
100
) ;
112
101
}
113
102
}
114
103
}
115
104
}
116
105
106
+ /// Return the names of `max_depth` number of methods called in the chain.
117
107
fn method_chain_names < ' tcx > ( expr : & ' tcx Expr < ' tcx > , max_depth : usize ) -> Vec < String > {
118
108
let mut method_names = Vec :: with_capacity ( max_depth) ;
119
-
120
109
let mut current = expr;
121
110
for _ in 0 ..max_depth {
122
- if let ExprKind :: MethodCall ( path, span , args) = & current. kind {
111
+ if let ExprKind :: MethodCall ( path, _ , args) = & current. kind {
123
112
if args. iter ( ) . any ( |e| e. span . from_expansion ( ) ) {
124
113
break ;
125
114
}
126
115
method_names. push ( path. ident . to_string ( ) ) ;
127
- println ! ( "{:?}" , method_names) ;
128
116
current = & args[ 0 ] ;
129
117
} else {
130
118
break ;
131
119
}
132
120
}
133
-
134
121
method_names
135
122
}
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