Skip to content

Commit 8af9b49

Browse files
committed
progress work on suggestion for auto fix
1 parent 34763a5 commit 8af9b49

File tree

5 files changed

+164
-0
lines changed

5 files changed

+164
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1284,6 +1284,7 @@ Released 2018-09-13
12841284
[`get_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_unwrap
12851285
[`identity_conversion`]: https://rust-lang.github.io/rust-clippy/master/index.html#identity_conversion
12861286
[`identity_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#identity_op
1287+
[`if_let_mutex`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_let_mutex
12871288
[`if_let_redundant_pattern_matching`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_let_redundant_pattern_matching
12881289
[`if_let_some_result`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_let_some_result
12891290
[`if_not_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_not_else

clippy_lints/src/if_let_mutex.rs

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

clippy_lints/src/lib.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,7 @@ mod functions;
220220
mod get_last_with_len;
221221
mod identity_conversion;
222222
mod identity_op;
223+
mod if_let_mutex;
223224
mod if_let_some_result;
224225
mod if_not_else;
225226
mod implicit_return;
@@ -566,6 +567,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
566567
&get_last_with_len::GET_LAST_WITH_LEN,
567568
&identity_conversion::IDENTITY_CONVERSION,
568569
&identity_op::IDENTITY_OP,
570+
&if_let_mutex::IF_LET_MUTEX,
569571
&if_let_some_result::IF_LET_SOME_RESULT,
570572
&if_not_else::IF_NOT_ELSE,
571573
&implicit_return::IMPLICIT_RETURN,
@@ -1039,6 +1041,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
10391041
store.register_late_pass(|| box verbose_file_reads::VerboseFileReads);
10401042
store.register_late_pass(|| box redundant_pub_crate::RedundantPubCrate::default());
10411043
store.register_late_pass(|| box unnamed_address::UnnamedAddress);
1044+
store.register_late_pass(|| box if_let_mutex::IfLetMutex);
10421045

10431046
store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![
10441047
LintId::of(&arithmetic::FLOAT_ARITHMETIC),
@@ -1214,6 +1217,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
12141217
LintId::of(&get_last_with_len::GET_LAST_WITH_LEN),
12151218
LintId::of(&identity_conversion::IDENTITY_CONVERSION),
12161219
LintId::of(&identity_op::IDENTITY_OP),
1220+
LintId::of(&if_let_mutex::IF_LET_MUTEX),
12171221
LintId::of(&if_let_some_result::IF_LET_SOME_RESULT),
12181222
LintId::of(&indexing_slicing::OUT_OF_BOUNDS_INDEXING),
12191223
LintId::of(&infinite_iter::INFINITE_ITER),
@@ -1600,6 +1604,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
16001604
LintId::of(&erasing_op::ERASING_OP),
16011605
LintId::of(&formatting::POSSIBLE_MISSING_COMMA),
16021606
LintId::of(&functions::NOT_UNSAFE_PTR_ARG_DEREF),
1607+
LintId::of(&if_let_mutex::IF_LET_MUTEX),
16031608
LintId::of(&indexing_slicing::OUT_OF_BOUNDS_INDEXING),
16041609
LintId::of(&infinite_iter::INFINITE_ITER),
16051610
LintId::of(&inherent_to_string::INHERENT_TO_STRING_SHADOW_DISPLAY),

src/lintlist/mod.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -717,6 +717,13 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
717717
deprecation: None,
718718
module: "identity_op",
719719
},
720+
Lint {
721+
name: "if_let_mutex",
722+
group: "correctness",
723+
desc: "default lint description",
724+
deprecation: None,
725+
module: "if_let_mutex",
726+
},
720727
Lint {
721728
name: "if_let_some_result",
722729
group: "style",

tests/ui/if_let_mutex.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#![warn(clippy::if_let_mutex)]
2+
3+
use std::sync::Mutex;
4+
5+
fn do_stuff() {}
6+
fn foo() {
7+
let m = Mutex::new(1u8);
8+
9+
if let Ok(locked) = m.lock() {
10+
do_stuff();
11+
} else {
12+
m.lock().unwrap();
13+
};
14+
}
15+
16+
fn main() {}

0 commit comments

Comments
 (0)