Skip to content

Commit 3be3fb7

Browse files
committed
Auto merge of #11016 - y21:issue10029, r=blyxyas,dswij
[`filter_next`]: suggest making binding mutable if it needs to be Fixes #10029 changelog: [`filter_next`]: suggest making binding mutable if it needs to be and adjust applicability
2 parents 9ab7137 + 23ac723 commit 3be3fb7

File tree

3 files changed

+66
-10
lines changed

3 files changed

+66
-10
lines changed

clippy_lints/src/methods/filter_next.rs

+41-10
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,29 @@
1-
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
1+
use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
22
use clippy_utils::source::snippet;
33
use clippy_utils::ty::implements_trait;
4+
use rustc_ast::{BindingAnnotation, Mutability};
45
use rustc_errors::Applicability;
56
use rustc_hir as hir;
67
use rustc_lint::LateContext;
78
use rustc_span::sym;
89

910
use super::FILTER_NEXT;
1011

12+
fn path_to_local(expr: &hir::Expr<'_>) -> Option<hir::HirId> {
13+
match expr.kind {
14+
hir::ExprKind::Field(f, _) => path_to_local(f),
15+
hir::ExprKind::Index(recv, _) => path_to_local(recv),
16+
hir::ExprKind::Path(hir::QPath::Resolved(
17+
_,
18+
hir::Path {
19+
res: rustc_hir::def::Res::Local(local),
20+
..
21+
},
22+
)) => Some(*local),
23+
_ => None,
24+
}
25+
}
26+
1127
/// lint use of `filter().next()` for `Iterators`
1228
pub(super) fn check<'tcx>(
1329
cx: &LateContext<'tcx>,
@@ -26,15 +42,30 @@ pub(super) fn check<'tcx>(
2642
if filter_snippet.lines().count() <= 1 {
2743
let iter_snippet = snippet(cx, recv.span, "..");
2844
// add note if not multi-line
29-
span_lint_and_sugg(
30-
cx,
31-
FILTER_NEXT,
32-
expr.span,
33-
msg,
34-
"try",
35-
format!("{iter_snippet}.find({filter_snippet})"),
36-
Applicability::MachineApplicable,
37-
);
45+
span_lint_and_then(cx, FILTER_NEXT, expr.span, msg, |diag| {
46+
let (applicability, pat) = if let Some(id) = path_to_local(recv)
47+
&& let Some(hir::Node::Pat(pat)) = cx.tcx.hir().find(id)
48+
&& let hir::PatKind::Binding(BindingAnnotation(_, Mutability::Not), _, ident, _) = pat.kind
49+
{
50+
(Applicability::Unspecified, Some((pat.span, ident)))
51+
} else {
52+
(Applicability::MachineApplicable, None)
53+
};
54+
55+
diag.span_suggestion(
56+
expr.span,
57+
"try",
58+
format!("{iter_snippet}.find({filter_snippet})"),
59+
applicability,
60+
);
61+
62+
if let Some((pat_span, ident)) = pat {
63+
diag.span_help(
64+
pat_span,
65+
format!("you will also need to make `{ident}` mutable, because `find` takes `&mut self`"),
66+
);
67+
}
68+
});
3869
} else {
3970
span_lint(cx, FILTER_NEXT, expr.span, msg);
4071
}

tests/ui/methods_unfixable.rs

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#![warn(clippy::filter_next)]
2+
3+
fn main() {
4+
issue10029();
5+
}
6+
7+
pub fn issue10029() {
8+
let iter = (0..10);
9+
let _ = iter.filter(|_| true).next();
10+
}

tests/ui/methods_unfixable.stderr

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
error: called `filter(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(..)` instead
2+
--> $DIR/methods_unfixable.rs:9:13
3+
|
4+
LL | let _ = iter.filter(|_| true).next();
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `iter.find(|_| true)`
6+
|
7+
help: you will also need to make `iter` mutable, because `find` takes `&mut self`
8+
--> $DIR/methods_unfixable.rs:8:9
9+
|
10+
LL | let iter = (0..10);
11+
| ^^^^
12+
= note: `-D clippy::filter-next` implied by `-D warnings`
13+
14+
error: aborting due to previous error
15+

0 commit comments

Comments
 (0)