-
Notifications
You must be signed in to change notification settings - Fork 1.8k
If let else mutex #5332
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
If let else mutex #5332
Changes from 1 commit
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
001a42e
progress work on suggestion for auto fix
DevinR528 139e2c6
creating suggestion
DevinR528 40bbdff
use span_lint_and_help, cargo dev fmt
DevinR528 51c2325
move closures to seperate fns, remove known problems
DevinR528 fca3537
add note about update-all-refs script, revert redundant pat to master
DevinR528 c6c77d9
use Visitor api to find Mutex::lock calls
DevinR528 930619b
change visitor name to OppVisitor
DevinR528 1ee04e4
fix internal clippy warnings
DevinR528 2e8a2de
dev update_lints
DevinR528 7242fa5
fix map import to rustc_middle
DevinR528 4cebe2b
cargo dev fmt
DevinR528 ae82092
use if chain
DevinR528 a9f1bb4
test for mutex eq, add another test case
DevinR528 d1b1a4c
update span_lint_and_help call to six args
DevinR528 489dd2e
factor ifs into function, add differing mutex test
DevinR528 3fbe321
update stderr file
DevinR528 File filter
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
commit 001a42e632573abca10b2f8272f50a3999846e70
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 { | ||
DevinR528 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| 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) = ¤t.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 | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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(); | ||
| }; | ||
| } | ||
DevinR528 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| fn main() {} | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.