]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/if_let_mutex.rs
progress work on suggestion for auto fix
[rust.git] / clippy_lints / src / if_let_mutex.rs
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 }