]> 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 87254c1dbc490a9fac3706194fa35d9759f4d4cc..667eb8eb283bbb09997d47f8589bd86118e53e52 100644 (file)
@@ -1,16 +1,18 @@
+use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
+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::{
-    implements_trait, is_adjusted, iter_input_pats, snippet_opt, span_lint_and_sugg, span_lint_and_then,
-    type_is_unsafe_function,
-};
-
 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
@@ -22,7 +24,7 @@
     /// **Known problems:** If creating the closure inside the closure has a side-
     /// effect then moving the closure creation out will change when that side-
     /// effect runs.
-    /// See rust-lang/rust-clippy#1439 for more details.
+    /// See [#1439](https://github.com/rust-lang/rust-clippy/issues/1439) for more details.
     ///
     /// **Example:**
     /// ```rust,ignore
@@ -45,8 +47,9 @@
     ///
     /// **Why is this bad?** It's unnecessary to create the closure.
     ///
-    /// **Known problems:** rust-lang/rust-clippy#3071, rust-lang/rust-clippy#4002,
-    /// rust-lang/rust-clippy#3942
+    /// **Known problems:** [#3071](https://github.com/rust-lang/rust-clippy/issues/3071),
+    /// [#3942](https://github.com/rust-lang/rust-clippy/issues/3942),
+    /// [#4002](https://github.com/rust-lang/rust-clippy/issues/4002)
     ///
     ///
     /// **Example:**
@@ -73,7 +76,10 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
         match expr.kind {
             ExprKind::Call(_, args) | ExprKind::MethodCall(_, _, args, _) => {
                 for arg in args {
-                    check_closure(cx, arg)
+                    // skip `foo(macro!())`
+                    if arg.span.ctxt() == expr.span.ctxt() {
+                        check_closure(cx, arg);
+                    }
                 }
             },
             _ => (),
@@ -81,13 +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 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;
 
@@ -99,18 +124,29 @@ fn check_closure(cx: &LateContext<'_>, expr: &Expr<'_>) {
 
             let fn_ty = cx.typeck_results().expr_ty(caller);
 
-            if matches!(fn_ty.kind, ty::FnDef(_, _) | ty::FnPtr(_) | ty::Closure(_, _));
+            if matches!(fn_ty.kind(), ty::FnDef(_, _) | ty::FnPtr(_) | ty::Closure(_, _));
 
             if !type_is_unsafe_function(cx, fn_ty);
 
             if compare_inputs(&mut iter_input_pats(decl, body), &mut args.iter());
 
             then {
-                span_lint_and_then(cx, REDUNDANT_CLOSURE, expr.span, "redundant closure found", |diag| {
-                    if let Some(snippet) = snippet_opt(cx, caller.span) {
+                span_lint_and_then(cx, REDUNDANT_CLOSURE, expr.span, "redundant closure", |diag| {
+                    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,
-                            "remove closure as shown",
+                            "replace the closure with the function itself",
                             snippet,
                             Applicability::MachineApplicable,
                         );
@@ -120,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();
@@ -140,8 +176,8 @@ fn check_closure(cx: &LateContext<'_>, expr: &Expr<'_>) {
                     cx,
                     REDUNDANT_CLOSURE_FOR_METHOD_CALLS,
                     expr.span,
-                    "redundant closure found",
-                    "remove closure as shown",
+                    "redundant closure",
+                    "replace the closure with the method itself",
                     format!("{}::{}", name, path.ident.name),
                     Applicability::MachineApplicable,
                 );
@@ -156,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));
@@ -165,22 +201,23 @@ 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),
+    match (&lhs.kind(), &rhs.kind()) {
+        (ty::Ref(_, t1, mut1), ty::Ref(_, t2, mut2)) => mut1 == mut2 && match_borrow_depth(t1, t2),
         (l, r) => !matches!((l, r), (ty::Ref(_, _, _), _) | (_, ty::Ref(_, _, _))),
     }
 }
 
 fn match_types(lhs: Ty<'_>, rhs: Ty<'_>) -> bool {
-    match (&lhs.kind, &rhs.kind) {
+    match (&lhs.kind(), &rhs.kind()) {
         (ty::Bool, ty::Bool)
         | (ty::Char, ty::Char)
         | (ty::Int(_), ty::Int(_))
@@ -194,9 +231,9 @@ fn match_types(lhs: Ty<'_>, rhs: Ty<'_>) -> bool {
 }
 
 fn get_type_name(cx: &LateContext<'_>, ty: Ty<'_>) -> String {
-    match ty.kind {
+    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(),
     }
 }
@@ -208,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;