]> git.lizzy.rs Git - rust.git/blobdiff - clippy_lints/src/eta_reduction.rs
Auto merge of #7460 - camsteffen:run-from-source, r=Manishearth
[rust.git] / clippy_lints / src / eta_reduction.rs
index 2557e9f2796c9f6964dca9d1a075e45b3d401d48..667eb8eb283bbb09997d47f8589bd86118e53e52 100644 (file)
@@ -1,18 +1,18 @@
 use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
-use clippy_utils::higher;
 use clippy_utils::higher::VecArgs;
 use clippy_utils::source::snippet_opt;
 use clippy_utils::ty::{implements_trait, type_is_unsafe_function};
+use clippy_utils::usage::UsedAfterExprVisitor;
+use clippy_utils::{get_enclosing_loop_or_closure, higher};
+use clippy_utils::{is_adjusted, iter_input_pats};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::{def_id, Expr, ExprKind, Param, PatKind, QPath};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
-use rustc_middle::ty::{self, Ty};
+use rustc_middle::ty::{self, ClosureKind, Ty};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 
-use crate::utils::{is_adjusted, iter_input_pats};
-
 declare_clippy_lint! {
     /// **What it does:** Checks for closures which just call another function where
     /// the function can be called directly. `unsafe` functions or calls where types
@@ -78,7 +78,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
                 for arg in args {
                     // skip `foo(macro!())`
                     if arg.span.ctxt() == expr.span.ctxt() {
-                        check_closure(cx, arg)
+                        check_closure(cx, arg);
                     }
                 }
             },
@@ -87,30 +87,32 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
     }
 }
 
-fn check_closure(cx: &LateContext<'_>, expr: &Expr<'_>) {
-    if let ExprKind::Closure(_, ref decl, eid, _, _) = expr.kind {
+fn check_closure<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+    if let ExprKind::Closure(_, decl, eid, _, _) = expr.kind {
         let body = cx.tcx.hir().body(eid);
         let ex = &body.value;
 
         if ex.span.ctxt() != expr.span.ctxt() {
-            if let Some(VecArgs::Vec(&[])) = higher::vec_macro(cx, ex) {
-                // replace `|| vec![]` with `Vec::new`
-                span_lint_and_sugg(
-                    cx,
-                    REDUNDANT_CLOSURE,
-                    expr.span,
-                    "redundant closure",
-                    "replace the closure with `Vec::new`",
-                    "std::vec::Vec::new".into(),
-                    Applicability::MachineApplicable,
-                );
+            if decl.inputs.is_empty() {
+                if let Some(VecArgs::Vec(&[])) = higher::vec_macro(cx, ex) {
+                    // replace `|| vec![]` with `Vec::new`
+                    span_lint_and_sugg(
+                        cx,
+                        REDUNDANT_CLOSURE,
+                        expr.span,
+                        "redundant closure",
+                        "replace the closure with `Vec::new`",
+                        "std::vec::Vec::new".into(),
+                        Applicability::MachineApplicable,
+                    );
+                }
             }
             // skip `foo(|| macro!())`
             return;
         }
 
         if_chain!(
-            if let ExprKind::Call(ref caller, ref args) = ex.kind;
+            if let ExprKind::Call(caller, args) = ex.kind;
 
             if let ExprKind::Path(_) = caller.kind;
 
@@ -130,7 +132,18 @@ fn check_closure(cx: &LateContext<'_>, expr: &Expr<'_>) {
 
             then {
                 span_lint_and_then(cx, REDUNDANT_CLOSURE, expr.span, "redundant closure", |diag| {
-                    if let Some(snippet) = snippet_opt(cx, caller.span) {
+                    if let Some(mut snippet) = snippet_opt(cx, caller.span) {
+                        if_chain! {
+                            if let ty::Closure(_, substs) = fn_ty.kind();
+                            if let ClosureKind::FnMut = substs.as_closure().kind();
+                            if UsedAfterExprVisitor::is_found(cx, caller)
+                                || get_enclosing_loop_or_closure(cx.tcx, expr).is_some();
+
+                            then {
+                                // Mutable closure is used after current expr; we cannot consume it.
+                                snippet = format!("&mut {}", snippet);
+                            }
+                        }
                         diag.span_suggestion(
                             expr.span,
                             "replace the closure with the function itself",
@@ -143,7 +156,7 @@ fn check_closure(cx: &LateContext<'_>, expr: &Expr<'_>) {
         );
 
         if_chain!(
-            if let ExprKind::MethodCall(ref path, _, ref args, _) = ex.kind;
+            if let ExprKind::MethodCall(path, _, args, _) = ex.kind;
 
             // Not the same number of arguments, there is no way the closure is the same as the function return;
             if args.len() == decl.inputs.len();
@@ -179,7 +192,7 @@ fn get_ufcs_type_name(cx: &LateContext<'_>, method_def_id: def_id::DefId, self_a
     let actual_type_of_self = &cx.typeck_results().node_type(self_arg.hir_id);
 
     if let Some(trait_id) = cx.tcx.trait_of_item(method_def_id) {
-        if match_borrow_depth(expected_type_of_self, &actual_type_of_self)
+        if match_borrow_depth(expected_type_of_self, actual_type_of_self)
             && implements_trait(cx, actual_type_of_self, trait_id, &[])
         {
             return Some(cx.tcx.def_path_str(trait_id));
@@ -188,16 +201,17 @@ fn get_ufcs_type_name(cx: &LateContext<'_>, method_def_id: def_id::DefId, self_a
 
     cx.tcx.impl_of_method(method_def_id).and_then(|_| {
         //a type may implicitly implement other type's methods (e.g. Deref)
-        if match_types(expected_type_of_self, &actual_type_of_self) {
-            return Some(get_type_name(cx, &actual_type_of_self));
+        if match_types(expected_type_of_self, actual_type_of_self) {
+            Some(get_type_name(cx, actual_type_of_self))
+        } else {
+            None
         }
-        None
     })
 }
 
 fn match_borrow_depth(lhs: Ty<'_>, rhs: Ty<'_>) -> bool {
     match (&lhs.kind(), &rhs.kind()) {
-        (ty::Ref(_, t1, mut1), ty::Ref(_, t2, mut2)) => mut1 == mut2 && match_borrow_depth(&t1, &t2),
+        (ty::Ref(_, t1, mut1), ty::Ref(_, t2, mut2)) => mut1 == mut2 && match_borrow_depth(t1, t2),
         (l, r) => !matches!((l, r), (ty::Ref(_, _, _), _) | (_, ty::Ref(_, _, _))),
     }
 }
@@ -219,7 +233,7 @@ fn match_types(lhs: Ty<'_>, rhs: Ty<'_>) -> bool {
 fn get_type_name(cx: &LateContext<'_>, ty: Ty<'_>) -> String {
     match ty.kind() {
         ty::Adt(t, _) => cx.tcx.def_path_str(t.did),
-        ty::Ref(_, r, _) => get_type_name(cx, &r),
+        ty::Ref(_, r, _) => get_type_name(cx, r),
         _ => ty.to_string(),
     }
 }
@@ -231,7 +245,7 @@ fn compare_inputs(
     for (closure_input, function_arg) in closure_inputs.zip(call_args) {
         if let PatKind::Binding(_, _, ident, _) = closure_input.pat.kind {
             // XXXManishearth Should I be checking the binding mode here?
-            if let ExprKind::Path(QPath::Resolved(None, ref p)) = function_arg.kind {
+            if let ExprKind::Path(QPath::Resolved(None, p)) = function_arg.kind {
                 if p.segments.len() != 1 {
                     // If it's a proper path, it can't be a local variable
                     return false;