From c720d823e1e633cccfd24e1df76cc04a628e83b0 Mon Sep 17 00:00:00 2001
From: Tim Nielens <tim.nielens@gmail.com>
Date: Tue, 14 Jul 2020 20:27:25 +0200
Subject: [PATCH] redundant_closure_call - don't lint when used more than once

---
 clippy_lints/src/misc_early.rs         | 40 ++++++++++++++++++++------
 tests/ui/redundant_closure_call.rs     | 15 +++++++---
 tests/ui/redundant_closure_call.stderr | 14 +++------
 3 files changed, 47 insertions(+), 22 deletions(-)

diff --git a/clippy_lints/src/misc_early.rs b/clippy_lints/src/misc_early.rs
index b84a1a3fe24..125df226ceb 100644
--- a/clippy_lints/src/misc_early.rs
+++ b/clippy_lints/src/misc_early.rs
@@ -14,6 +14,7 @@ use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::source_map::Span;
+use rustc_span::symbol::Ident;
 
 declare_clippy_lint! {
     /// **What it does:** Checks for structure field patterns bound to wildcards.
@@ -493,6 +494,29 @@ impl EarlyLintPass for MiscEarlyLints {
     }
 
     fn check_block(&mut self, cx: &EarlyContext<'_>, block: &Block) {
+        fn count_closure_usage(block: &Block, ident: &Ident) -> usize {
+            struct ClosureUsageCount<'ast> {
+                ident: &'ast Ident,
+                count: usize,
+            };
+            impl<'ast> Visitor<'ast> for ClosureUsageCount<'ast> {
+                fn visit_expr(&mut self, expr: &'ast Expr) {
+                    if_chain! {
+                        if let ExprKind::Call(ref closure, _) = expr.kind;
+                        if let ExprKind::Path(_, ref path) = closure.kind;
+                        if self.ident == &path.segments[0].ident;
+                        then {
+                            self.count += 1;
+                        }
+                    }
+                    walk_expr(self, expr);
+                }
+            }
+            let mut closure_usage_count = ClosureUsageCount { ident, count: 0 };
+            closure_usage_count.visit_block(block);
+            closure_usage_count.count
+        }
+
         for w in block.stmts.windows(2) {
             if_chain! {
                 if let StmtKind::Local(ref local) = w[0].kind;
@@ -503,15 +527,15 @@ impl EarlyLintPass for MiscEarlyLints {
                 if let ExprKind::Assign(_, ref call, _) = second.kind;
                 if let ExprKind::Call(ref closure, _) = call.kind;
                 if let ExprKind::Path(_, ref path) = closure.kind;
+                if ident == path.segments[0].ident;
+                if  count_closure_usage(block, &ident) == 1;
                 then {
-                    if ident == path.segments[0].ident {
-                        span_lint(
-                            cx,
-                            REDUNDANT_CLOSURE_CALL,
-                            second.span,
-                            "Closure called just once immediately after it was declared",
-                        );
-                    }
+                    span_lint(
+                        cx,
+                        REDUNDANT_CLOSURE_CALL,
+                        second.span,
+                        "Closure called just once immediately after it was declared",
+                    );
                 }
             }
         }
diff --git a/tests/ui/redundant_closure_call.rs b/tests/ui/redundant_closure_call.rs
index bacd67db7c3..0f2ba4a075d 100644
--- a/tests/ui/redundant_closure_call.rs
+++ b/tests/ui/redundant_closure_call.rs
@@ -8,14 +8,21 @@ fn main() {
 
     k = (|a, b| a * b)(1, 5);
 
-    let closure = || 32;
-    i = closure();
-
+    // don't lint here, the closure is used more than once
     let closure = |i| i + 1;
     i = closure(3);
-
     i = closure(4);
 
+    // lint here
+    let redun_closure = || 1;
+    i = redun_closure();
+
+    // the lint is applicable here but the lint doesn't support redefinition
+    let redefined_closure = || 1;
+    i = redefined_closure();
+    let redefined_closure = || 2;
+    i = redefined_closure();
+
     #[allow(clippy::needless_return)]
     (|| return 2)();
     (|| -> Option<i32> { None? })();
diff --git a/tests/ui/redundant_closure_call.stderr b/tests/ui/redundant_closure_call.stderr
index 68c1416bb6b..d5e0664319b 100644
--- a/tests/ui/redundant_closure_call.stderr
+++ b/tests/ui/redundant_closure_call.stderr
@@ -1,17 +1,11 @@
 error: Closure called just once immediately after it was declared
-  --> $DIR/redundant_closure_call.rs:12:5
+  --> $DIR/redundant_closure_call.rs:18:5
    |
-LL |     i = closure();
-   |     ^^^^^^^^^^^^^
+LL |     i = redun_closure();
+   |     ^^^^^^^^^^^^^^^^^^^
    |
    = note: `-D clippy::redundant-closure-call` implied by `-D warnings`
 
-error: Closure called just once immediately after it was declared
-  --> $DIR/redundant_closure_call.rs:15:5
-   |
-LL |     i = closure(3);
-   |     ^^^^^^^^^^^^^^
-
 error: Try not to call a closure in the expression where it is declared.
   --> $DIR/redundant_closure_call.rs:7:17
    |
@@ -24,5 +18,5 @@ error: Try not to call a closure in the expression where it is declared.
 LL |     k = (|a, b| a * b)(1, 5);
    |         ^^^^^^^^^^^^^^^^^^^^
 
-error: aborting due to 4 previous errors
+error: aborting due to 3 previous errors