]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/eta_reduction.rs
TyCtxt::map is now called TyCtxt::hir
[rust.git] / clippy_lints / src / eta_reduction.rs
1 use rustc::lint::*;
2 use rustc::ty;
3 use rustc::hir::*;
4 use utils::{snippet_opt, span_lint_and_then, is_adjusted, iter_input_pats};
5
6 #[allow(missing_copy_implementations)]
7 pub struct EtaPass;
8
9
10 /// **What it does:** Checks for closures which just call another function where
11 /// the function can be called directly. `unsafe` functions or calls where types
12 /// get adjusted are ignored.
13 ///
14 /// **Why is this bad?** Needlessly creating a closure adds code for no benefit
15 /// and gives the optimizer more work.
16 ///
17 /// **Known problems:** None.
18 ///
19 /// **Example:**
20 /// ```rust
21 /// xs.map(|x| foo(x))
22 /// ```
23 /// where `foo(_)` is a plain function that takes the exact argument type of `x`.
24 declare_lint! {
25     pub REDUNDANT_CLOSURE,
26     Warn,
27     "redundant closures, i.e. `|a| foo(a)` (which can be written as just `foo`)"
28 }
29
30 impl LintPass for EtaPass {
31     fn get_lints(&self) -> LintArray {
32         lint_array!(REDUNDANT_CLOSURE)
33     }
34 }
35
36 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for EtaPass {
37     fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
38         match expr.node {
39             ExprCall(_, ref args) |
40             ExprMethodCall(_, _, ref args) => {
41                 for arg in args {
42                     check_closure(cx, arg)
43                 }
44             },
45             _ => (),
46         }
47     }
48 }
49
50 fn check_closure(cx: &LateContext, expr: &Expr) {
51     if let ExprClosure(_, ref decl, eid, _) = expr.node {
52         let body = cx.tcx.hir.body(eid);
53         let ex = &body.value;
54         if let ExprCall(ref caller, ref args) = ex.node {
55             if args.len() != decl.inputs.len() {
56                 // Not the same number of arguments, there
57                 // is no way the closure is the same as the function
58                 return;
59             }
60             if is_adjusted(cx, ex) || args.iter().any(|arg| is_adjusted(cx, arg)) {
61                 // Are the expression or the arguments type-adjusted? Then we need the closure
62                 return;
63             }
64             let fn_ty = cx.tables.expr_ty(caller);
65             match fn_ty.sty {
66                 // Is it an unsafe function? They don't implement the closure traits
67                 ty::TyFnDef(_, _, fn_ty) |
68                 ty::TyFnPtr(fn_ty) => {
69                     if fn_ty.unsafety == Unsafety::Unsafe || fn_ty.sig.skip_binder().output().sty == ty::TyNever {
70                         return;
71                     }
72                 },
73                 _ => (),
74             }
75             for (a1, a2) in iter_input_pats(decl, body).zip(args) {
76                 if let PatKind::Binding(_, _, ident, _) = a1.pat.node {
77                     // XXXManishearth Should I be checking the binding mode here?
78                     if let ExprPath(QPath::Resolved(None, ref p)) = a2.node {
79                         if p.segments.len() != 1 {
80                             // If it's a proper path, it can't be a local variable
81                             return;
82                         }
83                         if p.segments[0].name != ident.node {
84                             // The two idents should be the same
85                             return;
86                         }
87                     } else {
88                         return;
89                     }
90                 } else {
91                     return;
92                 }
93             }
94             span_lint_and_then(cx,
95                                REDUNDANT_CLOSURE,
96                                expr.span,
97                                "redundant closure found",
98                                |db| if let Some(snippet) = snippet_opt(cx, caller.span) {
99                                    db.span_suggestion(expr.span, "remove closure as shown:", snippet);
100                                });
101         }
102     }
103 }