]> git.lizzy.rs Git - rust.git/blobdiff - clippy_lints/src/eta_reduction.rs
Auto merge of #4551 - mikerite:fix-ice-reporting, r=llogiq
[rust.git] / clippy_lints / src / eta_reduction.rs
index 8334faa6ec6c97cc384b06ab6e63455bcb0f9aec..485a29ed3d89c80e77874965065a7278ee493a0b 100644 (file)
@@ -6,7 +6,9 @@
 use rustc::{declare_lint_pass, declare_tool_lint};
 use rustc_errors::Applicability;
 
-use crate::utils::{is_adjusted, iter_input_pats, snippet_opt, span_lint_and_then, type_is_unsafe_function};
+use crate::utils::{
+    implements_trait, is_adjusted, iter_input_pats, snippet_opt, span_lint_and_then, type_is_unsafe_function,
+};
 
 declare_clippy_lint! {
     /// **What it does:** Checks for closures which just call another function where
     ///
     /// **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
+    ///
+    ///
     /// **Example:**
     /// ```rust,ignore
     /// Some('a').map(|s| s.to_uppercase());
     /// ```rust,ignore
     /// Some('a').map(char::to_uppercase);
     /// ```
-    pub REDUNDANT_CLOSURES_FOR_METHOD_CALLS,
+    pub REDUNDANT_CLOSURE_FOR_METHOD_CALLS,
     pedantic,
     "redundant closures for method calls"
 }
 
-declare_lint_pass!(EtaReduction => [REDUNDANT_CLOSURE, REDUNDANT_CLOSURES_FOR_METHOD_CALLS]);
+declare_lint_pass!(EtaReduction => [REDUNDANT_CLOSURE, REDUNDANT_CLOSURE_FOR_METHOD_CALLS]);
 
 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for EtaReduction {
     fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
@@ -78,6 +84,8 @@ fn check_closure(cx: &LateContext<'_, '_>, expr: &Expr) {
         if_chain!(
             if let ExprKind::Call(ref caller, ref args) = ex.node;
 
+            if let ExprKind::Path(_) = caller.node;
+
             // 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();
 
@@ -123,7 +131,7 @@ fn check_closure(cx: &LateContext<'_, '_>, expr: &Expr) {
             if let Some(name) = get_ufcs_type_name(cx, method_def_id, &args[0]);
 
             then {
-                span_lint_and_then(cx, REDUNDANT_CLOSURES_FOR_METHOD_CALLS, expr.span, "redundant closure found", |db| {
+                span_lint_and_then(cx, REDUNDANT_CLOSURE_FOR_METHOD_CALLS, expr.span, "redundant closure found", |db| {
                     db.span_suggestion(
                         expr.span,
                         "remove closure as shown",
@@ -146,7 +154,9 @@ fn get_ufcs_type_name(
     let actual_type_of_self = &cx.tables.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));
         }
     }
@@ -162,7 +172,7 @@ fn get_ufcs_type_name(
 
 fn match_borrow_depth(lhs: Ty<'_>, rhs: Ty<'_>) -> bool {
     match (&lhs.sty, &rhs.sty) {
-        (ty::Ref(_, t1, _), ty::Ref(_, t2, _)) => match_borrow_depth(&t1, &t2),
+        (ty::Ref(_, t1, mut1), ty::Ref(_, t2, mut2)) => mut1 == mut2 && match_borrow_depth(&t1, &t2),
         (l, r) => match (l, r) {
             (ty::Ref(_, _, _), _) | (_, ty::Ref(_, _, _)) => false,
             (_, _) => true,
@@ -177,9 +187,8 @@ fn match_types(lhs: Ty<'_>, rhs: Ty<'_>) -> bool {
         | (ty::Int(_), ty::Int(_))
         | (ty::Uint(_), ty::Uint(_))
         | (ty::Str, ty::Str) => true,
-        (ty::Ref(_, t1, _), ty::Ref(_, t2, _))
-        | (ty::Array(t1, _), ty::Array(t2, _))
-        | (ty::Slice(t1), ty::Slice(t2)) => match_types(t1, t2),
+        (ty::Ref(_, t1, mut1), ty::Ref(_, t2, mut2)) => mut1 == mut2 && match_types(t1, t2),
+        (ty::Array(t1, _), ty::Array(t2, _)) | (ty::Slice(t1), ty::Slice(t2)) => match_types(t1, t2),
         (ty::Adt(def1, _), ty::Adt(def2, _)) => def1 == def2,
         (_, _) => false,
     }
@@ -193,7 +202,10 @@ fn get_type_name(cx: &LateContext<'_, '_>, ty: Ty<'_>) -> String {
     }
 }
 
-fn compare_inputs(closure_inputs: &mut dyn Iterator<Item = &Arg>, call_args: &mut dyn Iterator<Item = &Expr>) -> bool {
+fn compare_inputs(
+    closure_inputs: &mut dyn Iterator<Item = &Param>,
+    call_args: &mut dyn Iterator<Item = &Expr>,
+) -> bool {
     for (closure_input, function_arg) in closure_inputs.zip(call_args) {
         if let PatKind::Binding(_, _, ident, _) = closure_input.pat.node {
             // XXXManishearth Should I be checking the binding mode here?