@@ -10,51 +10,68 @@ use rustc_span::sym;
1010
1111use super :: FILTER_NEXT ;
1212
13- /// lint use of `filter().next()` for `Iterators`
13+ #[ derive( Copy , Clone ) ]
14+ pub ( super ) enum Direction {
15+ Forward ,
16+ Backward ,
17+ }
18+
19+ /// lint use of `filter().next()` for `Iterator` and `filter().next_back()` for
20+ /// `DoubleEndedIterator`
1421pub ( super ) fn check < ' tcx > (
1522 cx : & LateContext < ' tcx > ,
1623 expr : & ' tcx hir:: Expr < ' _ > ,
1724 recv : & ' tcx hir:: Expr < ' _ > ,
1825 filter_arg : & ' tcx hir:: Expr < ' _ > ,
26+ direction : Direction ,
1927) {
20- // lint if caller of `.filter().next()` is an Iterator
21- let recv_impls_iterator = cx
28+ // lint if caller of `.filter().next()` is an Iterator or `.filter().next_back()` is a
29+ // DoubleEndedIterator
30+ let ( required_trait, next_method, find_method) = match direction {
31+ Direction :: Forward => ( sym:: Iterator , "next" , "find" ) ,
32+ Direction :: Backward => ( sym:: DoubleEndedIterator , "next_back" , "rfind" ) ,
33+ } ;
34+ if !cx
2235 . tcx
23- . get_diagnostic_item ( sym:: Iterator )
24- . is_some_and ( |id| implements_trait ( cx, cx. typeck_results ( ) . expr_ty ( recv) , id, & [ ] ) ) ;
25- if recv_impls_iterator {
26- let msg = "called `filter(..).next()` on an `Iterator`. This is more succinctly expressed by calling \
27- `.find(..)` instead";
28- let filter_snippet = snippet ( cx, filter_arg. span , ".." ) ;
29- if filter_snippet. lines ( ) . count ( ) <= 1 {
30- let iter_snippet = snippet ( cx, recv. span , ".." ) ;
31- // add note if not multi-line
32- span_lint_and_then ( cx, FILTER_NEXT , expr. span , msg, |diag| {
33- let ( applicability, pat) = if let Some ( id) = path_to_local_with_projections ( recv)
34- && let hir:: Node :: Pat ( pat) = cx. tcx . hir_node ( id)
35- && let hir:: PatKind :: Binding ( BindingMode ( _, Mutability :: Not ) , _, ident, _) = pat. kind
36- {
37- ( Applicability :: Unspecified , Some ( ( pat. span , ident) ) )
38- } else {
39- ( Applicability :: MachineApplicable , None )
40- } ;
36+ . get_diagnostic_item ( required_trait)
37+ . is_some_and ( |id| implements_trait ( cx, cx. typeck_results ( ) . expr_ty ( recv) , id, & [ ] ) )
38+ {
39+ return ;
40+ }
41+ let msg = format ! (
42+ "called `filter(..).{next_method}()` on an `{}`. This is more succinctly expressed by calling \
43+ `.{find_method}(..)` instead",
44+ required_trait. as_str( )
45+ ) ;
46+ let filter_snippet = snippet ( cx, filter_arg. span , ".." ) ;
47+ if filter_snippet. lines ( ) . count ( ) <= 1 {
48+ let iter_snippet = snippet ( cx, recv. span , ".." ) ;
49+ // add note if not multi-line
50+ span_lint_and_then ( cx, FILTER_NEXT , expr. span , msg, |diag| {
51+ let ( applicability, pat) = if let Some ( id) = path_to_local_with_projections ( recv)
52+ && let hir:: Node :: Pat ( pat) = cx. tcx . hir_node ( id)
53+ && let hir:: PatKind :: Binding ( BindingMode ( _, Mutability :: Not ) , _, ident, _) = pat. kind
54+ {
55+ ( Applicability :: Unspecified , Some ( ( pat. span , ident) ) )
56+ } else {
57+ ( Applicability :: MachineApplicable , None )
58+ } ;
4159
42- diag. span_suggestion (
43- expr. span ,
44- "try" ,
45- format ! ( "{iter_snippet}.find ({filter_snippet})" ) ,
46- applicability,
47- ) ;
60+ diag. span_suggestion (
61+ expr. span ,
62+ "try" ,
63+ format ! ( "{iter_snippet}.{find_method} ({filter_snippet})" ) ,
64+ applicability,
65+ ) ;
4866
49- if let Some ( ( pat_span, ident) ) = pat {
50- diag. span_help (
51- pat_span,
52- format ! ( "you will also need to make `{ident}` mutable, because `find` takes `&mut self`" ) ,
53- ) ;
54- }
55- } ) ;
56- } else {
57- span_lint ( cx, FILTER_NEXT , expr. span , msg) ;
58- }
67+ if let Some ( ( pat_span, ident) ) = pat {
68+ diag. span_help (
69+ pat_span,
70+ format ! ( "you will also need to make `{ident}` mutable, because `{find_method}` takes `&mut self`" ) ,
71+ ) ;
72+ }
73+ } ) ;
74+ } else {
75+ span_lint ( cx, FILTER_NEXT , expr. span , msg) ;
5976 }
6077}
0 commit comments