From 23ac72316d7cf0c7979bf94544b2cc8f3ea3f89f Mon Sep 17 00:00:00 2001 From: y21 <30553356+y21@users.noreply.github.com> Date: Fri, 23 Jun 2023 23:23:36 +0200 Subject: [PATCH] adjust applicability and suggest making binding mutable --- clippy_lints/src/methods/filter_next.rs | 51 ++++++++++++++++++++----- tests/ui/methods_unfixable.rs | 10 +++++ tests/ui/methods_unfixable.stderr | 15 ++++++++ 3 files changed, 66 insertions(+), 10 deletions(-) create mode 100644 tests/ui/methods_unfixable.rs create mode 100644 tests/ui/methods_unfixable.stderr diff --git a/clippy_lints/src/methods/filter_next.rs b/clippy_lints/src/methods/filter_next.rs index ad71b4946190..ce7f9997b5a6 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 { + 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 000000000000..3d88ce4b6bbd --- /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 000000000000..6e101fe16b09 --- /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 +