mirror of
https://github.com/rust-lang/rust-clippy
synced 2024-11-26 22:50:56 +00:00
use span_lint_and_help, cargo dev fmt
This commit is contained in:
parent
139e2c6227
commit
40bbdffc89
4 changed files with 64 additions and 45 deletions
|
@ -1,10 +1,6 @@
|
||||||
use crate::utils::{
|
use crate::utils::{match_type, paths, span_lint_and_help};
|
||||||
match_type, method_calls, method_chain_args, paths, snippet, snippet_with_applicability, span_lint_and_sugg,
|
|
||||||
};
|
|
||||||
use if_chain::if_chain;
|
use if_chain::if_chain;
|
||||||
use rustc::ty;
|
use rustc_hir::{Expr, ExprKind, MatchSource, StmtKind};
|
||||||
use rustc_errors::Applicability;
|
|
||||||
use rustc_hir::{print, Expr, ExprKind, MatchSource, PatKind, QPath, StmtKind};
|
|
||||||
use rustc_lint::{LateContext, LateLintPass};
|
use rustc_lint::{LateContext, LateLintPass};
|
||||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||||
|
|
||||||
|
@ -15,19 +11,26 @@ declare_clippy_lint! {
|
||||||
/// **Why is this bad?** The Mutex lock remains held for the whole
|
/// **Why is this bad?** The Mutex lock remains held for the whole
|
||||||
/// `if let ... else` block and deadlocks.
|
/// `if let ... else` block and deadlocks.
|
||||||
///
|
///
|
||||||
/// **Known problems:** None.
|
/// **Known problems:** This lint does not generate an auto-applicable suggestion.
|
||||||
///
|
///
|
||||||
/// **Example:**
|
/// **Example:**
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust,ignore
|
||||||
/// # use std::sync::Mutex;
|
|
||||||
/// let mutex = Mutex::new(10);
|
|
||||||
/// if let Ok(thing) = mutex.lock() {
|
/// if let Ok(thing) = mutex.lock() {
|
||||||
/// do_thing();
|
/// do_thing();
|
||||||
/// } else {
|
/// } else {
|
||||||
/// mutex.lock();
|
/// mutex.lock();
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
/// Should be written
|
||||||
|
/// ```rust,ignore
|
||||||
|
/// let locked = mutex.lock();
|
||||||
|
/// if let Ok(thing) = locked {
|
||||||
|
/// do_thing(thing);
|
||||||
|
/// } else {
|
||||||
|
/// use_locked(locked);
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
pub IF_LET_MUTEX,
|
pub IF_LET_MUTEX,
|
||||||
correctness,
|
correctness,
|
||||||
"locking a `Mutex` in an `if let` block can cause deadlocks"
|
"locking a `Mutex` in an `if let` block can cause deadlocks"
|
||||||
|
@ -43,93 +46,92 @@ impl LateLintPass<'_, '_> for IfLetMutex {
|
||||||
}) = ex.kind; // if let ... {} else {}
|
}) = ex.kind; // if let ... {} else {}
|
||||||
if let ExprKind::MethodCall(_, _, ref args) = op.kind;
|
if let ExprKind::MethodCall(_, _, ref args) = op.kind;
|
||||||
let ty = cx.tables.expr_ty(&args[0]);
|
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 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
|
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 arms.iter().any(|arm| if_chain! {
|
||||||
if let ExprKind::Block(ref block, _l) = arm.body.kind;
|
if let ExprKind::Block(ref block, _l) = arm.body.kind;
|
||||||
if block.stmts.iter().any(|stmt| match stmt.kind {
|
if block.stmts.iter().any(|stmt| match stmt.kind {
|
||||||
StmtKind::Local(l) => if_chain! {
|
StmtKind::Local(l) => if_chain! {
|
||||||
if let Some(ex) = l.init;
|
if let Some(ex) = l.init;
|
||||||
if let ExprKind::MethodCall(_, _, ref args) = op.kind;
|
if let ExprKind::MethodCall(_, _, _) = op.kind;
|
||||||
if method_chain_names(ex, 10).iter().any(|s| s == "lock"); // and lock is called
|
if method_chain_names(ex, 10).iter().any(|s| s == "lock"); // and lock is called
|
||||||
then {
|
then {
|
||||||
let ty = cx.tables.expr_ty(&args[0]);
|
match_type_method_chain(cx, ex, 5)
|
||||||
// // make sure receiver is Result<MutexGuard<...>>
|
|
||||||
match_type(cx, ty, &paths::RESULT)
|
|
||||||
} else {
|
} else {
|
||||||
suggestion.push_str(&format!(" {}\n", snippet(cx, l.span, "_")));
|
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
StmtKind::Expr(e) => if_chain! {
|
StmtKind::Expr(e) => if_chain! {
|
||||||
if let ExprKind::MethodCall(_, _, ref args) = e.kind;
|
if let ExprKind::MethodCall(_, _, _) = e.kind;
|
||||||
if method_chain_names(e, 10).iter().any(|s| s == "lock"); // and lock is called
|
if method_chain_names(e, 10).iter().any(|s| s == "lock"); // and lock is called
|
||||||
then {
|
then {
|
||||||
let ty = cx.tables.expr_ty(&args[0]);
|
match_type_method_chain(cx, ex, 5)
|
||||||
// // make sure receiver is Result<MutexGuard<...>>
|
|
||||||
match_type(cx, ty, &paths::RESULT)
|
|
||||||
} else {
|
} else {
|
||||||
suggestion.push_str(&format!(" {}\n", snippet(cx, e.span, "_")));
|
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
StmtKind::Semi(e) => if_chain! {
|
StmtKind::Semi(e) => if_chain! {
|
||||||
if let ExprKind::MethodCall(_, _, ref args) = e.kind;
|
if let ExprKind::MethodCall(_, _, _) = e.kind;
|
||||||
if method_chain_names(e, 10).iter().any(|s| s == "lock"); // and lock is called
|
if method_chain_names(e, 10).iter().any(|s| s == "lock"); // and lock is called
|
||||||
then {
|
then {
|
||||||
let ty = cx.tables.expr_ty(&args[0]);
|
match_type_method_chain(cx, ex, 5)
|
||||||
// // make sure receiver is Result<MutexGuard<...>>
|
|
||||||
match_type(cx, ty, &paths::RESULT)
|
|
||||||
} else {
|
} else {
|
||||||
suggestion.push_str(&format!(" {}\n", snippet(cx, e.span, "_")));
|
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
_ => { suggestion.push_str(&format!(" {}\n", snippet(cx, stmt.span, "_"))); false },
|
_ => {
|
||||||
|
false
|
||||||
|
},
|
||||||
});
|
});
|
||||||
then {
|
then {
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
suggestion.push_str(&format!("else {}\n", snippet(cx, arm.span, "_")));
|
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
then {
|
then {
|
||||||
println!("{}", suggestion);
|
span_lint_and_help(
|
||||||
span_lint_and_sugg(
|
|
||||||
cx,
|
cx,
|
||||||
IF_LET_MUTEX,
|
IF_LET_MUTEX,
|
||||||
ex.span,
|
ex.span,
|
||||||
"using a `Mutex` in inner scope of `.lock` call",
|
"calling `Mutex::lock` inside the scope of another `Mutex::lock` causes a deadlock",
|
||||||
"try",
|
"move the lock call outside of the `if let ...` expression",
|
||||||
format!("{:?}", "hello"),
|
|
||||||
Applicability::MachineApplicable,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return the names of `max_depth` number of methods called in the chain.
|
||||||
fn method_chain_names<'tcx>(expr: &'tcx Expr<'tcx>, max_depth: usize) -> Vec<String> {
|
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 method_names = Vec::with_capacity(max_depth);
|
||||||
|
|
||||||
let mut current = expr;
|
let mut current = expr;
|
||||||
for _ in 0..max_depth {
|
for _ in 0..max_depth {
|
||||||
if let ExprKind::MethodCall(path, span, args) = ¤t.kind {
|
if let ExprKind::MethodCall(path, _, args) = ¤t.kind {
|
||||||
if args.iter().any(|e| e.span.from_expansion()) {
|
if args.iter().any(|e| e.span.from_expansion()) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
method_names.push(path.ident.to_string());
|
method_names.push(path.ident.to_string());
|
||||||
println!("{:?}", method_names);
|
|
||||||
current = &args[0];
|
current = &args[0];
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
method_names
|
method_names
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Check that lock is called on a `Mutex`.
|
||||||
|
fn match_type_method_chain<'tcx>(cx: &LateContext<'_, '_>, expr: &'tcx Expr<'tcx>, max_depth: usize) -> bool {
|
||||||
|
let mut current = expr;
|
||||||
|
for _ in 0..max_depth {
|
||||||
|
if let ExprKind::MethodCall(_, _, args) = ¤t.kind {
|
||||||
|
let ty = cx.tables.expr_ty(&args[0]);
|
||||||
|
if match_type(cx, ty, &paths::MUTEX) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
current = &args[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
|
@ -734,7 +734,7 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
|
||||||
Lint {
|
Lint {
|
||||||
name: "if_let_mutex",
|
name: "if_let_mutex",
|
||||||
group: "correctness",
|
group: "correctness",
|
||||||
desc: "default lint description",
|
desc: "locking a `Mutex` in an `if let` block can cause deadlocks",
|
||||||
deprecation: None,
|
deprecation: None,
|
||||||
module: "if_let_mutex",
|
module: "if_let_mutex",
|
||||||
},
|
},
|
||||||
|
|
|
@ -2,14 +2,15 @@
|
||||||
|
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
|
|
||||||
fn do_stuff() {}
|
fn do_stuff<T>(_: T) {}
|
||||||
fn foo() {
|
fn foo() {
|
||||||
let m = Mutex::new(1u8);
|
let m = Mutex::new(1u8);
|
||||||
|
|
||||||
if let Ok(locked) = m.lock() {
|
if let Err(locked) = m.lock() {
|
||||||
do_stuff();
|
do_stuff(locked);
|
||||||
} else {
|
} else {
|
||||||
m.lock().unwrap();
|
let lock = m.lock().unwrap();
|
||||||
|
do_stuff(lock);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
16
tests/ui/if_let_mutex.stderr
Normal file
16
tests/ui/if_let_mutex.stderr
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
error: calling `Mutex::lock` inside the scope of another `Mutex::lock` causes a deadlock
|
||||||
|
--> $DIR/if_let_mutex.rs:9:5
|
||||||
|
|
|
||||||
|
LL | / if let Err(locked) = m.lock() {
|
||||||
|
LL | | do_stuff(locked);
|
||||||
|
LL | | } else {
|
||||||
|
LL | | let lock = m.lock().unwrap();
|
||||||
|
LL | | do_stuff(lock);
|
||||||
|
LL | | };
|
||||||
|
| |_____^
|
||||||
|
|
|
||||||
|
= note: `-D clippy::if-let-mutex` implied by `-D warnings`
|
||||||
|
= help: move the lock call outside of the `if let ...` expression
|
||||||
|
|
||||||
|
error: aborting due to previous error
|
||||||
|
|
Loading…
Reference in a new issue