about summary refs log tree commit diff
diff options
context:
space:
mode:
authory21 <30553356+y21@users.noreply.github.com>2023-06-23 23:23:36 +0200
committery21 <30553356+y21@users.noreply.github.com>2023-07-10 11:24:16 +0200
commit23ac72316d7cf0c7979bf94544b2cc8f3ea3f89f (patch)
treee200664390638b4873b0f2c5f6b5f00522d38e37
parent9058b040c89b040edf7a4273945a92bd258cb954 (diff)
downloadrust-23ac72316d7cf0c7979bf94544b2cc8f3ea3f89f.tar.gz
rust-23ac72316d7cf0c7979bf94544b2cc8f3ea3f89f.zip
adjust applicability and suggest making binding mutable
-rw-r--r--clippy_lints/src/methods/filter_next.rs51
-rw-r--r--tests/ui/methods_unfixable.rs10
-rw-r--r--tests/ui/methods_unfixable.stderr15
3 files changed, 66 insertions, 10 deletions
diff --git a/clippy_lints/src/methods/filter_next.rs b/clippy_lints/src/methods/filter_next.rs
index ad71b494619..ce7f9997b5a 100644
--- a/clippy_lints/src/methods/filter_next.rs
+++ b/clippy_lints/src/methods/filter_next.rs
@@ -1,6 +1,7 @@
-use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
+use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
 use clippy_utils::source::snippet;
 use clippy_utils::ty::implements_trait;
+use rustc_ast::{BindingAnnotation, Mutability};
 use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_lint::LateContext;
@@ -8,6 +9,21 @@ use rustc_span::sym;
 
 use super::FILTER_NEXT;
 
+fn path_to_local(expr: &hir::Expr<'_>) -> Option<hir::HirId> {
+    match expr.kind {
+        hir::ExprKind::Field(f, _) => path_to_local(f),
+        hir::ExprKind::Index(recv, _) => path_to_local(recv),
+        hir::ExprKind::Path(hir::QPath::Resolved(
+            _,
+            hir::Path {
+                res: rustc_hir::def::Res::Local(local),
+                ..
+            },
+        )) => Some(*local),
+        _ => None,
+    }
+}
+
 /// lint use of `filter().next()` for `Iterators`
 pub(super) fn check<'tcx>(
     cx: &LateContext<'tcx>,
@@ -26,15 +42,30 @@ pub(super) fn check<'tcx>(
         if filter_snippet.lines().count() <= 1 {
             let iter_snippet = snippet(cx, recv.span, "..");
             // add note if not multi-line
-            span_lint_and_sugg(
-                cx,
-                FILTER_NEXT,
-                expr.span,
-                msg,
-                "try",
-                format!("{iter_snippet}.find({filter_snippet})"),
-                Applicability::MachineApplicable,
-            );
+            span_lint_and_then(cx, FILTER_NEXT, expr.span, msg, |diag| {
+                let (applicability, pat) = if let Some(id) = path_to_local(recv)
+                    && let Some(hir::Node::Pat(pat)) = cx.tcx.hir().find(id)
+                    && let hir::PatKind::Binding(BindingAnnotation(_, Mutability::Not), _, ident, _) = pat.kind
+                {
+                    (Applicability::Unspecified, Some((pat.span, ident)))
+                } else {
+                    (Applicability::MachineApplicable, None)
+                };
+
+                diag.span_suggestion(
+                    expr.span,
+                    "try",
+                    format!("{iter_snippet}.find({filter_snippet})"),
+                    applicability,
+                );
+
+                if let Some((pat_span, ident)) = pat {
+                    diag.span_help(
+                        pat_span,
+                        format!("you will also need to make `{ident}` mutable, because `find` takes `&mut self`"),
+                    );
+                }
+            });
         } else {
             span_lint(cx, FILTER_NEXT, expr.span, msg);
         }
diff --git a/tests/ui/methods_unfixable.rs b/tests/ui/methods_unfixable.rs
new file mode 100644
index 00000000000..3d88ce4b6bb
--- /dev/null
+++ b/tests/ui/methods_unfixable.rs
@@ -0,0 +1,10 @@
+#![warn(clippy::filter_next)]
+
+fn main() {
+    issue10029();
+}
+
+pub fn issue10029() {
+    let iter = (0..10);
+    let _ = iter.filter(|_| true).next();
+}
diff --git a/tests/ui/methods_unfixable.stderr b/tests/ui/methods_unfixable.stderr
new file mode 100644
index 00000000000..6e101fe16b0
--- /dev/null
+++ b/tests/ui/methods_unfixable.stderr
@@ -0,0 +1,15 @@
+error: called `filter(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(..)` instead
+  --> $DIR/methods_unfixable.rs:9:13
+   |
+LL |     let _ = iter.filter(|_| true).next();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `iter.find(|_| true)`
+   |
+help: you will also need to make `iter` mutable, because `find` takes `&mut self`
+  --> $DIR/methods_unfixable.rs:8:9
+   |
+LL |     let iter = (0..10);
+   |         ^^^^
+   = note: `-D clippy::filter-next` implied by `-D warnings`
+
+error: aborting due to previous error
+