Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
progress work on suggestion for auto fix
  • Loading branch information
DevinR528 committed Apr 20, 2020
commit 001a42e632573abca10b2f8272f50a3999846e70
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -1286,6 +1286,7 @@ Released 2018-09-13
[`get_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_unwrap
[`identity_conversion`]: https://rust-lang.github.io/rust-clippy/master/index.html#identity_conversion
[`identity_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#identity_op
[`if_let_mutex`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_let_mutex
[`if_let_redundant_pattern_matching`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_let_redundant_pattern_matching
[`if_let_some_result`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_let_some_result
[`if_not_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_not_else
Expand Down
135 changes: 135 additions & 0 deletions clippy_lints/src/if_let_mutex.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
use crate::utils::{
match_type, method_calls, method_chain_args, paths, snippet, snippet_with_applicability, span_lint_and_sugg,
};
use if_chain::if_chain;
use rustc::ty;
use rustc_errors::Applicability;
use rustc_hir::{print, Expr, ExprKind, MatchSource, PatKind, QPath, StmtKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};

declare_clippy_lint! {
/// **What it does:** Checks for `Mutex::lock` calls in `if let` expression
/// with lock calls in any of the else blocks.
///
/// **Why is this bad?** The Mutex lock remains held for the whole
/// `if let ... else` block and deadlocks.
///
/// **Known problems:** None.
///
/// **Example:**
///
/// ```rust
/// # use std::sync::Mutex;
/// let mutex = Mutex::new(10);
/// if let Ok(thing) = mutex.lock() {
/// do_thing();
/// } else {
/// mutex.lock();
/// }
/// ```
pub IF_LET_MUTEX,
correctness,
"locking a `Mutex` in an `if let` block can cause deadlock"
}

declare_lint_pass!(IfLetMutex => [IF_LET_MUTEX]);

impl LateLintPass<'_, '_> for IfLetMutex {
fn check_expr(&mut self, cx: &LateContext<'_, '_>, ex: &'_ Expr<'_>) {
if_chain! {
if let ExprKind::Match(ref op, ref arms, MatchSource::IfLetDesugar {
contains_else_clause: true,
}) = ex.kind; // if let ... {} else {}
if let ExprKind::MethodCall(_, _, ref args) = op.kind;
let ty = cx.tables.expr_ty(&args[0]);
if let ty::Adt(_, subst) = ty.kind;
if match_type(cx, ty, &paths::MUTEX); // make sure receiver is Mutex
if method_chain_names(op, 10).iter().any(|s| s == "lock"); // and lock is called

let mut suggestion = String::from(&format!("if let _ = {} {{\n", snippet(cx, op.span, "_")));

if arms.iter().any(|arm| if_chain! {
if let ExprKind::Block(ref block, _l) = arm.body.kind;
if block.stmts.iter().any(|stmt| match stmt.kind {
StmtKind::Local(l) => if_chain! {
if let Some(ex) = l.init;
if let ExprKind::MethodCall(_, _, ref args) = op.kind;
if method_chain_names(ex, 10).iter().any(|s| s == "lock"); // and lock is called
then {
let ty = cx.tables.expr_ty(&args[0]);
// // make sure receiver is Result<MutexGuard<...>>
match_type(cx, ty, &paths::RESULT)
} else {
suggestion.push_str(&format!(" {}\n", snippet(cx, l.span, "_")));
false
}
},
StmtKind::Expr(e) => if_chain! {
if let ExprKind::MethodCall(_, _, ref args) = e.kind;
if method_chain_names(e, 10).iter().any(|s| s == "lock"); // and lock is called
then {
let ty = cx.tables.expr_ty(&args[0]);
// // make sure receiver is Result<MutexGuard<...>>
match_type(cx, ty, &paths::RESULT)
} else {
suggestion.push_str(&format!(" {}\n", snippet(cx, e.span, "_")));
false
}
},
StmtKind::Semi(e) => if_chain! {
if let ExprKind::MethodCall(_, _, ref args) = e.kind;
if method_chain_names(e, 10).iter().any(|s| s == "lock"); // and lock is called
then {
let ty = cx.tables.expr_ty(&args[0]);
// // make sure receiver is Result<MutexGuard<...>>
match_type(cx, ty, &paths::RESULT)
} else {
suggestion.push_str(&format!(" {}\n", snippet(cx, e.span, "_")));
false
}
},
_ => { suggestion.push_str(&format!(" {}\n", snippet(cx, stmt.span, "_"))); false },
});
then {
true
} else {
suggestion.push_str(&format!("else {}\n", snippet(cx, arm.span, "_")));
false
}
});
then {
println!("{}", suggestion);
span_lint_and_sugg(
cx,
IF_LET_MUTEX,
ex.span,
"using a `Mutex` in inner scope of `.lock` call",
"try",
format!("{:?}", "hello"),
Applicability::MachineApplicable,
);
}
}
}
}

fn method_chain_names<'tcx>(expr: &'tcx Expr<'tcx>, max_depth: usize) -> Vec<String> {
let mut method_names = Vec::with_capacity(max_depth);

let mut current = expr;
for _ in 0..max_depth {
if let ExprKind::MethodCall(path, span, args) = &current.kind {
if args.iter().any(|e| e.span.from_expansion()) {
break;
}
method_names.push(path.ident.to_string());
println!("{:?}", method_names);
current = &args[0];
} else {
break;
}
}

method_names
}
5 changes: 5 additions & 0 deletions clippy_lints/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,7 @@ mod future_not_send;
mod get_last_with_len;
mod identity_conversion;
mod identity_op;
mod if_let_mutex;
mod if_let_some_result;
mod if_not_else;
mod implicit_return;
Expand Down Expand Up @@ -572,6 +573,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
&get_last_with_len::GET_LAST_WITH_LEN,
&identity_conversion::IDENTITY_CONVERSION,
&identity_op::IDENTITY_OP,
&if_let_mutex::IF_LET_MUTEX,
&if_let_some_result::IF_LET_SOME_RESULT,
&if_not_else::IF_NOT_ELSE,
&implicit_return::IMPLICIT_RETURN,
Expand Down Expand Up @@ -1053,6 +1055,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(|| box dereference::Dereferencing);
store.register_late_pass(|| box future_not_send::FutureNotSend);
store.register_late_pass(|| box utils::internal_lints::CollapsibleCalls);
store.register_late_pass(|| box if_let_mutex::IfLetMutex);

store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![
LintId::of(&arithmetic::FLOAT_ARITHMETIC),
Expand Down Expand Up @@ -1231,6 +1234,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&get_last_with_len::GET_LAST_WITH_LEN),
LintId::of(&identity_conversion::IDENTITY_CONVERSION),
LintId::of(&identity_op::IDENTITY_OP),
LintId::of(&if_let_mutex::IF_LET_MUTEX),
LintId::of(&if_let_some_result::IF_LET_SOME_RESULT),
LintId::of(&indexing_slicing::OUT_OF_BOUNDS_INDEXING),
LintId::of(&infinite_iter::INFINITE_ITER),
Expand Down Expand Up @@ -1618,6 +1622,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&erasing_op::ERASING_OP),
LintId::of(&formatting::POSSIBLE_MISSING_COMMA),
LintId::of(&functions::NOT_UNSAFE_PTR_ARG_DEREF),
LintId::of(&if_let_mutex::IF_LET_MUTEX),
LintId::of(&indexing_slicing::OUT_OF_BOUNDS_INDEXING),
LintId::of(&infinite_iter::INFINITE_ITER),
LintId::of(&inherent_to_string::INHERENT_TO_STRING_SHADOW_DISPLAY),
Expand Down
7 changes: 7 additions & 0 deletions src/lintlist/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -731,6 +731,13 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
deprecation: None,
module: "identity_op",
},
Lint {
name: "if_let_mutex",
group: "correctness",
desc: "default lint description",
deprecation: None,
module: "if_let_mutex",
},
Lint {
name: "if_let_some_result",
group: "style",
Expand Down
16 changes: 16 additions & 0 deletions tests/ui/if_let_mutex.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#![warn(clippy::if_let_mutex)]

use std::sync::Mutex;

fn do_stuff() {}
fn foo() {
let m = Mutex::new(1u8);

if let Ok(locked) = m.lock() {
do_stuff();
} else {
m.lock().unwrap();
};
}

fn main() {}