Skip to content

Commit d36dde4

Browse files
committed
use span_lint_and_help, cargo dev fmt
1 parent 0f513fc commit d36dde4

File tree

5 files changed

+66
-47
lines changed

5 files changed

+66
-47
lines changed

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code.
77

8-
[There are 361 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
8+
[There are 362 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
99

1010
We have a bunch of lint categories to allow you to choose how much Clippy is supposed to ~~annoy~~ help you:
1111

clippy_lints/src/if_let_mutex.rs

+42-40
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,6 @@
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};
42
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};
84
use rustc_lint::{LateContext, LateLintPass};
95
use 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.
117107
fn 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+
}

src/lintlist/mod.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ pub use lint::Lint;
66
pub use lint::LINT_LEVELS;
77

88
// begin lint list, do not remove this comment, it’s used in `update_lints`
9-
pub const ALL_LINTS: [Lint; 361] = [
9+
pub const ALL_LINTS: [Lint; 362] = [
1010
Lint {
1111
name: "absurd_extreme_comparisons",
1212
group: "correctness",
@@ -710,7 +710,7 @@ pub const ALL_LINTS: [Lint; 361] = [
710710
Lint {
711711
name: "if_let_mutex",
712712
group: "correctness",
713-
desc: "default lint description",
713+
desc: "locking a `Mutex` in an `if let` block can cause deadlocks",
714714
deprecation: None,
715715
module: "if_let_mutex",
716716
},

tests/ui/if_let_mutex.rs

+5-4
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,15 @@
22

33
use std::sync::Mutex;
44

5-
fn do_stuff() {}
5+
fn do_stuff<T>(_: T) {}
66
fn foo() {
77
let m = Mutex::new(1u8);
88

9-
if let Ok(locked) = m.lock() {
10-
do_stuff();
9+
if let Err(locked) = m.lock() {
10+
do_stuff(locked);
1111
} else {
12-
m.lock().unwrap();
12+
let lock = m.lock().unwrap();
13+
do_stuff(lock);
1314
};
1415
}
1516

tests/ui/if_let_mutex.stderr

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
error: calling `Mutex::lock` inside the scope of another `Mutex::lock` causes a deadlock
2+
--> $DIR/if_let_mutex.rs:9:5
3+
|
4+
LL | / if let Err(locked) = m.lock() {
5+
LL | | do_stuff(locked);
6+
LL | | } else {
7+
LL | | let lock = m.lock().unwrap();
8+
LL | | do_stuff(lock);
9+
LL | | };
10+
| |_____^
11+
|
12+
= note: `-D clippy::if-let-mutex` implied by `-D warnings`
13+
= help: move the lock call outside of the `if let ...` expression
14+
15+
error: aborting due to previous error
16+

0 commit comments

Comments
 (0)